diff options
Diffstat (limited to 'drivers')
1347 files changed, 101879 insertions, 44892 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index 808e0ae66aa8..54ec5e718c0e 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -5,7 +5,7 @@ # Rewritten to use lists instead of if-statements. # -obj-$(CONFIG_HAVE_GPIO_LIB) += gpio/ +obj-y += gpio/ obj-$(CONFIG_PCI) += pci/ obj-$(CONFIG_PARISC) += parisc/ obj-$(CONFIG_RAPIDIO) += rapidio/ diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 55c17afbe669..2655bc1b4eeb 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -263,22 +263,22 @@ static int acpi_fan_add(struct acpi_device *device) goto end; } - printk(KERN_INFO PREFIX - "%s is registered as cooling_device%d\n", - device->dev.bus_id, cdev->id); + dev_info(&device->dev, "registered as cooling_device%d\n", cdev->id); acpi_driver_data(device) = cdev; result = sysfs_create_link(&device->dev.kobj, &cdev->device.kobj, "thermal_cooling"); if (result) - printk(KERN_ERR PREFIX "Create sysfs link\n"); + dev_err(&device->dev, "Failed to create sysfs link " + "'thermal_cooling'\n"); result = sysfs_create_link(&cdev->device.kobj, &device->dev.kobj, "device"); if (result) - printk(KERN_ERR PREFIX "Create sysfs link\n"); + dev_err(&device->dev, "Failed to create sysfs link " + "'device'\n"); result = acpi_fan_add_fs(device); if (result) diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 2f173e83f8a7..084109507c9f 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -146,8 +146,7 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle) acpi_status status; if (dev->archdata.acpi_handle) { - printk(KERN_WARNING PREFIX - "Drivers changed 'acpi_handle' for %s\n", dev->bus_id); + dev_warn(dev, "Drivers changed 'acpi_handle'\n"); return -EINVAL; } get_device(dev); @@ -195,8 +194,7 @@ static int acpi_unbind_one(struct device *dev) /* acpi_bind_one increase refcnt by one */ put_device(dev); } else { - printk(KERN_ERR PREFIX - "Oops, 'acpi_handle' corrupt for %s\n", dev->bus_id); + dev_err(dev, "Oops, 'acpi_handle' corrupt\n"); } return 0; } diff --git a/drivers/acpi/namespace/nsnames.c b/drivers/acpi/namespace/nsnames.c index cffef1bcbdbc..549db42f16cf 100644 --- a/drivers/acpi/namespace/nsnames.c +++ b/drivers/acpi/namespace/nsnames.c @@ -137,6 +137,10 @@ char *acpi_ns_get_external_pathname(struct acpi_namespace_node *node) /* Calculate required buffer size based on depth below root */ size = acpi_ns_get_pathname_length(node); + if (!size) { + ACPI_ERROR((AE_INFO, "Invalid node failure")); + return_PTR(NULL); + } /* Allocate a buffer to be returned to caller */ @@ -229,6 +233,10 @@ acpi_ns_handle_to_pathname(acpi_handle target_handle, /* Determine size required for the caller buffer */ required_size = acpi_ns_get_pathname_length(node); + if (!required_size) { + ACPI_ERROR((AE_INFO, "Invalid node failure")); + return_ACPI_STATUS(AE_ERROR); + } /* Validate/Allocate/Clear caller buffer */ diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 233c40c51684..89f3b2abfdc7 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -113,20 +113,23 @@ acpi_pci_link_check_possible(struct acpi_resource *resource, void *context) switch (resource->type) { case ACPI_RESOURCE_TYPE_START_DEPENDENT: + case ACPI_RESOURCE_TYPE_END_TAG: return AE_OK; case ACPI_RESOURCE_TYPE_IRQ: { struct acpi_resource_irq *p = &resource->data.irq; if (!p || !p->interrupt_count) { - printk(KERN_WARNING PREFIX "Blank IRQ resource\n"); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Blank _PRS IRQ resource\n")); return AE_OK; } for (i = 0; (i < p->interrupt_count && i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) { if (!p->interrupts[i]) { - printk(KERN_WARNING PREFIX "Invalid IRQ %d\n", - p->interrupts[i]); + printk(KERN_WARNING PREFIX + "Invalid _PRS IRQ %d\n", + p->interrupts[i]); continue; } link->irq.possible[i] = p->interrupts[i]; @@ -143,15 +146,16 @@ acpi_pci_link_check_possible(struct acpi_resource *resource, void *context) &resource->data.extended_irq; if (!p || !p->interrupt_count) { printk(KERN_WARNING PREFIX - "Blank EXT IRQ resource\n"); + "Blank _PRS EXT IRQ resource\n"); return AE_OK; } for (i = 0; (i < p->interrupt_count && i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) { if (!p->interrupts[i]) { - printk(KERN_WARNING PREFIX "Invalid IRQ %d\n", - p->interrupts[i]); + printk(KERN_WARNING PREFIX + "Invalid _PRS IRQ %d\n", + p->interrupts[i]); continue; } link->irq.possible[i] = p->interrupts[i]; @@ -163,7 +167,8 @@ acpi_pci_link_check_possible(struct acpi_resource *resource, void *context) break; } default: - printk(KERN_ERR PREFIX "Resource is not an IRQ entry\n"); + printk(KERN_ERR PREFIX "_PRS resource type 0x%x isn't an IRQ\n", + resource->type); return AE_OK; } @@ -199,6 +204,9 @@ acpi_pci_link_check_current(struct acpi_resource *resource, void *context) switch (resource->type) { + case ACPI_RESOURCE_TYPE_START_DEPENDENT: + case ACPI_RESOURCE_TYPE_END_TAG: + return AE_OK; case ACPI_RESOURCE_TYPE_IRQ: { struct acpi_resource_irq *p = &resource->data.irq; @@ -208,7 +216,7 @@ acpi_pci_link_check_current(struct acpi_resource *resource, void *context) * particularly those those w/ _STA disabled */ ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Blank IRQ resource\n")); + "Blank _CRS IRQ resource\n")); return AE_OK; } *irq = p->interrupts[0]; @@ -224,7 +232,7 @@ acpi_pci_link_check_current(struct acpi_resource *resource, void *context) * return at least 1 IRQ */ printk(KERN_WARNING PREFIX - "Blank EXT IRQ resource\n"); + "Blank _CRS EXT IRQ resource\n"); return AE_OK; } *irq = p->interrupts[0]; @@ -232,10 +240,11 @@ acpi_pci_link_check_current(struct acpi_resource *resource, void *context) } break; default: - printk(KERN_ERR PREFIX "Resource %d isn't an IRQ\n", resource->type); - case ACPI_RESOURCE_TYPE_END_TAG: + printk(KERN_ERR PREFIX "_CRS resource type 0x%x isn't an IRQ\n", + resource->type); return AE_OK; } + return AE_CTRL_TERMINATE; } diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c index b9ab030a52d5..d5b4ef898879 100644 --- a/drivers/acpi/pci_slot.c +++ b/drivers/acpi/pci_slot.c @@ -6,8 +6,8 @@ * Thanks to Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> for code * review and fixes. * - * Copyright (C) 2007 Alex Chiang <achiang@hp.com> - * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. + * Copyright (C) 2007-2008 Hewlett-Packard Development Company, L.P. + * Alex Chiang <achiang@hp.com> * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -76,9 +76,9 @@ static struct acpi_pci_driver acpi_pci_slot_driver = { }; static int -check_slot(acpi_handle handle, int *device, unsigned long *sun) +check_slot(acpi_handle handle, unsigned long *sun) { - int retval = 0; + int device = -1; unsigned long adr, sta; acpi_status status; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -89,32 +89,27 @@ check_slot(acpi_handle handle, int *device, unsigned long *sun) if (check_sta_before_sun) { /* If SxFy doesn't have _STA, we just assume it's there */ status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); - if (ACPI_SUCCESS(status) && !(sta & ACPI_STA_DEVICE_PRESENT)) { - retval = -1; + if (ACPI_SUCCESS(status) && !(sta & ACPI_STA_DEVICE_PRESENT)) goto out; - } } status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); if (ACPI_FAILURE(status)) { dbg("_ADR returned %d on %s\n", status, (char *)buffer.pointer); - retval = -1; goto out; } - *device = (adr >> 16) & 0xffff; - /* No _SUN == not a slot == bail */ status = acpi_evaluate_integer(handle, "_SUN", NULL, sun); if (ACPI_FAILURE(status)) { dbg("_SUN returned %d on %s\n", status, (char *)buffer.pointer); - retval = -1; goto out; } + device = (adr >> 16) & 0xffff; out: kfree(buffer.pointer); - return retval; + return device; } struct callback_args { @@ -144,7 +139,8 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) struct callback_args *parent_context = context; struct pci_bus *pci_bus = parent_context->pci_bus; - if (check_slot(handle, &device, &sun)) + device = check_slot(handle, &sun); + if (device < 0) return AE_OK; slot = kmalloc(sizeof(*slot), GFP_KERNEL); @@ -158,6 +154,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) if (IS_ERR(pci_slot)) { err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot)); kfree(slot); + return AE_OK; } slot->root_handle = parent_context->root_handle; diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index ec0f2d581ece..e36422a7122c 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -714,9 +714,8 @@ static int __cpuinit acpi_processor_start(struct acpi_device *device) goto end; } - printk(KERN_INFO PREFIX - "%s is registered as cooling_device%d\n", - device->dev.bus_id, pr->cdev->id); + dev_info(&device->dev, "registered as cooling_device%d\n", + pr->cdev->id); result = sysfs_create_link(&device->dev.kobj, &pr->cdev->device.kobj, diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index d592dbb1d12a..283c08f5f4d4 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -272,6 +272,8 @@ static atomic_t c3_cpu_count; /* Common C-state entry for C2, C3, .. */ static void acpi_cstate_enter(struct acpi_processor_cx *cstate) { + /* Don't trace irqs off for idle */ + stop_critical_timings(); if (cstate->entry_method == ACPI_CSTATE_FFH) { /* Call into architectural FFH based C-state */ acpi_processor_ffh_cstate_enter(cstate); @@ -284,6 +286,7 @@ static void acpi_cstate_enter(struct acpi_processor_cx *cstate) gets asserted in time to freeze execution properly. */ unused = inl(acpi_gbl_FADT.xpm_timer_block.address); } + start_critical_timings(); } #endif /* !CONFIG_CPU_IDLE */ @@ -1329,9 +1332,15 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr) if (!pr->flags.power_setup_done) return -ENODEV; - /* Fall back to the default idle loop */ - pm_idle = pm_idle_save; - synchronize_sched(); /* Relies on interrupts forcing exit from idle. */ + /* + * Fall back to the default idle loop, when pm_idle_save had + * been initialized. + */ + if (pm_idle_save) { + pm_idle = pm_idle_save; + /* Relies on interrupts forcing exit from idle. */ + synchronize_sched(); + } pr->flags.power = 0; result = acpi_processor_get_power_info(pr); @@ -1418,6 +1427,8 @@ static inline void acpi_idle_update_bm_rld(struct acpi_processor *pr, */ static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx) { + /* Don't trace irqs off for idle */ + stop_critical_timings(); if (cx->entry_method == ACPI_CSTATE_FFH) { /* Call into architectural FFH based C-state */ acpi_processor_ffh_cstate_enter(cx); @@ -1432,6 +1443,7 @@ static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx) gets asserted in time to freeze execution properly. */ unused = inl(acpi_gbl_FADT.xpm_timer_block.address); } + start_critical_timings(); } /** @@ -1890,7 +1902,8 @@ int acpi_processor_power_exit(struct acpi_processor *pr, /* Unregister the idle handler when processor #0 is removed. */ if (pr->id == 0) { - pm_idle = pm_idle_save; + if (pm_idle_save) + pm_idle = pm_idle_save; /* * We are about to unload the current idle thread pm callback diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index 0622ace05220..a56fc6c4394b 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -1013,7 +1013,7 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state) * affected cpu in order to get one proper T-state. * The notifier event is THROTTLING_PRECHANGE. */ - for_each_cpu_mask(i, online_throttling_cpus) { + for_each_cpu_mask_nr(i, online_throttling_cpus) { t_state.cpu = i; acpi_processor_throttling_notifier(THROTTLING_PRECHANGE, &t_state); @@ -1034,7 +1034,7 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state) * it is necessary to set T-state for every affected * cpus. */ - for_each_cpu_mask(i, online_throttling_cpus) { + for_each_cpu_mask_nr(i, online_throttling_cpus) { match_pr = per_cpu(processors, i); /* * If the pointer is invalid, we will report the @@ -1068,7 +1068,7 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state) * affected cpu to update the T-states. * The notifier event is THROTTLING_POSTCHANGE */ - for_each_cpu_mask(i, online_throttling_cpus) { + for_each_cpu_mask_nr(i, online_throttling_cpus) { t_state.cpu = i; acpi_processor_throttling_notifier(THROTTLING_POSTCHANGE, &t_state); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index f3132aa47a69..f6f52c1a2aba 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -471,7 +471,7 @@ static int acpi_device_register(struct acpi_device *device, device->dev.release = &acpi_device_release; result = device_add(&device->dev); if(result) { - printk(KERN_ERR PREFIX "Error adding device %s", device->dev.bus_id); + dev_err(&device->dev, "Error adding device\n"); goto end; } diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 0489a7d1d42c..d13194a031bf 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -280,9 +280,36 @@ static struct platform_suspend_ops acpi_suspend_ops_old = { .end = acpi_pm_end, .recover = acpi_pm_finish, }; + +static int __init init_old_suspend_ordering(const struct dmi_system_id *d) +{ + old_suspend_ordering = true; + return 0; +} + +static struct dmi_system_id __initdata acpisleep_dmi_table[] = { + { + .callback = init_old_suspend_ordering, + .ident = "Abit KN9 (nForce4 variant)", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "http://www.abit.com.tw/"), + DMI_MATCH(DMI_BOARD_NAME, "KN9 Series(NF-CK804)"), + }, + }, + {}, +}; #endif /* CONFIG_SUSPEND */ #ifdef CONFIG_HIBERNATION +static unsigned long s4_hardware_signature; +static struct acpi_table_facs *facs; +static bool nosigcheck; + +void __init acpi_no_s4_hw_signature(void) +{ + nosigcheck = true; +} + static int acpi_hibernation_begin(void) { acpi_target_sleep_state = ACPI_STATE_S4; @@ -316,6 +343,12 @@ static void acpi_hibernation_leave(void) acpi_enable(); /* Reprogram control registers and execute _BFS */ acpi_leave_sleep_state_prep(ACPI_STATE_S4); + /* Check the hardware signature */ + if (facs && s4_hardware_signature != facs->hardware_signature) { + printk(KERN_EMERG "ACPI: Hardware changed while hibernated, " + "cannot resume!\n"); + panic("ACPI S4 hardware signature mismatch"); + } } static void acpi_pm_enable_gpes(void) @@ -516,6 +549,8 @@ int __init acpi_sleep_init(void) u8 type_a, type_b; #ifdef CONFIG_SUSPEND int i = 0; + + dmi_check_system(acpisleep_dmi_table); #endif if (acpi_disabled) @@ -544,6 +579,13 @@ int __init acpi_sleep_init(void) &acpi_hibernation_ops_old : &acpi_hibernation_ops); sleep_states[ACPI_STATE_S4] = 1; printk(" S4"); + if (!nosigcheck) { + acpi_get_table_by_index(ACPI_TABLE_INDEX_FACS, + (struct acpi_table_header **)&facs); + if (facs) + s4_hardware_signature = + facs->hardware_signature; + } } #endif status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b); diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c index d8e3f153b295..91dec448b3ed 100644 --- a/drivers/acpi/system.c +++ b/drivers/acpi/system.c @@ -26,6 +26,7 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/init.h> +#include <linux/string.h> #include <asm/uaccess.h> #include <acpi/acpi_drivers.h> diff --git a/drivers/acpi/tables/tbfadt.c b/drivers/acpi/tables/tbfadt.c index ccb5b64bbef3..a4a41ba2484b 100644 --- a/drivers/acpi/tables/tbfadt.c +++ b/drivers/acpi/tables/tbfadt.c @@ -124,7 +124,7 @@ static struct acpi_fadt_info fadt_info_table[] = { static void inline acpi_tb_init_generic_address(struct acpi_generic_address *generic_address, - u8 byte_width, u64 address) + u8 bit_width, u64 address) { /* @@ -136,7 +136,7 @@ acpi_tb_init_generic_address(struct acpi_generic_address *generic_address, /* All other fields are byte-wide */ generic_address->space_id = ACPI_ADR_SPACE_SYSTEM_IO; - generic_address->bit_width = byte_width << 3; + generic_address->bit_width = bit_width; generic_address->bit_offset = 0; generic_address->access_width = 0; } @@ -343,11 +343,9 @@ static void acpi_tb_convert_fadt(void) * * The PM event blocks are split into two register blocks, first is the * PM Status Register block, followed immediately by the PM Enable Register - * block. Each is of length (xpm1x_event_block.bit_width/2) + * block. Each is of length (pm1_event_length/2) */ - WARN_ON(ACPI_MOD_16(acpi_gbl_FADT.xpm1a_event_block.bit_width)); - pm1_register_length = (u8) ACPI_DIV_16(acpi_gbl_FADT - .xpm1a_event_block.bit_width); + pm1_register_length = (u8) ACPI_DIV_2(acpi_gbl_FADT.pm1_event_length); /* The PM1A register block is required */ @@ -362,17 +360,14 @@ static void acpi_tb_convert_fadt(void) /* The PM1B register block is optional, ignore if not present */ if (acpi_gbl_FADT.xpm1b_event_block.address) { - WARN_ON(ACPI_MOD_16(acpi_gbl_FADT.xpm1b_event_block.bit_width)); - pm1_register_length = (u8) ACPI_DIV_16(acpi_gbl_FADT - .xpm1b_event_block - .bit_width); acpi_tb_init_generic_address(&acpi_gbl_xpm1b_enable, pm1_register_length, (acpi_gbl_FADT.xpm1b_event_block. address + pm1_register_length)); /* Don't forget to copy space_id of the GAS */ acpi_gbl_xpm1b_enable.space_id = - acpi_gbl_FADT.xpm1b_event_block.space_id; + acpi_gbl_FADT.xpm1a_event_block.space_id; + } } diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 84c795fb9b1e..912703691d36 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -769,6 +769,47 @@ static void acpi_thermal_run(unsigned long data) acpi_os_execute(OSL_GPE_HANDLER, acpi_thermal_check, (void *)data); } +static void acpi_thermal_active_off(void *data) +{ + int result = 0; + struct acpi_thermal *tz = data; + int i = 0; + int j = 0; + struct acpi_thermal_active *active = NULL; + + if (!tz) { + printk(KERN_ERR PREFIX "Invalid (NULL) context\n"); + return; + } + + result = acpi_thermal_get_temperature(tz); + if (result) + return; + + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { + active = &(tz->trips.active[i]); + if (!active || !active->flags.valid) + break; + if (tz->temperature >= active->temperature) { + /* + * If the thermal temperature is greater than the + * active threshod, unnecessary to turn off the + * the active cooling device. + */ + continue; + } + /* + * Below Threshold? + * ---------------- + * Turn OFF all cooling devices associated with this + * threshold. + */ + for (j = 0; j < active->devices.count; j++) + result = acpi_bus_set_power(active->devices.handles[j], + ACPI_STATE_D3); + } +} + static void acpi_thermal_check(void *data) { int result = 0; @@ -1179,8 +1220,8 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) tz->tz_enabled = 1; - printk(KERN_INFO PREFIX "%s is registered as thermal_zone%d\n", - tz->device->dev.bus_id, tz->thermal_zone->id); + dev_info(&tz->device->dev, "registered as thermal_zone%d\n", + tz->thermal_zone->id); return 0; } @@ -1624,6 +1665,8 @@ static int acpi_thermal_add(struct acpi_device *device) init_timer(&tz->timer); + acpi_thermal_active_off(tz); + acpi_thermal_check(tz); status = acpi_install_notify_handler(device->handle, diff --git a/drivers/acpi/utilities/utalloc.c b/drivers/acpi/utilities/utalloc.c index 3dfb8a442b26..e7bf34a7b1d2 100644 --- a/drivers/acpi/utilities/utalloc.c +++ b/drivers/acpi/utilities/utalloc.c @@ -242,6 +242,10 @@ acpi_ut_initialize_buffer(struct acpi_buffer * buffer, { acpi_status status = AE_OK; + if (!required_length) { + WARN_ON(1); + return AE_ERROR; + } switch (buffer->length) { case ACPI_NO_BUFFER: diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 64c889331f3b..e8a51a1700f7 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -741,7 +741,7 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) max_level = acpi_video_init_brightness(device); - if (device->cap._BCL && device->cap._BCM && device->cap._BQC && max_level > 0){ + if (device->cap._BCL && device->cap._BCM && max_level > 0) { int result; static int count = 0; char *name; @@ -753,7 +753,17 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) device->backlight = backlight_device_register(name, NULL, device, &acpi_backlight_ops); device->backlight->props.max_brightness = device->brightness->count-3; - device->backlight->props.brightness = acpi_video_get_brightness(device->backlight); + /* + * If there exists the _BQC object, the _BQC object will be + * called to get the current backlight brightness. Otherwise + * the brightness will be set to the maximum. + */ + if (device->cap._BQC) + device->backlight->props.brightness = + acpi_video_get_brightness(device->backlight); + else + device->backlight->props.brightness = + device->backlight->props.max_brightness; backlight_update_status(device->backlight); kfree(name); @@ -762,9 +772,8 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) if (IS_ERR(device->cdev)) return; - printk(KERN_INFO PREFIX - "%s is registered as cooling_device%d\n", - device->dev->dev.bus_id, device->cdev->id); + dev_info(&device->dev->dev, "registered as cooling_device%d\n", + device->cdev->id); result = sysfs_create_link(&device->dev->dev.kobj, &device->cdev->device.kobj, "thermal_cooling"); diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index dc7596f028b6..ef3e5522e1a4 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1273,7 +1273,7 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; u32 em_ctl; u32 message[] = {0, 0}; - unsigned int flags; + unsigned long flags; int pmp; struct ahci_em_priv *emp; diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c index de8d186f5abf..2014253f6c88 100644 --- a/drivers/ata/pata_ixp4xx_cf.c +++ b/drivers/ata/pata_ixp4xx_cf.c @@ -169,7 +169,7 @@ static __devinit int ixp4xx_pata_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq) - set_irq_type(irq, IRQT_RISING); + set_irq_type(irq, IRQ_TYPE_EDGE_RISING); /* Setup expansion bus chip selects */ *data->cs0_cfg = data->cs0_bits; diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c index d5c1bbfbe79d..73338d231db9 100644 --- a/drivers/atm/fore200e.c +++ b/drivers/atm/fore200e.c @@ -2562,7 +2562,8 @@ fore200e_load_and_start_fw(struct fore200e* fore200e) const struct firmware *firmware; struct device *device; struct fw_header *fw_header; - u32 *fw_data, fw_size; + const __le32 *fw_data; + u32 fw_size; u32 __iomem *load_addr; char buf[48]; int err = -ENODEV; @@ -2582,7 +2583,7 @@ fore200e_load_and_start_fw(struct fore200e* fore200e) return err; } - fw_data = (u32 *) firmware->data; + fw_data = (__le32 *) firmware->data; fw_size = firmware->size / sizeof(u32); fw_header = (struct fw_header *) firmware->data; load_addr = fore200e->virt_base + le32_to_cpu(fw_header->load_offset); @@ -3199,6 +3200,14 @@ static const struct fore200e_bus fore200e_bus[] = { {} }; -#ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); +#ifdef CONFIG_PCI +#ifdef __LITTLE_ENDIAN__ +MODULE_FIRMWARE("pca200e.bin"); +#else +MODULE_FIRMWARE("pca200e_ecd.bin2"); +#endif +#endif /* CONFIG_PCI */ +#ifdef CONFIG_SBUS +MODULE_FIRMWARE("sba200e_ecd.bin2"); #endif diff --git a/drivers/auxdisplay/cfag12864b.c b/drivers/auxdisplay/cfag12864b.c index 683509f013ab..eacb175f6bd3 100644 --- a/drivers/auxdisplay/cfag12864b.c +++ b/drivers/auxdisplay/cfag12864b.c @@ -336,16 +336,9 @@ static int __init cfag12864b_init(void) "ks0108 is not initialized\n"); goto none; } + BUILD_BUG_ON(PAGE_SIZE < CFAG12864B_SIZE); - if (PAGE_SIZE < CFAG12864B_SIZE) { - printk(KERN_ERR CFAG12864B_NAME ": ERROR: " - "page size (%i) < cfag12864b size (%i)\n", - (unsigned int)PAGE_SIZE, CFAG12864B_SIZE); - ret = -ENOMEM; - goto none; - } - - cfag12864b_buffer = (unsigned char *) __get_free_page(GFP_KERNEL); + cfag12864b_buffer = (unsigned char *) get_zeroed_page(GFP_KERNEL); if (cfag12864b_buffer == NULL) { printk(KERN_ERR CFAG12864B_NAME ": ERROR: " "can't get a free page\n"); @@ -367,8 +360,6 @@ static int __init cfag12864b_init(void) if (cfag12864b_workqueue == NULL) goto cachealloced; - memset(cfag12864b_buffer, 0, CFAG12864B_SIZE); - cfag12864b_clear(); cfag12864b_on(); diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index d47482fa1d21..6318f6b57360 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -27,8 +27,9 @@ config PREVENT_FIRMWARE_BUILD If unsure say Y here. config FW_LOADER - tristate "Userspace firmware loading support" + tristate "Userspace firmware loading support" if EMBEDDED depends on HOTPLUG + default y ---help--- This option is provided for the case where no in-kernel-tree modules require userspace firmware loading support, but a module built outside diff --git a/drivers/base/base.h b/drivers/base/base.h index 2c9ae43e2219..31dc0cd84afa 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -36,6 +36,33 @@ struct driver_private { }; #define to_driver(obj) container_of(obj, struct driver_private, kobj) + +/** + * struct class_private - structure to hold the private to the driver core portions of the class structure. + * + * @class_subsys - the struct kset that defines this class. This is the main kobject + * @class_devices - list of devices associated with this class + * @class_interfaces - list of class_interfaces associated with this class + * @class_dirs - "glue" directory for virtual devices associated with this class + * @class_mutex - mutex to protect the children, devices, and interfaces lists. + * @class - pointer back to the struct class that this structure is associated + * with. + * + * This structure is the one that is the actual kobject allowing struct + * class to be statically allocated safely. Nothing outside of the driver + * core should ever touch these fields. + */ +struct class_private { + struct kset class_subsys; + struct list_head class_devices; + struct list_head class_interfaces; + struct kset class_dirs; + struct mutex class_mutex; + struct class *class; +}; +#define to_class(obj) \ + container_of(obj, struct class_private, class_subsys.kobj) + /* initialisation functions */ extern int devices_init(void); extern int buses_init(void); diff --git a/drivers/base/class.c b/drivers/base/class.c index e085af0ff94f..839d27cecb36 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -18,20 +18,20 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/genhd.h> +#include <linux/mutex.h> #include "base.h" #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr) -#define to_class(obj) container_of(obj, struct class, subsys.kobj) static ssize_t class_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct class_attribute *class_attr = to_class_attr(attr); - struct class *dc = to_class(kobj); + struct class_private *cp = to_class(kobj); ssize_t ret = -EIO; if (class_attr->show) - ret = class_attr->show(dc, buf); + ret = class_attr->show(cp->class, buf); return ret; } @@ -39,17 +39,18 @@ static ssize_t class_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { struct class_attribute *class_attr = to_class_attr(attr); - struct class *dc = to_class(kobj); + struct class_private *cp = to_class(kobj); ssize_t ret = -EIO; if (class_attr->store) - ret = class_attr->store(dc, buf, count); + ret = class_attr->store(cp->class, buf, count); return ret; } static void class_release(struct kobject *kobj) { - struct class *class = to_class(kobj); + struct class_private *cp = to_class(kobj); + struct class *class = cp->class; pr_debug("class '%s': release.\n", class->name); @@ -70,7 +71,7 @@ static struct kobj_type class_ktype = { .release = class_release, }; -/* Hotplug events for classes go to the class_obj subsys */ +/* Hotplug events for classes go to the class class_subsys */ static struct kset *class_kset; @@ -78,7 +79,8 @@ int class_create_file(struct class *cls, const struct class_attribute *attr) { int error; if (cls) - error = sysfs_create_file(&cls->subsys.kobj, &attr->attr); + error = sysfs_create_file(&cls->p->class_subsys.kobj, + &attr->attr); else error = -EINVAL; return error; @@ -87,21 +89,20 @@ int class_create_file(struct class *cls, const struct class_attribute *attr) void class_remove_file(struct class *cls, const struct class_attribute *attr) { if (cls) - sysfs_remove_file(&cls->subsys.kobj, &attr->attr); + sysfs_remove_file(&cls->p->class_subsys.kobj, &attr->attr); } static struct class *class_get(struct class *cls) { if (cls) - return container_of(kset_get(&cls->subsys), - struct class, subsys); - return NULL; + kset_get(&cls->p->class_subsys); + return cls; } static void class_put(struct class *cls) { if (cls) - kset_put(&cls->subsys); + kset_put(&cls->p->class_subsys); } static int add_class_attrs(struct class *cls) @@ -134,42 +135,57 @@ static void remove_class_attrs(struct class *cls) } } -int class_register(struct class *cls) +int __class_register(struct class *cls, struct lock_class_key *key) { + struct class_private *cp; int error; pr_debug("device class '%s': registering\n", cls->name); - 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.kobj, "%s", cls->name); - if (error) + cp = kzalloc(sizeof(*cp), GFP_KERNEL); + if (!cp) + return -ENOMEM; + INIT_LIST_HEAD(&cp->class_devices); + INIT_LIST_HEAD(&cp->class_interfaces); + kset_init(&cp->class_dirs); + __mutex_init(&cp->class_mutex, "struct class mutex", key); + error = kobject_set_name(&cp->class_subsys.kobj, "%s", cls->name); + if (error) { + kfree(cp); return error; + } + + /* set the default /sys/dev directory for devices of this class */ + if (!cls->dev_kobj) + cls->dev_kobj = sysfs_dev_char_kobj; #if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK) /* let the block class directory show up in the root of sysfs */ if (cls != &block_class) - cls->subsys.kobj.kset = class_kset; + cp->class_subsys.kobj.kset = class_kset; #else - cls->subsys.kobj.kset = class_kset; + cp->class_subsys.kobj.kset = class_kset; #endif - cls->subsys.kobj.ktype = &class_ktype; + cp->class_subsys.kobj.ktype = &class_ktype; + cp->class = cls; + cls->p = cp; - error = kset_register(&cls->subsys); - if (!error) { - error = add_class_attrs(class_get(cls)); - class_put(cls); + error = kset_register(&cp->class_subsys); + if (error) { + kfree(cp); + return error; } + error = add_class_attrs(class_get(cls)); + class_put(cls); return error; } +EXPORT_SYMBOL_GPL(__class_register); void class_unregister(struct class *cls) { pr_debug("device class '%s': unregistering\n", cls->name); remove_class_attrs(cls); - kset_unregister(&cls->subsys); + kset_unregister(&cls->p->class_subsys); } static void class_create_release(struct class *cls) @@ -189,7 +205,8 @@ static void class_create_release(struct class *cls) * Note, the pointer created here is to be destroyed when finished by * making a call to class_destroy(). */ -struct class *class_create(struct module *owner, const char *name) +struct class *__class_create(struct module *owner, const char *name, + struct lock_class_key *key) { struct class *cls; int retval; @@ -204,7 +221,7 @@ struct class *class_create(struct module *owner, const char *name) cls->owner = owner; cls->class_release = class_create_release; - retval = class_register(cls); + retval = __class_register(cls, key); if (retval) goto error; @@ -214,6 +231,7 @@ error: kfree(cls); return ERR_PTR(retval); } +EXPORT_SYMBOL_GPL(__class_create); /** * class_destroy - destroys a struct class structure @@ -252,39 +270,44 @@ char *make_class_name(const char *name, struct kobject *kobj) /** * class_for_each_device - device iterator * @class: the class we're iterating + * @start: the device to start with in the list, if any. * @data: data for the callback * @fn: function to be called for each device * * Iterate over @class's list of devices, and call @fn for each, - * passing it @data. + * passing it @data. If @start is set, the list iteration will start + * there, otherwise if it is NULL, the iteration starts at the + * beginning of the list. * * We check the return of @fn each time. If it returns anything * other than 0, we break out and return that value. * - * Note, we hold class->sem in this function, so it can not be + * Note, we hold class->class_mutex in this function, so it can not be * re-acquired in @fn, otherwise it will self-deadlocking. For * example, calls to add or remove class members would be verboten. */ -int class_for_each_device(struct class *class, void *data, - int (*fn)(struct device *, void *)) +int class_for_each_device(struct class *class, struct device *start, + void *data, int (*fn)(struct device *, void *)) { struct device *dev; int error = 0; if (!class) return -EINVAL; - down(&class->sem); - list_for_each_entry(dev, &class->devices, node) { + mutex_lock(&class->p->class_mutex); + list_for_each_entry(dev, &class->p->class_devices, node) { + if (start) { + if (start == dev) + start = NULL; + continue; + } dev = get_device(dev); - if (dev) { - error = fn(dev, data); - put_device(dev); - } else - error = -ENODEV; + error = fn(dev, data); + put_device(dev); if (error) break; } - up(&class->sem); + mutex_unlock(&class->p->class_mutex); return error; } @@ -293,6 +316,7 @@ EXPORT_SYMBOL_GPL(class_for_each_device); /** * class_find_device - device iterator for locating a particular device * @class: the class we're iterating + * @start: Device to begin with * @data: data for the match function * @match: function to check device * @@ -306,12 +330,13 @@ EXPORT_SYMBOL_GPL(class_for_each_device); * * Note, you will need to drop the reference with put_device() after use. * - * We hold class->sem in this function, so it can not be + * We hold class->class_mutex in this function, so it can not be * re-acquired in @match, otherwise it will self-deadlocking. For * example, calls to add or remove class members would be verboten. */ -struct device *class_find_device(struct class *class, void *data, - int (*match)(struct device *, void *)) +struct device *class_find_device(struct class *class, struct device *start, + void *data, + int (*match)(struct device *, void *)) { struct device *dev; int found = 0; @@ -319,19 +344,21 @@ struct device *class_find_device(struct class *class, void *data, if (!class) return NULL; - down(&class->sem); - list_for_each_entry(dev, &class->devices, node) { + mutex_lock(&class->p->class_mutex); + list_for_each_entry(dev, &class->p->class_devices, node) { + if (start) { + if (start == dev) + start = NULL; + continue; + } dev = get_device(dev); - if (dev) { - if (match(dev, data)) { - found = 1; - break; - } else - put_device(dev); - } else + if (match(dev, data)) { + found = 1; break; + } else + put_device(dev); } - up(&class->sem); + mutex_unlock(&class->p->class_mutex); return found ? dev : NULL; } @@ -349,13 +376,13 @@ int class_interface_register(struct class_interface *class_intf) if (!parent) return -EINVAL; - down(&parent->sem); - list_add_tail(&class_intf->node, &parent->interfaces); + mutex_lock(&parent->p->class_mutex); + list_add_tail(&class_intf->node, &parent->p->class_interfaces); if (class_intf->add_dev) { - list_for_each_entry(dev, &parent->devices, node) + list_for_each_entry(dev, &parent->p->class_devices, node) class_intf->add_dev(dev, class_intf); } - up(&parent->sem); + mutex_unlock(&parent->p->class_mutex); return 0; } @@ -368,13 +395,13 @@ void class_interface_unregister(struct class_interface *class_intf) if (!parent) return; - down(&parent->sem); + mutex_lock(&parent->p->class_mutex); list_del_init(&class_intf->node); if (class_intf->remove_dev) { - list_for_each_entry(dev, &parent->devices, node) + list_for_each_entry(dev, &parent->p->class_devices, node) class_intf->remove_dev(dev, class_intf); } - up(&parent->sem); + mutex_unlock(&parent->p->class_mutex); class_put(parent); } @@ -389,9 +416,7 @@ int __init classes_init(void) EXPORT_SYMBOL_GPL(class_create_file); EXPORT_SYMBOL_GPL(class_remove_file); -EXPORT_SYMBOL_GPL(class_register); EXPORT_SYMBOL_GPL(class_unregister); -EXPORT_SYMBOL_GPL(class_create); EXPORT_SYMBOL_GPL(class_destroy); EXPORT_SYMBOL_GPL(class_interface_register); diff --git a/drivers/base/core.c b/drivers/base/core.c index ee0a51a3a41d..068aa1c9538c 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -21,12 +21,16 @@ #include <linux/genhd.h> #include <linux/kallsyms.h> #include <linux/semaphore.h> +#include <linux/mutex.h> #include "base.h" #include "power/power.h" int (*platform_notify)(struct device *dev) = NULL; int (*platform_notify_remove)(struct device *dev) = NULL; +static struct kobject *dev_kobj; +struct kobject *sysfs_dev_char_kobj; +struct kobject *sysfs_dev_block_kobj; #ifdef CONFIG_BLOCK static inline int device_is_not_partition(struct device *dev) @@ -112,12 +116,10 @@ static void device_release(struct kobject *kobj) dev->type->release(dev); else if (dev->class && dev->class->dev_release) dev->class->dev_release(dev); - else { - printk(KERN_ERR "Device '%s' does not have a release() " + else + WARN(1, KERN_ERR "Device '%s' does not have a release() " "function, it is broken and must be fixed.\n", dev->bus_id); - WARN_ON(1); - } } static struct kobj_type device_ktype = { @@ -548,7 +550,7 @@ static struct kobject *get_device_parent(struct device *dev, { /* class devices without a parent live in /sys/class/<classname>/ */ if (dev->class && (!parent || parent->class != dev->class)) - return &dev->class->subsys.kobj; + return &dev->class->p->class_subsys.kobj; /* all other devices keep their parent */ else if (parent) return &parent->kobj; @@ -594,13 +596,13 @@ static struct kobject *get_device_parent(struct device *dev, 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) + spin_lock(&dev->class->p->class_dirs.list_lock); + list_for_each_entry(k, &dev->class->p->class_dirs.list, entry) if (k->parent == parent_kobj) { kobj = kobject_get(k); break; } - spin_unlock(&dev->class->class_dirs.list_lock); + spin_unlock(&dev->class->p->class_dirs.list_lock); if (kobj) return kobj; @@ -608,7 +610,7 @@ static struct kobject *get_device_parent(struct device *dev, k = kobject_create(); if (!k) return NULL; - k->kset = &dev->class->class_dirs; + k->kset = &dev->class->p->class_dirs; retval = kobject_add(k, parent_kobj, "%s", dev->class->name); if (retval < 0) { kobject_put(k); @@ -627,7 +629,7 @@ static void cleanup_glue_dir(struct device *dev, struct kobject *glue_dir) { /* see if we live in a "glue" directory */ if (!glue_dir || !dev->class || - glue_dir->kset != &dev->class->class_dirs) + glue_dir->kset != &dev->class->p->class_dirs) return; kobject_put(glue_dir); @@ -654,17 +656,18 @@ static int device_add_class_symlinks(struct device *dev) if (!dev->class) return 0; - error = sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj, + error = sysfs_create_link(&dev->kobj, + &dev->class->p->class_subsys.kobj, "subsystem"); if (error) goto out; #ifdef CONFIG_SYSFS_DEPRECATED /* stacked class devices need a symlink in the class directory */ - if (dev->kobj.parent != &dev->class->subsys.kobj && + if (dev->kobj.parent != &dev->class->p->class_subsys.kobj && device_is_not_partition(dev)) { - error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, - dev->bus_id); + error = sysfs_create_link(&dev->class->p->class_subsys.kobj, + &dev->kobj, dev->bus_id); if (error) goto out_subsys; } @@ -701,13 +704,14 @@ out_device: if (dev->parent && device_is_not_partition(dev)) sysfs_remove_link(&dev->kobj, "device"); out_busid: - if (dev->kobj.parent != &dev->class->subsys.kobj && + if (dev->kobj.parent != &dev->class->p->class_subsys.kobj && device_is_not_partition(dev)) - sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); + sysfs_remove_link(&dev->class->p->class_subsys.kobj, + dev->bus_id); #else /* link in the class directory pointing to the device */ - error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, - dev->bus_id); + error = sysfs_create_link(&dev->class->p->class_subsys.kobj, + &dev->kobj, dev->bus_id); if (error) goto out_subsys; @@ -720,7 +724,7 @@ out_busid: return 0; out_busid: - sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); + sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev->bus_id); #endif out_subsys: @@ -746,14 +750,15 @@ static void device_remove_class_symlinks(struct device *dev) sysfs_remove_link(&dev->kobj, "device"); } - if (dev->kobj.parent != &dev->class->subsys.kobj && + if (dev->kobj.parent != &dev->class->p->class_subsys.kobj && device_is_not_partition(dev)) - sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); + sysfs_remove_link(&dev->class->p->class_subsys.kobj, + dev->bus_id); #else if (dev->parent && device_is_not_partition(dev)) sysfs_remove_link(&dev->kobj, "device"); - sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); + sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev->bus_id); #endif sysfs_remove_link(&dev->kobj, "subsystem"); @@ -776,6 +781,54 @@ int dev_set_name(struct device *dev, const char *fmt, ...) EXPORT_SYMBOL_GPL(dev_set_name); /** + * device_to_dev_kobj - select a /sys/dev/ directory for the device + * @dev: device + * + * By default we select char/ for new entries. Setting class->dev_obj + * to NULL prevents an entry from being created. class->dev_kobj must + * be set (or cleared) before any devices are registered to the class + * otherwise device_create_sys_dev_entry() and + * device_remove_sys_dev_entry() will disagree about the the presence + * of the link. + */ +static struct kobject *device_to_dev_kobj(struct device *dev) +{ + struct kobject *kobj; + + if (dev->class) + kobj = dev->class->dev_kobj; + else + kobj = sysfs_dev_char_kobj; + + return kobj; +} + +static int device_create_sys_dev_entry(struct device *dev) +{ + struct kobject *kobj = device_to_dev_kobj(dev); + int error = 0; + char devt_str[15]; + + if (kobj) { + format_dev_t(devt_str, dev->devt); + error = sysfs_create_link(kobj, &dev->kobj, devt_str); + } + + return error; +} + +static void device_remove_sys_dev_entry(struct device *dev) +{ + struct kobject *kobj = device_to_dev_kobj(dev); + char devt_str[15]; + + if (kobj) { + format_dev_t(devt_str, dev->devt); + sysfs_remove_link(kobj, devt_str); + } +} + +/** * device_add - add device to device hierarchy. * @dev: device. * @@ -829,6 +882,10 @@ int device_add(struct device *dev) error = device_create_file(dev, &devt_attr); if (error) goto ueventattrError; + + error = device_create_sys_dev_entry(dev); + if (error) + goto devtattrError; } error = device_add_class_symlinks(dev); @@ -849,15 +906,16 @@ int device_add(struct device *dev) klist_add_tail(&dev->knode_parent, &parent->klist_children); if (dev->class) { - down(&dev->class->sem); + mutex_lock(&dev->class->p->class_mutex); /* tie the class to the device */ - list_add_tail(&dev->node, &dev->class->devices); + list_add_tail(&dev->node, &dev->class->p->class_devices); /* notify any interfaces that the device is here */ - list_for_each_entry(class_intf, &dev->class->interfaces, node) + list_for_each_entry(class_intf, + &dev->class->p->class_interfaces, node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf); - up(&dev->class->sem); + mutex_unlock(&dev->class->p->class_mutex); } Done: put_device(dev); @@ -873,6 +931,9 @@ int device_add(struct device *dev) device_remove_class_symlinks(dev); SymlinkError: if (MAJOR(dev->devt)) + device_remove_sys_dev_entry(dev); + devtattrError: + if (MAJOR(dev->devt)) device_remove_file(dev, &devt_attr); ueventattrError: device_remove_file(dev, &uevent_attr); @@ -948,19 +1009,22 @@ void device_del(struct device *dev) device_pm_remove(dev); if (parent) klist_del(&dev->knode_parent); - if (MAJOR(dev->devt)) + if (MAJOR(dev->devt)) { + device_remove_sys_dev_entry(dev); device_remove_file(dev, &devt_attr); + } if (dev->class) { device_remove_class_symlinks(dev); - down(&dev->class->sem); + mutex_lock(&dev->class->p->class_mutex); /* notify any interfaces that the device is now gone */ - list_for_each_entry(class_intf, &dev->class->interfaces, node) + list_for_each_entry(class_intf, + &dev->class->p->class_interfaces, node) if (class_intf->remove_dev) class_intf->remove_dev(dev, class_intf); /* remove the device from the class list */ list_del_init(&dev->node); - up(&dev->class->sem); + mutex_unlock(&dev->class->p->class_mutex); } device_remove_file(dev, &uevent_attr); device_remove_attrs(dev); @@ -1074,7 +1138,25 @@ int __init devices_init(void) devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL); if (!devices_kset) return -ENOMEM; + dev_kobj = kobject_create_and_add("dev", NULL); + if (!dev_kobj) + goto dev_kobj_err; + sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj); + if (!sysfs_dev_block_kobj) + goto block_kobj_err; + sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj); + if (!sysfs_dev_char_kobj) + goto char_kobj_err; + return 0; + + char_kobj_err: + kobject_put(sysfs_dev_block_kobj); + block_kobj_err: + kobject_put(dev_kobj); + dev_kobj_err: + kset_unregister(devices_kset); + return -ENOMEM; } EXPORT_SYMBOL_GPL(device_for_each_child); @@ -1158,48 +1240,11 @@ error: EXPORT_SYMBOL_GPL(device_create_vargs); /** - * device_create_drvdata - creates a device and registers it with sysfs - * @class: pointer to the struct class that this device should be registered to - * @parent: pointer to the parent struct device of this new device, if any - * @devt: the dev_t for the char device to be added - * @drvdata: the data to be added to the device for callbacks - * @fmt: string for the device's name - * - * This function can be used by char device classes. A struct device - * will be created in sysfs, registered to the specified class. - * - * A "dev" file will be created, showing the dev_t for the device, if - * the dev_t is not 0,0. - * If a pointer to a parent struct device is passed in, the newly created - * struct device will be a child of that device in sysfs. - * The pointer to the struct device will be returned from the call. - * Any further sysfs files that might be required can be created using this - * pointer. - * - * Note: the struct class passed to this function must have previously - * been created with a call to class_create(). - */ -struct device *device_create_drvdata(struct class *class, - struct device *parent, - dev_t devt, - void *drvdata, - const char *fmt, ...) -{ - va_list vargs; - struct device *dev; - - va_start(vargs, fmt); - dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs); - va_end(vargs); - return dev; -} -EXPORT_SYMBOL_GPL(device_create_drvdata); - -/** * device_create - creates a device and registers it with sysfs * @class: pointer to the struct class that this device should be registered to * @parent: pointer to the parent struct device of this new device, if any * @devt: the dev_t for the char device to be added + * @drvdata: the data to be added to the device for callbacks * @fmt: string for the device's name * * This function can be used by char device classes. A struct device @@ -1217,13 +1262,13 @@ EXPORT_SYMBOL_GPL(device_create_drvdata); * been created with a call to class_create(). */ struct device *device_create(struct class *class, struct device *parent, - dev_t devt, const char *fmt, ...) + dev_t devt, void *drvdata, const char *fmt, ...) { va_list vargs; struct device *dev; va_start(vargs, fmt); - dev = device_create_vargs(class, parent, devt, NULL, fmt, vargs); + dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs); va_end(vargs); return dev; } @@ -1248,7 +1293,7 @@ void device_destroy(struct class *class, dev_t devt) { struct device *dev; - dev = class_find_device(class, &devt, __match_devt); + dev = class_find_device(class, NULL, &devt, __match_devt); if (dev) { put_device(dev); device_unregister(dev); @@ -1298,8 +1343,9 @@ int device_rename(struct device *dev, char *new_name) if (old_class_name) { new_class_name = make_class_name(dev->class->name, &dev->kobj); if (new_class_name) { - error = sysfs_create_link(&dev->parent->kobj, - &dev->kobj, new_class_name); + error = sysfs_create_link_nowarn(&dev->parent->kobj, + &dev->kobj, + new_class_name); if (error) goto out; sysfs_remove_link(&dev->parent->kobj, old_class_name); @@ -1307,11 +1353,12 @@ int device_rename(struct device *dev, char *new_name) } #else if (dev->class) { - error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, - dev->bus_id); + error = sysfs_create_link_nowarn(&dev->class->p->class_subsys.kobj, + &dev->kobj, dev->bus_id); if (error) goto out; - sysfs_remove_link(&dev->class->subsys.kobj, old_device_name); + sysfs_remove_link(&dev->class->p->class_subsys.kobj, + old_device_name); } #endif @@ -1447,4 +1494,7 @@ void device_shutdown(void) dev->driver->shutdown(dev); } } + kobject_put(sysfs_dev_char_kobj); + kobject_put(sysfs_dev_block_kobj); + kobject_put(dev_kobj); } diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index e38dfed41d80..64f5d54f7edc 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -21,15 +21,16 @@ EXPORT_SYMBOL(cpu_sysdev_class); static DEFINE_PER_CPU(struct sys_device *, cpu_sys_devices); #ifdef CONFIG_HOTPLUG_CPU -static ssize_t show_online(struct sys_device *dev, char *buf) +static ssize_t show_online(struct sys_device *dev, struct sysdev_attribute *attr, + char *buf) { struct cpu *cpu = container_of(dev, struct cpu, sysdev); return sprintf(buf, "%u\n", !!cpu_online(cpu->sysdev.id)); } -static ssize_t __ref store_online(struct sys_device *dev, const char *buf, - size_t count) +static ssize_t __ref store_online(struct sys_device *dev, struct sysdev_attribute *attr, + const char *buf, size_t count) { struct cpu *cpu = container_of(dev, struct cpu, sysdev); ssize_t ret; @@ -80,7 +81,8 @@ static inline void register_cpu_control(struct cpu *cpu) #ifdef CONFIG_KEXEC #include <linux/kexec.h> -static ssize_t show_crash_notes(struct sys_device *dev, char *buf) +static ssize_t show_crash_notes(struct sys_device *dev, struct sysdev_attribute *attr, + char *buf) { struct cpu *cpu = container_of(dev, struct cpu, sysdev); ssize_t rc; @@ -119,14 +121,14 @@ static ssize_t print_cpus_##type(struct sysdev_class *class, char *buf) \ { \ return print_cpus_map(buf, &cpu_##type##_map); \ } \ -struct sysdev_class_attribute attr_##type##_map = \ +static struct sysdev_class_attribute attr_##type##_map = \ _SYSDEV_CLASS_ATTR(type, 0444, print_cpus_##type, NULL) print_cpus_func(online); print_cpus_func(possible); print_cpus_func(present); -struct sysdev_class_attribute *cpu_state_attr[] = { +static struct sysdev_class_attribute *cpu_state_attr[] = { &attr_online_map, &attr_possible_map, &attr_present_map, diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index b0be1d18fee2..c9c92b00fd55 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -184,7 +184,7 @@ firmware_data_read(struct kobject *kobj, struct bin_attribute *bin_attr, struct device *dev = to_dev(kobj); struct firmware_priv *fw_priv = dev_get_drvdata(dev); struct firmware *fw; - ssize_t ret_count = count; + ssize_t ret_count; mutex_lock(&fw_lock); fw = fw_priv->fw; @@ -192,14 +192,8 @@ firmware_data_read(struct kobject *kobj, struct bin_attribute *bin_attr, ret_count = -ENODEV; goto out; } - if (offset > fw->size) { - ret_count = 0; - goto out; - } - if (offset + ret_count > fw->size) - ret_count = fw->size - offset; - - memcpy(buffer, fw->data + offset, ret_count); + ret_count = memory_read_from_buffer(buffer, count, &offset, + fw->data, fw->size); out: mutex_unlock(&fw_lock); return ret_count; diff --git a/drivers/base/isa.c b/drivers/base/isa.c index d2222397a401..efd577574948 100644 --- a/drivers/base/isa.c +++ b/drivers/base/isa.c @@ -7,6 +7,7 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/dma-mapping.h> #include <linux/isa.h> static struct device isa_bus = { @@ -141,6 +142,9 @@ int isa_register_driver(struct isa_driver *isa_driver, unsigned int ndev) isa_dev->dev.release = isa_dev_release; isa_dev->id = id; + isa_dev->dev.coherent_dma_mask = DMA_24BIT_MASK; + isa_dev->dev.dma_mask = &isa_dev->dev.coherent_dma_mask; + error = device_register(&isa_dev->dev); if (error) { put_device(&isa_dev->dev); diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 937e8258981d..af0d175c025d 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -92,7 +92,8 @@ unregister_memory(struct memory_block *memory, struct mem_section *section) * uses. */ -static ssize_t show_mem_phys_index(struct sys_device *dev, char *buf) +static ssize_t show_mem_phys_index(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct memory_block *mem = container_of(dev, struct memory_block, sysdev); @@ -100,9 +101,26 @@ static ssize_t show_mem_phys_index(struct sys_device *dev, char *buf) } /* + * Show whether the section of memory is likely to be hot-removable + */ +static ssize_t show_mem_removable(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) +{ + unsigned long start_pfn; + int ret; + struct memory_block *mem = + container_of(dev, struct memory_block, sysdev); + + start_pfn = section_nr_to_pfn(mem->phys_index); + ret = is_mem_section_removable(start_pfn, PAGES_PER_SECTION); + return sprintf(buf, "%d\n", ret); +} + +/* * online, offline, going offline, etc. */ -static ssize_t show_mem_state(struct sys_device *dev, char *buf) +static ssize_t show_mem_state(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct memory_block *mem = container_of(dev, struct memory_block, sysdev); @@ -187,9 +205,8 @@ memory_block_action(struct memory_block *mem, unsigned long action) } break; default: - printk(KERN_WARNING "%s(%p, %ld) unknown action: %ld\n", + WARN(1, KERN_WARNING "%s(%p, %ld) unknown action: %ld\n", __func__, mem, action, action); - WARN_ON(1); ret = -EINVAL; } @@ -217,7 +234,8 @@ out: } static ssize_t -store_mem_state(struct sys_device *dev, const char *buf, size_t count) +store_mem_state(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { struct memory_block *mem; unsigned int phys_section_nr; @@ -248,7 +266,8 @@ out: * s.t. if I offline all of these sections I can then * remove the physical device? */ -static ssize_t show_phys_device(struct sys_device *dev, char *buf) +static ssize_t show_phys_device(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct memory_block *mem = container_of(dev, struct memory_block, sysdev); @@ -258,6 +277,7 @@ static ssize_t show_phys_device(struct sys_device *dev, char *buf) static SYSDEV_ATTR(phys_index, 0444, show_mem_phys_index, NULL); static SYSDEV_ATTR(state, 0644, show_mem_state, store_mem_state); static SYSDEV_ATTR(phys_device, 0444, show_phys_device, NULL); +static SYSDEV_ATTR(removable, 0444, show_mem_removable, NULL); #define mem_create_simple_file(mem, attr_name) \ sysdev_create_file(&mem->sysdev, &attr_##attr_name) @@ -346,6 +366,8 @@ static int add_memory_block(unsigned long node_id, struct mem_section *section, ret = mem_create_simple_file(mem, state); if (!ret) ret = mem_create_simple_file(mem, phys_device); + if (!ret) + ret = mem_create_simple_file(mem, removable); return ret; } @@ -390,6 +412,7 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section, mem_remove_simple_file(mem, phys_index); mem_remove_simple_file(mem, state); mem_remove_simple_file(mem, phys_device); + mem_remove_simple_file(mem, removable); unregister_memory(mem, section); return 0; diff --git a/drivers/base/node.c b/drivers/base/node.c index 0f867a083338..5116b78c6325 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -36,11 +36,13 @@ static ssize_t node_read_cpumap(struct sys_device *dev, int type, char *buf) return len; } -static inline ssize_t node_read_cpumask(struct sys_device *dev, char *buf) +static inline ssize_t node_read_cpumask(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { return node_read_cpumap(dev, 0, buf); } -static inline ssize_t node_read_cpulist(struct sys_device *dev, char *buf) +static inline ssize_t node_read_cpulist(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { return node_read_cpumap(dev, 1, buf); } @@ -49,7 +51,8 @@ static SYSDEV_ATTR(cpumap, S_IRUGO, node_read_cpumask, NULL); static SYSDEV_ATTR(cpulist, S_IRUGO, node_read_cpulist, NULL); #define K(x) ((x) << (PAGE_SHIFT - 10)) -static ssize_t node_read_meminfo(struct sys_device * dev, char * buf) +static ssize_t node_read_meminfo(struct sys_device * dev, + struct sysdev_attribute *attr, char * buf) { int n; int nid = dev->id; @@ -112,7 +115,8 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf) #undef K static SYSDEV_ATTR(meminfo, S_IRUGO, node_read_meminfo, NULL); -static ssize_t node_read_numastat(struct sys_device * dev, char * buf) +static ssize_t node_read_numastat(struct sys_device * dev, + struct sysdev_attribute *attr, char * buf) { return sprintf(buf, "numa_hit %lu\n" @@ -130,7 +134,8 @@ static ssize_t node_read_numastat(struct sys_device * dev, char * buf) } static SYSDEV_ATTR(numastat, S_IRUGO, node_read_numastat, NULL); -static ssize_t node_read_distance(struct sys_device * dev, char * buf) +static ssize_t node_read_distance(struct sys_device * dev, + struct sysdev_attribute *attr, char * buf) { int nid = dev->id; int len = 0; diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c index 9b1b20b59e0a..2aa6e8fc4def 100644 --- a/drivers/base/power/trace.c +++ b/drivers/base/power/trace.c @@ -194,7 +194,7 @@ static int show_dev_hash(unsigned int value) struct device * dev = to_device(entry); unsigned int hash = hash_string(DEVSEED, dev->bus_id, DEVHASH); if (hash == value) { - printk(" hash matches device %s\n", dev->bus_id); + dev_info(dev, "hash matches\n"); match++; } entry = entry->prev; diff --git a/drivers/base/sys.c b/drivers/base/sys.c index 358bb0be3c08..75dd6e22faff 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -36,7 +36,7 @@ sysdev_show(struct kobject * kobj, struct attribute * attr, char * buffer) struct sysdev_attribute * sysdev_attr = to_sysdev_attr(attr); if (sysdev_attr->show) - return sysdev_attr->show(sysdev, buffer); + return sysdev_attr->show(sysdev, sysdev_attr, buffer); return -EIO; } @@ -49,7 +49,7 @@ sysdev_store(struct kobject * kobj, struct attribute * attr, struct sysdev_attribute * sysdev_attr = to_sysdev_attr(attr); if (sysdev_attr->store) - return sysdev_attr->store(sysdev, buffer, count); + return sysdev_attr->store(sysdev, sysdev_attr, buffer, count); return -EIO; } @@ -130,8 +130,8 @@ static struct kset *system_kset; int sysdev_class_register(struct sysdev_class * cls) { - pr_debug("Registering sysdev class '%s'\n", - kobject_name(&cls->kset.kobj)); + pr_debug("Registering sysdev class '%s'\n", cls->name); + INIT_LIST_HEAD(&cls->drivers); memset(&cls->kset.kobj, 0x00, sizeof(struct kobject)); cls->kset.kobj.parent = &system_kset->kobj; @@ -168,19 +168,16 @@ int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv) int err = 0; if (!cls) { - printk(KERN_WARNING "sysdev: invalid class passed to " + WARN(1, KERN_WARNING "sysdev: invalid class passed to " "sysdev_driver_register!\n"); - WARN_ON(1); return -EINVAL; } /* Check whether this driver has already been added to a class. */ - if (drv->entry.next && !list_empty(&drv->entry)) { - printk(KERN_WARNING "sysdev: class %s: driver (%p) has already" + if (drv->entry.next && !list_empty(&drv->entry)) + WARN(1, KERN_WARNING "sysdev: class %s: driver (%p) has already" " been registered to a class, something is wrong, but " "will forge on!\n", cls->name, drv); - WARN_ON(1); - } mutex_lock(&sysdev_drivers_lock); if (cls && kset_get(&cls->kset)) { @@ -194,8 +191,7 @@ int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv) } } else { err = -EINVAL; - printk(KERN_ERR "%s: invalid device class\n", __func__); - WARN_ON(1); + WARN(1, KERN_ERR "%s: invalid device class\n", __func__); } mutex_unlock(&sysdev_drivers_lock); return err; @@ -241,7 +237,8 @@ int sysdev_register(struct sys_device * sysdev) if (!cls) return -EINVAL; - pr_debug("Registering sys device '%s'\n", kobject_name(&sysdev->kobj)); + pr_debug("Registering sys device of class '%s'\n", + kobject_name(&cls->kset.kobj)); /* initialize the kobject to 0, in case it had previously been used */ memset(&sysdev->kobj, 0x00, sizeof(struct kobject)); @@ -257,6 +254,9 @@ int sysdev_register(struct sys_device * sysdev) if (!error) { struct sysdev_driver * drv; + pr_debug("Registering sys device '%s'\n", + kobject_name(&sysdev->kobj)); + mutex_lock(&sysdev_drivers_lock); /* Generic notification is implicit, because it's that * code that should have called us. @@ -269,6 +269,7 @@ int sysdev_register(struct sys_device * sysdev) } mutex_unlock(&sysdev_drivers_lock); } + kobject_uevent(&sysdev->kobj, KOBJ_ADD); return error; } @@ -474,3 +475,52 @@ int __init system_bus_init(void) EXPORT_SYMBOL_GPL(sysdev_register); EXPORT_SYMBOL_GPL(sysdev_unregister); + +#define to_ext_attr(x) container_of(x, struct sysdev_ext_attribute, attr) + +ssize_t sysdev_store_ulong(struct sys_device *sysdev, + struct sysdev_attribute *attr, + const char *buf, size_t size) +{ + struct sysdev_ext_attribute *ea = to_ext_attr(attr); + char *end; + unsigned long new = simple_strtoul(buf, &end, 0); + if (end == buf) + return -EINVAL; + *(unsigned long *)(ea->var) = new; + return end - buf; +} +EXPORT_SYMBOL_GPL(sysdev_store_ulong); + +ssize_t sysdev_show_ulong(struct sys_device *sysdev, + struct sysdev_attribute *attr, + char *buf) +{ + struct sysdev_ext_attribute *ea = to_ext_attr(attr); + return snprintf(buf, PAGE_SIZE, "%lx\n", *(unsigned long *)(ea->var)); +} +EXPORT_SYMBOL_GPL(sysdev_show_ulong); + +ssize_t sysdev_store_int(struct sys_device *sysdev, + struct sysdev_attribute *attr, + const char *buf, size_t size) +{ + struct sysdev_ext_attribute *ea = to_ext_attr(attr); + char *end; + long new = simple_strtol(buf, &end, 0); + if (end == buf || new > INT_MAX || new < INT_MIN) + return -EINVAL; + *(int *)(ea->var) = new; + return end - buf; +} +EXPORT_SYMBOL_GPL(sysdev_store_int); + +ssize_t sysdev_show_int(struct sys_device *sysdev, + struct sysdev_attribute *attr, + char *buf) +{ + struct sysdev_ext_attribute *ea = to_ext_attr(attr); + return snprintf(buf, PAGE_SIZE, "%d\n", *(int *)(ea->var)); +} +EXPORT_SYMBOL_GPL(sysdev_show_int); + diff --git a/drivers/base/topology.c b/drivers/base/topology.c index 3f6d9b0a6abe..199cd97e32e6 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c @@ -34,7 +34,8 @@ static SYSDEV_ATTR(_name, 0444, show_##_name, NULL) #define define_id_show_func(name) \ -static ssize_t show_##name(struct sys_device *dev, char *buf) \ +static ssize_t show_##name(struct sys_device *dev, \ + struct sysdev_attribute *attr, char *buf) \ { \ unsigned int cpu = dev->id; \ return sprintf(buf, "%d\n", topology_##name(cpu)); \ @@ -59,14 +60,17 @@ static ssize_t show_cpumap(int type, cpumask_t *mask, char *buf) #ifdef arch_provides_topology_pointers #define define_siblings_show_map(name) \ -static ssize_t show_##name(struct sys_device *dev, char *buf) \ +static ssize_t show_##name(struct sys_device *dev, \ + struct sysdev_attribute *attr, char *buf) \ { \ unsigned int cpu = dev->id; \ return show_cpumap(0, &(topology_##name(cpu)), buf); \ } #define define_siblings_show_list(name) \ -static ssize_t show_##name##_list(struct sys_device *dev, char *buf) \ +static ssize_t show_##name##_list(struct sys_device *dev, \ + struct sysdev_attribute *attr, \ + char *buf) \ { \ unsigned int cpu = dev->id; \ return show_cpumap(1, &(topology_##name(cpu)), buf); \ @@ -74,7 +78,8 @@ static ssize_t show_##name##_list(struct sys_device *dev, char *buf) \ #else #define define_siblings_show_map(name) \ -static ssize_t show_##name(struct sys_device *dev, char *buf) \ +static ssize_t show_##name(struct sys_device *dev, \ + struct sysdev_attribute *attr, char *buf) \ { \ unsigned int cpu = dev->id; \ cpumask_t mask = topology_##name(cpu); \ @@ -82,7 +87,9 @@ static ssize_t show_##name(struct sys_device *dev, char *buf) \ } #define define_siblings_show_list(name) \ -static ssize_t show_##name##_list(struct sys_device *dev, char *buf) \ +static ssize_t show_##name##_list(struct sys_device *dev, \ + struct sysdev_attribute *attr, \ + char *buf) \ { \ unsigned int cpu = dev->id; \ cpumask_t mask = topology_##name(cpu); \ diff --git a/drivers/block/aoe/aoechr.c b/drivers/block/aoe/aoechr.c index d1de68a31920..181ebb85f0be 100644 --- a/drivers/block/aoe/aoechr.c +++ b/drivers/block/aoe/aoechr.c @@ -6,6 +6,7 @@ #include <linux/hdreg.h> #include <linux/blkdev.h> +#include <linux/completion.h> #include <linux/delay.h> #include <linux/smp_lock.h> #include "aoe.h" @@ -36,7 +37,7 @@ struct ErrMsg { static struct ErrMsg emsgs[NMSG]; static int emsgs_head_idx, emsgs_tail_idx; -static struct semaphore emsgs_sema; +static struct completion emsgs_comp; static spinlock_t emsgs_lock; static int nblocked_emsgs_readers; static struct class *aoe_class; @@ -141,7 +142,7 @@ bail: spin_unlock_irqrestore(&emsgs_lock, flags); spin_unlock_irqrestore(&emsgs_lock, flags); if (nblocked_emsgs_readers) - up(&emsgs_sema); + complete(&emsgs_comp); } static ssize_t @@ -221,7 +222,7 @@ aoechr_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off) spin_unlock_irqrestore(&emsgs_lock, flags); - n = down_interruptible(&emsgs_sema); + n = wait_for_completion_interruptible(&emsgs_comp); spin_lock_irqsave(&emsgs_lock, flags); @@ -269,7 +270,7 @@ aoechr_init(void) printk(KERN_ERR "aoe: can't register char device\n"); return n; } - sema_init(&emsgs_sema, 0); + init_completion(&emsgs_comp); spin_lock_init(&emsgs_lock); aoe_class = class_create(THIS_MODULE, "aoe"); if (IS_ERR(aoe_class)) { @@ -277,8 +278,9 @@ aoechr_init(void) return PTR_ERR(aoe_class); } for (i = 0; i < ARRAY_SIZE(chardevs); ++i) - device_create(aoe_class, NULL, - MKDEV(AOE_MAJOR, chardevs[i].minor), chardevs[i].name); + device_create_drvdata(aoe_class, NULL, + MKDEV(AOE_MAJOR, chardevs[i].minor), + NULL, chardevs[i].name); return 0; } diff --git a/drivers/block/paride/pg.c b/drivers/block/paride/pg.c index 9d92636350e5..d731ca42f802 100644 --- a/drivers/block/paride/pg.c +++ b/drivers/block/paride/pg.c @@ -686,8 +686,9 @@ static int __init pg_init(void) for (unit = 0; unit < PG_UNITS; unit++) { struct pg *dev = &devices[unit]; if (dev->present) - device_create(pg_class, NULL, MKDEV(major, unit), - "pg%u", unit); + device_create_drvdata(pg_class, NULL, + MKDEV(major, unit), NULL, + "pg%u", unit); } err = 0; goto out; diff --git a/drivers/block/paride/pt.c b/drivers/block/paride/pt.c index 5c74c3574a5a..673b8b2fd337 100644 --- a/drivers/block/paride/pt.c +++ b/drivers/block/paride/pt.c @@ -979,10 +979,12 @@ static int __init pt_init(void) for (unit = 0; unit < PT_UNITS; unit++) if (pt[unit].present) { - device_create(pt_class, NULL, MKDEV(major, unit), - "pt%d", unit); - device_create(pt_class, NULL, MKDEV(major, unit + 128), - "pt%dn", unit); + device_create_drvdata(pt_class, NULL, + MKDEV(major, unit), NULL, + "pt%d", unit); + device_create_drvdata(pt_class, NULL, + MKDEV(major, unit + 128), NULL, + "pt%dn", unit); } goto out; diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 45bee918c46a..158eed4d5161 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -303,7 +303,9 @@ static struct kobj_type kobj_pkt_type_wqueue = { static void pkt_sysfs_dev_new(struct pktcdvd_device *pd) { if (class_pktcdvd) { - pd->dev = device_create(class_pktcdvd, NULL, pd->pkt_dev, "%s", pd->name); + pd->dev = device_create_drvdata(class_pktcdvd, NULL, + pd->pkt_dev, NULL, + "%s", pd->name); if (IS_ERR(pd->dev)) pd->dev = NULL; } diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index dd7ea203f940..42251095134f 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -196,6 +196,7 @@ static int virtblk_probe(struct virtio_device *vdev) int err; u64 cap; u32 v; + u32 blk_size; if (index_to_minor(index) >= 1 << MINORBITS) return -ENOSPC; @@ -290,6 +291,13 @@ static int virtblk_probe(struct virtio_device *vdev) if (!err) blk_queue_max_hw_segments(vblk->disk->queue, v); + /* Host can optionally specify the block size of the device */ + err = virtio_config_val(vdev, VIRTIO_BLK_F_BLK_SIZE, + offsetof(struct virtio_blk_config, blk_size), + &blk_size); + if (!err) + blk_queue_hardsect_size(vblk->disk->queue, blk_size); + add_disk(vblk->disk); return 0; @@ -330,7 +338,7 @@ static struct virtio_device_id id_table[] = { static unsigned int features[] = { VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, - VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_RO, + VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE, }; static struct virtio_driver virtio_blk = { diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 650e6b44ce65..d0ac944e1696 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -36,6 +36,14 @@ config VT If unsure, say Y, or else you won't be able to do much with your new shiny Linux system :-) +config CONSOLE_TRANSLATIONS + depends on VT + default y + bool "Enable character translations in console" if EMBEDDED + ---help--- + This enables support for font mapping and Unicode translation + on virtual consoles. + config VT_CONSOLE bool "Support for console on virtual terminal" if EMBEDDED depends on VT @@ -300,16 +308,6 @@ config SPECIALIX and compile this driver as kernel loadable module which will be called specialix. -config SPECIALIX_RTSCTS - bool "Specialix DTR/RTS pin is RTS" - depends on SPECIALIX - help - The Specialix IO8+ card can only support either RTS or DTR. If you - say N here, the driver will use the pin as "DTR" when the tty is in - software handshake mode. If you say Y here or hardware handshake is - on, it will always be RTS. Read the file - <file:Documentation/specialix.txt> for more information. - config SX tristate "Specialix SX (and SI) card support" depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA) @@ -588,11 +586,14 @@ config HVC_DRIVER It will automatically be selected if one of the back-end console drivers is selected. +config HVC_IRQ + bool config HVC_CONSOLE bool "pSeries Hypervisor Virtual Console support" depends on PPC_PSERIES select HVC_DRIVER + select HVC_IRQ help pSeries machines when partitioned support a hypervisor virtual console. This driver allows each pSeries partition to have a console @@ -603,6 +604,7 @@ config HVC_ISERIES depends on PPC_ISERIES default y select HVC_DRIVER + select HVC_IRQ help iSeries machines support a hypervisor virtual console. @@ -624,13 +626,18 @@ config HVC_XEN bool "Xen Hypervisor Console support" depends on XEN select HVC_DRIVER + select HVC_IRQ default y help Xen virtual console device driver config VIRTIO_CONSOLE - bool + tristate "Virtio console" + depends on VIRTIO select HVC_DRIVER + help + Virtio console for use with lguest and other hypervisors. + config HVCS tristate "IBM Hypervisor Virtual Console Server support" @@ -867,13 +874,6 @@ config DS1302 endif # RTC_LIB -config COBALT_LCD - bool "Support for Cobalt LCD" - depends on MIPS_COBALT - help - This option enables support for the LCD display and buttons found - on Cobalt systems through a misc device. - config DTLK tristate "Double Talk PC internal speech card support" depends on ISA diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 0e0d12a06462..8a161c30e1dc 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -7,13 +7,13 @@ # FONTMAPFILE = cp437.uni -obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o +obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o obj-$(CONFIG_LEGACY_PTYS) += pty.o obj-$(CONFIG_UNIX98_PTYS) += pty.o obj-y += misc.o -obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o consolemap.o \ - consolemap_deftbl.o selection.o keyboard.o +obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o +obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o obj-$(CONFIG_AUDIT) += tty_audit.o obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o @@ -48,6 +48,7 @@ obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o obj-$(CONFIG_HVC_BEAT) += hvc_beat.o obj-$(CONFIG_HVC_DRIVER) += hvc_console.o +obj-$(CONFIG_HVC_IRQ) += hvc_irq.o obj-$(CONFIG_HVC_XEN) += hvc_xen.o obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o obj-$(CONFIG_RAW_DRIVER) += raw.o @@ -63,7 +64,6 @@ obj-$(CONFIG_BRIQ_PANEL) += briq_panel.o obj-$(CONFIG_BFIN_OTP) += bfin-otp.o obj-$(CONFIG_PRINTER) += lp.o -obj-$(CONFIG_TIPAR) += tipar.o obj-$(CONFIG_APM_EMULATION) += apm-emulation.o @@ -88,7 +88,6 @@ obj-$(CONFIG_TOSHIBA) += toshiba.o obj-$(CONFIG_I8K) += i8k.o obj-$(CONFIG_DS1620) += ds1620.o obj-$(CONFIG_HW_RANDOM) += hw_random/ -obj-$(CONFIG_COBALT_LCD) += lcd.o obj-$(CONFIG_PPDEV) += ppdev.o obj-$(CONFIG_NWBUTTON) += nwbutton.o obj-$(CONFIG_NWFLASH) += nwflash.o diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index 37457e5a4f2b..3530ff417a51 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -1248,7 +1248,7 @@ static int rs_tiocmset(struct tty_struct *tty, struct file *file, /* * rs_break() --- routine which turns the break handling on or off */ -static void rs_break(struct tty_struct *tty, int break_state) +static int rs_break(struct tty_struct *tty, int break_state) { struct async_struct * info = (struct async_struct *)tty->driver_data; unsigned long flags; @@ -1263,6 +1263,7 @@ static void rs_break(struct tty_struct *tty, int break_state) custom.adkcon = AC_UARTBRK; mb(); local_irq_restore(flags); + return 0; } diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index e991dc85f2fb..fe6d774fe2e4 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -3700,14 +3700,15 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, /* * cy_break() --- routine which turns the break handling on or off */ -static void cy_break(struct tty_struct *tty, int break_state) +static int cy_break(struct tty_struct *tty, int break_state) { struct cyclades_port *info = tty->driver_data; struct cyclades_card *card; unsigned long flags; + int retval = 0; if (serial_paranoia_check(info, tty->name, "cy_break")) - return; + return -EINVAL; card = info->card; @@ -3736,8 +3737,6 @@ static void cy_break(struct tty_struct *tty, int break_state) } } } else { - int retval; - if (break_state == -1) { retval = cyz_issue_cmd(card, info->line - card->first_line, @@ -3758,6 +3757,7 @@ static void cy_break(struct tty_struct *tty, int break_state) } } spin_unlock_irqrestore(&card->card_lock, flags); + return retval; } /* cy_break */ static int get_mon_info(struct cyclades_port *info, diff --git a/drivers/char/ds1302.c b/drivers/char/ds1302.c index fada6ddefbae..c5e67a623951 100644 --- a/drivers/char/ds1302.c +++ b/drivers/char/ds1302.c @@ -20,10 +20,11 @@ #include <linux/miscdevice.h> #include <linux/delay.h> #include <linux/bcd.h> +#include <linux/smp_lock.h> +#include <linux/uaccess.h> +#include <linux/io.h> -#include <asm/uaccess.h> #include <asm/system.h> -#include <asm/io.h> #include <asm/rtc.h> #if defined(CONFIG_M32R) #include <asm/m32r.h> @@ -153,9 +154,7 @@ static unsigned char days_in_mo[] = /* ioctl that supports RTC_RD_TIME and RTC_SET_TIME (read and set time/date). */ -static int -rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) +static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { unsigned long flags; @@ -165,7 +164,9 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, struct rtc_time rtc_tm; memset(&rtc_tm, 0, sizeof (struct rtc_time)); + lock_kernel(); get_rtc_time(&rtc_tm); + unlock_kernel(); if (copy_to_user((struct rtc_time*)arg, &rtc_tm, sizeof(struct rtc_time))) return -EFAULT; return 0; @@ -217,6 +218,7 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, BIN_TO_BCD(mon); BIN_TO_BCD(yrs); + lock_kernel(); local_irq_save(flags); CMOS_WRITE(yrs, RTC_YEAR); CMOS_WRITE(mon, RTC_MONTH); @@ -225,6 +227,7 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, CMOS_WRITE(min, RTC_MINUTES); CMOS_WRITE(sec, RTC_SECONDS); local_irq_restore(flags); + unlock_kernel(); /* Notice that at this point, the RTC is updated but * the kernel is still running with the old time. @@ -244,8 +247,10 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, if(copy_from_user(&tcs_val, (int*)arg, sizeof(int))) return -EFAULT; + lock_kernel(); tcs_val = RTC_TCR_PATTERN | (tcs_val & 0x0F); ds1302_writereg(RTC_TRICKLECHARGER, tcs_val); + unlock_kernel(); return 0; } default: @@ -282,7 +287,7 @@ get_rtc_status(char *buf) static const struct file_operations rtc_fops = { .owner = THIS_MODULE, - .ioctl = rtc_ioctl, + .unlocked_ioctl = rtc_ioctl, }; /* Probe for the chip by writing something to its RAM and try reading it back. */ diff --git a/drivers/char/dsp56k.c b/drivers/char/dsp56k.c index b9a30c30e2b8..ca7c72a486b2 100644 --- a/drivers/char/dsp56k.c +++ b/drivers/char/dsp56k.c @@ -36,10 +36,10 @@ #include <linux/smp_lock.h> #include <linux/firmware.h> #include <linux/platform_device.h> +#include <linux/uaccess.h> /* For put_user and get_user */ #include <asm/atarihw.h> #include <asm/traps.h> -#include <asm/uaccess.h> /* For put_user and get_user */ #include <asm/dsp56k.h> @@ -303,10 +303,10 @@ static ssize_t dsp56k_write(struct file *file, const char __user *buf, size_t co } } -static int dsp56k_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long dsp56k_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { - int dev = iminor(inode) & 0x0f; + int dev = iminor(file->f_path.dentry->d_inode) & 0x0f; void __user *argp = (void __user *)arg; switch(dev) @@ -331,8 +331,9 @@ static int dsp56k_ioctl(struct inode *inode, struct file *file, if (len > DSP56K_MAX_BINARY_LENGTH) { return -EINVAL; } - + lock_kernel(); r = dsp56k_upload(bin, len); + unlock_kernel(); if (r < 0) { return r; } @@ -342,12 +343,16 @@ static int dsp56k_ioctl(struct inode *inode, struct file *file, case DSP56K_SET_TX_WSIZE: if (arg > 4 || arg < 1) return -EINVAL; + lock_kernel(); dsp56k.tx_wsize = (int) arg; + unlock_kernel(); break; case DSP56K_SET_RX_WSIZE: if (arg > 4 || arg < 1) return -EINVAL; + lock_kernel(); dsp56k.rx_wsize = (int) arg; + unlock_kernel(); break; case DSP56K_HOST_FLAGS: { @@ -359,6 +364,7 @@ static int dsp56k_ioctl(struct inode *inode, struct file *file, if(get_user(out, &hf->out) < 0) return -EFAULT; + lock_kernel(); if ((dir & 0x1) && (out & 0x1)) dsp56k_host_interface.icr |= DSP56K_ICR_HF0; else if (dir & 0x1) @@ -373,14 +379,16 @@ static int dsp56k_ioctl(struct inode *inode, struct file *file, if (dsp56k_host_interface.icr & DSP56K_ICR_HF1) status |= 0x2; if (dsp56k_host_interface.isr & DSP56K_ISR_HF2) status |= 0x4; if (dsp56k_host_interface.isr & DSP56K_ISR_HF3) status |= 0x8; - + unlock_kernel(); return put_user(status, &hf->status); } case DSP56K_HOST_CMD: if (arg > 31 || arg < 0) return -EINVAL; + lock_kernel(); dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) | DSP56K_CVR_HC); + unlock_kernel(); break; default: return -EINVAL; @@ -472,7 +480,7 @@ static const struct file_operations dsp56k_fops = { .owner = THIS_MODULE, .read = dsp56k_read, .write = dsp56k_write, - .ioctl = dsp56k_ioctl, + .unlocked_ioctl = dsp56k_ioctl, .open = dsp56k_open, .release = dsp56k_release, }; @@ -500,7 +508,8 @@ static int __init dsp56k_init_driver(void) err = PTR_ERR(dsp56k_class); goto out_chrdev; } - device_create(dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0), "dsp56k"); + device_create_drvdata(dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0), + NULL, "dsp56k"); printk(banner); goto out; diff --git a/drivers/char/efirtc.c b/drivers/char/efirtc.c index d57ca3e4e534..67fbd7aab5db 100644 --- a/drivers/char/efirtc.c +++ b/drivers/char/efirtc.c @@ -37,8 +37,9 @@ #include <linux/rtc.h> #include <linux/proc_fs.h> #include <linux/efi.h> +#include <linux/smp_lock.h> +#include <linux/uaccess.h> -#include <asm/uaccess.h> #include <asm/system.h> #define EFI_RTC_VERSION "0.4" @@ -51,8 +52,8 @@ static DEFINE_SPINLOCK(efi_rtc_lock); -static int efi_rtc_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); +static long efi_rtc_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); #define is_leap(year) \ ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) @@ -146,9 +147,8 @@ convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime) } } -static int -efi_rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) +static long efi_rtc_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { efi_status_t status; @@ -175,13 +175,13 @@ efi_rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, return -EINVAL; case RTC_RD_TIME: - + lock_kernel(); spin_lock_irqsave(&efi_rtc_lock, flags); status = efi.get_time(&eft, &cap); spin_unlock_irqrestore(&efi_rtc_lock,flags); - + unlock_kernel(); if (status != EFI_SUCCESS) { /* should never happen */ printk(KERN_ERR "efitime: can't read time\n"); @@ -203,11 +203,13 @@ efi_rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, convert_to_efi_time(&wtime, &eft); + lock_kernel(); spin_lock_irqsave(&efi_rtc_lock, flags); status = efi.set_time(&eft); spin_unlock_irqrestore(&efi_rtc_lock,flags); + unlock_kernel(); return status == EFI_SUCCESS ? 0 : -EINVAL; @@ -223,6 +225,7 @@ efi_rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, convert_to_efi_time(&wtime, &eft); + lock_kernel(); spin_lock_irqsave(&efi_rtc_lock, flags); /* * XXX Fixme: @@ -233,16 +236,19 @@ efi_rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, status = efi.set_wakeup_time((efi_bool_t)enabled, &eft); spin_unlock_irqrestore(&efi_rtc_lock,flags); + unlock_kernel(); return status == EFI_SUCCESS ? 0 : -EINVAL; case RTC_WKALM_RD: + lock_kernel(); spin_lock_irqsave(&efi_rtc_lock, flags); status = efi.get_wakeup_time((efi_bool_t *)&enabled, (efi_bool_t *)&pending, &eft); spin_unlock_irqrestore(&efi_rtc_lock,flags); + unlock_kernel(); if (status != EFI_SUCCESS) return -EINVAL; @@ -256,7 +262,7 @@ efi_rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, return copy_to_user(&ewp->time, &wtime, sizeof(struct rtc_time)) ? -EFAULT : 0; } - return -EINVAL; + return -ENOTTY; } /* @@ -265,8 +271,7 @@ efi_rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, * up things on a close. */ -static int -efi_rtc_open(struct inode *inode, struct file *file) +static int efi_rtc_open(struct inode *inode, struct file *file) { /* * nothing special to do here @@ -277,8 +282,7 @@ efi_rtc_open(struct inode *inode, struct file *file) return 0; } -static int -efi_rtc_close(struct inode *inode, struct file *file) +static int efi_rtc_close(struct inode *inode, struct file *file) { return 0; } @@ -289,13 +293,12 @@ efi_rtc_close(struct inode *inode, struct file *file) static const struct file_operations efi_rtc_fops = { .owner = THIS_MODULE, - .ioctl = efi_rtc_ioctl, + .unlocked_ioctl = efi_rtc_ioctl, .open = efi_rtc_open, .release = efi_rtc_close, }; -static struct miscdevice efi_rtc_dev= -{ +static struct miscdevice efi_rtc_dev= { EFI_RTC_MINOR, "efirtc", &efi_rtc_fops diff --git a/drivers/char/epca.c b/drivers/char/epca.c index ac9995f6578b..456e4ede049f 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -184,9 +184,8 @@ static void pc_stop(struct tty_struct *); static void pc_start(struct tty_struct *); static void pc_throttle(struct tty_struct *tty); static void pc_unthrottle(struct tty_struct *tty); -static void digi_send_break(struct channel *ch, int msec); +static int pc_send_break(struct tty_struct *tty, int msec); static void setup_empty_event(struct tty_struct *tty, struct channel *ch); -static void epca_setup(char *, int *); static int pc_write(struct tty_struct *, const unsigned char *, int); static int pc_init(void); @@ -1040,6 +1039,7 @@ static const struct tty_operations pc_ops = { .throttle = pc_throttle, .unthrottle = pc_unthrottle, .hangup = pc_hangup, + .break_ctl = pc_send_break }; static int info_open(struct tty_struct *tty, struct file *filp) @@ -1132,7 +1132,7 @@ static int __init pc_init(void) pc_driver->init_termios.c_lflag = 0; pc_driver->init_termios.c_ispeed = 9600; pc_driver->init_termios.c_ospeed = 9600; - pc_driver->flags = TTY_DRIVER_REAL_RAW; + pc_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_HARDWARE_BREAK; tty_set_operations(pc_driver, &pc_ops); pc_info->owner = THIS_MODULE; @@ -2177,7 +2177,6 @@ static int pc_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { digiflow_t dflow; - int retval; unsigned long flags; unsigned int mflag, mstat; unsigned char startc, stopc; @@ -2189,37 +2188,7 @@ static int pc_ioctl(struct tty_struct *tty, struct file *file, bc = ch->brdchan; else return -EINVAL; - /* - * For POSIX compliance we need to add more ioctls. See tty_ioctl.c in - * /usr/src/linux/drivers/char for a good example. In particular think - * about adding TCSETAF, TCSETAW, TCSETA, TCSETSF, TCSETSW, TCSETS. - */ switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) - return retval; - /* Setup an event to indicate when the transmit - buffer empties */ - spin_lock_irqsave(&epca_lock, flags); - setup_empty_event(tty, ch); - spin_unlock_irqrestore(&epca_lock, flags); - tty_wait_until_sent(tty, 0); - if (!arg) - digi_send_break(ch, HZ / 4); /* 1/4 second */ - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return retval; - /* Setup an event to indicate when the transmit buffer - empties */ - spin_lock_irqsave(&epca_lock, flags); - setup_empty_event(tty, ch); - spin_unlock_irqrestore(&epca_lock, flags); - tty_wait_until_sent(tty, 0); - digi_send_break(ch, arg ? arg*(HZ/10) : HZ/4); - return 0; case TIOCMODG: mflag = pc_tiocmget(tty, file); if (put_user(mflag, (unsigned long __user *)argp)) @@ -2505,10 +2474,14 @@ static void pc_unthrottle(struct tty_struct *tty) } } -static void digi_send_break(struct channel *ch, int msec) +static int pc_send_break(struct tty_struct *tty, int msec) { + struct channel *ch = (struct channel *) tty->driver_data; unsigned long flags; + if (msec == -1) + return -EOPNOTSUPP; + spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); /* @@ -2521,6 +2494,7 @@ static void digi_send_break(struct channel *ch, int msec) fepcmd(ch, SENDBREAK, msec, 0, 10, 0); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); + return 0; } /* Caller MUST hold the lock */ @@ -2538,7 +2512,8 @@ static void setup_empty_event(struct tty_struct *tty, struct channel *ch) memoff(ch); } -static void epca_setup(char *str, int *ints) +#ifndef MODULE +static void __init epca_setup(char *str, int *ints) { struct board_info board; int index, loop, last; @@ -2792,6 +2767,17 @@ static void epca_setup(char *str, int *ints) num_cards++; } +static int __init epca_real_setup(char *str) +{ + int ints[11]; + + epca_setup(get_options(str, 11, ints), ints); + return 1; +} + +__setup("digiepca", epca_real_setup); +#endif + enum epic_board_types { brd_xr = 0, brd_xem, diff --git a/drivers/char/esp.c b/drivers/char/esp.c index 2eaf09f93e3d..7f077c0097f6 100644 --- a/drivers/char/esp.c +++ b/drivers/char/esp.c @@ -1725,13 +1725,13 @@ static int esp_tiocmset(struct tty_struct *tty, struct file *file, /* * rs_break() --- routine which turns the break handling on or off */ -static void esp_break(struct tty_struct *tty, int break_state) +static int esp_break(struct tty_struct *tty, int break_state) { struct esp_struct *info = tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->name, "esp_break")) - return; + return -EINVAL; if (break_state == -1) { spin_lock_irqsave(&info->lock, flags); @@ -1747,6 +1747,7 @@ static void esp_break(struct tty_struct *tty, int break_state) serial_out(info, UART_ESI_CMD2, 0x00); spin_unlock_irqrestore(&info->lock, flags); } + return 0; } static int rs_ioctl(struct tty_struct *tty, struct file *file, diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index fb0a85a1eb36..b3f5dbc6d880 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -623,6 +623,7 @@ static inline int hpet_tpcheck(struct hpet_task *tp) return -ENXIO; } +#if 0 int hpet_unregister(struct hpet_task *tp) { struct hpet_dev *devp; @@ -652,6 +653,7 @@ int hpet_unregister(struct hpet_task *tp) return 0; } +#endif /* 0 */ static ctl_table hpet_table[] = { { diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 2f9759d625cc..02aac104842d 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -27,7 +27,6 @@ #include <linux/init.h> #include <linux/kbd_kern.h> #include <linux/kernel.h> -#include <linux/kref.h> #include <linux/kthread.h> #include <linux/list.h> #include <linux/module.h> @@ -75,23 +74,6 @@ static int hvc_init(void); static int sysrq_pressed; #endif -struct hvc_struct { - spinlock_t lock; - int index; - struct tty_struct *tty; - unsigned int count; - int do_wakeup; - char *outbuf; - int outbuf_size; - int n_outbuf; - uint32_t vtermno; - struct hv_ops *ops; - int irq_requested; - int irq; - struct list_head next; - struct kref kref; /* ref count & hvc_struct lifetime */ -}; - /* dynamic list of hvc_struct instances */ static LIST_HEAD(hvc_structs); @@ -298,27 +280,15 @@ int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops) return 0; } +EXPORT_SYMBOL_GPL(hvc_instantiate); /* Wake the sleeping khvcd */ -static void hvc_kick(void) +void hvc_kick(void) { hvc_kicked = 1; wake_up_process(hvc_task); } - -static int hvc_poll(struct hvc_struct *hp); - -/* - * NOTE: This API isn't used if the console adapter doesn't support interrupts. - * In this case the console is poll driven. - */ -static irqreturn_t hvc_handle_interrupt(int irq, void *dev_instance) -{ - /* if hvc_poll request a repoll, then kick the hvcd thread */ - if (hvc_poll(dev_instance)) - hvc_kick(); - return IRQ_HANDLED; -} +EXPORT_SYMBOL_GPL(hvc_kick); static void hvc_unthrottle(struct tty_struct *tty) { @@ -333,7 +303,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp) { struct hvc_struct *hp; unsigned long flags; - int irq = 0; int rc = 0; /* Auto increments kref reference if found. */ @@ -352,18 +321,15 @@ static int hvc_open(struct tty_struct *tty, struct file * filp) tty->low_latency = 1; /* Makes flushes to ldisc synchronous. */ hp->tty = tty; - /* Save for request_irq outside of spin_lock. */ - irq = hp->irq; - if (irq) - hp->irq_requested = 1; + + if (hp->ops->notifier_add) + rc = hp->ops->notifier_add(hp, hp->data); spin_unlock_irqrestore(&hp->lock, flags); - /* check error, fallback to non-irq */ - if (irq) - rc = request_irq(irq, hvc_handle_interrupt, IRQF_DISABLED, "hvc_console", hp); + /* - * If the request_irq() fails and we return an error. The tty layer + * If the notifier fails we return an error. The tty layer * will call hvc_close() after a failed open but we don't want to clean * up there so we'll clean up here and clear out the previously set * tty fields and return the kref reference. @@ -371,7 +337,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp) if (rc) { spin_lock_irqsave(&hp->lock, flags); hp->tty = NULL; - hp->irq_requested = 0; spin_unlock_irqrestore(&hp->lock, flags); tty->driver_data = NULL; kref_put(&hp->kref, destroy_hvc_struct); @@ -386,7 +351,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp) static void hvc_close(struct tty_struct *tty, struct file * filp) { struct hvc_struct *hp; - int irq = 0; unsigned long flags; if (tty_hung_up_p(filp)) @@ -404,9 +368,8 @@ static void hvc_close(struct tty_struct *tty, struct file * filp) spin_lock_irqsave(&hp->lock, flags); if (--hp->count == 0) { - if (hp->irq_requested) - irq = hp->irq; - hp->irq_requested = 0; + if (hp->ops->notifier_del) + hp->ops->notifier_del(hp, hp->data); /* We are done with the tty pointer now. */ hp->tty = NULL; @@ -418,10 +381,6 @@ static void hvc_close(struct tty_struct *tty, struct file * filp) * waking periodically to check chars_in_buffer(). */ tty_wait_until_sent(tty, HVC_CLOSE_WAIT); - - if (irq) - free_irq(irq, hp); - } else { if (hp->count < 0) printk(KERN_ERR "hvc_close %X: oops, count is %d\n", @@ -436,7 +395,6 @@ static void hvc_hangup(struct tty_struct *tty) { struct hvc_struct *hp = tty->driver_data; unsigned long flags; - int irq = 0; int temp_open_count; if (!hp) @@ -458,13 +416,12 @@ static void hvc_hangup(struct tty_struct *tty) hp->count = 0; hp->n_outbuf = 0; hp->tty = NULL; - if (hp->irq_requested) - /* Saved for use outside of spin_lock. */ - irq = hp->irq; - hp->irq_requested = 0; + + if (hp->ops->notifier_del) + hp->ops->notifier_del(hp, hp->data); + spin_unlock_irqrestore(&hp->lock, flags); - if (irq) - free_irq(irq, hp); + while(temp_open_count) { --temp_open_count; kref_put(&hp->kref, destroy_hvc_struct); @@ -575,7 +532,7 @@ static u32 timeout = MIN_TIMEOUT; #define HVC_POLL_READ 0x00000001 #define HVC_POLL_WRITE 0x00000002 -static int hvc_poll(struct hvc_struct *hp) +int hvc_poll(struct hvc_struct *hp) { struct tty_struct *tty; int i, n, poll_mask = 0; @@ -602,10 +559,10 @@ static int hvc_poll(struct hvc_struct *hp) if (test_bit(TTY_THROTTLED, &tty->flags)) goto throttled; - /* If we aren't interrupt driven and aren't throttled, we always + /* If we aren't notifier driven and aren't throttled, we always * request a reschedule */ - if (hp->irq == 0) + if (!hp->irq_requested) poll_mask |= HVC_POLL_READ; /* Read data if any */ @@ -674,6 +631,7 @@ static int hvc_poll(struct hvc_struct *hp) return poll_mask; } +EXPORT_SYMBOL_GPL(hvc_poll); /* * This kthread is either polling or interrupt driven. This is determined by @@ -733,7 +691,7 @@ static const struct tty_operations hvc_ops = { .chars_in_buffer = hvc_chars_in_buffer, }; -struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq, +struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int data, struct hv_ops *ops, int outbuf_size) { struct hvc_struct *hp; @@ -754,7 +712,7 @@ struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq, memset(hp, 0x00, sizeof(*hp)); hp->vtermno = vtermno; - hp->irq = irq; + hp->data = data; hp->ops = ops; hp->outbuf_size = outbuf_size; hp->outbuf = &((char *)hp)[ALIGN(sizeof(*hp), sizeof(long))]; @@ -784,6 +742,7 @@ struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq, return hp; } +EXPORT_SYMBOL_GPL(hvc_alloc); int __devexit hvc_remove(struct hvc_struct *hp) { diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h index 42ffb17e15df..d9ce10915625 100644 --- a/drivers/char/hvc_console.h +++ b/drivers/char/hvc_console.h @@ -26,6 +26,7 @@ #ifndef HVC_CONSOLE_H #define HVC_CONSOLE_H +#include <linux/kref.h> /* * This is the max number of console adapters that can/will be found as @@ -42,24 +43,50 @@ */ #define HVC_ALLOC_TTY_ADAPTERS 8 +struct hvc_struct { + spinlock_t lock; + int index; + struct tty_struct *tty; + unsigned int count; + int do_wakeup; + char *outbuf; + int outbuf_size; + int n_outbuf; + uint32_t vtermno; + struct hv_ops *ops; + int irq_requested; + int data; + struct list_head next; + struct kref kref; /* ref count & hvc_struct lifetime */ +}; /* implemented by a low level driver */ struct hv_ops { int (*get_chars)(uint32_t vtermno, char *buf, int count); int (*put_chars)(uint32_t vtermno, const char *buf, int count); -}; -struct hvc_struct; + /* Callbacks for notification. Called in open and close */ + int (*notifier_add)(struct hvc_struct *hp, int irq); + void (*notifier_del)(struct hvc_struct *hp, int irq); +}; /* Register a vterm and a slot index for use as a console (console_init) */ extern int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops); /* register a vterm for hvc tty operation (module_init or hotplug add) */ -extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int irq, +extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int data, struct hv_ops *ops, int outbuf_size); -/* remove a vterm from hvc tty operation (modele_exit or hotplug remove) */ +/* remove a vterm from hvc tty operation (module_exit or hotplug remove) */ extern int __devexit hvc_remove(struct hvc_struct *hp); +/* data available */ +int hvc_poll(struct hvc_struct *hp); +void hvc_kick(void); + +/* default notifier for irq based notification */ +extern int notifier_add_irq(struct hvc_struct *hp, int data); +extern void notifier_del_irq(struct hvc_struct *hp, int data); + #if defined(CONFIG_XMON) && defined(CONFIG_SMP) #include <asm/xmon.h> diff --git a/drivers/char/hvc_irq.c b/drivers/char/hvc_irq.c new file mode 100644 index 000000000000..73a59cdb8947 --- /dev/null +++ b/drivers/char/hvc_irq.c @@ -0,0 +1,44 @@ +/* + * Copyright IBM Corp. 2001,2008 + * + * This file contains the IRQ specific code for hvc_console + * + */ + +#include <linux/interrupt.h> + +#include "hvc_console.h" + +static irqreturn_t hvc_handle_interrupt(int irq, void *dev_instance) +{ + /* if hvc_poll request a repoll, then kick the hvcd thread */ + if (hvc_poll(dev_instance)) + hvc_kick(); + return IRQ_HANDLED; +} + +/* + * For IRQ based systems these callbacks can be used + */ +int notifier_add_irq(struct hvc_struct *hp, int irq) +{ + int rc; + + if (!irq) { + hp->irq_requested = 0; + return 0; + } + rc = request_irq(irq, hvc_handle_interrupt, IRQF_DISABLED, + "hvc_console", hp); + if (!rc) + hp->irq_requested = 1; + return rc; +} + +void notifier_del_irq(struct hvc_struct *hp, int irq) +{ + if (!irq) + return; + free_irq(irq, hp); + hp->irq_requested = 0; +} diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c index a08f8f981c11..b71c610fe5ae 100644 --- a/drivers/char/hvc_iseries.c +++ b/drivers/char/hvc_iseries.c @@ -200,6 +200,8 @@ done: static struct hv_ops hvc_get_put_ops = { .get_chars = get_chars, .put_chars = put_chars, + .notifier_add = notifier_add_irq, + .notifier_del = notifier_del_irq, }; static int __devinit hvc_vio_probe(struct vio_dev *vdev, diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c index 79711aa4b41d..93f3840c1682 100644 --- a/drivers/char/hvc_vio.c +++ b/drivers/char/hvc_vio.c @@ -80,6 +80,8 @@ static int filtered_get_chars(uint32_t vtermno, char *buf, int count) static struct hv_ops hvc_get_put_ops = { .get_chars = filtered_get_chars, .put_chars = hvc_put_chars, + .notifier_add = notifier_add_irq, + .notifier_del = notifier_del_irq, }; static int __devinit hvc_vio_probe(struct vio_dev *vdev, diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c index db2ae4216279..6b70aa66a587 100644 --- a/drivers/char/hvc_xen.c +++ b/drivers/char/hvc_xen.c @@ -100,6 +100,8 @@ static int read_console(uint32_t vtermno, char *buf, int len) static struct hv_ops hvc_ops = { .get_chars = read_console, .put_chars = write_console, + .notifier_add = notifier_add_irq, + .notifier_del = notifier_del_irq, }; static int __init xen_init(void) diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index efd0b4db7c8e..8822eca58ffa 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -59,6 +59,19 @@ config HW_RANDOM_GEODE If unsure, say Y. +config HW_RANDOM_N2RNG + tristate "Niagara2 Random Number Generator support" + depends on HW_RANDOM && SPARC64 + default HW_RANDOM + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on Niagara2 cpus. + + To compile this driver as a module, choose M here: the + module will be called n2-rng. + + If unsure, say Y. + config HW_RANDOM_VIA tristate "VIA HW Random Number Generator support" depends on HW_RANDOM && X86_32 diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index b4940ddbb35f..b6effb7522c2 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -7,6 +7,8 @@ rng-core-y := core.o obj-$(CONFIG_HW_RANDOM_INTEL) += intel-rng.o obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o +obj-$(CONFIG_HW_RANDOM_N2RNG) += n2-rng.o +n2-rng-y := n2-drv.o n2-asm.o obj-$(CONFIG_HW_RANDOM_VIA) += via-rng.o obj-$(CONFIG_HW_RANDOM_IXP4XX) += ixp4xx-rng.o obj-$(CONFIG_HW_RANDOM_OMAP) += omap-rng.o diff --git a/drivers/char/hw_random/intel-rng.c b/drivers/char/hw_random/intel-rng.c index 27fdc0866496..8a2fce0756ec 100644 --- a/drivers/char/hw_random/intel-rng.c +++ b/drivers/char/hw_random/intel-rng.c @@ -241,7 +241,7 @@ static int __init intel_rng_hw_init(void *_intel_rng_hw) struct intel_rng_hw *intel_rng_hw = _intel_rng_hw; u8 mfc, dvc; - /* interrupts disabled in stop_machine_run call */ + /* interrupts disabled in stop_machine call */ if (!(intel_rng_hw->fwh_dec_en1_val & FWH_F8_EN_MASK)) pci_write_config_byte(intel_rng_hw->dev, @@ -365,10 +365,10 @@ static int __init mod_init(void) * location with the Read ID command, all activity on the system * must be stopped until the state is back to normal. * - * Use stop_machine_run because IPIs can be blocked by disabling + * Use stop_machine because IPIs can be blocked by disabling * interrupts. */ - err = stop_machine_run(intel_rng_hw_init, intel_rng_hw, NR_CPUS); + err = stop_machine(intel_rng_hw_init, intel_rng_hw, NULL); pci_dev_put(dev); iounmap(intel_rng_hw->mem); kfree(intel_rng_hw); diff --git a/drivers/char/hw_random/n2-asm.S b/drivers/char/hw_random/n2-asm.S new file mode 100644 index 000000000000..9b6eb5cd59f6 --- /dev/null +++ b/drivers/char/hw_random/n2-asm.S @@ -0,0 +1,79 @@ +/* n2-asm.S: Niagara2 RNG hypervisor call assembler. + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ +#include <linux/linkage.h> +#include <asm/hypervisor.h> +#include "n2rng.h" + + .text + +ENTRY(sun4v_rng_get_diag_ctl) + mov HV_FAST_RNG_GET_DIAG_CTL, %o5 + ta HV_FAST_TRAP + retl + nop +ENDPROC(sun4v_rng_get_diag_ctl) + +ENTRY(sun4v_rng_ctl_read_v1) + mov %o1, %o3 + mov %o2, %o4 + mov HV_FAST_RNG_CTL_READ, %o5 + ta HV_FAST_TRAP + stx %o1, [%o3] + retl + stx %o2, [%o4] +ENDPROC(sun4v_rng_ctl_read_v1) + +ENTRY(sun4v_rng_ctl_read_v2) + save %sp, -192, %sp + mov %i0, %o0 + mov %i1, %o1 + mov HV_FAST_RNG_CTL_READ, %o5 + ta HV_FAST_TRAP + stx %o1, [%i2] + stx %o2, [%i3] + stx %o3, [%i4] + stx %o4, [%i5] + ret + restore %g0, %o0, %o0 +ENDPROC(sun4v_rng_ctl_read_v2) + +ENTRY(sun4v_rng_ctl_write_v1) + mov %o3, %o4 + mov HV_FAST_RNG_CTL_WRITE, %o5 + ta HV_FAST_TRAP + retl + stx %o1, [%o4] +ENDPROC(sun4v_rng_ctl_write_v1) + +ENTRY(sun4v_rng_ctl_write_v2) + mov HV_FAST_RNG_CTL_WRITE, %o5 + ta HV_FAST_TRAP + retl + nop +ENDPROC(sun4v_rng_ctl_write_v2) + +ENTRY(sun4v_rng_data_read_diag_v1) + mov %o2, %o4 + mov HV_FAST_RNG_DATA_READ_DIAG, %o5 + ta HV_FAST_TRAP + retl + stx %o1, [%o4] +ENDPROC(sun4v_rng_data_read_diag_v1) + +ENTRY(sun4v_rng_data_read_diag_v2) + mov %o3, %o4 + mov HV_FAST_RNG_DATA_READ_DIAG, %o5 + ta HV_FAST_TRAP + retl + stx %o1, [%o4] +ENDPROC(sun4v_rng_data_read_diag_v2) + +ENTRY(sun4v_rng_data_read) + mov %o1, %o4 + mov HV_FAST_RNG_DATA_READ, %o5 + ta HV_FAST_TRAP + retl + stx %o1, [%o4] +ENDPROC(sun4v_rng_data_read) diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c new file mode 100644 index 000000000000..5220f541df25 --- /dev/null +++ b/drivers/char/hw_random/n2-drv.c @@ -0,0 +1,771 @@ +/* n2-drv.c: Niagara-2 RNG driver. + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/preempt.h> +#include <linux/hw_random.h> + +#include <linux/of.h> +#include <linux/of_device.h> + +#include <asm/hypervisor.h> + +#include "n2rng.h" + +#define DRV_MODULE_NAME "n2rng" +#define PFX DRV_MODULE_NAME ": " +#define DRV_MODULE_VERSION "0.1" +#define DRV_MODULE_RELDATE "May 15, 2008" + +static char version[] __devinitdata = + DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; + +MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); +MODULE_DESCRIPTION("Niagara2 RNG driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_MODULE_VERSION); + +/* The Niagara2 RNG provides a 64-bit read-only random number + * register, plus a control register. Access to the RNG is + * virtualized through the hypervisor so that both guests and control + * nodes can access the device. + * + * The entropy source consists of raw entropy sources, each + * constructed from a voltage controlled oscillator whose phase is + * jittered by thermal noise sources. + * + * The oscillator in each of the three raw entropy sources run at + * different frequencies. Normally, all three generator outputs are + * gathered, xored together, and fed into a CRC circuit, the output of + * which is the 64-bit read-only register. + * + * Some time is necessary for all the necessary entropy to build up + * such that a full 64-bits of entropy are available in the register. + * In normal operating mode (RNG_CTL_LFSR is set), the chip implements + * an interlock which blocks register reads until sufficient entropy + * is available. + * + * A control register is provided for adjusting various aspects of RNG + * operation, and to enable diagnostic modes. Each of the three raw + * entropy sources has an enable bit (RNG_CTL_ES{1,2,3}). Also + * provided are fields for controlling the minimum time in cycles + * between read accesses to the register (RNG_CTL_WAIT, this controls + * the interlock described in the previous paragraph). + * + * The standard setting is to have the mode bit (RNG_CTL_LFSR) set, + * all three entropy sources enabled, and the interlock time set + * appropriately. + * + * The CRC polynomial used by the chip is: + * + * P(X) = x64 + x61 + x57 + x56 + x52 + x51 + x50 + x48 + x47 + x46 + + * x43 + x42 + x41 + x39 + x38 + x37 + x35 + x32 + x28 + x25 + + * x22 + x21 + x17 + x15 + x13 + x12 + x11 + x7 + x5 + x + 1 + * + * The RNG_CTL_VCO value of each noise cell must be programmed + * seperately. This is why 4 control register values must be provided + * to the hypervisor. During a write, the hypervisor writes them all, + * one at a time, to the actual RNG_CTL register. The first three + * values are used to setup the desired RNG_CTL_VCO for each entropy + * source, for example: + * + * control 0: (1 << RNG_CTL_VCO_SHIFT) | RNG_CTL_ES1 + * control 1: (2 << RNG_CTL_VCO_SHIFT) | RNG_CTL_ES2 + * control 2: (3 << RNG_CTL_VCO_SHIFT) | RNG_CTL_ES3 + * + * And then the fourth value sets the final chip state and enables + * desired. + */ + +static int n2rng_hv_err_trans(unsigned long hv_err) +{ + switch (hv_err) { + case HV_EOK: + return 0; + case HV_EWOULDBLOCK: + return -EAGAIN; + case HV_ENOACCESS: + return -EPERM; + case HV_EIO: + return -EIO; + case HV_EBUSY: + return -EBUSY; + case HV_EBADALIGN: + case HV_ENORADDR: + return -EFAULT; + default: + return -EINVAL; + } +} + +static unsigned long n2rng_generic_read_control_v2(unsigned long ra, + unsigned long unit) +{ + unsigned long hv_err, state, ticks, watchdog_delta, watchdog_status; + int block = 0, busy = 0; + + while (1) { + hv_err = sun4v_rng_ctl_read_v2(ra, unit, &state, + &ticks, + &watchdog_delta, + &watchdog_status); + if (hv_err == HV_EOK) + break; + + if (hv_err == HV_EBUSY) { + if (++busy >= N2RNG_BUSY_LIMIT) + break; + + udelay(1); + } else if (hv_err == HV_EWOULDBLOCK) { + if (++block >= N2RNG_BLOCK_LIMIT) + break; + + __delay(ticks); + } else + break; + } + + return hv_err; +} + +/* In multi-socket situations, the hypervisor might need to + * queue up the RNG control register write if it's for a unit + * that is on a cpu socket other than the one we are executing on. + * + * We poll here waiting for a successful read of that control + * register to make sure the write has been actually performed. + */ +static unsigned long n2rng_control_settle_v2(struct n2rng *np, int unit) +{ + unsigned long ra = __pa(&np->scratch_control[0]); + + return n2rng_generic_read_control_v2(ra, unit); +} + +static unsigned long n2rng_write_ctl_one(struct n2rng *np, int unit, + unsigned long state, + unsigned long control_ra, + unsigned long watchdog_timeout, + unsigned long *ticks) +{ + unsigned long hv_err; + + if (np->hvapi_major == 1) { + hv_err = sun4v_rng_ctl_write_v1(control_ra, state, + watchdog_timeout, ticks); + } else { + hv_err = sun4v_rng_ctl_write_v2(control_ra, state, + watchdog_timeout, unit); + if (hv_err == HV_EOK) + hv_err = n2rng_control_settle_v2(np, unit); + *ticks = N2RNG_ACCUM_CYCLES_DEFAULT; + } + + return hv_err; +} + +static int n2rng_generic_read_data(unsigned long data_ra) +{ + unsigned long ticks, hv_err; + int block = 0, hcheck = 0; + + while (1) { + hv_err = sun4v_rng_data_read(data_ra, &ticks); + if (hv_err == HV_EOK) + return 0; + + if (hv_err == HV_EWOULDBLOCK) { + if (++block >= N2RNG_BLOCK_LIMIT) + return -EWOULDBLOCK; + __delay(ticks); + } else if (hv_err == HV_ENOACCESS) { + return -EPERM; + } else if (hv_err == HV_EIO) { + if (++hcheck >= N2RNG_HCHECK_LIMIT) + return -EIO; + udelay(10000); + } else + return -ENODEV; + } +} + +static unsigned long n2rng_read_diag_data_one(struct n2rng *np, + unsigned long unit, + unsigned long data_ra, + unsigned long data_len, + unsigned long *ticks) +{ + unsigned long hv_err; + + if (np->hvapi_major == 1) { + hv_err = sun4v_rng_data_read_diag_v1(data_ra, data_len, ticks); + } else { + hv_err = sun4v_rng_data_read_diag_v2(data_ra, data_len, + unit, ticks); + if (!*ticks) + *ticks = N2RNG_ACCUM_CYCLES_DEFAULT; + } + return hv_err; +} + +static int n2rng_generic_read_diag_data(struct n2rng *np, + unsigned long unit, + unsigned long data_ra, + unsigned long data_len) +{ + unsigned long ticks, hv_err; + int block = 0; + + while (1) { + hv_err = n2rng_read_diag_data_one(np, unit, + data_ra, data_len, + &ticks); + if (hv_err == HV_EOK) + return 0; + + if (hv_err == HV_EWOULDBLOCK) { + if (++block >= N2RNG_BLOCK_LIMIT) + return -EWOULDBLOCK; + __delay(ticks); + } else if (hv_err == HV_ENOACCESS) { + return -EPERM; + } else if (hv_err == HV_EIO) { + return -EIO; + } else + return -ENODEV; + } +} + + +static int n2rng_generic_write_control(struct n2rng *np, + unsigned long control_ra, + unsigned long unit, + unsigned long state) +{ + unsigned long hv_err, ticks; + int block = 0, busy = 0; + + while (1) { + hv_err = n2rng_write_ctl_one(np, unit, state, control_ra, + np->wd_timeo, &ticks); + if (hv_err == HV_EOK) + return 0; + + if (hv_err == HV_EWOULDBLOCK) { + if (++block >= N2RNG_BLOCK_LIMIT) + return -EWOULDBLOCK; + __delay(ticks); + } else if (hv_err == HV_EBUSY) { + if (++busy >= N2RNG_BUSY_LIMIT) + return -EBUSY; + udelay(1); + } else + return -ENODEV; + } +} + +/* Just try to see if we can successfully access the control register + * of the RNG on the domain on which we are currently executing. + */ +static int n2rng_try_read_ctl(struct n2rng *np) +{ + unsigned long hv_err; + unsigned long x; + + if (np->hvapi_major == 1) { + hv_err = sun4v_rng_get_diag_ctl(); + } else { + /* We purposefully give invalid arguments, HV_NOACCESS + * is higher priority than the errors we'd get from + * these other cases, and that's the error we are + * truly interested in. + */ + hv_err = sun4v_rng_ctl_read_v2(0UL, ~0UL, &x, &x, &x, &x); + switch (hv_err) { + case HV_EWOULDBLOCK: + case HV_ENOACCESS: + break; + default: + hv_err = HV_EOK; + break; + } + } + + return n2rng_hv_err_trans(hv_err); +} + +#define CONTROL_DEFAULT_BASE \ + ((2 << RNG_CTL_ASEL_SHIFT) | \ + (N2RNG_ACCUM_CYCLES_DEFAULT << RNG_CTL_WAIT_SHIFT) | \ + RNG_CTL_LFSR) + +#define CONTROL_DEFAULT_0 \ + (CONTROL_DEFAULT_BASE | \ + (1 << RNG_CTL_VCO_SHIFT) | \ + RNG_CTL_ES1) +#define CONTROL_DEFAULT_1 \ + (CONTROL_DEFAULT_BASE | \ + (2 << RNG_CTL_VCO_SHIFT) | \ + RNG_CTL_ES2) +#define CONTROL_DEFAULT_2 \ + (CONTROL_DEFAULT_BASE | \ + (3 << RNG_CTL_VCO_SHIFT) | \ + RNG_CTL_ES3) +#define CONTROL_DEFAULT_3 \ + (CONTROL_DEFAULT_BASE | \ + RNG_CTL_ES1 | RNG_CTL_ES2 | RNG_CTL_ES3) + +static void n2rng_control_swstate_init(struct n2rng *np) +{ + int i; + + np->flags |= N2RNG_FLAG_CONTROL; + + np->health_check_sec = N2RNG_HEALTH_CHECK_SEC_DEFAULT; + np->accum_cycles = N2RNG_ACCUM_CYCLES_DEFAULT; + np->wd_timeo = N2RNG_WD_TIMEO_DEFAULT; + + for (i = 0; i < np->num_units; i++) { + struct n2rng_unit *up = &np->units[i]; + + up->control[0] = CONTROL_DEFAULT_0; + up->control[1] = CONTROL_DEFAULT_1; + up->control[2] = CONTROL_DEFAULT_2; + up->control[3] = CONTROL_DEFAULT_3; + } + + np->hv_state = HV_RNG_STATE_UNCONFIGURED; +} + +static int n2rng_grab_diag_control(struct n2rng *np) +{ + int i, busy_count, err = -ENODEV; + + busy_count = 0; + for (i = 0; i < 100; i++) { + err = n2rng_try_read_ctl(np); + if (err != -EAGAIN) + break; + + if (++busy_count > 100) { + dev_err(&np->op->dev, + "Grab diag control timeout.\n"); + return -ENODEV; + } + + udelay(1); + } + + return err; +} + +static int n2rng_init_control(struct n2rng *np) +{ + int err = n2rng_grab_diag_control(np); + + /* Not in the control domain, that's OK we are only a consumer + * of the RNG data, we don't setup and program it. + */ + if (err == -EPERM) + return 0; + if (err) + return err; + + n2rng_control_swstate_init(np); + + return 0; +} + +static int n2rng_data_read(struct hwrng *rng, u32 *data) +{ + struct n2rng *np = (struct n2rng *) rng->priv; + unsigned long ra = __pa(&np->test_data); + int len; + + if (!(np->flags & N2RNG_FLAG_READY)) { + len = 0; + } else if (np->flags & N2RNG_FLAG_BUFFER_VALID) { + np->flags &= ~N2RNG_FLAG_BUFFER_VALID; + *data = np->buffer; + len = 4; + } else { + int err = n2rng_generic_read_data(ra); + if (!err) { + np->buffer = np->test_data >> 32; + *data = np->test_data & 0xffffffff; + len = 4; + } else { + dev_err(&np->op->dev, "RNG error, restesting\n"); + np->flags &= ~N2RNG_FLAG_READY; + if (!(np->flags & N2RNG_FLAG_SHUTDOWN)) + schedule_delayed_work(&np->work, 0); + len = 0; + } + } + + return len; +} + +/* On a guest node, just make sure we can read random data properly. + * If a control node reboots or reloads it's n2rng driver, this won't + * work during that time. So we have to keep probing until the device + * becomes usable. + */ +static int n2rng_guest_check(struct n2rng *np) +{ + unsigned long ra = __pa(&np->test_data); + + return n2rng_generic_read_data(ra); +} + +static int n2rng_entropy_diag_read(struct n2rng *np, unsigned long unit, + u64 *pre_control, u64 pre_state, + u64 *buffer, unsigned long buf_len, + u64 *post_control, u64 post_state) +{ + unsigned long post_ctl_ra = __pa(post_control); + unsigned long pre_ctl_ra = __pa(pre_control); + unsigned long buffer_ra = __pa(buffer); + int err; + + err = n2rng_generic_write_control(np, pre_ctl_ra, unit, pre_state); + if (err) + return err; + + err = n2rng_generic_read_diag_data(np, unit, + buffer_ra, buf_len); + + (void) n2rng_generic_write_control(np, post_ctl_ra, unit, + post_state); + + return err; +} + +static u64 advance_polynomial(u64 poly, u64 val, int count) +{ + int i; + + for (i = 0; i < count; i++) { + int highbit_set = ((s64)val < 0); + + val <<= 1; + if (highbit_set) + val ^= poly; + } + + return val; +} + +static int n2rng_test_buffer_find(struct n2rng *np, u64 val) +{ + int i, count = 0; + + /* Purposefully skip over the first word. */ + for (i = 1; i < SELFTEST_BUFFER_WORDS; i++) { + if (np->test_buffer[i] == val) + count++; + } + return count; +} + +static void n2rng_dump_test_buffer(struct n2rng *np) +{ + int i; + + for (i = 0; i < SELFTEST_BUFFER_WORDS; i++) + dev_err(&np->op->dev, "Test buffer slot %d [0x%016lx]\n", + i, np->test_buffer[i]); +} + +static int n2rng_check_selftest_buffer(struct n2rng *np, unsigned long unit) +{ + u64 val = SELFTEST_VAL; + int err, matches, limit; + + matches = 0; + for (limit = 0; limit < SELFTEST_LOOPS_MAX; limit++) { + matches += n2rng_test_buffer_find(np, val); + if (matches >= SELFTEST_MATCH_GOAL) + break; + val = advance_polynomial(SELFTEST_POLY, val, 1); + } + + err = 0; + if (limit >= SELFTEST_LOOPS_MAX) { + err = -ENODEV; + dev_err(&np->op->dev, "Selftest failed on unit %lu\n", unit); + n2rng_dump_test_buffer(np); + } else + dev_info(&np->op->dev, "Selftest passed on unit %lu\n", unit); + + return err; +} + +static int n2rng_control_selftest(struct n2rng *np, unsigned long unit) +{ + int err; + + np->test_control[0] = (0x2 << RNG_CTL_ASEL_SHIFT); + np->test_control[1] = (0x2 << RNG_CTL_ASEL_SHIFT); + np->test_control[2] = (0x2 << RNG_CTL_ASEL_SHIFT); + np->test_control[3] = ((0x2 << RNG_CTL_ASEL_SHIFT) | + RNG_CTL_LFSR | + ((SELFTEST_TICKS - 2) << RNG_CTL_WAIT_SHIFT)); + + + err = n2rng_entropy_diag_read(np, unit, np->test_control, + HV_RNG_STATE_HEALTHCHECK, + np->test_buffer, + sizeof(np->test_buffer), + &np->units[unit].control[0], + np->hv_state); + if (err) + return err; + + return n2rng_check_selftest_buffer(np, unit); +} + +static int n2rng_control_check(struct n2rng *np) +{ + int i; + + for (i = 0; i < np->num_units; i++) { + int err = n2rng_control_selftest(np, i); + if (err) + return err; + } + return 0; +} + +/* The sanity checks passed, install the final configuration into the + * chip, it's ready to use. + */ +static int n2rng_control_configure_units(struct n2rng *np) +{ + int unit, err; + + err = 0; + for (unit = 0; unit < np->num_units; unit++) { + struct n2rng_unit *up = &np->units[unit]; + unsigned long ctl_ra = __pa(&up->control[0]); + int esrc; + u64 base; + + base = ((np->accum_cycles << RNG_CTL_WAIT_SHIFT) | + (2 << RNG_CTL_ASEL_SHIFT) | + RNG_CTL_LFSR); + + /* XXX This isn't the best. We should fetch a bunch + * XXX of words using each entropy source combined XXX + * with each VCO setting, and see which combinations + * XXX give the best random data. + */ + for (esrc = 0; esrc < 3; esrc++) + up->control[esrc] = base | + (esrc << RNG_CTL_VCO_SHIFT) | + (RNG_CTL_ES1 << esrc); + + up->control[3] = base | + (RNG_CTL_ES1 | RNG_CTL_ES2 | RNG_CTL_ES3); + + err = n2rng_generic_write_control(np, ctl_ra, unit, + HV_RNG_STATE_CONFIGURED); + if (err) + break; + } + + return err; +} + +static void n2rng_work(struct work_struct *work) +{ + struct n2rng *np = container_of(work, struct n2rng, work.work); + int err = 0; + + if (!(np->flags & N2RNG_FLAG_CONTROL)) { + err = n2rng_guest_check(np); + } else { + preempt_disable(); + err = n2rng_control_check(np); + preempt_enable(); + + if (!err) + err = n2rng_control_configure_units(np); + } + + if (!err) { + np->flags |= N2RNG_FLAG_READY; + dev_info(&np->op->dev, "RNG ready\n"); + } + + if (err && !(np->flags & N2RNG_FLAG_SHUTDOWN)) + schedule_delayed_work(&np->work, HZ * 2); +} + +static void __devinit n2rng_driver_version(void) +{ + static int n2rng_version_printed; + + if (n2rng_version_printed++ == 0) + pr_info("%s", version); +} + +static int __devinit n2rng_probe(struct of_device *op, + const struct of_device_id *match) +{ + int victoria_falls = (match->data != NULL); + int err = -ENOMEM; + struct n2rng *np; + + n2rng_driver_version(); + + np = kzalloc(sizeof(*np), GFP_KERNEL); + if (!np) + goto out; + np->op = op; + + INIT_DELAYED_WORK(&np->work, n2rng_work); + + if (victoria_falls) + np->flags |= N2RNG_FLAG_VF; + + err = -ENODEV; + np->hvapi_major = 2; + if (sun4v_hvapi_register(HV_GRP_RNG, + np->hvapi_major, + &np->hvapi_minor)) { + np->hvapi_major = 1; + if (sun4v_hvapi_register(HV_GRP_RNG, + np->hvapi_major, + &np->hvapi_minor)) { + dev_err(&op->dev, "Cannot register suitable " + "HVAPI version.\n"); + goto out_free; + } + } + + if (np->flags & N2RNG_FLAG_VF) { + if (np->hvapi_major < 2) { + dev_err(&op->dev, "VF RNG requires HVAPI major " + "version 2 or later, got %lu\n", + np->hvapi_major); + goto out_hvapi_unregister; + } + np->num_units = of_getintprop_default(op->node, + "rng-#units", 0); + if (!np->num_units) { + dev_err(&op->dev, "VF RNG lacks rng-#units property\n"); + goto out_hvapi_unregister; + } + } else + np->num_units = 1; + + dev_info(&op->dev, "Registered RNG HVAPI major %lu minor %lu\n", + np->hvapi_major, np->hvapi_minor); + + np->units = kzalloc(sizeof(struct n2rng_unit) * np->num_units, + GFP_KERNEL); + err = -ENOMEM; + if (!np->units) + goto out_hvapi_unregister; + + err = n2rng_init_control(np); + if (err) + goto out_free_units; + + dev_info(&op->dev, "Found %s RNG, units: %d\n", + ((np->flags & N2RNG_FLAG_VF) ? + "Victoria Falls" : "Niagara2"), + np->num_units); + + np->hwrng.name = "n2rng"; + np->hwrng.data_read = n2rng_data_read; + np->hwrng.priv = (unsigned long) np; + + err = hwrng_register(&np->hwrng); + if (err) + goto out_free_units; + + dev_set_drvdata(&op->dev, np); + + schedule_delayed_work(&np->work, 0); + + return 0; + +out_free_units: + kfree(np->units); + np->units = NULL; + +out_hvapi_unregister: + sun4v_hvapi_unregister(HV_GRP_RNG); + +out_free: + kfree(np); +out: + return err; +} + +static int __devexit n2rng_remove(struct of_device *op) +{ + struct n2rng *np = dev_get_drvdata(&op->dev); + + np->flags |= N2RNG_FLAG_SHUTDOWN; + + cancel_delayed_work_sync(&np->work); + + hwrng_unregister(&np->hwrng); + + sun4v_hvapi_unregister(HV_GRP_RNG); + + kfree(np->units); + np->units = NULL; + + kfree(np); + + dev_set_drvdata(&op->dev, NULL); + + return 0; +} + +static struct of_device_id n2rng_match[] = { + { + .name = "random-number-generator", + .compatible = "SUNW,n2-rng", + }, + { + .name = "random-number-generator", + .compatible = "SUNW,vf-rng", + .data = (void *) 1, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, n2rng_match); + +static struct of_platform_driver n2rng_driver = { + .name = "n2rng", + .match_table = n2rng_match, + .probe = n2rng_probe, + .remove = __devexit_p(n2rng_remove), +}; + +static int __init n2rng_init(void) +{ + return of_register_driver(&n2rng_driver, &of_bus_type); +} + +static void __exit n2rng_exit(void) +{ + of_unregister_driver(&n2rng_driver); +} + +module_init(n2rng_init); +module_exit(n2rng_exit); diff --git a/drivers/char/hw_random/n2rng.h b/drivers/char/hw_random/n2rng.h new file mode 100644 index 000000000000..a2b81e7bfc18 --- /dev/null +++ b/drivers/char/hw_random/n2rng.h @@ -0,0 +1,118 @@ +/* n2rng.h: Niagara2 RNG defines. + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ + +#ifndef _N2RNG_H +#define _N2RNG_H + +#define RNG_CTL_WAIT 0x0000000001fffe00ULL /* Minimum wait time */ +#define RNG_CTL_WAIT_SHIFT 9 +#define RNG_CTL_BYPASS 0x0000000000000100ULL /* VCO voltage source */ +#define RNG_CTL_VCO 0x00000000000000c0ULL /* VCO rate control */ +#define RNG_CTL_VCO_SHIFT 6 +#define RNG_CTL_ASEL 0x0000000000000030ULL /* Analog MUX select */ +#define RNG_CTL_ASEL_SHIFT 4 +#define RNG_CTL_LFSR 0x0000000000000008ULL /* Use LFSR or plain shift */ +#define RNG_CTL_ES3 0x0000000000000004ULL /* Enable entropy source 3 */ +#define RNG_CTL_ES2 0x0000000000000002ULL /* Enable entropy source 2 */ +#define RNG_CTL_ES1 0x0000000000000001ULL /* Enable entropy source 1 */ + +#define HV_FAST_RNG_GET_DIAG_CTL 0x130 +#define HV_FAST_RNG_CTL_READ 0x131 +#define HV_FAST_RNG_CTL_WRITE 0x132 +#define HV_FAST_RNG_DATA_READ_DIAG 0x133 +#define HV_FAST_RNG_DATA_READ 0x134 + +#define HV_RNG_STATE_UNCONFIGURED 0 +#define HV_RNG_STATE_CONFIGURED 1 +#define HV_RNG_STATE_HEALTHCHECK 2 +#define HV_RNG_STATE_ERROR 3 + +#define HV_RNG_NUM_CONTROL 4 + +#ifndef __ASSEMBLY__ +extern unsigned long sun4v_rng_get_diag_ctl(void); +extern unsigned long sun4v_rng_ctl_read_v1(unsigned long ctl_regs_ra, + unsigned long *state, + unsigned long *tick_delta); +extern unsigned long sun4v_rng_ctl_read_v2(unsigned long ctl_regs_ra, + unsigned long unit, + unsigned long *state, + unsigned long *tick_delta, + unsigned long *watchdog, + unsigned long *write_status); +extern unsigned long sun4v_rng_ctl_write_v1(unsigned long ctl_regs_ra, + unsigned long state, + unsigned long write_timeout, + unsigned long *tick_delta); +extern unsigned long sun4v_rng_ctl_write_v2(unsigned long ctl_regs_ra, + unsigned long state, + unsigned long write_timeout, + unsigned long unit); +extern unsigned long sun4v_rng_data_read_diag_v1(unsigned long data_ra, + unsigned long len, + unsigned long *tick_delta); +extern unsigned long sun4v_rng_data_read_diag_v2(unsigned long data_ra, + unsigned long len, + unsigned long unit, + unsigned long *tick_delta); +extern unsigned long sun4v_rng_data_read(unsigned long data_ra, + unsigned long *tick_delta); + +struct n2rng_unit { + u64 control[HV_RNG_NUM_CONTROL]; +}; + +struct n2rng { + struct of_device *op; + + unsigned long flags; +#define N2RNG_FLAG_VF 0x00000001 /* Victoria Falls RNG, else N2 */ +#define N2RNG_FLAG_CONTROL 0x00000002 /* Operating in control domain */ +#define N2RNG_FLAG_READY 0x00000008 /* Ready for hw-rng layer */ +#define N2RNG_FLAG_SHUTDOWN 0x00000010 /* Driver unregistering */ +#define N2RNG_FLAG_BUFFER_VALID 0x00000020 /* u32 buffer holds valid data */ + + int num_units; + struct n2rng_unit *units; + + struct hwrng hwrng; + u32 buffer; + + /* Registered hypervisor group API major and minor version. */ + unsigned long hvapi_major; + unsigned long hvapi_minor; + + struct delayed_work work; + + unsigned long hv_state; /* HV_RNG_STATE_foo */ + + unsigned long health_check_sec; + unsigned long accum_cycles; + unsigned long wd_timeo; +#define N2RNG_HEALTH_CHECK_SEC_DEFAULT 0 +#define N2RNG_ACCUM_CYCLES_DEFAULT 2048 +#define N2RNG_WD_TIMEO_DEFAULT 0 + + u64 scratch_control[HV_RNG_NUM_CONTROL]; + +#define SELFTEST_TICKS 38859 +#define SELFTEST_VAL ((u64)0xB8820C7BD387E32C) +#define SELFTEST_POLY ((u64)0x231DCEE91262B8A3) +#define SELFTEST_MATCH_GOAL 6 +#define SELFTEST_LOOPS_MAX 40000 +#define SELFTEST_BUFFER_WORDS 8 + + u64 test_data; + u64 test_control[HV_RNG_NUM_CONTROL]; + u64 test_buffer[SELFTEST_BUFFER_WORDS]; +}; + +#define N2RNG_BLOCK_LIMIT 60000 +#define N2RNG_BUSY_LIMIT 100 +#define N2RNG_HCHECK_LIMIT 100 + +#endif /* !(__ASSEMBLY__) */ + +#endif /* _N2RNG_H */ diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c index 5dc74404058f..689f9dcd3b86 100644 --- a/drivers/char/ip2/ip2main.c +++ b/drivers/char/ip2/ip2main.c @@ -203,7 +203,7 @@ static int set_serial_info(i2ChanStrPtr, struct serial_struct __user *); static ssize_t ip2_ipl_read(struct file *, char __user *, size_t, loff_t *); static ssize_t ip2_ipl_write(struct file *, const char __user *, size_t, loff_t *); -static int ip2_ipl_ioctl(struct inode *, struct file *, UINT, ULONG); +static long ip2_ipl_ioctl(struct file *, UINT, ULONG); static int ip2_ipl_open(struct inode *, struct file *); static int DumpTraceBuffer(char __user *, int); @@ -236,7 +236,7 @@ static const struct file_operations ip2_ipl = { .owner = THIS_MODULE, .read = ip2_ipl_read, .write = ip2_ipl_write, - .ioctl = ip2_ipl_ioctl, + .unlocked_ioctl = ip2_ipl_ioctl, .open = ip2_ipl_open, }; @@ -718,12 +718,12 @@ ip2_loadmain(int *iop, int *irqp) } if ( NULL != ( pB = i2BoardPtrTable[i] ) ) { - device_create(ip2_class, NULL, - MKDEV(IP2_IPL_MAJOR, 4 * i), - "ipl%d", i); - device_create(ip2_class, NULL, - MKDEV(IP2_IPL_MAJOR, 4 * i + 1), - "stat%d", i); + device_create_drvdata(ip2_class, NULL, + MKDEV(IP2_IPL_MAJOR, 4 * i), + NULL, "ipl%d", i); + device_create_drvdata(ip2_class, NULL, + MKDEV(IP2_IPL_MAJOR, 4 * i + 1), + NULL, "stat%d", i); for ( box = 0; box < ABS_MAX_BOXES; ++box ) { @@ -2845,10 +2845,10 @@ ip2_ipl_write(struct file *pFile, const char __user *pData, size_t count, loff_t /* */ /* */ /******************************************************************************/ -static int -ip2_ipl_ioctl ( struct inode *pInode, struct file *pFile, UINT cmd, ULONG arg ) +static long +ip2_ipl_ioctl (struct file *pFile, UINT cmd, ULONG arg ) { - unsigned int iplminor = iminor(pInode); + unsigned int iplminor = iminor(pFile->f_path.dentry->d_inode); int rc = 0; void __user *argp = (void __user *)arg; ULONG __user *pIndex = argp; @@ -2859,6 +2859,8 @@ ip2_ipl_ioctl ( struct inode *pInode, struct file *pFile, UINT cmd, ULONG arg ) printk (KERN_DEBUG "IP2IPL: ioctl cmd %d, arg %ld\n", cmd, arg ); #endif + lock_kernel(); + switch ( iplminor ) { case 0: // IPL device rc = -EINVAL; @@ -2919,6 +2921,7 @@ ip2_ipl_ioctl ( struct inode *pInode, struct file *pFile, UINT cmd, ULONG arg ) rc = -ENODEV; break; } + unlock_kernel(); return rc; } diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index c11a40483459..64e1c169e826 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -871,7 +871,7 @@ static void ipmi_new_smi(int if_num, struct device *device) entry->dev = dev; mutex_lock(®_list_mutex); - device_create(ipmi_class, device, dev, "ipmi%d", if_num); + device_create_drvdata(ipmi_class, device, dev, NULL, "ipmi%d", if_num); list_add(&entry->link, ®_list); mutex_unlock(®_list_mutex); } diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index d4281df10c22..8f7cc190b62d 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -1181,14 +1181,17 @@ static int isicom_chars_in_buffer(struct tty_struct *tty) } /* ioctl et all */ -static inline void isicom_send_break(struct isi_port *port, - unsigned long length) +static int isicom_send_break(struct tty_struct *tty, int length) { + struct isi_port *port = tty->driver_data; struct isi_board *card = port->card; unsigned long base = card->base; + if (length == -1) + return -EOPNOTSUPP; + if (!lock_card(card)) - return; + return -EINVAL; outw(0x8000 | ((port->channel) << (card->shift_count)) | 0x3, base); outw((length & 0xff) << 8 | 0x00, base); @@ -1196,6 +1199,7 @@ static inline void isicom_send_break(struct isi_port *port, InterruptTheCard(base); unlock_card(card); + return 0; } static int isicom_tiocmget(struct tty_struct *tty, struct file *file) @@ -1305,28 +1309,11 @@ static int isicom_ioctl(struct tty_struct *tty, struct file *filp, { struct isi_port *port = tty->driver_data; void __user *argp = (void __user *)arg; - int retval; if (isicom_paranoia_check(port, tty->name, "isicom_ioctl")) return -ENODEV; switch (cmd) { - case TCSBRK: - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (!arg) - isicom_send_break(port, HZ/4); - return 0; - - case TCSBRKP: - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - isicom_send_break(port, arg ? arg * (HZ/10) : HZ/4); - return 0; case TIOCGSERIAL: return isicom_get_serial_info(port, argp); @@ -1459,6 +1446,7 @@ static const struct tty_operations isicom_ops = { .flush_buffer = isicom_flush_buffer, .tiocmget = isicom_tiocmget, .tiocmset = isicom_tiocmset, + .break_ctl = isicom_send_break, }; static int __devinit reset_card(struct pci_dev *pdev, @@ -1832,7 +1820,7 @@ static int __init isicom_init(void) isicom_normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; isicom_normal->flags = TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV; + TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK; tty_set_operations(isicom_normal, &isicom_ops); retval = tty_register_driver(isicom_normal); diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 6ef1c565705c..843a2afaf204 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -598,7 +598,7 @@ static int stli_parsebrd(struct stlconf *confp, char **argp); static int stli_open(struct tty_struct *tty, struct file *filp); static void stli_close(struct tty_struct *tty, struct file *filp); static int stli_write(struct tty_struct *tty, const unsigned char *buf, int count); -static void stli_putchar(struct tty_struct *tty, unsigned char ch); +static int stli_putchar(struct tty_struct *tty, unsigned char ch); static void stli_flushchars(struct tty_struct *tty); static int stli_writeroom(struct tty_struct *tty); static int stli_charsinbuffer(struct tty_struct *tty); @@ -609,7 +609,7 @@ static void stli_unthrottle(struct tty_struct *tty); static void stli_stop(struct tty_struct *tty); static void stli_start(struct tty_struct *tty); static void stli_flushbuffer(struct tty_struct *tty); -static void stli_breakctl(struct tty_struct *tty, int state); +static int stli_breakctl(struct tty_struct *tty, int state); static void stli_waituntilsent(struct tty_struct *tty, int timeout); static void stli_sendxchar(struct tty_struct *tty, char ch); static void stli_hangup(struct tty_struct *tty); @@ -826,7 +826,7 @@ static int stli_open(struct tty_struct *tty, struct file *filp) */ portp->port.tty = tty; tty->driver_data = portp; - portp->refcount++; + portp->port.count++; wait_event_interruptible(portp->raw_wait, !test_bit(ST_INITIALIZING, &portp->state)); @@ -888,9 +888,9 @@ static void stli_close(struct tty_struct *tty, struct file *filp) spin_unlock_irqrestore(&stli_lock, flags); return; } - if ((tty->count == 1) && (portp->refcount != 1)) - portp->refcount = 1; - if (portp->refcount-- > 1) { + if ((tty->count == 1) && (portp->port.count != 1)) + portp->port.count = 1; + if (portp->port.count-- > 1) { spin_unlock_irqrestore(&stli_lock, flags); return; } @@ -925,8 +925,7 @@ static void stli_close(struct tty_struct *tty, struct file *filp) clear_bit(ST_TXBUSY, &portp->state); clear_bit(ST_RXSTOP, &portp->state); set_bit(TTY_IO_ERROR, &tty->flags); - if (tty->ldisc.flush_buffer) - (tty->ldisc.flush_buffer)(tty); + tty_ldisc_flush(tty); set_bit(ST_DOFLUSHRX, &portp->state); stli_flushbuffer(tty); @@ -1202,7 +1201,7 @@ static int stli_waitcarrier(struct stlibrd *brdp, struct stliport *portp, struct spin_lock_irqsave(&stli_lock, flags); portp->openwaitcnt++; if (! tty_hung_up_p(filp)) - portp->refcount--; + portp->port.count--; spin_unlock_irqrestore(&stli_lock, flags); for (;;) { @@ -1231,7 +1230,7 @@ static int stli_waitcarrier(struct stlibrd *brdp, struct stliport *portp, struct spin_lock_irqsave(&stli_lock, flags); if (! tty_hung_up_p(filp)) - portp->refcount++; + portp->port.count++; portp->openwaitcnt--; spin_unlock_irqrestore(&stli_lock, flags); @@ -1333,7 +1332,7 @@ static int stli_write(struct tty_struct *tty, const unsigned char *buf, int coun * first them do the new ports. */ -static void stli_putchar(struct tty_struct *tty, unsigned char ch) +static int stli_putchar(struct tty_struct *tty, unsigned char ch) { if (tty != stli_txcooktty) { if (stli_txcooktty != NULL) @@ -1342,6 +1341,7 @@ static void stli_putchar(struct tty_struct *tty, unsigned char ch) } stli_txcookbuf[stli_txcooksize++] = ch; + return 0; } /*****************************************************************************/ @@ -1660,7 +1660,6 @@ static int stli_ioctl(struct tty_struct *tty, struct file *file, unsigned int cm { struct stliport *portp; struct stlibrd *brdp; - unsigned int ival; int rc; void __user *argp = (void __user *)arg; @@ -1857,7 +1856,7 @@ static void stli_hangup(struct tty_struct *tty) set_bit(TTY_IO_ERROR, &tty->flags); portp->port.tty = NULL; portp->port.flags &= ~ASYNC_NORMAL_ACTIVE; - portp->refcount = 0; + portp->port.count = 0; spin_unlock_irqrestore(&stli_lock, flags); wake_up_interruptible(&portp->port.open_wait); @@ -1909,7 +1908,7 @@ static void stli_flushbuffer(struct tty_struct *tty) /*****************************************************************************/ -static void stli_breakctl(struct tty_struct *tty, int state) +static int stli_breakctl(struct tty_struct *tty, int state) { struct stlibrd *brdp; struct stliport *portp; @@ -1917,15 +1916,16 @@ static void stli_breakctl(struct tty_struct *tty, int state) portp = tty->driver_data; if (portp == NULL) - return; + return -EINVAL; if (portp->brdnr >= stli_nrbrds) - return; + return -EINVAL; brdp = stli_brds[portp->brdnr]; if (brdp == NULL) - return; + return -EINVAL; arg = (state == -1) ? BREAKON : BREAKOFF; stli_cmdwait(brdp, portp, A_BREAK, &arg, sizeof(long), 0); + return 0; } /*****************************************************************************/ @@ -4246,7 +4246,7 @@ static int stli_portcmdstats(struct stliport *portp) stli_comstats.panel = portp->panelnr; stli_comstats.port = portp->portnr; stli_comstats.state = portp->state; - stli_comstats.flags = portp->port.flag; + stli_comstats.flags = portp->port.flags; spin_lock_irqsave(&brd_lock, flags); if (portp->port.tty != NULL) { @@ -4599,8 +4599,9 @@ static int __init istallion_module_init(void) istallion_class = class_create(THIS_MODULE, "staliomem"); for (i = 0; i < 4; i++) - device_create(istallion_class, NULL, MKDEV(STL_SIOMEMMAJOR, i), - "staliomem%d", i); + device_create_drvdata(istallion_class, NULL, + MKDEV(STL_SIOMEMMAJOR, i), + NULL, "staliomem%d", i); return 0; err_deinit: diff --git a/drivers/char/lcd.c b/drivers/char/lcd.c deleted file mode 100644 index 1c29b20e4f4c..000000000000 --- a/drivers/char/lcd.c +++ /dev/null @@ -1,516 +0,0 @@ -/* - * LCD, LED and Button interface for Cobalt - * - * 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. - * - * Copyright (C) 1996, 1997 by Andrew Bose - * - * Linux kernel version history: - * March 2001: Ported from 2.0.34 by Liam Davies - * - */ -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/miscdevice.h> -#include <linux/slab.h> -#include <linux/ioport.h> -#include <linux/fcntl.h> -#include <linux/mc146818rtc.h> -#include <linux/netdevice.h> -#include <linux/sched.h> -#include <linux/smp_lock.h> -#include <linux/delay.h> - -#include <asm/io.h> -#include <asm/uaccess.h> -#include <asm/system.h> - -#include "lcd.h" - -static int lcd_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); - -static unsigned int lcd_present = 1; - -/* used in arch/mips/cobalt/reset.c */ -int led_state = 0; - -#if defined(CONFIG_TULIP) && 0 - -#define MAX_INTERFACES 8 -static linkcheck_func_t linkcheck_callbacks[MAX_INTERFACES]; -static void *linkcheck_cookies[MAX_INTERFACES]; - -int lcd_register_linkcheck_func(int iface_num, void *func, void *cookie) -{ - if (iface_num < 0 || - iface_num >= MAX_INTERFACES || - linkcheck_callbacks[iface_num] != NULL) - return -1; - linkcheck_callbacks[iface_num] = (linkcheck_func_t) func; - linkcheck_cookies[iface_num] = cookie; - return 0; -} -#endif - -static int lcd_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct lcd_display button_display; - unsigned long address, a; - - switch (cmd) { - case LCD_On: - udelay(150); - BusyCheck(); - LCDWriteInst(0x0F); - break; - - case LCD_Off: - udelay(150); - BusyCheck(); - LCDWriteInst(0x08); - break; - - case LCD_Reset: - udelay(150); - LCDWriteInst(0x3F); - udelay(150); - LCDWriteInst(0x3F); - udelay(150); - LCDWriteInst(0x3F); - udelay(150); - LCDWriteInst(0x3F); - udelay(150); - LCDWriteInst(0x01); - udelay(150); - LCDWriteInst(0x06); - break; - - case LCD_Clear: - udelay(150); - BusyCheck(); - LCDWriteInst(0x01); - break; - - case LCD_Cursor_Left: - udelay(150); - BusyCheck(); - LCDWriteInst(0x10); - break; - - case LCD_Cursor_Right: - udelay(150); - BusyCheck(); - LCDWriteInst(0x14); - break; - - case LCD_Cursor_Off: - udelay(150); - BusyCheck(); - LCDWriteInst(0x0C); - break; - - case LCD_Cursor_On: - udelay(150); - BusyCheck(); - LCDWriteInst(0x0F); - break; - - case LCD_Blink_Off: - udelay(150); - BusyCheck(); - LCDWriteInst(0x0E); - break; - - case LCD_Get_Cursor_Pos:{ - struct lcd_display display; - - udelay(150); - BusyCheck(); - display.cursor_address = (LCDReadInst); - display.cursor_address = - (display.cursor_address & 0x07F); - if (copy_to_user - ((struct lcd_display *) arg, &display, - sizeof(struct lcd_display))) - return -EFAULT; - - break; - } - - - case LCD_Set_Cursor_Pos:{ - struct lcd_display display; - - if (copy_from_user - (&display, (struct lcd_display *) arg, - sizeof(struct lcd_display))) - return -EFAULT; - - a = (display.cursor_address | kLCD_Addr); - - udelay(150); - BusyCheck(); - LCDWriteInst(a); - - break; - } - - case LCD_Get_Cursor:{ - struct lcd_display display; - - udelay(150); - BusyCheck(); - display.character = LCDReadData; - - if (copy_to_user - ((struct lcd_display *) arg, &display, - sizeof(struct lcd_display))) - return -EFAULT; - udelay(150); - BusyCheck(); - LCDWriteInst(0x10); - - break; - } - - case LCD_Set_Cursor:{ - struct lcd_display display; - - if (copy_from_user - (&display, (struct lcd_display *) arg, - sizeof(struct lcd_display))) - return -EFAULT; - - udelay(150); - BusyCheck(); - LCDWriteData(display.character); - udelay(150); - BusyCheck(); - LCDWriteInst(0x10); - - break; - } - - - case LCD_Disp_Left: - udelay(150); - BusyCheck(); - LCDWriteInst(0x18); - break; - - case LCD_Disp_Right: - udelay(150); - BusyCheck(); - LCDWriteInst(0x1C); - break; - - case LCD_Home: - udelay(150); - BusyCheck(); - LCDWriteInst(0x02); - break; - - case LCD_Write:{ - struct lcd_display display; - unsigned int index; - - - if (copy_from_user - (&display, (struct lcd_display *) arg, - sizeof(struct lcd_display))) - return -EFAULT; - - udelay(150); - BusyCheck(); - LCDWriteInst(0x80); - udelay(150); - BusyCheck(); - - for (index = 0; index < (display.size1); index++) { - udelay(150); - BusyCheck(); - LCDWriteData(display.line1[index]); - BusyCheck(); - } - - udelay(150); - BusyCheck(); - LCDWriteInst(0xC0); - udelay(150); - BusyCheck(); - for (index = 0; index < (display.size2); index++) { - udelay(150); - BusyCheck(); - LCDWriteData(display.line2[index]); - } - - break; - } - - case LCD_Read:{ - struct lcd_display display; - - BusyCheck(); - for (address = kDD_R00; address <= kDD_R01; - address++) { - a = (address | kLCD_Addr); - - udelay(150); - BusyCheck(); - LCDWriteInst(a); - udelay(150); - BusyCheck(); - display.line1[address] = LCDReadData; - } - - display.line1[0x27] = '\0'; - - for (address = kDD_R10; address <= kDD_R11; - address++) { - a = (address | kLCD_Addr); - - udelay(150); - BusyCheck(); - LCDWriteInst(a); - - udelay(150); - BusyCheck(); - display.line2[address - 0x40] = - LCDReadData; - } - - display.line2[0x27] = '\0'; - - if (copy_to_user - ((struct lcd_display *) arg, &display, - sizeof(struct lcd_display))) - return -EFAULT; - break; - } - -// set all GPIO leds to led_display.leds - - case LED_Set:{ - struct lcd_display led_display; - - - if (copy_from_user - (&led_display, (struct lcd_display *) arg, - sizeof(struct lcd_display))) - return -EFAULT; - - led_state = led_display.leds; - LEDSet(led_state); - - break; - } - - -// set only bit led_display.leds - - case LED_Bit_Set:{ - unsigned int i; - int bit = 1; - struct lcd_display led_display; - - - if (copy_from_user - (&led_display, (struct lcd_display *) arg, - sizeof(struct lcd_display))) - return -EFAULT; - - for (i = 0; i < (int) led_display.leds; i++) { - bit = 2 * bit; - } - - led_state = led_state | bit; - LEDSet(led_state); - break; - } - -// clear only bit led_display.leds - - case LED_Bit_Clear:{ - unsigned int i; - int bit = 1; - struct lcd_display led_display; - - - if (copy_from_user - (&led_display, (struct lcd_display *) arg, - sizeof(struct lcd_display))) - return -EFAULT; - - for (i = 0; i < (int) led_display.leds; i++) { - bit = 2 * bit; - } - - led_state = led_state & ~bit; - LEDSet(led_state); - break; - } - - - case BUTTON_Read:{ - button_display.buttons = GPIRead; - if (copy_to_user - ((struct lcd_display *) arg, &button_display, - sizeof(struct lcd_display))) - return -EFAULT; - break; - } - - case LINK_Check:{ - button_display.buttons = - *((volatile unsigned long *) (0xB0100060)); - if (copy_to_user - ((struct lcd_display *) arg, &button_display, - sizeof(struct lcd_display))) - return -EFAULT; - break; - } - - case LINK_Check_2:{ - int iface_num; - - /* panel-utils should pass in the desired interface status is wanted for - * in "buttons" of the structure. We will set this to non-zero if the - * link is in fact up for the requested interface. --DaveM - */ - if (copy_from_user - (&button_display, (struct lcd_display *) arg, - sizeof(button_display))) - return -EFAULT; - iface_num = button_display.buttons; -#if defined(CONFIG_TULIP) && 0 - if (iface_num >= 0 && - iface_num < MAX_INTERFACES && - linkcheck_callbacks[iface_num] != NULL) { - button_display.buttons = - linkcheck_callbacks[iface_num] - (linkcheck_cookies[iface_num]); - } else -#endif - button_display.buttons = 0; - - if (__copy_to_user - ((struct lcd_display *) arg, &button_display, - sizeof(struct lcd_display))) - return -EFAULT; - break; - } - - default: - return -EINVAL; - - } - - return 0; - -} - -static int lcd_open(struct inode *inode, struct file *file) -{ - cycle_kernel_lock(); - - if (!lcd_present) - return -ENXIO; - else - return 0; -} - -/* Only RESET or NEXT counts as button pressed */ - -static inline int button_pressed(void) -{ - unsigned long buttons = GPIRead; - - if ((buttons == BUTTON_Next) || (buttons == BUTTON_Next_B) - || (buttons == BUTTON_Reset_B)) - return buttons; - return 0; -} - -/* LED daemon sits on this and we wake him up once a key is pressed. */ - -static int lcd_waiters = 0; - -static ssize_t lcd_read(struct file *file, char *buf, - size_t count, loff_t *ofs) -{ - long buttons_now; - - if (lcd_waiters > 0) - return -EINVAL; - - lcd_waiters++; - while (((buttons_now = (long) button_pressed()) == 0) && - !(signal_pending(current))) { - msleep_interruptible(2000); - } - lcd_waiters--; - - if (signal_pending(current)) - return -ERESTARTSYS; - return buttons_now; -} - -/* - * The various file operations we support. - */ - -static const struct file_operations lcd_fops = { - .read = lcd_read, - .ioctl = lcd_ioctl, - .open = lcd_open, -}; - -static struct miscdevice lcd_dev = { - MISC_DYNAMIC_MINOR, - "lcd", - &lcd_fops -}; - -static int lcd_init(void) -{ - int ret; - unsigned long data; - - pr_info("%s\n", LCD_DRIVER); - ret = misc_register(&lcd_dev); - if (ret) { - printk(KERN_WARNING LCD "Unable to register misc device.\n"); - return ret; - } - - /* Check region? Naaah! Just snarf it up. */ -/* request_region(RTC_PORT(0), RTC_IO_EXTENT, "lcd");*/ - - udelay(150); - data = LCDReadData; - if ((data & 0x000000FF) == (0x00)) { - lcd_present = 0; - pr_info(LCD "LCD Not Present\n"); - } else { - lcd_present = 1; - WRITE_GAL(kGal_DevBank2PReg, kGal_DevBank2Cfg); - WRITE_GAL(kGal_DevBank3PReg, kGal_DevBank3Cfg); - } - - return 0; -} - -static void __exit lcd_exit(void) -{ - misc_deregister(&lcd_dev); -} - -module_init(lcd_init); -module_exit(lcd_exit); - -MODULE_AUTHOR("Andrew Bose"); -MODULE_LICENSE("GPL"); diff --git a/drivers/char/lcd.h b/drivers/char/lcd.h deleted file mode 100644 index 290b3ff23b03..000000000000 --- a/drivers/char/lcd.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * LED, LCD and Button panel driver for Cobalt - * - * 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. - * - * Copyright (C) 1996, 1997 by Andrew Bose - * - * Linux kernel version history: - * March 2001: Ported from 2.0.34 by Liam Davies - * - */ - -// function headers - -#define LCD_CHARS_PER_LINE 40 -#define MAX_IDLE_TIME 120 - -struct lcd_display { - unsigned buttons; - int size1; - int size2; - unsigned char line1[LCD_CHARS_PER_LINE]; - unsigned char line2[LCD_CHARS_PER_LINE]; - unsigned char cursor_address; - unsigned char character; - unsigned char leds; - unsigned char *RomImage; -}; - - - -#define LCD_DRIVER "Cobalt LCD Driver v2.10" - -#define LCD "lcd: " - -#define kLCD_IR 0x0F000000 -#define kLCD_DR 0x0F000010 -#define kGPI 0x0D000000 -#define kLED 0x0C000000 - -#define kDD_R00 0x00 -#define kDD_R01 0x27 -#define kDD_R10 0x40 -#define kDD_R11 0x67 - -#define kLCD_Addr 0x00000080 - -#define LCDTimeoutValue 0xfff - - -// Macros - -#define LCDWriteData(x) outl((x << 24), kLCD_DR) -#define LCDWriteInst(x) outl((x << 24), kLCD_IR) - -#define LCDReadData (inl(kLCD_DR) >> 24) -#define LCDReadInst (inl(kLCD_IR) >> 24) - -#define GPIRead (inl(kGPI) >> 24) - -#define LEDSet(x) outb((char)x, kLED) - -#define WRITE_GAL(x,y) outl(y, 0x04000000 | (x)) -#define BusyCheck() while ((LCDReadInst & 0x80) == 0x80) - - - -/* - * Function command codes for io_ctl. - */ -#define LCD_On 1 -#define LCD_Off 2 -#define LCD_Clear 3 -#define LCD_Reset 4 -#define LCD_Cursor_Left 5 -#define LCD_Cursor_Right 6 -#define LCD_Disp_Left 7 -#define LCD_Disp_Right 8 -#define LCD_Get_Cursor 9 -#define LCD_Set_Cursor 10 -#define LCD_Home 11 -#define LCD_Read 12 -#define LCD_Write 13 -#define LCD_Cursor_Off 14 -#define LCD_Cursor_On 15 -#define LCD_Get_Cursor_Pos 16 -#define LCD_Set_Cursor_Pos 17 -#define LCD_Blink_Off 18 - -#define LED_Set 40 -#define LED_Bit_Set 41 -#define LED_Bit_Clear 42 - - -// Button defs -#define BUTTON_Read 50 - - -// Ethernet LINK check hackaroo -#define LINK_Check 90 -#define LINK_Check_2 91 - -// Button patterns _B - single layer lcd boards - -#define BUTTON_NONE 0x3F -#define BUTTON_NONE_B 0xFE - -#define BUTTON_Left 0x3B -#define BUTTON_Left_B 0xFA - -#define BUTTON_Right 0x37 -#define BUTTON_Right_B 0xDE - -#define BUTTON_Up 0x2F -#define BUTTON_Up_B 0xF6 - -#define BUTTON_Down 0x1F -#define BUTTON_Down_B 0xEE - -#define BUTTON_Next 0x3D -#define BUTTON_Next_B 0x7E - -#define BUTTON_Enter 0x3E -#define BUTTON_Enter_B 0xBE - -#define BUTTON_Reset_B 0xFC - - -// debounce constants - -#define BUTTON_SENSE 160000 -#define BUTTON_DEBOUNCE 5000 - - -// Galileo register stuff - -#define kGal_DevBank2Cfg 0x1466DB33 -#define kGal_DevBank2PReg 0x464 -#define kGal_DevBank3Cfg 0x146FDFFB -#define kGal_DevBank3PReg 0x468 - -// Network - -#define kIPADDR 1 -#define kNETMASK 2 -#define kGATEWAY 3 -#define kDNS 4 - -#define kClassA 5 -#define kClassB 6 -#define kClassC 7 - diff --git a/drivers/char/lp.c b/drivers/char/lp.c index 71abb4c33aa2..3f2719b9f77b 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -813,7 +813,8 @@ static int lp_register(int nr, struct parport *port) if (reset) lp_reset(nr); - device_create(lp_class, port->dev, MKDEV(LP_MAJOR, nr), "lp%d", nr); + device_create_drvdata(lp_class, port->dev, MKDEV(LP_MAJOR, nr), NULL, + "lp%d", nr); printk(KERN_INFO "lp%d: using %s (%s).\n", nr, port->name, (port->irq == PARPORT_IRQ_NONE)?"polling":"interrupt-driven"); diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 070e22e8ea9e..672b08e694d0 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -80,7 +80,7 @@ static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size) } #endif -#ifdef CONFIG_NONPROMISC_DEVMEM +#ifdef CONFIG_STRICT_DEVMEM static inline int range_is_allowed(unsigned long pfn, unsigned long size) { u64 from = ((u64)pfn) << PAGE_SHIFT; @@ -327,7 +327,10 @@ static void mmap_mem_close(struct vm_area_struct *vma) static struct vm_operations_struct mmap_mem_ops = { .open = mmap_mem_open, - .close = mmap_mem_close + .close = mmap_mem_close, +#ifdef CONFIG_HAVE_IOREMAP_PROT + .access = generic_access_phys +#endif }; static int mmap_mem(struct file * file, struct vm_area_struct * vma) @@ -989,9 +992,9 @@ static int __init chr_dev_init(void) mem_class = class_create(THIS_MODULE, "mem"); for (i = 0; i < ARRAY_SIZE(devlist); i++) - device_create(mem_class, NULL, - MKDEV(MEM_MAJOR, devlist[i].minor), - devlist[i].name); + device_create_drvdata(mem_class, NULL, + MKDEV(MEM_MAJOR, devlist[i].minor), + NULL, devlist[i].name); return 0; } diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 6e1563c3d30a..999aa779c08a 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -217,8 +217,8 @@ int misc_register(struct miscdevice * misc) misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7); dev = MKDEV(MISC_MAJOR, misc->minor); - misc->this_device = device_create(misc_class, misc->parent, dev, - "%s", misc->name); + misc->this_device = device_create_drvdata(misc_class, misc->parent, + dev, NULL, "%s", misc->name); if (IS_ERR(misc->this_device)) { err = PTR_ERR(misc->this_device); goto out; diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c index 192961fd7173..918711aa56f3 100644 --- a/drivers/char/mmtimer.c +++ b/drivers/char/mmtimer.c @@ -32,6 +32,7 @@ #include <linux/interrupt.h> #include <linux/time.h> #include <linux/math64.h> +#include <linux/smp_lock.h> #include <asm/uaccess.h> #include <asm/sn/addrs.h> @@ -57,8 +58,8 @@ extern unsigned long sn_rtc_cycles_per_second; #define rtc_time() (*RTC_COUNTER_ADDR) -static int mmtimer_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); +static long mmtimer_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); static int mmtimer_mmap(struct file *file, struct vm_area_struct *vma); /* @@ -67,9 +68,9 @@ static int mmtimer_mmap(struct file *file, struct vm_area_struct *vma); static unsigned long mmtimer_femtoperiod = 0; static const struct file_operations mmtimer_fops = { - .owner = THIS_MODULE, - .mmap = mmtimer_mmap, - .ioctl = mmtimer_ioctl, + .owner = THIS_MODULE, + .mmap = mmtimer_mmap, + .unlocked_ioctl = mmtimer_ioctl, }; /* @@ -339,7 +340,6 @@ restart: /** * mmtimer_ioctl - ioctl interface for /dev/mmtimer - * @inode: inode of the device * @file: file structure for the device * @cmd: command to execute * @arg: optional argument to command @@ -365,11 +365,13 @@ restart: * %MMTIMER_GETCOUNTER - Gets the current value in the counter and places it * in the address specified by @arg. */ -static int mmtimer_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long mmtimer_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { int ret = 0; + lock_kernel(); + switch (cmd) { case MMTIMER_GETOFFSET: /* offset of the counter */ /* @@ -384,15 +386,14 @@ static int mmtimer_ioctl(struct inode *inode, struct file *file, case MMTIMER_GETRES: /* resolution of the clock in 10^-15 s */ if(copy_to_user((unsigned long __user *)arg, &mmtimer_femtoperiod, sizeof(unsigned long))) - return -EFAULT; + ret = -EFAULT; break; case MMTIMER_GETFREQ: /* frequency in Hz */ if(copy_to_user((unsigned long __user *)arg, &sn_rtc_cycles_per_second, sizeof(unsigned long))) - return -EFAULT; - ret = 0; + ret = -EFAULT; break; case MMTIMER_GETBITS: /* number of bits in the clock */ @@ -406,13 +407,13 @@ static int mmtimer_ioctl(struct inode *inode, struct file *file, case MMTIMER_GETCOUNTER: if(copy_to_user((unsigned long __user *)arg, RTC_COUNTER_ADDR, sizeof(unsigned long))) - return -EFAULT; + ret = -EFAULT; break; default: - ret = -ENOSYS; + ret = -ENOTTY; break; } - + unlock_kernel(); return ret; } diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index 2bba250ffc8e..d3d7864e0c1e 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -374,12 +374,13 @@ copy: return ret; } -static void moxa_break_ctl(struct tty_struct *tty, int state) +static int moxa_break_ctl(struct tty_struct *tty, int state) { struct moxa_port *port = tty->driver_data; moxafunc(port->tableAddr, state ? FC_SendBreak : FC_StopBreak, Magic_code); + return 0; } static const struct tty_operations moxa_ops = { diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c index fe2a95b5d3c0..30f095a8c2d4 100644 --- a/drivers/char/mspec.c +++ b/drivers/char/mspec.c @@ -193,25 +193,23 @@ mspec_close(struct vm_area_struct *vma) } /* - * mspec_nopfn + * mspec_fault * * Creates a mspec page and maps it to user space. */ -static unsigned long -mspec_nopfn(struct vm_area_struct *vma, unsigned long address) +static int +mspec_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { unsigned long paddr, maddr; unsigned long pfn; - int index; + pgoff_t index = vmf->pgoff; struct vma_data *vdata = vma->vm_private_data; - BUG_ON(address < vdata->vm_start || address >= vdata->vm_end); - index = (address - vdata->vm_start) >> PAGE_SHIFT; maddr = (volatile unsigned long) vdata->maddr[index]; if (maddr == 0) { maddr = uncached_alloc_page(numa_node_id(), 1); if (maddr == 0) - return NOPFN_OOM; + return VM_FAULT_OOM; spin_lock(&vdata->lock); if (vdata->maddr[index] == 0) { @@ -231,13 +229,20 @@ mspec_nopfn(struct vm_area_struct *vma, unsigned long address) pfn = paddr >> PAGE_SHIFT; - return pfn; + /* + * vm_insert_pfn can fail with -EBUSY, but in that case it will + * be because another thread has installed the pte first, so it + * is no problem. + */ + vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn); + + return VM_FAULT_NOPAGE; } static struct vm_operations_struct mspec_vm_ops = { .open = mspec_open, .close = mspec_close, - .nopfn = mspec_nopfn + .fault = mspec_fault, }; /* diff --git a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c index 50243fcd87e8..4f8d67fed292 100644 --- a/drivers/char/mwave/mwavedd.c +++ b/drivers/char/mwave/mwavedd.c @@ -86,8 +86,8 @@ module_param(mwave_uart_io, int, 0); static int mwave_open(struct inode *inode, struct file *file); static int mwave_close(struct inode *inode, struct file *file); -static int mwave_ioctl(struct inode *inode, struct file *filp, - unsigned int iocmd, unsigned long ioarg); +static long mwave_ioctl(struct file *filp, unsigned int iocmd, + unsigned long ioarg); MWAVE_DEVICE_DATA mwave_s_mdd; @@ -119,16 +119,16 @@ static int mwave_close(struct inode *inode, struct file *file) return retval; } -static int mwave_ioctl(struct inode *inode, struct file *file, - unsigned int iocmd, unsigned long ioarg) +static long mwave_ioctl(struct file *file, unsigned int iocmd, + unsigned long ioarg) { unsigned int retval = 0; pMWAVE_DEVICE_DATA pDrvData = &mwave_s_mdd; void __user *arg = (void __user *)ioarg; - PRINTK_5(TRACE_MWAVE, - "mwavedd::mwave_ioctl, entry inode %p file %p cmd %x arg %x\n", - inode, file, iocmd, (int) ioarg); + PRINTK_4(TRACE_MWAVE, + "mwavedd::mwave_ioctl, entry file %p cmd %x arg %x\n", + file, iocmd, (int) ioarg); switch (iocmd) { @@ -136,7 +136,9 @@ static int mwave_ioctl(struct inode *inode, struct file *file, PRINTK_1(TRACE_MWAVE, "mwavedd::mwave_ioctl, IOCTL_MW_RESET" " calling tp3780I_ResetDSP\n"); + lock_kernel(); retval = tp3780I_ResetDSP(&pDrvData->rBDData); + unlock_kernel(); PRINTK_2(TRACE_MWAVE, "mwavedd::mwave_ioctl, IOCTL_MW_RESET" " retval %x from tp3780I_ResetDSP\n", @@ -147,7 +149,9 @@ static int mwave_ioctl(struct inode *inode, struct file *file, PRINTK_1(TRACE_MWAVE, "mwavedd::mwave_ioctl, IOCTL_MW_RUN" " calling tp3780I_StartDSP\n"); + lock_kernel(); retval = tp3780I_StartDSP(&pDrvData->rBDData); + unlock_kernel(); PRINTK_2(TRACE_MWAVE, "mwavedd::mwave_ioctl, IOCTL_MW_RUN" " retval %x from tp3780I_StartDSP\n", @@ -161,8 +165,10 @@ static int mwave_ioctl(struct inode *inode, struct file *file, "mwavedd::mwave_ioctl," " IOCTL_MW_DSP_ABILITIES calling" " tp3780I_QueryAbilities\n"); + lock_kernel(); retval = tp3780I_QueryAbilities(&pDrvData->rBDData, &rAbilities); + unlock_kernel(); PRINTK_2(TRACE_MWAVE, "mwavedd::mwave_ioctl, IOCTL_MW_DSP_ABILITIES" " retval %x from tp3780I_QueryAbilities\n", @@ -193,11 +199,13 @@ static int mwave_ioctl(struct inode *inode, struct file *file, "mwavedd::mwave_ioctl IOCTL_MW_READ_DATA," " size %lx, ioarg %lx pusBuffer %p\n", rReadData.ulDataLength, ioarg, pusBuffer); + lock_kernel(); retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData, iocmd, pusBuffer, rReadData.ulDataLength, rReadData.usDspAddress); + unlock_kernel(); } break; @@ -215,10 +223,12 @@ static int mwave_ioctl(struct inode *inode, struct file *file, " size %lx, ioarg %lx pusBuffer %p\n", rReadData.ulDataLength / 2, ioarg, pusBuffer); + lock_kernel(); retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData, iocmd, pusBuffer, rReadData.ulDataLength / 2, rReadData.usDspAddress); + unlock_kernel(); } break; @@ -236,10 +246,12 @@ static int mwave_ioctl(struct inode *inode, struct file *file, " size %lx, ioarg %lx pusBuffer %p\n", rWriteData.ulDataLength, ioarg, pusBuffer); + lock_kernel(); retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData, iocmd, pusBuffer, rWriteData.ulDataLength, rWriteData.usDspAddress); + unlock_kernel(); } break; @@ -257,10 +269,12 @@ static int mwave_ioctl(struct inode *inode, struct file *file, " size %lx, ioarg %lx pusBuffer %p\n", rWriteData.ulDataLength, ioarg, pusBuffer); + lock_kernel(); retval = tp3780I_ReadWriteDspIStore(&pDrvData->rBDData, iocmd, pusBuffer, rWriteData.ulDataLength, rWriteData.usDspAddress); + unlock_kernel(); } break; @@ -281,8 +295,10 @@ static int mwave_ioctl(struct inode *inode, struct file *file, ipcnum); return -EINVAL; } + lock_kernel(); pDrvData->IPCs[ipcnum].bIsHere = FALSE; pDrvData->IPCs[ipcnum].bIsEnabled = TRUE; + unlock_kernel(); PRINTK_2(TRACE_MWAVE, "mwavedd::mwave_ioctl IOCTL_MW_REGISTER_IPC" @@ -307,6 +323,7 @@ static int mwave_ioctl(struct inode *inode, struct file *file, return -EINVAL; } + lock_kernel(); if (pDrvData->IPCs[ipcnum].bIsEnabled == TRUE) { DECLARE_WAITQUEUE(wait, current); @@ -347,6 +364,7 @@ static int mwave_ioctl(struct inode *inode, struct file *file, " processing\n", ipcnum); } + unlock_kernel(); } break; @@ -365,19 +383,18 @@ static int mwave_ioctl(struct inode *inode, struct file *file, ipcnum); return -EINVAL; } + lock_kernel(); if (pDrvData->IPCs[ipcnum].bIsEnabled == TRUE) { pDrvData->IPCs[ipcnum].bIsEnabled = FALSE; if (pDrvData->IPCs[ipcnum].bIsHere == TRUE) { wake_up_interruptible(&pDrvData->IPCs[ipcnum].ipc_wait_queue); } } + unlock_kernel(); } break; default: - PRINTK_ERROR(KERN_ERR_MWAVE "mwavedd::mwave_ioctl:" - " Error: Unrecognized iocmd %x\n", - iocmd); return -ENOTTY; break; } /* switch */ @@ -460,7 +477,7 @@ static const struct file_operations mwave_fops = { .owner = THIS_MODULE, .read = mwave_read, .write = mwave_write, - .ioctl = mwave_ioctl, + .unlocked_ioctl = mwave_ioctl, .open = mwave_open, .release = mwave_close }; diff --git a/drivers/char/mwave/mwavedd.h b/drivers/char/mwave/mwavedd.h index 8eca61e0a19c..7e0d530e2e07 100644 --- a/drivers/char/mwave/mwavedd.h +++ b/drivers/char/mwave/mwavedd.h @@ -147,4 +147,6 @@ typedef struct _MWAVE_DEVICE_DATA { } MWAVE_DEVICE_DATA, *pMWAVE_DEVICE_DATA; +extern MWAVE_DEVICE_DATA mwave_s_mdd; + #endif diff --git a/drivers/char/mwave/tp3780i.c b/drivers/char/mwave/tp3780i.c index f282976daaac..c68969708068 100644 --- a/drivers/char/mwave/tp3780i.c +++ b/drivers/char/mwave/tp3780i.c @@ -57,8 +57,6 @@ #include "3780i.h" #include "mwavepub.h" -extern MWAVE_DEVICE_DATA mwave_s_mdd; - static unsigned short s_ausThinkpadIrqToField[16] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, 0x0002, 0x0003, 0xFFFF, 0x0004, 0xFFFF, 0xFFFF, 0x0005, 0x0006, 0xFFFF, 0xFFFF, 0xFFFF, 0x0007 }; diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index 6307e301bd26..e30575e87648 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -16,7 +16,6 @@ * Fed through a cleanup, indent and remove of non 2.6 code by Alan Cox * <alan@redhat.com>. The original 1.8 code is available on www.moxa.com. * - Fixed x86_64 cleanness - * - Fixed sleep with spinlock held in mxser_send_break */ #include <linux/module.h> @@ -47,20 +46,14 @@ #include "mxser.h" -#define MXSER_VERSION "2.0.3" /* 1.11 */ +#define MXSER_VERSION "2.0.4" /* 1.12 */ #define MXSERMAJOR 174 -#define MXSERCUMAJOR 175 #define MXSER_BOARDS 4 /* Max. boards */ #define MXSER_PORTS_PER_BOARD 8 /* Max. ports per board */ #define MXSER_PORTS (MXSER_BOARDS * MXSER_PORTS_PER_BOARD) #define MXSER_ISR_PASS_LIMIT 100 -#define MXSER_ERR_IOADDR -1 -#define MXSER_ERR_IRQ -2 -#define MXSER_ERR_IRQ_CONFLIT -3 -#define MXSER_ERR_VECTOR -4 - /*CheckIsMoxaMust return value*/ #define MOXA_OTHER_UART 0x00 #define MOXA_MUST_MU150_HWID 0x01 @@ -71,12 +64,13 @@ #define UART_MCR_AFE 0x20 #define UART_LSR_SPECIAL 0x1E +#define PCI_DEVICE_ID_POS104UL 0x1044 #define PCI_DEVICE_ID_CB108 0x1080 +#define PCI_DEVICE_ID_CP102UF 0x1023 #define PCI_DEVICE_ID_CB114 0x1142 #define PCI_DEVICE_ID_CP114UL 0x1143 #define PCI_DEVICE_ID_CB134I 0x1341 #define PCI_DEVICE_ID_CP138U 0x1380 -#define PCI_DEVICE_ID_POS104UL 0x1044 #define C168_ASIC_ID 1 @@ -142,7 +136,8 @@ static const struct mxser_cardinfo mxser_cards[] = { { "CB-134I series", 4, }, { "CP-138U series", 8, }, { "POS-104UL series", 4, }, - { "CP-114UL series", 4, } + { "CP-114UL series", 4, }, +/*30*/ { "CP-102UF series", 2, } }; /* driver_data correspond to the lines in the structure above @@ -172,18 +167,20 @@ static struct pci_device_id mxser_pcibrds[] = { { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP138U), .driver_data = 27 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_POS104UL), .driver_data = 28 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP114UL), .driver_data = 29 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP102UF), .driver_data = 30 }, { } }; MODULE_DEVICE_TABLE(pci, mxser_pcibrds); -static int ioaddr[MXSER_BOARDS] = { 0, 0, 0, 0 }; +static unsigned long ioaddr[MXSER_BOARDS]; static int ttymajor = MXSERMAJOR; /* Variables for insmod */ MODULE_AUTHOR("Casper Yang"); MODULE_DESCRIPTION("MOXA Smartio/Industio Family Multiport Board Device Driver"); -module_param_array(ioaddr, int, NULL, 0); +module_param_array(ioaddr, ulong, NULL, 0); +MODULE_PARM_DESC(ioaddr, "ISA io addresses to look for a moxa board"); module_param(ttymajor, int, 0); MODULE_LICENSE("GPL"); @@ -193,7 +190,6 @@ struct mxser_log { unsigned long txcnt[MXSER_PORTS]; }; - struct mxser_mon { unsigned long rxcnt; unsigned long txcnt; @@ -284,19 +280,9 @@ struct mxser_mstatus { int dcd; }; -static struct mxser_mstatus GMStatus[MXSER_PORTS]; - -static int mxserBoardCAP[MXSER_BOARDS] = { - 0, 0, 0, 0 - /* 0x180, 0x280, 0x200, 0x320 */ -}; - static struct mxser_board mxser_boards[MXSER_BOARDS]; static struct tty_driver *mxvar_sdriver; static struct mxser_log mxvar_log; -static int mxvar_diagflag; -static unsigned char mxser_msr[MXSER_PORTS + 1]; -static struct mxser_mon_ext mon_data_ext; static int mxser_set_baud_method[MXSER_PORTS + 1]; static void mxser_enable_must_enchance_mode(unsigned long baseio) @@ -540,6 +526,7 @@ static void process_txrx_fifo(struct mxser_port *info) static unsigned char mxser_get_msr(int baseaddr, int mode, int port) { + static unsigned char mxser_msr[MXSER_PORTS + 1]; unsigned char status = 0; status = inb(baseaddr + UART_MSR); @@ -1316,13 +1303,9 @@ static void mxser_flush_chars(struct tty_struct *tty) struct mxser_port *info = tty->driver_data; unsigned long flags; - if (info->xmit_cnt <= 0 || - tty->stopped || - !info->port.xmit_buf || - (tty->hw_stopped && - (info->type != PORT_16550A) && - (!info->board->chip_flag) - )) + if (info->xmit_cnt <= 0 || tty->stopped || !info->port.xmit_buf || + (tty->hw_stopped && info->type != PORT_16550A && + !info->board->chip_flag)) return; spin_lock_irqsave(&info->slock, flags); @@ -1340,9 +1323,7 @@ static int mxser_write_room(struct tty_struct *tty) int ret; ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; - if (ret < 0) - ret = 0; - return ret; + return ret < 0 ? 0 : ret; } static int mxser_chars_in_buffer(struct tty_struct *tty) @@ -1414,7 +1395,6 @@ static int mxser_set_serial_info(struct mxser_port *info, info->port.closing_wait = new_serial.closing_wait * HZ / 100; info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; - info->port.tty->low_latency = 0; if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST && (new_serial.baud_base != info->baud_base || new_serial.custom_divisor != @@ -1464,27 +1444,6 @@ static int mxser_get_lsr_info(struct mxser_port *info, return put_user(result, value); } -/* - * This routine sends a break character out the serial port. - */ -static void mxser_send_break(struct mxser_port *info, int duration) -{ - unsigned long flags; - - if (!info->ioaddr) - return; - set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&info->slock, flags); - outb(inb(info->ioaddr + UART_LCR) | UART_LCR_SBC, - info->ioaddr + UART_LCR); - spin_unlock_irqrestore(&info->slock, flags); - schedule_timeout(duration); - spin_lock_irqsave(&info->slock, flags); - outb(inb(info->ioaddr + UART_LCR) & ~UART_LCR_SBC, - info->ioaddr + UART_LCR); - spin_unlock_irqrestore(&info->slock, flags); -} - static int mxser_tiocmget(struct tty_struct *tty, struct file *file) { struct mxser_port *info = tty->driver_data; @@ -1653,6 +1612,8 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) switch (cmd) { case MOXA_GET_MAJOR: + printk(KERN_WARNING "mxser: '%s' uses deprecated ioctl %x, fix " + "your userspace\n", current->comm, cmd); return put_user(ttymajor, (int __user *)argp); case MOXA_CHKPORTENABLE: @@ -1670,62 +1631,60 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) ret = -EFAULT; unlock_kernel(); return ret; - case MOXA_GETMSTATUS: + case MOXA_GETMSTATUS: { + struct mxser_mstatus ms, __user *msu = argp; lock_kernel(); for (i = 0; i < MXSER_BOARDS; i++) for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) { port = &mxser_boards[i].ports[j]; + memset(&ms, 0, sizeof(ms)); - GMStatus[i].ri = 0; - if (!port->ioaddr) { - GMStatus[i].dcd = 0; - GMStatus[i].dsr = 0; - GMStatus[i].cts = 0; - continue; - } + if (!port->ioaddr) + goto copy; if (!port->port.tty || !port->port.tty->termios) - GMStatus[i].cflag = - port->normal_termios.c_cflag; + ms.cflag = port->normal_termios.c_cflag; else - GMStatus[i].cflag = - port->port.tty->termios->c_cflag; + ms.cflag = port->port.tty->termios->c_cflag; status = inb(port->ioaddr + UART_MSR); - if (status & 0x80 /*UART_MSR_DCD */ ) - GMStatus[i].dcd = 1; - else - GMStatus[i].dcd = 0; - - if (status & 0x20 /*UART_MSR_DSR */ ) - GMStatus[i].dsr = 1; - else - GMStatus[i].dsr = 0; - - - if (status & 0x10 /*UART_MSR_CTS */ ) - GMStatus[i].cts = 1; - else - GMStatus[i].cts = 0; + if (status & UART_MSR_DCD) + ms.dcd = 1; + if (status & UART_MSR_DSR) + ms.dsr = 1; + if (status & UART_MSR_CTS) + ms.cts = 1; + copy: + if (copy_to_user(msu, &ms, sizeof(ms))) { + unlock_kernel(); + return -EFAULT; + } + msu++; } unlock_kernel(); - if (copy_to_user(argp, GMStatus, - sizeof(struct mxser_mstatus) * MXSER_PORTS)) - return -EFAULT; return 0; + } case MOXA_ASPP_MON_EXT: { - int p, shiftbit; - unsigned long opmode; - unsigned cflag, iflag; + struct mxser_mon_ext *me; /* it's 2k, stack unfriendly */ + unsigned int cflag, iflag, p; + u8 opmode; + + me = kzalloc(sizeof(*me), GFP_KERNEL); + if (!me) + return -ENOMEM; lock_kernel(); - for (i = 0; i < MXSER_BOARDS; i++) { - for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) { + for (i = 0, p = 0; i < MXSER_BOARDS; i++) { + for (j = 0; j < MXSER_PORTS_PER_BOARD; j++, p++) { + if (p >= ARRAY_SIZE(me->rx_cnt)) { + i = MXSER_BOARDS; + break; + } port = &mxser_boards[i].ports[j]; if (!port->ioaddr) continue; - status = mxser_get_msr(port->ioaddr, 0, i); + status = mxser_get_msr(port->ioaddr, 0, p); if (status & UART_MSR_TERI) port->icount.rng++; @@ -1737,16 +1696,13 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) port->icount.cts++; port->mon_data.modem_status = status; - mon_data_ext.rx_cnt[i] = port->mon_data.rxcnt; - mon_data_ext.tx_cnt[i] = port->mon_data.txcnt; - mon_data_ext.up_rxcnt[i] = - port->mon_data.up_rxcnt; - mon_data_ext.up_txcnt[i] = - port->mon_data.up_txcnt; - mon_data_ext.modem_status[i] = + me->rx_cnt[p] = port->mon_data.rxcnt; + me->tx_cnt[p] = port->mon_data.txcnt; + me->up_rxcnt[p] = port->mon_data.up_rxcnt; + me->up_txcnt[p] = port->mon_data.up_txcnt; + me->modem_status[p] = port->mon_data.modem_status; - mon_data_ext.baudrate[i] = - tty_get_baud_rate(port->port.tty); + me->baudrate[p] = tty_get_baud_rate(port->port.tty); if (!port->port.tty || !port->port.tty->termios) { cflag = port->normal_termios.c_cflag; @@ -1756,40 +1712,31 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) iflag = port->port.tty->termios->c_iflag; } - mon_data_ext.databits[i] = cflag & CSIZE; - - mon_data_ext.stopbits[i] = cflag & CSTOPB; - - mon_data_ext.parity[i] = - cflag & (PARENB | PARODD | CMSPAR); - - mon_data_ext.flowctrl[i] = 0x00; + me->databits[p] = cflag & CSIZE; + me->stopbits[p] = cflag & CSTOPB; + me->parity[p] = cflag & (PARENB | PARODD | + CMSPAR); if (cflag & CRTSCTS) - mon_data_ext.flowctrl[i] |= 0x03; + me->flowctrl[p] |= 0x03; if (iflag & (IXON | IXOFF)) - mon_data_ext.flowctrl[i] |= 0x0C; + me->flowctrl[p] |= 0x0C; if (port->type == PORT_16550A) - mon_data_ext.fifo[i] = 1; - else - mon_data_ext.fifo[i] = 0; + me->fifo[p] = 1; - p = i % 4; - shiftbit = p * 2; - opmode = inb(port->opmode_ioaddr) >> shiftbit; + opmode = inb(port->opmode_ioaddr) >> + ((p % 4) * 2); opmode &= OP_MODE_MASK; - - mon_data_ext.iftype[i] = opmode; - + me->iftype[p] = opmode; } } unlock_kernel(); - if (copy_to_user(argp, &mon_data_ext, - sizeof(mon_data_ext))) - return -EFAULT; - return 0; + if (copy_to_user(argp, me, sizeof(*me))) + ret = -EFAULT; + kfree(me); + return ret; } default: return -ENOIOCTLCMD; @@ -1823,7 +1770,6 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, { struct mxser_port *info = tty->driver_data; struct async_icount cnow; - struct serial_icounter_struct __user *p_cuser; unsigned long flags; void __user *argp = (void __user *)arg; int retval; @@ -1872,21 +1818,6 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, return -EIO; 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) - mxser_send_break(info, 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); - mxser_send_break(info, arg ? arg * (HZ / 10) : HZ / 4); - return 0; case TIOCGSERIAL: lock_kernel(); retval = mxser_get_serial_info(info, argp); @@ -1918,30 +1849,26 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, * NB: both 1->0 and 0->1 transitions are counted except for * RI where only 0->1 is counted. */ - case TIOCGICOUNT: + case TIOCGICOUNT: { + struct serial_icounter_struct icnt = { 0 }; spin_lock_irqsave(&info->slock, flags); cnow = info->icount; spin_unlock_irqrestore(&info->slock, flags); - p_cuser = argp; - if (put_user(cnow.frame, &p_cuser->frame)) - return -EFAULT; - if (put_user(cnow.brk, &p_cuser->brk)) - return -EFAULT; - if (put_user(cnow.overrun, &p_cuser->overrun)) - return -EFAULT; - if (put_user(cnow.buf_overrun, &p_cuser->buf_overrun)) - return -EFAULT; - if (put_user(cnow.parity, &p_cuser->parity)) - return -EFAULT; - if (put_user(cnow.rx, &p_cuser->rx)) - return -EFAULT; - if (put_user(cnow.tx, &p_cuser->tx)) - return -EFAULT; - put_user(cnow.cts, &p_cuser->cts); - put_user(cnow.dsr, &p_cuser->dsr); - put_user(cnow.rng, &p_cuser->rng); - put_user(cnow.dcd, &p_cuser->dcd); - return 0; + + icnt.frame = cnow.frame; + icnt.brk = cnow.brk; + icnt.overrun = cnow.overrun; + icnt.buf_overrun = cnow.buf_overrun; + icnt.parity = cnow.parity; + icnt.rx = cnow.rx; + icnt.tx = cnow.tx; + icnt.cts = cnow.cts; + icnt.dsr = cnow.dsr; + icnt.rng = cnow.rng; + icnt.dcd = cnow.dcd; + + return copy_to_user(argp, &icnt, sizeof(icnt)) ? -EFAULT : 0; + } case MOXA_HighSpeedOn: return put_user(info->baud_base != 115200 ? 1 : 0, (int __user *)argp); case MOXA_SDS_RSTICOUNTER: @@ -2219,7 +2146,7 @@ static void mxser_hangup(struct tty_struct *tty) /* * mxser_rs_break() --- routine which turns the break handling on or off */ -static void mxser_rs_break(struct tty_struct *tty, int break_state) +static int mxser_rs_break(struct tty_struct *tty, int break_state) { struct mxser_port *info = tty->driver_data; unsigned long flags; @@ -2232,6 +2159,7 @@ static void mxser_rs_break(struct tty_struct *tty, int break_state) outb(inb(info->ioaddr + UART_LCR) & ~UART_LCR_SBC, info->ioaddr + UART_LCR); spin_unlock_irqrestore(&info->slock, flags); + return 0; } static void mxser_receive_chars(struct mxser_port *port, int *status) @@ -2536,7 +2464,8 @@ static int __devinit mxser_initbrd(struct mxser_board *brd, unsigned int i; int retval; - printk(KERN_INFO "max. baud rate = %d bps.\n", brd->ports[0].max_baud); + printk(KERN_INFO "mxser: max. baud rate = %d bps\n", + brd->ports[0].max_baud); for (i = 0; i < brd->info->nports; i++) { info = &brd->ports[i]; @@ -2619,28 +2548,32 @@ static int __init mxser_get_ISA_conf(int cap, struct mxser_board *brd) irq = regs[9] & 0xF000; irq = irq | (irq >> 4); if (irq != (regs[9] & 0xFF00)) - return MXSER_ERR_IRQ_CONFLIT; + goto err_irqconflict; } else if (brd->info->nports == 4) { irq = regs[9] & 0xF000; irq = irq | (irq >> 4); irq = irq | (irq >> 8); if (irq != regs[9]) - return MXSER_ERR_IRQ_CONFLIT; + goto err_irqconflict; } else if (brd->info->nports == 8) { irq = regs[9] & 0xF000; irq = irq | (irq >> 4); irq = irq | (irq >> 8); if ((irq != regs[9]) || (irq != regs[10])) - return MXSER_ERR_IRQ_CONFLIT; + goto err_irqconflict; } - if (!irq) - return MXSER_ERR_IRQ; + if (!irq) { + printk(KERN_ERR "mxser: interrupt number unset\n"); + return -EIO; + } brd->irq = ((int)(irq & 0xF000) >> 12); for (i = 0; i < 8; i++) brd->ports[i].ioaddr = (int) regs[i + 1] & 0xFFF8; - if ((regs[12] & 0x80) == 0) - return MXSER_ERR_VECTOR; + if ((regs[12] & 0x80) == 0) { + printk(KERN_ERR "mxser: invalid interrupt vector\n"); + return -EIO; + } brd->vector = (int)regs[11]; /* interrupt vector */ if (id == 1) brd->vector_mask = 0x00FF; @@ -2667,13 +2600,26 @@ static int __init mxser_get_ISA_conf(int cap, struct mxser_board *brd) else brd->uart_type = PORT_16450; if (!request_region(brd->ports[0].ioaddr, 8 * brd->info->nports, - "mxser(IO)")) - return MXSER_ERR_IOADDR; + "mxser(IO)")) { + printk(KERN_ERR "mxser: can't request ports I/O region: " + "0x%.8lx-0x%.8lx\n", + brd->ports[0].ioaddr, brd->ports[0].ioaddr + + 8 * brd->info->nports - 1); + return -EIO; + } if (!request_region(brd->vector, 1, "mxser(vector)")) { release_region(brd->ports[0].ioaddr, 8 * brd->info->nports); - return MXSER_ERR_VECTOR; + printk(KERN_ERR "mxser: can't request interrupt vector region: " + "0x%.8lx-0x%.8lx\n", + brd->ports[0].ioaddr, brd->ports[0].ioaddr + + 8 * brd->info->nports - 1); + return -EIO; } return brd->info->nports; + +err_irqconflict: + printk(KERN_ERR "mxser: invalid interrupt number\n"); + return -EIO; } static int __devinit mxser_probe(struct pci_dev *pdev, @@ -2690,20 +2636,20 @@ static int __devinit mxser_probe(struct pci_dev *pdev, break; if (i >= MXSER_BOARDS) { - printk(KERN_ERR "Too many Smartio/Industio family boards found " - "(maximum %d), board not configured\n", MXSER_BOARDS); + dev_err(&pdev->dev, "too many boards found (maximum %d), board " + "not configured\n", MXSER_BOARDS); goto err; } brd = &mxser_boards[i]; brd->idx = i * MXSER_PORTS_PER_BOARD; - printk(KERN_INFO "Found MOXA %s board (BusNo=%d, DevNo=%d)\n", + dev_info(&pdev->dev, "found MOXA %s board (BusNo=%d, DevNo=%d)\n", mxser_cards[ent->driver_data].name, pdev->bus->number, PCI_SLOT(pdev->devfn)); retval = pci_enable_device(pdev); if (retval) { - printk(KERN_ERR "Moxa SmartI/O PCI enable fail !\n"); + dev_err(&pdev->dev, "PCI enable failed\n"); goto err; } @@ -2805,11 +2751,8 @@ static struct pci_driver mxser_driver = { static int __init mxser_module_init(void) { struct mxser_board *brd; - unsigned long cap; - unsigned int i, m, isaloop; - int retval, b; - - pr_debug("Loading module mxser ...\n"); + unsigned int b, i, m; + int retval; mxvar_sdriver = alloc_tty_driver(MXSER_PORTS + 1); if (!mxvar_sdriver) @@ -2839,74 +2782,43 @@ static int __init mxser_module_init(void) goto err_put; } - mxvar_diagflag = 0; - - m = 0; /* Start finding ISA boards here */ - for (isaloop = 0; isaloop < 2; isaloop++) - for (b = 0; b < MXSER_BOARDS && m < MXSER_BOARDS; b++) { - if (!isaloop) - cap = mxserBoardCAP[b]; /* predefined */ - else - cap = ioaddr[b]; /* module param */ + for (m = 0, b = 0; b < MXSER_BOARDS; b++) { + if (!ioaddr[b]) + continue; + + brd = &mxser_boards[m]; + retval = mxser_get_ISA_conf(!ioaddr[b], brd); + if (retval <= 0) { + brd->info = NULL; + continue; + } - if (!cap) - continue; + printk(KERN_INFO "mxser: found MOXA %s board (CAP=0x%lx)\n", + brd->info->name, ioaddr[b]); - brd = &mxser_boards[m]; - retval = mxser_get_ISA_conf(cap, brd); - - if (retval != 0) - printk(KERN_INFO "Found MOXA %s board " - "(CAP=0x%x)\n", - brd->info->name, ioaddr[b]); - - if (retval <= 0) { - if (retval == MXSER_ERR_IRQ) - printk(KERN_ERR "Invalid interrupt " - "number, board not " - "configured\n"); - else if (retval == MXSER_ERR_IRQ_CONFLIT) - printk(KERN_ERR "Invalid interrupt " - "number, board not " - "configured\n"); - else if (retval == MXSER_ERR_VECTOR) - printk(KERN_ERR "Invalid interrupt " - "vector, board not " - "configured\n"); - else if (retval == MXSER_ERR_IOADDR) - printk(KERN_ERR "Invalid I/O address, " - "board not configured\n"); - - brd->info = NULL; - continue; - } - - /* mxser_initbrd will hook ISR. */ - if (mxser_initbrd(brd, NULL) < 0) { - brd->info = NULL; - continue; - } + /* mxser_initbrd will hook ISR. */ + if (mxser_initbrd(brd, NULL) < 0) { + brd->info = NULL; + continue; + } - brd->idx = m * MXSER_PORTS_PER_BOARD; - for (i = 0; i < brd->info->nports; i++) - tty_register_device(mxvar_sdriver, brd->idx + i, - NULL); + brd->idx = m * MXSER_PORTS_PER_BOARD; + for (i = 0; i < brd->info->nports; i++) + tty_register_device(mxvar_sdriver, brd->idx + i, NULL); - m++; - } + m++; + } retval = pci_register_driver(&mxser_driver); if (retval) { - printk(KERN_ERR "Can't register pci driver\n"); + printk(KERN_ERR "mxser: can't register pci driver\n"); if (!m) { retval = -ENODEV; goto err_unr; } /* else: we have some ISA cards under control */ } - pr_debug("Done.\n"); - return 0; err_unr: tty_unregister_driver(mxvar_sdriver); @@ -2919,8 +2831,6 @@ static void __exit mxser_module_exit(void) { unsigned int i, j; - pr_debug("Unloading module mxser ...\n"); - pci_unregister_driver(&mxser_driver); for (i = 0; i < MXSER_BOARDS; i++) /* ISA remains */ @@ -2934,8 +2844,6 @@ static void __exit mxser_module_exit(void) for (i = 0; i < MXSER_BOARDS; i++) if (mxser_boards[i].info != NULL) mxser_release_res(&mxser_boards[i], NULL, 1); - - pr_debug("Done.\n"); } module_init(mxser_module_init); diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c index ed4e03333ab4..69ec6399c714 100644 --- a/drivers/char/n_hdlc.c +++ b/drivers/char/n_hdlc.c @@ -677,6 +677,10 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file, /* Allocate transmit buffer */ /* sleep until transmit buffer available */ while (!(tbuf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list))) { + if (file->f_flags & O_NONBLOCK) { + error = -EAGAIN; + break; + } schedule(); n_hdlc = tty2n_hdlc (tty); diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c index a22662b6a1a5..39f6357e3b5d 100644 --- a/drivers/char/nvram.c +++ b/drivers/char/nvram.c @@ -107,7 +107,6 @@ #include <linux/init.h> #include <linux/proc_fs.h> #include <linux/spinlock.h> -#include <linux/smp_lock.h> #include <asm/io.h> #include <asm/uaccess.h> diff --git a/drivers/char/nwflash.c b/drivers/char/nwflash.c index ba012c2bdf7a..006be92ee3f3 100644 --- a/drivers/char/nwflash.c +++ b/drivers/char/nwflash.c @@ -122,35 +122,20 @@ static int flash_ioctl(struct inode *inodep, struct file *filep, unsigned int cm static ssize_t flash_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { - unsigned long p = *ppos; - unsigned int count = size; - int ret = 0; + ssize_t ret; if (flashdebug) - printk(KERN_DEBUG "flash_read: flash_read: offset=0x%lX, " - "buffer=%p, count=0x%X.\n", p, buf, count); - - if (count) - ret = -ENXIO; + printk(KERN_DEBUG "flash_read: flash_read: offset=0x%llx, " + "buffer=%p, count=0x%zx.\n", *ppos, buf, size); + /* + * We now lock against reads and writes. --rmk + */ + if (mutex_lock_interruptible(&nwflash_mutex)) + return -ERESTARTSYS; - if (p < gbFlashSize) { - if (count > gbFlashSize - p) - count = gbFlashSize - p; + ret = simple_read_from_buffer(buf, size, ppos, (void *)FLASH_BASE, gbFlashSize); + mutex_unlock(&nwflash_mutex); - /* - * We now lock against reads and writes. --rmk - */ - if (mutex_lock_interruptible(&nwflash_mutex)) - return -ERESTARTSYS; - - ret = copy_to_user(buf, (void *)(FLASH_BASE + p), count); - if (ret == 0) { - ret = count; - *ppos += count; - } else - ret = -EFAULT; - mutex_unlock(&nwflash_mutex); - } return ret; } diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index e4a4fbd37d7a..f070ae7bd91a 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -1896,7 +1896,7 @@ static int cm4000_probe(struct pcmcia_device *link) return ret; } - device_create(cmm_class, NULL, MKDEV(major, i), "cmm%d", i); + device_create_drvdata(cmm_class, NULL, MKDEV(major, i), NULL, "cmm%d", i); return 0; } diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c index 6181f8a9b0bd..0b5934bef7a4 100644 --- a/drivers/char/pcmcia/cm4040_cs.c +++ b/drivers/char/pcmcia/cm4040_cs.c @@ -653,7 +653,8 @@ static int reader_probe(struct pcmcia_device *link) return ret; } - device_create(cmx_class, NULL, MKDEV(major, i), "cmx%d", i); + device_create_drvdata(cmx_class, NULL, MKDEV(major, i), NULL, + "cmx%d", i); return 0; } diff --git a/drivers/char/pcmcia/ipwireless/hardware.c b/drivers/char/pcmcia/ipwireless/hardware.c index 929101ecbae2..4c1820cad712 100644 --- a/drivers/char/pcmcia/ipwireless/hardware.c +++ b/drivers/char/pcmcia/ipwireless/hardware.c @@ -30,11 +30,11 @@ static void ipw_send_setup_packet(struct ipw_hardware *hw); static void handle_received_SETUP_packet(struct ipw_hardware *ipw, unsigned int address, - unsigned char *data, int len, + const unsigned char *data, int len, int is_last); static void ipwireless_setup_timer(unsigned long data); static void handle_received_CTRL_packet(struct ipw_hardware *hw, - unsigned int channel_idx, unsigned char *data, int len); + unsigned int channel_idx, const unsigned char *data, int len); /*#define TIMING_DIAGNOSTICS*/ @@ -79,8 +79,7 @@ static void report_timing(void) timing_stats.last_report_time = jiffies; if (!first) printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": %u us elapsed - read %lu bytes in %u us, " - "wrote %lu bytes in %u us\n", + ": %u us elapsed - read %lu bytes in %u us, wrote %lu bytes in %u us\n", jiffies_to_usecs(since), timing_stats.read_bytes, jiffies_to_usecs(timing_stats.read_time), @@ -133,29 +132,17 @@ enum { #define NL_FOLLOWING_PACKET_HEADER_SIZE 1 struct nl_first_packet_header { -#if defined(__BIG_ENDIAN_BITFIELD) - unsigned char packet_rank:2; - unsigned char address:3; - unsigned char protocol:3; -#else unsigned char protocol:3; unsigned char address:3; unsigned char packet_rank:2; -#endif unsigned char length_lsb; unsigned char length_msb; }; struct nl_packet_header { -#if defined(__BIG_ENDIAN_BITFIELD) - unsigned char packet_rank:2; - unsigned char address:3; - unsigned char protocol:3; -#else unsigned char protocol:3; unsigned char address:3; unsigned char packet_rank:2; -#endif }; /* Value of 'packet_rank' above */ @@ -227,15 +214,12 @@ struct MEMINFREG { unsigned short memreg_tx_new; /* TX2 (new) Register (R/W) */ }; -#define IODMADPR 0x00 /* DMA Data Port Register (R/W) */ - #define CARD_PRESENT_VALUE (0xBEEFCAFEUL) #define MEMTX_TX 0x0001 #define MEMRX_RX 0x0001 #define MEMRX_RX_DONE 0x0001 #define MEMRX_PCINTACKK 0x0001 -#define MEMRX_MEMSPURIOUSINT 0x0001 #define NL_NUM_OF_PRIORITIES 3 #define NL_NUM_OF_PROTOCOLS 3 @@ -245,7 +229,7 @@ struct ipw_hardware { unsigned int base_port; short hw_version; unsigned short ll_mtu; - spinlock_t spinlock; + spinlock_t lock; int initializing; int init_loops; @@ -386,26 +370,52 @@ static void dump_data_bytes(const char *type, const unsigned char *data, length < DUMP_MAX_BYTES ? length : DUMP_MAX_BYTES); } -static int do_send_fragment(struct ipw_hardware *hw, const unsigned char *data, +static void swap_packet_bitfield_to_le(unsigned char *data) +{ +#ifdef __BIG_ENDIAN_BITFIELD + unsigned char tmp = *data, ret = 0; + + /* + * transform bits from aa.bbb.ccc to ccc.bbb.aa + */ + ret |= tmp & 0xc0 >> 6; + ret |= tmp & 0x38 >> 1; + ret |= tmp & 0x07 << 5; + *data = ret & 0xff; +#endif +} + +static void swap_packet_bitfield_from_le(unsigned char *data) +{ +#ifdef __BIG_ENDIAN_BITFIELD + unsigned char tmp = *data, ret = 0; + + /* + * transform bits from ccc.bbb.aa to aa.bbb.ccc + */ + ret |= tmp & 0xe0 >> 5; + ret |= tmp & 0x1c << 1; + ret |= tmp & 0x03 << 6; + *data = ret & 0xff; +#endif +} + +static void do_send_fragment(struct ipw_hardware *hw, unsigned char *data, unsigned length) { - int i; + unsigned i; unsigned long flags; start_timing(); - - if (length == 0) - return 0; - - if (length > hw->ll_mtu) - return -1; + BUG_ON(length > hw->ll_mtu); if (ipwireless_debug) dump_data_bytes("send", data, length); - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); hw->tx_ready = 0; + swap_packet_bitfield_to_le(data); if (hw->hw_version == HW_VERSION_1) { outw((unsigned short) length, hw->base_port + IODWR); @@ -414,7 +424,7 @@ static int do_send_fragment(struct ipw_hardware *hw, const unsigned char *data, unsigned short d = data[i]; __le16 raw_data; - if (likely(i + 1 < length)) + if (i + 1 < length) d |= data[i + 1] << 8; raw_data = cpu_to_le16(d); outw(raw_data, hw->base_port + IODWR); @@ -422,32 +432,30 @@ static int do_send_fragment(struct ipw_hardware *hw, const unsigned char *data, outw(DCR_TXDONE, hw->base_port + IODCR); } else if (hw->hw_version == HW_VERSION_2) { - outw((unsigned short) length, hw->base_port + IODMADPR); + outw((unsigned short) length, hw->base_port); for (i = 0; i < length; i += 2) { unsigned short d = data[i]; __le16 raw_data; - if ((i + 1 < length)) + if (i + 1 < length) d |= data[i + 1] << 8; raw_data = cpu_to_le16(d); - outw(raw_data, hw->base_port + IODMADPR); + outw(raw_data, hw->base_port); } while ((i & 3) != 2) { - outw((unsigned short) 0xDEAD, hw->base_port + IODMADPR); + outw((unsigned short) 0xDEAD, hw->base_port); i += 2; } writew(MEMRX_RX, &hw->memory_info_regs->memreg_rx); } - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); end_write_timing(length); - - return 0; } -static int do_send_packet(struct ipw_hardware *hw, struct ipw_tx_packet *packet) +static void do_send_packet(struct ipw_hardware *hw, struct ipw_tx_packet *packet) { unsigned short fragment_data_len; unsigned short data_left = packet->length - packet->offset; @@ -462,6 +470,10 @@ static int do_send_packet(struct ipw_hardware *hw, struct ipw_tx_packet *packet) if (data_left < fragment_data_len) fragment_data_len = data_left; + /* + * hdr_first is now in machine bitfield order, which will be swapped + * to le just before it goes to hw + */ pkt.hdr_first.protocol = packet->protocol; pkt.hdr_first.address = packet->dest_addr; pkt.hdr_first.packet_rank = 0; @@ -493,25 +505,23 @@ static int do_send_packet(struct ipw_hardware *hw, struct ipw_tx_packet *packet) */ unsigned long flags; - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); list_add(&packet->queue, &hw->tx_queue[0]); hw->tx_queued++; - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); } else { if (packet->packet_callback) packet->packet_callback(packet->callback_data, packet->length); kfree(packet); } - - return 0; } static void ipw_setup_hardware(struct ipw_hardware *hw) { unsigned long flags; - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); if (hw->hw_version == HW_VERSION_1) { /* Reset RX FIFO */ outw(DCR_RXRESET, hw->base_port + IODCR); @@ -530,7 +540,7 @@ static void ipw_setup_hardware(struct ipw_hardware *hw) csr |= 1; writew(csr, &hw->memregs_CCR->reg_config_and_status); } - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); } /* @@ -549,28 +559,23 @@ static struct ipw_rx_packet *pool_allocate(struct ipw_hardware *hw, if (!packet) { unsigned long flags; - /* - * If this is the first fragment, then we will need to fetch a - * packet to put it in. - */ - spin_lock_irqsave(&hw->spinlock, flags); - /* If we have one in our pool, then pull it out. */ + spin_lock_irqsave(&hw->lock, flags); if (!list_empty(&hw->rx_pool)) { packet = list_first_entry(&hw->rx_pool, struct ipw_rx_packet, queue); - list_del(&packet->queue); hw->rx_pool_size--; - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); + list_del(&packet->queue); } else { - /* Otherwise allocate a new one. */ - static int min_capacity = 256; + const int min_capacity = + ipwireless_ppp_mru(hw->network) + 2; int new_capacity; - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); new_capacity = - minimum_free_space > min_capacity - ? minimum_free_space - : min_capacity; + (minimum_free_space > min_capacity + ? minimum_free_space + : min_capacity); packet = kmalloc(sizeof(struct ipw_rx_packet) + new_capacity, GFP_ATOMIC); if (!packet) @@ -580,10 +585,6 @@ static struct ipw_rx_packet *pool_allocate(struct ipw_hardware *hw, packet->length = 0; } - /* - * If this packet does not have sufficient capacity for the data we - * want to add, then make it bigger. - */ if (packet->length + minimum_free_space > packet->capacity) { struct ipw_rx_packet *old_packet = packet; @@ -610,13 +611,15 @@ static void pool_free(struct ipw_hardware *hw, struct ipw_rx_packet *packet) kfree(packet); else { hw->rx_pool_size++; - list_add_tail(&packet->queue, &hw->rx_pool); + list_add(&packet->queue, &hw->rx_pool); } } static void queue_received_packet(struct ipw_hardware *hw, - unsigned int protocol, unsigned int address, - unsigned char *data, int length, int is_last) + unsigned int protocol, + unsigned int address, + const unsigned char *data, int length, + int is_last) { unsigned int channel_idx = address - 1; struct ipw_rx_packet *packet = NULL; @@ -658,9 +661,9 @@ static void queue_received_packet(struct ipw_hardware *hw, packet = *assem; *assem = NULL; /* Count queued DATA bytes only */ - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); hw->rx_bytes_queued += packet->length; - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); } } else { /* If it's a CTRL packet, don't assemble, just queue it. */ @@ -682,13 +685,13 @@ static void queue_received_packet(struct ipw_hardware *hw, * network layer. */ if (packet) { - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); list_add_tail(&packet->queue, &hw->rx_queue); /* Block reception of incoming packets if queue is full. */ hw->blocking_rx = - hw->rx_bytes_queued >= IPWIRELESS_RX_QUEUE_SIZE; + (hw->rx_bytes_queued >= IPWIRELESS_RX_QUEUE_SIZE); - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); schedule_work(&hw->work_rx); } } @@ -702,7 +705,7 @@ static void ipw_receive_data_work(struct work_struct *work_rx) container_of(work_rx, struct ipw_hardware, work_rx); unsigned long flags; - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); while (!list_empty(&hw->rx_queue)) { struct ipw_rx_packet *packet = list_first_entry(&hw->rx_queue, @@ -720,7 +723,7 @@ static void ipw_receive_data_work(struct work_struct *work_rx) if (packet->protocol == TL_PROTOCOLID_COM_DATA) { if (hw->network != NULL) { /* If the network hasn't been disconnected. */ - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); /* * This must run unlocked due to tty processing * and mutex locking @@ -731,7 +734,7 @@ static void ipw_receive_data_work(struct work_struct *work_rx) (unsigned char *)packet + sizeof(struct ipw_rx_packet), packet->length); - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); } /* Count queued DATA bytes only */ hw->rx_bytes_queued -= packet->length; @@ -755,15 +758,15 @@ static void ipw_receive_data_work(struct work_struct *work_rx) if (hw->shutting_down) break; } - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); } static void handle_received_CTRL_packet(struct ipw_hardware *hw, unsigned int channel_idx, - unsigned char *data, int len) + const unsigned char *data, int len) { - struct ipw_control_packet_body *body = - (struct ipw_control_packet_body *) data; + const struct ipw_control_packet_body *body = + (const struct ipw_control_packet_body *) data; unsigned int changed_mask; if (len != sizeof(struct ipw_control_packet_body)) { @@ -805,13 +808,13 @@ static void handle_received_CTRL_packet(struct ipw_hardware *hw, } static void handle_received_packet(struct ipw_hardware *hw, - union nl_packet *packet, + const union nl_packet *packet, unsigned short len) { unsigned int protocol = packet->hdr.protocol; unsigned int address = packet->hdr.address; unsigned int header_length; - unsigned char *data; + const unsigned char *data; unsigned int data_len; int is_last = packet->hdr.packet_rank & NL_LAST_PACKET; @@ -850,7 +853,7 @@ static void acknowledge_data_read(struct ipw_hardware *hw) static void do_receive_packet(struct ipw_hardware *hw) { unsigned len; - unsigned int i; + unsigned i; unsigned char pkt[LL_MTU_MAX]; start_timing(); @@ -859,8 +862,7 @@ static void do_receive_packet(struct ipw_hardware *hw) len = inw(hw->base_port + IODRR); if (len > hw->ll_mtu) { printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": received a packet of %u bytes - " - "longer than the MTU!\n", len); + ": received a packet of %u bytes - longer than the MTU!\n", len); outw(DCR_RXDONE | DCR_RXRESET, hw->base_port + IODCR); return; } @@ -873,18 +875,17 @@ static void do_receive_packet(struct ipw_hardware *hw) pkt[i + 1] = (unsigned char) (data >> 8); } } else { - len = inw(hw->base_port + IODMADPR); + len = inw(hw->base_port); if (len > hw->ll_mtu) { printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": received a packet of %u bytes - " - "longer than the MTU!\n", len); + ": received a packet of %u bytes - longer than the MTU!\n", len); writew(MEMRX_PCINTACKK, &hw->memory_info_regs->memreg_pc_interrupt_ack); return; } for (i = 0; i < len; i += 2) { - __le16 raw_data = inw(hw->base_port + IODMADPR); + __le16 raw_data = inw(hw->base_port); unsigned short data = le16_to_cpu(raw_data); pkt[i] = (unsigned char) data; @@ -892,13 +893,15 @@ static void do_receive_packet(struct ipw_hardware *hw) } while ((i & 3) != 2) { - inw(hw->base_port + IODMADPR); + inw(hw->base_port); i += 2; } } acknowledge_data_read(hw); + swap_packet_bitfield_from_le(pkt); + if (ipwireless_debug) dump_data_bytes("recv", pkt, len); @@ -916,8 +919,7 @@ static int get_current_packet_priority(struct ipw_hardware *hw) * until setup is complete. */ return (hw->to_setup || hw->initializing - ? PRIO_SETUP + 1 : - NL_NUM_OF_PRIORITIES); + ? PRIO_SETUP + 1 : NL_NUM_OF_PRIORITIES); } /* @@ -928,17 +930,17 @@ static int get_packets_from_hw(struct ipw_hardware *hw) int received = 0; unsigned long flags; - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); while (hw->rx_ready && !hw->blocking_rx) { received = 1; hw->rx_ready--; - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); do_receive_packet(hw); - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); } - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); return received; } @@ -954,7 +956,7 @@ static int send_pending_packet(struct ipw_hardware *hw, int priority_limit) int more_to_send = 0; unsigned long flags; - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); if (hw->tx_queued && hw->tx_ready) { int priority; struct ipw_tx_packet *packet = NULL; @@ -975,17 +977,17 @@ static int send_pending_packet(struct ipw_hardware *hw, int priority_limit) } if (!packet) { hw->tx_queued = 0; - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); return 0; } - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); /* Send */ do_send_packet(hw, packet); /* Check if more to send */ - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); for (priority = 0; priority < priority_limit; priority++) if (!list_empty(&hw->tx_queue[priority])) { more_to_send = 1; @@ -995,7 +997,7 @@ static int send_pending_packet(struct ipw_hardware *hw, int priority_limit) if (!more_to_send) hw->tx_queued = 0; } - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); return more_to_send; } @@ -1008,9 +1010,9 @@ static void ipwireless_do_tasklet(unsigned long hw_) struct ipw_hardware *hw = (struct ipw_hardware *) hw_; unsigned long flags; - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); if (hw->shutting_down) { - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); return; } @@ -1019,7 +1021,7 @@ static void ipwireless_do_tasklet(unsigned long hw_) * Initial setup data sent to hardware */ hw->to_setup = 2; - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); ipw_setup_hardware(hw); ipw_send_setup_packet(hw); @@ -1030,7 +1032,7 @@ static void ipwireless_do_tasklet(unsigned long hw_) int priority_limit = get_current_packet_priority(hw); int again; - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); do { again = send_pending_packet(hw, priority_limit); @@ -1068,16 +1070,16 @@ static irqreturn_t ipwireless_handle_v1_interrupt(int irq, /* Transmit complete. */ if (irqn & IR_TXINTR) { ack |= IR_TXINTR; - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); hw->tx_ready = 1; - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); } /* Received data */ if (irqn & IR_RXINTR) { ack |= IR_RXINTR; - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); hw->rx_ready++; - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); } if (ack != 0) { outw(ack, hw->base_port + IOIR); @@ -1128,9 +1130,8 @@ static irqreturn_t ipwireless_handle_v2_v3_interrupt(int irq, } else { return IRQ_NONE; } - } else { + } else return IRQ_NONE; - } } /* @@ -1149,9 +1150,9 @@ static irqreturn_t ipwireless_handle_v2_v3_interrupt(int irq, if (hw->serial_number_detected) { if (memtx_serial != hw->last_memtx_serial) { hw->last_memtx_serial = memtx_serial; - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); hw->rx_ready++; - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); rx = 1; } else /* Ignore 'Timer Recovery' duplicates. */ @@ -1166,18 +1167,18 @@ static irqreturn_t ipwireless_handle_v2_v3_interrupt(int irq, printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": memreg_tx serial num detected\n"); - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); hw->rx_ready++; - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); } rx = 1; } } if (memrxdone & MEMRX_RX_DONE) { writew(0, &hw->memory_info_regs->memreg_rx_done); - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); hw->tx_ready = 1; - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); tx = 1; } if (tx) @@ -1195,8 +1196,7 @@ static irqreturn_t ipwireless_handle_v2_v3_interrupt(int irq, ": spurious interrupt - new_tx mode\n"); else { printk(KERN_WARNING IPWIRELESS_PCCARD_NAME - ": no valid memreg_tx value - " - "switching to the old memreg_tx\n"); + ": no valid memreg_tx value - switching to the old memreg_tx\n"); hw->memreg_tx = &hw->memory_info_regs->memreg_tx_old; try_mem_tx_old = 1; @@ -1211,7 +1211,7 @@ static irqreturn_t ipwireless_handle_v2_v3_interrupt(int irq, return IRQ_HANDLED; } -irqreturn_t ipwireless_interrupt(int irq, void *dev_id, struct pt_regs *regs) +irqreturn_t ipwireless_interrupt(int irq, void *dev_id) { struct ipw_hardware *hw = dev_id; @@ -1226,9 +1226,9 @@ static void flush_packets_to_hw(struct ipw_hardware *hw) int priority_limit; unsigned long flags; - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); priority_limit = get_current_packet_priority(hw); - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); while (send_pending_packet(hw, priority_limit)); } @@ -1238,10 +1238,10 @@ static void send_packet(struct ipw_hardware *hw, int priority, { unsigned long flags; - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); list_add_tail(&packet->queue, &hw->tx_queue[priority]); hw->tx_queued++; - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); flush_packets_to_hw(hw); } @@ -1291,21 +1291,20 @@ static void *alloc_ctrl_packet(int header_size, } int ipwireless_send_packet(struct ipw_hardware *hw, unsigned int channel_idx, - unsigned char *data, unsigned int length, + const unsigned char *data, unsigned int length, void (*callback) (void *cb, unsigned int length), void *callback_data) { struct ipw_tx_packet *packet; - packet = alloc_data_packet(length, - (unsigned char) (channel_idx + 1), - TL_PROTOCOLID_COM_DATA); + packet = alloc_data_packet(length, (channel_idx + 1), + TL_PROTOCOLID_COM_DATA); if (!packet) return -ENOMEM; packet->packet_callback = callback; packet->callback_data = callback_data; - memcpy((unsigned char *) packet + - sizeof(struct ipw_tx_packet), data, length); + memcpy((unsigned char *) packet + sizeof(struct ipw_tx_packet), data, + length); send_packet(hw, PRIO_DATA, packet); return 0; @@ -1321,12 +1320,11 @@ static int set_control_line(struct ipw_hardware *hw, int prio, protocolid = TL_PROTOCOLID_SETUP; packet = alloc_ctrl_packet(sizeof(struct ipw_control_packet), - (unsigned char) (channel_idx + 1), - protocolid, line); + (channel_idx + 1), protocolid, line); if (!packet) return -ENOMEM; packet->header.length = sizeof(struct ipw_control_packet_body); - packet->body.value = (unsigned char) (state == 0 ? 0 : 1); + packet->body.value = (state == 0 ? 0 : 1); send_packet(hw, prio, &packet->header); return 0; } @@ -1504,8 +1502,7 @@ static void handle_setup_get_version_rsp(struct ipw_hardware *hw, if (vers_no == TL_SETUP_VERSION) __handle_setup_get_version_rsp(hw); else - printk(KERN_ERR - IPWIRELESS_PCCARD_NAME + printk(KERN_ERR IPWIRELESS_PCCARD_NAME ": invalid hardware version no %u\n", (unsigned int) vers_no); } @@ -1528,10 +1525,10 @@ static void ipw_send_setup_packet(struct ipw_hardware *hw) static void handle_received_SETUP_packet(struct ipw_hardware *hw, unsigned int address, - unsigned char *data, int len, + const unsigned char *data, int len, int is_last) { - union ipw_setup_rx_msg *rx_msg = (union ipw_setup_rx_msg *) data; + const union ipw_setup_rx_msg *rx_msg = (const union ipw_setup_rx_msg *) data; if (address != ADDR_SETUP_PROT) { printk(KERN_INFO IPWIRELESS_PCCARD_NAME @@ -1629,7 +1626,7 @@ struct ipw_hardware *ipwireless_hardware_create(void) INIT_LIST_HEAD(&hw->rx_queue); INIT_LIST_HEAD(&hw->rx_pool); - spin_lock_init(&hw->spinlock); + spin_lock_init(&hw->lock); tasklet_init(&hw->tasklet, ipwireless_do_tasklet, (unsigned long) hw); INIT_WORK(&hw->work_rx, ipw_receive_data_work); setup_timer(&hw->setup_timer, ipwireless_setup_timer, @@ -1651,8 +1648,8 @@ void ipwireless_init_hardware_v1(struct ipw_hardware *hw, enable_irq(hw->irq); } hw->base_port = base_port; - hw->hw_version = is_v2_card ? HW_VERSION_2 : HW_VERSION_1; - hw->ll_mtu = hw->hw_version == HW_VERSION_1 ? LL_MTU_V1 : LL_MTU_V2; + hw->hw_version = (is_v2_card ? HW_VERSION_2 : HW_VERSION_1); + hw->ll_mtu = (hw->hw_version == HW_VERSION_1 ? LL_MTU_V1 : LL_MTU_V2); hw->memregs_CCR = (struct MEMCCR __iomem *) ((unsigned short __iomem *) attr_memory + 0x200); hw->memory_info_regs = (struct MEMINFREG __iomem *) common_memory; @@ -1695,10 +1692,10 @@ static void ipwireless_setup_timer(unsigned long data) if (is_card_present(hw)) { unsigned long flags; - spin_lock_irqsave(&hw->spinlock, flags); + spin_lock_irqsave(&hw->lock, flags); hw->to_setup = 1; hw->tx_ready = 1; - spin_unlock_irqrestore(&hw->spinlock, flags); + spin_unlock_irqrestore(&hw->lock, flags); tasklet_schedule(&hw->tasklet); } diff --git a/drivers/char/pcmcia/ipwireless/hardware.h b/drivers/char/pcmcia/ipwireless/hardware.h index 19ce5eb266b1..90a8590e43b0 100644 --- a/drivers/char/pcmcia/ipwireless/hardware.h +++ b/drivers/char/pcmcia/ipwireless/hardware.h @@ -34,14 +34,14 @@ struct ipw_network; struct ipw_hardware *ipwireless_hardware_create(void); void ipwireless_hardware_free(struct ipw_hardware *hw); -irqreturn_t ipwireless_interrupt(int irq, void *dev_id, struct pt_regs *regs); +irqreturn_t ipwireless_interrupt(int irq, void *dev_id); int ipwireless_set_DTR(struct ipw_hardware *hw, unsigned int channel_idx, int state); int ipwireless_set_RTS(struct ipw_hardware *hw, unsigned int channel_idx, int state); int ipwireless_send_packet(struct ipw_hardware *hw, unsigned int channel_idx, - unsigned char *data, + const unsigned char *data, unsigned int length, void (*packet_sent_callback) (void *cb, unsigned int length), diff --git a/drivers/char/pcmcia/ipwireless/main.c b/drivers/char/pcmcia/ipwireless/main.c index cc7dcea2d283..5eca7a99afe6 100644 --- a/drivers/char/pcmcia/ipwireless/main.c +++ b/drivers/char/pcmcia/ipwireless/main.c @@ -49,7 +49,7 @@ static void ipwireless_detach(struct pcmcia_device *link); /* Debug mode: more verbose, print sent/recv bytes */ int ipwireless_debug; int ipwireless_loopback; -int ipwireless_out_queue = 1; +int ipwireless_out_queue = 10; module_param_named(debug, ipwireless_debug, int, 0); module_param_named(loopback, ipwireless_loopback, int, 0); @@ -57,7 +57,7 @@ module_param_named(out_queue, ipwireless_out_queue, int, 0); MODULE_PARM_DESC(debug, "switch on debug messages [0]"); MODULE_PARM_DESC(loopback, "debug: enable ras_raw channel [0]"); -MODULE_PARM_DESC(out_queue, "debug: set size of outgoing queue [1]"); +MODULE_PARM_DESC(out_queue, "debug: set size of outgoing PPP queue [10]"); /* Executes in process context. */ static void signalled_reboot_work(struct work_struct *work_reboot) @@ -88,8 +88,6 @@ static int config_ipwireless(struct ipw_dev *ipw) unsigned short buf[64]; cisparse_t parse; unsigned short cor_value; - win_req_t request_attr_memory; - win_req_t request_common_memory; memreq_t memreq_attr_memory; memreq_t memreq_common_memory; @@ -188,6 +186,9 @@ static int config_ipwireless(struct ipw_dev *ipw) goto exit0; } + request_region(link->io.BasePort1, link->io.NumPorts1, + IPWIRELESS_PCCARD_NAME); + /* memory settings */ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; @@ -214,16 +215,16 @@ static int config_ipwireless(struct ipw_dev *ipw) } if (parse.cftable_entry.mem.nwin > 0) { - request_common_memory.Attributes = + ipw->request_common_memory.Attributes = WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM | WIN_ENABLE; - request_common_memory.Base = + ipw->request_common_memory.Base = parse.cftable_entry.mem.win[0].host_addr; - request_common_memory.Size = parse.cftable_entry.mem.win[0].len; - if (request_common_memory.Size < 0x1000) - request_common_memory.Size = 0x1000; - request_common_memory.AccessSpeed = 0; + ipw->request_common_memory.Size = parse.cftable_entry.mem.win[0].len; + if (ipw->request_common_memory.Size < 0x1000) + ipw->request_common_memory.Size = 0x1000; + ipw->request_common_memory.AccessSpeed = 0; - ret = pcmcia_request_window(&link, &request_common_memory, + ret = pcmcia_request_window(&link, &ipw->request_common_memory, &ipw->handle_common_memory); if (ret != CS_SUCCESS) { @@ -246,16 +247,18 @@ static int config_ipwireless(struct ipw_dev *ipw) ipw->is_v2_card = parse.cftable_entry.mem.win[0].len == 0x100; - ipw->common_memory = ioremap(request_common_memory.Base, - request_common_memory.Size); + ipw->common_memory = ioremap(ipw->request_common_memory.Base, + ipw->request_common_memory.Size); + request_mem_region(ipw->request_common_memory.Base, + ipw->request_common_memory.Size, IPWIRELESS_PCCARD_NAME); - request_attr_memory.Attributes = + ipw->request_attr_memory.Attributes = WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_AM | WIN_ENABLE; - request_attr_memory.Base = 0; - request_attr_memory.Size = 0; /* this used to be 0x1000 */ - request_attr_memory.AccessSpeed = 0; + ipw->request_attr_memory.Base = 0; + ipw->request_attr_memory.Size = 0; /* this used to be 0x1000 */ + ipw->request_attr_memory.AccessSpeed = 0; - ret = pcmcia_request_window(&link, &request_attr_memory, + ret = pcmcia_request_window(&link, &ipw->request_attr_memory, &ipw->handle_attr_memory); if (ret != CS_SUCCESS) { @@ -274,8 +277,10 @@ static int config_ipwireless(struct ipw_dev *ipw) goto exit2; } - ipw->attr_memory = ioremap(request_attr_memory.Base, - request_attr_memory.Size); + ipw->attr_memory = ioremap(ipw->request_attr_memory.Base, + ipw->request_attr_memory.Size); + request_mem_region(ipw->request_attr_memory.Base, ipw->request_attr_memory.Size, + IPWIRELESS_PCCARD_NAME); } INIT_WORK(&ipw->work_reboot, signalled_reboot_work); @@ -311,14 +316,13 @@ static int config_ipwireless(struct ipw_dev *ipw) (unsigned int) link->irq.AssignedIRQ); if (ipw->attr_memory && ipw->common_memory) printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": attr memory 0x%08lx-0x%08lx, " - "common memory 0x%08lx-0x%08lx\n", - request_attr_memory.Base, - request_attr_memory.Base - + request_attr_memory.Size - 1, - request_common_memory.Base, - request_common_memory.Base - + request_common_memory.Size - 1); + ": attr memory 0x%08lx-0x%08lx, common memory 0x%08lx-0x%08lx\n", + ipw->request_attr_memory.Base, + ipw->request_attr_memory.Base + + ipw->request_attr_memory.Size - 1, + ipw->request_common_memory.Base, + ipw->request_common_memory.Base + + ipw->request_common_memory.Size - 1); ipw->network = ipwireless_network_create(ipw->hardware); if (!ipw->network) @@ -350,12 +354,16 @@ exit4: pcmcia_disable_device(link); exit3: if (ipw->attr_memory) { + release_mem_region(ipw->request_attr_memory.Base, + ipw->request_attr_memory.Size); iounmap(ipw->attr_memory); pcmcia_release_window(ipw->handle_attr_memory); pcmcia_disable_device(link); } exit2: if (ipw->common_memory) { + release_mem_region(ipw->request_common_memory.Base, + ipw->request_common_memory.Size); iounmap(ipw->common_memory); pcmcia_release_window(ipw->handle_common_memory); } @@ -367,19 +375,25 @@ exit0: static void release_ipwireless(struct ipw_dev *ipw) { - struct pcmcia_device *link = ipw->link; - - pcmcia_disable_device(link); + pcmcia_disable_device(ipw->link); - if (ipw->common_memory) + if (ipw->common_memory) { + release_mem_region(ipw->request_common_memory.Base, + ipw->request_common_memory.Size); iounmap(ipw->common_memory); - if (ipw->attr_memory) + } + if (ipw->attr_memory) { + release_mem_region(ipw->request_attr_memory.Base, + ipw->request_attr_memory.Size); iounmap(ipw->attr_memory); + } if (ipw->common_memory) pcmcia_release_window(ipw->handle_common_memory); if (ipw->attr_memory) pcmcia_release_window(ipw->handle_attr_memory); - pcmcia_disable_device(link); + + /* Break the link with Card Services */ + pcmcia_disable_device(ipw->link); } /* @@ -437,10 +451,6 @@ static void ipwireless_detach(struct pcmcia_device *link) release_ipwireless(ipw); - /* Break the link with Card Services */ - if (link) - pcmcia_disable_device(link); - if (ipw->tty != NULL) ipwireless_tty_free(ipw->tty); if (ipw->network != NULL) diff --git a/drivers/char/pcmcia/ipwireless/main.h b/drivers/char/pcmcia/ipwireless/main.h index 1bfdcc8d47d6..0e0363af9ab2 100644 --- a/drivers/char/pcmcia/ipwireless/main.h +++ b/drivers/char/pcmcia/ipwireless/main.h @@ -45,10 +45,15 @@ struct ipw_tty; struct ipw_dev { struct pcmcia_device *link; int is_v2_card; + window_handle_t handle_attr_memory; void __iomem *attr_memory; + win_req_t request_attr_memory; + window_handle_t handle_common_memory; void __iomem *common_memory; + win_req_t request_common_memory; + dev_node_t nodes[2]; /* Reference to attribute memory, containing CIS data */ void *attribute_memory; diff --git a/drivers/char/pcmcia/ipwireless/network.c b/drivers/char/pcmcia/ipwireless/network.c index fe914d34f7f6..590762a7f217 100644 --- a/drivers/char/pcmcia/ipwireless/network.c +++ b/drivers/char/pcmcia/ipwireless/network.c @@ -29,7 +29,6 @@ #include "main.h" #include "tty.h" -#define MAX_OUTGOING_PACKETS_QUEUED ipwireless_out_queue #define MAX_ASSOCIATED_TTYS 2 #define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP) @@ -46,7 +45,7 @@ struct ipw_network { /* Number of packets queued up in hardware module. */ int outgoing_packets_queued; /* Spinlock to avoid interrupts during shutdown */ - spinlock_t spinlock; + spinlock_t lock; struct mutex close_lock; /* PPP ioctl data, not actually used anywere */ @@ -68,20 +67,20 @@ static void notify_packet_sent(void *callback_data, unsigned int packet_length) struct ipw_network *network = callback_data; unsigned long flags; - spin_lock_irqsave(&network->spinlock, flags); + spin_lock_irqsave(&network->lock, flags); network->outgoing_packets_queued--; if (network->ppp_channel != NULL) { if (network->ppp_blocked) { network->ppp_blocked = 0; - spin_unlock_irqrestore(&network->spinlock, flags); + spin_unlock_irqrestore(&network->lock, flags); ppp_output_wakeup(network->ppp_channel); if (ipwireless_debug) - printk(KERN_INFO IPWIRELESS_PCCARD_NAME + printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": ppp unblocked\n"); } else - spin_unlock_irqrestore(&network->spinlock, flags); + spin_unlock_irqrestore(&network->lock, flags); } else - spin_unlock_irqrestore(&network->spinlock, flags); + spin_unlock_irqrestore(&network->lock, flags); } /* @@ -93,8 +92,8 @@ static int ipwireless_ppp_start_xmit(struct ppp_channel *ppp_channel, struct ipw_network *network = ppp_channel->private; unsigned long flags; - spin_lock_irqsave(&network->spinlock, flags); - if (network->outgoing_packets_queued < MAX_OUTGOING_PACKETS_QUEUED) { + spin_lock_irqsave(&network->lock, flags); + if (network->outgoing_packets_queued < ipwireless_out_queue) { unsigned char *buf; static unsigned char header[] = { PPP_ALLSTATIONS, /* 0xff */ @@ -103,7 +102,7 @@ static int ipwireless_ppp_start_xmit(struct ppp_channel *ppp_channel, int ret; network->outgoing_packets_queued++; - spin_unlock_irqrestore(&network->spinlock, flags); + spin_unlock_irqrestore(&network->lock, flags); /* * If we have the requested amount of headroom in the skb we @@ -144,7 +143,9 @@ static int ipwireless_ppp_start_xmit(struct ppp_channel *ppp_channel, * needs to be unblocked once we are ready to send. */ network->ppp_blocked = 1; - spin_unlock_irqrestore(&network->spinlock, flags); + spin_unlock_irqrestore(&network->lock, flags); + if (ipwireless_debug) + printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": ppp blocked\n"); return 0; } } @@ -249,11 +250,11 @@ static void do_go_online(struct work_struct *work_go_online) work_go_online); unsigned long flags; - spin_lock_irqsave(&network->spinlock, flags); + spin_lock_irqsave(&network->lock, flags); if (!network->ppp_channel) { struct ppp_channel *channel; - spin_unlock_irqrestore(&network->spinlock, flags); + spin_unlock_irqrestore(&network->lock, flags); channel = kzalloc(sizeof(struct ppp_channel), GFP_KERNEL); if (!channel) { printk(KERN_ERR IPWIRELESS_PCCARD_NAME @@ -273,10 +274,10 @@ static void do_go_online(struct work_struct *work_go_online) network->xaccm[3] = 0x60000000U; network->raccm = ~0U; ppp_register_channel(channel); - spin_lock_irqsave(&network->spinlock, flags); + spin_lock_irqsave(&network->lock, flags); network->ppp_channel = channel; } - spin_unlock_irqrestore(&network->spinlock, flags); + spin_unlock_irqrestore(&network->lock, flags); } static void do_go_offline(struct work_struct *work_go_offline) @@ -287,16 +288,16 @@ static void do_go_offline(struct work_struct *work_go_offline) unsigned long flags; mutex_lock(&network->close_lock); - spin_lock_irqsave(&network->spinlock, flags); + spin_lock_irqsave(&network->lock, flags); if (network->ppp_channel != NULL) { struct ppp_channel *channel = network->ppp_channel; network->ppp_channel = NULL; - spin_unlock_irqrestore(&network->spinlock, flags); + spin_unlock_irqrestore(&network->lock, flags); mutex_unlock(&network->close_lock); ppp_unregister_channel(channel); } else { - spin_unlock_irqrestore(&network->spinlock, flags); + spin_unlock_irqrestore(&network->lock, flags); mutex_unlock(&network->close_lock); } } @@ -381,18 +382,18 @@ void ipwireless_network_packet_received(struct ipw_network *network, * the PPP layer. */ mutex_lock(&network->close_lock); - spin_lock_irqsave(&network->spinlock, flags); + spin_lock_irqsave(&network->lock, flags); if (network->ppp_channel != NULL) { struct sk_buff *skb; - spin_unlock_irqrestore(&network->spinlock, + spin_unlock_irqrestore(&network->lock, flags); /* Send the data to the ppp_generic module. */ skb = ipw_packet_received_skb(data, length); ppp_input(network->ppp_channel, skb); } else - spin_unlock_irqrestore(&network->spinlock, + spin_unlock_irqrestore(&network->lock, flags); mutex_unlock(&network->close_lock); } @@ -410,7 +411,7 @@ struct ipw_network *ipwireless_network_create(struct ipw_hardware *hw) if (!network) return NULL; - spin_lock_init(&network->spinlock); + spin_lock_init(&network->lock); mutex_init(&network->close_lock); network->hardware = hw; @@ -478,10 +479,10 @@ int ipwireless_ppp_channel_index(struct ipw_network *network) int ret = -1; unsigned long flags; - spin_lock_irqsave(&network->spinlock, flags); + spin_lock_irqsave(&network->lock, flags); if (network->ppp_channel != NULL) ret = ppp_channel_index(network->ppp_channel); - spin_unlock_irqrestore(&network->spinlock, flags); + spin_unlock_irqrestore(&network->lock, flags); return ret; } @@ -491,10 +492,15 @@ int ipwireless_ppp_unit_number(struct ipw_network *network) int ret = -1; unsigned long flags; - spin_lock_irqsave(&network->spinlock, flags); + spin_lock_irqsave(&network->lock, flags); if (network->ppp_channel != NULL) ret = ppp_unit_number(network->ppp_channel); - spin_unlock_irqrestore(&network->spinlock, flags); + spin_unlock_irqrestore(&network->lock, flags); return ret; } + +int ipwireless_ppp_mru(const struct ipw_network *network) +{ + return network->mru; +} diff --git a/drivers/char/pcmcia/ipwireless/network.h b/drivers/char/pcmcia/ipwireless/network.h index ccacd26fc7ef..561f765b3334 100644 --- a/drivers/char/pcmcia/ipwireless/network.h +++ b/drivers/char/pcmcia/ipwireless/network.h @@ -48,5 +48,6 @@ void ipwireless_ppp_open(struct ipw_network *net); void ipwireless_ppp_close(struct ipw_network *net); int ipwireless_ppp_channel_index(struct ipw_network *net); int ipwireless_ppp_unit_number(struct ipw_network *net); +int ipwireless_ppp_mru(const struct ipw_network *net); #endif diff --git a/drivers/char/pcmcia/ipwireless/tty.c b/drivers/char/pcmcia/ipwireless/tty.c index 42f3815c5ce3..b1414507997c 100644 --- a/drivers/char/pcmcia/ipwireless/tty.c +++ b/drivers/char/pcmcia/ipwireless/tty.c @@ -259,7 +259,7 @@ static int ipw_write(struct tty_struct *linux_tty, } ret = ipwireless_send_packet(tty->hardware, IPW_CHANNEL_RAS, - (unsigned char *) buf, count, + buf, count, ipw_write_packet_sent_callback, tty); if (ret == -1) { mutex_unlock(&tty->ipw_tty_mutex); diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index b694d430f10e..d1fceabe3aef 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -2230,7 +2230,7 @@ static int tiocmset(struct tty_struct *tty, struct file *file, * Arguments: tty pointer to tty instance data * break_state -1=set break condition, 0=clear */ -static void mgslpc_break(struct tty_struct *tty, int break_state) +static int mgslpc_break(struct tty_struct *tty, int break_state) { MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data; unsigned long flags; @@ -2240,7 +2240,7 @@ static void mgslpc_break(struct tty_struct *tty, int break_state) __FILE__,__LINE__, info->device_name, break_state); if (mgslpc_paranoia_check(info, tty->name, "mgslpc_break")) - return; + return -EINVAL; spin_lock_irqsave(&info->lock,flags); if (break_state == -1) @@ -2248,6 +2248,7 @@ static void mgslpc_break(struct tty_struct *tty, int break_state) else clear_reg_bits(info, CHA+DAFO, BIT6); spin_unlock_irqrestore(&info->lock,flags); + return 0; } /* Service an IOCTL request diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index f6e6acadd9a0..bee39fdfba73 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -67,7 +67,7 @@ #include <linux/major.h> #include <linux/ppdev.h> #include <linux/smp_lock.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #define PP_VERSION "ppdev: user-space parallel port driver" #define CHRDEV "ppdev" @@ -328,10 +328,9 @@ static enum ieee1284_phase init_phase (int mode) return IEEE1284_PH_FWD_IDLE; } -static int pp_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - unsigned int minor = iminor(inode); + unsigned int minor = iminor(file->f_path.dentry->d_inode); struct pp_struct *pp = file->private_data; struct parport * port; void __user *argp = (void __user *)arg; @@ -634,6 +633,15 @@ static int pp_ioctl(struct inode *inode, struct file *file, return 0; } +static long pp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + long ret; + lock_kernel(); + ret = pp_do_ioctl(file, cmd, arg); + unlock_kernel(); + return ret; +} + static int pp_open (struct inode * inode, struct file * file) { unsigned int minor = iminor(inode); @@ -745,15 +753,16 @@ static const struct file_operations pp_fops = { .read = pp_read, .write = pp_write, .poll = pp_poll, - .ioctl = pp_ioctl, + .unlocked_ioctl = pp_ioctl, .open = pp_open, .release = pp_release, }; static void pp_attach(struct parport *port) { - device_create(ppdev_class, port->dev, MKDEV(PP_MAJOR, port->number), - "parport%d", port->number); + device_create_drvdata(ppdev_class, port->dev, + MKDEV(PP_MAJOR, port->number), + NULL, "parport%d", port->number); } static void pp_detach(struct parport *port) diff --git a/drivers/char/random.c b/drivers/char/random.c index 0cf98bd4f2d2..e0d0e371909c 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -236,6 +236,7 @@ #include <linux/fs.h> #include <linux/genhd.h> #include <linux/interrupt.h> +#include <linux/mm.h> #include <linux/spinlock.h> #include <linux/percpu.h> #include <linux/cryptohash.h> diff --git a/drivers/char/raw.c b/drivers/char/raw.c index 505fcbe884a4..47b8cf281d4a 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c @@ -131,8 +131,8 @@ raw_ioctl(struct inode *inode, struct file *filp, static void bind_device(struct raw_config_request *rq) { device_destroy(raw_class, MKDEV(RAW_MAJOR, rq->raw_minor)); - device_create(raw_class, NULL, MKDEV(RAW_MAJOR, rq->raw_minor), - "raw%d", rq->raw_minor); + device_create_drvdata(raw_class, NULL, MKDEV(RAW_MAJOR, rq->raw_minor), + NULL, "raw%d", rq->raw_minor); } /* @@ -283,7 +283,8 @@ static int __init raw_init(void) ret = PTR_ERR(raw_class); goto error_region; } - device_create(raw_class, NULL, MKDEV(RAW_MAJOR, 0), "rawctl"); + device_create_drvdata(raw_class, NULL, MKDEV(RAW_MAJOR, 0), NULL, + "rawctl"); return 0; diff --git a/drivers/char/rio/rio_linux.c b/drivers/char/rio/rio_linux.c index 0cdfee152916..a8f68a3f14dd 100644 --- a/drivers/char/rio/rio_linux.c +++ b/drivers/char/rio/rio_linux.c @@ -179,7 +179,7 @@ static int rio_set_real_termios(void *ptr); static void rio_hungup(void *ptr); static void rio_close(void *ptr); static int rio_chars_in_buffer(void *ptr); -static int rio_fw_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +static long rio_fw_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); static int rio_init_drivers(void); static void my_hd(void *addr, int len); @@ -240,7 +240,7 @@ static struct real_driver rio_real_driver = { static const struct file_operations rio_fw_fops = { .owner = THIS_MODULE, - .ioctl = rio_fw_ioctl, + .unlocked_ioctl = rio_fw_ioctl, }; static struct miscdevice rio_fw_device = { @@ -560,13 +560,15 @@ static void rio_close(void *ptr) -static int rio_fw_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +static long rio_fw_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int rc = 0; func_enter(); /* The "dev" argument isn't used. */ + lock_kernel(); rc = riocontrol(p, 0, cmd, arg, capable(CAP_SYS_ADMIN)); + unlock_kernel(); func_exit(); return rc; diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 724b2b20f4b2..2c6c8f33d6b4 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -1250,11 +1250,15 @@ static int rc_tiocmset(struct tty_struct *tty, struct file *file, return 0; } -static void rc_send_break(struct riscom_port *port, unsigned long length) +static int rc_send_break(struct tty_struct *tty, int length) { + struct riscom_port *port = (struct riscom_port *)tty->driver_data; struct riscom_board *bp = port_Board(port); unsigned long flags; + if (length == 0 || length == -1) + return -EOPNOTSUPP; + spin_lock_irqsave(&riscom_lock, flags); port->break_length = RISCOM_TPS / HZ * length; @@ -1268,6 +1272,7 @@ static void rc_send_break(struct riscom_port *port, unsigned long length) rc_wait_CCR(bp); spin_unlock_irqrestore(&riscom_lock, flags); + return 0; } static int rc_set_serial_info(struct riscom_port *port, @@ -1342,27 +1347,12 @@ static int rc_ioctl(struct tty_struct *tty, struct file *filp, { struct riscom_port *port = (struct riscom_port *)tty->driver_data; void __user *argp = (void __user *)arg; - int retval = 0; + int retval; if (rc_paranoia_check(port, tty->name, "rc_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) - rc_send_break(port, HZ/4); /* 1/4 second */ - break; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - rc_send_break(port, arg ? arg*(HZ/10) : HZ/4); - break; case TIOCGSERIAL: lock_kernel(); retval = rc_get_serial_info(port, argp); @@ -1517,6 +1507,7 @@ static const struct tty_operations riscom_ops = { .hangup = rc_hangup, .tiocmget = rc_tiocmget, .tiocmset = rc_tiocmset, + .break_ctl = rc_send_break, }; static int __init rc_init_drivers(void) @@ -1538,7 +1529,7 @@ static int __init rc_init_drivers(void) B9600 | CS8 | CREAD | HUPCL | CLOCAL; riscom_driver->init_termios.c_ispeed = 9600; riscom_driver->init_termios.c_ospeed = 9600; - riscom_driver->flags = TTY_DRIVER_REAL_RAW; + riscom_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_HARDWARE_BREAK; tty_set_operations(riscom_driver, &riscom_ops); error = tty_register_driver(riscom_driver); if (error != 0) { diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index e670eae2f510..584d791e84a6 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -1236,13 +1236,13 @@ static void rp_set_termios(struct tty_struct *tty, } } -static void rp_break(struct tty_struct *tty, int break_state) +static int rp_break(struct tty_struct *tty, int break_state) { struct r_port *info = (struct r_port *) tty->driver_data; unsigned long flags; if (rocket_paranoia_check(info, "rp_break")) - return; + return -EINVAL; spin_lock_irqsave(&info->slock, flags); if (break_state == -1) @@ -1250,6 +1250,7 @@ static void rp_break(struct tty_struct *tty, int break_state) else sClrBreak(&info->channel); spin_unlock_irqrestore(&info->slock, flags); + return 0; } /* diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index fa92a8af5a5a..d9799e2bcfbf 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -78,9 +78,10 @@ #include <linux/wait.h> #include <linux/bcd.h> #include <linux/delay.h> +#include <linux/smp_lock.h> +#include <linux/uaccess.h> #include <asm/current.h> -#include <asm/uaccess.h> #include <asm/system.h> #ifdef CONFIG_X86 @@ -120,8 +121,6 @@ static irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id) return 0; } #endif -#else -extern irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id); #endif /* @@ -144,8 +143,8 @@ static DEFINE_TIMER(rtc_irq_timer, rtc_dropped_irq, 0, 0); static ssize_t rtc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos); -static int rtc_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); +static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +static void rtc_get_rtc_time(struct rtc_time *rtc_tm); #ifdef RTC_IRQ static unsigned int rtc_poll(struct file *file, poll_table *wait); @@ -237,7 +236,7 @@ static inline unsigned char rtc_is_updating(void) * (See ./arch/XXXX/kernel/time.c for the set_rtc_mmss() function.) */ -irqreturn_t rtc_interrupt(int irq, void *dev_id) +static irqreturn_t rtc_interrupt(int irq, void *dev_id) { /* * Can be an alarm interrupt, update complete interrupt, @@ -719,10 +718,13 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel) &wtime, sizeof wtime) ? -EFAULT : 0; } -static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) +static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - return rtc_do_ioctl(cmd, arg, 0); + long ret; + lock_kernel(); + ret = rtc_do_ioctl(cmd, arg, 0); + unlock_kernel(); + return ret; } /* @@ -915,7 +917,7 @@ static const struct file_operations rtc_fops = { #ifdef RTC_IRQ .poll = rtc_poll, #endif - .ioctl = rtc_ioctl, + .unlocked_ioctl = rtc_ioctl, .open = rtc_open, .release = rtc_release, .fasync = rtc_fasync, @@ -1302,7 +1304,7 @@ static int rtc_proc_open(struct inode *inode, struct file *file) } #endif -void rtc_get_rtc_time(struct rtc_time *rtc_tm) +static void rtc_get_rtc_time(struct rtc_time *rtc_tm) { unsigned long uip_watchdog = jiffies, flags; unsigned char ctrl; diff --git a/drivers/char/ser_a2232.c b/drivers/char/ser_a2232.c index 4ba3aec9e1cd..7b0c35207d9b 100644 --- a/drivers/char/ser_a2232.c +++ b/drivers/char/ser_a2232.c @@ -192,7 +192,7 @@ static inline void a2232_receive_char(struct a2232_port *port, int ch, int err) Maybe one could implement a more efficient version by not only transferring one character at a time. */ - struct tty_struct *tty = port->gs.tty; + struct tty_struct *tty = port->gs.port.tty; #if 0 switch(err) { @@ -226,7 +226,7 @@ static void a2232_disable_tx_interrupts(void *ptr) /* Does this here really have to be? */ local_irq_save(flags); - port->gs.flags &= ~GS_TX_INTEN; + port->gs.port.flags &= ~GS_TX_INTEN; local_irq_restore(flags); } @@ -242,7 +242,7 @@ static void a2232_enable_tx_interrupts(void *ptr) /* Does this here really have to be? */ local_irq_save(flags); - port->gs.flags |= GS_TX_INTEN; + port->gs.port.flags |= GS_TX_INTEN; local_irq_restore(flags); } @@ -276,9 +276,9 @@ static void a2232_shutdown_port(void *ptr) local_irq_save(flags); - port->gs.flags &= ~GS_ACTIVE; + port->gs.port.flags &= ~GS_ACTIVE; - if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL) { + if (port->gs.port.tty && port->gs.port.tty->termios->c_cflag & HUPCL) { /* Set DTR and RTS to Low, flush output. The NetBSD driver "msc.c" does it this way. */ stat->Command = ( (stat->Command & ~A2232CMD_CMask) | @@ -309,7 +309,7 @@ static int a2232_set_real_termios(void *ptr) volatile struct a2232status *status; volatile struct a2232memory *mem; - if (!port->gs.tty || !port->gs.tty->termios) return 0; + if (!port->gs.port.tty || !port->gs.port.tty->termios) return 0; status = a2232stat(port->which_a2232, port->which_port_on_a2232); mem = a2232mem(port->which_a2232); @@ -345,7 +345,7 @@ static int a2232_set_real_termios(void *ptr) } a2232_param |= rate; - cflag = port->gs.tty->termios->c_cflag; + cflag = port->gs.port.tty->termios->c_cflag; // get character size chsize = cflag & CSIZE; @@ -382,7 +382,7 @@ static int a2232_set_real_termios(void *ptr) the conventional way of inserting START/STOP characters by hand in throttle()/unthrottle(). */ - softflow = !!( port->gs.tty->termios->c_iflag & IXOFF ); + softflow = !!( port->gs.port.tty->termios->c_iflag & IXOFF ); // get Parity (Enabled/Disabled? If Enabled, Odd or Even?) parity = cflag & (PARENB | PARODD); @@ -400,9 +400,9 @@ static int a2232_set_real_termios(void *ptr) /* Hmm. Maybe an own a2232_port structure member would be cleaner? */ if (cflag & CLOCAL) - port->gs.flags &= ~ASYNC_CHECK_CD; + port->gs.port.flags &= ~ASYNC_CHECK_CD; else - port->gs.flags |= ASYNC_CHECK_CD; + port->gs.port.flags |= ASYNC_CHECK_CD; /* Now we have all parameters and can go to set them: */ @@ -482,18 +482,18 @@ static int a2232_open(struct tty_struct * tty, struct file * filp) port = &a2232_ports[line]; tty->driver_data = port; - port->gs.tty = tty; - port->gs.count++; + port->gs.port.tty = tty; + port->gs.port.count++; retval = gs_init_port(&port->gs); if (retval) { - port->gs.count--; + port->gs.port.count--; return retval; } - port->gs.flags |= GS_ACTIVE; + port->gs.port.flags |= GS_ACTIVE; retval = gs_block_til_ready(port, filp); if (retval) { - port->gs.count--; + port->gs.port.count--; return retval; } @@ -522,7 +522,7 @@ int ch, err, n, p; for (p = 0; p < NUMLINES; p++){ /* for every port on this board */ err = 0; port = &a2232_ports[n*NUMLINES+p]; - if ( port->gs.flags & GS_ACTIVE ){ /* if the port is used */ + if ( port->gs.port.flags & GS_ACTIVE ){ /* if the port is used */ status = a2232stat(n,p); @@ -577,8 +577,8 @@ int ch, err, n, p; obuf = mem->OutBuf[p]; bufpos = status->OutHead; while ( (port->gs.xmit_cnt > 0) && - (!port->gs.tty->stopped) && - (!port->gs.tty->hw_stopped) ){ /* While there are chars to transmit */ + (!port->gs.port.tty->stopped) && + (!port->gs.port.tty->hw_stopped) ){ /* While there are chars to transmit */ if (((bufpos+1) & A2232_IOBUFLENMASK) != status->OutTail) { /* If the A2232 buffer is not full */ ch = port->gs.xmit_buf[port->gs.xmit_tail]; /* get the next char to transmit */ port->gs.xmit_tail = (port->gs.xmit_tail+1) & (SERIAL_XMIT_SIZE-1); /* modulo-addition for the gs.xmit_buf ring-buffer */ @@ -592,8 +592,8 @@ int ch, err, n, p; status->OutHead = bufpos; /* WakeUp if output buffer runs low */ - if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.tty) { - tty_wakeup(port->gs.tty); + if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.port.tty) { + tty_wakeup(port->gs.port.tty); } } // if the port is used } // for every port on the board @@ -613,16 +613,16 @@ int ch, err, n, p; struct a2232_port *port = &a2232_ports[n*7+p]; port->cd_status = !(ncd & 1); /* ncd&1 <=> CD is now off */ - if (!(port->gs.flags & ASYNC_CHECK_CD)) + if (!(port->gs.port.flags & ASYNC_CHECK_CD)) ; /* Don't report DCD changes */ else if (port->cd_status) { // if DCD on: DCD went UP! /* Are we blocking in open?*/ - wake_up_interruptible(&port->gs.open_wait); + wake_up_interruptible(&port->gs.port.open_wait); } else { // if DCD off: DCD went DOWN! - if (port->gs.tty) - tty_hangup (port->gs.tty); + if (port->gs.port.tty) + tty_hangup (port->gs.port.tty); } } // if CD changed for this port @@ -655,8 +655,8 @@ static void a2232_init_portstructs(void) #ifdef NEW_WRITE_LOCKING mutex_init(&(port->gs.port_write_mutex)); #endif - init_waitqueue_head(&port->gs.open_wait); - init_waitqueue_head(&port->gs.close_wait); + init_waitqueue_head(&port->gs.port.open_wait); + init_waitqueue_head(&port->gs.port.close_wait); } } diff --git a/drivers/char/snsc.c b/drivers/char/snsc.c index 0b799ac1b049..3ce60df14c0a 100644 --- a/drivers/char/snsc.c +++ b/drivers/char/snsc.c @@ -444,7 +444,8 @@ scdrv_init(void) continue; } - device_create(snsc_class, NULL, dev, "%s", devname); + device_create_drvdata(snsc_class, NULL, dev, NULL, + "%s", devname); ia64_sn_irtr_intr_enable(scd->scd_nasid, 0 /*ignored */ , diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index 037dc47e4cb1..242fd46fda22 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -77,7 +77,7 @@ #include <linux/module.h> -#include <asm/io.h> +#include <linux/io.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/ioport.h> @@ -92,7 +92,7 @@ #include <linux/delay.h> #include <linux/pci.h> #include <linux/init.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include "specialix_io8.h" #include "cd1865.h" @@ -110,9 +110,10 @@ static int sx_debug; static int sx_rxfifo = SPECIALIX_RXFIFO; +static int sx_rtscts; #ifdef DEBUG -#define dprintk(f, str...) if (sx_debug & f) printk (str) +#define dprintk(f, str...) if (sx_debug & f) printk(str) #else #define dprintk(f, str...) /* nothing */ #endif @@ -131,10 +132,8 @@ static int sx_rxfifo = SPECIALIX_RXFIFO; #define SX_DEBUG_FIFO 0x0800 -#define func_enter() dprintk (SX_DEBUG_FLOW, "io8: enter %s\n",__func__) -#define func_exit() dprintk (SX_DEBUG_FLOW, "io8: exit %s\n", __func__) - -#define jiffies_from_ms(a) ((((a) * HZ)/1000)+1) +#define func_enter() dprintk(SX_DEBUG_FLOW, "io8: enter %s\n", __func__) +#define func_exit() dprintk(SX_DEBUG_FLOW, "io8: exit %s\n", __func__) /* Configurable options: */ @@ -142,17 +141,6 @@ static int sx_rxfifo = SPECIALIX_RXFIFO; /* Am I paranoid or not ? ;-) */ #define SPECIALIX_PARANOIA_CHECK -/* Do I trust the IRQ from the card? (enabeling it doesn't seem to help) - When the IRQ routine leaves the chip in a state that is keeps on - requiring attention, the timer doesn't help either. */ -#undef SPECIALIX_TIMER - -#ifdef SPECIALIX_TIMER -static int sx_poll = HZ; -#endif - - - /* * The following defines are mostly for testing purposes. But if you need * some nice reporting in your syslog, you can define them also. @@ -162,16 +150,6 @@ static int sx_poll = HZ; -#ifdef CONFIG_SPECIALIX_RTSCTS -#define SX_CRTSCTS(bla) 1 -#else -#define SX_CRTSCTS(tty) C_CRTSCTS(tty) -#endif - - -/* Used to be outb (0xff, 0x80); */ -#define short_pause() udelay (1) - #define SPECIALIX_LEGAL_FLAGS \ (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \ @@ -190,21 +168,14 @@ static struct specialix_board sx_board[SX_NBOARD] = { static struct specialix_port sx_port[SX_NBOARD * SX_NPORT]; -#ifdef SPECIALIX_TIMER -static struct timer_list missed_irq_timer; -static irqreturn_t sx_interrupt(int irq, void * dev_id); -#endif - - - -static inline int sx_paranoia_check(struct specialix_port const * port, +static int sx_paranoia_check(struct specialix_port const *port, char *name, const char *routine) { #ifdef SPECIALIX_PARANOIA_CHECK - static const char *badmagic = - KERN_ERR "sx: Warning: bad specialix port magic number for device %s in %s\n"; - static const char *badinfo = - KERN_ERR "sx: Warning: null specialix port for device %s in %s\n"; + static const char *badmagic = KERN_ERR + "sx: Warning: bad specialix port magic number for device %s in %s\n"; + static const char *badinfo = KERN_ERR + "sx: Warning: null specialix port for device %s in %s\n"; if (!port) { printk(badinfo, name, routine); @@ -226,66 +197,69 @@ static inline int sx_paranoia_check(struct specialix_port const * port, */ /* Get board number from pointer */ -static inline int board_No (struct specialix_board * bp) +static inline int board_No(struct specialix_board *bp) { return bp - sx_board; } /* Get port number from pointer */ -static inline int port_No (struct specialix_port const * port) +static inline int port_No(struct specialix_port const *port) { return SX_PORT(port - sx_port); } /* Get pointer to board from pointer to port */ -static inline struct specialix_board * port_Board(struct specialix_port const * port) +static inline struct specialix_board *port_Board( + struct specialix_port const *port) { return &sx_board[SX_BOARD(port - sx_port)]; } /* Input Byte from CL CD186x register */ -static inline unsigned char sx_in(struct specialix_board * bp, unsigned short reg) +static inline unsigned char sx_in(struct specialix_board *bp, + unsigned short reg) { bp->reg = reg | 0x80; - outb (reg | 0x80, bp->base + SX_ADDR_REG); - return inb (bp->base + SX_DATA_REG); + outb(reg | 0x80, bp->base + SX_ADDR_REG); + return inb(bp->base + SX_DATA_REG); } /* Output Byte to CL CD186x register */ -static inline void sx_out(struct specialix_board * bp, unsigned short reg, +static inline void sx_out(struct specialix_board *bp, unsigned short reg, unsigned char val) { bp->reg = reg | 0x80; - outb (reg | 0x80, bp->base + SX_ADDR_REG); - outb (val, bp->base + SX_DATA_REG); + outb(reg | 0x80, bp->base + SX_ADDR_REG); + outb(val, bp->base + SX_DATA_REG); } /* Input Byte from CL CD186x register */ -static inline unsigned char sx_in_off(struct specialix_board * bp, unsigned short reg) +static inline unsigned char sx_in_off(struct specialix_board *bp, + unsigned short reg) { bp->reg = reg; - outb (reg, bp->base + SX_ADDR_REG); - return inb (bp->base + SX_DATA_REG); + outb(reg, bp->base + SX_ADDR_REG); + return inb(bp->base + SX_DATA_REG); } /* Output Byte to CL CD186x register */ -static inline void sx_out_off(struct specialix_board * bp, unsigned short reg, - unsigned char val) +static inline void sx_out_off(struct specialix_board *bp, + unsigned short reg, unsigned char val) { bp->reg = reg; - outb (reg, bp->base + SX_ADDR_REG); - outb (val, bp->base + SX_DATA_REG); + outb(reg, bp->base + SX_ADDR_REG); + outb(val, bp->base + SX_DATA_REG); } /* Wait for Channel Command Register ready */ -static inline void sx_wait_CCR(struct specialix_board * bp) +static void sx_wait_CCR(struct specialix_board *bp) { unsigned long delay, flags; unsigned char ccr; @@ -296,7 +270,7 @@ static inline void sx_wait_CCR(struct specialix_board * bp) spin_unlock_irqrestore(&bp->lock, flags); if (!ccr) return; - udelay (1); + udelay(1); } printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp)); @@ -304,7 +278,7 @@ static inline void sx_wait_CCR(struct specialix_board * bp) /* Wait for Channel Command Register ready */ -static inline void sx_wait_CCR_off(struct specialix_board * bp) +static void sx_wait_CCR_off(struct specialix_board *bp) { unsigned long delay; unsigned char crr; @@ -316,7 +290,7 @@ static inline void sx_wait_CCR_off(struct specialix_board * bp) spin_unlock_irqrestore(&bp->lock, flags); if (!crr) return; - udelay (1); + udelay(1); } printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp)); @@ -327,7 +301,7 @@ static inline void sx_wait_CCR_off(struct specialix_board * bp) * specialix IO8+ IO range functions. */ -static inline int sx_request_io_range(struct specialix_board * bp) +static int sx_request_io_range(struct specialix_board *bp) { return request_region(bp->base, bp->flags & SX_BOARD_IS_PCI ? SX_PCI_IO_SPACE : SX_IO_SPACE, @@ -335,15 +309,15 @@ static inline int sx_request_io_range(struct specialix_board * bp) } -static inline void sx_release_io_range(struct specialix_board * bp) +static void sx_release_io_range(struct specialix_board *bp) { - release_region(bp->base, - bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE); + release_region(bp->base, bp->flags & SX_BOARD_IS_PCI ? + SX_PCI_IO_SPACE : SX_IO_SPACE); } /* Set the IRQ using the RTS lines that run to the PAL on the board.... */ -static int sx_set_irq ( struct specialix_board *bp) +static int sx_set_irq(struct specialix_board *bp) { int virq; int i; @@ -353,15 +327,24 @@ static int sx_set_irq ( struct specialix_board *bp) return 1; switch (bp->irq) { /* In the same order as in the docs... */ - case 15: virq = 0;break; - case 12: virq = 1;break; - case 11: virq = 2;break; - case 9: virq = 3;break; - default: printk (KERN_ERR "Speclialix: cannot set irq to %d.\n", bp->irq); - return 0; + case 15: + virq = 0; + break; + case 12: + virq = 1; + break; + case 11: + virq = 2; + break; + case 9: + virq = 3; + break; + default:printk(KERN_ERR + "Speclialix: cannot set irq to %d.\n", bp->irq); + return 0; } spin_lock_irqsave(&bp->lock, flags); - for (i=0;i<2;i++) { + for (i = 0; i < 2; i++) { sx_out(bp, CD186x_CAR, i); sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0); } @@ -371,7 +354,7 @@ static int sx_set_irq ( struct specialix_board *bp) /* Reset and setup CD186x chip */ -static int sx_init_CD186x(struct specialix_board * bp) +static int sx_init_CD186x(struct specialix_board *bp) { unsigned long flags; int scaler; @@ -390,7 +373,7 @@ static int sx_init_CD186x(struct specialix_board * bp) sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */ sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */ /* Set RegAckEn */ - sx_out_off(bp, CD186x_SRCR, sx_in (bp, CD186x_SRCR) | SRCR_REGACKEN); + sx_out_off(bp, CD186x_SRCR, sx_in(bp, CD186x_SRCR) | SRCR_REGACKEN); /* Setting up prescaler. We need 4 ticks per 1 ms */ scaler = SX_OSCFREQ/SPECIALIX_TPS; @@ -399,9 +382,9 @@ static int sx_init_CD186x(struct specialix_board * bp) sx_out_off(bp, CD186x_PPRL, scaler & 0xff); spin_unlock_irqrestore(&bp->lock, flags); - if (!sx_set_irq (bp)) { + if (!sx_set_irq(bp)) { /* Figure out how to pass this along... */ - printk (KERN_ERR "Cannot set irq to %d.\n", bp->irq); + printk(KERN_ERR "Cannot set irq to %d.\n", bp->irq); rv = 0; } @@ -410,16 +393,16 @@ static int sx_init_CD186x(struct specialix_board * bp) } -static int read_cross_byte (struct specialix_board *bp, int reg, int bit) +static int read_cross_byte(struct specialix_board *bp, int reg, int bit) { int i; int t; unsigned long flags; spin_lock_irqsave(&bp->lock, flags); - for (i=0, t=0;i<8;i++) { - sx_out_off (bp, CD186x_CAR, i); - if (sx_in_off (bp, reg) & bit) + for (i = 0, t = 0; i < 8; i++) { + sx_out_off(bp, CD186x_CAR, i); + if (sx_in_off(bp, reg) & bit) t |= 1 << i; } spin_unlock_irqrestore(&bp->lock, flags); @@ -428,37 +411,10 @@ static int read_cross_byte (struct specialix_board *bp, int reg, int bit) } -#ifdef SPECIALIX_TIMER -void missed_irq (unsigned long data) -{ - unsigned char irq; - unsigned long flags; - struct specialix_board *bp = (struct specialix_board *)data; - - spin_lock_irqsave(&bp->lock, flags); - irq = sx_in ((struct specialix_board *)data, CD186x_SRSR) & - (SRSR_RREQint | - SRSR_TREQint | - SRSR_MREQint); - spin_unlock_irqrestore(&bp->lock, flags); - if (irq) { - printk (KERN_INFO "Missed interrupt... Calling int from timer. \n"); - sx_interrupt (-1, bp); - } - mod_timer(&missed_irq_timer, jiffies + sx_poll); -} -#endif - - - /* Main probing routine, also sets irq. */ static int sx_probe(struct specialix_board *bp) { unsigned char val1, val2; -#if 0 - int irqs = 0; - int retries; -#endif int rev; int chip; @@ -471,17 +427,18 @@ static int sx_probe(struct specialix_board *bp) /* Are the I/O ports here ? */ sx_out_off(bp, CD186x_PPRL, 0x5a); - short_pause (); + udelay(1); val1 = sx_in_off(bp, CD186x_PPRL); sx_out_off(bp, CD186x_PPRL, 0xa5); - short_pause (); + udelay(1); val2 = sx_in_off(bp, CD186x_PPRL); - if ((val1 != 0x5a) || (val2 != 0xa5)) { - printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n", - board_No(bp), bp->base); + if (val1 != 0x5a || val2 != 0xa5) { + printk(KERN_INFO + "sx%d: specialix IO8+ Board at 0x%03x not found.\n", + board_No(bp), bp->base); sx_release_io_range(bp); func_exit(); return 1; @@ -489,10 +446,11 @@ static int sx_probe(struct specialix_board *bp) /* Check the DSR lines that Specialix uses as board identification */ - val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR); - val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS); - dprintk (SX_DEBUG_INIT, "sx%d: DSR lines are: %02x, rts lines are: %02x\n", - board_No(bp), val1, val2); + val1 = read_cross_byte(bp, CD186x_MSVR, MSVR_DSR); + val2 = read_cross_byte(bp, CD186x_MSVR, MSVR_RTS); + dprintk(SX_DEBUG_INIT, + "sx%d: DSR lines are: %02x, rts lines are: %02x\n", + board_No(bp), val1, val2); /* They managed to switch the bit order between the docs and the IO8+ card. The new PCI card now conforms to old docs. @@ -500,7 +458,8 @@ static int sx_probe(struct specialix_board *bp) old card. */ val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2; if (val1 != val2) { - printk(KERN_INFO "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n", + printk(KERN_INFO + "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n", board_No(bp), val2, bp->base, val1); sx_release_io_range(bp); func_exit(); @@ -508,47 +467,6 @@ static int sx_probe(struct specialix_board *bp) } -#if 0 - /* It's time to find IRQ for this board */ - for (retries = 0; retries < 5 && irqs <= 0; retries++) { - irqs = probe_irq_on(); - sx_init_CD186x(bp); /* Reset CD186x chip */ - sx_out(bp, CD186x_CAR, 2); /* Select port 2 */ - sx_wait_CCR(bp); - sx_out(bp, CD186x_CCR, CCR_TXEN); /* Enable transmitter */ - sx_out(bp, CD186x_IER, IER_TXRDY); /* Enable tx empty intr */ - msleep(50); - irqs = probe_irq_off(irqs); - - dprintk (SX_DEBUG_INIT, "SRSR = %02x, ", sx_in(bp, CD186x_SRSR)); - dprintk (SX_DEBUG_INIT, "TRAR = %02x, ", sx_in(bp, CD186x_TRAR)); - dprintk (SX_DEBUG_INIT, "GIVR = %02x, ", sx_in(bp, CD186x_GIVR)); - dprintk (SX_DEBUG_INIT, "GICR = %02x, ", sx_in(bp, CD186x_GICR)); - dprintk (SX_DEBUG_INIT, "\n"); - - /* Reset CD186x again */ - if (!sx_init_CD186x(bp)) { - /* Hmmm. This is dead code anyway. */ - } - - dprintk (SX_DEBUG_INIT "val1 = %02x, val2 = %02x, val3 = %02x.\n", - val1, val2, val3); - - } - -#if 0 - if (irqs <= 0) { - printk(KERN_ERR "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n", - board_No(bp), bp->base); - sx_release_io_range(bp); - func_exit(); - return 1; - } -#endif - printk (KERN_INFO "Started with irq=%d, but now have irq=%d.\n", bp->irq, irqs); - if (irqs > 0) - bp->irq = irqs; -#endif /* Reset CD186x again */ if (!sx_init_CD186x(bp)) { sx_release_io_range(bp); @@ -560,7 +478,7 @@ static int sx_probe(struct specialix_board *bp) bp->flags |= SX_BOARD_PRESENT; /* Chip revcode pkgtype - GFRCR SRCR bit 7 + GFRCR SRCR bit 7 CD180 rev B 0x81 0 CD180 rev C 0x82 0 CD1864 rev A 0x82 1 @@ -570,24 +488,32 @@ static int sx_probe(struct specialix_board *bp) */ switch (sx_in_off(bp, CD186x_GFRCR)) { - case 0x82:chip = 1864;rev='A';break; - case 0x83:chip = 1865;rev='A';break; - case 0x84:chip = 1865;rev='B';break; - case 0x85:chip = 1865;rev='C';break; /* Does not exist at this time */ - default:chip=-1;rev='x'; + case 0x82: + chip = 1864; + rev = 'A'; + break; + case 0x83: + chip = 1865; + rev = 'A'; + break; + case 0x84: + chip = 1865; + rev = 'B'; + break; + case 0x85: + chip = 1865; + rev = 'C'; + break; /* Does not exist at this time */ + default: + chip = -1; + rev = 'x'; } - dprintk (SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) ); - -#ifdef SPECIALIX_TIMER - setup_timer(&missed_irq_timer, missed_irq, (unsigned long)bp); - mod_timer(&missed_irq_timer, jiffies + sx_poll); -#endif + dprintk(SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR)); - printk(KERN_INFO"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n", - board_No(bp), - bp->base, bp->irq, - chip, rev); + printk(KERN_INFO + "sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n", + board_No(bp), bp->base, bp->irq, chip, rev); func_exit(); return 0; @@ -598,20 +524,22 @@ static int sx_probe(struct specialix_board *bp) * Interrupt processing routines. * */ -static inline struct specialix_port * sx_get_port(struct specialix_board * bp, - unsigned char const * what) +static struct specialix_port *sx_get_port(struct specialix_board *bp, + unsigned char const *what) { unsigned char channel; - struct specialix_port * port = NULL; + struct specialix_port *port = NULL; channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF; - dprintk (SX_DEBUG_CHAN, "channel: %d\n", channel); + dprintk(SX_DEBUG_CHAN, "channel: %d\n", channel); if (channel < CD186x_NCH) { port = &sx_port[board_No(bp) * SX_NPORT + channel]; - dprintk (SX_DEBUG_CHAN, "port: %d %p flags: 0x%lx\n",board_No(bp) * SX_NPORT + channel, port, port->port.flags & ASYNC_INITIALIZED); + dprintk(SX_DEBUG_CHAN, "port: %d %p flags: 0x%lx\n", + board_No(bp) * SX_NPORT + channel, port, + port->port.flags & ASYNC_INITIALIZED); if (port->port.flags & ASYNC_INITIALIZED) { - dprintk (SX_DEBUG_CHAN, "port: %d %p\n", channel, port); + dprintk(SX_DEBUG_CHAN, "port: %d %p\n", channel, port); func_exit(); return port; } @@ -622,7 +550,7 @@ static inline struct specialix_port * sx_get_port(struct specialix_board * bp, } -static inline void sx_receive_exc(struct specialix_board * bp) +static void sx_receive_exc(struct specialix_board *bp) { struct specialix_port *port; struct tty_struct *tty; @@ -633,7 +561,7 @@ static inline void sx_receive_exc(struct specialix_board * bp) port = sx_get_port(bp, "Receive"); if (!port) { - dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n"); + dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n"); func_exit(); return; } @@ -641,19 +569,21 @@ static inline void sx_receive_exc(struct specialix_board * bp) status = sx_in(bp, CD186x_RCSR); - dprintk (SX_DEBUG_RX, "status: 0x%x\n", status); + dprintk(SX_DEBUG_RX, "status: 0x%x\n", status); if (status & RCSR_OE) { port->overrun++; - dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Overrun. Total %ld overruns.\n", - board_No(bp), port_No(port), port->overrun); + dprintk(SX_DEBUG_FIFO, + "sx%d: port %d: Overrun. Total %ld overruns.\n", + board_No(bp), port_No(port), port->overrun); } status &= port->mark_mask; /* This flip buffer check needs to be below the reading of the status register to reset the chip's IRQ.... */ if (tty_buffer_request_room(tty, 1) == 0) { - dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Working around flip buffer overflow.\n", - board_No(bp), port_No(port)); + dprintk(SX_DEBUG_FIFO, + "sx%d: port %d: Working around flip buffer overflow.\n", + board_No(bp), port_No(port)); func_exit(); return; } @@ -664,8 +594,9 @@ static inline void sx_receive_exc(struct specialix_board * bp) return; } if (status & RCSR_TOUT) { - printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n", - board_No(bp), port_No(port)); + printk(KERN_INFO + "sx%d: port %d: Receiver timeout. Hardware problems ?\n", + board_No(bp), port_No(port)); func_exit(); return; @@ -688,13 +619,13 @@ static inline void sx_receive_exc(struct specialix_board * bp) else flag = TTY_NORMAL; - if(tty_insert_flip_char(tty, ch, flag)) + if (tty_insert_flip_char(tty, ch, flag)) tty_flip_buffer_push(tty); func_exit(); } -static inline void sx_receive(struct specialix_board * bp) +static void sx_receive(struct specialix_board *bp) { struct specialix_port *port; struct tty_struct *tty; @@ -702,15 +633,16 @@ static inline void sx_receive(struct specialix_board * bp) func_enter(); - if (!(port = sx_get_port(bp, "Receive"))) { - dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n"); + port = sx_get_port(bp, "Receive"); + if (port == NULL) { + dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n"); func_exit(); return; } tty = port->port.tty; count = sx_in(bp, CD186x_RDCR); - dprintk (SX_DEBUG_RX, "port: %p: count: %d\n", port, count); + dprintk(SX_DEBUG_RX, "port: %p: count: %d\n", port, count); port->hits[count > 8 ? 9 : count]++; tty_buffer_request_room(tty, count); @@ -722,18 +654,19 @@ static inline void sx_receive(struct specialix_board * bp) } -static inline void sx_transmit(struct specialix_board * bp) +static void sx_transmit(struct specialix_board *bp) { struct specialix_port *port; struct tty_struct *tty; unsigned char count; func_enter(); - if (!(port = sx_get_port(bp, "Transmit"))) { + port = sx_get_port(bp, "Transmit"); + if (port == NULL) { func_exit(); return; } - dprintk (SX_DEBUG_TX, "port: %p\n", port); + dprintk(SX_DEBUG_TX, "port: %p\n", port); tty = port->port.tty; if (port->IER & IER_TXEMPTY) { @@ -765,7 +698,8 @@ static inline void sx_transmit(struct specialix_board * bp) sx_out(bp, CD186x_TDR, CD186x_C_ESC); sx_out(bp, CD186x_TDR, CD186x_C_DELAY); sx_out(bp, CD186x_TDR, count); - if (!(port->break_length -= count)) + port->break_length -= count; + if (port->break_length == 0) port->break_length--; } else { sx_out(bp, CD186x_TDR, CD186x_C_ESC); @@ -794,36 +728,36 @@ static inline void sx_transmit(struct specialix_board * bp) sx_out(bp, CD186x_IER, port->IER); } if (port->xmit_cnt <= port->wakeup_chars) - tty_wakeup(tty); + tty_wakeup(tty); func_exit(); } -static inline void sx_check_modem(struct specialix_board * bp) +static void sx_check_modem(struct specialix_board *bp) { struct specialix_port *port; struct tty_struct *tty; unsigned char mcr; int msvr_cd; - dprintk (SX_DEBUG_SIGNALS, "Modem intr. "); - if (!(port = sx_get_port(bp, "Modem"))) + dprintk(SX_DEBUG_SIGNALS, "Modem intr. "); + port = sx_get_port(bp, "Modem"); + if (port == NULL) return; tty = port->port.tty; mcr = sx_in(bp, CD186x_MCR); - printk ("mcr = %02x.\n", mcr); if ((mcr & MCR_CDCHG)) { - dprintk (SX_DEBUG_SIGNALS, "CD just changed... "); + dprintk(SX_DEBUG_SIGNALS, "CD just changed... "); msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD; if (msvr_cd) { - dprintk (SX_DEBUG_SIGNALS, "Waking up guys in open.\n"); + dprintk(SX_DEBUG_SIGNALS, "Waking up guys in open.\n"); wake_up_interruptible(&port->port.open_wait); } else { - dprintk (SX_DEBUG_SIGNALS, "Sending HUP.\n"); + dprintk(SX_DEBUG_SIGNALS, "Sending HUP.\n"); tty_hangup(tty); } } @@ -874,9 +808,12 @@ static irqreturn_t sx_interrupt(int dummy, void *dev_id) spin_lock_irqsave(&bp->lock, flags); - dprintk (SX_DEBUG_FLOW, "enter %s port %d room: %ld\n", __func__, port_No(sx_get_port(bp, "INT")), SERIAL_XMIT_SIZE - sx_get_port(bp, "ITN")->xmit_cnt - 1); + dprintk(SX_DEBUG_FLOW, "enter %s port %d room: %ld\n", __func__, + port_No(sx_get_port(bp, "INT")), + SERIAL_XMIT_SIZE - sx_get_port(bp, "ITN")->xmit_cnt - 1); if (!(bp->flags & SX_BOARD_ACTIVE)) { - dprintk (SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n", bp->irq); + dprintk(SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n", + bp->irq); spin_unlock_irqrestore(&bp->lock, flags); func_exit(); return IRQ_NONE; @@ -884,10 +821,11 @@ static irqreturn_t sx_interrupt(int dummy, void *dev_id) saved_reg = bp->reg; - while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) & - (SRSR_RREQint | - SRSR_TREQint | - SRSR_MREQint)))) { + while (++loop < 16) { + status = sx_in(bp, CD186x_SRSR) & + (SRSR_RREQint | SRSR_TREQint | SRSR_MREQint); + if (status == 0) + break; if (status & SRSR_RREQint) { ack = sx_in(bp, CD186x_RRAR); @@ -896,8 +834,9 @@ static irqreturn_t sx_interrupt(int dummy, void *dev_id) else if (ack == (SX_ID | GIVR_IT_REXC)) sx_receive_exc(bp); else - printk(KERN_ERR "sx%d: status: 0x%x Bad receive ack 0x%02x.\n", - board_No(bp), status, ack); + printk(KERN_ERR + "sx%d: status: 0x%x Bad receive ack 0x%02x.\n", + board_No(bp), status, ack); } else if (status & SRSR_TREQint) { ack = sx_in(bp, CD186x_TRAR); @@ -906,14 +845,16 @@ static irqreturn_t sx_interrupt(int dummy, void *dev_id) sx_transmit(bp); else printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n", - board_No(bp), status, ack, port_No (sx_get_port (bp, "Int"))); + board_No(bp), status, ack, + port_No(sx_get_port(bp, "Int"))); } else if (status & SRSR_MREQint) { ack = sx_in(bp, CD186x_MRAR); if (ack == (SX_ID | GIVR_IT_MODEM)) sx_check_modem(bp); else - printk(KERN_ERR "sx%d: status: 0x%x Bad modem ack 0x%02x.\n", + printk(KERN_ERR + "sx%d: status: 0x%x Bad modem ack 0x%02x.\n", board_No(bp), status, ack); } @@ -921,7 +862,7 @@ static irqreturn_t sx_interrupt(int dummy, void *dev_id) sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */ } bp->reg = saved_reg; - outb (bp->reg, bp->base + SX_ADDR_REG); + outb(bp->reg, bp->base + SX_ADDR_REG); spin_unlock_irqrestore(&bp->lock, flags); func_exit(); return IRQ_HANDLED; @@ -932,36 +873,26 @@ static irqreturn_t sx_interrupt(int dummy, void *dev_id) * Routines for open & close processing. */ -static void turn_ints_off (struct specialix_board *bp) +static void turn_ints_off(struct specialix_board *bp) { unsigned long flags; func_enter(); - if (bp->flags & SX_BOARD_IS_PCI) { - /* This was intended for enabeling the interrupt on the - * PCI card. However it seems that it's already enabled - * and as PCI interrupts can be shared, there is no real - * reason to have to turn it off. */ - } - spin_lock_irqsave(&bp->lock, flags); - (void) sx_in_off (bp, 0); /* Turn off interrupts. */ + (void) sx_in_off(bp, 0); /* Turn off interrupts. */ spin_unlock_irqrestore(&bp->lock, flags); func_exit(); } -static void turn_ints_on (struct specialix_board *bp) +static void turn_ints_on(struct specialix_board *bp) { unsigned long flags; func_enter(); - if (bp->flags & SX_BOARD_IS_PCI) { - /* play with the PCI chip. See comment above. */ - } spin_lock_irqsave(&bp->lock, flags); - (void) sx_in (bp, 0); /* Turn ON interrupts. */ + (void) sx_in(bp, 0); /* Turn ON interrupts. */ spin_unlock_irqrestore(&bp->lock, flags); func_exit(); @@ -969,7 +900,7 @@ static void turn_ints_on (struct specialix_board *bp) /* Called with disabled interrupts */ -static inline int sx_setup_board(struct specialix_board * bp) +static int sx_setup_board(struct specialix_board *bp) { int error; @@ -977,14 +908,16 @@ static inline int sx_setup_board(struct specialix_board * bp) return 0; if (bp->flags & SX_BOARD_IS_PCI) - error = request_irq(bp->irq, sx_interrupt, IRQF_DISABLED | IRQF_SHARED, "specialix IO8+", bp); + error = request_irq(bp->irq, sx_interrupt, + IRQF_DISABLED | IRQF_SHARED, "specialix IO8+", bp); else - error = request_irq(bp->irq, sx_interrupt, IRQF_DISABLED, "specialix IO8+", bp); + error = request_irq(bp->irq, sx_interrupt, + IRQF_DISABLED, "specialix IO8+", bp); if (error) return error; - turn_ints_on (bp); + turn_ints_on(bp); bp->flags |= SX_BOARD_ACTIVE; return 0; @@ -992,7 +925,7 @@ static inline int sx_setup_board(struct specialix_board * bp) /* Called with disabled interrupts */ -static inline void sx_shutdown_board(struct specialix_board *bp) +static void sx_shutdown_board(struct specialix_board *bp) { func_enter(); @@ -1003,22 +936,26 @@ static inline void sx_shutdown_board(struct specialix_board *bp) bp->flags &= ~SX_BOARD_ACTIVE; - dprintk (SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n", - bp->irq, board_No (bp)); + dprintk(SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n", + bp->irq, board_No(bp)); free_irq(bp->irq, bp); - - turn_ints_off (bp); - - + turn_ints_off(bp); func_exit(); } +static unsigned int sx_crtscts(struct tty_struct *tty) +{ + if (sx_rtscts) + return C_CRTSCTS(tty); + return 1; +} /* * Setting up port characteristics. * Must be called with disabled interrupts */ -static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port) +static void sx_change_speed(struct specialix_board *bp, + struct specialix_port *port) { struct tty_struct *tty; unsigned long baud; @@ -1030,7 +967,8 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p func_enter(); - if (!(tty = port->port.tty) || !tty->termios) { + tty = port->port.tty; + if (!tty || !tty->termios) { func_exit(); return; } @@ -1043,12 +981,12 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p /* The Specialix board doens't implement the RTS lines. They are used to set the IRQ level. Don't touch them. */ - if (SX_CRTSCTS(tty)) + if (sx_crtscts(tty)) port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS); else port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS); spin_unlock_irqrestore(&bp->lock, flags); - dprintk (SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR); + dprintk(SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR); baud = tty_get_baud_rate(tty); if (baud == 38400) { @@ -1060,21 +998,19 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p if (!baud) { /* Drop DTR & exit */ - dprintk (SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n"); - if (!SX_CRTSCTS (tty)) { - port -> MSVR &= ~ MSVR_DTR; + dprintk(SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n"); + if (!sx_crtscts(tty)) { + port->MSVR &= ~MSVR_DTR; spin_lock_irqsave(&bp->lock, flags); - sx_out(bp, CD186x_MSVR, port->MSVR ); + sx_out(bp, CD186x_MSVR, port->MSVR); spin_unlock_irqrestore(&bp->lock, flags); - } - else - dprintk (SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n"); + } else + dprintk(SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n"); return; } else { /* Set DTR on */ - if (!SX_CRTSCTS (tty)) { - port ->MSVR |= MSVR_DTR; - } + if (!sx_crtscts(tty)) + port->MSVR |= MSVR_DTR; } /* @@ -1083,28 +1019,27 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p /* Set baud rate for port */ tmp = port->custom_divisor ; - if ( tmp ) - printk (KERN_INFO "sx%d: Using custom baud rate divisor %ld. \n" - "This is an untested option, please be carefull.\n", - port_No (port), tmp); + if (tmp) + printk(KERN_INFO + "sx%d: Using custom baud rate divisor %ld. \n" + "This is an untested option, please be careful.\n", + port_No(port), tmp); else - tmp = (((SX_OSCFREQ + baud/2) / baud + - CD186x_TPC/2) / CD186x_TPC); + tmp = (((SX_OSCFREQ + baud/2) / baud + CD186x_TPC/2) / + CD186x_TPC); - if ((tmp < 0x10) && time_before(again, jiffies)) { + if (tmp < 0x10 && time_before(again, jiffies)) { again = jiffies + HZ * 60; /* Page 48 of version 2.0 of the CL-CD1865 databook */ if (tmp >= 12) { - printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n" - "Performance degradation is possible.\n" - "Read specialix.txt for more info.\n", - port_No (port), tmp); + printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n" + "Performance degradation is possible.\n" + "Read specialix.txt for more info.\n", + port_No(port), tmp); } else { - printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n" - "Warning: overstressing Cirrus chip. " - "This might not work.\n" - "Read specialix.txt for more info.\n", - port_No (port), tmp); + printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n" + "Warning: overstressing Cirrus chip. This might not work.\n" + "Read specialix.txt for more info.\n", port_No(port), tmp); } } spin_lock_irqsave(&bp->lock, flags); @@ -1114,7 +1049,8 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p sx_out(bp, CD186x_TBPRL, tmp & 0xff); spin_unlock_irqrestore(&bp->lock, flags); if (port->custom_divisor) - baud = (SX_OSCFREQ + port->custom_divisor/2) / port->custom_divisor; + baud = (SX_OSCFREQ + port->custom_divisor/2) / + port->custom_divisor; baud = (baud + 5) / 10; /* Estimated CPS */ /* Two timer ticks seems enough to wakeup something like SLIP driver */ @@ -1129,16 +1065,16 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p sx_out(bp, CD186x_RTPR, tmp); spin_unlock_irqrestore(&bp->lock, flags); switch (C_CSIZE(tty)) { - case CS5: + case CS5: cor1 |= COR1_5BITS; break; - case CS6: + case CS6: cor1 |= COR1_6BITS; break; - case CS7: + case CS7: cor1 |= COR1_7BITS; break; - case CS8: + case CS8: cor1 |= COR1_8BITS; break; } @@ -1175,7 +1111,8 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD; mcor2 |= MCOR2_DSROD | MCOR2_CTSOD; spin_lock_irqsave(&bp->lock, flags); - tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR)); + tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & + (MSVR_CTS|MSVR_DSR)); spin_unlock_irqrestore(&bp->lock, flags); #else port->COR2 |= COR2_CTSAE; @@ -1219,7 +1156,8 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p spin_lock_irqsave(&bp->lock, flags); sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3); /* Setting up modem option registers */ - dprintk (SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2); + dprintk(SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n", + mcor1, mcor2); sx_out(bp, CD186x_MCOR1, mcor1); sx_out(bp, CD186x_MCOR2, mcor2); spin_unlock_irqrestore(&bp->lock, flags); @@ -1238,7 +1176,8 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p /* Must be called with interrupts enabled */ -static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port) +static int sx_setup_port(struct specialix_board *bp, + struct specialix_port *port) { unsigned long flags; @@ -1253,7 +1192,8 @@ static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port /* We may sleep in get_zeroed_page() */ unsigned long tmp; - if (!(tmp = get_zeroed_page(GFP_KERNEL))) { + tmp = get_zeroed_page(GFP_KERNEL); + if (tmp == 0L) { func_exit(); return -ENOMEM; } @@ -1284,7 +1224,8 @@ static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port /* Must be called with interrupts disabled */ -static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port) +static void sx_shutdown_port(struct specialix_board *bp, + struct specialix_port *port) { struct tty_struct *tty; int i; @@ -1298,11 +1239,11 @@ static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port * } if (sx_debug & SX_DEBUG_FIFO) { - dprintk(SX_DEBUG_FIFO, "sx%d: port %d: %ld overruns, FIFO hits [ ", - board_No(bp), port_No(port), port->overrun); - for (i = 0; i < 10; i++) { + dprintk(SX_DEBUG_FIFO, + "sx%d: port %d: %ld overruns, FIFO hits [ ", + board_No(bp), port_No(port), port->overrun); + for (i = 0; i < 10; i++) dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]); - } dprintk(SX_DEBUG_FIFO, "].\n"); } @@ -1315,7 +1256,8 @@ static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port * spin_lock_irqsave(&bp->lock, flags); sx_out(bp, CD186x_CAR, port_No(port)); - if (!(tty = port->port.tty) || C_HUPCL(tty)) { + tty = port->port.tty; + if (tty == NULL || C_HUPCL(tty)) { /* Drop DTR */ sx_out(bp, CD186x_MSVDTR, 0); } @@ -1338,8 +1280,8 @@ static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port * } -static int block_til_ready(struct tty_struct *tty, struct file * filp, - struct specialix_port *port) +static int block_til_ready(struct tty_struct *tty, struct file *filp, + struct specialix_port *port) { DECLARE_WAITQUEUE(wait, current); struct specialix_board *bp = port_Board(port); @@ -1389,23 +1331,22 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, retval = 0; add_wait_queue(&port->port.open_wait, &wait); spin_lock_irqsave(&port->lock, flags); - if (!tty_hung_up_p(filp)) { + if (!tty_hung_up_p(filp)) port->port.count--; - } spin_unlock_irqrestore(&port->lock, flags); port->port.blocked_open++; while (1) { spin_lock_irqsave(&bp->lock, flags); sx_out(bp, CD186x_CAR, port_No(port)); CD = sx_in(bp, CD186x_MSVR) & MSVR_CD; - if (SX_CRTSCTS (tty)) { + if (sx_crtscts(tty)) { /* Activate RTS */ port->MSVR |= MSVR_DTR; /* WTF? */ - sx_out (bp, CD186x_MSVR, port->MSVR); + sx_out(bp, CD186x_MSVR, port->MSVR); } else { /* Activate DTR */ port->MSVR |= MSVR_DTR; - sx_out (bp, CD186x_MSVR, port->MSVR); + sx_out(bp, CD186x_MSVR, port->MSVR); } spin_unlock_irqrestore(&bp->lock, flags); set_current_state(TASK_INTERRUPTIBLE); @@ -1430,9 +1371,8 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, set_current_state(TASK_RUNNING); remove_wait_queue(&port->port.open_wait, &wait); spin_lock_irqsave(&port->lock, flags); - if (!tty_hung_up_p(filp)) { + if (!tty_hung_up_p(filp)) port->port.count++; - } port->port.blocked_open--; spin_unlock_irqrestore(&port->lock, flags); if (retval) { @@ -1446,12 +1386,12 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, } -static int sx_open(struct tty_struct * tty, struct file * filp) +static int sx_open(struct tty_struct *tty, struct file *filp) { int board; int error; - struct specialix_port * port; - struct specialix_board * bp; + struct specialix_port *port; + struct specialix_board *bp; int i; unsigned long flags; @@ -1468,17 +1408,19 @@ static int sx_open(struct tty_struct * tty, struct file * filp) port = sx_port + board * SX_NPORT + SX_PORT(tty->index); port->overrun = 0; for (i = 0; i < 10; i++) - port->hits[i]=0; + port->hits[i] = 0; - dprintk (SX_DEBUG_OPEN, "Board = %d, bp = %p, port = %p, portno = %d.\n", - board, bp, port, SX_PORT(tty->index)); + dprintk(SX_DEBUG_OPEN, + "Board = %d, bp = %p, port = %p, portno = %d.\n", + board, bp, port, SX_PORT(tty->index)); if (sx_paranoia_check(port, tty->name, "sx_open")) { func_enter(); return -ENODEV; } - if ((error = sx_setup_board(bp))) { + error = sx_setup_board(bp); + if (error) { func_exit(); return error; } @@ -1490,12 +1432,14 @@ static int sx_open(struct tty_struct * tty, struct file * filp) port->port.tty = tty; spin_unlock_irqrestore(&bp->lock, flags); - if ((error = sx_setup_port(bp, port))) { + error = sx_setup_port(bp, port); + if (error) { func_enter(); return error; } - if ((error = block_til_ready(tty, filp, port))) { + error = block_til_ready(tty, filp, port); + if (error) { func_enter(); return error; } @@ -1508,7 +1452,7 @@ static void sx_flush_buffer(struct tty_struct *tty) { struct specialix_port *port = (struct specialix_port *)tty->driver_data; unsigned long flags; - struct specialix_board * bp; + struct specialix_board *bp; func_enter(); @@ -1526,9 +1470,9 @@ static void sx_flush_buffer(struct tty_struct *tty) func_exit(); } -static void sx_close(struct tty_struct * tty, struct file * filp) +static void sx_close(struct tty_struct *tty, struct file *filp) { - struct specialix_port *port = (struct specialix_port *) tty->driver_data; + struct specialix_port *port = (struct specialix_port *)tty->driver_data; struct specialix_board *bp; unsigned long flags; unsigned long timeout; @@ -1547,7 +1491,7 @@ static void sx_close(struct tty_struct * tty, struct file * filp) } bp = port_Board(port); - if ((tty->count == 1) && (port->port.count != 1)) { + if (tty->count == 1 && port->port.count != 1) { printk(KERN_ERR "sx%d: sx_close: bad port count;" " tty->count is 1, port count is %d\n", board_No(bp), port->port.count); @@ -1570,17 +1514,16 @@ static void sx_close(struct tty_struct * tty, struct file * filp) */ tty->closing = 1; spin_unlock_irqrestore(&port->lock, flags); - dprintk (SX_DEBUG_OPEN, "Closing\n"); - if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) { + dprintk(SX_DEBUG_OPEN, "Closing\n"); + if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, port->port.closing_wait); - } /* * At this point we stop accepting input. To do this, we * disable the receive line status interrupts, and tell the * interrupt driver to stop checking the data ready bit in the * line status register. */ - dprintk (SX_DEBUG_OPEN, "Closed\n"); + dprintk(SX_DEBUG_OPEN, "Closed\n"); port->IER &= ~IER_RXD; if (port->port.flags & ASYNC_INITIALIZED) { port->IER &= ~IER_TXRDY; @@ -1595,11 +1538,11 @@ static void sx_close(struct tty_struct * tty, struct file * filp) * important if there is a transmit FIFO! */ timeout = jiffies+HZ; - while(port->IER & IER_TXEMPTY) { - set_current_state (TASK_INTERRUPTIBLE); + while (port->IER & IER_TXEMPTY) { + set_current_state(TASK_INTERRUPTIBLE); msleep_interruptible(jiffies_to_msecs(port->timeout)); if (time_after(jiffies, timeout)) { - printk (KERN_INFO "Timeout waiting for close\n"); + printk(KERN_INFO "Timeout waiting for close\n"); break; } } @@ -1607,13 +1550,15 @@ static void sx_close(struct tty_struct * tty, struct file * filp) } if (--bp->count < 0) { - printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d port: %d\n", - board_No(bp), bp->count, tty->index); + printk(KERN_ERR + "sx%d: sx_shutdown_port: bad board count: %d port: %d\n", + board_No(bp), bp->count, tty->index); bp->count = 0; } if (--port->port.count < 0) { - printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n", - board_No(bp), port_No(port), port->port.count); + printk(KERN_ERR + "sx%d: sx_close: bad port count for tty%d: %d\n", + board_No(bp), port_No(port), port->port.count); port->port.count = 0; } @@ -1625,9 +1570,9 @@ static void sx_close(struct tty_struct * tty, struct file * filp) port->port.tty = NULL; spin_unlock_irqrestore(&port->lock, flags); if (port->port.blocked_open) { - if (port->port.close_delay) { - msleep_interruptible(jiffies_to_msecs(port->port.close_delay)); - } + if (port->port.close_delay) + msleep_interruptible( + jiffies_to_msecs(port->port.close_delay)); wake_up_interruptible(&port->port.open_wait); } port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); @@ -1637,8 +1582,8 @@ static void sx_close(struct tty_struct * tty, struct file * filp) } -static int sx_write(struct tty_struct * tty, - const unsigned char *buf, int count) +static int sx_write(struct tty_struct *tty, + const unsigned char *buf, int count) { struct specialix_port *port = (struct specialix_port *)tty->driver_data; struct specialix_board *bp; @@ -1690,11 +1635,11 @@ static int sx_write(struct tty_struct * tty, } -static int sx_put_char(struct tty_struct * tty, unsigned char ch) +static int sx_put_char(struct tty_struct *tty, unsigned char ch) { struct specialix_port *port = (struct specialix_port *)tty->driver_data; unsigned long flags; - struct specialix_board * bp; + struct specialix_board *bp; func_enter(); @@ -1702,7 +1647,7 @@ static int sx_put_char(struct tty_struct * tty, unsigned char ch) func_exit(); return 0; } - dprintk (SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf); + dprintk(SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf); if (!port->xmit_buf) { func_exit(); return 0; @@ -1710,14 +1655,15 @@ static int sx_put_char(struct tty_struct * tty, unsigned char ch) bp = port_Board(port); spin_lock_irqsave(&port->lock, flags); - dprintk (SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n", port->xmit_cnt, port->xmit_buf); - if ((port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) || (!port->xmit_buf)) { + dprintk(SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n", + port->xmit_cnt, port->xmit_buf); + if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1 || !port->xmit_buf) { spin_unlock_irqrestore(&port->lock, flags); - dprintk (SX_DEBUG_TX, "Exit size\n"); + dprintk(SX_DEBUG_TX, "Exit size\n"); func_exit(); return 0; } - dprintk (SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf); + dprintk(SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf); port->xmit_buf[port->xmit_head++] = ch; port->xmit_head &= SERIAL_XMIT_SIZE - 1; port->xmit_cnt++; @@ -1728,11 +1674,11 @@ static int sx_put_char(struct tty_struct * tty, unsigned char ch) } -static void sx_flush_chars(struct tty_struct * tty) +static void sx_flush_chars(struct tty_struct *tty) { struct specialix_port *port = (struct specialix_port *)tty->driver_data; unsigned long flags; - struct specialix_board * bp = port_Board(port); + struct specialix_board *bp = port_Board(port); func_enter(); @@ -1755,7 +1701,7 @@ static void sx_flush_chars(struct tty_struct * tty) } -static int sx_write_room(struct tty_struct * tty) +static int sx_write_room(struct tty_struct *tty) { struct specialix_port *port = (struct specialix_port *)tty->driver_data; int ret; @@ -1790,12 +1736,10 @@ static int sx_chars_in_buffer(struct tty_struct *tty) return port->xmit_cnt; } - - static int sx_tiocmget(struct tty_struct *tty, struct file *file) { struct specialix_port *port = (struct specialix_port *)tty->driver_data; - struct specialix_board * bp; + struct specialix_board *bp; unsigned char status; unsigned int result; unsigned long flags; @@ -1808,25 +1752,23 @@ static int sx_tiocmget(struct tty_struct *tty, struct file *file) } bp = port_Board(port); - spin_lock_irqsave (&bp->lock, flags); + spin_lock_irqsave(&bp->lock, flags); sx_out(bp, CD186x_CAR, port_No(port)); status = sx_in(bp, CD186x_MSVR); spin_unlock_irqrestore(&bp->lock, flags); - dprintk (SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n", - port_No(port), status, sx_in (bp, CD186x_CAR)); - dprintk (SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port); - if (SX_CRTSCTS(port->port.tty)) { - result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */ - | ((status & MSVR_DTR) ? TIOCM_RTS : 0) - | ((status & MSVR_CD) ? TIOCM_CAR : 0) - |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */ - | ((status & MSVR_CTS) ? TIOCM_CTS : 0); + dprintk(SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n", + port_No(port), status, sx_in(bp, CD186x_CAR)); + dprintk(SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port); + if (sx_crtscts(port->port.tty)) { + result = TIOCM_DTR | TIOCM_DSR + | ((status & MSVR_DTR) ? TIOCM_RTS : 0) + | ((status & MSVR_CD) ? TIOCM_CAR : 0) + | ((status & MSVR_CTS) ? TIOCM_CTS : 0); } else { - result = /* (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */ - | ((status & MSVR_DTR) ? TIOCM_DTR : 0) - | ((status & MSVR_CD) ? TIOCM_CAR : 0) - |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */ - | ((status & MSVR_CTS) ? TIOCM_CTS : 0); + result = TIOCM_RTS | TIOCM_DSR + | ((status & MSVR_DTR) ? TIOCM_DTR : 0) + | ((status & MSVR_CD) ? TIOCM_CAR : 0) + | ((status & MSVR_CTS) ? TIOCM_CTS : 0); } func_exit(); @@ -1852,24 +1794,14 @@ static int sx_tiocmset(struct tty_struct *tty, struct file *file, bp = port_Board(port); spin_lock_irqsave(&port->lock, flags); - /* if (set & TIOCM_RTS) - port->MSVR |= MSVR_RTS; */ - /* if (set & TIOCM_DTR) - port->MSVR |= MSVR_DTR; */ - - if (SX_CRTSCTS(port->port.tty)) { + if (sx_crtscts(port->port.tty)) { if (set & TIOCM_RTS) port->MSVR |= MSVR_DTR; } else { if (set & TIOCM_DTR) port->MSVR |= MSVR_DTR; } - - /* if (clear & TIOCM_RTS) - port->MSVR &= ~MSVR_RTS; */ - /* if (clear & TIOCM_DTR) - port->MSVR &= ~MSVR_DTR; */ - if (SX_CRTSCTS(port->port.tty)) { + if (sx_crtscts(port->port.tty)) { if (clear & TIOCM_RTS) port->MSVR &= ~MSVR_DTR; } else { @@ -1886,14 +1818,17 @@ static int sx_tiocmset(struct tty_struct *tty, struct file *file, } -static inline void sx_send_break(struct specialix_port * port, unsigned long length) +static int sx_send_break(struct tty_struct *tty, int length) { + struct specialix_port *port = (struct specialix_port *)tty->driver_data; struct specialix_board *bp = port_Board(port); unsigned long flags; func_enter(); + if (length == 0 || length == -1) + return -EOPNOTSUPP; - spin_lock_irqsave (&port->lock, flags); + spin_lock_irqsave(&port->lock, flags); port->break_length = SPECIALIX_TPS / HZ * length; port->COR2 |= COR2_ETC; port->IER |= IER_TXRDY; @@ -1902,7 +1837,7 @@ static inline void sx_send_break(struct specialix_port * port, unsigned long len sx_out(bp, CD186x_COR2, port->COR2); sx_out(bp, CD186x_IER, port->IER); spin_unlock_irqrestore(&bp->lock, flags); - spin_unlock_irqrestore (&port->lock, flags); + spin_unlock_irqrestore(&port->lock, flags); sx_wait_CCR(bp); spin_lock_irqsave(&bp->lock, flags); sx_out(bp, CD186x_CCR, CCR_CORCHG2); @@ -1910,11 +1845,12 @@ static inline void sx_send_break(struct specialix_port * port, unsigned long len sx_wait_CCR(bp); func_exit(); + return 0; } -static inline int sx_set_serial_info(struct specialix_port * port, - struct serial_struct __user * newinfo) +static int sx_set_serial_info(struct specialix_port *port, + struct serial_struct __user *newinfo) { struct serial_struct tmp; struct specialix_board *bp = port_Board(port); @@ -1943,25 +1879,25 @@ static inline int sx_set_serial_info(struct specialix_port * port, return -EPERM; } port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) | - (tmp.flags & ASYNC_USR_MASK)); + (tmp.flags & ASYNC_USR_MASK)); port->custom_divisor = tmp.custom_divisor; } else { port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) | - (tmp.flags & ASYNC_FLAGS)); + (tmp.flags & ASYNC_FLAGS)); port->port.close_delay = tmp.close_delay; port->port.closing_wait = tmp.closing_wait; port->custom_divisor = tmp.custom_divisor; } - if (change_speed) { + if (change_speed) sx_change_speed(bp, port); - } + func_exit(); unlock_kernel(); return 0; } -static inline int sx_get_serial_info(struct specialix_port * port, +static int sx_get_serial_info(struct specialix_port *port, struct serial_struct __user *retinfo) { struct serial_struct tmp; @@ -1992,11 +1928,10 @@ static inline int sx_get_serial_info(struct specialix_port * port, } -static int sx_ioctl(struct tty_struct * tty, struct file * filp, - unsigned int cmd, unsigned long arg) +static int sx_ioctl(struct tty_struct *tty, struct file *filp, + unsigned int cmd, unsigned long arg) { struct specialix_port *port = (struct specialix_port *)tty->driver_data; - int retval; void __user *argp = (void __user *)arg; func_enter(); @@ -2007,34 +1942,14 @@ static int sx_ioctl(struct tty_struct * tty, struct file * filp, } switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) { - func_exit(); - return retval; - } - tty_wait_until_sent(tty, 0); - if (!arg) - sx_send_break(port, HZ/4); /* 1/4 second */ - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) { - func_exit(); - return retval; - } - tty_wait_until_sent(tty, 0); - sx_send_break(port, arg ? arg*(HZ/10) : HZ/4); + case TIOCGSERIAL: func_exit(); - return 0; - case TIOCGSERIAL: - func_exit(); return sx_get_serial_info(port, argp); - case TIOCSSERIAL: - func_exit(); + case TIOCSSERIAL: + func_exit(); return sx_set_serial_info(port, argp); - default: - func_exit(); + default: + func_exit(); return -ENOIOCTLCMD; } func_exit(); @@ -2042,7 +1957,7 @@ static int sx_ioctl(struct tty_struct * tty, struct file * filp, } -static void sx_throttle(struct tty_struct * tty) +static void sx_throttle(struct tty_struct *tty) { struct specialix_port *port = (struct specialix_port *)tty->driver_data; struct specialix_board *bp; @@ -2058,15 +1973,16 @@ static void sx_throttle(struct tty_struct * tty) bp = port_Board(port); /* Use DTR instead of RTS ! */ - if (SX_CRTSCTS (tty)) + if (sx_crtscts(tty)) port->MSVR &= ~MSVR_DTR; else { /* Auch!!! I think the system shouldn't call this then. */ /* Or maybe we're supposed (allowed?) to do our side of hw handshake anyway, even when hardware handshake is off. When you see this in your logs, please report.... */ - printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n", - port_No (port)); + printk(KERN_ERR + "sx%d: Need to throttle, but can't (hardware hs is off)\n", + port_No(port)); } spin_lock_irqsave(&bp->lock, flags); sx_out(bp, CD186x_CAR, port_No(port)); @@ -2086,7 +2002,7 @@ static void sx_throttle(struct tty_struct * tty) } -static void sx_unthrottle(struct tty_struct * tty) +static void sx_unthrottle(struct tty_struct *tty) { struct specialix_port *port = (struct specialix_port *)tty->driver_data; struct specialix_board *bp; @@ -2103,9 +2019,9 @@ static void sx_unthrottle(struct tty_struct * tty) spin_lock_irqsave(&port->lock, flags); /* XXXX Use DTR INSTEAD???? */ - if (SX_CRTSCTS(tty)) { + if (sx_crtscts(tty)) port->MSVR |= MSVR_DTR; - } /* Else clause: see remark in "sx_throttle"... */ + /* Else clause: see remark in "sx_throttle"... */ spin_lock_irqsave(&bp->lock, flags); sx_out(bp, CD186x_CAR, port_No(port)); spin_unlock_irqrestore(&bp->lock, flags); @@ -2127,7 +2043,7 @@ static void sx_unthrottle(struct tty_struct * tty) } -static void sx_stop(struct tty_struct * tty) +static void sx_stop(struct tty_struct *tty) { struct specialix_port *port = (struct specialix_port *)tty->driver_data; struct specialix_board *bp; @@ -2154,7 +2070,7 @@ static void sx_stop(struct tty_struct * tty) } -static void sx_start(struct tty_struct * tty) +static void sx_start(struct tty_struct *tty) { struct specialix_port *port = (struct specialix_port *)tty->driver_data; struct specialix_board *bp; @@ -2182,7 +2098,7 @@ static void sx_start(struct tty_struct * tty) func_exit(); } -static void sx_hangup(struct tty_struct * tty) +static void sx_hangup(struct tty_struct *tty) { struct specialix_port *port = (struct specialix_port *)tty->driver_data; struct specialix_board *bp; @@ -2201,8 +2117,9 @@ static void sx_hangup(struct tty_struct * tty) spin_lock_irqsave(&port->lock, flags); bp->count -= port->port.count; if (bp->count < 0) { - printk(KERN_ERR "sx%d: sx_hangup: bad board count: %d port: %d\n", - board_No(bp), bp->count, tty->index); + printk(KERN_ERR + "sx%d: sx_hangup: bad board count: %d port: %d\n", + board_No(bp), bp->count, tty->index); bp->count = 0; } port->port.count = 0; @@ -2215,11 +2132,12 @@ static void sx_hangup(struct tty_struct * tty) } -static void sx_set_termios(struct tty_struct * tty, struct ktermios * old_termios) +static void sx_set_termios(struct tty_struct *tty, + struct ktermios *old_termios) { struct specialix_port *port = (struct specialix_port *)tty->driver_data; unsigned long flags; - struct specialix_board * bp; + struct specialix_board *bp; if (sx_paranoia_check(port, tty->name, "sx_set_termios")) return; @@ -2254,6 +2172,7 @@ static const struct tty_operations sx_ops = { .hangup = sx_hangup, .tiocmget = sx_tiocmget, .tiocmset = sx_tiocmset, + .break_ctl = sx_send_break, }; static int sx_init_drivers(void) @@ -2280,13 +2199,16 @@ static int sx_init_drivers(void) B9600 | CS8 | CREAD | HUPCL | CLOCAL; specialix_driver->init_termios.c_ispeed = 9600; specialix_driver->init_termios.c_ospeed = 9600; - specialix_driver->flags = TTY_DRIVER_REAL_RAW; + specialix_driver->flags = TTY_DRIVER_REAL_RAW | + TTY_DRIVER_HARDWARE_BREAK; tty_set_operations(specialix_driver, &sx_ops); - if ((error = tty_register_driver(specialix_driver))) { + error = tty_register_driver(specialix_driver); + if (error) { put_tty_driver(specialix_driver); - printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n", - error); + printk(KERN_ERR + "sx: Couldn't register specialix IO8+ driver, error = %d\n", + error); func_exit(); return 1; } @@ -2322,11 +2244,11 @@ static int __init specialix_init(void) printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n"); printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n"); -#ifdef CONFIG_SPECIALIX_RTSCTS - printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n"); -#else - printk (KERN_INFO "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n"); -#endif + if (sx_rtscts) + printk(KERN_INFO + "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n"); + else + printk(KERN_INFO "sx: DTR/RTS pin is always RTS.\n"); for (i = 0; i < SX_NBOARD; i++) spin_lock_init(&sx_board[i].lock); @@ -2344,27 +2266,27 @@ static int __init specialix_init(void) { struct pci_dev *pdev = NULL; - i=0; + i = 0; while (i < SX_NBOARD) { if (sx_board[i].flags & SX_BOARD_PRESENT) { i++; continue; } - pdev = pci_get_device (PCI_VENDOR_ID_SPECIALIX, - PCI_DEVICE_ID_SPECIALIX_IO8, - pdev); - if (!pdev) break; + pdev = pci_get_device(PCI_VENDOR_ID_SPECIALIX, + PCI_DEVICE_ID_SPECIALIX_IO8, pdev); + if (!pdev) + break; if (pci_enable_device(pdev)) continue; sx_board[i].irq = pdev->irq; - sx_board[i].base = pci_resource_start (pdev, 2); + sx_board[i].base = pci_resource_start(pdev, 2); sx_board[i].flags |= SX_BOARD_IS_PCI; if (!sx_probe(&sx_board[i])) - found ++; + found++; } /* May exit pci_get sequence early with lots of boards */ if (pdev != NULL) @@ -2384,16 +2306,13 @@ static int __init specialix_init(void) } static int iobase[SX_NBOARD] = {0,}; - -static int irq [SX_NBOARD] = {0,}; +static int irq[SX_NBOARD] = {0,}; module_param_array(iobase, int, NULL, 0); module_param_array(irq, int, NULL, 0); module_param(sx_debug, int, 0); +module_param(sx_rtscts, int, 0); module_param(sx_rxfifo, int, 0); -#ifdef SPECIALIX_TIMER -module_param(sx_poll, int, 0); -#endif /* * You can setup up to 4 boards. @@ -2411,10 +2330,10 @@ static int __init specialix_init_module(void) func_enter(); if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) { - for(i = 0; i < SX_NBOARD; i++) { + for (i = 0; i < SX_NBOARD; i++) { sx_board[i].base = iobase[i]; sx_board[i].irq = irq[i]; - sx_board[i].count= 0; + sx_board[i].count = 0; } } @@ -2433,10 +2352,6 @@ static void __exit specialix_exit_module(void) for (i = 0; i < SX_NBOARD; i++) if (sx_board[i].flags & SX_BOARD_PRESENT) sx_release_io_range(&sx_board[i]); -#ifdef SPECIALIX_TIMER - del_timer_sync(&missed_irq_timer); -#endif - func_exit(); } diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 0243efb0be95..19db1eb87c26 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -1025,7 +1025,7 @@ static int stl_write(struct tty_struct *tty, const unsigned char *buf, int count /*****************************************************************************/ -static void stl_putchar(struct tty_struct *tty, unsigned char ch) +static int stl_putchar(struct tty_struct *tty, unsigned char ch) { struct stlport *portp; unsigned int len; @@ -1034,12 +1034,12 @@ static void stl_putchar(struct tty_struct *tty, unsigned char ch) pr_debug("stl_putchar(tty=%p,ch=%x)\n", tty, ch); if (tty == NULL) - return; + return -EINVAL; portp = tty->driver_data; if (portp == NULL) - return; + return -EINVAL; if (portp->tx.buf == NULL) - return; + return -EINVAL; head = portp->tx.head; tail = portp->tx.tail; @@ -1053,6 +1053,7 @@ static void stl_putchar(struct tty_struct *tty, unsigned char ch) head = portp->tx.buf; } portp->tx.head = head; + return 0; } /*****************************************************************************/ @@ -1255,7 +1256,6 @@ static int stl_tiocmset(struct tty_struct *tty, struct file *file, static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { struct stlport *portp; - unsigned int ival; int rc; void __user *argp = (void __user *)arg; @@ -1460,19 +1460,20 @@ static void stl_hangup(struct tty_struct *tty) /*****************************************************************************/ -static void stl_breakctl(struct tty_struct *tty, int state) +static int stl_breakctl(struct tty_struct *tty, int state) { struct stlport *portp; pr_debug("stl_breakctl(tty=%p,state=%d)\n", tty, state); if (tty == NULL) - return; + return -EINVAL; portp = tty->driver_data; if (portp == NULL) - return; + return -EINVAL; stl_sendbreak(portp, ((state == -1) ? 1 : 2)); + return 0; } /*****************************************************************************/ @@ -4753,8 +4754,8 @@ static int __init stallion_module_init(void) if (IS_ERR(stallion_class)) printk("STALLION: failed to create class\n"); for (i = 0; i < 4; i++) - device_create(stallion_class, NULL, MKDEV(STL_SIOMEMMAJOR, i), - "staliomem%d", i); + device_create_drvdata(stallion_class, NULL, MKDEV(STL_SIOMEMMAJOR, i), + NULL, "staliomem%d", i); return 0; err_unrtty: diff --git a/drivers/char/sx.c b/drivers/char/sx.c index d5cffcd6a572..c385206f9db5 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -286,8 +286,8 @@ static void sx_close(void *ptr); static int sx_chars_in_buffer(void *ptr); static int sx_init_board(struct sx_board *board); static int sx_init_portstructs(int nboards, int nports); -static int sx_fw_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg); +static long sx_fw_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg); static int sx_init_drivers(void); static struct tty_driver *sx_driver; @@ -396,7 +396,7 @@ static struct real_driver sx_real_driver = { static const struct file_operations sx_fw_fops = { .owner = THIS_MODULE, - .ioctl = sx_fw_ioctl, + .unlocked_ioctl = sx_fw_ioctl, }; static struct miscdevice sx_fw_device = { @@ -1686,10 +1686,10 @@ static int do_memtest_w(struct sx_board *board, int min, int max) } #endif -static int sx_fw_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) +static long sx_fw_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) { - int rc = 0; + long rc = 0; int __user *descr = (int __user *)arg; int i; static struct sx_board *board = NULL; @@ -1699,13 +1699,10 @@ static int sx_fw_ioctl(struct inode *inode, struct file *filp, func_enter(); -#if 0 - /* Removed superuser check: Sysops can use the permissions on the device - file to restrict access. Recommendation: Root only. (root.root 600) */ - if (!capable(CAP_SYS_ADMIN)) { + if (!capable(CAP_SYS_RAWIO)) return -EPERM; - } -#endif + + lock_kernel(); sx_dprintk(SX_DEBUG_FIRMWARE, "IOCTL %x: %lx\n", cmd, arg); @@ -1720,19 +1717,23 @@ static int sx_fw_ioctl(struct inode *inode, struct file *filp, for (i = 0; i < SX_NBOARDS; i++) sx_dprintk(SX_DEBUG_FIRMWARE, "<%x> ", boards[i].flags); sx_dprintk(SX_DEBUG_FIRMWARE, "\n"); + unlock_kernel(); return -EIO; } switch (cmd) { case SXIO_SET_BOARD: sx_dprintk(SX_DEBUG_FIRMWARE, "set board to %ld\n", arg); + rc = -EIO; if (arg >= SX_NBOARDS) - return -EIO; + break; sx_dprintk(SX_DEBUG_FIRMWARE, "not out of range\n"); if (!(boards[arg].flags & SX_BOARD_PRESENT)) - return -EIO; + break; sx_dprintk(SX_DEBUG_FIRMWARE, ".. and present!\n"); board = &boards[arg]; + rc = 0; + /* FIXME: And this does ... nothing?? */ break; case SXIO_GET_TYPE: rc = -ENOENT; /* If we manage to miss one, return error. */ @@ -1746,7 +1747,7 @@ static int sx_fw_ioctl(struct inode *inode, struct file *filp, rc = SX_TYPE_SI; if (IS_EISA_BOARD(board)) rc = SX_TYPE_SI; - sx_dprintk(SX_DEBUG_FIRMWARE, "returning type= %d\n", rc); + sx_dprintk(SX_DEBUG_FIRMWARE, "returning type= %ld\n", rc); break; case SXIO_DO_RAMTEST: if (sx_initialized) /* Already initialized: better not ramtest the board. */ @@ -1760,19 +1761,26 @@ static int sx_fw_ioctl(struct inode *inode, struct file *filp, rc = do_memtest(board, 0, 0x7ff8); /* if (!rc) rc = do_memtest_w (board, 0, 0x7ff8); */ } - sx_dprintk(SX_DEBUG_FIRMWARE, "returning memtest result= %d\n", - rc); + sx_dprintk(SX_DEBUG_FIRMWARE, + "returning memtest result= %ld\n", rc); break; case SXIO_DOWNLOAD: - if (sx_initialized) /* Already initialized */ - return -EEXIST; - if (!sx_reset(board)) - return -EIO; + if (sx_initialized) {/* Already initialized */ + rc = -EEXIST; + break; + } + if (!sx_reset(board)) { + rc = -EIO; + break; + } sx_dprintk(SX_DEBUG_INIT, "reset the board...\n"); tmp = kmalloc(SX_CHUNK_SIZE, GFP_USER); - if (!tmp) - return -ENOMEM; + if (!tmp) { + rc = -ENOMEM; + break; + } + /* FIXME: check returns */ get_user(nbytes, descr++); get_user(offset, descr++); get_user(data, descr++); @@ -1782,7 +1790,8 @@ static int sx_fw_ioctl(struct inode *inode, struct file *filp, (i + SX_CHUNK_SIZE > nbytes) ? nbytes - i : SX_CHUNK_SIZE)) { kfree(tmp); - return -EFAULT; + rc = -EFAULT; + break; } memcpy_toio(board->base2 + offset + i, tmp, (i + SX_CHUNK_SIZE > nbytes) ? @@ -1798,13 +1807,17 @@ static int sx_fw_ioctl(struct inode *inode, struct file *filp, rc = sx_nports; break; case SXIO_INIT: - if (sx_initialized) /* Already initialized */ - return -EEXIST; + if (sx_initialized) { /* Already initialized */ + rc = -EEXIST; + break; + } /* This is not allowed until all boards are initialized... */ for (i = 0; i < SX_NBOARDS; i++) { if ((boards[i].flags & SX_BOARD_PRESENT) && - !(boards[i].flags & SX_BOARD_INITIALIZED)) - return -EIO; + !(boards[i].flags & SX_BOARD_INITIALIZED)) { + rc = -EIO; + break; + } } for (i = 0; i < SX_NBOARDS; i++) if (!(boards[i].flags & SX_BOARD_PRESENT)) @@ -1832,15 +1845,15 @@ static int sx_fw_ioctl(struct inode *inode, struct file *filp, rc = sx_nports; break; default: - printk(KERN_WARNING "Unknown ioctl on firmware device (%x).\n", - cmd); + rc = -ENOTTY; break; } + unlock_kernel(); func_exit(); return rc; } -static void sx_break(struct tty_struct *tty, int flag) +static int sx_break(struct tty_struct *tty, int flag) { struct sx_port *port = tty->driver_data; int rv; @@ -1857,6 +1870,7 @@ static void sx_break(struct tty_struct *tty, int flag) read_sx_byte(port->board, CHAN_OFFSET(port, hi_hstat))); unlock_kernel(); func_exit(); + return 0; } static int sx_tiocmget(struct tty_struct *tty, struct file *file) diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index 527d220aa4aa..ef6706f09061 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -2897,9 +2897,9 @@ static int tiocmset(struct tty_struct *tty, struct file *file, * * Arguments: tty pointer to tty instance data * break_state -1=set break condition, 0=clear - * Return Value: None + * Return Value: error code */ -static void mgsl_break(struct tty_struct *tty, int break_state) +static int mgsl_break(struct tty_struct *tty, int break_state) { struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data; unsigned long flags; @@ -2909,7 +2909,7 @@ static void mgsl_break(struct tty_struct *tty, int break_state) __FILE__,__LINE__, info->device_name, break_state); if (mgsl_paranoia_check(info, tty->name, "mgsl_break")) - return; + return -EINVAL; spin_lock_irqsave(&info->irq_spinlock,flags); if (break_state == -1) @@ -2917,6 +2917,7 @@ static void mgsl_break(struct tty_struct *tty, int break_state) else usc_OutReg(info,IOCR,(u16)(usc_InReg(info,IOCR) & ~BIT7)); spin_unlock_irqrestore(&info->irq_spinlock,flags); + return 0; } /* end of mgsl_break() */ diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 2c3e43bb2cc9..3e9058993e41 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -165,7 +165,7 @@ static int read_proc(char *page, char **start, off_t off, int count,int *eof, v static int chars_in_buffer(struct tty_struct *tty); static void throttle(struct tty_struct * tty); static void unthrottle(struct tty_struct * tty); -static void set_break(struct tty_struct *tty, int break_state); +static int set_break(struct tty_struct *tty, int break_state); /* * generic HDLC support and callbacks @@ -214,6 +214,7 @@ struct slgt_desc char *buf; /* virtual address of data buffer */ unsigned int pdesc; /* physical address of this descriptor */ dma_addr_t buf_dma_addr; + unsigned short buf_count; }; #define set_desc_buffer(a,b) (a).pbuf = cpu_to_le32((unsigned int)(b)) @@ -302,7 +303,7 @@ struct slgt_info { u32 idle_mode; u32 max_frame_size; /* as set by device config */ - unsigned int raw_rx_size; + unsigned int rbuf_fill_level; unsigned int if_mode; /* device status */ @@ -466,6 +467,7 @@ static void tx_start(struct slgt_info *info); static void tx_stop(struct slgt_info *info); static void tx_set_idle(struct slgt_info *info); static unsigned int free_tbuf_count(struct slgt_info *info); +static unsigned int tbuf_bytes(struct slgt_info *info); static void reset_tbufs(struct slgt_info *info); static void tdma_reset(struct slgt_info *info); static void tdma_start(struct slgt_info *info); @@ -513,7 +515,7 @@ static int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr); static int tiocmget(struct tty_struct *tty, struct file *file); static int tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); -static void set_break(struct tty_struct *tty, int break_state); +static int set_break(struct tty_struct *tty, int break_state); static int get_interface(struct slgt_info *info, int __user *if_mode); static int set_interface(struct slgt_info *info, int if_mode); static int set_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); @@ -849,6 +851,7 @@ static int write(struct tty_struct *tty, int ret = 0; struct slgt_info *info = tty->driver_data; unsigned long flags; + unsigned int bufs_needed; if (sanity_check(info, tty->name, "write")) goto cleanup; @@ -865,25 +868,16 @@ static int write(struct tty_struct *tty, if (!count) goto cleanup; - if (info->params.mode == MGSL_MODE_RAW || - info->params.mode == MGSL_MODE_MONOSYNC || - info->params.mode == MGSL_MODE_BISYNC) { - unsigned int bufs_needed = (count/DMABUFSIZE); - unsigned int bufs_free = free_tbuf_count(info); - if (count % DMABUFSIZE) - ++bufs_needed; - if (bufs_needed > bufs_free) - goto cleanup; - } else { - if (info->tx_active) - goto cleanup; - if (info->tx_count) { - /* send accumulated data from send_char() calls */ - /* as frame and wait before accepting more data. */ - tx_load(info, info->tx_buf, info->tx_count); - goto start; - } + if (!info->tx_active && info->tx_count) { + /* send accumulated data from send_char() */ + tx_load(info, info->tx_buf, info->tx_count); + goto start; } + bufs_needed = (count/DMABUFSIZE); + if (count % DMABUFSIZE) + ++bufs_needed; + if (bufs_needed > free_tbuf_count(info)) + goto cleanup; ret = info->tx_count = count; tx_load(info, buf, count); @@ -1396,10 +1390,12 @@ done: static int chars_in_buffer(struct tty_struct *tty) { struct slgt_info *info = tty->driver_data; + int count; if (sanity_check(info, tty->name, "chars_in_buffer")) return 0; - DBGINFO(("%s chars_in_buffer()=%d\n", info->device_name, info->tx_count)); - return info->tx_count; + count = tbuf_bytes(info); + DBGINFO(("%s chars_in_buffer()=%d\n", info->device_name, count)); + return count; } /* @@ -1452,14 +1448,14 @@ static void unthrottle(struct tty_struct * tty) * set or clear transmit break condition * break_state -1=set break condition, 0=clear */ -static void set_break(struct tty_struct *tty, int break_state) +static int set_break(struct tty_struct *tty, int break_state) { struct slgt_info *info = tty->driver_data; unsigned short value; unsigned long flags; if (sanity_check(info, tty->name, "set_break")) - return; + return -EINVAL; DBGINFO(("%s set_break(%d)\n", info->device_name, break_state)); spin_lock_irqsave(&info->lock,flags); @@ -1470,6 +1466,7 @@ static void set_break(struct tty_struct *tty, int break_state) value &= ~BIT6; wr_reg16(info, TCR, value); spin_unlock_irqrestore(&info->lock,flags); + return 0; } #if SYNCLINK_GENERIC_HDLC @@ -2679,8 +2676,31 @@ static int tx_abort(struct slgt_info *info) static int rx_enable(struct slgt_info *info, int enable) { unsigned long flags; - DBGINFO(("%s rx_enable(%d)\n", info->device_name, enable)); + unsigned int rbuf_fill_level; + DBGINFO(("%s rx_enable(%08x)\n", info->device_name, enable)); spin_lock_irqsave(&info->lock,flags); + /* + * enable[31..16] = receive DMA buffer fill level + * 0 = noop (leave fill level unchanged) + * fill level must be multiple of 4 and <= buffer size + */ + rbuf_fill_level = ((unsigned int)enable) >> 16; + if (rbuf_fill_level) { + if ((rbuf_fill_level > DMABUFSIZE) || (rbuf_fill_level % 4)) { + spin_unlock_irqrestore(&info->lock, flags); + return -EINVAL; + } + info->rbuf_fill_level = rbuf_fill_level; + rx_stop(info); /* restart receiver to use new fill level */ + } + + /* + * enable[1..0] = receiver enable command + * 0 = disable + * 1 = enable + * 2 = enable or force hunt mode if already enabled + */ + enable &= 3; if (enable) { if (!info->rx_enabled) rx_start(info); @@ -3447,7 +3467,7 @@ static struct slgt_info *alloc_dev(int adapter_num, int port_num, struct pci_dev info->magic = MGSL_MAGIC; INIT_WORK(&info->task, bh_handler); info->max_frame_size = 4096; - info->raw_rx_size = DMABUFSIZE; + info->rbuf_fill_level = DMABUFSIZE; info->port.close_delay = 5*HZ/10; info->port.closing_wait = 30*HZ; init_waitqueue_head(&info->status_event_wait_q); @@ -3934,15 +3954,7 @@ static void tdma_start(struct slgt_info *info) /* set 1st descriptor address */ wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc); - switch(info->params.mode) { - case MGSL_MODE_RAW: - case MGSL_MODE_MONOSYNC: - case MGSL_MODE_BISYNC: - wr_reg32(info, TDCSR, BIT2 + BIT0); /* IRQ + DMA enable */ - break; - default: - wr_reg32(info, TDCSR, BIT0); /* DMA enable */ - } + wr_reg32(info, TDCSR, BIT2 + BIT0); /* IRQ + DMA enable */ } static void tx_stop(struct slgt_info *info) @@ -4145,7 +4157,7 @@ static void sync_mode(struct slgt_info *info) * 01 enable * 00 auto-CTS enable */ - val = 0; + val = BIT2; switch(info->params.mode) { case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break; @@ -4418,6 +4430,8 @@ static void msc_set_vcr(struct slgt_info *info) break; } + if (info->if_mode & MGSL_INTERFACE_MSB_FIRST) + val |= BIT4; if (info->signals & SerialSignal_DTR) val |= BIT3; if (info->signals & SerialSignal_RTS) @@ -4456,16 +4470,7 @@ static void free_rbufs(struct slgt_info *info, unsigned int i, unsigned int last while(!done) { /* reset current buffer for reuse */ info->rbufs[i].status = 0; - switch(info->params.mode) { - case MGSL_MODE_RAW: - case MGSL_MODE_MONOSYNC: - case MGSL_MODE_BISYNC: - set_desc_count(info->rbufs[i], info->raw_rx_size); - break; - default: - set_desc_count(info->rbufs[i], DMABUFSIZE); - } - + set_desc_count(info->rbufs[i], info->rbuf_fill_level); if (i == last) done = 1; if (++i == info->rbuf_count) @@ -4572,7 +4577,7 @@ check_again: DBGBH(("%s rx frame status=%04X size=%d\n", info->device_name, status, framesize)); - DBGDATA(info, info->rbufs[start].buf, min_t(int, framesize, DMABUFSIZE), "rx"); + DBGDATA(info, info->rbufs[start].buf, min_t(int, framesize, info->rbuf_fill_level), "rx"); if (framesize) { if (!(info->params.crc_type & HDLC_CRC_RETURN_EX)) { @@ -4592,7 +4597,7 @@ check_again: info->icount.rxok++; while(copy_count) { - int partial_count = min(copy_count, DMABUFSIZE); + int partial_count = min_t(int, copy_count, info->rbuf_fill_level); memcpy(p, info->rbufs[i].buf, partial_count); p += partial_count; copy_count -= partial_count; @@ -4684,6 +4689,56 @@ static unsigned int free_tbuf_count(struct slgt_info *info) } /* + * return number of bytes in unsent transmit DMA buffers + * and the serial controller tx FIFO + */ +static unsigned int tbuf_bytes(struct slgt_info *info) +{ + unsigned int total_count = 0; + unsigned int i = info->tbuf_current; + unsigned int reg_value; + unsigned int count; + unsigned int active_buf_count = 0; + + /* + * Add descriptor counts for all tx DMA buffers. + * If count is zero (cleared by DMA controller after read), + * the buffer is complete or is actively being read from. + * + * Record buf_count of last buffer with zero count starting + * from current ring position. buf_count is mirror + * copy of count and is not cleared by serial controller. + * If DMA controller is active, that buffer is actively + * being read so add to total. + */ + do { + count = desc_count(info->tbufs[i]); + if (count) + total_count += count; + else if (!total_count) + active_buf_count = info->tbufs[i].buf_count; + if (++i == info->tbuf_count) + i = 0; + } while (i != info->tbuf_current); + + /* read tx DMA status register */ + reg_value = rd_reg32(info, TDCSR); + + /* if tx DMA active, last zero count buffer is in use */ + if (reg_value & BIT0) + total_count += active_buf_count; + + /* add tx FIFO count = reg_value[15..8] */ + total_count += (reg_value >> 8) & 0xff; + + /* if transmitter active add one byte for shift register */ + if (info->tx_active) + total_count++; + + return total_count; +} + +/* * load transmit DMA buffer(s) with data */ static void tx_load(struct slgt_info *info, const char *buf, unsigned int size) @@ -4721,6 +4776,7 @@ static void tx_load(struct slgt_info *info, const char *buf, unsigned int size) set_desc_eof(*d, 0); set_desc_count(*d, count); + d->buf_count = count; } info->tbuf_current = i; diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index 5768c4136342..c0490cbd0db2 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -527,7 +527,7 @@ static int read_proc(char *page, char **start, off_t off, int count,int *eof, v static int chars_in_buffer(struct tty_struct *tty); static void throttle(struct tty_struct * tty); static void unthrottle(struct tty_struct * tty); -static void set_break(struct tty_struct *tty, int break_state); +static int set_break(struct tty_struct *tty, int break_state); #if SYNCLINK_GENERIC_HDLC #define dev_to_port(D) (dev_to_hdlc(D)->priv) @@ -552,7 +552,7 @@ static int wait_mgsl_event(SLMP_INFO *info, int __user *mask_ptr); static int tiocmget(struct tty_struct *tty, struct file *file); static int tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); -static void set_break(struct tty_struct *tty, int break_state); +static int set_break(struct tty_struct *tty, int break_state); static void add_device(SLMP_INFO *info); static void device_init(int adapter_num, struct pci_dev *pdev); @@ -1587,7 +1587,7 @@ static void unthrottle(struct tty_struct * tty) /* set or clear transmit break condition * break_state -1=set break condition, 0=clear */ -static void set_break(struct tty_struct *tty, int break_state) +static int set_break(struct tty_struct *tty, int break_state) { unsigned char RegValue; SLMP_INFO * info = (SLMP_INFO *)tty->driver_data; @@ -1598,7 +1598,7 @@ static void set_break(struct tty_struct *tty, int break_state) __FILE__,__LINE__, info->device_name, break_state); if (sanity_check(info, tty->name, "set_break")) - return; + return -EINVAL; spin_lock_irqsave(&info->lock,flags); RegValue = read_reg(info, CTL); @@ -1608,6 +1608,7 @@ static void set_break(struct tty_struct *tty, int break_state) RegValue &= ~BIT3; write_reg(info, CTL, RegValue); spin_unlock_irqrestore(&info->lock,flags); + return 0; } #if SYNCLINK_GENERIC_HDLC diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index e1fc193d9396..ae766d868454 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -580,91 +580,133 @@ void tpm_continue_selftest(struct tpm_chip *chip) } EXPORT_SYMBOL_GPL(tpm_continue_selftest); +#define TPM_INTERNAL_RESULT_SIZE 200 + ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr, char *buf) { - u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 35)]; + u8 *data; ssize_t rc; struct tpm_chip *chip = dev_get_drvdata(dev); if (chip == NULL) return -ENODEV; + data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL); + if (!data) + return -ENOMEM; + memcpy(data, tpm_cap, sizeof(tpm_cap)); data[TPM_CAP_IDX] = TPM_CAP_FLAG; data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_PERM; - rc = transmit_cmd(chip, data, sizeof(data), - "attemtping to determine the permanent state"); - if (rc) + rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE, + "attemtping to determine the permanent enabled state"); + if (rc) { + kfree(data); return 0; - return sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_DISABLE_IDX]); + } + + rc = sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_DISABLE_IDX]); + + kfree(data); + return rc; } EXPORT_SYMBOL_GPL(tpm_show_enabled); ssize_t tpm_show_active(struct device * dev, struct device_attribute * attr, char *buf) { - u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 35)]; + u8 *data; ssize_t rc; struct tpm_chip *chip = dev_get_drvdata(dev); if (chip == NULL) return -ENODEV; + data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL); + if (!data) + return -ENOMEM; + memcpy(data, tpm_cap, sizeof(tpm_cap)); data[TPM_CAP_IDX] = TPM_CAP_FLAG; data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_PERM; - rc = transmit_cmd(chip, data, sizeof(data), - "attemtping to determine the permanent state"); - if (rc) + rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE, + "attemtping to determine the permanent active state"); + if (rc) { + kfree(data); return 0; - return sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_INACTIVE_IDX]); + } + + rc = sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_INACTIVE_IDX]); + + kfree(data); + return rc; } EXPORT_SYMBOL_GPL(tpm_show_active); ssize_t tpm_show_owned(struct device * dev, struct device_attribute * attr, char *buf) { - u8 data[sizeof(tpm_cap)]; + u8 *data; ssize_t rc; struct tpm_chip *chip = dev_get_drvdata(dev); if (chip == NULL) return -ENODEV; + data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL); + if (!data) + return -ENOMEM; + memcpy(data, tpm_cap, sizeof(tpm_cap)); data[TPM_CAP_IDX] = TPM_CAP_PROP; data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_OWNER; - rc = transmit_cmd(chip, data, sizeof(data), + rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE, "attempting to determine the owner state"); - if (rc) + if (rc) { + kfree(data); return 0; - return sprintf(buf, "%d\n", data[TPM_GET_CAP_RET_BOOL_1_IDX]); + } + + rc = sprintf(buf, "%d\n", data[TPM_GET_CAP_RET_BOOL_1_IDX]); + + kfree(data); + return rc; } EXPORT_SYMBOL_GPL(tpm_show_owned); ssize_t tpm_show_temp_deactivated(struct device * dev, struct device_attribute * attr, char *buf) { - u8 data[sizeof(tpm_cap)]; + u8 *data; ssize_t rc; struct tpm_chip *chip = dev_get_drvdata(dev); if (chip == NULL) return -ENODEV; + data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL); + if (!data) + return -ENOMEM; + memcpy(data, tpm_cap, sizeof(tpm_cap)); data[TPM_CAP_IDX] = TPM_CAP_FLAG; data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_VOL; - rc = transmit_cmd(chip, data, sizeof(data), + rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE, "attempting to determine the temporary state"); - if (rc) + if (rc) { + kfree(data); return 0; - return sprintf(buf, "%d\n", data[TPM_GET_CAP_TEMP_INACTIVE_IDX]); + } + + rc = sprintf(buf, "%d\n", data[TPM_GET_CAP_TEMP_INACTIVE_IDX]); + + kfree(data); + return rc; } EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated); @@ -678,7 +720,7 @@ static const u8 pcrread[] = { ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr, char *buf) { - u8 data[max_t(int, max(ARRAY_SIZE(tpm_cap), ARRAY_SIZE(pcrread)), 30)]; + u8 *data; ssize_t rc; int i, j, num_pcrs; __be32 index; @@ -688,21 +730,27 @@ ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr, if (chip == NULL) return -ENODEV; + data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL); + if (!data) + return -ENOMEM; + memcpy(data, tpm_cap, sizeof(tpm_cap)); data[TPM_CAP_IDX] = TPM_CAP_PROP; data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_PCR; - rc = transmit_cmd(chip, data, sizeof(data), + rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE, "attempting to determine the number of PCRS"); - if (rc) + if (rc) { + kfree(data); return 0; + } num_pcrs = be32_to_cpu(*((__be32 *) (data + 14))); for (i = 0; i < num_pcrs; i++) { memcpy(data, pcrread, sizeof(pcrread)); index = cpu_to_be32(i); memcpy(data + 10, &index, 4); - rc = transmit_cmd(chip, data, sizeof(data), + rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE, "attempting to read a PCR"); if (rc) goto out; @@ -712,6 +760,7 @@ ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr, str += sprintf(str, "\n"); } out: + kfree(data); return str - buf; } EXPORT_SYMBOL_GPL(tpm_show_pcrs); @@ -795,7 +844,7 @@ static const u8 cap_version[] = { ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr, char *buf) { - u8 data[max_t(int, max(ARRAY_SIZE(tpm_cap), ARRAY_SIZE(cap_version)), 30)]; + u8 *data; ssize_t rc; char *str = buf; @@ -803,21 +852,27 @@ ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr, if (chip == NULL) return -ENODEV; + data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL); + if (!data) + return -ENOMEM; + memcpy(data, tpm_cap, sizeof(tpm_cap)); data[TPM_CAP_IDX] = TPM_CAP_PROP; data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_MANUFACTURER; - rc = transmit_cmd(chip, data, sizeof(data), + rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE, "attempting to determine the manufacturer"); - if (rc) + if (rc) { + kfree(data); return 0; + } str += sprintf(str, "Manufacturer: 0x%x\n", be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX)))); memcpy(data, cap_version, sizeof(cap_version)); data[CAP_VERSION_IDX] = CAP_VERSION_1_1; - rc = transmit_cmd(chip, data, sizeof(data), + rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE, "attempting to determine the 1.1 version"); if (rc) goto out; @@ -828,6 +883,7 @@ ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr, (int) data[17]); out: + kfree(data); return str - buf; } EXPORT_SYMBOL_GPL(tpm_show_caps); @@ -835,7 +891,7 @@ EXPORT_SYMBOL_GPL(tpm_show_caps); ssize_t tpm_show_caps_1_2(struct device * dev, struct device_attribute * attr, char *buf) { - u8 data[max_t(int, max(ARRAY_SIZE(tpm_cap), ARRAY_SIZE(cap_version)), 30)]; + u8 *data; ssize_t len; char *str = buf; @@ -843,15 +899,20 @@ ssize_t tpm_show_caps_1_2(struct device * dev, if (chip == NULL) return -ENODEV; + data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL); + if (!data) + return -ENOMEM; + memcpy(data, tpm_cap, sizeof(tpm_cap)); data[TPM_CAP_IDX] = TPM_CAP_PROP; data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_MANUFACTURER; - if ((len = tpm_transmit(chip, data, sizeof(data))) <= - TPM_ERROR_SIZE) { + len = tpm_transmit(chip, data, TPM_INTERNAL_RESULT_SIZE); + if (len <= TPM_ERROR_SIZE) { dev_dbg(chip->dev, "A TPM error (%d) occurred " "attempting to determine the manufacturer\n", be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX)))); + kfree(data); return 0; } @@ -861,8 +922,8 @@ ssize_t tpm_show_caps_1_2(struct device * dev, memcpy(data, cap_version, sizeof(cap_version)); data[CAP_VERSION_IDX] = CAP_VERSION_1_2; - if ((len = tpm_transmit(chip, data, sizeof(data))) <= - TPM_ERROR_SIZE) { + len = tpm_transmit(chip, data, TPM_INTERNAL_RESULT_SIZE); + if (len <= TPM_ERROR_SIZE) { dev_err(chip->dev, "A TPM error (%d) occurred " "attempting to determine the 1.2 version\n", be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX)))); @@ -874,6 +935,7 @@ ssize_t tpm_show_caps_1_2(struct device * dev, (int) data[19]); out: + kfree(data); return str - buf; } EXPORT_SYMBOL_GPL(tpm_show_caps_1_2); @@ -966,7 +1028,7 @@ ssize_t tpm_write(struct file *file, const char __user *buf, size_t size, loff_t *off) { struct tpm_chip *chip = file->private_data; - int in_size = size, out_size; + size_t in_size = size, out_size; /* cannot perform a write until the read has cleared either via tpm_read or a user_read_timer timeout */ @@ -1001,7 +1063,7 @@ ssize_t tpm_read(struct file *file, char __user *buf, size_t size, loff_t *off) { struct tpm_chip *chip = file->private_data; - int ret_size; + ssize_t ret_size; del_singleshot_timer_sync(&chip->user_read_timer); flush_scheduled_work(); diff --git a/drivers/char/tpm/tpm_bios.c b/drivers/char/tpm/tpm_bios.c index 60a2d2630e36..68f052b42ed7 100644 --- a/drivers/char/tpm/tpm_bios.c +++ b/drivers/char/tpm/tpm_bios.c @@ -448,7 +448,7 @@ out_free: goto out; } -const struct file_operations tpm_ascii_bios_measurements_ops = { +static const struct file_operations tpm_ascii_bios_measurements_ops = { .open = tpm_ascii_bios_measurements_open, .read = seq_read, .llseek = seq_lseek, @@ -486,7 +486,7 @@ out_free: goto out; } -const struct file_operations tpm_binary_bios_measurements_ops = { +static const struct file_operations tpm_binary_bios_measurements_ops = { .open = tpm_binary_bios_measurements_open, .read = seq_read, .llseek = seq_lseek, diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index c7a977bc03e8..ed1879c0dd8d 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -622,6 +622,7 @@ static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = { {"ATM1200", 0}, /* Atmel */ {"IFX0102", 0}, /* Infineon */ {"BCM0101", 0}, /* Broadcom */ + {"BCM0102", 0}, /* Broadcom */ {"NSC1200", 0}, /* National */ {"ICO0102", 0}, /* Intel */ /* Add new here */ diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 82f6a8c86332..e1b46bc7e43c 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -656,558 +656,6 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); /** - * tty_set_termios_ldisc - set ldisc field - * @tty: tty structure - * @num: line discipline number - * - * This is probably overkill for real world processors but - * they are not on hot paths so a little discipline won't do - * any harm. - * - * Locking: takes termios_mutex - */ - -static void tty_set_termios_ldisc(struct tty_struct *tty, int num) -{ - mutex_lock(&tty->termios_mutex); - tty->termios->c_line = num; - mutex_unlock(&tty->termios_mutex); -} - -/* - * This guards the refcounted line discipline lists. The lock - * must be taken with irqs off because there are hangup path - * callers who will do ldisc lookups and cannot sleep. - */ - -static DEFINE_SPINLOCK(tty_ldisc_lock); -static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); -/* Line disc dispatch table */ -static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; - -/** - * tty_register_ldisc - install a line discipline - * @disc: ldisc number - * @new_ldisc: pointer to the ldisc object - * - * Installs a new line discipline into the kernel. The discipline - * is set up as unreferenced and then made available to the kernel - * from this point onwards. - * - * Locking: - * takes tty_ldisc_lock to guard against ldisc races - */ - -int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) -{ - unsigned long flags; - int ret = 0; - - if (disc < N_TTY || disc >= NR_LDISCS) - return -EINVAL; - - spin_lock_irqsave(&tty_ldisc_lock, flags); - tty_ldiscs[disc] = new_ldisc; - new_ldisc->num = disc; - new_ldisc->refcount = 0; - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - - return ret; -} -EXPORT_SYMBOL(tty_register_ldisc); - -/** - * tty_unregister_ldisc - unload a line discipline - * @disc: ldisc number - * @new_ldisc: pointer to the ldisc object - * - * Remove a line discipline from the kernel providing it is not - * currently in use. - * - * Locking: - * takes tty_ldisc_lock to guard against ldisc races - */ - -int tty_unregister_ldisc(int disc) -{ - unsigned long flags; - int ret = 0; - - if (disc < N_TTY || disc >= NR_LDISCS) - return -EINVAL; - - spin_lock_irqsave(&tty_ldisc_lock, flags); - if (tty_ldiscs[disc]->refcount) - ret = -EBUSY; - else - tty_ldiscs[disc] = NULL; - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - - return ret; -} -EXPORT_SYMBOL(tty_unregister_ldisc); - - -/** - * tty_ldisc_try_get - try and reference an ldisc - * @disc: ldisc number - * @ld: tty ldisc structure to complete - * - * Attempt to open and lock a line discipline into place. Return - * the line discipline refcounted and assigned in ld. On an error - * report the error code back - */ - -static int tty_ldisc_try_get(int disc, struct tty_ldisc *ld) -{ - unsigned long flags; - struct tty_ldisc_ops *ldops; - int err = -EINVAL; - - spin_lock_irqsave(&tty_ldisc_lock, flags); - ld->ops = NULL; - ldops = tty_ldiscs[disc]; - /* Check the entry is defined */ - if (ldops) { - /* If the module is being unloaded we can't use it */ - if (!try_module_get(ldops->owner)) - err = -EAGAIN; - else { - /* lock it */ - ldops->refcount++; - ld->ops = ldops; - err = 0; - } - } - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - return err; -} - -/** - * tty_ldisc_get - take a reference to an ldisc - * @disc: ldisc number - * @ld: tty line discipline structure to use - * - * Takes a reference to a line discipline. Deals with refcounts and - * module locking counts. Returns NULL if the discipline is not available. - * Returns a pointer to the discipline and bumps the ref count if it is - * available - * - * Locking: - * takes tty_ldisc_lock to guard against ldisc races - */ - -static int tty_ldisc_get(int disc, struct tty_ldisc *ld) -{ - int err; - - if (disc < N_TTY || disc >= NR_LDISCS) - return -EINVAL; - err = tty_ldisc_try_get(disc, ld); - if (err == -EAGAIN) { - request_module("tty-ldisc-%d", disc); - err = tty_ldisc_try_get(disc, ld); - } - return err; -} - -/** - * tty_ldisc_put - drop ldisc reference - * @disc: ldisc number - * - * Drop a reference to a line discipline. Manage refcounts and - * module usage counts - * - * Locking: - * takes tty_ldisc_lock to guard against ldisc races - */ - -static void tty_ldisc_put(struct tty_ldisc_ops *ld) -{ - unsigned long flags; - int disc = ld->num; - - BUG_ON(disc < N_TTY || disc >= NR_LDISCS); - - spin_lock_irqsave(&tty_ldisc_lock, flags); - ld = tty_ldiscs[disc]; - BUG_ON(ld->refcount == 0); - ld->refcount--; - module_put(ld->owner); - spin_unlock_irqrestore(&tty_ldisc_lock, flags); -} - -static void * tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) -{ - return (*pos < NR_LDISCS) ? pos : NULL; -} - -static void * tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos) -{ - (*pos)++; - return (*pos < NR_LDISCS) ? pos : NULL; -} - -static void tty_ldiscs_seq_stop(struct seq_file *m, void *v) -{ -} - -static int tty_ldiscs_seq_show(struct seq_file *m, void *v) -{ - int i = *(loff_t *)v; - struct tty_ldisc ld; - - if (tty_ldisc_get(i, &ld) < 0) - return 0; - seq_printf(m, "%-10s %2d\n", ld.ops->name ? ld.ops->name : "???", i); - tty_ldisc_put(ld.ops); - return 0; -} - -static const struct seq_operations tty_ldiscs_seq_ops = { - .start = tty_ldiscs_seq_start, - .next = tty_ldiscs_seq_next, - .stop = tty_ldiscs_seq_stop, - .show = tty_ldiscs_seq_show, -}; - -static int proc_tty_ldiscs_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &tty_ldiscs_seq_ops); -} - -const struct file_operations tty_ldiscs_proc_fops = { - .owner = THIS_MODULE, - .open = proc_tty_ldiscs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -/** - * tty_ldisc_assign - set ldisc on a tty - * @tty: tty to assign - * @ld: line discipline - * - * Install an instance of a line discipline into a tty structure. The - * ldisc must have a reference count above zero to ensure it remains/ - * The tty instance refcount starts at zero. - * - * Locking: - * Caller must hold references - */ - -static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld) -{ - ld->refcount = 0; - tty->ldisc = *ld; -} - -/** - * tty_ldisc_try - internal helper - * @tty: the tty - * - * Make a single attempt to grab and bump the refcount on - * the tty ldisc. Return 0 on failure or 1 on success. This is - * used to implement both the waiting and non waiting versions - * of tty_ldisc_ref - * - * Locking: takes tty_ldisc_lock - */ - -static int tty_ldisc_try(struct tty_struct *tty) -{ - unsigned long flags; - struct tty_ldisc *ld; - int ret = 0; - - spin_lock_irqsave(&tty_ldisc_lock, flags); - ld = &tty->ldisc; - if (test_bit(TTY_LDISC, &tty->flags)) { - ld->refcount++; - ret = 1; - } - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - return ret; -} - -/** - * tty_ldisc_ref_wait - wait for the tty ldisc - * @tty: tty device - * - * Dereference the line discipline for the terminal and take a - * reference to it. If the line discipline is in flux then - * wait patiently until it changes. - * - * Note: Must not be called from an IRQ/timer context. The caller - * must also be careful not to hold other locks that will deadlock - * against a discipline change, such as an existing ldisc reference - * (which we check for) - * - * Locking: call functions take tty_ldisc_lock - */ - -struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) -{ - /* wait_event is a macro */ - wait_event(tty_ldisc_wait, tty_ldisc_try(tty)); - if (tty->ldisc.refcount == 0) - printk(KERN_ERR "tty_ldisc_ref_wait\n"); - return &tty->ldisc; -} - -EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); - -/** - * tty_ldisc_ref - get the tty ldisc - * @tty: tty device - * - * Dereference the line discipline for the terminal and take a - * reference to it. If the line discipline is in flux then - * return NULL. Can be called from IRQ and timer functions. - * - * Locking: called functions take tty_ldisc_lock - */ - -struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty) -{ - if (tty_ldisc_try(tty)) - return &tty->ldisc; - return NULL; -} - -EXPORT_SYMBOL_GPL(tty_ldisc_ref); - -/** - * tty_ldisc_deref - free a tty ldisc reference - * @ld: reference to free up - * - * Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May - * be called in IRQ context. - * - * Locking: takes tty_ldisc_lock - */ - -void tty_ldisc_deref(struct tty_ldisc *ld) -{ - unsigned long flags; - - BUG_ON(ld == NULL); - - spin_lock_irqsave(&tty_ldisc_lock, flags); - if (ld->refcount == 0) - printk(KERN_ERR "tty_ldisc_deref: no references.\n"); - else - ld->refcount--; - if (ld->refcount == 0) - wake_up(&tty_ldisc_wait); - spin_unlock_irqrestore(&tty_ldisc_lock, flags); -} - -EXPORT_SYMBOL_GPL(tty_ldisc_deref); - -/** - * tty_ldisc_enable - allow ldisc use - * @tty: terminal to activate ldisc on - * - * Set the TTY_LDISC flag when the line discipline can be called - * again. Do necessary wakeups for existing sleepers. - * - * Note: nobody should set this bit except via this function. Clearing - * directly is allowed. - */ - -static void tty_ldisc_enable(struct tty_struct *tty) -{ - set_bit(TTY_LDISC, &tty->flags); - wake_up(&tty_ldisc_wait); -} - -/** - * tty_ldisc_restore - helper for tty ldisc change - * @tty: tty to recover - * @old: previous ldisc - * - * Restore the previous line discipline or N_TTY when a line discipline - * change fails due to an open error - */ - -static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) -{ - char buf[64]; - struct tty_ldisc new_ldisc; - - /* There is an outstanding reference here so this is safe */ - tty_ldisc_get(old->ops->num, old); - tty_ldisc_assign(tty, old); - tty_set_termios_ldisc(tty, old->ops->num); - if (old->ops->open && (old->ops->open(tty) < 0)) { - tty_ldisc_put(old->ops); - /* This driver is always present */ - if (tty_ldisc_get(N_TTY, &new_ldisc) < 0) - panic("n_tty: get"); - tty_ldisc_assign(tty, &new_ldisc); - tty_set_termios_ldisc(tty, N_TTY); - if (new_ldisc.ops->open) { - int r = new_ldisc.ops->open(tty); - if (r < 0) - panic("Couldn't open N_TTY ldisc for " - "%s --- error %d.", - tty_name(tty, buf), r); - } - } -} - -/** - * tty_set_ldisc - set line discipline - * @tty: the terminal to set - * @ldisc: the line discipline - * - * Set the discipline of a tty line. Must be called from a process - * context. - * - * Locking: takes tty_ldisc_lock. - * called functions take termios_mutex - */ - -static int tty_set_ldisc(struct tty_struct *tty, int ldisc) -{ - int retval; - struct tty_ldisc o_ldisc, new_ldisc; - int work; - unsigned long flags; - struct tty_struct *o_tty; - -restart: - /* This is a bit ugly for now but means we can break the 'ldisc - is part of the tty struct' assumption later */ - retval = tty_ldisc_get(ldisc, &new_ldisc); - if (retval) - return retval; - - /* - * Problem: What do we do if this blocks ? - */ - - tty_wait_until_sent(tty, 0); - - if (tty->ldisc.ops->num == ldisc) { - tty_ldisc_put(new_ldisc.ops); - return 0; - } - - /* - * No more input please, we are switching. The new ldisc - * will update this value in the ldisc open function - */ - - tty->receive_room = 0; - - o_ldisc = tty->ldisc; - o_tty = tty->link; - - /* - * Make sure we don't change while someone holds a - * reference to the line discipline. The TTY_LDISC bit - * prevents anyone taking a reference once it is clear. - * We need the lock to avoid racing reference takers. - */ - - spin_lock_irqsave(&tty_ldisc_lock, flags); - if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) { - if (tty->ldisc.refcount) { - /* Free the new ldisc we grabbed. Must drop the lock - first. */ - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - tty_ldisc_put(o_ldisc.ops); - /* - * There are several reasons we may be busy, including - * random momentary I/O traffic. We must therefore - * retry. We could distinguish between blocking ops - * and retries if we made tty_ldisc_wait() smarter. - * That is up for discussion. - */ - if (wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0) - return -ERESTARTSYS; - goto restart; - } - if (o_tty && o_tty->ldisc.refcount) { - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - tty_ldisc_put(o_tty->ldisc.ops); - if (wait_event_interruptible(tty_ldisc_wait, o_tty->ldisc.refcount == 0) < 0) - return -ERESTARTSYS; - goto restart; - } - } - /* - * If the TTY_LDISC bit is set, then we are racing against - * another ldisc change - */ - if (!test_bit(TTY_LDISC, &tty->flags)) { - struct tty_ldisc *ld; - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - tty_ldisc_put(new_ldisc.ops); - ld = tty_ldisc_ref_wait(tty); - tty_ldisc_deref(ld); - goto restart; - } - - clear_bit(TTY_LDISC, &tty->flags); - if (o_tty) - clear_bit(TTY_LDISC, &o_tty->flags); - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - - /* - * From this point on we know nobody has an ldisc - * usage reference, nor can they obtain one until - * we say so later on. - */ - - work = cancel_delayed_work(&tty->buf.work); - /* - * Wait for ->hangup_work and ->buf.work handlers to terminate - * MUST NOT hold locks here. - */ - flush_scheduled_work(); - /* Shutdown the current discipline. */ - if (o_ldisc.ops->close) - (o_ldisc.ops->close)(tty); - - /* Now set up the new line discipline. */ - tty_ldisc_assign(tty, &new_ldisc); - tty_set_termios_ldisc(tty, ldisc); - if (new_ldisc.ops->open) - retval = (new_ldisc.ops->open)(tty); - if (retval < 0) { - tty_ldisc_put(new_ldisc.ops); - tty_ldisc_restore(tty, &o_ldisc); - } - /* At this point we hold a reference to the new ldisc and a - a reference to the old ldisc. If we ended up flipping back - to the existing ldisc we have two references to it */ - - if (tty->ldisc.ops->num != o_ldisc.ops->num && tty->ops->set_ldisc) - tty->ops->set_ldisc(tty); - - tty_ldisc_put(o_ldisc.ops); - - /* - * Allow ldisc referencing to occur as soon as the driver - * ldisc callback completes. - */ - - tty_ldisc_enable(tty); - if (o_tty) - tty_ldisc_enable(o_tty); - - /* Restart it in case no characters kick it off. Safe if - already running */ - if (work) - schedule_delayed_work(&tty->buf.work, 1); - return retval; -} - -/** * get_tty_driver - find device of a tty * @dev_t: device identifier * @index: returns the index of the tty @@ -1467,7 +915,7 @@ static void tty_reset_termios(struct tty_struct *tty) * do_tty_hangup - actual handler for hangup events * @work: tty device * -k * This can be called by the "eventd" kernel thread. That is process + * This can be called by the "eventd" kernel thread. That is process * synchronous but doesn't hold any locks, so we need to make sure we * have the appropriate locks for what we're doing. * @@ -1671,19 +1119,6 @@ int tty_hung_up_p(struct file *filp) EXPORT_SYMBOL(tty_hung_up_p); -/** - * is_tty - checker whether file is a TTY - * @filp: file handle that may be a tty - * - * Check if the file handle is a tty handle. - */ - -int is_tty(struct file *filp) -{ - return filp->f_op->read == tty_read - || filp->f_op->read == hung_up_tty_read; -} - static void session_clear_tty(struct pid *session) { struct task_struct *p; @@ -2193,7 +1628,6 @@ static int init_dev(struct tty_driver *driver, int idx, struct ktermios *tp, **tp_loc, *o_tp, **o_tp_loc; struct ktermios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc; int retval = 0; - struct tty_ldisc *ld; /* check whether we're reopening an existing tty */ if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { @@ -2342,25 +1776,12 @@ static int init_dev(struct tty_driver *driver, int idx, * If we fail here just call release_tty to clean up. No need * to decrement the use counts, as release_tty doesn't care. */ - - ld = &tty->ldisc; - if (ld->ops->open) { - retval = (ld->ops->open)(tty); - if (retval) - goto release_mem_out; - } - if (o_tty && o_tty->ldisc.ops->open) { - retval = (o_tty->ldisc.ops->open)(o_tty); - if (retval) { - if (ld->ops->close) - (ld->ops->close)(tty); - goto release_mem_out; - } - tty_ldisc_enable(o_tty); - } - tty_ldisc_enable(tty); - goto success; + retval = tty_ldisc_setup(tty, o_tty); + + if (retval) + goto release_mem_out; + goto success; /* * This fast open can be used if the tty is already open. @@ -2498,12 +1919,10 @@ static void release_tty(struct tty_struct *tty, int idx) static void release_dev(struct file *filp) { struct tty_struct *tty, *o_tty; - struct tty_ldisc ld; int pty_master, tty_closing, o_tty_closing, do_sleep; int devpts; int idx; char buf[64]; - unsigned long flags; tty = (struct tty_struct *)filp->private_data; if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, @@ -2705,56 +2124,9 @@ static void release_dev(struct file *filp) printk(KERN_DEBUG "freeing tty structure..."); #endif /* - * Prevent flush_to_ldisc() from rescheduling the work for later. Then - * kill any delayed work. As this is the final close it does not - * race with the set_ldisc code path. + * Ask the line discipline code to release its structures */ - clear_bit(TTY_LDISC, &tty->flags); - cancel_delayed_work(&tty->buf.work); - - /* - * Wait for ->hangup_work and ->buf.work handlers to terminate - */ - - flush_scheduled_work(); - - /* - * Wait for any short term users (we know they are just driver - * side waiters as the file is closing so user count on the file - * side is zero. - */ - spin_lock_irqsave(&tty_ldisc_lock, flags); - while (tty->ldisc.refcount) { - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0); - spin_lock_irqsave(&tty_ldisc_lock, flags); - } - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - /* - * Shutdown the current line discipline, and reset it to N_TTY. - * - * FIXME: this MUST get fixed for the new reflocking - */ - if (tty->ldisc.ops->close) - (tty->ldisc.ops->close)(tty); - tty_ldisc_put(tty->ldisc.ops); - - /* - * Switch the line discipline back - */ - WARN_ON(tty_ldisc_get(N_TTY, &ld)); - tty_ldisc_assign(tty, &ld); - tty_set_termios_ldisc(tty, N_TTY); - if (o_tty) { - /* FIXME: could o_tty be in setldisc here ? */ - clear_bit(TTY_LDISC, &o_tty->flags); - if (o_tty->ldisc.ops->close) - (o_tty->ldisc.ops->close)(o_tty); - tty_ldisc_put(o_tty->ldisc.ops); - WARN_ON(tty_ldisc_get(N_TTY, &ld)); - tty_ldisc_assign(o_tty, &ld); - tty_set_termios_ldisc(o_tty, N_TTY); - } + tty_ldisc_release(tty, o_tty); /* * The release_tty function takes care of the details of clearing * the slots and preserving the termios structure. @@ -3464,16 +2836,29 @@ static int tiocsetd(struct tty_struct *tty, int __user *p) static int send_break(struct tty_struct *tty, unsigned int duration) { - if (tty_write_lock(tty, 0) < 0) - return -EINTR; - tty->ops->break_ctl(tty, -1); - if (!signal_pending(current)) - msleep_interruptible(duration); - tty->ops->break_ctl(tty, 0); - tty_write_unlock(tty); - if (signal_pending(current)) - return -EINTR; - return 0; + int retval; + + if (tty->ops->break_ctl == NULL) + return 0; + + if (tty->driver->flags & TTY_DRIVER_HARDWARE_BREAK) + retval = tty->ops->break_ctl(tty, duration); + else { + /* Do the work ourselves */ + if (tty_write_lock(tty, 0) < 0) + return -EINTR; + retval = tty->ops->break_ctl(tty, -1); + if (retval) + goto out; + if (!signal_pending(current)) + msleep_interruptible(duration); + retval = tty->ops->break_ctl(tty, 0); +out: + tty_write_unlock(tty); + if (signal_pending(current)) + retval = -EINTR; + } + return retval; } /** @@ -3564,36 +2949,6 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) tty->driver->subtype == PTY_TYPE_MASTER) real_tty = tty->link; - /* - * Break handling by driver - */ - - retval = -EINVAL; - - if (!tty->ops->break_ctl) { - switch (cmd) { - case TIOCSBRK: - case TIOCCBRK: - if (tty->ops->ioctl) - retval = tty->ops->ioctl(tty, file, cmd, arg); - if (retval != -EINVAL && retval != -ENOIOCTLCMD) - printk(KERN_WARNING "tty: driver %s needs updating to use break_ctl\n", tty->driver->name); - return retval; - - /* These two ioctl's always return success; even if */ - /* the driver doesn't support them. */ - case TCSBRK: - case TCSBRKP: - if (!tty->ops->ioctl) - return 0; - retval = tty->ops->ioctl(tty, file, cmd, arg); - if (retval != -EINVAL && retval != -ENOIOCTLCMD) - printk(KERN_WARNING "tty: driver %s needs updating to use break_ctl\n", tty->driver->name); - if (retval == -ENOIOCTLCMD) - retval = 0; - return retval; - } - } /* * Factor out some common prep work @@ -3615,6 +2970,9 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } + /* + * Now do the stuff. + */ switch (cmd) { case TIOCSTI: return tiocsti(tty, p); @@ -3658,12 +3016,11 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) */ case TIOCSBRK: /* Turn break on, unconditionally */ if (tty->ops->break_ctl) - tty->ops->break_ctl(tty, -1); + return tty->ops->break_ctl(tty, -1); return 0; - case TIOCCBRK: /* Turn break off, unconditionally */ if (tty->ops->break_ctl) - tty->ops->break_ctl(tty, 0); + return tty->ops->break_ctl(tty, 0); return 0; case TCSBRK: /* SVID version: non-zero arg --> no break */ /* non-zero arg means wait for all output data @@ -3962,12 +3319,9 @@ EXPORT_SYMBOL(tty_flip_buffer_push); static void initialize_tty_struct(struct tty_struct *tty) { - struct tty_ldisc ld; memset(tty, 0, sizeof(struct tty_struct)); tty->magic = TTY_MAGIC; - if (tty_ldisc_get(N_TTY, &ld) < 0) - panic("n_tty: init_tty"); - tty_ldisc_assign(tty, &ld); + tty_ldisc_init(tty); tty->session = NULL; tty->pgrp = NULL; tty->overrun_time = jiffies; @@ -4045,7 +3399,7 @@ struct device *tty_register_device(struct tty_driver *driver, unsigned index, else tty_line_name(driver, index, name); - return device_create(tty_class, device, dev, name); + return device_create_drvdata(tty_class, device, dev, NULL, name); } /** @@ -4226,7 +3580,6 @@ void proc_clear_tty(struct task_struct *p) p->signal->tty = NULL; spin_unlock_irq(&p->sighand->siglock); } -EXPORT_SYMBOL(proc_clear_tty); /* Called under the sighand lock */ @@ -4280,7 +3633,7 @@ void __init console_init(void) initcall_t *call; /* Setup the default TTY line discipline. */ - (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY); + tty_ldisc_begin(); /* * set up the console device so that later boot sequences can @@ -4323,20 +3676,22 @@ static int __init tty_init(void) if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) || register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0) panic("Couldn't register /dev/tty driver\n"); - device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), "tty"); + device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, + "tty"); cdev_init(&console_cdev, &console_fops); if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) || register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0) panic("Couldn't register /dev/console driver\n"); - device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), "console"); + device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, + "console"); #ifdef CONFIG_UNIX98_PTYS cdev_init(&ptmx_cdev, &ptmx_fops); if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) || register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0) panic("Couldn't register /dev/ptmx driver\n"); - device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), "ptmx"); + device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx"); #endif #ifdef CONFIG_VT @@ -4344,7 +3699,7 @@ static int __init tty_init(void) if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) || register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0) panic("Couldn't register /dev/tty0 driver\n"); - device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), "tty0"); + device_create_drvdata(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0"); vty_init(); #endif diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c new file mode 100644 index 000000000000..241cbdea65ab --- /dev/null +++ b/drivers/char/tty_ldisc.c @@ -0,0 +1,714 @@ +#include <linux/types.h> +#include <linux/major.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/fcntl.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/devpts_fs.h> +#include <linux/file.h> +#include <linux/fdtable.h> +#include <linux/console.h> +#include <linux/timer.h> +#include <linux/ctype.h> +#include <linux/kd.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/poll.h> +#include <linux/proc_fs.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/smp_lock.h> +#include <linux/device.h> +#include <linux/wait.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/seq_file.h> + +#include <linux/uaccess.h> +#include <asm/system.h> + +#include <linux/kbd_kern.h> +#include <linux/vt_kern.h> +#include <linux/selection.h> + +#include <linux/kmod.h> +#include <linux/nsproxy.h> + +/* + * This guards the refcounted line discipline lists. The lock + * must be taken with irqs off because there are hangup path + * callers who will do ldisc lookups and cannot sleep. + */ + +static DEFINE_SPINLOCK(tty_ldisc_lock); +static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); +/* Line disc dispatch table */ +static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; + +/** + * tty_register_ldisc - install a line discipline + * @disc: ldisc number + * @new_ldisc: pointer to the ldisc object + * + * Installs a new line discipline into the kernel. The discipline + * is set up as unreferenced and then made available to the kernel + * from this point onwards. + * + * Locking: + * takes tty_ldisc_lock to guard against ldisc races + */ + +int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) +{ + unsigned long flags; + int ret = 0; + + if (disc < N_TTY || disc >= NR_LDISCS) + return -EINVAL; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + tty_ldiscs[disc] = new_ldisc; + new_ldisc->num = disc; + new_ldisc->refcount = 0; + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + + return ret; +} +EXPORT_SYMBOL(tty_register_ldisc); + +/** + * tty_unregister_ldisc - unload a line discipline + * @disc: ldisc number + * @new_ldisc: pointer to the ldisc object + * + * Remove a line discipline from the kernel providing it is not + * currently in use. + * + * Locking: + * takes tty_ldisc_lock to guard against ldisc races + */ + +int tty_unregister_ldisc(int disc) +{ + unsigned long flags; + int ret = 0; + + if (disc < N_TTY || disc >= NR_LDISCS) + return -EINVAL; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + if (tty_ldiscs[disc]->refcount) + ret = -EBUSY; + else + tty_ldiscs[disc] = NULL; + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + + return ret; +} +EXPORT_SYMBOL(tty_unregister_ldisc); + + +/** + * tty_ldisc_try_get - try and reference an ldisc + * @disc: ldisc number + * @ld: tty ldisc structure to complete + * + * Attempt to open and lock a line discipline into place. Return + * the line discipline refcounted and assigned in ld. On an error + * report the error code back + */ + +static int tty_ldisc_try_get(int disc, struct tty_ldisc *ld) +{ + unsigned long flags; + struct tty_ldisc_ops *ldops; + int err = -EINVAL; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + ld->ops = NULL; + ldops = tty_ldiscs[disc]; + /* Check the entry is defined */ + if (ldops) { + /* If the module is being unloaded we can't use it */ + if (!try_module_get(ldops->owner)) + err = -EAGAIN; + else { + /* lock it */ + ldops->refcount++; + ld->ops = ldops; + err = 0; + } + } + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + return err; +} + +/** + * tty_ldisc_get - take a reference to an ldisc + * @disc: ldisc number + * @ld: tty line discipline structure to use + * + * Takes a reference to a line discipline. Deals with refcounts and + * module locking counts. Returns NULL if the discipline is not available. + * Returns a pointer to the discipline and bumps the ref count if it is + * available + * + * Locking: + * takes tty_ldisc_lock to guard against ldisc races + */ + +static int tty_ldisc_get(int disc, struct tty_ldisc *ld) +{ + int err; + + if (disc < N_TTY || disc >= NR_LDISCS) + return -EINVAL; + err = tty_ldisc_try_get(disc, ld); + if (err == -EAGAIN) { + request_module("tty-ldisc-%d", disc); + err = tty_ldisc_try_get(disc, ld); + } + return err; +} + +/** + * tty_ldisc_put - drop ldisc reference + * @disc: ldisc number + * + * Drop a reference to a line discipline. Manage refcounts and + * module usage counts + * + * Locking: + * takes tty_ldisc_lock to guard against ldisc races + */ + +static void tty_ldisc_put(struct tty_ldisc_ops *ld) +{ + unsigned long flags; + int disc = ld->num; + + BUG_ON(disc < N_TTY || disc >= NR_LDISCS); + + spin_lock_irqsave(&tty_ldisc_lock, flags); + ld = tty_ldiscs[disc]; + BUG_ON(ld->refcount == 0); + ld->refcount--; + module_put(ld->owner); + spin_unlock_irqrestore(&tty_ldisc_lock, flags); +} + +static void * tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) +{ + return (*pos < NR_LDISCS) ? pos : NULL; +} + +static void * tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos) +{ + (*pos)++; + return (*pos < NR_LDISCS) ? pos : NULL; +} + +static void tty_ldiscs_seq_stop(struct seq_file *m, void *v) +{ +} + +static int tty_ldiscs_seq_show(struct seq_file *m, void *v) +{ + int i = *(loff_t *)v; + struct tty_ldisc ld; + + if (tty_ldisc_get(i, &ld) < 0) + return 0; + seq_printf(m, "%-10s %2d\n", ld.ops->name ? ld.ops->name : "???", i); + tty_ldisc_put(ld.ops); + return 0; +} + +static const struct seq_operations tty_ldiscs_seq_ops = { + .start = tty_ldiscs_seq_start, + .next = tty_ldiscs_seq_next, + .stop = tty_ldiscs_seq_stop, + .show = tty_ldiscs_seq_show, +}; + +static int proc_tty_ldiscs_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &tty_ldiscs_seq_ops); +} + +const struct file_operations tty_ldiscs_proc_fops = { + .owner = THIS_MODULE, + .open = proc_tty_ldiscs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +/** + * tty_ldisc_assign - set ldisc on a tty + * @tty: tty to assign + * @ld: line discipline + * + * Install an instance of a line discipline into a tty structure. The + * ldisc must have a reference count above zero to ensure it remains/ + * The tty instance refcount starts at zero. + * + * Locking: + * Caller must hold references + */ + +static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld) +{ + ld->refcount = 0; + tty->ldisc = *ld; +} + +/** + * tty_ldisc_try - internal helper + * @tty: the tty + * + * Make a single attempt to grab and bump the refcount on + * the tty ldisc. Return 0 on failure or 1 on success. This is + * used to implement both the waiting and non waiting versions + * of tty_ldisc_ref + * + * Locking: takes tty_ldisc_lock + */ + +static int tty_ldisc_try(struct tty_struct *tty) +{ + unsigned long flags; + struct tty_ldisc *ld; + int ret = 0; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + ld = &tty->ldisc; + if (test_bit(TTY_LDISC, &tty->flags)) { + ld->refcount++; + ret = 1; + } + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + return ret; +} + +/** + * tty_ldisc_ref_wait - wait for the tty ldisc + * @tty: tty device + * + * Dereference the line discipline for the terminal and take a + * reference to it. If the line discipline is in flux then + * wait patiently until it changes. + * + * Note: Must not be called from an IRQ/timer context. The caller + * must also be careful not to hold other locks that will deadlock + * against a discipline change, such as an existing ldisc reference + * (which we check for) + * + * Locking: call functions take tty_ldisc_lock + */ + +struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) +{ + /* wait_event is a macro */ + wait_event(tty_ldisc_wait, tty_ldisc_try(tty)); + if (tty->ldisc.refcount == 0) + printk(KERN_ERR "tty_ldisc_ref_wait\n"); + return &tty->ldisc; +} + +EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); + +/** + * tty_ldisc_ref - get the tty ldisc + * @tty: tty device + * + * Dereference the line discipline for the terminal and take a + * reference to it. If the line discipline is in flux then + * return NULL. Can be called from IRQ and timer functions. + * + * Locking: called functions take tty_ldisc_lock + */ + +struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty) +{ + if (tty_ldisc_try(tty)) + return &tty->ldisc; + return NULL; +} + +EXPORT_SYMBOL_GPL(tty_ldisc_ref); + +/** + * tty_ldisc_deref - free a tty ldisc reference + * @ld: reference to free up + * + * Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May + * be called in IRQ context. + * + * Locking: takes tty_ldisc_lock + */ + +void tty_ldisc_deref(struct tty_ldisc *ld) +{ + unsigned long flags; + + BUG_ON(ld == NULL); + + spin_lock_irqsave(&tty_ldisc_lock, flags); + if (ld->refcount == 0) + printk(KERN_ERR "tty_ldisc_deref: no references.\n"); + else + ld->refcount--; + if (ld->refcount == 0) + wake_up(&tty_ldisc_wait); + spin_unlock_irqrestore(&tty_ldisc_lock, flags); +} + +EXPORT_SYMBOL_GPL(tty_ldisc_deref); + +/** + * tty_ldisc_enable - allow ldisc use + * @tty: terminal to activate ldisc on + * + * Set the TTY_LDISC flag when the line discipline can be called + * again. Do necessary wakeups for existing sleepers. + * + * Note: nobody should set this bit except via this function. Clearing + * directly is allowed. + */ + +void tty_ldisc_enable(struct tty_struct *tty) +{ + set_bit(TTY_LDISC, &tty->flags); + wake_up(&tty_ldisc_wait); +} + +/** + * tty_set_termios_ldisc - set ldisc field + * @tty: tty structure + * @num: line discipline number + * + * This is probably overkill for real world processors but + * they are not on hot paths so a little discipline won't do + * any harm. + * + * Locking: takes termios_mutex + */ + +static void tty_set_termios_ldisc(struct tty_struct *tty, int num) +{ + mutex_lock(&tty->termios_mutex); + tty->termios->c_line = num; + mutex_unlock(&tty->termios_mutex); +} + + +/** + * tty_ldisc_restore - helper for tty ldisc change + * @tty: tty to recover + * @old: previous ldisc + * + * Restore the previous line discipline or N_TTY when a line discipline + * change fails due to an open error + */ + +static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) +{ + char buf[64]; + struct tty_ldisc new_ldisc; + + /* There is an outstanding reference here so this is safe */ + tty_ldisc_get(old->ops->num, old); + tty_ldisc_assign(tty, old); + tty_set_termios_ldisc(tty, old->ops->num); + if (old->ops->open && (old->ops->open(tty) < 0)) { + tty_ldisc_put(old->ops); + /* This driver is always present */ + if (tty_ldisc_get(N_TTY, &new_ldisc) < 0) + panic("n_tty: get"); + tty_ldisc_assign(tty, &new_ldisc); + tty_set_termios_ldisc(tty, N_TTY); + if (new_ldisc.ops->open) { + int r = new_ldisc.ops->open(tty); + if (r < 0) + panic("Couldn't open N_TTY ldisc for " + "%s --- error %d.", + tty_name(tty, buf), r); + } + } +} + +/** + * tty_set_ldisc - set line discipline + * @tty: the terminal to set + * @ldisc: the line discipline + * + * Set the discipline of a tty line. Must be called from a process + * context. + * + * Locking: takes tty_ldisc_lock. + * called functions take termios_mutex + */ + +int tty_set_ldisc(struct tty_struct *tty, int ldisc) +{ + int retval; + struct tty_ldisc o_ldisc, new_ldisc; + int work; + unsigned long flags; + struct tty_struct *o_tty; + +restart: + /* This is a bit ugly for now but means we can break the 'ldisc + is part of the tty struct' assumption later */ + retval = tty_ldisc_get(ldisc, &new_ldisc); + if (retval) + return retval; + + /* + * Problem: What do we do if this blocks ? + */ + + tty_wait_until_sent(tty, 0); + + if (tty->ldisc.ops->num == ldisc) { + tty_ldisc_put(new_ldisc.ops); + return 0; + } + + /* + * No more input please, we are switching. The new ldisc + * will update this value in the ldisc open function + */ + + tty->receive_room = 0; + + o_ldisc = tty->ldisc; + o_tty = tty->link; + + /* + * Make sure we don't change while someone holds a + * reference to the line discipline. The TTY_LDISC bit + * prevents anyone taking a reference once it is clear. + * We need the lock to avoid racing reference takers. + */ + + spin_lock_irqsave(&tty_ldisc_lock, flags); + if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) { + if (tty->ldisc.refcount) { + /* Free the new ldisc we grabbed. Must drop the lock + first. */ + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + tty_ldisc_put(o_ldisc.ops); + /* + * There are several reasons we may be busy, including + * random momentary I/O traffic. We must therefore + * retry. We could distinguish between blocking ops + * and retries if we made tty_ldisc_wait() smarter. + * That is up for discussion. + */ + if (wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0) + return -ERESTARTSYS; + goto restart; + } + if (o_tty && o_tty->ldisc.refcount) { + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + tty_ldisc_put(o_tty->ldisc.ops); + if (wait_event_interruptible(tty_ldisc_wait, o_tty->ldisc.refcount == 0) < 0) + return -ERESTARTSYS; + goto restart; + } + } + /* + * If the TTY_LDISC bit is set, then we are racing against + * another ldisc change + */ + if (!test_bit(TTY_LDISC, &tty->flags)) { + struct tty_ldisc *ld; + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + tty_ldisc_put(new_ldisc.ops); + ld = tty_ldisc_ref_wait(tty); + tty_ldisc_deref(ld); + goto restart; + } + + clear_bit(TTY_LDISC, &tty->flags); + if (o_tty) + clear_bit(TTY_LDISC, &o_tty->flags); + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + + /* + * From this point on we know nobody has an ldisc + * usage reference, nor can they obtain one until + * we say so later on. + */ + + work = cancel_delayed_work(&tty->buf.work); + /* + * Wait for ->hangup_work and ->buf.work handlers to terminate + * MUST NOT hold locks here. + */ + flush_scheduled_work(); + /* Shutdown the current discipline. */ + if (o_ldisc.ops->close) + (o_ldisc.ops->close)(tty); + + /* Now set up the new line discipline. */ + tty_ldisc_assign(tty, &new_ldisc); + tty_set_termios_ldisc(tty, ldisc); + if (new_ldisc.ops->open) + retval = (new_ldisc.ops->open)(tty); + if (retval < 0) { + tty_ldisc_put(new_ldisc.ops); + tty_ldisc_restore(tty, &o_ldisc); + } + /* At this point we hold a reference to the new ldisc and a + a reference to the old ldisc. If we ended up flipping back + to the existing ldisc we have two references to it */ + + if (tty->ldisc.ops->num != o_ldisc.ops->num && tty->ops->set_ldisc) + tty->ops->set_ldisc(tty); + + tty_ldisc_put(o_ldisc.ops); + + /* + * Allow ldisc referencing to occur as soon as the driver + * ldisc callback completes. + */ + + tty_ldisc_enable(tty); + if (o_tty) + tty_ldisc_enable(o_tty); + + /* Restart it in case no characters kick it off. Safe if + already running */ + if (work) + schedule_delayed_work(&tty->buf.work, 1); + return retval; +} + + +/** + * tty_ldisc_setup - open line discipline + * @tty: tty being shut down + * @o_tty: pair tty for pty/tty pairs + * + * Called during the initial open of a tty/pty pair in order to set up the + * line discplines and bind them to the tty. + */ + +int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) +{ + struct tty_ldisc *ld = &tty->ldisc; + int retval; + + if (ld->ops->open) { + retval = (ld->ops->open)(tty); + if (retval) + return retval; + } + if (o_tty && o_tty->ldisc.ops->open) { + retval = (o_tty->ldisc.ops->open)(o_tty); + if (retval) { + if (ld->ops->close) + (ld->ops->close)(tty); + return retval; + } + tty_ldisc_enable(o_tty); + } + tty_ldisc_enable(tty); + return 0; +} + +/** + * tty_ldisc_release - release line discipline + * @tty: tty being shut down + * @o_tty: pair tty for pty/tty pairs + * + * Called during the final close of a tty/pty pair in order to shut down the + * line discpline layer. + */ + +void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) +{ + unsigned long flags; + struct tty_ldisc ld; + /* + * Prevent flush_to_ldisc() from rescheduling the work for later. Then + * kill any delayed work. As this is the final close it does not + * race with the set_ldisc code path. + */ + clear_bit(TTY_LDISC, &tty->flags); + cancel_delayed_work(&tty->buf.work); + + /* + * Wait for ->hangup_work and ->buf.work handlers to terminate + */ + + flush_scheduled_work(); + + /* + * Wait for any short term users (we know they are just driver + * side waiters as the file is closing so user count on the file + * side is zero. + */ + spin_lock_irqsave(&tty_ldisc_lock, flags); + while (tty->ldisc.refcount) { + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0); + spin_lock_irqsave(&tty_ldisc_lock, flags); + } + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + /* + * Shutdown the current line discipline, and reset it to N_TTY. + * + * FIXME: this MUST get fixed for the new reflocking + */ + if (tty->ldisc.ops->close) + (tty->ldisc.ops->close)(tty); + tty_ldisc_put(tty->ldisc.ops); + + /* + * Switch the line discipline back + */ + WARN_ON(tty_ldisc_get(N_TTY, &ld)); + tty_ldisc_assign(tty, &ld); + tty_set_termios_ldisc(tty, N_TTY); + if (o_tty) { + /* FIXME: could o_tty be in setldisc here ? */ + clear_bit(TTY_LDISC, &o_tty->flags); + if (o_tty->ldisc.ops->close) + (o_tty->ldisc.ops->close)(o_tty); + tty_ldisc_put(o_tty->ldisc.ops); + WARN_ON(tty_ldisc_get(N_TTY, &ld)); + tty_ldisc_assign(o_tty, &ld); + tty_set_termios_ldisc(o_tty, N_TTY); + } +} + +/** + * tty_ldisc_init - ldisc setup for new tty + * @tty: tty being allocated + * + * Set up the line discipline objects for a newly allocated tty. Note that + * the tty structure is not completely set up when this call is made. + */ + +void tty_ldisc_init(struct tty_struct *tty) +{ + struct tty_ldisc ld; + if (tty_ldisc_get(N_TTY, &ld) < 0) + panic("n_tty: init_tty"); + tty_ldisc_assign(tty, &ld); +} + +void tty_ldisc_begin(void) +{ + /* Setup the default TTY line discipline. */ + (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY); +} diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c index eebfad2777d2..c2ae52dd53d1 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/char/vc_screen.c @@ -481,10 +481,10 @@ static struct class *vc_class; void vcs_make_sysfs(struct tty_struct *tty) { - device_create(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 1), - "vcs%u", tty->index + 1); - device_create(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 129), - "vcsa%u", tty->index + 1); + device_create_drvdata(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 1), + NULL, "vcs%u", tty->index + 1); + device_create_drvdata(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 129), + NULL, "vcsa%u", tty->index + 1); } void vcs_remove_sysfs(struct tty_struct *tty) @@ -499,7 +499,7 @@ int __init vcs_init(void) panic("unable to get major %d for vcs device", VCS_MAJOR); vc_class = class_create(THIS_MODULE, "vc"); - device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), "vcs"); - device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), "vcsa"); + device_create_drvdata(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs"); + device_create_drvdata(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa"); return 0; } diff --git a/drivers/char/viotape.c b/drivers/char/viotape.c index e5da98d8f9cd..7a70a40ad639 100644 --- a/drivers/char/viotape.c +++ b/drivers/char/viotape.c @@ -886,10 +886,10 @@ static int viotape_probe(struct vio_dev *vdev, const struct vio_device_id *id) state[i].cur_part = 0; for (j = 0; j < MAX_PARTITIONS; ++j) state[i].part_stat_rwi[j] = VIOT_IDLE; - device_create(tape_class, NULL, MKDEV(VIOTAPE_MAJOR, i), - "iseries!vt%d", i); - device_create(tape_class, NULL, MKDEV(VIOTAPE_MAJOR, i | 0x80), - "iseries!nvt%d", i); + device_create_drvdata(tape_class, NULL, MKDEV(VIOTAPE_MAJOR, i), + NULL, "iseries!vt%d", i); + device_create_drvdata(tape_class, NULL, MKDEV(VIOTAPE_MAJOR, i | 0x80), + NULL, "iseries!nvt%d", i); printk(VIOTAPE_KERN_INFO "tape iseries/vt%d is iSeries " "resource %10.10s type %4.4s, model %3.3s\n", i, viotape_unitinfo[i].rsrcname, diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index dc17fe3a88bc..d0f4eb6fdb7f 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -46,6 +46,9 @@ static char *in, *inbuf; /* The operations for our console. */ static struct hv_ops virtio_cons; +/* The hvc device */ +static struct hvc_struct *hvc; + /*D:310 The put_chars() callback is pretty straightforward. * * We turn the characters into a scatter-gather list, add it to the output @@ -134,6 +137,27 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) return hvc_instantiate(0, 0, &virtio_cons); } +/* + * we support only one console, the hvc struct is a global var + * There is no need to do anything + */ +static int notifier_add_vio(struct hvc_struct *hp, int data) +{ + hp->irq_requested = 1; + return 0; +} + +static void notifier_del_vio(struct hvc_struct *hp, int data) +{ + hp->irq_requested = 0; +} + +static void hvc_handle_input(struct virtqueue *vq) +{ + if (hvc_poll(hvc)) + hvc_kick(); +} + /*D:370 Once we're further in boot, we get probed like any other virtio device. * At this stage we set up the output virtqueue. * @@ -144,7 +168,6 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) static int __devinit virtcons_probe(struct virtio_device *dev) { int err; - struct hvc_struct *hvc; vdev = dev; @@ -158,7 +181,7 @@ static int __devinit virtcons_probe(struct virtio_device *dev) /* Find the input queue. */ /* FIXME: This is why we want to wean off hvc: we do nothing * when input comes in. */ - in_vq = vdev->config->find_vq(vdev, 0, NULL); + in_vq = vdev->config->find_vq(vdev, 0, hvc_handle_input); if (IS_ERR(in_vq)) { err = PTR_ERR(in_vq); goto free; @@ -173,15 +196,18 @@ static int __devinit virtcons_probe(struct virtio_device *dev) /* Start using the new console output. */ virtio_cons.get_chars = get_chars; virtio_cons.put_chars = put_chars; + virtio_cons.notifier_add = notifier_add_vio; + virtio_cons.notifier_del = notifier_del_vio; /* The first argument of hvc_alloc() is the virtual console number, so - * we use zero. The second argument is the interrupt number; we - * currently leave this as zero: it would be better not to use the - * hvc mechanism and fix this (FIXME!). + * we use zero. The second argument is the parameter for the + * notification mechanism (like irq number). We currently leave this + * as zero, virtqueues have implicit notifications. * * The third argument is a "struct hv_ops" containing the put_chars() - * and get_chars() pointers. The final argument is the output buffer - * size: we can do any size, so we put PAGE_SIZE here. */ + * get_chars(), notifier_add() and notifier_del() pointers. + * The final argument is the output buffer size: we can do any size, + * so we put PAGE_SIZE here. */ hvc = hvc_alloc(0, 0, &virtio_cons, PAGE_SIZE); if (IS_ERR(hvc)) { err = PTR_ERR(hvc); diff --git a/drivers/char/vme_scc.c b/drivers/char/vme_scc.c index f17ac043b551..1718b3c481db 100644 --- a/drivers/char/vme_scc.c +++ b/drivers/char/vme_scc.c @@ -85,7 +85,7 @@ static irqreturn_t scc_rx_int(int irq, void *data); static irqreturn_t scc_stat_int(int irq, void *data); static irqreturn_t scc_spcond_int(int irq, void *data); static void scc_setsignals(struct scc_port *port, int dtr, int rts); -static void scc_break_ctl(struct tty_struct *tty, int break_state); +static int scc_break_ctl(struct tty_struct *tty, int break_state); static struct tty_driver *scc_driver; @@ -183,8 +183,8 @@ static void scc_init_portstructs(void) #ifdef NEW_WRITE_LOCKING port->gs.port_write_mutex = MUTEX; #endif - init_waitqueue_head(&port->gs.open_wait); - init_waitqueue_head(&port->gs.close_wait); + init_waitqueue_head(&port->gs.port.open_wait); + init_waitqueue_head(&port->gs.port.close_wait); } } @@ -422,7 +422,7 @@ static irqreturn_t scc_rx_int(int irq, void *data) { unsigned char ch; struct scc_port *port = data; - struct tty_struct *tty = port->gs.tty; + struct tty_struct *tty = port->gs.port.tty; SCC_ACCESS_INIT(port); ch = SCCread_NB(RX_DATA_REG); @@ -453,7 +453,7 @@ static irqreturn_t scc_rx_int(int irq, void *data) static irqreturn_t scc_spcond_int(int irq, void *data) { struct scc_port *port = data; - struct tty_struct *tty = port->gs.tty; + struct tty_struct *tty = port->gs.port.tty; unsigned char stat, ch, err; int int_pending_mask = port->channel == CHANNEL_A ? IPR_A_RX : IPR_B_RX; @@ -500,7 +500,7 @@ static irqreturn_t scc_tx_int(int irq, void *data) struct scc_port *port = data; SCC_ACCESS_INIT(port); - if (!port->gs.tty) { + if (!port->gs.port.tty) { printk(KERN_WARNING "scc_tx_int with NULL tty!\n"); SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0); SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET); @@ -512,8 +512,9 @@ static irqreturn_t scc_tx_int(int irq, void *data) SCCwrite(TX_DATA_REG, port->x_char); port->x_char = 0; } - else if ((port->gs.xmit_cnt <= 0) || port->gs.tty->stopped || - port->gs.tty->hw_stopped) + else if ((port->gs.xmit_cnt <= 0) || + port->gs.port.tty->stopped || + port->gs.port.tty->hw_stopped) break; else { SCCwrite(TX_DATA_REG, port->gs.xmit_buf[port->gs.xmit_tail++]); @@ -522,15 +523,15 @@ static irqreturn_t scc_tx_int(int irq, void *data) break; } } - if ((port->gs.xmit_cnt <= 0) || port->gs.tty->stopped || - port->gs.tty->hw_stopped) { + if ((port->gs.xmit_cnt <= 0) || port->gs.port.tty->stopped || + port->gs.port.tty->hw_stopped) { /* disable tx interrupts */ SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0); SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET); /* disable tx_int on next tx underrun? */ - port->gs.flags &= ~GS_TX_INTEN; + port->gs.port.flags &= ~GS_TX_INTEN; } - if (port->gs.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars) - tty_wakeup(port->gs.tty); + if (port->gs.port.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars) + tty_wakeup(port->gs.port.tty); SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); return IRQ_HANDLED; @@ -550,14 +551,14 @@ static irqreturn_t scc_stat_int(int irq, void *data) if (changed & SR_DCD) { port->c_dcd = !!(sr & SR_DCD); - if (!(port->gs.flags & ASYNC_CHECK_CD)) + if (!(port->gs.port.flags & ASYNC_CHECK_CD)) ; /* Don't report DCD changes */ else if (port->c_dcd) { - wake_up_interruptible(&port->gs.open_wait); + wake_up_interruptible(&port->gs.port.open_wait); } else { - if (port->gs.tty) - tty_hangup (port->gs.tty); + if (port->gs.port.tty) + tty_hangup (port->gs.port.tty); } } SCCwrite(COMMAND_REG, CR_EXTSTAT_RESET); @@ -578,7 +579,7 @@ static void scc_disable_tx_interrupts(void *ptr) local_irq_save(flags); SCCmod(INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0); - port->gs.flags &= ~GS_TX_INTEN; + port->gs.port.flags &= ~GS_TX_INTEN; local_irq_restore(flags); } @@ -636,8 +637,8 @@ static void scc_shutdown_port(void *ptr) { struct scc_port *port = ptr; - port->gs.flags &= ~ GS_ACTIVE; - if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL) { + port->gs.port.flags &= ~ GS_ACTIVE; + if (port->gs.port.tty && port->gs.port.tty->termios->c_cflag & HUPCL) { scc_setsignals (port, 0, 0); } } @@ -652,14 +653,14 @@ static int scc_set_real_termios (void *ptr) struct scc_port *port = ptr; SCC_ACCESS_INIT(port); - if (!port->gs.tty || !port->gs.tty->termios) return 0; + if (!port->gs.port.tty || !port->gs.port.tty->termios) return 0; channel = port->channel; if (channel == CHANNEL_A) return 0; /* Settings controlled by boot PROM */ - cflag = port->gs.tty->termios->c_cflag; + cflag = port->gs.port.tty->termios->c_cflag; baud = port->gs.baud; chsize = (cflag & CSIZE) >> 4; @@ -678,9 +679,9 @@ static int scc_set_real_termios (void *ptr) } if (cflag & CLOCAL) - port->gs.flags &= ~ASYNC_CHECK_CD; + port->gs.port.flags &= ~ASYNC_CHECK_CD; else - port->gs.flags |= ASYNC_CHECK_CD; + port->gs.port.flags |= ASYNC_CHECK_CD; #ifdef CONFIG_MVME147_SCC if (MACH_IS_MVME147) @@ -856,7 +857,7 @@ static int scc_open (struct tty_struct * tty, struct file * filp) { COMMAND_REG, CR_EXTSTAT_RESET }, }; #endif - if (!(port->gs.flags & ASYNC_INITIALIZED)) { + if (!(port->gs.port.flags & ASYNC_INITIALIZED)) { local_irq_save(flags); #if defined(CONFIG_MVME147_SCC) || defined(CONFIG_MVME162_SCC) if (MACH_IS_MVME147 || MACH_IS_MVME16x) { @@ -880,18 +881,18 @@ static int scc_open (struct tty_struct * tty, struct file * filp) } tty->driver_data = port; - port->gs.tty = tty; - port->gs.count++; + port->gs.port.tty = tty; + port->gs.port.count++; retval = gs_init_port(&port->gs); if (retval) { - port->gs.count--; + port->gs.port.count--; return retval; } - port->gs.flags |= GS_ACTIVE; + port->gs.port.flags |= GS_ACTIVE; retval = gs_block_til_ready(port, filp); if (retval) { - port->gs.count--; + port->gs.port.count--; return retval; } @@ -942,7 +943,7 @@ static int scc_ioctl(struct tty_struct *tty, struct file *file, } -static void scc_break_ctl(struct tty_struct *tty, int break_state) +static int scc_break_ctl(struct tty_struct *tty, int break_state) { struct scc_port *port = (struct scc_port *)tty->driver_data; unsigned long flags; @@ -952,6 +953,7 @@ static void scc_break_ctl(struct tty_struct *tty, int break_state) SCCmod(TX_CTRL_REG, ~TCR_SEND_BREAK, break_state ? TCR_SEND_BREAK : 0); local_irq_restore(flags); + return 0; } diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 935f1c207a1f..82a51f38a546 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -261,7 +261,7 @@ static void notify_update(struct vc_data *vc) #ifdef VT_BUF_VRAM_ONLY #define DO_UPDATE(vc) 0 #else -#define DO_UPDATE(vc) CON_IS_VISIBLE(vc) +#define DO_UPDATE(vc) (CON_IS_VISIBLE(vc) && !console_blanked) #endif static inline unsigned short *screenpos(struct vc_data *vc, int offset, int viewed) @@ -2211,7 +2211,7 @@ rescan_last_byte: c = 0xfffd; tc = c; } else { /* no utf or alternate charset mode */ - tc = vc->vc_translate[vc->vc_toggle_meta ? (c | 0x80) : c]; + tc = vc_translate(vc, c); } param.c = tc; @@ -2749,8 +2749,8 @@ static int con_open(struct tty_struct *tty, struct file *filp) tty->termios->c_iflag |= IUTF8; else tty->termios->c_iflag &= ~IUTF8; - release_console_sem(); vcs_make_sysfs(tty); + release_console_sem(); return ret; } } @@ -2775,8 +2775,8 @@ static void con_close(struct tty_struct *tty, struct file *filp) if (vc) vc->vc_tty = NULL; tty->driver_data = NULL; - release_console_sem(); vcs_remove_sysfs(tty); + release_console_sem(); mutex_unlock(&tty_mutex); /* * tty_mutex is released, but we still hold BKL, so there is @@ -3425,9 +3425,10 @@ int register_con_driver(const struct consw *csw, int first, int last) if (retval) goto err; - con_driver->dev = device_create(vtconsole_class, NULL, - MKDEV(0, con_driver->node), - "vtcon%i", con_driver->node); + con_driver->dev = device_create_drvdata(vtconsole_class, NULL, + MKDEV(0, con_driver->node), + NULL, "vtcon%i", + con_driver->node); if (IS_ERR(con_driver->dev)) { printk(KERN_WARNING "Unable to create device for %s; " @@ -3535,9 +3536,10 @@ static int __init vtconsole_class_init(void) struct con_driver *con = ®istered_con_driver[i]; if (con->con && !con->dev) { - con->dev = device_create(vtconsole_class, NULL, - MKDEV(0, con->node), - "vtcon%i", con->node); + con->dev = device_create_drvdata(vtconsole_class, NULL, + MKDEV(0, con->node), + NULL, "vtcon%i", + con->node); if (IS_ERR(con->dev)) { printk(KERN_WARNING "Unable to create " diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c index 1e1b81e57cdc..8bfee5fb7223 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -87,7 +87,6 @@ #include <linux/mutex.h> #include <linux/smp_lock.h> #include <linux/sysctl.h> -#include <linux/version.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/platform_device.h> @@ -658,8 +657,9 @@ static int __devinit hwicap_setup(struct device *dev, int id, dev_err(dev, "cdev_add() failed\n"); goto failed3; } - /* devfs_mk_cdev(devt, S_IFCHR|S_IRUGO|S_IWUGO, DRIVER_NAME); */ - device_create(icap_class, dev, devt, "%s%d", DRIVER_NAME, id); + + device_create_drvdata(icap_class, dev, devt, NULL, + "%s%d", DRIVER_NAME, id); return 0; /* success */ failed3: diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 1d41496ed2f8..8d6a3ff02672 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -38,10 +38,10 @@ * also protects the cpufreq_cpu_data array. */ static struct cpufreq_driver *cpufreq_driver; -static struct cpufreq_policy *cpufreq_cpu_data[NR_CPUS]; +static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data); #ifdef CONFIG_HOTPLUG_CPU /* This one keeps track of the previously set governor of a removed CPU */ -static struct cpufreq_governor *cpufreq_cpu_governor[NR_CPUS]; +static DEFINE_PER_CPU(struct cpufreq_governor *, cpufreq_cpu_governor); #endif static DEFINE_SPINLOCK(cpufreq_driver_lock); @@ -135,7 +135,7 @@ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) struct cpufreq_policy *data; unsigned long flags; - if (cpu >= NR_CPUS) + if (cpu >= nr_cpu_ids) goto err_out; /* get the cpufreq driver */ @@ -149,7 +149,7 @@ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) /* get the CPU */ - data = cpufreq_cpu_data[cpu]; + data = per_cpu(cpufreq_cpu_data, cpu); if (!data) goto err_out_put_module; @@ -327,7 +327,7 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) dprintk("notification %u of frequency transition to %u kHz\n", state, freqs->new); - policy = cpufreq_cpu_data[freqs->cpu]; + policy = per_cpu(cpufreq_cpu_data, freqs->cpu); switch (state) { case CPUFREQ_PRECHANGE: @@ -589,7 +589,7 @@ static ssize_t show_cpus(cpumask_t mask, char *buf) ssize_t i = 0; unsigned int cpu; - for_each_cpu_mask(cpu, mask) { + for_each_cpu_mask_nr(cpu, mask) { if (i) i += scnprintf(&buf[i], (PAGE_SIZE - i - 2), " "); i += scnprintf(&buf[i], (PAGE_SIZE - i - 2), "%u", cpu); @@ -828,14 +828,14 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) #ifdef CONFIG_SMP #ifdef CONFIG_HOTPLUG_CPU - if (cpufreq_cpu_governor[cpu]){ - policy->governor = cpufreq_cpu_governor[cpu]; + if (per_cpu(cpufreq_cpu_governor, cpu)) { + policy->governor = per_cpu(cpufreq_cpu_governor, cpu); dprintk("Restoring governor %s for cpu %d\n", policy->governor->name, cpu); } #endif - for_each_cpu_mask(j, policy->cpus) { + for_each_cpu_mask_nr(j, policy->cpus) { if (cpu == j) continue; @@ -854,7 +854,7 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) spin_lock_irqsave(&cpufreq_driver_lock, flags); managed_policy->cpus = policy->cpus; - cpufreq_cpu_data[cpu] = managed_policy; + per_cpu(cpufreq_cpu_data, cpu) = managed_policy; spin_unlock_irqrestore(&cpufreq_driver_lock, flags); dprintk("CPU already managed, adding link\n"); @@ -898,14 +898,14 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) } spin_lock_irqsave(&cpufreq_driver_lock, flags); - for_each_cpu_mask(j, policy->cpus) { - cpufreq_cpu_data[j] = policy; + for_each_cpu_mask_nr(j, policy->cpus) { + per_cpu(cpufreq_cpu_data, j) = policy; per_cpu(policy_cpu, j) = policy->cpu; } spin_unlock_irqrestore(&cpufreq_driver_lock, flags); /* symlink affected CPUs */ - for_each_cpu_mask(j, policy->cpus) { + for_each_cpu_mask_nr(j, policy->cpus) { if (j == cpu) continue; if (!cpu_online(j)) @@ -945,8 +945,8 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) err_out_unregister: spin_lock_irqsave(&cpufreq_driver_lock, flags); - for_each_cpu_mask(j, policy->cpus) - cpufreq_cpu_data[j] = NULL; + for_each_cpu_mask_nr(j, policy->cpus) + per_cpu(cpufreq_cpu_data, j) = NULL; spin_unlock_irqrestore(&cpufreq_driver_lock, flags); kobject_put(&policy->kobj); @@ -989,7 +989,7 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev) dprintk("unregistering CPU %u\n", cpu); spin_lock_irqsave(&cpufreq_driver_lock, flags); - data = cpufreq_cpu_data[cpu]; + data = per_cpu(cpufreq_cpu_data, cpu); if (!data) { spin_unlock_irqrestore(&cpufreq_driver_lock, flags); @@ -997,7 +997,7 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev) unlock_policy_rwsem_write(cpu); return -EINVAL; } - cpufreq_cpu_data[cpu] = NULL; + per_cpu(cpufreq_cpu_data, cpu) = NULL; #ifdef CONFIG_SMP @@ -1019,31 +1019,31 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev) #ifdef CONFIG_SMP #ifdef CONFIG_HOTPLUG_CPU - cpufreq_cpu_governor[cpu] = data->governor; + per_cpu(cpufreq_cpu_governor, cpu) = data->governor; #endif /* if we have other CPUs still registered, we need to unlink them, * or else wait_for_completion below will lock up. Clean the - * cpufreq_cpu_data[] while holding the lock, and remove the sysfs - * links afterwards. + * per_cpu(cpufreq_cpu_data) while holding the lock, and remove + * the sysfs links afterwards. */ if (unlikely(cpus_weight(data->cpus) > 1)) { - for_each_cpu_mask(j, data->cpus) { + for_each_cpu_mask_nr(j, data->cpus) { if (j == cpu) continue; - cpufreq_cpu_data[j] = NULL; + per_cpu(cpufreq_cpu_data, j) = NULL; } } spin_unlock_irqrestore(&cpufreq_driver_lock, flags); if (unlikely(cpus_weight(data->cpus) > 1)) { - for_each_cpu_mask(j, data->cpus) { + for_each_cpu_mask_nr(j, data->cpus) { if (j == cpu) continue; dprintk("removing link for cpu %u\n", j); #ifdef CONFIG_HOTPLUG_CPU - cpufreq_cpu_governor[j] = data->governor; + per_cpu(cpufreq_cpu_governor, j) = data->governor; #endif cpu_sys_dev = get_cpu_sysdev(j); sysfs_remove_link(&cpu_sys_dev->kobj, "cpufreq"); @@ -1153,7 +1153,7 @@ EXPORT_SYMBOL(cpufreq_quick_get); static unsigned int __cpufreq_get(unsigned int cpu) { - struct cpufreq_policy *policy = cpufreq_cpu_data[cpu]; + struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); unsigned int ret_freq = 0; if (!cpufreq_driver->get) @@ -1822,16 +1822,19 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) cpufreq_driver = driver_data; spin_unlock_irqrestore(&cpufreq_driver_lock, flags); - ret = sysdev_driver_register(&cpu_sysdev_class,&cpufreq_sysdev_driver); + ret = sysdev_driver_register(&cpu_sysdev_class, + &cpufreq_sysdev_driver); if ((!ret) && !(cpufreq_driver->flags & CPUFREQ_STICKY)) { int i; ret = -ENODEV; /* check for at least one working CPU */ - for (i=0; i<NR_CPUS; i++) - if (cpufreq_cpu_data[i]) + for (i = 0; i < nr_cpu_ids; i++) + if (cpu_possible(i) && per_cpu(cpufreq_cpu_data, i)) { ret = 0; + break; + } /* if all ->init() calls failed, unregister */ if (ret) { diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 5d3a04ba6ad2..fe565ee43757 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -497,7 +497,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, return rc; } - for_each_cpu_mask(j, policy->cpus) { + for_each_cpu_mask_nr(j, policy->cpus) { struct cpu_dbs_info_s *j_dbs_info; j_dbs_info = &per_cpu(cpu_dbs_info, j); j_dbs_info->cur_policy = policy; diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index d2af20dda382..33855cb3cf16 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -367,7 +367,7 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) /* Get Idle Time */ idle_ticks = UINT_MAX; - for_each_cpu_mask(j, policy->cpus) { + for_each_cpu_mask_nr(j, policy->cpus) { cputime64_t total_idle_ticks; unsigned int tmp_idle_ticks; struct cpu_dbs_info_s *j_dbs_info; @@ -521,7 +521,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, return rc; } - for_each_cpu_mask(j, policy->cpus) { + for_each_cpu_mask_nr(j, policy->cpus) { struct cpu_dbs_info_s *j_dbs_info; j_dbs_info = &per_cpu(cpu_dbs_info, j); j_dbs_info->cur_policy = policy; diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index ae70d63a8b26..c0ff97d375d7 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -43,7 +43,7 @@ struct cpufreq_stats { #endif }; -static struct cpufreq_stats *cpufreq_stats_table[NR_CPUS]; +static DEFINE_PER_CPU(struct cpufreq_stats *, cpufreq_stats_table); struct cpufreq_stats_attribute { struct attribute attr; @@ -58,7 +58,7 @@ cpufreq_stats_update (unsigned int cpu) cur_time = get_jiffies_64(); spin_lock(&cpufreq_stats_lock); - stat = cpufreq_stats_table[cpu]; + stat = per_cpu(cpufreq_stats_table, cpu); if (stat->time_in_state) stat->time_in_state[stat->last_index] = cputime64_add(stat->time_in_state[stat->last_index], @@ -71,11 +71,11 @@ cpufreq_stats_update (unsigned int cpu) static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf) { - struct cpufreq_stats *stat = cpufreq_stats_table[policy->cpu]; + struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu); if (!stat) return 0; return sprintf(buf, "%d\n", - cpufreq_stats_table[stat->cpu]->total_trans); + per_cpu(cpufreq_stats_table, stat->cpu)->total_trans); } static ssize_t @@ -83,7 +83,7 @@ show_time_in_state(struct cpufreq_policy *policy, char *buf) { ssize_t len = 0; int i; - struct cpufreq_stats *stat = cpufreq_stats_table[policy->cpu]; + struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu); if (!stat) return 0; cpufreq_stats_update(stat->cpu); @@ -101,7 +101,7 @@ show_trans_table(struct cpufreq_policy *policy, char *buf) ssize_t len = 0; int i, j; - struct cpufreq_stats *stat = cpufreq_stats_table[policy->cpu]; + struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu); if (!stat) return 0; cpufreq_stats_update(stat->cpu); @@ -170,7 +170,7 @@ freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq) static void cpufreq_stats_free_table(unsigned int cpu) { - struct cpufreq_stats *stat = cpufreq_stats_table[cpu]; + struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, cpu); struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); if (policy && policy->cpu == cpu) sysfs_remove_group(&policy->kobj, &stats_attr_group); @@ -178,7 +178,7 @@ static void cpufreq_stats_free_table(unsigned int cpu) kfree(stat->time_in_state); kfree(stat); } - cpufreq_stats_table[cpu] = NULL; + per_cpu(cpufreq_stats_table, cpu) = NULL; if (policy) cpufreq_cpu_put(policy); } @@ -192,7 +192,7 @@ cpufreq_stats_create_table (struct cpufreq_policy *policy, struct cpufreq_policy *data; unsigned int alloc_size; unsigned int cpu = policy->cpu; - if (cpufreq_stats_table[cpu]) + if (per_cpu(cpufreq_stats_table, cpu)) return -EBUSY; if ((stat = kzalloc(sizeof(struct cpufreq_stats), GFP_KERNEL)) == NULL) return -ENOMEM; @@ -207,7 +207,7 @@ cpufreq_stats_create_table (struct cpufreq_policy *policy, goto error_out; stat->cpu = cpu; - cpufreq_stats_table[cpu] = stat; + per_cpu(cpufreq_stats_table, cpu) = stat; for (i=0; table[i].frequency != CPUFREQ_TABLE_END; i++) { unsigned int freq = table[i].frequency; @@ -251,7 +251,7 @@ error_out: cpufreq_cpu_put(data); error_get_fail: kfree(stat); - cpufreq_stats_table[cpu] = NULL; + per_cpu(cpufreq_stats_table, cpu) = NULL; return ret; } @@ -284,7 +284,7 @@ cpufreq_stat_notifier_trans (struct notifier_block *nb, unsigned long val, if (val != CPUFREQ_POSTCHANGE) return 0; - stat = cpufreq_stats_table[freq->cpu]; + stat = per_cpu(cpufreq_stats_table, freq->cpu); if (!stat) return 0; diff --git a/drivers/cpufreq/cpufreq_userspace.c b/drivers/cpufreq/cpufreq_userspace.c index cb2ac01a41a1..32244aa7cc0c 100644 --- a/drivers/cpufreq/cpufreq_userspace.c +++ b/drivers/cpufreq/cpufreq_userspace.c @@ -30,16 +30,18 @@ /** * A few values needed by the userspace governor */ -static unsigned int cpu_max_freq[NR_CPUS]; -static unsigned int cpu_min_freq[NR_CPUS]; -static unsigned int cpu_cur_freq[NR_CPUS]; /* current CPU freq */ -static unsigned int cpu_set_freq[NR_CPUS]; /* CPU freq desired by userspace */ -static unsigned int cpu_is_managed[NR_CPUS]; +static DEFINE_PER_CPU(unsigned int, cpu_max_freq); +static DEFINE_PER_CPU(unsigned int, cpu_min_freq); +static DEFINE_PER_CPU(unsigned int, cpu_cur_freq); /* current CPU freq */ +static DEFINE_PER_CPU(unsigned int, cpu_set_freq); /* CPU freq desired by + userspace */ +static DEFINE_PER_CPU(unsigned int, cpu_is_managed); static DEFINE_MUTEX (userspace_mutex); static int cpus_using_userspace_governor; -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "userspace", msg) +#define dprintk(msg...) \ + cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "userspace", msg) /* keep track of frequency transitions */ static int @@ -48,12 +50,12 @@ userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val, { struct cpufreq_freqs *freq = data; - if (!cpu_is_managed[freq->cpu]) + if (!per_cpu(cpu_is_managed, freq->cpu)) return 0; dprintk("saving cpu_cur_freq of cpu %u to be %u kHz\n", freq->cpu, freq->new); - cpu_cur_freq[freq->cpu] = freq->new; + per_cpu(cpu_cur_freq, freq->cpu) = freq->new; return 0; } @@ -77,15 +79,15 @@ static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq) dprintk("cpufreq_set for cpu %u, freq %u kHz\n", policy->cpu, freq); mutex_lock(&userspace_mutex); - if (!cpu_is_managed[policy->cpu]) + if (!per_cpu(cpu_is_managed, policy->cpu)) goto err; - cpu_set_freq[policy->cpu] = freq; + per_cpu(cpu_set_freq, policy->cpu) = freq; - if (freq < cpu_min_freq[policy->cpu]) - freq = cpu_min_freq[policy->cpu]; - if (freq > cpu_max_freq[policy->cpu]) - freq = cpu_max_freq[policy->cpu]; + if (freq < per_cpu(cpu_min_freq, policy->cpu)) + freq = per_cpu(cpu_min_freq, policy->cpu); + if (freq > per_cpu(cpu_max_freq, policy->cpu)) + freq = per_cpu(cpu_max_freq, policy->cpu); /* * We're safe from concurrent calls to ->target() here @@ -104,7 +106,7 @@ static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq) static ssize_t show_speed(struct cpufreq_policy *policy, char *buf) { - return sprintf(buf, "%u\n", cpu_cur_freq[policy->cpu]); + return sprintf(buf, "%u\n", per_cpu(cpu_cur_freq, policy->cpu)); } static int cpufreq_governor_userspace(struct cpufreq_policy *policy, @@ -127,12 +129,17 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy, } cpus_using_userspace_governor++; - cpu_is_managed[cpu] = 1; - cpu_min_freq[cpu] = policy->min; - cpu_max_freq[cpu] = policy->max; - cpu_cur_freq[cpu] = policy->cur; - cpu_set_freq[cpu] = policy->cur; - dprintk("managing cpu %u started (%u - %u kHz, currently %u kHz)\n", cpu, cpu_min_freq[cpu], cpu_max_freq[cpu], cpu_cur_freq[cpu]); + per_cpu(cpu_is_managed, cpu) = 1; + per_cpu(cpu_min_freq, cpu) = policy->min; + per_cpu(cpu_max_freq, cpu) = policy->max; + per_cpu(cpu_cur_freq, cpu) = policy->cur; + per_cpu(cpu_set_freq, cpu) = policy->cur; + dprintk("managing cpu %u started " + "(%u - %u kHz, currently %u kHz)\n", + cpu, + per_cpu(cpu_min_freq, cpu), + per_cpu(cpu_max_freq, cpu), + per_cpu(cpu_cur_freq, cpu)); mutex_unlock(&userspace_mutex); break; @@ -145,34 +152,34 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy, CPUFREQ_TRANSITION_NOTIFIER); } - cpu_is_managed[cpu] = 0; - cpu_min_freq[cpu] = 0; - cpu_max_freq[cpu] = 0; - cpu_set_freq[cpu] = 0; + per_cpu(cpu_is_managed, cpu) = 0; + per_cpu(cpu_min_freq, cpu) = 0; + per_cpu(cpu_max_freq, cpu) = 0; + per_cpu(cpu_set_freq, cpu) = 0; dprintk("managing cpu %u stopped\n", cpu); mutex_unlock(&userspace_mutex); break; case CPUFREQ_GOV_LIMITS: mutex_lock(&userspace_mutex); - dprintk("limit event for cpu %u: %u - %u kHz," + dprintk("limit event for cpu %u: %u - %u kHz, " "currently %u kHz, last set to %u kHz\n", cpu, policy->min, policy->max, - cpu_cur_freq[cpu], cpu_set_freq[cpu]); - if (policy->max < cpu_set_freq[cpu]) { + per_cpu(cpu_cur_freq, cpu), + per_cpu(cpu_set_freq, cpu)); + if (policy->max < per_cpu(cpu_set_freq, cpu)) { __cpufreq_driver_target(policy, policy->max, CPUFREQ_RELATION_H); - } - else if (policy->min > cpu_set_freq[cpu]) { + } else if (policy->min > per_cpu(cpu_set_freq, cpu)) { __cpufreq_driver_target(policy, policy->min, CPUFREQ_RELATION_L); - } - else { - __cpufreq_driver_target(policy, cpu_set_freq[cpu], + } else { + __cpufreq_driver_target(policy, + per_cpu(cpu_set_freq, cpu), CPUFREQ_RELATION_L); } - cpu_min_freq[cpu] = policy->min; - cpu_max_freq[cpu] = policy->max; - cpu_cur_freq[cpu] = policy->cur; + per_cpu(cpu_min_freq, cpu) = policy->min; + per_cpu(cpu_max_freq, cpu) = policy->max; + per_cpu(cpu_cur_freq, cpu) = policy->cur; mutex_unlock(&userspace_mutex); break; } diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index b64c6bc445e3..9071d80fbba2 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c @@ -174,7 +174,7 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, } EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target); -static struct cpufreq_frequency_table *show_table[NR_CPUS]; +static DEFINE_PER_CPU(struct cpufreq_frequency_table *, show_table); /** * show_available_freqs - show available frequencies for the specified CPU */ @@ -185,10 +185,10 @@ static ssize_t show_available_freqs (struct cpufreq_policy *policy, char *buf) ssize_t count = 0; struct cpufreq_frequency_table *table; - if (!show_table[cpu]) + if (!per_cpu(show_table, cpu)) return -ENODEV; - table = show_table[cpu]; + table = per_cpu(show_table, cpu); for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) { if (table[i].frequency == CPUFREQ_ENTRY_INVALID) @@ -217,20 +217,20 @@ void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table, unsigned int cpu) { dprintk("setting show_table for cpu %u to %p\n", cpu, table); - show_table[cpu] = table; + per_cpu(show_table, cpu) = table; } EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_attr); void cpufreq_frequency_table_put_attr(unsigned int cpu) { dprintk("clearing show_table for cpu %u\n", cpu); - show_table[cpu] = NULL; + per_cpu(show_table, cpu) = NULL; } EXPORT_SYMBOL_GPL(cpufreq_frequency_table_put_attr); struct cpufreq_frequency_table *cpufreq_frequency_get_table(unsigned int cpu) { - return show_table[cpu]; + return per_cpu(show_table, cpu); } EXPORT_SYMBOL_GPL(cpufreq_frequency_get_table); diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 5405769020a1..5ce07b517c58 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -94,7 +94,7 @@ void cpuidle_install_idle_handler(void) */ void cpuidle_uninstall_idle_handler(void) { - if (enabled_devices && (pm_idle != pm_idle_old)) { + if (enabled_devices && pm_idle_old && (pm_idle != pm_idle_old)) { pm_idle = pm_idle_old; cpuidle_kick_cpus(); } diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index e949618b9be0..31a0e0b455b6 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c @@ -21,7 +21,8 @@ static int __init cpuidle_sysfs_setup(char *unused) } __setup("cpuidle_sysfs_switch", cpuidle_sysfs_setup); -static ssize_t show_available_governors(struct sys_device *dev, char *buf) +static ssize_t show_available_governors(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { ssize_t i = 0; struct cpuidle_governor *tmp; @@ -39,7 +40,8 @@ out: return i; } -static ssize_t show_current_driver(struct sys_device *dev, char *buf) +static ssize_t show_current_driver(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { ssize_t ret; @@ -53,7 +55,8 @@ static ssize_t show_current_driver(struct sys_device *dev, char *buf) return ret; } -static ssize_t show_current_governor(struct sys_device *dev, char *buf) +static ssize_t show_current_governor(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { ssize_t ret; @@ -68,6 +71,7 @@ static ssize_t show_current_governor(struct sys_device *dev, char *buf) } static ssize_t store_current_governor(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { char gov_name[CPUIDLE_NAME_LEN]; diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index b11943dadefd..681c15f42083 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -99,6 +99,9 @@ struct talitos_private { /* next channel to be assigned next incoming descriptor */ atomic_t last_chan; + /* per-channel number of requests pending in channel h/w fifo */ + atomic_t *submit_count; + /* per-channel request fifo */ struct talitos_request **fifo; @@ -263,15 +266,15 @@ static int talitos_submit(struct device *dev, struct talitos_desc *desc, spin_lock_irqsave(&priv->head_lock[ch], flags); - head = priv->head[ch]; - request = &priv->fifo[ch][head]; - - if (request->desc) { - /* request queue is full */ + if (!atomic_inc_not_zero(&priv->submit_count[ch])) { + /* h/w fifo is full */ spin_unlock_irqrestore(&priv->head_lock[ch], flags); return -EAGAIN; } + head = priv->head[ch]; + request = &priv->fifo[ch][head]; + /* map descriptor and save caller data */ request->dma_desc = dma_map_single(dev, desc, sizeof(*desc), DMA_BIDIRECTIONAL); @@ -335,6 +338,9 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch) priv->tail[ch] = (tail + 1) & (priv->fifo_len - 1); spin_unlock_irqrestore(&priv->tail_lock[ch], flags); + + atomic_dec(&priv->submit_count[ch]); + saved_req.callback(dev, saved_req.desc, saved_req.context, status); /* channel may resume processing in single desc error case */ @@ -842,7 +848,7 @@ static int sg_to_link_tbl(struct scatterlist *sg, int sg_count, /* adjust (decrease) last one (or two) entry's len to cryptlen */ link_tbl_ptr--; - while (link_tbl_ptr->len <= (-cryptlen)) { + while (be16_to_cpu(link_tbl_ptr->len) <= (-cryptlen)) { /* Empty this entry, and move to previous one */ cryptlen += be16_to_cpu(link_tbl_ptr->len); link_tbl_ptr->len = 0; @@ -874,7 +880,7 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq, unsigned int cryptlen = areq->cryptlen; unsigned int authsize = ctx->authsize; unsigned int ivsize; - int sg_count; + int sg_count, ret; /* hmac key */ map_single_talitos_ptr(dev, &desc->ptr[0], ctx->authkeylen, &ctx->key, @@ -978,7 +984,12 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq, map_single_talitos_ptr(dev, &desc->ptr[6], ivsize, ctx->iv, 0, DMA_FROM_DEVICE); - return talitos_submit(dev, desc, callback, areq); + ret = talitos_submit(dev, desc, callback, areq); + if (ret != -EINPROGRESS) { + ipsec_esp_unmap(dev, edesc, areq); + kfree(edesc); + } + return ret; } @@ -1009,6 +1020,8 @@ static struct ipsec_esp_edesc *ipsec_esp_edesc_alloc(struct aead_request *areq, struct talitos_ctx *ctx = crypto_aead_ctx(authenc); struct ipsec_esp_edesc *edesc; int src_nents, dst_nents, alloc_len, dma_len; + gfp_t flags = areq->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL : + GFP_ATOMIC; if (areq->cryptlen + ctx->authsize > TALITOS_MAX_DATA_LEN) { dev_err(ctx->dev, "cryptlen exceeds h/w max limit\n"); @@ -1022,7 +1035,7 @@ static struct ipsec_esp_edesc *ipsec_esp_edesc_alloc(struct aead_request *areq, dst_nents = src_nents; } else { dst_nents = sg_count(areq->dst, areq->cryptlen + ctx->authsize); - dst_nents = (dst_nents == 1) ? 0 : src_nents; + dst_nents = (dst_nents == 1) ? 0 : dst_nents; } /* @@ -1040,7 +1053,7 @@ static struct ipsec_esp_edesc *ipsec_esp_edesc_alloc(struct aead_request *areq, alloc_len += icv_stashing ? ctx->authsize : 0; } - edesc = kmalloc(alloc_len, GFP_DMA); + edesc = kmalloc(alloc_len, GFP_DMA | flags); if (!edesc) { dev_err(ctx->dev, "could not allocate edescriptor\n"); return ERR_PTR(-ENOMEM); @@ -1337,6 +1350,7 @@ static int __devexit talitos_remove(struct of_device *ofdev) if (hw_supports(dev, DESC_HDR_SEL0_RNG)) talitos_unregister_rng(dev); + kfree(priv->submit_count); kfree(priv->tail); kfree(priv->head); @@ -1466,9 +1480,6 @@ static int talitos_probe(struct of_device *ofdev, goto err_out; } - of_node_put(np); - np = NULL; - priv->head_lock = kmalloc(sizeof(spinlock_t) * priv->num_channels, GFP_KERNEL); priv->tail_lock = kmalloc(sizeof(spinlock_t) * priv->num_channels, @@ -1504,6 +1515,16 @@ static int talitos_probe(struct of_device *ofdev, } } + priv->submit_count = kmalloc(sizeof(atomic_t) * priv->num_channels, + GFP_KERNEL); + if (!priv->submit_count) { + dev_err(dev, "failed to allocate fifo submit count space\n"); + err = -ENOMEM; + goto err_out; + } + for (i = 0; i < priv->num_channels; i++) + atomic_set(&priv->submit_count[i], -priv->chfifo_len); + priv->head = kzalloc(sizeof(int) * priv->num_channels, GFP_KERNEL); priv->tail = kzalloc(sizeof(int) * priv->num_channels, GFP_KERNEL); if (!priv->head || !priv->tail) { @@ -1559,8 +1580,6 @@ static int talitos_probe(struct of_device *ofdev, err_out: talitos_remove(ofdev); - if (np) - of_node_put(np); return err; } diff --git a/drivers/dca/dca-core.c b/drivers/dca/dca-core.c index bf5b92f86df7..ec249d2db633 100644 --- a/drivers/dca/dca-core.c +++ b/drivers/dca/dca-core.c @@ -28,13 +28,29 @@ #include <linux/device.h> #include <linux/dca.h> -MODULE_LICENSE("GPL"); +#define DCA_VERSION "1.4" -/* For now we're assuming a single, global, DCA provider for the system. */ +MODULE_VERSION(DCA_VERSION); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Intel Corporation"); static DEFINE_SPINLOCK(dca_lock); -static struct dca_provider *global_dca = NULL; +static LIST_HEAD(dca_providers); + +static struct dca_provider *dca_find_provider_by_dev(struct device *dev) +{ + struct dca_provider *dca, *ret = NULL; + + list_for_each_entry(dca, &dca_providers, node) { + if ((!dev) || (dca->ops->dev_managed(dca, dev))) { + ret = dca; + break; + } + } + + return ret; +} /** * dca_add_requester - add a dca client to the list @@ -42,25 +58,39 @@ static struct dca_provider *global_dca = NULL; */ int dca_add_requester(struct device *dev) { - int err, slot; + struct dca_provider *dca; + int err, slot = -ENODEV; - if (!global_dca) - return -ENODEV; + if (!dev) + return -EFAULT; spin_lock(&dca_lock); - slot = global_dca->ops->add_requester(global_dca, dev); - spin_unlock(&dca_lock); - if (slot < 0) + + /* check if the requester has not been added already */ + dca = dca_find_provider_by_dev(dev); + if (dca) { + spin_unlock(&dca_lock); + return -EEXIST; + } + + list_for_each_entry(dca, &dca_providers, node) { + slot = dca->ops->add_requester(dca, dev); + if (slot >= 0) + break; + } + if (slot < 0) { + spin_unlock(&dca_lock); return slot; + } - err = dca_sysfs_add_req(global_dca, dev, slot); + err = dca_sysfs_add_req(dca, dev, slot); if (err) { - spin_lock(&dca_lock); - global_dca->ops->remove_requester(global_dca, dev); + dca->ops->remove_requester(dca, dev); spin_unlock(&dca_lock); return err; } + spin_unlock(&dca_lock); return 0; } EXPORT_SYMBOL_GPL(dca_add_requester); @@ -71,30 +101,78 @@ EXPORT_SYMBOL_GPL(dca_add_requester); */ int dca_remove_requester(struct device *dev) { + struct dca_provider *dca; int slot; - if (!global_dca) - return -ENODEV; + + if (!dev) + return -EFAULT; spin_lock(&dca_lock); - slot = global_dca->ops->remove_requester(global_dca, dev); - spin_unlock(&dca_lock); - if (slot < 0) + dca = dca_find_provider_by_dev(dev); + if (!dca) { + spin_unlock(&dca_lock); + return -ENODEV; + } + slot = dca->ops->remove_requester(dca, dev); + if (slot < 0) { + spin_unlock(&dca_lock); return slot; + } - dca_sysfs_remove_req(global_dca, slot); + dca_sysfs_remove_req(dca, slot); + + spin_unlock(&dca_lock); return 0; } EXPORT_SYMBOL_GPL(dca_remove_requester); /** - * dca_get_tag - return the dca tag for the given cpu + * dca_common_get_tag - return the dca tag (serves both new and old api) + * @dev - the device that wants dca service * @cpu - the cpuid as returned by get_cpu() */ -u8 dca_get_tag(int cpu) +u8 dca_common_get_tag(struct device *dev, int cpu) { - if (!global_dca) + struct dca_provider *dca; + u8 tag; + + spin_lock(&dca_lock); + + dca = dca_find_provider_by_dev(dev); + if (!dca) { + spin_unlock(&dca_lock); return -ENODEV; - return global_dca->ops->get_tag(global_dca, cpu); + } + tag = dca->ops->get_tag(dca, dev, cpu); + + spin_unlock(&dca_lock); + return tag; +} + +/** + * dca3_get_tag - return the dca tag to the requester device + * for the given cpu (new api) + * @dev - the device that wants dca service + * @cpu - the cpuid as returned by get_cpu() + */ +u8 dca3_get_tag(struct device *dev, int cpu) +{ + if (!dev) + return -EFAULT; + + return dca_common_get_tag(dev, cpu); +} +EXPORT_SYMBOL_GPL(dca3_get_tag); + +/** + * dca_get_tag - return the dca tag for the given cpu (old api) + * @cpu - the cpuid as returned by get_cpu() + */ +u8 dca_get_tag(int cpu) +{ + struct device *dev = NULL; + + return dca_common_get_tag(dev, cpu); } EXPORT_SYMBOL_GPL(dca_get_tag); @@ -140,12 +218,10 @@ int register_dca_provider(struct dca_provider *dca, struct device *dev) { int err; - if (global_dca) - return -EEXIST; err = dca_sysfs_add_provider(dca, dev); if (err) return err; - global_dca = dca; + list_add(&dca->node, &dca_providers); blocking_notifier_call_chain(&dca_provider_chain, DCA_PROVIDER_ADD, NULL); return 0; @@ -158,11 +234,9 @@ EXPORT_SYMBOL_GPL(register_dca_provider); */ void unregister_dca_provider(struct dca_provider *dca) { - if (!global_dca) - return; blocking_notifier_call_chain(&dca_provider_chain, DCA_PROVIDER_REMOVE, NULL); - global_dca = NULL; + list_del(&dca->node); dca_sysfs_remove_provider(dca); } EXPORT_SYMBOL_GPL(unregister_dca_provider); @@ -187,6 +261,7 @@ EXPORT_SYMBOL_GPL(dca_unregister_notify); static int __init dca_init(void) { + printk(KERN_ERR "dca service started, version %s\n", DCA_VERSION); return dca_sysfs_init(); } diff --git a/drivers/dca/dca-sysfs.c b/drivers/dca/dca-sysfs.c index 011328faa5f2..7af4b403bd2d 100644 --- a/drivers/dca/dca-sysfs.c +++ b/drivers/dca/dca-sysfs.c @@ -13,9 +13,11 @@ static spinlock_t dca_idr_lock; int dca_sysfs_add_req(struct dca_provider *dca, struct device *dev, int slot) { struct device *cd; + static int req_count; - cd = device_create(dca_class, dca->cd, MKDEV(0, slot + 1), - "requester%d", slot); + cd = device_create_drvdata(dca_class, dca->cd, + MKDEV(0, slot + 1), NULL, + "requester%d", req_count++); if (IS_ERR(cd)) return PTR_ERR(cd); return 0; @@ -46,7 +48,8 @@ idr_try_again: return err; } - cd = device_create(dca_class, dev, MKDEV(0, 0), "dca%d", dca->id); + cd = device_create_drvdata(dca_class, dev, MKDEV(0, 0), NULL, + "dca%d", dca->id); if (IS_ERR(cd)) { spin_lock(&dca_idr_lock); idr_remove(&dca_idr, dca->id); diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 6239c3df30ac..cd303901eb5b 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -4,13 +4,14 @@ menuconfig DMADEVICES bool "DMA Engine support" - depends on (PCI && X86) || ARCH_IOP32X || ARCH_IOP33X || ARCH_IOP13XX || PPC - depends on !HIGHMEM64G + depends on !HIGHMEM64G && HAS_DMA help DMA engines can do asynchronous data transfers without involving the host CPU. Currently, this framework can be used to offload memory copies in the network stack and - RAID operations in the MD driver. + RAID operations in the MD driver. This menu only presents + DMA Device drivers supported by the configured arch, it may + be empty in some cases. if DMADEVICES @@ -37,6 +38,15 @@ config INTEL_IOP_ADMA help Enable support for the Intel(R) IOP Series RAID engines. +config DW_DMAC + tristate "Synopsys DesignWare AHB DMA support" + depends on AVR32 + select DMA_ENGINE + default y if CPU_AT32AP7000 + help + Support the Synopsys DesignWare AHB DMA controller. This + can be integrated in chips such as the Atmel AT32ap7000. + config FSL_DMA bool "Freescale MPC85xx/MPC83xx DMA support" depends on PPC @@ -46,6 +56,14 @@ config FSL_DMA MPC8560/40, MPC8555, MPC8548 and MPC8641 processors. The MPC8349, MPC8360 is also supported. +config MV_XOR + bool "Marvell XOR engine support" + depends on PLAT_ORION + select ASYNC_CORE + select DMA_ENGINE + ---help--- + Enable support for the Marvell XOR engine. + config DMA_ENGINE bool @@ -55,10 +73,19 @@ comment "DMA Clients" config NET_DMA bool "Network: TCP receive copy offload" depends on DMA_ENGINE && NET + default (INTEL_IOATDMA || FSL_DMA) help This enables the use of DMA engines in the network stack to offload receive copy-to-user operations, freeing CPU cycles. - Since this is the main user of the DMA engine, it should be enabled; - say Y here. + + Say Y here if you enabled INTEL_IOATDMA or FSL_DMA, otherwise + say N. + +config DMATEST + tristate "DMA Test client" + depends on DMA_ENGINE + help + Simple DMA test client. Say N unless you're debugging a + DMA Device driver. endif diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index c8036d945902..14f59527d4f6 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -1,6 +1,9 @@ obj-$(CONFIG_DMA_ENGINE) += dmaengine.o obj-$(CONFIG_NET_DMA) += iovlock.o +obj-$(CONFIG_DMATEST) += dmatest.o obj-$(CONFIG_INTEL_IOATDMA) += ioatdma.o ioatdma-objs := ioat.o ioat_dma.o ioat_dca.o obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o obj-$(CONFIG_FSL_DMA) += fsldma.o +obj-$(CONFIG_MV_XOR) += mv_xor.o +obj-$(CONFIG_DW_DMAC) += dw_dmac.o diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 97b329e76798..dc003a3a787d 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -169,12 +169,18 @@ static void dma_client_chan_alloc(struct dma_client *client) enum dma_state_client ack; /* Find a channel */ - list_for_each_entry(device, &dma_device_list, global_node) + list_for_each_entry(device, &dma_device_list, global_node) { + /* Does the client require a specific DMA controller? */ + if (client->slave && client->slave->dma_dev + && client->slave->dma_dev != device->dev) + continue; + list_for_each_entry(chan, &device->channels, device_node) { if (!dma_chan_satisfies_mask(chan, client->cap_mask)) continue; - desc = chan->device->device_alloc_chan_resources(chan); + desc = chan->device->device_alloc_chan_resources( + chan, client); if (desc >= 0) { ack = client->event_callback(client, chan, @@ -183,12 +189,14 @@ static void dma_client_chan_alloc(struct dma_client *client) /* we are done once this client rejects * an available resource */ - if (ack == DMA_ACK) + if (ack == DMA_ACK) { dma_chan_get(chan); - else if (ack == DMA_NAK) + chan->client_count++; + } else if (ack == DMA_NAK) return; } } + } } enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie) @@ -272,8 +280,10 @@ static void dma_clients_notify_removed(struct dma_chan *chan) /* client was holding resources for this channel so * free it */ - if (ack == DMA_ACK) + if (ack == DMA_ACK) { dma_chan_put(chan); + chan->client_count--; + } } mutex_unlock(&dma_list_mutex); @@ -285,6 +295,10 @@ static void dma_clients_notify_removed(struct dma_chan *chan) */ void dma_async_client_register(struct dma_client *client) { + /* validate client data */ + BUG_ON(dma_has_cap(DMA_SLAVE, client->cap_mask) && + !client->slave); + mutex_lock(&dma_list_mutex); list_add_tail(&client->global_node, &dma_client_list); mutex_unlock(&dma_list_mutex); @@ -313,8 +327,10 @@ void dma_async_client_unregister(struct dma_client *client) ack = client->event_callback(client, chan, DMA_RESOURCE_REMOVED); - if (ack == DMA_ACK) + if (ack == DMA_ACK) { dma_chan_put(chan); + chan->client_count--; + } } list_del(&client->global_node); @@ -359,6 +375,10 @@ int dma_async_device_register(struct dma_device *device) !device->device_prep_dma_memset); BUG_ON(dma_has_cap(DMA_INTERRUPT, device->cap_mask) && !device->device_prep_dma_interrupt); + BUG_ON(dma_has_cap(DMA_SLAVE, device->cap_mask) && + !device->device_prep_slave_sg); + BUG_ON(dma_has_cap(DMA_SLAVE, device->cap_mask) && + !device->device_terminate_all); BUG_ON(!device->device_alloc_chan_resources); BUG_ON(!device->device_free_chan_resources); @@ -378,7 +398,7 @@ int dma_async_device_register(struct dma_device *device) chan->chan_id = chancnt++; chan->dev.class = &dma_devclass; - chan->dev.parent = NULL; + chan->dev.parent = device->dev; snprintf(chan->dev.bus_id, BUS_ID_SIZE, "dma%dchan%d", device->dev_id, chan->chan_id); @@ -394,6 +414,7 @@ int dma_async_device_register(struct dma_device *device) kref_get(&device->refcount); kref_get(&device->refcount); kref_init(&chan->refcount); + chan->client_count = 0; chan->slow_ref = 0; INIT_RCU_HEAD(&chan->rcu); } diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c new file mode 100644 index 000000000000..a08d19704743 --- /dev/null +++ b/drivers/dma/dmatest.c @@ -0,0 +1,444 @@ +/* + * DMA Engine test module + * + * Copyright (C) 2007 Atmel Corporation + * + * 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/delay.h> +#include <linux/dmaengine.h> +#include <linux/init.h> +#include <linux/kthread.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/random.h> +#include <linux/wait.h> + +static unsigned int test_buf_size = 16384; +module_param(test_buf_size, uint, S_IRUGO); +MODULE_PARM_DESC(test_buf_size, "Size of the memcpy test buffer"); + +static char test_channel[BUS_ID_SIZE]; +module_param_string(channel, test_channel, sizeof(test_channel), S_IRUGO); +MODULE_PARM_DESC(channel, "Bus ID of the channel to test (default: any)"); + +static char test_device[BUS_ID_SIZE]; +module_param_string(device, test_device, sizeof(test_device), S_IRUGO); +MODULE_PARM_DESC(device, "Bus ID of the DMA Engine to test (default: any)"); + +static unsigned int threads_per_chan = 1; +module_param(threads_per_chan, uint, S_IRUGO); +MODULE_PARM_DESC(threads_per_chan, + "Number of threads to start per channel (default: 1)"); + +static unsigned int max_channels; +module_param(max_channels, uint, S_IRUGO); +MODULE_PARM_DESC(nr_channels, + "Maximum number of channels to use (default: all)"); + +/* + * Initialization patterns. All bytes in the source buffer has bit 7 + * set, all bytes in the destination buffer has bit 7 cleared. + * + * Bit 6 is set for all bytes which are to be copied by the DMA + * engine. Bit 5 is set for all bytes which are to be overwritten by + * the DMA engine. + * + * The remaining bits are the inverse of a counter which increments by + * one for each byte address. + */ +#define PATTERN_SRC 0x80 +#define PATTERN_DST 0x00 +#define PATTERN_COPY 0x40 +#define PATTERN_OVERWRITE 0x20 +#define PATTERN_COUNT_MASK 0x1f + +struct dmatest_thread { + struct list_head node; + struct task_struct *task; + struct dma_chan *chan; + u8 *srcbuf; + u8 *dstbuf; +}; + +struct dmatest_chan { + struct list_head node; + struct dma_chan *chan; + struct list_head threads; +}; + +/* + * These are protected by dma_list_mutex since they're only used by + * the DMA client event callback + */ +static LIST_HEAD(dmatest_channels); +static unsigned int nr_channels; + +static bool dmatest_match_channel(struct dma_chan *chan) +{ + if (test_channel[0] == '\0') + return true; + return strcmp(chan->dev.bus_id, test_channel) == 0; +} + +static bool dmatest_match_device(struct dma_device *device) +{ + if (test_device[0] == '\0') + return true; + return strcmp(device->dev->bus_id, test_device) == 0; +} + +static unsigned long dmatest_random(void) +{ + unsigned long buf; + + get_random_bytes(&buf, sizeof(buf)); + return buf; +} + +static void dmatest_init_srcbuf(u8 *buf, unsigned int start, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < start; i++) + buf[i] = PATTERN_SRC | (~i & PATTERN_COUNT_MASK); + for ( ; i < start + len; i++) + buf[i] = PATTERN_SRC | PATTERN_COPY + | (~i & PATTERN_COUNT_MASK);; + for ( ; i < test_buf_size; i++) + buf[i] = PATTERN_SRC | (~i & PATTERN_COUNT_MASK); +} + +static void dmatest_init_dstbuf(u8 *buf, unsigned int start, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < start; i++) + buf[i] = PATTERN_DST | (~i & PATTERN_COUNT_MASK); + for ( ; i < start + len; i++) + buf[i] = PATTERN_DST | PATTERN_OVERWRITE + | (~i & PATTERN_COUNT_MASK); + for ( ; i < test_buf_size; i++) + buf[i] = PATTERN_DST | (~i & PATTERN_COUNT_MASK); +} + +static void dmatest_mismatch(u8 actual, u8 pattern, unsigned int index, + unsigned int counter, bool is_srcbuf) +{ + u8 diff = actual ^ pattern; + u8 expected = pattern | (~counter & PATTERN_COUNT_MASK); + const char *thread_name = current->comm; + + if (is_srcbuf) + pr_warning("%s: srcbuf[0x%x] overwritten!" + " Expected %02x, got %02x\n", + thread_name, index, expected, actual); + else if ((pattern & PATTERN_COPY) + && (diff & (PATTERN_COPY | PATTERN_OVERWRITE))) + pr_warning("%s: dstbuf[0x%x] not copied!" + " Expected %02x, got %02x\n", + thread_name, index, expected, actual); + else if (diff & PATTERN_SRC) + pr_warning("%s: dstbuf[0x%x] was copied!" + " Expected %02x, got %02x\n", + thread_name, index, expected, actual); + else + pr_warning("%s: dstbuf[0x%x] mismatch!" + " Expected %02x, got %02x\n", + thread_name, index, expected, actual); +} + +static unsigned int dmatest_verify(u8 *buf, unsigned int start, + unsigned int end, unsigned int counter, u8 pattern, + bool is_srcbuf) +{ + unsigned int i; + unsigned int error_count = 0; + u8 actual; + + for (i = start; i < end; i++) { + actual = buf[i]; + if (actual != (pattern | (~counter & PATTERN_COUNT_MASK))) { + if (error_count < 32) + dmatest_mismatch(actual, pattern, i, counter, + is_srcbuf); + error_count++; + } + counter++; + } + + if (error_count > 32) + pr_warning("%s: %u errors suppressed\n", + current->comm, error_count - 32); + + return error_count; +} + +/* + * This function repeatedly tests DMA transfers of various lengths and + * offsets until it is told to exit by kthread_stop(). There may be + * multiple threads running this function in parallel for a single + * channel, and there may be multiple channels being tested in + * parallel. + * + * Before each test, the source and destination buffer is initialized + * with a known pattern. This pattern is different depending on + * whether it's in an area which is supposed to be copied or + * overwritten, and different in the source and destination buffers. + * So if the DMA engine doesn't copy exactly what we tell it to copy, + * we'll notice. + */ +static int dmatest_func(void *data) +{ + struct dmatest_thread *thread = data; + struct dma_chan *chan; + const char *thread_name; + unsigned int src_off, dst_off, len; + unsigned int error_count; + unsigned int failed_tests = 0; + unsigned int total_tests = 0; + dma_cookie_t cookie; + enum dma_status status; + int ret; + + thread_name = current->comm; + + ret = -ENOMEM; + thread->srcbuf = kmalloc(test_buf_size, GFP_KERNEL); + if (!thread->srcbuf) + goto err_srcbuf; + thread->dstbuf = kmalloc(test_buf_size, GFP_KERNEL); + if (!thread->dstbuf) + goto err_dstbuf; + + smp_rmb(); + chan = thread->chan; + dma_chan_get(chan); + + while (!kthread_should_stop()) { + total_tests++; + + len = dmatest_random() % test_buf_size + 1; + src_off = dmatest_random() % (test_buf_size - len + 1); + dst_off = dmatest_random() % (test_buf_size - len + 1); + + dmatest_init_srcbuf(thread->srcbuf, src_off, len); + dmatest_init_dstbuf(thread->dstbuf, dst_off, len); + + cookie = dma_async_memcpy_buf_to_buf(chan, + thread->dstbuf + dst_off, + thread->srcbuf + src_off, + len); + if (dma_submit_error(cookie)) { + pr_warning("%s: #%u: submit error %d with src_off=0x%x " + "dst_off=0x%x len=0x%x\n", + thread_name, total_tests - 1, cookie, + src_off, dst_off, len); + msleep(100); + failed_tests++; + continue; + } + dma_async_memcpy_issue_pending(chan); + + do { + msleep(1); + status = dma_async_memcpy_complete( + chan, cookie, NULL, NULL); + } while (status == DMA_IN_PROGRESS); + + if (status == DMA_ERROR) { + pr_warning("%s: #%u: error during copy\n", + thread_name, total_tests - 1); + failed_tests++; + continue; + } + + error_count = 0; + + pr_debug("%s: verifying source buffer...\n", thread_name); + error_count += dmatest_verify(thread->srcbuf, 0, src_off, + 0, PATTERN_SRC, true); + error_count += dmatest_verify(thread->srcbuf, src_off, + src_off + len, src_off, + PATTERN_SRC | PATTERN_COPY, true); + error_count += dmatest_verify(thread->srcbuf, src_off + len, + test_buf_size, src_off + len, + PATTERN_SRC, true); + + pr_debug("%s: verifying dest buffer...\n", + thread->task->comm); + error_count += dmatest_verify(thread->dstbuf, 0, dst_off, + 0, PATTERN_DST, false); + error_count += dmatest_verify(thread->dstbuf, dst_off, + dst_off + len, src_off, + PATTERN_SRC | PATTERN_COPY, false); + error_count += dmatest_verify(thread->dstbuf, dst_off + len, + test_buf_size, dst_off + len, + PATTERN_DST, false); + + if (error_count) { + pr_warning("%s: #%u: %u errors with " + "src_off=0x%x dst_off=0x%x len=0x%x\n", + thread_name, total_tests - 1, error_count, + src_off, dst_off, len); + failed_tests++; + } else { + pr_debug("%s: #%u: No errors with " + "src_off=0x%x dst_off=0x%x len=0x%x\n", + thread_name, total_tests - 1, + src_off, dst_off, len); + } + } + + ret = 0; + dma_chan_put(chan); + kfree(thread->dstbuf); +err_dstbuf: + kfree(thread->srcbuf); +err_srcbuf: + pr_notice("%s: terminating after %u tests, %u failures (status %d)\n", + thread_name, total_tests, failed_tests, ret); + return ret; +} + +static void dmatest_cleanup_channel(struct dmatest_chan *dtc) +{ + struct dmatest_thread *thread; + struct dmatest_thread *_thread; + int ret; + + list_for_each_entry_safe(thread, _thread, &dtc->threads, node) { + ret = kthread_stop(thread->task); + pr_debug("dmatest: thread %s exited with status %d\n", + thread->task->comm, ret); + list_del(&thread->node); + kfree(thread); + } + kfree(dtc); +} + +static enum dma_state_client dmatest_add_channel(struct dma_chan *chan) +{ + struct dmatest_chan *dtc; + struct dmatest_thread *thread; + unsigned int i; + + dtc = kmalloc(sizeof(struct dmatest_chan), GFP_ATOMIC); + if (!dtc) { + pr_warning("dmatest: No memory for %s\n", chan->dev.bus_id); + return DMA_NAK; + } + + dtc->chan = chan; + INIT_LIST_HEAD(&dtc->threads); + + for (i = 0; i < threads_per_chan; i++) { + thread = kzalloc(sizeof(struct dmatest_thread), GFP_KERNEL); + if (!thread) { + pr_warning("dmatest: No memory for %s-test%u\n", + chan->dev.bus_id, i); + break; + } + thread->chan = dtc->chan; + smp_wmb(); + thread->task = kthread_run(dmatest_func, thread, "%s-test%u", + chan->dev.bus_id, i); + if (IS_ERR(thread->task)) { + pr_warning("dmatest: Failed to run thread %s-test%u\n", + chan->dev.bus_id, i); + kfree(thread); + break; + } + + /* srcbuf and dstbuf are allocated by the thread itself */ + + list_add_tail(&thread->node, &dtc->threads); + } + + pr_info("dmatest: Started %u threads using %s\n", i, chan->dev.bus_id); + + list_add_tail(&dtc->node, &dmatest_channels); + nr_channels++; + + return DMA_ACK; +} + +static enum dma_state_client dmatest_remove_channel(struct dma_chan *chan) +{ + struct dmatest_chan *dtc, *_dtc; + + list_for_each_entry_safe(dtc, _dtc, &dmatest_channels, node) { + if (dtc->chan == chan) { + list_del(&dtc->node); + dmatest_cleanup_channel(dtc); + pr_debug("dmatest: lost channel %s\n", + chan->dev.bus_id); + return DMA_ACK; + } + } + + return DMA_DUP; +} + +/* + * Start testing threads as new channels are assigned to us, and kill + * them when the channels go away. + * + * When we unregister the client, all channels are removed so this + * will also take care of cleaning things up when the module is + * unloaded. + */ +static enum dma_state_client +dmatest_event(struct dma_client *client, struct dma_chan *chan, + enum dma_state state) +{ + enum dma_state_client ack = DMA_NAK; + + switch (state) { + case DMA_RESOURCE_AVAILABLE: + if (!dmatest_match_channel(chan) + || !dmatest_match_device(chan->device)) + ack = DMA_DUP; + else if (max_channels && nr_channels >= max_channels) + ack = DMA_NAK; + else + ack = dmatest_add_channel(chan); + break; + + case DMA_RESOURCE_REMOVED: + ack = dmatest_remove_channel(chan); + break; + + default: + pr_info("dmatest: Unhandled event %u (%s)\n", + state, chan->dev.bus_id); + break; + } + + return ack; +} + +static struct dma_client dmatest_client = { + .event_callback = dmatest_event, +}; + +static int __init dmatest_init(void) +{ + dma_cap_set(DMA_MEMCPY, dmatest_client.cap_mask); + dma_async_client_register(&dmatest_client); + dma_async_client_chan_request(&dmatest_client); + + return 0; +} +module_init(dmatest_init); + +static void __exit dmatest_exit(void) +{ + dma_async_client_unregister(&dmatest_client); +} +module_exit(dmatest_exit); + +MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c new file mode 100644 index 000000000000..94df91771243 --- /dev/null +++ b/drivers/dma/dw_dmac.c @@ -0,0 +1,1122 @@ +/* + * Driver for the Synopsys DesignWare DMA Controller (aka DMACA on + * AVR32 systems.) + * + * Copyright (C) 2007-2008 Atmel Corporation + * + * 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/clk.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "dw_dmac_regs.h" + +/* + * This supports the Synopsys "DesignWare AHB Central DMA Controller", + * (DW_ahb_dmac) which is used with various AMBA 2.0 systems (not all + * of which use ARM any more). See the "Databook" from Synopsys for + * information beyond what licensees probably provide. + * + * The driver has currently been tested only with the Atmel AT32AP7000, + * which does not support descriptor writeback. + */ + +/* NOTE: DMS+SMS is system-specific. We should get this information + * from the platform code somehow. + */ +#define DWC_DEFAULT_CTLLO (DWC_CTLL_DST_MSIZE(0) \ + | DWC_CTLL_SRC_MSIZE(0) \ + | DWC_CTLL_DMS(0) \ + | DWC_CTLL_SMS(1) \ + | DWC_CTLL_LLP_D_EN \ + | DWC_CTLL_LLP_S_EN) + +/* + * This is configuration-dependent and usually a funny size like 4095. + * Let's round it down to the nearest power of two. + * + * Note that this is a transfer count, i.e. if we transfer 32-bit + * words, we can do 8192 bytes per descriptor. + * + * This parameter is also system-specific. + */ +#define DWC_MAX_COUNT 2048U + +/* + * Number of descriptors to allocate for each channel. This should be + * made configurable somehow; preferably, the clients (at least the + * ones using slave transfers) should be able to give us a hint. + */ +#define NR_DESCS_PER_CHANNEL 64 + +/*----------------------------------------------------------------------*/ + +/* + * Because we're not relying on writeback from the controller (it may not + * even be configured into the core!) we don't need to use dma_pool. These + * descriptors -- and associated data -- are cacheable. We do need to make + * sure their dcache entries are written back before handing them off to + * the controller, though. + */ + +static struct dw_desc *dwc_first_active(struct dw_dma_chan *dwc) +{ + return list_entry(dwc->active_list.next, struct dw_desc, desc_node); +} + +static struct dw_desc *dwc_first_queued(struct dw_dma_chan *dwc) +{ + return list_entry(dwc->queue.next, struct dw_desc, desc_node); +} + +static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) +{ + struct dw_desc *desc, *_desc; + struct dw_desc *ret = NULL; + unsigned int i = 0; + + spin_lock_bh(&dwc->lock); + list_for_each_entry_safe(desc, _desc, &dwc->free_list, desc_node) { + if (async_tx_test_ack(&desc->txd)) { + list_del(&desc->desc_node); + ret = desc; + break; + } + dev_dbg(&dwc->chan.dev, "desc %p not ACKed\n", desc); + i++; + } + spin_unlock_bh(&dwc->lock); + + dev_vdbg(&dwc->chan.dev, "scanned %u descriptors on freelist\n", i); + + return ret; +} + +static void dwc_sync_desc_for_cpu(struct dw_dma_chan *dwc, struct dw_desc *desc) +{ + struct dw_desc *child; + + list_for_each_entry(child, &desc->txd.tx_list, desc_node) + dma_sync_single_for_cpu(dwc->chan.dev.parent, + child->txd.phys, sizeof(child->lli), + DMA_TO_DEVICE); + dma_sync_single_for_cpu(dwc->chan.dev.parent, + desc->txd.phys, sizeof(desc->lli), + DMA_TO_DEVICE); +} + +/* + * Move a descriptor, including any children, to the free list. + * `desc' must not be on any lists. + */ +static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) +{ + if (desc) { + struct dw_desc *child; + + dwc_sync_desc_for_cpu(dwc, desc); + + spin_lock_bh(&dwc->lock); + list_for_each_entry(child, &desc->txd.tx_list, desc_node) + dev_vdbg(&dwc->chan.dev, + "moving child desc %p to freelist\n", + child); + list_splice_init(&desc->txd.tx_list, &dwc->free_list); + dev_vdbg(&dwc->chan.dev, "moving desc %p to freelist\n", desc); + list_add(&desc->desc_node, &dwc->free_list); + spin_unlock_bh(&dwc->lock); + } +} + +/* Called with dwc->lock held and bh disabled */ +static dma_cookie_t +dwc_assign_cookie(struct dw_dma_chan *dwc, struct dw_desc *desc) +{ + dma_cookie_t cookie = dwc->chan.cookie; + + if (++cookie < 0) + cookie = 1; + + dwc->chan.cookie = cookie; + desc->txd.cookie = cookie; + + return cookie; +} + +/*----------------------------------------------------------------------*/ + +/* Called with dwc->lock held and bh disabled */ +static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) +{ + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + + /* ASSERT: channel is idle */ + if (dma_readl(dw, CH_EN) & dwc->mask) { + dev_err(&dwc->chan.dev, + "BUG: Attempted to start non-idle channel\n"); + dev_err(&dwc->chan.dev, + " SAR: 0x%x DAR: 0x%x LLP: 0x%x CTL: 0x%x:%08x\n", + channel_readl(dwc, SAR), + channel_readl(dwc, DAR), + channel_readl(dwc, LLP), + channel_readl(dwc, CTL_HI), + channel_readl(dwc, CTL_LO)); + + /* The tasklet will hopefully advance the queue... */ + return; + } + + channel_writel(dwc, LLP, first->txd.phys); + channel_writel(dwc, CTL_LO, + DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN); + channel_writel(dwc, CTL_HI, 0); + channel_set_bit(dw, CH_EN, dwc->mask); +} + +/*----------------------------------------------------------------------*/ + +static void +dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc) +{ + dma_async_tx_callback callback; + void *param; + struct dma_async_tx_descriptor *txd = &desc->txd; + + dev_vdbg(&dwc->chan.dev, "descriptor %u complete\n", txd->cookie); + + dwc->completed = txd->cookie; + callback = txd->callback; + param = txd->callback_param; + + dwc_sync_desc_for_cpu(dwc, desc); + list_splice_init(&txd->tx_list, &dwc->free_list); + list_move(&desc->desc_node, &dwc->free_list); + + /* + * We use dma_unmap_page() regardless of how the buffers were + * mapped before they were submitted... + */ + if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) + dma_unmap_page(dwc->chan.dev.parent, desc->lli.dar, desc->len, + DMA_FROM_DEVICE); + if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) + dma_unmap_page(dwc->chan.dev.parent, desc->lli.sar, desc->len, + DMA_TO_DEVICE); + + /* + * The API requires that no submissions are done from a + * callback, so we don't need to drop the lock here + */ + if (callback) + callback(param); +} + +static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc) +{ + struct dw_desc *desc, *_desc; + LIST_HEAD(list); + + if (dma_readl(dw, CH_EN) & dwc->mask) { + dev_err(&dwc->chan.dev, + "BUG: XFER bit set, but channel not idle!\n"); + + /* Try to continue after resetting the channel... */ + channel_clear_bit(dw, CH_EN, dwc->mask); + while (dma_readl(dw, CH_EN) & dwc->mask) + cpu_relax(); + } + + /* + * Submit queued descriptors ASAP, i.e. before we go through + * the completed ones. + */ + if (!list_empty(&dwc->queue)) + dwc_dostart(dwc, dwc_first_queued(dwc)); + list_splice_init(&dwc->active_list, &list); + list_splice_init(&dwc->queue, &dwc->active_list); + + list_for_each_entry_safe(desc, _desc, &list, desc_node) + dwc_descriptor_complete(dwc, desc); +} + +static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) +{ + dma_addr_t llp; + struct dw_desc *desc, *_desc; + struct dw_desc *child; + u32 status_xfer; + + /* + * Clear block interrupt flag before scanning so that we don't + * miss any, and read LLP before RAW_XFER to ensure it is + * valid if we decide to scan the list. + */ + dma_writel(dw, CLEAR.BLOCK, dwc->mask); + llp = channel_readl(dwc, LLP); + status_xfer = dma_readl(dw, RAW.XFER); + + if (status_xfer & dwc->mask) { + /* Everything we've submitted is done */ + dma_writel(dw, CLEAR.XFER, dwc->mask); + dwc_complete_all(dw, dwc); + return; + } + + dev_vdbg(&dwc->chan.dev, "scan_descriptors: llp=0x%x\n", llp); + + list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) { + if (desc->lli.llp == llp) + /* This one is currently in progress */ + return; + + list_for_each_entry(child, &desc->txd.tx_list, desc_node) + if (child->lli.llp == llp) + /* Currently in progress */ + return; + + /* + * No descriptors so far seem to be in progress, i.e. + * this one must be done. + */ + dwc_descriptor_complete(dwc, desc); + } + + dev_err(&dwc->chan.dev, + "BUG: All descriptors done, but channel not idle!\n"); + + /* Try to continue after resetting the channel... */ + channel_clear_bit(dw, CH_EN, dwc->mask); + while (dma_readl(dw, CH_EN) & dwc->mask) + cpu_relax(); + + if (!list_empty(&dwc->queue)) { + dwc_dostart(dwc, dwc_first_queued(dwc)); + list_splice_init(&dwc->queue, &dwc->active_list); + } +} + +static void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_lli *lli) +{ + dev_printk(KERN_CRIT, &dwc->chan.dev, + " desc: s0x%x d0x%x l0x%x c0x%x:%x\n", + lli->sar, lli->dar, lli->llp, + lli->ctlhi, lli->ctllo); +} + +static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) +{ + struct dw_desc *bad_desc; + struct dw_desc *child; + + dwc_scan_descriptors(dw, dwc); + + /* + * The descriptor currently at the head of the active list is + * borked. Since we don't have any way to report errors, we'll + * just have to scream loudly and try to carry on. + */ + bad_desc = dwc_first_active(dwc); + list_del_init(&bad_desc->desc_node); + list_splice_init(&dwc->queue, dwc->active_list.prev); + + /* Clear the error flag and try to restart the controller */ + dma_writel(dw, CLEAR.ERROR, dwc->mask); + if (!list_empty(&dwc->active_list)) + dwc_dostart(dwc, dwc_first_active(dwc)); + + /* + * KERN_CRITICAL may seem harsh, but since this only happens + * when someone submits a bad physical address in a + * descriptor, we should consider ourselves lucky that the + * controller flagged an error instead of scribbling over + * random memory locations. + */ + dev_printk(KERN_CRIT, &dwc->chan.dev, + "Bad descriptor submitted for DMA!\n"); + dev_printk(KERN_CRIT, &dwc->chan.dev, + " cookie: %d\n", bad_desc->txd.cookie); + dwc_dump_lli(dwc, &bad_desc->lli); + list_for_each_entry(child, &bad_desc->txd.tx_list, desc_node) + dwc_dump_lli(dwc, &child->lli); + + /* Pretend the descriptor completed successfully */ + dwc_descriptor_complete(dwc, bad_desc); +} + +static void dw_dma_tasklet(unsigned long data) +{ + struct dw_dma *dw = (struct dw_dma *)data; + struct dw_dma_chan *dwc; + u32 status_block; + u32 status_xfer; + u32 status_err; + int i; + + status_block = dma_readl(dw, RAW.BLOCK); + status_xfer = dma_readl(dw, RAW.BLOCK); + status_err = dma_readl(dw, RAW.ERROR); + + dev_vdbg(dw->dma.dev, "tasklet: status_block=%x status_err=%x\n", + status_block, status_err); + + for (i = 0; i < dw->dma.chancnt; i++) { + dwc = &dw->chan[i]; + spin_lock(&dwc->lock); + if (status_err & (1 << i)) + dwc_handle_error(dw, dwc); + else if ((status_block | status_xfer) & (1 << i)) + dwc_scan_descriptors(dw, dwc); + spin_unlock(&dwc->lock); + } + + /* + * Re-enable interrupts. Block Complete interrupts are only + * enabled if the INT_EN bit in the descriptor is set. This + * will trigger a scan before the whole list is done. + */ + channel_set_bit(dw, MASK.XFER, dw->all_chan_mask); + channel_set_bit(dw, MASK.BLOCK, dw->all_chan_mask); + channel_set_bit(dw, MASK.ERROR, dw->all_chan_mask); +} + +static irqreturn_t dw_dma_interrupt(int irq, void *dev_id) +{ + struct dw_dma *dw = dev_id; + u32 status; + + dev_vdbg(dw->dma.dev, "interrupt: status=0x%x\n", + dma_readl(dw, STATUS_INT)); + + /* + * Just disable the interrupts. We'll turn them back on in the + * softirq handler. + */ + channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); + channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask); + channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); + + status = dma_readl(dw, STATUS_INT); + if (status) { + dev_err(dw->dma.dev, + "BUG: Unexpected interrupts pending: 0x%x\n", + status); + + /* Try to recover */ + channel_clear_bit(dw, MASK.XFER, (1 << 8) - 1); + channel_clear_bit(dw, MASK.BLOCK, (1 << 8) - 1); + channel_clear_bit(dw, MASK.SRC_TRAN, (1 << 8) - 1); + channel_clear_bit(dw, MASK.DST_TRAN, (1 << 8) - 1); + channel_clear_bit(dw, MASK.ERROR, (1 << 8) - 1); + } + + tasklet_schedule(&dw->tasklet); + + return IRQ_HANDLED; +} + +/*----------------------------------------------------------------------*/ + +static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct dw_desc *desc = txd_to_dw_desc(tx); + struct dw_dma_chan *dwc = to_dw_dma_chan(tx->chan); + dma_cookie_t cookie; + + spin_lock_bh(&dwc->lock); + cookie = dwc_assign_cookie(dwc, desc); + + /* + * REVISIT: We should attempt to chain as many descriptors as + * possible, perhaps even appending to those already submitted + * for DMA. But this is hard to do in a race-free manner. + */ + if (list_empty(&dwc->active_list)) { + dev_vdbg(&tx->chan->dev, "tx_submit: started %u\n", + desc->txd.cookie); + dwc_dostart(dwc, desc); + list_add_tail(&desc->desc_node, &dwc->active_list); + } else { + dev_vdbg(&tx->chan->dev, "tx_submit: queued %u\n", + desc->txd.cookie); + + list_add_tail(&desc->desc_node, &dwc->queue); + } + + spin_unlock_bh(&dwc->lock); + + return cookie; +} + +static struct dma_async_tx_descriptor * +dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, + size_t len, unsigned long flags) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_desc *desc; + struct dw_desc *first; + struct dw_desc *prev; + size_t xfer_count; + size_t offset; + unsigned int src_width; + unsigned int dst_width; + u32 ctllo; + + dev_vdbg(&chan->dev, "prep_dma_memcpy d0x%x s0x%x l0x%zx f0x%lx\n", + dest, src, len, flags); + + if (unlikely(!len)) { + dev_dbg(&chan->dev, "prep_dma_memcpy: length is zero!\n"); + return NULL; + } + + /* + * We can be a lot more clever here, but this should take care + * of the most common optimization. + */ + if (!((src | dest | len) & 3)) + src_width = dst_width = 2; + else if (!((src | dest | len) & 1)) + src_width = dst_width = 1; + else + src_width = dst_width = 0; + + ctllo = DWC_DEFAULT_CTLLO + | DWC_CTLL_DST_WIDTH(dst_width) + | DWC_CTLL_SRC_WIDTH(src_width) + | DWC_CTLL_DST_INC + | DWC_CTLL_SRC_INC + | DWC_CTLL_FC_M2M; + prev = first = NULL; + + for (offset = 0; offset < len; offset += xfer_count << src_width) { + xfer_count = min_t(size_t, (len - offset) >> src_width, + DWC_MAX_COUNT); + + desc = dwc_desc_get(dwc); + if (!desc) + goto err_desc_get; + + desc->lli.sar = src + offset; + desc->lli.dar = dest + offset; + desc->lli.ctllo = ctllo; + desc->lli.ctlhi = xfer_count; + + if (!first) { + first = desc; + } else { + prev->lli.llp = desc->txd.phys; + dma_sync_single_for_device(chan->dev.parent, + prev->txd.phys, sizeof(prev->lli), + DMA_TO_DEVICE); + list_add_tail(&desc->desc_node, + &first->txd.tx_list); + } + prev = desc; + } + + + if (flags & DMA_PREP_INTERRUPT) + /* Trigger interrupt after last block */ + prev->lli.ctllo |= DWC_CTLL_INT_EN; + + prev->lli.llp = 0; + dma_sync_single_for_device(chan->dev.parent, + prev->txd.phys, sizeof(prev->lli), + DMA_TO_DEVICE); + + first->txd.flags = flags; + first->len = len; + + return &first->txd; + +err_desc_get: + dwc_desc_put(dwc, first); + return NULL; +} + +static struct dma_async_tx_descriptor * +dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_data_direction direction, + unsigned long flags) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma_slave *dws = dwc->dws; + struct dw_desc *prev; + struct dw_desc *first; + u32 ctllo; + dma_addr_t reg; + unsigned int reg_width; + unsigned int mem_width; + unsigned int i; + struct scatterlist *sg; + size_t total_len = 0; + + dev_vdbg(&chan->dev, "prep_dma_slave\n"); + + if (unlikely(!dws || !sg_len)) + return NULL; + + reg_width = dws->slave.reg_width; + prev = first = NULL; + + sg_len = dma_map_sg(chan->dev.parent, sgl, sg_len, direction); + + switch (direction) { + case DMA_TO_DEVICE: + ctllo = (DWC_DEFAULT_CTLLO + | DWC_CTLL_DST_WIDTH(reg_width) + | DWC_CTLL_DST_FIX + | DWC_CTLL_SRC_INC + | DWC_CTLL_FC_M2P); + reg = dws->slave.tx_reg; + for_each_sg(sgl, sg, sg_len, i) { + struct dw_desc *desc; + u32 len; + u32 mem; + + desc = dwc_desc_get(dwc); + if (!desc) { + dev_err(&chan->dev, + "not enough descriptors available\n"); + goto err_desc_get; + } + + mem = sg_phys(sg); + len = sg_dma_len(sg); + mem_width = 2; + if (unlikely(mem & 3 || len & 3)) + mem_width = 0; + + desc->lli.sar = mem; + desc->lli.dar = reg; + desc->lli.ctllo = ctllo | DWC_CTLL_SRC_WIDTH(mem_width); + desc->lli.ctlhi = len >> mem_width; + + if (!first) { + first = desc; + } else { + prev->lli.llp = desc->txd.phys; + dma_sync_single_for_device(chan->dev.parent, + prev->txd.phys, + sizeof(prev->lli), + DMA_TO_DEVICE); + list_add_tail(&desc->desc_node, + &first->txd.tx_list); + } + prev = desc; + total_len += len; + } + break; + case DMA_FROM_DEVICE: + ctllo = (DWC_DEFAULT_CTLLO + | DWC_CTLL_SRC_WIDTH(reg_width) + | DWC_CTLL_DST_INC + | DWC_CTLL_SRC_FIX + | DWC_CTLL_FC_P2M); + + reg = dws->slave.rx_reg; + for_each_sg(sgl, sg, sg_len, i) { + struct dw_desc *desc; + u32 len; + u32 mem; + + desc = dwc_desc_get(dwc); + if (!desc) { + dev_err(&chan->dev, + "not enough descriptors available\n"); + goto err_desc_get; + } + + mem = sg_phys(sg); + len = sg_dma_len(sg); + mem_width = 2; + if (unlikely(mem & 3 || len & 3)) + mem_width = 0; + + desc->lli.sar = reg; + desc->lli.dar = mem; + desc->lli.ctllo = ctllo | DWC_CTLL_DST_WIDTH(mem_width); + desc->lli.ctlhi = len >> reg_width; + + if (!first) { + first = desc; + } else { + prev->lli.llp = desc->txd.phys; + dma_sync_single_for_device(chan->dev.parent, + prev->txd.phys, + sizeof(prev->lli), + DMA_TO_DEVICE); + list_add_tail(&desc->desc_node, + &first->txd.tx_list); + } + prev = desc; + total_len += len; + } + break; + default: + return NULL; + } + + if (flags & DMA_PREP_INTERRUPT) + /* Trigger interrupt after last block */ + prev->lli.ctllo |= DWC_CTLL_INT_EN; + + prev->lli.llp = 0; + dma_sync_single_for_device(chan->dev.parent, + prev->txd.phys, sizeof(prev->lli), + DMA_TO_DEVICE); + + first->len = total_len; + + return &first->txd; + +err_desc_get: + dwc_desc_put(dwc, first); + return NULL; +} + +static void dwc_terminate_all(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_desc *desc, *_desc; + LIST_HEAD(list); + + /* + * This is only called when something went wrong elsewhere, so + * we don't really care about the data. Just disable the + * channel. We still have to poll the channel enable bit due + * to AHB/HSB limitations. + */ + spin_lock_bh(&dwc->lock); + + channel_clear_bit(dw, CH_EN, dwc->mask); + + while (dma_readl(dw, CH_EN) & dwc->mask) + cpu_relax(); + + /* active_list entries will end up before queued entries */ + list_splice_init(&dwc->queue, &list); + list_splice_init(&dwc->active_list, &list); + + spin_unlock_bh(&dwc->lock); + + /* Flush all pending and queued descriptors */ + list_for_each_entry_safe(desc, _desc, &list, desc_node) + dwc_descriptor_complete(dwc, desc); +} + +static enum dma_status +dwc_is_tx_complete(struct dma_chan *chan, + dma_cookie_t cookie, + dma_cookie_t *done, dma_cookie_t *used) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + dma_cookie_t last_used; + dma_cookie_t last_complete; + int ret; + + last_complete = dwc->completed; + last_used = chan->cookie; + + ret = dma_async_is_complete(cookie, last_complete, last_used); + if (ret != DMA_SUCCESS) { + dwc_scan_descriptors(to_dw_dma(chan->device), dwc); + + last_complete = dwc->completed; + last_used = chan->cookie; + + ret = dma_async_is_complete(cookie, last_complete, last_used); + } + + if (done) + *done = last_complete; + if (used) + *used = last_used; + + return ret; +} + +static void dwc_issue_pending(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + + spin_lock_bh(&dwc->lock); + if (!list_empty(&dwc->queue)) + dwc_scan_descriptors(to_dw_dma(chan->device), dwc); + spin_unlock_bh(&dwc->lock); +} + +static int dwc_alloc_chan_resources(struct dma_chan *chan, + struct dma_client *client) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_desc *desc; + struct dma_slave *slave; + struct dw_dma_slave *dws; + int i; + u32 cfghi; + u32 cfglo; + + dev_vdbg(&chan->dev, "alloc_chan_resources\n"); + + /* Channels doing slave DMA can only handle one client. */ + if (dwc->dws || client->slave) { + if (chan->client_count) + return -EBUSY; + } + + /* ASSERT: channel is idle */ + if (dma_readl(dw, CH_EN) & dwc->mask) { + dev_dbg(&chan->dev, "DMA channel not idle?\n"); + return -EIO; + } + + dwc->completed = chan->cookie = 1; + + cfghi = DWC_CFGH_FIFO_MODE; + cfglo = 0; + + slave = client->slave; + if (slave) { + /* + * We need controller-specific data to set up slave + * transfers. + */ + BUG_ON(!slave->dma_dev || slave->dma_dev != dw->dma.dev); + + dws = container_of(slave, struct dw_dma_slave, slave); + + dwc->dws = dws; + cfghi = dws->cfg_hi; + cfglo = dws->cfg_lo; + } else { + dwc->dws = NULL; + } + + channel_writel(dwc, CFG_LO, cfglo); + channel_writel(dwc, CFG_HI, cfghi); + + /* + * NOTE: some controllers may have additional features that we + * need to initialize here, like "scatter-gather" (which + * doesn't mean what you think it means), and status writeback. + */ + + spin_lock_bh(&dwc->lock); + i = dwc->descs_allocated; + while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) { + spin_unlock_bh(&dwc->lock); + + desc = kzalloc(sizeof(struct dw_desc), GFP_KERNEL); + if (!desc) { + dev_info(&chan->dev, + "only allocated %d descriptors\n", i); + spin_lock_bh(&dwc->lock); + break; + } + + dma_async_tx_descriptor_init(&desc->txd, chan); + desc->txd.tx_submit = dwc_tx_submit; + desc->txd.flags = DMA_CTRL_ACK; + INIT_LIST_HEAD(&desc->txd.tx_list); + desc->txd.phys = dma_map_single(chan->dev.parent, &desc->lli, + sizeof(desc->lli), DMA_TO_DEVICE); + dwc_desc_put(dwc, desc); + + spin_lock_bh(&dwc->lock); + i = ++dwc->descs_allocated; + } + + /* Enable interrupts */ + channel_set_bit(dw, MASK.XFER, dwc->mask); + channel_set_bit(dw, MASK.BLOCK, dwc->mask); + channel_set_bit(dw, MASK.ERROR, dwc->mask); + + spin_unlock_bh(&dwc->lock); + + dev_dbg(&chan->dev, + "alloc_chan_resources allocated %d descriptors\n", i); + + return i; +} + +static void dwc_free_chan_resources(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_desc *desc, *_desc; + LIST_HEAD(list); + + dev_dbg(&chan->dev, "free_chan_resources (descs allocated=%u)\n", + dwc->descs_allocated); + + /* ASSERT: channel is idle */ + BUG_ON(!list_empty(&dwc->active_list)); + BUG_ON(!list_empty(&dwc->queue)); + BUG_ON(dma_readl(to_dw_dma(chan->device), CH_EN) & dwc->mask); + + spin_lock_bh(&dwc->lock); + list_splice_init(&dwc->free_list, &list); + dwc->descs_allocated = 0; + dwc->dws = NULL; + + /* Disable interrupts */ + channel_clear_bit(dw, MASK.XFER, dwc->mask); + channel_clear_bit(dw, MASK.BLOCK, dwc->mask); + channel_clear_bit(dw, MASK.ERROR, dwc->mask); + + spin_unlock_bh(&dwc->lock); + + list_for_each_entry_safe(desc, _desc, &list, desc_node) { + dev_vdbg(&chan->dev, " freeing descriptor %p\n", desc); + dma_unmap_single(chan->dev.parent, desc->txd.phys, + sizeof(desc->lli), DMA_TO_DEVICE); + kfree(desc); + } + + dev_vdbg(&chan->dev, "free_chan_resources done\n"); +} + +/*----------------------------------------------------------------------*/ + +static void dw_dma_off(struct dw_dma *dw) +{ + dma_writel(dw, CFG, 0); + + channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); + channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask); + channel_clear_bit(dw, MASK.SRC_TRAN, dw->all_chan_mask); + channel_clear_bit(dw, MASK.DST_TRAN, dw->all_chan_mask); + channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); + + while (dma_readl(dw, CFG) & DW_CFG_DMA_EN) + cpu_relax(); +} + +static int __init dw_probe(struct platform_device *pdev) +{ + struct dw_dma_platform_data *pdata; + struct resource *io; + struct dw_dma *dw; + size_t size; + int irq; + int err; + int i; + + pdata = pdev->dev.platform_data; + if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) + return -EINVAL; + + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!io) + return -EINVAL; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + size = sizeof(struct dw_dma); + size += pdata->nr_channels * sizeof(struct dw_dma_chan); + dw = kzalloc(size, GFP_KERNEL); + if (!dw) + return -ENOMEM; + + if (!request_mem_region(io->start, DW_REGLEN, pdev->dev.driver->name)) { + err = -EBUSY; + goto err_kfree; + } + + memset(dw, 0, sizeof *dw); + + dw->regs = ioremap(io->start, DW_REGLEN); + if (!dw->regs) { + err = -ENOMEM; + goto err_release_r; + } + + dw->clk = clk_get(&pdev->dev, "hclk"); + if (IS_ERR(dw->clk)) { + err = PTR_ERR(dw->clk); + goto err_clk; + } + clk_enable(dw->clk); + + /* force dma off, just in case */ + dw_dma_off(dw); + + err = request_irq(irq, dw_dma_interrupt, 0, "dw_dmac", dw); + if (err) + goto err_irq; + + platform_set_drvdata(pdev, dw); + + tasklet_init(&dw->tasklet, dw_dma_tasklet, (unsigned long)dw); + + dw->all_chan_mask = (1 << pdata->nr_channels) - 1; + + INIT_LIST_HEAD(&dw->dma.channels); + for (i = 0; i < pdata->nr_channels; i++, dw->dma.chancnt++) { + struct dw_dma_chan *dwc = &dw->chan[i]; + + dwc->chan.device = &dw->dma; + dwc->chan.cookie = dwc->completed = 1; + dwc->chan.chan_id = i; + list_add_tail(&dwc->chan.device_node, &dw->dma.channels); + + dwc->ch_regs = &__dw_regs(dw)->CHAN[i]; + spin_lock_init(&dwc->lock); + dwc->mask = 1 << i; + + INIT_LIST_HEAD(&dwc->active_list); + INIT_LIST_HEAD(&dwc->queue); + INIT_LIST_HEAD(&dwc->free_list); + + channel_clear_bit(dw, CH_EN, dwc->mask); + } + + /* Clear/disable all interrupts on all channels. */ + dma_writel(dw, CLEAR.XFER, dw->all_chan_mask); + dma_writel(dw, CLEAR.BLOCK, dw->all_chan_mask); + dma_writel(dw, CLEAR.SRC_TRAN, dw->all_chan_mask); + dma_writel(dw, CLEAR.DST_TRAN, dw->all_chan_mask); + dma_writel(dw, CLEAR.ERROR, dw->all_chan_mask); + + channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); + channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask); + channel_clear_bit(dw, MASK.SRC_TRAN, dw->all_chan_mask); + channel_clear_bit(dw, MASK.DST_TRAN, dw->all_chan_mask); + channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); + + dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask); + dma_cap_set(DMA_SLAVE, dw->dma.cap_mask); + dw->dma.dev = &pdev->dev; + dw->dma.device_alloc_chan_resources = dwc_alloc_chan_resources; + dw->dma.device_free_chan_resources = dwc_free_chan_resources; + + dw->dma.device_prep_dma_memcpy = dwc_prep_dma_memcpy; + + dw->dma.device_prep_slave_sg = dwc_prep_slave_sg; + dw->dma.device_terminate_all = dwc_terminate_all; + + dw->dma.device_is_tx_complete = dwc_is_tx_complete; + dw->dma.device_issue_pending = dwc_issue_pending; + + dma_writel(dw, CFG, DW_CFG_DMA_EN); + + printk(KERN_INFO "%s: DesignWare DMA Controller, %d channels\n", + pdev->dev.bus_id, dw->dma.chancnt); + + dma_async_device_register(&dw->dma); + + return 0; + +err_irq: + clk_disable(dw->clk); + clk_put(dw->clk); +err_clk: + iounmap(dw->regs); + dw->regs = NULL; +err_release_r: + release_resource(io); +err_kfree: + kfree(dw); + return err; +} + +static int __exit dw_remove(struct platform_device *pdev) +{ + struct dw_dma *dw = platform_get_drvdata(pdev); + struct dw_dma_chan *dwc, *_dwc; + struct resource *io; + + dw_dma_off(dw); + dma_async_device_unregister(&dw->dma); + + free_irq(platform_get_irq(pdev, 0), dw); + tasklet_kill(&dw->tasklet); + + list_for_each_entry_safe(dwc, _dwc, &dw->dma.channels, + chan.device_node) { + list_del(&dwc->chan.device_node); + channel_clear_bit(dw, CH_EN, dwc->mask); + } + + clk_disable(dw->clk); + clk_put(dw->clk); + + iounmap(dw->regs); + dw->regs = NULL; + + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(io->start, DW_REGLEN); + + kfree(dw); + + return 0; +} + +static void dw_shutdown(struct platform_device *pdev) +{ + struct dw_dma *dw = platform_get_drvdata(pdev); + + dw_dma_off(platform_get_drvdata(pdev)); + clk_disable(dw->clk); +} + +static int dw_suspend_late(struct platform_device *pdev, pm_message_t mesg) +{ + struct dw_dma *dw = platform_get_drvdata(pdev); + + dw_dma_off(platform_get_drvdata(pdev)); + clk_disable(dw->clk); + return 0; +} + +static int dw_resume_early(struct platform_device *pdev) +{ + struct dw_dma *dw = platform_get_drvdata(pdev); + + clk_enable(dw->clk); + dma_writel(dw, CFG, DW_CFG_DMA_EN); + return 0; + +} + +static struct platform_driver dw_driver = { + .remove = __exit_p(dw_remove), + .shutdown = dw_shutdown, + .suspend_late = dw_suspend_late, + .resume_early = dw_resume_early, + .driver = { + .name = "dw_dmac", + }, +}; + +static int __init dw_init(void) +{ + return platform_driver_probe(&dw_driver, dw_probe); +} +module_init(dw_init); + +static void __exit dw_exit(void) +{ + platform_driver_unregister(&dw_driver); +} +module_exit(dw_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller driver"); +MODULE_AUTHOR("Haavard Skinnemoen <haavard.skinnemoen@atmel.com>"); diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h new file mode 100644 index 000000000000..00fdd187bb0c --- /dev/null +++ b/drivers/dma/dw_dmac_regs.h @@ -0,0 +1,225 @@ +/* + * Driver for the Synopsys DesignWare AHB DMA Controller + * + * Copyright (C) 2005-2007 Atmel Corporation + * + * 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/dw_dmac.h> + +#define DW_DMA_MAX_NR_CHANNELS 8 + +/* + * Redefine this macro to handle differences between 32- and 64-bit + * addressing, big vs. little endian, etc. + */ +#define DW_REG(name) u32 name; u32 __pad_##name + +/* Hardware register definitions. */ +struct dw_dma_chan_regs { + DW_REG(SAR); /* Source Address Register */ + DW_REG(DAR); /* Destination Address Register */ + DW_REG(LLP); /* Linked List Pointer */ + u32 CTL_LO; /* Control Register Low */ + u32 CTL_HI; /* Control Register High */ + DW_REG(SSTAT); + DW_REG(DSTAT); + DW_REG(SSTATAR); + DW_REG(DSTATAR); + u32 CFG_LO; /* Configuration Register Low */ + u32 CFG_HI; /* Configuration Register High */ + DW_REG(SGR); + DW_REG(DSR); +}; + +struct dw_dma_irq_regs { + DW_REG(XFER); + DW_REG(BLOCK); + DW_REG(SRC_TRAN); + DW_REG(DST_TRAN); + DW_REG(ERROR); +}; + +struct dw_dma_regs { + /* per-channel registers */ + struct dw_dma_chan_regs CHAN[DW_DMA_MAX_NR_CHANNELS]; + + /* irq handling */ + struct dw_dma_irq_regs RAW; /* r */ + struct dw_dma_irq_regs STATUS; /* r (raw & mask) */ + struct dw_dma_irq_regs MASK; /* rw (set = irq enabled) */ + struct dw_dma_irq_regs CLEAR; /* w (ack, affects "raw") */ + + DW_REG(STATUS_INT); /* r */ + + /* software handshaking */ + DW_REG(REQ_SRC); + DW_REG(REQ_DST); + DW_REG(SGL_REQ_SRC); + DW_REG(SGL_REQ_DST); + DW_REG(LAST_SRC); + DW_REG(LAST_DST); + + /* miscellaneous */ + DW_REG(CFG); + DW_REG(CH_EN); + DW_REG(ID); + DW_REG(TEST); + + /* optional encoded params, 0x3c8..0x3 */ +}; + +/* Bitfields in CTL_LO */ +#define DWC_CTLL_INT_EN (1 << 0) /* irqs enabled? */ +#define DWC_CTLL_DST_WIDTH(n) ((n)<<1) /* bytes per element */ +#define DWC_CTLL_SRC_WIDTH(n) ((n)<<4) +#define DWC_CTLL_DST_INC (0<<7) /* DAR update/not */ +#define DWC_CTLL_DST_DEC (1<<7) +#define DWC_CTLL_DST_FIX (2<<7) +#define DWC_CTLL_SRC_INC (0<<7) /* SAR update/not */ +#define DWC_CTLL_SRC_DEC (1<<9) +#define DWC_CTLL_SRC_FIX (2<<9) +#define DWC_CTLL_DST_MSIZE(n) ((n)<<11) /* burst, #elements */ +#define DWC_CTLL_SRC_MSIZE(n) ((n)<<14) +#define DWC_CTLL_S_GATH_EN (1 << 17) /* src gather, !FIX */ +#define DWC_CTLL_D_SCAT_EN (1 << 18) /* dst scatter, !FIX */ +#define DWC_CTLL_FC_M2M (0 << 20) /* mem-to-mem */ +#define DWC_CTLL_FC_M2P (1 << 20) /* mem-to-periph */ +#define DWC_CTLL_FC_P2M (2 << 20) /* periph-to-mem */ +#define DWC_CTLL_FC_P2P (3 << 20) /* periph-to-periph */ +/* plus 4 transfer types for peripheral-as-flow-controller */ +#define DWC_CTLL_DMS(n) ((n)<<23) /* dst master select */ +#define DWC_CTLL_SMS(n) ((n)<<25) /* src master select */ +#define DWC_CTLL_LLP_D_EN (1 << 27) /* dest block chain */ +#define DWC_CTLL_LLP_S_EN (1 << 28) /* src block chain */ + +/* Bitfields in CTL_HI */ +#define DWC_CTLH_DONE 0x00001000 +#define DWC_CTLH_BLOCK_TS_MASK 0x00000fff + +/* Bitfields in CFG_LO. Platform-configurable bits are in <linux/dw_dmac.h> */ +#define DWC_CFGL_CH_SUSP (1 << 8) /* pause xfer */ +#define DWC_CFGL_FIFO_EMPTY (1 << 9) /* pause xfer */ +#define DWC_CFGL_HS_DST (1 << 10) /* handshake w/dst */ +#define DWC_CFGL_HS_SRC (1 << 11) /* handshake w/src */ +#define DWC_CFGL_MAX_BURST(x) ((x) << 20) +#define DWC_CFGL_RELOAD_SAR (1 << 30) +#define DWC_CFGL_RELOAD_DAR (1 << 31) + +/* Bitfields in CFG_HI. Platform-configurable bits are in <linux/dw_dmac.h> */ +#define DWC_CFGH_DS_UPD_EN (1 << 5) +#define DWC_CFGH_SS_UPD_EN (1 << 6) + +/* Bitfields in SGR */ +#define DWC_SGR_SGI(x) ((x) << 0) +#define DWC_SGR_SGC(x) ((x) << 20) + +/* Bitfields in DSR */ +#define DWC_DSR_DSI(x) ((x) << 0) +#define DWC_DSR_DSC(x) ((x) << 20) + +/* Bitfields in CFG */ +#define DW_CFG_DMA_EN (1 << 0) + +#define DW_REGLEN 0x400 + +struct dw_dma_chan { + struct dma_chan chan; + void __iomem *ch_regs; + u8 mask; + + spinlock_t lock; + + /* these other elements are all protected by lock */ + dma_cookie_t completed; + struct list_head active_list; + struct list_head queue; + struct list_head free_list; + + struct dw_dma_slave *dws; + + unsigned int descs_allocated; +}; + +static inline struct dw_dma_chan_regs __iomem * +__dwc_regs(struct dw_dma_chan *dwc) +{ + return dwc->ch_regs; +} + +#define channel_readl(dwc, name) \ + __raw_readl(&(__dwc_regs(dwc)->name)) +#define channel_writel(dwc, name, val) \ + __raw_writel((val), &(__dwc_regs(dwc)->name)) + +static inline struct dw_dma_chan *to_dw_dma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct dw_dma_chan, chan); +} + + +struct dw_dma { + struct dma_device dma; + void __iomem *regs; + struct tasklet_struct tasklet; + struct clk *clk; + + u8 all_chan_mask; + + struct dw_dma_chan chan[0]; +}; + +static inline struct dw_dma_regs __iomem *__dw_regs(struct dw_dma *dw) +{ + return dw->regs; +} + +#define dma_readl(dw, name) \ + __raw_readl(&(__dw_regs(dw)->name)) +#define dma_writel(dw, name, val) \ + __raw_writel((val), &(__dw_regs(dw)->name)) + +#define channel_set_bit(dw, reg, mask) \ + dma_writel(dw, reg, ((mask) << 8) | (mask)) +#define channel_clear_bit(dw, reg, mask) \ + dma_writel(dw, reg, ((mask) << 8) | 0) + +static inline struct dw_dma *to_dw_dma(struct dma_device *ddev) +{ + return container_of(ddev, struct dw_dma, dma); +} + +/* LLI == Linked List Item; a.k.a. DMA block descriptor */ +struct dw_lli { + /* values that are not changed by hardware */ + dma_addr_t sar; + dma_addr_t dar; + dma_addr_t llp; /* chain to next lli */ + u32 ctllo; + /* values that may get written back: */ + u32 ctlhi; + /* sstat and dstat can snapshot peripheral register state. + * silicon config may discard either or both... + */ + u32 sstat; + u32 dstat; +}; + +struct dw_desc { + /* FIRST values the hardware uses */ + struct dw_lli lli; + + /* THEN values for driver housekeeping */ + struct list_head desc_node; + struct dma_async_tx_descriptor txd; + size_t len; +}; + +static inline struct dw_desc * +txd_to_dw_desc(struct dma_async_tx_descriptor *txd) +{ + return container_of(txd, struct dw_desc, txd); +} diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 054eabffc185..c0059ca58340 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -366,7 +366,8 @@ static struct fsl_desc_sw *fsl_dma_alloc_descriptor( * * Return - The number of descriptors allocated. */ -static int fsl_dma_alloc_chan_resources(struct dma_chan *chan) +static int fsl_dma_alloc_chan_resources(struct dma_chan *chan, + struct dma_client *client) { struct fsl_dma_chan *fsl_chan = to_fsl_chan(chan); LIST_HEAD(tmp_list); @@ -809,8 +810,7 @@ static int fsl_dma_self_test(struct fsl_dma_chan *fsl_chan) if (!src) { dev_err(fsl_chan->dev, "selftest: Cannot alloc memory for test!\n"); - err = -ENOMEM; - goto out; + return -ENOMEM; } dest = src + test_size; @@ -820,7 +820,7 @@ static int fsl_dma_self_test(struct fsl_dma_chan *fsl_chan) chan = &fsl_chan->common; - if (fsl_dma_alloc_chan_resources(chan) < 1) { + if (fsl_dma_alloc_chan_resources(chan, NULL) < 1) { dev_err(fsl_chan->dev, "selftest: Cannot alloc resources for DMA\n"); err = -ENODEV; @@ -842,13 +842,13 @@ static int fsl_dma_self_test(struct fsl_dma_chan *fsl_chan) if (fsl_dma_is_complete(chan, cookie, NULL, NULL) != DMA_SUCCESS) { dev_err(fsl_chan->dev, "selftest: Time out!\n"); err = -ENODEV; - goto out; + goto free_resources; } /* Test free and re-alloc channel resources */ fsl_dma_free_chan_resources(chan); - if (fsl_dma_alloc_chan_resources(chan) < 1) { + if (fsl_dma_alloc_chan_resources(chan, NULL) < 1) { dev_err(fsl_chan->dev, "selftest: Cannot alloc resources for DMA\n"); err = -ENODEV; @@ -927,8 +927,7 @@ static int __devinit of_fsl_dma_chan_probe(struct of_device *dev, if (!new_fsl_chan) { dev_err(&dev->dev, "No free memory for allocating " "dma channels!\n"); - err = -ENOMEM; - goto err; + return -ENOMEM; } /* get dma channel register base */ @@ -936,7 +935,7 @@ static int __devinit of_fsl_dma_chan_probe(struct of_device *dev, if (err) { dev_err(&dev->dev, "Can't get %s property 'reg'\n", dev->node->full_name); - goto err; + goto err_no_reg; } new_fsl_chan->feature = *(u32 *)match->data; @@ -958,7 +957,7 @@ static int __devinit of_fsl_dma_chan_probe(struct of_device *dev, dev_err(&dev->dev, "There is no %d channel!\n", new_fsl_chan->id); err = -EINVAL; - goto err; + goto err_no_chan; } fdev->chan[new_fsl_chan->id] = new_fsl_chan; tasklet_init(&new_fsl_chan->tasklet, dma_do_tasklet, @@ -997,23 +996,26 @@ static int __devinit of_fsl_dma_chan_probe(struct of_device *dev, if (err) { dev_err(&dev->dev, "DMA channel %s request_irq error " "with return %d\n", dev->node->full_name, err); - goto err; + goto err_no_irq; } } err = fsl_dma_self_test(new_fsl_chan); if (err) - goto err; + goto err_self_test; dev_info(&dev->dev, "#%d (%s), irq %d\n", new_fsl_chan->id, match->compatible, new_fsl_chan->irq); return 0; -err: - dma_halt(new_fsl_chan); - iounmap(new_fsl_chan->reg_base); + +err_self_test: free_irq(new_fsl_chan->irq, new_fsl_chan); +err_no_irq: list_del(&new_fsl_chan->common.device_node); +err_no_chan: + iounmap(new_fsl_chan->reg_base); +err_no_reg: kfree(new_fsl_chan); return err; } @@ -1054,8 +1056,7 @@ static int __devinit of_fsl_dma_probe(struct of_device *dev, fdev = kzalloc(sizeof(struct fsl_dma_device), GFP_KERNEL); if (!fdev) { dev_err(&dev->dev, "No enough memory for 'priv'\n"); - err = -ENOMEM; - goto err; + return -ENOMEM; } fdev->dev = &dev->dev; INIT_LIST_HEAD(&fdev->common.channels); @@ -1065,7 +1066,7 @@ static int __devinit of_fsl_dma_probe(struct of_device *dev, if (err) { dev_err(&dev->dev, "Can't get %s property 'reg'\n", dev->node->full_name); - goto err; + goto err_no_reg; } dev_info(&dev->dev, "Probe the Freescale DMA driver for %s " @@ -1103,6 +1104,7 @@ static int __devinit of_fsl_dma_probe(struct of_device *dev, err: iounmap(fdev->reg_base); +err_no_reg: kfree(fdev); return err; } diff --git a/drivers/dma/ioat.c b/drivers/dma/ioat.c index 16e0fd8facfb..9b16a3af9a0a 100644 --- a/drivers/dma/ioat.c +++ b/drivers/dma/ioat.c @@ -47,6 +47,16 @@ static struct pci_device_id ioat_pci_tbl[] = { /* I/OAT v2 platforms */ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB) }, + + /* I/OAT v3 platforms */ + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG3) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG4) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG5) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG6) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG7) }, { 0, } }; @@ -83,6 +93,11 @@ static int ioat_setup_functionality(struct pci_dev *pdev, void __iomem *iobase) if (device->dma && ioat_dca_enabled) device->dca = ioat2_dca_init(pdev, iobase); break; + case IOAT_VER_3_0: + device->dma = ioat_dma_probe(pdev, iobase); + if (device->dma && ioat_dca_enabled) + device->dca = ioat3_dca_init(pdev, iobase); + break; default: err = -ENODEV; break; diff --git a/drivers/dma/ioat_dca.c b/drivers/dma/ioat_dca.c index 9e922760b7ff..6cf622da0286 100644 --- a/drivers/dma/ioat_dca.c +++ b/drivers/dma/ioat_dca.c @@ -37,12 +37,18 @@ #include "ioatdma_registers.h" /* - * Bit 16 of a tag map entry is the "valid" bit, if it is set then bits 0:15 + * Bit 7 of a tag map entry is the "valid" bit, if it is set then bits 0:6 * contain the bit number of the APIC ID to map into the DCA tag. If the valid * bit is not set, then the value must be 0 or 1 and defines the bit in the tag. */ #define DCA_TAG_MAP_VALID 0x80 +#define DCA3_TAG_MAP_BIT_TO_INV 0x80 +#define DCA3_TAG_MAP_BIT_TO_SEL 0x40 +#define DCA3_TAG_MAP_LITERAL_VAL 0x1 + +#define DCA_TAG_MAP_MASK 0xDF + /* * "Legacy" DCA systems do not implement the DCA register set in the * I/OAT device. Software needs direct support for their tag mappings. @@ -95,6 +101,7 @@ struct ioat_dca_slot { }; #define IOAT_DCA_MAX_REQ 6 +#define IOAT3_DCA_MAX_REQ 2 struct ioat_dca_priv { void __iomem *iobase; @@ -171,7 +178,9 @@ static int ioat_dca_remove_requester(struct dca_provider *dca, return -ENODEV; } -static u8 ioat_dca_get_tag(struct dca_provider *dca, int cpu) +static u8 ioat_dca_get_tag(struct dca_provider *dca, + struct device *dev, + int cpu) { struct ioat_dca_priv *ioatdca = dca_priv(dca); int i, apic_id, bit, value; @@ -193,10 +202,26 @@ static u8 ioat_dca_get_tag(struct dca_provider *dca, int cpu) return tag; } +static int ioat_dca_dev_managed(struct dca_provider *dca, + struct device *dev) +{ + struct ioat_dca_priv *ioatdca = dca_priv(dca); + struct pci_dev *pdev; + int i; + + pdev = to_pci_dev(dev); + for (i = 0; i < ioatdca->max_requesters; i++) { + if (ioatdca->req_slots[i].pdev == pdev) + return 1; + } + return 0; +} + static struct dca_ops ioat_dca_ops = { .add_requester = ioat_dca_add_requester, .remove_requester = ioat_dca_remove_requester, .get_tag = ioat_dca_get_tag, + .dev_managed = ioat_dca_dev_managed, }; @@ -207,6 +232,8 @@ struct dca_provider *ioat_dca_init(struct pci_dev *pdev, void __iomem *iobase) u8 *tag_map = NULL; int i; int err; + u8 version; + u8 max_requesters; if (!system_has_dca_enabled(pdev)) return NULL; @@ -237,15 +264,20 @@ struct dca_provider *ioat_dca_init(struct pci_dev *pdev, void __iomem *iobase) if (tag_map == NULL) return NULL; + version = readb(iobase + IOAT_VER_OFFSET); + if (version == IOAT_VER_3_0) + max_requesters = IOAT3_DCA_MAX_REQ; + else + max_requesters = IOAT_DCA_MAX_REQ; + dca = alloc_dca_provider(&ioat_dca_ops, sizeof(*ioatdca) + - (sizeof(struct ioat_dca_slot) * IOAT_DCA_MAX_REQ)); + (sizeof(struct ioat_dca_slot) * max_requesters)); if (!dca) return NULL; ioatdca = dca_priv(dca); - ioatdca->max_requesters = IOAT_DCA_MAX_REQ; - + ioatdca->max_requesters = max_requesters; ioatdca->dca_base = iobase + 0x54; /* copy over the APIC ID to DCA tag mapping */ @@ -323,11 +355,13 @@ static int ioat2_dca_remove_requester(struct dca_provider *dca, return -ENODEV; } -static u8 ioat2_dca_get_tag(struct dca_provider *dca, int cpu) +static u8 ioat2_dca_get_tag(struct dca_provider *dca, + struct device *dev, + int cpu) { u8 tag; - tag = ioat_dca_get_tag(dca, cpu); + tag = ioat_dca_get_tag(dca, dev, cpu); tag = (~tag) & 0x1F; return tag; } @@ -336,6 +370,7 @@ static struct dca_ops ioat2_dca_ops = { .add_requester = ioat2_dca_add_requester, .remove_requester = ioat2_dca_remove_requester, .get_tag = ioat2_dca_get_tag, + .dev_managed = ioat_dca_dev_managed, }; static int ioat2_dca_count_dca_slots(void __iomem *iobase, u16 dca_offset) @@ -425,3 +460,198 @@ struct dca_provider *ioat2_dca_init(struct pci_dev *pdev, void __iomem *iobase) return dca; } + +static int ioat3_dca_add_requester(struct dca_provider *dca, struct device *dev) +{ + struct ioat_dca_priv *ioatdca = dca_priv(dca); + struct pci_dev *pdev; + int i; + u16 id; + u16 global_req_table; + + /* This implementation only supports PCI-Express */ + if (dev->bus != &pci_bus_type) + return -ENODEV; + pdev = to_pci_dev(dev); + id = dcaid_from_pcidev(pdev); + + if (ioatdca->requester_count == ioatdca->max_requesters) + return -ENODEV; + + for (i = 0; i < ioatdca->max_requesters; i++) { + if (ioatdca->req_slots[i].pdev == NULL) { + /* found an empty slot */ + ioatdca->requester_count++; + ioatdca->req_slots[i].pdev = pdev; + ioatdca->req_slots[i].rid = id; + global_req_table = + readw(ioatdca->dca_base + IOAT3_DCA_GREQID_OFFSET); + writel(id | IOAT_DCA_GREQID_VALID, + ioatdca->iobase + global_req_table + (i * 4)); + return i; + } + } + /* Error, ioatdma->requester_count is out of whack */ + return -EFAULT; +} + +static int ioat3_dca_remove_requester(struct dca_provider *dca, + struct device *dev) +{ + struct ioat_dca_priv *ioatdca = dca_priv(dca); + struct pci_dev *pdev; + int i; + u16 global_req_table; + + /* This implementation only supports PCI-Express */ + if (dev->bus != &pci_bus_type) + return -ENODEV; + pdev = to_pci_dev(dev); + + for (i = 0; i < ioatdca->max_requesters; i++) { + if (ioatdca->req_slots[i].pdev == pdev) { + global_req_table = + readw(ioatdca->dca_base + IOAT3_DCA_GREQID_OFFSET); + writel(0, ioatdca->iobase + global_req_table + (i * 4)); + ioatdca->req_slots[i].pdev = NULL; + ioatdca->req_slots[i].rid = 0; + ioatdca->requester_count--; + return i; + } + } + return -ENODEV; +} + +static u8 ioat3_dca_get_tag(struct dca_provider *dca, + struct device *dev, + int cpu) +{ + u8 tag; + + struct ioat_dca_priv *ioatdca = dca_priv(dca); + int i, apic_id, bit, value; + u8 entry; + + tag = 0; + apic_id = cpu_physical_id(cpu); + + for (i = 0; i < IOAT_TAG_MAP_LEN; i++) { + entry = ioatdca->tag_map[i]; + if (entry & DCA3_TAG_MAP_BIT_TO_SEL) { + bit = entry & + ~(DCA3_TAG_MAP_BIT_TO_SEL | DCA3_TAG_MAP_BIT_TO_INV); + value = (apic_id & (1 << bit)) ? 1 : 0; + } else if (entry & DCA3_TAG_MAP_BIT_TO_INV) { + bit = entry & ~DCA3_TAG_MAP_BIT_TO_INV; + value = (apic_id & (1 << bit)) ? 0 : 1; + } else { + value = (entry & DCA3_TAG_MAP_LITERAL_VAL) ? 1 : 0; + } + tag |= (value << i); + } + + return tag; +} + +static struct dca_ops ioat3_dca_ops = { + .add_requester = ioat3_dca_add_requester, + .remove_requester = ioat3_dca_remove_requester, + .get_tag = ioat3_dca_get_tag, + .dev_managed = ioat_dca_dev_managed, +}; + +static int ioat3_dca_count_dca_slots(void *iobase, u16 dca_offset) +{ + int slots = 0; + u32 req; + u16 global_req_table; + + global_req_table = readw(iobase + dca_offset + IOAT3_DCA_GREQID_OFFSET); + if (global_req_table == 0) + return 0; + + do { + req = readl(iobase + global_req_table + (slots * sizeof(u32))); + slots++; + } while ((req & IOAT_DCA_GREQID_LASTID) == 0); + + return slots; +} + +struct dca_provider *ioat3_dca_init(struct pci_dev *pdev, void __iomem *iobase) +{ + struct dca_provider *dca; + struct ioat_dca_priv *ioatdca; + int slots; + int i; + int err; + u16 dca_offset; + u16 csi_fsb_control; + u16 pcie_control; + u8 bit; + + union { + u64 full; + struct { + u32 low; + u32 high; + }; + } tag_map; + + if (!system_has_dca_enabled(pdev)) + return NULL; + + dca_offset = readw(iobase + IOAT_DCAOFFSET_OFFSET); + if (dca_offset == 0) + return NULL; + + slots = ioat3_dca_count_dca_slots(iobase, dca_offset); + if (slots == 0) + return NULL; + + dca = alloc_dca_provider(&ioat3_dca_ops, + sizeof(*ioatdca) + + (sizeof(struct ioat_dca_slot) * slots)); + if (!dca) + return NULL; + + ioatdca = dca_priv(dca); + ioatdca->iobase = iobase; + ioatdca->dca_base = iobase + dca_offset; + ioatdca->max_requesters = slots; + + /* some bios might not know to turn these on */ + csi_fsb_control = readw(ioatdca->dca_base + IOAT3_CSI_CONTROL_OFFSET); + if ((csi_fsb_control & IOAT3_CSI_CONTROL_PREFETCH) == 0) { + csi_fsb_control |= IOAT3_CSI_CONTROL_PREFETCH; + writew(csi_fsb_control, + ioatdca->dca_base + IOAT3_CSI_CONTROL_OFFSET); + } + pcie_control = readw(ioatdca->dca_base + IOAT3_PCI_CONTROL_OFFSET); + if ((pcie_control & IOAT3_PCI_CONTROL_MEMWR) == 0) { + pcie_control |= IOAT3_PCI_CONTROL_MEMWR; + writew(pcie_control, + ioatdca->dca_base + IOAT3_PCI_CONTROL_OFFSET); + } + + + /* TODO version, compatibility and configuration checks */ + + /* copy out the APIC to DCA tag map */ + tag_map.low = + readl(ioatdca->dca_base + IOAT3_APICID_TAG_MAP_OFFSET_LOW); + tag_map.high = + readl(ioatdca->dca_base + IOAT3_APICID_TAG_MAP_OFFSET_HIGH); + for (i = 0; i < 8; i++) { + bit = tag_map.full >> (8 * i); + ioatdca->tag_map[i] = bit & DCA_TAG_MAP_MASK; + } + + err = register_dca_provider(dca, &pdev->dev); + if (err) { + free_dca_provider(dca); + return NULL; + } + + return dca; +} diff --git a/drivers/dma/ioat_dma.c b/drivers/dma/ioat_dma.c index 318e8a22d814..a52156e56886 100644 --- a/drivers/dma/ioat_dma.c +++ b/drivers/dma/ioat_dma.c @@ -32,6 +32,7 @@ #include <linux/dmaengine.h> #include <linux/delay.h> #include <linux/dma-mapping.h> +#include <linux/workqueue.h> #include "ioatdma.h" #include "ioatdma_registers.h" #include "ioatdma_hw.h" @@ -41,11 +42,23 @@ #define to_ioat_desc(lh) container_of(lh, struct ioat_desc_sw, node) #define tx_to_ioat_desc(tx) container_of(tx, struct ioat_desc_sw, async_tx) +#define chan_num(ch) ((int)((ch)->reg_base - (ch)->device->reg_base) / 0x80) static int ioat_pending_level = 4; module_param(ioat_pending_level, int, 0644); MODULE_PARM_DESC(ioat_pending_level, "high-water mark for pushing ioat descriptors (default: 4)"); +#define RESET_DELAY msecs_to_jiffies(100) +#define WATCHDOG_DELAY round_jiffies(msecs_to_jiffies(2000)) +static void ioat_dma_chan_reset_part2(struct work_struct *work); +static void ioat_dma_chan_watchdog(struct work_struct *work); + +/* + * workaround for IOAT ver.3.0 null descriptor issue + * (channel returns error when size is 0) + */ +#define NULL_DESC_BUFFER_SIZE 1 + /* internal functions */ static void ioat_dma_start_null_desc(struct ioat_dma_chan *ioat_chan); static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan); @@ -122,6 +135,38 @@ static int ioat_dma_enumerate_channels(struct ioatdma_device *device) int i; struct ioat_dma_chan *ioat_chan; + /* + * IOAT ver.3 workarounds + */ + if (device->version == IOAT_VER_3_0) { + u32 chan_err_mask; + u16 dev_id; + u32 dmauncerrsts; + + /* + * Write CHANERRMSK_INT with 3E07h to mask out the errors + * that can cause stability issues for IOAT ver.3 + */ + chan_err_mask = 0x3E07; + pci_write_config_dword(device->pdev, + IOAT_PCI_CHANERRMASK_INT_OFFSET, + chan_err_mask); + + /* + * Clear DMAUNCERRSTS Cfg-Reg Parity Error status bit + * (workaround for spurious config parity error after restart) + */ + pci_read_config_word(device->pdev, + IOAT_PCI_DEVICE_ID_OFFSET, + &dev_id); + if (dev_id == PCI_DEVICE_ID_INTEL_IOAT_TBG0) { + dmauncerrsts = 0x10; + pci_write_config_dword(device->pdev, + IOAT_PCI_DMAUNCERRSTS_OFFSET, + dmauncerrsts); + } + } + device->common.chancnt = readb(device->reg_base + IOAT_CHANCNT_OFFSET); xfercap_scale = readb(device->reg_base + IOAT_XFERCAP_OFFSET); xfercap = (xfercap_scale == 0 ? -1 : (1UL << xfercap_scale)); @@ -137,6 +182,7 @@ static int ioat_dma_enumerate_channels(struct ioatdma_device *device) ioat_chan->reg_base = device->reg_base + (0x80 * (i + 1)); ioat_chan->xfercap = xfercap; ioat_chan->desccount = 0; + INIT_DELAYED_WORK(&ioat_chan->work, ioat_dma_chan_reset_part2); if (ioat_chan->device->version != IOAT_VER_1_2) { writel(IOAT_DCACTRL_CMPL_WRITE_ENABLE | IOAT_DMA_DCA_ANY_CPU, @@ -175,7 +221,7 @@ static void ioat1_dma_memcpy_issue_pending(struct dma_chan *chan) { struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); - if (ioat_chan->pending != 0) { + if (ioat_chan->pending > 0) { spin_lock_bh(&ioat_chan->desc_lock); __ioat1_dma_memcpy_issue_pending(ioat_chan); spin_unlock_bh(&ioat_chan->desc_lock); @@ -194,13 +240,228 @@ static void ioat2_dma_memcpy_issue_pending(struct dma_chan *chan) { struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); - if (ioat_chan->pending != 0) { + if (ioat_chan->pending > 0) { spin_lock_bh(&ioat_chan->desc_lock); __ioat2_dma_memcpy_issue_pending(ioat_chan); spin_unlock_bh(&ioat_chan->desc_lock); } } + +/** + * ioat_dma_chan_reset_part2 - reinit the channel after a reset + */ +static void ioat_dma_chan_reset_part2(struct work_struct *work) +{ + struct ioat_dma_chan *ioat_chan = + container_of(work, struct ioat_dma_chan, work.work); + struct ioat_desc_sw *desc; + + spin_lock_bh(&ioat_chan->cleanup_lock); + spin_lock_bh(&ioat_chan->desc_lock); + + ioat_chan->completion_virt->low = 0; + ioat_chan->completion_virt->high = 0; + ioat_chan->pending = 0; + + /* + * count the descriptors waiting, and be sure to do it + * right for both the CB1 line and the CB2 ring + */ + ioat_chan->dmacount = 0; + if (ioat_chan->used_desc.prev) { + desc = to_ioat_desc(ioat_chan->used_desc.prev); + do { + ioat_chan->dmacount++; + desc = to_ioat_desc(desc->node.next); + } while (&desc->node != ioat_chan->used_desc.next); + } + + /* + * write the new starting descriptor address + * this puts channel engine into ARMED state + */ + desc = to_ioat_desc(ioat_chan->used_desc.prev); + switch (ioat_chan->device->version) { + case IOAT_VER_1_2: + writel(((u64) desc->async_tx.phys) & 0x00000000FFFFFFFF, + ioat_chan->reg_base + IOAT1_CHAINADDR_OFFSET_LOW); + writel(((u64) desc->async_tx.phys) >> 32, + ioat_chan->reg_base + IOAT1_CHAINADDR_OFFSET_HIGH); + + writeb(IOAT_CHANCMD_START, ioat_chan->reg_base + + IOAT_CHANCMD_OFFSET(ioat_chan->device->version)); + break; + case IOAT_VER_2_0: + writel(((u64) desc->async_tx.phys) & 0x00000000FFFFFFFF, + ioat_chan->reg_base + IOAT2_CHAINADDR_OFFSET_LOW); + writel(((u64) desc->async_tx.phys) >> 32, + ioat_chan->reg_base + IOAT2_CHAINADDR_OFFSET_HIGH); + + /* tell the engine to go with what's left to be done */ + writew(ioat_chan->dmacount, + ioat_chan->reg_base + IOAT_CHAN_DMACOUNT_OFFSET); + + break; + } + dev_err(&ioat_chan->device->pdev->dev, + "chan%d reset - %d descs waiting, %d total desc\n", + chan_num(ioat_chan), ioat_chan->dmacount, ioat_chan->desccount); + + spin_unlock_bh(&ioat_chan->desc_lock); + spin_unlock_bh(&ioat_chan->cleanup_lock); +} + +/** + * ioat_dma_reset_channel - restart a channel + * @ioat_chan: IOAT DMA channel handle + */ +static void ioat_dma_reset_channel(struct ioat_dma_chan *ioat_chan) +{ + u32 chansts, chanerr; + + if (!ioat_chan->used_desc.prev) + return; + + chanerr = readl(ioat_chan->reg_base + IOAT_CHANERR_OFFSET); + chansts = (ioat_chan->completion_virt->low + & IOAT_CHANSTS_DMA_TRANSFER_STATUS); + if (chanerr) { + dev_err(&ioat_chan->device->pdev->dev, + "chan%d, CHANSTS = 0x%08x CHANERR = 0x%04x, clearing\n", + chan_num(ioat_chan), chansts, chanerr); + writel(chanerr, ioat_chan->reg_base + IOAT_CHANERR_OFFSET); + } + + /* + * whack it upside the head with a reset + * and wait for things to settle out. + * force the pending count to a really big negative + * to make sure no one forces an issue_pending + * while we're waiting. + */ + + spin_lock_bh(&ioat_chan->desc_lock); + ioat_chan->pending = INT_MIN; + writeb(IOAT_CHANCMD_RESET, + ioat_chan->reg_base + + IOAT_CHANCMD_OFFSET(ioat_chan->device->version)); + spin_unlock_bh(&ioat_chan->desc_lock); + + /* schedule the 2nd half instead of sleeping a long time */ + schedule_delayed_work(&ioat_chan->work, RESET_DELAY); +} + +/** + * ioat_dma_chan_watchdog - watch for stuck channels + */ +static void ioat_dma_chan_watchdog(struct work_struct *work) +{ + struct ioatdma_device *device = + container_of(work, struct ioatdma_device, work.work); + struct ioat_dma_chan *ioat_chan; + int i; + + union { + u64 full; + struct { + u32 low; + u32 high; + }; + } completion_hw; + unsigned long compl_desc_addr_hw; + + for (i = 0; i < device->common.chancnt; i++) { + ioat_chan = ioat_lookup_chan_by_index(device, i); + + if (ioat_chan->device->version == IOAT_VER_1_2 + /* have we started processing anything yet */ + && ioat_chan->last_completion + /* have we completed any since last watchdog cycle? */ + && (ioat_chan->last_completion == + ioat_chan->watchdog_completion) + /* has TCP stuck on one cookie since last watchdog? */ + && (ioat_chan->watchdog_tcp_cookie == + ioat_chan->watchdog_last_tcp_cookie) + && (ioat_chan->watchdog_tcp_cookie != + ioat_chan->completed_cookie) + /* is there something in the chain to be processed? */ + /* CB1 chain always has at least the last one processed */ + && (ioat_chan->used_desc.prev != ioat_chan->used_desc.next) + && ioat_chan->pending == 0) { + + /* + * check CHANSTS register for completed + * descriptor address. + * if it is different than completion writeback, + * it is not zero + * and it has changed since the last watchdog + * we can assume that channel + * is still working correctly + * and the problem is in completion writeback. + * update completion writeback + * with actual CHANSTS value + * else + * try resetting the channel + */ + + completion_hw.low = readl(ioat_chan->reg_base + + IOAT_CHANSTS_OFFSET_LOW(ioat_chan->device->version)); + completion_hw.high = readl(ioat_chan->reg_base + + IOAT_CHANSTS_OFFSET_HIGH(ioat_chan->device->version)); +#if (BITS_PER_LONG == 64) + compl_desc_addr_hw = + completion_hw.full + & IOAT_CHANSTS_COMPLETED_DESCRIPTOR_ADDR; +#else + compl_desc_addr_hw = + completion_hw.low & IOAT_LOW_COMPLETION_MASK; +#endif + + if ((compl_desc_addr_hw != 0) + && (compl_desc_addr_hw != ioat_chan->watchdog_completion) + && (compl_desc_addr_hw != ioat_chan->last_compl_desc_addr_hw)) { + ioat_chan->last_compl_desc_addr_hw = compl_desc_addr_hw; + ioat_chan->completion_virt->low = completion_hw.low; + ioat_chan->completion_virt->high = completion_hw.high; + } else { + ioat_dma_reset_channel(ioat_chan); + ioat_chan->watchdog_completion = 0; + ioat_chan->last_compl_desc_addr_hw = 0; + } + + /* + * for version 2.0 if there are descriptors yet to be processed + * and the last completed hasn't changed since the last watchdog + * if they haven't hit the pending level + * issue the pending to push them through + * else + * try resetting the channel + */ + } else if (ioat_chan->device->version == IOAT_VER_2_0 + && ioat_chan->used_desc.prev + && ioat_chan->last_completion + && ioat_chan->last_completion == ioat_chan->watchdog_completion) { + + if (ioat_chan->pending < ioat_pending_level) + ioat2_dma_memcpy_issue_pending(&ioat_chan->common); + else { + ioat_dma_reset_channel(ioat_chan); + ioat_chan->watchdog_completion = 0; + } + } else { + ioat_chan->last_compl_desc_addr_hw = 0; + ioat_chan->watchdog_completion + = ioat_chan->last_completion; + } + + ioat_chan->watchdog_last_tcp_cookie = + ioat_chan->watchdog_tcp_cookie; + } + + schedule_delayed_work(&device->work, WATCHDOG_DELAY); +} + static dma_cookie_t ioat1_tx_submit(struct dma_async_tx_descriptor *tx) { struct ioat_dma_chan *ioat_chan = to_ioat_chan(tx->chan); @@ -250,6 +511,13 @@ static dma_cookie_t ioat1_tx_submit(struct dma_async_tx_descriptor *tx) prev = new; } while (len && (new = ioat1_dma_get_next_descriptor(ioat_chan))); + if (!new) { + dev_err(&ioat_chan->device->pdev->dev, + "tx submit failed\n"); + spin_unlock_bh(&ioat_chan->desc_lock); + return -ENOMEM; + } + hw->ctl = IOAT_DMA_DESCRIPTOR_CTL_CP_STS; if (new->async_tx.callback) { hw->ctl |= IOAT_DMA_DESCRIPTOR_CTL_INT_GN; @@ -335,7 +603,14 @@ static dma_cookie_t ioat2_tx_submit(struct dma_async_tx_descriptor *tx) desc_count++; } while (len && (new = ioat2_dma_get_next_descriptor(ioat_chan))); - hw->ctl = IOAT_DMA_DESCRIPTOR_CTL_CP_STS; + if (!new) { + dev_err(&ioat_chan->device->pdev->dev, + "tx submit failed\n"); + spin_unlock_bh(&ioat_chan->desc_lock); + return -ENOMEM; + } + + hw->ctl |= IOAT_DMA_DESCRIPTOR_CTL_CP_STS; if (new->async_tx.callback) { hw->ctl |= IOAT_DMA_DESCRIPTOR_CTL_INT_GN; if (first != new) { @@ -406,6 +681,7 @@ static struct ioat_desc_sw *ioat_dma_alloc_descriptor( desc_sw->async_tx.tx_submit = ioat1_tx_submit; break; case IOAT_VER_2_0: + case IOAT_VER_3_0: desc_sw->async_tx.tx_submit = ioat2_tx_submit; break; } @@ -452,7 +728,8 @@ static void ioat2_dma_massage_chan_desc(struct ioat_dma_chan *ioat_chan) * ioat_dma_alloc_chan_resources - returns the number of allocated descriptors * @chan: the channel to be filled out */ -static int ioat_dma_alloc_chan_resources(struct dma_chan *chan) +static int ioat_dma_alloc_chan_resources(struct dma_chan *chan, + struct dma_client *client) { struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); struct ioat_desc_sw *desc; @@ -555,6 +832,7 @@ static void ioat_dma_free_chan_resources(struct dma_chan *chan) } break; case IOAT_VER_2_0: + case IOAT_VER_3_0: list_for_each_entry_safe(desc, _desc, ioat_chan->free_desc.next, node) { list_del(&desc->node); @@ -585,6 +863,10 @@ static void ioat_dma_free_chan_resources(struct dma_chan *chan) ioat_chan->last_completion = ioat_chan->completion_addr = 0; ioat_chan->pending = 0; ioat_chan->dmacount = 0; + ioat_chan->watchdog_completion = 0; + ioat_chan->last_compl_desc_addr_hw = 0; + ioat_chan->watchdog_tcp_cookie = + ioat_chan->watchdog_last_tcp_cookie = 0; } /** @@ -640,7 +922,8 @@ ioat2_dma_get_next_descriptor(struct ioat_dma_chan *ioat_chan) /* set up the noop descriptor */ noop_desc = to_ioat_desc(ioat_chan->used_desc.next); - noop_desc->hw->size = 0; + /* set size to non-zero value (channel returns error when size is 0) */ + noop_desc->hw->size = NULL_DESC_BUFFER_SIZE; noop_desc->hw->ctl = IOAT_DMA_DESCRIPTOR_NUL; noop_desc->hw->src_addr = 0; noop_desc->hw->dst_addr = 0; @@ -690,6 +973,7 @@ static struct ioat_desc_sw *ioat_dma_get_next_descriptor( return ioat1_dma_get_next_descriptor(ioat_chan); break; case IOAT_VER_2_0: + case IOAT_VER_3_0: return ioat2_dma_get_next_descriptor(ioat_chan); break; } @@ -716,8 +1000,12 @@ static struct dma_async_tx_descriptor *ioat1_dma_prep_memcpy( new->src = dma_src; new->async_tx.flags = flags; return &new->async_tx; - } else + } else { + dev_err(&ioat_chan->device->pdev->dev, + "chan%d - get_next_desc failed: %d descs waiting, %d total desc\n", + chan_num(ioat_chan), ioat_chan->dmacount, ioat_chan->desccount); return NULL; + } } static struct dma_async_tx_descriptor *ioat2_dma_prep_memcpy( @@ -744,8 +1032,13 @@ static struct dma_async_tx_descriptor *ioat2_dma_prep_memcpy( new->src = dma_src; new->async_tx.flags = flags; return &new->async_tx; - } else + } else { + spin_unlock_bh(&ioat_chan->desc_lock); + dev_err(&ioat_chan->device->pdev->dev, + "chan%d - get_next_desc failed: %d descs waiting, %d total desc\n", + chan_num(ioat_chan), ioat_chan->dmacount, ioat_chan->desccount); return NULL; + } } static void ioat_dma_cleanup_tasklet(unsigned long data) @@ -756,6 +1049,27 @@ static void ioat_dma_cleanup_tasklet(unsigned long data) chan->reg_base + IOAT_CHANCTRL_OFFSET); } +static void +ioat_dma_unmap(struct ioat_dma_chan *ioat_chan, struct ioat_desc_sw *desc) +{ + /* + * yes we are unmapping both _page and _single + * alloc'd regions with unmap_page. Is this + * *really* that bad? + */ + if (!(desc->async_tx.flags & DMA_COMPL_SKIP_DEST_UNMAP)) + pci_unmap_page(ioat_chan->device->pdev, + pci_unmap_addr(desc, dst), + pci_unmap_len(desc, len), + PCI_DMA_FROMDEVICE); + + if (!(desc->async_tx.flags & DMA_COMPL_SKIP_SRC_UNMAP)) + pci_unmap_page(ioat_chan->device->pdev, + pci_unmap_addr(desc, src), + pci_unmap_len(desc, len), + PCI_DMA_TODEVICE); +} + /** * ioat_dma_memcpy_cleanup - cleanup up finished descriptors * @chan: ioat channel to be cleaned up @@ -799,11 +1113,27 @@ static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan) if (phys_complete == ioat_chan->last_completion) { spin_unlock_bh(&ioat_chan->cleanup_lock); + /* + * perhaps we're stuck so hard that the watchdog can't go off? + * try to catch it after 2 seconds + */ + if (ioat_chan->device->version != IOAT_VER_3_0) { + if (time_after(jiffies, + ioat_chan->last_completion_time + HZ*WATCHDOG_DELAY)) { + ioat_dma_chan_watchdog(&(ioat_chan->device->work.work)); + ioat_chan->last_completion_time = jiffies; + } + } return; } + ioat_chan->last_completion_time = jiffies; cookie = 0; - spin_lock_bh(&ioat_chan->desc_lock); + if (!spin_trylock_bh(&ioat_chan->desc_lock)) { + spin_unlock_bh(&ioat_chan->cleanup_lock); + return; + } + switch (ioat_chan->device->version) { case IOAT_VER_1_2: list_for_each_entry_safe(desc, _desc, @@ -816,21 +1146,7 @@ static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan) */ if (desc->async_tx.cookie) { cookie = desc->async_tx.cookie; - - /* - * yes we are unmapping both _page and _single - * alloc'd regions with unmap_page. Is this - * *really* that bad? - */ - pci_unmap_page(ioat_chan->device->pdev, - pci_unmap_addr(desc, dst), - pci_unmap_len(desc, len), - PCI_DMA_FROMDEVICE); - pci_unmap_page(ioat_chan->device->pdev, - pci_unmap_addr(desc, src), - pci_unmap_len(desc, len), - PCI_DMA_TODEVICE); - + ioat_dma_unmap(ioat_chan, desc); if (desc->async_tx.callback) { desc->async_tx.callback(desc->async_tx.callback_param); desc->async_tx.callback = NULL; @@ -862,6 +1178,7 @@ static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan) } break; case IOAT_VER_2_0: + case IOAT_VER_3_0: /* has some other thread has already cleaned up? */ if (ioat_chan->used_desc.prev == NULL) break; @@ -889,16 +1206,7 @@ static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan) if (desc->async_tx.cookie) { cookie = desc->async_tx.cookie; desc->async_tx.cookie = 0; - - pci_unmap_page(ioat_chan->device->pdev, - pci_unmap_addr(desc, dst), - pci_unmap_len(desc, len), - PCI_DMA_FROMDEVICE); - pci_unmap_page(ioat_chan->device->pdev, - pci_unmap_addr(desc, src), - pci_unmap_len(desc, len), - PCI_DMA_TODEVICE); - + ioat_dma_unmap(ioat_chan, desc); if (desc->async_tx.callback) { desc->async_tx.callback(desc->async_tx.callback_param); desc->async_tx.callback = NULL; @@ -943,6 +1251,7 @@ static enum dma_status ioat_dma_is_complete(struct dma_chan *chan, last_used = chan->cookie; last_complete = ioat_chan->completed_cookie; + ioat_chan->watchdog_tcp_cookie = cookie; if (done) *done = last_complete; @@ -973,10 +1282,19 @@ static void ioat_dma_start_null_desc(struct ioat_dma_chan *ioat_chan) spin_lock_bh(&ioat_chan->desc_lock); desc = ioat_dma_get_next_descriptor(ioat_chan); + + if (!desc) { + dev_err(&ioat_chan->device->pdev->dev, + "Unable to start null desc - get next desc failed\n"); + spin_unlock_bh(&ioat_chan->desc_lock); + return; + } + desc->hw->ctl = IOAT_DMA_DESCRIPTOR_NUL | IOAT_DMA_DESCRIPTOR_CTL_INT_GN | IOAT_DMA_DESCRIPTOR_CTL_CP_STS; - desc->hw->size = 0; + /* set size to non-zero value (channel returns error when size is 0) */ + desc->hw->size = NULL_DESC_BUFFER_SIZE; desc->hw->src_addr = 0; desc->hw->dst_addr = 0; async_tx_ack(&desc->async_tx); @@ -994,6 +1312,7 @@ static void ioat_dma_start_null_desc(struct ioat_dma_chan *ioat_chan) + IOAT_CHANCMD_OFFSET(ioat_chan->device->version)); break; case IOAT_VER_2_0: + case IOAT_VER_3_0: writel(((u64) desc->async_tx.phys) & 0x00000000FFFFFFFF, ioat_chan->reg_base + IOAT2_CHAINADDR_OFFSET_LOW); writel(((u64) desc->async_tx.phys) >> 32, @@ -1049,7 +1368,7 @@ static int ioat_dma_self_test(struct ioatdma_device *device) dma_chan = container_of(device->common.channels.next, struct dma_chan, device_node); - if (device->common.device_alloc_chan_resources(dma_chan) < 1) { + if (device->common.device_alloc_chan_resources(dma_chan, NULL) < 1) { dev_err(&device->pdev->dev, "selftest cannot allocate chan resource\n"); err = -ENODEV; @@ -1312,6 +1631,7 @@ struct ioatdma_device *ioat_dma_probe(struct pci_dev *pdev, ioat1_dma_memcpy_issue_pending; break; case IOAT_VER_2_0: + case IOAT_VER_3_0: device->common.device_prep_dma_memcpy = ioat2_dma_prep_memcpy; device->common.device_issue_pending = ioat2_dma_memcpy_issue_pending; @@ -1331,8 +1651,16 @@ struct ioatdma_device *ioat_dma_probe(struct pci_dev *pdev, if (err) goto err_self_test; + ioat_set_tcp_copy_break(device); + dma_async_device_register(&device->common); + if (device->version != IOAT_VER_3_0) { + INIT_DELAYED_WORK(&device->work, ioat_dma_chan_watchdog); + schedule_delayed_work(&device->work, + WATCHDOG_DELAY); + } + return device; err_self_test: @@ -1365,6 +1693,10 @@ void ioat_dma_remove(struct ioatdma_device *device) pci_release_regions(device->pdev); pci_disable_device(device->pdev); + if (device->version != IOAT_VER_3_0) { + cancel_delayed_work(&device->work); + } + list_for_each_entry_safe(chan, _chan, &device->common.channels, device_node) { ioat_chan = to_ioat_chan(chan); diff --git a/drivers/dma/ioatdma.h b/drivers/dma/ioatdma.h index f2c7fedbf009..a3306d0e1372 100644 --- a/drivers/dma/ioatdma.h +++ b/drivers/dma/ioatdma.h @@ -27,8 +27,9 @@ #include <linux/dmapool.h> #include <linux/cache.h> #include <linux/pci_ids.h> +#include <net/tcp.h> -#define IOAT_DMA_VERSION "2.04" +#define IOAT_DMA_VERSION "3.30" enum ioat_interrupt { none = 0, @@ -40,6 +41,7 @@ enum ioat_interrupt { #define IOAT_LOW_COMPLETION_MASK 0xffffffc0 #define IOAT_DMA_DCA_ANY_CPU ~0 +#define IOAT_WATCHDOG_PERIOD (2 * HZ) /** @@ -62,6 +64,7 @@ struct ioatdma_device { struct dma_device common; u8 version; enum ioat_interrupt irq_mode; + struct delayed_work work; struct msix_entry msix_entries[4]; struct ioat_dma_chan *idx[4]; }; @@ -75,6 +78,7 @@ struct ioat_dma_chan { dma_cookie_t completed_cookie; unsigned long last_completion; + unsigned long last_completion_time; size_t xfercap; /* XFERCAP register value expanded out */ @@ -82,6 +86,10 @@ struct ioat_dma_chan { spinlock_t desc_lock; struct list_head free_desc; struct list_head used_desc; + unsigned long watchdog_completion; + int watchdog_tcp_cookie; + u32 watchdog_last_tcp_cookie; + struct delayed_work work; int pending; int dmacount; @@ -98,6 +106,7 @@ struct ioat_dma_chan { u32 high; }; } *completion_virt; + unsigned long last_compl_desc_addr_hw; struct tasklet_struct cleanup_task; }; @@ -121,17 +130,34 @@ struct ioat_desc_sw { struct dma_async_tx_descriptor async_tx; }; +static inline void ioat_set_tcp_copy_break(struct ioatdma_device *dev) +{ + #ifdef CONFIG_NET_DMA + switch (dev->version) { + case IOAT_VER_1_2: + case IOAT_VER_3_0: + sysctl_tcp_dma_copybreak = 4096; + break; + case IOAT_VER_2_0: + sysctl_tcp_dma_copybreak = 2048; + break; + } + #endif +} + #if defined(CONFIG_INTEL_IOATDMA) || defined(CONFIG_INTEL_IOATDMA_MODULE) struct ioatdma_device *ioat_dma_probe(struct pci_dev *pdev, void __iomem *iobase); void ioat_dma_remove(struct ioatdma_device *device); struct dca_provider *ioat_dca_init(struct pci_dev *pdev, void __iomem *iobase); struct dca_provider *ioat2_dca_init(struct pci_dev *pdev, void __iomem *iobase); +struct dca_provider *ioat3_dca_init(struct pci_dev *pdev, void __iomem *iobase); #else #define ioat_dma_probe(pdev, iobase) NULL #define ioat_dma_remove(device) do { } while (0) #define ioat_dca_init(pdev, iobase) NULL #define ioat2_dca_init(pdev, iobase) NULL +#define ioat3_dca_init(pdev, iobase) NULL #endif #endif /* IOATDMA_H */ diff --git a/drivers/dma/ioatdma_hw.h b/drivers/dma/ioatdma_hw.h index dd470fa91d86..f1ae2c776f74 100644 --- a/drivers/dma/ioatdma_hw.h +++ b/drivers/dma/ioatdma_hw.h @@ -35,6 +35,7 @@ #define IOAT_PCI_SID 0x8086 #define IOAT_VER_1_2 0x12 /* Version 1.2 */ #define IOAT_VER_2_0 0x20 /* Version 2.0 */ +#define IOAT_VER_3_0 0x30 /* Version 3.0 */ struct ioat_dma_descriptor { uint32_t size; diff --git a/drivers/dma/ioatdma_registers.h b/drivers/dma/ioatdma_registers.h index 9832d7ebd931..827cb503cac6 100644 --- a/drivers/dma/ioatdma_registers.h +++ b/drivers/dma/ioatdma_registers.h @@ -25,6 +25,10 @@ #define IOAT_PCI_DMACTRL_DMA_EN 0x00000001 #define IOAT_PCI_DMACTRL_MSI_EN 0x00000002 +#define IOAT_PCI_DEVICE_ID_OFFSET 0x02 +#define IOAT_PCI_DMAUNCERRSTS_OFFSET 0x148 +#define IOAT_PCI_CHANERRMASK_INT_OFFSET 0x184 + /* MMIO Device Registers */ #define IOAT_CHANCNT_OFFSET 0x00 /* 8-bit */ @@ -149,7 +153,23 @@ #define IOAT_DCA_GREQID_VALID 0x20000000 #define IOAT_DCA_GREQID_LASTID 0x80000000 +#define IOAT3_CSI_CAPABILITY_OFFSET 0x08 +#define IOAT3_CSI_CAPABILITY_PREFETCH 0x1 + +#define IOAT3_PCI_CAPABILITY_OFFSET 0x0A +#define IOAT3_PCI_CAPABILITY_MEMWR 0x1 + +#define IOAT3_CSI_CONTROL_OFFSET 0x0C +#define IOAT3_CSI_CONTROL_PREFETCH 0x1 + +#define IOAT3_PCI_CONTROL_OFFSET 0x0E +#define IOAT3_PCI_CONTROL_MEMWR 0x1 + +#define IOAT3_APICID_TAG_MAP_OFFSET 0x10 +#define IOAT3_APICID_TAG_MAP_OFFSET_LOW 0x10 +#define IOAT3_APICID_TAG_MAP_OFFSET_HIGH 0x14 +#define IOAT3_DCA_GREQID_OFFSET 0x02 #define IOAT1_CHAINADDR_OFFSET 0x0C /* 64-bit Descriptor Chain Address Register */ #define IOAT2_CHAINADDR_OFFSET 0x10 /* 64-bit Descriptor Chain Address Register */ diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 0ec0f431e6a1..85bfeba4d85e 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -82,17 +82,24 @@ iop_adma_run_tx_complete_actions(struct iop_adma_desc_slot *desc, struct device *dev = &iop_chan->device->pdev->dev; u32 len = unmap->unmap_len; - u32 src_cnt = unmap->unmap_src_cnt; - dma_addr_t addr = iop_desc_get_dest_addr(unmap, - iop_chan); - - dma_unmap_page(dev, addr, len, DMA_FROM_DEVICE); - while (src_cnt--) { - addr = iop_desc_get_src_addr(unmap, - iop_chan, - src_cnt); - dma_unmap_page(dev, addr, len, - DMA_TO_DEVICE); + enum dma_ctrl_flags flags = desc->async_tx.flags; + u32 src_cnt; + dma_addr_t addr; + + if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) { + addr = iop_desc_get_dest_addr(unmap, iop_chan); + dma_unmap_page(dev, addr, len, DMA_FROM_DEVICE); + } + + if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) { + src_cnt = unmap->unmap_src_cnt; + while (src_cnt--) { + addr = iop_desc_get_src_addr(unmap, + iop_chan, + src_cnt); + dma_unmap_page(dev, addr, len, + DMA_TO_DEVICE); + } } desc->group_head = NULL; } @@ -366,8 +373,8 @@ retry: if (!retry++) goto retry; - /* try to free some slots if the allocation fails */ - tasklet_schedule(&iop_chan->irq_tasklet); + /* perform direct reclaim if the allocation fails */ + __iop_adma_slot_cleanup(iop_chan); return NULL; } @@ -443,8 +450,18 @@ iop_adma_tx_submit(struct dma_async_tx_descriptor *tx) static void iop_chan_start_null_memcpy(struct iop_adma_chan *iop_chan); static void iop_chan_start_null_xor(struct iop_adma_chan *iop_chan); -/* returns the number of allocated descriptors */ -static int iop_adma_alloc_chan_resources(struct dma_chan *chan) +/** + * iop_adma_alloc_chan_resources - returns the number of allocated descriptors + * @chan - allocate descriptor resources for this channel + * @client - current client requesting the channel be ready for requests + * + * Note: We keep the slots for 1 operation on iop_chan->chain at all times. To + * avoid deadlock, via async_xor, num_descs_in_pool must at a minimum be + * greater than 2x the number slots needed to satisfy a device->max_xor + * request. + * */ +static int iop_adma_alloc_chan_resources(struct dma_chan *chan, + struct dma_client *client) { char *hw_desc; int idx; @@ -838,7 +855,7 @@ static int __devinit iop_adma_memcpy_self_test(struct iop_adma_device *device) dma_chan = container_of(device->common.channels.next, struct dma_chan, device_node); - if (iop_adma_alloc_chan_resources(dma_chan) < 1) { + if (iop_adma_alloc_chan_resources(dma_chan, NULL) < 1) { err = -ENODEV; goto out; } @@ -936,7 +953,7 @@ iop_adma_xor_zero_sum_self_test(struct iop_adma_device *device) dma_chan = container_of(device->common.channels.next, struct dma_chan, device_node); - if (iop_adma_alloc_chan_resources(dma_chan) < 1) { + if (iop_adma_alloc_chan_resources(dma_chan, NULL) < 1) { err = -ENODEV; goto out; } @@ -1387,6 +1404,8 @@ static void iop_chan_start_null_xor(struct iop_adma_chan *iop_chan) spin_unlock_bh(&iop_chan->lock); } +MODULE_ALIAS("platform:iop-adma"); + static struct platform_driver iop_adma_driver = { .probe = iop_adma_probe, .remove = iop_adma_remove, diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c new file mode 100644 index 000000000000..a4e4494663bf --- /dev/null +++ b/drivers/dma/mv_xor.c @@ -0,0 +1,1375 @@ +/* + * offload engine driver for the Marvell XOR engine + * Copyright (C) 2007, 2008, Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/async_tx.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/memory.h> +#include <asm/plat-orion/mv_xor.h> +#include "mv_xor.h" + +static void mv_xor_issue_pending(struct dma_chan *chan); + +#define to_mv_xor_chan(chan) \ + container_of(chan, struct mv_xor_chan, common) + +#define to_mv_xor_device(dev) \ + container_of(dev, struct mv_xor_device, common) + +#define to_mv_xor_slot(tx) \ + container_of(tx, struct mv_xor_desc_slot, async_tx) + +static void mv_desc_init(struct mv_xor_desc_slot *desc, unsigned long flags) +{ + struct mv_xor_desc *hw_desc = desc->hw_desc; + + hw_desc->status = (1 << 31); + hw_desc->phy_next_desc = 0; + hw_desc->desc_command = (1 << 31); +} + +static u32 mv_desc_get_dest_addr(struct mv_xor_desc_slot *desc) +{ + struct mv_xor_desc *hw_desc = desc->hw_desc; + return hw_desc->phy_dest_addr; +} + +static u32 mv_desc_get_src_addr(struct mv_xor_desc_slot *desc, + int src_idx) +{ + struct mv_xor_desc *hw_desc = desc->hw_desc; + return hw_desc->phy_src_addr[src_idx]; +} + + +static void mv_desc_set_byte_count(struct mv_xor_desc_slot *desc, + u32 byte_count) +{ + struct mv_xor_desc *hw_desc = desc->hw_desc; + hw_desc->byte_count = byte_count; +} + +static void mv_desc_set_next_desc(struct mv_xor_desc_slot *desc, + u32 next_desc_addr) +{ + struct mv_xor_desc *hw_desc = desc->hw_desc; + BUG_ON(hw_desc->phy_next_desc); + hw_desc->phy_next_desc = next_desc_addr; +} + +static void mv_desc_clear_next_desc(struct mv_xor_desc_slot *desc) +{ + struct mv_xor_desc *hw_desc = desc->hw_desc; + hw_desc->phy_next_desc = 0; +} + +static void mv_desc_set_block_fill_val(struct mv_xor_desc_slot *desc, u32 val) +{ + desc->value = val; +} + +static void mv_desc_set_dest_addr(struct mv_xor_desc_slot *desc, + dma_addr_t addr) +{ + struct mv_xor_desc *hw_desc = desc->hw_desc; + hw_desc->phy_dest_addr = addr; +} + +static int mv_chan_memset_slot_count(size_t len) +{ + return 1; +} + +#define mv_chan_memcpy_slot_count(c) mv_chan_memset_slot_count(c) + +static void mv_desc_set_src_addr(struct mv_xor_desc_slot *desc, + int index, dma_addr_t addr) +{ + struct mv_xor_desc *hw_desc = desc->hw_desc; + hw_desc->phy_src_addr[index] = addr; + if (desc->type == DMA_XOR) + hw_desc->desc_command |= (1 << index); +} + +static u32 mv_chan_get_current_desc(struct mv_xor_chan *chan) +{ + return __raw_readl(XOR_CURR_DESC(chan)); +} + +static void mv_chan_set_next_descriptor(struct mv_xor_chan *chan, + u32 next_desc_addr) +{ + __raw_writel(next_desc_addr, XOR_NEXT_DESC(chan)); +} + +static void mv_chan_set_dest_pointer(struct mv_xor_chan *chan, u32 desc_addr) +{ + __raw_writel(desc_addr, XOR_DEST_POINTER(chan)); +} + +static void mv_chan_set_block_size(struct mv_xor_chan *chan, u32 block_size) +{ + __raw_writel(block_size, XOR_BLOCK_SIZE(chan)); +} + +static void mv_chan_set_value(struct mv_xor_chan *chan, u32 value) +{ + __raw_writel(value, XOR_INIT_VALUE_LOW(chan)); + __raw_writel(value, XOR_INIT_VALUE_HIGH(chan)); +} + +static void mv_chan_unmask_interrupts(struct mv_xor_chan *chan) +{ + u32 val = __raw_readl(XOR_INTR_MASK(chan)); + val |= XOR_INTR_MASK_VALUE << (chan->idx * 16); + __raw_writel(val, XOR_INTR_MASK(chan)); +} + +static u32 mv_chan_get_intr_cause(struct mv_xor_chan *chan) +{ + u32 intr_cause = __raw_readl(XOR_INTR_CAUSE(chan)); + intr_cause = (intr_cause >> (chan->idx * 16)) & 0xFFFF; + return intr_cause; +} + +static int mv_is_err_intr(u32 intr_cause) +{ + if (intr_cause & ((1<<4)|(1<<5)|(1<<6)|(1<<7)|(1<<8)|(1<<9))) + return 1; + + return 0; +} + +static void mv_xor_device_clear_eoc_cause(struct mv_xor_chan *chan) +{ + u32 val = (1 << (1 + (chan->idx * 16))); + dev_dbg(chan->device->common.dev, "%s, val 0x%08x\n", __func__, val); + __raw_writel(val, XOR_INTR_CAUSE(chan)); +} + +static void mv_xor_device_clear_err_status(struct mv_xor_chan *chan) +{ + u32 val = 0xFFFF0000 >> (chan->idx * 16); + __raw_writel(val, XOR_INTR_CAUSE(chan)); +} + +static int mv_can_chain(struct mv_xor_desc_slot *desc) +{ + struct mv_xor_desc_slot *chain_old_tail = list_entry( + desc->chain_node.prev, struct mv_xor_desc_slot, chain_node); + + if (chain_old_tail->type != desc->type) + return 0; + if (desc->type == DMA_MEMSET) + return 0; + + return 1; +} + +static void mv_set_mode(struct mv_xor_chan *chan, + enum dma_transaction_type type) +{ + u32 op_mode; + u32 config = __raw_readl(XOR_CONFIG(chan)); + + switch (type) { + case DMA_XOR: + op_mode = XOR_OPERATION_MODE_XOR; + break; + case DMA_MEMCPY: + op_mode = XOR_OPERATION_MODE_MEMCPY; + break; + case DMA_MEMSET: + op_mode = XOR_OPERATION_MODE_MEMSET; + break; + default: + dev_printk(KERN_ERR, chan->device->common.dev, + "error: unsupported operation %d.\n", + type); + BUG(); + return; + } + + config &= ~0x7; + config |= op_mode; + __raw_writel(config, XOR_CONFIG(chan)); + chan->current_type = type; +} + +static void mv_chan_activate(struct mv_xor_chan *chan) +{ + u32 activation; + + dev_dbg(chan->device->common.dev, " activate chan.\n"); + activation = __raw_readl(XOR_ACTIVATION(chan)); + activation |= 0x1; + __raw_writel(activation, XOR_ACTIVATION(chan)); +} + +static char mv_chan_is_busy(struct mv_xor_chan *chan) +{ + u32 state = __raw_readl(XOR_ACTIVATION(chan)); + + state = (state >> 4) & 0x3; + + return (state == 1) ? 1 : 0; +} + +static int mv_chan_xor_slot_count(size_t len, int src_cnt) +{ + return 1; +} + +/** + * mv_xor_free_slots - flags descriptor slots for reuse + * @slot: Slot to free + * Caller must hold &mv_chan->lock while calling this function + */ +static void mv_xor_free_slots(struct mv_xor_chan *mv_chan, + struct mv_xor_desc_slot *slot) +{ + dev_dbg(mv_chan->device->common.dev, "%s %d slot %p\n", + __func__, __LINE__, slot); + + slot->slots_per_op = 0; + +} + +/* + * mv_xor_start_new_chain - program the engine to operate on new chain headed by + * sw_desc + * Caller must hold &mv_chan->lock while calling this function + */ +static void mv_xor_start_new_chain(struct mv_xor_chan *mv_chan, + struct mv_xor_desc_slot *sw_desc) +{ + dev_dbg(mv_chan->device->common.dev, "%s %d: sw_desc %p\n", + __func__, __LINE__, sw_desc); + if (sw_desc->type != mv_chan->current_type) + mv_set_mode(mv_chan, sw_desc->type); + + if (sw_desc->type == DMA_MEMSET) { + /* for memset requests we need to program the engine, no + * descriptors used. + */ + struct mv_xor_desc *hw_desc = sw_desc->hw_desc; + mv_chan_set_dest_pointer(mv_chan, hw_desc->phy_dest_addr); + mv_chan_set_block_size(mv_chan, sw_desc->unmap_len); + mv_chan_set_value(mv_chan, sw_desc->value); + } else { + /* set the hardware chain */ + mv_chan_set_next_descriptor(mv_chan, sw_desc->async_tx.phys); + } + mv_chan->pending += sw_desc->slot_cnt; + mv_xor_issue_pending(&mv_chan->common); +} + +static dma_cookie_t +mv_xor_run_tx_complete_actions(struct mv_xor_desc_slot *desc, + struct mv_xor_chan *mv_chan, dma_cookie_t cookie) +{ + BUG_ON(desc->async_tx.cookie < 0); + + if (desc->async_tx.cookie > 0) { + cookie = desc->async_tx.cookie; + + /* call the callback (must not sleep or submit new + * operations to this channel) + */ + if (desc->async_tx.callback) + desc->async_tx.callback( + desc->async_tx.callback_param); + + /* unmap dma addresses + * (unmap_single vs unmap_page?) + */ + if (desc->group_head && desc->unmap_len) { + struct mv_xor_desc_slot *unmap = desc->group_head; + struct device *dev = + &mv_chan->device->pdev->dev; + u32 len = unmap->unmap_len; + enum dma_ctrl_flags flags = desc->async_tx.flags; + u32 src_cnt; + dma_addr_t addr; + + if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) { + addr = mv_desc_get_dest_addr(unmap); + dma_unmap_page(dev, addr, len, DMA_FROM_DEVICE); + } + + if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) { + src_cnt = unmap->unmap_src_cnt; + while (src_cnt--) { + addr = mv_desc_get_src_addr(unmap, + src_cnt); + dma_unmap_page(dev, addr, len, + DMA_TO_DEVICE); + } + } + desc->group_head = NULL; + } + } + + /* run dependent operations */ + async_tx_run_dependencies(&desc->async_tx); + + return cookie; +} + +static int +mv_xor_clean_completed_slots(struct mv_xor_chan *mv_chan) +{ + struct mv_xor_desc_slot *iter, *_iter; + + dev_dbg(mv_chan->device->common.dev, "%s %d\n", __func__, __LINE__); + list_for_each_entry_safe(iter, _iter, &mv_chan->completed_slots, + completed_node) { + + if (async_tx_test_ack(&iter->async_tx)) { + list_del(&iter->completed_node); + mv_xor_free_slots(mv_chan, iter); + } + } + return 0; +} + +static int +mv_xor_clean_slot(struct mv_xor_desc_slot *desc, + struct mv_xor_chan *mv_chan) +{ + dev_dbg(mv_chan->device->common.dev, "%s %d: desc %p flags %d\n", + __func__, __LINE__, desc, desc->async_tx.flags); + list_del(&desc->chain_node); + /* the client is allowed to attach dependent operations + * until 'ack' is set + */ + if (!async_tx_test_ack(&desc->async_tx)) { + /* move this slot to the completed_slots */ + list_add_tail(&desc->completed_node, &mv_chan->completed_slots); + return 0; + } + + mv_xor_free_slots(mv_chan, desc); + return 0; +} + +static void __mv_xor_slot_cleanup(struct mv_xor_chan *mv_chan) +{ + struct mv_xor_desc_slot *iter, *_iter; + dma_cookie_t cookie = 0; + int busy = mv_chan_is_busy(mv_chan); + u32 current_desc = mv_chan_get_current_desc(mv_chan); + int seen_current = 0; + + dev_dbg(mv_chan->device->common.dev, "%s %d\n", __func__, __LINE__); + dev_dbg(mv_chan->device->common.dev, "current_desc %x\n", current_desc); + mv_xor_clean_completed_slots(mv_chan); + + /* free completed slots from the chain starting with + * the oldest descriptor + */ + + list_for_each_entry_safe(iter, _iter, &mv_chan->chain, + chain_node) { + prefetch(_iter); + prefetch(&_iter->async_tx); + + /* do not advance past the current descriptor loaded into the + * hardware channel, subsequent descriptors are either in + * process or have not been submitted + */ + if (seen_current) + break; + + /* stop the search if we reach the current descriptor and the + * channel is busy + */ + if (iter->async_tx.phys == current_desc) { + seen_current = 1; + if (busy) + break; + } + + cookie = mv_xor_run_tx_complete_actions(iter, mv_chan, cookie); + + if (mv_xor_clean_slot(iter, mv_chan)) + break; + } + + if ((busy == 0) && !list_empty(&mv_chan->chain)) { + struct mv_xor_desc_slot *chain_head; + chain_head = list_entry(mv_chan->chain.next, + struct mv_xor_desc_slot, + chain_node); + + mv_xor_start_new_chain(mv_chan, chain_head); + } + + if (cookie > 0) + mv_chan->completed_cookie = cookie; +} + +static void +mv_xor_slot_cleanup(struct mv_xor_chan *mv_chan) +{ + spin_lock_bh(&mv_chan->lock); + __mv_xor_slot_cleanup(mv_chan); + spin_unlock_bh(&mv_chan->lock); +} + +static void mv_xor_tasklet(unsigned long data) +{ + struct mv_xor_chan *chan = (struct mv_xor_chan *) data; + __mv_xor_slot_cleanup(chan); +} + +static struct mv_xor_desc_slot * +mv_xor_alloc_slots(struct mv_xor_chan *mv_chan, int num_slots, + int slots_per_op) +{ + struct mv_xor_desc_slot *iter, *_iter, *alloc_start = NULL; + LIST_HEAD(chain); + int slots_found, retry = 0; + + /* start search from the last allocated descrtiptor + * if a contiguous allocation can not be found start searching + * from the beginning of the list + */ +retry: + slots_found = 0; + if (retry == 0) + iter = mv_chan->last_used; + else + iter = list_entry(&mv_chan->all_slots, + struct mv_xor_desc_slot, + slot_node); + + list_for_each_entry_safe_continue( + iter, _iter, &mv_chan->all_slots, slot_node) { + prefetch(_iter); + prefetch(&_iter->async_tx); + if (iter->slots_per_op) { + /* give up after finding the first busy slot + * on the second pass through the list + */ + if (retry) + break; + + slots_found = 0; + continue; + } + + /* start the allocation if the slot is correctly aligned */ + if (!slots_found++) + alloc_start = iter; + + if (slots_found == num_slots) { + struct mv_xor_desc_slot *alloc_tail = NULL; + struct mv_xor_desc_slot *last_used = NULL; + iter = alloc_start; + while (num_slots) { + int i; + + /* pre-ack all but the last descriptor */ + async_tx_ack(&iter->async_tx); + + list_add_tail(&iter->chain_node, &chain); + alloc_tail = iter; + iter->async_tx.cookie = 0; + iter->slot_cnt = num_slots; + iter->xor_check_result = NULL; + for (i = 0; i < slots_per_op; i++) { + iter->slots_per_op = slots_per_op - i; + last_used = iter; + iter = list_entry(iter->slot_node.next, + struct mv_xor_desc_slot, + slot_node); + } + num_slots -= slots_per_op; + } + alloc_tail->group_head = alloc_start; + alloc_tail->async_tx.cookie = -EBUSY; + list_splice(&chain, &alloc_tail->async_tx.tx_list); + mv_chan->last_used = last_used; + mv_desc_clear_next_desc(alloc_start); + mv_desc_clear_next_desc(alloc_tail); + return alloc_tail; + } + } + if (!retry++) + goto retry; + + /* try to free some slots if the allocation fails */ + tasklet_schedule(&mv_chan->irq_tasklet); + + return NULL; +} + +static dma_cookie_t +mv_desc_assign_cookie(struct mv_xor_chan *mv_chan, + struct mv_xor_desc_slot *desc) +{ + dma_cookie_t cookie = mv_chan->common.cookie; + + if (++cookie < 0) + cookie = 1; + mv_chan->common.cookie = desc->async_tx.cookie = cookie; + return cookie; +} + +/************************ DMA engine API functions ****************************/ +static dma_cookie_t +mv_xor_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct mv_xor_desc_slot *sw_desc = to_mv_xor_slot(tx); + struct mv_xor_chan *mv_chan = to_mv_xor_chan(tx->chan); + struct mv_xor_desc_slot *grp_start, *old_chain_tail; + dma_cookie_t cookie; + int new_hw_chain = 1; + + dev_dbg(mv_chan->device->common.dev, + "%s sw_desc %p: async_tx %p\n", + __func__, sw_desc, &sw_desc->async_tx); + + grp_start = sw_desc->group_head; + + spin_lock_bh(&mv_chan->lock); + cookie = mv_desc_assign_cookie(mv_chan, sw_desc); + + if (list_empty(&mv_chan->chain)) + list_splice_init(&sw_desc->async_tx.tx_list, &mv_chan->chain); + else { + new_hw_chain = 0; + + old_chain_tail = list_entry(mv_chan->chain.prev, + struct mv_xor_desc_slot, + chain_node); + list_splice_init(&grp_start->async_tx.tx_list, + &old_chain_tail->chain_node); + + if (!mv_can_chain(grp_start)) + goto submit_done; + + dev_dbg(mv_chan->device->common.dev, "Append to last desc %x\n", + old_chain_tail->async_tx.phys); + + /* fix up the hardware chain */ + mv_desc_set_next_desc(old_chain_tail, grp_start->async_tx.phys); + + /* if the channel is not busy */ + if (!mv_chan_is_busy(mv_chan)) { + u32 current_desc = mv_chan_get_current_desc(mv_chan); + /* + * and the curren desc is the end of the chain before + * the append, then we need to start the channel + */ + if (current_desc == old_chain_tail->async_tx.phys) + new_hw_chain = 1; + } + } + + if (new_hw_chain) + mv_xor_start_new_chain(mv_chan, grp_start); + +submit_done: + spin_unlock_bh(&mv_chan->lock); + + return cookie; +} + +/* returns the number of allocated descriptors */ +static int mv_xor_alloc_chan_resources(struct dma_chan *chan, + struct dma_client *client) +{ + char *hw_desc; + int idx; + struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan); + struct mv_xor_desc_slot *slot = NULL; + struct mv_xor_platform_data *plat_data = + mv_chan->device->pdev->dev.platform_data; + int num_descs_in_pool = plat_data->pool_size/MV_XOR_SLOT_SIZE; + + /* Allocate descriptor slots */ + idx = mv_chan->slots_allocated; + while (idx < num_descs_in_pool) { + slot = kzalloc(sizeof(*slot), GFP_KERNEL); + if (!slot) { + printk(KERN_INFO "MV XOR Channel only initialized" + " %d descriptor slots", idx); + break; + } + hw_desc = (char *) mv_chan->device->dma_desc_pool_virt; + slot->hw_desc = (void *) &hw_desc[idx * MV_XOR_SLOT_SIZE]; + + dma_async_tx_descriptor_init(&slot->async_tx, chan); + slot->async_tx.tx_submit = mv_xor_tx_submit; + INIT_LIST_HEAD(&slot->chain_node); + INIT_LIST_HEAD(&slot->slot_node); + INIT_LIST_HEAD(&slot->async_tx.tx_list); + hw_desc = (char *) mv_chan->device->dma_desc_pool; + slot->async_tx.phys = + (dma_addr_t) &hw_desc[idx * MV_XOR_SLOT_SIZE]; + slot->idx = idx++; + + spin_lock_bh(&mv_chan->lock); + mv_chan->slots_allocated = idx; + list_add_tail(&slot->slot_node, &mv_chan->all_slots); + spin_unlock_bh(&mv_chan->lock); + } + + if (mv_chan->slots_allocated && !mv_chan->last_used) + mv_chan->last_used = list_entry(mv_chan->all_slots.next, + struct mv_xor_desc_slot, + slot_node); + + dev_dbg(mv_chan->device->common.dev, + "allocated %d descriptor slots last_used: %p\n", + mv_chan->slots_allocated, mv_chan->last_used); + + return mv_chan->slots_allocated ? : -ENOMEM; +} + +static struct dma_async_tx_descriptor * +mv_xor_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, + size_t len, unsigned long flags) +{ + struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan); + struct mv_xor_desc_slot *sw_desc, *grp_start; + int slot_cnt; + + dev_dbg(mv_chan->device->common.dev, + "%s dest: %x src %x len: %u flags: %ld\n", + __func__, dest, src, len, flags); + if (unlikely(len < MV_XOR_MIN_BYTE_COUNT)) + return NULL; + + BUG_ON(unlikely(len > MV_XOR_MAX_BYTE_COUNT)); + + spin_lock_bh(&mv_chan->lock); + slot_cnt = mv_chan_memcpy_slot_count(len); + sw_desc = mv_xor_alloc_slots(mv_chan, slot_cnt, 1); + if (sw_desc) { + sw_desc->type = DMA_MEMCPY; + sw_desc->async_tx.flags = flags; + grp_start = sw_desc->group_head; + mv_desc_init(grp_start, flags); + mv_desc_set_byte_count(grp_start, len); + mv_desc_set_dest_addr(sw_desc->group_head, dest); + mv_desc_set_src_addr(grp_start, 0, src); + sw_desc->unmap_src_cnt = 1; + sw_desc->unmap_len = len; + } + spin_unlock_bh(&mv_chan->lock); + + dev_dbg(mv_chan->device->common.dev, + "%s sw_desc %p async_tx %p\n", + __func__, sw_desc, sw_desc ? &sw_desc->async_tx : 0); + + return sw_desc ? &sw_desc->async_tx : NULL; +} + +static struct dma_async_tx_descriptor * +mv_xor_prep_dma_memset(struct dma_chan *chan, dma_addr_t dest, int value, + size_t len, unsigned long flags) +{ + struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan); + struct mv_xor_desc_slot *sw_desc, *grp_start; + int slot_cnt; + + dev_dbg(mv_chan->device->common.dev, + "%s dest: %x len: %u flags: %ld\n", + __func__, dest, len, flags); + if (unlikely(len < MV_XOR_MIN_BYTE_COUNT)) + return NULL; + + BUG_ON(unlikely(len > MV_XOR_MAX_BYTE_COUNT)); + + spin_lock_bh(&mv_chan->lock); + slot_cnt = mv_chan_memset_slot_count(len); + sw_desc = mv_xor_alloc_slots(mv_chan, slot_cnt, 1); + if (sw_desc) { + sw_desc->type = DMA_MEMSET; + sw_desc->async_tx.flags = flags; + grp_start = sw_desc->group_head; + mv_desc_init(grp_start, flags); + mv_desc_set_byte_count(grp_start, len); + mv_desc_set_dest_addr(sw_desc->group_head, dest); + mv_desc_set_block_fill_val(grp_start, value); + sw_desc->unmap_src_cnt = 1; + sw_desc->unmap_len = len; + } + spin_unlock_bh(&mv_chan->lock); + dev_dbg(mv_chan->device->common.dev, + "%s sw_desc %p async_tx %p \n", + __func__, sw_desc, &sw_desc->async_tx); + return sw_desc ? &sw_desc->async_tx : NULL; +} + +static struct dma_async_tx_descriptor * +mv_xor_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src, + unsigned int src_cnt, size_t len, unsigned long flags) +{ + struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan); + struct mv_xor_desc_slot *sw_desc, *grp_start; + int slot_cnt; + + if (unlikely(len < MV_XOR_MIN_BYTE_COUNT)) + return NULL; + + BUG_ON(unlikely(len > MV_XOR_MAX_BYTE_COUNT)); + + dev_dbg(mv_chan->device->common.dev, + "%s src_cnt: %d len: dest %x %u flags: %ld\n", + __func__, src_cnt, len, dest, flags); + + spin_lock_bh(&mv_chan->lock); + slot_cnt = mv_chan_xor_slot_count(len, src_cnt); + sw_desc = mv_xor_alloc_slots(mv_chan, slot_cnt, 1); + if (sw_desc) { + sw_desc->type = DMA_XOR; + sw_desc->async_tx.flags = flags; + grp_start = sw_desc->group_head; + mv_desc_init(grp_start, flags); + /* the byte count field is the same as in memcpy desc*/ + mv_desc_set_byte_count(grp_start, len); + mv_desc_set_dest_addr(sw_desc->group_head, dest); + sw_desc->unmap_src_cnt = src_cnt; + sw_desc->unmap_len = len; + while (src_cnt--) + mv_desc_set_src_addr(grp_start, src_cnt, src[src_cnt]); + } + spin_unlock_bh(&mv_chan->lock); + dev_dbg(mv_chan->device->common.dev, + "%s sw_desc %p async_tx %p \n", + __func__, sw_desc, &sw_desc->async_tx); + return sw_desc ? &sw_desc->async_tx : NULL; +} + +static void mv_xor_free_chan_resources(struct dma_chan *chan) +{ + struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan); + struct mv_xor_desc_slot *iter, *_iter; + int in_use_descs = 0; + + mv_xor_slot_cleanup(mv_chan); + + spin_lock_bh(&mv_chan->lock); + list_for_each_entry_safe(iter, _iter, &mv_chan->chain, + chain_node) { + in_use_descs++; + list_del(&iter->chain_node); + } + list_for_each_entry_safe(iter, _iter, &mv_chan->completed_slots, + completed_node) { + in_use_descs++; + list_del(&iter->completed_node); + } + list_for_each_entry_safe_reverse( + iter, _iter, &mv_chan->all_slots, slot_node) { + list_del(&iter->slot_node); + kfree(iter); + mv_chan->slots_allocated--; + } + mv_chan->last_used = NULL; + + dev_dbg(mv_chan->device->common.dev, "%s slots_allocated %d\n", + __func__, mv_chan->slots_allocated); + spin_unlock_bh(&mv_chan->lock); + + if (in_use_descs) + dev_err(mv_chan->device->common.dev, + "freeing %d in use descriptors!\n", in_use_descs); +} + +/** + * mv_xor_is_complete - poll the status of an XOR transaction + * @chan: XOR channel handle + * @cookie: XOR transaction identifier + */ +static enum dma_status mv_xor_is_complete(struct dma_chan *chan, + dma_cookie_t cookie, + dma_cookie_t *done, + dma_cookie_t *used) +{ + struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan); + dma_cookie_t last_used; + dma_cookie_t last_complete; + enum dma_status ret; + + last_used = chan->cookie; + last_complete = mv_chan->completed_cookie; + mv_chan->is_complete_cookie = cookie; + if (done) + *done = last_complete; + if (used) + *used = last_used; + + ret = dma_async_is_complete(cookie, last_complete, last_used); + if (ret == DMA_SUCCESS) { + mv_xor_clean_completed_slots(mv_chan); + return ret; + } + mv_xor_slot_cleanup(mv_chan); + + last_used = chan->cookie; + last_complete = mv_chan->completed_cookie; + + if (done) + *done = last_complete; + if (used) + *used = last_used; + + return dma_async_is_complete(cookie, last_complete, last_used); +} + +static void mv_dump_xor_regs(struct mv_xor_chan *chan) +{ + u32 val; + + val = __raw_readl(XOR_CONFIG(chan)); + dev_printk(KERN_ERR, chan->device->common.dev, + "config 0x%08x.\n", val); + + val = __raw_readl(XOR_ACTIVATION(chan)); + dev_printk(KERN_ERR, chan->device->common.dev, + "activation 0x%08x.\n", val); + + val = __raw_readl(XOR_INTR_CAUSE(chan)); + dev_printk(KERN_ERR, chan->device->common.dev, + "intr cause 0x%08x.\n", val); + + val = __raw_readl(XOR_INTR_MASK(chan)); + dev_printk(KERN_ERR, chan->device->common.dev, + "intr mask 0x%08x.\n", val); + + val = __raw_readl(XOR_ERROR_CAUSE(chan)); + dev_printk(KERN_ERR, chan->device->common.dev, + "error cause 0x%08x.\n", val); + + val = __raw_readl(XOR_ERROR_ADDR(chan)); + dev_printk(KERN_ERR, chan->device->common.dev, + "error addr 0x%08x.\n", val); +} + +static void mv_xor_err_interrupt_handler(struct mv_xor_chan *chan, + u32 intr_cause) +{ + if (intr_cause & (1 << 4)) { + dev_dbg(chan->device->common.dev, + "ignore this error\n"); + return; + } + + dev_printk(KERN_ERR, chan->device->common.dev, + "error on chan %d. intr cause 0x%08x.\n", + chan->idx, intr_cause); + + mv_dump_xor_regs(chan); + BUG(); +} + +static irqreturn_t mv_xor_interrupt_handler(int irq, void *data) +{ + struct mv_xor_chan *chan = data; + u32 intr_cause = mv_chan_get_intr_cause(chan); + + dev_dbg(chan->device->common.dev, "intr cause %x\n", intr_cause); + + if (mv_is_err_intr(intr_cause)) + mv_xor_err_interrupt_handler(chan, intr_cause); + + tasklet_schedule(&chan->irq_tasklet); + + mv_xor_device_clear_eoc_cause(chan); + + return IRQ_HANDLED; +} + +static void mv_xor_issue_pending(struct dma_chan *chan) +{ + struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan); + + if (mv_chan->pending >= MV_XOR_THRESHOLD) { + mv_chan->pending = 0; + mv_chan_activate(mv_chan); + } +} + +/* + * Perform a transaction to verify the HW works. + */ +#define MV_XOR_TEST_SIZE 2000 + +static int __devinit mv_xor_memcpy_self_test(struct mv_xor_device *device) +{ + int i; + void *src, *dest; + dma_addr_t src_dma, dest_dma; + struct dma_chan *dma_chan; + dma_cookie_t cookie; + struct dma_async_tx_descriptor *tx; + int err = 0; + struct mv_xor_chan *mv_chan; + + src = kmalloc(sizeof(u8) * MV_XOR_TEST_SIZE, GFP_KERNEL); + if (!src) + return -ENOMEM; + + dest = kzalloc(sizeof(u8) * MV_XOR_TEST_SIZE, GFP_KERNEL); + if (!dest) { + kfree(src); + return -ENOMEM; + } + + /* Fill in src buffer */ + for (i = 0; i < MV_XOR_TEST_SIZE; i++) + ((u8 *) src)[i] = (u8)i; + + /* Start copy, using first DMA channel */ + dma_chan = container_of(device->common.channels.next, + struct dma_chan, + device_node); + if (mv_xor_alloc_chan_resources(dma_chan, NULL) < 1) { + err = -ENODEV; + goto out; + } + + dest_dma = dma_map_single(dma_chan->device->dev, dest, + MV_XOR_TEST_SIZE, DMA_FROM_DEVICE); + + src_dma = dma_map_single(dma_chan->device->dev, src, + MV_XOR_TEST_SIZE, DMA_TO_DEVICE); + + tx = mv_xor_prep_dma_memcpy(dma_chan, dest_dma, src_dma, + MV_XOR_TEST_SIZE, 0); + cookie = mv_xor_tx_submit(tx); + mv_xor_issue_pending(dma_chan); + async_tx_ack(tx); + msleep(1); + + if (mv_xor_is_complete(dma_chan, cookie, NULL, NULL) != + DMA_SUCCESS) { + dev_printk(KERN_ERR, dma_chan->device->dev, + "Self-test copy timed out, disabling\n"); + err = -ENODEV; + goto free_resources; + } + + mv_chan = to_mv_xor_chan(dma_chan); + dma_sync_single_for_cpu(&mv_chan->device->pdev->dev, dest_dma, + MV_XOR_TEST_SIZE, DMA_FROM_DEVICE); + if (memcmp(src, dest, MV_XOR_TEST_SIZE)) { + dev_printk(KERN_ERR, dma_chan->device->dev, + "Self-test copy failed compare, disabling\n"); + err = -ENODEV; + goto free_resources; + } + +free_resources: + mv_xor_free_chan_resources(dma_chan); +out: + kfree(src); + kfree(dest); + return err; +} + +#define MV_XOR_NUM_SRC_TEST 4 /* must be <= 15 */ +static int __devinit +mv_xor_xor_self_test(struct mv_xor_device *device) +{ + int i, src_idx; + struct page *dest; + struct page *xor_srcs[MV_XOR_NUM_SRC_TEST]; + dma_addr_t dma_srcs[MV_XOR_NUM_SRC_TEST]; + dma_addr_t dest_dma; + struct dma_async_tx_descriptor *tx; + struct dma_chan *dma_chan; + dma_cookie_t cookie; + u8 cmp_byte = 0; + u32 cmp_word; + int err = 0; + struct mv_xor_chan *mv_chan; + + for (src_idx = 0; src_idx < MV_XOR_NUM_SRC_TEST; src_idx++) { + xor_srcs[src_idx] = alloc_page(GFP_KERNEL); + if (!xor_srcs[src_idx]) + while (src_idx--) { + __free_page(xor_srcs[src_idx]); + return -ENOMEM; + } + } + + dest = alloc_page(GFP_KERNEL); + if (!dest) + while (src_idx--) { + __free_page(xor_srcs[src_idx]); + return -ENOMEM; + } + + /* Fill in src buffers */ + for (src_idx = 0; src_idx < MV_XOR_NUM_SRC_TEST; src_idx++) { + u8 *ptr = page_address(xor_srcs[src_idx]); + for (i = 0; i < PAGE_SIZE; i++) + ptr[i] = (1 << src_idx); + } + + for (src_idx = 0; src_idx < MV_XOR_NUM_SRC_TEST; src_idx++) + cmp_byte ^= (u8) (1 << src_idx); + + cmp_word = (cmp_byte << 24) | (cmp_byte << 16) | + (cmp_byte << 8) | cmp_byte; + + memset(page_address(dest), 0, PAGE_SIZE); + + dma_chan = container_of(device->common.channels.next, + struct dma_chan, + device_node); + if (mv_xor_alloc_chan_resources(dma_chan, NULL) < 1) { + err = -ENODEV; + goto out; + } + + /* test xor */ + dest_dma = dma_map_page(dma_chan->device->dev, dest, 0, PAGE_SIZE, + DMA_FROM_DEVICE); + + for (i = 0; i < MV_XOR_NUM_SRC_TEST; i++) + dma_srcs[i] = dma_map_page(dma_chan->device->dev, xor_srcs[i], + 0, PAGE_SIZE, DMA_TO_DEVICE); + + tx = mv_xor_prep_dma_xor(dma_chan, dest_dma, dma_srcs, + MV_XOR_NUM_SRC_TEST, PAGE_SIZE, 0); + + cookie = mv_xor_tx_submit(tx); + mv_xor_issue_pending(dma_chan); + async_tx_ack(tx); + msleep(8); + + if (mv_xor_is_complete(dma_chan, cookie, NULL, NULL) != + DMA_SUCCESS) { + dev_printk(KERN_ERR, dma_chan->device->dev, + "Self-test xor timed out, disabling\n"); + err = -ENODEV; + goto free_resources; + } + + mv_chan = to_mv_xor_chan(dma_chan); + dma_sync_single_for_cpu(&mv_chan->device->pdev->dev, dest_dma, + PAGE_SIZE, DMA_FROM_DEVICE); + for (i = 0; i < (PAGE_SIZE / sizeof(u32)); i++) { + u32 *ptr = page_address(dest); + if (ptr[i] != cmp_word) { + dev_printk(KERN_ERR, dma_chan->device->dev, + "Self-test xor failed compare, disabling." + " index %d, data %x, expected %x\n", i, + ptr[i], cmp_word); + err = -ENODEV; + goto free_resources; + } + } + +free_resources: + mv_xor_free_chan_resources(dma_chan); +out: + src_idx = MV_XOR_NUM_SRC_TEST; + while (src_idx--) + __free_page(xor_srcs[src_idx]); + __free_page(dest); + return err; +} + +static int __devexit mv_xor_remove(struct platform_device *dev) +{ + struct mv_xor_device *device = platform_get_drvdata(dev); + struct dma_chan *chan, *_chan; + struct mv_xor_chan *mv_chan; + struct mv_xor_platform_data *plat_data = dev->dev.platform_data; + + dma_async_device_unregister(&device->common); + + dma_free_coherent(&dev->dev, plat_data->pool_size, + device->dma_desc_pool_virt, device->dma_desc_pool); + + list_for_each_entry_safe(chan, _chan, &device->common.channels, + device_node) { + mv_chan = to_mv_xor_chan(chan); + list_del(&chan->device_node); + } + + return 0; +} + +static int __devinit mv_xor_probe(struct platform_device *pdev) +{ + int ret = 0; + int irq; + struct mv_xor_device *adev; + struct mv_xor_chan *mv_chan; + struct dma_device *dma_dev; + struct mv_xor_platform_data *plat_data = pdev->dev.platform_data; + + + adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL); + if (!adev) + return -ENOMEM; + + dma_dev = &adev->common; + + /* allocate coherent memory for hardware descriptors + * note: writecombine gives slightly better performance, but + * requires that we explicitly flush the writes + */ + adev->dma_desc_pool_virt = dma_alloc_writecombine(&pdev->dev, + plat_data->pool_size, + &adev->dma_desc_pool, + GFP_KERNEL); + if (!adev->dma_desc_pool_virt) + return -ENOMEM; + + adev->id = plat_data->hw_id; + + /* discover transaction capabilites from the platform data */ + dma_dev->cap_mask = plat_data->cap_mask; + adev->pdev = pdev; + platform_set_drvdata(pdev, adev); + + adev->shared = platform_get_drvdata(plat_data->shared); + + INIT_LIST_HEAD(&dma_dev->channels); + + /* set base routines */ + dma_dev->device_alloc_chan_resources = mv_xor_alloc_chan_resources; + dma_dev->device_free_chan_resources = mv_xor_free_chan_resources; + dma_dev->device_is_tx_complete = mv_xor_is_complete; + dma_dev->device_issue_pending = mv_xor_issue_pending; + dma_dev->dev = &pdev->dev; + + /* set prep routines based on capability */ + if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) + dma_dev->device_prep_dma_memcpy = mv_xor_prep_dma_memcpy; + if (dma_has_cap(DMA_MEMSET, dma_dev->cap_mask)) + dma_dev->device_prep_dma_memset = mv_xor_prep_dma_memset; + if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) { + dma_dev->max_xor = 8; ; + dma_dev->device_prep_dma_xor = mv_xor_prep_dma_xor; + } + + mv_chan = devm_kzalloc(&pdev->dev, sizeof(*mv_chan), GFP_KERNEL); + if (!mv_chan) { + ret = -ENOMEM; + goto err_free_dma; + } + mv_chan->device = adev; + mv_chan->idx = plat_data->hw_id; + mv_chan->mmr_base = adev->shared->xor_base; + + if (!mv_chan->mmr_base) { + ret = -ENOMEM; + goto err_free_dma; + } + tasklet_init(&mv_chan->irq_tasklet, mv_xor_tasklet, (unsigned long) + mv_chan); + + /* clear errors before enabling interrupts */ + mv_xor_device_clear_err_status(mv_chan); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + goto err_free_dma; + } + ret = devm_request_irq(&pdev->dev, irq, + mv_xor_interrupt_handler, + 0, dev_name(&pdev->dev), mv_chan); + if (ret) + goto err_free_dma; + + mv_chan_unmask_interrupts(mv_chan); + + mv_set_mode(mv_chan, DMA_MEMCPY); + + spin_lock_init(&mv_chan->lock); + INIT_LIST_HEAD(&mv_chan->chain); + INIT_LIST_HEAD(&mv_chan->completed_slots); + INIT_LIST_HEAD(&mv_chan->all_slots); + INIT_RCU_HEAD(&mv_chan->common.rcu); + mv_chan->common.device = dma_dev; + + list_add_tail(&mv_chan->common.device_node, &dma_dev->channels); + + if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) { + ret = mv_xor_memcpy_self_test(adev); + dev_dbg(&pdev->dev, "memcpy self test returned %d\n", ret); + if (ret) + goto err_free_dma; + } + + if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) { + ret = mv_xor_xor_self_test(adev); + dev_dbg(&pdev->dev, "xor self test returned %d\n", ret); + if (ret) + goto err_free_dma; + } + + dev_printk(KERN_INFO, &pdev->dev, "Marvell XOR: " + "( %s%s%s%s)\n", + dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "", + dma_has_cap(DMA_MEMSET, dma_dev->cap_mask) ? "fill " : "", + dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "", + dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : ""); + + dma_async_device_register(dma_dev); + goto out; + + err_free_dma: + dma_free_coherent(&adev->pdev->dev, plat_data->pool_size, + adev->dma_desc_pool_virt, adev->dma_desc_pool); + out: + return ret; +} + +static void +mv_xor_conf_mbus_windows(struct mv_xor_shared_private *msp, + struct mbus_dram_target_info *dram) +{ + void __iomem *base = msp->xor_base; + u32 win_enable = 0; + int i; + + for (i = 0; i < 8; i++) { + writel(0, base + WINDOW_BASE(i)); + writel(0, base + WINDOW_SIZE(i)); + if (i < 4) + writel(0, base + WINDOW_REMAP_HIGH(i)); + } + + for (i = 0; i < dram->num_cs; i++) { + struct mbus_dram_window *cs = dram->cs + i; + + writel((cs->base & 0xffff0000) | + (cs->mbus_attr << 8) | + dram->mbus_dram_target_id, base + WINDOW_BASE(i)); + writel((cs->size - 1) & 0xffff0000, base + WINDOW_SIZE(i)); + + win_enable |= (1 << i); + win_enable |= 3 << (16 + (2 * i)); + } + + writel(win_enable, base + WINDOW_BAR_ENABLE(0)); + writel(win_enable, base + WINDOW_BAR_ENABLE(1)); +} + +static struct platform_driver mv_xor_driver = { + .probe = mv_xor_probe, + .remove = mv_xor_remove, + .driver = { + .owner = THIS_MODULE, + .name = MV_XOR_NAME, + }, +}; + +static int mv_xor_shared_probe(struct platform_device *pdev) +{ + struct mv_xor_platform_shared_data *msd = pdev->dev.platform_data; + struct mv_xor_shared_private *msp; + struct resource *res; + + dev_printk(KERN_NOTICE, &pdev->dev, "Marvell shared XOR driver\n"); + + msp = devm_kzalloc(&pdev->dev, sizeof(*msp), GFP_KERNEL); + if (!msp) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + msp->xor_base = devm_ioremap(&pdev->dev, res->start, + res->end - res->start + 1); + if (!msp->xor_base) + return -EBUSY; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + return -ENODEV; + + msp->xor_high_base = devm_ioremap(&pdev->dev, res->start, + res->end - res->start + 1); + if (!msp->xor_high_base) + return -EBUSY; + + platform_set_drvdata(pdev, msp); + + /* + * (Re-)program MBUS remapping windows if we are asked to. + */ + if (msd != NULL && msd->dram != NULL) + mv_xor_conf_mbus_windows(msp, msd->dram); + + return 0; +} + +static int mv_xor_shared_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver mv_xor_shared_driver = { + .probe = mv_xor_shared_probe, + .remove = mv_xor_shared_remove, + .driver = { + .owner = THIS_MODULE, + .name = MV_XOR_SHARED_NAME, + }, +}; + + +static int __init mv_xor_init(void) +{ + int rc; + + rc = platform_driver_register(&mv_xor_shared_driver); + if (!rc) { + rc = platform_driver_register(&mv_xor_driver); + if (rc) + platform_driver_unregister(&mv_xor_shared_driver); + } + return rc; +} +module_init(mv_xor_init); + +/* it's currently unsafe to unload this module */ +#if 0 +static void __exit mv_xor_exit(void) +{ + platform_driver_unregister(&mv_xor_driver); + platform_driver_unregister(&mv_xor_shared_driver); + return; +} + +module_exit(mv_xor_exit); +#endif + +MODULE_AUTHOR("Saeed Bishara <saeed@marvell.com>"); +MODULE_DESCRIPTION("DMA engine driver for Marvell's XOR engine"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dma/mv_xor.h b/drivers/dma/mv_xor.h new file mode 100644 index 000000000000..06cafe1ef521 --- /dev/null +++ b/drivers/dma/mv_xor.h @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2007, 2008, Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MV_XOR_H +#define MV_XOR_H + +#include <linux/types.h> +#include <linux/io.h> +#include <linux/dmaengine.h> +#include <linux/interrupt.h> + +#define USE_TIMER +#define MV_XOR_SLOT_SIZE 64 +#define MV_XOR_THRESHOLD 1 + +#define XOR_OPERATION_MODE_XOR 0 +#define XOR_OPERATION_MODE_MEMCPY 2 +#define XOR_OPERATION_MODE_MEMSET 4 + +#define XOR_CURR_DESC(chan) (chan->mmr_base + 0x210 + (chan->idx * 4)) +#define XOR_NEXT_DESC(chan) (chan->mmr_base + 0x200 + (chan->idx * 4)) +#define XOR_BYTE_COUNT(chan) (chan->mmr_base + 0x220 + (chan->idx * 4)) +#define XOR_DEST_POINTER(chan) (chan->mmr_base + 0x2B0 + (chan->idx * 4)) +#define XOR_BLOCK_SIZE(chan) (chan->mmr_base + 0x2C0 + (chan->idx * 4)) +#define XOR_INIT_VALUE_LOW(chan) (chan->mmr_base + 0x2E0) +#define XOR_INIT_VALUE_HIGH(chan) (chan->mmr_base + 0x2E4) + +#define XOR_CONFIG(chan) (chan->mmr_base + 0x10 + (chan->idx * 4)) +#define XOR_ACTIVATION(chan) (chan->mmr_base + 0x20 + (chan->idx * 4)) +#define XOR_INTR_CAUSE(chan) (chan->mmr_base + 0x30) +#define XOR_INTR_MASK(chan) (chan->mmr_base + 0x40) +#define XOR_ERROR_CAUSE(chan) (chan->mmr_base + 0x50) +#define XOR_ERROR_ADDR(chan) (chan->mmr_base + 0x60) +#define XOR_INTR_MASK_VALUE 0x3F5 + +#define WINDOW_BASE(w) (0x250 + ((w) << 2)) +#define WINDOW_SIZE(w) (0x270 + ((w) << 2)) +#define WINDOW_REMAP_HIGH(w) (0x290 + ((w) << 2)) +#define WINDOW_BAR_ENABLE(chan) (0x240 + ((chan) << 2)) + +struct mv_xor_shared_private { + void __iomem *xor_base; + void __iomem *xor_high_base; +}; + + +/** + * struct mv_xor_device - internal representation of a XOR device + * @pdev: Platform device + * @id: HW XOR Device selector + * @dma_desc_pool: base of DMA descriptor region (DMA address) + * @dma_desc_pool_virt: base of DMA descriptor region (CPU address) + * @common: embedded struct dma_device + */ +struct mv_xor_device { + struct platform_device *pdev; + int id; + dma_addr_t dma_desc_pool; + void *dma_desc_pool_virt; + struct dma_device common; + struct mv_xor_shared_private *shared; +}; + +/** + * struct mv_xor_chan - internal representation of a XOR channel + * @pending: allows batching of hardware operations + * @completed_cookie: identifier for the most recently completed operation + * @lock: serializes enqueue/dequeue operations to the descriptors pool + * @mmr_base: memory mapped register base + * @idx: the index of the xor channel + * @chain: device chain view of the descriptors + * @completed_slots: slots completed by HW but still need to be acked + * @device: parent device + * @common: common dmaengine channel object members + * @last_used: place holder for allocation to continue from where it left off + * @all_slots: complete domain of slots usable by the channel + * @slots_allocated: records the actual size of the descriptor slot pool + * @irq_tasklet: bottom half where mv_xor_slot_cleanup runs + */ +struct mv_xor_chan { + int pending; + dma_cookie_t completed_cookie; + spinlock_t lock; /* protects the descriptor slot pool */ + void __iomem *mmr_base; + unsigned int idx; + enum dma_transaction_type current_type; + struct list_head chain; + struct list_head completed_slots; + struct mv_xor_device *device; + struct dma_chan common; + struct mv_xor_desc_slot *last_used; + struct list_head all_slots; + int slots_allocated; + struct tasklet_struct irq_tasklet; +#ifdef USE_TIMER + unsigned long cleanup_time; + u32 current_on_last_cleanup; + dma_cookie_t is_complete_cookie; +#endif +}; + +/** + * struct mv_xor_desc_slot - software descriptor + * @slot_node: node on the mv_xor_chan.all_slots list + * @chain_node: node on the mv_xor_chan.chain list + * @completed_node: node on the mv_xor_chan.completed_slots list + * @hw_desc: virtual address of the hardware descriptor chain + * @phys: hardware address of the hardware descriptor chain + * @group_head: first operation in a transaction + * @slot_cnt: total slots used in an transaction (group of operations) + * @slots_per_op: number of slots per operation + * @idx: pool index + * @unmap_src_cnt: number of xor sources + * @unmap_len: transaction bytecount + * @async_tx: support for the async_tx api + * @group_list: list of slots that make up a multi-descriptor transaction + * for example transfer lengths larger than the supported hw max + * @xor_check_result: result of zero sum + * @crc32_result: result crc calculation + */ +struct mv_xor_desc_slot { + struct list_head slot_node; + struct list_head chain_node; + struct list_head completed_node; + enum dma_transaction_type type; + void *hw_desc; + struct mv_xor_desc_slot *group_head; + u16 slot_cnt; + u16 slots_per_op; + u16 idx; + u16 unmap_src_cnt; + u32 value; + size_t unmap_len; + struct dma_async_tx_descriptor async_tx; + union { + u32 *xor_check_result; + u32 *crc32_result; + }; +#ifdef USE_TIMER + unsigned long arrival_time; + struct timer_list timeout; +#endif +}; + +/* This structure describes XOR descriptor size 64bytes */ +struct mv_xor_desc { + u32 status; /* descriptor execution status */ + u32 crc32_result; /* result of CRC-32 calculation */ + u32 desc_command; /* type of operation to be carried out */ + u32 phy_next_desc; /* next descriptor address pointer */ + u32 byte_count; /* size of src/dst blocks in bytes */ + u32 phy_dest_addr; /* destination block address */ + u32 phy_src_addr[8]; /* source block addresses */ + u32 reserved0; + u32 reserved1; +}; + +#define to_mv_sw_desc(addr_hw_desc) \ + container_of(addr_hw_desc, struct mv_xor_desc_slot, hw_desc) + +#define mv_hw_desc_slot_idx(hw_desc, idx) \ + ((void *)(((unsigned long)hw_desc) + ((idx) << 5))) + +#define MV_XOR_MIN_BYTE_COUNT (128) +#define XOR_MAX_BYTE_COUNT ((16 * 1024 * 1024) - 1) +#define MV_XOR_MAX_BYTE_COUNT XOR_MAX_BYTE_COUNT + + +#endif diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 6e6c3c4aea6b..5a11e3cbcae2 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -123,6 +123,13 @@ config EDAC_I5000 Support for error detection and correction the Intel Greekcreek/Blackford chipsets. +config EDAC_I5100 + tristate "Intel San Clemente MCH" + depends on EDAC_MM_EDAC && X86 && PCI + help + Support for error detection and correction the Intel + San Clemente MCH. + config EDAC_MPC85XX tristate "Freescale MPC85xx" depends on EDAC_MM_EDAC && FSL_SOC && MPC85xx diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 83807731d4a9..e5e9104b5520 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -19,6 +19,7 @@ endif obj-$(CONFIG_EDAC_AMD76X) += amd76x_edac.o obj-$(CONFIG_EDAC_I5000) += i5000_edac.o +obj-$(CONFIG_EDAC_I5100) += i5100_edac.o obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o obj-$(CONFIG_EDAC_E752X) += e752x_edac.o obj-$(CONFIG_EDAC_I82443BXGX) += i82443bxgx_edac.o diff --git a/drivers/edac/cell_edac.c b/drivers/edac/cell_edac.c index b54112ffd282..0e024fe2d8c4 100644 --- a/drivers/edac/cell_edac.c +++ b/drivers/edac/cell_edac.c @@ -33,7 +33,7 @@ static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar) { struct cell_edac_priv *priv = mci->pvt_info; struct csrow_info *csrow = &mci->csrows[0]; - unsigned long address, pfn, offset; + unsigned long address, pfn, offset, syndrome; dev_dbg(mci->dev, "ECC CE err on node %d, channel %d, ar = 0x%016lx\n", priv->node, chan, ar); @@ -44,10 +44,11 @@ static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar) address = (address << 1) | chan; pfn = address >> PAGE_SHIFT; offset = address & ~PAGE_MASK; + syndrome = (ar & 0x000000001fe00000ul) >> 21; /* TODO: Decoding of the error addresss */ edac_mc_handle_ce(mci, csrow->first_page + pfn, offset, - 0, 0, chan, ""); + syndrome, 0, chan, ""); } static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar) diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c index c94a0eb492cb..facfdb1fa71c 100644 --- a/drivers/edac/e752x_edac.c +++ b/drivers/edac/e752x_edac.c @@ -28,6 +28,7 @@ #define E752X_REVISION " Ver: 2.0.2 " __DATE__ #define EDAC_MOD_STR "e752x_edac" +static int report_non_memory_errors; static int force_function_unhide; static int sysbus_parity = -1; @@ -117,7 +118,7 @@ static struct edac_pci_ctl_info *e752x_pci; #define E752X_BUF_FERR 0x70 /* Memory buffer first error reg (8b) */ #define E752X_BUF_NERR 0x72 /* Memory buffer next error reg (8b) */ #define E752X_BUF_ERRMASK 0x74 /* Memory buffer error mask reg (8b) */ -#define E752X_BUF_SMICMD 0x7A /* Memory buffer SMI command reg (8b) */ +#define E752X_BUF_SMICMD 0x7A /* Memory buffer SMI cmd reg (8b) */ #define E752X_DRAM_FERR 0x80 /* DRAM first error register (16b) */ #define E752X_DRAM_NERR 0x82 /* DRAM next error register (16b) */ #define E752X_DRAM_ERRMASK 0x84 /* DRAM error mask register (8b) */ @@ -127,7 +128,7 @@ static struct edac_pci_ctl_info *e752x_pci; /* error address register (32b) */ /* * 31 Reserved - * 30:2 CE address (64 byte block 34:6) + * 30:2 CE address (64 byte block 34:6 * 1 Reserved * 0 HiLoCS */ @@ -147,11 +148,11 @@ static struct edac_pci_ctl_info *e752x_pci; * 1 Reserved * 0 HiLoCS */ -#define E752X_DRAM_SCRB_ADD 0xA8 /* DRAM first uncorrectable scrub memory */ +#define E752X_DRAM_SCRB_ADD 0xA8 /* DRAM 1st uncorrectable scrub mem */ /* error address register (32b) */ /* * 31 Reserved - * 30:2 CE address (64 byte block 34:6) + * 30:2 CE address (64 byte block 34:6 * 1 Reserved * 0 HiLoCS */ @@ -394,9 +395,12 @@ static void do_process_ded_retry(struct mem_ctl_info *mci, u16 error, struct e752x_pvt *pvt = (struct e752x_pvt *)mci->pvt_info; error_1b = retry_add; - page = error_1b >> (PAGE_SHIFT - 4); /* convert the addr to 4k page */ - row = pvt->mc_symmetric ? ((page >> 1) & 3) : /* chip select are bits 14 & 13 */ + page = error_1b >> (PAGE_SHIFT - 4); /* convert the addr to 4k page */ + + /* chip select are bits 14 & 13 */ + row = pvt->mc_symmetric ? ((page >> 1) & 3) : edac_mc_find_csrow_by_page(mci, page); + e752x_mc_printk(mci, KERN_WARNING, "CE page 0x%lx, row %d : Memory read retry\n", (long unsigned int)page, row); @@ -422,12 +426,21 @@ static inline void process_threshold_ce(struct mem_ctl_info *mci, u16 error, } static char *global_message[11] = { - "PCI Express C1", "PCI Express C", "PCI Express B1", - "PCI Express B", "PCI Express A1", "PCI Express A", - "DMA Controler", "HUB or NS Interface", "System Bus", - "DRAM Controler", "Internal Buffer" + "PCI Express C1", + "PCI Express C", + "PCI Express B1", + "PCI Express B", + "PCI Express A1", + "PCI Express A", + "DMA Controller", + "HUB or NS Interface", + "System Bus", + "DRAM Controller", /* 9th entry */ + "Internal Buffer" }; +#define DRAM_ENTRY 9 + static char *fatal_message[2] = { "Non-Fatal ", "Fatal " }; static void do_global_error(int fatal, u32 errors) @@ -435,9 +448,16 @@ static void do_global_error(int fatal, u32 errors) int i; for (i = 0; i < 11; i++) { - if (errors & (1 << i)) - e752x_printk(KERN_WARNING, "%sError %s\n", - fatal_message[fatal], global_message[i]); + if (errors & (1 << i)) { + /* If the error is from DRAM Controller OR + * we are to report ALL errors, then + * report the error + */ + if ((i == DRAM_ENTRY) || report_non_memory_errors) + e752x_printk(KERN_WARNING, "%sError %s\n", + fatal_message[fatal], + global_message[i]); + } } } @@ -1021,7 +1041,7 @@ static int e752x_get_devs(struct pci_dev *pdev, int dev_idx, struct pci_dev *dev; pvt->bridge_ck = pci_get_device(PCI_VENDOR_ID_INTEL, - pvt->dev_info->err_dev, pvt->bridge_ck); + pvt->dev_info->err_dev, pvt->bridge_ck); if (pvt->bridge_ck == NULL) pvt->bridge_ck = pci_scan_single_device(pdev->bus, @@ -1034,8 +1054,9 @@ static int e752x_get_devs(struct pci_dev *pdev, int dev_idx, return 1; } - dev = pci_get_device(PCI_VENDOR_ID_INTEL, e752x_devs[dev_idx].ctl_dev, - NULL); + dev = pci_get_device(PCI_VENDOR_ID_INTEL, + e752x_devs[dev_idx].ctl_dev, + NULL); if (dev == NULL) goto fail; @@ -1316,7 +1337,8 @@ MODULE_DESCRIPTION("MC support for Intel e752x/3100 memory controllers"); module_param(force_function_unhide, int, 0444); MODULE_PARM_DESC(force_function_unhide, "if BIOS sets Dev0:Fun1 up as hidden:" - " 1=force unhide and hope BIOS doesn't fight driver for Dev0:Fun1 access"); + " 1=force unhide and hope BIOS doesn't fight driver for " + "Dev0:Fun1 access"); module_param(edac_op_state, int, 0444); MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); @@ -1324,3 +1346,6 @@ MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); module_param(sysbus_parity, int, 0444); MODULE_PARM_DESC(sysbus_parity, "0=disable system bus parity checking," " 1=enable system bus parity checking, default=auto-detect"); +module_param(report_non_memory_errors, int, 0644); +MODULE_PARM_DESC(report_non_memory_errors, "0=disable non-memory error " + "reporting, 1=enable non-memory error reporting"); diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 021d18795145..ad218fe4942d 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -44,6 +44,25 @@ int edac_mc_get_poll_msec(void) return edac_mc_poll_msec; } +static int edac_set_poll_msec(const char *val, struct kernel_param *kp) +{ + long l; + int ret; + + if (!val) + return -EINVAL; + + ret = strict_strtol(val, 0, &l); + if (ret == -EINVAL || ((int)l != l)) + return -EINVAL; + *((int *)kp->arg) = l; + + /* notify edac_mc engine to reset the poll period */ + edac_mc_reset_delay_period(l); + + return 0; +} + /* Parameter declarations for above */ module_param(edac_mc_panic_on_ue, int, 0644); MODULE_PARM_DESC(edac_mc_panic_on_ue, "Panic on uncorrected error: 0=off 1=on"); @@ -53,7 +72,8 @@ MODULE_PARM_DESC(edac_mc_log_ue, module_param(edac_mc_log_ce, int, 0644); MODULE_PARM_DESC(edac_mc_log_ce, "Log correctable error to console: 0=off 1=on"); -module_param(edac_mc_poll_msec, int, 0644); +module_param_call(edac_mc_poll_msec, edac_set_poll_msec, param_get_int, + &edac_mc_poll_msec, 0644); MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds"); /* @@ -103,16 +123,6 @@ static const char *edac_caps[] = { -/* - * /sys/devices/system/edac/mc; - * data structures and methods - */ -static ssize_t memctrl_int_show(void *ptr, char *buffer) -{ - int *value = (int *)ptr; - return sprintf(buffer, "%u\n", *value); -} - static ssize_t memctrl_int_store(void *ptr, const char *buffer, size_t count) { int *value = (int *)ptr; @@ -123,23 +133,6 @@ static ssize_t memctrl_int_store(void *ptr, const char *buffer, size_t count) return count; } -/* - * mc poll_msec time value - */ -static ssize_t poll_msec_int_store(void *ptr, const char *buffer, size_t count) -{ - int *value = (int *)ptr; - - if (isdigit(*buffer)) { - *value = simple_strtoul(buffer, NULL, 0); - - /* notify edac_mc engine to reset the poll period */ - edac_mc_reset_delay_period(*value); - } - - return count; -} - /* EDAC sysfs CSROW data structures and methods */ @@ -185,7 +178,11 @@ static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data, static ssize_t channel_dimm_label_show(struct csrow_info *csrow, char *data, int channel) { - return snprintf(data, EDAC_MC_LABEL_LEN, "%s", + /* if field has not been initialized, there is nothing to send */ + if (!csrow->channels[channel].label[0]) + return 0; + + return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", csrow->channels[channel].label); } @@ -649,98 +646,10 @@ static struct kobj_type ktype_mci = { .default_attrs = (struct attribute **)mci_attr, }; -/* show/store, tables, etc for the MC kset */ - - -struct memctrl_dev_attribute { - struct attribute attr; - void *value; - ssize_t(*show) (void *, char *); - ssize_t(*store) (void *, const char *, size_t); -}; - -/* Set of show/store abstract level functions for memory control object */ -static ssize_t memctrl_dev_show(struct kobject *kobj, - struct attribute *attr, char *buffer) -{ - struct memctrl_dev_attribute *memctrl_dev; - memctrl_dev = (struct memctrl_dev_attribute *)attr; - - if (memctrl_dev->show) - return memctrl_dev->show(memctrl_dev->value, buffer); - - return -EIO; -} - -static ssize_t memctrl_dev_store(struct kobject *kobj, struct attribute *attr, - const char *buffer, size_t count) -{ - struct memctrl_dev_attribute *memctrl_dev; - memctrl_dev = (struct memctrl_dev_attribute *)attr; - - if (memctrl_dev->store) - return memctrl_dev->store(memctrl_dev->value, buffer, count); - - return -EIO; -} - -static struct sysfs_ops memctrlfs_ops = { - .show = memctrl_dev_show, - .store = memctrl_dev_store -}; - -#define MEMCTRL_ATTR(_name, _mode, _show, _store) \ -static struct memctrl_dev_attribute attr_##_name = { \ - .attr = {.name = __stringify(_name), .mode = _mode }, \ - .value = &_name, \ - .show = _show, \ - .store = _store, \ -}; - -#define MEMCTRL_STRING_ATTR(_name, _data, _mode, _show, _store) \ -static struct memctrl_dev_attribute attr_##_name = { \ - .attr = {.name = __stringify(_name), .mode = _mode }, \ - .value = _data, \ - .show = _show, \ - .store = _store, \ -}; - -/* csrow<id> control files */ -MEMCTRL_ATTR(edac_mc_panic_on_ue, - S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store); - -MEMCTRL_ATTR(edac_mc_log_ue, - S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store); - -MEMCTRL_ATTR(edac_mc_log_ce, - S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store); - -MEMCTRL_ATTR(edac_mc_poll_msec, - S_IRUGO | S_IWUSR, memctrl_int_show, poll_msec_int_store); - -/* Base Attributes of the memory ECC object */ -static struct memctrl_dev_attribute *memctrl_attr[] = { - &attr_edac_mc_panic_on_ue, - &attr_edac_mc_log_ue, - &attr_edac_mc_log_ce, - &attr_edac_mc_poll_msec, - NULL, -}; - - -/* the ktype for the mc_kset internal kobj */ -static struct kobj_type ktype_mc_set_attribs = { - .sysfs_ops = &memctrlfs_ops, - .default_attrs = (struct attribute **)memctrl_attr, -}; - /* EDAC memory controller sysfs kset: * /sys/devices/system/edac/mc */ -static struct kset mc_kset = { - .kobj = {.ktype = &ktype_mc_set_attribs }, -}; - +static struct kset *mc_kset; /* * edac_mc_register_sysfs_main_kobj @@ -771,7 +680,7 @@ int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci) } /* this instance become part of the mc_kset */ - kobj_mci->kset = &mc_kset; + kobj_mci->kset = mc_kset; /* register the mc<id> kobject to the mc_kset */ err = kobject_init_and_add(kobj_mci, &ktype_mci, NULL, @@ -1001,12 +910,9 @@ int edac_sysfs_setup_mc_kset(void) } /* Init the MC's kobject */ - kobject_set_name(&mc_kset.kobj, "mc"); - mc_kset.kobj.parent = &edac_class->kset.kobj; - - /* register the mc_kset */ - err = kset_register(&mc_kset); - if (err) { + mc_kset = kset_create_and_add("mc", NULL, &edac_class->kset.kobj); + if (!mc_kset) { + err = -ENOMEM; debugf1("%s() Failed to register '.../edac/mc'\n", __func__); goto fail_out; } @@ -1028,6 +934,6 @@ fail_out: */ void edac_sysfs_teardown_mc_kset(void) { - kset_unregister(&mc_kset); + kset_unregister(mc_kset); } diff --git a/drivers/edac/edac_pci_sysfs.c b/drivers/edac/edac_pci_sysfs.c index 2c1fa1bb6df2..5c153dccc95e 100644 --- a/drivers/edac/edac_pci_sysfs.c +++ b/drivers/edac/edac_pci_sysfs.c @@ -28,7 +28,7 @@ static int edac_pci_poll_msec = 1000; /* one second workq period */ static atomic_t pci_parity_count = ATOMIC_INIT(0); static atomic_t pci_nonparity_count = ATOMIC_INIT(0); -static struct kobject edac_pci_top_main_kobj; +static struct kobject *edac_pci_top_main_kobj; static atomic_t edac_pci_sysfs_refcount = ATOMIC_INIT(0); /* getter functions for the data variables */ @@ -83,7 +83,7 @@ static void edac_pci_instance_release(struct kobject *kobj) pci = to_instance(kobj); /* decrement reference count on top main kobj */ - kobject_put(&edac_pci_top_main_kobj); + kobject_put(edac_pci_top_main_kobj); kfree(pci); /* Free the control struct */ } @@ -166,7 +166,7 @@ static int edac_pci_create_instance_kobj(struct edac_pci_ctl_info *pci, int idx) * track the number of PCI instances we have, and thus nest * properly on keeping the module loaded */ - main_kobj = kobject_get(&edac_pci_top_main_kobj); + main_kobj = kobject_get(edac_pci_top_main_kobj); if (!main_kobj) { err = -ENODEV; goto error_out; @@ -174,11 +174,11 @@ static int edac_pci_create_instance_kobj(struct edac_pci_ctl_info *pci, int idx) /* And now register this new kobject under the main kobj */ err = kobject_init_and_add(&pci->kobj, &ktype_pci_instance, - &edac_pci_top_main_kobj, "pci%d", idx); + edac_pci_top_main_kobj, "pci%d", idx); if (err != 0) { debugf2("%s() failed to register instance pci%d\n", __func__, idx); - kobject_put(&edac_pci_top_main_kobj); + kobject_put(edac_pci_top_main_kobj); goto error_out; } @@ -316,9 +316,10 @@ static struct edac_pci_dev_attribute *edac_pci_attr[] = { */ static void edac_pci_release_main_kobj(struct kobject *kobj) { - debugf0("%s() here to module_put(THIS_MODULE)\n", __func__); + kfree(kobj); + /* last reference to top EDAC PCI kobject has been removed, * NOW release our ref count on the core module */ @@ -369,8 +370,16 @@ static int edac_pci_main_kobj_setup(void) goto decrement_count_fail; } + edac_pci_top_main_kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL); + if (!edac_pci_top_main_kobj) { + debugf1("Failed to allocate\n"); + err = -ENOMEM; + goto kzalloc_fail; + } + /* Instanstiate the pci object */ - err = kobject_init_and_add(&edac_pci_top_main_kobj, &ktype_edac_pci_main_kobj, + err = kobject_init_and_add(edac_pci_top_main_kobj, + &ktype_edac_pci_main_kobj, &edac_class->kset.kobj, "pci"); if (err) { debugf1("Failed to register '.../edac/pci'\n"); @@ -381,13 +390,16 @@ static int edac_pci_main_kobj_setup(void) * for EDAC PCI, then edac_pci_main_kobj_teardown() * must be used, for resources to be cleaned up properly */ - kobject_uevent(&edac_pci_top_main_kobj, KOBJ_ADD); + kobject_uevent(edac_pci_top_main_kobj, KOBJ_ADD); debugf1("Registered '.../edac/pci' kobject\n"); return 0; /* Error unwind statck */ kobject_init_and_add_fail: + kfree(edac_pci_top_main_kobj); + +kzalloc_fail: module_put(THIS_MODULE); decrement_count_fail: @@ -414,7 +426,7 @@ static void edac_pci_main_kobj_teardown(void) if (atomic_dec_return(&edac_pci_sysfs_refcount) == 0) { debugf0("%s() called kobject_put on main kobj\n", __func__); - kobject_put(&edac_pci_top_main_kobj); + kobject_put(edac_pci_top_main_kobj); } } diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c new file mode 100644 index 000000000000..22db05a67bfb --- /dev/null +++ b/drivers/edac/i5100_edac.c @@ -0,0 +1,981 @@ +/* + * Intel 5100 Memory Controllers kernel module + * + * This file may be distributed under the terms of the + * GNU General Public License. + * + * This module is based on the following document: + * + * Intel 5100X Chipset Memory Controller Hub (MCH) - Datasheet + * http://download.intel.com/design/chipsets/datashts/318378.pdf + * + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> +#include <linux/slab.h> +#include <linux/edac.h> +#include <linux/delay.h> +#include <linux/mmzone.h> + +#include "edac_core.h" + +/* register addresses */ + +/* device 16, func 1 */ +#define I5100_MC 0x40 /* Memory Control Register */ +#define I5100_MS 0x44 /* Memory Status Register */ +#define I5100_SPDDATA 0x48 /* Serial Presence Detect Status Reg */ +#define I5100_SPDCMD 0x4c /* Serial Presence Detect Command Reg */ +#define I5100_TOLM 0x6c /* Top of Low Memory */ +#define I5100_MIR0 0x80 /* Memory Interleave Range 0 */ +#define I5100_MIR1 0x84 /* Memory Interleave Range 1 */ +#define I5100_AMIR_0 0x8c /* Adjusted Memory Interleave Range 0 */ +#define I5100_AMIR_1 0x90 /* Adjusted Memory Interleave Range 1 */ +#define I5100_FERR_NF_MEM 0xa0 /* MC First Non Fatal Errors */ +#define I5100_FERR_NF_MEM_M16ERR_MASK (1 << 16) +#define I5100_FERR_NF_MEM_M15ERR_MASK (1 << 15) +#define I5100_FERR_NF_MEM_M14ERR_MASK (1 << 14) +#define I5100_FERR_NF_MEM_M12ERR_MASK (1 << 12) +#define I5100_FERR_NF_MEM_M11ERR_MASK (1 << 11) +#define I5100_FERR_NF_MEM_M10ERR_MASK (1 << 10) +#define I5100_FERR_NF_MEM_M6ERR_MASK (1 << 6) +#define I5100_FERR_NF_MEM_M5ERR_MASK (1 << 5) +#define I5100_FERR_NF_MEM_M4ERR_MASK (1 << 4) +#define I5100_FERR_NF_MEM_M1ERR_MASK 1 +#define I5100_FERR_NF_MEM_ANY_MASK \ + (I5100_FERR_NF_MEM_M16ERR_MASK | \ + I5100_FERR_NF_MEM_M15ERR_MASK | \ + I5100_FERR_NF_MEM_M14ERR_MASK | \ + I5100_FERR_NF_MEM_M12ERR_MASK | \ + I5100_FERR_NF_MEM_M11ERR_MASK | \ + I5100_FERR_NF_MEM_M10ERR_MASK | \ + I5100_FERR_NF_MEM_M6ERR_MASK | \ + I5100_FERR_NF_MEM_M5ERR_MASK | \ + I5100_FERR_NF_MEM_M4ERR_MASK | \ + I5100_FERR_NF_MEM_M1ERR_MASK) +#define I5100_NERR_NF_MEM 0xa4 /* MC Next Non-Fatal Errors */ +#define I5100_EMASK_MEM 0xa8 /* MC Error Mask Register */ + +/* device 21 and 22, func 0 */ +#define I5100_MTR_0 0x154 /* Memory Technology Registers 0-3 */ +#define I5100_DMIR 0x15c /* DIMM Interleave Range */ +#define I5100_VALIDLOG 0x18c /* Valid Log Markers */ +#define I5100_NRECMEMA 0x190 /* Non-Recoverable Memory Error Log Reg A */ +#define I5100_NRECMEMB 0x194 /* Non-Recoverable Memory Error Log Reg B */ +#define I5100_REDMEMA 0x198 /* Recoverable Memory Data Error Log Reg A */ +#define I5100_REDMEMB 0x19c /* Recoverable Memory Data Error Log Reg B */ +#define I5100_RECMEMA 0x1a0 /* Recoverable Memory Error Log Reg A */ +#define I5100_RECMEMB 0x1a4 /* Recoverable Memory Error Log Reg B */ +#define I5100_MTR_4 0x1b0 /* Memory Technology Registers 4,5 */ + +/* bit field accessors */ + +static inline u32 i5100_mc_errdeten(u32 mc) +{ + return mc >> 5 & 1; +} + +static inline u16 i5100_spddata_rdo(u16 a) +{ + return a >> 15 & 1; +} + +static inline u16 i5100_spddata_sbe(u16 a) +{ + return a >> 13 & 1; +} + +static inline u16 i5100_spddata_busy(u16 a) +{ + return a >> 12 & 1; +} + +static inline u16 i5100_spddata_data(u16 a) +{ + return a & ((1 << 8) - 1); +} + +static inline u32 i5100_spdcmd_create(u32 dti, u32 ckovrd, u32 sa, u32 ba, + u32 data, u32 cmd) +{ + return ((dti & ((1 << 4) - 1)) << 28) | + ((ckovrd & 1) << 27) | + ((sa & ((1 << 3) - 1)) << 24) | + ((ba & ((1 << 8) - 1)) << 16) | + ((data & ((1 << 8) - 1)) << 8) | + (cmd & 1); +} + +static inline u16 i5100_tolm_tolm(u16 a) +{ + return a >> 12 & ((1 << 4) - 1); +} + +static inline u16 i5100_mir_limit(u16 a) +{ + return a >> 4 & ((1 << 12) - 1); +} + +static inline u16 i5100_mir_way1(u16 a) +{ + return a >> 1 & 1; +} + +static inline u16 i5100_mir_way0(u16 a) +{ + return a & 1; +} + +static inline u32 i5100_ferr_nf_mem_chan_indx(u32 a) +{ + return a >> 28 & 1; +} + +static inline u32 i5100_ferr_nf_mem_any(u32 a) +{ + return a & I5100_FERR_NF_MEM_ANY_MASK; +} + +static inline u32 i5100_nerr_nf_mem_any(u32 a) +{ + return i5100_ferr_nf_mem_any(a); +} + +static inline u32 i5100_dmir_limit(u32 a) +{ + return a >> 16 & ((1 << 11) - 1); +} + +static inline u32 i5100_dmir_rank(u32 a, u32 i) +{ + return a >> (4 * i) & ((1 << 2) - 1); +} + +static inline u16 i5100_mtr_present(u16 a) +{ + return a >> 10 & 1; +} + +static inline u16 i5100_mtr_ethrottle(u16 a) +{ + return a >> 9 & 1; +} + +static inline u16 i5100_mtr_width(u16 a) +{ + return a >> 8 & 1; +} + +static inline u16 i5100_mtr_numbank(u16 a) +{ + return a >> 6 & 1; +} + +static inline u16 i5100_mtr_numrow(u16 a) +{ + return a >> 2 & ((1 << 2) - 1); +} + +static inline u16 i5100_mtr_numcol(u16 a) +{ + return a & ((1 << 2) - 1); +} + + +static inline u32 i5100_validlog_redmemvalid(u32 a) +{ + return a >> 2 & 1; +} + +static inline u32 i5100_validlog_recmemvalid(u32 a) +{ + return a >> 1 & 1; +} + +static inline u32 i5100_validlog_nrecmemvalid(u32 a) +{ + return a & 1; +} + +static inline u32 i5100_nrecmema_merr(u32 a) +{ + return a >> 15 & ((1 << 5) - 1); +} + +static inline u32 i5100_nrecmema_bank(u32 a) +{ + return a >> 12 & ((1 << 3) - 1); +} + +static inline u32 i5100_nrecmema_rank(u32 a) +{ + return a >> 8 & ((1 << 3) - 1); +} + +static inline u32 i5100_nrecmema_dm_buf_id(u32 a) +{ + return a & ((1 << 8) - 1); +} + +static inline u32 i5100_nrecmemb_cas(u32 a) +{ + return a >> 16 & ((1 << 13) - 1); +} + +static inline u32 i5100_nrecmemb_ras(u32 a) +{ + return a & ((1 << 16) - 1); +} + +static inline u32 i5100_redmemb_ecc_locator(u32 a) +{ + return a & ((1 << 18) - 1); +} + +static inline u32 i5100_recmema_merr(u32 a) +{ + return i5100_nrecmema_merr(a); +} + +static inline u32 i5100_recmema_bank(u32 a) +{ + return i5100_nrecmema_bank(a); +} + +static inline u32 i5100_recmema_rank(u32 a) +{ + return i5100_nrecmema_rank(a); +} + +static inline u32 i5100_recmema_dm_buf_id(u32 a) +{ + return i5100_nrecmema_dm_buf_id(a); +} + +static inline u32 i5100_recmemb_cas(u32 a) +{ + return i5100_nrecmemb_cas(a); +} + +static inline u32 i5100_recmemb_ras(u32 a) +{ + return i5100_nrecmemb_ras(a); +} + +/* some generic limits */ +#define I5100_MAX_RANKS_PER_CTLR 6 +#define I5100_MAX_CTLRS 2 +#define I5100_MAX_RANKS_PER_DIMM 4 +#define I5100_DIMM_ADDR_LINES (6 - 3) /* 64 bits / 8 bits per byte */ +#define I5100_MAX_DIMM_SLOTS_PER_CTLR 4 +#define I5100_MAX_RANK_INTERLEAVE 4 +#define I5100_MAX_DMIRS 5 + +struct i5100_priv { + /* ranks on each dimm -- 0 maps to not present -- obtained via SPD */ + int dimm_numrank[I5100_MAX_CTLRS][I5100_MAX_DIMM_SLOTS_PER_CTLR]; + + /* + * mainboard chip select map -- maps i5100 chip selects to + * DIMM slot chip selects. In the case of only 4 ranks per + * controller, the mapping is fairly obvious but not unique. + * we map -1 -> NC and assume both controllers use the same + * map... + * + */ + int dimm_csmap[I5100_MAX_DIMM_SLOTS_PER_CTLR][I5100_MAX_RANKS_PER_DIMM]; + + /* memory interleave range */ + struct { + u64 limit; + unsigned way[2]; + } mir[I5100_MAX_CTLRS]; + + /* adjusted memory interleave range register */ + unsigned amir[I5100_MAX_CTLRS]; + + /* dimm interleave range */ + struct { + unsigned rank[I5100_MAX_RANK_INTERLEAVE]; + u64 limit; + } dmir[I5100_MAX_CTLRS][I5100_MAX_DMIRS]; + + /* memory technology registers... */ + struct { + unsigned present; /* 0 or 1 */ + unsigned ethrottle; /* 0 or 1 */ + unsigned width; /* 4 or 8 bits */ + unsigned numbank; /* 2 or 3 lines */ + unsigned numrow; /* 13 .. 16 lines */ + unsigned numcol; /* 11 .. 12 lines */ + } mtr[I5100_MAX_CTLRS][I5100_MAX_RANKS_PER_CTLR]; + + u64 tolm; /* top of low memory in bytes */ + unsigned ranksperctlr; /* number of ranks per controller */ + + struct pci_dev *mc; /* device 16 func 1 */ + struct pci_dev *ch0mm; /* device 21 func 0 */ + struct pci_dev *ch1mm; /* device 22 func 0 */ +}; + +/* map a rank/ctlr to a slot number on the mainboard */ +static int i5100_rank_to_slot(const struct mem_ctl_info *mci, + int ctlr, int rank) +{ + const struct i5100_priv *priv = mci->pvt_info; + int i; + + for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CTLR; i++) { + int j; + const int numrank = priv->dimm_numrank[ctlr][i]; + + for (j = 0; j < numrank; j++) + if (priv->dimm_csmap[i][j] == rank) + return i * 2 + ctlr; + } + + return -1; +} + +static const char *i5100_err_msg(unsigned err) +{ + static const char *merrs[] = { + "unknown", /* 0 */ + "uncorrectable data ECC on replay", /* 1 */ + "unknown", /* 2 */ + "unknown", /* 3 */ + "aliased uncorrectable demand data ECC", /* 4 */ + "aliased uncorrectable spare-copy data ECC", /* 5 */ + "aliased uncorrectable patrol data ECC", /* 6 */ + "unknown", /* 7 */ + "unknown", /* 8 */ + "unknown", /* 9 */ + "non-aliased uncorrectable demand data ECC", /* 10 */ + "non-aliased uncorrectable spare-copy data ECC", /* 11 */ + "non-aliased uncorrectable patrol data ECC", /* 12 */ + "unknown", /* 13 */ + "correctable demand data ECC", /* 14 */ + "correctable spare-copy data ECC", /* 15 */ + "correctable patrol data ECC", /* 16 */ + "unknown", /* 17 */ + "SPD protocol error", /* 18 */ + "unknown", /* 19 */ + "spare copy initiated", /* 20 */ + "spare copy completed", /* 21 */ + }; + unsigned i; + + for (i = 0; i < ARRAY_SIZE(merrs); i++) + if (1 << i & err) + return merrs[i]; + + return "none"; +} + +/* convert csrow index into a rank (per controller -- 0..5) */ +static int i5100_csrow_to_rank(const struct mem_ctl_info *mci, int csrow) +{ + const struct i5100_priv *priv = mci->pvt_info; + + return csrow % priv->ranksperctlr; +} + +/* convert csrow index into a controller (0..1) */ +static int i5100_csrow_to_cntlr(const struct mem_ctl_info *mci, int csrow) +{ + const struct i5100_priv *priv = mci->pvt_info; + + return csrow / priv->ranksperctlr; +} + +static unsigned i5100_rank_to_csrow(const struct mem_ctl_info *mci, + int ctlr, int rank) +{ + const struct i5100_priv *priv = mci->pvt_info; + + return ctlr * priv->ranksperctlr + rank; +} + +static void i5100_handle_ce(struct mem_ctl_info *mci, + int ctlr, + unsigned bank, + unsigned rank, + unsigned long syndrome, + unsigned cas, + unsigned ras, + const char *msg) +{ + const int csrow = i5100_rank_to_csrow(mci, ctlr, rank); + + printk(KERN_ERR + "CE ctlr %d, bank %u, rank %u, syndrome 0x%lx, " + "cas %u, ras %u, csrow %u, label \"%s\": %s\n", + ctlr, bank, rank, syndrome, cas, ras, + csrow, mci->csrows[csrow].channels[0].label, msg); + + mci->ce_count++; + mci->csrows[csrow].ce_count++; + mci->csrows[csrow].channels[0].ce_count++; +} + +static void i5100_handle_ue(struct mem_ctl_info *mci, + int ctlr, + unsigned bank, + unsigned rank, + unsigned long syndrome, + unsigned cas, + unsigned ras, + const char *msg) +{ + const int csrow = i5100_rank_to_csrow(mci, ctlr, rank); + + printk(KERN_ERR + "UE ctlr %d, bank %u, rank %u, syndrome 0x%lx, " + "cas %u, ras %u, csrow %u, label \"%s\": %s\n", + ctlr, bank, rank, syndrome, cas, ras, + csrow, mci->csrows[csrow].channels[0].label, msg); + + mci->ue_count++; + mci->csrows[csrow].ue_count++; +} + +static void i5100_read_log(struct mem_ctl_info *mci, int ctlr, + u32 ferr, u32 nerr) +{ + struct i5100_priv *priv = mci->pvt_info; + struct pci_dev *pdev = (ctlr) ? priv->ch1mm : priv->ch0mm; + u32 dw; + u32 dw2; + unsigned syndrome = 0; + unsigned ecc_loc = 0; + unsigned merr; + unsigned bank; + unsigned rank; + unsigned cas; + unsigned ras; + + pci_read_config_dword(pdev, I5100_VALIDLOG, &dw); + + if (i5100_validlog_redmemvalid(dw)) { + pci_read_config_dword(pdev, I5100_REDMEMA, &dw2); + syndrome = dw2; + pci_read_config_dword(pdev, I5100_REDMEMB, &dw2); + ecc_loc = i5100_redmemb_ecc_locator(dw2); + } + + if (i5100_validlog_recmemvalid(dw)) { + const char *msg; + + pci_read_config_dword(pdev, I5100_RECMEMA, &dw2); + merr = i5100_recmema_merr(dw2); + bank = i5100_recmema_bank(dw2); + rank = i5100_recmema_rank(dw2); + + pci_read_config_dword(pdev, I5100_RECMEMB, &dw2); + cas = i5100_recmemb_cas(dw2); + ras = i5100_recmemb_ras(dw2); + + /* FIXME: not really sure if this is what merr is... + */ + if (!merr) + msg = i5100_err_msg(ferr); + else + msg = i5100_err_msg(nerr); + + i5100_handle_ce(mci, ctlr, bank, rank, syndrome, cas, ras, msg); + } + + if (i5100_validlog_nrecmemvalid(dw)) { + const char *msg; + + pci_read_config_dword(pdev, I5100_NRECMEMA, &dw2); + merr = i5100_nrecmema_merr(dw2); + bank = i5100_nrecmema_bank(dw2); + rank = i5100_nrecmema_rank(dw2); + + pci_read_config_dword(pdev, I5100_NRECMEMB, &dw2); + cas = i5100_nrecmemb_cas(dw2); + ras = i5100_nrecmemb_ras(dw2); + + /* FIXME: not really sure if this is what merr is... + */ + if (!merr) + msg = i5100_err_msg(ferr); + else + msg = i5100_err_msg(nerr); + + i5100_handle_ue(mci, ctlr, bank, rank, syndrome, cas, ras, msg); + } + + pci_write_config_dword(pdev, I5100_VALIDLOG, dw); +} + +static void i5100_check_error(struct mem_ctl_info *mci) +{ + struct i5100_priv *priv = mci->pvt_info; + u32 dw; + + + pci_read_config_dword(priv->mc, I5100_FERR_NF_MEM, &dw); + if (i5100_ferr_nf_mem_any(dw)) { + u32 dw2; + + pci_read_config_dword(priv->mc, I5100_NERR_NF_MEM, &dw2); + if (dw2) + pci_write_config_dword(priv->mc, I5100_NERR_NF_MEM, + dw2); + pci_write_config_dword(priv->mc, I5100_FERR_NF_MEM, dw); + + i5100_read_log(mci, i5100_ferr_nf_mem_chan_indx(dw), + i5100_ferr_nf_mem_any(dw), + i5100_nerr_nf_mem_any(dw2)); + } +} + +static struct pci_dev *pci_get_device_func(unsigned vendor, + unsigned device, + unsigned func) +{ + struct pci_dev *ret = NULL; + + while (1) { + ret = pci_get_device(vendor, device, ret); + + if (!ret) + break; + + if (PCI_FUNC(ret->devfn) == func) + break; + } + + return ret; +} + +static unsigned long __devinit i5100_npages(struct mem_ctl_info *mci, + int csrow) +{ + struct i5100_priv *priv = mci->pvt_info; + const unsigned ctlr_rank = i5100_csrow_to_rank(mci, csrow); + const unsigned ctlr = i5100_csrow_to_cntlr(mci, csrow); + unsigned addr_lines; + + /* dimm present? */ + if (!priv->mtr[ctlr][ctlr_rank].present) + return 0ULL; + + addr_lines = + I5100_DIMM_ADDR_LINES + + priv->mtr[ctlr][ctlr_rank].numcol + + priv->mtr[ctlr][ctlr_rank].numrow + + priv->mtr[ctlr][ctlr_rank].numbank; + + return (unsigned long) + ((unsigned long long) (1ULL << addr_lines) / PAGE_SIZE); +} + +static void __devinit i5100_init_mtr(struct mem_ctl_info *mci) +{ + struct i5100_priv *priv = mci->pvt_info; + struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm }; + int i; + + for (i = 0; i < I5100_MAX_CTLRS; i++) { + int j; + struct pci_dev *pdev = mms[i]; + + for (j = 0; j < I5100_MAX_RANKS_PER_CTLR; j++) { + const unsigned addr = + (j < 4) ? I5100_MTR_0 + j * 2 : + I5100_MTR_4 + (j - 4) * 2; + u16 w; + + pci_read_config_word(pdev, addr, &w); + + priv->mtr[i][j].present = i5100_mtr_present(w); + priv->mtr[i][j].ethrottle = i5100_mtr_ethrottle(w); + priv->mtr[i][j].width = 4 + 4 * i5100_mtr_width(w); + priv->mtr[i][j].numbank = 2 + i5100_mtr_numbank(w); + priv->mtr[i][j].numrow = 13 + i5100_mtr_numrow(w); + priv->mtr[i][j].numcol = 10 + i5100_mtr_numcol(w); + } + } +} + +/* + * FIXME: make this into a real i2c adapter (so that dimm-decode + * will work)? + */ +static int i5100_read_spd_byte(const struct mem_ctl_info *mci, + u8 ch, u8 slot, u8 addr, u8 *byte) +{ + struct i5100_priv *priv = mci->pvt_info; + u16 w; + unsigned long et; + + pci_read_config_word(priv->mc, I5100_SPDDATA, &w); + if (i5100_spddata_busy(w)) + return -1; + + pci_write_config_dword(priv->mc, I5100_SPDCMD, + i5100_spdcmd_create(0xa, 1, ch * 4 + slot, addr, + 0, 0)); + + /* wait up to 100ms */ + et = jiffies + HZ / 10; + udelay(100); + while (1) { + pci_read_config_word(priv->mc, I5100_SPDDATA, &w); + if (!i5100_spddata_busy(w)) + break; + udelay(100); + } + + if (!i5100_spddata_rdo(w) || i5100_spddata_sbe(w)) + return -1; + + *byte = i5100_spddata_data(w); + + return 0; +} + +/* + * fill dimm chip select map + * + * FIXME: + * o only valid for 4 ranks per controller + * o not the only way to may chip selects to dimm slots + * o investigate if there is some way to obtain this map from the bios + */ +static void __devinit i5100_init_dimm_csmap(struct mem_ctl_info *mci) +{ + struct i5100_priv *priv = mci->pvt_info; + int i; + + WARN_ON(priv->ranksperctlr != 4); + + for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CTLR; i++) { + int j; + + for (j = 0; j < I5100_MAX_RANKS_PER_DIMM; j++) + priv->dimm_csmap[i][j] = -1; /* default NC */ + } + + /* only 2 chip selects per slot... */ + priv->dimm_csmap[0][0] = 0; + priv->dimm_csmap[0][1] = 3; + priv->dimm_csmap[1][0] = 1; + priv->dimm_csmap[1][1] = 2; + priv->dimm_csmap[2][0] = 2; + priv->dimm_csmap[3][0] = 3; +} + +static void __devinit i5100_init_dimm_layout(struct pci_dev *pdev, + struct mem_ctl_info *mci) +{ + struct i5100_priv *priv = mci->pvt_info; + int i; + + for (i = 0; i < I5100_MAX_CTLRS; i++) { + int j; + + for (j = 0; j < I5100_MAX_DIMM_SLOTS_PER_CTLR; j++) { + u8 rank; + + if (i5100_read_spd_byte(mci, i, j, 5, &rank) < 0) + priv->dimm_numrank[i][j] = 0; + else + priv->dimm_numrank[i][j] = (rank & 3) + 1; + } + } + + i5100_init_dimm_csmap(mci); +} + +static void __devinit i5100_init_interleaving(struct pci_dev *pdev, + struct mem_ctl_info *mci) +{ + u16 w; + u32 dw; + struct i5100_priv *priv = mci->pvt_info; + struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm }; + int i; + + pci_read_config_word(pdev, I5100_TOLM, &w); + priv->tolm = (u64) i5100_tolm_tolm(w) * 256 * 1024 * 1024; + + pci_read_config_word(pdev, I5100_MIR0, &w); + priv->mir[0].limit = (u64) i5100_mir_limit(w) << 28; + priv->mir[0].way[1] = i5100_mir_way1(w); + priv->mir[0].way[0] = i5100_mir_way0(w); + + pci_read_config_word(pdev, I5100_MIR1, &w); + priv->mir[1].limit = (u64) i5100_mir_limit(w) << 28; + priv->mir[1].way[1] = i5100_mir_way1(w); + priv->mir[1].way[0] = i5100_mir_way0(w); + + pci_read_config_word(pdev, I5100_AMIR_0, &w); + priv->amir[0] = w; + pci_read_config_word(pdev, I5100_AMIR_1, &w); + priv->amir[1] = w; + + for (i = 0; i < I5100_MAX_CTLRS; i++) { + int j; + + for (j = 0; j < 5; j++) { + int k; + + pci_read_config_dword(mms[i], I5100_DMIR + j * 4, &dw); + + priv->dmir[i][j].limit = + (u64) i5100_dmir_limit(dw) << 28; + for (k = 0; k < I5100_MAX_RANKS_PER_DIMM; k++) + priv->dmir[i][j].rank[k] = + i5100_dmir_rank(dw, k); + } + } + + i5100_init_mtr(mci); +} + +static void __devinit i5100_init_csrows(struct mem_ctl_info *mci) +{ + int i; + unsigned long total_pages = 0UL; + struct i5100_priv *priv = mci->pvt_info; + + for (i = 0; i < mci->nr_csrows; i++) { + const unsigned long npages = i5100_npages(mci, i); + const unsigned cntlr = i5100_csrow_to_cntlr(mci, i); + const unsigned rank = i5100_csrow_to_rank(mci, i); + + if (!npages) + continue; + + /* + * FIXME: these two are totally bogus -- I don't see how to + * map them correctly to this structure... + */ + mci->csrows[i].first_page = total_pages; + mci->csrows[i].last_page = total_pages + npages - 1; + mci->csrows[i].page_mask = 0UL; + + mci->csrows[i].nr_pages = npages; + mci->csrows[i].grain = 32; + mci->csrows[i].csrow_idx = i; + mci->csrows[i].dtype = + (priv->mtr[cntlr][rank].width == 4) ? DEV_X4 : DEV_X8; + mci->csrows[i].ue_count = 0; + mci->csrows[i].ce_count = 0; + mci->csrows[i].mtype = MEM_RDDR2; + mci->csrows[i].edac_mode = EDAC_SECDED; + mci->csrows[i].mci = mci; + mci->csrows[i].nr_channels = 1; + mci->csrows[i].channels[0].chan_idx = 0; + mci->csrows[i].channels[0].ce_count = 0; + mci->csrows[i].channels[0].csrow = mci->csrows + i; + snprintf(mci->csrows[i].channels[0].label, + sizeof(mci->csrows[i].channels[0].label), + "DIMM%u", i5100_rank_to_slot(mci, cntlr, rank)); + + total_pages += npages; + } +} + +static int __devinit i5100_init_one(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int rc; + struct mem_ctl_info *mci; + struct i5100_priv *priv; + struct pci_dev *ch0mm, *ch1mm; + int ret = 0; + u32 dw; + int ranksperch; + + if (PCI_FUNC(pdev->devfn) != 1) + return -ENODEV; + + rc = pci_enable_device(pdev); + if (rc < 0) { + ret = rc; + goto bail; + } + + /* ECC enabled? */ + pci_read_config_dword(pdev, I5100_MC, &dw); + if (!i5100_mc_errdeten(dw)) { + printk(KERN_INFO "i5100_edac: ECC not enabled.\n"); + ret = -ENODEV; + goto bail_pdev; + } + + /* figure out how many ranks, from strapped state of 48GB_Mode input */ + pci_read_config_dword(pdev, I5100_MS, &dw); + ranksperch = !!(dw & (1 << 8)) * 2 + 4; + + if (ranksperch != 4) { + /* FIXME: get 6 ranks / controller to work - need hw... */ + printk(KERN_INFO "i5100_edac: unsupported configuration.\n"); + ret = -ENODEV; + goto bail_pdev; + } + + /* enable error reporting... */ + pci_read_config_dword(pdev, I5100_EMASK_MEM, &dw); + dw &= ~I5100_FERR_NF_MEM_ANY_MASK; + pci_write_config_dword(pdev, I5100_EMASK_MEM, dw); + + /* device 21, func 0, Channel 0 Memory Map, Error Flag/Mask, etc... */ + ch0mm = pci_get_device_func(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_5100_21, 0); + if (!ch0mm) { + ret = -ENODEV; + goto bail_pdev; + } + + rc = pci_enable_device(ch0mm); + if (rc < 0) { + ret = rc; + goto bail_ch0; + } + + /* device 22, func 0, Channel 1 Memory Map, Error Flag/Mask, etc... */ + ch1mm = pci_get_device_func(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_5100_22, 0); + if (!ch1mm) { + ret = -ENODEV; + goto bail_disable_ch0; + } + + rc = pci_enable_device(ch1mm); + if (rc < 0) { + ret = rc; + goto bail_ch1; + } + + mci = edac_mc_alloc(sizeof(*priv), ranksperch * 2, 1, 0); + if (!mci) { + ret = -ENOMEM; + goto bail_disable_ch1; + } + + mci->dev = &pdev->dev; + + priv = mci->pvt_info; + priv->ranksperctlr = ranksperch; + priv->mc = pdev; + priv->ch0mm = ch0mm; + priv->ch1mm = ch1mm; + + i5100_init_dimm_layout(pdev, mci); + i5100_init_interleaving(pdev, mci); + + mci->mtype_cap = MEM_FLAG_FB_DDR2; + mci->edac_ctl_cap = EDAC_FLAG_SECDED; + mci->edac_cap = EDAC_FLAG_SECDED; + mci->mod_name = "i5100_edac.c"; + mci->mod_ver = "not versioned"; + mci->ctl_name = "i5100"; + mci->dev_name = pci_name(pdev); + mci->ctl_page_to_phys = NULL; + + mci->edac_check = i5100_check_error; + + i5100_init_csrows(mci); + + /* this strange construction seems to be in every driver, dunno why */ + switch (edac_op_state) { + case EDAC_OPSTATE_POLL: + case EDAC_OPSTATE_NMI: + break; + default: + edac_op_state = EDAC_OPSTATE_POLL; + break; + } + + if (edac_mc_add_mc(mci)) { + ret = -ENODEV; + goto bail_mc; + } + + return ret; + +bail_mc: + edac_mc_free(mci); + +bail_disable_ch1: + pci_disable_device(ch1mm); + +bail_ch1: + pci_dev_put(ch1mm); + +bail_disable_ch0: + pci_disable_device(ch0mm); + +bail_ch0: + pci_dev_put(ch0mm); + +bail_pdev: + pci_disable_device(pdev); + +bail: + return ret; +} + +static void __devexit i5100_remove_one(struct pci_dev *pdev) +{ + struct mem_ctl_info *mci; + struct i5100_priv *priv; + + mci = edac_mc_del_mc(&pdev->dev); + + if (!mci) + return; + + priv = mci->pvt_info; + pci_disable_device(pdev); + pci_disable_device(priv->ch0mm); + pci_disable_device(priv->ch1mm); + pci_dev_put(priv->ch0mm); + pci_dev_put(priv->ch1mm); + + edac_mc_free(mci); +} + +static const struct pci_device_id i5100_pci_tbl[] __devinitdata = { + /* Device 16, Function 0, Channel 0 Memory Map, Error Flag/Mask, ... */ + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5100_16) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, i5100_pci_tbl); + +static struct pci_driver i5100_driver = { + .name = KBUILD_BASENAME, + .probe = i5100_init_one, + .remove = __devexit_p(i5100_remove_one), + .id_table = i5100_pci_tbl, +}; + +static int __init i5100_init(void) +{ + int pci_rc; + + pci_rc = pci_register_driver(&i5100_driver); + + return (pci_rc < 0) ? pci_rc : 0; +} + +static void __exit i5100_exit(void) +{ + pci_unregister_driver(&i5100_driver); +} + +module_init(i5100_init); +module_exit(i5100_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR + ("Arthur Jones <ajones@riverbed.com>"); +MODULE_DESCRIPTION("MC Driver for Intel I5100 memory controllers"); diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c index d49361bfe670..2265d9ca1535 100644 --- a/drivers/edac/mpc85xx_edac.c +++ b/drivers/edac/mpc85xx_edac.c @@ -195,14 +195,15 @@ static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static int __devinit mpc85xx_pci_err_probe(struct platform_device *pdev) +static int __devinit mpc85xx_pci_err_probe(struct of_device *op, + const struct of_device_id *match) { struct edac_pci_ctl_info *pci; struct mpc85xx_pci_pdata *pdata; - struct resource *r; + struct resource r; int res = 0; - if (!devres_open_group(&pdev->dev, mpc85xx_pci_err_probe, GFP_KERNEL)) + if (!devres_open_group(&op->dev, mpc85xx_pci_err_probe, GFP_KERNEL)) return -ENOMEM; pci = edac_pci_alloc_ctl_info(sizeof(*pdata), "mpc85xx_pci_err"); @@ -212,34 +213,37 @@ static int __devinit mpc85xx_pci_err_probe(struct platform_device *pdev) pdata = pci->pvt_info; pdata->name = "mpc85xx_pci_err"; pdata->irq = NO_IRQ; - platform_set_drvdata(pdev, pci); - pci->dev = &pdev->dev; + dev_set_drvdata(&op->dev, pci); + pci->dev = &op->dev; pci->mod_name = EDAC_MOD_STR; pci->ctl_name = pdata->name; - pci->dev_name = pdev->dev.bus_id; + pci->dev_name = op->dev.bus_id; if (edac_op_state == EDAC_OPSTATE_POLL) pci->edac_check = mpc85xx_pci_check; pdata->edac_idx = edac_pci_idx++; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) { + res = of_address_to_resource(op->node, 0, &r); + if (res) { printk(KERN_ERR "%s: Unable to get resource for " "PCI err regs\n", __func__); goto err; } - if (!devm_request_mem_region(&pdev->dev, r->start, - r->end - r->start + 1, pdata->name)) { + /* we only need the error registers */ + r.start += 0xe00; + + if (!devm_request_mem_region(&op->dev, r.start, + r.end - r.start + 1, pdata->name)) { printk(KERN_ERR "%s: Error while requesting mem region\n", __func__); res = -EBUSY; goto err; } - pdata->pci_vbase = devm_ioremap(&pdev->dev, r->start, - r->end - r->start + 1); + pdata->pci_vbase = devm_ioremap(&op->dev, r.start, + r.end - r.start + 1); if (!pdata->pci_vbase) { printk(KERN_ERR "%s: Unable to setup PCI err regs\n", __func__); res = -ENOMEM; @@ -266,14 +270,15 @@ static int __devinit mpc85xx_pci_err_probe(struct platform_device *pdev) } if (edac_op_state == EDAC_OPSTATE_INT) { - pdata->irq = platform_get_irq(pdev, 0); - res = devm_request_irq(&pdev->dev, pdata->irq, + pdata->irq = irq_of_parse_and_map(op->node, 0); + res = devm_request_irq(&op->dev, pdata->irq, mpc85xx_pci_isr, IRQF_DISABLED, "[EDAC] PCI err", pci); if (res < 0) { printk(KERN_ERR "%s: Unable to requiest irq %d for " "MPC85xx PCI err\n", __func__, pdata->irq); + irq_dispose_mapping(pdata->irq); res = -ENODEV; goto err2; } @@ -282,23 +287,23 @@ static int __devinit mpc85xx_pci_err_probe(struct platform_device *pdev) pdata->irq); } - devres_remove_group(&pdev->dev, mpc85xx_pci_err_probe); + devres_remove_group(&op->dev, mpc85xx_pci_err_probe); debugf3("%s(): success\n", __func__); printk(KERN_INFO EDAC_MOD_STR " PCI err registered\n"); return 0; err2: - edac_pci_del_device(&pdev->dev); + edac_pci_del_device(&op->dev); err: edac_pci_free_ctl_info(pci); - devres_release_group(&pdev->dev, mpc85xx_pci_err_probe); + devres_release_group(&op->dev, mpc85xx_pci_err_probe); return res; } -static int mpc85xx_pci_err_remove(struct platform_device *pdev) +static int mpc85xx_pci_err_remove(struct of_device *op) { - struct edac_pci_ctl_info *pci = platform_get_drvdata(pdev); + struct edac_pci_ctl_info *pci = dev_get_drvdata(&op->dev); struct mpc85xx_pci_pdata *pdata = pci->pvt_info; debugf0("%s()\n", __func__); @@ -318,12 +323,26 @@ static int mpc85xx_pci_err_remove(struct platform_device *pdev) return 0; } -static struct platform_driver mpc85xx_pci_err_driver = { +static struct of_device_id mpc85xx_pci_err_of_match[] = { + { + .compatible = "fsl,mpc8540-pcix", + }, + { + .compatible = "fsl,mpc8540-pci", + }, + {}, +}; + +static struct of_platform_driver mpc85xx_pci_err_driver = { + .owner = THIS_MODULE, + .name = "mpc85xx_pci_err", + .match_table = mpc85xx_pci_err_of_match, .probe = mpc85xx_pci_err_probe, .remove = __devexit_p(mpc85xx_pci_err_remove), .driver = { - .name = "mpc85xx_pci_err", - } + .name = "mpc85xx_pci_err", + .owner = THIS_MODULE, + }, }; #endif /* CONFIG_PCI */ @@ -1002,7 +1021,7 @@ static int __init mpc85xx_mc_init(void) printk(KERN_WARNING EDAC_MOD_STR "L2 fails to register\n"); #ifdef CONFIG_PCI - res = platform_driver_register(&mpc85xx_pci_err_driver); + res = of_register_platform_driver(&mpc85xx_pci_err_driver); if (res) printk(KERN_WARNING EDAC_MOD_STR "PCI fails to register\n"); #endif @@ -1025,7 +1044,7 @@ static void __exit mpc85xx_mc_exit(void) { mtspr(SPRN_HID1, orig_hid1); #ifdef CONFIG_PCI - platform_driver_unregister(&mpc85xx_pci_err_driver); + of_unregister_platform_driver(&mpc85xx_pci_err_driver); #endif of_unregister_platform_driver(&mpc85xx_l2_err_driver); of_unregister_platform_driver(&mpc85xx_mc_err_driver); diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c index bf071f140a05..083ce8d0c63d 100644 --- a/drivers/edac/mv64x60_edac.c +++ b/drivers/edac/mv64x60_edac.c @@ -71,6 +71,35 @@ static irqreturn_t mv64x60_pci_isr(int irq, void *dev_id) return IRQ_HANDLED; } +/* + * Bit 0 of MV64x60_PCIx_ERR_MASK does not exist on the 64360 and because of + * errata FEr-#11 and FEr-##16 for the 64460, it should be 0 on that chip as + * well. IOW, don't set bit 0. + */ + +/* Erratum FEr PCI-#16: clear bit 0 of PCI SERRn Mask reg. */ +static int __init mv64x60_pci_fixup(struct platform_device *pdev) +{ + struct resource *r; + void __iomem *pci_serr; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!r) { + printk(KERN_ERR "%s: Unable to get resource for " + "PCI err regs\n", __func__); + return -ENOENT; + } + + pci_serr = ioremap(r->start, r->end - r->start + 1); + if (!pci_serr) + return -ENOMEM; + + out_le32(pci_serr, in_le32(pci_serr) & ~0x1); + iounmap(pci_serr); + + return 0; +} + static int __devinit mv64x60_pci_err_probe(struct platform_device *pdev) { struct edac_pci_ctl_info *pci; @@ -128,6 +157,12 @@ static int __devinit mv64x60_pci_err_probe(struct platform_device *pdev) goto err; } + res = mv64x60_pci_fixup(pdev); + if (res < 0) { + printk(KERN_ERR "%s: PCI fixup failed\n", __func__); + goto err; + } + out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE, 0); out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_MASK, 0); out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_MASK, @@ -612,7 +647,7 @@ static void get_total_mem(struct mv64x60_mc_pdata *pdata) if (!np) return; - reg = get_property(np, "reg", NULL); + reg = of_get_property(np, "reg", NULL); pdata->total_mem = reg[1]; } diff --git a/drivers/eisa/Makefile b/drivers/eisa/Makefile index 70abf93fe6b0..5369ce957c6d 100644 --- a/drivers/eisa/Makefile +++ b/drivers/eisa/Makefile @@ -9,7 +9,7 @@ obj-${CONFIG_EISA_VIRTUAL_ROOT} += virtual_root.o # Ugly hack to get DEVICE_NAME_SIZE value... -DEVICE_NAME_SIZE =$(shell awk '$$1=="\#define" && $$2=="DEVICE_NAME_SIZE" {print $$3-1}' $(srctree)/include/linux/device.h) +DEVICE_NAME_SIZE = 50 $(obj)/eisa-bus.o: $(obj)/devlist.h diff --git a/drivers/eisa/eisa-bus.c b/drivers/eisa/eisa-bus.c index 65dcf0432653..c950bf8606d9 100644 --- a/drivers/eisa/eisa-bus.c +++ b/drivers/eisa/eisa-bus.c @@ -22,7 +22,7 @@ struct eisa_device_info { struct eisa_device_id id; - char name[DEVICE_NAME_SIZE]; + char name[50]; }; #ifdef CONFIG_EISA_NAMES @@ -63,7 +63,7 @@ static void __init eisa_name_device (struct eisa_device *edev) if (!strcmp (edev->id.sig, eisa_table[i].id.sig)) { strlcpy (edev->pretty_name, eisa_table[i].name, - DEVICE_NAME_SIZE); + sizeof(edev->pretty_name)); return; } } diff --git a/drivers/firewire/Kconfig b/drivers/firewire/Kconfig index 76f26710fc16..fa6d6abefd4d 100644 --- a/drivers/firewire/Kconfig +++ b/drivers/firewire/Kconfig @@ -16,8 +16,13 @@ config FIREWIRE enable the new stack. To compile this driver as a module, say M here: the module will be - called firewire-core. It functionally replaces ieee1394, raw1394, - and video1394. + called firewire-core. + + This module functionally replaces ieee1394, raw1394, and video1394. + To access it from application programs, you generally need at least + libraw1394 version 2. IIDC/DCAM applications also need libdc1394 + version 2. No libraries are required to access storage devices + through the firewire-sbp2 driver. config FIREWIRE_OHCI tristate "OHCI-1394 controllers" diff --git a/drivers/firewire/fw-card.c b/drivers/firewire/fw-card.c index da873d795aad..bbd73a406e53 100644 --- a/drivers/firewire/fw-card.c +++ b/drivers/firewire/fw-card.c @@ -539,7 +539,7 @@ fw_core_remove_card(struct fw_card *card) wait_for_completion(&card->done); cancel_delayed_work_sync(&card->work); - fw_flush_transactions(card); + WARN_ON(!list_empty(&card->transaction_list)); del_timer_sync(&card->flush_timer); } EXPORT_SYMBOL(fw_core_remove_card); diff --git a/drivers/firewire/fw-cdev.c b/drivers/firewire/fw-cdev.c index c639915fc3cb..bc81d6fcd2fd 100644 --- a/drivers/firewire/fw-cdev.c +++ b/drivers/firewire/fw-cdev.c @@ -382,9 +382,9 @@ complete_transaction(struct fw_card *card, int rcode, response->response.type = FW_CDEV_EVENT_RESPONSE; response->response.rcode = rcode; - queue_event(client, &response->event, - &response->response, sizeof(response->response), - response->response.data, response->response.length); + queue_event(client, &response->event, &response->response, + sizeof(response->response) + response->response.length, + NULL, 0); } static int ioctl_send_request(struct client *client, void *buffer) diff --git a/drivers/firewire/fw-iso.c b/drivers/firewire/fw-iso.c index bcbe794a3ea5..e14c03dc0065 100644 --- a/drivers/firewire/fw-iso.c +++ b/drivers/firewire/fw-iso.c @@ -50,7 +50,7 @@ fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, address = dma_map_page(card->device, buffer->pages[i], 0, PAGE_SIZE, direction); - if (dma_mapping_error(address)) { + if (dma_mapping_error(card->device, address)) { __free_page(buffer->pages[i]); goto out_pages; } diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c index 333b12544dd1..251416f2148f 100644 --- a/drivers/firewire/fw-ohci.c +++ b/drivers/firewire/fw-ohci.c @@ -171,7 +171,6 @@ struct iso_context { struct fw_ohci { struct fw_card card; - u32 version; __iomem char *registers; dma_addr_t self_id_bus; __le32 *self_id_cpu; @@ -180,6 +179,8 @@ struct fw_ohci { int generation; int request_generation; /* for timestamping incoming requests */ u32 bus_seconds; + + bool use_dualbuffer; bool old_uninorth; bool bus_reset_packet_quirk; @@ -953,7 +954,7 @@ at_context_queue_packet(struct context *ctx, struct fw_packet *packet) payload_bus = dma_map_single(ohci->card.device, packet->payload, packet->payload_length, DMA_TO_DEVICE); - if (dma_mapping_error(payload_bus)) { + if (dma_mapping_error(ohci->card.device, payload_bus)) { packet->ack = RCODE_SEND_ERROR; return -1; } @@ -1885,7 +1886,7 @@ ohci_allocate_iso_context(struct fw_card *card, int type, size_t header_size) } else { mask = &ohci->ir_context_mask; list = ohci->ir_context_list; - if (ohci->version >= OHCI_VERSION_1_1) + if (ohci->use_dualbuffer) callback = handle_ir_dualbuffer_packet; else callback = handle_ir_packet_per_buffer; @@ -1949,7 +1950,7 @@ static int ohci_start_iso(struct fw_iso_context *base, } else { index = ctx - ohci->ir_context_list; control = IR_CONTEXT_ISOCH_HEADER; - if (ohci->version >= OHCI_VERSION_1_1) + if (ohci->use_dualbuffer) control |= IR_CONTEXT_DUAL_BUFFER_MODE; match = (tags << 28) | (sync << 8) | ctx->base.channel; if (cycle >= 0) { @@ -2279,7 +2280,7 @@ ohci_queue_iso(struct fw_iso_context *base, spin_lock_irqsave(&ctx->context.ohci->lock, flags); if (base->type == FW_ISO_CONTEXT_TRANSMIT) retval = ohci_queue_iso_transmit(base, packet, buffer, payload); - else if (ctx->context.ohci->version >= OHCI_VERSION_1_1) + else if (ctx->context.ohci->use_dualbuffer) retval = ohci_queue_iso_receive_dualbuffer(base, packet, buffer, payload); else @@ -2341,7 +2342,7 @@ static int __devinit pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) { struct fw_ohci *ohci; - u32 bus_options, max_receive, link_speed; + u32 bus_options, max_receive, link_speed, version; u64 guid; int err; size_t size; @@ -2366,12 +2367,6 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) pci_write_config_dword(dev, OHCI1394_PCI_HCI_Control, 0); pci_set_drvdata(dev, ohci); -#if defined(CONFIG_PPC_PMAC) && defined(CONFIG_PPC32) - ohci->old_uninorth = dev->vendor == PCI_VENDOR_ID_APPLE && - dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW; -#endif - ohci->bus_reset_packet_quirk = dev->vendor == PCI_VENDOR_ID_TI; - spin_lock_init(&ohci->lock); tasklet_init(&ohci->bus_reset_tasklet, @@ -2390,6 +2385,23 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) goto fail_iomem; } + version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff; + ohci->use_dualbuffer = version >= OHCI_VERSION_1_1; + +/* x86-32 currently doesn't use highmem for dma_alloc_coherent */ +#if !defined(CONFIG_X86_32) + /* dual-buffer mode is broken with descriptor addresses above 2G */ + if (dev->vendor == PCI_VENDOR_ID_TI && + dev->device == PCI_DEVICE_ID_TI_TSB43AB22) + ohci->use_dualbuffer = false; +#endif + +#if defined(CONFIG_PPC_PMAC) && defined(CONFIG_PPC32) + ohci->old_uninorth = dev->vendor == PCI_VENDOR_ID_APPLE && + dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW; +#endif + ohci->bus_reset_packet_quirk = dev->vendor == PCI_VENDOR_ID_TI; + ar_context_init(&ohci->ar_request_ctx, ohci, OHCI1394_AsReqRcvContextControlSet); @@ -2441,9 +2453,8 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) if (err < 0) goto fail_self_id; - ohci->version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff; fw_notify("Added fw-ohci device %s, OHCI version %x.%x\n", - dev->dev.bus_id, ohci->version >> 16, ohci->version & 0xff); + dev->dev.bus_id, version >> 16, version & 0xff); return 0; fail_self_id: diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index 53fc5a641e6d..aaff50ebba1d 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c @@ -543,7 +543,7 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, orb->response_bus = dma_map_single(device->card->device, &orb->response, sizeof(orb->response), DMA_FROM_DEVICE); - if (dma_mapping_error(orb->response_bus)) + if (dma_mapping_error(device->card->device, orb->response_bus)) goto fail_mapping_response; orb->request.response.high = 0; @@ -577,7 +577,7 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, orb->base.request_bus = dma_map_single(device->card->device, &orb->request, sizeof(orb->request), DMA_TO_DEVICE); - if (dma_mapping_error(orb->base.request_bus)) + if (dma_mapping_error(device->card->device, orb->base.request_bus)) goto fail_mapping_request; sbp2_send_orb(&orb->base, lu, node_id, generation, @@ -1424,7 +1424,7 @@ sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device, orb->page_table_bus = dma_map_single(device->card->device, orb->page_table, sizeof(orb->page_table), DMA_TO_DEVICE); - if (dma_mapping_error(orb->page_table_bus)) + if (dma_mapping_error(device->card->device, orb->page_table_bus)) goto fail_page_table; /* @@ -1509,7 +1509,7 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done) orb->base.request_bus = dma_map_single(device->card->device, &orb->request, sizeof(orb->request), DMA_TO_DEVICE); - if (dma_mapping_error(orb->base.request_bus)) + if (dma_mapping_error(device->card->device, orb->base.request_bus)) goto out; sbp2_send_orb(&orb->base, lu, lu->tgt->node_id, lu->generation, diff --git a/drivers/firewire/fw-topology.c b/drivers/firewire/fw-topology.c index 213b0ff8f3d6..c1b81077c4a8 100644 --- a/drivers/firewire/fw-topology.c +++ b/drivers/firewire/fw-topology.c @@ -510,8 +510,6 @@ fw_core_handle_bus_reset(struct fw_card *card, struct fw_node *local_node; unsigned long flags; - fw_flush_transactions(card); - spin_lock_irqsave(&card->lock, flags); /* diff --git a/drivers/firewire/fw-transaction.c b/drivers/firewire/fw-transaction.c index 40db80752272..e5d1a0b64fcf 100644 --- a/drivers/firewire/fw-transaction.c +++ b/drivers/firewire/fw-transaction.c @@ -22,6 +22,7 @@ #include <linux/kernel.h> #include <linux/kref.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/pci.h> @@ -151,7 +152,7 @@ transmit_complete_callback(struct fw_packet *packet, static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel, - int node_id, int source_id, int generation, int speed, + int destination_id, int source_id, int generation, int speed, unsigned long long offset, void *payload, size_t length) { int ext_tcode; @@ -166,7 +167,7 @@ fw_fill_request(struct fw_packet *packet, int tcode, int tlabel, HEADER_RETRY(RETRY_X) | HEADER_TLABEL(tlabel) | HEADER_TCODE(tcode) | - HEADER_DESTINATION(node_id); + HEADER_DESTINATION(destination_id); packet->header[1] = HEADER_OFFSET_HIGH(offset >> 32) | HEADER_SOURCE(source_id); packet->header[2] = @@ -252,7 +253,7 @@ fw_send_request(struct fw_card *card, struct fw_transaction *t, fw_transaction_callback_t callback, void *callback_data) { unsigned long flags; - int tlabel, source; + int tlabel; /* * Bump the flush timer up 100ms first of all so we @@ -268,7 +269,6 @@ fw_send_request(struct fw_card *card, struct fw_transaction *t, spin_lock_irqsave(&card->lock, flags); - source = card->node_id; tlabel = card->current_tlabel; if (card->tlabel_mask & (1 << tlabel)) { spin_unlock_irqrestore(&card->lock, flags); @@ -279,77 +279,58 @@ fw_send_request(struct fw_card *card, struct fw_transaction *t, card->current_tlabel = (card->current_tlabel + 1) & 0x1f; card->tlabel_mask |= (1 << tlabel); - list_add_tail(&t->link, &card->transaction_list); - - spin_unlock_irqrestore(&card->lock, flags); - - /* Initialize rest of transaction, fill out packet and send it. */ t->node_id = node_id; t->tlabel = tlabel; t->callback = callback; t->callback_data = callback_data; - fw_fill_request(&t->packet, tcode, t->tlabel, - node_id, source, generation, - speed, offset, payload, length); + fw_fill_request(&t->packet, tcode, t->tlabel, node_id, card->node_id, + generation, speed, offset, payload, length); t->packet.callback = transmit_complete_callback; + list_add_tail(&t->link, &card->transaction_list); + + spin_unlock_irqrestore(&card->lock, flags); + card->driver->send_request(card, &t->packet); } EXPORT_SYMBOL(fw_send_request); -struct fw_phy_packet { - struct fw_packet packet; - struct completion done; - struct kref kref; -}; - -static void phy_packet_release(struct kref *kref) -{ - struct fw_phy_packet *p = - container_of(kref, struct fw_phy_packet, kref); - kfree(p); -} +static DEFINE_MUTEX(phy_config_mutex); +static DECLARE_COMPLETION(phy_config_done); static void transmit_phy_packet_callback(struct fw_packet *packet, struct fw_card *card, int status) { - struct fw_phy_packet *p = - container_of(packet, struct fw_phy_packet, packet); - - complete(&p->done); - kref_put(&p->kref, phy_packet_release); + complete(&phy_config_done); } +static struct fw_packet phy_config_packet = { + .header_length = 8, + .payload_length = 0, + .speed = SCODE_100, + .callback = transmit_phy_packet_callback, +}; + void fw_send_phy_config(struct fw_card *card, int node_id, int generation, int gap_count) { - struct fw_phy_packet *p; long timeout = DIV_ROUND_UP(HZ, 10); u32 data = PHY_IDENTIFIER(PHY_PACKET_CONFIG) | PHY_CONFIG_ROOT_ID(node_id) | PHY_CONFIG_GAP_COUNT(gap_count); - p = kmalloc(sizeof(*p), GFP_KERNEL); - if (p == NULL) - return; + mutex_lock(&phy_config_mutex); + + phy_config_packet.header[0] = data; + phy_config_packet.header[1] = ~data; + phy_config_packet.generation = generation; + INIT_COMPLETION(phy_config_done); + + card->driver->send_request(card, &phy_config_packet); + wait_for_completion_timeout(&phy_config_done, timeout); - p->packet.header[0] = data; - p->packet.header[1] = ~data; - p->packet.header_length = 8; - p->packet.payload_length = 0; - p->packet.speed = SCODE_100; - p->packet.generation = generation; - p->packet.callback = transmit_phy_packet_callback; - init_completion(&p->done); - kref_set(&p->kref, 2); - - card->driver->send_request(card, &p->packet); - timeout = wait_for_completion_timeout(&p->done, timeout); - kref_put(&p->kref, phy_packet_release); - - /* will leak p if the callback is never executed */ - WARN_ON(timeout == 0); + mutex_unlock(&phy_config_mutex); } void fw_flush_transactions(struct fw_card *card) diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c index 25918f7dfd0f..50a071f1c945 100644 --- a/drivers/firmware/dcdbas.c +++ b/drivers/firmware/dcdbas.c @@ -152,20 +152,11 @@ static ssize_t smi_data_read(struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) { - size_t max_read; ssize_t ret; mutex_lock(&smi_data_lock); - - if (pos >= smi_data_buf_size) { - ret = 0; - goto out; - } - - max_read = smi_data_buf_size - pos; - ret = min(max_read, count); - memcpy(buf, smi_data_buf + pos, ret); -out: + ret = memory_read_from_buffer(buf, count, &pos, smi_data_buf, + smi_data_buf_size); mutex_unlock(&smi_data_lock); return ret; } diff --git a/drivers/firmware/dell_rbu.c b/drivers/firmware/dell_rbu.c index 7430e218cda6..13946ebd77d6 100644 --- a/drivers/firmware/dell_rbu.c +++ b/drivers/firmware/dell_rbu.c @@ -507,11 +507,6 @@ static ssize_t read_packet_data(char *buffer, loff_t pos, size_t count) static ssize_t read_rbu_mono_data(char *buffer, loff_t pos, size_t count) { - unsigned char *ptemp = NULL; - size_t bytes_left = 0; - size_t data_length = 0; - ssize_t ret_count = 0; - /* check to see if we have something to return */ if ((rbu_data.image_update_buffer == NULL) || (rbu_data.bios_image_size == 0)) { @@ -519,28 +514,11 @@ static ssize_t read_rbu_mono_data(char *buffer, loff_t pos, size_t count) "bios_image_size %lu\n", rbu_data.image_update_buffer, rbu_data.bios_image_size); - ret_count = -ENOMEM; - goto read_rbu_data_exit; - } - - if (pos > rbu_data.bios_image_size) { - ret_count = 0; - goto read_rbu_data_exit; + return -ENOMEM; } - bytes_left = rbu_data.bios_image_size - pos; - data_length = min(bytes_left, count); - - ptemp = rbu_data.image_update_buffer; - memcpy(buffer, (ptemp + pos), data_length); - - if ((pos + count) > rbu_data.bios_image_size) - /* this was the last copy */ - ret_count = bytes_left; - else - ret_count = count; - read_rbu_data_exit: - return ret_count; + return memory_read_from_buffer(buffer, count, &pos, + rbu_data.image_update_buffer, rbu_data.bios_image_size); } static ssize_t read_rbu_data(struct kobject *kobj, diff --git a/drivers/firmware/memmap.c b/drivers/firmware/memmap.c index e23399c7f773..001622eb86f9 100644 --- a/drivers/firmware/memmap.c +++ b/drivers/firmware/memmap.c @@ -153,12 +153,14 @@ int __init firmware_map_add_early(resource_size_t start, resource_size_t end, static ssize_t start_show(struct firmware_map_entry *entry, char *buf) { - return snprintf(buf, PAGE_SIZE, "0x%llx\n", entry->start); + return snprintf(buf, PAGE_SIZE, "0x%llx\n", + (unsigned long long)entry->start); } static ssize_t end_show(struct firmware_map_entry *entry, char *buf) { - return snprintf(buf, PAGE_SIZE, "0x%llx\n", entry->end); + return snprintf(buf, PAGE_SIZE, "0x%llx\n", + (unsigned long long)entry->end); } static ssize_t type_show(struct firmware_map_entry *entry, char *buf) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 008c38ba774f..dbd42d6c93a7 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -2,15 +2,40 @@ # GPIO infrastructure and expanders # -config HAVE_GPIO_LIB +config ARCH_WANT_OPTIONAL_GPIOLIB bool help + Select this config option from the architecture Kconfig, if + it is possible to use gpiolib on the architecture, but let the + user decide whether to actually build it or not. + Select this instead of ARCH_REQUIRE_GPIOLIB, if your architecture does + not depend on GPIOs being available, but rather let the user + decide whether he needs it or not. + +config ARCH_REQUIRE_GPIOLIB + bool + select GPIOLIB + help Platforms select gpiolib if they use this infrastructure for all their GPIOs, usually starting with ones integrated into SOC processors. + Selecting this from the architecture code will cause the gpiolib + code to always get built in. + + + +menuconfig GPIOLIB + bool "GPIO Support" + depends on ARCH_WANT_OPTIONAL_GPIOLIB || ARCH_REQUIRE_GPIOLIB + select GENERIC_GPIO + help + This enables GPIO support through the generic GPIO library. + You only need to enable this, if you also want to enable + one or more of the GPIO expansion card drivers below. + + If unsure, say N. -menu "GPIO Support" - depends on HAVE_GPIO_LIB +if GPIOLIB config DEBUG_GPIO bool "Debug GPIO calls" @@ -23,10 +48,44 @@ config DEBUG_GPIO slower. The diagnostics help catch the type of setup errors that are most common when setting up new platforms or boards. +config GPIO_SYSFS + bool "/sys/class/gpio/... (sysfs interface)" + depends on SYSFS && EXPERIMENTAL + help + Say Y here to add a sysfs interface for GPIOs. + + This is mostly useful to work around omissions in a system's + kernel support. Those are common in custom and semicustom + hardware assembled using standard kernels with a minimum of + custom patches. In those cases, userspace code may import + a given GPIO from the kernel, if no kernel driver requested it. + + Kernel drivers may also request that a particular GPIO be + exported to userspace; this can be useful when debugging. + # put expanders in the right section, in alphabetical order comment "I2C GPIO expanders:" +config GPIO_MAX732X + tristate "MAX7319, MAX7320-7327 I2C Port Expanders" + depends on I2C + help + Say yes here to support the MAX7319, MAX7320-7327 series of I2C + Port Expanders. Each IO port on these chips has a fixed role of + Input (designated by 'I'), Push-Pull Output ('O'), or Open-Drain + Input and Output (designed by 'P'). The combinations are listed + below: + + 8 bits: max7319 (8I), max7320 (8O), max7321 (8P), + max7322 (4I4O), max7323 (4P4O) + + 16 bits: max7324 (8I8O), max7325 (8P8O), + max7326 (4I12O), max7327 (4P12O) + + Board setup code must specify the model to use, and the start + number for these GPIOs. + config GPIO_PCA953X tristate "PCA953x, PCA955x, and MAX7310 I/O ports" depends on I2C @@ -45,7 +104,7 @@ config GPIO_PCA953X will be called pca953x. config GPIO_PCF857X - tristate "PCF857x, PCA857x, and PCA967x I2C GPIO expanders" + tristate "PCF857x, PCA{85,96}7x, and MAX732[89] I2C GPIO expanders" depends on I2C help Say yes here to provide access to most "quasi-bidirectional" I2C @@ -54,7 +113,8 @@ config GPIO_PCF857X some of them. Compatible models include: 8 bits: pcf8574, pcf8574a, pca8574, pca8574a, - pca9670, pca9672, pca9674, pca9674a + pca9670, pca9672, pca9674, pca9674a, + max7328, max7329 16 bits: pcf8575, pcf8575c, pca8575, pca9671, pca9673, pca9675 @@ -67,8 +127,32 @@ config GPIO_PCF857X This driver provides an in-kernel interface to those GPIOs using platform-neutral GPIO calls. +comment "PCI GPIO expanders:" + +config GPIO_BT8XX + tristate "BT8XX GPIO abuser" + depends on PCI && VIDEO_BT848=n + help + The BT8xx frame grabber chip has 24 GPIO pins than can be abused + as a cheap PCI GPIO card. + + This chip can be found on Miro, Hauppauge and STB TV-cards. + + The card needs to be physically altered for using it as a + GPIO card. For more information on how to build a GPIO card + from a BT8xx TV card, see the documentation file at + Documentation/bt8xxgpio.txt + + If unsure, say N. + comment "SPI GPIO expanders:" +config GPIO_MAX7301 + tristate "Maxim MAX7301 GPIO expander" + depends on SPI_MASTER + help + gpio driver for Maxim MAX7301 SPI GPIO expander. + config GPIO_MCP23S08 tristate "Microchip MCP23S08 I/O expander" depends on SPI_MASTER @@ -76,4 +160,4 @@ config GPIO_MCP23S08 SPI driver for Microchip MCP23S08 I/O expander. This provides a GPIO interface supporting inputs and outputs. -endmenu +endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index fdde9923cf33..01b4bbde1956 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -2,8 +2,11 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG -obj-$(CONFIG_HAVE_GPIO_LIB) += gpiolib.o +obj-$(CONFIG_GPIOLIB) += gpiolib.o +obj-$(CONFIG_GPIO_MAX7301) += max7301.o +obj-$(CONFIG_GPIO_MAX732X) += max732x.o obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o obj-$(CONFIG_GPIO_PCA953X) += pca953x.o obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o +obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o diff --git a/drivers/gpio/bt8xxgpio.c b/drivers/gpio/bt8xxgpio.c new file mode 100644 index 000000000000..7a1168249dd5 --- /dev/null +++ b/drivers/gpio/bt8xxgpio.c @@ -0,0 +1,348 @@ +/* + + bt8xx GPIO abuser + + Copyright (C) 2008 Michael Buesch <mb@bu3sch.de> + + Please do _only_ contact the people listed _above_ with issues related to this driver. + All the other people listed below are not related to this driver. Their names + are only here, because this driver is derived from the bt848 driver. + + + Derived from the bt848 driver: + + Copyright (C) 1996,97,98 Ralph Metzler + & Marcus Metzler + (c) 1999-2002 Gerd Knorr + + some v4l2 code lines are taken from Justin's bttv2 driver which is + (c) 2000 Justin Schoeman + + V4L1 removal from: + (c) 2005-2006 Nickolay V. Shmyrev + + Fixes to be fully V4L2 compliant by + (c) 2006 Mauro Carvalho Chehab + + Cropping and overscan support + Copyright (C) 2005, 2006 Michael H. Schimek + 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 + (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/pci.h> +#include <linux/spinlock.h> + +#include <asm/gpio.h> + +/* Steal the hardware definitions from the bttv driver. */ +#include "../media/video/bt8xx/bt848.h" + + +#define BT8XXGPIO_NR_GPIOS 24 /* We have 24 GPIO pins */ + + +struct bt8xxgpio { + spinlock_t lock; + + void __iomem *mmio; + struct pci_dev *pdev; + struct gpio_chip gpio; + +#ifdef CONFIG_PM + u32 saved_outen; + u32 saved_data; +#endif +}; + +#define bgwrite(dat, adr) writel((dat), bg->mmio+(adr)) +#define bgread(adr) readl(bg->mmio+(adr)) + + +static int modparam_gpiobase = -1/* dynamic */; +module_param_named(gpiobase, modparam_gpiobase, int, 0444); +MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, which is the default."); + + +static int bt8xxgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) +{ + struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); + unsigned long flags; + u32 outen, data; + + spin_lock_irqsave(&bg->lock, flags); + + data = bgread(BT848_GPIO_DATA); + data &= ~(1 << nr); + bgwrite(data, BT848_GPIO_DATA); + + outen = bgread(BT848_GPIO_OUT_EN); + outen &= ~(1 << nr); + bgwrite(outen, BT848_GPIO_OUT_EN); + + spin_unlock_irqrestore(&bg->lock, flags); + + return 0; +} + +static int bt8xxgpio_gpio_get(struct gpio_chip *gpio, unsigned nr) +{ + struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&bg->lock, flags); + val = bgread(BT848_GPIO_DATA); + spin_unlock_irqrestore(&bg->lock, flags); + + return !!(val & (1 << nr)); +} + +static int bt8xxgpio_gpio_direction_output(struct gpio_chip *gpio, + unsigned nr, int val) +{ + struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); + unsigned long flags; + u32 outen, data; + + spin_lock_irqsave(&bg->lock, flags); + + outen = bgread(BT848_GPIO_OUT_EN); + outen |= (1 << nr); + bgwrite(outen, BT848_GPIO_OUT_EN); + + data = bgread(BT848_GPIO_DATA); + if (val) + data |= (1 << nr); + else + data &= ~(1 << nr); + bgwrite(data, BT848_GPIO_DATA); + + spin_unlock_irqrestore(&bg->lock, flags); + + return 0; +} + +static void bt8xxgpio_gpio_set(struct gpio_chip *gpio, + unsigned nr, int val) +{ + struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); + unsigned long flags; + u32 data; + + spin_lock_irqsave(&bg->lock, flags); + + data = bgread(BT848_GPIO_DATA); + if (val) + data |= (1 << nr); + else + data &= ~(1 << nr); + bgwrite(data, BT848_GPIO_DATA); + + spin_unlock_irqrestore(&bg->lock, flags); +} + +static void bt8xxgpio_gpio_setup(struct bt8xxgpio *bg) +{ + struct gpio_chip *c = &bg->gpio; + + c->label = bg->pdev->dev.bus_id; + c->owner = THIS_MODULE; + c->direction_input = bt8xxgpio_gpio_direction_input; + c->get = bt8xxgpio_gpio_get; + c->direction_output = bt8xxgpio_gpio_direction_output; + c->set = bt8xxgpio_gpio_set; + c->dbg_show = NULL; + c->base = modparam_gpiobase; + c->ngpio = BT8XXGPIO_NR_GPIOS; + c->can_sleep = 0; +} + +static int bt8xxgpio_probe(struct pci_dev *dev, + const struct pci_device_id *pci_id) +{ + struct bt8xxgpio *bg; + int err; + + bg = kzalloc(sizeof(*bg), GFP_KERNEL); + if (!bg) + return -ENOMEM; + + bg->pdev = dev; + spin_lock_init(&bg->lock); + + err = pci_enable_device(dev); + if (err) { + printk(KERN_ERR "bt8xxgpio: Can't enable device.\n"); + goto err_freebg; + } + if (!request_mem_region(pci_resource_start(dev, 0), + pci_resource_len(dev, 0), + "bt8xxgpio")) { + printk(KERN_WARNING "bt8xxgpio: Can't request iomem (0x%llx).\n", + (unsigned long long)pci_resource_start(dev, 0)); + err = -EBUSY; + goto err_disable; + } + pci_set_master(dev); + pci_set_drvdata(dev, bg); + + bg->mmio = ioremap(pci_resource_start(dev, 0), 0x1000); + if (!bg->mmio) { + printk(KERN_ERR "bt8xxgpio: ioremap() failed\n"); + err = -EIO; + goto err_release_mem; + } + + /* Disable interrupts */ + bgwrite(0, BT848_INT_MASK); + + /* gpio init */ + bgwrite(0, BT848_GPIO_DMA_CTL); + bgwrite(0, BT848_GPIO_REG_INP); + bgwrite(0, BT848_GPIO_OUT_EN); + + bt8xxgpio_gpio_setup(bg); + err = gpiochip_add(&bg->gpio); + if (err) { + printk(KERN_ERR "bt8xxgpio: Failed to register GPIOs\n"); + goto err_release_mem; + } + + printk(KERN_INFO "bt8xxgpio: Abusing BT8xx card for GPIOs %d to %d\n", + bg->gpio.base, bg->gpio.base + BT8XXGPIO_NR_GPIOS - 1); + + return 0; + +err_release_mem: + release_mem_region(pci_resource_start(dev, 0), + pci_resource_len(dev, 0)); + pci_set_drvdata(dev, NULL); +err_disable: + pci_disable_device(dev); +err_freebg: + kfree(bg); + + return err; +} + +static void bt8xxgpio_remove(struct pci_dev *pdev) +{ + struct bt8xxgpio *bg = pci_get_drvdata(pdev); + + gpiochip_remove(&bg->gpio); + + bgwrite(0, BT848_INT_MASK); + bgwrite(~0x0, BT848_INT_STAT); + bgwrite(0x0, BT848_GPIO_OUT_EN); + + iounmap(bg->mmio); + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + pci_disable_device(pdev); + + pci_set_drvdata(pdev, NULL); + kfree(bg); +} + +#ifdef CONFIG_PM +static int bt8xxgpio_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct bt8xxgpio *bg = pci_get_drvdata(pdev); + unsigned long flags; + + spin_lock_irqsave(&bg->lock, flags); + + bg->saved_outen = bgread(BT848_GPIO_OUT_EN); + bg->saved_data = bgread(BT848_GPIO_DATA); + + bgwrite(0, BT848_INT_MASK); + bgwrite(~0x0, BT848_INT_STAT); + bgwrite(0x0, BT848_GPIO_OUT_EN); + + spin_unlock_irqrestore(&bg->lock, flags); + + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int bt8xxgpio_resume(struct pci_dev *pdev) +{ + struct bt8xxgpio *bg = pci_get_drvdata(pdev); + unsigned long flags; + int err; + + pci_set_power_state(pdev, 0); + err = pci_enable_device(pdev); + if (err) + return err; + pci_restore_state(pdev); + + spin_lock_irqsave(&bg->lock, flags); + + bgwrite(0, BT848_INT_MASK); + bgwrite(0, BT848_GPIO_DMA_CTL); + bgwrite(0, BT848_GPIO_REG_INP); + bgwrite(bg->saved_outen, BT848_GPIO_OUT_EN); + bgwrite(bg->saved_data & bg->saved_outen, + BT848_GPIO_DATA); + + spin_unlock_irqrestore(&bg->lock, flags); + + return 0; +} +#else +#define bt8xxgpio_suspend NULL +#define bt8xxgpio_resume NULL +#endif /* CONFIG_PM */ + +static struct pci_device_id bt8xxgpio_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879) }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, bt8xxgpio_pci_tbl); + +static struct pci_driver bt8xxgpio_pci_driver = { + .name = "bt8xxgpio", + .id_table = bt8xxgpio_pci_tbl, + .probe = bt8xxgpio_probe, + .remove = bt8xxgpio_remove, + .suspend = bt8xxgpio_suspend, + .resume = bt8xxgpio_resume, +}; + +static int bt8xxgpio_init(void) +{ + return pci_register_driver(&bt8xxgpio_pci_driver); +} +module_init(bt8xxgpio_init) + +static void bt8xxgpio_exit(void) +{ + pci_unregister_driver(&bt8xxgpio_pci_driver); +} +module_exit(bt8xxgpio_exit) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Buesch"); +MODULE_DESCRIPTION("Abuse a BT8xx framegrabber card as generic GPIO card"); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index beaf6b3a37dc..8d2940517c99 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2,8 +2,11 @@ #include <linux/module.h> #include <linux/irq.h> #include <linux/spinlock.h> - -#include <asm/gpio.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/gpio.h> /* Optional implementation infrastructure for GPIO interfaces. @@ -44,6 +47,8 @@ struct gpio_desc { #define FLAG_REQUESTED 0 #define FLAG_IS_OUT 1 #define FLAG_RESERVED 2 +#define FLAG_EXPORT 3 /* protected by sysfs_lock */ +#define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */ #ifdef CONFIG_DEBUG_FS const char *label; @@ -151,6 +156,482 @@ err: return ret; } +#ifdef CONFIG_GPIO_SYSFS + +/* lock protects against unexport_gpio() being called while + * sysfs files are active. + */ +static DEFINE_MUTEX(sysfs_lock); + +/* + * /sys/class/gpio/gpioN... only for GPIOs that are exported + * /direction + * * MAY BE OMITTED if kernel won't allow direction changes + * * is read/write as "in" or "out" + * * may also be written as "high" or "low", initializing + * output value as specified ("out" implies "low") + * /value + * * always readable, subject to hardware behavior + * * may be writable, as zero/nonzero + * + * REVISIT there will likely be an attribute for configuring async + * notifications, e.g. to specify polling interval or IRQ trigger type + * that would for example trigger a poll() on the "value". + */ + +static ssize_t gpio_direction_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else + status = sprintf(buf, "%s\n", + test_bit(FLAG_IS_OUT, &desc->flags) + ? "out" : "in"); + + mutex_unlock(&sysfs_lock); + return status; +} + +static ssize_t gpio_direction_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + unsigned gpio = desc - gpio_desc; + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else if (sysfs_streq(buf, "high")) + status = gpio_direction_output(gpio, 1); + else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low")) + status = gpio_direction_output(gpio, 0); + else if (sysfs_streq(buf, "in")) + status = gpio_direction_input(gpio); + else + status = -EINVAL; + + mutex_unlock(&sysfs_lock); + return status ? : size; +} + +static const DEVICE_ATTR(direction, 0644, + gpio_direction_show, gpio_direction_store); + +static ssize_t gpio_value_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + unsigned gpio = desc - gpio_desc; + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else + status = sprintf(buf, "%d\n", gpio_get_value_cansleep(gpio)); + + mutex_unlock(&sysfs_lock); + return status; +} + +static ssize_t gpio_value_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + unsigned gpio = desc - gpio_desc; + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else if (!test_bit(FLAG_IS_OUT, &desc->flags)) + status = -EPERM; + else { + long value; + + status = strict_strtol(buf, 0, &value); + if (status == 0) { + gpio_set_value_cansleep(gpio, value != 0); + status = size; + } + } + + mutex_unlock(&sysfs_lock); + return status; +} + +static /*const*/ DEVICE_ATTR(value, 0644, + gpio_value_show, gpio_value_store); + +static const struct attribute *gpio_attrs[] = { + &dev_attr_direction.attr, + &dev_attr_value.attr, + NULL, +}; + +static const struct attribute_group gpio_attr_group = { + .attrs = (struct attribute **) gpio_attrs, +}; + +/* + * /sys/class/gpio/gpiochipN/ + * /base ... matching gpio_chip.base (N) + * /label ... matching gpio_chip.label + * /ngpio ... matching gpio_chip.ngpio + */ + +static ssize_t chip_base_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_chip *chip = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", chip->base); +} +static DEVICE_ATTR(base, 0444, chip_base_show, NULL); + +static ssize_t chip_label_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_chip *chip = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", chip->label ? : ""); +} +static DEVICE_ATTR(label, 0444, chip_label_show, NULL); + +static ssize_t chip_ngpio_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_chip *chip = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", chip->ngpio); +} +static DEVICE_ATTR(ngpio, 0444, chip_ngpio_show, NULL); + +static const struct attribute *gpiochip_attrs[] = { + &dev_attr_base.attr, + &dev_attr_label.attr, + &dev_attr_ngpio.attr, + NULL, +}; + +static const struct attribute_group gpiochip_attr_group = { + .attrs = (struct attribute **) gpiochip_attrs, +}; + +/* + * /sys/class/gpio/export ... write-only + * integer N ... number of GPIO to export (full access) + * /sys/class/gpio/unexport ... write-only + * integer N ... number of GPIO to unexport + */ +static ssize_t export_store(struct class *class, const char *buf, size_t len) +{ + long gpio; + int status; + + status = strict_strtol(buf, 0, &gpio); + if (status < 0) + goto done; + + /* No extra locking here; FLAG_SYSFS just signifies that the + * request and export were done by on behalf of userspace, so + * they may be undone on its behalf too. + */ + + status = gpio_request(gpio, "sysfs"); + if (status < 0) + goto done; + + status = gpio_export(gpio, true); + if (status < 0) + gpio_free(gpio); + else + set_bit(FLAG_SYSFS, &gpio_desc[gpio].flags); + +done: + if (status) + pr_debug("%s: status %d\n", __func__, status); + return status ? : len; +} + +static ssize_t unexport_store(struct class *class, const char *buf, size_t len) +{ + long gpio; + int status; + + status = strict_strtol(buf, 0, &gpio); + if (status < 0) + goto done; + + status = -EINVAL; + + /* reject bogus commands (gpio_unexport ignores them) */ + if (!gpio_is_valid(gpio)) + goto done; + + /* No extra locking here; FLAG_SYSFS just signifies that the + * request and export were done by on behalf of userspace, so + * they may be undone on its behalf too. + */ + if (test_and_clear_bit(FLAG_SYSFS, &gpio_desc[gpio].flags)) { + status = 0; + gpio_free(gpio); + } +done: + if (status) + pr_debug("%s: status %d\n", __func__, status); + return status ? : len; +} + +static struct class_attribute gpio_class_attrs[] = { + __ATTR(export, 0200, NULL, export_store), + __ATTR(unexport, 0200, NULL, unexport_store), + __ATTR_NULL, +}; + +static struct class gpio_class = { + .name = "gpio", + .owner = THIS_MODULE, + + .class_attrs = gpio_class_attrs, +}; + + +/** + * gpio_export - export a GPIO through sysfs + * @gpio: gpio to make available, already requested + * @direction_may_change: true if userspace may change gpio direction + * Context: arch_initcall or later + * + * When drivers want to make a GPIO accessible to userspace after they + * have requested it -- perhaps while debugging, or as part of their + * public interface -- they may use this routine. If the GPIO can + * change direction (some can't) and the caller allows it, userspace + * will see "direction" sysfs attribute which may be used to change + * the gpio's direction. A "value" attribute will always be provided. + * + * Returns zero on success, else an error. + */ +int gpio_export(unsigned gpio, bool direction_may_change) +{ + unsigned long flags; + struct gpio_desc *desc; + int status = -EINVAL; + + /* can't export until sysfs is available ... */ + if (!gpio_class.p) { + pr_debug("%s: called too early!\n", __func__); + return -ENOENT; + } + + if (!gpio_is_valid(gpio)) + goto done; + + mutex_lock(&sysfs_lock); + + spin_lock_irqsave(&gpio_lock, flags); + desc = &gpio_desc[gpio]; + if (test_bit(FLAG_REQUESTED, &desc->flags) + && !test_bit(FLAG_EXPORT, &desc->flags)) { + status = 0; + if (!desc->chip->direction_input + || !desc->chip->direction_output) + direction_may_change = false; + } + spin_unlock_irqrestore(&gpio_lock, flags); + + if (status == 0) { + struct device *dev; + + dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0), + desc, "gpio%d", gpio); + if (dev) { + if (direction_may_change) + status = sysfs_create_group(&dev->kobj, + &gpio_attr_group); + else + status = device_create_file(dev, + &dev_attr_value); + if (status != 0) + device_unregister(dev); + } else + status = -ENODEV; + if (status == 0) + set_bit(FLAG_EXPORT, &desc->flags); + } + + mutex_unlock(&sysfs_lock); + +done: + if (status) + pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); + + return status; +} +EXPORT_SYMBOL_GPL(gpio_export); + +static int match_export(struct device *dev, void *data) +{ + return dev_get_drvdata(dev) == data; +} + +/** + * gpio_unexport - reverse effect of gpio_export() + * @gpio: gpio to make unavailable + * + * This is implicit on gpio_free(). + */ +void gpio_unexport(unsigned gpio) +{ + struct gpio_desc *desc; + int status = -EINVAL; + + if (!gpio_is_valid(gpio)) + goto done; + + mutex_lock(&sysfs_lock); + + desc = &gpio_desc[gpio]; + if (test_bit(FLAG_EXPORT, &desc->flags)) { + struct device *dev = NULL; + + dev = class_find_device(&gpio_class, NULL, desc, match_export); + if (dev) { + clear_bit(FLAG_EXPORT, &desc->flags); + put_device(dev); + device_unregister(dev); + status = 0; + } else + status = -ENODEV; + } + + mutex_unlock(&sysfs_lock); +done: + if (status) + pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); +} +EXPORT_SYMBOL_GPL(gpio_unexport); + +static int gpiochip_export(struct gpio_chip *chip) +{ + int status; + struct device *dev; + + /* Many systems register gpio chips for SOC support very early, + * before driver model support is available. In those cases we + * export this later, in gpiolib_sysfs_init() ... here we just + * verify that _some_ field of gpio_class got initialized. + */ + if (!gpio_class.p) + return 0; + + /* use chip->base for the ID; it's already known to be unique */ + mutex_lock(&sysfs_lock); + dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0), chip, + "gpiochip%d", chip->base); + if (dev) { + status = sysfs_create_group(&dev->kobj, + &gpiochip_attr_group); + } else + status = -ENODEV; + chip->exported = (status == 0); + mutex_unlock(&sysfs_lock); + + if (status) { + unsigned long flags; + unsigned gpio; + + spin_lock_irqsave(&gpio_lock, flags); + gpio = chip->base; + while (gpio_desc[gpio].chip == chip) + gpio_desc[gpio++].chip = NULL; + spin_unlock_irqrestore(&gpio_lock, flags); + + pr_debug("%s: chip %s status %d\n", __func__, + chip->label, status); + } + + return status; +} + +static void gpiochip_unexport(struct gpio_chip *chip) +{ + int status; + struct device *dev; + + mutex_lock(&sysfs_lock); + dev = class_find_device(&gpio_class, NULL, chip, match_export); + if (dev) { + put_device(dev); + device_unregister(dev); + chip->exported = 0; + status = 0; + } else + status = -ENODEV; + mutex_unlock(&sysfs_lock); + + if (status) + pr_debug("%s: chip %s status %d\n", __func__, + chip->label, status); +} + +static int __init gpiolib_sysfs_init(void) +{ + int status; + unsigned long flags; + unsigned gpio; + + status = class_register(&gpio_class); + if (status < 0) + return status; + + /* Scan and register the gpio_chips which registered very + * early (e.g. before the class_register above was called). + * + * We run before arch_initcall() so chip->dev nodes can have + * registered, and so arch_initcall() can always gpio_export(). + */ + spin_lock_irqsave(&gpio_lock, flags); + for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++) { + struct gpio_chip *chip; + + chip = gpio_desc[gpio].chip; + if (!chip || chip->exported) + continue; + + spin_unlock_irqrestore(&gpio_lock, flags); + status = gpiochip_export(chip); + spin_lock_irqsave(&gpio_lock, flags); + } + spin_unlock_irqrestore(&gpio_lock, flags); + + + return status; +} +postcore_initcall(gpiolib_sysfs_init); + +#else +static inline int gpiochip_export(struct gpio_chip *chip) +{ + return 0; +} + +static inline void gpiochip_unexport(struct gpio_chip *chip) +{ +} + +#endif /* CONFIG_GPIO_SYSFS */ + /** * gpiochip_add() - register a gpio_chip * @chip: the chip to register, with chip->base initialized @@ -160,6 +641,11 @@ err: * because the chip->base is invalid or already associated with a * different chip. Otherwise it returns zero as a success code. * + * When gpiochip_add() is called very early during boot, so that GPIOs + * can be freely used, the chip->dev device must be registered before + * the gpio framework's arch_initcall(). Otherwise sysfs initialization + * for GPIOs will fail rudely. + * * If chip->base is negative, this requests dynamic assignment of * a range of valid GPIOs. */ @@ -182,7 +668,7 @@ int gpiochip_add(struct gpio_chip *chip) base = gpiochip_find_base(chip->ngpio); if (base < 0) { status = base; - goto fail_unlock; + goto unlock; } chip->base = base; } @@ -197,12 +683,23 @@ int gpiochip_add(struct gpio_chip *chip) if (status == 0) { for (id = base; id < base + chip->ngpio; id++) { gpio_desc[id].chip = chip; - gpio_desc[id].flags = 0; + + /* REVISIT: most hardware initializes GPIOs as + * inputs (often with pullups enabled) so power + * usage is minimized. Linux code should set the + * gpio direction first thing; but until it does, + * we may expose the wrong direction in sysfs. + */ + gpio_desc[id].flags = !chip->direction_input + ? (1 << FLAG_IS_OUT) + : 0; } } -fail_unlock: +unlock: spin_unlock_irqrestore(&gpio_lock, flags); + if (status == 0) + status = gpiochip_export(chip); fail: /* failures here can mean systems won't boot... */ if (status) @@ -239,6 +736,10 @@ int gpiochip_remove(struct gpio_chip *chip) } spin_unlock_irqrestore(&gpio_lock, flags); + + if (status == 0) + gpiochip_unexport(chip); + return status; } EXPORT_SYMBOL_GPL(gpiochip_remove); @@ -296,6 +797,8 @@ void gpio_free(unsigned gpio) return; } + gpio_unexport(gpio); + spin_lock_irqsave(&gpio_lock, flags); desc = &gpio_desc[gpio]; @@ -534,10 +1037,6 @@ EXPORT_SYMBOL_GPL(gpio_set_value_cansleep); #ifdef CONFIG_DEBUG_FS -#include <linux/debugfs.h> -#include <linux/seq_file.h> - - static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) { unsigned i; @@ -614,17 +1113,28 @@ static int gpiolib_show(struct seq_file *s, void *unused) /* REVISIT this isn't locked against gpio_chip removal ... */ for (gpio = 0; gpio_is_valid(gpio); gpio++) { + struct device *dev; + if (chip == gpio_desc[gpio].chip) continue; chip = gpio_desc[gpio].chip; if (!chip) continue; - seq_printf(s, "%sGPIOs %d-%d, %s%s:\n", + seq_printf(s, "%sGPIOs %d-%d", started ? "\n" : "", - chip->base, chip->base + chip->ngpio - 1, - chip->label ? : "generic", - chip->can_sleep ? ", can sleep" : ""); + chip->base, chip->base + chip->ngpio - 1); + dev = chip->dev; + if (dev) + seq_printf(s, ", %s/%s", + dev->bus ? dev->bus->name : "no-bus", + dev->bus_id); + if (chip->label) + seq_printf(s, ", %s", chip->label); + if (chip->can_sleep) + seq_printf(s, ", can sleep"); + seq_printf(s, ":\n"); + started = 1; if (chip->dbg_show) chip->dbg_show(s, chip); diff --git a/drivers/gpio/max7301.c b/drivers/gpio/max7301.c new file mode 100644 index 000000000000..39c795ad8312 --- /dev/null +++ b/drivers/gpio/max7301.c @@ -0,0 +1,339 @@ +/** + * drivers/gpio/max7301.c + * + * Copyright (C) 2006 Juergen Beisert, Pengutronix + * Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * The Maxim's MAX7301 device is an SPI driven GPIO expander. There are + * 28 GPIOs. 8 of them can trigger an interrupt. See datasheet for more + * details + * Note: + * - DIN must be stable at the rising edge of clock. + * - when writing: + * - always clock in 16 clocks at once + * - at DIN: D15 first, D0 last + * - D0..D7 = databyte, D8..D14 = commandbyte + * - D15 = low -> write command + * - when reading + * - always clock in 16 clocks at once + * - at DIN: D15 first, D0 last + * - D0..D7 = dummy, D8..D14 = register address + * - D15 = high -> read command + * - raise CS and assert it again + * - always clock in 16 clocks at once + * - at DOUT: D15 first, D0 last + * - D0..D7 contains the data from the first cycle + * + * The driver exports a standard gpiochip interface + */ + +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/spi/spi.h> +#include <linux/spi/max7301.h> +#include <linux/gpio.h> + +#define DRIVER_NAME "max7301" + +/* + * Pin configurations, see MAX7301 datasheet page 6 + */ +#define PIN_CONFIG_MASK 0x03 +#define PIN_CONFIG_IN_PULLUP 0x03 +#define PIN_CONFIG_IN_WO_PULLUP 0x02 +#define PIN_CONFIG_OUT 0x01 + +#define PIN_NUMBER 28 + + +/* + * Some registers must be read back to modify. + * To save time we cache them here in memory + */ +struct max7301 { + struct mutex lock; + u8 port_config[8]; /* field 0 is unused */ + u32 out_level; /* cached output levels */ + struct gpio_chip chip; + struct spi_device *spi; +}; + +/** + * max7301_write - Write a new register content + * @spi: The SPI device + * @reg: Register offset + * @val: Value to write + * + * A write to the MAX7301 means one message with one transfer + * + * Returns 0 if successful or a negative value on error + */ +static int max7301_write(struct spi_device *spi, unsigned int reg, unsigned int val) +{ + u16 word = ((reg & 0x7F) << 8) | (val & 0xFF); + return spi_write(spi, (const u8 *)&word, sizeof(word)); +} + +/** + * max7301_read - Read back register content + * @spi: The SPI device + * @reg: Register offset + * + * A read from the MAX7301 means two transfers; here, one message each + * + * Returns positive 8 bit value from device if successful or a + * negative value on error + */ +static int max7301_read(struct spi_device *spi, unsigned int reg) +{ + int ret; + u16 word; + + word = 0x8000 | (reg << 8); + ret = spi_write(spi, (const u8 *)&word, sizeof(word)); + if (ret) + return ret; + /* + * This relies on the fact, that a transfer with NULL tx_buf shifts out + * zero bytes (=NOOP for MAX7301) + */ + ret = spi_read(spi, (u8 *)&word, sizeof(word)); + if (ret) + return ret; + return word & 0xff; +} + +static int max7301_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + u8 *config; + int ret; + + /* First 4 pins are unused in the controller */ + offset += 4; + + config = &ts->port_config[offset >> 2]; + + mutex_lock(&ts->lock); + + /* Standard GPIO API doesn't support pull-ups, has to be extended. + * Hard-coding no pollup for now. */ + *config = (*config & ~(3 << (offset & 3))) | (1 << (offset & 3)); + + ret = max7301_write(ts->spi, 0x08 + (offset >> 2), *config); + + mutex_unlock(&ts->lock); + + return ret; +} + +static int __max7301_set(struct max7301 *ts, unsigned offset, int value) +{ + if (value) { + ts->out_level |= 1 << offset; + return max7301_write(ts->spi, 0x20 + offset, 0x01); + } else { + ts->out_level &= ~(1 << offset); + return max7301_write(ts->spi, 0x20 + offset, 0x00); + } +} + +static int max7301_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + u8 *config; + int ret; + + /* First 4 pins are unused in the controller */ + offset += 4; + + config = &ts->port_config[offset >> 2]; + + mutex_lock(&ts->lock); + + *config = (*config & ~(3 << (offset & 3))) | (1 << (offset & 3)); + + ret = __max7301_set(ts, offset, value); + + if (!ret) + ret = max7301_write(ts->spi, 0x08 + (offset >> 2), *config); + + mutex_unlock(&ts->lock); + + return ret; +} + +static int max7301_get(struct gpio_chip *chip, unsigned offset) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + int config, level = -EINVAL; + + /* First 4 pins are unused in the controller */ + offset += 4; + + mutex_lock(&ts->lock); + + config = (ts->port_config[offset >> 2] >> ((offset & 3) * 2)) & 3; + + switch (config) { + case 1: + /* Output: return cached level */ + level = !!(ts->out_level & (1 << offset)); + break; + case 2: + case 3: + /* Input: read out */ + level = max7301_read(ts->spi, 0x20 + offset) & 0x01; + } + mutex_unlock(&ts->lock); + + return level; +} + +static void max7301_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + + /* First 4 pins are unused in the controller */ + offset += 4; + + mutex_lock(&ts->lock); + + __max7301_set(ts, offset, value); + + mutex_unlock(&ts->lock); +} + +static int __devinit max7301_probe(struct spi_device *spi) +{ + struct max7301 *ts; + struct max7301_platform_data *pdata; + int i, ret; + + pdata = spi->dev.platform_data; + if (!pdata || !pdata->base) + return -ENODEV; + + /* + * bits_per_word cannot be configured in platform data + */ + spi->bits_per_word = 16; + + ret = spi_setup(spi); + if (ret < 0) + return ret; + + ts = kzalloc(sizeof(struct max7301), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + mutex_init(&ts->lock); + + dev_set_drvdata(&spi->dev, ts); + + /* Power up the chip and disable IRQ output */ + max7301_write(spi, 0x04, 0x01); + + ts->spi = spi; + + ts->chip.label = DRIVER_NAME, + + ts->chip.direction_input = max7301_direction_input; + ts->chip.get = max7301_get; + ts->chip.direction_output = max7301_direction_output; + ts->chip.set = max7301_set; + + ts->chip.base = pdata->base; + ts->chip.ngpio = PIN_NUMBER; + ts->chip.can_sleep = 1; + ts->chip.dev = &spi->dev; + ts->chip.owner = THIS_MODULE; + + ret = gpiochip_add(&ts->chip); + if (ret) + goto exit_destroy; + + /* + * tristate all pins in hardware and cache the + * register values for later use. + */ + for (i = 1; i < 8; i++) { + int j; + /* 0xAA means input with internal pullup disabled */ + max7301_write(spi, 0x08 + i, 0xAA); + ts->port_config[i] = 0xAA; + for (j = 0; j < 4; j++) { + int idx = ts->chip.base + (i - 1) * 4 + j; + ret = gpio_direction_input(idx); + if (ret) + goto exit_remove; + gpio_free(idx); + } + } + return ret; + +exit_remove: + gpiochip_remove(&ts->chip); +exit_destroy: + dev_set_drvdata(&spi->dev, NULL); + mutex_destroy(&ts->lock); + kfree(ts); + return ret; +} + +static int max7301_remove(struct spi_device *spi) +{ + struct max7301 *ts; + int ret; + + ts = dev_get_drvdata(&spi->dev); + if (ts == NULL) + return -ENODEV; + + dev_set_drvdata(&spi->dev, NULL); + + /* Power down the chip and disable IRQ output */ + max7301_write(spi, 0x04, 0x00); + + ret = gpiochip_remove(&ts->chip); + if (!ret) { + mutex_destroy(&ts->lock); + kfree(ts); + } else + dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n", + ret); + + return ret; +} + +static struct spi_driver max7301_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = max7301_probe, + .remove = __devexit_p(max7301_remove), +}; + +static int __init max7301_init(void) +{ + return spi_register_driver(&max7301_driver); +} + +static void __exit max7301_exit(void) +{ + spi_unregister_driver(&max7301_driver); +} + +module_init(max7301_init); +module_exit(max7301_exit); + +MODULE_AUTHOR("Juergen Beisert"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MAX7301 SPI based GPIO-Expander"); diff --git a/drivers/gpio/max732x.c b/drivers/gpio/max732x.c new file mode 100644 index 000000000000..b51c8135ca28 --- /dev/null +++ b/drivers/gpio/max732x.c @@ -0,0 +1,385 @@ +/* + * max732x.c - I2C Port Expander with 8/16 I/O + * + * Copyright (C) 2007 Marvell International Ltd. + * Copyright (C) 2008 Jack Ren <jack.ren@marvell.com> + * Copyright (C) 2008 Eric Miao <eric.miao@marvell.com> + * + * Derived from drivers/gpio/pca953x.c + * + * 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. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/gpio.h> + +#include <linux/i2c.h> +#include <linux/i2c/max732x.h> + + +/* + * Each port of MAX732x (including MAX7319) falls into one of the + * following three types: + * + * - Push Pull Output + * - Input + * - Open Drain I/O + * + * designated by 'O', 'I' and 'P' individually according to MAXIM's + * datasheets. + * + * There are two groups of I/O ports, each group usually includes + * up to 8 I/O ports, and is accessed by a specific I2C address: + * + * - Group A : by I2C address 0b'110xxxx + * - Group B : by I2C address 0b'101xxxx + * + * where 'xxxx' is decided by the connections of pin AD2/AD0. The + * address used also affects the initial state of output signals. + * + * Within each group of ports, there are five known combinations of + * I/O ports: 4I4O, 4P4O, 8I, 8P, 8O, see the definitions below for + * the detailed organization of these ports. + * + * GPIO numbers start from 'gpio_base + 0' to 'gpio_base + 8/16', + * and GPIOs from GROUP_A are numbered before those from GROUP_B + * (if there are two groups). + * + * NOTE: MAX7328/MAX7329 are drop-in replacements for PCF8574/a, so + * they are not supported by this driver. + */ + +#define PORT_NONE 0x0 /* '/' No Port */ +#define PORT_OUTPUT 0x1 /* 'O' Push-Pull, Output Only */ +#define PORT_INPUT 0x2 /* 'I' Input Only */ +#define PORT_OPENDRAIN 0x3 /* 'P' Open-Drain, I/O */ + +#define IO_4I4O 0x5AA5 /* O7 O6 I5 I4 I3 I2 O1 O0 */ +#define IO_4P4O 0x5FF5 /* O7 O6 P5 P4 P3 P2 O1 O0 */ +#define IO_8I 0xAAAA /* I7 I6 I5 I4 I3 I2 I1 I0 */ +#define IO_8P 0xFFFF /* P7 P6 P5 P4 P3 P2 P1 P0 */ +#define IO_8O 0x5555 /* O7 O6 O5 O4 O3 O2 O1 O0 */ + +#define GROUP_A(x) ((x) & 0xffff) /* I2C Addr: 0b'110xxxx */ +#define GROUP_B(x) ((x) << 16) /* I2C Addr: 0b'101xxxx */ + +static const struct i2c_device_id max732x_id[] = { + { "max7319", GROUP_A(IO_8I) }, + { "max7320", GROUP_B(IO_8O) }, + { "max7321", GROUP_A(IO_8P) }, + { "max7322", GROUP_A(IO_4I4O) }, + { "max7323", GROUP_A(IO_4P4O) }, + { "max7324", GROUP_A(IO_8I) | GROUP_B(IO_8O) }, + { "max7325", GROUP_A(IO_8P) | GROUP_B(IO_8O) }, + { "max7326", GROUP_A(IO_4I4O) | GROUP_B(IO_8O) }, + { "max7327", GROUP_A(IO_4P4O) | GROUP_B(IO_8O) }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, max732x_id); + +struct max732x_chip { + struct gpio_chip gpio_chip; + + struct i2c_client *client; /* "main" client */ + struct i2c_client *client_dummy; + struct i2c_client *client_group_a; + struct i2c_client *client_group_b; + + unsigned int mask_group_a; + unsigned int dir_input; + unsigned int dir_output; + + struct mutex lock; + uint8_t reg_out[2]; +}; + +static int max732x_write(struct max732x_chip *chip, int group_a, uint8_t val) +{ + struct i2c_client *client; + int ret; + + client = group_a ? chip->client_group_a : chip->client_group_b; + ret = i2c_smbus_write_byte(client, val); + if (ret < 0) { + dev_err(&client->dev, "failed writing\n"); + return ret; + } + + return 0; +} + +static int max732x_read(struct max732x_chip *chip, int group_a, uint8_t *val) +{ + struct i2c_client *client; + int ret; + + client = group_a ? chip->client_group_a : chip->client_group_b; + ret = i2c_smbus_read_byte(client); + if (ret < 0) { + dev_err(&client->dev, "failed reading\n"); + return ret; + } + + *val = (uint8_t)ret; + return 0; +} + +static inline int is_group_a(struct max732x_chip *chip, unsigned off) +{ + return (1u << off) & chip->mask_group_a; +} + +static int max732x_gpio_get_value(struct gpio_chip *gc, unsigned off) +{ + struct max732x_chip *chip; + uint8_t reg_val; + int ret; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + ret = max732x_read(chip, is_group_a(chip, off), ®_val); + if (ret < 0) + return 0; + + return reg_val & (1u << (off & 0x7)); +} + +static void max732x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) +{ + struct max732x_chip *chip; + uint8_t reg_out, mask = 1u << (off & 0x7); + int ret; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + mutex_lock(&chip->lock); + + reg_out = (off > 7) ? chip->reg_out[1] : chip->reg_out[0]; + reg_out = (val) ? reg_out | mask : reg_out & ~mask; + + ret = max732x_write(chip, is_group_a(chip, off), reg_out); + if (ret < 0) + goto out; + + /* update the shadow register then */ + if (off > 7) + chip->reg_out[1] = reg_out; + else + chip->reg_out[0] = reg_out; +out: + mutex_unlock(&chip->lock); +} + +static int max732x_gpio_direction_input(struct gpio_chip *gc, unsigned off) +{ + struct max732x_chip *chip; + unsigned int mask = 1u << off; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + if ((mask & chip->dir_input) == 0) { + dev_dbg(&chip->client->dev, "%s port %d is output only\n", + chip->client->name, off); + return -EACCES; + } + + return 0; +} + +static int max732x_gpio_direction_output(struct gpio_chip *gc, + unsigned off, int val) +{ + struct max732x_chip *chip; + unsigned int mask = 1u << off; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + if ((mask & chip->dir_output) == 0) { + dev_dbg(&chip->client->dev, "%s port %d is input only\n", + chip->client->name, off); + return -EACCES; + } + + max732x_gpio_set_value(gc, off, val); + return 0; +} + +static int __devinit max732x_setup_gpio(struct max732x_chip *chip, + const struct i2c_device_id *id, + unsigned gpio_start) +{ + struct gpio_chip *gc = &chip->gpio_chip; + uint32_t id_data = id->driver_data; + int i, port = 0; + + for (i = 0; i < 16; i++, id_data >>= 2) { + unsigned int mask = 1 << port; + + switch (id_data & 0x3) { + case PORT_OUTPUT: + chip->dir_output |= mask; + break; + case PORT_INPUT: + chip->dir_input |= mask; + break; + case PORT_OPENDRAIN: + chip->dir_output |= mask; + chip->dir_input |= mask; + break; + default: + continue; + } + + if (i < 8) + chip->mask_group_a |= mask; + port++; + } + + if (chip->dir_input) + gc->direction_input = max732x_gpio_direction_input; + if (chip->dir_output) { + gc->direction_output = max732x_gpio_direction_output; + gc->set = max732x_gpio_set_value; + } + gc->get = max732x_gpio_get_value; + gc->can_sleep = 1; + + gc->base = gpio_start; + gc->ngpio = port; + gc->label = chip->client->name; + gc->owner = THIS_MODULE; + + return port; +} + +static int __devinit max732x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max732x_platform_data *pdata; + struct max732x_chip *chip; + struct i2c_client *c; + uint16_t addr_a, addr_b; + int ret, nr_port; + + pdata = client->dev.platform_data; + if (pdata == NULL) + return -ENODEV; + + chip = kzalloc(sizeof(struct max732x_chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->client = client; + + nr_port = max732x_setup_gpio(chip, id, pdata->gpio_base); + + addr_a = (client->addr & 0x0f) | 0x60; + addr_b = (client->addr & 0x0f) | 0x50; + + switch (client->addr & 0x70) { + case 0x60: + chip->client_group_a = client; + if (nr_port > 7) { + c = i2c_new_dummy(client->adapter, addr_b); + chip->client_group_b = chip->client_dummy = c; + } + break; + case 0x50: + chip->client_group_b = client; + if (nr_port > 7) { + c = i2c_new_dummy(client->adapter, addr_a); + chip->client_group_a = chip->client_dummy = c; + } + break; + default: + dev_err(&client->dev, "invalid I2C address specified %02x\n", + client->addr); + ret = -EINVAL; + goto out_failed; + } + + mutex_init(&chip->lock); + + max732x_read(chip, is_group_a(chip, 0), &chip->reg_out[0]); + if (nr_port > 7) + max732x_read(chip, is_group_a(chip, 8), &chip->reg_out[1]); + + ret = gpiochip_add(&chip->gpio_chip); + if (ret) + goto out_failed; + + if (pdata->setup) { + ret = pdata->setup(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) + dev_warn(&client->dev, "setup failed, %d\n", ret); + } + + i2c_set_clientdata(client, chip); + return 0; + +out_failed: + kfree(chip); + return ret; +} + +static int __devexit max732x_remove(struct i2c_client *client) +{ + struct max732x_platform_data *pdata = client->dev.platform_data; + struct max732x_chip *chip = i2c_get_clientdata(client); + int ret; + + if (pdata->teardown) { + ret = pdata->teardown(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) { + dev_err(&client->dev, "%s failed, %d\n", + "teardown", ret); + return ret; + } + } + + ret = gpiochip_remove(&chip->gpio_chip); + if (ret) { + dev_err(&client->dev, "%s failed, %d\n", + "gpiochip_remove()", ret); + return ret; + } + + /* unregister any dummy i2c_client */ + if (chip->client_dummy) + i2c_unregister_device(chip->client_dummy); + + kfree(chip); + return 0; +} + +static struct i2c_driver max732x_driver = { + .driver = { + .name = "max732x", + .owner = THIS_MODULE, + }, + .probe = max732x_probe, + .remove = __devexit_p(max732x_remove), + .id_table = max732x_id, +}; + +static int __init max732x_init(void) +{ + return i2c_add_driver(&max732x_driver); +} +module_init(max732x_init); + +static void __exit max732x_exit(void) +{ + i2c_del_driver(&max732x_driver); +} +module_exit(max732x_exit); + +MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); +MODULE_DESCRIPTION("GPIO expander driver for MAX732X"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/mcp23s08.c b/drivers/gpio/mcp23s08.c index 7f92fdd5f0e2..8a1b405fefda 100644 --- a/drivers/gpio/mcp23s08.c +++ b/drivers/gpio/mcp23s08.c @@ -40,15 +40,26 @@ struct mcp23s08 { struct spi_device *spi; u8 addr; + u8 cache[11]; /* lock protects the cached values */ struct mutex lock; - u8 cache[11]; struct gpio_chip chip; struct work_struct work; }; +/* A given spi_device can represent up to four mcp23s08 chips + * sharing the same chipselect but using different addresses + * (e.g. chips #0 and #3 might be populated, but not #1 or $2). + * Driver data holds all the per-chip data. + */ +struct mcp23s08_driver_data { + unsigned ngpio; + struct mcp23s08 *mcp[4]; + struct mcp23s08 chip[]; +}; + static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg) { u8 tx[2], rx[1]; @@ -208,25 +219,18 @@ done: /*----------------------------------------------------------------------*/ -static int mcp23s08_probe(struct spi_device *spi) +static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr, + unsigned base, unsigned pullups) { - struct mcp23s08 *mcp; - struct mcp23s08_platform_data *pdata; + struct mcp23s08_driver_data *data = spi_get_drvdata(spi); + struct mcp23s08 *mcp = data->mcp[addr]; int status; int do_update = 0; - pdata = spi->dev.platform_data; - if (!pdata || pdata->slave > 3 || !pdata->base) - return -ENODEV; - - mcp = kzalloc(sizeof *mcp, GFP_KERNEL); - if (!mcp) - return -ENOMEM; - mutex_init(&mcp->lock); mcp->spi = spi; - mcp->addr = 0x40 | (pdata->slave << 1); + mcp->addr = 0x40 | (addr << 1); mcp->chip.label = "mcp23s08", @@ -236,26 +240,28 @@ static int mcp23s08_probe(struct spi_device *spi) mcp->chip.set = mcp23s08_set; mcp->chip.dbg_show = mcp23s08_dbg_show; - mcp->chip.base = pdata->base; + mcp->chip.base = base; mcp->chip.ngpio = 8; mcp->chip.can_sleep = 1; + mcp->chip.dev = &spi->dev; mcp->chip.owner = THIS_MODULE; - spi_set_drvdata(spi, mcp); - - /* verify MCP_IOCON.SEQOP = 0, so sequential reads work */ + /* verify MCP_IOCON.SEQOP = 0, so sequential reads work, + * and MCP_IOCON.HAEN = 1, so we work with all chips. + */ status = mcp23s08_read(mcp, MCP_IOCON); if (status < 0) goto fail; - if (status & IOCON_SEQOP) { + if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) { status &= ~IOCON_SEQOP; + status |= IOCON_HAEN; status = mcp23s08_write(mcp, MCP_IOCON, (u8) status); if (status < 0) goto fail; } /* configure ~100K pullups */ - status = mcp23s08_write(mcp, MCP_GPPU, pdata->pullups); + status = mcp23s08_write(mcp, MCP_GPPU, pullups); if (status < 0) goto fail; @@ -282,11 +288,58 @@ static int mcp23s08_probe(struct spi_device *spi) tx[1] = MCP_IPOL; memcpy(&tx[2], &mcp->cache[MCP_IPOL], sizeof(tx) - 2); status = spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0); - - /* FIXME check status... */ + if (status < 0) + goto fail; } status = gpiochip_add(&mcp->chip); +fail: + if (status < 0) + dev_dbg(&spi->dev, "can't setup chip %d, --> %d\n", + addr, status); + return status; +} + +static int mcp23s08_probe(struct spi_device *spi) +{ + struct mcp23s08_platform_data *pdata; + unsigned addr; + unsigned chips = 0; + struct mcp23s08_driver_data *data; + int status; + unsigned base; + + pdata = spi->dev.platform_data; + if (!pdata || !gpio_is_valid(pdata->base)) + return -ENODEV; + + for (addr = 0; addr < 4; addr++) { + if (!pdata->chip[addr].is_present) + continue; + chips++; + } + if (!chips) + return -ENODEV; + + data = kzalloc(sizeof *data + chips * sizeof(struct mcp23s08), + GFP_KERNEL); + if (!data) + return -ENOMEM; + spi_set_drvdata(spi, data); + + base = pdata->base; + for (addr = 0; addr < 4; addr++) { + if (!pdata->chip[addr].is_present) + continue; + chips--; + data->mcp[addr] = &data->chip[chips]; + status = mcp23s08_probe_one(spi, addr, base, + pdata->chip[addr].pullups); + if (status < 0) + goto fail; + base += 8; + } + data->ngpio = base - pdata->base; /* NOTE: these chips have a relatively sane IRQ framework, with * per-signal masking and level/edge triggering. It's not yet @@ -294,8 +347,9 @@ static int mcp23s08_probe(struct spi_device *spi) */ if (pdata->setup) { - status = pdata->setup(spi, mcp->chip.base, - mcp->chip.ngpio, pdata->context); + status = pdata->setup(spi, + pdata->base, data->ngpio, + pdata->context); if (status < 0) dev_dbg(&spi->dev, "setup --> %d\n", status); } @@ -303,19 +357,29 @@ static int mcp23s08_probe(struct spi_device *spi) return 0; fail: - kfree(mcp); + for (addr = 0; addr < 4; addr++) { + int tmp; + + if (!data->mcp[addr]) + continue; + tmp = gpiochip_remove(&data->mcp[addr]->chip); + if (tmp < 0) + dev_err(&spi->dev, "%s --> %d\n", "remove", tmp); + } + kfree(data); return status; } static int mcp23s08_remove(struct spi_device *spi) { - struct mcp23s08 *mcp = spi_get_drvdata(spi); + struct mcp23s08_driver_data *data = spi_get_drvdata(spi); struct mcp23s08_platform_data *pdata = spi->dev.platform_data; + unsigned addr; int status = 0; if (pdata->teardown) { status = pdata->teardown(spi, - mcp->chip.base, mcp->chip.ngpio, + pdata->base, data->ngpio, pdata->context); if (status < 0) { dev_err(&spi->dev, "%s --> %d\n", "teardown", status); @@ -323,11 +387,20 @@ static int mcp23s08_remove(struct spi_device *spi) } } - status = gpiochip_remove(&mcp->chip); + for (addr = 0; addr < 4; addr++) { + int tmp; + + if (!data->mcp[addr]) + continue; + + tmp = gpiochip_remove(&data->mcp[addr]->chip); + if (tmp < 0) { + dev_err(&spi->dev, "%s --> %d\n", "remove", tmp); + status = tmp; + } + } if (status == 0) - kfree(mcp); - else - dev_err(&spi->dev, "%s --> %d\n", "remove", status); + kfree(data); return status; } @@ -355,4 +428,3 @@ static void __exit mcp23s08_exit(void) module_exit(mcp23s08_exit); MODULE_LICENSE("GPL"); - diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c index a380730b61ab..cc8468692ae0 100644 --- a/drivers/gpio/pca953x.c +++ b/drivers/gpio/pca953x.c @@ -188,6 +188,7 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) gc->base = chip->gpio_start; gc->ngpio = gpios; gc->label = chip->client->name; + gc->dev = &chip->client->dev; gc->owner = THIS_MODULE; } diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/pcf857x.c index aa6cc8b2a2bc..fc9c6ae739ee 100644 --- a/drivers/gpio/pcf857x.c +++ b/drivers/gpio/pcf857x.c @@ -37,6 +37,8 @@ static const struct i2c_device_id pcf857x_id[] = { { "pca9671", 16 }, { "pca9673", 16 }, { "pca9675", 16 }, + { "max7328", 8 }, + { "max7329", 8 }, { } }; MODULE_DEVICE_TABLE(i2c, pcf857x_id); @@ -56,6 +58,7 @@ MODULE_DEVICE_TABLE(i2c, pcf857x_id); struct pcf857x { struct gpio_chip chip; struct i2c_client *client; + struct mutex lock; /* protect 'out' */ unsigned out; /* software latch */ }; @@ -66,9 +69,14 @@ struct pcf857x { static int pcf857x_input8(struct gpio_chip *chip, unsigned offset) { struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); + int status; + mutex_lock(&gpio->lock); gpio->out |= (1 << offset); - return i2c_smbus_write_byte(gpio->client, gpio->out); + status = i2c_smbus_write_byte(gpio->client, gpio->out); + mutex_unlock(&gpio->lock); + + return status; } static int pcf857x_get8(struct gpio_chip *chip, unsigned offset) @@ -84,12 +92,17 @@ static int pcf857x_output8(struct gpio_chip *chip, unsigned offset, int value) { struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); unsigned bit = 1 << offset; + int status; + mutex_lock(&gpio->lock); if (value) gpio->out |= bit; else gpio->out &= ~bit; - return i2c_smbus_write_byte(gpio->client, gpio->out); + status = i2c_smbus_write_byte(gpio->client, gpio->out); + mutex_unlock(&gpio->lock); + + return status; } static void pcf857x_set8(struct gpio_chip *chip, unsigned offset, int value) @@ -124,9 +137,14 @@ static int i2c_read_le16(struct i2c_client *client) static int pcf857x_input16(struct gpio_chip *chip, unsigned offset) { struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); + int status; + mutex_lock(&gpio->lock); gpio->out |= (1 << offset); - return i2c_write_le16(gpio->client, gpio->out); + status = i2c_write_le16(gpio->client, gpio->out); + mutex_unlock(&gpio->lock); + + return status; } static int pcf857x_get16(struct gpio_chip *chip, unsigned offset) @@ -142,12 +160,17 @@ static int pcf857x_output16(struct gpio_chip *chip, unsigned offset, int value) { struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); unsigned bit = 1 << offset; + int status; + mutex_lock(&gpio->lock); if (value) gpio->out |= bit; else gpio->out &= ~bit; - return i2c_write_le16(gpio->client, gpio->out); + status = i2c_write_le16(gpio->client, gpio->out); + mutex_unlock(&gpio->lock); + + return status; } static void pcf857x_set16(struct gpio_chip *chip, unsigned offset, int value) @@ -173,8 +196,11 @@ static int pcf857x_probe(struct i2c_client *client, if (!gpio) return -ENOMEM; + mutex_init(&gpio->lock); + gpio->chip.base = pdata->gpio_base; gpio->chip.can_sleep = 1; + gpio->chip.dev = &client->dev; gpio->chip.owner = THIS_MODULE; /* NOTE: the OnSemi jlc1562b is also largely compatible with diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 564138714bb5..452c2d866ec5 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -318,7 +318,7 @@ static void drm_cleanup(struct drm_device * dev) DRM_ERROR("Cannot unload module\n"); } -int drm_minors_cleanup(int id, void *ptr, void *data) +static int drm_minors_cleanup(int id, void *ptr, void *data) { struct drm_minor *minor = ptr; struct drm_device *dev; diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index f43d6d3cf2fa..426ac5add585 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -780,7 +780,7 @@ static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n) */ static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value) { - __le64 x; + u64 x; u64 m = (1ULL << n) - 1; if (n > 32) @@ -796,10 +796,10 @@ static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u3 report += offset >> 3; offset &= 7; - x = get_unaligned((__le64 *)report); - x &= cpu_to_le64(~(m << offset)); - x |= cpu_to_le64(((u64) value) << offset); - put_unaligned(x, (__le64 *) report); + x = get_unaligned_le64(report); + x &= ~(m << offset); + x |= ((u64)value) << offset; + put_unaligned_le64(x, report); } /* diff --git a/drivers/hid/hid-input-quirks.c b/drivers/hid/hid-input-quirks.c index 4c2052c658f1..16feea014494 100644 --- a/drivers/hid/hid-input-quirks.c +++ b/drivers/hid/hid-input-quirks.c @@ -89,6 +89,29 @@ static int quirk_logitech_ultrax_remote(struct hid_usage *usage, struct input_de return 1; } +static int quirk_gyration_remote(struct hid_usage *usage, struct input_dev *input, + unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) + return 0; + + set_bit(EV_REP, input->evbit); + switch(usage->hid & HID_USAGE) { + /* Reported on Gyration MCE Remote */ + case 0x00d: map_key_clear(KEY_HOME); break; + case 0x024: map_key_clear(KEY_DVD); break; + case 0x025: map_key_clear(KEY_PVR); break; + case 0x046: map_key_clear(KEY_MEDIA); break; + case 0x047: map_key_clear(KEY_MP3); break; + case 0x049: map_key_clear(KEY_CAMERA); break; + case 0x04a: map_key_clear(KEY_VIDEO); break; + + default: + return 0; + } + return 1; +} + static int quirk_chicony_tactical_pad(struct hid_usage *usage, struct input_dev *input, unsigned long **bit, int *max) { @@ -303,6 +326,9 @@ static int quirk_sunplus_wdesktop(struct hid_usage *usage, struct input_dev *inp #define VENDOR_ID_EZKEY 0x0518 #define DEVICE_ID_BTC_8193 0x0002 +#define VENDOR_ID_GYRATION 0x0c16 +#define DEVICE_ID_GYRATION_REMOTE 0x0002 + #define VENDOR_ID_LOGITECH 0x046d #define DEVICE_ID_LOGITECH_RECEIVER 0xc101 #define DEVICE_ID_S510_RECEIVER 0xc50c @@ -337,6 +363,8 @@ static const struct hid_input_blacklist { { VENDOR_ID_EZKEY, DEVICE_ID_BTC_8193, quirk_btc_8193 }, + { VENDOR_ID_GYRATION, DEVICE_ID_GYRATION_REMOTE, quirk_gyration_remote }, + { VENDOR_ID_LOGITECH, DEVICE_ID_LOGITECH_RECEIVER, quirk_logitech_ultrax_remote }, { VENDOR_ID_LOGITECH, DEVICE_ID_S510_RECEIVER, quirk_logitech_wireless }, { VENDOR_ID_LOGITECH, DEVICE_ID_S510_RECEIVER_2, quirk_logitech_wireless }, @@ -438,6 +466,18 @@ int hidinput_event_quirks(struct hid_device *hid, struct hid_field *field, struc input_event(input, usage->type, REL_WHEEL, -value); return 1; } + + /* Gyration MCE remote "Sleep" key */ + if (hid->vendor == VENDOR_ID_GYRATION && + hid->product == DEVICE_ID_GYRATION_REMOTE && + (usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK && + (usage->hid & 0xff) == 0x82) { + input_event(input, usage->type, usage->code, 1); + input_sync(input); + input_event(input, usage->type, usage->code, 0); + input_sync(input); + return 1; + } return 0; } diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 5c52a20ad344..1b2e8dc3398d 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -100,6 +100,8 @@ static struct hidinput_key_translation apple_fn_keys[] = { { KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY }, { KEY_F3, KEY_FN_F5, APPLE_FLAG_FKEY }, /* Exposé */ { KEY_F4, KEY_FN_F4, APPLE_FLAG_FKEY }, /* Dashboard */ + { KEY_F5, KEY_KBDILLUMDOWN, APPLE_FLAG_FKEY }, + { KEY_F6, KEY_KBDILLUMUP, APPLE_FLAG_FKEY }, { KEY_F7, KEY_PREVIOUSSONG, APPLE_FLAG_FKEY }, { KEY_F8, KEY_PLAYPAUSE, APPLE_FLAG_FKEY }, { KEY_F9, KEY_NEXTSONG, APPLE_FLAG_FKEY }, @@ -612,6 +614,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x0b6: map_key_clear(KEY_PREVIOUSSONG); break; case 0x0b7: map_key_clear(KEY_STOPCD); break; case 0x0b8: map_key_clear(KEY_EJECTCD); break; + case 0x0bc: map_key_clear(KEY_MEDIA_REPEAT); break; case 0x0cd: map_key_clear(KEY_PLAYPAUSE); break; case 0x0e0: map_abs_clear(ABS_VOLUME); break; diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 2fde6c63f47d..c40f0403edaf 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -105,6 +105,7 @@ out: static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { unsigned int minor = iminor(file->f_path.dentry->d_inode); + /* FIXME: What stops hidraw_table going NULL */ struct hid_device *dev = hidraw_table[minor]->hid; __u8 *buf; int ret = 0; @@ -211,38 +212,43 @@ static int hidraw_release(struct inode * inode, struct file * file) kfree(list->hidraw); } + kfree(list); + return 0; } -static int hidraw_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static long hidraw_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { + struct inode *inode = file->f_path.dentry->d_inode; unsigned int minor = iminor(inode); + long ret = 0; + /* FIXME: What stops hidraw_table going NULL */ struct hidraw *dev = hidraw_table[minor]; void __user *user_arg = (void __user*) arg; + lock_kernel(); switch (cmd) { case HIDIOCGRDESCSIZE: if (put_user(dev->hid->rsize, (int __user *)arg)) - return -EFAULT; - return 0; + ret = -EFAULT; + break; case HIDIOCGRDESC: { __u32 len; if (get_user(len, (int __user *)arg)) - return -EFAULT; - - if (len > HID_MAX_DESCRIPTOR_SIZE - 1) - return -EINVAL; - - if (copy_to_user(user_arg + offsetof( - struct hidraw_report_descriptor, - value[0]), - dev->hid->rdesc, - min(dev->hid->rsize, len))) - return -EFAULT; - return 0; + ret = -EFAULT; + else if (len > HID_MAX_DESCRIPTOR_SIZE - 1) + ret = -EINVAL; + else if (copy_to_user(user_arg + offsetof( + struct hidraw_report_descriptor, + value[0]), + dev->hid->rdesc, + min(dev->hid->rsize, len))) + ret = -EFAULT; + break; } case HIDIOCGRAWINFO: { @@ -252,15 +258,13 @@ static int hidraw_ioctl(struct inode *inode, struct file *file, unsigned int cmd dinfo.vendor = dev->hid->vendor; dinfo.product = dev->hid->product; if (copy_to_user(user_arg, &dinfo, sizeof(dinfo))) - return -EFAULT; - - return 0; + ret = -EFAULT; + break; } default: - printk(KERN_EMERG "hidraw: unsupported ioctl() %x\n", - cmd); + ret = -ENOTTY; } - return -EINVAL; + return ret; } static const struct file_operations hidraw_ops = { @@ -270,7 +274,7 @@ static const struct file_operations hidraw_ops = { .poll = hidraw_poll, .open = hidraw_open, .release = hidraw_release, - .ioctl = hidraw_ioctl, + .unlocked_ioctl = hidraw_ioctl, }; void hidraw_report_event(struct hid_device *hid, u8 *data, int len) @@ -322,8 +326,9 @@ int hidraw_connect(struct hid_device *hid) goto out; } - dev->dev = device_create(hidraw_class, NULL, MKDEV(hidraw_major, minor), - "%s%d", "hidraw", minor); + dev->dev = device_create_drvdata(hidraw_class, NULL, + MKDEV(hidraw_major, minor), NULL, + "%s%d", "hidraw", minor); if (IS_ERR(dev->dev)) { spin_lock(&minors_lock); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 01427c51c7cc..27fe4d8912cb 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -122,7 +122,7 @@ static void hid_reset(struct work_struct *work) dev_dbg(&usbhid->intf->dev, "resetting device\n"); rc = rc_lock = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf); if (rc_lock >= 0) { - rc = usb_reset_composite_device(hid_to_usb_dev(hid), usbhid->intf); + rc = usb_reset_device(hid_to_usb_dev(hid)); if (rc_lock) usb_unlock_device(hid_to_usb_dev(hid)); } diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 1df832a8fcbc..61e78a4369b9 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -69,12 +69,18 @@ #define USB_DEVICE_ID_APPLE_ALU_ANSI 0x0220 #define USB_DEVICE_ID_APPLE_ALU_ISO 0x0221 #define USB_DEVICE_ID_APPLE_ALU_JIS 0x0222 +#define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI 0x0223 +#define USB_DEVICE_ID_APPLE_WELLSPRING_ISO 0x0224 +#define USB_DEVICE_ID_APPLE_WELLSPRING_JIS 0x0225 #define USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI 0x0229 #define USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO 0x022a #define USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS 0x022b #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI 0x022c #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO 0x022d #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS 0x022e +#define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI 0x0230 +#define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO 0x0231 +#define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS 0x0232 #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b #define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242 @@ -241,6 +247,8 @@ #define USB_DEVICE_ID_LD_MACHINETEST 0x2040 #define USB_VENDOR_ID_LOGITECH 0x046d +#define USB_DEVICE_ID_LOGITECH_LX3 0xc044 +#define USB_DEVICE_ID_LOGITECH_V150 0xc047 #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 #define USB_DEVICE_ID_LOGITECH_HARMONY 0xc110 #define USB_DEVICE_ID_LOGITECH_HARMONY_2 0xc111 @@ -314,6 +322,7 @@ #define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 #define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512 #define USB_DEVICE_ID_MX3000_RECEIVER 0xc513 +#define USB_DEVICE_ID_DINOVO_DESKTOP 0xc704 #define USB_DEVICE_ID_DINOVO_EDGE 0xc714 #define USB_DEVICE_ID_DINOVO_MINI 0xc71f @@ -443,7 +452,8 @@ static const struct hid_blacklist { { 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_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP, HID_QUIRK_DUPLICATE_USAGES }, { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES }, { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI, HID_QUIRK_DUPLICATE_USAGES }, @@ -593,6 +603,8 @@ static const struct hid_blacklist { { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD, HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL | HID_QUIRK_LOGITECH_EXPANDED_KEYMAP }, { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500, HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL | HID_QUIRK_LOGITECH_EXPANDED_KEYMAP }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3, HID_QUIRK_INVERT_HWHEEL }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150, HID_QUIRK_INVERT_HWHEEL }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K, HID_QUIRK_MICROSOFT_KEYS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K, HID_QUIRK_MICROSOFT_KEYS }, @@ -642,6 +654,12 @@ static const struct hid_blacklist { { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN }, { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD }, { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI, HID_QUIRK_APPLE_HAS_FN }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS, HID_QUIRK_APPLE_HAS_FN }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI, HID_QUIRK_APPLE_HAS_FN }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS, HID_QUIRK_APPLE_HAS_FN }, { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, @@ -1128,7 +1146,7 @@ static void usbhid_fixup_microsoft_descriptor(unsigned char *rdesc, int rsize) && rdesc[557] == 0x19 && rdesc[559] == 0x29) { printk(KERN_INFO "Fixing up Microsoft Wireless Receiver Model 1028 report descriptor\n"); - rdesc[284] = rdesc[304] = rdesc[558] = 0x35; + rdesc[284] = rdesc[304] = rdesc[557] = 0x35; rdesc[352] = 0x36; rdesc[286] = rdesc[355] = 0x46; rdesc[306] = rdesc[559] = 0x45; diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 95cc192bc7af..842e9edb888e 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -406,6 +406,7 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd, uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL); if (!uref_multi) return -ENOMEM; + lock_kernel(); uref = &uref_multi->uref; if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) { if (copy_from_user(uref_multi, user_arg, @@ -501,12 +502,15 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd, } goodreturn: + unlock_kernel(); kfree(uref_multi); return 0; fault: + unlock_kernel(); kfree(uref_multi); return -EFAULT; inval: + unlock_kernel(); kfree(uref_multi); return -EINVAL; } @@ -540,7 +544,7 @@ static noinline int hiddev_ioctl_string(struct hiddev *hiddev, unsigned int cmd, return len; } -static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct hiddev_list *list = file->private_data; struct hiddev *hiddev = list->hiddev; @@ -555,7 +559,10 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd struct usbhid_device *usbhid = hid->driver_data; void __user *user_arg = (void __user *)arg; int i; + + /* Called without BKL by compat methods so no BKL taken */ + /* FIXME: Who or what stop this racing with a disconnect ?? */ if (!hiddev->exist) return -EIO; @@ -756,8 +763,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd #ifdef CONFIG_COMPAT static long hiddev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct inode *inode = file->f_path.dentry->d_inode; - return hiddev_ioctl(inode, file, cmd, (unsigned long)compat_ptr(arg)); + return hiddev_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); } #endif @@ -768,7 +774,7 @@ static const struct file_operations hiddev_fops = { .poll = hiddev_poll, .open = hiddev_open, .release = hiddev_release, - .ioctl = hiddev_ioctl, + .unlocked_ioctl = hiddev_ioctl, .fasync = hiddev_fasync, #ifdef CONFIG_COMPAT .compat_ioctl = hiddev_compat_ioctl, diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c index 3cd46d2e53c1..0caaafe01843 100644 --- a/drivers/hid/usbhid/usbkbd.c +++ b/drivers/hid/usbhid/usbkbd.c @@ -43,7 +43,7 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE(DRIVER_LICENSE); -static unsigned char usb_kbd_keycode[256] = { +static const unsigned char usb_kbd_keycode[256] = { 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, @@ -233,14 +233,6 @@ static int usb_kbd_probe(struct usb_interface *iface, if (!usb_endpoint_is_int_in(endpoint)) return -ENODEV; -#ifdef CONFIG_USB_HID - if (usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)) - & HID_QUIRK_IGNORE) { - return -ENODEV; - } -#endif - pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); diff --git a/drivers/hid/usbhid/usbmouse.c b/drivers/hid/usbhid/usbmouse.c index 703e9d0e8714..35689ef172cc 100644 --- a/drivers/hid/usbhid/usbmouse.c +++ b/drivers/hid/usbhid/usbmouse.c @@ -129,14 +129,6 @@ static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_i if (!usb_endpoint_is_int_in(endpoint)) return -ENODEV; -#ifdef CONFIG_USB_HID - if (usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)) - & (HID_QUIRK_IGNORE|HID_QUIRK_IGNORE_MOUSE)) { - return -ENODEV; - } -#endif - pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 3db28450a3b3..7321a88a5112 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -55,7 +55,8 @@ again: return ERR_PTR(err); id = id & MAX_ID_MASK; - hwdev = device_create(hwmon_class, dev, MKDEV(0,0), HWMON_ID_FORMAT, id); + hwdev = device_create_drvdata(hwmon_class, dev, MKDEV(0, 0), NULL, + HWMON_ID_FORMAT, id); if (IS_ERR(hwdev)) { spin_lock(&idr_lock); diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c index 48d084bdf7c8..3c855ff2992f 100644 --- a/drivers/i2c/busses/i2c-bfin-twi.c +++ b/drivers/i2c/busses/i2c-bfin-twi.c @@ -49,6 +49,8 @@ struct bfin_twi_iface { struct i2c_msg *pmsg; int msg_num; int cur_msg; + u16 saved_clkdiv; + u16 saved_control; void __iomem *regs_base; }; @@ -565,32 +567,43 @@ static u32 bfin_twi_functionality(struct i2c_adapter *adap) I2C_FUNC_I2C; } - static struct i2c_algorithm bfin_twi_algorithm = { .master_xfer = bfin_twi_master_xfer, .smbus_xfer = bfin_twi_smbus_xfer, .functionality = bfin_twi_functionality, }; - -static int i2c_bfin_twi_suspend(struct platform_device *dev, pm_message_t state) +static int i2c_bfin_twi_suspend(struct platform_device *pdev, pm_message_t state) { - struct bfin_twi_iface *iface = platform_get_drvdata(dev); + struct bfin_twi_iface *iface = platform_get_drvdata(pdev); + + iface->saved_clkdiv = read_CLKDIV(iface); + iface->saved_control = read_CONTROL(iface); + + free_irq(iface->irq, iface); /* Disable TWI */ - write_CONTROL(iface, read_CONTROL(iface) & ~TWI_ENA); - SSYNC(); + write_CONTROL(iface, iface->saved_control & ~TWI_ENA); return 0; } -static int i2c_bfin_twi_resume(struct platform_device *dev) +static int i2c_bfin_twi_resume(struct platform_device *pdev) { - struct bfin_twi_iface *iface = platform_get_drvdata(dev); + struct bfin_twi_iface *iface = platform_get_drvdata(pdev); - /* Enable TWI */ - write_CONTROL(iface, read_CONTROL(iface) | TWI_ENA); - SSYNC(); + int rc = request_irq(iface->irq, bfin_twi_interrupt_entry, + IRQF_DISABLED, pdev->name, iface); + if (rc) { + dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq); + return -ENODEV; + } + + /* Resume TWI interface clock as specified */ + write_CLKDIV(iface, iface->saved_clkdiv); + + /* Resume TWI */ + write_CONTROL(iface, iface->saved_control); return 0; } diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index 79b455a1f090..32104eac8d3d 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -77,7 +77,7 @@ static int i2c_gpio_getscl(void *data) return gpio_get_value(pdata->scl_pin); } -static int __init i2c_gpio_probe(struct platform_device *pdev) +static int __devinit i2c_gpio_probe(struct platform_device *pdev) { struct i2c_gpio_platform_data *pdata; struct i2c_algo_bit_data *bit_data; @@ -174,7 +174,7 @@ err_alloc_adap: return ret; } -static int __exit i2c_gpio_remove(struct platform_device *pdev) +static int __devexit i2c_gpio_remove(struct platform_device *pdev) { struct i2c_gpio_platform_data *pdata; struct i2c_adapter *adap; @@ -196,14 +196,15 @@ static struct platform_driver i2c_gpio_driver = { .name = "i2c-gpio", .owner = THIS_MODULE, }, - .remove = __exit_p(i2c_gpio_remove), + .probe = i2c_gpio_probe, + .remove = __devexit_p(i2c_gpio_remove), }; static int __init i2c_gpio_init(void) { int ret; - ret = platform_driver_probe(&i2c_gpio_driver, i2c_gpio_probe); + ret = platform_driver_register(&i2c_gpio_driver); if (ret) printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret); diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 007390ad9810..4864723c7425 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -33,6 +33,7 @@ #include <linux/err.h> #include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/cpufreq.h> #include <asm/hardware.h> #include <asm/irq.h> @@ -64,6 +65,7 @@ struct s3c24xx_i2c { unsigned int tx_setup; enum s3c24xx_i2c_state state; + unsigned long clkrate; void __iomem *regs; struct clk *clk; @@ -71,6 +73,10 @@ struct s3c24xx_i2c { struct resource *irq; struct resource *ioarea; struct i2c_adapter adap; + +#ifdef CONFIG_CPU_FREQ + struct notifier_block freq_transition; +#endif }; /* default platform data to use if not supplied in the platform_device @@ -501,6 +507,9 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int unsigned long timeout; int ret; + if (!readl(i2c->regs + S3C2410_IICCON) & S3C2410_IICCON_IRQEN) + return -EIO; + ret = s3c24xx_i2c_set_master(i2c); if (ret != 0) { dev_err(i2c->dev, "cannot get bus (error %d)\n", ret); @@ -636,27 +645,28 @@ static inline int freq_acceptable(unsigned int freq, unsigned int wanted) return (diff >= -2 && diff <= 2); } -/* s3c24xx_i2c_getdivisor +/* s3c24xx_i2c_clockrate * * work out a divisor for the user requested frequency setting, * either by the requested frequency, or scanning the acceptable * range of frequencies until something is found */ -static int s3c24xx_i2c_getdivisor(struct s3c24xx_i2c *i2c, - struct s3c2410_platform_i2c *pdata, - unsigned long *iicon, - unsigned int *got) +static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got) { + struct s3c2410_platform_i2c *pdata; unsigned long clkin = clk_get_rate(i2c->clk); - unsigned int divs, div1; + u32 iiccon; int freq; int start, end; + i2c->clkrate = clkin; + + pdata = s3c24xx_i2c_get_platformdata(i2c->adap.dev.parent); clkin /= 1000; /* clkin now in KHz */ - dev_dbg(i2c->dev, "pdata %p, freq %lu %lu..%lu\n", + dev_dbg(i2c->dev, "pdata %p, freq %lu %lu..%lu\n", pdata, pdata->bus_freq, pdata->min_freq, pdata->max_freq); if (pdata->bus_freq != 0) { @@ -688,11 +698,79 @@ static int s3c24xx_i2c_getdivisor(struct s3c24xx_i2c *i2c, found: *got = freq; - *iicon |= (divs-1); - *iicon |= (div1 == 512) ? S3C2410_IICCON_TXDIV_512 : 0; + + iiccon = readl(i2c->regs + S3C2410_IICCON); + iiccon &= ~(S3C2410_IICCON_SCALEMASK | S3C2410_IICCON_TXDIV_512); + iiccon |= (divs-1); + + if (div1 == 512) + iiccon |= S3C2410_IICCON_TXDIV_512; + + writel(iiccon, i2c->regs + S3C2410_IICCON); + + return 0; +} + +#ifdef CONFIG_CPU_FREQ + +#define freq_to_i2c(_n) container_of(_n, struct s3c24xx_i2c, freq_transition) + +static int s3c24xx_i2c_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct s3c24xx_i2c *i2c = freq_to_i2c(nb); + unsigned long flags; + unsigned int got; + int delta_f; + int ret; + + delta_f = clk_get_rate(i2c->clk) - i2c->clkrate; + + /* if we're post-change and the input clock has slowed down + * or at pre-change and the clock is about to speed up, then + * adjust our clock rate. <0 is slow, >0 speedup. + */ + + if ((val == CPUFREQ_POSTCHANGE && delta_f < 0) || + (val == CPUFREQ_PRECHANGE && delta_f > 0)) { + spin_lock_irqsave(&i2c->lock, flags); + ret = s3c24xx_i2c_clockrate(i2c, &got); + spin_unlock_irqrestore(&i2c->lock, flags); + + if (ret < 0) + dev_err(i2c->dev, "cannot find frequency\n"); + else + dev_info(i2c->dev, "setting freq %d\n", got); + } + + return 0; +} + +static inline int s3c24xx_i2c_register_cpufreq(struct s3c24xx_i2c *i2c) +{ + i2c->freq_transition.notifier_call = s3c24xx_i2c_cpufreq_transition; + + return cpufreq_register_notifier(&i2c->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +static inline void s3c24xx_i2c_deregister_cpufreq(struct s3c24xx_i2c *i2c) +{ + cpufreq_unregister_notifier(&i2c->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +#else +static inline int s3c24xx_i2c_register_cpufreq(struct s3c24xx_i2c *i2c) +{ return 0; } +static inline void s3c24xx_i2c_deregister_cpufreq(struct s3c24xx_i2c *i2c) +{ +} +#endif + /* s3c24xx_i2c_init * * initialise the controller, set the IO lines and frequency @@ -719,9 +797,12 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr); + writel(iicon, i2c->regs + S3C2410_IICCON); + /* we need to work out the divisors for the clock... */ - if (s3c24xx_i2c_getdivisor(i2c, pdata, &iicon, &freq) != 0) { + if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) { + writel(0, i2c->regs + S3C2410_IICCON); dev_err(i2c->dev, "cannot meet bus frequency required\n"); return -EINVAL; } @@ -730,8 +811,6 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq); dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon); - - writel(iicon, i2c->regs + S3C2410_IICCON); /* check for s3c2440 i2c controller */ @@ -752,9 +831,12 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) static int s3c24xx_i2c_probe(struct platform_device *pdev) { struct s3c24xx_i2c *i2c = &s3c24xx_i2c; + struct s3c2410_platform_i2c *pdata; struct resource *res; int ret; + pdata = s3c24xx_i2c_get_platformdata(&pdev->dev); + /* find the clock and enable it */ i2c->dev = &pdev->dev; @@ -832,17 +914,34 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "irq resource %p (%lu)\n", res, (unsigned long)res->start); - ret = i2c_add_adapter(&i2c->adap); + ret = s3c24xx_i2c_register_cpufreq(i2c); if (ret < 0) { - dev_err(&pdev->dev, "failed to add bus to i2c core\n"); + dev_err(&pdev->dev, "failed to register cpufreq notifier\n"); goto err_irq; } + /* Note, previous versions of the driver used i2c_add_adapter() + * to add the bus at any number. We now pass the bus number via + * the platform data, so if unset it will now default to always + * being bus 0. + */ + + i2c->adap.nr = pdata->bus_num; + + ret = i2c_add_numbered_adapter(&i2c->adap); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add bus to i2c core\n"); + goto err_cpufreq; + } + platform_set_drvdata(pdev, i2c); dev_info(&pdev->dev, "%s: S3C I2C adapter\n", i2c->adap.dev.bus_id); return 0; + err_cpufreq: + s3c24xx_i2c_deregister_cpufreq(i2c); + err_irq: free_irq(i2c->irq->start, i2c); @@ -870,6 +969,8 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev) { struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); + s3c24xx_i2c_deregister_cpufreq(i2c); + i2c_del_adapter(&i2c->adap); free_irq(i2c->irq->start, i2c); diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index 50e0a4653741..a95cb9465d65 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -126,7 +126,7 @@ config ISP1301_OMAP config TPS65010 tristate "TPS6501x Power Management chips" - depends on HAVE_GPIO_LIB + depends on GPIOLIB default y if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_OSK help If you say yes here you get support for the TPS6501x series of diff --git a/drivers/i2c/chips/tps65010.c b/drivers/i2c/chips/tps65010.c index 85949685191b..cf02e8fceb42 100644 --- a/drivers/i2c/chips/tps65010.c +++ b/drivers/i2c/chips/tps65010.c @@ -636,6 +636,8 @@ static int tps65010_probe(struct i2c_client *client, tps->outmask = board->outmask; tps->chip.label = client->name; + tps->chip.dev = &client->dev; + tps->chip.owner = THIS_MODULE; tps->chip.set = tps65010_gpio_set; tps->chip.direction_output = tps65010_output; diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 7608df83d6d1..7bf38c418086 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -722,7 +722,8 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver) INIT_LIST_HEAD(&driver->clients); /* Walk the adapters that are already present */ - class_for_each_device(&i2c_adapter_class, driver, __attach_adapter); + class_for_each_device(&i2c_adapter_class, NULL, driver, + __attach_adapter); mutex_unlock(&core_lock); return 0; @@ -782,7 +783,8 @@ void i2c_del_driver(struct i2c_driver *driver) { mutex_lock(&core_lock); - class_for_each_device(&i2c_adapter_class, driver, __detach_adapter); + class_for_each_device(&i2c_adapter_class, NULL, driver, + __detach_adapter); driver_unregister(&driver->driver); pr_debug("i2c-core: driver [%s] unregistered\n", driver->driver.name); diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 86727fa8858f..9d55c6383b23 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -521,9 +521,9 @@ static int i2cdev_attach_adapter(struct i2c_adapter *adap) return PTR_ERR(i2c_dev); /* register this i2c device with the driver core */ - i2c_dev->dev = device_create(i2c_dev_class, &adap->dev, - MKDEV(I2C_MAJOR, adap->nr), - "i2c-%d", adap->nr); + i2c_dev->dev = device_create_drvdata(i2c_dev_class, &adap->dev, + MKDEV(I2C_MAJOR, adap->nr), + NULL, "i2c-%d", adap->nr); if (IS_ERR(i2c_dev->dev)) { res = PTR_ERR(i2c_dev->dev); goto error; diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 15b09b89588a..130ef64b44f7 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -314,7 +314,7 @@ comment "IDE chipset support/bugfixes" config IDE_GENERIC tristate "generic/default IDE chipset support" - depends on ALPHA || X86 || IA64 || M32R || MIPS || PPC32 + depends on ALPHA || X86 || IA64 || M32R || MIPS help If unsure, say N. @@ -510,6 +510,7 @@ config BLK_DEV_TRIFLEX config BLK_DEV_CY82C693 tristate "CY82C693 chipset support" + depends on ALPHA select IDE_TIMINGS select BLK_DEV_IDEDMA_PCI help @@ -548,6 +549,7 @@ config BLK_DEV_CS5535 config BLK_DEV_HPT34X tristate "HPT34X chipset support" + depends on BROKEN select BLK_DEV_IDEDMA_PCI help This driver adds up to 4 more EIDE devices sharing a single diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index 5d414e301a5a..64e0ecdc4ed5 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -1,13 +1,6 @@ # -# Makefile for the kernel ata, atapi, and ide block device drivers. -# -# 12 September 2000, Bartlomiej Zolnierkiewicz <bkz@linux-ide.org> -# Rewritten to use lists instead of if-statements. -# -# Note : at this point, these files are compiled on all systems. -# In the future, some of these should be built conditionally. -# # link order is important here +# EXTRA_CFLAGS += -Idrivers/ide diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index 52f58c885783..df4af4083954 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -72,7 +72,7 @@ struct icside_state { void __iomem *ioc_base; unsigned int sel; unsigned int type; - ide_hwif_t *hwif[2]; + struct ide_host *host; }; #define ICS_TYPE_A3IN 0 @@ -375,12 +375,14 @@ static int icside_dma_test_irq(ide_drive_t *drive) static void icside_dma_timeout(ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; + printk(KERN_ERR "%s: DMA timeout occurred: ", drive->name); if (icside_dma_test_irq(drive)) return; - ide_dump_status(drive, "DMA timeout", ide_read_status(drive)); + ide_dump_status(drive, "DMA timeout", hwif->tp_ops->read_status(hwif)); icside_dma_end(drive); } @@ -440,10 +442,10 @@ static void icside_setup_ports(hw_regs_t *hw, void __iomem *base, static int __init icside_register_v5(struct icside_state *state, struct expansion_card *ec) { - ide_hwif_t *hwif; void __iomem *base; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw; + struct ide_host *host; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; + int ret; base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0); if (!base) @@ -463,22 +465,23 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec) icside_setup_ports(&hw, base, &icside_cardinfo_v5, ec); - hwif = ide_find_port(); - if (!hwif) + host = ide_host_alloc(NULL, hws); + if (host == NULL) return -ENODEV; - ide_init_port_hw(hwif, &hw); - default_hwif_mmiops(hwif); - - state->hwif[0] = hwif; + state->host = host; ecard_set_drvdata(ec, state); - idx[0] = hwif->index; - - ide_device_add(idx, NULL); + ret = ide_host_register(host, NULL, hws); + if (ret) + goto err_free; return 0; +err_free: + ide_host_free(host); + ecard_set_drvdata(ec, NULL); + return ret; } static const struct ide_port_info icside_v6_port_info __initdata = { @@ -493,13 +496,12 @@ static const struct ide_port_info icside_v6_port_info __initdata = { static int __init icside_register_v6(struct icside_state *state, struct expansion_card *ec) { - ide_hwif_t *hwif, *mate; void __iomem *ioc_base, *easi_base; + struct ide_host *host; unsigned int sel = 0; int ret; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + hw_regs_t hw[2], *hws[] = { &hw[0], NULL, NULL, NULL }; struct ide_port_info d = icside_v6_port_info; - hw_regs_t hw[2]; ioc_base = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0); if (!ioc_base) { @@ -538,28 +540,11 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec) icside_setup_ports(&hw[0], easi_base, &icside_cardinfo_v6_1, ec); icside_setup_ports(&hw[1], easi_base, &icside_cardinfo_v6_2, ec); - /* - * Find and register the interfaces. - */ - hwif = ide_find_port(); - if (hwif == NULL) + host = ide_host_alloc(&d, hws); + if (host == NULL) return -ENODEV; - ide_init_port_hw(hwif, &hw[0]); - default_hwif_mmiops(hwif); - - idx[0] = hwif->index; - - mate = ide_find_port(); - if (mate) { - ide_init_port_hw(mate, &hw[1]); - default_hwif_mmiops(mate); - - idx[1] = mate->index; - } - - state->hwif[0] = hwif; - state->hwif[1] = mate; + state->host = host; ecard_set_drvdata(ec, state); @@ -569,11 +554,17 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec) d.dma_ops = NULL; } - ide_device_add(idx, &d); + ret = ide_host_register(host, NULL, hws); + if (ret) + goto err_free; return 0; - - out: +err_free: + ide_host_free(host); + if (d.dma_ops) + free_dma(ec->dma); + ecard_set_drvdata(ec, NULL); +out: return ret; } @@ -719,8 +710,14 @@ static int __init icside_init(void) return ecard_register_driver(&icside_driver); } +static void __exit icside_exit(void); +{ + ecard_unregister_driver(&icside_driver); +} + MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("ICS IDE driver"); module_init(icside_init); +module_exit(icside_exit); diff --git a/drivers/ide/arm/ide_arm.c b/drivers/ide/arm/ide_arm.c index 2f311da4c963..176532ffae0e 100644 --- a/drivers/ide/arm/ide_arm.c +++ b/drivers/ide/arm/ide_arm.c @@ -28,10 +28,8 @@ static int __init ide_arm_init(void) { - ide_hwif_t *hwif; - hw_regs_t hw; unsigned long base = IDE_ARM_IO, ctl = IDE_ARM_IO + 0x206; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; if (!request_region(base, 8, DRV_NAME)) { printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n", @@ -51,15 +49,7 @@ static int __init ide_arm_init(void) hw.irq = IDE_ARM_IRQ; hw.chipset = ide_generic; - hwif = ide_find_port(); - if (hwif) { - ide_init_port_hw(hwif, &hw); - idx[0] = hwif->index; - - ide_device_add(idx, NULL); - } - - return 0; + return ide_host_add(NULL, hws, NULL); } module_init(ide_arm_init); diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c index c79b85b6e4a3..3e842d60eae9 100644 --- a/drivers/ide/arm/palm_bk3710.c +++ b/drivers/ide/arm/palm_bk3710.c @@ -82,6 +82,7 @@ static const struct palm_bk3710_udmatiming palm_bk3710_udmatimings[6] = { {100, 120}, /* UDMA Mode 2 */ {100, 90}, /* UDMA Mode 3 */ {100, 60}, /* UDMA Mode 4 */ + {85, 40}, /* UDMA Mode 5 */ }; static void palm_bk3710_setudmamode(void __iomem *base, unsigned int dev, @@ -316,15 +317,14 @@ static u8 __devinit palm_bk3710_cable_detect(ide_hwif_t *hwif) static int __devinit palm_bk3710_init_dma(ide_hwif_t *hwif, const struct ide_port_info *d) { - unsigned long base = - hwif->io_ports.data_addr - IDE_PALM_ATA_PRI_REG_OFFSET; - printk(KERN_INFO " %s: MMIO-DMA\n", hwif->name); if (ide_allocate_dma_engine(hwif)) return -1; - ide_setup_dma(hwif, base); + hwif->dma_base = hwif->io_ports.data_addr - IDE_PALM_ATA_PRI_REG_OFFSET; + + hwif->dma_ops = &sff_dma_ops; return 0; } @@ -335,12 +335,11 @@ static const struct ide_port_ops palm_bk3710_ports_ops = { .cable_detect = palm_bk3710_cable_detect, }; -static const struct ide_port_info __devinitdata palm_bk3710_port_info = { +static struct ide_port_info __devinitdata palm_bk3710_port_info = { .init_dma = palm_bk3710_init_dma, .port_ops = &palm_bk3710_ports_ops, .host_flags = IDE_HFLAG_MMIO, .pio_mask = ATA_PIO4, - .udma_mask = ATA_UDMA4, /* (input clk 99MHz) */ .mwdma_mask = ATA_MWDMA2, }; @@ -348,13 +347,12 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev) { struct clk *clk; struct resource *mem, *irq; - ide_hwif_t *hwif; + struct ide_host *host; unsigned long base, rate; - int i; - hw_regs_t hw; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + int i, rc; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - clk = clk_get(NULL, "IDECLK"); + clk = clk_get(&pdev->dev, "IDECLK"); if (IS_ERR(clk)) return -ENODEV; @@ -394,24 +392,17 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev) hw.irq = irq->start; hw.chipset = ide_palm3710; - hwif = ide_find_port(); - if (hwif == NULL) - goto out; - - i = hwif->index; + palm_bk3710_port_info.udma_mask = rate < 100000000 ? ATA_UDMA4 : + ATA_UDMA5; - ide_init_port_hw(hwif, &hw); - - default_hwif_mmiops(hwif); - - idx[0] = i; - - ide_device_add(idx, &palm_bk3710_port_info); + rc = ide_host_add(&palm_bk3710_port_info, hws, NULL); + if (rc) + goto out; return 0; out: printk(KERN_WARNING "Palm Chip BK3710 IDE Register Fail\n"); - return -ENODEV; + return rc; } /* work with hotplug and coldplug */ diff --git a/drivers/ide/arm/rapide.c b/drivers/ide/arm/rapide.c index 43057e0303c8..78d27d9ae430 100644 --- a/drivers/ide/arm/rapide.c +++ b/drivers/ide/arm/rapide.c @@ -32,11 +32,10 @@ static void rapide_setup_ports(hw_regs_t *hw, void __iomem *base, static int __devinit rapide_probe(struct expansion_card *ec, const struct ecard_id *id) { - ide_hwif_t *hwif; void __iomem *base; + struct ide_host *host; int ret; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; ret = ecard_request_resources(ec); if (ret) @@ -53,20 +52,11 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id) hw.chipset = ide_generic; hw.dev = &ec->dev; - hwif = ide_find_port(); - if (hwif == NULL) { - ret = -ENOENT; + ret = ide_host_add(&rapide_port_info, hws, &host); + if (ret) goto release; - } - - ide_init_port_hw(hwif, &hw); - default_hwif_mmiops(hwif); - - idx[0] = hwif->index; - ide_device_add(idx, &rapide_port_info); - - ecard_set_drvdata(ec, hwif); + ecard_set_drvdata(ec, host); goto out; release: @@ -77,11 +67,11 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id) static void __devexit rapide_remove(struct expansion_card *ec) { - ide_hwif_t *hwif = ecard_get_drvdata(ec); + struct ide_host *host = ecard_get_drvdata(ec); ecard_set_drvdata(ec, NULL); - ide_unregister(hwif); + ide_host_remove(host); ecard_release_resources(ec); } @@ -105,7 +95,13 @@ static int __init rapide_init(void) return ecard_register_driver(&rapide_driver); } +static void __exit rapide_exit(void) +{ + ecard_unregister_driver(&rapide_driver); +} + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Yellowstone RAPIDE driver"); module_init(rapide_init); +module_exit(rapide_exit); diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c index 20fad6d542cc..bde7a585f198 100644 --- a/drivers/ide/h8300/ide-h8300.c +++ b/drivers/ide/h8300/ide-h8300.c @@ -100,6 +100,8 @@ static void h8300_tf_read(ide_drive_t *drive, ide_task_t *task) /* be sure we're looking at the low order bits */ outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + if (task->tf_flags & IDE_TFLAG_IN_FEATURE) + tf->feature = inb(io_ports->feature_addr); if (task->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = inb(io_ports->nsect_addr); if (task->tf_flags & IDE_TFLAG_IN_LBAL) @@ -153,6 +155,21 @@ static void h8300_output_data(ide_drive_t *drive, struct request *rq, mm_outsw(drive->hwif->io_ports.data_addr, buf, (len + 1) / 2); } +static const struct ide_tp_ops h8300_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = h8300_tf_load, + .tf_read = h8300_tf_read, + + .input_data = h8300_input_data, + .output_data = h8300_output_data, +}; + #define H8300_IDE_GAP (2) static inline void hw_setup(hw_regs_t *hw) @@ -167,27 +184,14 @@ static inline void hw_setup(hw_regs_t *hw) hw->chipset = ide_generic; } -static inline void hwif_setup(ide_hwif_t *hwif) -{ - default_hwif_iops(hwif); - - hwif->tf_load = h8300_tf_load; - hwif->tf_read = h8300_tf_read; - - hwif->input_data = h8300_input_data; - hwif->output_data = h8300_output_data; -} - static const struct ide_port_info h8300_port_info = { + .tp_ops = &h8300_tp_ops, .host_flags = IDE_HFLAG_NO_IO_32BIT | IDE_HFLAG_NO_DMA, }; static int __init h8300_ide_init(void) { - hw_regs_t hw; - ide_hwif_t *hwif; - int index; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; printk(KERN_INFO DRV_NAME ": H8/300 generic IDE interface\n"); @@ -200,19 +204,7 @@ static int __init h8300_ide_init(void) hw_setup(&hw); - hwif = ide_find_port_slot(&h8300_port_info); - if (hwif == NULL) - return -ENOENT; - - index = hwif->index; - ide_init_port_hw(hwif, &hw); - hwif_setup(hwif); - - idx[0] = index; - - ide_device_add(idx, &h8300_port_info); - - return 0; + return ide_host_add(&h8300_port_info, hws, NULL); out_busy: printk(KERN_ERR "ide-h8300: IDE I/F resource already used.\n"); diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 2802031de670..adf04f99cdeb 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -22,6 +22,8 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, void (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned, int)) { ide_hwif_t *hwif = drive->hwif; + struct request *rq = hwif->hwgroup->rq; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; xfer_func_t *xferfunc; unsigned int temp; u16 bcount; @@ -30,12 +32,12 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, debug_log("Enter %s - interrupt handler\n", __func__); if (pc->flags & PC_FLAG_TIMEDOUT) { - pc->callback(drive); + drive->pc_callback(drive); return ide_stopped; } /* Clear the interrupt */ - stat = ide_read_status(drive); + stat = tp_ops->read_status(hwif); if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { if (hwif->dma_ops->dma_end(drive) || @@ -63,8 +65,9 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, local_irq_enable_in_hardirq(); if (drive->media == ide_tape && !scsi && - (stat & ERR_STAT) && pc->c[0] == REQUEST_SENSE) + (stat & ERR_STAT) && rq->cmd[0] == REQUEST_SENSE) stat &= ~ERR_STAT; + if ((stat & ERR_STAT) || (pc->flags & PC_FLAG_DMA_ERROR)) { /* Error detected */ debug_log("%s: I/O error\n", drive->name); @@ -75,16 +78,17 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, goto cmd_finished; } - if (pc->c[0] == REQUEST_SENSE) { + if (rq->cmd[0] == REQUEST_SENSE) { printk(KERN_ERR "%s: I/O error in request sense" " command\n", drive->name); return ide_do_reset(drive); } - debug_log("[cmd %x]: check condition\n", pc->c[0]); + debug_log("[cmd %x]: check condition\n", rq->cmd[0]); /* Retry operation */ retry_pc(drive); + /* queued, but not started */ return ide_stopped; } @@ -95,8 +99,10 @@ cmd_finished: dsc_handle(drive); return ide_stopped; } + /* Command finished - Call the callback function */ - pc->callback(drive); + drive->pc_callback(drive); + return ide_stopped; } @@ -107,16 +113,15 @@ cmd_finished: ide_dma_off(drive); return ide_do_reset(drive); } - /* Get the number of bytes to transfer on this interrupt. */ - bcount = (hwif->INB(hwif->io_ports.lbah_addr) << 8) | - hwif->INB(hwif->io_ports.lbam_addr); - ireason = hwif->INB(hwif->io_ports.nsect_addr); + /* Get the number of bytes to transfer on this interrupt. */ + ide_read_bcount_and_ireason(drive, &bcount, &ireason); if (ireason & CD) { printk(KERN_ERR "%s: CoD != 0 in %s\n", drive->name, __func__); return ide_do_reset(drive); } + if (((ireason & IO) == IO) == !!(pc->flags & PC_FLAG_WRITING)) { /* Hopefully, we will never get here */ printk(KERN_ERR "%s: We wanted to %s, but the device wants us " @@ -125,6 +130,7 @@ cmd_finished: (ireason & IO) ? "Read" : "Write"); return ide_do_reset(drive); } + if (!(pc->flags & PC_FLAG_WRITING)) { /* Reading - Check that we have enough space */ temp = pc->xferred + bcount; @@ -142,7 +148,7 @@ cmd_finished: if (pc->sg) io_buffers(drive, pc, temp, 0); else - hwif->input_data(drive, NULL, + tp_ops->input_data(drive, NULL, pc->cur_pos, temp); printk(KERN_ERR "%s: transferred %d of " "%d bytes\n", @@ -159,9 +165,9 @@ cmd_finished: debug_log("The device wants to send us more data than " "expected - allowing transfer\n"); } - xferfunc = hwif->input_data; + xferfunc = tp_ops->input_data; } else - xferfunc = hwif->output_data; + xferfunc = tp_ops->output_data; if ((drive->media == ide_floppy && !scsi && !pc->buf) || (drive->media == ide_tape && !scsi && pc->bh) || @@ -175,7 +181,7 @@ cmd_finished: pc->cur_pos += bcount; debug_log("[cmd %x] transferred %d bytes on that intr.\n", - pc->c[0], bcount); + rq->cmd[0], bcount); /* And set the interrupt handler again */ ide_set_handler(drive, handler, timeout, expiry); @@ -183,16 +189,27 @@ cmd_finished: } EXPORT_SYMBOL_GPL(ide_pc_intr); +static u8 ide_read_ireason(ide_drive_t *drive) +{ + ide_task_t task; + + memset(&task, 0, sizeof(task)); + task.tf_flags = IDE_TFLAG_IN_NSECT; + + drive->hwif->tp_ops->tf_read(drive, &task); + + return task.tf.nsect & 3; +} + static u8 ide_wait_ireason(ide_drive_t *drive, u8 ireason) { - ide_hwif_t *hwif = drive->hwif; int retries = 100; while (retries-- && ((ireason & CD) == 0 || (ireason & IO))) { printk(KERN_ERR "%s: (IO,CoD != (0,1) while issuing " "a packet command, retrying\n", drive->name); udelay(100); - ireason = hwif->INB(hwif->io_ports.nsect_addr); + ireason = ide_read_ireason(drive); if (retries == 0) { printk(KERN_ERR "%s: (IO,CoD != (0,1) while issuing " "a packet command, ignoring\n", @@ -210,6 +227,7 @@ ide_startstop_t ide_transfer_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, ide_expiry_t *expiry) { ide_hwif_t *hwif = drive->hwif; + struct request *rq = hwif->hwgroup->rq; ide_startstop_t startstop; u8 ireason; @@ -219,7 +237,7 @@ ide_startstop_t ide_transfer_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, return startstop; } - ireason = hwif->INB(hwif->io_ports.nsect_addr); + ireason = ide_read_ireason(drive); if (drive->media == ide_tape && !drive->scsi) ireason = ide_wait_ireason(drive, ireason); @@ -239,8 +257,8 @@ ide_startstop_t ide_transfer_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, } /* Send the actual packet */ - if ((pc->flags & PC_FLAG_ZIP_DRIVE) == 0) - hwif->output_data(drive, NULL, pc->c, 12); + if ((drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) == 0) + hwif->tp_ops->output_data(drive, NULL, rq->cmd, 12); return ide_started; } @@ -284,7 +302,7 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, bcount, dma); /* Issue the packet command */ - if (pc->flags & PC_FLAG_DRQ_INTERRUPT) { + if (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT) { ide_execute_command(drive, WIN_PACKETCMD, handler, timeout, NULL); return ide_started; diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 6e29dd532090..e617cf08aef6 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -57,23 +57,29 @@ static DEFINE_MUTEX(idecd_ref_mutex); #define ide_cd_g(disk) \ container_of((disk)->private_data, struct cdrom_info, driver) +static void ide_cd_release(struct kref *); + static struct cdrom_info *ide_cd_get(struct gendisk *disk) { struct cdrom_info *cd = NULL; mutex_lock(&idecd_ref_mutex); cd = ide_cd_g(disk); - if (cd) + if (cd) { kref_get(&cd->kref); + if (ide_device_get(cd->drive)) { + kref_put(&cd->kref, ide_cd_release); + cd = NULL; + } + } mutex_unlock(&idecd_ref_mutex); return cd; } -static void ide_cd_release(struct kref *); - static void ide_cd_put(struct cdrom_info *cd) { mutex_lock(&idecd_ref_mutex); + ide_device_put(cd->drive); kref_put(&cd->kref, ide_cd_release); mutex_unlock(&idecd_ref_mutex); } @@ -85,10 +91,8 @@ static void ide_cd_put(struct cdrom_info *cd) /* Mark that we've seen a media change and invalidate our internal buffers. */ static void cdrom_saw_media_change(ide_drive_t *drive) { - struct cdrom_info *cd = drive->driver_data; - - cd->cd_flags |= IDE_CD_FLAG_MEDIA_CHANGED; - cd->cd_flags &= ~IDE_CD_FLAG_TOC_VALID; + drive->atapi_flags |= IDE_AFLAG_MEDIA_CHANGED; + drive->atapi_flags &= ~IDE_AFLAG_TOC_VALID; } static int cdrom_log_sense(ide_drive_t *drive, struct request *rq, @@ -280,11 +284,12 @@ static void ide_dump_status_no_sense(ide_drive_t *drive, const char *msg, u8 st) */ 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 = drive->hwif; + struct request *rq = hwif->hwgroup->rq; int stat, err, sense_key; /* check for errors */ - stat = ide_read_status(drive); + stat = hwif->tp_ops->read_status(hwif); if (stat_ret) *stat_ret = stat; @@ -528,7 +533,7 @@ static ide_startstop_t cdrom_start_packet_command(ide_drive_t *drive, ide_pktcmd_tf_load(drive, IDE_TFLAG_OUT_NSECT | IDE_TFLAG_OUT_LBAL, xferlen, info->dma); - if (info->cd_flags & IDE_CD_FLAG_DRQ_INTERRUPT) { + if (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT) { /* waiting for CDB interrupt, not DMA yet. */ if (info->dma) drive->waiting_for_dma = 0; @@ -560,7 +565,7 @@ static ide_startstop_t cdrom_transfer_packet_command(ide_drive_t *drive, struct cdrom_info *info = drive->driver_data; ide_startstop_t startstop; - if (info->cd_flags & IDE_CD_FLAG_DRQ_INTERRUPT) { + if (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT) { /* * Here we should have been called after receiving an interrupt * from the device. DRQ should how be set. @@ -589,7 +594,7 @@ static ide_startstop_t cdrom_transfer_packet_command(ide_drive_t *drive, cmd_len = ATAPI_MIN_CDB_BYTES; /* send the command to the device */ - hwif->output_data(drive, NULL, rq->cmd, cmd_len); + hwif->tp_ops->output_data(drive, NULL, rq->cmd, cmd_len); /* start the DMA if need be */ if (info->dma) @@ -606,6 +611,8 @@ static ide_startstop_t cdrom_transfer_packet_command(ide_drive_t *drive, static int ide_cd_check_ireason(ide_drive_t *drive, struct request *rq, int len, int ireason, int rw) { + ide_hwif_t *hwif = drive->hwif; + /* * ireason == 0: the drive wants to receive data from us * ireason == 2: the drive is expecting to transfer data to us @@ -624,7 +631,7 @@ static int ide_cd_check_ireason(ide_drive_t *drive, struct request *rq, * Some drives (ASUS) seem to tell us that status info is * available. Just get it and ignore. */ - (void)ide_read_status(drive); + (void)hwif->tp_ops->read_status(hwif); return 0; } else { /* drive wants a command packet, or invalid ireason... */ @@ -645,20 +652,18 @@ static int ide_cd_check_ireason(ide_drive_t *drive, struct request *rq, */ static int ide_cd_check_transfer_size(ide_drive_t *drive, int len) { - struct cdrom_info *cd = drive->driver_data; - if ((len % SECTOR_SIZE) == 0) return 0; printk(KERN_ERR "%s: %s: Bad transfer size %d\n", drive->name, __func__, len); - if (cd->cd_flags & IDE_CD_FLAG_LIMIT_NFRAMES) + if (drive->atapi_flags & IDE_AFLAG_LIMIT_NFRAMES) printk(KERN_ERR " This drive is not supported by " "this version of the driver\n"); else { printk(KERN_ERR " Trying to limit transfer sizes\n"); - cd->cd_flags |= IDE_CD_FLAG_LIMIT_NFRAMES; + drive->atapi_flags |= IDE_AFLAG_LIMIT_NFRAMES; } return 1; @@ -735,7 +740,7 @@ static ide_startstop_t cdrom_seek_intr(ide_drive_t *drive) if (cdrom_decode_status(drive, 0, &stat)) return ide_stopped; - info->cd_flags |= IDE_CD_FLAG_SEEKING; + drive->atapi_flags |= IDE_AFLAG_SEEKING; if (retry && time_after(jiffies, info->start_seek + IDECD_SEEK_TIMER)) { if (--retry == 0) @@ -892,10 +897,11 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) struct request *rq = HWGROUP(drive)->rq; xfer_func_t *xferfunc; ide_expiry_t *expiry = NULL; - int dma_error = 0, dma, stat, ireason, len, thislen, uptodate = 0; + int dma_error = 0, dma, stat, thislen, uptodate = 0; int write = (rq_data_dir(rq) == WRITE) ? 1 : 0; unsigned int timeout; - u8 lowcyl, highcyl; + u16 len; + u8 ireason; /* check for errors */ dma = info->dma; @@ -923,12 +929,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) goto end_request; } - /* ok we fall to pio :/ */ - ireason = hwif->INB(hwif->io_ports.nsect_addr) & 0x3; - lowcyl = hwif->INB(hwif->io_ports.lbam_addr); - highcyl = hwif->INB(hwif->io_ports.lbah_addr); - - len = lowcyl + (256 * highcyl); + ide_read_bcount_and_ireason(drive, &len, &ireason); thislen = blk_fs_request(rq) ? len : rq->data_len; if (thislen > len) @@ -991,10 +992,10 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) if (ireason == 0) { write = 1; - xferfunc = hwif->output_data; + xferfunc = hwif->tp_ops->output_data; } else { write = 0; - xferfunc = hwif->input_data; + xferfunc = hwif->tp_ops->input_data; } /* transfer data */ @@ -1198,9 +1199,10 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, int xferlen; if (blk_fs_request(rq)) { - if (info->cd_flags & IDE_CD_FLAG_SEEKING) { + if (drive->atapi_flags & IDE_AFLAG_SEEKING) { + ide_hwif_t *hwif = drive->hwif; unsigned long elapsed = jiffies - info->start_seek; - int stat = ide_read_status(drive); + int stat = hwif->tp_ops->read_status(hwif); if ((stat & SEEK_STAT) != SEEK_STAT) { if (elapsed < IDECD_SEEK_TIMEOUT) { @@ -1211,7 +1213,7 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, printk(KERN_ERR "%s: DSC timeout\n", drive->name); } - info->cd_flags &= ~IDE_CD_FLAG_SEEKING; + drive->atapi_flags &= ~IDE_AFLAG_SEEKING; } if (rq_data_dir(rq) == READ && IDE_LARGE_SEEK(info->last_block, block, @@ -1288,7 +1290,7 @@ int cdrom_check_status(ide_drive_t *drive, struct request_sense *sense) */ cmd[7] = cdi->sanyo_slot % 3; - return ide_cd_queue_pc(drive, cmd, 0, NULL, 0, sense, 0, REQ_QUIET); + return ide_cd_queue_pc(drive, cmd, 0, NULL, NULL, sense, 0, REQ_QUIET); } static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity, @@ -1296,8 +1298,8 @@ static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity, struct request_sense *sense) { struct { - __u32 lba; - __u32 blocklen; + __be32 lba; + __be32 blocklen; } capbuf; int stat; @@ -1309,13 +1311,30 @@ static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity, stat = ide_cd_queue_pc(drive, cmd, 0, &capbuf, &len, sense, 0, REQ_QUIET); - if (stat == 0) { - *capacity = 1 + be32_to_cpu(capbuf.lba); - *sectors_per_frame = - be32_to_cpu(capbuf.blocklen) >> SECTOR_BITS; + if (stat) + return stat; + + /* + * Sanity check the given block size + */ + switch (capbuf.blocklen) { + case __constant_cpu_to_be32(512): + case __constant_cpu_to_be32(1024): + case __constant_cpu_to_be32(2048): + case __constant_cpu_to_be32(4096): + break; + default: + printk(KERN_ERR "%s: weird block size %u\n", + drive->name, capbuf.blocklen); + printk(KERN_ERR "%s: default to 2kb block size\n", + drive->name); + capbuf.blocklen = __constant_cpu_to_be32(2048); + break; } - return stat; + *capacity = 1 + be32_to_cpu(capbuf.lba); + *sectors_per_frame = be32_to_cpu(capbuf.blocklen) >> SECTOR_BITS; + return 0; } static int cdrom_read_tocentry(ide_drive_t *drive, int trackno, int msf_flag, @@ -1369,7 +1388,7 @@ int ide_cd_read_toc(ide_drive_t *drive, struct request_sense *sense) */ (void) cdrom_check_status(drive, sense); - if (info->cd_flags & IDE_CD_FLAG_TOC_VALID) + if (drive->atapi_flags & IDE_AFLAG_TOC_VALID) return 0; /* try to get the total cdrom capacity and sector size */ @@ -1391,7 +1410,7 @@ int ide_cd_read_toc(ide_drive_t *drive, struct request_sense *sense) if (stat) return stat; - if (info->cd_flags & IDE_CD_FLAG_TOCTRACKS_AS_BCD) { + if (drive->atapi_flags & IDE_AFLAG_TOCTRACKS_AS_BCD) { toc->hdr.first_track = BCD2BIN(toc->hdr.first_track); toc->hdr.last_track = BCD2BIN(toc->hdr.last_track); } @@ -1432,7 +1451,7 @@ int ide_cd_read_toc(ide_drive_t *drive, struct request_sense *sense) if (stat) return stat; - if (info->cd_flags & IDE_CD_FLAG_TOCTRACKS_AS_BCD) { + if (drive->atapi_flags & IDE_AFLAG_TOCTRACKS_AS_BCD) { toc->hdr.first_track = (u8)BIN2BCD(CDROM_LEADOUT); toc->hdr.last_track = (u8)BIN2BCD(CDROM_LEADOUT); } else { @@ -1446,14 +1465,14 @@ int ide_cd_read_toc(ide_drive_t *drive, struct request_sense *sense) toc->hdr.toc_length = be16_to_cpu(toc->hdr.toc_length); - if (info->cd_flags & IDE_CD_FLAG_TOCTRACKS_AS_BCD) { + if (drive->atapi_flags & IDE_AFLAG_TOCTRACKS_AS_BCD) { toc->hdr.first_track = BCD2BIN(toc->hdr.first_track); toc->hdr.last_track = BCD2BIN(toc->hdr.last_track); } for (i = 0; i <= ntracks; i++) { - if (info->cd_flags & IDE_CD_FLAG_TOCADDR_AS_BCD) { - if (info->cd_flags & IDE_CD_FLAG_TOCTRACKS_AS_BCD) + if (drive->atapi_flags & IDE_AFLAG_TOCADDR_AS_BCD) { + if (drive->atapi_flags & IDE_AFLAG_TOCTRACKS_AS_BCD) toc->ent[i].track = BCD2BIN(toc->ent[i].track); msf_from_bcd(&toc->ent[i].addr.msf); } @@ -1476,7 +1495,7 @@ int ide_cd_read_toc(ide_drive_t *drive, struct request_sense *sense) toc->last_session_lba = msf_to_lba(0, 2, 0); /* 0m 2s 0f */ } - if (info->cd_flags & IDE_CD_FLAG_TOCADDR_AS_BCD) { + if (drive->atapi_flags & IDE_AFLAG_TOCADDR_AS_BCD) { /* re-read multisession information using MSF format */ stat = cdrom_read_tocentry(drive, 0, 1, 1, (char *)&ms_tmp, sizeof(ms_tmp), sense); @@ -1500,7 +1519,7 @@ int ide_cd_read_toc(ide_drive_t *drive, struct request_sense *sense) } /* Remember that we've read this stuff. */ - info->cd_flags |= IDE_CD_FLAG_TOC_VALID; + drive->atapi_flags |= IDE_AFLAG_TOC_VALID; return 0; } @@ -1512,7 +1531,7 @@ int ide_cdrom_get_capabilities(ide_drive_t *drive, u8 *buf) struct packet_command cgc; int stat, attempts = 3, size = ATAPI_CAPABILITIES_PAGE_SIZE; - if ((info->cd_flags & IDE_CD_FLAG_FULL_CAPS_PAGE) == 0) + if ((drive->atapi_flags & IDE_AFLAG_FULL_CAPS_PAGE) == 0) size -= ATAPI_CAPABILITIES_PAGE_PAD_SIZE; init_cdrom_command(&cgc, buf, size, CGC_DATA_UNKNOWN); @@ -1530,15 +1549,12 @@ void ide_cdrom_update_speed(ide_drive_t *drive, u8 *buf) struct cdrom_info *cd = drive->driver_data; u16 curspeed, maxspeed; - curspeed = *(u16 *)&buf[8 + 14]; - maxspeed = *(u16 *)&buf[8 + 8]; - - if (cd->cd_flags & IDE_CD_FLAG_LE_SPEED_FIELDS) { - curspeed = le16_to_cpu(curspeed); - maxspeed = le16_to_cpu(maxspeed); + if (drive->atapi_flags & IDE_AFLAG_LE_SPEED_FIELDS) { + curspeed = le16_to_cpup((__le16 *)&buf[8 + 14]); + maxspeed = le16_to_cpup((__le16 *)&buf[8 + 8]); } else { - curspeed = be16_to_cpu(curspeed); - maxspeed = be16_to_cpu(maxspeed); + curspeed = be16_to_cpup((__be16 *)&buf[8 + 14]); + maxspeed = be16_to_cpup((__be16 *)&buf[8 + 8]); } cd->current_speed = (curspeed + (176/2)) / 176; @@ -1579,7 +1595,7 @@ static int ide_cdrom_register(ide_drive_t *drive, int nslots) devinfo->handle = drive; strcpy(devinfo->name, drive->name); - if (info->cd_flags & IDE_CD_FLAG_NO_SPEED_SELECT) + if (drive->atapi_flags & IDE_AFLAG_NO_SPEED_SELECT) devinfo->mask |= CDC_SELECT_SPEED; devinfo->disk = info->disk; @@ -1605,8 +1621,8 @@ static int ide_cdrom_probe_capabilities(ide_drive_t *drive) return nslots; } - if (cd->cd_flags & IDE_CD_FLAG_PRE_ATAPI12) { - cd->cd_flags &= ~IDE_CD_FLAG_NO_EJECT; + if (drive->atapi_flags & IDE_AFLAG_PRE_ATAPI12) { + drive->atapi_flags &= ~IDE_AFLAG_NO_EJECT; cdi->mask &= ~CDC_PLAY_AUDIO; return nslots; } @@ -1624,9 +1640,9 @@ static int ide_cdrom_probe_capabilities(ide_drive_t *drive) return 0; if ((buf[8 + 6] & 0x01) == 0) - cd->cd_flags |= IDE_CD_FLAG_NO_DOORLOCK; + drive->atapi_flags |= IDE_AFLAG_NO_DOORLOCK; if (buf[8 + 6] & 0x08) - cd->cd_flags &= ~IDE_CD_FLAG_NO_EJECT; + drive->atapi_flags &= ~IDE_AFLAG_NO_EJECT; if (buf[8 + 3] & 0x01) cdi->mask &= ~CDC_CD_R; if (buf[8 + 3] & 0x02) @@ -1637,7 +1653,7 @@ static int ide_cdrom_probe_capabilities(ide_drive_t *drive) cdi->mask &= ~(CDC_DVD_RAM | CDC_RAM); if (buf[8 + 3] & 0x10) cdi->mask &= ~CDC_DVD_R; - if ((buf[8 + 4] & 0x01) || (cd->cd_flags & IDE_CD_FLAG_PLAY_AUDIO_OK)) + if ((buf[8 + 4] & 0x01) || (drive->atapi_flags & IDE_AFLAG_PLAY_AUDIO_OK)) cdi->mask &= ~CDC_PLAY_AUDIO; mechtype = buf[8 + 6] >> 5; @@ -1679,7 +1695,7 @@ static int ide_cdrom_probe_capabilities(ide_drive_t *drive) else printk(KERN_CONT " drive"); - printk(KERN_CONT ", %dkB Cache\n", be16_to_cpu(*(u16 *)&buf[8 + 12])); + printk(KERN_CONT ", %dkB Cache\n", be16_to_cpup((__be16 *)&buf[8 + 12])); return nslots; } @@ -1802,43 +1818,43 @@ static inline void ide_cdrom_add_settings(ide_drive_t *drive) { ; } static const struct cd_list_entry ide_cd_quirks_list[] = { /* Limit transfer size per interrupt. */ - { "SAMSUNG CD-ROM SCR-2430", NULL, IDE_CD_FLAG_LIMIT_NFRAMES }, - { "SAMSUNG CD-ROM SCR-2432", NULL, IDE_CD_FLAG_LIMIT_NFRAMES }, + { "SAMSUNG CD-ROM SCR-2430", NULL, IDE_AFLAG_LIMIT_NFRAMES }, + { "SAMSUNG CD-ROM SCR-2432", NULL, IDE_AFLAG_LIMIT_NFRAMES }, /* SCR-3231 doesn't support the SET_CD_SPEED command. */ - { "SAMSUNG CD-ROM SCR-3231", NULL, IDE_CD_FLAG_NO_SPEED_SELECT }, + { "SAMSUNG CD-ROM SCR-3231", NULL, IDE_AFLAG_NO_SPEED_SELECT }, /* Old NEC260 (not R) was released before ATAPI 1.2 spec. */ - { "NEC CD-ROM DRIVE:260", "1.01", IDE_CD_FLAG_TOCADDR_AS_BCD | - IDE_CD_FLAG_PRE_ATAPI12, }, + { "NEC CD-ROM DRIVE:260", "1.01", IDE_AFLAG_TOCADDR_AS_BCD | + IDE_AFLAG_PRE_ATAPI12, }, /* Vertos 300, some versions of this drive like to talk BCD. */ - { "V003S0DS", NULL, IDE_CD_FLAG_VERTOS_300_SSD, }, + { "V003S0DS", NULL, IDE_AFLAG_VERTOS_300_SSD, }, /* Vertos 600 ESD. */ - { "V006E0DS", NULL, IDE_CD_FLAG_VERTOS_600_ESD, }, + { "V006E0DS", NULL, IDE_AFLAG_VERTOS_600_ESD, }, /* * Sanyo 3 CD changer uses a non-standard command for CD changing * (by default standard ATAPI support for CD changers is used). */ - { "CD-ROM CDR-C3 G", NULL, IDE_CD_FLAG_SANYO_3CD }, - { "CD-ROM CDR-C3G", NULL, IDE_CD_FLAG_SANYO_3CD }, - { "CD-ROM CDR_C36", NULL, IDE_CD_FLAG_SANYO_3CD }, + { "CD-ROM CDR-C3 G", NULL, IDE_AFLAG_SANYO_3CD }, + { "CD-ROM CDR-C3G", NULL, IDE_AFLAG_SANYO_3CD }, + { "CD-ROM CDR_C36", NULL, IDE_AFLAG_SANYO_3CD }, /* Stingray 8X CD-ROM. */ - { "STINGRAY 8422 IDE 8X CD-ROM 7-27-95", NULL, IDE_CD_FLAG_PRE_ATAPI12}, + { "STINGRAY 8422 IDE 8X CD-ROM 7-27-95", NULL, IDE_AFLAG_PRE_ATAPI12 }, /* * ACER 50X CD-ROM and WPI 32X CD-ROM require the full spec length * mode sense page capabilities size, but older drives break. */ - { "ATAPI CD ROM DRIVE 50X MAX", NULL, IDE_CD_FLAG_FULL_CAPS_PAGE }, - { "WPI CDS-32X", NULL, IDE_CD_FLAG_FULL_CAPS_PAGE }, + { "ATAPI CD ROM DRIVE 50X MAX", NULL, IDE_AFLAG_FULL_CAPS_PAGE }, + { "WPI CDS-32X", NULL, IDE_AFLAG_FULL_CAPS_PAGE }, /* ACER/AOpen 24X CD-ROM has the speed fields byte-swapped. */ - { "", "241N", IDE_CD_FLAG_LE_SPEED_FIELDS }, + { "", "241N", IDE_AFLAG_LE_SPEED_FIELDS }, /* * Some drives used by Apple don't advertise audio play * but they do support reading TOC & audio datas. */ - { "MATSHITADVD-ROM SR-8187", NULL, IDE_CD_FLAG_PLAY_AUDIO_OK }, - { "MATSHITADVD-ROM SR-8186", NULL, IDE_CD_FLAG_PLAY_AUDIO_OK }, - { "MATSHITADVD-ROM SR-8176", NULL, IDE_CD_FLAG_PLAY_AUDIO_OK }, - { "MATSHITADVD-ROM SR-8174", NULL, IDE_CD_FLAG_PLAY_AUDIO_OK }, - { "Optiarc DVD RW AD-5200A", NULL, IDE_CD_FLAG_PLAY_AUDIO_OK }, + { "MATSHITADVD-ROM SR-8187", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, + { "MATSHITADVD-ROM SR-8186", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, + { "MATSHITADVD-ROM SR-8176", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, + { "MATSHITADVD-ROM SR-8174", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, + { "Optiarc DVD RW AD-5200A", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, { NULL, NULL, 0 } }; @@ -1873,20 +1889,20 @@ static int ide_cdrom_setup(ide_drive_t *drive) drive->special.all = 0; - cd->cd_flags = IDE_CD_FLAG_MEDIA_CHANGED | IDE_CD_FLAG_NO_EJECT | + drive->atapi_flags = IDE_AFLAG_MEDIA_CHANGED | IDE_AFLAG_NO_EJECT | ide_cd_flags(id); if ((id->config & 0x0060) == 0x20) - cd->cd_flags |= IDE_CD_FLAG_DRQ_INTERRUPT; + drive->atapi_flags |= IDE_AFLAG_DRQ_INTERRUPT; - if ((cd->cd_flags & IDE_CD_FLAG_VERTOS_300_SSD) && + if ((drive->atapi_flags & IDE_AFLAG_VERTOS_300_SSD) && id->fw_rev[4] == '1' && id->fw_rev[6] <= '2') - cd->cd_flags |= (IDE_CD_FLAG_TOCTRACKS_AS_BCD | - IDE_CD_FLAG_TOCADDR_AS_BCD); - else if ((cd->cd_flags & IDE_CD_FLAG_VERTOS_600_ESD) && + drive->atapi_flags |= (IDE_AFLAG_TOCTRACKS_AS_BCD | + IDE_AFLAG_TOCADDR_AS_BCD); + else if ((drive->atapi_flags & IDE_AFLAG_VERTOS_600_ESD) && id->fw_rev[4] == '1' && id->fw_rev[6] <= '2') - cd->cd_flags |= IDE_CD_FLAG_TOCTRACKS_AS_BCD; - else if (cd->cd_flags & IDE_CD_FLAG_SANYO_3CD) + drive->atapi_flags |= IDE_AFLAG_TOCTRACKS_AS_BCD; + else if (drive->atapi_flags & IDE_AFLAG_SANYO_3CD) /* 3 => use CD in slot 0 */ cdi->sanyo_slot = 3; diff --git a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h index fe0ea36e4124..61a4599b77db 100644 --- a/drivers/ide/ide-cd.h +++ b/drivers/ide/ide-cd.h @@ -27,42 +27,6 @@ #define ATAPI_CAPABILITIES_PAGE_SIZE (8 + 20) #define ATAPI_CAPABILITIES_PAGE_PAD_SIZE 4 -enum { - /* Device sends an interrupt when ready for a packet command. */ - IDE_CD_FLAG_DRQ_INTERRUPT = (1 << 0), - /* Drive cannot lock the door. */ - IDE_CD_FLAG_NO_DOORLOCK = (1 << 1), - /* Drive cannot eject the disc. */ - IDE_CD_FLAG_NO_EJECT = (1 << 2), - /* Drive is a pre ATAPI 1.2 drive. */ - IDE_CD_FLAG_PRE_ATAPI12 = (1 << 3), - /* TOC addresses are in BCD. */ - IDE_CD_FLAG_TOCADDR_AS_BCD = (1 << 4), - /* TOC track numbers are in BCD. */ - IDE_CD_FLAG_TOCTRACKS_AS_BCD = (1 << 5), - /* - * Drive does not provide data in multiples of SECTOR_SIZE - * when more than one interrupt is needed. - */ - IDE_CD_FLAG_LIMIT_NFRAMES = (1 << 6), - /* Seeking in progress. */ - IDE_CD_FLAG_SEEKING = (1 << 7), - /* Driver has noticed a media change. */ - IDE_CD_FLAG_MEDIA_CHANGED = (1 << 8), - /* Saved TOC information is current. */ - IDE_CD_FLAG_TOC_VALID = (1 << 9), - /* We think that the drive door is locked. */ - IDE_CD_FLAG_DOOR_LOCKED = (1 << 10), - /* SET_CD_SPEED command is unsupported. */ - IDE_CD_FLAG_NO_SPEED_SELECT = (1 << 11), - IDE_CD_FLAG_VERTOS_300_SSD = (1 << 12), - IDE_CD_FLAG_VERTOS_600_ESD = (1 << 13), - IDE_CD_FLAG_SANYO_3CD = (1 << 14), - IDE_CD_FLAG_FULL_CAPS_PAGE = (1 << 15), - IDE_CD_FLAG_PLAY_AUDIO_OK = (1 << 16), - IDE_CD_FLAG_LE_SPEED_FIELDS = (1 << 17), -}; - /* Structure of a MSF cdrom address. */ struct atapi_msf { byte reserved; @@ -128,8 +92,6 @@ struct cdrom_info { unsigned long last_block; unsigned long start_seek; - unsigned int cd_flags; - u8 max_speed; /* Max speed of the drive. */ u8 current_speed; /* Current speed of the drive. */ diff --git a/drivers/ide/ide-cd_ioctl.c b/drivers/ide/ide-cd_ioctl.c index 24d002addf73..74231b41f611 100644 --- a/drivers/ide/ide-cd_ioctl.c +++ b/drivers/ide/ide-cd_ioctl.c @@ -27,10 +27,9 @@ int ide_cdrom_open_real(struct cdrom_device_info *cdi, int purpose) void ide_cdrom_release_real(struct cdrom_device_info *cdi) { ide_drive_t *drive = cdi->handle; - struct cdrom_info *cd = drive->driver_data; if (!cdi->use_count) - cd->cd_flags &= ~IDE_CD_FLAG_TOC_VALID; + drive->atapi_flags &= ~IDE_AFLAG_TOC_VALID; } /* @@ -83,13 +82,12 @@ int ide_cdrom_check_media_change_real(struct cdrom_device_info *cdi, int slot_nr) { ide_drive_t *drive = cdi->handle; - struct cdrom_info *cd = drive->driver_data; int retval; if (slot_nr == CDSL_CURRENT) { (void) cdrom_check_status(drive, NULL); - retval = (cd->cd_flags & IDE_CD_FLAG_MEDIA_CHANGED) ? 1 : 0; - cd->cd_flags &= ~IDE_CD_FLAG_MEDIA_CHANGED; + retval = (drive->atapi_flags & IDE_AFLAG_MEDIA_CHANGED) ? 1 : 0; + drive->atapi_flags &= ~IDE_AFLAG_MEDIA_CHANGED; return retval; } else { return -EINVAL; @@ -107,11 +105,11 @@ int cdrom_eject(ide_drive_t *drive, int ejectflag, char loej = 0x02; unsigned char cmd[BLK_MAX_CDB]; - if ((cd->cd_flags & IDE_CD_FLAG_NO_EJECT) && !ejectflag) + if ((drive->atapi_flags & IDE_AFLAG_NO_EJECT) && !ejectflag) return -EDRIVE_CANT_DO_THIS; /* reload fails on some drives, if the tray is locked */ - if ((cd->cd_flags & IDE_CD_FLAG_DOOR_LOCKED) && ejectflag) + if ((drive->atapi_flags & IDE_AFLAG_DOOR_LOCKED) && ejectflag) return 0; /* only tell drive to close tray if open, if it can do that */ @@ -123,7 +121,7 @@ int cdrom_eject(ide_drive_t *drive, int ejectflag, cmd[0] = GPCMD_START_STOP_UNIT; cmd[4] = loej | (ejectflag != 0); - return ide_cd_queue_pc(drive, cmd, 0, NULL, 0, sense, 0, 0); + return ide_cd_queue_pc(drive, cmd, 0, NULL, NULL, sense, 0, 0); } /* Lock the door if LOCKFLAG is nonzero; unlock it otherwise. */ @@ -131,7 +129,6 @@ static int ide_cd_lockdoor(ide_drive_t *drive, int lockflag, struct request_sense *sense) { - struct cdrom_info *cd = drive->driver_data; struct request_sense my_sense; int stat; @@ -139,7 +136,7 @@ int ide_cd_lockdoor(ide_drive_t *drive, int lockflag, sense = &my_sense; /* If the drive cannot lock the door, just pretend. */ - if (cd->cd_flags & IDE_CD_FLAG_NO_DOORLOCK) { + if (drive->atapi_flags & IDE_AFLAG_NO_DOORLOCK) { stat = 0; } else { unsigned char cmd[BLK_MAX_CDB]; @@ -149,7 +146,7 @@ int ide_cd_lockdoor(ide_drive_t *drive, int lockflag, cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL; cmd[4] = lockflag ? 1 : 0; - stat = ide_cd_queue_pc(drive, cmd, 0, NULL, 0, + stat = ide_cd_queue_pc(drive, cmd, 0, NULL, NULL, sense, 0, 0); } @@ -160,7 +157,7 @@ int ide_cd_lockdoor(ide_drive_t *drive, int lockflag, (sense->asc == 0x24 || sense->asc == 0x20)) { printk(KERN_ERR "%s: door locking not supported\n", drive->name); - cd->cd_flags |= IDE_CD_FLAG_NO_DOORLOCK; + drive->atapi_flags |= IDE_AFLAG_NO_DOORLOCK; stat = 0; } @@ -170,9 +167,9 @@ int ide_cd_lockdoor(ide_drive_t *drive, int lockflag, if (stat == 0) { if (lockflag) - cd->cd_flags |= IDE_CD_FLAG_DOOR_LOCKED; + drive->atapi_flags |= IDE_AFLAG_DOOR_LOCKED; else - cd->cd_flags &= ~IDE_CD_FLAG_DOOR_LOCKED; + drive->atapi_flags &= ~IDE_AFLAG_DOOR_LOCKED; } return stat; @@ -231,7 +228,7 @@ int ide_cdrom_select_speed(struct cdrom_device_info *cdi, int speed) cmd[5] = speed & 0xff; } - stat = ide_cd_queue_pc(drive, cmd, 0, NULL, 0, &sense, 0, 0); + stat = ide_cd_queue_pc(drive, cmd, 0, NULL, NULL, &sense, 0, 0); if (!ide_cdrom_get_capabilities(drive, buf)) { ide_cdrom_update_speed(drive, buf); @@ -250,7 +247,7 @@ int ide_cdrom_get_last_session(struct cdrom_device_info *cdi, struct request_sense sense; int ret; - if ((info->cd_flags & IDE_CD_FLAG_TOC_VALID) == 0 || !info->toc) { + if ((drive->atapi_flags & IDE_AFLAG_TOC_VALID) == 0 || !info->toc) { ret = ide_cd_read_toc(drive, &sense); if (ret) return ret; @@ -308,7 +305,7 @@ int ide_cdrom_reset(struct cdrom_device_info *cdi) * A reset will unlock the door. If it was previously locked, * lock it again. */ - if (cd->cd_flags & IDE_CD_FLAG_DOOR_LOCKED) + if (drive->atapi_flags & IDE_AFLAG_DOOR_LOCKED) (void)ide_cd_lockdoor(drive, 1, &sense); return ret; @@ -324,7 +321,7 @@ static int ide_cd_get_toc_entry(ide_drive_t *drive, int track, /* * don't serve cached data, if the toc isn't valid */ - if ((info->cd_flags & IDE_CD_FLAG_TOC_VALID) == 0) + if ((drive->atapi_flags & IDE_AFLAG_TOC_VALID) == 0) return -EINVAL; /* Check validity of requested track number. */ @@ -374,7 +371,7 @@ static int ide_cd_fake_play_trkind(ide_drive_t *drive, void *arg) lba_to_msf(lba_start, &cmd[3], &cmd[4], &cmd[5]); lba_to_msf(lba_end - 1, &cmd[6], &cmd[7], &cmd[8]); - return ide_cd_queue_pc(drive, cmd, 0, NULL, 0, &sense, 0, 0); + return ide_cd_queue_pc(drive, cmd, 0, NULL, NULL, &sense, 0, 0); } static int ide_cd_read_tochdr(ide_drive_t *drive, void *arg) diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 3a2e80237c10..28d85b410f7c 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -56,23 +56,29 @@ static DEFINE_MUTEX(idedisk_ref_mutex); #define ide_disk_g(disk) \ container_of((disk)->private_data, struct ide_disk_obj, driver) +static void ide_disk_release(struct kref *); + static struct ide_disk_obj *ide_disk_get(struct gendisk *disk) { struct ide_disk_obj *idkp = NULL; mutex_lock(&idedisk_ref_mutex); idkp = ide_disk_g(disk); - if (idkp) + if (idkp) { kref_get(&idkp->kref); + if (ide_device_get(idkp->drive)) { + kref_put(&idkp->kref, ide_disk_release); + idkp = NULL; + } + } mutex_unlock(&idedisk_ref_mutex); return idkp; } -static void ide_disk_release(struct kref *); - static void ide_disk_put(struct ide_disk_obj *idkp) { mutex_lock(&idedisk_ref_mutex); + ide_device_put(idkp->drive); kref_put(&idkp->kref, ide_disk_release); mutex_unlock(&idedisk_ref_mutex); } @@ -158,7 +164,7 @@ static void ide_tf_set_cmd(ide_drive_t *drive, ide_task_t *task, u8 dma) write = (task->tf_flags & IDE_TFLAG_WRITE) ? 1 : 0; if (dma) - index = drive->vdma ? 4 : 8; + index = 8; else index = drive->mult_count ? 0 : 4; diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index 7ee44f86bc54..71c377a7bcf2 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -100,10 +100,11 @@ static const struct drive_list_entry drive_blacklist [] = { ide_startstop_t ide_dma_intr (ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; u8 stat = 0, dma_stat = 0; - dma_stat = drive->hwif->dma_ops->dma_end(drive); - stat = ide_read_status(drive); + dma_stat = hwif->dma_ops->dma_end(drive); + stat = hwif->tp_ops->read_status(hwif); if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) { if (!dma_stat) { @@ -172,7 +173,7 @@ EXPORT_SYMBOL_GPL(ide_build_sglist); int ide_build_dmatable (ide_drive_t *drive, struct request *rq) { ide_hwif_t *hwif = HWIF(drive); - unsigned int *table = hwif->dmatable_cpu; + __le32 *table = (__le32 *)hwif->dmatable_cpu; unsigned int is_trm290 = (hwif->chipset == ide_trm290) ? 1 : 0; unsigned int count = 0; int i; @@ -334,7 +335,7 @@ static int config_drive_for_dma (ide_drive_t *drive) static int dma_timer_expiry (ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); - u8 dma_stat = hwif->INB(hwif->dma_status); + u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); printk(KERN_WARNING "%s: dma_timer_expiry: dma status == 0x%02x\n", drive->name, dma_stat); @@ -369,14 +370,18 @@ void ide_dma_host_set(ide_drive_t *drive, int on) { ide_hwif_t *hwif = HWIF(drive); u8 unit = (drive->select.b.unit & 0x01); - u8 dma_stat = hwif->INB(hwif->dma_status); + u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); if (on) dma_stat |= (1 << (5 + unit)); else dma_stat &= ~(1 << (5 + unit)); - hwif->OUTB(dma_stat, hwif->dma_status); + if (hwif->host_flags & IDE_HFLAG_MMIO) + writeb(dma_stat, + (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); + else + outb(dma_stat, hwif->dma_base + ATA_DMA_STATUS); } EXPORT_SYMBOL_GPL(ide_dma_host_set); @@ -449,6 +454,7 @@ int ide_dma_setup(ide_drive_t *drive) ide_hwif_t *hwif = drive->hwif; struct request *rq = HWGROUP(drive)->rq; unsigned int reading; + u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; u8 dma_stat; if (rq_data_dir(rq)) @@ -470,13 +476,21 @@ int ide_dma_setup(ide_drive_t *drive) outl(hwif->dmatable_dma, hwif->dma_base + ATA_DMA_TABLE_OFS); /* specify r/w */ - hwif->OUTB(reading, hwif->dma_command); + if (mmio) + writeb(reading, (void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); + else + outb(reading, hwif->dma_base + ATA_DMA_CMD); - /* read dma_status for INTR & ERROR flags */ - dma_stat = hwif->INB(hwif->dma_status); + /* read DMA status for INTR & ERROR flags */ + dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); /* clear INTR & ERROR flags */ - hwif->OUTB(dma_stat|6, hwif->dma_status); + if (mmio) + writeb(dma_stat | 6, + (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); + else + outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS); + drive->waiting_for_dma = 1; return 0; } @@ -492,16 +506,24 @@ EXPORT_SYMBOL_GPL(ide_dma_exec_cmd); void ide_dma_start(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); - u8 dma_cmd = hwif->INB(hwif->dma_command); + ide_hwif_t *hwif = drive->hwif; + u8 dma_cmd; /* Note that this is done *after* the cmd has * been issued to the drive, as per the BM-IDE spec. * The Promise Ultra33 doesn't work correctly when * we do this part before issuing the drive cmd. */ - /* start DMA */ - hwif->OUTB(dma_cmd|1, hwif->dma_command); + if (hwif->host_flags & IDE_HFLAG_MMIO) { + dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); + /* start DMA */ + writeb(dma_cmd | 1, + (void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); + } else { + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); + outb(dma_cmd | 1, hwif->dma_base + ATA_DMA_CMD); + } + hwif->dma = 1; wmb(); } @@ -511,18 +533,33 @@ EXPORT_SYMBOL_GPL(ide_dma_start); /* returns 1 on error, 0 otherwise */ int __ide_dma_end (ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; + u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; u8 dma_stat = 0, dma_cmd = 0; drive->waiting_for_dma = 0; - /* get dma_command mode */ - dma_cmd = hwif->INB(hwif->dma_command); - /* stop DMA */ - hwif->OUTB(dma_cmd&~1, hwif->dma_command); + + if (mmio) { + /* get DMA command mode */ + dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); + /* stop DMA */ + writeb(dma_cmd & ~1, + (void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); + } else { + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); + outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD); + } + /* get DMA status */ - dma_stat = hwif->INB(hwif->dma_status); - /* clear the INTR & ERROR bits */ - hwif->OUTB(dma_stat|6, hwif->dma_status); + dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); + + if (mmio) + /* clear the INTR & ERROR bits */ + writeb(dma_stat | 6, + (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); + else + outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS); + /* purge DMA mappings */ ide_destroy_dmatable(drive); /* verify good DMA status */ @@ -537,7 +574,7 @@ EXPORT_SYMBOL(__ide_dma_end); int ide_dma_test_irq(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); - u8 dma_stat = hwif->INB(hwif->dma_status); + u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); /* return 1 if INTR asserted */ if ((dma_stat & 4) == 4) @@ -719,9 +756,8 @@ static int ide_tune_dma(ide_drive_t *drive) static int ide_dma_check(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - int vdma = (hwif->host_flags & IDE_HFLAG_VDMA)? 1 : 0; - if (!vdma && ide_tune_dma(drive)) + if (ide_tune_dma(drive)) return 0; /* TODO: always do PIO fallback */ @@ -730,7 +766,7 @@ static int ide_dma_check(ide_drive_t *drive) ide_set_max_pio(drive); - return vdma ? 0 : -1; + return -1; } int ide_id_dma_bug(ide_drive_t *drive) @@ -842,7 +878,7 @@ int ide_allocate_dma_engine(ide_hwif_t *hwif) } EXPORT_SYMBOL_GPL(ide_allocate_dma_engine); -static const struct ide_dma_ops sff_dma_ops = { +const struct ide_dma_ops sff_dma_ops = { .dma_host_set = ide_dma_host_set, .dma_setup = ide_dma_setup, .dma_exec_cmd = ide_dma_exec_cmd, @@ -852,18 +888,5 @@ static const struct ide_dma_ops sff_dma_ops = { .dma_timeout = ide_dma_timeout, .dma_lost_irq = ide_dma_lost_irq, }; - -void ide_setup_dma(ide_hwif_t *hwif, unsigned long base) -{ - hwif->dma_base = base; - - if (!hwif->dma_command) - hwif->dma_command = hwif->dma_base + 0; - if (!hwif->dma_status) - hwif->dma_status = hwif->dma_base + 2; - - hwif->dma_ops = &sff_dma_ops; -} - -EXPORT_SYMBOL_GPL(ide_setup_dma); +EXPORT_SYMBOL_GPL(sff_dma_ops); #endif /* CONFIG_BLK_DEV_IDEDMA_SFF */ diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 011d72011cc4..ca11a26746f1 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -125,26 +125,10 @@ typedef struct ide_floppy_obj { int wp; /* Supports format progress report */ int srfp; - /* Status/Action flags */ - unsigned long flags; } idefloppy_floppy_t; #define IDEFLOPPY_TICKS_DELAY HZ/20 /* default delay for ZIP 100 (50ms) */ -/* Floppy flag bits values. */ -enum { - /* DRQ interrupt device */ - IDEFLOPPY_FLAG_DRQ_INTERRUPT = (1 << 0), - /* Media may have changed */ - IDEFLOPPY_FLAG_MEDIA_CHANGED = (1 << 1), - /* Format in progress */ - IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS = (1 << 2), - /* Avoid commands not supported in Clik drive */ - IDEFLOPPY_FLAG_CLIK_DRIVE = (1 << 3), - /* Requires BH algorithm for packets */ - IDEFLOPPY_FLAG_ZIP_DRIVE = (1 << 4), -}; - /* Defines for the MODE SENSE command */ #define MODE_SENSE_CURRENT 0x00 #define MODE_SENSE_CHANGEABLE 0x01 @@ -174,23 +158,29 @@ static DEFINE_MUTEX(idefloppy_ref_mutex); #define ide_floppy_g(disk) \ container_of((disk)->private_data, struct ide_floppy_obj, driver) +static void idefloppy_cleanup_obj(struct kref *); + static struct ide_floppy_obj *ide_floppy_get(struct gendisk *disk) { struct ide_floppy_obj *floppy = NULL; mutex_lock(&idefloppy_ref_mutex); floppy = ide_floppy_g(disk); - if (floppy) + if (floppy) { kref_get(&floppy->kref); + if (ide_device_get(floppy->drive)) { + kref_put(&floppy->kref, idefloppy_cleanup_obj); + floppy = NULL; + } + } mutex_unlock(&idefloppy_ref_mutex); return floppy; } -static void idefloppy_cleanup_obj(struct kref *); - static void ide_floppy_put(struct ide_floppy_obj *floppy) { mutex_lock(&idefloppy_ref_mutex); + ide_device_put(floppy->drive); kref_put(&floppy->kref, idefloppy_cleanup_obj); mutex_unlock(&idefloppy_ref_mutex); } @@ -247,9 +237,9 @@ static void ide_floppy_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, data = bvec_kmap_irq(bvec, &flags); if (direction) - hwif->output_data(drive, NULL, data, count); + hwif->tp_ops->output_data(drive, NULL, data, count); else - hwif->input_data(drive, NULL, data, count); + hwif->tp_ops->input_data(drive, NULL, data, count); bvec_kunmap_irq(data, &flags); bcount -= count; @@ -291,6 +281,7 @@ static void idefloppy_queue_pc_head(ide_drive_t *drive, struct ide_atapi_pc *pc, rq->cmd_type = REQ_TYPE_SPECIAL; rq->cmd_flags |= REQ_PREEMPT; rq->rq_disk = floppy->disk; + memcpy(rq->cmd, pc->c, 12); ide_do_drive_cmd(drive, rq); } @@ -354,7 +345,6 @@ static void idefloppy_init_pc(struct ide_atapi_pc *pc) memset(pc, 0, sizeof(*pc)); pc->buf = pc->pc_buf; pc->buf_size = IDEFLOPPY_PC_BUFFER_SIZE; - pc->callback = ide_floppy_callback; } static void idefloppy_create_request_sense_cmd(struct ide_atapi_pc *pc) @@ -402,7 +392,7 @@ static int idefloppy_transfer_pc(ide_drive_t *drive) idefloppy_floppy_t *floppy = drive->driver_data; /* Send the actual packet */ - drive->hwif->output_data(drive, NULL, floppy->pc->c, 12); + drive->hwif->tp_ops->output_data(drive, NULL, floppy->pc->c, 12); /* Timeout for the packet command */ return IDEFLOPPY_WAIT_CMD; @@ -429,7 +419,7 @@ static ide_startstop_t idefloppy_start_pc_transfer(ide_drive_t *drive) * 40 and 50msec work well. idefloppy_pc_intr will not be actually * used until after the packet is moved in about 50 msec. */ - if (pc->flags & PC_FLAG_ZIP_DRIVE) { + if (drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) { timeout = floppy->ticks; expiry = &idefloppy_transfer_pc; } else { @@ -474,7 +464,7 @@ static ide_startstop_t idefloppy_issue_pc(ide_drive_t *drive, pc->error = IDEFLOPPY_ERROR_GENERAL; floppy->failed_pc = NULL; - pc->callback(drive); + drive->pc_callback(drive); return ide_stopped; } @@ -574,6 +564,8 @@ static void idefloppy_create_rw_cmd(idefloppy_floppy_t *floppy, put_unaligned(cpu_to_be16(blocks), (unsigned short *)&pc->c[7]); put_unaligned(cpu_to_be32(block), (unsigned int *) &pc->c[2]); + memcpy(rq->cmd, pc->c, 12); + pc->rq = rq; pc->b_count = cmd == READ ? 0 : rq->bio->bi_size; if (rq->cmd_flags & REQ_RW) @@ -647,12 +639,6 @@ static ide_startstop_t idefloppy_do_request(ide_drive_t *drive, return ide_stopped; } - if (floppy->flags & IDEFLOPPY_FLAG_DRQ_INTERRUPT) - pc->flags |= PC_FLAG_DRQ_INTERRUPT; - - if (floppy->flags & IDEFLOPPY_FLAG_ZIP_DRIVE) - pc->flags |= PC_FLAG_ZIP_DRIVE; - pc->rq = rq; return idefloppy_issue_pc(drive, pc); @@ -671,6 +657,7 @@ static int idefloppy_queue_pc_tail(ide_drive_t *drive, struct ide_atapi_pc *pc) rq = blk_get_request(drive->queue, READ, __GFP_WAIT); rq->buffer = (char *) pc; rq->cmd_type = REQ_TYPE_SPECIAL; + memcpy(rq->cmd, pc->c, 12); error = blk_execute_rq(drive->queue, floppy->disk, rq, 0); blk_put_request(rq); @@ -795,7 +782,7 @@ static int ide_floppy_get_capacity(ide_drive_t *drive) switch (pc.buf[desc_start + 4] & 0x03) { /* Clik! drive returns this instead of CAPACITY_CURRENT */ case CAPACITY_UNFORMATTED: - if (!(floppy->flags & IDEFLOPPY_FLAG_CLIK_DRIVE)) + if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) /* * If it is not a clik drive, break out * (maintains previous driver behaviour) @@ -841,7 +828,7 @@ static int ide_floppy_get_capacity(ide_drive_t *drive) } /* Clik! disk does not support get_flexible_disk_page */ - if (!(floppy->flags & IDEFLOPPY_FLAG_CLIK_DRIVE)) + if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) (void) ide_floppy_get_flexible_disk_page(drive); set_capacity(floppy->disk, floppy->blocks * floppy->bs_factor); @@ -949,11 +936,12 @@ static int idefloppy_get_format_progress(ide_drive_t *drive, int __user *arg) /* Else assume format_unit has finished, and we're at 0x10000 */ } else { + ide_hwif_t *hwif = drive->hwif; unsigned long flags; u8 stat; local_irq_save(flags); - stat = ide_read_status(drive); + stat = hwif->tp_ops->read_status(hwif); local_irq_restore(flags); progress_indication = ((stat & SEEK_STAT) == 0) ? 0 : 0x10000; @@ -1039,9 +1027,10 @@ static void idefloppy_setup(ide_drive_t *drive, idefloppy_floppy_t *floppy) *((u16 *) &gcw) = drive->id->config; floppy->pc = floppy->pc_stack; + drive->pc_callback = ide_floppy_callback; if (((gcw[0] & 0x60) >> 5) == 1) - floppy->flags |= IDEFLOPPY_FLAG_DRQ_INTERRUPT; + drive->atapi_flags |= IDE_AFLAG_DRQ_INTERRUPT; /* * We used to check revisions here. At this point however I'm giving up. * Just assume they are all broken, its easier. @@ -1052,7 +1041,7 @@ static void idefloppy_setup(ide_drive_t *drive, idefloppy_floppy_t *floppy) * we'll leave the limitation below for the 2.2.x tree. */ if (!strncmp(drive->id->model, "IOMEGA ZIP 100 ATAPI", 20)) { - floppy->flags |= IDEFLOPPY_FLAG_ZIP_DRIVE; + drive->atapi_flags |= IDE_AFLAG_ZIP_DRIVE; /* This value will be visible in the /proc/ide/hdx/settings */ floppy->ticks = IDEFLOPPY_TICKS_DELAY; blk_queue_max_sectors(drive->queue, 64); @@ -1064,7 +1053,7 @@ static void idefloppy_setup(ide_drive_t *drive, idefloppy_floppy_t *floppy) */ if (strncmp(drive->id->model, "IOMEGA Clik!", 11) == 0) { blk_queue_max_sectors(drive->queue, 64); - floppy->flags |= IDEFLOPPY_FLAG_CLIK_DRIVE; + drive->atapi_flags |= IDE_AFLAG_CLIK_DRIVE; } (void) ide_floppy_get_capacity(drive); @@ -1153,7 +1142,7 @@ static int idefloppy_open(struct inode *inode, struct file *filp) floppy->openers++; if (floppy->openers == 1) { - floppy->flags &= ~IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS; + drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS; /* Just in case */ idefloppy_init_pc(&pc); @@ -1180,14 +1169,14 @@ static int idefloppy_open(struct inode *inode, struct file *filp) ret = -EROFS; goto out_put_floppy; } - floppy->flags |= IDEFLOPPY_FLAG_MEDIA_CHANGED; + drive->atapi_flags |= IDE_AFLAG_MEDIA_CHANGED; /* IOMEGA Clik! drives do not support lock/unlock commands */ - if (!(floppy->flags & IDEFLOPPY_FLAG_CLIK_DRIVE)) { + if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) { idefloppy_create_prevent_cmd(&pc, 1); (void) idefloppy_queue_pc_tail(drive, &pc); } check_disk_change(inode->i_bdev); - } else if (floppy->flags & IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS) { + } else if (drive->atapi_flags & IDE_AFLAG_FORMAT_IN_PROGRESS) { ret = -EBUSY; goto out_put_floppy; } @@ -1210,12 +1199,12 @@ static int idefloppy_release(struct inode *inode, struct file *filp) if (floppy->openers == 1) { /* IOMEGA Clik! drives do not support lock/unlock commands */ - if (!(floppy->flags & IDEFLOPPY_FLAG_CLIK_DRIVE)) { + if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) { idefloppy_create_prevent_cmd(&pc, 0); (void) idefloppy_queue_pc_tail(drive, &pc); } - floppy->flags &= ~IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS; + drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS; } floppy->openers--; @@ -1236,15 +1225,17 @@ static int idefloppy_getgeo(struct block_device *bdev, struct hd_geometry *geo) return 0; } -static int ide_floppy_lockdoor(idefloppy_floppy_t *floppy, - struct ide_atapi_pc *pc, unsigned long arg, unsigned int cmd) +static int ide_floppy_lockdoor(ide_drive_t *drive, struct ide_atapi_pc *pc, + unsigned long arg, unsigned int cmd) { + idefloppy_floppy_t *floppy = drive->driver_data; + if (floppy->openers > 1) return -EBUSY; /* The IOMEGA Clik! Drive doesn't support this command - * no room for an eject mechanism */ - if (!(floppy->flags & IDEFLOPPY_FLAG_CLIK_DRIVE)) { + if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) { int prevent = arg ? 1 : 0; if (cmd == CDROMEJECT) @@ -1265,16 +1256,17 @@ static int ide_floppy_lockdoor(idefloppy_floppy_t *floppy, static int ide_floppy_format_unit(idefloppy_floppy_t *floppy, int __user *arg) { - int blocks, length, flags, err = 0; struct ide_atapi_pc pc; + ide_drive_t *drive = floppy->drive; + int blocks, length, flags, err = 0; if (floppy->openers > 1) { /* Don't format if someone is using the disk */ - floppy->flags &= ~IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS; + drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS; return -EBUSY; } - floppy->flags |= IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS; + drive->atapi_flags |= IDE_AFLAG_FORMAT_IN_PROGRESS; /* * Send ATAPI_FORMAT_UNIT to the drive. @@ -1298,15 +1290,15 @@ static int ide_floppy_format_unit(idefloppy_floppy_t *floppy, goto out; } - (void) idefloppy_get_sfrp_bit(floppy->drive); + (void) idefloppy_get_sfrp_bit(drive); idefloppy_create_format_unit_cmd(&pc, blocks, length, flags); - if (idefloppy_queue_pc_tail(floppy->drive, &pc)) + if (idefloppy_queue_pc_tail(drive, &pc)) err = -EIO; out: if (err) - floppy->flags &= ~IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS; + drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS; return err; } @@ -1325,7 +1317,7 @@ static int idefloppy_ioctl(struct inode *inode, struct file *file, case CDROMEJECT: /* fall through */ case CDROM_LOCKDOOR: - return ide_floppy_lockdoor(floppy, &pc, arg, cmd); + return ide_floppy_lockdoor(drive, &pc, arg, cmd); case IDEFLOPPY_IOCTL_FORMAT_SUPPORTED: return 0; case IDEFLOPPY_IOCTL_FORMAT_GET_CAPACITY: @@ -1366,8 +1358,8 @@ static int idefloppy_media_changed(struct gendisk *disk) drive->attach = 0; return 0; } - ret = !!(floppy->flags & IDEFLOPPY_FLAG_MEDIA_CHANGED); - floppy->flags &= ~IDEFLOPPY_FLAG_MEDIA_CHANGED; + ret = !!(drive->atapi_flags & IDE_AFLAG_MEDIA_CHANGED); + drive->atapi_flags &= ~IDE_AFLAG_MEDIA_CHANGED; return ret; } diff --git a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c index 2d92214096ab..8fe8b5b9cf7d 100644 --- a/drivers/ide/ide-generic.c +++ b/drivers/ide/ide-generic.c @@ -20,6 +20,11 @@ #include <linux/module.h> #include <linux/ide.h> +/* FIXME: convert m32r to use ide_platform host driver */ +#ifdef CONFIG_M32R +#include <asm/m32r.h> +#endif + #define DRV_NAME "ide_generic" static int probe_mask = 0x03; @@ -28,29 +33,21 @@ MODULE_PARM_DESC(probe_mask, "probe mask for legacy ISA IDE ports"); static ssize_t store_add(struct class *cls, const char *buf, size_t n) { - ide_hwif_t *hwif; unsigned int base, ctl; - int irq; - hw_regs_t hw; - u8 idx[] = { 0xff, 0xff, 0xff, 0xff }; + int irq, rc; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; if (sscanf(buf, "%x:%x:%d", &base, &ctl, &irq) != 3) return -EINVAL; - hwif = ide_find_port(); - if (hwif == NULL) - return -ENOENT; - memset(&hw, 0, sizeof(hw)); ide_std_init_ports(&hw, base, ctl); hw.irq = irq; hw.chipset = ide_generic; - ide_init_port_hw(hwif, &hw); - - idx[0] = hwif->index; - - ide_device_add(idx, NULL); + rc = ide_host_add(NULL, hws, NULL); + if (rc) + return rc; return n; }; @@ -88,20 +85,41 @@ static int __init ide_generic_sysfs_init(void) return 0; } +#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_MAPPI2) \ + || defined(CONFIG_PLAT_OPSPUT) +static const u16 legacy_bases[] = { 0x1f0 }; +static const int legacy_irqs[] = { PLD_IRQ_CFIREQ }; +#elif defined(CONFIG_PLAT_MAPPI3) +static const u16 legacy_bases[] = { 0x1f0, 0x170 }; +static const int legacy_irqs[] = { PLD_IRQ_CFIREQ, PLD_IRQ_IDEIREQ }; +#elif defined(CONFIG_ALPHA) +static const u16 legacy_bases[] = { 0x1f0, 0x170, 0x1e8, 0x168 }; +static const int legacy_irqs[] = { 14, 15, 11, 10 }; +#else +static const u16 legacy_bases[] = { 0x1f0, 0x170, 0x1e8, 0x168, 0x1e0, 0x160 }; +static const int legacy_irqs[] = { 14, 15, 11, 10, 8, 12 }; +#endif + static int __init ide_generic_init(void) { - u8 idx[MAX_HWIFS]; - int i; - + hw_regs_t hw[MAX_HWIFS], *hws[MAX_HWIFS]; + struct ide_host *host; + unsigned long io_addr; + int i, rc; + +#ifdef CONFIG_MIPS + if (!ide_probe_legacy()) + return -ENODEV; +#endif printk(KERN_INFO DRV_NAME ": please use \"probe_mask=0x3f\" module " "parameter for probing all legacy ISA IDE ports\n"); - for (i = 0; i < MAX_HWIFS; i++) { - ide_hwif_t *hwif; - unsigned long io_addr = ide_default_io_base(i); - hw_regs_t hw; + memset(hws, 0, sizeof(hw_regs_t *) * MAX_HWIFS); + + for (i = 0; i < ARRAY_SIZE(legacy_bases); i++) { + io_addr = legacy_bases[i]; - idx[i] = 0xff; + hws[i] = NULL; if ((probe_mask & (1 << i)) && io_addr) { if (!request_region(io_addr, 8, DRV_NAME)) { @@ -119,33 +137,46 @@ static int __init ide_generic_init(void) continue; } - /* - * Skip probing if the corresponding - * slot is already occupied. - */ - hwif = ide_find_port(); - if (hwif == NULL || hwif->index != i) { - idx[i] = 0xff; - continue; - } + memset(&hw[i], 0, sizeof(hw[i])); + ide_std_init_ports(&hw[i], io_addr, io_addr + 0x206); +#ifdef CONFIG_IA64 + hw[i].irq = isa_irq_to_vector(legacy_irqs[i]); +#else + hw[i].irq = legacy_irqs[i]; +#endif + hw[i].chipset = ide_generic; - memset(&hw, 0, sizeof(hw)); - ide_std_init_ports(&hw, io_addr, io_addr + 0x206); - hw.irq = ide_default_irq(io_addr); - hw.chipset = ide_generic; - ide_init_port_hw(hwif, &hw); - - idx[i] = i; + hws[i] = &hw[i]; } } - ide_device_add_all(idx, NULL); + host = ide_host_alloc_all(NULL, hws); + if (host == NULL) { + rc = -ENOMEM; + goto err; + } + + rc = ide_host_register(host, NULL, hws); + if (rc) + goto err_free; if (ide_generic_sysfs_init()) printk(KERN_ERR DRV_NAME ": failed to create ide_generic " "class\n"); return 0; +err_free: + ide_host_free(host); +err: + for (i = 0; i < MAX_HWIFS; i++) { + if (hws[i] == NULL) + continue; + + io_addr = hws[i]->io_ports.data_addr; + release_region(io_addr + 0x206, 1); + release_region(io_addr, 8); + } + return rc; } module_init(ide_generic_init); diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 661b75a89d4d..a896a283f27f 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -330,7 +330,7 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err) tf->error = err; tf->status = stat; - drive->hwif->tf_read(drive, task); + drive->hwif->tp_ops->tf_read(drive, task); if (task->tf_flags & IDE_TFLAG_DYN) kfree(task); @@ -381,8 +381,7 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 if (err == ABRT_ERR) { if (drive->select.b.lba && /* some newer drives don't support WIN_SPECIFY */ - hwif->INB(hwif->io_ports.command_addr) == - WIN_SPECIFY) + hwif->tp_ops->read_status(hwif) == WIN_SPECIFY) return ide_stopped; } else if ((err & BAD_CRC) == BAD_CRC) { /* UDMA crc error, just retry the operation */ @@ -408,7 +407,7 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 return ide_stopped; } - if (ide_read_status(drive) & (BUSY_STAT | DRQ_STAT)) + if (hwif->tp_ops->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) rq->errors |= ERROR_RESET; if ((rq->errors & ERROR_RESET) == ERROR_RESET) { @@ -435,10 +434,9 @@ static ide_startstop_t ide_atapi_error(ide_drive_t *drive, struct request *rq, u /* add decoding error stuff */ } - if (ide_read_status(drive) & (BUSY_STAT | DRQ_STAT)) + if (hwif->tp_ops->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) /* force an abort */ - hwif->OUTBSYNC(hwif, WIN_IDLEIMMEDIATE, - hwif->io_ports.command_addr); + hwif->tp_ops->exec_command(hwif, WIN_IDLEIMMEDIATE); if (rq->errors >= ERROR_MAX) { ide_kill_rq(drive, rq); @@ -712,7 +710,8 @@ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, #ifdef DEBUG printk("%s: DRIVE_CMD (null)\n", drive->name); #endif - ide_end_drive_cmd(drive, ide_read_status(drive), ide_read_error(drive)); + ide_end_drive_cmd(drive, hwif->tp_ops->read_status(hwif), + ide_read_error(drive)); return ide_stopped; } @@ -747,16 +746,17 @@ static void ide_check_pm_state(ide_drive_t *drive, struct request *rq) * the bus may be broken enough to walk on our toes at this * point. */ + ide_hwif_t *hwif = drive->hwif; int rc; #ifdef DEBUG_PM printk("%s: Wakeup request inited, waiting for !BSY...\n", drive->name); #endif - rc = ide_wait_not_busy(HWIF(drive), 35000); + rc = ide_wait_not_busy(hwif, 35000); if (rc) printk(KERN_WARNING "%s: bus not ready on wakeup\n", drive->name); SELECT_DRIVE(drive); - ide_set_irq(drive, 1); - rc = ide_wait_not_busy(HWIF(drive), 100000); + hwif->tp_ops->set_irq(hwif, 1); + rc = ide_wait_not_busy(hwif, 100000); if (rc) printk(KERN_WARNING "%s: drive not ready on wakeup\n", drive->name); } @@ -1042,7 +1042,7 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) * quirk_list may not like intr setups/cleanups */ if (drive->quirk_list != 1) - ide_set_irq(drive, 0); + hwif->tp_ops->set_irq(hwif, 0); } hwgroup->hwif = hwif; hwgroup->drive = drive; @@ -1142,7 +1142,7 @@ static ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) printk(KERN_WARNING "%s: DMA timeout error\n", drive->name); (void)hwif->dma_ops->dma_end(drive); ret = ide_error(drive, "dma timeout error", - ide_read_status(drive)); + hwif->tp_ops->read_status(hwif)); } else { printk(KERN_WARNING "%s: DMA timeout retry\n", drive->name); hwif->dma_ops->dma_timeout(drive); @@ -1267,7 +1267,7 @@ void ide_timer_expiry (unsigned long data) } else startstop = ide_error(drive, "irq timeout", - ide_read_status(drive)); + hwif->tp_ops->read_status(hwif)); } drive->service_time = jiffies - drive->service_start; spin_lock_irq(&ide_lock); @@ -1323,7 +1323,8 @@ static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup) */ do { if (hwif->irq == irq) { - stat = hwif->INB(hwif->io_ports.status_addr); + stat = hwif->tp_ops->read_status(hwif); + if (!OK_STAT(stat, READY_STAT, BAD_STAT)) { /* Try to not flood the console with msgs */ static unsigned long last_msgtime, count; @@ -1413,7 +1414,7 @@ irqreturn_t ide_intr (int irq, void *dev_id) * Whack the status register, just in case * we have a leftover pending IRQ. */ - (void) hwif->INB(hwif->io_ports.status_addr); + (void)hwif->tp_ops->read_status(hwif); #endif /* CONFIG_BLK_DEV_IDEPCI */ } spin_unlock_irqrestore(&ide_lock, flags); @@ -1519,6 +1520,7 @@ EXPORT_SYMBOL(ide_do_drive_cmd); void ide_pktcmd_tf_load(ide_drive_t *drive, u32 tf_flags, u16 bcount, u8 dma) { + ide_hwif_t *hwif = drive->hwif; ide_task_t task; memset(&task, 0, sizeof(task)); @@ -1529,9 +1531,9 @@ void ide_pktcmd_tf_load(ide_drive_t *drive, u32 tf_flags, u16 bcount, u8 dma) task.tf.lbah = (bcount >> 8) & 0xff; ide_tf_dump(drive->name, &task.tf); - ide_set_irq(drive, 1); + hwif->tp_ops->set_irq(hwif, 1); SELECT_MASK(drive, 0); - drive->hwif->tf_load(drive, &task); + hwif->tp_ops->tf_load(drive, &task); } EXPORT_SYMBOL_GPL(ide_pktcmd_tf_load); @@ -1543,9 +1545,9 @@ void ide_pad_transfer(ide_drive_t *drive, int write, int len) while (len > 0) { if (write) - hwif->output_data(drive, NULL, buf, min(4, len)); + hwif->tp_ops->output_data(drive, NULL, buf, min(4, len)); else - hwif->input_data(drive, NULL, buf, min(4, len)); + hwif->tp_ops->input_data(drive, NULL, buf, min(4, len)); len -= 4; } } diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 44aaec256a30..8aae91764513 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -42,18 +42,6 @@ static void ide_outb (u8 val, unsigned long port) outb(val, port); } -static void ide_outbsync(ide_hwif_t *hwif, u8 addr, unsigned long port) -{ - outb(addr, port); -} - -void default_hwif_iops (ide_hwif_t *hwif) -{ - hwif->OUTB = ide_outb; - hwif->OUTBSYNC = ide_outbsync; - hwif->INB = ide_inb; -} - /* * MMIO operations, typically used for SATA controllers */ @@ -68,31 +56,19 @@ static void ide_mm_outb (u8 value, unsigned long port) writeb(value, (void __iomem *) port); } -static void ide_mm_outbsync(ide_hwif_t *hwif, u8 value, unsigned long port) -{ - writeb(value, (void __iomem *) port); -} - -void default_hwif_mmiops (ide_hwif_t *hwif) -{ - hwif->OUTB = ide_mm_outb; - /* Most systems will need to override OUTBSYNC, alas however - this one is controller specific! */ - hwif->OUTBSYNC = ide_mm_outbsync; - hwif->INB = ide_mm_inb; -} - -EXPORT_SYMBOL(default_hwif_mmiops); - void SELECT_DRIVE (ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; const struct ide_port_ops *port_ops = hwif->port_ops; + ide_task_t task; if (port_ops && port_ops->selectproc) port_ops->selectproc(drive); - hwif->OUTB(drive->select.all, hwif->io_ports.device_addr); + memset(&task, 0, sizeof(task)); + task.tf_flags = IDE_TFLAG_OUT_DEVICE; + + drive->hwif->tp_ops->tf_load(drive, &task); } void SELECT_MASK(ide_drive_t *drive, int mask) @@ -103,7 +79,61 @@ void SELECT_MASK(ide_drive_t *drive, int mask) port_ops->maskproc(drive, mask); } -static void ide_tf_load(ide_drive_t *drive, ide_task_t *task) +void ide_exec_command(ide_hwif_t *hwif, u8 cmd) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + writeb(cmd, (void __iomem *)hwif->io_ports.command_addr); + else + outb(cmd, hwif->io_ports.command_addr); +} +EXPORT_SYMBOL_GPL(ide_exec_command); + +u8 ide_read_status(ide_hwif_t *hwif) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + return readb((void __iomem *)hwif->io_ports.status_addr); + else + return inb(hwif->io_ports.status_addr); +} +EXPORT_SYMBOL_GPL(ide_read_status); + +u8 ide_read_altstatus(ide_hwif_t *hwif) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + return readb((void __iomem *)hwif->io_ports.ctl_addr); + else + return inb(hwif->io_ports.ctl_addr); +} +EXPORT_SYMBOL_GPL(ide_read_altstatus); + +u8 ide_read_sff_dma_status(ide_hwif_t *hwif) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + return readb((void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); + else + return inb(hwif->dma_base + ATA_DMA_STATUS); +} +EXPORT_SYMBOL_GPL(ide_read_sff_dma_status); + +void ide_set_irq(ide_hwif_t *hwif, int on) +{ + u8 ctl = ATA_DEVCTL_OBS; + + if (on == 4) { /* hack for SRST */ + ctl |= 4; + on &= ~4; + } + + ctl |= on ? 0 : 2; + + if (hwif->host_flags & IDE_HFLAG_MMIO) + writeb(ctl, (void __iomem *)hwif->io_ports.ctl_addr); + else + outb(ctl, hwif->io_ports.ctl_addr); +} +EXPORT_SYMBOL_GPL(ide_set_irq); + +void ide_tf_load(ide_drive_t *drive, ide_task_t *task) { ide_hwif_t *hwif = drive->hwif; struct ide_io_ports *io_ports = &hwif->io_ports; @@ -155,8 +185,9 @@ static void ide_tf_load(ide_drive_t *drive, ide_task_t *task) tf_outb((tf->device & HIHI) | drive->select.all, io_ports->device_addr); } +EXPORT_SYMBOL_GPL(ide_tf_load); -static void ide_tf_read(ide_drive_t *drive, ide_task_t *task) +void ide_tf_read(ide_drive_t *drive, ide_task_t *task) { ide_hwif_t *hwif = drive->hwif; struct ide_io_ports *io_ports = &hwif->io_ports; @@ -188,6 +219,8 @@ static void ide_tf_read(ide_drive_t *drive, ide_task_t *task) /* be sure we're looking at the low order bits */ tf_outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + if (task->tf_flags & IDE_TFLAG_IN_FEATURE) + tf->feature = tf_inb(io_ports->feature_addr); if (task->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = tf_inb(io_ports->nsect_addr); if (task->tf_flags & IDE_TFLAG_IN_LBAL) @@ -214,6 +247,7 @@ static void ide_tf_read(ide_drive_t *drive, ide_task_t *task) tf->hob_lbah = tf_inb(io_ports->lbah_addr); } } +EXPORT_SYMBOL_GPL(ide_tf_read); /* * Some localbus EIDE interfaces require a special access sequence @@ -236,8 +270,8 @@ static void ata_vlb_sync(unsigned long port) * so if an odd len is specified, be sure that there's at least one * extra byte allocated for the buffer. */ -static void ata_input_data(ide_drive_t *drive, struct request *rq, - void *buf, unsigned int len) +void ide_input_data(ide_drive_t *drive, struct request *rq, void *buf, + unsigned int len) { ide_hwif_t *hwif = drive->hwif; struct ide_io_ports *io_ports = &hwif->io_ports; @@ -277,12 +311,13 @@ static void ata_input_data(ide_drive_t *drive, struct request *rq, insw(data_addr, buf, len / 2); } } +EXPORT_SYMBOL_GPL(ide_input_data); /* * This is used for most PIO data transfers *to* the IDE interface */ -static void ata_output_data(ide_drive_t *drive, struct request *rq, - void *buf, unsigned int len) +void ide_output_data(ide_drive_t *drive, struct request *rq, void *buf, + unsigned int len) { ide_hwif_t *hwif = drive->hwif; struct ide_io_ports *io_ports = &hwif->io_ports; @@ -320,15 +355,50 @@ static void ata_output_data(ide_drive_t *drive, struct request *rq, outsw(data_addr, buf, len / 2); } } +EXPORT_SYMBOL_GPL(ide_output_data); + +u8 ide_read_error(ide_drive_t *drive) +{ + ide_task_t task; + + memset(&task, 0, sizeof(task)); + task.tf_flags = IDE_TFLAG_IN_FEATURE; + + drive->hwif->tp_ops->tf_read(drive, &task); + + return task.tf.error; +} +EXPORT_SYMBOL_GPL(ide_read_error); -void default_hwif_transport(ide_hwif_t *hwif) +void ide_read_bcount_and_ireason(ide_drive_t *drive, u16 *bcount, u8 *ireason) { - hwif->tf_load = ide_tf_load; - hwif->tf_read = ide_tf_read; + ide_task_t task; - hwif->input_data = ata_input_data; - hwif->output_data = ata_output_data; + memset(&task, 0, sizeof(task)); + task.tf_flags = IDE_TFLAG_IN_LBAH | IDE_TFLAG_IN_LBAM | + IDE_TFLAG_IN_NSECT; + + drive->hwif->tp_ops->tf_read(drive, &task); + + *bcount = (task.tf.lbah << 8) | task.tf.lbam; + *ireason = task.tf.nsect & 3; } +EXPORT_SYMBOL_GPL(ide_read_bcount_and_ireason); + +const struct ide_tp_ops default_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; void ide_fix_driveid (struct hd_driveid *id) { @@ -440,10 +510,8 @@ void ide_fixstring (u8 *s, const int bytecount, const int byteswap) if (byteswap) { /* convert from big-endian to host byte order */ - for (p = end ; p != s;) { - unsigned short *pp = (unsigned short *) (p -= 2); - *pp = ntohs(*pp); - } + for (p = end ; p != s;) + be16_to_cpus((u16 *)(p -= 2)); } /* strip leading blanks */ while (s != end && *s == ' ') @@ -483,10 +551,10 @@ int drive_is_ready (ide_drive_t *drive) * about possible isa-pnp and pci-pnp issues yet. */ if (hwif->io_ports.ctl_addr) - stat = ide_read_altstatus(drive); + stat = hwif->tp_ops->read_altstatus(hwif); else /* Note: this may clear a pending IRQ!! */ - stat = ide_read_status(drive); + stat = hwif->tp_ops->read_status(hwif); if (stat & BUSY_STAT) /* drive busy: definitely not interrupting */ @@ -511,24 +579,26 @@ EXPORT_SYMBOL(drive_is_ready); */ static int __ide_wait_stat(ide_drive_t *drive, u8 good, u8 bad, unsigned long timeout, u8 *rstat) { + ide_hwif_t *hwif = drive->hwif; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; unsigned long flags; int i; u8 stat; udelay(1); /* spec allows drive 400ns to assert "BUSY" */ - stat = ide_read_status(drive); + stat = tp_ops->read_status(hwif); if (stat & BUSY_STAT) { local_irq_set(flags); timeout += jiffies; - while ((stat = ide_read_status(drive)) & BUSY_STAT) { + while ((stat = tp_ops->read_status(hwif)) & BUSY_STAT) { if (time_after(jiffies, timeout)) { /* * One last read after the timeout in case * heavy interrupt load made us not make any * progress during the timeout.. */ - stat = ide_read_status(drive); + stat = tp_ops->read_status(hwif); if (!(stat & BUSY_STAT)) break; @@ -548,7 +618,7 @@ static int __ide_wait_stat(ide_drive_t *drive, u8 good, u8 bad, unsigned long ti */ for (i = 0; i < 10; i++) { udelay(1); - stat = ide_read_status(drive); + stat = tp_ops->read_status(hwif); if (OK_STAT(stat, good, bad)) { *rstat = stat; @@ -674,6 +744,7 @@ no_80w: int ide_driveid_update(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; struct hd_driveid *id; unsigned long timeout, flags; u8 stat; @@ -684,9 +755,9 @@ int ide_driveid_update(ide_drive_t *drive) */ SELECT_MASK(drive, 1); - ide_set_irq(drive, 0); + tp_ops->set_irq(hwif, 0); msleep(50); - hwif->OUTBSYNC(hwif, WIN_IDENTIFY, hwif->io_ports.command_addr); + tp_ops->exec_command(hwif, WIN_IDENTIFY); timeout = jiffies + WAIT_WORSTCASE; do { if (time_after(jiffies, timeout)) { @@ -695,11 +766,11 @@ int ide_driveid_update(ide_drive_t *drive) } msleep(50); /* give drive a breather */ - stat = ide_read_altstatus(drive); + stat = tp_ops->read_altstatus(hwif); } while (stat & BUSY_STAT); msleep(50); /* wait for IRQ and DRQ_STAT */ - stat = ide_read_status(drive); + stat = tp_ops->read_status(hwif); if (!OK_STAT(stat, DRQ_STAT, BAD_R_STAT)) { SELECT_MASK(drive, 0); @@ -713,8 +784,8 @@ int ide_driveid_update(ide_drive_t *drive) local_irq_restore(flags); return 0; } - hwif->input_data(drive, NULL, id, SECTOR_SIZE); - (void)ide_read_status(drive); /* clear drive IRQ */ + tp_ops->input_data(drive, NULL, id, SECTOR_SIZE); + (void)tp_ops->read_status(hwif); /* clear drive IRQ */ local_irq_enable(); local_irq_restore(flags); ide_fix_driveid(id); @@ -735,9 +806,10 @@ int ide_driveid_update(ide_drive_t *drive) int ide_config_drive_speed(ide_drive_t *drive, u8 speed) { ide_hwif_t *hwif = drive->hwif; - struct ide_io_ports *io_ports = &hwif->io_ports; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; int error = 0; u8 stat; + ide_task_t task; #ifdef CONFIG_BLK_DEV_IDEDMA if (hwif->dma_ops) /* check if host supports DMA */ @@ -770,12 +842,19 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) SELECT_DRIVE(drive); SELECT_MASK(drive, 0); udelay(1); - ide_set_irq(drive, 0); - hwif->OUTB(speed, io_ports->nsect_addr); - hwif->OUTB(SETFEATURES_XFER, io_ports->feature_addr); - hwif->OUTBSYNC(hwif, WIN_SETFEATURES, io_ports->command_addr); + tp_ops->set_irq(hwif, 0); + + memset(&task, 0, sizeof(task)); + task.tf_flags = IDE_TFLAG_OUT_FEATURE | IDE_TFLAG_OUT_NSECT; + task.tf.feature = SETFEATURES_XFER; + task.tf.nsect = speed; + + tp_ops->tf_load(drive, &task); + + tp_ops->exec_command(hwif, WIN_SETFEATURES); + if (drive->quirk_list == 2) - ide_set_irq(drive, 1); + tp_ops->set_irq(hwif, 1); error = __ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT|ERR_STAT, @@ -796,8 +875,7 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) skip: #ifdef CONFIG_BLK_DEV_IDEDMA - if ((speed >= XFER_SW_DMA_0 || (hwif->host_flags & IDE_HFLAG_VDMA)) && - drive->using_dma) + if (speed >= XFER_SW_DMA_0 && drive->using_dma) hwif->dma_ops->dma_host_set(drive, 1); else if (hwif->dma_ops) /* check if host supports DMA */ ide_dma_off_quietly(drive); @@ -881,7 +959,7 @@ void ide_execute_command(ide_drive_t *drive, u8 cmd, ide_handler_t *handler, spin_lock_irqsave(&ide_lock, flags); __ide_set_handler(drive, handler, timeout, expiry); - hwif->OUTBSYNC(hwif, cmd, hwif->io_ports.command_addr); + hwif->tp_ops->exec_command(hwif, cmd); /* * Drive takes 400nS to respond, we must avoid the IRQ being * serviced before that. @@ -899,7 +977,7 @@ void ide_execute_pkt_cmd(ide_drive_t *drive) unsigned long flags; spin_lock_irqsave(&ide_lock, flags); - hwif->OUTBSYNC(hwif, WIN_PACKETCMD, hwif->io_ports.command_addr); + hwif->tp_ops->exec_command(hwif, WIN_PACKETCMD); ndelay(400); spin_unlock_irqrestore(&ide_lock, flags); } @@ -924,12 +1002,13 @@ static ide_startstop_t do_reset1 (ide_drive_t *, int); */ static ide_startstop_t atapi_reset_pollfunc (ide_drive_t *drive) { - ide_hwgroup_t *hwgroup = HWGROUP(drive); + ide_hwif_t *hwif = drive->hwif; + ide_hwgroup_t *hwgroup = hwif->hwgroup; u8 stat; SELECT_DRIVE(drive); udelay (10); - stat = ide_read_status(drive); + stat = hwif->tp_ops->read_status(hwif); if (OK_STAT(stat, 0, BUSY_STAT)) printk("%s: ATAPI reset complete\n", drive->name); @@ -975,7 +1054,7 @@ static ide_startstop_t reset_pollfunc (ide_drive_t *drive) } } - tmp = ide_read_status(drive); + tmp = hwif->tp_ops->read_status(hwif); if (!OK_STAT(tmp, 0, BUSY_STAT)) { if (time_before(jiffies, hwgroup->poll_timeout)) { @@ -1089,8 +1168,8 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) ide_hwif_t *hwif; ide_hwgroup_t *hwgroup; struct ide_io_ports *io_ports; + const struct ide_tp_ops *tp_ops; const struct ide_port_ops *port_ops; - u8 ctl; spin_lock_irqsave(&ide_lock, flags); hwif = HWIF(drive); @@ -1098,6 +1177,8 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) io_ports = &hwif->io_ports; + tp_ops = hwif->tp_ops; + /* We must not reset with running handlers */ BUG_ON(hwgroup->handler != NULL); @@ -1106,7 +1187,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) pre_reset(drive); SELECT_DRIVE(drive); udelay (20); - hwif->OUTBSYNC(hwif, WIN_SRST, io_ports->command_addr); + tp_ops->exec_command(hwif, WIN_SRST); ndelay(400); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; hwgroup->polling = 1; @@ -1135,16 +1216,15 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) * immediate interrupt due to the edge transition it produces. * This single interrupt gives us a "fast poll" for drives that * recover from reset very quickly, saving us the first 50ms wait time. + * + * TODO: add ->softreset method and stop abusing ->set_irq */ /* set SRST and nIEN */ - hwif->OUTBSYNC(hwif, ATA_DEVCTL_OBS | 6, io_ports->ctl_addr); + tp_ops->set_irq(hwif, 4); /* more than enough time */ udelay(10); - if (drive->quirk_list == 2) - ctl = ATA_DEVCTL_OBS; /* clear SRST and nIEN */ - else - ctl = ATA_DEVCTL_OBS | 2; /* clear SRST, leave nIEN */ - hwif->OUTBSYNC(hwif, ctl, io_ports->ctl_addr); + /* clear SRST, leave nIEN (unless device is on the quirk list) */ + tp_ops->set_irq(hwif, drive->quirk_list == 2); /* more than enough time */ udelay(10); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; @@ -1189,7 +1269,7 @@ int ide_wait_not_busy(ide_hwif_t *hwif, unsigned long timeout) * about locking issues (2.5 work ?). */ mdelay(1); - stat = hwif->INB(hwif->io_ports.status_addr); + stat = hwif->tp_ops->read_status(hwif); if ((stat & BUSY_STAT) == 0) return 0; /* diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c index 13af72f09ec4..97fefabea8b8 100644 --- a/drivers/ide/ide-lib.c +++ b/drivers/ide/ide-lib.c @@ -266,22 +266,11 @@ int ide_set_xfer_rate(ide_drive_t *drive, u8 rate) rate = ide_rate_filter(drive, rate); + BUG_ON(rate < XFER_PIO_0); + if (rate >= XFER_PIO_0 && rate <= XFER_PIO_5) return ide_set_pio_mode(drive, rate); - /* - * TODO: transfer modes 0x00-0x07 passed from the user-space are - * currently handled here which needs fixing (please note that such - * case could happen iff the transfer mode has already been set on - * the device by ide-proc.c::set_xfer_rate()). - */ - if (rate < XFER_PIO_0) { - if (hwif->host_flags & IDE_HFLAG_ABUSE_SET_DMA_MODE) - return ide_set_dma_mode(drive, rate); - else - return ide_config_drive_speed(drive, rate); - } - return ide_set_dma_mode(drive, rate); } @@ -336,7 +325,7 @@ static void ide_dump_sector(ide_drive_t *drive) else task.tf_flags = IDE_TFLAG_IN_LBA | IDE_TFLAG_IN_DEVICE; - drive->hwif->tf_read(drive, &task); + drive->hwif->tp_ops->tf_read(drive, &task); if (lba48 || (tf->device & ATA_LBA)) printk(", LBAsect=%llu", diff --git a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c index 03f2ef5470a3..bac9b392b689 100644 --- a/drivers/ide/ide-pnp.c +++ b/drivers/ide/ide-pnp.c @@ -29,9 +29,10 @@ static struct pnp_device_id idepnp_devices[] = { static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) { - hw_regs_t hw; - ide_hwif_t *hwif; + struct ide_host *host; unsigned long base, ctl; + int rc; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; printk(KERN_INFO DRV_NAME ": generic PnP IDE interface\n"); @@ -59,31 +60,25 @@ static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) hw.irq = pnp_irq(dev, 0); hw.chipset = ide_generic; - hwif = ide_find_port(); - if (hwif) { - u8 index = hwif->index; - u8 idx[4] = { index, 0xff, 0xff, 0xff }; + rc = ide_host_add(NULL, hws, &host); + if (rc) + goto out; - ide_init_port_hw(hwif, &hw); - - pnp_set_drvdata(dev, hwif); - - ide_device_add(idx, NULL); - - return 0; - } + pnp_set_drvdata(dev, host); + return 0; +out: release_region(ctl, 1); release_region(base, 8); - return -1; + return rc; } static void idepnp_remove(struct pnp_dev *dev) { - ide_hwif_t *hwif = pnp_get_drvdata(dev); + struct ide_host *host = pnp_get_drvdata(dev); - ide_unregister(hwif); + ide_host_remove(host); release_region(pnp_port_start(dev, 1), 1); release_region(pnp_port_start(dev, 0), 8); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 235ebdb29b28..994e41099b42 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -39,8 +39,6 @@ #include <asm/uaccess.h> #include <asm/io.h> -static ide_hwif_t ide_hwifs[MAX_HWIFS]; /* master data repository */ - /** * generic_id - add a generic drive id * @drive: drive to make an ID block for @@ -126,7 +124,7 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) id = drive->id; /* read 512 bytes of id info */ - hwif->input_data(drive, NULL, id, SECTOR_SIZE); + hwif->tp_ops->input_data(drive, NULL, id, SECTOR_SIZE); drive->id_read = 1; local_irq_enable(); @@ -136,18 +134,6 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) #endif ide_fix_driveid(id); -#if defined (CONFIG_SCSI_EATA_PIO) || defined (CONFIG_SCSI_EATA) - /* - * EATA SCSI controllers do a hardware ATA emulation: - * Ignore them if there is a driver for them available. - */ - if ((id->model[0] == 'P' && id->model[1] == 'M') || - (id->model[0] == 'S' && id->model[1] == 'K')) { - printk("%s: EATA SCSI HBA %.10s\n", drive->name, id->model); - goto err_misc; - } -#endif /* CONFIG_SCSI_EATA || CONFIG_SCSI_EATA_PIO */ - /* * WIN_IDENTIFY returns little-endian info, * WIN_PIDENTIFY *usually* returns little-endian info. @@ -169,7 +155,8 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) if (strstr(id->model, "E X A B Y T E N E S T")) goto err_misc; - printk("%s: %s, ", drive->name, id->model); + printk(KERN_INFO "%s: %s, ", drive->name, id->model); + drive->present = 1; drive->dead = 0; @@ -178,16 +165,17 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) */ if (cmd == WIN_PIDENTIFY) { u8 type = (id->config >> 8) & 0x1f; - printk("ATAPI "); + + printk(KERN_CONT "ATAPI "); switch (type) { case ide_floppy: if (!strstr(id->model, "CD-ROM")) { if (!strstr(id->model, "oppy") && !strstr(id->model, "poyp") && !strstr(id->model, "ZIP")) - printk("cdrom or floppy?, assuming "); + printk(KERN_CONT "cdrom or floppy?, assuming "); if (drive->media != ide_cdrom) { - printk ("FLOPPY"); + printk(KERN_CONT "FLOPPY"); drive->removable = 1; break; } @@ -200,25 +188,25 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) /* kludge for Apple PowerBook internal zip */ if (!strstr(id->model, "CD-ROM") && strstr(id->model, "ZIP")) { - printk ("FLOPPY"); + printk(KERN_CONT "FLOPPY"); type = ide_floppy; break; } #endif - printk ("CD/DVD-ROM"); + printk(KERN_CONT "CD/DVD-ROM"); break; case ide_tape: - printk ("TAPE"); + printk(KERN_CONT "TAPE"); break; case ide_optical: - printk ("OPTICAL"); + printk(KERN_CONT "OPTICAL"); drive->removable = 1; break; default: - printk("UNKNOWN (type %d)", type); + printk(KERN_CONT "UNKNOWN (type %d)", type); break; } - printk (" drive\n"); + printk(KERN_CONT " drive\n"); drive->media = type; /* an ATAPI device ignores DRDY */ drive->ready_stat = 0; @@ -238,7 +226,9 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) drive->removable = 1; drive->media = ide_disk; - printk("%s DISK drive\n", (id->config == 0x848a) ? "CFA" : "ATA" ); + + printk(KERN_CONT "%s DISK drive\n", + (id->config == 0x848a) ? "CFA" : "ATA"); return; @@ -267,6 +257,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) { ide_hwif_t *hwif = HWIF(drive); struct ide_io_ports *io_ports = &hwif->io_ports; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; int use_altstatus = 0, rc; unsigned long timeout; u8 s = 0, a = 0; @@ -275,8 +266,8 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) msleep(50); if (io_ports->ctl_addr) { - a = ide_read_altstatus(drive); - s = ide_read_status(drive); + a = tp_ops->read_altstatus(hwif); + s = tp_ops->read_status(hwif); if ((a ^ s) & ~INDEX_STAT) /* ancient Seagate drives, broken interfaces */ printk(KERN_INFO "%s: probing with STATUS(0x%02x) " @@ -290,12 +281,18 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) /* set features register for atapi * identify command to be sure of reply */ - if ((cmd == WIN_PIDENTIFY)) - /* disable dma & overlap */ - hwif->OUTB(0, io_ports->feature_addr); + if (cmd == WIN_PIDENTIFY) { + ide_task_t task; + + memset(&task, 0, sizeof(task)); + /* disable DMA & overlap */ + task.tf_flags = IDE_TFLAG_OUT_FEATURE; + + tp_ops->tf_load(drive, &task); + } /* ask drive for ID */ - hwif->OUTBSYNC(hwif, cmd, hwif->io_ports.command_addr); + tp_ops->exec_command(hwif, cmd); timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2; timeout += jiffies; @@ -306,13 +303,13 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) } /* give drive a breather */ msleep(50); - s = use_altstatus ? ide_read_altstatus(drive) - : ide_read_status(drive); + s = use_altstatus ? tp_ops->read_altstatus(hwif) + : tp_ops->read_status(hwif); } while (s & BUSY_STAT); /* wait for IRQ and DRQ_STAT */ msleep(50); - s = ide_read_status(drive); + s = tp_ops->read_status(hwif); if (OK_STAT(s, DRQ_STAT, BAD_R_STAT)) { unsigned long flags; @@ -324,7 +321,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) /* drive responded with ID */ rc = 0; /* clear drive IRQ */ - (void)ide_read_status(drive); + (void)tp_ops->read_status(hwif); local_irq_restore(flags); } else { /* drive refused ID */ @@ -346,6 +343,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) static int try_to_identify (ide_drive_t *drive, u8 cmd) { ide_hwif_t *hwif = HWIF(drive); + const struct ide_tp_ops *tp_ops = hwif->tp_ops; int retval; int autoprobe = 0; unsigned long cookie = 0; @@ -361,7 +359,7 @@ static int try_to_identify (ide_drive_t *drive, u8 cmd) autoprobe = 1; cookie = probe_irq_on(); } - ide_set_irq(drive, autoprobe); + tp_ops->set_irq(hwif, autoprobe); } retval = actual_try_to_identify(drive, cmd); @@ -369,9 +367,9 @@ static int try_to_identify (ide_drive_t *drive, u8 cmd) if (autoprobe) { int irq; - ide_set_irq(drive, 0); + tp_ops->set_irq(hwif, 0); /* clear drive IRQ */ - (void)ide_read_status(drive); + (void)tp_ops->read_status(hwif); udelay(5); irq = probe_irq_off(cookie); if (!hwif->irq) { @@ -381,7 +379,7 @@ static int try_to_identify (ide_drive_t *drive, u8 cmd) /* Mmmm.. multiple IRQs.. * don't know which was ours */ - printk("%s: IRQ probe failed (0x%lx)\n", + printk(KERN_ERR "%s: IRQ probe failed (0x%lx)\n", drive->name, cookie); } } @@ -396,7 +394,7 @@ static int ide_busy_sleep(ide_hwif_t *hwif) do { msleep(50); - stat = hwif->INB(hwif->io_ports.status_addr); + stat = hwif->tp_ops->read_status(hwif); if ((stat & BUSY_STAT) == 0) return 0; } while (time_before(jiffies, timeout)); @@ -404,6 +402,18 @@ static int ide_busy_sleep(ide_hwif_t *hwif) return 1; } +static u8 ide_read_device(ide_drive_t *drive) +{ + ide_task_t task; + + memset(&task, 0, sizeof(task)); + task.tf_flags = IDE_TFLAG_IN_DEVICE; + + drive->hwif->tp_ops->tf_read(drive, &task); + + return task.tf.device; +} + /** * do_probe - probe an IDE device * @drive: drive to probe @@ -428,7 +438,7 @@ static int ide_busy_sleep(ide_hwif_t *hwif) static int do_probe (ide_drive_t *drive, u8 cmd) { ide_hwif_t *hwif = HWIF(drive); - struct ide_io_ports *io_ports = &hwif->io_ports; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; int rc; u8 stat; @@ -438,7 +448,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) return 4; } #ifdef DEBUG - printk("probing for %s: present=%d, media=%d, probetype=%s\n", + printk(KERN_INFO "probing for %s: present=%d, media=%d, probetype=%s\n", drive->name, drive->present, drive->media, (cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI"); #endif @@ -449,8 +459,8 @@ static int do_probe (ide_drive_t *drive, u8 cmd) msleep(50); SELECT_DRIVE(drive); msleep(50); - if (hwif->INB(io_ports->device_addr) != drive->select.all && - !drive->present) { + + if (ide_read_device(drive) != drive->select.all && !drive->present) { if (drive->select.b.unit != 0) { /* exit with drive0 selected */ SELECT_DRIVE(&hwif->drives[0]); @@ -461,7 +471,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) return 3; } - stat = ide_read_status(drive); + stat = tp_ops->read_status(hwif); if (OK_STAT(stat, READY_STAT, BUSY_STAT) || drive->present || cmd == WIN_PIDENTIFY) { @@ -471,7 +481,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) rc = try_to_identify(drive,cmd); } - stat = ide_read_status(drive); + stat = tp_ops->read_status(hwif); if (stat == (BUSY_STAT | READY_STAT)) return 4; @@ -482,13 +492,13 @@ static int do_probe (ide_drive_t *drive, u8 cmd) msleep(50); SELECT_DRIVE(drive); msleep(50); - hwif->OUTBSYNC(hwif, WIN_SRST, io_ports->command_addr); + tp_ops->exec_command(hwif, WIN_SRST); (void)ide_busy_sleep(hwif); rc = try_to_identify(drive, cmd); } /* ensure drive IRQ is clear */ - stat = ide_read_status(drive); + stat = tp_ops->read_status(hwif); if (rc == 1) printk(KERN_ERR "%s: no response (status = 0x%02x)\n", @@ -502,7 +512,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) SELECT_DRIVE(&hwif->drives[0]); msleep(50); /* ensure drive irq is clear */ - (void)ide_read_status(drive); + (void)tp_ops->read_status(hwif); } return rc; } @@ -513,12 +523,14 @@ static int do_probe (ide_drive_t *drive, u8 cmd) static void enable_nest (ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); + const struct ide_tp_ops *tp_ops = hwif->tp_ops; u8 stat; - printk("%s: enabling %s -- ", hwif->name, drive->id->model); + printk(KERN_INFO "%s: enabling %s -- ", hwif->name, drive->id->model); + SELECT_DRIVE(drive); msleep(50); - hwif->OUTBSYNC(hwif, EXABYTE_ENABLE_NEST, hwif->io_ports.command_addr); + tp_ops->exec_command(hwif, EXABYTE_ENABLE_NEST); if (ide_busy_sleep(hwif)) { printk(KERN_CONT "failed (timeout)\n"); @@ -527,7 +539,7 @@ static void enable_nest (ide_drive_t *drive) msleep(50); - stat = ide_read_status(drive); + stat = tp_ops->read_status(hwif); if (!OK_STAT(stat, 0, BAD_STAT)) printk(KERN_CONT "failed (status = 0x%02x)\n", stat); @@ -619,7 +631,7 @@ static inline u8 probe_for_drive (ide_drive_t *drive) return drive->present; } -static void hwif_release_dev (struct device *dev) +static void hwif_release_dev(struct device *dev) { ide_hwif_t *hwif = container_of(dev, ide_hwif_t, gendev); @@ -709,7 +721,7 @@ static int ide_port_wait_ready(ide_hwif_t *hwif) /* Ignore disks that we will not probe for later. */ if (!drive->noprobe || drive->present) { SELECT_DRIVE(drive); - ide_set_irq(drive, 1); + hwif->tp_ops->set_irq(hwif, 1); mdelay(2); rc = ide_wait_not_busy(hwif, 35000); if (rc) @@ -864,7 +876,7 @@ static void save_match(ide_hwif_t *hwif, ide_hwif_t *new, ide_hwif_t **match) if (m && m->hwgroup && m->hwgroup != new->hwgroup) { if (!new->hwgroup) return; - printk("%s: potential irq problem with %s and %s\n", + printk(KERN_WARNING "%s: potential IRQ problem with %s and %s\n", hwif->name, new->name, m->name); } if (!m || m->irq != hwif->irq) /* don't undo a prior perfect match */ @@ -971,6 +983,45 @@ static void ide_port_setup_devices(ide_hwif_t *hwif) mutex_unlock(&ide_cfg_mtx); } +static ide_hwif_t *ide_ports[MAX_HWIFS]; + +void ide_remove_port_from_hwgroup(ide_hwif_t *hwif) +{ + ide_hwgroup_t *hwgroup = hwif->hwgroup; + + ide_ports[hwif->index] = NULL; + + spin_lock_irq(&ide_lock); + /* + * Remove us from the hwgroup, and free + * the hwgroup if we were the only member + */ + if (hwif->next == hwif) { + BUG_ON(hwgroup->hwif != hwif); + kfree(hwgroup); + } else { + /* There is another interface in hwgroup. + * Unlink us, and set hwgroup->drive and ->hwif to + * something sane. + */ + ide_hwif_t *g = hwgroup->hwif; + + while (g->next != hwif) + g = g->next; + g->next = hwif->next; + if (hwgroup->hwif == hwif) { + /* Chose a random hwif for hwgroup->hwif. + * It's guaranteed that there are no drives + * left in the hwgroup. + */ + BUG_ON(hwgroup->drive != NULL); + hwgroup->hwif = g; + } + BUG_ON(hwgroup->hwif == hwif); + } + spin_unlock_irq(&ide_lock); +} + /* * This routine sets up the irq for an ide interface, and creates a new * hwgroup for the irq/hwif if none was previously assigned. @@ -998,8 +1049,9 @@ static int init_irq (ide_hwif_t *hwif) * Group up with any other hwifs that share our irq(s). */ for (index = 0; index < MAX_HWIFS; index++) { - ide_hwif_t *h = &ide_hwifs[index]; - if (h->hwgroup) { /* scan only initialized hwif's */ + ide_hwif_t *h = ide_ports[index]; + + if (h && h->hwgroup) { /* scan only initialized ports */ if (hwif->irq == h->irq) { hwif->sharing_irq = h->sharing_irq = 1; if (hwif->chipset != ide_pci || @@ -1053,6 +1105,8 @@ static int init_irq (ide_hwif_t *hwif) hwgroup->timer.data = (unsigned long) hwgroup; } + ide_ports[hwif->index] = hwif; + /* * Allocate the irq, if not already obtained for another hwif */ @@ -1066,8 +1120,7 @@ static int init_irq (ide_hwif_t *hwif) sa = IRQF_SHARED; if (io_ports->ctl_addr) - /* clear nIEN */ - hwif->OUTBSYNC(hwif, ATA_DEVCTL_OBS, io_ports->ctl_addr); + hwif->tp_ops->set_irq(hwif, 1); if (request_irq(hwif->irq,&ide_intr,sa,hwif->name,hwgroup)) goto out_unlink; @@ -1082,17 +1135,17 @@ static int init_irq (ide_hwif_t *hwif) } #if !defined(__mc68000__) - printk("%s at 0x%03lx-0x%03lx,0x%03lx on irq %d", hwif->name, + printk(KERN_INFO "%s at 0x%03lx-0x%03lx,0x%03lx on irq %d", hwif->name, io_ports->data_addr, io_ports->status_addr, io_ports->ctl_addr, hwif->irq); #else - printk("%s at 0x%08lx on irq %d", hwif->name, + printk(KERN_INFO "%s at 0x%08lx on irq %d", hwif->name, io_ports->data_addr, hwif->irq); #endif /* __mc68000__ */ if (match) - printk(" (%sed with %s)", + printk(KERN_CONT " (%sed with %s)", hwif->sharing_irq ? "shar" : "serializ", match->name); - printk("\n"); + printk(KERN_CONT "\n"); mutex_unlock(&ide_cfg_mtx); return 0; @@ -1227,7 +1280,7 @@ static int hwif_init(ide_hwif_t *hwif) if (!hwif->irq) { hwif->irq = __ide_default_irq(hwif->io_ports.data_addr); if (!hwif->irq) { - printk("%s: DISABLED, NO IRQ\n", hwif->name); + printk(KERN_ERR "%s: disabled, no IRQ\n", hwif->name); return 0; } } @@ -1257,16 +1310,16 @@ static int hwif_init(ide_hwif_t *hwif) */ hwif->irq = __ide_default_irq(hwif->io_ports.data_addr); if (!hwif->irq) { - printk("%s: Disabled unable to get IRQ %d.\n", + printk(KERN_ERR "%s: disabled, unable to get IRQ %d\n", hwif->name, old_irq); goto out; } if (init_irq(hwif)) { - printk("%s: probed IRQ %d and default IRQ %d failed.\n", + printk(KERN_ERR "%s: probed IRQ %d and default IRQ %d failed\n", hwif->name, old_irq, hwif->irq); goto out; } - printk("%s: probed IRQ %d failed, using default.\n", + printk(KERN_WARNING "%s: probed IRQ %d failed, using default\n", hwif->name, hwif->irq); done: @@ -1345,6 +1398,9 @@ static void ide_init_port(ide_hwif_t *hwif, unsigned int port, hwif->host_flags |= d->host_flags; hwif->pio_mask = d->pio_mask; + if (d->tp_ops) + hwif->tp_ops = d->tp_ops; + /* ->set_pio_mode for DTC2278 is currently limited to port 0 */ if (hwif->chipset != ide_dtc2278 || hwif->channel == 0) hwif->port_ops = d->port_ops; @@ -1363,6 +1419,7 @@ static void ide_init_port(ide_hwif_t *hwif, unsigned int port, if (rc < 0) { printk(KERN_INFO "%s: DMA disabled\n", hwif->name); + hwif->dma_base = 0; hwif->swdma_mask = 0; hwif->mwdma_mask = 0; hwif->ultra_mask = 0; @@ -1446,18 +1503,20 @@ static int ide_sysfs_register_port(ide_hwif_t *hwif) return rc; } +static unsigned int ide_indexes; + /** - * ide_find_port_slot - find free ide_hwifs[] slot + * ide_find_port_slot - find free port slot * @d: IDE port info * - * Return the new hwif. If we are out of free slots return NULL. + * Return the new port slot index or -ENOENT if we are out of free slots. */ -ide_hwif_t *ide_find_port_slot(const struct ide_port_info *d) +static int ide_find_port_slot(const struct ide_port_info *d) { - ide_hwif_t *hwif; - int i; + int idx = -ENOENT; u8 bootable = (d && (d->host_flags & IDE_HFLAG_NON_BOOTABLE)) ? 0 : 1; + u8 i = (d && (d->host_flags & IDE_HFLAG_QD_2ND_PORT)) ? 1 : 0;; /* * Claim an unassigned slot. @@ -1469,51 +1528,114 @@ ide_hwif_t *ide_find_port_slot(const struct ide_port_info *d) * Unless there is a bootable card that does not use the standard * ports 0x1f0/0x170 (the ide0/ide1 defaults). */ - if (bootable) { - i = (d && (d->host_flags & IDE_HFLAG_QD_2ND_PORT)) ? 1 : 0; - - for (; i < MAX_HWIFS; i++) { - hwif = &ide_hwifs[i]; - if (hwif->chipset == ide_unknown) - goto out_found; - } + mutex_lock(&ide_cfg_mtx); + if (MAX_HWIFS == 1) { + if (ide_indexes == 0 && i == 0) + idx = 1; } else { - for (i = 2; i < MAX_HWIFS; i++) { - hwif = &ide_hwifs[i]; - if (hwif->chipset == ide_unknown) - goto out_found; + if (bootable) { + if ((ide_indexes | i) != (1 << MAX_HWIFS) - 1) + idx = ffz(ide_indexes | i); + } else { + if ((ide_indexes | 3) != (1 << MAX_HWIFS) - 1) + idx = ffz(ide_indexes | 3); + else if ((ide_indexes & 3) != 3) + idx = ffz(ide_indexes); } - for (i = 0; i < 2 && i < MAX_HWIFS; i++) { - hwif = &ide_hwifs[i]; - if (hwif->chipset == ide_unknown) - goto out_found; + } + if (idx >= 0) + ide_indexes |= (1 << idx); + mutex_unlock(&ide_cfg_mtx); + + return idx; +} + +static void ide_free_port_slot(int idx) +{ + mutex_lock(&ide_cfg_mtx); + ide_indexes &= ~(1 << idx); + mutex_unlock(&ide_cfg_mtx); +} + +struct ide_host *ide_host_alloc_all(const struct ide_port_info *d, + hw_regs_t **hws) +{ + struct ide_host *host; + int i; + + host = kzalloc(sizeof(*host), GFP_KERNEL); + if (host == NULL) + return NULL; + + for (i = 0; i < MAX_HWIFS; i++) { + ide_hwif_t *hwif; + int idx; + + if (hws[i] == NULL) + continue; + + hwif = kzalloc(sizeof(*hwif), GFP_KERNEL); + if (hwif == NULL) + continue; + + idx = ide_find_port_slot(d); + if (idx < 0) { + printk(KERN_ERR "%s: no free slot for interface\n", + d ? d->name : "ide"); + kfree(hwif); + continue; } + + ide_init_port_data(hwif, idx); + + hwif->host = host; + + host->ports[i] = hwif; + host->n_ports++; } - printk(KERN_ERR "%s: no free slot for interface\n", - d ? d->name : "ide"); + if (host->n_ports == 0) { + kfree(host); + return NULL; + } - return NULL; + if (hws[0]) + host->dev[0] = hws[0]->dev; + + if (d) + host->host_flags = d->host_flags; -out_found: - ide_init_port_data(hwif, i); - return hwif; + return host; } -EXPORT_SYMBOL_GPL(ide_find_port_slot); +EXPORT_SYMBOL_GPL(ide_host_alloc_all); -int ide_device_add_all(u8 *idx, const struct ide_port_info *d) +struct ide_host *ide_host_alloc(const struct ide_port_info *d, hw_regs_t **hws) +{ + hw_regs_t *hws_all[MAX_HWIFS]; + int i; + + for (i = 0; i < MAX_HWIFS; i++) + hws_all[i] = (i < 4) ? hws[i] : NULL; + + return ide_host_alloc_all(d, hws_all); +} +EXPORT_SYMBOL_GPL(ide_host_alloc); + +int ide_host_register(struct ide_host *host, const struct ide_port_info *d, + hw_regs_t **hws) { ide_hwif_t *hwif, *mate = NULL; - int i, rc = 0; + int i, j = 0; for (i = 0; i < MAX_HWIFS; i++) { - if (idx[i] == 0xff) { + hwif = host->ports[i]; + + if (hwif == NULL) { mate = NULL; continue; } - hwif = &ide_hwifs[idx[i]]; - + ide_init_port_hw(hwif, hws[i]); ide_port_apply_params(hwif); if (d == NULL) { @@ -1534,10 +1656,10 @@ int ide_device_add_all(u8 *idx, const struct ide_port_info *d) } for (i = 0; i < MAX_HWIFS; i++) { - if (idx[i] == 0xff) - continue; + hwif = host->ports[i]; - hwif = &ide_hwifs[idx[i]]; + if (hwif == NULL) + continue; if (ide_probe_port(hwif) == 0) hwif->present = 1; @@ -1551,19 +1673,20 @@ int ide_device_add_all(u8 *idx, const struct ide_port_info *d) } for (i = 0; i < MAX_HWIFS; i++) { - if (idx[i] == 0xff) - continue; + hwif = host->ports[i]; - hwif = &ide_hwifs[idx[i]]; + if (hwif == NULL) + continue; if (hwif_init(hwif) == 0) { printk(KERN_INFO "%s: failed to initialize IDE " "interface\n", hwif->name); hwif->present = 0; - rc = -1; continue; } + j++; + if (hwif->present) ide_port_setup_devices(hwif); @@ -1574,10 +1697,10 @@ int ide_device_add_all(u8 *idx, const struct ide_port_info *d) } for (i = 0; i < MAX_HWIFS; i++) { - if (idx[i] == 0xff) - continue; + hwif = host->ports[i]; - hwif = &ide_hwifs[idx[i]]; + if (hwif == NULL) + continue; if (hwif->chipset == ide_unknown) hwif->chipset = ide_generic; @@ -1587,10 +1710,10 @@ int ide_device_add_all(u8 *idx, const struct ide_port_info *d) } for (i = 0; i < MAX_HWIFS; i++) { - if (idx[i] == 0xff) - continue; + hwif = host->ports[i]; - hwif = &ide_hwifs[idx[i]]; + if (hwif == NULL) + continue; ide_sysfs_register_port(hwif); ide_proc_register_port(hwif); @@ -1599,21 +1722,64 @@ int ide_device_add_all(u8 *idx, const struct ide_port_info *d) ide_proc_port_register_devices(hwif); } - return rc; + return j ? 0 : -1; } -EXPORT_SYMBOL_GPL(ide_device_add_all); +EXPORT_SYMBOL_GPL(ide_host_register); -int ide_device_add(u8 idx[4], const struct ide_port_info *d) +int ide_host_add(const struct ide_port_info *d, hw_regs_t **hws, + struct ide_host **hostp) { - u8 idx_all[MAX_HWIFS]; + struct ide_host *host; + int rc; + + host = ide_host_alloc(d, hws); + if (host == NULL) + return -ENOMEM; + + rc = ide_host_register(host, d, hws); + if (rc) { + ide_host_free(host); + return rc; + } + + if (hostp) + *hostp = host; + + return 0; +} +EXPORT_SYMBOL_GPL(ide_host_add); + +void ide_host_free(struct ide_host *host) +{ + ide_hwif_t *hwif; int i; - for (i = 0; i < MAX_HWIFS; i++) - idx_all[i] = (i < 4) ? idx[i] : 0xff; + for (i = 0; i < MAX_HWIFS; i++) { + hwif = host->ports[i]; + + if (hwif == NULL) + continue; + + ide_free_port_slot(hwif->index); + kfree(hwif); + } - return ide_device_add_all(idx_all, d); + kfree(host); } -EXPORT_SYMBOL_GPL(ide_device_add); +EXPORT_SYMBOL_GPL(ide_host_free); + +void ide_host_remove(struct ide_host *host) +{ + int i; + + for (i = 0; i < MAX_HWIFS; i++) { + if (host->ports[i]) + ide_unregister(host->ports[i]); + } + + ide_host_free(host); +} +EXPORT_SYMBOL_GPL(ide_host_remove); void ide_port_scan(ide_hwif_t *hwif) { @@ -1634,11 +1800,10 @@ void ide_port_scan(ide_hwif_t *hwif) } EXPORT_SYMBOL_GPL(ide_port_scan); -static void ide_legacy_init_one(u8 *idx, hw_regs_t *hw, u8 port_no, - const struct ide_port_info *d, +static void ide_legacy_init_one(hw_regs_t **hws, hw_regs_t *hw, + u8 port_no, const struct ide_port_info *d, unsigned long config) { - ide_hwif_t *hwif; unsigned long base, ctl; int irq; @@ -1668,33 +1833,25 @@ static void ide_legacy_init_one(u8 *idx, hw_regs_t *hw, u8 port_no, ide_std_init_ports(hw, base, ctl); hw->irq = irq; hw->chipset = d->chipset; + hw->config = config; - hwif = ide_find_port_slot(d); - if (hwif) { - ide_init_port_hw(hwif, hw); - if (config) - hwif->config_data = config; - idx[port_no] = hwif->index; - } + hws[port_no] = hw; } int ide_legacy_device_add(const struct ide_port_info *d, unsigned long config) { - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw[2]; + hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL }; memset(&hw, 0, sizeof(hw)); if ((d->host_flags & IDE_HFLAG_QD_2ND_PORT) == 0) - ide_legacy_init_one(idx, &hw[0], 0, d, config); - ide_legacy_init_one(idx, &hw[1], 1, d, config); + ide_legacy_init_one(hws, &hw[0], 0, d, config); + ide_legacy_init_one(hws, &hw[1], 1, d, config); - if (idx[0] == 0xff && idx[1] == 0xff && + if (hws[0] == NULL && hws[1] == NULL && (d->host_flags & IDE_HFLAG_SINGLE)) return -ENOENT; - ide_device_add(idx, d); - - return 0; + return ide_host_add(d, hws, NULL); } EXPORT_SYMBOL_GPL(ide_legacy_device_add); diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index 8af88bf0969b..f66c9c3f6fc6 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c @@ -105,7 +105,7 @@ static int proc_ide_read_identify len = sprintf(page, "\n"); if (drive) { - unsigned short *val = (unsigned short *) page; + __le16 *val = (__le16 *)page; err = taskfile_lib_get_identify(drive, page); if (!err) { @@ -113,7 +113,7 @@ static int proc_ide_read_identify page = out; do { out += sprintf(out, "%04x%c", - le16_to_cpu(*val), (++i & 7) ? ' ' : '\n'); + le16_to_cpup(val), (++i & 7) ? ' ' : '\n'); val += 1; } while (i < (SECTOR_WORDS * 2)); len = out - page; @@ -345,7 +345,7 @@ static int set_xfer_rate (ide_drive_t *drive, int arg) ide_task_t task; int err; - if (arg < 0 || arg > 70) + if (arg < XFER_PIO_0 || arg > XFER_UDMA_6) return -EINVAL; memset(&task, 0, sizeof(task)); @@ -357,7 +357,7 @@ static int set_xfer_rate (ide_drive_t *drive, int arg) err = ide_no_data_taskfile(drive, &task); - if (!err && arg) { + if (!err) { ide_set_xfer_rate(drive, (u8) arg); ide_driveid_update(drive); } diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index b711ab96e287..82c2afe4d28a 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -195,23 +195,6 @@ enum { #define IDETAPE_BLOCK_DESCRIPTOR 0 #define IDETAPE_CAPABILITIES_PAGE 0x2a -/* Tape flag bits values. */ -enum { - IDETAPE_FLAG_IGNORE_DSC = (1 << 0), - /* 0 When the tape position is unknown */ - IDETAPE_FLAG_ADDRESS_VALID = (1 << 1), - /* Device already opened */ - IDETAPE_FLAG_BUSY = (1 << 2), - /* Attempt to auto-detect the current user block size */ - IDETAPE_FLAG_DETECT_BS = (1 << 3), - /* Currently on a filemark */ - IDETAPE_FLAG_FILEMARK = (1 << 4), - /* DRQ interrupt device */ - IDETAPE_FLAG_DRQ_INTERRUPT = (1 << 5), - /* 0 = no tape is loaded, so we don't rewind after ejecting */ - IDETAPE_FLAG_MEDIUM_PRESENT = (1 << 6), -}; - /* * Most of our global data which we need to save even as we leave the driver due * to an interrupt or a timer event is stored in the struct defined below. @@ -312,8 +295,6 @@ typedef struct ide_tape_obj { /* Wasted space in each stage */ int excess_bh_size; - /* Status/Action flags: long for set_bit */ - unsigned long flags; /* protects the ide-tape queue */ spinlock_t lock; @@ -341,23 +322,29 @@ static struct class *idetape_sysfs_class; #define ide_tape_g(disk) \ container_of((disk)->private_data, struct ide_tape_obj, driver) +static void ide_tape_release(struct kref *); + static struct ide_tape_obj *ide_tape_get(struct gendisk *disk) { struct ide_tape_obj *tape = NULL; mutex_lock(&idetape_ref_mutex); tape = ide_tape_g(disk); - if (tape) + if (tape) { kref_get(&tape->kref); + if (ide_device_get(tape->drive)) { + kref_put(&tape->kref, ide_tape_release); + tape = NULL; + } + } mutex_unlock(&idetape_ref_mutex); return tape; } -static void ide_tape_release(struct kref *); - static void ide_tape_put(struct ide_tape_obj *tape) { mutex_lock(&idetape_ref_mutex); + ide_device_put(tape->drive); kref_put(&tape->kref, ide_tape_release); mutex_unlock(&idetape_ref_mutex); } @@ -398,7 +385,7 @@ static void idetape_input_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, count = min( (unsigned int)(bh->b_size - atomic_read(&bh->b_count)), bcount); - drive->hwif->input_data(drive, NULL, bh->b_data + + drive->hwif->tp_ops->input_data(drive, NULL, bh->b_data + atomic_read(&bh->b_count), count); bcount -= count; atomic_add(count, &bh->b_count); @@ -424,7 +411,7 @@ static void idetape_output_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, return; } count = min((unsigned int)pc->b_count, (unsigned int)bcount); - drive->hwif->output_data(drive, NULL, pc->b_data, count); + drive->hwif->tp_ops->output_data(drive, NULL, pc->b_data, count); bcount -= count; pc->b_data += count; pc->b_count -= count; @@ -585,7 +572,6 @@ static void ide_tape_kfree_buffer(idetape_tape_t *tape) bh = bh->b_reqnext; kfree(prev_bh); } - kfree(tape->merge_bh); } static int idetape_end_request(ide_drive_t *drive, int uptodate, int nr_sects) @@ -665,15 +651,15 @@ static void ide_tape_callback(ide_drive_t *drive) if (readpos[0] & 0x4) { printk(KERN_INFO "ide-tape: Block location is unknown" "to the tape\n"); - clear_bit(IDETAPE_FLAG_ADDRESS_VALID, &tape->flags); + clear_bit(IDE_AFLAG_ADDRESS_VALID, &drive->atapi_flags); uptodate = 0; } else { debug_log(DBG_SENSE, "Block Location - %u\n", - be32_to_cpu(*(u32 *)&readpos[4])); + be32_to_cpup((__be32 *)&readpos[4])); tape->partition = readpos[1]; - tape->first_frame = be32_to_cpu(*(u32 *)&readpos[4]); - set_bit(IDETAPE_FLAG_ADDRESS_VALID, &tape->flags); + tape->first_frame = be32_to_cpup((__be32 *)&readpos[4]); + set_bit(IDE_AFLAG_ADDRESS_VALID, &drive->atapi_flags); } } @@ -690,7 +676,6 @@ static void idetape_init_pc(struct ide_atapi_pc *pc) pc->buf_size = IDETAPE_PC_BUFFER_SIZE; pc->bh = NULL; pc->b_data = NULL; - pc->callback = ide_tape_callback; } static void idetape_create_request_sense_cmd(struct ide_atapi_pc *pc) @@ -705,7 +690,7 @@ static void idetape_init_rq(struct request *rq, u8 cmd) { blk_rq_init(NULL, rq); rq->cmd_type = REQ_TYPE_SPECIAL; - rq->cmd[0] = cmd; + rq->cmd[13] = cmd; } /* @@ -732,6 +717,7 @@ static void idetape_queue_pc_head(ide_drive_t *drive, struct ide_atapi_pc *pc, rq->cmd_flags |= REQ_PREEMPT; rq->buffer = (char *) pc; rq->rq_disk = tape->disk; + memcpy(rq->cmd, pc->c, 12); ide_do_drive_cmd(drive, rq); } @@ -742,7 +728,6 @@ static void idetape_queue_pc_head(ide_drive_t *drive, struct ide_atapi_pc *pc, */ static void idetape_retry_pc(ide_drive_t *drive) { - idetape_tape_t *tape = drive->driver_data; struct ide_atapi_pc *pc; struct request *rq; @@ -750,7 +735,7 @@ static void idetape_retry_pc(ide_drive_t *drive) pc = idetape_next_pc_storage(drive); rq = idetape_next_rq_storage(drive); idetape_create_request_sense_cmd(pc); - set_bit(IDETAPE_FLAG_IGNORE_DSC, &tape->flags); + set_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags); idetape_queue_pc_head(drive, pc, rq); } @@ -887,7 +872,7 @@ static ide_startstop_t idetape_issue_pc(ide_drive_t *drive, pc->error = IDETAPE_ERROR_GENERAL; } tape->failed_pc = NULL; - pc->callback(drive); + drive->pc_callback(drive); return ide_stopped; } debug_log(DBG_SENSE, "Retry #%d, cmd = %02X\n", pc->retries, pc->c[0]); @@ -927,11 +912,12 @@ static void idetape_create_mode_sense_cmd(struct ide_atapi_pc *pc, u8 page_code) static ide_startstop_t idetape_media_access_finished(ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; idetape_tape_t *tape = drive->driver_data; struct ide_atapi_pc *pc = tape->pc; u8 stat; - stat = ide_read_status(drive); + stat = hwif->tp_ops->read_status(hwif); if (stat & SEEK_STAT) { if (stat & ERR_STAT) { @@ -948,14 +934,17 @@ static ide_startstop_t idetape_media_access_finished(ide_drive_t *drive) pc->error = IDETAPE_ERROR_GENERAL; tape->failed_pc = NULL; } - pc->callback(drive); + drive->pc_callback(drive); return ide_stopped; } static void ide_tape_create_rw_cmd(idetape_tape_t *tape, - struct ide_atapi_pc *pc, unsigned int length, - struct idetape_bh *bh, u8 opcode) + struct ide_atapi_pc *pc, struct request *rq, + u8 opcode) { + struct idetape_bh *bh = (struct idetape_bh *)rq->special; + unsigned int length = rq->current_nr_sectors; + idetape_init_pc(pc); put_unaligned(cpu_to_be32(length), (unsigned int *) &pc->c[1]); pc->c[1] = 1; @@ -975,11 +964,14 @@ static void ide_tape_create_rw_cmd(idetape_tape_t *tape, pc->b_data = bh->b_data; pc->b_count = atomic_read(&bh->b_count); } + + memcpy(rq->cmd, pc->c, 12); } static ide_startstop_t idetape_do_request(ide_drive_t *drive, struct request *rq, sector_t block) { + ide_hwif_t *hwif = drive->hwif; idetape_tape_t *tape = drive->driver_data; struct ide_atapi_pc *pc = NULL; struct request *postponed_rq = tape->postponed_rq; @@ -1017,17 +1009,17 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive, * If the tape is still busy, postpone our request and service * the other device meanwhile. */ - stat = ide_read_status(drive); + stat = hwif->tp_ops->read_status(hwif); - if (!drive->dsc_overlap && !(rq->cmd[0] & REQ_IDETAPE_PC2)) - set_bit(IDETAPE_FLAG_IGNORE_DSC, &tape->flags); + if (!drive->dsc_overlap && !(rq->cmd[13] & REQ_IDETAPE_PC2)) + set_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags); if (drive->post_reset == 1) { - set_bit(IDETAPE_FLAG_IGNORE_DSC, &tape->flags); + set_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags); drive->post_reset = 0; } - if (!test_and_clear_bit(IDETAPE_FLAG_IGNORE_DSC, &tape->flags) && + if (!test_and_clear_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags) && (stat & SEEK_STAT) == 0) { if (postponed_rq == NULL) { tape->dsc_polling_start = jiffies; @@ -1036,7 +1028,7 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive, } else if (time_after(jiffies, tape->dsc_timeout)) { printk(KERN_ERR "ide-tape: %s: DSC timeout\n", tape->name); - if (rq->cmd[0] & REQ_IDETAPE_PC2) { + if (rq->cmd[13] & REQ_IDETAPE_PC2) { idetape_media_access_finished(drive); return ide_stopped; } else { @@ -1049,35 +1041,29 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive, idetape_postpone_request(drive); return ide_stopped; } - if (rq->cmd[0] & REQ_IDETAPE_READ) { + if (rq->cmd[13] & REQ_IDETAPE_READ) { pc = idetape_next_pc_storage(drive); - ide_tape_create_rw_cmd(tape, pc, rq->current_nr_sectors, - (struct idetape_bh *)rq->special, - READ_6); + ide_tape_create_rw_cmd(tape, pc, rq, READ_6); goto out; } - if (rq->cmd[0] & REQ_IDETAPE_WRITE) { + if (rq->cmd[13] & REQ_IDETAPE_WRITE) { pc = idetape_next_pc_storage(drive); - ide_tape_create_rw_cmd(tape, pc, rq->current_nr_sectors, - (struct idetape_bh *)rq->special, - WRITE_6); + ide_tape_create_rw_cmd(tape, pc, rq, WRITE_6); goto out; } - if (rq->cmd[0] & REQ_IDETAPE_PC1) { + if (rq->cmd[13] & REQ_IDETAPE_PC1) { pc = (struct ide_atapi_pc *) rq->buffer; - rq->cmd[0] &= ~(REQ_IDETAPE_PC1); - rq->cmd[0] |= REQ_IDETAPE_PC2; + rq->cmd[13] &= ~(REQ_IDETAPE_PC1); + rq->cmd[13] |= REQ_IDETAPE_PC2; goto out; } - if (rq->cmd[0] & REQ_IDETAPE_PC2) { + if (rq->cmd[13] & REQ_IDETAPE_PC2) { idetape_media_access_finished(drive); return ide_stopped; } BUG(); -out: - if (test_bit(IDETAPE_FLAG_DRQ_INTERRUPT, &tape->flags)) - pc->flags |= PC_FLAG_DRQ_INTERRUPT; +out: return idetape_issue_pc(drive, pc); } @@ -1281,8 +1267,9 @@ static int idetape_queue_pc_tail(ide_drive_t *drive, struct ide_atapi_pc *pc) rq = blk_get_request(drive->queue, READ, __GFP_WAIT); rq->cmd_type = REQ_TYPE_SPECIAL; - rq->cmd[0] = REQ_IDETAPE_PC1; + rq->cmd[13] = REQ_IDETAPE_PC1; rq->buffer = (char *)pc; + memcpy(rq->cmd, pc->c, 12); error = blk_execute_rq(drive->queue, tape->disk, rq, 0); blk_put_request(rq); return error; @@ -1304,7 +1291,7 @@ static int idetape_wait_ready(ide_drive_t *drive, unsigned long timeout) int load_attempted = 0; /* Wait for the tape to become ready */ - set_bit(IDETAPE_FLAG_MEDIUM_PRESENT, &tape->flags); + set_bit(IDE_AFLAG_MEDIUM_PRESENT, &drive->atapi_flags); timeout += jiffies; while (time_before(jiffies, timeout)) { idetape_create_test_unit_ready_cmd(&pc); @@ -1397,7 +1384,7 @@ static void __ide_tape_discard_merge_buffer(ide_drive_t *drive) if (tape->chrdev_dir != IDETAPE_DIR_READ) return; - clear_bit(IDETAPE_FLAG_FILEMARK, &tape->flags); + clear_bit(IDE_AFLAG_FILEMARK, &drive->atapi_flags); tape->merge_bh_size = 0; if (tape->merge_bh != NULL) { ide_tape_kfree_buffer(tape); @@ -1465,7 +1452,7 @@ static int idetape_queue_rw_tail(ide_drive_t *drive, int cmd, int blocks, rq = blk_get_request(drive->queue, READ, __GFP_WAIT); rq->cmd_type = REQ_TYPE_SPECIAL; - rq->cmd[0] = cmd; + rq->cmd[13] = cmd; rq->rq_disk = tape->disk; rq->special = (void *)bh; rq->sector = tape->first_frame; @@ -1636,7 +1623,7 @@ static int idetape_add_chrdev_read_request(ide_drive_t *drive, int blocks) debug_log(DBG_PROCS, "Enter %s, %d blocks\n", __func__, blocks); /* If we are at a filemark, return a read length of 0 */ - if (test_bit(IDETAPE_FLAG_FILEMARK, &tape->flags)) + if (test_bit(IDE_AFLAG_FILEMARK, &drive->atapi_flags)) return 0; idetape_init_read(drive); @@ -1746,7 +1733,7 @@ static int idetape_space_over_filemarks(ide_drive_t *drive, short mt_op, if (tape->chrdev_dir == IDETAPE_DIR_READ) { tape->merge_bh_size = 0; - if (test_and_clear_bit(IDETAPE_FLAG_FILEMARK, &tape->flags)) + if (test_and_clear_bit(IDE_AFLAG_FILEMARK, &drive->atapi_flags)) ++count; ide_tape_discard_merge_buffer(drive, 0); } @@ -1801,7 +1788,7 @@ static ssize_t idetape_chrdev_read(struct file *file, char __user *buf, debug_log(DBG_CHRDEV, "Enter %s, count %Zd\n", __func__, count); if (tape->chrdev_dir != IDETAPE_DIR_READ) { - if (test_bit(IDETAPE_FLAG_DETECT_BS, &tape->flags)) + if (test_bit(IDE_AFLAG_DETECT_BS, &drive->atapi_flags)) if (count > tape->blk_size && (count % tape->blk_size) == 0) tape->user_bs_factor = count / tape->blk_size; @@ -1841,7 +1828,7 @@ static ssize_t idetape_chrdev_read(struct file *file, char __user *buf, tape->merge_bh_size = bytes_read-temp; } finish: - if (!actually_read && test_bit(IDETAPE_FLAG_FILEMARK, &tape->flags)) { + if (!actually_read && test_bit(IDE_AFLAG_FILEMARK, &drive->atapi_flags)) { debug_log(DBG_SENSE, "%s: spacing over filemark\n", tape->name); idetape_space_over_filemarks(drive, MTFSF, 1); @@ -2027,7 +2014,7 @@ static int idetape_mtioctop(ide_drive_t *drive, short mt_op, int mt_count) !IDETAPE_LU_LOAD_MASK); retval = idetape_queue_pc_tail(drive, &pc); if (!retval) - clear_bit(IDETAPE_FLAG_MEDIUM_PRESENT, &tape->flags); + clear_bit(IDE_AFLAG_MEDIUM_PRESENT, &drive->atapi_flags); return retval; case MTNOP: ide_tape_discard_merge_buffer(drive, 0); @@ -2050,9 +2037,9 @@ static int idetape_mtioctop(ide_drive_t *drive, short mt_op, int mt_count) mt_count % tape->blk_size) return -EIO; tape->user_bs_factor = mt_count / tape->blk_size; - clear_bit(IDETAPE_FLAG_DETECT_BS, &tape->flags); + clear_bit(IDE_AFLAG_DETECT_BS, &drive->atapi_flags); } else - set_bit(IDETAPE_FLAG_DETECT_BS, &tape->flags); + set_bit(IDE_AFLAG_DETECT_BS, &drive->atapi_flags); return 0; case MTSEEK: ide_tape_discard_merge_buffer(drive, 0); @@ -2202,20 +2189,20 @@ static int idetape_chrdev_open(struct inode *inode, struct file *filp) filp->private_data = tape; - if (test_and_set_bit(IDETAPE_FLAG_BUSY, &tape->flags)) { + if (test_and_set_bit(IDE_AFLAG_BUSY, &drive->atapi_flags)) { retval = -EBUSY; goto out_put_tape; } retval = idetape_wait_ready(drive, 60 * HZ); if (retval) { - clear_bit(IDETAPE_FLAG_BUSY, &tape->flags); + clear_bit(IDE_AFLAG_BUSY, &drive->atapi_flags); printk(KERN_ERR "ide-tape: %s: drive not ready\n", tape->name); goto out_put_tape; } idetape_read_position(drive); - if (!test_bit(IDETAPE_FLAG_ADDRESS_VALID, &tape->flags)) + if (!test_bit(IDE_AFLAG_ADDRESS_VALID, &drive->atapi_flags)) (void)idetape_rewind_tape(drive); /* Read block size and write protect status from drive. */ @@ -2231,7 +2218,7 @@ static int idetape_chrdev_open(struct inode *inode, struct file *filp) if (tape->write_prot) { if ((filp->f_flags & O_ACCMODE) == O_WRONLY || (filp->f_flags & O_ACCMODE) == O_RDWR) { - clear_bit(IDETAPE_FLAG_BUSY, &tape->flags); + clear_bit(IDE_AFLAG_BUSY, &drive->atapi_flags); retval = -EROFS; goto out_put_tape; } @@ -2291,7 +2278,7 @@ static int idetape_chrdev_release(struct inode *inode, struct file *filp) ide_tape_discard_merge_buffer(drive, 1); } - if (minor < 128 && test_bit(IDETAPE_FLAG_MEDIUM_PRESENT, &tape->flags)) + if (minor < 128 && test_bit(IDE_AFLAG_MEDIUM_PRESENT, &drive->atapi_flags)) (void) idetape_rewind_tape(drive); if (tape->chrdev_dir == IDETAPE_DIR_NONE) { if (tape->door_locked == DOOR_LOCKED) { @@ -2301,7 +2288,7 @@ static int idetape_chrdev_release(struct inode *inode, struct file *filp) } } } - clear_bit(IDETAPE_FLAG_BUSY, &tape->flags); + clear_bit(IDE_AFLAG_BUSY, &drive->atapi_flags); ide_tape_put(tape); unlock_kernel(); return 0; @@ -2394,23 +2381,23 @@ static void idetape_get_mode_sense_results(ide_drive_t *drive) caps = pc.buf + 4 + pc.buf[3]; /* convert to host order and save for later use */ - speed = be16_to_cpu(*(u16 *)&caps[14]); - max_speed = be16_to_cpu(*(u16 *)&caps[8]); + speed = be16_to_cpup((__be16 *)&caps[14]); + max_speed = be16_to_cpup((__be16 *)&caps[8]); - put_unaligned(max_speed, (u16 *)&caps[8]); - put_unaligned(be16_to_cpu(*(u16 *)&caps[12]), (u16 *)&caps[12]); - put_unaligned(speed, (u16 *)&caps[14]); - put_unaligned(be16_to_cpu(*(u16 *)&caps[16]), (u16 *)&caps[16]); + *(u16 *)&caps[8] = max_speed; + *(u16 *)&caps[12] = be16_to_cpup((__be16 *)&caps[12]); + *(u16 *)&caps[14] = speed; + *(u16 *)&caps[16] = be16_to_cpup((__be16 *)&caps[16]); if (!speed) { printk(KERN_INFO "ide-tape: %s: invalid tape speed " "(assuming 650KB/sec)\n", drive->name); - put_unaligned(650, (u16 *)&caps[14]); + *(u16 *)&caps[14] = 650; } if (!max_speed) { printk(KERN_INFO "ide-tape: %s: invalid max_speed " "(assuming 650KB/sec)\n", drive->name); - put_unaligned(650, (u16 *)&caps[8]); + *(u16 *)&caps[8] = 650; } memcpy(&tape->caps, caps, 20); @@ -2464,6 +2451,8 @@ static void idetape_setup(ide_drive_t *drive, idetape_tape_t *tape, int minor) u8 gcw[2]; u16 *ctl = (u16 *)&tape->caps[12]; + drive->pc_callback = ide_tape_callback; + spin_lock_init(&tape->lock); drive->dsc_overlap = 1; if (drive->hwif->host_flags & IDE_HFLAG_NO_DSC) { @@ -2484,7 +2473,7 @@ static void idetape_setup(ide_drive_t *drive, idetape_tape_t *tape, int minor) /* Command packet DRQ type */ if (((gcw[0] & 0x60) >> 5) == 1) - set_bit(IDETAPE_FLAG_DRQ_INTERRUPT, &tape->flags); + set_bit(IDE_AFLAG_DRQ_INTERRUPT, &drive->atapi_flags); idetape_get_inquiry_results(drive); idetape_get_mode_sense_results(drive); @@ -2697,10 +2686,12 @@ static int ide_tape_probe(ide_drive_t *drive) idetape_setup(drive, tape, minor); - device_create(idetape_sysfs_class, &drive->gendev, - MKDEV(IDETAPE_MAJOR, minor), "%s", tape->name); - device_create(idetape_sysfs_class, &drive->gendev, - MKDEV(IDETAPE_MAJOR, minor + 128), "n%s", tape->name); + device_create_drvdata(idetape_sysfs_class, &drive->gendev, + MKDEV(IDETAPE_MAJOR, minor), NULL, + "%s", tape->name); + device_create_drvdata(idetape_sysfs_class, &drive->gendev, + MKDEV(IDETAPE_MAJOR, minor + 128), NULL, + "n%s", tape->name); g->fops = &idetape_block_ops; ide_register_region(g); diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 1fbdb746dc88..7fb6f1c86272 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -64,6 +64,7 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) ide_hwif_t *hwif = HWIF(drive); struct ide_taskfile *tf = &task->tf; ide_handler_t *handler = NULL; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; const struct ide_dma_ops *dma_ops = hwif->dma_ops; if (task->data_phase == TASKFILE_MULTI_IN || @@ -80,15 +81,15 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) if ((task->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) { ide_tf_dump(drive->name, tf); - ide_set_irq(drive, 1); + tp_ops->set_irq(hwif, 1); SELECT_MASK(drive, 0); - hwif->tf_load(drive, task); + tp_ops->tf_load(drive, task); } switch (task->data_phase) { case TASKFILE_MULTI_OUT: case TASKFILE_OUT: - hwif->OUTBSYNC(hwif, tf->command, hwif->io_ports.command_addr); + tp_ops->exec_command(hwif, tf->command); ndelay(400); /* FIXME */ return pre_task_out_intr(drive, task->rq); case TASKFILE_MULTI_IN: @@ -124,7 +125,11 @@ EXPORT_SYMBOL_GPL(do_rw_taskfile); */ static ide_startstop_t set_multmode_intr(ide_drive_t *drive) { - u8 stat = ide_read_status(drive); + ide_hwif_t *hwif = drive->hwif; + u8 stat; + + local_irq_enable_in_hardirq(); + stat = hwif->tp_ops->read_status(hwif); if (OK_STAT(stat, READY_STAT, BAD_STAT)) drive->mult_count = drive->mult_req; @@ -141,11 +146,18 @@ static ide_startstop_t set_multmode_intr(ide_drive_t *drive) */ static ide_startstop_t set_geometry_intr(ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; int retries = 5; u8 stat; - while (((stat = ide_read_status(drive)) & BUSY_STAT) && retries--) + local_irq_enable_in_hardirq(); + + while (1) { + stat = hwif->tp_ops->read_status(hwif); + if ((stat & BUSY_STAT) == 0 || retries-- == 0) + break; udelay(10); + }; if (OK_STAT(stat, READY_STAT, BAD_STAT)) return ide_stopped; @@ -162,7 +174,11 @@ static ide_startstop_t set_geometry_intr(ide_drive_t *drive) */ static ide_startstop_t recal_intr(ide_drive_t *drive) { - u8 stat = ide_read_status(drive); + ide_hwif_t *hwif = drive->hwif; + u8 stat; + + local_irq_enable_in_hardirq(); + stat = hwif->tp_ops->read_status(hwif); if (!OK_STAT(stat, READY_STAT, BAD_STAT)) return ide_error(drive, "recal_intr", stat); @@ -174,11 +190,12 @@ static ide_startstop_t recal_intr(ide_drive_t *drive) */ static ide_startstop_t task_no_data_intr(ide_drive_t *drive) { - ide_task_t *args = HWGROUP(drive)->rq->special; + ide_hwif_t *hwif = drive->hwif; + ide_task_t *args = hwif->hwgroup->rq->special; u8 stat; local_irq_enable_in_hardirq(); - stat = ide_read_status(drive); + stat = hwif->tp_ops->read_status(hwif); if (!OK_STAT(stat, READY_STAT, BAD_STAT)) return ide_error(drive, "task_no_data_intr", stat); @@ -192,6 +209,7 @@ static ide_startstop_t task_no_data_intr(ide_drive_t *drive) static u8 wait_drive_not_busy(ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; int retries; u8 stat; @@ -200,7 +218,7 @@ static u8 wait_drive_not_busy(ide_drive_t *drive) * take up to 6 ms on some ATAPI devices, so we will wait max 10 ms. */ for (retries = 0; retries < 1000; retries++) { - stat = ide_read_status(drive); + stat = hwif->tp_ops->read_status(hwif); if (stat & BUSY_STAT) udelay(10); @@ -255,9 +273,9 @@ static void ide_pio_sector(ide_drive_t *drive, struct request *rq, /* do the actual data transfer */ if (write) - hwif->output_data(drive, rq, buf, SECTOR_SIZE); + hwif->tp_ops->output_data(drive, rq, buf, SECTOR_SIZE); else - hwif->input_data(drive, rq, buf, SECTOR_SIZE); + hwif->tp_ops->input_data(drive, rq, buf, SECTOR_SIZE); kunmap_atomic(buf, KM_BIO_SRC_IRQ); #ifdef CONFIG_HIGHMEM @@ -383,8 +401,8 @@ static ide_startstop_t task_in_unexpected(ide_drive_t *drive, struct request *rq static ide_startstop_t task_in_intr(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - struct request *rq = HWGROUP(drive)->rq; - u8 stat = ide_read_status(drive); + struct request *rq = hwif->hwgroup->rq; + u8 stat = hwif->tp_ops->read_status(hwif); /* Error? */ if (stat & ERR_STAT) @@ -418,7 +436,7 @@ static ide_startstop_t task_out_intr (ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; struct request *rq = HWGROUP(drive)->rq; - u8 stat = ide_read_status(drive); + u8 stat = hwif->tp_ops->read_status(hwif); if (!OK_STAT(stat, DRIVE_READY, drive->bad_wstat)) return task_error(drive, rq, __func__, stat); diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index d4a6b102a772..772451600e4d 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -1,6 +1,6 @@ /* * Copyright (C) 1994-1998 Linus Torvalds & authors (see below) - * Copyrifht (C) 2003-2005, 2007 Bartlomiej Zolnierkiewicz + * Copyright (C) 2003-2005, 2007 Bartlomiej Zolnierkiewicz */ /* @@ -101,8 +101,7 @@ void ide_init_port_data(ide_hwif_t *hwif, unsigned int index) init_completion(&hwif->gendev_rel_comp); - default_hwif_iops(hwif); - default_hwif_transport(hwif); + hwif->tp_ops = &default_tp_ops; ide_port_init_devices_data(hwif); } @@ -134,41 +133,6 @@ static void ide_port_init_devices_data(ide_hwif_t *hwif) } } -void ide_remove_port_from_hwgroup(ide_hwif_t *hwif) -{ - ide_hwgroup_t *hwgroup = hwif->hwgroup; - - spin_lock_irq(&ide_lock); - /* - * Remove us from the hwgroup, and free - * the hwgroup if we were the only member - */ - if (hwif->next == hwif) { - BUG_ON(hwgroup->hwif != hwif); - kfree(hwgroup); - } else { - /* There is another interface in hwgroup. - * Unlink us, and set hwgroup->drive and ->hwif to - * something sane. - */ - ide_hwif_t *g = hwgroup->hwif; - - while (g->next != hwif) - g = g->next; - g->next = hwif->next; - if (hwgroup->hwif == hwif) { - /* Chose a random hwif for hwgroup->hwif. - * It's guaranteed that there are no drives - * left in the hwgroup. - */ - BUG_ON(hwgroup->drive != NULL); - hwgroup->hwif = g; - } - BUG_ON(hwgroup->hwif == hwif); - } - spin_unlock_irq(&ide_lock); -} - /* Called with ide_lock held. */ static void __ide_port_unregister_devices(ide_hwif_t *hwif) { @@ -269,16 +233,9 @@ void ide_unregister(ide_hwif_t *hwif) if (hwif->dma_base) ide_release_dma_engine(hwif); - spin_lock_irq(&ide_lock); - /* restore hwif data to pristine status */ - ide_init_port_data(hwif, hwif->index); - spin_unlock_irq(&ide_lock); - mutex_unlock(&ide_cfg_mtx); } -EXPORT_SYMBOL(ide_unregister); - void ide_init_port_hw(ide_hwif_t *hwif, hw_regs_t *hw) { memcpy(&hwif->io_ports, &hw->io_ports, sizeof(hwif->io_ports)); @@ -287,8 +244,8 @@ void ide_init_port_hw(ide_hwif_t *hwif, hw_regs_t *hw) hwif->dev = hw->dev; hwif->gendev.parent = hw->parent ? hw->parent : hw->dev; hwif->ack_intr = hw->ack_intr; + hwif->config_data = hw->config; } -EXPORT_SYMBOL_GPL(ide_init_port_hw); /* * Locks for IDE setting functionality @@ -661,6 +618,53 @@ set_val: EXPORT_SYMBOL(generic_ide_ioctl); +/** + * ide_device_get - get an additional reference to a ide_drive_t + * @drive: device to get a reference to + * + * Gets a reference to the ide_drive_t and increments the use count of the + * underlying LLDD module. + */ +int ide_device_get(ide_drive_t *drive) +{ + struct device *host_dev; + struct module *module; + + if (!get_device(&drive->gendev)) + return -ENXIO; + + host_dev = drive->hwif->host->dev[0]; + module = host_dev ? host_dev->driver->owner : NULL; + + if (module && !try_module_get(module)) { + put_device(&drive->gendev); + return -ENXIO; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ide_device_get); + +/** + * ide_device_put - release a reference to a ide_drive_t + * @drive: device to release a reference on + * + * Release a reference to the ide_drive_t and decrements the use count of + * the underlying LLDD module. + */ +void ide_device_put(ide_drive_t *drive) +{ +#ifdef CONFIG_MODULE_UNLOAD + struct device *host_dev = drive->hwif->host->dev[0]; + struct module *module = host_dev ? host_dev->driver->owner : NULL; + + if (module) + module_put(module); +#endif + put_device(&drive->gendev); +} +EXPORT_SYMBOL_GPL(ide_device_put); + static int ide_bus_match(struct device *dev, struct device_driver *drv) { return 1; diff --git a/drivers/ide/legacy/buddha.c b/drivers/ide/legacy/buddha.c index 0497e7f85b09..7c2afa97f417 100644 --- a/drivers/ide/legacy/buddha.c +++ b/drivers/ide/legacy/buddha.c @@ -37,6 +37,8 @@ #define CATWEASEL_NUM_HWIFS 3 #define XSURF_NUM_HWIFS 2 +#define MAX_NUM_HWIFS 3 + /* * Bases of the IDE interfaces (relative to the board address) */ @@ -148,18 +150,14 @@ static void __init buddha_setup_ports(hw_regs_t *hw, unsigned long base, static int __init buddha_init(void) { - hw_regs_t hw; - ide_hwif_t *hwif; - int i; - struct zorro_dev *z = NULL; u_long buddha_board = 0; BuddhaType type; - int buddha_num_hwifs; + int buddha_num_hwifs, i; while ((z = zorro_find_device(ZORRO_WILDCARD, z))) { unsigned long board; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + hw_regs_t hw[MAX_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL }; if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA) { buddha_num_hwifs = BUDDHA_NUM_HWIFS; @@ -221,19 +219,13 @@ fail_base2: ack_intr = xsurf_ack_intr; } - buddha_setup_ports(&hw, base, ctl, irq_port, ack_intr); + buddha_setup_ports(&hw[i], base, ctl, irq_port, + ack_intr); - hwif = ide_find_port(); - if (hwif) { - u8 index = hwif->index; - - ide_init_port_hw(hwif, &hw); - - idx[i] = index; - } + hws[i] = &hw[i]; } - ide_device_add(idx, NULL); + ide_host_add(NULL, hws, NULL); } return 0; diff --git a/drivers/ide/legacy/falconide.c b/drivers/ide/legacy/falconide.c index 129a812bb57f..724f95073d80 100644 --- a/drivers/ide/legacy/falconide.c +++ b/drivers/ide/legacy/falconide.c @@ -66,6 +66,27 @@ static void falconide_output_data(ide_drive_t *drive, struct request *rq, outsw_swapw(data_addr, buf, (len + 1) / 2); } +/* Atari has a byte-swapped IDE interface */ +static const struct ide_tp_ops falconide_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = falconide_input_data, + .output_data = falconide_output_data, +}; + +static const struct ide_port_info falconide_port_info = { + .tp_ops = &falconide_tp_ops, + .host_flags = IDE_HFLAG_NO_DMA, +}; + static void __init falconide_setup_ports(hw_regs_t *hw) { int i; @@ -91,11 +112,12 @@ static void __init falconide_setup_ports(hw_regs_t *hw) static int __init falconide_init(void) { - hw_regs_t hw; - ide_hwif_t *hwif; + struct ide_host *host; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; + int rc; if (!MACH_IS_ATARI || !ATARIHW_PRESENT(IDE)) - return 0; + return -ENODEV; printk(KERN_INFO "ide: Falcon IDE controller\n"); @@ -106,23 +128,25 @@ static int __init falconide_init(void) falconide_setup_ports(&hw); - hwif = ide_find_port(); - if (hwif) { - u8 index = hwif->index; - u8 idx[4] = { index, 0xff, 0xff, 0xff }; - - ide_init_port_hw(hwif, &hw); + host = ide_host_alloc(&falconide_port_info, hws); + if (host == NULL) { + rc = -ENOMEM; + goto err; + } - /* Atari has a byte-swapped IDE interface */ - hwif->input_data = falconide_input_data; - hwif->output_data = falconide_output_data; + ide_get_lock(NULL, NULL); + rc = ide_host_register(host, &falconide_port_info, hws); + ide_release_lock(); - ide_get_lock(NULL, NULL); - ide_device_add(idx, NULL); - ide_release_lock(); - } + if (rc) + goto err_free; return 0; +err_free: + ide_host_free(host); +err: + release_mem_region(ATA_HD_BASE, 0x40); + return rc; } module_init(falconide_init); diff --git a/drivers/ide/legacy/gayle.c b/drivers/ide/legacy/gayle.c index 7e74b20202df..51ba085d7aa8 100644 --- a/drivers/ide/legacy/gayle.c +++ b/drivers/ide/legacy/gayle.c @@ -31,6 +31,8 @@ #define GAYLE_BASE_4000 0xdd2020 /* A4000/A4000T */ #define GAYLE_BASE_1200 0xda0000 /* A1200/A600 and E-Matrix 530 */ +#define GAYLE_IDEREG_SIZE 0x2000 + /* * Offsets from one of the above bases */ @@ -56,13 +58,11 @@ #define GAYLE_NUM_HWIFS 1 #define GAYLE_NUM_PROBE_HWIFS GAYLE_NUM_HWIFS #define GAYLE_HAS_CONTROL_REG 1 -#define GAYLE_IDEREG_SIZE 0x2000 #else /* CONFIG_BLK_DEV_IDEDOUBLER */ #define GAYLE_NUM_HWIFS 2 #define GAYLE_NUM_PROBE_HWIFS (ide_doubler ? GAYLE_NUM_HWIFS : \ GAYLE_NUM_HWIFS-1) #define GAYLE_HAS_CONTROL_REG (!ide_doubler) -#define GAYLE_IDEREG_SIZE (ide_doubler ? 0x1000 : 0x2000) static int ide_doubler; module_param_named(doubler, ide_doubler, bool, 0); @@ -124,8 +124,11 @@ static void __init gayle_setup_ports(hw_regs_t *hw, unsigned long base, static int __init gayle_init(void) { - int a4000, i; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + unsigned long phys_base, res_start, res_n; + unsigned long base, ctrlport, irqport; + ide_ack_intr_t *ack_intr; + int a4000, i, rc; + hw_regs_t hw[GAYLE_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL }; if (!MACH_IS_AMIGA) return -ENODEV; @@ -148,13 +151,6 @@ found: #endif ""); - for (i = 0; i < GAYLE_NUM_PROBE_HWIFS; i++) { - unsigned long base, ctrlport, irqport; - ide_ack_intr_t *ack_intr; - hw_regs_t hw; - ide_hwif_t *hwif; - unsigned long phys_base, res_start, res_n; - if (a4000) { phys_base = GAYLE_BASE_4000; irqport = (unsigned long)ZTWO_VADDR(GAYLE_IRQ_4000); @@ -168,33 +164,26 @@ found: * FIXME: we now have selectable modes between mmio v/s iomio */ - phys_base += i*GAYLE_NEXT_PORT; - res_start = ((unsigned long)phys_base) & ~(GAYLE_NEXT_PORT-1); res_n = GAYLE_IDEREG_SIZE; if (!request_mem_region(res_start, res_n, "IDE")) - continue; + return -EBUSY; - base = (unsigned long)ZTWO_VADDR(phys_base); + for (i = 0; i < GAYLE_NUM_PROBE_HWIFS; i++) { + base = (unsigned long)ZTWO_VADDR(phys_base + i * GAYLE_NEXT_PORT); ctrlport = GAYLE_HAS_CONTROL_REG ? (base + GAYLE_CONTROL) : 0; - gayle_setup_ports(&hw, base, ctrlport, irqport, ack_intr); - - hwif = ide_find_port(); - if (hwif) { - u8 index = hwif->index; - - ide_init_port_hw(hwif, &hw); + gayle_setup_ports(&hw[i], base, ctrlport, irqport, ack_intr); - idx[i] = index; - } else - release_mem_region(res_start, res_n); + hws[i] = &hw[i]; } - ide_device_add(idx, NULL); + rc = ide_host_add(NULL, hws, NULL); + if (rc) + release_mem_region(res_start, res_n); - return 0; + return rc; } module_init(gayle_init); diff --git a/drivers/ide/legacy/ht6560b.c b/drivers/ide/legacy/ht6560b.c index 7bc8fd59ea9e..98f7c95e39ed 100644 --- a/drivers/ide/legacy/ht6560b.c +++ b/drivers/ide/legacy/ht6560b.c @@ -3,34 +3,12 @@ */ /* - * - * Version 0.01 Initial version hacked out of ide.c - * - * Version 0.02 Added support for PIO modes, auto-tune - * - * Version 0.03 Some cleanups - * - * Version 0.05 PIO mode cycle timings auto-tune using bus-speed - * - * Version 0.06 Prefetch mode now defaults no OFF. To set - * prefetch mode OFF/ON use "hdparm -p8/-p9". - * Unmask irq is disabled when prefetch mode - * is enabled. - * - * Version 0.07 Trying to fix CD-ROM detection problem. - * "Prefetch" mode bit OFF for ide disks and - * ON for anything else. - * - * Version 0.08 Need to force prefetch for CDs and other non-disk - * devices. (not sure which devices exactly need - * prefetch) - * * HT-6560B EIDE-controller support * To activate controller support use kernel parameter "ide0=ht6560b". * Use hdparm utility to enable PIO mode support. * * Author: Mikko Ala-Fossi <maf@iki.fi> - * Jan Evert van Grootheest <janevert@caiway.nl> + * Jan Evert van Grootheest <j.e.van.grootheest@caiway.nl> * * Try: http://www.maf.iki.fi/~maf/ht6560b/ */ diff --git a/drivers/ide/legacy/ide-4drives.c b/drivers/ide/legacy/ide-4drives.c index 89c8ff0a4d08..c76d55de6996 100644 --- a/drivers/ide/legacy/ide-4drives.c +++ b/drivers/ide/legacy/ide-4drives.c @@ -28,10 +28,8 @@ static const struct ide_port_info ide_4drives_port_info = { static int __init ide_4drives_init(void) { - ide_hwif_t *hwif, *mate; unsigned long base = 0x1f0, ctl = 0x3f6; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw; + hw_regs_t hw, *hws[] = { &hw, &hw, NULL, NULL }; if (probe_4drives == 0) return -ENODEV; @@ -55,21 +53,7 @@ static int __init ide_4drives_init(void) hw.irq = 14; hw.chipset = ide_4drives; - hwif = ide_find_port(); - if (hwif) { - ide_init_port_hw(hwif, &hw); - idx[0] = hwif->index; - } - - mate = ide_find_port(); - if (mate) { - ide_init_port_hw(mate, &hw); - idx[1] = mate->index; - } - - ide_device_add(idx, &ide_4drives_port_info); - - return 0; + return ide_host_add(&ide_4drives_port_info, hws, NULL); } module_init(ide_4drives_init); diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c index 27b1e0b7ecb4..21bfac137844 100644 --- a/drivers/ide/legacy/ide-cs.c +++ b/drivers/ide/legacy/ide-cs.c @@ -74,7 +74,7 @@ INT_MODULE_PARM(pc_debug, 0); typedef struct ide_info_t { struct pcmcia_device *p_dev; - ide_hwif_t *hwif; + struct ide_host *host; int ndev; dev_node_t node; } ide_info_t; @@ -132,7 +132,7 @@ static int ide_probe(struct pcmcia_device *link) static void ide_detach(struct pcmcia_device *link) { ide_info_t *info = link->priv; - ide_hwif_t *hwif = info->hwif; + ide_hwif_t *hwif = info->host->ports[0]; unsigned long data_addr, ctl_addr; DEBUG(0, "ide_detach(0x%p)\n", link); @@ -157,13 +157,13 @@ static const struct ide_port_info idecs_port_info = { .host_flags = IDE_HFLAG_NO_DMA, }; -static ide_hwif_t *idecs_register(unsigned long io, unsigned long ctl, +static struct ide_host *idecs_register(unsigned long io, unsigned long ctl, unsigned long irq, struct pcmcia_device *handle) { + struct ide_host *host; ide_hwif_t *hwif; - hw_regs_t hw; - int i; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + int i, rc; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; if (!request_region(io, 8, DRV_NAME)) { printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n", @@ -184,30 +184,24 @@ static ide_hwif_t *idecs_register(unsigned long io, unsigned long ctl, hw.chipset = ide_pci; hw.dev = &handle->dev; - hwif = ide_find_port(); - if (hwif == NULL) + rc = ide_host_add(&idecs_port_info, hws, &host); + if (rc) goto out_release; - i = hwif->index; - - ide_init_port_hw(hwif, &hw); - - idx[0] = i; - - ide_device_add(idx, &idecs_port_info); + hwif = host->ports[0]; if (hwif->present) - return hwif; + return host; /* retry registration in case device is still spinning up */ for (i = 0; i < 10; i++) { msleep(100); ide_port_scan(hwif); if (hwif->present) - return hwif; + return host; } - return hwif; + return host; out_release: release_region(ctl, 1); @@ -239,7 +233,7 @@ static int ide_config(struct pcmcia_device *link) cistpl_cftable_entry_t *cfg; int pass, last_ret = 0, last_fn = 0, is_kme = 0; unsigned long io_base, ctl_base; - ide_hwif_t *hwif; + struct ide_host *host; DEBUG(0, "ide_config(0x%p)\n", link); @@ -334,21 +328,21 @@ static int ide_config(struct pcmcia_device *link) if (is_kme) outb(0x81, ctl_base+1); - hwif = idecs_register(io_base, ctl_base, link->irq.AssignedIRQ, link); - if (hwif == NULL && link->io.NumPorts1 == 0x20) { + host = idecs_register(io_base, ctl_base, link->irq.AssignedIRQ, link); + if (host == NULL && link->io.NumPorts1 == 0x20) { outb(0x02, ctl_base + 0x10); - hwif = idecs_register(io_base + 0x10, ctl_base + 0x10, + host = idecs_register(io_base + 0x10, ctl_base + 0x10, link->irq.AssignedIRQ, link); } - if (hwif == NULL) + if (host == NULL) goto failed; info->ndev = 1; - sprintf(info->node.dev_name, "hd%c", 'a' + hwif->index * 2); - info->node.major = hwif->major; + sprintf(info->node.dev_name, "hd%c", 'a' + host->ports[0]->index * 2); + info->node.major = host->ports[0]->major; info->node.minor = 0; - info->hwif = hwif; + info->host = host; link->dev_node = &info->node; printk(KERN_INFO "ide-cs: %s: Vpp = %d.%d\n", info->node.dev_name, link->conf.Vpp / 10, link->conf.Vpp % 10); @@ -379,15 +373,15 @@ failed: static void ide_release(struct pcmcia_device *link) { ide_info_t *info = link->priv; - ide_hwif_t *hwif = info->hwif; + struct ide_host *host = info->host; DEBUG(0, "ide_release(0x%p)\n", link); - if (info->ndev) { + if (info->ndev) /* FIXME: if this fails we need to queue the cleanup somehow -- need to investigate the required PCMCIA magic */ - ide_unregister(hwif); - } + ide_host_remove(host); + info->ndev = 0; pcmcia_disable_device(link); diff --git a/drivers/ide/legacy/ide_platform.c b/drivers/ide/legacy/ide_platform.c index a249562b34b5..051b4ab0f359 100644 --- a/drivers/ide/legacy/ide_platform.c +++ b/drivers/ide/legacy/ide_platform.c @@ -52,12 +52,10 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) { struct resource *res_base, *res_alt, *res_irq; void __iomem *base, *alt_base; - ide_hwif_t *hwif; struct pata_platform_info *pdata; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - int ret = 0; - int mmio = 0; - hw_regs_t hw; + struct ide_host *host; + int ret = 0, mmio = 0; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; struct ide_port_info d = platform_ide_port_info; pdata = pdev->dev.platform_data; @@ -94,28 +92,18 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) res_alt->start, res_alt->end - res_alt->start + 1); } - hwif = ide_find_port(); - if (!hwif) { - ret = -ENODEV; - goto out; - } - memset(&hw, 0, sizeof(hw)); plat_ide_setup_ports(&hw, base, alt_base, pdata, res_irq->start); hw.dev = &pdev->dev; - ide_init_port_hw(hwif, &hw); - - if (mmio) { + if (mmio) d.host_flags |= IDE_HFLAG_MMIO; - default_hwif_mmiops(hwif); - } - idx[0] = hwif->index; - - ide_device_add(idx, &d); + ret = ide_host_add(&d, hws, &host); + if (ret) + goto out; - platform_set_drvdata(pdev, hwif); + platform_set_drvdata(pdev, host); return 0; @@ -125,9 +113,9 @@ out: static int __devexit plat_ide_remove(struct platform_device *pdev) { - ide_hwif_t *hwif = pdev->dev.driver_data; + struct ide_host *host = pdev->dev.driver_data; - ide_unregister(hwif); + ide_host_remove(host); return 0; } diff --git a/drivers/ide/legacy/macide.c b/drivers/ide/legacy/macide.c index 0a6195bcfeda..a0bb167980e7 100644 --- a/drivers/ide/legacy/macide.c +++ b/drivers/ide/legacy/macide.c @@ -91,11 +91,10 @@ static const char *mac_ide_name[] = static int __init macide_init(void) { - ide_hwif_t *hwif; ide_ack_intr_t *ack_intr; unsigned long base; int irq; - hw_regs_t hw; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; if (!MACH_IS_MAC) return -ENODEV; @@ -125,17 +124,7 @@ static int __init macide_init(void) macide_setup_ports(&hw, base, irq, ack_intr); - hwif = ide_find_port(); - if (hwif) { - u8 index = hwif->index; - u8 idx[4] = { index, 0xff, 0xff, 0xff }; - - ide_init_port_hw(hwif, &hw); - - ide_device_add(idx, NULL); - } - - return 0; + return ide_host_add(NULL, hws, NULL); } module_init(macide_init); diff --git a/drivers/ide/legacy/q40ide.c b/drivers/ide/legacy/q40ide.c index 9c2b9d078f69..4abd8fc78197 100644 --- a/drivers/ide/legacy/q40ide.c +++ b/drivers/ide/legacy/q40ide.c @@ -96,6 +96,27 @@ static void q40ide_output_data(ide_drive_t *drive, struct request *rq, outsw_swapw(data_addr, buf, (len + 1) / 2); } +/* Q40 has a byte-swapped IDE interface */ +static const struct ide_tp_ops q40ide_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = q40ide_input_data, + .output_data = q40ide_output_data, +}; + +static const struct ide_port_info q40ide_port_info = { + .tp_ops = &q40ide_tp_ops, + .host_flags = IDE_HFLAG_NO_DMA, +}; + /* * the static array is needed to have the name reported in /proc/ioports, * hwif->name unfortunately isn't available yet @@ -111,9 +132,7 @@ static const char *q40_ide_names[Q40IDE_NUM_HWIFS]={ static int __init q40ide_init(void) { int i; - ide_hwif_t *hwif; - const char *name; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + hw_regs_t hw[Q40IDE_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL }; if (!MACH_IS_Q40) return -ENODEV; @@ -121,9 +140,8 @@ static int __init q40ide_init(void) printk(KERN_INFO "ide: Q40 IDE controller\n"); for (i = 0; i < Q40IDE_NUM_HWIFS; i++) { - hw_regs_t hw; + const char *name = q40_ide_names[i]; - name = q40_ide_names[i]; if (!request_region(pcide_bases[i], 8, name)) { printk("could not reserve ports %lx-%lx for %s\n", pcide_bases[i],pcide_bases[i]+8,name); @@ -135,26 +153,13 @@ static int __init q40ide_init(void) release_region(pcide_bases[i], 8); continue; } - q40_ide_setup_ports(&hw, pcide_bases[i], - NULL, -// m68kide_iops, + q40_ide_setup_ports(&hw[i], pcide_bases[i], NULL, q40ide_default_irq(pcide_bases[i])); - hwif = ide_find_port(); - if (hwif) { - ide_init_port_hw(hwif, &hw); - - /* Q40 has a byte-swapped IDE interface */ - hwif->input_data = q40ide_input_data; - hwif->output_data = q40ide_output_data; - - idx[i] = hwif->index; - } + hws[i] = &hw[i]; } - ide_device_add(idx, NULL); - - return 0; + return ide_host_add(&q40ide_port_info, hws, NULL); } module_init(q40ide_init); diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c index 48d57cae63c6..11b7f61aae40 100644 --- a/drivers/ide/mips/au1xxx-ide.c +++ b/drivers/ide/mips/au1xxx-ide.c @@ -519,6 +519,23 @@ static void auide_setup_ports(hw_regs_t *hw, _auide_hwif *ahwif) *ata_regs = ahwif->regbase + (14 << IDE_REG_SHIFT); } +#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA +static const struct ide_tp_ops au1xxx_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = au1xxx_input_data, + .output_data = au1xxx_output_data, +}; +#endif + static const struct ide_port_ops au1xxx_port_ops = { .set_pio_mode = au1xxx_set_pio_mode, .set_dma_mode = auide_set_dma_mode, @@ -526,6 +543,9 @@ static const struct ide_port_ops au1xxx_port_ops = { static const struct ide_port_info au1xxx_port_info = { .init_dma = auide_ddma_init, +#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA + .tp_ops = &au1xxx_tp_ops, +#endif .port_ops = &au1xxx_port_ops, #ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA .dma_ops = &au1xxx_dma_ops, @@ -543,11 +563,10 @@ static int au_ide_probe(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); _auide_hwif *ahwif = &auide_hwif; - ide_hwif_t *hwif; struct resource *res; + struct ide_host *host; int ret = 0; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; #if defined(CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA) char *mode = "MWDMA2"; @@ -584,36 +603,19 @@ static int au_ide_probe(struct device *dev) goto out; } - hwif = ide_find_port(); - if (hwif == NULL) { - ret = -ENOENT; - goto out; - } - memset(&hw, 0, sizeof(hw)); auide_setup_ports(&hw, ahwif); hw.irq = ahwif->irq; hw.dev = dev; hw.chipset = ide_au1xxx; - ide_init_port_hw(hwif, &hw); - - /* If the user has selected DDMA assisted copies, - then set up a few local I/O function entry points - */ - -#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA - hwif->input_data = au1xxx_input_data; - hwif->output_data = au1xxx_output_data; -#endif - - auide_hwif.hwif = hwif; - - idx[0] = hwif->index; + ret = ide_host_add(&au1xxx_port_info, hws, &host); + if (ret) + goto out; - ide_device_add(idx, &au1xxx_port_info); + auide_hwif.hwif = host->ports[0]; - dev_set_drvdata(dev, hwif); + dev_set_drvdata(dev, host); printk(KERN_INFO "Au1xxx IDE(builtin) configured for %s\n", mode ); @@ -625,10 +627,10 @@ static int au_ide_remove(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct resource *res; - ide_hwif_t *hwif = dev_get_drvdata(dev); + struct ide_host *host = dev_get_drvdata(dev); _auide_hwif *ahwif = &auide_hwif; - ide_unregister(hwif); + ide_host_remove(host); iounmap((void *)ahwif->regbase); diff --git a/drivers/ide/mips/swarm.c b/drivers/ide/mips/swarm.c index 9f1212cc4aed..badf79fc9e3a 100644 --- a/drivers/ide/mips/swarm.c +++ b/drivers/ide/mips/swarm.c @@ -72,12 +72,11 @@ static const struct ide_port_info swarm_port_info = { */ static int __devinit swarm_ide_probe(struct device *dev) { - ide_hwif_t *hwif; u8 __iomem *base; + struct ide_host *host; phys_t offset, size; - hw_regs_t hw; - int i; - u8 idx[] = { 0xff, 0xff, 0xff, 0xff }; + int i, rc; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; if (!SIBYTE_HAVE_IDE) return -ENODEV; @@ -116,26 +115,17 @@ static int __devinit swarm_ide_probe(struct device *dev) hw.irq = K_INT_GB_IDE; hw.chipset = ide_generic; - hwif = ide_find_port_slot(&swarm_port_info); - if (hwif == NULL) + rc = ide_host_add(&swarm_port_info, hws, &host); + if (rc) goto err; - ide_init_port_hw(hwif, &hw); - - /* Setup MMIO ops. */ - default_hwif_mmiops(hwif); - - idx[0] = hwif->index; - - ide_device_add(idx, &swarm_port_info); - - dev_set_drvdata(dev, hwif); + dev_set_drvdata(dev, host); return 0; err: release_resource(&swarm_ide_resource); iounmap(base); - return -ENOMEM; + return rc; } static struct device_driver swarm_ide_driver = { diff --git a/drivers/ide/pci/aec62xx.c b/drivers/ide/pci/aec62xx.c index ae7a4329a581..e0c8fe7d9fea 100644 --- a/drivers/ide/pci/aec62xx.c +++ b/drivers/ide/pci/aec62xx.c @@ -13,6 +13,8 @@ #include <asm/io.h> +#define DRV_NAME "aec62xx" + struct chipset_bus_clock_list_entry { u8 xfer_speed; u8 chipset_settings; @@ -59,10 +61,6 @@ static const struct chipset_bus_clock_list_entry aec6xxx_34_base [] = { { 0, 0x00, 0x00 } }; -#define BUSCLOCK(D) \ - ((struct chipset_bus_clock_list_entry *) pci_get_drvdata((D))) - - /* * TO DO: active tuning and correction of cards without a bios. */ @@ -88,6 +86,8 @@ static void aec6210_set_mode(ide_drive_t *drive, const u8 speed) { ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = to_pci_dev(hwif->dev); + struct ide_host *host = pci_get_drvdata(dev); + struct chipset_bus_clock_list_entry *bus_clock = host->host_priv; u16 d_conf = 0; u8 ultra = 0, ultra_conf = 0; u8 tmp0 = 0, tmp1 = 0, tmp2 = 0; @@ -96,7 +96,7 @@ static void aec6210_set_mode(ide_drive_t *drive, const u8 speed) local_irq_save(flags); /* 0x40|(2*drive->dn): Active, 0x41|(2*drive->dn): Recovery */ pci_read_config_word(dev, 0x40|(2*drive->dn), &d_conf); - tmp0 = pci_bus_clock_list(speed, BUSCLOCK(dev)); + tmp0 = pci_bus_clock_list(speed, bus_clock); d_conf = ((tmp0 & 0xf0) << 4) | (tmp0 & 0xf); pci_write_config_word(dev, 0x40|(2*drive->dn), d_conf); @@ -104,7 +104,7 @@ static void aec6210_set_mode(ide_drive_t *drive, const u8 speed) tmp2 = 0x00; pci_read_config_byte(dev, 0x54, &ultra); tmp1 = ((0x00 << (2*drive->dn)) | (ultra & ~(3 << (2*drive->dn)))); - ultra_conf = pci_bus_clock_list_ultra(speed, BUSCLOCK(dev)); + ultra_conf = pci_bus_clock_list_ultra(speed, bus_clock); tmp2 = ((ultra_conf << (2*drive->dn)) | (tmp1 & ~(3 << (2*drive->dn)))); pci_write_config_byte(dev, 0x54, tmp2); local_irq_restore(flags); @@ -114,6 +114,8 @@ static void aec6260_set_mode(ide_drive_t *drive, const u8 speed) { ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = to_pci_dev(hwif->dev); + struct ide_host *host = pci_get_drvdata(dev); + struct chipset_bus_clock_list_entry *bus_clock = host->host_priv; u8 unit = (drive->select.b.unit & 0x01); u8 tmp1 = 0, tmp2 = 0; u8 ultra = 0, drive_conf = 0, ultra_conf = 0; @@ -122,12 +124,12 @@ static void aec6260_set_mode(ide_drive_t *drive, const u8 speed) local_irq_save(flags); /* high 4-bits: Active, low 4-bits: Recovery */ pci_read_config_byte(dev, 0x40|drive->dn, &drive_conf); - drive_conf = pci_bus_clock_list(speed, BUSCLOCK(dev)); + drive_conf = pci_bus_clock_list(speed, bus_clock); pci_write_config_byte(dev, 0x40|drive->dn, drive_conf); pci_read_config_byte(dev, (0x44|hwif->channel), &ultra); tmp1 = ((0x00 << (4*unit)) | (ultra & ~(7 << (4*unit)))); - ultra_conf = pci_bus_clock_list_ultra(speed, BUSCLOCK(dev)); + ultra_conf = pci_bus_clock_list_ultra(speed, bus_clock); tmp2 = ((ultra_conf << (4*unit)) | (tmp1 & ~(7 << (4*unit)))); pci_write_config_byte(dev, (0x44|hwif->channel), tmp2); local_irq_restore(flags); @@ -138,15 +140,8 @@ static void aec_set_pio_mode(ide_drive_t *drive, const u8 pio) drive->hwif->port_ops->set_dma_mode(drive, pio + XFER_PIO_0); } -static unsigned int __devinit init_chipset_aec62xx(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_aec62xx(struct pci_dev *dev) { - int bus_speed = ide_pci_clk ? ide_pci_clk : 33; - - if (bus_speed <= 33) - pci_set_drvdata(dev, (void *) aec6xxx_33_base); - else - pci_set_drvdata(dev, (void *) aec6xxx_34_base); - /* These are necessary to get AEC6280 Macintosh cards to work */ if ((dev->device == PCI_DEVICE_ID_ARTOP_ATP865) || (dev->device == PCI_DEVICE_ID_ARTOP_ATP865R)) { @@ -187,57 +182,56 @@ static const struct ide_port_ops atp86x_port_ops = { }; static const struct ide_port_info aec62xx_chipsets[] __devinitdata = { - { /* 0 */ - .name = "AEC6210", + { /* 0: AEC6210 */ + .name = DRV_NAME, .init_chipset = init_chipset_aec62xx, .enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, .port_ops = &atp850_port_ops, .host_flags = IDE_HFLAG_SERIALIZE | IDE_HFLAG_NO_ATAPI_DMA | IDE_HFLAG_NO_DSC | - IDE_HFLAG_ABUSE_SET_DMA_MODE | IDE_HFLAG_OFF_BOARD, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA2, - },{ /* 1 */ - .name = "AEC6260", + }, + { /* 1: AEC6260 */ + .name = DRV_NAME, .init_chipset = init_chipset_aec62xx, .port_ops = &atp86x_port_ops, .host_flags = IDE_HFLAG_NO_ATAPI_DMA | IDE_HFLAG_NO_AUTODMA | - IDE_HFLAG_ABUSE_SET_DMA_MODE | IDE_HFLAG_OFF_BOARD, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA4, - },{ /* 2 */ - .name = "AEC6260R", + }, + { /* 2: AEC6260R */ + .name = DRV_NAME, .init_chipset = init_chipset_aec62xx, .enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, .port_ops = &atp86x_port_ops, .host_flags = IDE_HFLAG_NO_ATAPI_DMA | - IDE_HFLAG_ABUSE_SET_DMA_MODE | IDE_HFLAG_NON_BOOTABLE, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA4, - },{ /* 3 */ - .name = "AEC6280", + }, + { /* 3: AEC6280 */ + .name = DRV_NAME, .init_chipset = init_chipset_aec62xx, .port_ops = &atp86x_port_ops, .host_flags = IDE_HFLAG_NO_ATAPI_DMA | - IDE_HFLAG_ABUSE_SET_DMA_MODE | IDE_HFLAG_OFF_BOARD, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA5, - },{ /* 4 */ - .name = "AEC6280R", + }, + { /* 4: AEC6280R */ + .name = DRV_NAME, .init_chipset = init_chipset_aec62xx, .enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, .port_ops = &atp86x_port_ops, .host_flags = IDE_HFLAG_NO_ATAPI_DMA | - IDE_HFLAG_ABUSE_SET_DMA_MODE | IDE_HFLAG_OFF_BOARD, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, @@ -259,10 +253,17 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = { static int __devinit aec62xx_init_one(struct pci_dev *dev, const struct pci_device_id *id) { + const struct chipset_bus_clock_list_entry *bus_clock; struct ide_port_info d; u8 idx = id->driver_data; + int bus_speed = ide_pci_clk ? ide_pci_clk : 33; int err; + if (bus_speed <= 33) + bus_clock = aec6xxx_33_base; + else + bus_clock = aec6xxx_34_base; + err = pci_enable_device(dev); if (err) return err; @@ -273,18 +274,25 @@ static int __devinit aec62xx_init_one(struct pci_dev *dev, const struct pci_devi unsigned long dma_base = pci_resource_start(dev, 4); if (inb(dma_base + 2) & 0x10) { - d.name = (idx == 4) ? "AEC6880R" : "AEC6880"; + printk(KERN_INFO DRV_NAME " %s: AEC6880%s card detected" + "\n", pci_name(dev), (idx == 4) ? "R" : ""); d.udma_mask = ATA_UDMA6; } } - err = ide_setup_pci_device(dev, &d); + err = ide_pci_init_one(dev, &d, (void *)bus_clock); if (err) pci_disable_device(dev); return err; } +static void __devexit aec62xx_remove(struct pci_dev *dev) +{ + ide_pci_remove(dev); + pci_disable_device(dev); +} + static const struct pci_device_id aec62xx_pci_tbl[] = { { PCI_VDEVICE(ARTOP, PCI_DEVICE_ID_ARTOP_ATP850UF), 0 }, { PCI_VDEVICE(ARTOP, PCI_DEVICE_ID_ARTOP_ATP860), 1 }, @@ -299,6 +307,7 @@ static struct pci_driver driver = { .name = "AEC62xx_IDE", .id_table = aec62xx_pci_tbl, .probe = aec62xx_init_one, + .remove = aec62xx_remove, }; static int __init aec62xx_ide_init(void) @@ -306,7 +315,13 @@ static int __init aec62xx_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit aec62xx_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(aec62xx_ide_init); +module_exit(aec62xx_ide_exit); MODULE_AUTHOR("Andre Hedrick"); MODULE_DESCRIPTION("PCI driver module for ARTOP AEC62xx IDE"); diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c index 80d19c0eb780..b582687e0cd4 100644 --- a/drivers/ide/pci/alim15x3.c +++ b/drivers/ide/pci/alim15x3.c @@ -38,6 +38,8 @@ #include <asm/io.h> +#define DRV_NAME "alim15x3" + /* * Allow UDMA on M1543C-E chipset for WDC disks that ignore CRC checking * (this is DANGEROUS and could result in data corruption). @@ -207,13 +209,12 @@ static int ali15x3_dma_setup(ide_drive_t *drive) /** * init_chipset_ali15x3 - Initialise an ALi IDE controller * @dev: PCI device - * @name: Name of the controller * * This function initializes the ALI IDE controller and where * appropriate also sets up the 1533 southbridge. */ - -static unsigned int __devinit init_chipset_ali15x3 (struct pci_dev *dev, const char *name) + +static unsigned int __devinit init_chipset_ali15x3(struct pci_dev *dev) { unsigned long flags; u8 tmpbyte; @@ -471,7 +472,15 @@ static int __devinit init_dma_ali15x3(ide_hwif_t *hwif, struct pci_dev *dev = to_pci_dev(hwif->dev); unsigned long base = ide_pci_dma_base(hwif, d); - if (base == 0 || ide_pci_set_master(dev, d->name) < 0) + if (base == 0) + return -1; + + hwif->dma_base = base; + + if (ide_pci_check_simplex(hwif, d) < 0) + return -1; + + if (ide_pci_set_master(dev, d->name) < 0) return -1; if (!hwif->channel) @@ -483,7 +492,7 @@ static int __devinit init_dma_ali15x3(ide_hwif_t *hwif, if (ide_allocate_dma_engine(hwif)) return -1; - ide_setup_dma(hwif, base); + hwif->dma_ops = &sff_dma_ops; return 0; } @@ -507,7 +516,7 @@ static const struct ide_dma_ops ali_dma_ops = { }; static const struct ide_port_info ali15x3_chipset __devinitdata = { - .name = "ALI15X3", + .name = DRV_NAME, .init_chipset = init_chipset_ali15x3, .init_hwif = init_hwif_ali15x3, .init_dma = init_dma_ali15x3, @@ -557,7 +566,7 @@ static int __devinit alim15x3_init_one(struct pci_dev *dev, const struct pci_dev if (idx == 0) d.host_flags |= IDE_HFLAG_CLEAR_SIMPLEX; - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } @@ -572,6 +581,7 @@ static struct pci_driver driver = { .name = "ALI15x3_IDE", .id_table = alim15x3_pci_tbl, .probe = alim15x3_init_one, + .remove = ide_pci_remove, }; static int __init ali15x3_ide_init(void) @@ -579,7 +589,13 @@ static int __init ali15x3_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit ali15x3_ide_exit(void) +{ + return pci_unregister_driver(&driver); +} + module_init(ali15x3_ide_init); +module_exit(ali15x3_ide_exit); MODULE_AUTHOR("Michael Aubry, Andrzej Krzysztofowicz, CJ, Andre Hedrick, Alan Cox"); MODULE_DESCRIPTION("PCI driver module for ALi 15x3 IDE"); diff --git a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c index 0bfcdd0e77b3..2cea7bf51a0f 100644 --- a/drivers/ide/pci/amd74xx.c +++ b/drivers/ide/pci/amd74xx.c @@ -21,6 +21,8 @@ #include <linux/init.h> #include <linux/ide.h> +#define DRV_NAME "amd74xx" + enum { AMD_IDE_CONFIG = 0x41, AMD_CABLE_DETECT = 0x42, @@ -110,15 +112,13 @@ static void amd_set_pio_mode(ide_drive_t *drive, const u8 pio) amd_set_drive(drive, XFER_PIO_0 + pio); } -static void __devinit amd7409_cable_detect(struct pci_dev *dev, - const char *name) +static void __devinit amd7409_cable_detect(struct pci_dev *dev) { /* no host side cable detection */ amd_80w = 0x03; } -static void __devinit amd7411_cable_detect(struct pci_dev *dev, - const char *name) +static void __devinit amd7411_cable_detect(struct pci_dev *dev) { int i; u32 u = 0; @@ -129,9 +129,9 @@ static void __devinit amd7411_cable_detect(struct pci_dev *dev, amd_80w = ((t & 0x3) ? 1 : 0) | ((t & 0xc) ? 2 : 0); for (i = 24; i >= 0; i -= 8) if (((u >> i) & 4) && !(amd_80w & (1 << (1 - (i >> 4))))) { - printk(KERN_WARNING "%s: BIOS didn't set cable bits " - "correctly. Enabling workaround.\n", - name); + printk(KERN_WARNING DRV_NAME " %s: BIOS didn't set " + "cable bits correctly. Enabling workaround.\n", + pci_name(dev)); amd_80w |= (1 << (1 - (i >> 4))); } } @@ -140,8 +140,7 @@ static void __devinit amd7411_cable_detect(struct pci_dev *dev, * The initialization callback. Initialize drive independent registers. */ -static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev, - const char *name) +static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev) { u8 t = 0, offset = amd_offset(dev); @@ -154,9 +153,9 @@ static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev, ; /* no UDMA > 2 */ else if (dev->vendor == PCI_VENDOR_ID_AMD && dev->device == PCI_DEVICE_ID_AMD_VIPER_7409) - amd7409_cable_detect(dev, name); + amd7409_cable_detect(dev); else - amd7411_cable_detect(dev, name); + amd7411_cable_detect(dev); /* * Take care of prefetch & postwrite. @@ -173,24 +172,6 @@ static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev, t |= 0xf0; pci_write_config_byte(dev, AMD_IDE_CONFIG + offset, t); -/* - * Determine the system bus clock. - */ - - amd_clock = (ide_pci_clk ? ide_pci_clk : 33) * 1000; - - switch (amd_clock) { - case 33000: amd_clock = 33333; break; - case 37000: amd_clock = 37500; break; - case 41000: amd_clock = 41666; break; - } - - if (amd_clock < 20000 || amd_clock > 50000) { - printk(KERN_WARNING "%s: User given PCI clock speed impossible (%d), using 33 MHz instead.\n", - name, amd_clock); - amd_clock = 33333; - } - return dev->irq; } @@ -218,14 +199,13 @@ static const struct ide_port_ops amd_port_ops = { #define IDE_HFLAGS_AMD \ (IDE_HFLAG_PIO_NO_BLACKLIST | \ - IDE_HFLAG_ABUSE_SET_DMA_MODE | \ IDE_HFLAG_POST_SET_MODE | \ IDE_HFLAG_IO_32BIT | \ IDE_HFLAG_UNMASK_IRQS) -#define DECLARE_AMD_DEV(name_str, swdma, udma) \ +#define DECLARE_AMD_DEV(swdma, udma) \ { \ - .name = name_str, \ + .name = DRV_NAME, \ .init_chipset = init_chipset_amd74xx, \ .init_hwif = init_hwif_amd74xx, \ .enablebits = {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, \ @@ -237,9 +217,9 @@ static const struct ide_port_ops amd_port_ops = { .udma_mask = udma, \ } -#define DECLARE_NV_DEV(name_str, udma) \ +#define DECLARE_NV_DEV(udma) \ { \ - .name = name_str, \ + .name = DRV_NAME, \ .init_chipset = init_chipset_amd74xx, \ .init_hwif = init_hwif_amd74xx, \ .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, \ @@ -252,31 +232,15 @@ static const struct ide_port_ops amd_port_ops = { } static const struct ide_port_info amd74xx_chipsets[] __devinitdata = { - /* 0 */ DECLARE_AMD_DEV("AMD7401", 0x00, ATA_UDMA2), - /* 1 */ DECLARE_AMD_DEV("AMD7409", ATA_SWDMA2, ATA_UDMA4), - /* 2 */ DECLARE_AMD_DEV("AMD7411", ATA_SWDMA2, ATA_UDMA5), - /* 3 */ DECLARE_AMD_DEV("AMD7441", ATA_SWDMA2, ATA_UDMA5), - /* 4 */ DECLARE_AMD_DEV("AMD8111", ATA_SWDMA2, ATA_UDMA6), - - /* 5 */ DECLARE_NV_DEV("NFORCE", ATA_UDMA5), - /* 6 */ DECLARE_NV_DEV("NFORCE2", ATA_UDMA6), - /* 7 */ DECLARE_NV_DEV("NFORCE2-U400R", ATA_UDMA6), - /* 8 */ DECLARE_NV_DEV("NFORCE2-U400R-SATA", ATA_UDMA6), - /* 9 */ DECLARE_NV_DEV("NFORCE3-150", ATA_UDMA6), - /* 10 */ DECLARE_NV_DEV("NFORCE3-250", ATA_UDMA6), - /* 11 */ DECLARE_NV_DEV("NFORCE3-250-SATA", ATA_UDMA6), - /* 12 */ DECLARE_NV_DEV("NFORCE3-250-SATA2", ATA_UDMA6), - /* 13 */ DECLARE_NV_DEV("NFORCE-CK804", ATA_UDMA6), - /* 14 */ DECLARE_NV_DEV("NFORCE-MCP04", ATA_UDMA6), - /* 15 */ DECLARE_NV_DEV("NFORCE-MCP51", ATA_UDMA6), - /* 16 */ DECLARE_NV_DEV("NFORCE-MCP55", ATA_UDMA6), - /* 17 */ DECLARE_NV_DEV("NFORCE-MCP61", ATA_UDMA6), - /* 18 */ DECLARE_NV_DEV("NFORCE-MCP65", ATA_UDMA6), - /* 19 */ DECLARE_NV_DEV("NFORCE-MCP67", ATA_UDMA6), - /* 20 */ DECLARE_NV_DEV("NFORCE-MCP73", ATA_UDMA6), - /* 21 */ DECLARE_NV_DEV("NFORCE-MCP77", ATA_UDMA6), - - /* 22 */ DECLARE_AMD_DEV("AMD5536", ATA_SWDMA2, ATA_UDMA5), + /* 0: AMD7401 */ DECLARE_AMD_DEV(0x00, ATA_UDMA2), + /* 1: AMD7409 */ DECLARE_AMD_DEV(ATA_SWDMA2, ATA_UDMA4), + /* 2: AMD7411/7441 */ DECLARE_AMD_DEV(ATA_SWDMA2, ATA_UDMA5), + /* 3: AMD8111 */ DECLARE_AMD_DEV(ATA_SWDMA2, ATA_UDMA6), + + /* 4: NFORCE */ DECLARE_NV_DEV(ATA_UDMA5), + /* 5: >= NFORCE2 */ DECLARE_NV_DEV(ATA_UDMA6), + + /* 6: AMD5536 */ DECLARE_AMD_DEV(ATA_SWDMA2, ATA_UDMA5), }; static int __devinit amd74xx_probe(struct pci_dev *dev, const struct pci_device_id *id) @@ -293,47 +257,64 @@ static int __devinit amd74xx_probe(struct pci_dev *dev, const struct pci_device_ if (dev->revision <= 7) d.swdma_mask = 0; d.host_flags |= IDE_HFLAG_CLEAR_SIMPLEX; - } else if (idx == 4) { + } else if (idx == 3) { if (dev->subsystem_vendor == PCI_VENDOR_ID_AMD && dev->subsystem_device == PCI_DEVICE_ID_AMD_SERENADE) d.udma_mask = ATA_UDMA5; } - printk(KERN_INFO "%s: %s (rev %02x) UDMA%s controller\n", - d.name, pci_name(dev), dev->revision, - amd_dma[fls(d.udma_mask) - 1]); + printk(KERN_INFO "%s %s: UDMA%s controller\n", + d.name, pci_name(dev), amd_dma[fls(d.udma_mask) - 1]); + + /* + * Determine the system bus clock. + */ + amd_clock = (ide_pci_clk ? ide_pci_clk : 33) * 1000; + + switch (amd_clock) { + case 33000: amd_clock = 33333; break; + case 37000: amd_clock = 37500; break; + case 41000: amd_clock = 41666; break; + } + + if (amd_clock < 20000 || amd_clock > 50000) { + printk(KERN_WARNING "%s: User given PCI clock speed impossible" + " (%d), using 33 MHz instead.\n", + d.name, amd_clock); + amd_clock = 33333; + } - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id amd74xx_pci_tbl[] = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_COBRA_7401), 0 }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_VIPER_7409), 1 }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_VIPER_7411), 2 }, - { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_OPUS_7441), 3 }, - { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_8111_IDE), 4 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_IDE), 5 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE), 6 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE), 7 }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_OPUS_7441), 2 }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_8111_IDE), 3 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_IDE), 4 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE), 5 }, #ifdef CONFIG_BLK_DEV_IDE_SATA - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA), 8 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA), 5 }, #endif - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE), 9 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE), 10 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE), 5 }, #ifdef CONFIG_BLK_DEV_IDE_SATA - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA), 11 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2), 12 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2), 5 }, #endif - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE), 13 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE), 14 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE), 15 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE), 16 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE), 17 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE), 18 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE), 19 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE), 20 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE), 21 }, - { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5536_IDE), 22 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE), 5 }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5536_IDE), 6 }, { 0, }, }; MODULE_DEVICE_TABLE(pci, amd74xx_pci_tbl); @@ -342,6 +323,7 @@ static struct pci_driver driver = { .name = "AMD_IDE", .id_table = amd74xx_pci_tbl, .probe = amd74xx_probe, + .remove = ide_pci_remove, }; static int __init amd74xx_ide_init(void) @@ -349,7 +331,13 @@ static int __init amd74xx_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit amd74xx_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(amd74xx_ide_init); +module_exit(amd74xx_ide_exit); MODULE_AUTHOR("Vojtech Pavlik"); MODULE_DESCRIPTION("AMD PCI IDE driver"); diff --git a/drivers/ide/pci/atiixp.c b/drivers/ide/pci/atiixp.c index 8b637181681a..332f08f43b56 100644 --- a/drivers/ide/pci/atiixp.c +++ b/drivers/ide/pci/atiixp.c @@ -11,6 +11,8 @@ #include <linux/ide.h> #include <linux/init.h> +#define DRV_NAME "atiixp" + #define ATIIXP_IDE_PIO_TIMING 0x40 #define ATIIXP_IDE_MDMA_TIMING 0x44 #define ATIIXP_IDE_PIO_CONTROL 0x48 @@ -137,16 +139,17 @@ static const struct ide_port_ops atiixp_port_ops = { }; static const struct ide_port_info atiixp_pci_info[] __devinitdata = { - { /* 0 */ - .name = "ATIIXP", + { /* 0: IXP200/300/400/700 */ + .name = DRV_NAME, .enablebits = {{0x48,0x01,0x00}, {0x48,0x08,0x00}}, .port_ops = &atiixp_port_ops, .host_flags = IDE_HFLAG_LEGACY_IRQS, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA5, - },{ /* 1 */ - .name = "SB600_PATA", + }, + { /* 1: IXP600 */ + .name = DRV_NAME, .enablebits = {{0x48,0x01,0x00}, {0x00,0x00,0x00}}, .port_ops = &atiixp_port_ops, .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_LEGACY_IRQS, @@ -167,7 +170,7 @@ static const struct ide_port_info atiixp_pci_info[] __devinitdata = { static int __devinit atiixp_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &atiixp_pci_info[id->driver_data]); + return ide_pci_init_one(dev, &atiixp_pci_info[id->driver_data], NULL); } static const struct pci_device_id atiixp_pci_tbl[] = { @@ -184,6 +187,7 @@ static struct pci_driver driver = { .name = "ATIIXP_IDE", .id_table = atiixp_pci_tbl, .probe = atiixp_init_one, + .remove = ide_pci_remove, }; static int __init atiixp_ide_init(void) @@ -191,7 +195,13 @@ static int __init atiixp_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit atiixp_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(atiixp_ide_init); +module_exit(atiixp_ide_exit); MODULE_AUTHOR("HUI YU"); MODULE_DESCRIPTION("PCI driver module for ATI IXP IDE"); diff --git a/drivers/ide/pci/cmd640.c b/drivers/ide/pci/cmd640.c index 1ad1e23e3105..e6c62006ca1a 100644 --- a/drivers/ide/pci/cmd640.c +++ b/drivers/ide/pci/cmd640.c @@ -181,11 +181,6 @@ static u8 recovery_counts[4] = {16, 16, 16, 16}; /* Recovery count (encoded) */ static DEFINE_SPINLOCK(cmd640_lock); /* - * These are initialized to point at the devices we control - */ -static ide_hwif_t *cmd_hwif0, *cmd_hwif1; - -/* * Interface to access cmd640x registers */ static unsigned int cmd640_key; @@ -717,8 +712,7 @@ static int __init cmd640x_init(void) int second_port_cmd640 = 0, rc; const char *bus_type, *port2; u8 b, cfr; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw[2]; + hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL }; if (cmd640_vlb && probe_for_cmd640_vlb()) { bus_type = "VLB"; @@ -781,15 +775,10 @@ static int __init cmd640x_init(void) printk(KERN_INFO "cmd640: buggy cmd640%c interface on %s, config=0x%02x" "\n", 'a' + cmd640_chip_version - 1, bus_type, cfr); - cmd_hwif0 = ide_find_port(); - /* * Initialize data for primary port */ - if (cmd_hwif0) { - ide_init_port_hw(cmd_hwif0, &hw[0]); - idx[0] = cmd_hwif0->index; - } + hws[0] = &hw[0]; /* * Ensure compatibility by always using the slowest timings @@ -829,13 +818,9 @@ static int __init cmd640x_init(void) /* * Initialize data for secondary cmd640 port, if enabled */ - if (second_port_cmd640) { - cmd_hwif1 = ide_find_port(); - if (cmd_hwif1) { - ide_init_port_hw(cmd_hwif1, &hw[1]); - idx[1] = cmd_hwif1->index; - } - } + if (second_port_cmd640) + hws[1] = &hw[1]; + printk(KERN_INFO "cmd640: %sserialized, secondary interface %s\n", second_port_cmd640 ? "" : "not ", port2); @@ -843,9 +828,7 @@ static int __init cmd640x_init(void) cmd640_dump_regs(); #endif - ide_device_add(idx, &cmd640_port_info); - - return 1; + return ide_host_add(&cmd640_port_info, hws, NULL); } module_param_named(probe_vlb, cmd640_vlb, bool, 0); diff --git a/drivers/ide/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c index cfa784bacf48..1360b4fa9fd3 100644 --- a/drivers/ide/pci/cmd64x.c +++ b/drivers/ide/pci/cmd64x.c @@ -19,6 +19,8 @@ #include <asm/io.h> +#define DRV_NAME "cmd64x" + #define CMD_DEBUG 0 #if CMD_DEBUG @@ -262,7 +264,7 @@ static int cmd648_dma_test_irq(ide_drive_t *drive) unsigned long base = hwif->dma_base - (hwif->channel * 8); u8 irq_mask = hwif->channel ? MRDMODE_INTR_CH1 : MRDMODE_INTR_CH0; - u8 dma_stat = inb(hwif->dma_status); + u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); u8 mrdmode = inb(base + 1); #ifdef DEBUG @@ -286,7 +288,7 @@ static int cmd64x_dma_test_irq(ide_drive_t *drive) int irq_reg = hwif->channel ? ARTTIM23 : CFR; u8 irq_mask = hwif->channel ? ARTTIM23_INTR_CH1 : CFR_INTR_CH0; - u8 dma_stat = inb(hwif->dma_status); + u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); u8 irq_stat = 0; (void) pci_read_config_byte(dev, irq_reg, &irq_stat); @@ -317,41 +319,23 @@ static int cmd646_1_dma_end(ide_drive_t *drive) drive->waiting_for_dma = 0; /* get DMA status */ - dma_stat = inb(hwif->dma_status); + dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); /* read DMA command state */ - dma_cmd = inb(hwif->dma_command); + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); /* stop DMA */ - outb(dma_cmd & ~1, hwif->dma_command); + outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD); /* clear the INTR & ERROR bits */ - outb(dma_stat | 6, hwif->dma_status); + outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS); /* and free any DMA resources */ ide_destroy_dmatable(drive); /* verify good DMA status */ return (dma_stat & 7) != 4; } -static unsigned int __devinit init_chipset_cmd64x(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_cmd64x(struct pci_dev *dev) { u8 mrdmode = 0; - if (dev->device == PCI_DEVICE_ID_CMD_646) { - - switch (dev->revision) { - case 0x07: - case 0x05: - printk("%s: UltraDMA capable\n", name); - break; - case 0x03: - default: - printk("%s: MultiWord DMA force limited\n", name); - break; - case 0x01: - printk("%s: MultiWord DMA limited, " - "IRQ workaround enabled\n", name); - break; - } - } - /* Set a good latency timer and cache line size value. */ (void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); /* FIXME: pci_set_master() to ensure a good latency timer value */ @@ -425,8 +409,8 @@ static const struct ide_dma_ops cmd648_dma_ops = { }; static const struct ide_port_info cmd64x_chipsets[] __devinitdata = { - { /* 0 */ - .name = "CMD643", + { /* 0: CMD643 */ + .name = DRV_NAME, .init_chipset = init_chipset_cmd64x, .enablebits = {{0x00,0x00,0x00}, {0x51,0x08,0x08}}, .port_ops = &cmd64x_port_ops, @@ -436,8 +420,9 @@ static const struct ide_port_info cmd64x_chipsets[] __devinitdata = { .pio_mask = ATA_PIO5, .mwdma_mask = ATA_MWDMA2, .udma_mask = 0x00, /* no udma */ - },{ /* 1 */ - .name = "CMD646", + }, + { /* 1: CMD646 */ + .name = DRV_NAME, .init_chipset = init_chipset_cmd64x, .enablebits = {{0x51,0x04,0x04}, {0x51,0x08,0x08}}, .chipset = ide_cmd646, @@ -447,8 +432,9 @@ static const struct ide_port_info cmd64x_chipsets[] __devinitdata = { .pio_mask = ATA_PIO5, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA2, - },{ /* 2 */ - .name = "CMD648", + }, + { /* 2: CMD648 */ + .name = DRV_NAME, .init_chipset = init_chipset_cmd64x, .enablebits = {{0x51,0x04,0x04}, {0x51,0x08,0x08}}, .port_ops = &cmd64x_port_ops, @@ -457,8 +443,9 @@ static const struct ide_port_info cmd64x_chipsets[] __devinitdata = { .pio_mask = ATA_PIO5, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA4, - },{ /* 3 */ - .name = "CMD649", + }, + { /* 3: CMD649 */ + .name = DRV_NAME, .init_chipset = init_chipset_cmd64x, .enablebits = {{0x51,0x04,0x04}, {0x51,0x08,0x08}}, .port_ops = &cmd64x_port_ops, @@ -507,7 +494,7 @@ static int __devinit cmd64x_init_one(struct pci_dev *dev, const struct pci_devic } } - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id cmd64x_pci_tbl[] = { @@ -523,6 +510,7 @@ static struct pci_driver driver = { .name = "CMD64x_IDE", .id_table = cmd64x_pci_tbl, .probe = cmd64x_init_one, + .remove = ide_pci_remove, }; static int __init cmd64x_ide_init(void) @@ -530,7 +518,13 @@ static int __init cmd64x_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit cmd64x_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(cmd64x_ide_init); +module_exit(cmd64x_ide_exit); MODULE_AUTHOR("Eddie Dost, David Miller, Andre Hedrick"); MODULE_DESCRIPTION("PCI driver module for CMD64x IDE"); diff --git a/drivers/ide/pci/cs5520.c b/drivers/ide/pci/cs5520.c index 992b1cf8db69..c0364b287f17 100644 --- a/drivers/ide/pci/cs5520.c +++ b/drivers/ide/pci/cs5520.c @@ -41,6 +41,8 @@ #include <linux/ide.h> #include <linux/dma-mapping.h> +#define DRV_NAME "cs5520" + struct pio_clocks { int address; @@ -62,8 +64,6 @@ static void cs5520_set_pio_mode(ide_drive_t *drive, const u8 pio) struct pci_dev *pdev = to_pci_dev(hwif->dev); int controller = drive->dn > 1 ? 1 : 0; - /* FIXME: if DMA = 1 do we need to set the DMA bit here ? */ - /* 8bit CAT/CRT - 8bit command timing for channel */ pci_write_config_byte(pdev, 0x62 + controller, (cs5520_pio_clocks[pio].recovery << 4) | @@ -89,52 +89,16 @@ static void cs5520_set_dma_mode(ide_drive_t *drive, const u8 speed) cs5520_set_pio_mode(drive, 0); } -/* - * We wrap the DMA activate to set the vdma flag. This is needed - * so that the IDE DMA layer issues PIO not DMA commands over the - * DMA channel - * - * ATAPI is harder so disable it for now using IDE_HFLAG_NO_ATAPI_DMA - */ - -static void cs5520_dma_host_set(ide_drive_t *drive, int on) -{ - drive->vdma = on; - ide_dma_host_set(drive, on); -} - static const struct ide_port_ops cs5520_port_ops = { .set_pio_mode = cs5520_set_pio_mode, .set_dma_mode = cs5520_set_dma_mode, }; -static const struct ide_dma_ops cs5520_dma_ops = { - .dma_host_set = cs5520_dma_host_set, - .dma_setup = ide_dma_setup, - .dma_exec_cmd = ide_dma_exec_cmd, - .dma_start = ide_dma_start, - .dma_end = __ide_dma_end, - .dma_test_irq = ide_dma_test_irq, - .dma_lost_irq = ide_dma_lost_irq, - .dma_timeout = ide_dma_timeout, -}; - -/* FIXME: VDMA is disabled because it caused system hangs */ -#define DECLARE_CS_DEV(name_str) \ - { \ - .name = name_str, \ - .port_ops = &cs5520_port_ops, \ - .dma_ops = &cs5520_dma_ops, \ - .host_flags = IDE_HFLAG_ISA_PORTS | \ - IDE_HFLAG_CS5520 | \ - IDE_HFLAG_NO_ATAPI_DMA | \ - IDE_HFLAG_ABUSE_SET_DMA_MODE, \ - .pio_mask = ATA_PIO4, \ - } - -static const struct ide_port_info cyrix_chipsets[] __devinitdata = { - /* 0 */ DECLARE_CS_DEV("Cyrix 5510"), - /* 1 */ DECLARE_CS_DEV("Cyrix 5520") +static const struct ide_port_info cyrix_chipset __devinitdata = { + .name = DRV_NAME, + .port_ops = &cs5520_port_ops, + .host_flags = IDE_HFLAG_ISA_PORTS | IDE_HFLAG_CS5520, + .pio_mask = ATA_PIO4, }; /* @@ -145,8 +109,8 @@ static const struct ide_port_info cyrix_chipsets[] __devinitdata = { static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - const struct ide_port_info *d = &cyrix_chipsets[id->driver_data]; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + const struct ide_port_info *d = &cyrix_chipset; + hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; ide_setup_pci_noise(dev, d); @@ -159,7 +123,8 @@ static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_devic } pci_set_master(dev); if (pci_set_dma_mask(dev, DMA_32BIT_MASK)) { - printk(KERN_WARNING "cs5520: No suitable DMA available.\n"); + printk(KERN_WARNING "%s: No suitable DMA available.\n", + d->name); return -ENODEV; } @@ -168,11 +133,9 @@ static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_devic * do all the device setup for us */ - ide_pci_setup_ports(dev, d, 14, &idx[0]); - - ide_device_add(idx, d); + ide_pci_setup_ports(dev, d, 14, &hw[0], &hws[0]); - return 0; + return ide_host_add(d, hws, NULL); } static const struct pci_device_id cs5520_pci_tbl[] = { diff --git a/drivers/ide/pci/cs5530.c b/drivers/ide/pci/cs5530.c index f5534c1ff349..f235db8c678b 100644 --- a/drivers/ide/pci/cs5530.c +++ b/drivers/ide/pci/cs5530.c @@ -22,6 +22,8 @@ #include <asm/io.h> +#define DRV_NAME "cs5530" + /* * Here are the standard PIO mode 0-4 timings for each "format". * Format-0 uses fast data reg timings, with slower command reg timings. @@ -127,12 +129,11 @@ static void cs5530_set_dma_mode(ide_drive_t *drive, const u8 mode) /** * init_chipset_5530 - set up 5530 bridge * @dev: PCI device - * @name: device name * * Initialize the cs5530 bridge for reliable IDE DMA operation. */ -static unsigned int __devinit init_chipset_cs5530 (struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_cs5530(struct pci_dev *dev) { struct pci_dev *master_0 = NULL, *cs5530_0 = NULL; @@ -151,11 +152,11 @@ static unsigned int __devinit init_chipset_cs5530 (struct pci_dev *dev, const ch } } if (!master_0) { - printk(KERN_ERR "%s: unable to locate PCI MASTER function\n", name); + printk(KERN_ERR DRV_NAME ": unable to locate PCI MASTER function\n"); goto out; } if (!cs5530_0) { - printk(KERN_ERR "%s: unable to locate CS5530 LEGACY function\n", name); + printk(KERN_ERR DRV_NAME ": unable to locate CS5530 LEGACY function\n"); goto out; } @@ -243,7 +244,7 @@ static const struct ide_port_ops cs5530_port_ops = { }; static const struct ide_port_info cs5530_chipset __devinitdata = { - .name = "CS5530", + .name = DRV_NAME, .init_chipset = init_chipset_cs5530, .init_hwif = init_hwif_cs5530, .port_ops = &cs5530_port_ops, @@ -256,7 +257,7 @@ static const struct ide_port_info cs5530_chipset __devinitdata = { static int __devinit cs5530_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &cs5530_chipset); + return ide_pci_init_one(dev, &cs5530_chipset, NULL); } static const struct pci_device_id cs5530_pci_tbl[] = { @@ -269,6 +270,7 @@ static struct pci_driver driver = { .name = "CS5530 IDE", .id_table = cs5530_pci_tbl, .probe = cs5530_init_one, + .remove = ide_pci_remove, }; static int __init cs5530_ide_init(void) @@ -276,7 +278,13 @@ static int __init cs5530_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit cs5530_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(cs5530_ide_init); +module_exit(cs5530_ide_exit); MODULE_AUTHOR("Mark Lord"); MODULE_DESCRIPTION("PCI driver module for Cyrix/NS 5530 IDE"); diff --git a/drivers/ide/pci/cs5535.c b/drivers/ide/pci/cs5535.c index dc97c48623f3..f7b50cdeefa6 100644 --- a/drivers/ide/pci/cs5535.c +++ b/drivers/ide/pci/cs5535.c @@ -26,6 +26,8 @@ #include <linux/pci.h> #include <linux/ide.h> +#define DRV_NAME "cs5535" + #define MSR_ATAC_BASE 0x51300000 #define ATAC_GLD_MSR_CAP (MSR_ATAC_BASE+0) #define ATAC_GLD_MSR_CONFIG (MSR_ATAC_BASE+0x01) @@ -169,10 +171,9 @@ static const struct ide_port_ops cs5535_port_ops = { }; static const struct ide_port_info cs5535_chipset __devinitdata = { - .name = "CS5535", + .name = DRV_NAME, .port_ops = &cs5535_port_ops, - .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_POST_SET_MODE | - IDE_HFLAG_ABUSE_SET_DMA_MODE, + .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_POST_SET_MODE, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA4, @@ -181,7 +182,7 @@ static const struct ide_port_info cs5535_chipset __devinitdata = { static int __devinit cs5535_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &cs5535_chipset); + return ide_pci_init_one(dev, &cs5535_chipset, NULL); } static const struct pci_device_id cs5535_pci_tbl[] = { @@ -195,6 +196,7 @@ static struct pci_driver driver = { .name = "CS5535_IDE", .id_table = cs5535_pci_tbl, .probe = cs5535_init_one, + .remove = ide_pci_remove, }; static int __init cs5535_ide_init(void) @@ -202,7 +204,13 @@ static int __init cs5535_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit cs5535_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(cs5535_ide_init); +module_exit(cs5535_ide_exit); MODULE_AUTHOR("AMD"); MODULE_DESCRIPTION("PCI driver module for AMD/NS CS5535 IDE"); diff --git a/drivers/ide/pci/cy82c693.c b/drivers/ide/pci/cy82c693.c index e14ad5530fa4..bfae2f882f48 100644 --- a/drivers/ide/pci/cy82c693.c +++ b/drivers/ide/pci/cy82c693.c @@ -48,6 +48,8 @@ #include <asm/io.h> +#define DRV_NAME "cy82c693" + /* the current version */ #define CY82_VERSION "CY82C693U driver v0.34 99-13-12 Andreas S. Krebs (akrebs@altavista.net)" @@ -330,7 +332,7 @@ static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio) /* * this function is called during init and is used to setup the cy82c693 chip */ -static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev) { if (PCI_FUNC(dev->devfn) != 1) return 0; @@ -349,8 +351,8 @@ static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev, const c data = inb(CY82_DATA_PORT); #if CY82C693_DEBUG_INFO - printk(KERN_INFO "%s: Peripheral Configuration Register: 0x%X\n", - name, data); + printk(KERN_INFO DRV_NAME ": Peripheral Configuration Register: 0x%X\n", + data); #endif /* CY82C693_DEBUG_INFO */ /* @@ -371,8 +373,8 @@ static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev, const c outb(data, CY82_DATA_PORT); #if CY82C693_DEBUG_INFO - printk(KERN_INFO "%s: New Peripheral Configuration Register: 0x%X\n", - name, data); + printk(KERN_INFO ": New Peripheral Configuration Register: 0x%X\n", + data); #endif /* CY82C693_DEBUG_INFO */ #endif /* CY82C693_SETDMA_CLOCK */ @@ -398,7 +400,7 @@ static const struct ide_port_ops cy82c693_port_ops = { }; static const struct ide_port_info cy82c693_chipset __devinitdata = { - .name = "CY82C693", + .name = DRV_NAME, .init_chipset = init_chipset_cy82c693, .init_iops = init_iops_cy82c693, .port_ops = &cy82c693_port_ops, @@ -419,12 +421,22 @@ 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, &cy82c693_chipset); - /* We leak pci refs here but thats ok - we can't be unloaded */ + ret = ide_pci_init_two(dev, dev2, &cy82c693_chipset, NULL); + if (ret) + pci_dev_put(dev2); } return ret; } +static void __devexit cy82c693_remove(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + struct pci_dev *dev2 = host->dev[1] ? to_pci_dev(host->dev[1]) : NULL; + + ide_pci_remove(dev); + pci_dev_put(dev2); +} + static const struct pci_device_id cy82c693_pci_tbl[] = { { PCI_VDEVICE(CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693), 0 }, { 0, }, @@ -435,6 +447,7 @@ static struct pci_driver driver = { .name = "Cypress_IDE", .id_table = cy82c693_pci_tbl, .probe = cy82c693_init_one, + .remove = cy82c693_remove, }; static int __init cy82c693_ide_init(void) @@ -442,7 +455,13 @@ static int __init cy82c693_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit cy82c693_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(cy82c693_ide_init); +module_exit(cy82c693_ide_exit); MODULE_AUTHOR("Andreas Krebs, Andre Hedrick"); MODULE_DESCRIPTION("PCI driver module for the Cypress CY82C693 IDE"); diff --git a/drivers/ide/pci/delkin_cb.c b/drivers/ide/pci/delkin_cb.c index 0106e2a2df77..f84bfb4f600f 100644 --- a/drivers/ide/pci/delkin_cb.c +++ b/drivers/ide/pci/delkin_cb.c @@ -56,11 +56,10 @@ static const struct ide_port_info delkin_cb_port_info = { static int __devinit delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id) { + struct ide_host *host; unsigned long base; - hw_regs_t hw; - ide_hwif_t *hwif = NULL; int i, rc; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; rc = pci_enable_device(dev); if (rc) { @@ -87,34 +86,26 @@ delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id) hw.dev = &dev->dev; hw.chipset = ide_pci; /* this enables IRQ sharing */ - hwif = ide_find_port(); - if (hwif == NULL) + rc = ide_host_add(&delkin_cb_port_info, hws, &host); + if (rc) goto out_disable; - i = hwif->index; - - ide_init_port_hw(hwif, &hw); - - idx[0] = i; - - ide_device_add(idx, &delkin_cb_port_info); - - pci_set_drvdata(dev, hwif); + pci_set_drvdata(dev, host); return 0; out_disable: pci_release_regions(dev); pci_disable_device(dev); - return -ENODEV; + return rc; } static void delkin_cb_remove (struct pci_dev *dev) { - ide_hwif_t *hwif = pci_get_drvdata(dev); + struct ide_host *host = pci_get_drvdata(dev); - ide_unregister(hwif); + ide_host_remove(host); pci_release_regions(dev); pci_disable_device(dev); diff --git a/drivers/ide/pci/generic.c b/drivers/ide/pci/generic.c index 041720e22762..b07d4f4273b3 100644 --- a/drivers/ide/pci/generic.c +++ b/drivers/ide/pci/generic.c @@ -27,6 +27,8 @@ #include <linux/ide.h> #include <linux/init.h> +#define DRV_NAME "ide_pci_generic" + static int ide_generic_all; /* Set to claim all devices */ module_param_named(all_generic_ide, ide_generic_all, bool, 0444); @@ -34,9 +36,9 @@ MODULE_PARM_DESC(all_generic_ide, "IDE generic will claim all unknown PCI IDE st #define IDE_HFLAGS_UMC (IDE_HFLAG_NO_DMA | IDE_HFLAG_FORCE_LEGACY_IRQS) -#define DECLARE_GENERIC_PCI_DEV(name_str, extra_flags) \ +#define DECLARE_GENERIC_PCI_DEV(extra_flags) \ { \ - .name = name_str, \ + .name = DRV_NAME, \ .host_flags = IDE_HFLAG_TRUST_BIOS_FOR_DMA | \ extra_flags, \ .swdma_mask = ATA_SWDMA2, \ @@ -45,10 +47,11 @@ MODULE_PARM_DESC(all_generic_ide, "IDE generic will claim all unknown PCI IDE st } static const struct ide_port_info generic_chipsets[] __devinitdata = { - /* 0 */ DECLARE_GENERIC_PCI_DEV("Unknown", 0), + /* 0: Unknown */ + DECLARE_GENERIC_PCI_DEV(0), - { /* 1 */ - .name = "NS87410", + { /* 1: NS87410 */ + .name = DRV_NAME, .enablebits = { {0x43, 0x08, 0x08}, {0x47, 0x08, 0x08} }, .host_flags = IDE_HFLAG_TRUST_BIOS_FOR_DMA, .swdma_mask = ATA_SWDMA2, @@ -56,17 +59,15 @@ static const struct ide_port_info generic_chipsets[] __devinitdata = { .udma_mask = ATA_UDMA6, }, - /* 2 */ DECLARE_GENERIC_PCI_DEV("SAMURAI", 0), - /* 3 */ DECLARE_GENERIC_PCI_DEV("HT6565", 0), - /* 4 */ DECLARE_GENERIC_PCI_DEV("UM8673F", IDE_HFLAGS_UMC), - /* 5 */ DECLARE_GENERIC_PCI_DEV("UM8886A", IDE_HFLAGS_UMC), - /* 6 */ DECLARE_GENERIC_PCI_DEV("UM8886BF", IDE_HFLAGS_UMC), - /* 7 */ DECLARE_GENERIC_PCI_DEV("HINT_IDE", 0), - /* 8 */ DECLARE_GENERIC_PCI_DEV("VIA_IDE", IDE_HFLAG_NO_AUTODMA), - /* 9 */ DECLARE_GENERIC_PCI_DEV("OPTI621V", IDE_HFLAG_NO_AUTODMA), - - { /* 10 */ - .name = "VIA8237SATA", + /* 2: SAMURAI / HT6565 / HINT_IDE */ + DECLARE_GENERIC_PCI_DEV(0), + /* 3: UM8673F / UM8886A / UM8886BF */ + DECLARE_GENERIC_PCI_DEV(IDE_HFLAGS_UMC), + /* 4: VIA_IDE / OPTI621V / Piccolo010{2,3,5} */ + DECLARE_GENERIC_PCI_DEV(IDE_HFLAG_NO_AUTODMA), + + { /* 5: VIA8237SATA */ + .name = DRV_NAME, .host_flags = IDE_HFLAG_TRUST_BIOS_FOR_DMA | IDE_HFLAG_OFF_BOARD, .swdma_mask = ATA_SWDMA2, @@ -74,12 +75,8 @@ static const struct ide_port_info generic_chipsets[] __devinitdata = { .udma_mask = ATA_UDMA6, }, - /* 11 */ DECLARE_GENERIC_PCI_DEV("Piccolo0102", IDE_HFLAG_NO_AUTODMA), - /* 12 */ DECLARE_GENERIC_PCI_DEV("Piccolo0103", IDE_HFLAG_NO_AUTODMA), - /* 13 */ DECLARE_GENERIC_PCI_DEV("Piccolo0105", IDE_HFLAG_NO_AUTODMA), - - { /* 14 */ - .name = "Revolution", + { /* 6: Revolution */ + .name = DRV_NAME, .host_flags = IDE_HFLAG_CLEAR_SIMPLEX | IDE_HFLAG_TRUST_BIOS_FOR_DMA | IDE_HFLAG_OFF_BOARD, @@ -134,12 +131,12 @@ static int __devinit generic_init_one(struct pci_dev *dev, const struct pci_devi u16 command; pci_read_config_word(dev, PCI_COMMAND, &command); if (!(command & PCI_COMMAND_IO)) { - printk(KERN_INFO "Skipping disabled %s IDE " - "controller.\n", d->name); + printk(KERN_INFO "%s %s: skipping disabled " + "controller\n", d->name, pci_name(dev)); goto out; } } - ret = ide_setup_pci_device(dev, d); + ret = ide_pci_init_one(dev, d, NULL); out: return ret; } @@ -147,20 +144,20 @@ out: static const struct pci_device_id generic_pci_tbl[] = { { PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_87410), 1 }, { PCI_VDEVICE(PCTECH, PCI_DEVICE_ID_PCTECH_SAMURAI_IDE), 2 }, - { PCI_VDEVICE(HOLTEK, PCI_DEVICE_ID_HOLTEK_6565), 3 }, - { PCI_VDEVICE(UMC, PCI_DEVICE_ID_UMC_UM8673F), 4 }, - { PCI_VDEVICE(UMC, PCI_DEVICE_ID_UMC_UM8886A), 5 }, - { PCI_VDEVICE(UMC, PCI_DEVICE_ID_UMC_UM8886BF), 6 }, - { PCI_VDEVICE(HINT, PCI_DEVICE_ID_HINT_VXPROII_IDE), 7 }, - { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C561), 8 }, - { PCI_VDEVICE(OPTI, PCI_DEVICE_ID_OPTI_82C558), 9 }, + { PCI_VDEVICE(HOLTEK, PCI_DEVICE_ID_HOLTEK_6565), 2 }, + { PCI_VDEVICE(UMC, PCI_DEVICE_ID_UMC_UM8673F), 3 }, + { PCI_VDEVICE(UMC, PCI_DEVICE_ID_UMC_UM8886A), 3 }, + { PCI_VDEVICE(UMC, PCI_DEVICE_ID_UMC_UM8886BF), 3 }, + { PCI_VDEVICE(HINT, PCI_DEVICE_ID_HINT_VXPROII_IDE), 2 }, + { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C561), 4 }, + { PCI_VDEVICE(OPTI, PCI_DEVICE_ID_OPTI_82C558), 4 }, #ifdef CONFIG_BLK_DEV_IDE_SATA - { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_8237_SATA), 10 }, + { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_8237_SATA), 5 }, #endif - { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO), 11 }, - { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_1), 12 }, - { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_2), 13 }, - { PCI_VDEVICE(NETCELL, PCI_DEVICE_ID_REVOLUTION), 14 }, + { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO), 4 }, + { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_1), 4 }, + { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_2), 4 }, + { PCI_VDEVICE(NETCELL, PCI_DEVICE_ID_REVOLUTION), 6 }, /* * Must come last. If you add entries adjust * this table and generic_chipsets[] appropriately. @@ -174,6 +171,7 @@ static struct pci_driver driver = { .name = "PCI_IDE", .id_table = generic_pci_tbl, .probe = generic_init_one, + .remove = ide_pci_remove, }; static int __init generic_ide_init(void) @@ -181,7 +179,13 @@ static int __init generic_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit generic_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(generic_ide_init); +module_exit(generic_ide_exit); MODULE_AUTHOR("Andre Hedrick"); MODULE_DESCRIPTION("PCI driver module for generic PCI IDE"); diff --git a/drivers/ide/pci/hpt34x.c b/drivers/ide/pci/hpt34x.c index 84c36c117194..6009b0b9655d 100644 --- a/drivers/ide/pci/hpt34x.c +++ b/drivers/ide/pci/hpt34x.c @@ -33,6 +33,8 @@ #include <linux/init.h> #include <linux/ide.h> +#define DRV_NAME "hpt34x" + #define HPT343_DEBUG_DRIVE_INFO 0 static void hpt34x_set_mode(ide_drive_t *drive, const u8 speed) @@ -77,7 +79,7 @@ static void hpt34x_set_pio_mode(ide_drive_t *drive, const u8 pio) */ #define HPT34X_PCI_INIT_REG 0x80 -static unsigned int __devinit init_chipset_hpt34x(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_hpt34x(struct pci_dev *dev) { int i = 0; unsigned long hpt34xIoBase = pci_resource_start(dev, 4); @@ -123,19 +125,18 @@ static const struct ide_port_ops hpt34x_port_ops = { #define IDE_HFLAGS_HPT34X \ (IDE_HFLAG_NO_ATAPI_DMA | \ IDE_HFLAG_NO_DSC | \ - IDE_HFLAG_ABUSE_SET_DMA_MODE | \ IDE_HFLAG_NO_AUTODMA) static const struct ide_port_info hpt34x_chipsets[] __devinitdata = { - { /* 0 */ - .name = "HPT343", + { /* 0: HPT343 */ + .name = DRV_NAME, .init_chipset = init_chipset_hpt34x, .port_ops = &hpt34x_port_ops, .host_flags = IDE_HFLAGS_HPT34X | IDE_HFLAG_NON_BOOTABLE, .pio_mask = ATA_PIO5, }, - { /* 1 */ - .name = "HPT345", + { /* 1: HPT345 */ + .name = DRV_NAME, .init_chipset = init_chipset_hpt34x, .port_ops = &hpt34x_port_ops, .host_flags = IDE_HFLAGS_HPT34X | IDE_HFLAG_OFF_BOARD, @@ -157,7 +158,7 @@ static int __devinit hpt34x_init_one(struct pci_dev *dev, const struct pci_devic d = &hpt34x_chipsets[(pcicmd & PCI_COMMAND_MEMORY) ? 1 : 0]; - return ide_setup_pci_device(dev, d); + return ide_pci_init_one(dev, d, NULL); } static const struct pci_device_id hpt34x_pci_tbl[] = { @@ -170,6 +171,7 @@ static struct pci_driver driver = { .name = "HPT34x_IDE", .id_table = hpt34x_pci_tbl, .probe = hpt34x_init_one, + .remove = ide_pci_remove, }; static int __init hpt34x_ide_init(void) @@ -177,7 +179,13 @@ static int __init hpt34x_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit hpt34x_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(hpt34x_ide_init); +module_exit(hpt34x_ide_exit); MODULE_AUTHOR("Andre Hedrick"); MODULE_DESCRIPTION("PCI driver module for Highpoint 34x IDE"); diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index 397c6cbe953c..5271b246b88c 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -131,6 +131,8 @@ #include <asm/uaccess.h> #include <asm/io.h> +#define DRV_NAME "hpt366" + /* various tuning parameters */ #define HPT_RESET_STATE_ENGINE #undef HPT_DELAY_INTERRUPT @@ -620,7 +622,8 @@ static u8 hpt3xx_udma_filter(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = to_pci_dev(hwif->dev); - struct hpt_info *info = pci_get_drvdata(dev); + struct ide_host *host = pci_get_drvdata(dev); + struct hpt_info *info = host->host_priv + (hwif->dev == host->dev[1]); u8 mask = hwif->ultra_mask; switch (info->chip_type) { @@ -660,7 +663,8 @@ static u8 hpt3xx_mdma_filter(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = to_pci_dev(hwif->dev); - struct hpt_info *info = pci_get_drvdata(dev); + struct ide_host *host = pci_get_drvdata(dev); + struct hpt_info *info = host->host_priv + (hwif->dev == host->dev[1]); switch (info->chip_type) { case HPT372 : @@ -694,8 +698,10 @@ static u32 get_speed_setting(u8 speed, struct hpt_info *info) static void hpt3xx_set_mode(ide_drive_t *drive, const u8 speed) { - struct pci_dev *dev = to_pci_dev(drive->hwif->dev); - struct hpt_info *info = pci_get_drvdata(dev); + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + struct ide_host *host = pci_get_drvdata(dev); + struct hpt_info *info = host->host_priv + (hwif->dev == host->dev[1]); struct hpt_timings *t = info->timings; u8 itr_addr = 0x40 + (drive->dn * 4); u32 old_itr = 0; @@ -738,7 +744,8 @@ static void hpt3xx_maskproc(ide_drive_t *drive, int mask) { ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = to_pci_dev(hwif->dev); - struct hpt_info *info = pci_get_drvdata(dev); + struct ide_host *host = pci_get_drvdata(dev); + struct hpt_info *info = host->host_priv + (hwif->dev == host->dev[1]); if (drive->quirk_list) { if (info->chip_type >= HPT370) { @@ -801,9 +808,9 @@ static void hpt370_irq_timeout(ide_drive_t *drive) printk(KERN_DEBUG "%s: %d bytes in FIFO\n", drive->name, bfifo & 0x1ff); /* get DMA command mode */ - dma_cmd = inb(hwif->dma_command); + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); /* stop DMA */ - outb(dma_cmd & ~0x1, hwif->dma_command); + outb(dma_cmd & ~0x1, hwif->dma_base + ATA_DMA_CMD); hpt370_clear_engine(drive); } @@ -818,12 +825,12 @@ static void hpt370_dma_start(ide_drive_t *drive) static int hpt370_dma_end(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); - u8 dma_stat = inb(hwif->dma_status); + u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); if (dma_stat & 0x01) { /* wait a little */ udelay(20); - dma_stat = inb(hwif->dma_status); + dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); if (dma_stat & 0x01) hpt370_irq_timeout(drive); } @@ -850,7 +857,7 @@ static int hpt374_dma_test_irq(ide_drive_t *drive) return 0; } - dma_stat = inb(hwif->dma_status); + dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); /* return 1 if INTR asserted */ if (dma_stat & 4) return 1; @@ -963,24 +970,16 @@ static int __devinit hpt37x_calibrate_dpll(struct pci_dev *dev, u16 f_low, u16 f return 1; } -static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev) { - struct hpt_info *info = kmalloc(sizeof(struct hpt_info), GFP_KERNEL); unsigned long io_base = pci_resource_start(dev, 4); + struct ide_host *host = pci_get_drvdata(dev); + struct hpt_info *info = host->host_priv + (&dev->dev == host->dev[1]); + const char *name = DRV_NAME; u8 pci_clk, dpll_clk = 0; /* PCI and DPLL clock in MHz */ u8 chip_type; enum ata_clock clock; - if (info == NULL) { - printk(KERN_ERR "%s: out of memory!\n", name); - return -ENOMEM; - } - - /* - * Copy everything from a static "template" structure - * to just allocated per-chip hpt_info structure. - */ - memcpy(info, pci_get_drvdata(dev), sizeof(struct hpt_info)); chip_type = info->chip_type; pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, (L1_CACHE_BYTES / 4)); @@ -1048,8 +1047,8 @@ static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const cha if ((temp & 0xFFFFF000) != 0xABCDE000) { int i; - printk(KERN_WARNING "%s: no clock data saved by BIOS\n", - name); + printk(KERN_WARNING "%s %s: no clock data saved by " + "BIOS\n", name, pci_name(dev)); /* Calculate the average value of f_CNT. */ for (temp = i = 0; i < 128; i++) { @@ -1074,8 +1073,9 @@ static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const cha else pci_clk = 66; - printk(KERN_INFO "%s: DPLL base: %d MHz, f_CNT: %d, " - "assuming %d MHz PCI\n", name, dpll_clk, f_cnt, pci_clk); + printk(KERN_INFO "%s %s: DPLL base: %d MHz, f_CNT: %d, " + "assuming %d MHz PCI\n", name, pci_name(dev), + dpll_clk, f_cnt, pci_clk); } else { u32 itr1 = 0; @@ -1141,8 +1141,8 @@ static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const cha } if (info->timings->clock_table[clock] == NULL) { - printk(KERN_ERR "%s: unknown bus timing!\n", name); - kfree(info); + printk(KERN_ERR "%s %s: unknown bus timing!\n", + name, pci_name(dev)); return -EIO; } @@ -1168,17 +1168,19 @@ static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const cha f_low += adjust >> 1; } if (adjust == 8) { - printk(KERN_ERR "%s: DPLL did not stabilize!\n", name); - kfree(info); + printk(KERN_ERR "%s %s: DPLL did not stabilize!\n", + name, pci_name(dev)); return -EIO; } - printk("%s: using %d MHz DPLL clock\n", name, dpll_clk); + printk(KERN_INFO "%s %s: using %d MHz DPLL clock\n", + name, pci_name(dev), dpll_clk); } else { /* Mark the fact that we're not using the DPLL. */ dpll_clk = 0; - printk("%s: using %d MHz PCI clock\n", name, pci_clk); + printk(KERN_INFO "%s %s: using %d MHz PCI clock\n", + name, pci_name(dev), pci_clk); } /* Store the clock frequencies. */ @@ -1186,9 +1188,6 @@ static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const cha info->pci_clk = pci_clk; info->clock = clock; - /* Point to this chip's own instance of the hpt_info structure. */ - pci_set_drvdata(dev, info); - if (chip_type >= HPT370) { u8 mcr1, mcr4; @@ -1218,7 +1217,8 @@ static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const cha static u8 __devinit hpt3xx_cable_detect(ide_hwif_t *hwif) { struct pci_dev *dev = to_pci_dev(hwif->dev); - struct hpt_info *info = pci_get_drvdata(dev); + struct ide_host *host = pci_get_drvdata(dev); + struct hpt_info *info = host->host_priv + (hwif->dev == host->dev[1]); u8 chip_type = info->chip_type; u8 scr1 = 0, ata66 = hwif->channel ? 0x01 : 0x02; @@ -1262,7 +1262,8 @@ static u8 __devinit hpt3xx_cable_detect(ide_hwif_t *hwif) static void __devinit init_hwif_hpt366(ide_hwif_t *hwif) { struct pci_dev *dev = to_pci_dev(hwif->dev); - struct hpt_info *info = pci_get_drvdata(dev); + struct ide_host *host = pci_get_drvdata(dev); + struct hpt_info *info = host->host_priv + (hwif->dev == host->dev[1]); int serialize = HPT_SERIALIZE_IO; u8 chip_type = info->chip_type; u8 new_mcr, old_mcr = 0; @@ -1320,7 +1321,15 @@ static int __devinit init_dma_hpt366(ide_hwif_t *hwif, unsigned long flags, base = ide_pci_dma_base(hwif, d); u8 dma_old, dma_new, masterdma = 0, slavedma = 0; - if (base == 0 || ide_pci_set_master(dev, d->name) < 0) + if (base == 0) + return -1; + + hwif->dma_base = base; + + if (ide_pci_check_simplex(hwif, d) < 0) + return -1; + + if (ide_pci_set_master(dev, d->name) < 0) return -1; dma_old = inb(base + 2); @@ -1346,7 +1355,7 @@ static int __devinit init_dma_hpt366(ide_hwif_t *hwif, if (ide_allocate_dma_engine(hwif)) return -1; - ide_setup_dma(hwif, base); + hwif->dma_ops = &sff_dma_ops; return 0; } @@ -1356,7 +1365,8 @@ static void __devinit hpt374_init(struct pci_dev *dev, struct pci_dev *dev2) if (dev2->irq != dev->irq) { /* FIXME: we need a core pci_set_interrupt() */ dev2->irq = dev->irq; - printk(KERN_INFO "HPT374: PCI config space interrupt fixed\n"); + printk(KERN_INFO DRV_NAME " %s: PCI config space interrupt " + "fixed\n", pci_name(dev2)); } } @@ -1391,8 +1401,8 @@ static int __devinit hpt36x_init(struct pci_dev *dev, struct pci_dev *dev2) pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin2); if (pin1 != pin2 && dev->irq == dev2->irq) { - printk(KERN_INFO "HPT36x: onboard version of chipset, " - "pin1=%d pin2=%d\n", pin1, pin2); + printk(KERN_INFO DRV_NAME " %s: onboard version of chipset, " + "pin1=%d pin2=%d\n", pci_name(dev), pin1, pin2); return 1; } @@ -1401,7 +1411,6 @@ static int __devinit hpt36x_init(struct pci_dev *dev, struct pci_dev *dev2) #define IDE_HFLAGS_HPT3XX \ (IDE_HFLAG_NO_ATAPI_DMA | \ - IDE_HFLAG_ABUSE_SET_DMA_MODE | \ IDE_HFLAG_OFF_BOARD) static const struct ide_port_ops hpt3xx_port_ops = { @@ -1448,8 +1457,8 @@ static const struct ide_dma_ops hpt36x_dma_ops = { }; static const struct ide_port_info hpt366_chipsets[] __devinitdata = { - { /* 0 */ - .name = "HPT36x", + { /* 0: HPT36x */ + .name = DRV_NAME, .init_chipset = init_chipset_hpt366, .init_hwif = init_hwif_hpt366, .init_dma = init_dma_hpt366, @@ -1465,53 +1474,9 @@ static const struct ide_port_info hpt366_chipsets[] __devinitdata = { .host_flags = IDE_HFLAGS_HPT3XX | IDE_HFLAG_SINGLE, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, - },{ /* 1 */ - .name = "HPT372A", - .init_chipset = init_chipset_hpt366, - .init_hwif = init_hwif_hpt366, - .init_dma = init_dma_hpt366, - .enablebits = {{0x50,0x04,0x04}, {0x54,0x04,0x04}}, - .port_ops = &hpt3xx_port_ops, - .dma_ops = &hpt37x_dma_ops, - .host_flags = IDE_HFLAGS_HPT3XX, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, - },{ /* 2 */ - .name = "HPT302", - .init_chipset = init_chipset_hpt366, - .init_hwif = init_hwif_hpt366, - .init_dma = init_dma_hpt366, - .enablebits = {{0x50,0x04,0x04}, {0x54,0x04,0x04}}, - .port_ops = &hpt3xx_port_ops, - .dma_ops = &hpt37x_dma_ops, - .host_flags = IDE_HFLAGS_HPT3XX, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, - },{ /* 3 */ - .name = "HPT371", - .init_chipset = init_chipset_hpt366, - .init_hwif = init_hwif_hpt366, - .init_dma = init_dma_hpt366, - .enablebits = {{0x50,0x04,0x04}, {0x54,0x04,0x04}}, - .port_ops = &hpt3xx_port_ops, - .dma_ops = &hpt37x_dma_ops, - .host_flags = IDE_HFLAGS_HPT3XX, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, - },{ /* 4 */ - .name = "HPT374", - .init_chipset = init_chipset_hpt366, - .init_hwif = init_hwif_hpt366, - .init_dma = init_dma_hpt366, - .enablebits = {{0x50,0x04,0x04}, {0x54,0x04,0x04}}, - .udma_mask = ATA_UDMA5, - .port_ops = &hpt3xx_port_ops, - .dma_ops = &hpt37x_dma_ops, - .host_flags = IDE_HFLAGS_HPT3XX, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, - },{ /* 5 */ - .name = "HPT372N", + }, + { /* 1: HPT3xx */ + .name = DRV_NAME, .init_chipset = init_chipset_hpt366, .init_hwif = init_hwif_hpt366, .init_dma = init_dma_hpt366, @@ -1535,10 +1500,12 @@ static const struct ide_port_info hpt366_chipsets[] __devinitdata = { static int __devinit hpt366_init_one(struct pci_dev *dev, const struct pci_device_id *id) { const struct hpt_info *info = NULL; + struct hpt_info *dyn_info; struct pci_dev *dev2 = NULL; struct ide_port_info d; u8 idx = id->driver_data; u8 rev = dev->revision; + int ret; if ((idx == 0 || idx == 4) && (PCI_FUNC(dev->devfn) & 1)) return -ENODEV; @@ -1575,24 +1542,35 @@ static int __devinit hpt366_init_one(struct pci_dev *dev, const struct pci_devic break; } - d = hpt366_chipsets[idx]; + printk(KERN_INFO DRV_NAME ": %s chipset detected\n", info->chip_name); + + d = hpt366_chipsets[min_t(u8, idx, 1)]; - d.name = info->chip_name; d.udma_mask = info->udma_mask; /* fixup ->dma_ops for HPT370/HPT370A */ if (info == &hpt370 || info == &hpt370a) d.dma_ops = &hpt370_dma_ops; - pci_set_drvdata(dev, (void *)info); - if (info == &hpt36x || info == &hpt374) dev2 = pci_get_slot(dev->bus, dev->devfn + 1); - if (dev2) { - int ret; + dyn_info = kzalloc(sizeof(*dyn_info) * (dev2 ? 2 : 1), GFP_KERNEL); + if (dyn_info == NULL) { + printk(KERN_ERR "%s %s: out of memory!\n", + d.name, pci_name(dev)); + pci_dev_put(dev2); + return -ENOMEM; + } - pci_set_drvdata(dev2, (void *)info); + /* + * Copy everything from a static "template" structure + * to just allocated per-chip hpt_info structure. + */ + memcpy(dyn_info, info, sizeof(*dyn_info)); + + if (dev2) { + memcpy(dyn_info + 1, info, sizeof(*dyn_info)); if (info == &hpt374) hpt374_init(dev, dev2); @@ -1601,13 +1579,30 @@ static int __devinit hpt366_init_one(struct pci_dev *dev, const struct pci_devic d.host_flags &= ~IDE_HFLAG_NON_BOOTABLE; } - ret = ide_setup_pci_devices(dev, dev2, &d); - if (ret < 0) + ret = ide_pci_init_two(dev, dev2, &d, dyn_info); + if (ret < 0) { pci_dev_put(dev2); + kfree(dyn_info); + } return ret; } - return ide_setup_pci_device(dev, &d); + ret = ide_pci_init_one(dev, &d, dyn_info); + if (ret < 0) + kfree(dyn_info); + + return ret; +} + +static void __devexit hpt366_remove(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + struct ide_info *info = host->host_priv; + struct pci_dev *dev2 = host->dev[1] ? to_pci_dev(host->dev[1]) : NULL; + + ide_pci_remove(dev); + pci_dev_put(dev2); + kfree(info); } static const struct pci_device_id hpt366_pci_tbl[] __devinitconst = { @@ -1625,6 +1620,7 @@ static struct pci_driver driver = { .name = "HPT366_IDE", .id_table = hpt366_pci_tbl, .probe = hpt366_init_one, + .remove = hpt366_remove, }; static int __init hpt366_ide_init(void) @@ -1632,7 +1628,13 @@ static int __init hpt366_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit hpt366_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(hpt366_ide_init); +module_exit(hpt366_ide_exit); MODULE_AUTHOR("Andre Hedrick"); MODULE_DESCRIPTION("PCI driver module for Highpoint HPT366 IDE"); diff --git a/drivers/ide/pci/it8213.c b/drivers/ide/pci/it8213.c index 2b71bdf74e73..6eba8f188264 100644 --- a/drivers/ide/pci/it8213.c +++ b/drivers/ide/pci/it8213.c @@ -14,6 +14,8 @@ #include <linux/ide.h> #include <linux/init.h> +#define DRV_NAME "it8213" + /** * it8213_set_pio_mode - set host controller for PIO mode * @drive: drive @@ -155,23 +157,17 @@ static const struct ide_port_ops it8213_port_ops = { .cable_detect = it8213_cable_detect, }; -#define DECLARE_ITE_DEV(name_str) \ - { \ - .name = name_str, \ - .enablebits = { {0x41, 0x80, 0x80} }, \ - .port_ops = &it8213_port_ops, \ - .host_flags = IDE_HFLAG_SINGLE, \ - .pio_mask = ATA_PIO4, \ - .swdma_mask = ATA_SWDMA2_ONLY, \ - .mwdma_mask = ATA_MWDMA12_ONLY, \ - .udma_mask = ATA_UDMA6, \ - } - -static const struct ide_port_info it8213_chipsets[] __devinitdata = { - /* 0 */ DECLARE_ITE_DEV("IT8213"), +static const struct ide_port_info it8213_chipset __devinitdata = { + .name = DRV_NAME, + .enablebits = { {0x41, 0x80, 0x80} }, + .port_ops = &it8213_port_ops, + .host_flags = IDE_HFLAG_SINGLE, + .pio_mask = ATA_PIO4, + .swdma_mask = ATA_SWDMA2_ONLY, + .mwdma_mask = ATA_MWDMA12_ONLY, + .udma_mask = ATA_UDMA6, }; - /** * it8213_init_one - pci layer discovery entry * @dev: PCI device @@ -184,7 +180,7 @@ static const struct ide_port_info it8213_chipsets[] __devinitdata = { static int __devinit it8213_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &it8213_chipsets[id->driver_data]); + return ide_pci_init_one(dev, &it8213_chipset, NULL); } static const struct pci_device_id it8213_pci_tbl[] = { @@ -198,6 +194,7 @@ static struct pci_driver driver = { .name = "ITE8213_IDE", .id_table = it8213_pci_tbl, .probe = it8213_init_one, + .remove = ide_pci_remove, }; static int __init it8213_ide_init(void) @@ -205,7 +202,13 @@ static int __init it8213_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit it8213_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(it8213_ide_init); +module_exit(it8213_ide_exit); MODULE_AUTHOR("Jack Lee, Alan Cox"); MODULE_DESCRIPTION("PCI driver module for the ITE 8213"); diff --git a/drivers/ide/pci/it821x.c b/drivers/ide/pci/it821x.c index cbf647202994..e16a1d113a2a 100644 --- a/drivers/ide/pci/it821x.c +++ b/drivers/ide/pci/it821x.c @@ -67,6 +67,8 @@ #include <linux/ide.h> #include <linux/init.h> +#define DRV_NAME "it821x" + struct it821x_dev { unsigned int smart:1, /* Are we in smart raid mode */ @@ -534,8 +536,9 @@ static struct ide_dma_ops it821x_pass_through_dma_ops = { static void __devinit init_hwif_it821x(ide_hwif_t *hwif) { struct pci_dev *dev = to_pci_dev(hwif->dev); - struct it821x_dev **itdevs = (struct it821x_dev **)pci_get_drvdata(dev); - struct it821x_dev *idev = itdevs[hwif->channel]; + struct ide_host *host = pci_get_drvdata(dev); + struct it821x_dev *itdevs = host->host_priv; + struct it821x_dev *idev = itdevs + hwif->channel; u8 conf; ide_set_hwifdata(hwif, idev); @@ -568,7 +571,8 @@ static void __devinit init_hwif_it821x(ide_hwif_t *hwif) idev->timing10 = 1; hwif->host_flags |= IDE_HFLAG_NO_ATAPI_DMA; if (idev->smart == 0) - printk(KERN_WARNING "it821x: Revision 0x10, workarounds activated.\n"); + printk(KERN_WARNING DRV_NAME " %s: revision 0x10, " + "workarounds activated\n", pci_name(dev)); } if (idev->smart == 0) { @@ -601,18 +605,20 @@ static void __devinit it8212_disable_raid(struct pci_dev *dev) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20); } -static unsigned int __devinit init_chipset_it821x(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_it821x(struct pci_dev *dev) { u8 conf; static char *mode[2] = { "pass through", "smart" }; /* Force the card into bypass mode if so requested */ if (it8212_noraid) { - printk(KERN_INFO "it8212: forcing bypass mode.\n"); + printk(KERN_INFO DRV_NAME " %s: forcing bypass mode\n", + pci_name(dev)); it8212_disable_raid(dev); } pci_read_config_byte(dev, 0x50, &conf); - printk(KERN_INFO "it821x: controller in %s mode.\n", mode[conf & 1]); + printk(KERN_INFO DRV_NAME " %s: controller in %s mode\n", + pci_name(dev), mode[conf & 1]); return 0; } @@ -624,17 +630,12 @@ static const struct ide_port_ops it821x_port_ops = { .cable_detect = it821x_cable_detect, }; -#define DECLARE_ITE_DEV(name_str) \ - { \ - .name = name_str, \ - .init_chipset = init_chipset_it821x, \ - .init_hwif = init_hwif_it821x, \ - .port_ops = &it821x_port_ops, \ - .pio_mask = ATA_PIO4, \ - } - -static const struct ide_port_info it821x_chipsets[] __devinitdata = { - /* 0 */ DECLARE_ITE_DEV("IT8212"), +static const struct ide_port_info it821x_chipset __devinitdata = { + .name = DRV_NAME, + .init_chipset = init_chipset_it821x, + .init_hwif = init_hwif_it821x, + .port_ops = &it821x_port_ops, + .pio_mask = ATA_PIO4, }; /** @@ -648,23 +649,29 @@ static const struct ide_port_info it821x_chipsets[] __devinitdata = { static int __devinit it821x_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - struct it821x_dev *itdevs[2] = { NULL, NULL} , *itdev; - unsigned int i; - - for (i = 0; i < 2; i++) { - itdev = kzalloc(sizeof(*itdev), GFP_KERNEL); - if (itdev == NULL) { - kfree(itdevs[0]); - printk(KERN_ERR "it821x: out of memory\n"); - return -ENOMEM; - } + struct it821x_dev *itdevs; + int rc; - itdevs[i] = itdev; + itdevs = kzalloc(2 * sizeof(*itdevs), GFP_KERNEL); + if (itdevs == NULL) { + printk(KERN_ERR DRV_NAME " %s: out of memory\n", pci_name(dev)); + return -ENOMEM; } - pci_set_drvdata(dev, itdevs); + rc = ide_pci_init_one(dev, &it821x_chipset, itdevs); + if (rc) + kfree(itdevs); - return ide_setup_pci_device(dev, &it821x_chipsets[id->driver_data]); + return rc; +} + +static void __devexit it821x_remove(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + struct it821x_dev *itdevs = host->host_priv; + + ide_pci_remove(dev); + kfree(itdevs); } static const struct pci_device_id it821x_pci_tbl[] = { @@ -679,6 +686,7 @@ static struct pci_driver driver = { .name = "ITE821x IDE", .id_table = it821x_pci_tbl, .probe = it821x_init_one, + .remove = it821x_remove, }; static int __init it821x_ide_init(void) @@ -686,7 +694,13 @@ static int __init it821x_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit it821x_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(it821x_ide_init); +module_exit(it821x_ide_exit); module_param_named(noraid, it8212_noraid, int, S_IRUGO); MODULE_PARM_DESC(noraid, "Force card into bypass mode"); diff --git a/drivers/ide/pci/jmicron.c b/drivers/ide/pci/jmicron.c index 96ef7394f283..545b6e172d9b 100644 --- a/drivers/ide/pci/jmicron.c +++ b/drivers/ide/pci/jmicron.c @@ -12,6 +12,8 @@ #include <linux/ide.h> #include <linux/init.h> +#define DRV_NAME "jmicron" + typedef enum { PORT_PATA0 = 0, PORT_PATA1 = 1, @@ -102,7 +104,7 @@ static const struct ide_port_ops jmicron_port_ops = { }; static const struct ide_port_info jmicron_chipset __devinitdata = { - .name = "JMB", + .name = DRV_NAME, .enablebits = { { 0x40, 0x01, 0x01 }, { 0x40, 0x10, 0x10 } }, .port_ops = &jmicron_port_ops, .pio_mask = ATA_PIO5, @@ -121,7 +123,7 @@ static const struct ide_port_info jmicron_chipset __devinitdata = { static int __devinit jmicron_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &jmicron_chipset); + return ide_pci_init_one(dev, &jmicron_chipset, NULL); } /* All JMB PATA controllers have and will continue to have the same @@ -152,6 +154,7 @@ static struct pci_driver driver = { .name = "JMicron IDE", .id_table = jmicron_pci_tbl, .probe = jmicron_init_one, + .remove = ide_pci_remove, }; static int __init jmicron_ide_init(void) @@ -159,7 +162,13 @@ static int __init jmicron_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit jmicron_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(jmicron_ide_init); +module_exit(jmicron_ide_exit); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("PCI driver module for the JMicron in legacy modes"); diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index 45ba71a7182f..ffefcd15196c 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -19,6 +19,8 @@ #include <asm/io.h> +#define DRV_NAME "ns87415" + #ifdef CONFIG_SUPERIO /* SUPERIO 87560 is a PoS chip that NatSem denies exists. * Unfortunately, it's built-in on all Astro-based PA-RISC workstations @@ -28,10 +30,6 @@ */ #include <asm/superio.h> -static unsigned long superio_ide_status[2]; -static unsigned long superio_ide_select[2]; -static unsigned long superio_ide_dma_status[2]; - #define SUPERIO_IDE_MAX_RETRIES 25 /* Because of a defect in Super I/O, all reads of the PCI DMA status @@ -40,27 +38,28 @@ static unsigned long superio_ide_dma_status[2]; */ static u8 superio_ide_inb (unsigned long port) { - if (port == superio_ide_status[0] || - port == superio_ide_status[1] || - port == superio_ide_select[0] || - port == superio_ide_select[1] || - port == superio_ide_dma_status[0] || - port == superio_ide_dma_status[1]) { - u8 tmp; - int retries = SUPERIO_IDE_MAX_RETRIES; + u8 tmp; + int retries = SUPERIO_IDE_MAX_RETRIES; - /* printk(" [ reading port 0x%x with retry ] ", port); */ + /* printk(" [ reading port 0x%x with retry ] ", port); */ - do { - tmp = inb(port); - if (tmp == 0) - udelay(50); - } while (tmp == 0 && retries-- > 0); + do { + tmp = inb(port); + if (tmp == 0) + udelay(50); + } while (tmp == 0 && retries-- > 0); - return tmp; - } + return tmp; +} - return inb(port); +static u8 superio_read_status(ide_hwif_t *hwif) +{ + return superio_ide_inb(hwif->io_ports.status_addr); +} + +static u8 superio_read_sff_dma_status(ide_hwif_t *hwif) +{ + return superio_ide_inb(hwif->dma_base + ATA_DMA_STATUS); } static void superio_tf_read(ide_drive_t *drive, ide_task_t *task) @@ -78,6 +77,8 @@ static void superio_tf_read(ide_drive_t *drive, ide_task_t *task) /* be sure we're looking at the low order bits */ outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + if (task->tf_flags & IDE_TFLAG_IN_FEATURE) + tf->feature = inb(io_ports->feature_addr); if (task->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = inb(io_ports->nsect_addr); if (task->tf_flags & IDE_TFLAG_IN_LBAL) @@ -105,36 +106,32 @@ static void superio_tf_read(ide_drive_t *drive, ide_task_t *task) } } -static void __devinit superio_ide_init_iops (struct hwif_s *hwif) -{ - struct pci_dev *pdev = to_pci_dev(hwif->dev); - u32 base, dmabase; - u8 port = hwif->channel, tmp; - - base = pci_resource_start(pdev, port * 2) & ~3; - dmabase = pci_resource_start(pdev, 4) & ~3; - - superio_ide_status[port] = base + 7; - superio_ide_select[port] = base + 6; - superio_ide_dma_status[port] = dmabase + (!port ? 2 : 0xa); +static const struct ide_tp_ops superio_tp_ops = { + .exec_command = ide_exec_command, + .read_status = superio_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = superio_read_sff_dma_status, - /* Clear error/interrupt, enable dma */ - tmp = superio_ide_inb(superio_ide_dma_status[port]); - outb(tmp | 0x66, superio_ide_dma_status[port]); + .set_irq = ide_set_irq, - hwif->tf_read = superio_tf_read; + .tf_load = ide_tf_load, + .tf_read = superio_tf_read, - /* We need to override inb to workaround a SuperIO errata */ - hwif->INB = superio_ide_inb; -} + .input_data = ide_input_data, + .output_data = ide_output_data, +}; -static void __devinit init_iops_ns87415(ide_hwif_t *hwif) +static void __devinit superio_init_iops(struct hwif_s *hwif) { - struct pci_dev *dev = to_pci_dev(hwif->dev); + struct pci_dev *pdev = to_pci_dev(hwif->dev); + u32 dma_stat; + u8 port = hwif->channel, tmp; - if (PCI_SLOT(dev->devfn) == 0xE) - /* Built-in - assume it's under superio. */ - superio_ide_init_iops(hwif); + dma_stat = (pci_resource_start(pdev, 4) & ~3) + (!port ? 2 : 0xa); + + /* Clear error/interrupt, enable dma */ + tmp = superio_ide_inb(dma_stat); + outb(tmp | 0x66, dma_stat); } #endif @@ -200,14 +197,14 @@ static int ns87415_dma_end(ide_drive_t *drive) u8 dma_stat = 0, dma_cmd = 0; drive->waiting_for_dma = 0; - dma_stat = hwif->INB(hwif->dma_status); - /* get dma command mode */ - dma_cmd = hwif->INB(hwif->dma_command); + dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); + /* get DMA command mode */ + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); /* stop DMA */ - outb(dma_cmd & ~1, hwif->dma_command); + outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD); /* from ERRATA: clear the INTR & ERROR bits */ - dma_cmd = hwif->INB(hwif->dma_command); - outb(dma_cmd | 6, hwif->dma_command); + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); + outb(dma_cmd | 6, hwif->dma_base + ATA_DMA_CMD); /* and free any DMA resources */ ide_destroy_dmatable(drive); /* verify good DMA status */ @@ -276,7 +273,7 @@ static void __devinit init_hwif_ns87415 (ide_hwif_t *hwif) outb(8, hwif->io_ports.ctl_addr); do { udelay(50); - stat = hwif->INB(hwif->io_ports.status_addr); + stat = hwif->tp_ops->read_status(hwif); if (stat == 0xff) break; } while ((stat & BUSY_STAT) && --timeout); @@ -291,7 +288,7 @@ static void __devinit init_hwif_ns87415 (ide_hwif_t *hwif) if (!hwif->dma_base) return; - outb(0x60, hwif->dma_status); + outb(0x60, hwif->dma_base + ATA_DMA_STATUS); } static const struct ide_port_ops ns87415_port_ops = { @@ -310,10 +307,7 @@ static const struct ide_dma_ops ns87415_dma_ops = { }; static const struct ide_port_info ns87415_chipset __devinitdata = { - .name = "NS87415", -#ifdef CONFIG_SUPERIO - .init_iops = init_iops_ns87415, -#endif + .name = DRV_NAME, .init_hwif = init_hwif_ns87415, .port_ops = &ns87415_port_ops, .dma_ops = &ns87415_dma_ops, @@ -323,7 +317,16 @@ static const struct ide_port_info ns87415_chipset __devinitdata = { static int __devinit ns87415_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &ns87415_chipset); + struct ide_port_info d = ns87415_chipset; + +#ifdef CONFIG_SUPERIO + if (PCI_SLOT(dev->devfn) == 0xE) { + /* Built-in - assume it's under superio. */ + d.init_iops = superio_init_iops; + d.tp_ops = &superio_tp_ops; + } +#endif + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id ns87415_pci_tbl[] = { @@ -336,6 +339,7 @@ static struct pci_driver driver = { .name = "NS87415_IDE", .id_table = ns87415_pci_tbl, .probe = ns87415_init_one, + .remove = ide_pci_remove, }; static int __init ns87415_ide_init(void) @@ -343,7 +347,13 @@ static int __init ns87415_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit ns87415_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(ns87415_ide_init); +module_exit(ns87415_ide_exit); MODULE_AUTHOR("Mark Lord, Eddie Dost, Andre Hedrick"); MODULE_DESCRIPTION("PCI driver module for NS87415 IDE"); diff --git a/drivers/ide/pci/opti621.c b/drivers/ide/pci/opti621.c index 725c80508d90..e28e672ddafc 100644 --- a/drivers/ide/pci/opti621.c +++ b/drivers/ide/pci/opti621.c @@ -90,6 +90,8 @@ #include <asm/io.h> +#define DRV_NAME "opti621" + #define READ_REG 0 /* index of Read cycle timing register */ #define WRITE_REG 1 /* index of Write cycle timing register */ #define CNTRL_REG 3 /* index of Control register */ @@ -200,7 +202,7 @@ static const struct ide_port_ops opti621_port_ops = { }; static const struct ide_port_info opti621_chipset __devinitdata = { - .name = "OPTI621/X", + .name = DRV_NAME, .enablebits = { {0x45, 0x80, 0x00}, {0x40, 0x08, 0x00} }, .port_ops = &opti621_port_ops, .host_flags = IDE_HFLAG_NO_DMA, @@ -209,7 +211,7 @@ static const struct ide_port_info opti621_chipset __devinitdata = { static int __devinit opti621_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &opti621_chipset); + return ide_pci_init_one(dev, &opti621_chipset, NULL); } static const struct pci_device_id opti621_pci_tbl[] = { @@ -223,6 +225,7 @@ static struct pci_driver driver = { .name = "Opti621_IDE", .id_table = opti621_pci_tbl, .probe = opti621_init_one, + .remove = ide_pci_remove, }; static int __init opti621_ide_init(void) @@ -230,7 +233,13 @@ static int __init opti621_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit opti621_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(opti621_ide_init); +module_exit(opti621_ide_exit); MODULE_AUTHOR("Jaromir Koutek, Jan Harkes, Mark Lord"); MODULE_DESCRIPTION("PCI driver module for Opti621 IDE"); diff --git a/drivers/ide/pci/pdc202xx_new.c b/drivers/ide/pci/pdc202xx_new.c index 070df8ab3b21..998615fa285f 100644 --- a/drivers/ide/pci/pdc202xx_new.c +++ b/drivers/ide/pci/pdc202xx_new.c @@ -31,6 +31,8 @@ #include <asm/pci-bridge.h> #endif +#define DRV_NAME "pdc202xx_new" + #undef DEBUG #ifdef DEBUG @@ -324,8 +326,9 @@ static void __devinit apple_kiwi_init(struct pci_dev *pdev) } #endif /* CONFIG_PPC_PMAC */ -static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev) { + const char *name = DRV_NAME; unsigned long dma_base = pci_resource_start(dev, 4); unsigned long sec_dma_base = dma_base + 0x08; long pll_input, pll_output, ratio; @@ -358,12 +361,13 @@ static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev, const cha * registers setting. */ pll_input = detect_pll_input_clock(dma_base); - printk("%s: PLL input clock is %ld kHz\n", name, pll_input / 1000); + printk(KERN_INFO "%s %s: PLL input clock is %ld kHz\n", + name, pci_name(dev), pll_input / 1000); /* Sanity check */ if (unlikely(pll_input < 5000000L || pll_input > 70000000L)) { - printk(KERN_ERR "%s: Bad PLL input clock %ld Hz, giving up!\n", - name, pll_input); + printk(KERN_ERR "%s %s: Bad PLL input clock %ld Hz, giving up!" + "\n", name, pci_name(dev), pll_input); goto out; } @@ -399,7 +403,8 @@ static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev, const cha r = 0x00; } else { /* Invalid ratio */ - printk(KERN_ERR "%s: Bad ratio %ld, giving up!\n", name, ratio); + printk(KERN_ERR "%s %s: Bad ratio %ld, giving up!\n", + name, pci_name(dev), ratio); goto out; } @@ -409,7 +414,8 @@ static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev, const cha if (unlikely(f < 0 || f > 127)) { /* Invalid F */ - printk(KERN_ERR "%s: F[%d] invalid!\n", name, f); + printk(KERN_ERR "%s %s: F[%d] invalid!\n", + name, pci_name(dev), f); goto out; } @@ -455,8 +461,8 @@ static struct pci_dev * __devinit pdc20270_get_dev2(struct pci_dev *dev) if (dev2->irq != dev->irq) { dev2->irq = dev->irq; - printk(KERN_INFO "PDC20270: PCI config space " - "interrupt fixed\n"); + printk(KERN_INFO DRV_NAME " %s: PCI config space " + "interrupt fixed\n", pci_name(dev)); } return dev2; @@ -473,9 +479,9 @@ static const struct ide_port_ops pdcnew_port_ops = { .cable_detect = pdcnew_cable_detect, }; -#define DECLARE_PDCNEW_DEV(name_str, udma) \ +#define DECLARE_PDCNEW_DEV(udma) \ { \ - .name = name_str, \ + .name = DRV_NAME, \ .init_chipset = init_chipset_pdcnew, \ .port_ops = &pdcnew_port_ops, \ .host_flags = IDE_HFLAG_POST_SET_MODE | \ @@ -487,13 +493,8 @@ static const struct ide_port_ops pdcnew_port_ops = { } static const struct ide_port_info pdcnew_chipsets[] __devinitdata = { - /* 0 */ DECLARE_PDCNEW_DEV("PDC20268", ATA_UDMA5), - /* 1 */ DECLARE_PDCNEW_DEV("PDC20269", ATA_UDMA6), - /* 2 */ DECLARE_PDCNEW_DEV("PDC20270", ATA_UDMA5), - /* 3 */ DECLARE_PDCNEW_DEV("PDC20271", ATA_UDMA6), - /* 4 */ DECLARE_PDCNEW_DEV("PDC20275", ATA_UDMA6), - /* 5 */ DECLARE_PDCNEW_DEV("PDC20276", ATA_UDMA6), - /* 6 */ DECLARE_PDCNEW_DEV("PDC20277", ATA_UDMA6), + /* 0: PDC202{68,70} */ DECLARE_PDCNEW_DEV(ATA_UDMA5), + /* 1: PDC202{69,71,75,76,77} */ DECLARE_PDCNEW_DEV(ATA_UDMA6), }; /** @@ -507,13 +508,10 @@ static const struct ide_port_info pdcnew_chipsets[] __devinitdata = { static int __devinit pdc202new_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - const struct ide_port_info *d; + const struct ide_port_info *d = &pdcnew_chipsets[id->driver_data]; struct pci_dev *bridge = dev->bus->self; - u8 idx = id->driver_data; - - d = &pdcnew_chipsets[idx]; - if (idx == 2 && bridge && + if (dev->device == PCI_DEVICE_ID_PROMISE_20270 && bridge && bridge->vendor == PCI_VENDOR_ID_DEC && bridge->device == PCI_DEVICE_ID_DEC_21150) { struct pci_dev *dev2; @@ -524,33 +522,42 @@ static int __devinit pdc202new_init_one(struct pci_dev *dev, const struct pci_de dev2 = pdc20270_get_dev2(dev); if (dev2) { - int ret = ide_setup_pci_devices(dev, dev2, d); + int ret = ide_pci_init_two(dev, dev2, d, NULL); if (ret < 0) pci_dev_put(dev2); return ret; } } - if (idx == 5 && bridge && + if (dev->device == PCI_DEVICE_ID_PROMISE_20276 && bridge && bridge->vendor == PCI_VENDOR_ID_INTEL && (bridge->device == PCI_DEVICE_ID_INTEL_I960 || bridge->device == PCI_DEVICE_ID_INTEL_I960RM)) { - printk(KERN_INFO "PDC20276: attached to I2O RAID controller, " - "skipping\n"); + printk(KERN_INFO DRV_NAME " %s: attached to I2O RAID controller," + " skipping\n", pci_name(dev)); return -ENODEV; } - return ide_setup_pci_device(dev, d); + return ide_pci_init_one(dev, d, NULL); +} + +static void __devexit pdc202new_remove(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + struct pci_dev *dev2 = host->dev[1] ? to_pci_dev(host->dev[1]) : NULL; + + ide_pci_remove(dev); + pci_dev_put(dev2); } static const struct pci_device_id pdc202new_pci_tbl[] = { { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20268), 0 }, { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20269), 1 }, - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20270), 2 }, - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20271), 3 }, - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20275), 4 }, - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20276), 5 }, - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20277), 6 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20270), 0 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20271), 1 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20275), 1 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20276), 1 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20277), 1 }, { 0, }, }; MODULE_DEVICE_TABLE(pci, pdc202new_pci_tbl); @@ -559,6 +566,7 @@ static struct pci_driver driver = { .name = "Promise_IDE", .id_table = pdc202new_pci_tbl, .probe = pdc202new_init_one, + .remove = pdc202new_remove, }; static int __init pdc202new_ide_init(void) @@ -566,7 +574,13 @@ static int __init pdc202new_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit pdc202new_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(pdc202new_ide_init); +module_exit(pdc202new_ide_exit); MODULE_AUTHOR("Andre Hedrick, Frank Tiernan"); MODULE_DESCRIPTION("PCI driver module for Promise PDC20268 and higher"); diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c index fca89eda5c02..6ff2def58da0 100644 --- a/drivers/ide/pci/pdc202xx_old.c +++ b/drivers/ide/pci/pdc202xx_old.c @@ -20,6 +20,8 @@ #include <asm/io.h> +#define DRV_NAME "pdc202xx_old" + #define PDC202XX_DEBUG_DRIVE_INFO 0 static const char *pdc_quirk_drives[] = { @@ -206,7 +208,7 @@ static int pdc202xx_dma_test_irq(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); unsigned long high_16 = hwif->extra_base - 16; - u8 dma_stat = inb(hwif->dma_status); + u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); u8 sc1d = inb(high_16 + 0x001d); if (hwif->channel) { @@ -263,8 +265,7 @@ static void pdc202xx_dma_timeout(ide_drive_t *drive) ide_dma_timeout(drive); } -static unsigned int __devinit init_chipset_pdc202xx(struct pci_dev *dev, - const char *name) +static unsigned int __devinit init_chipset_pdc202xx(struct pci_dev *dev) { unsigned long dmabase = pci_resource_start(dev, 4); u8 udma_speed_flag = 0, primary_mode = 0, secondary_mode = 0; @@ -304,15 +305,14 @@ static void __devinit pdc202ata4_fixup_irq(struct pci_dev *dev, if (irq != irq2) { pci_write_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, irq); /* 0xbc */ - printk(KERN_INFO "%s: PCI config space interrupt " - "mirror fixed\n", name); + printk(KERN_INFO "%s %s: PCI config space interrupt " + "mirror fixed\n", name, pci_name(dev)); } } } #define IDE_HFLAGS_PDC202XX \ (IDE_HFLAG_ERROR_STOPS_FIFO | \ - IDE_HFLAG_ABUSE_SET_DMA_MODE | \ IDE_HFLAG_OFF_BOARD) static const struct ide_port_ops pdc20246_port_ops = { @@ -351,9 +351,9 @@ static const struct ide_dma_ops pdc2026x_dma_ops = { .dma_timeout = pdc202xx_dma_timeout, }; -#define DECLARE_PDC2026X_DEV(name_str, udma, extra_flags) \ +#define DECLARE_PDC2026X_DEV(udma, extra_flags) \ { \ - .name = name_str, \ + .name = DRV_NAME, \ .init_chipset = init_chipset_pdc202xx, \ .port_ops = &pdc2026x_port_ops, \ .dma_ops = &pdc2026x_dma_ops, \ @@ -364,8 +364,8 @@ static const struct ide_dma_ops pdc2026x_dma_ops = { } static const struct ide_port_info pdc202xx_chipsets[] __devinitdata = { - { /* 0 */ - .name = "PDC20246", + { /* 0: PDC20246 */ + .name = DRV_NAME, .init_chipset = init_chipset_pdc202xx, .port_ops = &pdc20246_port_ops, .dma_ops = &pdc20246_dma_ops, @@ -375,10 +375,10 @@ static const struct ide_port_info pdc202xx_chipsets[] __devinitdata = { .udma_mask = ATA_UDMA2, }, - /* 1 */ DECLARE_PDC2026X_DEV("PDC20262", ATA_UDMA4, 0), - /* 2 */ DECLARE_PDC2026X_DEV("PDC20263", ATA_UDMA4, 0), - /* 3 */ DECLARE_PDC2026X_DEV("PDC20265", ATA_UDMA5, IDE_HFLAG_RQSIZE_256), - /* 4 */ DECLARE_PDC2026X_DEV("PDC20267", ATA_UDMA5, IDE_HFLAG_RQSIZE_256), + /* 1: PDC2026{2,3} */ + DECLARE_PDC2026X_DEV(ATA_UDMA4, 0), + /* 2: PDC2026{5,7} */ + DECLARE_PDC2026X_DEV(ATA_UDMA5, IDE_HFLAG_RQSIZE_256), }; /** @@ -397,31 +397,32 @@ static int __devinit pdc202xx_init_one(struct pci_dev *dev, const struct pci_dev d = &pdc202xx_chipsets[idx]; - if (idx < 3) + if (idx < 2) pdc202ata4_fixup_irq(dev, d->name); - if (idx == 3) { + if (dev->vendor == PCI_DEVICE_ID_PROMISE_20265) { struct pci_dev *bridge = dev->bus->self; if (bridge && bridge->vendor == PCI_VENDOR_ID_INTEL && (bridge->device == PCI_DEVICE_ID_INTEL_I960 || bridge->device == PCI_DEVICE_ID_INTEL_I960RM)) { - printk(KERN_INFO "ide: Skipping Promise PDC20265 " - "attached to I2O RAID controller\n"); + printk(KERN_INFO DRV_NAME " %s: skipping Promise " + "PDC20265 attached to I2O RAID controller\n", + pci_name(dev)); return -ENODEV; } } - return ide_setup_pci_device(dev, d); + return ide_pci_init_one(dev, d, NULL); } static const struct pci_device_id pdc202xx_pci_tbl[] = { { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20246), 0 }, { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20262), 1 }, - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20263), 2 }, - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20265), 3 }, - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20267), 4 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20263), 1 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20265), 2 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20267), 2 }, { 0, }, }; MODULE_DEVICE_TABLE(pci, pdc202xx_pci_tbl); @@ -430,6 +431,7 @@ static struct pci_driver driver = { .name = "Promise_Old_IDE", .id_table = pdc202xx_pci_tbl, .probe = pdc202xx_init_one, + .remove = ide_pci_remove, }; static int __init pdc202xx_ide_init(void) @@ -437,7 +439,13 @@ static int __init pdc202xx_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit pdc202xx_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(pdc202xx_ide_init); +module_exit(pdc202xx_ide_exit); MODULE_AUTHOR("Andre Hedrick, Frank Tiernan"); MODULE_DESCRIPTION("PCI driver module for older Promise IDE"); diff --git a/drivers/ide/pci/piix.c b/drivers/ide/pci/piix.c index f04738d14a6f..7fc3022dcf68 100644 --- a/drivers/ide/pci/piix.c +++ b/drivers/ide/pci/piix.c @@ -54,6 +54,8 @@ #include <asm/io.h> +#define DRV_NAME "piix" + static int no_piix_dma; /** @@ -198,13 +200,12 @@ static void piix_set_dma_mode(ide_drive_t *drive, const u8 speed) /** * init_chipset_ich - set up the ICH chipset * @dev: PCI device to set up - * @name: Name of the device * * Initialize the PCI device as required. For the ICH this turns * out to be nice and simple. */ -static unsigned int __devinit init_chipset_ich(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_ich(struct pci_dev *dev) { u32 extra = 0; @@ -227,9 +228,9 @@ static void piix_dma_clear_irq(ide_drive_t *drive) u8 dma_stat; /* clear the INTR & ERROR bits */ - dma_stat = inb(hwif->dma_status); + dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); /* Should we force the bit as well ? */ - outb(dma_stat, hwif->dma_status); + outb(dma_stat, hwif->dma_base + ATA_DMA_STATUS); } struct ich_laptop { @@ -314,9 +315,9 @@ static const struct ide_port_ops piix_port_ops = { #define IDE_HFLAGS_PIIX 0 #endif -#define DECLARE_PIIX_DEV(name_str, udma) \ +#define DECLARE_PIIX_DEV(udma) \ { \ - .name = name_str, \ + .name = DRV_NAME, \ .init_hwif = init_hwif_piix, \ .enablebits = {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, \ .port_ops = &piix_port_ops, \ @@ -327,9 +328,9 @@ static const struct ide_port_ops piix_port_ops = { .udma_mask = udma, \ } -#define DECLARE_ICH_DEV(name_str, udma) \ +#define DECLARE_ICH_DEV(udma) \ { \ - .name = name_str, \ + .name = DRV_NAME, \ .init_chipset = init_chipset_ich, \ .init_hwif = init_hwif_ich, \ .enablebits = {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, \ @@ -342,45 +343,31 @@ static const struct ide_port_ops piix_port_ops = { } static const struct ide_port_info piix_pci_info[] __devinitdata = { - /* 0 */ DECLARE_PIIX_DEV("PIIXa", 0x00), /* no udma */ - /* 1 */ DECLARE_PIIX_DEV("PIIXb", 0x00), /* no udma */ - - /* 2 */ + /* 0: MPIIX */ { /* * MPIIX actually has only a single IDE channel mapped to * the primary or secondary ports depending on the value * of the bit 14 of the IDETIM register at offset 0x6c */ - .name = "MPIIX", + .name = DRV_NAME, .enablebits = {{0x6d,0xc0,0x80}, {0x6d,0xc0,0xc0}}, .host_flags = IDE_HFLAG_ISA_PORTS | IDE_HFLAG_NO_DMA | IDE_HFLAGS_PIIX, .pio_mask = ATA_PIO4, /* This is a painful system best to let it self tune for now */ }, - - /* 3 */ DECLARE_PIIX_DEV("PIIX3", 0x00), /* no udma */ - /* 4 */ DECLARE_PIIX_DEV("PIIX4", ATA_UDMA2), - /* 5 */ DECLARE_ICH_DEV("ICH0", ATA_UDMA2), - /* 6 */ DECLARE_PIIX_DEV("PIIX4", ATA_UDMA2), - /* 7 */ DECLARE_ICH_DEV("ICH", ATA_UDMA4), - /* 8 */ DECLARE_PIIX_DEV("PIIX4", ATA_UDMA4), - /* 9 */ DECLARE_PIIX_DEV("PIIX4", ATA_UDMA2), - /* 10 */ DECLARE_ICH_DEV("ICH2", ATA_UDMA5), - /* 11 */ DECLARE_ICH_DEV("ICH2M", ATA_UDMA5), - /* 12 */ DECLARE_ICH_DEV("ICH3M", ATA_UDMA5), - /* 13 */ DECLARE_ICH_DEV("ICH3", ATA_UDMA5), - /* 14 */ DECLARE_ICH_DEV("ICH4", ATA_UDMA5), - /* 15 */ DECLARE_ICH_DEV("ICH5", ATA_UDMA5), - /* 16 */ DECLARE_ICH_DEV("C-ICH", ATA_UDMA5), - /* 17 */ DECLARE_ICH_DEV("ICH4", ATA_UDMA5), - /* 18 */ DECLARE_ICH_DEV("ICH5-SATA", ATA_UDMA5), - /* 19 */ DECLARE_ICH_DEV("ICH5", ATA_UDMA5), - /* 20 */ DECLARE_ICH_DEV("ICH6", ATA_UDMA5), - /* 21 */ DECLARE_ICH_DEV("ICH7", ATA_UDMA5), - /* 22 */ DECLARE_ICH_DEV("ICH4", ATA_UDMA5), - /* 23 */ DECLARE_ICH_DEV("ESB2", ATA_UDMA5), - /* 24 */ DECLARE_ICH_DEV("ICH8M", ATA_UDMA5), + /* 1: PIIXa/PIIXb/PIIX3 */ + DECLARE_PIIX_DEV(0x00), /* no udma */ + /* 2: PIIX4 */ + DECLARE_PIIX_DEV(ATA_UDMA2), + /* 3: ICH0 */ + DECLARE_ICH_DEV(ATA_UDMA2), + /* 4: ICH */ + DECLARE_ICH_DEV(ATA_UDMA4), + /* 5: PIIX4 */ + DECLARE_PIIX_DEV(ATA_UDMA4), + /* 6: ICH[2-7]/ICH[2-3]M/C-ICH/ICH5-SATA/ESB2/ICH8M */ + DECLARE_ICH_DEV(ATA_UDMA5), }; /** @@ -394,7 +381,7 @@ static const struct ide_port_info piix_pci_info[] __devinitdata = { static int __devinit piix_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &piix_pci_info[id->driver_data]); + return ide_pci_init_one(dev, &piix_pci_info[id->driver_data], NULL); } /** @@ -421,39 +408,39 @@ static void __devinit piix_check_450nx(void) no_piix_dma = 2; } if(no_piix_dma) - printk(KERN_WARNING "piix: 450NX errata present, disabling IDE DMA.\n"); + printk(KERN_WARNING DRV_NAME ": 450NX errata present, disabling IDE DMA.\n"); if(no_piix_dma == 2) - printk(KERN_WARNING "piix: A BIOS update may resolve this.\n"); + printk(KERN_WARNING DRV_NAME ": A BIOS update may resolve this.\n"); } static const struct pci_device_id piix_pci_tbl[] = { - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371FB_0), 0 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371FB_1), 1 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371MX), 2 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371SB_1), 3 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371AB), 4 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801AB_1), 5 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82443MX_1), 6 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801AA_1), 7 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82372FB_1), 8 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82451NX), 9 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801BA_9), 10 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801BA_8), 11 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801CA_10), 12 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801CA_11), 13 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801DB_11), 14 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801EB_11), 15 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801E_11), 16 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801DB_10), 17 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371FB_0), 1 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371FB_1), 1 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371MX), 0 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371SB_1), 1 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371AB), 2 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801AB_1), 3 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82443MX_1), 2 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801AA_1), 4 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82372FB_1), 5 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82451NX), 2 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801BA_9), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801BA_8), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801CA_10), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801CA_11), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801DB_11), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801EB_11), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801E_11), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801DB_10), 6 }, #ifdef CONFIG_BLK_DEV_IDE_SATA - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801EB_1), 18 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801EB_1), 6 }, #endif - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ESB_2), 19 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH6_19), 20 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH7_21), 21 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801DB_1), 22 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ESB2_18), 23 }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH8_6), 24 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ESB_2), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH6_19), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH7_21), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801DB_1), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ESB2_18), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH8_6), 6 }, { 0, }, }; MODULE_DEVICE_TABLE(pci, piix_pci_tbl); @@ -462,6 +449,7 @@ static struct pci_driver driver = { .name = "PIIX_IDE", .id_table = piix_pci_tbl, .probe = piix_init_one, + .remove = ide_pci_remove, }; static int __init piix_ide_init(void) @@ -470,7 +458,13 @@ static int __init piix_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit piix_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(piix_ide_init); +module_exit(piix_ide_exit); MODULE_AUTHOR("Andre Hedrick, Andrzej Krzysztofowicz"); MODULE_DESCRIPTION("PCI driver module for Intel PIIX IDE"); diff --git a/drivers/ide/pci/rz1000.c b/drivers/ide/pci/rz1000.c index 532154adba29..8d11ee838a2a 100644 --- a/drivers/ide/pci/rz1000.c +++ b/drivers/ide/pci/rz1000.c @@ -21,6 +21,8 @@ #include <linux/ide.h> #include <linux/init.h> +#define DRV_NAME "rz1000" + static void __devinit init_hwif_rz1000 (ide_hwif_t *hwif) { struct pci_dev *dev = to_pci_dev(hwif->dev); @@ -40,7 +42,7 @@ static void __devinit init_hwif_rz1000 (ide_hwif_t *hwif) } static const struct ide_port_info rz1000_chipset __devinitdata = { - .name = "RZ100x", + .name = DRV_NAME, .init_hwif = init_hwif_rz1000, .chipset = ide_rz1000, .host_flags = IDE_HFLAG_NO_DMA, @@ -48,7 +50,7 @@ static const struct ide_port_info rz1000_chipset __devinitdata = { static int __devinit rz1000_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &rz1000_chipset); + return ide_pci_init_one(dev, &rz1000_chipset, NULL); } static const struct pci_device_id rz1000_pci_tbl[] = { @@ -62,6 +64,7 @@ static struct pci_driver driver = { .name = "RZ1000_IDE", .id_table = rz1000_pci_tbl, .probe = rz1000_init_one, + .remove = ide_pci_remove, }; static int __init rz1000_ide_init(void) @@ -69,7 +72,13 @@ static int __init rz1000_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit rz1000_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(rz1000_ide_init); +module_exit(rz1000_ide_exit); MODULE_AUTHOR("Andre Hedrick"); MODULE_DESCRIPTION("PCI driver module for RZ1000 IDE"); diff --git a/drivers/ide/pci/sc1200.c b/drivers/ide/pci/sc1200.c index 14c787b5d95f..8efaed16fea3 100644 --- a/drivers/ide/pci/sc1200.c +++ b/drivers/ide/pci/sc1200.c @@ -22,6 +22,8 @@ #include <asm/io.h> +#define DRV_NAME "sc1200" + #define SC1200_REV_A 0x00 #define SC1200_REV_B1 0x01 #define SC1200_REV_B3 0x02 @@ -234,21 +236,11 @@ static int sc1200_suspend (struct pci_dev *dev, pm_message_t state) * we only save state when going from full power to less */ if (state.event == PM_EVENT_ON) { - struct sc1200_saved_state *ss; + struct ide_host *host = pci_get_drvdata(dev); + struct sc1200_saved_state *ss = host->host_priv; unsigned int r; /* - * allocate a permanent save area, if not already allocated - */ - ss = (struct sc1200_saved_state *)pci_get_drvdata(dev); - if (ss == NULL) { - ss = kmalloc(sizeof(*ss), GFP_KERNEL); - if (ss == NULL) - return -ENOMEM; - pci_set_drvdata(dev, ss); - } - - /* * save timing registers * (this may be unnecessary if BIOS also does it) */ @@ -263,7 +255,8 @@ static int sc1200_suspend (struct pci_dev *dev, pm_message_t state) static int sc1200_resume (struct pci_dev *dev) { - struct sc1200_saved_state *ss; + struct ide_host *host = pci_get_drvdata(dev); + struct sc1200_saved_state *ss = host->host_priv; unsigned int r; int i; @@ -271,16 +264,12 @@ static int sc1200_resume (struct pci_dev *dev) if (i) return i; - ss = (struct sc1200_saved_state *)pci_get_drvdata(dev); - /* * restore timing registers * (this may be unnecessary if BIOS also does it) */ - if (ss) { - for (r = 0; r < 8; r++) - pci_write_config_dword(dev, 0x40 + r * 4, ss->regs[r]); - } + for (r = 0; r < 8; r++) + pci_write_config_dword(dev, 0x40 + r * 4, ss->regs[r]); return 0; } @@ -304,7 +293,7 @@ static const struct ide_dma_ops sc1200_dma_ops = { }; static const struct ide_port_info sc1200_chipset __devinitdata = { - .name = "SC1200", + .name = DRV_NAME, .port_ops = &sc1200_port_ops, .dma_ops = &sc1200_dma_ops, .host_flags = IDE_HFLAG_SERIALIZE | @@ -317,7 +306,19 @@ static const struct ide_port_info sc1200_chipset __devinitdata = { static int __devinit sc1200_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &sc1200_chipset); + struct sc1200_saved_state *ss = NULL; + int rc; + +#ifdef CONFIG_PM + ss = kmalloc(sizeof(*ss), GFP_KERNEL); + if (ss == NULL) + return -ENOMEM; +#endif + rc = ide_pci_init_one(dev, &sc1200_chipset, ss); + if (rc) + kfree(ss); + + return rc; } static const struct pci_device_id sc1200_pci_tbl[] = { @@ -330,6 +331,7 @@ static struct pci_driver driver = { .name = "SC1200_IDE", .id_table = sc1200_pci_tbl, .probe = sc1200_init_one, + .remove = ide_pci_remove, #ifdef CONFIG_PM .suspend = sc1200_suspend, .resume = sc1200_resume, @@ -341,7 +343,13 @@ static int __init sc1200_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit sc1200_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(sc1200_ide_init); +module_exit(sc1200_ide_exit); MODULE_AUTHOR("Mark Lord"); MODULE_DESCRIPTION("PCI driver module for NS SC1200 IDE"); diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 789c66dfbde5..94a7ab864236 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -65,7 +65,7 @@ static struct scc_ports { unsigned long ctl, dma; - ide_hwif_t *hwif; /* for removing port from system */ + struct ide_host *host; /* for removing port from system */ } scc_ports[MAX_HWIFS]; /* PIO transfer mode table */ @@ -126,6 +126,46 @@ static u8 scc_ide_inb(unsigned long port) return (u8)data; } +static void scc_exec_command(ide_hwif_t *hwif, u8 cmd) +{ + out_be32((void *)hwif->io_ports.command_addr, cmd); + eieio(); + in_be32((void *)(hwif->dma_base + 0x01c)); + eieio(); +} + +static u8 scc_read_status(ide_hwif_t *hwif) +{ + return (u8)in_be32((void *)hwif->io_ports.status_addr); +} + +static u8 scc_read_altstatus(ide_hwif_t *hwif) +{ + return (u8)in_be32((void *)hwif->io_ports.ctl_addr); +} + +static u8 scc_read_sff_dma_status(ide_hwif_t *hwif) +{ + return (u8)in_be32((void *)(hwif->dma_base + 4)); +} + +static void scc_set_irq(ide_hwif_t *hwif, int on) +{ + u8 ctl = ATA_DEVCTL_OBS; + + if (on == 4) { /* hack for SRST */ + ctl |= 4; + on &= ~4; + } + + ctl |= on ? 0 : 2; + + out_be32((void *)hwif->io_ports.ctl_addr, ctl); + eieio(); + in_be32((void *)(hwif->dma_base + 0x01c)); + eieio(); +} + static void scc_ide_insw(unsigned long port, void *addr, u32 count) { u16 *ptr = (u16 *)addr; @@ -148,14 +188,6 @@ static void scc_ide_outb(u8 addr, unsigned long port) out_be32((void*)port, addr); } -static void scc_ide_outbsync(ide_hwif_t *hwif, u8 addr, unsigned long port) -{ - out_be32((void*)port, addr); - eieio(); - in_be32((void*)(hwif->dma_base + 0x01c)); - eieio(); -} - static void scc_ide_outsw(unsigned long port, void *addr, u32 count) { @@ -261,14 +293,14 @@ static void scc_dma_host_set(ide_drive_t *drive, int on) { ide_hwif_t *hwif = drive->hwif; u8 unit = (drive->select.b.unit & 0x01); - u8 dma_stat = scc_ide_inb(hwif->dma_status); + u8 dma_stat = scc_ide_inb(hwif->dma_base + 4); if (on) dma_stat |= (1 << (5 + unit)); else dma_stat &= ~(1 << (5 + unit)); - scc_ide_outb(dma_stat, hwif->dma_status); + scc_ide_outb(dma_stat, hwif->dma_base + 4); } /** @@ -304,13 +336,13 @@ static int scc_dma_setup(ide_drive_t *drive) out_be32((void __iomem *)(hwif->dma_base + 8), hwif->dmatable_dma); /* specify r/w */ - out_be32((void __iomem *)hwif->dma_command, reading); + out_be32((void __iomem *)hwif->dma_base, reading); - /* read dma_status for INTR & ERROR flags */ - dma_stat = in_be32((void __iomem *)hwif->dma_status); + /* read DMA status for INTR & ERROR flags */ + dma_stat = in_be32((void __iomem *)(hwif->dma_base + 4)); /* clear INTR & ERROR flags */ - out_be32((void __iomem *)hwif->dma_status, dma_stat|6); + out_be32((void __iomem *)(hwif->dma_base + 4), dma_stat | 6); drive->waiting_for_dma = 1; return 0; } @@ -318,10 +350,10 @@ static int scc_dma_setup(ide_drive_t *drive) static void scc_dma_start(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - u8 dma_cmd = scc_ide_inb(hwif->dma_command); + u8 dma_cmd = scc_ide_inb(hwif->dma_base); /* start DMA */ - scc_ide_outb(dma_cmd | 1, hwif->dma_command); + scc_ide_outb(dma_cmd | 1, hwif->dma_base); hwif->dma = 1; wmb(); } @@ -333,13 +365,13 @@ static int __scc_dma_end(ide_drive_t *drive) drive->waiting_for_dma = 0; /* get DMA command mode */ - dma_cmd = scc_ide_inb(hwif->dma_command); + dma_cmd = scc_ide_inb(hwif->dma_base); /* stop DMA */ - scc_ide_outb(dma_cmd & ~1, hwif->dma_command); + scc_ide_outb(dma_cmd & ~1, hwif->dma_base); /* get DMA status */ - dma_stat = scc_ide_inb(hwif->dma_status); + dma_stat = scc_ide_inb(hwif->dma_base + 4); /* clear the INTR & ERROR bits */ - scc_ide_outb(dma_stat | 6, hwif->dma_status); + scc_ide_outb(dma_stat | 6, hwif->dma_base + 4); /* purge DMA mappings */ ide_destroy_dmatable(drive); /* verify good DMA status */ @@ -359,6 +391,7 @@ static int __scc_dma_end(ide_drive_t *drive) static int scc_dma_end(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); + void __iomem *dma_base = (void __iomem *)hwif->dma_base; unsigned long intsts_port = hwif->dma_base + 0x014; u32 reg; int dma_stat, data_loss = 0; @@ -397,7 +430,7 @@ static int scc_dma_end(ide_drive_t *drive) 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); + out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS); continue; } @@ -412,7 +445,7 @@ static int scc_dma_end(ide_drive_t *drive) 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); + out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS); continue; } @@ -420,12 +453,12 @@ static int scc_dma_end(ide_drive_t *drive) 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); + out_be32(dma_base, in_be32(dma_base) & ~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); + out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS); printk(KERN_WARNING "%s: Illegal Configuration\n", SCC_PATA_NAME); out_be32((void __iomem *)intsts_port, INTSTS_ICERR|INTSTS_BMSINT); @@ -553,14 +586,9 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) { struct scc_ports *ports = pci_get_drvdata(dev); - ide_hwif_t *hwif = NULL; - hw_regs_t hw; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - int i; - - hwif = ide_find_port_slot(d); - if (hwif == NULL) - return -ENOMEM; + struct ide_host *host; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; + int i, rc; memset(&hw, 0, sizeof(hw)); for (i = 0; i <= 8; i++) @@ -568,11 +596,12 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev, hw.irq = dev->irq; hw.dev = &dev->dev; hw.chipset = ide_pci; - ide_init_port_hw(hwif, &hw); - idx[0] = hwif->index; + rc = ide_host_add(d, hws, &host); + if (rc) + return rc; - ide_device_add(idx, d); + ports->host = host; return 0; } @@ -701,6 +730,8 @@ static void scc_tf_read(ide_drive_t *drive, ide_task_t *task) /* be sure we're looking at the low order bits */ scc_ide_outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + if (task->tf_flags & IDE_TFLAG_IN_FEATURE) + tf->feature = scc_ide_inb(io_ports->feature_addr); if (task->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = scc_ide_inb(io_ports->nsect_addr); if (task->tf_flags & IDE_TFLAG_IN_LBAL) @@ -774,16 +805,6 @@ static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif) ide_set_hwifdata(hwif, ports); - hwif->tf_load = scc_tf_load; - hwif->tf_read = scc_tf_read; - - hwif->input_data = scc_input_data; - hwif->output_data = scc_output_data; - - hwif->INB = scc_ide_inb; - hwif->OUTB = scc_ide_outb; - hwif->OUTBSYNC = scc_ide_outbsync; - hwif->dma_base = dma_base; hwif->config_data = ports->ctl; } @@ -824,11 +845,6 @@ static void __devinit init_hwif_scc(ide_hwif_t *hwif) { struct scc_ports *ports = ide_get_hwifdata(hwif); - ports->hwif = hwif; - - hwif->dma_command = hwif->dma_base; - hwif->dma_status = hwif->dma_base + 0x04; - /* PTERADD */ out_be32((void __iomem *)(hwif->dma_base + 0x018), hwif->dmatable_dma); @@ -838,6 +854,21 @@ static void __devinit init_hwif_scc(ide_hwif_t *hwif) hwif->ultra_mask = ATA_UDMA5; /* 100MHz */ } +static const struct ide_tp_ops scc_tp_ops = { + .exec_command = scc_exec_command, + .read_status = scc_read_status, + .read_altstatus = scc_read_altstatus, + .read_sff_dma_status = scc_read_sff_dma_status, + + .set_irq = scc_set_irq, + + .tf_load = scc_tf_load, + .tf_read = scc_tf_read, + + .input_data = scc_input_data, + .output_data = scc_output_data, +}; + static const struct ide_port_ops scc_port_ops = { .set_pio_mode = scc_set_pio_mode, .set_dma_mode = scc_set_dma_mode, @@ -861,6 +892,7 @@ static const struct ide_dma_ops scc_dma_ops = { .name = name_str, \ .init_iops = init_iops_scc, \ .init_hwif = init_hwif_scc, \ + .tp_ops = &scc_tp_ops, \ .port_ops = &scc_port_ops, \ .dma_ops = &scc_dma_ops, \ .host_flags = IDE_HFLAG_SINGLE, \ @@ -895,7 +927,8 @@ static int __devinit scc_init_one(struct pci_dev *dev, const struct pci_device_i static void __devexit scc_remove(struct pci_dev *dev) { struct scc_ports *ports = pci_get_drvdata(dev); - ide_hwif_t *hwif = ports->hwif; + struct ide_host *host = ports->host; + ide_hwif_t *hwif = host->ports[0]; if (hwif->dmatable_cpu) { pci_free_consistent(dev, PRD_ENTRIES * PRD_BYTES, @@ -903,7 +936,7 @@ static void __devexit scc_remove(struct pci_dev *dev) hwif->dmatable_cpu = NULL; } - ide_unregister(hwif); + ide_host_remove(host); iounmap((void*)ports->dma); iounmap((void*)ports->ctl); diff --git a/drivers/ide/pci/serverworks.c b/drivers/ide/pci/serverworks.c index a1fb20826a5b..d173f2937722 100644 --- a/drivers/ide/pci/serverworks.c +++ b/drivers/ide/pci/serverworks.c @@ -38,6 +38,8 @@ #include <asm/io.h> +#define DRV_NAME "serverworks" + #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) */ @@ -172,7 +174,7 @@ static void svwks_set_dma_mode(ide_drive_t *drive, const u8 speed) pci_write_config_byte(dev, 0x54, ultra_enable); } -static unsigned int __devinit init_chipset_svwks (struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_svwks(struct pci_dev *dev) { unsigned int reg; u8 btr; @@ -188,7 +190,8 @@ static unsigned int __devinit init_chipset_svwks (struct pci_dev *dev, const cha pci_read_config_dword(isa_dev, 0x64, ®); reg &= ~0x00002000; /* disable 600ns interrupt mask */ if(!(reg & 0x00004000)) - printk(KERN_DEBUG "%s: UDMA not BIOS enabled.\n", name); + printk(KERN_DEBUG DRV_NAME " %s: UDMA not BIOS " + "enabled.\n", pci_name(dev)); reg |= 0x00004000; /* enable UDMA/33 support */ pci_write_config_dword(isa_dev, 0x64, reg); } @@ -349,45 +352,47 @@ static const struct ide_port_ops svwks_port_ops = { .cable_detect = svwks_cable_detect, }; -#define IDE_HFLAGS_SVWKS \ - (IDE_HFLAG_LEGACY_IRQS | \ - IDE_HFLAG_ABUSE_SET_DMA_MODE) +#define IDE_HFLAGS_SVWKS IDE_HFLAG_LEGACY_IRQS static const struct ide_port_info serverworks_chipsets[] __devinitdata = { - { /* 0 */ - .name = "SvrWks OSB4", + { /* 0: OSB4 */ + .name = DRV_NAME, .init_chipset = init_chipset_svwks, .port_ops = &osb4_port_ops, .host_flags = IDE_HFLAGS_SVWKS, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = 0x00, /* UDMA is problematic on OSB4 */ - },{ /* 1 */ - .name = "SvrWks CSB5", + }, + { /* 1: CSB5 */ + .name = DRV_NAME, .init_chipset = init_chipset_svwks, .port_ops = &svwks_port_ops, .host_flags = IDE_HFLAGS_SVWKS, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA5, - },{ /* 2 */ - .name = "SvrWks CSB6", + }, + { /* 2: CSB6 */ + .name = DRV_NAME, .init_chipset = init_chipset_svwks, .port_ops = &svwks_port_ops, .host_flags = IDE_HFLAGS_SVWKS, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA5, - },{ /* 3 */ - .name = "SvrWks CSB6", + }, + { /* 3: CSB6-2 */ + .name = DRV_NAME, .init_chipset = init_chipset_svwks, .port_ops = &svwks_port_ops, .host_flags = IDE_HFLAGS_SVWKS | IDE_HFLAG_SINGLE, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA5, - },{ /* 4 */ - .name = "SvrWks HT1000", + }, + { /* 4: HT1000 */ + .name = DRV_NAME, .init_chipset = init_chipset_svwks, .port_ops = &svwks_port_ops, .host_flags = IDE_HFLAGS_SVWKS | IDE_HFLAG_SINGLE, @@ -424,7 +429,7 @@ static int __devinit svwks_init_one(struct pci_dev *dev, const struct pci_device d.host_flags &= ~IDE_HFLAG_SINGLE; } - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id svwks_pci_tbl[] = { @@ -441,6 +446,7 @@ static struct pci_driver driver = { .name = "Serverworks_IDE", .id_table = svwks_pci_tbl, .probe = svwks_init_one, + .remove = ide_pci_remove, }; static int __init svwks_ide_init(void) @@ -448,7 +454,13 @@ static int __init svwks_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit svwks_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(svwks_ide_init); +module_exit(svwks_ide_exit); MODULE_AUTHOR("Michael Aubry. Andrzej Krzysztofowicz, Andre Hedrick"); MODULE_DESCRIPTION("PCI driver module for Serverworks OSB4/CSB5/CSB6 IDE"); diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index c79ff5b41088..42eef19a18f1 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -127,7 +127,7 @@ sgiioc4_checkirq(ide_hwif_t * hwif) return 0; } -static u8 sgiioc4_INB(unsigned long); +static u8 sgiioc4_read_status(ide_hwif_t *); static int sgiioc4_clearirq(ide_drive_t * drive) @@ -141,18 +141,19 @@ sgiioc4_clearirq(ide_drive_t * drive) intr_reg = readl((void __iomem *)other_ir); if (intr_reg & 0x03) { /* Valid IOC4-IDE interrupt */ /* - * Using sgiioc4_INB to read the Status register has a side - * effect of clearing the interrupt. The first read should + * Using sgiioc4_read_status to read the Status register 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 = sgiioc4_INB(io_ports->status_addr); + u8 stat = sgiioc4_read_status(hwif); int count = 0; - stat = sgiioc4_INB(io_ports->status_addr); + + stat = sgiioc4_read_status(hwif); while ((stat & 0x80) && (count++ < 100)) { udelay(1); - stat = sgiioc4_INB(io_ports->status_addr); + stat = sgiioc4_read_status(hwif); } if (intr_reg & 0x02) { @@ -304,9 +305,9 @@ sgiioc4_dma_lost_irq(ide_drive_t * drive) ide_dma_lost_irq(drive); } -static u8 -sgiioc4_INB(unsigned long port) +static u8 sgiioc4_read_status(ide_hwif_t *hwif) { + unsigned long port = hwif->io_ports.status_addr; u8 reg = (u8) readb((void __iomem *) port); if ((port & 0xFFF) == 0x11C) { /* Status register of IOC4 */ @@ -549,6 +550,21 @@ static int sgiioc4_dma_setup(ide_drive_t *drive) return 0; } +static const struct ide_tp_ops sgiioc4_tp_ops = { + .exec_command = ide_exec_command, + .read_status = sgiioc4_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + static const struct ide_port_ops sgiioc4_port_ops = { .set_dma_mode = sgiioc4_set_dma_mode, /* reset DMA engine, clear IRQs */ @@ -571,6 +587,7 @@ static const struct ide_port_info sgiioc4_port_info __devinitdata = { .name = DRV_NAME, .chipset = ide_pci, .init_dma = ide_dma_sgiioc4, + .tp_ops = &sgiioc4_tp_ops, .port_ops = &sgiioc4_port_ops, .dma_ops = &sgiioc4_dma_ops, .host_flags = IDE_HFLAG_MMIO, @@ -583,10 +600,10 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) unsigned long cmd_base, irqport; unsigned long bar0, cmd_phys_base, ctl; void __iomem *virt_base; - ide_hwif_t *hwif; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw; + struct ide_host *host; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; struct ide_port_info d = sgiioc4_port_info; + int rc; /* Get the CmdBlk and CtrlBlk Base Registers */ bar0 = pci_resource_start(dev, 0); @@ -618,30 +635,26 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) hw.chipset = ide_pci; hw.dev = &dev->dev; - hwif = ide_find_port_slot(&d); - if (hwif == NULL) - goto err; - - ide_init_port_hw(hwif, &hw); - - /* The IOC4 uses MMIO rather than Port IO. */ - default_hwif_mmiops(hwif); - /* Initializing chipset IRQ Registers */ writel(0x03, (void __iomem *)(irqport + IOC4_INTR_SET * 4)); - hwif->INB = &sgiioc4_INB; - - idx[0] = hwif->index; + host = ide_host_alloc(&d, hws); + if (host == NULL) { + rc = -ENOMEM; + goto err; + } - if (ide_device_add(idx, &d)) - return -EIO; + rc = ide_host_register(host, &d, hws); + if (rc) + goto err_free; return 0; +err_free: + ide_host_free(host); err: release_mem_region(cmd_phys_base, IOC4_CMD_CTL_BLK_SIZE); iounmap(virt_base); - return -ENOMEM; + return rc; } static unsigned int __devinit diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c index 6e9d7655d89c..b8ad9ad6cf0d 100644 --- a/drivers/ide/pci/siimage.c +++ b/drivers/ide/pci/siimage.c @@ -44,6 +44,8 @@ #include <linux/init.h> #include <linux/io.h> +#define DRV_NAME "siimage" + /** * pdev_is_sata - check if device is SATA * @pdev: PCI device to check @@ -127,9 +129,10 @@ static inline unsigned long siimage_seldev(ide_drive_t *drive, int r) static u8 sil_ioread8(struct pci_dev *dev, unsigned long addr) { + struct ide_host *host = pci_get_drvdata(dev); u8 tmp = 0; - if (pci_get_drvdata(dev)) + if (host->host_priv) tmp = readb((void __iomem *)addr); else pci_read_config_byte(dev, addr, &tmp); @@ -139,9 +142,10 @@ static u8 sil_ioread8(struct pci_dev *dev, unsigned long addr) static u16 sil_ioread16(struct pci_dev *dev, unsigned long addr) { + struct ide_host *host = pci_get_drvdata(dev); u16 tmp = 0; - if (pci_get_drvdata(dev)) + if (host->host_priv) tmp = readw((void __iomem *)addr); else pci_read_config_word(dev, addr, &tmp); @@ -151,7 +155,9 @@ static u16 sil_ioread16(struct pci_dev *dev, unsigned long addr) static void sil_iowrite8(struct pci_dev *dev, u8 val, unsigned long addr) { - if (pci_get_drvdata(dev)) + struct ide_host *host = pci_get_drvdata(dev); + + if (host->host_priv) writeb(val, (void __iomem *)addr); else pci_write_config_byte(dev, addr, val); @@ -159,7 +165,9 @@ static void sil_iowrite8(struct pci_dev *dev, u8 val, unsigned long addr) static void sil_iowrite16(struct pci_dev *dev, u16 val, unsigned long addr) { - if (pci_get_drvdata(dev)) + struct ide_host *host = pci_get_drvdata(dev); + + if (host->host_priv) writew(val, (void __iomem *)addr); else pci_write_config_word(dev, addr, val); @@ -167,7 +175,9 @@ static void sil_iowrite16(struct pci_dev *dev, u16 val, unsigned long addr) static void sil_iowrite32(struct pci_dev *dev, u32 val, unsigned long addr) { - if (pci_get_drvdata(dev)) + struct ide_host *host = pci_get_drvdata(dev); + + if (host->host_priv) writel(val, (void __iomem *)addr); else pci_write_config_dword(dev, addr, val); @@ -334,7 +344,7 @@ static int siimage_io_dma_test_irq(ide_drive_t *drive) unsigned long addr = siimage_selreg(hwif, 1); /* return 1 if INTR asserted */ - if (hwif->INB(hwif->dma_status) & 4) + if (inb(hwif->dma_base + ATA_DMA_STATUS) & 4) return 1; /* return 1 if Device INTR asserted */ @@ -382,7 +392,7 @@ static int siimage_mmio_dma_test_irq(ide_drive_t *drive) } /* return 1 if INTR asserted */ - if (readb((void __iomem *)hwif->dma_status) & 0x04) + if (readb((void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)) & 4) return 1; /* return 1 if Device INTR asserted */ @@ -445,66 +455,24 @@ static void sil_sata_pre_reset(ide_drive_t *drive) } /** - * setup_mmio_siimage - switch controller into MMIO mode - * @dev: PCI device we are configuring - * @name: device name - * - * Attempt to put the device into MMIO mode. There are some slight - * complications here with certain systems where the MMIO BAR isn't - * mapped, so we have to be sure that we can fall back to I/O. - */ - -static unsigned int setup_mmio_siimage(struct pci_dev *dev, const char *name) -{ - resource_size_t bar5 = pci_resource_start(dev, 5); - unsigned long barsize = pci_resource_len(dev, 5); - void __iomem *ioaddr; - - /* - * Drop back to PIO if we can't map the MMIO. Some systems - * seem to get terminally confused in the PCI spaces. - */ - if (!request_mem_region(bar5, barsize, name)) { - printk(KERN_WARNING "siimage: IDE controller MMIO ports not " - "available.\n"); - return 0; - } - - ioaddr = ioremap(bar5, barsize); - if (ioaddr == NULL) { - release_mem_region(bar5, barsize); - return 0; - } - - pci_set_master(dev); - pci_set_drvdata(dev, (void *) ioaddr); - - return 1; -} - -/** * init_chipset_siimage - set up an SI device * @dev: PCI device - * @name: device name * * Perform the initial PCI set up for this device. Attempt to switch * to 133 MHz clocking if the system isn't already set up to do it. */ -static unsigned int __devinit init_chipset_siimage(struct pci_dev *dev, - const char *name) +static unsigned int __devinit init_chipset_siimage(struct pci_dev *dev) { + struct ide_host *host = pci_get_drvdata(dev); + void __iomem *ioaddr = host->host_priv; unsigned long base, scsc_addr; - void __iomem *ioaddr = NULL; - u8 rev = dev->revision, tmp, BA5_EN; + u8 rev = dev->revision, tmp; pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, rev ? 1 : 255); - pci_read_config_byte(dev, 0x8A, &BA5_EN); - - if ((BA5_EN & 0x01) || pci_resource_start(dev, 5)) - if (setup_mmio_siimage(dev, name)) - ioaddr = pci_get_drvdata(dev); + if (ioaddr) + pci_set_master(dev); base = (unsigned long)ioaddr; @@ -571,7 +539,8 @@ static unsigned int __devinit init_chipset_siimage(struct pci_dev *dev, { "== 100", "== 133", "== 2X PCI", "DISABLED!" }; tmp >>= 4; - printk(KERN_INFO "%s: BASE CLOCK %s\n", name, clk_str[tmp & 3]); + printk(KERN_INFO DRV_NAME " %s: BASE CLOCK %s\n", + pci_name(dev), clk_str[tmp & 3]); } return 0; @@ -592,7 +561,8 @@ static unsigned int __devinit init_chipset_siimage(struct pci_dev *dev, static void __devinit init_mmio_iops_siimage(ide_hwif_t *hwif) { struct pci_dev *dev = to_pci_dev(hwif->dev); - void *addr = pci_get_drvdata(dev); + struct ide_host *host = pci_get_drvdata(dev); + void *addr = host->host_priv; u8 ch = hwif->channel; struct ide_io_ports *io_ports = &hwif->io_ports; unsigned long base; @@ -601,7 +571,7 @@ static void __devinit init_mmio_iops_siimage(ide_hwif_t *hwif) * Fill in the basic hwif bits */ hwif->host_flags |= IDE_HFLAG_MMIO; - default_hwif_mmiops(hwif); + hwif->hwif_data = addr; /* @@ -691,16 +661,15 @@ static void __devinit sil_quirkproc(ide_drive_t *drive) static void __devinit init_iops_siimage(ide_hwif_t *hwif) { struct pci_dev *dev = to_pci_dev(hwif->dev); + struct ide_host *host = pci_get_drvdata(dev); hwif->hwif_data = NULL; /* Pessimal until we finish probing */ hwif->rqsize = 15; - if (pci_get_drvdata(dev) == NULL) - return; - - init_mmio_iops_siimage(hwif); + if (host->host_priv) + init_mmio_iops_siimage(hwif); } /** @@ -748,9 +717,9 @@ static const struct ide_dma_ops sil_dma_ops = { .dma_lost_irq = ide_dma_lost_irq, }; -#define DECLARE_SII_DEV(name_str, p_ops) \ +#define DECLARE_SII_DEV(p_ops) \ { \ - .name = name_str, \ + .name = DRV_NAME, \ .init_chipset = init_chipset_siimage, \ .init_iops = init_iops_siimage, \ .port_ops = p_ops, \ @@ -761,9 +730,8 @@ static const struct ide_dma_ops sil_dma_ops = { } static const struct ide_port_info siimage_chipsets[] __devinitdata = { - /* 0 */ DECLARE_SII_DEV("SiI680", &sil_pata_port_ops), - /* 1 */ DECLARE_SII_DEV("SiI3112 Serial ATA", &sil_sata_port_ops), - /* 2 */ DECLARE_SII_DEV("Adaptec AAR-1210SA", &sil_sata_port_ops) + /* 0: SiI680 */ DECLARE_SII_DEV(&sil_pata_port_ops), + /* 1: SiI3112 */ DECLARE_SII_DEV(&sil_sata_port_ops) }; /** @@ -778,8 +746,13 @@ static const struct ide_port_info siimage_chipsets[] __devinitdata = { static int __devinit siimage_init_one(struct pci_dev *dev, const struct pci_device_id *id) { + void __iomem *ioaddr = NULL; + resource_size_t bar5 = pci_resource_start(dev, 5); + unsigned long barsize = pci_resource_len(dev, 5); + int rc; struct ide_port_info d; u8 idx = id->driver_data; + u8 BA5_EN; d = siimage_chipsets[idx]; @@ -787,7 +760,7 @@ static int __devinit siimage_init_one(struct pci_dev *dev, static int first = 1; if (first) { - printk(KERN_INFO "siimage: For full SATA support you " + printk(KERN_INFO DRV_NAME ": For full SATA support you " "should use the libata sata_sil module.\n"); first = 0; } @@ -795,14 +768,61 @@ static int __devinit siimage_init_one(struct pci_dev *dev, d.host_flags |= IDE_HFLAG_NO_ATAPI_DMA; } - return ide_setup_pci_device(dev, &d); + rc = pci_enable_device(dev); + if (rc) + return rc; + + pci_read_config_byte(dev, 0x8A, &BA5_EN); + if ((BA5_EN & 0x01) || bar5) { + /* + * Drop back to PIO if we can't map the MMIO. Some systems + * seem to get terminally confused in the PCI spaces. + */ + if (!request_mem_region(bar5, barsize, d.name)) { + printk(KERN_WARNING DRV_NAME " %s: MMIO ports not " + "available\n", pci_name(dev)); + } else { + ioaddr = ioremap(bar5, barsize); + if (ioaddr == NULL) + release_mem_region(bar5, barsize); + } + } + + rc = ide_pci_init_one(dev, &d, ioaddr); + if (rc) { + if (ioaddr) { + iounmap(ioaddr); + release_mem_region(bar5, barsize); + } + pci_disable_device(dev); + } + + return rc; +} + +static void __devexit siimage_remove(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + void __iomem *ioaddr = host->host_priv; + + ide_pci_remove(dev); + + if (ioaddr) { + resource_size_t bar5 = pci_resource_start(dev, 5); + unsigned long barsize = pci_resource_len(dev, 5); + + iounmap(ioaddr); + release_mem_region(bar5, barsize); + } + + pci_disable_device(dev); } static const struct pci_device_id siimage_pci_tbl[] = { { PCI_VDEVICE(CMD, PCI_DEVICE_ID_SII_680), 0 }, #ifdef CONFIG_BLK_DEV_IDE_SATA { PCI_VDEVICE(CMD, PCI_DEVICE_ID_SII_3112), 1 }, - { PCI_VDEVICE(CMD, PCI_DEVICE_ID_SII_1210SA), 2 }, + { PCI_VDEVICE(CMD, PCI_DEVICE_ID_SII_1210SA), 1 }, #endif { 0, }, }; @@ -812,6 +832,7 @@ static struct pci_driver driver = { .name = "SiI_IDE", .id_table = siimage_pci_tbl, .probe = siimage_init_one, + .remove = siimage_remove, }; static int __init siimage_ide_init(void) @@ -819,7 +840,13 @@ static int __init siimage_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit siimage_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(siimage_ide_init); +module_exit(siimage_ide_exit); MODULE_AUTHOR("Andre Hedrick, Alan Cox"); MODULE_DESCRIPTION("PCI driver module for SiI IDE"); diff --git a/drivers/ide/pci/sis5513.c b/drivers/ide/pci/sis5513.c index 2389945ca95d..cc95f90b53b7 100644 --- a/drivers/ide/pci/sis5513.c +++ b/drivers/ide/pci/sis5513.c @@ -52,6 +52,8 @@ #include <linux/init.h> #include <linux/ide.h> +#define DRV_NAME "sis5513" + /* registers layout and init values are chipset family dependant */ #define ATA_16 0x01 @@ -380,8 +382,9 @@ static int __devinit sis_find_family(struct pci_dev *dev) } pci_dev_put(host); - printk(KERN_INFO "SIS5513: %s %s controller\n", - SiSHostChipInfo[i].name, chipset_capability[chipset_family]); + printk(KERN_INFO DRV_NAME " %s: %s %s controller\n", + pci_name(dev), SiSHostChipInfo[i].name, + chipset_capability[chipset_family]); } if (!chipset_family) { /* Belongs to pci-quirks */ @@ -396,7 +399,8 @@ static int __devinit sis_find_family(struct pci_dev *dev) pci_write_config_dword(dev, 0x54, idemisc); if (trueid == 0x5518) { - printk(KERN_INFO "SIS5513: SiS 962/963 MuTIOL IDE UDMA133 controller\n"); + printk(KERN_INFO DRV_NAME " %s: SiS 962/963 MuTIOL IDE UDMA133 controller\n", + pci_name(dev)); chipset_family = ATA_133; /* Check for 5513 compability mapping @@ -405,7 +409,8 @@ static int __devinit sis_find_family(struct pci_dev *dev) */ if ((idemisc & 0x40000000) == 0) { pci_write_config_dword(dev, 0x54, idemisc | 0x40000000); - printk(KERN_INFO "SIS5513: Switching to 5513 register mapping\n"); + printk(KERN_INFO DRV_NAME " %s: Switching to 5513 register mapping\n", + pci_name(dev)); } } } @@ -429,10 +434,12 @@ static int __devinit sis_find_family(struct pci_dev *dev) pci_dev_put(lpc_bridge); if (lpc_bridge->revision == 0x10 && (prefctl & 0x80)) { - printk(KERN_INFO "SIS5513: SiS 961B MuTIOL IDE UDMA133 controller\n"); + printk(KERN_INFO DRV_NAME " %s: SiS 961B MuTIOL IDE UDMA133 controller\n", + pci_name(dev)); chipset_family = ATA_133a; } else { - printk(KERN_INFO "SIS5513: SiS 961 MuTIOL IDE UDMA100 controller\n"); + printk(KERN_INFO DRV_NAME " %s: SiS 961 MuTIOL IDE UDMA100 controller\n", + pci_name(dev)); chipset_family = ATA_100; } } @@ -441,8 +448,7 @@ static int __devinit sis_find_family(struct pci_dev *dev) return chipset_family; } -static unsigned int __devinit init_chipset_sis5513(struct pci_dev *dev, - const char *name) +static unsigned int __devinit init_chipset_sis5513(struct pci_dev *dev) { /* Make general config ops here 1/ tell IDE channels to operate in Compatibility mode only @@ -555,7 +561,7 @@ static const struct ide_port_ops sis_ata133_port_ops = { }; static const struct ide_port_info sis5513_chipset __devinitdata = { - .name = "SIS5513", + .name = DRV_NAME, .init_chipset = init_chipset_sis5513, .enablebits = { {0x4a, 0x02, 0x02}, {0x4a, 0x04, 0x04} }, .host_flags = IDE_HFLAG_LEGACY_IRQS | IDE_HFLAG_NO_AUTODMA, @@ -583,7 +589,13 @@ static int __devinit sis5513_init_one(struct pci_dev *dev, const struct pci_devi d.udma_mask = udma_rates[chipset_family]; - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); +} + +static void __devexit sis5513_remove(struct pci_dev *dev) +{ + ide_pci_remove(dev); + pci_disable_device(dev); } static const struct pci_device_id sis5513_pci_tbl[] = { @@ -598,6 +610,7 @@ static struct pci_driver driver = { .name = "SIS_IDE", .id_table = sis5513_pci_tbl, .probe = sis5513_init_one, + .remove = sis5513_remove, }; static int __init sis5513_ide_init(void) @@ -605,7 +618,13 @@ static int __init sis5513_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit sis5513_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(sis5513_ide_init); +module_exit(sis5513_ide_exit); MODULE_AUTHOR("Lionel Bouton, L C Chang, Andre Hedrick, Vojtech Pavlik"); MODULE_DESCRIPTION("PCI driver module for SIS IDE"); diff --git a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c index 6efbde297174..73905bcc08fb 100644 --- a/drivers/ide/pci/sl82c105.c +++ b/drivers/ide/pci/sl82c105.c @@ -23,6 +23,8 @@ #include <asm/io.h> +#define DRV_NAME "sl82c105" + #undef DEBUG #ifdef DEBUG @@ -157,9 +159,9 @@ static void sl82c105_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. */ - dma_cmd = inb(hwif->dma_command); + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); if (dma_cmd & 1) { - outb(dma_cmd & ~1, hwif->dma_command); + outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD); printk("sl82c105: DMA was enabled\n"); } @@ -270,7 +272,7 @@ static u8 sl82c105_bridge_revision(struct pci_dev *dev) * channel 0 here at least, but channel 1 has to be enabled by * firmware or arch code. We still set both to 16 bits mode. */ -static unsigned int __devinit init_chipset_sl82c105(struct pci_dev *dev, const char *msg) +static unsigned int __devinit init_chipset_sl82c105(struct pci_dev *dev) { u32 val; @@ -301,7 +303,7 @@ static const struct ide_dma_ops sl82c105_dma_ops = { }; static const struct ide_port_info sl82c105_chipset __devinitdata = { - .name = "W82C105", + .name = DRV_NAME, .init_chipset = init_chipset_sl82c105, .enablebits = {{0x40,0x01,0x01}, {0x40,0x10,0x10}}, .port_ops = &sl82c105_port_ops, @@ -328,14 +330,14 @@ static int __devinit sl82c105_init_one(struct pci_dev *dev, const struct pci_dev * Never ever EVER under any circumstances enable * DMA when the bridge is this old. */ - printk(KERN_INFO "W82C105_IDE: Winbond W83C553 bridge " + printk(KERN_INFO DRV_NAME ": Winbond W83C553 bridge " "revision %d, BM-DMA disabled\n", rev); d.dma_ops = NULL; d.mwdma_mask = 0; d.host_flags &= ~IDE_HFLAG_SERIALIZE_DMA; } - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id sl82c105_pci_tbl[] = { @@ -348,6 +350,7 @@ static struct pci_driver driver = { .name = "W82C105_IDE", .id_table = sl82c105_pci_tbl, .probe = sl82c105_init_one, + .remove = ide_pci_remove, }; static int __init sl82c105_ide_init(void) @@ -355,7 +358,13 @@ static int __init sl82c105_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit sl82c105_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(sl82c105_ide_init); +module_exit(sl82c105_ide_exit); MODULE_DESCRIPTION("PCI driver module for W82C105 IDE"); MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/slc90e66.c b/drivers/ide/pci/slc90e66.c index dae6e2c94d86..13d1fa491f26 100644 --- a/drivers/ide/pci/slc90e66.c +++ b/drivers/ide/pci/slc90e66.c @@ -15,6 +15,8 @@ #include <linux/ide.h> #include <linux/init.h> +#define DRV_NAME "slc90e66" + static DEFINE_SPINLOCK(slc90e66_lock); static void slc90e66_set_pio_mode(ide_drive_t *drive, const u8 pio) @@ -132,7 +134,7 @@ static const struct ide_port_ops slc90e66_port_ops = { }; static const struct ide_port_info slc90e66_chipset __devinitdata = { - .name = "SLC90E66", + .name = DRV_NAME, .enablebits = { {0x41, 0x80, 0x80}, {0x43, 0x80, 0x80} }, .port_ops = &slc90e66_port_ops, .host_flags = IDE_HFLAG_LEGACY_IRQS, @@ -144,7 +146,7 @@ static const struct ide_port_info slc90e66_chipset __devinitdata = { static int __devinit slc90e66_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &slc90e66_chipset); + return ide_pci_init_one(dev, &slc90e66_chipset, NULL); } static const struct pci_device_id slc90e66_pci_tbl[] = { @@ -157,6 +159,7 @@ static struct pci_driver driver = { .name = "SLC90e66_IDE", .id_table = slc90e66_pci_tbl, .probe = slc90e66_init_one, + .remove = ide_pci_remove, }; static int __init slc90e66_ide_init(void) @@ -164,7 +167,13 @@ static int __init slc90e66_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit slc90e66_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(slc90e66_ide_init); +module_exit(slc90e66_ide_exit); MODULE_AUTHOR("Andre Hedrick"); MODULE_DESCRIPTION("PCI driver module for SLC90E66 IDE"); diff --git a/drivers/ide/pci/tc86c001.c b/drivers/ide/pci/tc86c001.c index 9b4b27a4c711..b1cb8a9ce5a9 100644 --- a/drivers/ide/pci/tc86c001.c +++ b/drivers/ide/pci/tc86c001.c @@ -11,6 +11,8 @@ #include <linux/pci.h> #include <linux/ide.h> +#define DRV_NAME "tc86c001" + static void tc86c001_set_mode(ide_drive_t *drive, const u8 speed) { ide_hwif_t *hwif = HWIF(drive); @@ -63,7 +65,7 @@ static int tc86c001_timer_expiry(ide_drive_t *drive) ide_hwif_t *hwif = HWIF(drive); ide_expiry_t *expiry = ide_get_hwifdata(hwif); ide_hwgroup_t *hwgroup = HWGROUP(drive); - u8 dma_stat = inb(hwif->dma_status); + u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); /* Restore a higher level driver's expiry handler first. */ hwgroup->expiry = expiry; @@ -71,21 +73,24 @@ static int tc86c001_timer_expiry(ide_drive_t *drive) if ((dma_stat & 5) == 1) { /* DMA active and no interrupt */ unsigned long sc_base = hwif->config_data; unsigned long twcr_port = sc_base + (drive->dn ? 0x06 : 0x04); - u8 dma_cmd = inb(hwif->dma_command); + u8 dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); printk(KERN_WARNING "%s: DMA interrupt possibly stuck, " "attempting recovery...\n", drive->name); /* Stop DMA */ - outb(dma_cmd & ~0x01, hwif->dma_command); + outb(dma_cmd & ~0x01, hwif->dma_base + ATA_DMA_CMD); /* Setup the dummy DMA transfer */ outw(0, sc_base + 0x0a); /* Sector Count */ outw(0, twcr_port); /* Transfer Word Count 1 or 2 */ /* Start the dummy DMA transfer */ - outb(0x00, hwif->dma_command); /* clear R_OR_WCTR for write */ - outb(0x01, hwif->dma_command); /* set START_STOPBM */ + + /* clear R_OR_WCTR for write */ + outb(0x00, hwif->dma_base + ATA_DMA_CMD); + /* set START_STOPBM */ + outb(0x01, hwif->dma_base + ATA_DMA_CMD); /* * If an interrupt was pending, it should come thru shortly. @@ -170,16 +175,6 @@ static void __devinit init_hwif_tc86c001(ide_hwif_t *hwif) hwif->rqsize = 0xffff; } -static unsigned int __devinit init_chipset_tc86c001(struct pci_dev *dev, - const char *name) -{ - int err = pci_request_region(dev, 5, name); - - if (err) - printk(KERN_ERR "%s: system control regs already in use", name); - return err; -} - static const struct ide_port_ops tc86c001_port_ops = { .set_pio_mode = tc86c001_set_pio_mode, .set_dma_mode = tc86c001_set_mode, @@ -198,13 +193,11 @@ static const struct ide_dma_ops tc86c001_dma_ops = { }; static const struct ide_port_info tc86c001_chipset __devinitdata = { - .name = "TC86C001", - .init_chipset = init_chipset_tc86c001, + .name = DRV_NAME, .init_hwif = init_hwif_tc86c001, .port_ops = &tc86c001_port_ops, .dma_ops = &tc86c001_dma_ops, - .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_OFF_BOARD | - IDE_HFLAG_ABUSE_SET_DMA_MODE, + .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_OFF_BOARD, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA4, @@ -213,7 +206,37 @@ static const struct ide_port_info tc86c001_chipset __devinitdata = { static int __devinit tc86c001_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &tc86c001_chipset); + int rc; + + rc = pci_enable_device(dev); + if (rc) + goto out; + + rc = pci_request_region(dev, 5, DRV_NAME); + if (rc) { + printk(KERN_ERR DRV_NAME ": system control regs already in use"); + goto out_disable; + } + + rc = ide_pci_init_one(dev, &tc86c001_chipset, NULL); + if (rc) + goto out_release; + + goto out; + +out_release: + pci_release_region(dev, 5); +out_disable: + pci_disable_device(dev); +out: + return rc; +} + +static void __devexit tc86c001_remove(struct pci_dev *dev) +{ + ide_pci_remove(dev); + pci_release_region(dev, 5); + pci_disable_device(dev); } static const struct pci_device_id tc86c001_pci_tbl[] = { @@ -225,14 +248,22 @@ MODULE_DEVICE_TABLE(pci, tc86c001_pci_tbl); static struct pci_driver driver = { .name = "TC86C001", .id_table = tc86c001_pci_tbl, - .probe = tc86c001_init_one + .probe = tc86c001_init_one, + .remove = tc86c001_remove, }; static int __init tc86c001_ide_init(void) { return ide_pci_register_driver(&driver); } + +static void __exit tc86c001_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(tc86c001_ide_init); +module_exit(tc86c001_ide_exit); MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>"); MODULE_DESCRIPTION("PCI driver module for TC86C001 IDE"); diff --git a/drivers/ide/pci/triflex.c b/drivers/ide/pci/triflex.c index db65a558d4ec..b77ec35151b3 100644 --- a/drivers/ide/pci/triflex.c +++ b/drivers/ide/pci/triflex.c @@ -33,6 +33,8 @@ #include <linux/ide.h> #include <linux/init.h> +#define DRV_NAME "triflex" + static void triflex_set_mode(ide_drive_t *drive, const u8 speed) { ide_hwif_t *hwif = HWIF(drive); @@ -93,7 +95,7 @@ static const struct ide_port_ops triflex_port_ops = { }; static const struct ide_port_info triflex_device __devinitdata = { - .name = "TRIFLEX", + .name = DRV_NAME, .enablebits = {{0x80, 0x01, 0x01}, {0x80, 0x02, 0x02}}, .port_ops = &triflex_port_ops, .pio_mask = ATA_PIO4, @@ -104,7 +106,7 @@ static const struct ide_port_info triflex_device __devinitdata = { static int __devinit triflex_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &triflex_device); + return ide_pci_init_one(dev, &triflex_device, NULL); } static const struct pci_device_id triflex_pci_tbl[] = { @@ -117,6 +119,7 @@ static struct pci_driver driver = { .name = "TRIFLEX_IDE", .id_table = triflex_pci_tbl, .probe = triflex_init_one, + .remove = ide_pci_remove, }; static int __init triflex_ide_init(void) @@ -124,7 +127,13 @@ static int __init triflex_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit triflex_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(triflex_ide_init); +module_exit(triflex_ide_exit); MODULE_AUTHOR("Torben Mathiasen"); MODULE_DESCRIPTION("PCI driver module for Compaq Triflex IDE"); diff --git a/drivers/ide/pci/trm290.c b/drivers/ide/pci/trm290.c index a8a3138682ef..fd28b49977fd 100644 --- a/drivers/ide/pci/trm290.c +++ b/drivers/ide/pci/trm290.c @@ -141,6 +141,8 @@ #include <asm/io.h> +#define DRV_NAME "trm290" + static void trm290_prepare_drive (ide_drive_t *drive, unsigned int use_dma) { ide_hwif_t *hwif = HWIF(drive); @@ -245,10 +247,10 @@ static void __devinit init_hwif_trm290(ide_hwif_t *hwif) u8 reg = 0; if ((dev->class & 5) && cfg_base) - printk(KERN_INFO "TRM290: chip"); + printk(KERN_INFO DRV_NAME " %s: chip", pci_name(dev)); else { cfg_base = 0x3df0; - printk(KERN_INFO "TRM290: using default"); + printk(KERN_INFO DRV_NAME " %s: using default", pci_name(dev)); } printk(KERN_CONT " config base at 0x%04x\n", cfg_base); hwif->config_data = cfg_base; @@ -325,7 +327,7 @@ static struct ide_dma_ops trm290_dma_ops = { }; static const struct ide_port_info trm290_chipset __devinitdata = { - .name = "TRM290", + .name = DRV_NAME, .init_hwif = init_hwif_trm290, .chipset = ide_trm290, .port_ops = &trm290_port_ops, @@ -340,7 +342,7 @@ static const struct ide_port_info trm290_chipset __devinitdata = { static int __devinit trm290_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &trm290_chipset); + return ide_pci_init_one(dev, &trm290_chipset, NULL); } static const struct pci_device_id trm290_pci_tbl[] = { @@ -353,6 +355,7 @@ static struct pci_driver driver = { .name = "TRM290_IDE", .id_table = trm290_pci_tbl, .probe = trm290_init_one, + .remove = ide_pci_remove, }; static int __init trm290_ide_init(void) @@ -360,7 +363,13 @@ static int __init trm290_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit trm290_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(trm290_ide_init); +module_exit(trm290_ide_exit); MODULE_AUTHOR("Mark Lord"); MODULE_DESCRIPTION("PCI driver module for Tekram TRM290 IDE"); diff --git a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c index e47384c70c40..454d2bf62dce 100644 --- a/drivers/ide/pci/via82cxxx.c +++ b/drivers/ide/pci/via82cxxx.c @@ -35,6 +35,8 @@ #include <asm/processor.h> #endif +#define DRV_NAME "via82cxxx" + #define VIA_IDE_ENABLE 0x40 #define VIA_IDE_CONFIG 0x41 #define VIA_FIFO_CONFIG 0x43 @@ -113,7 +115,8 @@ struct via82cxxx_dev static void via_set_speed(ide_hwif_t *hwif, u8 dn, struct ide_timing *timing) { struct pci_dev *dev = to_pci_dev(hwif->dev); - struct via82cxxx_dev *vdev = pci_get_drvdata(dev); + struct ide_host *host = pci_get_drvdata(dev); + struct via82cxxx_dev *vdev = host->host_priv; u8 t; if (~vdev->via_config->flags & VIA_BAD_AST) { @@ -153,7 +156,8 @@ static void via_set_drive(ide_drive_t *drive, const u8 speed) ide_hwif_t *hwif = drive->hwif; ide_drive_t *peer = hwif->drives + (~drive->dn & 1); struct pci_dev *dev = to_pci_dev(hwif->dev); - struct via82cxxx_dev *vdev = pci_get_drvdata(dev); + struct ide_host *host = pci_get_drvdata(dev); + struct via82cxxx_dev *vdev = host->host_priv; struct ide_timing t, p; unsigned int T, UT; @@ -258,37 +262,19 @@ static void __devinit via_cable_detect(struct via82cxxx_dev *vdev, u32 u) /** * init_chipset_via82cxxx - initialization handler * @dev: PCI device - * @name: Name of interface * * The initialization callback. Here we determine the IDE chip type * and initialize its drive independent registers. */ -static unsigned int __devinit init_chipset_via82cxxx(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_via82cxxx(struct pci_dev *dev) { - struct pci_dev *isa = NULL; - struct via82cxxx_dev *vdev; - struct via_isa_bridge *via_config; + struct ide_host *host = pci_get_drvdata(dev); + struct via82cxxx_dev *vdev = host->host_priv; + struct via_isa_bridge *via_config = vdev->via_config; u8 t, v; u32 u; - vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); - if (!vdev) { - printk(KERN_ERR "VP_IDE: out of memory :(\n"); - return -ENOMEM; - } - pci_set_drvdata(dev, vdev); - - /* - * Find the ISA bridge to see how good the IDE is. - */ - vdev->via_config = via_config = via_config_find(&isa); - - /* We checked this earlier so if it fails here deeep badness - is involved */ - - BUG_ON(!via_config->id); - /* * Detect cable and configure Clk66 */ @@ -334,39 +320,6 @@ static unsigned int __devinit init_chipset_via82cxxx(struct pci_dev *dev, const pci_write_config_byte(dev, VIA_FIFO_CONFIG, t); - /* - * Determine system bus clock. - */ - - via_clock = (ide_pci_clk ? ide_pci_clk : 33) * 1000; - - switch (via_clock) { - case 33000: via_clock = 33333; break; - case 37000: via_clock = 37500; break; - case 41000: via_clock = 41666; break; - } - - if (via_clock < 20000 || via_clock > 50000) { - printk(KERN_WARNING "VP_IDE: User given PCI clock speed " - "impossible (%d), using 33 MHz instead.\n", via_clock); - printk(KERN_WARNING "VP_IDE: Use ide0=ata66 if you want " - "to assume 80-wire cable.\n"); - via_clock = 33333; - } - - /* - * Print the boot message. - */ - - printk(KERN_INFO "VP_IDE: VIA %s (rev %02x) IDE %sDMA%s " - "controller on pci%s\n", - via_config->name, isa->revision, - via_config->udma_mask ? "U" : "MW", - via_dma[via_config->udma_mask ? - (fls(via_config->udma_mask) - 1) : 0], - pci_name(dev)); - - pci_dev_put(isa); return 0; } @@ -402,7 +355,8 @@ static int via_cable_override(struct pci_dev *pdev) static u8 __devinit via82cxxx_cable_detect(ide_hwif_t *hwif) { struct pci_dev *pdev = to_pci_dev(hwif->dev); - struct via82cxxx_dev *vdev = pci_get_drvdata(pdev); + struct ide_host *host = pci_get_drvdata(pdev); + struct via82cxxx_dev *vdev = host->host_priv; if (via_cable_override(pdev)) return ATA_CBL_PATA40_SHORT; @@ -420,12 +374,11 @@ static const struct ide_port_ops via_port_ops = { }; static const struct ide_port_info via82cxxx_chipset __devinitdata = { - .name = "VP_IDE", + .name = DRV_NAME, .init_chipset = init_chipset_via82cxxx, .enablebits = { { 0x40, 0x02, 0x02 }, { 0x40, 0x01, 0x01 } }, .port_ops = &via_port_ops, .host_flags = IDE_HFLAG_PIO_NO_BLACKLIST | - IDE_HFLAG_ABUSE_SET_DMA_MODE | IDE_HFLAG_POST_SET_MODE | IDE_HFLAG_IO_32BIT, .pio_mask = ATA_PIO5, @@ -437,6 +390,8 @@ static int __devinit via_init_one(struct pci_dev *dev, const struct pci_device_i { struct pci_dev *isa = NULL; struct via_isa_bridge *via_config; + struct via82cxxx_dev *vdev; + int rc; u8 idx = id->driver_data; struct ide_port_info d; @@ -446,12 +401,42 @@ static int __devinit via_init_one(struct pci_dev *dev, const struct pci_device_i * Find the ISA bridge and check we know what it is. */ via_config = via_config_find(&isa); - pci_dev_put(isa); if (!via_config->id) { - printk(KERN_WARNING "VP_IDE: Unknown VIA SouthBridge, disabling DMA.\n"); + printk(KERN_WARNING DRV_NAME " %s: unknown chipset, skipping\n", + pci_name(dev)); return -ENODEV; } + /* + * Print the boot message. + */ + printk(KERN_INFO DRV_NAME " %s: VIA %s (rev %02x) IDE %sDMA%s\n", + pci_name(dev), via_config->name, isa->revision, + via_config->udma_mask ? "U" : "MW", + via_dma[via_config->udma_mask ? + (fls(via_config->udma_mask) - 1) : 0]); + + pci_dev_put(isa); + + /* + * Determine system bus clock. + */ + via_clock = (ide_pci_clk ? ide_pci_clk : 33) * 1000; + + switch (via_clock) { + case 33000: via_clock = 33333; break; + case 37000: via_clock = 37500; break; + case 41000: via_clock = 41666; break; + } + + if (via_clock < 20000 || via_clock > 50000) { + printk(KERN_WARNING DRV_NAME ": User given PCI clock speed " + "impossible (%d), using 33 MHz instead.\n", via_clock); + printk(KERN_WARNING DRV_NAME ": Use ide0=ata66 if you want " + "to assume 80-wire cable.\n"); + via_clock = 33333; + } + if (idx == 0) d.host_flags |= IDE_HFLAG_NO_AUTODMA; else @@ -467,7 +452,29 @@ static int __devinit via_init_one(struct pci_dev *dev, const struct pci_device_i d.udma_mask = via_config->udma_mask; - return ide_setup_pci_device(dev, &d); + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) { + printk(KERN_ERR DRV_NAME " %s: out of memory :(\n", + pci_name(dev)); + return -ENOMEM; + } + + vdev->via_config = via_config; + + rc = ide_pci_init_one(dev, &d, vdev); + if (rc) + kfree(vdev); + + return rc; +} + +static void __devexit via_remove(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + struct via82cxxx_dev *vdev = host->host_priv; + + ide_pci_remove(dev); + kfree(vdev); } static const struct pci_device_id via_pci_tbl[] = { @@ -484,6 +491,7 @@ static struct pci_driver driver = { .name = "VIA_IDE", .id_table = via_pci_tbl, .probe = via_init_one, + .remove = via_remove, }; static int __init via_ide_init(void) @@ -491,7 +499,13 @@ static int __init via_ide_init(void) return ide_pci_register_driver(&driver); } +static void __exit via_ide_exit(void) +{ + pci_unregister_driver(&driver); +} + module_init(via_ide_init); +module_exit(via_ide_exit); MODULE_AUTHOR("Vojtech Pavlik, Michel Aubry, Jeff Garzik, Andre Hedrick"); MODULE_DESCRIPTION("PCI driver module for VIA IDE"); diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index 93fb9067c043..c521bf6e1bf2 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -48,6 +48,8 @@ #include <asm/mediabay.h> #endif +#define DRV_NAME "ide-pmac" + #undef IDE_PMAC_DEBUG #define DMA_WAIT_TIMEOUT 50 @@ -424,7 +426,9 @@ static void pmac_ide_kauai_selectproc(ide_drive_t *drive); static void pmac_ide_selectproc(ide_drive_t *drive) { - pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = + (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent); if (pmif == NULL) return; @@ -444,7 +448,9 @@ pmac_ide_selectproc(ide_drive_t *drive) static void pmac_ide_kauai_selectproc(ide_drive_t *drive) { - pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = + (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent); if (pmif == NULL) return; @@ -465,7 +471,9 @@ pmac_ide_kauai_selectproc(ide_drive_t *drive) static void pmac_ide_do_update_timings(ide_drive_t *drive) { - pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = + (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent); if (pmif == NULL) return; @@ -478,12 +486,26 @@ pmac_ide_do_update_timings(ide_drive_t *drive) pmac_ide_selectproc(drive); } -static void pmac_outbsync(ide_hwif_t *hwif, u8 value, unsigned long port) +static void pmac_exec_command(ide_hwif_t *hwif, u8 cmd) { - u32 tmp; - - writeb(value, (void __iomem *) port); - tmp = readl((void __iomem *)(hwif->io_ports.data_addr + writeb(cmd, (void __iomem *)hwif->io_ports.command_addr); + (void)readl((void __iomem *)(hwif->io_ports.data_addr + + IDE_TIMING_CONFIG)); +} + +static void pmac_set_irq(ide_hwif_t *hwif, int on) +{ + u8 ctl = ATA_DEVCTL_OBS; + + if (on == 4) { /* hack for SRST */ + ctl |= 4; + on &= ~4; + } + + ctl |= on ? 0 : 2; + + writeb(ctl, (void __iomem *)hwif->io_ports.ctl_addr); + (void)readl((void __iomem *)(hwif->io_ports.data_addr + IDE_TIMING_CONFIG)); } @@ -493,11 +515,13 @@ static void pmac_outbsync(ide_hwif_t *hwif, u8 value, unsigned long port) static void pmac_ide_set_pio_mode(ide_drive_t *drive, const u8 pio) { + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = + (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent); struct ide_timing *tim = ide_timing_find_mode(XFER_PIO_0 + pio); u32 *timings, t; unsigned accessTicks, recTicks; unsigned accessTime, recTime; - pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; unsigned int cycle_time; if (pmif == NULL) @@ -778,9 +802,11 @@ set_timings_mdma(ide_drive_t *drive, int intf_type, u32 *timings, u32 *timings2, static void pmac_ide_set_dma_mode(ide_drive_t *drive, const u8 speed) { + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = + (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent); int unit = (drive->select.b.unit & 0x01); int ret = 0; - pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; u32 *timings, *timings2, tl[2]; timings = &pmif->timings[unit]; @@ -852,11 +878,8 @@ sanitize_timings(pmac_ide_hwif_t *pmif) /* Suspend call back, should be called after the child devices * have actually been suspended */ -static int -pmac_ide_do_suspend(ide_hwif_t *hwif) +static int pmac_ide_do_suspend(pmac_ide_hwif_t *pmif) { - pmac_ide_hwif_t *pmif = (pmac_ide_hwif_t *)hwif->hwif_data; - /* We clear the timings */ pmif->timings[0] = 0; pmif->timings[1] = 0; @@ -884,11 +907,8 @@ pmac_ide_do_suspend(ide_hwif_t *hwif) /* Resume call back, should be called before the child devices * are resumed */ -static int -pmac_ide_do_resume(ide_hwif_t *hwif) +static int pmac_ide_do_resume(pmac_ide_hwif_t *pmif) { - pmac_ide_hwif_t *pmif = (pmac_ide_hwif_t *)hwif->hwif_data; - /* Hard reset & re-enable controller (do we really need to reset ? -BenH) */ if (!pmif->mediabay) { ppc_md.feature_call(PMAC_FTR_IDE_RESET, pmif->node, pmif->aapl_bus_id, 1); @@ -916,7 +936,8 @@ pmac_ide_do_resume(ide_hwif_t *hwif) static u8 pmac_ide_cable_detect(ide_hwif_t *hwif) { - pmac_ide_hwif_t *pmif = (pmac_ide_hwif_t *)ide_get_hwifdata(hwif); + pmac_ide_hwif_t *pmif = + (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent); struct device_node *np = pmif->node; const char *cable = of_get_property(np, "cable-type", NULL); @@ -936,7 +957,40 @@ static u8 pmac_ide_cable_detect(ide_hwif_t *hwif) return ATA_CBL_PATA40; } +static void pmac_ide_init_dev(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = + (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent); + + if (pmif->mediabay) { +#ifdef CONFIG_PMAC_MEDIABAY + if (check_media_bay_by_base(pmif->regbase, MB_CD) == 0) { + drive->noprobe = 0; + return; + } +#endif + drive->noprobe = 1; + } +} + +static const struct ide_tp_ops pmac_tp_ops = { + .exec_command = pmac_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = pmac_set_irq, + + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + static const struct ide_port_ops pmac_ide_ata6_port_ops = { + .init_dev = pmac_ide_init_dev, .set_pio_mode = pmac_ide_set_pio_mode, .set_dma_mode = pmac_ide_set_dma_mode, .selectproc = pmac_ide_kauai_selectproc, @@ -944,6 +998,7 @@ static const struct ide_port_ops pmac_ide_ata6_port_ops = { }; static const struct ide_port_ops pmac_ide_ata4_port_ops = { + .init_dev = pmac_ide_init_dev, .set_pio_mode = pmac_ide_set_pio_mode, .set_dma_mode = pmac_ide_set_dma_mode, .selectproc = pmac_ide_selectproc, @@ -951,6 +1006,7 @@ static const struct ide_port_ops pmac_ide_ata4_port_ops = { }; static const struct ide_port_ops pmac_ide_port_ops = { + .init_dev = pmac_ide_init_dev, .set_pio_mode = pmac_ide_set_pio_mode, .set_dma_mode = pmac_ide_set_dma_mode, .selectproc = pmac_ide_selectproc, @@ -959,12 +1015,14 @@ static const struct ide_port_ops pmac_ide_port_ops = { static const struct ide_dma_ops pmac_dma_ops; static const struct ide_port_info pmac_port_info = { + .name = DRV_NAME, .init_dma = pmac_ide_init_dma, .chipset = ide_pmac, + .tp_ops = &pmac_tp_ops, + .port_ops = &pmac_ide_port_ops, #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC .dma_ops = &pmac_dma_ops, #endif - .port_ops = &pmac_ide_port_ops, .host_flags = IDE_HFLAG_SET_PIO_MODE_KEEP_DMA | IDE_HFLAG_POST_SET_MODE | IDE_HFLAG_MMIO | @@ -977,13 +1035,15 @@ static const struct ide_port_info pmac_port_info = { * Setup, register & probe an IDE channel driven by this driver, this is * called by one of the 2 probe functions (macio or PCI). */ -static int __devinit -pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif, hw_regs_t *hw) +static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) { struct device_node *np = pmif->node; const int *bidp; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + struct ide_host *host; + ide_hwif_t *hwif; + hw_regs_t *hws[] = { hw, NULL, NULL, NULL }; struct ide_port_info d = pmac_port_info; + int rc; pmif->broken_dma = pmif->broken_dma_warn = 0; if (of_device_is_compatible(np, "shasta-ata")) { @@ -1054,31 +1114,16 @@ pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif, hw_regs_t *hw) msleep(jiffies_to_msecs(IDE_WAKEUP_DELAY)); } - /* Setup MMIO ops */ - default_hwif_mmiops(hwif); - hwif->OUTBSYNC = pmac_outbsync; + printk(KERN_INFO DRV_NAME ": Found Apple %s controller (%s), " + "bus ID %d%s, irq %d\n", model_name[pmif->kind], + pmif->mdev ? "macio" : "PCI", pmif->aapl_bus_id, + pmif->mediabay ? " (mediabay)" : "", hw->irq); - hwif->hwif_data = pmif; - ide_init_port_hw(hwif, hw); + rc = ide_host_add(&d, hws, &host); + if (rc) + return rc; - printk(KERN_INFO "ide%d: Found Apple %s controller, bus ID %d%s, irq %d\n", - hwif->index, model_name[pmif->kind], pmif->aapl_bus_id, - pmif->mediabay ? " (mediabay)" : "", hwif->irq); - - if (pmif->mediabay) { -#ifdef CONFIG_PMAC_MEDIABAY - if (check_media_bay_by_base(pmif->regbase, MB_CD)) { -#else - if (1) { -#endif - hwif->drives[0].noprobe = 1; - hwif->drives[1].noprobe = 1; - } - } - - idx[0] = hwif->index; - - ide_device_add(idx, &d); + hwif = host->ports[0]; return 0; } @@ -1101,7 +1146,6 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match) { void __iomem *base; unsigned long regbase; - ide_hwif_t *hwif; pmac_ide_hwif_t *pmif; int irq, rc; hw_regs_t hw; @@ -1110,14 +1154,6 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match) if (pmif == NULL) return -ENOMEM; - hwif = ide_find_port(); - if (hwif == NULL) { - printk(KERN_ERR "ide-pmac: MacIO interface attach with no slot\n"); - printk(KERN_ERR " %s\n", mdev->ofdev.node->full_name); - rc = -ENODEV; - goto out_free_pmif; - } - if (macio_resource_count(mdev) == 0) { printk(KERN_WARNING "ide-pmac: no address for %s\n", mdev->ofdev.node->full_name); @@ -1164,7 +1200,7 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match) } else pmif->dma_regs = NULL; #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ - dev_set_drvdata(&mdev->ofdev.dev, hwif); + dev_set_drvdata(&mdev->ofdev.dev, pmif); memset(&hw, 0, sizeof(hw)); pmac_ide_init_ports(&hw, pmif->regbase); @@ -1172,7 +1208,7 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match) hw.dev = &mdev->bus->pdev->dev; hw.parent = &mdev->ofdev.dev; - rc = pmac_ide_setup_device(pmif, hwif, &hw); + rc = pmac_ide_setup_device(pmif, &hw); if (rc != 0) { /* The inteface is released to the common IDE layer */ dev_set_drvdata(&mdev->ofdev.dev, NULL); @@ -1195,12 +1231,13 @@ out_free_pmif: static int pmac_ide_macio_suspend(struct macio_dev *mdev, pm_message_t mesg) { - ide_hwif_t *hwif = (ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev); - int rc = 0; + pmac_ide_hwif_t *pmif = + (pmac_ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev); + int rc = 0; if (mesg.event != mdev->ofdev.dev.power.power_state.event && (mesg.event & PM_EVENT_SLEEP)) { - rc = pmac_ide_do_suspend(hwif); + rc = pmac_ide_do_suspend(pmif); if (rc == 0) mdev->ofdev.dev.power.power_state = mesg; } @@ -1211,11 +1248,12 @@ pmac_ide_macio_suspend(struct macio_dev *mdev, pm_message_t mesg) static int pmac_ide_macio_resume(struct macio_dev *mdev) { - ide_hwif_t *hwif = (ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev); - int rc = 0; - + pmac_ide_hwif_t *pmif = + (pmac_ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev); + int rc = 0; + if (mdev->ofdev.dev.power.power_state.event != PM_EVENT_ON) { - rc = pmac_ide_do_resume(hwif); + rc = pmac_ide_do_resume(pmif); if (rc == 0) mdev->ofdev.dev.power.power_state = PMSG_ON; } @@ -1229,7 +1267,6 @@ pmac_ide_macio_resume(struct macio_dev *mdev) static int __devinit pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id) { - ide_hwif_t *hwif; struct device_node *np; pmac_ide_hwif_t *pmif; void __iomem *base; @@ -1247,14 +1284,6 @@ pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id) if (pmif == NULL) return -ENOMEM; - hwif = ide_find_port(); - if (hwif == NULL) { - printk(KERN_ERR "ide-pmac: PCI interface attach with no slot\n"); - printk(KERN_ERR " %s\n", np->full_name); - rc = -ENODEV; - goto out_free_pmif; - } - if (pci_enable_device(pdev)) { printk(KERN_WARNING "ide-pmac: Can't enable PCI device for " "%s\n", np->full_name); @@ -1284,14 +1313,14 @@ pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id) pmif->kauai_fcr = base; pmif->irq = pdev->irq; - pci_set_drvdata(pdev, hwif); + pci_set_drvdata(pdev, pmif); memset(&hw, 0, sizeof(hw)); pmac_ide_init_ports(&hw, pmif->regbase); hw.irq = pdev->irq; hw.dev = &pdev->dev; - rc = pmac_ide_setup_device(pmif, hwif, &hw); + rc = pmac_ide_setup_device(pmif, &hw); if (rc != 0) { /* The inteface is released to the common IDE layer */ pci_set_drvdata(pdev, NULL); @@ -1310,12 +1339,12 @@ out_free_pmif: static int pmac_ide_pci_suspend(struct pci_dev *pdev, pm_message_t mesg) { - ide_hwif_t *hwif = (ide_hwif_t *)pci_get_drvdata(pdev); - int rc = 0; - + pmac_ide_hwif_t *pmif = (pmac_ide_hwif_t *)pci_get_drvdata(pdev); + int rc = 0; + if (mesg.event != pdev->dev.power.power_state.event && (mesg.event & PM_EVENT_SLEEP)) { - rc = pmac_ide_do_suspend(hwif); + rc = pmac_ide_do_suspend(pmif); if (rc == 0) pdev->dev.power.power_state = mesg; } @@ -1326,11 +1355,11 @@ pmac_ide_pci_suspend(struct pci_dev *pdev, pm_message_t mesg) static int pmac_ide_pci_resume(struct pci_dev *pdev) { - ide_hwif_t *hwif = (ide_hwif_t *)pci_get_drvdata(pdev); - int rc = 0; - + pmac_ide_hwif_t *pmif = (pmac_ide_hwif_t *)pci_get_drvdata(pdev); + int rc = 0; + if (pdev->dev.power.power_state.event != PM_EVENT_ON) { - rc = pmac_ide_do_resume(hwif); + rc = pmac_ide_do_resume(pmif); if (rc == 0) pdev->dev.power.power_state = PMSG_ON; } @@ -1421,10 +1450,11 @@ out: static int pmac_ide_build_dmatable(ide_drive_t *drive, struct request *rq) { + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = + (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent); struct dbdma_cmd *table; int i, count = 0; - ide_hwif_t *hwif = HWIF(drive); - pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)hwif->hwif_data; volatile struct dbdma_regs __iomem *dma = pmif->dma_regs; struct scatterlist *sg; int wr = (rq_data_dir(rq) == WRITE); @@ -1520,7 +1550,8 @@ static int pmac_ide_dma_setup(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); - pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)hwif->hwif_data; + pmac_ide_hwif_t *pmif = + (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent); struct request *rq = HWGROUP(drive)->rq; u8 unit = (drive->select.b.unit & 0x01); u8 ata4; @@ -1560,7 +1591,9 @@ pmac_ide_dma_exec_cmd(ide_drive_t *drive, u8 command) static void pmac_ide_dma_start(ide_drive_t *drive) { - pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = + (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent); volatile struct dbdma_regs __iomem *dma; dma = pmif->dma_regs; @@ -1576,7 +1609,9 @@ pmac_ide_dma_start(ide_drive_t *drive) static int pmac_ide_dma_end (ide_drive_t *drive) { - pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = + (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent); volatile struct dbdma_regs __iomem *dma; u32 dstat; @@ -1604,7 +1639,9 @@ pmac_ide_dma_end (ide_drive_t *drive) static int pmac_ide_dma_test_irq (ide_drive_t *drive) { - pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = + (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent); volatile struct dbdma_regs __iomem *dma; unsigned long status, timeout; @@ -1664,7 +1701,9 @@ static void pmac_ide_dma_host_set(ide_drive_t *drive, int on) static void pmac_ide_dma_lost_irq (ide_drive_t *drive) { - pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = + (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent); volatile struct dbdma_regs __iomem *dma; unsigned long status; @@ -1694,7 +1733,8 @@ static const struct ide_dma_ops pmac_dma_ops = { static int __devinit pmac_ide_init_dma(ide_hwif_t *hwif, const struct ide_port_info *d) { - pmac_ide_hwif_t *pmif = (pmac_ide_hwif_t *)hwif->hwif_data; + pmac_ide_hwif_t *pmif = + (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent); struct pci_dev *dev = to_pci_dev(hwif->dev); /* We won't need pci_dev if we switch to generic consistent diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index 65fc08b6b6d0..a8e9e8a69a52 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -39,17 +39,18 @@ static int ide_setup_pci_baseregs(struct pci_dev *dev, const char *name) if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) || (progif & 5) != 5) { if ((progif & 0xa) != 0xa) { - printk(KERN_INFO "%s: device not capable of full " - "native PCI mode\n", name); + printk(KERN_INFO "%s %s: device not capable of full " + "native PCI mode\n", name, pci_name(dev)); return -EOPNOTSUPP; } - printk("%s: placing both ports into native PCI mode\n", name); + printk(KERN_INFO "%s %s: placing both ports into native PCI " + "mode\n", name, pci_name(dev)); (void) pci_write_config_byte(dev, PCI_CLASS_PROG, progif|5); if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) || (progif & 5) != 5) { - printk(KERN_ERR "%s: rewrite of PROGIF failed, wanted " - "0x%04x, got 0x%04x\n", - name, progif|5, progif); + printk(KERN_ERR "%s %s: rewrite of PROGIF failed, " + "wanted 0x%04x, got 0x%04x\n", + name, pci_name(dev), progif | 5, progif); return -EOPNOTSUPP; } } @@ -57,14 +58,14 @@ static int ide_setup_pci_baseregs(struct pci_dev *dev, const char *name) } #ifdef CONFIG_BLK_DEV_IDEDMA_PCI -static void ide_pci_clear_simplex(unsigned long dma_base, const char *name) +static int ide_pci_clear_simplex(unsigned long dma_base, const char *name) { u8 dma_stat = inb(dma_base + 2); outb(dma_stat & 0x60, dma_base + 2); dma_stat = inb(dma_base + 2); - if (dma_stat & 0x80) - printk(KERN_INFO "%s: simplex device: DMA forced\n", name); + + return (dma_stat & 0x80) ? 1 : 0; } /** @@ -73,15 +74,12 @@ static void ide_pci_clear_simplex(unsigned long dma_base, const char *name) * @d: IDE port info * * Fetch the DMA Bus-Master-I/O-Base-Address (BMIBA) from PCI space. - * Where a device has a partner that is already in DMA mode we check - * and enforce IDE simplex rules. */ unsigned long ide_pci_dma_base(ide_hwif_t *hwif, const struct ide_port_info *d) { struct pci_dev *dev = to_pci_dev(hwif->dev); unsigned long dma_base = 0; - u8 dma_stat = 0; if (hwif->host_flags & IDE_HFLAG_MMIO) return hwif->dma_base; @@ -94,7 +92,8 @@ unsigned long ide_pci_dma_base(ide_hwif_t *hwif, const struct ide_port_info *d) dma_base = pci_resource_start(dev, baridx); if (dma_base == 0) { - printk(KERN_ERR "%s: DMA base is invalid\n", d->name); + printk(KERN_ERR "%s %s: DMA base is invalid\n", + d->name, pci_name(dev)); return 0; } } @@ -102,11 +101,22 @@ unsigned long ide_pci_dma_base(ide_hwif_t *hwif, const struct ide_port_info *d) if (hwif->channel) dma_base += 8; - if (d->host_flags & IDE_HFLAG_CS5520) + return dma_base; +} +EXPORT_SYMBOL_GPL(ide_pci_dma_base); + +int ide_pci_check_simplex(ide_hwif_t *hwif, const struct ide_port_info *d) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + u8 dma_stat; + + if (d->host_flags & (IDE_HFLAG_MMIO | IDE_HFLAG_CS5520)) goto out; if (d->host_flags & IDE_HFLAG_CLEAR_SIMPLEX) { - ide_pci_clear_simplex(dma_base, d->name); + if (ide_pci_clear_simplex(hwif->dma_base, d->name)) + printk(KERN_INFO "%s %s: simplex device: DMA forced\n", + d->name, pci_name(dev)); goto out; } @@ -120,15 +130,16 @@ unsigned long ide_pci_dma_base(ide_hwif_t *hwif, const struct ide_port_info *d) * we tune the drive then try to grab DMA ownership if we want to be * the DMA end. This has to be become dynamic to handle hot-plug. */ - dma_stat = hwif->INB(dma_base + 2); + dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); if ((dma_stat & 0x80) && hwif->mate && hwif->mate->dma_base) { - printk(KERN_INFO "%s: simplex device: DMA disabled\n", d->name); - dma_base = 0; + printk(KERN_INFO "%s %s: simplex device: DMA disabled\n", + d->name, pci_name(dev)); + return -1; } out: - return dma_base; + return 0; } -EXPORT_SYMBOL_GPL(ide_pci_dma_base); +EXPORT_SYMBOL_GPL(ide_pci_check_simplex); /* * Set up BM-DMA capability (PnP BIOS should have done this) @@ -144,8 +155,8 @@ int ide_pci_set_master(struct pci_dev *dev, const char *name) if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd) || (pcicmd & PCI_COMMAND_MASTER) == 0) { - printk(KERN_ERR "%s: error updating PCICMD on %s\n", - name, pci_name(dev)); + printk(KERN_ERR "%s %s: error updating PCICMD\n", + name, pci_name(dev)); return -EIO; } } @@ -157,9 +168,9 @@ EXPORT_SYMBOL_GPL(ide_pci_set_master); void ide_setup_pci_noise(struct pci_dev *dev, const struct ide_port_info *d) { - printk(KERN_INFO "%s: IDE controller (0x%04x:0x%04x rev 0x%02x) at " - " PCI slot %s\n", d->name, dev->vendor, dev->device, - dev->revision, pci_name(dev)); + printk(KERN_INFO "%s %s: IDE controller (0x%04x:0x%04x rev 0x%02x)\n", + d->name, pci_name(dev), + dev->vendor, dev->device, dev->revision); } EXPORT_SYMBOL_GPL(ide_setup_pci_noise); @@ -184,11 +195,12 @@ static int ide_pci_enable(struct pci_dev *dev, const struct ide_port_info *d) if (pci_enable_device(dev)) { ret = pci_enable_device_io(dev); if (ret < 0) { - printk(KERN_WARNING "%s: (ide_setup_pci_device:) " - "Could not enable device.\n", d->name); + printk(KERN_WARNING "%s %s: couldn't enable device\n", + d->name, pci_name(dev)); goto out; } - printk(KERN_WARNING "%s: BIOS configuration fixed.\n", d->name); + printk(KERN_WARNING "%s %s: BIOS configuration fixed\n", + d->name, pci_name(dev)); } /* @@ -198,7 +210,8 @@ static int ide_pci_enable(struct pci_dev *dev, const struct ide_port_info *d) */ ret = pci_set_dma_mask(dev, DMA_32BIT_MASK); if (ret < 0) { - printk(KERN_ERR "%s: can't set dma mask\n", d->name); + printk(KERN_ERR "%s %s: can't set DMA mask\n", + d->name, pci_name(dev)); goto out; } @@ -216,7 +229,8 @@ static int ide_pci_enable(struct pci_dev *dev, const struct ide_port_info *d) ret = pci_request_selected_regions(dev, bars, d->name); if (ret < 0) - printk(KERN_ERR "%s: can't reserve resources\n", d->name); + printk(KERN_ERR "%s %s: can't reserve resources\n", + d->name, pci_name(dev)); out: return ret; } @@ -242,15 +256,18 @@ static int ide_pci_configure(struct pci_dev *dev, const struct ide_port_info *d) */ if (ide_setup_pci_baseregs(dev, d->name) || pci_write_config_word(dev, PCI_COMMAND, pcicmd | PCI_COMMAND_IO)) { - printk(KERN_INFO "%s: device disabled (BIOS)\n", d->name); + printk(KERN_INFO "%s %s: device disabled (BIOS)\n", + d->name, pci_name(dev)); return -ENODEV; } if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd)) { - printk(KERN_ERR "%s: error accessing PCI regs\n", d->name); + printk(KERN_ERR "%s %s: error accessing PCI regs\n", + d->name, pci_name(dev)); return -EIO; } if (!(pcicmd & PCI_COMMAND_IO)) { - printk(KERN_ERR "%s: unable to enable IDE controller\n", d->name); + printk(KERN_ERR "%s %s: unable to enable IDE controller\n", + d->name, pci_name(dev)); return -ENXIO; } return 0; @@ -284,33 +301,32 @@ static int ide_pci_check_iomem(struct pci_dev *dev, const struct ide_port_info * } /** - * ide_hwif_configure - configure an IDE interface + * ide_hw_configure - configure a hw_regs_t instance * @dev: PCI device holding interface * @d: IDE port info * @port: port number * @irq: PCI IRQ + * @hw: hw_regs_t instance corresponding to this port * * Perform the initial set up for the hardware interface structure. This * is done per interface port rather than per PCI device. There may be * more than one port per device. * - * Returns the new hardware interface structure, or NULL on a failure + * Returns zero on success or an error code. */ -static ide_hwif_t *ide_hwif_configure(struct pci_dev *dev, - const struct ide_port_info *d, - unsigned int port, int irq) +static int ide_hw_configure(struct pci_dev *dev, const struct ide_port_info *d, + unsigned int port, int irq, hw_regs_t *hw) { unsigned long ctl = 0, base = 0; - ide_hwif_t *hwif; - struct hw_regs_s hw; if ((d->host_flags & IDE_HFLAG_ISA_PORTS) == 0) { if (ide_pci_check_iomem(dev, d, 2 * port) || ide_pci_check_iomem(dev, d, 2 * port + 1)) { - printk(KERN_ERR "%s: I/O baseregs (BIOS) are reported " - "as MEM for port %d!\n", d->name, port); - return NULL; + printk(KERN_ERR "%s %s: I/O baseregs (BIOS) are " + "reported as MEM for port %d!\n", + d->name, pci_name(dev), port); + return -EINVAL; } ctl = pci_resource_start(dev, 2*port+1); @@ -322,24 +338,18 @@ static ide_hwif_t *ide_hwif_configure(struct pci_dev *dev, } if (!base || !ctl) { - printk(KERN_ERR "%s: bad PCI BARs for port %d, skipping\n", - d->name, port); - return NULL; + printk(KERN_ERR "%s %s: bad PCI BARs for port %d, skipping\n", + d->name, pci_name(dev), port); + return -EINVAL; } - hwif = ide_find_port_slot(d); - if (hwif == NULL) - return NULL; - - memset(&hw, 0, sizeof(hw)); - hw.irq = irq; - hw.dev = &dev->dev; - hw.chipset = d->chipset ? d->chipset : ide_pci; - ide_std_init_ports(&hw, base, ctl | 2); + memset(hw, 0, sizeof(*hw)); + hw->irq = irq; + hw->dev = &dev->dev; + hw->chipset = d->chipset ? d->chipset : ide_pci; + ide_std_init_ports(hw, base, ctl | 2); - ide_init_port_hw(hwif, &hw); - - return hwif; + return 0; } #ifdef CONFIG_BLK_DEV_IDEDMA_PCI @@ -362,7 +372,15 @@ int ide_hwif_setup_dma(ide_hwif_t *hwif, const struct ide_port_info *d) (dev->class & 0x80))) { unsigned long base = ide_pci_dma_base(hwif, d); - if (base == 0 || ide_pci_set_master(dev, d->name) < 0) + if (base == 0) + return -1; + + hwif->dma_base = base; + + if (ide_pci_check_simplex(hwif, d) < 0) + return -1; + + if (ide_pci_set_master(dev, d->name) < 0) return -1; if (hwif->host_flags & IDE_HFLAG_MMIO) @@ -376,7 +394,7 @@ int ide_hwif_setup_dma(ide_hwif_t *hwif, const struct ide_port_info *d) if (ide_allocate_dma_engine(hwif)) return -1; - ide_setup_dma(hwif, base); + hwif->dma_ops = &sff_dma_ops; } return 0; @@ -388,14 +406,14 @@ int ide_hwif_setup_dma(ide_hwif_t *hwif, const struct ide_port_info *d) * @dev: PCI device * @d: IDE port info * @noisy: verbose flag - * @config: returned as 1 if we configured the hardware * * Set up the PCI and controller side of the IDE interface. This brings * up the PCI side of the device, checks that the device is enabled * and enables it if need be */ -static int ide_setup_pci_controller(struct pci_dev *dev, const struct ide_port_info *d, int noisy, int *config) +static int ide_setup_pci_controller(struct pci_dev *dev, + const struct ide_port_info *d, int noisy) { int ret; u16 pcicmd; @@ -409,15 +427,16 @@ static int ide_setup_pci_controller(struct pci_dev *dev, const struct ide_port_i ret = pci_read_config_word(dev, PCI_COMMAND, &pcicmd); if (ret < 0) { - printk(KERN_ERR "%s: error accessing PCI regs\n", d->name); + printk(KERN_ERR "%s %s: error accessing PCI regs\n", + d->name, pci_name(dev)); goto out; } if (!(pcicmd & PCI_COMMAND_IO)) { /* is device disabled? */ ret = ide_pci_configure(dev, d); if (ret < 0) goto out; - *config = 1; - printk(KERN_INFO "%s: device enabled (Linux)\n", d->name); + printk(KERN_INFO "%s %s: device enabled (Linux)\n", + d->name, pci_name(dev)); } out: @@ -429,7 +448,8 @@ out: * @dev: PCI device * @d: IDE port info * @pciirq: IRQ line - * @idx: ATA index table to update + * @hw: hw_regs_t instances corresponding to this PCI IDE device + * @hws: hw_regs_t pointers table to update * * Scan the interfaces attached to this device and do any * necessary per port setup. Attach the devices and ask the @@ -440,10 +460,10 @@ out: * where the chipset setup is not the default PCI IDE one. */ -void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d, int pciirq, u8 *idx) +void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d, + int pciirq, hw_regs_t *hw, hw_regs_t **hws) { int channels = (d->host_flags & IDE_HFLAG_SINGLE) ? 1 : 2, port; - ide_hwif_t *hwif; u8 tmp; /* @@ -455,15 +475,15 @@ void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d, int if (e->reg && (pci_read_config_byte(dev, e->reg, &tmp) || (tmp & e->mask) != e->val)) { - printk(KERN_INFO "%s: IDE port disabled\n", d->name); + printk(KERN_INFO "%s %s: IDE port disabled\n", + d->name, pci_name(dev)); continue; /* port not enabled */ } - hwif = ide_hwif_configure(dev, d, port, pciirq); - if (hwif == NULL) + if (ide_hw_configure(dev, d, port, pciirq, hw + port)) continue; - *(idx + port) = hwif->index; + *(hws + port) = hw + port; } } EXPORT_SYMBOL_GPL(ide_pci_setup_ports); @@ -480,95 +500,162 @@ EXPORT_SYMBOL_GPL(ide_pci_setup_ports); */ static int do_ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d, - u8 *idx, u8 noisy) + u8 noisy) { - int tried_config = 0; int pciirq, ret; - ret = ide_setup_pci_controller(dev, d, noisy, &tried_config); - if (ret < 0) - goto out; - /* * Can we trust the reported IRQ? */ pciirq = dev->irq; + /* + * This allows offboard ide-pci cards the enable a BIOS, + * verify interrupt settings of split-mirror pci-config + * space, place chipset into init-mode, and/or preserve + * an interrupt if the card is not native ide support. + */ + ret = d->init_chipset ? d->init_chipset(dev) : 0; + if (ret < 0) + goto out; + /* Is it an "IDE storage" device in non-PCI mode? */ if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && (dev->class & 5) != 5) { if (noisy) - printk(KERN_INFO "%s: not 100%% native mode: " - "will probe irqs later\n", d->name); - /* - * This allows offboard ide-pci cards the enable a BIOS, - * verify interrupt settings of split-mirror pci-config - * space, place chipset into init-mode, and/or preserve - * an interrupt if the card is not native ide support. - */ - ret = d->init_chipset ? d->init_chipset(dev, d->name) : 0; - if (ret < 0) - goto out; + printk(KERN_INFO "%s %s: not 100%% native mode: will " + "probe irqs later\n", d->name, pci_name(dev)); pciirq = ret; - } else if (tried_config) { - if (noisy) - printk(KERN_INFO "%s: will probe irqs later\n", d->name); - pciirq = 0; - } else if (!pciirq) { - if (noisy) - printk(KERN_WARNING "%s: bad irq (%d): will probe later\n", - d->name, pciirq); - pciirq = 0; - } else { - if (d->init_chipset) { - ret = d->init_chipset(dev, d->name); - if (ret < 0) - goto out; - } - if (noisy) - printk(KERN_INFO "%s: 100%% native mode on irq %d\n", - d->name, pciirq); + } else if (!pciirq && noisy) { + printk(KERN_WARNING "%s %s: bad irq (%d): will probe later\n", + d->name, pci_name(dev), pciirq); + } else if (noisy) { + printk(KERN_INFO "%s %s: 100%% native mode on irq %d\n", + d->name, pci_name(dev), pciirq); } - /* FIXME: silent failure can happen */ - - ide_pci_setup_ports(dev, d, pciirq, idx); + ret = pciirq; out: return ret; } -int ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) +int ide_pci_init_one(struct pci_dev *dev, const struct ide_port_info *d, + void *priv) { - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + struct ide_host *host; + hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; int ret; - ret = do_ide_setup_pci_device(dev, d, &idx[0], 1); + ret = ide_setup_pci_controller(dev, d, 1); + if (ret < 0) + goto out; + + ide_pci_setup_ports(dev, d, 0, &hw[0], &hws[0]); + + host = ide_host_alloc(d, hws); + if (host == NULL) { + ret = -ENOMEM; + goto out; + } + + host->dev[0] = &dev->dev; + + host->host_priv = priv; + + pci_set_drvdata(dev, host); + + ret = do_ide_setup_pci_device(dev, d, 1); + if (ret < 0) + goto out; - if (ret >= 0) - ide_device_add(idx, d); + /* fixup IRQ */ + hw[1].irq = hw[0].irq = ret; + ret = ide_host_register(host, d, hws); + if (ret) + ide_host_free(host); +out: return ret; } -EXPORT_SYMBOL_GPL(ide_setup_pci_device); +EXPORT_SYMBOL_GPL(ide_pci_init_one); -int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, - const struct ide_port_info *d) +int ide_pci_init_two(struct pci_dev *dev1, struct pci_dev *dev2, + const struct ide_port_info *d, void *priv) { struct pci_dev *pdev[] = { dev1, dev2 }; + struct ide_host *host; int ret, i; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; + + for (i = 0; i < 2; i++) { + ret = ide_setup_pci_controller(pdev[i], d, !i); + if (ret < 0) + goto out; + + ide_pci_setup_ports(pdev[i], d, 0, &hw[i*2], &hws[i*2]); + } + + host = ide_host_alloc(d, hws); + if (host == NULL) { + ret = -ENOMEM; + goto out; + } + + host->dev[0] = &dev1->dev; + host->dev[1] = &dev2->dev; + + host->host_priv = priv; + + pci_set_drvdata(pdev[0], host); + pci_set_drvdata(pdev[1], host); for (i = 0; i < 2; i++) { - ret = do_ide_setup_pci_device(pdev[i], d, &idx[i*2], !i); + ret = do_ide_setup_pci_device(pdev[i], d, !i); + /* * FIXME: Mom, mom, they stole me the helper function to undo * do_ide_setup_pci_device() on the first device! */ if (ret < 0) goto out; + + /* fixup IRQ */ + hw[i*2 + 1].irq = hw[i*2].irq = ret; } - ide_device_add(idx, d); + ret = ide_host_register(host, d, hws); + if (ret) + ide_host_free(host); out: return ret; } -EXPORT_SYMBOL_GPL(ide_setup_pci_devices); +EXPORT_SYMBOL_GPL(ide_pci_init_two); + +void ide_pci_remove(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + struct pci_dev *dev2 = host->dev[1] ? to_pci_dev(host->dev[1]) : NULL; + int bars; + + if (host->host_flags & IDE_HFLAG_SINGLE) + bars = (1 << 2) - 1; + else + bars = (1 << 4) - 1; + + if ((host->host_flags & IDE_HFLAG_NO_DMA) == 0) { + if (host->host_flags & IDE_HFLAG_CS5520) + bars |= (1 << 2); + else + bars |= (1 << 4); + } + + ide_host_remove(host); + + if (dev2) + pci_release_selected_regions(dev2, bars); + pci_release_selected_regions(dev, bars); + + if (dev2) + pci_disable_device(dev2); + pci_disable_device(dev); +} +EXPORT_SYMBOL_GPL(ide_pci_remove); diff --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c index 9d19aec5820a..b6eb2cf25914 100644 --- a/drivers/ieee1394/dv1394.c +++ b/drivers/ieee1394/dv1394.c @@ -2296,9 +2296,10 @@ static void dv1394_add_host(struct hpsb_host *host) ohci = (struct ti_ohci *)host->hostdata; - device_create(hpsb_protocol_class, NULL, MKDEV( - IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_DV1394 * 16 + (id<<2)), - "dv1394-%d", id); + device_create_drvdata(hpsb_protocol_class, NULL, + MKDEV(IEEE1394_MAJOR, + IEEE1394_MINOR_BLOCK_DV1394 * 16 + (id<<2)), NULL, + "dv1394-%d", id); dv1394_init(ohci, DV1394_NTSC, MODE_RECEIVE); dv1394_init(ohci, DV1394_NTSC, MODE_TRANSMIT); diff --git a/drivers/ieee1394/iso.c b/drivers/ieee1394/iso.c index 07ca35c98f96..1cf6487b65ba 100644 --- a/drivers/ieee1394/iso.c +++ b/drivers/ieee1394/iso.c @@ -11,6 +11,7 @@ #include <linux/pci.h> #include <linux/sched.h> +#include <linux/mm.h> #include <linux/slab.h> #include "hosts.h" diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c index 05710c7c1220..994a21e5a0aa 100644 --- a/drivers/ieee1394/nodemgr.c +++ b/drivers/ieee1394/nodemgr.c @@ -754,7 +754,8 @@ static void nodemgr_remove_uds(struct node_entry *ne) */ mutex_lock(&nodemgr_serialize_remove_uds); for (;;) { - dev = class_find_device(&nodemgr_ud_class, ne, __match_ne); + dev = class_find_device(&nodemgr_ud_class, NULL, ne, + __match_ne); if (!dev) break; ud = container_of(dev, struct unit_directory, unit_dev); @@ -901,7 +902,8 @@ static struct node_entry *find_entry_by_guid(u64 guid) struct device *dev; struct node_entry *ne; - dev = class_find_device(&nodemgr_ne_class, &guid, __match_ne_guid); + dev = class_find_device(&nodemgr_ne_class, NULL, &guid, + __match_ne_guid); if (!dev) return NULL; ne = container_of(dev, struct node_entry, node_dev); @@ -940,7 +942,8 @@ static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host, param.host = host; param.nodeid = nodeid; - dev = class_find_device(&nodemgr_ne_class, ¶m, __match_ne_nodeid); + dev = class_find_device(&nodemgr_ne_class, NULL, ¶m, + __match_ne_nodeid); if (!dev) return NULL; ne = container_of(dev, struct node_entry, node_dev); @@ -1453,7 +1456,8 @@ static void nodemgr_suspend_ne(struct node_entry *ne) ne->in_limbo = 1; WARN_ON(device_create_file(&ne->device, &dev_attr_ne_in_limbo)); - class_for_each_device(&nodemgr_ud_class, ne, __nodemgr_driver_suspend); + class_for_each_device(&nodemgr_ud_class, NULL, ne, + __nodemgr_driver_suspend); } @@ -1462,7 +1466,8 @@ static void nodemgr_resume_ne(struct node_entry *ne) ne->in_limbo = 0; device_remove_file(&ne->device, &dev_attr_ne_in_limbo); - class_for_each_device(&nodemgr_ud_class, ne, __nodemgr_driver_resume); + class_for_each_device(&nodemgr_ud_class, NULL, ne, + __nodemgr_driver_resume); HPSB_DEBUG("Node resumed: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid); } @@ -1498,7 +1503,8 @@ static int __nodemgr_update_pdrv(struct device *dev, void *data) static void nodemgr_update_pdrv(struct node_entry *ne) { - class_for_each_device(&nodemgr_ud_class, ne, __nodemgr_update_pdrv); + class_for_each_device(&nodemgr_ud_class, NULL, ne, + __nodemgr_update_pdrv); } @@ -1591,7 +1597,8 @@ static void nodemgr_node_probe(struct host_info *hi, int generation) * while probes are time-consuming. (Well, those probes need some * improvement...) */ - class_for_each_device(&nodemgr_ne_class, ¶m, __nodemgr_node_probe); + class_for_each_device(&nodemgr_ne_class, NULL, ¶m, + __nodemgr_node_probe); /* If we had a bus reset while we were scanning the bus, it is * possible that we did not probe all nodes. In that case, we @@ -1826,7 +1833,7 @@ int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *)) hip.cb = cb; hip.data = data; - error = class_for_each_device(&hpsb_host_class, &hip, + error = class_for_each_device(&hpsb_host_class, NULL, &hip, __nodemgr_for_each_host); return error; diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c index 96f2847b0405..6fa9e4a21840 100644 --- a/drivers/ieee1394/raw1394.c +++ b/drivers/ieee1394/raw1394.c @@ -3010,10 +3010,10 @@ static int __init init_raw1394(void) hpsb_register_highlevel(&raw1394_highlevel); if (IS_ERR - (device_create( + (device_create_drvdata( hpsb_protocol_class, NULL, MKDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_RAW1394 * 16), - RAW1394_DEVICE_NAME))) { + NULL, RAW1394_DEVICE_NAME))) { ret = -EFAULT; goto out_unreg; } diff --git a/drivers/ieee1394/video1394.c b/drivers/ieee1394/video1394.c index 069b9f6bf16d..25db6e67fa4e 100644 --- a/drivers/ieee1394/video1394.c +++ b/drivers/ieee1394/video1394.c @@ -1341,9 +1341,9 @@ static void video1394_add_host (struct hpsb_host *host) hpsb_set_hostinfo_key(&video1394_highlevel, host, ohci->host->id); minor = IEEE1394_MINOR_BLOCK_VIDEO1394 * 16 + ohci->host->id; - device_create(hpsb_protocol_class, NULL, - MKDEV(IEEE1394_MAJOR, minor), - "%s-%d", VIDEO1394_DRIVER_NAME, ohci->host->id); + device_create_drvdata(hpsb_protocol_class, NULL, + MKDEV(IEEE1394_MAJOR, minor), NULL, + "%s-%d", VIDEO1394_DRIVER_NAME, ohci->host->id); } diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index 55738eead3bf..922d35f4fc08 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -44,6 +44,7 @@ #include <linux/spinlock.h> #include <linux/sysfs.h> #include <linux/workqueue.h> +#include <linux/kdev_t.h> #include <rdma/ib_cache.h> #include <rdma/ib_cm.h> @@ -162,8 +163,8 @@ struct cm_port { struct cm_device { struct list_head list; - struct ib_device *device; - struct kobject dev_obj; + struct ib_device *ib_device; + struct device *device; u8 ack_delay; struct cm_port *port[0]; }; @@ -339,7 +340,7 @@ static void cm_init_av_for_response(struct cm_port *port, struct ib_wc *wc, { av->port = port; av->pkey_index = wc->pkey_index; - ib_init_ah_from_wc(port->cm_dev->device, port->port_num, wc, + ib_init_ah_from_wc(port->cm_dev->ib_device, port->port_num, wc, grh, &av->ah_attr); } @@ -353,7 +354,7 @@ static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av) read_lock_irqsave(&cm.device_lock, flags); list_for_each_entry(cm_dev, &cm.device_list, list) { - if (!ib_find_cached_gid(cm_dev->device, &path->sgid, + if (!ib_find_cached_gid(cm_dev->ib_device, &path->sgid, &p, NULL)) { port = cm_dev->port[p-1]; break; @@ -364,13 +365,13 @@ static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av) if (!port) return -EINVAL; - ret = ib_find_cached_pkey(cm_dev->device, port->port_num, + ret = ib_find_cached_pkey(cm_dev->ib_device, port->port_num, be16_to_cpu(path->pkey), &av->pkey_index); if (ret) return ret; av->port = port; - ib_init_ah_from_path(cm_dev->device, port->port_num, path, + ib_init_ah_from_path(cm_dev->ib_device, port->port_num, path, &av->ah_attr); av->timeout = path->packet_life_time + 1; return 0; @@ -1515,7 +1516,7 @@ static int cm_req_handler(struct cm_work *work) req_msg = (struct cm_req_msg *)work->mad_recv_wc->recv_buf.mad; - cm_id = ib_create_cm_id(work->port->cm_dev->device, NULL, NULL); + cm_id = ib_create_cm_id(work->port->cm_dev->ib_device, NULL, NULL); if (IS_ERR(cm_id)) return PTR_ERR(cm_id); @@ -1550,7 +1551,7 @@ static int cm_req_handler(struct cm_work *work) cm_format_paths_from_req(req_msg, &work->path[0], &work->path[1]); ret = cm_init_av_by_path(&work->path[0], &cm_id_priv->av); if (ret) { - ib_get_cached_gid(work->port->cm_dev->device, + ib_get_cached_gid(work->port->cm_dev->ib_device, work->port->port_num, 0, &work->path[0].sgid); ib_send_cm_rej(cm_id, IB_CM_REJ_INVALID_GID, &work->path[0].sgid, sizeof work->path[0].sgid, @@ -2950,7 +2951,7 @@ static int cm_sidr_req_handler(struct cm_work *work) struct cm_sidr_req_msg *sidr_req_msg; struct ib_wc *wc; - cm_id = ib_create_cm_id(work->port->cm_dev->device, NULL, NULL); + cm_id = ib_create_cm_id(work->port->cm_dev->ib_device, NULL, NULL); if (IS_ERR(cm_id)) return PTR_ERR(cm_id); cm_id_priv = container_of(cm_id, struct cm_id_private, id); @@ -3578,7 +3579,7 @@ static void cm_get_ack_delay(struct cm_device *cm_dev) { struct ib_device_attr attr; - if (ib_query_device(cm_dev->device, &attr)) + if (ib_query_device(cm_dev->ib_device, &attr)) cm_dev->ack_delay = 0; /* acks will rely on packet life time */ else cm_dev->ack_delay = attr.local_ca_ack_delay; @@ -3618,18 +3619,6 @@ static struct kobj_type cm_port_obj_type = { .release = cm_release_port_obj }; -static void cm_release_dev_obj(struct kobject *obj) -{ - struct cm_device *cm_dev; - - cm_dev = container_of(obj, struct cm_device, dev_obj); - kfree(cm_dev); -} - -static struct kobj_type cm_dev_obj_type = { - .release = cm_release_dev_obj -}; - struct class cm_class = { .name = "infiniband_cm", }; @@ -3640,7 +3629,7 @@ static int cm_create_port_fs(struct cm_port *port) int i, ret; ret = kobject_init_and_add(&port->port_obj, &cm_port_obj_type, - &port->cm_dev->dev_obj, + &port->cm_dev->device->kobj, "%d", port->port_num); if (ret) { kfree(port); @@ -3676,7 +3665,7 @@ static void cm_remove_port_fs(struct cm_port *port) kobject_put(&port->port_obj); } -static void cm_add_one(struct ib_device *device) +static void cm_add_one(struct ib_device *ib_device) { struct cm_device *cm_dev; struct cm_port *port; @@ -3691,26 +3680,27 @@ static void cm_add_one(struct ib_device *device) int ret; u8 i; - if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB) + if (rdma_node_get_transport(ib_device->node_type) != RDMA_TRANSPORT_IB) return; cm_dev = kzalloc(sizeof(*cm_dev) + sizeof(*port) * - device->phys_port_cnt, GFP_KERNEL); + ib_device->phys_port_cnt, GFP_KERNEL); if (!cm_dev) return; - cm_dev->device = device; + cm_dev->ib_device = ib_device; cm_get_ack_delay(cm_dev); - ret = kobject_init_and_add(&cm_dev->dev_obj, &cm_dev_obj_type, - &cm_class.subsys.kobj, "%s", device->name); - if (ret) { + cm_dev->device = device_create_drvdata(&cm_class, &ib_device->dev, + MKDEV(0, 0), NULL, + "%s", ib_device->name); + if (!cm_dev->device) { kfree(cm_dev); return; } set_bit(IB_MGMT_METHOD_SEND, reg_req.method_mask); - for (i = 1; i <= device->phys_port_cnt; i++) { + for (i = 1; i <= ib_device->phys_port_cnt; i++) { port = kzalloc(sizeof *port, GFP_KERNEL); if (!port) goto error1; @@ -3723,7 +3713,7 @@ static void cm_add_one(struct ib_device *device) if (ret) goto error1; - port->mad_agent = ib_register_mad_agent(device, i, + port->mad_agent = ib_register_mad_agent(ib_device, i, IB_QPT_GSI, ®_req, 0, @@ -3733,11 +3723,11 @@ static void cm_add_one(struct ib_device *device) if (IS_ERR(port->mad_agent)) goto error2; - ret = ib_modify_port(device, i, 0, &port_modify); + ret = ib_modify_port(ib_device, i, 0, &port_modify); if (ret) goto error3; } - ib_set_client_data(device, &cm_client, cm_dev); + ib_set_client_data(ib_device, &cm_client, cm_dev); write_lock_irqsave(&cm.device_lock, flags); list_add_tail(&cm_dev->list, &cm.device_list); @@ -3753,14 +3743,14 @@ error1: port_modify.clr_port_cap_mask = IB_PORT_CM_SUP; while (--i) { port = cm_dev->port[i-1]; - ib_modify_port(device, port->port_num, 0, &port_modify); + ib_modify_port(ib_device, port->port_num, 0, &port_modify); ib_unregister_mad_agent(port->mad_agent); cm_remove_port_fs(port); } - kobject_put(&cm_dev->dev_obj); + device_unregister(cm_dev->device); } -static void cm_remove_one(struct ib_device *device) +static void cm_remove_one(struct ib_device *ib_device) { struct cm_device *cm_dev; struct cm_port *port; @@ -3770,7 +3760,7 @@ static void cm_remove_one(struct ib_device *device) unsigned long flags; int i; - cm_dev = ib_get_client_data(device, &cm_client); + cm_dev = ib_get_client_data(ib_device, &cm_client); if (!cm_dev) return; @@ -3778,14 +3768,14 @@ static void cm_remove_one(struct ib_device *device) list_del(&cm_dev->list); write_unlock_irqrestore(&cm.device_lock, flags); - for (i = 1; i <= device->phys_port_cnt; i++) { + for (i = 1; i <= ib_device->phys_port_cnt; i++) { port = cm_dev->port[i-1]; - ib_modify_port(device, port->port_num, 0, &port_modify); + ib_modify_port(ib_device, port->port_num, 0, &port_modify); ib_unregister_mad_agent(port->mad_agent); flush_workqueue(cm.wq); cm_remove_port_fs(port); } - kobject_put(&cm_dev->dev_obj); + device_unregister(cm_dev->device); } static int __init ib_cm_init(void) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index ae11d5cc74d0..e980ff3335db 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -168,6 +168,12 @@ struct cma_work { struct rdma_cm_event event; }; +struct cma_ndev_work { + struct work_struct work; + struct rdma_id_private *id; + struct rdma_cm_event event; +}; + union cma_ip_addr { struct in6_addr ip6; struct { @@ -914,7 +920,10 @@ static int cma_ib_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event) struct rdma_cm_event event; int ret = 0; - if (cma_disable_callback(id_priv, CMA_CONNECT)) + if ((ib_event->event != IB_CM_TIMEWAIT_EXIT && + cma_disable_callback(id_priv, CMA_CONNECT)) || + (ib_event->event == IB_CM_TIMEWAIT_EXIT && + cma_disable_callback(id_priv, CMA_DISCONNECT))) return 0; memset(&event, 0, sizeof event); @@ -950,6 +959,8 @@ static int cma_ib_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event) event.event = RDMA_CM_EVENT_DISCONNECTED; break; case IB_CM_TIMEWAIT_EXIT: + event.event = RDMA_CM_EVENT_TIMEWAIT_EXIT; + break; case IB_CM_MRA_RECEIVED: /* ignore event */ goto out; @@ -1598,6 +1609,30 @@ out: kfree(work); } +static void cma_ndev_work_handler(struct work_struct *_work) +{ + struct cma_ndev_work *work = container_of(_work, struct cma_ndev_work, work); + struct rdma_id_private *id_priv = work->id; + int destroy = 0; + + mutex_lock(&id_priv->handler_mutex); + if (id_priv->state == CMA_DESTROYING || + id_priv->state == CMA_DEVICE_REMOVAL) + goto out; + + if (id_priv->id.event_handler(&id_priv->id, &work->event)) { + cma_exch(id_priv, CMA_DESTROYING); + destroy = 1; + } + +out: + mutex_unlock(&id_priv->handler_mutex); + cma_deref_id(id_priv); + if (destroy) + rdma_destroy_id(&id_priv->id); + kfree(work); +} + static int cma_resolve_ib_route(struct rdma_id_private *id_priv, int timeout_ms) { struct rdma_route *route = &id_priv->id.route; @@ -2723,6 +2758,65 @@ void rdma_leave_multicast(struct rdma_cm_id *id, struct sockaddr *addr) } EXPORT_SYMBOL(rdma_leave_multicast); +static int cma_netdev_change(struct net_device *ndev, struct rdma_id_private *id_priv) +{ + struct rdma_dev_addr *dev_addr; + struct cma_ndev_work *work; + + dev_addr = &id_priv->id.route.addr.dev_addr; + + if ((dev_addr->src_dev == ndev) && + memcmp(dev_addr->src_dev_addr, ndev->dev_addr, ndev->addr_len)) { + printk(KERN_INFO "RDMA CM addr change for ndev %s used by id %p\n", + ndev->name, &id_priv->id); + work = kzalloc(sizeof *work, GFP_KERNEL); + if (!work) + return -ENOMEM; + + INIT_WORK(&work->work, cma_ndev_work_handler); + work->id = id_priv; + work->event.event = RDMA_CM_EVENT_ADDR_CHANGE; + atomic_inc(&id_priv->refcount); + queue_work(cma_wq, &work->work); + } + + return 0; +} + +static int cma_netdev_callback(struct notifier_block *self, unsigned long event, + void *ctx) +{ + struct net_device *ndev = (struct net_device *)ctx; + struct cma_device *cma_dev; + struct rdma_id_private *id_priv; + int ret = NOTIFY_DONE; + + if (dev_net(ndev) != &init_net) + return NOTIFY_DONE; + + if (event != NETDEV_BONDING_FAILOVER) + return NOTIFY_DONE; + + if (!(ndev->flags & IFF_MASTER) || !(ndev->priv_flags & IFF_BONDING)) + return NOTIFY_DONE; + + mutex_lock(&lock); + list_for_each_entry(cma_dev, &dev_list, list) + list_for_each_entry(id_priv, &cma_dev->id_list, list) { + ret = cma_netdev_change(ndev, id_priv); + if (ret) + goto out; + } + +out: + mutex_unlock(&lock); + return ret; +} + +static struct notifier_block cma_nb = { + .notifier_call = cma_netdev_callback +}; + static void cma_add_one(struct ib_device *device) { struct cma_device *cma_dev; @@ -2831,6 +2925,7 @@ static int cma_init(void) ib_sa_register_client(&sa_client); rdma_addr_register_client(&addr_client); + register_netdevice_notifier(&cma_nb); ret = ib_register_client(&cma_client); if (ret) @@ -2838,6 +2933,7 @@ static int cma_init(void) return 0; err: + unregister_netdevice_notifier(&cma_nb); rdma_addr_unregister_client(&addr_client); ib_sa_unregister_client(&sa_client); destroy_workqueue(cma_wq); @@ -2847,6 +2943,7 @@ err: static void cma_cleanup(void) { ib_unregister_client(&cma_client); + unregister_netdevice_notifier(&cma_nb); rdma_addr_unregister_client(&addr_client); ib_sa_unregister_client(&sa_client); destroy_workqueue(cma_wq); diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c index 81c9195b512a..8f9509e1ebf7 100644 --- a/drivers/infiniband/core/iwcm.c +++ b/drivers/infiniband/core/iwcm.c @@ -942,8 +942,7 @@ static int iwcm_init_qp_init_attr(struct iwcm_id_private *cm_id_priv, case IW_CM_STATE_CONN_RECV: case IW_CM_STATE_ESTABLISHED: *qp_attr_mask = IB_QP_STATE | IB_QP_ACCESS_FLAGS; - qp_attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE | - IB_ACCESS_REMOTE_WRITE| + qp_attr->qp_access_flags = IB_ACCESS_REMOTE_WRITE| IB_ACCESS_REMOTE_READ; ret = 0; break; diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c index 1341de793e51..7863a50d56f2 100644 --- a/drivers/infiniband/core/sa_query.c +++ b/drivers/infiniband/core/sa_query.c @@ -1064,7 +1064,8 @@ static void ib_sa_remove_one(struct ib_device *device) for (i = 0; i <= sa_dev->end_port - sa_dev->start_port; ++i) { ib_unregister_mad_agent(sa_dev->port[i].agent); - kref_put(&sa_dev->port[i].sm_ah->ref, free_sm_ah); + if (sa_dev->port[i].sm_ah) + kref_put(&sa_dev->port[i].sm_ah->ref, free_sm_ah); } kfree(sa_dev); diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c index 9494005d1c9a..e603736682bf 100644 --- a/drivers/infiniband/core/ucm.c +++ b/drivers/infiniband/core/ucm.c @@ -43,7 +43,6 @@ #include <linux/cdev.h> #include <linux/idr.h> #include <linux/mutex.h> -#include <linux/smp_lock.h> #include <asm/uaccess.h> @@ -1154,11 +1153,18 @@ static unsigned int ib_ucm_poll(struct file *filp, return mask; } +/* + * ib_ucm_open() does not need the BKL: + * + * - no global state is referred to; + * - there is no ioctl method to race against; + * - no further module initialization is required for open to work + * after the device is registered. + */ static int ib_ucm_open(struct inode *inode, struct file *filp) { struct ib_ucm_file *file; - cycle_kernel_lock(); file = kmalloc(sizeof(*file), GFP_KERNEL); if (!file) return -ENOMEM; diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index 195f97302fe5..b41dd26bbfa1 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -38,7 +38,6 @@ #include <linux/in.h> #include <linux/in6.h> #include <linux/miscdevice.h> -#include <linux/smp_lock.h> #include <rdma/rdma_user_cm.h> #include <rdma/ib_marshall.h> @@ -1149,6 +1148,14 @@ static unsigned int ucma_poll(struct file *filp, struct poll_table_struct *wait) return mask; } +/* + * ucma_open() does not need the BKL: + * + * - no global state is referred to; + * - there is no ioctl method to race against; + * - no further module initialization is required for open to work + * after the device is registered. + */ static int ucma_open(struct inode *inode, struct file *filp) { struct ucma_file *file; @@ -1157,7 +1164,6 @@ static int ucma_open(struct inode *inode, struct file *filp) if (!file) return -ENOMEM; - lock_kernel(); INIT_LIST_HEAD(&file->event_list); INIT_LIST_HEAD(&file->ctx_list); init_waitqueue_head(&file->poll_wait); @@ -1165,7 +1171,6 @@ static int ucma_open(struct inode *inode, struct file *filp) filp->private_data = file; file->filp = filp; - unlock_kernel(); return 0; } diff --git a/drivers/infiniband/hw/ehca/ehca_classes.h b/drivers/infiniband/hw/ehca/ehca_classes.h index 1e9e99a13933..0b0618edd645 100644 --- a/drivers/infiniband/hw/ehca/ehca_classes.h +++ b/drivers/infiniband/hw/ehca/ehca_classes.h @@ -194,6 +194,7 @@ struct ehca_qp { u32 packet_count; atomic_t nr_events; /* events seen */ wait_queue_head_t wait_completion; + int mig_armed; }; #define IS_SRQ(qp) (qp->ext_type == EQPT_SRQ) diff --git a/drivers/infiniband/hw/ehca/ehca_hca.c b/drivers/infiniband/hw/ehca/ehca_hca.c index bc3b37d2070f..46288220cfbb 100644 --- a/drivers/infiniband/hw/ehca/ehca_hca.c +++ b/drivers/infiniband/hw/ehca/ehca_hca.c @@ -114,7 +114,9 @@ int ehca_query_device(struct ib_device *ibdev, struct ib_device_attr *props) } props->max_pkeys = 16; - props->local_ca_ack_delay = min_t(u8, rblock->local_ca_ack_delay, 255); + /* Some FW versions say 0 here; insert sensible value in that case */ + props->local_ca_ack_delay = rblock->local_ca_ack_delay ? + min_t(u8, rblock->local_ca_ack_delay, 255) : 12; props->max_raw_ipv6_qp = limit_uint(rblock->max_raw_ipv6_qp); props->max_raw_ethy_qp = limit_uint(rblock->max_raw_ethy_qp); props->max_mcast_grp = limit_uint(rblock->max_mcast_grp); diff --git a/drivers/infiniband/hw/ehca/ehca_irq.c b/drivers/infiniband/hw/ehca/ehca_irq.c index 0792d930c481..cb55be04442c 100644 --- a/drivers/infiniband/hw/ehca/ehca_irq.c +++ b/drivers/infiniband/hw/ehca/ehca_irq.c @@ -178,6 +178,10 @@ static void dispatch_qp_event(struct ehca_shca *shca, struct ehca_qp *qp, { struct ib_event event; + /* PATH_MIG without the QP ever having been armed is false alarm */ + if (event_type == IB_EVENT_PATH_MIG && !qp->mig_armed) + return; + event.device = &shca->ib_device; event.event = event_type; @@ -646,8 +650,8 @@ static inline int find_next_online_cpu(struct ehca_comp_pool *pool) ehca_dmp(&cpu_online_map, sizeof(cpumask_t), ""); spin_lock_irqsave(&pool->last_cpu_lock, flags); - cpu = next_cpu(pool->last_cpu, cpu_online_map); - if (cpu == NR_CPUS) + cpu = next_cpu_nr(pool->last_cpu, cpu_online_map); + if (cpu >= nr_cpu_ids) cpu = first_cpu(cpu_online_map); pool->last_cpu = cpu; spin_unlock_irqrestore(&pool->last_cpu_lock, flags); diff --git a/drivers/infiniband/hw/ehca/ehca_qp.c b/drivers/infiniband/hw/ehca/ehca_qp.c index 3f59587338ea..ea13efddf175 100644 --- a/drivers/infiniband/hw/ehca/ehca_qp.c +++ b/drivers/infiniband/hw/ehca/ehca_qp.c @@ -1460,6 +1460,8 @@ static int internal_modify_qp(struct ib_qp *ibqp, goto modify_qp_exit2; } mqpcb->path_migration_state = attr->path_mig_state + 1; + if (attr->path_mig_state == IB_MIG_REARM) + my_qp->mig_armed = 1; update_mask |= EHCA_BMASK_SET(MQPCB_MASK_PATH_MIGRATION_STATE, 1); } diff --git a/drivers/infiniband/hw/ehca/ipz_pt_fn.c b/drivers/infiniband/hw/ehca/ipz_pt_fn.c index 661f8db62706..c3a328465431 100644 --- a/drivers/infiniband/hw/ehca/ipz_pt_fn.c +++ b/drivers/infiniband/hw/ehca/ipz_pt_fn.c @@ -163,6 +163,7 @@ static int alloc_small_queue_page(struct ipz_queue *queue, struct ehca_pd *pd) out: ehca_err(pd->ib_pd.device, "failed to allocate small queue page"); + mutex_unlock(&pd->lock); return 0; } diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c index 35f301c88b57..56c0eda3c077 100644 --- a/drivers/infiniband/hw/ipath/ipath_file_ops.c +++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c @@ -2455,7 +2455,7 @@ static int init_cdev(int minor, char *name, const struct file_operations *fops, goto err_cdev; } - device = device_create(ipath_class, NULL, dev, name); + device = device_create_drvdata(ipath_class, NULL, dev, NULL, name); if (IS_ERR(device)) { ret = PTR_ERR(device); diff --git a/drivers/infiniband/hw/ipath/ipath_sdma.c b/drivers/infiniband/hw/ipath/ipath_sdma.c index eaba03273e4f..284c9bca517e 100644 --- a/drivers/infiniband/hw/ipath/ipath_sdma.c +++ b/drivers/infiniband/hw/ipath/ipath_sdma.c @@ -698,7 +698,7 @@ retry: addr = dma_map_single(&dd->pcidev->dev, tx->txreq.map_addr, tx->map_len, DMA_TO_DEVICE); - if (dma_mapping_error(addr)) { + if (dma_mapping_error(&dd->pcidev->dev, addr)) { ret = -EIO; goto unlock; } diff --git a/drivers/infiniband/hw/ipath/ipath_user_sdma.c b/drivers/infiniband/hw/ipath/ipath_user_sdma.c index 86e016916cd1..82d9a0b5ca2f 100644 --- a/drivers/infiniband/hw/ipath/ipath_user_sdma.c +++ b/drivers/infiniband/hw/ipath/ipath_user_sdma.c @@ -206,7 +206,7 @@ static int ipath_user_sdma_coalesce(const struct ipath_devdata *dd, dma_addr = dma_map_page(&dd->pcidev->dev, page, 0, len, DMA_TO_DEVICE); - if (dma_mapping_error(dma_addr)) { + if (dma_mapping_error(&dd->pcidev->dev, dma_addr)) { ret = -ENOMEM; goto free_unmap; } @@ -301,7 +301,7 @@ static int ipath_user_sdma_pin_pages(const struct ipath_devdata *dd, pages[j], 0, flen, DMA_TO_DEVICE); unsigned long fofs = addr & ~PAGE_MASK; - if (dma_mapping_error(dma_addr)) { + if (dma_mapping_error(&dd->pcidev->dev, dma_addr)) { ret = -ENOMEM; goto done; } @@ -508,7 +508,7 @@ static int ipath_user_sdma_queue_pkts(const struct ipath_devdata *dd, if (page) { dma_addr = dma_map_page(&dd->pcidev->dev, page, 0, len, DMA_TO_DEVICE); - if (dma_mapping_error(dma_addr)) { + if (dma_mapping_error(&dd->pcidev->dev, dma_addr)) { ret = -ENOMEM; goto free_pbc; } diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c index 299f20832ab6..a1464574bfdd 100644 --- a/drivers/infiniband/hw/mlx4/cq.c +++ b/drivers/infiniband/hw/mlx4/cq.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2007, 2008 Mellanox Technologies. 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 @@ -637,6 +638,7 @@ repoll: case MLX4_OPCODE_SEND_IMM: wc->wc_flags |= IB_WC_WITH_IMM; case MLX4_OPCODE_SEND: + case MLX4_OPCODE_SEND_INVAL: wc->opcode = IB_WC_SEND; break; case MLX4_OPCODE_RDMA_READ: @@ -657,6 +659,12 @@ repoll: case MLX4_OPCODE_LSO: wc->opcode = IB_WC_LSO; break; + case MLX4_OPCODE_FMR: + wc->opcode = IB_WC_FAST_REG_MR; + break; + case MLX4_OPCODE_LOCAL_INVAL: + wc->opcode = IB_WC_LOCAL_INV; + break; } } else { wc->byte_len = be32_to_cpu(cqe->byte_cnt); @@ -667,6 +675,11 @@ repoll: wc->wc_flags = IB_WC_WITH_IMM; wc->ex.imm_data = cqe->immed_rss_invalid; break; + case MLX4_RECV_OPCODE_SEND_INVAL: + wc->opcode = IB_WC_RECV; + wc->wc_flags = IB_WC_WITH_INVALIDATE; + wc->ex.invalidate_rkey = be32_to_cpu(cqe->immed_rss_invalid); + break; case MLX4_RECV_OPCODE_SEND: wc->opcode = IB_WC_RECV; wc->wc_flags = 0; diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index bcf50648fa18..a3c2851c0545 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2007, 2008 Mellanox Technologies. 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 @@ -104,6 +105,12 @@ static int mlx4_ib_query_device(struct ib_device *ibdev, props->device_cap_flags |= IB_DEVICE_UD_IP_CSUM; if (dev->dev->caps.max_gso_sz) props->device_cap_flags |= IB_DEVICE_UD_TSO; + if (dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_RESERVED_LKEY) + props->device_cap_flags |= IB_DEVICE_LOCAL_DMA_LKEY; + if ((dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_LOCAL_INV) && + (dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_REMOTE_INV) && + (dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_FAST_REG_WR)) + props->device_cap_flags |= IB_DEVICE_MEM_MGT_EXTENSIONS; props->vendor_id = be32_to_cpup((__be32 *) (out_mad->data + 36)) & 0xffffff; @@ -127,6 +134,7 @@ static int mlx4_ib_query_device(struct ib_device *ibdev, props->max_srq = dev->dev->caps.num_srqs - dev->dev->caps.reserved_srqs; props->max_srq_wr = dev->dev->caps.max_srq_wqes - 1; props->max_srq_sge = dev->dev->caps.max_srq_sge; + props->max_fast_reg_page_list_len = PAGE_SIZE / sizeof (u64); props->local_ca_ack_delay = dev->dev->caps.local_ca_ack_delay; props->atomic_cap = dev->dev->caps.flags & MLX4_DEV_CAP_FLAG_ATOMIC ? IB_ATOMIC_HCA : IB_ATOMIC_NONE; @@ -565,6 +573,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) strlcpy(ibdev->ib_dev.name, "mlx4_%d", IB_DEVICE_NAME_MAX); ibdev->ib_dev.owner = THIS_MODULE; ibdev->ib_dev.node_type = RDMA_NODE_IB_CA; + ibdev->ib_dev.local_dma_lkey = dev->caps.reserved_lkey; ibdev->ib_dev.phys_port_cnt = dev->caps.num_ports; ibdev->ib_dev.num_comp_vectors = 1; ibdev->ib_dev.dma_device = &dev->pdev->dev; @@ -627,6 +636,9 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) ibdev->ib_dev.get_dma_mr = mlx4_ib_get_dma_mr; ibdev->ib_dev.reg_user_mr = mlx4_ib_reg_user_mr; ibdev->ib_dev.dereg_mr = mlx4_ib_dereg_mr; + ibdev->ib_dev.alloc_fast_reg_mr = mlx4_ib_alloc_fast_reg_mr; + ibdev->ib_dev.alloc_fast_reg_page_list = mlx4_ib_alloc_fast_reg_page_list; + ibdev->ib_dev.free_fast_reg_page_list = mlx4_ib_free_fast_reg_page_list; ibdev->ib_dev.attach_mcast = mlx4_ib_mcg_attach; ibdev->ib_dev.detach_mcast = mlx4_ib_mcg_detach; ibdev->ib_dev.process_mad = mlx4_ib_process_mad; diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index c4cf5b69eefa..6e2b0dc21b61 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2006, 2007 Cisco Systems. All rights reserved. + * Copyright (c) 2007, 2008 Mellanox Technologies. 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 @@ -83,6 +84,11 @@ struct mlx4_ib_mr { struct ib_umem *umem; }; +struct mlx4_ib_fast_reg_page_list { + struct ib_fast_reg_page_list ibfrpl; + dma_addr_t map; +}; + struct mlx4_ib_fmr { struct ib_fmr ibfmr; struct mlx4_fmr mfmr; @@ -199,6 +205,11 @@ static inline struct mlx4_ib_mr *to_mmr(struct ib_mr *ibmr) return container_of(ibmr, struct mlx4_ib_mr, ibmr); } +static inline struct mlx4_ib_fast_reg_page_list *to_mfrpl(struct ib_fast_reg_page_list *ibfrpl) +{ + return container_of(ibfrpl, struct mlx4_ib_fast_reg_page_list, ibfrpl); +} + static inline struct mlx4_ib_fmr *to_mfmr(struct ib_fmr *ibfmr) { return container_of(ibfmr, struct mlx4_ib_fmr, ibfmr); @@ -239,6 +250,11 @@ struct ib_mr *mlx4_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, u64 virt_addr, int access_flags, struct ib_udata *udata); int mlx4_ib_dereg_mr(struct ib_mr *mr); +struct ib_mr *mlx4_ib_alloc_fast_reg_mr(struct ib_pd *pd, + int max_page_list_len); +struct ib_fast_reg_page_list *mlx4_ib_alloc_fast_reg_page_list(struct ib_device *ibdev, + int page_list_len); +void mlx4_ib_free_fast_reg_page_list(struct ib_fast_reg_page_list *page_list); int mlx4_ib_modify_cq(struct ib_cq *cq, u16 cq_count, u16 cq_period); int mlx4_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata); diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c index 68e92485fc76..a4cdb465cd1d 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2007, 2008 Mellanox Technologies. 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 @@ -183,6 +184,76 @@ int mlx4_ib_dereg_mr(struct ib_mr *ibmr) return 0; } +struct ib_mr *mlx4_ib_alloc_fast_reg_mr(struct ib_pd *pd, + int max_page_list_len) +{ + struct mlx4_ib_dev *dev = to_mdev(pd->device); + struct mlx4_ib_mr *mr; + int err; + + mr = kmalloc(sizeof *mr, GFP_KERNEL); + if (!mr) + return ERR_PTR(-ENOMEM); + + err = mlx4_mr_alloc(dev->dev, to_mpd(pd)->pdn, 0, 0, 0, + max_page_list_len, 0, &mr->mmr); + if (err) + goto err_free; + + err = mlx4_mr_enable(dev->dev, &mr->mmr); + if (err) + goto err_mr; + + return &mr->ibmr; + +err_mr: + mlx4_mr_free(dev->dev, &mr->mmr); + +err_free: + kfree(mr); + return ERR_PTR(err); +} + +struct ib_fast_reg_page_list *mlx4_ib_alloc_fast_reg_page_list(struct ib_device *ibdev, + int page_list_len) +{ + struct mlx4_ib_dev *dev = to_mdev(ibdev); + struct mlx4_ib_fast_reg_page_list *mfrpl; + int size = page_list_len * sizeof (u64); + + if (size > PAGE_SIZE) + return ERR_PTR(-EINVAL); + + mfrpl = kmalloc(sizeof *mfrpl, GFP_KERNEL); + if (!mfrpl) + return ERR_PTR(-ENOMEM); + + mfrpl->ibfrpl.page_list = dma_alloc_coherent(&dev->dev->pdev->dev, + size, &mfrpl->map, + GFP_KERNEL); + if (!mfrpl->ibfrpl.page_list) + goto err_free; + + WARN_ON(mfrpl->map & 0x3f); + + return &mfrpl->ibfrpl; + +err_free: + kfree(mfrpl); + return ERR_PTR(-ENOMEM); +} + +void mlx4_ib_free_fast_reg_page_list(struct ib_fast_reg_page_list *page_list) +{ + struct mlx4_ib_dev *dev = to_mdev(page_list->device); + struct mlx4_ib_fast_reg_page_list *mfrpl = to_mfrpl(page_list); + int size = page_list->max_page_list_len * sizeof (u64); + + dma_free_coherent(&dev->dev->pdev->dev, size, page_list->page_list, + mfrpl->map); + kfree(mfrpl); +} + struct ib_fmr *mlx4_ib_fmr_alloc(struct ib_pd *pd, int acc, struct ib_fmr_attr *fmr_attr) { diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index 89eb6cbe592e..f7bc7dd8578a 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2007, 2008 Mellanox Technologies. 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 @@ -78,6 +79,9 @@ static const __be32 mlx4_ib_opcode[] = { [IB_WR_RDMA_READ] = __constant_cpu_to_be32(MLX4_OPCODE_RDMA_READ), [IB_WR_ATOMIC_CMP_AND_SWP] = __constant_cpu_to_be32(MLX4_OPCODE_ATOMIC_CS), [IB_WR_ATOMIC_FETCH_AND_ADD] = __constant_cpu_to_be32(MLX4_OPCODE_ATOMIC_FA), + [IB_WR_SEND_WITH_INV] = __constant_cpu_to_be32(MLX4_OPCODE_SEND_INVAL), + [IB_WR_LOCAL_INV] = __constant_cpu_to_be32(MLX4_OPCODE_LOCAL_INVAL), + [IB_WR_FAST_REG_MR] = __constant_cpu_to_be32(MLX4_OPCODE_FMR), }; static struct mlx4_ib_sqp *to_msqp(struct mlx4_ib_qp *mqp) @@ -976,6 +980,10 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp, context->pd = cpu_to_be32(to_mpd(ibqp->pd)->pdn); context->params1 = cpu_to_be32(MLX4_IB_ACK_REQ_FREQ << 28); + /* Set "fast registration enabled" for all kernel QPs */ + if (!qp->ibqp.uobject) + context->params1 |= cpu_to_be32(1 << 11); + if (attr_mask & IB_QP_RNR_RETRY) { context->params1 |= cpu_to_be32(attr->rnr_retry << 13); optpar |= MLX4_QP_OPTPAR_RNR_RETRY; @@ -1322,6 +1330,38 @@ static int mlx4_wq_overflow(struct mlx4_ib_wq *wq, int nreq, struct ib_cq *ib_cq return cur + nreq >= wq->max_post; } +static __be32 convert_access(int acc) +{ + return (acc & IB_ACCESS_REMOTE_ATOMIC ? cpu_to_be32(MLX4_WQE_FMR_PERM_ATOMIC) : 0) | + (acc & IB_ACCESS_REMOTE_WRITE ? cpu_to_be32(MLX4_WQE_FMR_PERM_REMOTE_WRITE) : 0) | + (acc & IB_ACCESS_REMOTE_READ ? cpu_to_be32(MLX4_WQE_FMR_PERM_REMOTE_READ) : 0) | + (acc & IB_ACCESS_LOCAL_WRITE ? cpu_to_be32(MLX4_WQE_FMR_PERM_LOCAL_WRITE) : 0) | + cpu_to_be32(MLX4_WQE_FMR_PERM_LOCAL_READ); +} + +static void set_fmr_seg(struct mlx4_wqe_fmr_seg *fseg, struct ib_send_wr *wr) +{ + struct mlx4_ib_fast_reg_page_list *mfrpl = to_mfrpl(wr->wr.fast_reg.page_list); + + fseg->flags = convert_access(wr->wr.fast_reg.access_flags); + fseg->mem_key = cpu_to_be32(wr->wr.fast_reg.rkey); + fseg->buf_list = cpu_to_be64(mfrpl->map); + fseg->start_addr = cpu_to_be64(wr->wr.fast_reg.iova_start); + fseg->reg_len = cpu_to_be64(wr->wr.fast_reg.length); + fseg->offset = 0; /* XXX -- is this just for ZBVA? */ + fseg->page_size = cpu_to_be32(wr->wr.fast_reg.page_shift); + fseg->reserved[0] = 0; + fseg->reserved[1] = 0; +} + +static void set_local_inv_seg(struct mlx4_wqe_local_inval_seg *iseg, u32 rkey) +{ + iseg->flags = 0; + iseg->mem_key = cpu_to_be32(rkey); + iseg->guest_id = 0; + iseg->pa = 0; +} + static __always_inline void set_raddr_seg(struct mlx4_wqe_raddr_seg *rseg, u64 remote_addr, u32 rkey) { @@ -1395,7 +1435,7 @@ static void __set_data_seg(struct mlx4_wqe_data_seg *dseg, struct ib_sge *sg) dseg->addr = cpu_to_be64(sg->addr); } -static int build_lso_seg(struct mlx4_lso_seg *wqe, struct ib_send_wr *wr, +static int build_lso_seg(struct mlx4_wqe_lso_seg *wqe, struct ib_send_wr *wr, struct mlx4_ib_qp *qp, unsigned *lso_seg_len) { unsigned halign = ALIGN(sizeof *wqe + wr->wr.ud.hlen, 16); @@ -1423,6 +1463,21 @@ static int build_lso_seg(struct mlx4_lso_seg *wqe, struct ib_send_wr *wr, return 0; } +static __be32 send_ieth(struct ib_send_wr *wr) +{ + switch (wr->opcode) { + case IB_WR_SEND_WITH_IMM: + case IB_WR_RDMA_WRITE_WITH_IMM: + return wr->ex.imm_data; + + case IB_WR_SEND_WITH_INV: + return cpu_to_be32(wr->ex.invalidate_rkey); + + default: + return 0; + } +} + int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, struct ib_send_wr **bad_wr) { @@ -1469,11 +1524,7 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, MLX4_WQE_CTRL_TCP_UDP_CSUM) : 0) | qp->sq_signal_bits; - if (wr->opcode == IB_WR_SEND_WITH_IMM || - wr->opcode == IB_WR_RDMA_WRITE_WITH_IMM) - ctrl->imm = wr->ex.imm_data; - else - ctrl->imm = 0; + ctrl->imm = send_ieth(wr); wqe += sizeof *ctrl; size = sizeof *ctrl / 16; @@ -1505,6 +1556,18 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, size += sizeof (struct mlx4_wqe_raddr_seg) / 16; break; + case IB_WR_LOCAL_INV: + set_local_inv_seg(wqe, wr->ex.invalidate_rkey); + wqe += sizeof (struct mlx4_wqe_local_inval_seg); + size += sizeof (struct mlx4_wqe_local_inval_seg) / 16; + break; + + case IB_WR_FAST_REG_MR: + set_fmr_seg(wqe, wr); + wqe += sizeof (struct mlx4_wqe_fmr_seg); + size += sizeof (struct mlx4_wqe_fmr_seg) / 16; + break; + default: /* No extra segments required for sends */ break; diff --git a/drivers/infiniband/hw/mlx4/srq.c b/drivers/infiniband/hw/mlx4/srq.c index 12d6bc6f8007..d42565258fb7 100644 --- a/drivers/infiniband/hw/mlx4/srq.c +++ b/drivers/infiniband/hw/mlx4/srq.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2007, 2008 Mellanox Technologies. 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/mlx4/user.h b/drivers/infiniband/hw/mlx4/user.h index e2d11be4525c..13beedeeef9f 100644 --- a/drivers/infiniband/hw/mlx4/user.h +++ b/drivers/infiniband/hw/mlx4/user.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2007, 2008 Mellanox Technologies. 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/mthca/mthca_dev.h b/drivers/infiniband/hw/mthca/mthca_dev.h index ee4d073c889f..252590116df5 100644 --- a/drivers/infiniband/hw/mthca/mthca_dev.h +++ b/drivers/infiniband/hw/mthca/mthca_dev.h @@ -202,6 +202,7 @@ struct mthca_pd_table { struct mthca_buddy { unsigned long **bits; + int *num_free; int max_order; spinlock_t lock; }; diff --git a/drivers/infiniband/hw/mthca/mthca_eq.c b/drivers/infiniband/hw/mthca/mthca_eq.c index 4e36aa7cb3d2..cc6858f0b65b 100644 --- a/drivers/infiniband/hw/mthca/mthca_eq.c +++ b/drivers/infiniband/hw/mthca/mthca_eq.c @@ -780,7 +780,7 @@ int mthca_map_eq_icm(struct mthca_dev *dev, u64 icm_virt) return -ENOMEM; dev->eq_table.icm_dma = pci_map_page(dev->pdev, dev->eq_table.icm_page, 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(dev->eq_table.icm_dma)) { + if (pci_dma_mapping_error(dev->pdev, dev->eq_table.icm_dma)) { __free_page(dev->eq_table.icm_page); return -ENOMEM; } diff --git a/drivers/infiniband/hw/mthca/mthca_mr.c b/drivers/infiniband/hw/mthca/mthca_mr.c index 8489b1e81c0f..882e6b735915 100644 --- a/drivers/infiniband/hw/mthca/mthca_mr.c +++ b/drivers/infiniband/hw/mthca/mthca_mr.c @@ -89,23 +89,26 @@ static u32 mthca_buddy_alloc(struct mthca_buddy *buddy, int order) spin_lock(&buddy->lock); - for (o = order; o <= buddy->max_order; ++o) { - m = 1 << (buddy->max_order - o); - seg = find_first_bit(buddy->bits[o], m); - if (seg < m) - goto found; - } + for (o = order; o <= buddy->max_order; ++o) + if (buddy->num_free[o]) { + m = 1 << (buddy->max_order - o); + seg = find_first_bit(buddy->bits[o], m); + if (seg < m) + goto found; + } spin_unlock(&buddy->lock); return -1; found: clear_bit(seg, buddy->bits[o]); + --buddy->num_free[o]; while (o > order) { --o; seg <<= 1; set_bit(seg ^ 1, buddy->bits[o]); + ++buddy->num_free[o]; } spin_unlock(&buddy->lock); @@ -123,11 +126,13 @@ static void mthca_buddy_free(struct mthca_buddy *buddy, u32 seg, int order) while (test_bit(seg ^ 1, buddy->bits[order])) { clear_bit(seg ^ 1, buddy->bits[order]); + --buddy->num_free[order]; seg >>= 1; ++order; } set_bit(seg, buddy->bits[order]); + ++buddy->num_free[order]; spin_unlock(&buddy->lock); } @@ -141,7 +146,9 @@ static int mthca_buddy_init(struct mthca_buddy *buddy, int max_order) buddy->bits = kzalloc((buddy->max_order + 1) * sizeof (long *), GFP_KERNEL); - if (!buddy->bits) + buddy->num_free = kzalloc((buddy->max_order + 1) * sizeof (int *), + GFP_KERNEL); + if (!buddy->bits || !buddy->num_free) goto err_out; for (i = 0; i <= buddy->max_order; ++i) { @@ -154,6 +161,7 @@ static int mthca_buddy_init(struct mthca_buddy *buddy, int max_order) } set_bit(0, buddy->bits[buddy->max_order]); + buddy->num_free[buddy->max_order] = 1; return 0; @@ -161,9 +169,10 @@ err_out_free: for (i = 0; i <= buddy->max_order; ++i) kfree(buddy->bits[i]); +err_out: kfree(buddy->bits); + kfree(buddy->num_free); -err_out: return -ENOMEM; } @@ -175,6 +184,7 @@ static void mthca_buddy_cleanup(struct mthca_buddy *buddy) kfree(buddy->bits[i]); kfree(buddy->bits); + kfree(buddy->num_free); } static u32 mthca_alloc_mtt_range(struct mthca_dev *dev, int order, diff --git a/drivers/infiniband/hw/nes/nes.c b/drivers/infiniband/hw/nes/nes.c index d2884e778098..b0cab64e5e3d 100644 --- a/drivers/infiniband/hw/nes/nes.c +++ b/drivers/infiniband/hw/nes/nes.c @@ -276,6 +276,7 @@ static void nes_cqp_rem_ref_callback(struct nes_device *nesdev, struct nes_cqp_r } nes_free_resource(nesadapter, nesadapter->allocated_qps, nesqp->hwqp.qp_id); + nesadapter->qp_table[nesqp->hwqp.qp_id-NES_FIRST_QPN] = NULL; kfree(nesqp->allocated_buffer); } @@ -289,7 +290,6 @@ void nes_rem_ref(struct ib_qp *ibqp) struct nes_qp *nesqp; struct nes_vnic *nesvnic = to_nesvnic(ibqp->device); struct nes_device *nesdev = nesvnic->nesdev; - struct nes_adapter *nesadapter = nesdev->nesadapter; struct nes_hw_cqp_wqe *cqp_wqe; struct nes_cqp_request *cqp_request; u32 opcode; @@ -303,8 +303,6 @@ void nes_rem_ref(struct ib_qp *ibqp) } if (atomic_dec_and_test(&nesqp->refcount)) { - nesadapter->qp_table[nesqp->hwqp.qp_id-NES_FIRST_QPN] = NULL; - /* Destroy the QP */ cqp_request = nes_get_cqp_request(nesdev); if (cqp_request == NULL) { diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c index 6aa531d5276d..9f0b964b2c99 100644 --- a/drivers/infiniband/hw/nes/nes_cm.c +++ b/drivers/infiniband/hw/nes/nes_cm.c @@ -74,36 +74,59 @@ atomic_t cm_nodes_destroyed; atomic_t cm_accel_dropped_pkts; atomic_t cm_resets_recvd; -static inline int mini_cm_accelerated(struct nes_cm_core *, struct nes_cm_node *); +static inline int mini_cm_accelerated(struct nes_cm_core *, + struct nes_cm_node *); static struct nes_cm_listener *mini_cm_listen(struct nes_cm_core *, - struct nes_vnic *, struct nes_cm_info *); -static int add_ref_cm_node(struct nes_cm_node *); -static int rem_ref_cm_node(struct nes_cm_core *, struct nes_cm_node *); + struct nes_vnic *, struct nes_cm_info *); static int mini_cm_del_listen(struct nes_cm_core *, struct nes_cm_listener *); -static struct sk_buff *form_cm_frame(struct sk_buff *, struct nes_cm_node *, - void *, u32, void *, u32, u8); -static struct sk_buff *get_free_pkt(struct nes_cm_node *cm_node); - static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *, - struct nes_vnic *, - struct ietf_mpa_frame *, - struct nes_cm_info *); + struct nes_vnic *, u16, void *, struct nes_cm_info *); +static int mini_cm_close(struct nes_cm_core *, struct nes_cm_node *); static int mini_cm_accept(struct nes_cm_core *, struct ietf_mpa_frame *, - struct nes_cm_node *); + struct nes_cm_node *); static int mini_cm_reject(struct nes_cm_core *, struct ietf_mpa_frame *, - struct nes_cm_node *); -static int mini_cm_close(struct nes_cm_core *, struct nes_cm_node *); -static int mini_cm_recv_pkt(struct nes_cm_core *, struct nes_vnic *, - struct sk_buff *); + struct nes_cm_node *); +static void mini_cm_recv_pkt(struct nes_cm_core *, struct nes_vnic *, + struct sk_buff *); static int mini_cm_dealloc_core(struct nes_cm_core *); static int mini_cm_get(struct nes_cm_core *); static int mini_cm_set(struct nes_cm_core *, u32, u32); + +static struct sk_buff *form_cm_frame(struct sk_buff *, struct nes_cm_node *, + void *, u32, void *, u32, u8); +static struct sk_buff *get_free_pkt(struct nes_cm_node *cm_node); +static int add_ref_cm_node(struct nes_cm_node *); +static int rem_ref_cm_node(struct nes_cm_core *, struct nes_cm_node *); + static int nes_cm_disconn_true(struct nes_qp *); static int nes_cm_post_event(struct nes_cm_event *event); static int nes_disconnect(struct nes_qp *nesqp, int abrupt); static void nes_disconnect_worker(struct work_struct *work); -static int send_ack(struct nes_cm_node *cm_node); + +static int send_mpa_request(struct nes_cm_node *, struct sk_buff *); +static int send_syn(struct nes_cm_node *, u32, struct sk_buff *); +static int send_reset(struct nes_cm_node *, struct sk_buff *); +static int send_ack(struct nes_cm_node *cm_node, struct sk_buff *skb); static int send_fin(struct nes_cm_node *cm_node, struct sk_buff *skb); +static void process_packet(struct nes_cm_node *, struct sk_buff *, + struct nes_cm_core *); + +static void active_open_err(struct nes_cm_node *, struct sk_buff *, int); +static void passive_open_err(struct nes_cm_node *, struct sk_buff *, int); +static void cleanup_retrans_entry(struct nes_cm_node *); +static void handle_rcv_mpa(struct nes_cm_node *, struct sk_buff *, + enum nes_cm_event_type); +static void free_retrans_entry(struct nes_cm_node *cm_node); +static int handle_tcp_options(struct nes_cm_node *cm_node, struct tcphdr *tcph, + struct sk_buff *skb, int optionsize, int passive); + +/* CM event handler functions */ +static void cm_event_connected(struct nes_cm_event *); +static void cm_event_connect_error(struct nes_cm_event *); +static void cm_event_reset(struct nes_cm_event *); +static void cm_event_mpa_req(struct nes_cm_event *); + +static void print_core(struct nes_cm_core *core); /* External CM API Interface */ /* instance of function pointers for client API */ @@ -158,11 +181,11 @@ static struct nes_cm_event *create_event(struct nes_cm_node *cm_node, event->cm_info.loc_port = cm_node->loc_port; event->cm_info.cm_id = cm_node->cm_id; - nes_debug(NES_DBG_CM, "Created event=%p, type=%u, dst_addr=%08x[%x]," - " src_addr=%08x[%x]\n", - event, type, - event->cm_info.loc_addr, event->cm_info.loc_port, - event->cm_info.rem_addr, event->cm_info.rem_port); + nes_debug(NES_DBG_CM, "cm_node=%p Created event=%p, type=%u, " + "dst_addr=%08x[%x], src_addr=%08x[%x]\n", + cm_node, event, type, event->cm_info.loc_addr, + event->cm_info.loc_port, event->cm_info.rem_addr, + event->cm_info.rem_port); nes_cm_post_event(event); return event; @@ -172,14 +195,11 @@ static struct nes_cm_event *create_event(struct nes_cm_node *cm_node, /** * send_mpa_request */ -static int send_mpa_request(struct nes_cm_node *cm_node) +static int send_mpa_request(struct nes_cm_node *cm_node, struct sk_buff *skb) { - struct sk_buff *skb; int ret; - - skb = get_free_pkt(cm_node); if (!skb) { - nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n"); + nes_debug(NES_DBG_CM, "skb set to NULL\n"); return -1; } @@ -188,9 +208,8 @@ static int send_mpa_request(struct nes_cm_node *cm_node) cm_node->mpa_frame_size, SET_ACK); ret = schedule_nes_timer(cm_node, skb, NES_TIMER_TYPE_SEND, 1, 0); - if (ret < 0) { + if (ret < 0) return ret; - } return 0; } @@ -229,46 +248,12 @@ static int parse_mpa(struct nes_cm_node *cm_node, u8 *buffer, u32 len) /** - * handle_exception_pkt - process an exception packet. - * We have been in a TSA state, and we have now received SW - * TCP/IP traffic should be a FIN request or IP pkt with options - */ -static int handle_exception_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb) -{ - int ret = 0; - struct tcphdr *tcph = tcp_hdr(skb); - - /* first check to see if this a FIN pkt */ - if (tcph->fin) { - /* we need to ACK the FIN request */ - send_ack(cm_node); - - /* check which side we are (client/server) and set next state accordingly */ - if (cm_node->tcp_cntxt.client) - cm_node->state = NES_CM_STATE_CLOSING; - else { - /* we are the server side */ - cm_node->state = NES_CM_STATE_CLOSE_WAIT; - /* since this is a self contained CM we don't wait for */ - /* an APP to close us, just send final FIN immediately */ - ret = send_fin(cm_node, NULL); - cm_node->state = NES_CM_STATE_LAST_ACK; - } - } else { - ret = -EINVAL; - } - - return ret; -} - - -/** * form_cm_frame - get a free packet and build empty frame Use * node info to build. */ -static struct sk_buff *form_cm_frame(struct sk_buff *skb, struct nes_cm_node *cm_node, - void *options, u32 optionsize, void *data, - u32 datasize, u8 flags) +static struct sk_buff *form_cm_frame(struct sk_buff *skb, + struct nes_cm_node *cm_node, void *options, u32 optionsize, + void *data, u32 datasize, u8 flags) { struct tcphdr *tcph; struct iphdr *iph; @@ -332,10 +317,12 @@ static struct sk_buff *form_cm_frame(struct sk_buff *skb, struct nes_cm_node *cm cm_node->tcp_cntxt.loc_seq_num++; tcph->syn = 1; } else - cm_node->tcp_cntxt.loc_seq_num += datasize; /* data (no headers) */ + cm_node->tcp_cntxt.loc_seq_num += datasize; - if (flags & SET_FIN) + if (flags & SET_FIN) { + cm_node->tcp_cntxt.loc_seq_num++; tcph->fin = 1; + } if (flags & SET_RST) tcph->rst = 1; @@ -389,7 +376,7 @@ int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb, int close_when_complete) { unsigned long flags; - struct nes_cm_core *cm_core; + struct nes_cm_core *cm_core = cm_node->cm_core; struct nes_timer_entry *new_send; int ret = 0; u32 was_timer_set; @@ -411,7 +398,7 @@ int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb, new_send->close_when_complete = close_when_complete; if (type == NES_TIMER_TYPE_CLOSE) { - new_send->timetosend += (HZ/2); /* TODO: decide on the correct value here */ + new_send->timetosend += (HZ/10); spin_lock_irqsave(&cm_node->recv_list_lock, flags); list_add_tail(&new_send->list, &cm_node->recv_list); spin_unlock_irqrestore(&cm_node->recv_list_lock, flags); @@ -420,36 +407,28 @@ int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb, if (type == NES_TIMER_TYPE_SEND) { new_send->seq_num = ntohl(tcp_hdr(skb)->seq); atomic_inc(&new_send->skb->users); + spin_lock_irqsave(&cm_node->retrans_list_lock, flags); + cm_node->send_entry = new_send; + add_ref_cm_node(cm_node); + spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags); + new_send->timetosend = jiffies + NES_RETRY_TIMEOUT; ret = nes_nic_cm_xmit(new_send->skb, cm_node->netdev); if (ret != NETDEV_TX_OK) { - nes_debug(NES_DBG_CM, "Error sending packet %p (jiffies = %lu)\n", - new_send, jiffies); + nes_debug(NES_DBG_CM, "Error sending packet %p " + "(jiffies = %lu)\n", new_send, jiffies); atomic_dec(&new_send->skb->users); new_send->timetosend = jiffies; } else { cm_packets_sent++; if (!send_retrans) { + cleanup_retrans_entry(cm_node); if (close_when_complete) - rem_ref_cm_node(cm_node->cm_core, cm_node); - dev_kfree_skb_any(new_send->skb); - kfree(new_send); + rem_ref_cm_node(cm_core, cm_node); return ret; } - new_send->timetosend = jiffies + NES_RETRY_TIMEOUT; } - spin_lock_irqsave(&cm_node->retrans_list_lock, flags); - list_add_tail(&new_send->list, &cm_node->retrans_list); - spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags); - } - if (type == NES_TIMER_TYPE_RECV) { - new_send->seq_num = ntohl(tcp_hdr(skb)->seq); - new_send->timetosend = jiffies; - spin_lock_irqsave(&cm_node->recv_list_lock, flags); - list_add_tail(&new_send->list, &cm_node->recv_list); - spin_unlock_irqrestore(&cm_node->recv_list_lock, flags); } - cm_core = cm_node->cm_core; was_timer_set = timer_pending(&cm_core->tcp_timer); @@ -476,23 +455,27 @@ static void nes_cm_timer_tick(unsigned long pass) struct list_head *list_node, *list_node_temp; struct nes_cm_core *cm_core = g_cm_core; struct nes_qp *nesqp; - struct sk_buff *skb; u32 settimer = 0; int ret = NETDEV_TX_OK; - int node_done; + enum nes_cm_node_state last_state; spin_lock_irqsave(&cm_core->ht_lock, flags); - list_for_each_safe(list_node, list_core_temp, &cm_core->connected_nodes) { + list_for_each_safe(list_node, list_core_temp, + &cm_core->connected_nodes) { cm_node = container_of(list_node, struct nes_cm_node, list); add_ref_cm_node(cm_node); spin_unlock_irqrestore(&cm_core->ht_lock, flags); spin_lock_irqsave(&cm_node->recv_list_lock, flags); - list_for_each_safe(list_core, list_node_temp, &cm_node->recv_list) { - recv_entry = container_of(list_core, struct nes_timer_entry, list); - if ((time_after(recv_entry->timetosend, jiffies)) && - (recv_entry->type == NES_TIMER_TYPE_CLOSE)) { - if (nexttimeout > recv_entry->timetosend || !settimer) { + list_for_each_safe(list_core, list_node_temp, + &cm_node->recv_list) { + recv_entry = container_of(list_core, + struct nes_timer_entry, list); + if (!recv_entry) + break; + if (time_after(recv_entry->timetosend, jiffies)) { + if (nexttimeout > recv_entry->timetosend || + !settimer) { nexttimeout = recv_entry->timetosend; settimer = 1; } @@ -501,157 +484,143 @@ static void nes_cm_timer_tick(unsigned long pass) list_del(&recv_entry->list); cm_id = cm_node->cm_id; spin_unlock_irqrestore(&cm_node->recv_list_lock, flags); - if (recv_entry->type == NES_TIMER_TYPE_CLOSE) { - nesqp = (struct nes_qp *)recv_entry->skb; - spin_lock_irqsave(&nesqp->lock, qplockflags); - if (nesqp->cm_id) { - nes_debug(NES_DBG_CM, "QP%u: cm_id = %p, refcount = %d: " - "****** HIT A NES_TIMER_TYPE_CLOSE" - " with something to do!!! ******\n", - nesqp->hwqp.qp_id, cm_id, - atomic_read(&nesqp->refcount)); - nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED; - nesqp->last_aeq = NES_AEQE_AEID_RESET_SENT; - nesqp->ibqp_state = IB_QPS_ERR; - spin_unlock_irqrestore(&nesqp->lock, qplockflags); - nes_cm_disconn(nesqp); - } else { - spin_unlock_irqrestore(&nesqp->lock, qplockflags); - nes_debug(NES_DBG_CM, "QP%u: cm_id = %p, refcount = %d:" - " ****** HIT A NES_TIMER_TYPE_CLOSE" - " with nothing to do!!! ******\n", - nesqp->hwqp.qp_id, cm_id, - atomic_read(&nesqp->refcount)); - nes_rem_ref(&nesqp->ibqp); - } - if (cm_id) - cm_id->rem_ref(cm_id); + nesqp = (struct nes_qp *)recv_entry->skb; + spin_lock_irqsave(&nesqp->lock, qplockflags); + if (nesqp->cm_id) { + nes_debug(NES_DBG_CM, "QP%u: cm_id = %p, " + "refcount = %d: HIT A " + "NES_TIMER_TYPE_CLOSE with something " + "to do!!!\n", nesqp->hwqp.qp_id, cm_id, + atomic_read(&nesqp->refcount)); + nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED; + nesqp->last_aeq = NES_AEQE_AEID_RESET_SENT; + nesqp->ibqp_state = IB_QPS_ERR; + spin_unlock_irqrestore(&nesqp->lock, + qplockflags); + nes_cm_disconn(nesqp); + } else { + spin_unlock_irqrestore(&nesqp->lock, + qplockflags); + nes_debug(NES_DBG_CM, "QP%u: cm_id = %p, " + "refcount = %d: HIT A " + "NES_TIMER_TYPE_CLOSE with nothing " + "to do!!!\n", nesqp->hwqp.qp_id, cm_id, + atomic_read(&nesqp->refcount)); } + if (cm_id) + cm_id->rem_ref(cm_id); + kfree(recv_entry); spin_lock_irqsave(&cm_node->recv_list_lock, flags); } spin_unlock_irqrestore(&cm_node->recv_list_lock, flags); spin_lock_irqsave(&cm_node->retrans_list_lock, flags); - node_done = 0; - list_for_each_safe(list_core, list_node_temp, &cm_node->retrans_list) { - if (node_done) { - break; - } - send_entry = container_of(list_core, struct nes_timer_entry, list); + do { + send_entry = cm_node->send_entry; + if (!send_entry) + continue; if (time_after(send_entry->timetosend, jiffies)) { if (cm_node->state != NES_CM_STATE_TSA) { - if ((nexttimeout > send_entry->timetosend) || !settimer) { - nexttimeout = send_entry->timetosend; + if ((nexttimeout > + send_entry->timetosend) || + !settimer) { + nexttimeout = + send_entry->timetosend; settimer = 1; + continue; } - node_done = 1; - continue; } else { - list_del(&send_entry->list); - skb = send_entry->skb; - spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags); - dev_kfree_skb_any(skb); - kfree(send_entry); - spin_lock_irqsave(&cm_node->retrans_list_lock, flags); + free_retrans_entry(cm_node); continue; } } - if (send_entry->type == NES_TIMER_NODE_CLEANUP) { - list_del(&send_entry->list); - spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags); - kfree(send_entry); - spin_lock_irqsave(&cm_node->retrans_list_lock, flags); - continue; - } - if ((send_entry->seq_num < cm_node->tcp_cntxt.rem_ack_num) || - (cm_node->state == NES_CM_STATE_TSA) || - (cm_node->state == NES_CM_STATE_CLOSED)) { - skb = send_entry->skb; - list_del(&send_entry->list); - spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags); - kfree(send_entry); - dev_kfree_skb_any(skb); - spin_lock_irqsave(&cm_node->retrans_list_lock, flags); + + if ((cm_node->state == NES_CM_STATE_TSA) || + (cm_node->state == NES_CM_STATE_CLOSED)) { + free_retrans_entry(cm_node); continue; } - if (!send_entry->retranscount || !send_entry->retrycount) { + if (!send_entry->retranscount || + !send_entry->retrycount) { cm_packets_dropped++; - skb = send_entry->skb; - list_del(&send_entry->list); - spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags); - dev_kfree_skb_any(skb); - kfree(send_entry); - if (cm_node->state == NES_CM_STATE_SYN_RCVD) { - /* this node never even generated an indication up to the cm */ + last_state = cm_node->state; + cm_node->state = NES_CM_STATE_CLOSED; + free_retrans_entry(cm_node); + spin_unlock_irqrestore( + &cm_node->retrans_list_lock, flags); + if (last_state == NES_CM_STATE_SYN_RCVD) rem_ref_cm_node(cm_core, cm_node); - } else { - cm_node->state = NES_CM_STATE_CLOSED; - create_event(cm_node, NES_CM_EVENT_ABORTED); - } - spin_lock_irqsave(&cm_node->retrans_list_lock, flags); + else + create_event(cm_node, + NES_CM_EVENT_ABORTED); + spin_lock_irqsave(&cm_node->retrans_list_lock, + flags); continue; } - /* this seems like the correct place, but leave send entry unprotected */ - /* spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags); */ atomic_inc(&send_entry->skb->users); cm_packets_retrans++; - nes_debug(NES_DBG_CM, "Retransmitting send_entry %p for node %p," - " jiffies = %lu, time to send = %lu, retranscount = %u, " - "send_entry->seq_num = 0x%08X, cm_node->tcp_cntxt.rem_ack_num = 0x%08X\n", - send_entry, cm_node, jiffies, send_entry->timetosend, send_entry->retranscount, - send_entry->seq_num, cm_node->tcp_cntxt.rem_ack_num); - - spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags); + nes_debug(NES_DBG_CM, "Retransmitting send_entry %p " + "for node %p, jiffies = %lu, time to send = " + "%lu, retranscount = %u, send_entry->seq_num = " + "0x%08X, cm_node->tcp_cntxt.rem_ack_num = " + "0x%08X\n", send_entry, cm_node, jiffies, + send_entry->timetosend, + send_entry->retranscount, + send_entry->seq_num, + cm_node->tcp_cntxt.rem_ack_num); + + spin_unlock_irqrestore(&cm_node->retrans_list_lock, + flags); ret = nes_nic_cm_xmit(send_entry->skb, cm_node->netdev); + spin_lock_irqsave(&cm_node->retrans_list_lock, flags); if (ret != NETDEV_TX_OK) { + nes_debug(NES_DBG_CM, "rexmit failed for " + "node=%p\n", cm_node); cm_packets_bounced++; atomic_dec(&send_entry->skb->users); send_entry->retrycount--; nexttimeout = jiffies + NES_SHORT_TIME; settimer = 1; - node_done = 1; - spin_lock_irqsave(&cm_node->retrans_list_lock, flags); continue; } else { cm_packets_sent++; } - spin_lock_irqsave(&cm_node->retrans_list_lock, flags); - list_del(&send_entry->list); - nes_debug(NES_DBG_CM, "Packet Sent: retrans count = %u, retry count = %u.\n", - send_entry->retranscount, send_entry->retrycount); + nes_debug(NES_DBG_CM, "Packet Sent: retrans count = " + "%u, retry count = %u.\n", + send_entry->retranscount, + send_entry->retrycount); if (send_entry->send_retrans) { send_entry->retranscount--; - send_entry->timetosend = jiffies + NES_RETRY_TIMEOUT; - if (nexttimeout > send_entry->timetosend || !settimer) { + send_entry->timetosend = jiffies + + NES_RETRY_TIMEOUT; + if (nexttimeout > send_entry->timetosend || + !settimer) { nexttimeout = send_entry->timetosend; settimer = 1; } - list_add(&send_entry->list, &cm_node->retrans_list); - continue; } else { int close_when_complete; - skb = send_entry->skb; - close_when_complete = send_entry->close_when_complete; - spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags); - if (close_when_complete) { - BUG_ON(atomic_read(&cm_node->ref_count) == 1); - rem_ref_cm_node(cm_core, cm_node); - } - dev_kfree_skb_any(skb); - kfree(send_entry); - spin_lock_irqsave(&cm_node->retrans_list_lock, flags); - continue; + close_when_complete = + send_entry->close_when_complete; + nes_debug(NES_DBG_CM, "cm_node=%p state=%d\n", + cm_node, cm_node->state); + free_retrans_entry(cm_node); + if (close_when_complete) + rem_ref_cm_node(cm_node->cm_core, + cm_node); } - } - spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags); - - rem_ref_cm_node(cm_core, cm_node); + } while (0); + spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags); + rem_ref_cm_node(cm_node->cm_core, cm_node); spin_lock_irqsave(&cm_core->ht_lock, flags); - if (ret != NETDEV_TX_OK) + if (ret != NETDEV_TX_OK) { + nes_debug(NES_DBG_CM, "rexmit failed for cm_node=%p\n", + cm_node); break; + } } spin_unlock_irqrestore(&cm_core->ht_lock, flags); @@ -667,14 +636,14 @@ static void nes_cm_timer_tick(unsigned long pass) /** * send_syn */ -static int send_syn(struct nes_cm_node *cm_node, u32 sendack) +static int send_syn(struct nes_cm_node *cm_node, u32 sendack, + struct sk_buff *skb) { int ret; int flags = SET_SYN; - struct sk_buff *skb; char optionsbuffer[sizeof(struct option_mss) + - sizeof(struct option_windowscale) + - sizeof(struct option_base) + 1]; + sizeof(struct option_windowscale) + sizeof(struct option_base) + + TCP_OPTIONS_PADDING]; int optionssize = 0; /* Sending MSS option */ @@ -695,8 +664,7 @@ static int send_syn(struct nes_cm_node *cm_node, u32 sendack) options->as_windowscale.shiftcount = cm_node->tcp_cntxt.rcv_wscale; optionssize += sizeof(struct option_windowscale); - if (sendack && !(NES_DRV_OPT_SUPRESS_OPTION_BC & nes_drv_opt) - ) { + if (sendack && !(NES_DRV_OPT_SUPRESS_OPTION_BC & nes_drv_opt)) { options = (union all_known_options *)&optionsbuffer[optionssize]; options->as_base.optionnum = OPTION_NUMBER_WRITE0; options->as_base.length = sizeof(struct option_base); @@ -714,7 +682,8 @@ static int send_syn(struct nes_cm_node *cm_node, u32 sendack) options->as_end = OPTION_NUMBER_END; optionssize += 1; - skb = get_free_pkt(cm_node); + if (!skb) + skb = get_free_pkt(cm_node); if (!skb) { nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n"); return -1; @@ -733,18 +702,18 @@ static int send_syn(struct nes_cm_node *cm_node, u32 sendack) /** * send_reset */ -static int send_reset(struct nes_cm_node *cm_node) +static int send_reset(struct nes_cm_node *cm_node, struct sk_buff *skb) { int ret; - struct sk_buff *skb = get_free_pkt(cm_node); int flags = SET_RST | SET_ACK; + if (!skb) + skb = get_free_pkt(cm_node); if (!skb) { nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n"); return -1; } - add_ref_cm_node(cm_node); form_cm_frame(skb, cm_node, NULL, 0, NULL, 0, flags); ret = schedule_nes_timer(cm_node, skb, NES_TIMER_TYPE_SEND, 0, 1); @@ -755,10 +724,12 @@ static int send_reset(struct nes_cm_node *cm_node) /** * send_ack */ -static int send_ack(struct nes_cm_node *cm_node) +static int send_ack(struct nes_cm_node *cm_node, struct sk_buff *skb) { int ret; - struct sk_buff *skb = get_free_pkt(cm_node); + + if (!skb) + skb = get_free_pkt(cm_node); if (!skb) { nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n"); @@ -922,7 +893,8 @@ static int add_hte_node(struct nes_cm_core *cm_core, struct nes_cm_node *cm_node if (!cm_node || !cm_core) return -EINVAL; - nes_debug(NES_DBG_CM, "Adding Node to Active Connection HT\n"); + nes_debug(NES_DBG_CM, "Adding Node %p to Active Connection HT\n", + cm_node); /* first, make an index into our hash table */ hashkey = make_hashkey(cm_node->loc_port, cm_node->loc_addr, @@ -946,10 +918,35 @@ static int add_hte_node(struct nes_cm_core *cm_core, struct nes_cm_node *cm_node * mini_cm_dec_refcnt_listen */ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core, - struct nes_cm_listener *listener, int free_hanging_nodes) + struct nes_cm_listener *listener, int free_hanging_nodes) { int ret = 1; unsigned long flags; + struct list_head *list_pos = NULL; + struct list_head *list_temp = NULL; + struct nes_cm_node *cm_node = NULL; + + nes_debug(NES_DBG_CM, "attempting listener= %p free_nodes= %d, " + "refcnt=%d\n", listener, free_hanging_nodes, + atomic_read(&listener->ref_count)); + /* free non-accelerated child nodes for this listener */ + if (free_hanging_nodes) { + spin_lock_irqsave(&cm_core->ht_lock, flags); + list_for_each_safe(list_pos, list_temp, + &g_cm_core->connected_nodes) { + cm_node = container_of(list_pos, struct nes_cm_node, + list); + if ((cm_node->listener == listener) && + (!cm_node->accelerated)) { + cleanup_retrans_entry(cm_node); + spin_unlock_irqrestore(&cm_core->ht_lock, + flags); + send_reset(cm_node, NULL); + spin_lock_irqsave(&cm_core->ht_lock, flags); + } + } + spin_unlock_irqrestore(&cm_core->ht_lock, flags); + } spin_lock_irqsave(&cm_core->listen_list_lock, flags); if (!atomic_dec_return(&listener->ref_count)) { list_del(&listener->list); @@ -1067,18 +1064,18 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core, cm_node->loc_port = cm_info->loc_port; cm_node->rem_port = cm_info->rem_port; cm_node->send_write0 = send_first; - nes_debug(NES_DBG_CM, "Make node addresses : loc = " NIPQUAD_FMT ":%x, rem = " NIPQUAD_FMT ":%x\n", - HIPQUAD(cm_node->loc_addr), cm_node->loc_port, - HIPQUAD(cm_node->rem_addr), cm_node->rem_port); + nes_debug(NES_DBG_CM, "Make node addresses : loc = " NIPQUAD_FMT + ":%x, rem = " NIPQUAD_FMT ":%x\n", + HIPQUAD(cm_node->loc_addr), cm_node->loc_port, + HIPQUAD(cm_node->rem_addr), cm_node->rem_port); cm_node->listener = listener; cm_node->netdev = nesvnic->netdev; cm_node->cm_id = cm_info->cm_id; memcpy(cm_node->loc_mac, nesvnic->netdev->dev_addr, ETH_ALEN); - nes_debug(NES_DBG_CM, "listener=%p, cm_id=%p\n", - cm_node->listener, cm_node->cm_id); + nes_debug(NES_DBG_CM, "listener=%p, cm_id=%p\n", cm_node->listener, + cm_node->cm_id); - INIT_LIST_HEAD(&cm_node->retrans_list); spin_lock_init(&cm_node->retrans_list_lock); INIT_LIST_HEAD(&cm_node->recv_list); spin_lock_init(&cm_node->recv_list_lock); @@ -1142,10 +1139,9 @@ static int add_ref_cm_node(struct nes_cm_node *cm_node) * rem_ref_cm_node - destroy an instance of a cm node */ static int rem_ref_cm_node(struct nes_cm_core *cm_core, - struct nes_cm_node *cm_node) + struct nes_cm_node *cm_node) { unsigned long flags, qplockflags; - struct nes_timer_entry *send_entry; struct nes_timer_entry *recv_entry; struct iw_cm_id *cm_id; struct list_head *list_core, *list_node_temp; @@ -1169,48 +1165,33 @@ static int rem_ref_cm_node(struct nes_cm_core *cm_core, atomic_dec(&cm_node->listener->pend_accepts_cnt); BUG_ON(atomic_read(&cm_node->listener->pend_accepts_cnt) < 0); } - - spin_lock_irqsave(&cm_node->retrans_list_lock, flags); - list_for_each_safe(list_core, list_node_temp, &cm_node->retrans_list) { - send_entry = container_of(list_core, struct nes_timer_entry, list); - list_del(&send_entry->list); - spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags); - dev_kfree_skb_any(send_entry->skb); - kfree(send_entry); - spin_lock_irqsave(&cm_node->retrans_list_lock, flags); - continue; - } - spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags); - + BUG_ON(cm_node->send_entry); spin_lock_irqsave(&cm_node->recv_list_lock, flags); list_for_each_safe(list_core, list_node_temp, &cm_node->recv_list) { - recv_entry = container_of(list_core, struct nes_timer_entry, list); + recv_entry = container_of(list_core, struct nes_timer_entry, + list); list_del(&recv_entry->list); cm_id = cm_node->cm_id; spin_unlock_irqrestore(&cm_node->recv_list_lock, flags); - if (recv_entry->type == NES_TIMER_TYPE_CLOSE) { - nesqp = (struct nes_qp *)recv_entry->skb; - spin_lock_irqsave(&nesqp->lock, qplockflags); - if (nesqp->cm_id) { - nes_debug(NES_DBG_CM, "QP%u: cm_id = %p: ****** HIT A NES_TIMER_TYPE_CLOSE" - " with something to do!!! ******\n", - nesqp->hwqp.qp_id, cm_id); - nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED; - nesqp->last_aeq = NES_AEQE_AEID_RESET_SENT; - nesqp->ibqp_state = IB_QPS_ERR; - spin_unlock_irqrestore(&nesqp->lock, qplockflags); - nes_cm_disconn(nesqp); - } else { - spin_unlock_irqrestore(&nesqp->lock, qplockflags); - nes_debug(NES_DBG_CM, "QP%u: cm_id = %p: ****** HIT A NES_TIMER_TYPE_CLOSE" - " with nothing to do!!! ******\n", - nesqp->hwqp.qp_id, cm_id); - nes_rem_ref(&nesqp->ibqp); - } - cm_id->rem_ref(cm_id); - } else if (recv_entry->type == NES_TIMER_TYPE_RECV) { - dev_kfree_skb_any(recv_entry->skb); + nesqp = (struct nes_qp *)recv_entry->skb; + spin_lock_irqsave(&nesqp->lock, qplockflags); + if (nesqp->cm_id) { + nes_debug(NES_DBG_CM, "QP%u: cm_id = %p: HIT A " + "NES_TIMER_TYPE_CLOSE with something to do!\n", + nesqp->hwqp.qp_id, cm_id); + nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED; + nesqp->last_aeq = NES_AEQE_AEID_RESET_SENT; + nesqp->ibqp_state = IB_QPS_ERR; + spin_unlock_irqrestore(&nesqp->lock, qplockflags); + nes_cm_disconn(nesqp); + } else { + spin_unlock_irqrestore(&nesqp->lock, qplockflags); + nes_debug(NES_DBG_CM, "QP%u: cm_id = %p: HIT A " + "NES_TIMER_TYPE_CLOSE with nothing to do!\n", + nesqp->hwqp.qp_id, cm_id); } + cm_id->rem_ref(cm_id); + kfree(recv_entry); spin_lock_irqsave(&cm_node->recv_list_lock, flags); } @@ -1221,23 +1202,31 @@ static int rem_ref_cm_node(struct nes_cm_core *cm_core, } else { if (cm_node->apbvt_set && cm_node->nesvnic) { nes_manage_apbvt(cm_node->nesvnic, cm_node->loc_port, - PCI_FUNC(cm_node->nesvnic->nesdev->pcidev->devfn), - NES_MANAGE_APBVT_DEL); + PCI_FUNC( + cm_node->nesvnic->nesdev->pcidev->devfn), + NES_MANAGE_APBVT_DEL); } } - kfree(cm_node); atomic_dec(&cm_core->node_cnt); atomic_inc(&cm_nodes_destroyed); + nesqp = cm_node->nesqp; + if (nesqp) { + nesqp->cm_node = NULL; + nes_rem_ref(&nesqp->ibqp); + cm_node->nesqp = NULL; + } + cm_node->freed = 1; + kfree(cm_node); return 0; } - /** * process_options */ -static int process_options(struct nes_cm_node *cm_node, u8 *optionsloc, u32 optionsize, u32 syn_packet) +static int process_options(struct nes_cm_node *cm_node, u8 *optionsloc, + u32 optionsize, u32 syn_packet) { u32 tmp; u32 offset = 0; @@ -1247,35 +1236,37 @@ static int process_options(struct nes_cm_node *cm_node, u8 *optionsloc, u32 opti while (offset < optionsize) { all_options = (union all_known_options *)(optionsloc + offset); switch (all_options->as_base.optionnum) { - case OPTION_NUMBER_END: - offset = optionsize; - break; - case OPTION_NUMBER_NONE: - offset += 1; - continue; - case OPTION_NUMBER_MSS: - nes_debug(NES_DBG_CM, "%s: MSS Length: %d Offset: %d Size: %d\n", - __func__, - all_options->as_mss.length, offset, optionsize); - got_mss_option = 1; - if (all_options->as_mss.length != 4) { - return 1; - } else { - tmp = ntohs(all_options->as_mss.mss); - if (tmp > 0 && tmp < cm_node->tcp_cntxt.mss) - cm_node->tcp_cntxt.mss = tmp; - } - break; - case OPTION_NUMBER_WINDOW_SCALE: - cm_node->tcp_cntxt.snd_wscale = all_options->as_windowscale.shiftcount; - break; - case OPTION_NUMBER_WRITE0: - cm_node->send_write0 = 1; - break; - default: - nes_debug(NES_DBG_CM, "TCP Option not understood: %x\n", - all_options->as_base.optionnum); - break; + case OPTION_NUMBER_END: + offset = optionsize; + break; + case OPTION_NUMBER_NONE: + offset += 1; + continue; + case OPTION_NUMBER_MSS: + nes_debug(NES_DBG_CM, "%s: MSS Length: %d Offset: %d " + "Size: %d\n", __func__, + all_options->as_mss.length, offset, optionsize); + got_mss_option = 1; + if (all_options->as_mss.length != 4) { + return 1; + } else { + tmp = ntohs(all_options->as_mss.mss); + if (tmp > 0 && tmp < + cm_node->tcp_cntxt.mss) + cm_node->tcp_cntxt.mss = tmp; + } + break; + case OPTION_NUMBER_WINDOW_SCALE: + cm_node->tcp_cntxt.snd_wscale = + all_options->as_windowscale.shiftcount; + break; + case OPTION_NUMBER_WRITE0: + cm_node->send_write0 = 1; + break; + default: + nes_debug(NES_DBG_CM, "TCP Option not understood: %x\n", + all_options->as_base.optionnum); + break; } offset += all_options->as_base.length; } @@ -1284,300 +1275,491 @@ static int process_options(struct nes_cm_node *cm_node, u8 *optionsloc, u32 opti return 0; } +static void drop_packet(struct sk_buff *skb) +{ + atomic_inc(&cm_accel_dropped_pkts); + dev_kfree_skb_any(skb); +} -/** - * process_packet - */ -static int process_packet(struct nes_cm_node *cm_node, struct sk_buff *skb, - struct nes_cm_core *cm_core) +static void handle_fin_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb, + struct tcphdr *tcph) { - int optionsize; - int datasize; - int ret = 0; - struct tcphdr *tcph = tcp_hdr(skb); - u32 inc_sequence; - if (cm_node->state == NES_CM_STATE_SYN_SENT && tcph->syn) { - inc_sequence = ntohl(tcph->seq); - cm_node->tcp_cntxt.rcv_nxt = inc_sequence; + atomic_inc(&cm_resets_recvd); + nes_debug(NES_DBG_CM, "Received FIN, cm_node = %p, state = %u. " + "refcnt=%d\n", cm_node, cm_node->state, + atomic_read(&cm_node->ref_count)); + cm_node->tcp_cntxt.rcv_nxt++; + cleanup_retrans_entry(cm_node); + switch (cm_node->state) { + case NES_CM_STATE_SYN_RCVD: + case NES_CM_STATE_SYN_SENT: + case NES_CM_STATE_ESTABLISHED: + case NES_CM_STATE_MPAREQ_SENT: + cm_node->state = NES_CM_STATE_LAST_ACK; + send_fin(cm_node, skb); + break; + case NES_CM_STATE_FIN_WAIT1: + cm_node->state = NES_CM_STATE_CLOSING; + send_ack(cm_node, skb); + break; + case NES_CM_STATE_FIN_WAIT2: + cm_node->state = NES_CM_STATE_TIME_WAIT; + send_ack(cm_node, skb); + cm_node->state = NES_CM_STATE_CLOSED; + break; + case NES_CM_STATE_TSA: + default: + nes_debug(NES_DBG_CM, "Error Rcvd FIN for node-%p state = %d\n", + cm_node, cm_node->state); + drop_packet(skb); + break; } +} - if ((!tcph) || (cm_node->state == NES_CM_STATE_TSA)) { - BUG_ON(!tcph); - atomic_inc(&cm_accel_dropped_pkts); - return -1; - } - if (tcph->rst) { - atomic_inc(&cm_resets_recvd); - nes_debug(NES_DBG_CM, "Received Reset, cm_node = %p, state = %u. refcnt=%d\n", - cm_node, cm_node->state, atomic_read(&cm_node->ref_count)); - switch (cm_node->state) { - case NES_CM_STATE_LISTENING: - rem_ref_cm_node(cm_core, cm_node); - break; - case NES_CM_STATE_TSA: - case NES_CM_STATE_CLOSED: - break; - case NES_CM_STATE_SYN_RCVD: - nes_debug(NES_DBG_CM, "Received a reset for local 0x%08X:%04X," - " remote 0x%08X:%04X, node state = %u\n", - cm_node->loc_addr, cm_node->loc_port, - cm_node->rem_addr, cm_node->rem_port, - cm_node->state); - rem_ref_cm_node(cm_core, cm_node); - break; - case NES_CM_STATE_ONE_SIDE_ESTABLISHED: - case NES_CM_STATE_ESTABLISHED: - case NES_CM_STATE_MPAREQ_SENT: - default: - nes_debug(NES_DBG_CM, "Received a reset for local 0x%08X:%04X," - " remote 0x%08X:%04X, node state = %u refcnt=%d\n", - cm_node->loc_addr, cm_node->loc_port, - cm_node->rem_addr, cm_node->rem_port, - cm_node->state, atomic_read(&cm_node->ref_count)); - /* create event */ - cm_node->state = NES_CM_STATE_CLOSED; +static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb, + struct tcphdr *tcph) +{ - create_event(cm_node, NES_CM_EVENT_ABORTED); - break; + int reset = 0; /* whether to send reset in case of err.. */ + atomic_inc(&cm_resets_recvd); + nes_debug(NES_DBG_CM, "Received Reset, cm_node = %p, state = %u." + " refcnt=%d\n", cm_node, cm_node->state, + atomic_read(&cm_node->ref_count)); + cleanup_retrans_entry(cm_node); + switch (cm_node->state) { + case NES_CM_STATE_SYN_SENT: + case NES_CM_STATE_MPAREQ_SENT: + nes_debug(NES_DBG_CM, "%s[%u] create abort for cm_node=%p " + "listener=%p state=%d\n", __func__, __LINE__, cm_node, + cm_node->listener, cm_node->state); + active_open_err(cm_node, skb, reset); + break; + /* For PASSIVE open states, remove the cm_node event */ + case NES_CM_STATE_ESTABLISHED: + case NES_CM_STATE_SYN_RCVD: + case NES_CM_STATE_LISTENING: + nes_debug(NES_DBG_CM, "Bad state %s[%u]\n", __func__, __LINE__); + passive_open_err(cm_node, skb, reset); + break; + case NES_CM_STATE_TSA: + default: + break; + } +} +static void handle_rcv_mpa(struct nes_cm_node *cm_node, struct sk_buff *skb, + enum nes_cm_event_type type) +{ + + int ret; + int datasize = skb->len; + u8 *dataloc = skb->data; + ret = parse_mpa(cm_node, dataloc, datasize); + if (ret < 0) { + nes_debug(NES_DBG_CM, "didn't like MPA Request\n"); + if (type == NES_CM_EVENT_CONNECTED) { + nes_debug(NES_DBG_CM, "%s[%u] create abort for " + "cm_node=%p listener=%p state=%d\n", __func__, + __LINE__, cm_node, cm_node->listener, + cm_node->state); + active_open_err(cm_node, skb, 1); + } else { + passive_open_err(cm_node, skb, 1); } - return -1; + } else { + cleanup_retrans_entry(cm_node); + dev_kfree_skb_any(skb); + if (type == NES_CM_EVENT_CONNECTED) + cm_node->state = NES_CM_STATE_TSA; + create_event(cm_node, type); + + } + return ; +} + +static void indicate_pkt_err(struct nes_cm_node *cm_node, struct sk_buff *skb) +{ + switch (cm_node->state) { + case NES_CM_STATE_SYN_SENT: + case NES_CM_STATE_MPAREQ_SENT: + nes_debug(NES_DBG_CM, "%s[%u] create abort for cm_node=%p " + "listener=%p state=%d\n", __func__, __LINE__, cm_node, + cm_node->listener, cm_node->state); + active_open_err(cm_node, skb, 1); + break; + case NES_CM_STATE_ESTABLISHED: + case NES_CM_STATE_SYN_RCVD: + passive_open_err(cm_node, skb, 1); + break; + case NES_CM_STATE_TSA: + default: + drop_packet(skb); } +} + +static int check_syn(struct nes_cm_node *cm_node, struct tcphdr *tcph, + struct sk_buff *skb) +{ + int err; + + err = ((ntohl(tcph->ack_seq) == cm_node->tcp_cntxt.loc_seq_num))? 0 : 1; + if (err) + active_open_err(cm_node, skb, 1); + + return err; +} + +static int check_seq(struct nes_cm_node *cm_node, struct tcphdr *tcph, + struct sk_buff *skb) +{ + int err = 0; + u32 seq; + u32 ack_seq; + u32 loc_seq_num = cm_node->tcp_cntxt.loc_seq_num; + u32 rcv_nxt = cm_node->tcp_cntxt.rcv_nxt; + u32 rcv_wnd; + seq = ntohl(tcph->seq); + ack_seq = ntohl(tcph->ack_seq); + rcv_wnd = cm_node->tcp_cntxt.rcv_wnd; + if (ack_seq != loc_seq_num) + err = 1; + else if ((seq + rcv_wnd) < rcv_nxt) + err = 1; + if (err) { + nes_debug(NES_DBG_CM, "%s[%u] create abort for cm_node=%p " + "listener=%p state=%d\n", __func__, __LINE__, cm_node, + cm_node->listener, cm_node->state); + indicate_pkt_err(cm_node, skb); + nes_debug(NES_DBG_CM, "seq ERROR cm_node =%p seq=0x%08X " + "rcv_nxt=0x%08X rcv_wnd=0x%x\n", cm_node, seq, rcv_nxt, + rcv_wnd); + } + return err; +} + +/* + * handle_syn_pkt() is for Passive node. The syn packet is received when a node + * is created with a listener or it may comein as rexmitted packet which in + * that case will be just dropped. + */ + +static void handle_syn_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb, + struct tcphdr *tcph) +{ + int ret; + u32 inc_sequence; + int optionsize; optionsize = (tcph->doff << 2) - sizeof(struct tcphdr); + skb_pull(skb, tcph->doff << 2); + inc_sequence = ntohl(tcph->seq); - skb_pull(skb, ip_hdr(skb)->ihl << 2); + switch (cm_node->state) { + case NES_CM_STATE_SYN_SENT: + case NES_CM_STATE_MPAREQ_SENT: + /* Rcvd syn on active open connection*/ + active_open_err(cm_node, skb, 1); + break; + case NES_CM_STATE_LISTENING: + /* Passive OPEN */ + cm_node->accept_pend = 1; + atomic_inc(&cm_node->listener->pend_accepts_cnt); + if (atomic_read(&cm_node->listener->pend_accepts_cnt) > + cm_node->listener->backlog) { + nes_debug(NES_DBG_CM, "drop syn due to backlog " + "pressure \n"); + cm_backlog_drops++; + passive_open_err(cm_node, skb, 0); + break; + } + ret = handle_tcp_options(cm_node, tcph, skb, optionsize, + 1); + if (ret) { + passive_open_err(cm_node, skb, 0); + /* drop pkt */ + break; + } + cm_node->tcp_cntxt.rcv_nxt = inc_sequence + 1; + BUG_ON(cm_node->send_entry); + cm_node->state = NES_CM_STATE_SYN_RCVD; + send_syn(cm_node, 1, skb); + break; + case NES_CM_STATE_TSA: + case NES_CM_STATE_ESTABLISHED: + case NES_CM_STATE_FIN_WAIT1: + case NES_CM_STATE_FIN_WAIT2: + case NES_CM_STATE_MPAREQ_RCVD: + case NES_CM_STATE_LAST_ACK: + case NES_CM_STATE_CLOSING: + case NES_CM_STATE_UNKNOWN: + case NES_CM_STATE_CLOSED: + default: + drop_packet(skb); + break; + } +} + +static void handle_synack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb, + struct tcphdr *tcph) +{ + + int ret; + u32 inc_sequence; + int optionsize; + + optionsize = (tcph->doff << 2) - sizeof(struct tcphdr); skb_pull(skb, tcph->doff << 2); + inc_sequence = ntohl(tcph->seq); + switch (cm_node->state) { + case NES_CM_STATE_SYN_SENT: + /* active open */ + if (check_syn(cm_node, tcph, skb)) + return; + cm_node->tcp_cntxt.rem_ack_num = ntohl(tcph->ack_seq); + /* setup options */ + ret = handle_tcp_options(cm_node, tcph, skb, optionsize, 0); + if (ret) { + nes_debug(NES_DBG_CM, "cm_node=%p tcp_options failed\n", + cm_node); + break; + } + cleanup_retrans_entry(cm_node); + cm_node->tcp_cntxt.rcv_nxt = inc_sequence + 1; + send_mpa_request(cm_node, skb); + cm_node->state = NES_CM_STATE_MPAREQ_SENT; + break; + case NES_CM_STATE_MPAREQ_RCVD: + /* passive open, so should not be here */ + passive_open_err(cm_node, skb, 1); + break; + case NES_CM_STATE_ESTABLISHED: + case NES_CM_STATE_FIN_WAIT1: + case NES_CM_STATE_FIN_WAIT2: + case NES_CM_STATE_LAST_ACK: + case NES_CM_STATE_TSA: + case NES_CM_STATE_CLOSING: + case NES_CM_STATE_UNKNOWN: + case NES_CM_STATE_CLOSED: + case NES_CM_STATE_MPAREQ_SENT: + default: + drop_packet(skb); + break; + } +} - datasize = skb->len; +static void handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb, + struct tcphdr *tcph) +{ + int datasize = 0; + u32 inc_sequence; + u32 rem_seq_ack; + u32 rem_seq; + if (check_seq(cm_node, tcph, skb)) + return; + + skb_pull(skb, tcph->doff << 2); inc_sequence = ntohl(tcph->seq); - nes_debug(NES_DBG_CM, "datasize = %u, sequence = 0x%08X, ack_seq = 0x%08X," - " rcv_nxt = 0x%08X Flags: %s %s.\n", - datasize, inc_sequence, ntohl(tcph->ack_seq), - cm_node->tcp_cntxt.rcv_nxt, (tcph->syn ? "SYN":""), - (tcph->ack ? "ACK":"")); - - if (!tcph->syn && (inc_sequence != cm_node->tcp_cntxt.rcv_nxt) - ) { - nes_debug(NES_DBG_CM, "dropping packet, datasize = %u, sequence = 0x%08X," - " ack_seq = 0x%08X, rcv_nxt = 0x%08X Flags: %s.\n", - datasize, inc_sequence, ntohl(tcph->ack_seq), - cm_node->tcp_cntxt.rcv_nxt, (tcph->ack ? "ACK":"")); - if (cm_node->state == NES_CM_STATE_LISTENING) { - rem_ref_cm_node(cm_core, cm_node); + rem_seq = ntohl(tcph->seq); + rem_seq_ack = ntohl(tcph->ack_seq); + datasize = skb->len; + + switch (cm_node->state) { + case NES_CM_STATE_SYN_RCVD: + /* Passive OPEN */ + cm_node->tcp_cntxt.rem_ack_num = ntohl(tcph->ack_seq); + cm_node->state = NES_CM_STATE_ESTABLISHED; + if (datasize) { + cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize; + cm_node->state = NES_CM_STATE_MPAREQ_RCVD; + handle_rcv_mpa(cm_node, skb, NES_CM_EVENT_MPA_REQ); + } else { /* rcvd ACK only */ + dev_kfree_skb_any(skb); + cleanup_retrans_entry(cm_node); + } + break; + case NES_CM_STATE_ESTABLISHED: + /* Passive OPEN */ + /* We expect mpa frame to be received only */ + if (datasize) { + cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize; + cm_node->state = NES_CM_STATE_MPAREQ_RCVD; + handle_rcv_mpa(cm_node, skb, + NES_CM_EVENT_MPA_REQ); + } else + drop_packet(skb); + break; + case NES_CM_STATE_MPAREQ_SENT: + cm_node->tcp_cntxt.rem_ack_num = ntohl(tcph->ack_seq); + if (datasize) { + cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize; + handle_rcv_mpa(cm_node, skb, NES_CM_EVENT_CONNECTED); + } else { /* Could be just an ack pkt.. */ + cleanup_retrans_entry(cm_node); + dev_kfree_skb_any(skb); } - return -1; + break; + case NES_CM_STATE_FIN_WAIT1: + case NES_CM_STATE_SYN_SENT: + case NES_CM_STATE_FIN_WAIT2: + case NES_CM_STATE_TSA: + case NES_CM_STATE_CLOSED: + case NES_CM_STATE_MPAREQ_RCVD: + case NES_CM_STATE_LAST_ACK: + case NES_CM_STATE_CLOSING: + case NES_CM_STATE_UNKNOWN: + default: + drop_packet(skb); + break; } +} - cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize; +static int handle_tcp_options(struct nes_cm_node *cm_node, struct tcphdr *tcph, + struct sk_buff *skb, int optionsize, int passive) +{ + u8 *optionsloc = (u8 *)&tcph[1]; if (optionsize) { - u8 *optionsloc = (u8 *)&tcph[1]; - if (process_options(cm_node, optionsloc, optionsize, (u32)tcph->syn)) { - nes_debug(NES_DBG_CM, "%s: Node %p, Sending RESET\n", __func__, cm_node); - send_reset(cm_node); - if (cm_node->state != NES_CM_STATE_SYN_SENT) - rem_ref_cm_node(cm_core, cm_node); - return 0; + if (process_options(cm_node, optionsloc, optionsize, + (u32)tcph->syn)) { + nes_debug(NES_DBG_CM, "%s: Node %p, Sending RESET\n", + __func__, cm_node); + if (passive) + passive_open_err(cm_node, skb, 0); + else + active_open_err(cm_node, skb, 0); + return 1; } - } else if (tcph->syn) - cm_node->tcp_cntxt.mss = NES_CM_DEFAULT_MSS; + } cm_node->tcp_cntxt.snd_wnd = ntohs(tcph->window) << cm_node->tcp_cntxt.snd_wscale; - if (cm_node->tcp_cntxt.snd_wnd > cm_node->tcp_cntxt.max_snd_wnd) { + if (cm_node->tcp_cntxt.snd_wnd > cm_node->tcp_cntxt.max_snd_wnd) cm_node->tcp_cntxt.max_snd_wnd = cm_node->tcp_cntxt.snd_wnd; - } + return 0; +} - if (tcph->ack) { - cm_node->tcp_cntxt.rem_ack_num = ntohl(tcph->ack_seq); - switch (cm_node->state) { - case NES_CM_STATE_SYN_RCVD: - case NES_CM_STATE_SYN_SENT: - /* read and stash current sequence number */ - if (cm_node->tcp_cntxt.rem_ack_num != cm_node->tcp_cntxt.loc_seq_num) { - nes_debug(NES_DBG_CM, "ERROR - cm_node->tcp_cntxt.rem_ack_num !=" - " cm_node->tcp_cntxt.loc_seq_num\n"); - send_reset(cm_node); - return 0; - } - if (cm_node->state == NES_CM_STATE_SYN_SENT) - cm_node->state = NES_CM_STATE_ONE_SIDE_ESTABLISHED; - else { - cm_node->state = NES_CM_STATE_ESTABLISHED; - } - break; - case NES_CM_STATE_LAST_ACK: - cm_node->state = NES_CM_STATE_CLOSED; - break; - case NES_CM_STATE_FIN_WAIT1: - cm_node->state = NES_CM_STATE_FIN_WAIT2; - break; - case NES_CM_STATE_CLOSING: - cm_node->state = NES_CM_STATE_TIME_WAIT; - /* need to schedule this to happen in 2MSL timeouts */ - cm_node->state = NES_CM_STATE_CLOSED; - break; - case NES_CM_STATE_ONE_SIDE_ESTABLISHED: - case NES_CM_STATE_ESTABLISHED: - case NES_CM_STATE_MPAREQ_SENT: - case NES_CM_STATE_CLOSE_WAIT: - case NES_CM_STATE_TIME_WAIT: - case NES_CM_STATE_CLOSED: - break; - case NES_CM_STATE_LISTENING: - nes_debug(NES_DBG_CM, "Received an ACK on a listening port (SYN %d)\n", tcph->syn); - cm_node->tcp_cntxt.loc_seq_num = ntohl(tcph->ack_seq); - send_reset(cm_node); - /* send_reset bumps refcount, this should have been a new node */ - rem_ref_cm_node(cm_core, cm_node); - return -1; - break; - case NES_CM_STATE_TSA: - nes_debug(NES_DBG_CM, "Received a packet with the ack bit set while in TSA state\n"); - break; - case NES_CM_STATE_UNKNOWN: - case NES_CM_STATE_INITED: - case NES_CM_STATE_ACCEPTING: - case NES_CM_STATE_FIN_WAIT2: - default: - nes_debug(NES_DBG_CM, "Received ack from unknown state: %x\n", - cm_node->state); - send_reset(cm_node); - break; - } - } +/* + * active_open_err() will send reset() if flag set.. + * It will also send ABORT event. + */ - if (tcph->syn) { - if (cm_node->state == NES_CM_STATE_LISTENING) { - /* do not exceed backlog */ - atomic_inc(&cm_node->listener->pend_accepts_cnt); - if (atomic_read(&cm_node->listener->pend_accepts_cnt) > - cm_node->listener->backlog) { - nes_debug(NES_DBG_CM, "drop syn due to backlog pressure \n"); - cm_backlog_drops++; - atomic_dec(&cm_node->listener->pend_accepts_cnt); - rem_ref_cm_node(cm_core, cm_node); - return 0; - } - cm_node->accept_pend = 1; +static void active_open_err(struct nes_cm_node *cm_node, struct sk_buff *skb, + int reset) +{ + cleanup_retrans_entry(cm_node); + if (reset) { + nes_debug(NES_DBG_CM, "ERROR active err called for cm_node=%p, " + "state=%d\n", cm_node, cm_node->state); + add_ref_cm_node(cm_node); + send_reset(cm_node, skb); + } else + dev_kfree_skb_any(skb); - } - if (datasize == 0) - cm_node->tcp_cntxt.rcv_nxt ++; + cm_node->state = NES_CM_STATE_CLOSED; + create_event(cm_node, NES_CM_EVENT_ABORTED); +} - if (cm_node->state == NES_CM_STATE_LISTENING) { - cm_node->state = NES_CM_STATE_SYN_RCVD; - send_syn(cm_node, 1); - } - if (cm_node->state == NES_CM_STATE_ONE_SIDE_ESTABLISHED) { - cm_node->state = NES_CM_STATE_ESTABLISHED; - /* send final handshake ACK */ - ret = send_ack(cm_node); - if (ret < 0) - return ret; +/* + * passive_open_err() will either do a reset() or will free up the skb and + * remove the cm_node. + */ - cm_node->state = NES_CM_STATE_MPAREQ_SENT; - ret = send_mpa_request(cm_node); - if (ret < 0) - return ret; - } +static void passive_open_err(struct nes_cm_node *cm_node, struct sk_buff *skb, + int reset) +{ + cleanup_retrans_entry(cm_node); + cm_node->state = NES_CM_STATE_CLOSED; + if (reset) { + nes_debug(NES_DBG_CM, "passive_open_err sending RST for " + "cm_node=%p state =%d\n", cm_node, cm_node->state); + send_reset(cm_node, skb); + } else { + dev_kfree_skb_any(skb); + rem_ref_cm_node(cm_node->cm_core, cm_node); } +} - if (tcph->fin) { - cm_node->tcp_cntxt.rcv_nxt++; - switch (cm_node->state) { - case NES_CM_STATE_SYN_RCVD: - case NES_CM_STATE_SYN_SENT: - case NES_CM_STATE_ONE_SIDE_ESTABLISHED: - case NES_CM_STATE_ESTABLISHED: - case NES_CM_STATE_ACCEPTING: - case NES_CM_STATE_MPAREQ_SENT: - cm_node->state = NES_CM_STATE_CLOSE_WAIT; - cm_node->state = NES_CM_STATE_LAST_ACK; - ret = send_fin(cm_node, NULL); - break; - case NES_CM_STATE_FIN_WAIT1: - cm_node->state = NES_CM_STATE_CLOSING; - ret = send_ack(cm_node); - break; - case NES_CM_STATE_FIN_WAIT2: - cm_node->state = NES_CM_STATE_TIME_WAIT; - cm_node->tcp_cntxt.loc_seq_num ++; - ret = send_ack(cm_node); - /* need to schedule this to happen in 2MSL timeouts */ - cm_node->state = NES_CM_STATE_CLOSED; - break; - case NES_CM_STATE_CLOSE_WAIT: - case NES_CM_STATE_LAST_ACK: - case NES_CM_STATE_CLOSING: - case NES_CM_STATE_TSA: - default: - nes_debug(NES_DBG_CM, "Received a fin while in %x state\n", - cm_node->state); - ret = -EINVAL; - break; - } +/* + * free_retrans_entry() routines assumes that the retrans_list_lock has + * been acquired before calling. + */ +static void free_retrans_entry(struct nes_cm_node *cm_node) +{ + struct nes_timer_entry *send_entry; + send_entry = cm_node->send_entry; + if (send_entry) { + cm_node->send_entry = NULL; + dev_kfree_skb_any(send_entry->skb); + kfree(send_entry); + rem_ref_cm_node(cm_node->cm_core, cm_node); } +} - if (datasize) { - u8 *dataloc = skb->data; - /* figure out what state we are in and handle transition to next state */ - switch (cm_node->state) { - case NES_CM_STATE_LISTENING: - case NES_CM_STATE_SYN_RCVD: - case NES_CM_STATE_SYN_SENT: - case NES_CM_STATE_FIN_WAIT1: - case NES_CM_STATE_FIN_WAIT2: - case NES_CM_STATE_CLOSE_WAIT: - case NES_CM_STATE_LAST_ACK: - case NES_CM_STATE_CLOSING: - break; - case NES_CM_STATE_MPAREQ_SENT: - /* recv the mpa res frame, ret=frame len (incl priv data) */ - ret = parse_mpa(cm_node, dataloc, datasize); - if (ret < 0) - break; - /* set the req frame payload len in skb */ - /* we are done handling this state, set node to a TSA state */ - cm_node->state = NES_CM_STATE_TSA; - send_ack(cm_node); - create_event(cm_node, NES_CM_EVENT_CONNECTED); - break; - - case NES_CM_STATE_ESTABLISHED: - /* we are expecting an MPA req frame */ - ret = parse_mpa(cm_node, dataloc, datasize); - if (ret < 0) { - break; - } - cm_node->state = NES_CM_STATE_TSA; - send_ack(cm_node); - /* we got a valid MPA request, create an event */ - create_event(cm_node, NES_CM_EVENT_MPA_REQ); - break; - case NES_CM_STATE_TSA: - handle_exception_pkt(cm_node, skb); - break; - case NES_CM_STATE_UNKNOWN: - case NES_CM_STATE_INITED: - default: - ret = -1; - } - } +static void cleanup_retrans_entry(struct nes_cm_node *cm_node) +{ + unsigned long flags; - return ret; + spin_lock_irqsave(&cm_node->retrans_list_lock, flags); + free_retrans_entry(cm_node); + spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags); } +/** + * process_packet + * Returns skb if to be freed, else it will return NULL if already used.. + */ +static void process_packet(struct nes_cm_node *cm_node, struct sk_buff *skb, + struct nes_cm_core *cm_core) +{ + enum nes_tcpip_pkt_type pkt_type = NES_PKT_TYPE_UNKNOWN; + struct tcphdr *tcph = tcp_hdr(skb); + skb_pull(skb, ip_hdr(skb)->ihl << 2); + + nes_debug(NES_DBG_CM, "process_packet: cm_node=%p state =%d syn=%d " + "ack=%d rst=%d fin=%d\n", cm_node, cm_node->state, tcph->syn, + tcph->ack, tcph->rst, tcph->fin); + + if (tcph->rst) + pkt_type = NES_PKT_TYPE_RST; + else if (tcph->syn) { + pkt_type = NES_PKT_TYPE_SYN; + if (tcph->ack) + pkt_type = NES_PKT_TYPE_SYNACK; + } else if (tcph->fin) + pkt_type = NES_PKT_TYPE_FIN; + else if (tcph->ack) + pkt_type = NES_PKT_TYPE_ACK; + + switch (pkt_type) { + case NES_PKT_TYPE_SYN: + handle_syn_pkt(cm_node, skb, tcph); + break; + case NES_PKT_TYPE_SYNACK: + handle_synack_pkt(cm_node, skb, tcph); + break; + case NES_PKT_TYPE_ACK: + handle_ack_pkt(cm_node, skb, tcph); + break; + case NES_PKT_TYPE_RST: + handle_rst_pkt(cm_node, skb, tcph); + break; + case NES_PKT_TYPE_FIN: + handle_fin_pkt(cm_node, skb, tcph); + break; + default: + drop_packet(skb); + break; + } +} /** * mini_cm_listen - create a listen node with params */ static struct nes_cm_listener *mini_cm_listen(struct nes_cm_core *cm_core, - struct nes_vnic *nesvnic, struct nes_cm_info *cm_info) + struct nes_vnic *nesvnic, struct nes_cm_info *cm_info) { struct nes_cm_listener *listener; unsigned long flags; @@ -1644,37 +1826,36 @@ static struct nes_cm_listener *mini_cm_listen(struct nes_cm_core *cm_core, /** * mini_cm_connect - make a connection node with params */ -static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core, - struct nes_vnic *nesvnic, - struct ietf_mpa_frame *mpa_frame, - struct nes_cm_info *cm_info) +struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core, + struct nes_vnic *nesvnic, u16 private_data_len, + void *private_data, struct nes_cm_info *cm_info) { int ret = 0; struct nes_cm_node *cm_node; struct nes_cm_listener *loopbackremotelistener; struct nes_cm_node *loopbackremotenode; struct nes_cm_info loopback_cm_info; - - u16 mpa_frame_size = sizeof(struct ietf_mpa_frame) + - ntohs(mpa_frame->priv_data_len); - - cm_info->loc_addr = htonl(cm_info->loc_addr); - cm_info->rem_addr = htonl(cm_info->rem_addr); - cm_info->loc_port = htons(cm_info->loc_port); - cm_info->rem_port = htons(cm_info->rem_port); + u16 mpa_frame_size = sizeof(struct ietf_mpa_frame) + private_data_len; + struct ietf_mpa_frame *mpa_frame = NULL; /* create a CM connection node */ cm_node = make_cm_node(cm_core, nesvnic, cm_info, NULL); if (!cm_node) return NULL; + mpa_frame = &cm_node->mpa_frame; + strcpy(mpa_frame->key, IEFT_MPA_KEY_REQ); + mpa_frame->flags = IETF_MPA_FLAGS_CRC; + mpa_frame->rev = IETF_MPA_VERSION; + mpa_frame->priv_data_len = htons(private_data_len); /* set our node side to client (active) side */ cm_node->tcp_cntxt.client = 1; cm_node->tcp_cntxt.rcv_wscale = NES_CM_DEFAULT_RCV_WND_SCALE; if (cm_info->loc_addr == cm_info->rem_addr) { - loopbackremotelistener = find_listener(cm_core, cm_node->rem_addr, - cm_node->rem_port, NES_CM_LISTENER_ACTIVE_STATE); + loopbackremotelistener = find_listener(cm_core, + ntohl(nesvnic->local_ipaddr), cm_node->rem_port, + NES_CM_LISTENER_ACTIVE_STATE); if (loopbackremotelistener == NULL) { create_event(cm_node, NES_CM_EVENT_ABORTED); } else { @@ -1683,26 +1864,35 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core, loopback_cm_info.loc_port = cm_info->rem_port; loopback_cm_info.rem_port = cm_info->loc_port; loopback_cm_info.cm_id = loopbackremotelistener->cm_id; - loopbackremotenode = make_cm_node(cm_core, nesvnic, &loopback_cm_info, - loopbackremotelistener); + loopbackremotenode = make_cm_node(cm_core, nesvnic, + &loopback_cm_info, loopbackremotelistener); loopbackremotenode->loopbackpartner = cm_node; - loopbackremotenode->tcp_cntxt.rcv_wscale = NES_CM_DEFAULT_RCV_WND_SCALE; + loopbackremotenode->tcp_cntxt.rcv_wscale = + NES_CM_DEFAULT_RCV_WND_SCALE; cm_node->loopbackpartner = loopbackremotenode; - memcpy(loopbackremotenode->mpa_frame_buf, &mpa_frame->priv_data, - mpa_frame_size); - loopbackremotenode->mpa_frame_size = mpa_frame_size - - sizeof(struct ietf_mpa_frame); + memcpy(loopbackremotenode->mpa_frame_buf, private_data, + private_data_len); + loopbackremotenode->mpa_frame_size = private_data_len; - /* we are done handling this state, set node to a TSA state */ + /* we are done handling this state. */ + /* set node to a TSA state */ cm_node->state = NES_CM_STATE_TSA; - cm_node->tcp_cntxt.rcv_nxt = loopbackremotenode->tcp_cntxt.loc_seq_num; - loopbackremotenode->tcp_cntxt.rcv_nxt = cm_node->tcp_cntxt.loc_seq_num; - cm_node->tcp_cntxt.max_snd_wnd = loopbackremotenode->tcp_cntxt.rcv_wnd; - loopbackremotenode->tcp_cntxt.max_snd_wnd = cm_node->tcp_cntxt.rcv_wnd; - cm_node->tcp_cntxt.snd_wnd = loopbackremotenode->tcp_cntxt.rcv_wnd; - loopbackremotenode->tcp_cntxt.snd_wnd = cm_node->tcp_cntxt.rcv_wnd; - cm_node->tcp_cntxt.snd_wscale = loopbackremotenode->tcp_cntxt.rcv_wscale; - loopbackremotenode->tcp_cntxt.snd_wscale = cm_node->tcp_cntxt.rcv_wscale; + cm_node->tcp_cntxt.rcv_nxt = + loopbackremotenode->tcp_cntxt.loc_seq_num; + loopbackremotenode->tcp_cntxt.rcv_nxt = + cm_node->tcp_cntxt.loc_seq_num; + cm_node->tcp_cntxt.max_snd_wnd = + loopbackremotenode->tcp_cntxt.rcv_wnd; + loopbackremotenode->tcp_cntxt.max_snd_wnd = + cm_node->tcp_cntxt.rcv_wnd; + cm_node->tcp_cntxt.snd_wnd = + loopbackremotenode->tcp_cntxt.rcv_wnd; + loopbackremotenode->tcp_cntxt.snd_wnd = + cm_node->tcp_cntxt.rcv_wnd; + cm_node->tcp_cntxt.snd_wscale = + loopbackremotenode->tcp_cntxt.rcv_wscale; + loopbackremotenode->tcp_cntxt.snd_wscale = + cm_node->tcp_cntxt.rcv_wscale; create_event(loopbackremotenode, NES_CM_EVENT_MPA_REQ); } @@ -1712,16 +1902,29 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core, /* set our node side to client (active) side */ cm_node->tcp_cntxt.client = 1; /* init our MPA frame ptr */ - memcpy(&cm_node->mpa_frame, mpa_frame, mpa_frame_size); + memcpy(mpa_frame->priv_data, private_data, private_data_len); + cm_node->mpa_frame_size = mpa_frame_size; /* send a syn and goto syn sent state */ cm_node->state = NES_CM_STATE_SYN_SENT; - ret = send_syn(cm_node, 0); + ret = send_syn(cm_node, 0, NULL); + + if (ret) { + /* error in sending the syn free up the cm_node struct */ + nes_debug(NES_DBG_CM, "Api - connect() FAILED: dest " + "addr=0x%08X, port=0x%04x, cm_node=%p, cm_id = %p.\n", + cm_node->rem_addr, cm_node->rem_port, cm_node, + cm_node->cm_id); + rem_ref_cm_node(cm_node->cm_core, cm_node); + cm_node = NULL; + } - nes_debug(NES_DBG_CM, "Api - connect(): dest addr=0x%08X, port=0x%04x," - " cm_node=%p, cm_id = %p.\n", - cm_node->rem_addr, cm_node->rem_port, cm_node, cm_node->cm_id); + if (cm_node) + nes_debug(NES_DBG_CM, "Api - connect(): dest addr=0x%08X," + "port=0x%04x, cm_node=%p, cm_id = %p.\n", + cm_node->rem_addr, cm_node->rem_port, cm_node, + cm_node->cm_id); return cm_node; } @@ -1731,8 +1934,8 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core, * mini_cm_accept - accept a connection * This function is never called */ -static int mini_cm_accept(struct nes_cm_core *cm_core, struct ietf_mpa_frame *mpa_frame, - struct nes_cm_node *cm_node) +static int mini_cm_accept(struct nes_cm_core *cm_core, + struct ietf_mpa_frame *mpa_frame, struct nes_cm_node *cm_node) { return 0; } @@ -1742,32 +1945,26 @@ static int mini_cm_accept(struct nes_cm_core *cm_core, struct ietf_mpa_frame *mp * mini_cm_reject - reject and teardown a connection */ static int mini_cm_reject(struct nes_cm_core *cm_core, - struct ietf_mpa_frame *mpa_frame, - struct nes_cm_node *cm_node) + struct ietf_mpa_frame *mpa_frame, struct nes_cm_node *cm_node) { int ret = 0; - struct sk_buff *skb; - u16 mpa_frame_size = sizeof(struct ietf_mpa_frame) + - ntohs(mpa_frame->priv_data_len); - skb = get_free_pkt(cm_node); - if (!skb) { - nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n"); - return -1; - } - - /* send an MPA Request frame */ - form_cm_frame(skb, cm_node, NULL, 0, mpa_frame, mpa_frame_size, SET_ACK | SET_FIN); - ret = schedule_nes_timer(cm_node, skb, NES_TIMER_TYPE_SEND, 1, 0); + nes_debug(NES_DBG_CM, "%s cm_node=%p type=%d state=%d\n", + __func__, cm_node, cm_node->tcp_cntxt.client, cm_node->state); + if (cm_node->tcp_cntxt.client) + return ret; + cleanup_retrans_entry(cm_node); cm_node->state = NES_CM_STATE_CLOSED; ret = send_fin(cm_node, NULL); - if (ret < 0) { - printk(KERN_INFO PFX "failed to send MPA Reply (reject)\n"); - return ret; + if (cm_node->accept_pend) { + BUG_ON(!cm_node->listener); + atomic_dec(&cm_node->listener->pend_accepts_cnt); + BUG_ON(atomic_read(&cm_node->listener->pend_accepts_cnt) < 0); } + ret = send_reset(cm_node, NULL); return ret; } @@ -1783,35 +1980,39 @@ static int mini_cm_close(struct nes_cm_core *cm_core, struct nes_cm_node *cm_nod return -EINVAL; switch (cm_node->state) { - /* if passed in node is null, create a reference key node for node search */ - /* check if we found an owner node for this pkt */ - case NES_CM_STATE_SYN_RCVD: - case NES_CM_STATE_SYN_SENT: - case NES_CM_STATE_ONE_SIDE_ESTABLISHED: - case NES_CM_STATE_ESTABLISHED: - case NES_CM_STATE_ACCEPTING: - case NES_CM_STATE_MPAREQ_SENT: - cm_node->state = NES_CM_STATE_FIN_WAIT1; - send_fin(cm_node, NULL); - break; - case NES_CM_STATE_CLOSE_WAIT: - cm_node->state = NES_CM_STATE_LAST_ACK; - send_fin(cm_node, NULL); - break; - case NES_CM_STATE_FIN_WAIT1: - case NES_CM_STATE_FIN_WAIT2: - case NES_CM_STATE_LAST_ACK: - case NES_CM_STATE_TIME_WAIT: - case NES_CM_STATE_CLOSING: - ret = -1; - break; - case NES_CM_STATE_LISTENING: - case NES_CM_STATE_UNKNOWN: - case NES_CM_STATE_INITED: - case NES_CM_STATE_CLOSED: - case NES_CM_STATE_TSA: - ret = rem_ref_cm_node(cm_core, cm_node); - break; + case NES_CM_STATE_SYN_RCVD: + case NES_CM_STATE_SYN_SENT: + case NES_CM_STATE_ONE_SIDE_ESTABLISHED: + case NES_CM_STATE_ESTABLISHED: + case NES_CM_STATE_ACCEPTING: + case NES_CM_STATE_MPAREQ_SENT: + case NES_CM_STATE_MPAREQ_RCVD: + cleanup_retrans_entry(cm_node); + send_reset(cm_node, NULL); + break; + case NES_CM_STATE_CLOSE_WAIT: + cm_node->state = NES_CM_STATE_LAST_ACK; + send_fin(cm_node, NULL); + break; + case NES_CM_STATE_FIN_WAIT1: + case NES_CM_STATE_FIN_WAIT2: + case NES_CM_STATE_LAST_ACK: + case NES_CM_STATE_TIME_WAIT: + case NES_CM_STATE_CLOSING: + ret = -1; + break; + case NES_CM_STATE_LISTENING: + case NES_CM_STATE_UNKNOWN: + case NES_CM_STATE_INITED: + case NES_CM_STATE_CLOSED: + ret = rem_ref_cm_node(cm_core, cm_node); + break; + case NES_CM_STATE_TSA: + if (cm_node->send_entry) + printk(KERN_ERR "ERROR Close got called from STATE_TSA " + "send_entry=%p\n", cm_node->send_entry); + ret = rem_ref_cm_node(cm_core, cm_node); + break; } cm_node->cm_id = NULL; return ret; @@ -1822,25 +2023,30 @@ static int mini_cm_close(struct nes_cm_core *cm_core, struct nes_cm_node *cm_nod * recv_pkt - recv an ETHERNET packet, and process it through CM * node state machine */ -static int mini_cm_recv_pkt(struct nes_cm_core *cm_core, struct nes_vnic *nesvnic, - struct sk_buff *skb) +static void mini_cm_recv_pkt(struct nes_cm_core *cm_core, + struct nes_vnic *nesvnic, struct sk_buff *skb) { struct nes_cm_node *cm_node = NULL; struct nes_cm_listener *listener = NULL; struct iphdr *iph; struct tcphdr *tcph; struct nes_cm_info nfo; - int ret = 0; - if (!skb || skb->len < sizeof(struct iphdr) + sizeof(struct tcphdr)) { - ret = -EINVAL; - goto out; + if (!skb) + return; + if (skb->len < sizeof(struct iphdr) + sizeof(struct tcphdr)) { + dev_kfree_skb_any(skb); + return; } iph = (struct iphdr *)skb->data; tcph = (struct tcphdr *)(skb->data + sizeof(struct iphdr)); skb_reset_network_header(skb); skb_set_transport_header(skb, sizeof(*tcph)); + if (!tcph) { + dev_kfree_skb_any(skb); + return; + } skb->len = ntohs(iph->tot_len); nfo.loc_addr = ntohl(iph->daddr); @@ -1853,61 +2059,60 @@ static int mini_cm_recv_pkt(struct nes_cm_core *cm_core, struct nes_vnic *nesvni NIPQUAD(iph->daddr), tcph->dest, NIPQUAD(iph->saddr), tcph->source); - /* note: this call is going to increment cm_node ref count */ - cm_node = find_node(cm_core, + do { + cm_node = find_node(cm_core, nfo.rem_port, nfo.rem_addr, nfo.loc_port, nfo.loc_addr); - if (!cm_node) { - listener = find_listener(cm_core, nfo.loc_addr, nfo.loc_port, - NES_CM_LISTENER_ACTIVE_STATE); - if (listener) { - nfo.cm_id = listener->cm_id; - nfo.conn_type = listener->conn_type; - } else { - nfo.cm_id = NULL; - nfo.conn_type = 0; - } - - cm_node = make_cm_node(cm_core, nesvnic, &nfo, listener); if (!cm_node) { - nes_debug(NES_DBG_CM, "Unable to allocate node\n"); + /* Only type of packet accepted are for */ + /* the PASSIVE open (syn only) */ + if ((!tcph->syn) || (tcph->ack)) { + cm_packets_dropped++; + break; + } + listener = find_listener(cm_core, nfo.loc_addr, + nfo.loc_port, + NES_CM_LISTENER_ACTIVE_STATE); if (listener) { - nes_debug(NES_DBG_CM, "unable to allocate node and decrementing listener refcount\n"); + nfo.cm_id = listener->cm_id; + nfo.conn_type = listener->conn_type; + } else { + nes_debug(NES_DBG_CM, "Unable to find listener " + "for the pkt\n"); + cm_packets_dropped++; + dev_kfree_skb_any(skb); + break; + } + + cm_node = make_cm_node(cm_core, nesvnic, &nfo, + listener); + if (!cm_node) { + nes_debug(NES_DBG_CM, "Unable to allocate " + "node\n"); + cm_packets_dropped++; atomic_dec(&listener->ref_count); + dev_kfree_skb_any(skb); + break; } - ret = -1; - goto out; - } - if (!listener) { - nes_debug(NES_DBG_CM, "Packet found for unknown port %x refcnt=%d\n", - nfo.loc_port, atomic_read(&cm_node->ref_count)); - if (!tcph->rst) { - nes_debug(NES_DBG_CM, "Packet found for unknown port=%d" - " rem_port=%d refcnt=%d\n", - nfo.loc_port, nfo.rem_port, atomic_read(&cm_node->ref_count)); - - cm_node->tcp_cntxt.rcv_nxt = ntohl(tcph->seq); - cm_node->tcp_cntxt.loc_seq_num = ntohl(tcph->ack_seq); - send_reset(cm_node); + if (!tcph->rst && !tcph->fin) { + cm_node->state = NES_CM_STATE_LISTENING; + } else { + cm_packets_dropped++; + rem_ref_cm_node(cm_core, cm_node); + dev_kfree_skb_any(skb); + break; } + add_ref_cm_node(cm_node); + } else if (cm_node->state == NES_CM_STATE_TSA) { rem_ref_cm_node(cm_core, cm_node); - ret = -1; - goto out; + atomic_inc(&cm_accel_dropped_pkts); + dev_kfree_skb_any(skb); + break; } - add_ref_cm_node(cm_node); - cm_node->state = NES_CM_STATE_LISTENING; - } - - nes_debug(NES_DBG_CM, "Processing Packet for node %p, data = (%p):\n", - cm_node, skb->data); - process_packet(cm_node, skb, cm_core); - - rem_ref_cm_node(cm_core, cm_node); - out: - if (skb) - dev_kfree_skb_any(skb); - return ret; + process_packet(cm_node, skb, cm_core); + rem_ref_cm_node(cm_core, cm_node); + } while (0); } @@ -2107,15 +2312,12 @@ int nes_cm_disconn(struct nes_qp *nesqp) if (nesqp->disconn_pending == 0) { nesqp->disconn_pending++; spin_unlock_irqrestore(&nesqp->lock, flags); - /* nes_add_ref(&nesqp->ibqp); */ /* init our disconnect work element, to */ INIT_WORK(&nesqp->disconn_work, nes_disconnect_worker); queue_work(g_cm_core->disconn_wq, &nesqp->disconn_work); - } else { + } else spin_unlock_irqrestore(&nesqp->lock, flags); - nes_rem_ref(&nesqp->ibqp); - } return 0; } @@ -2161,7 +2363,6 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp) nes_debug(NES_DBG_CM, "QP%u disconnect_worker cmid is NULL\n", nesqp->hwqp.qp_id); spin_unlock_irqrestore(&nesqp->lock, flags); - nes_rem_ref(&nesqp->ibqp); return -1; } @@ -2182,30 +2383,31 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp) atomic_inc(&cm_disconnects); cm_event.event = IW_CM_EVENT_DISCONNECT; if (last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET) { - issued_disconnect_reset = 1; cm_event.status = IW_CM_EVENT_STATUS_RESET; - nes_debug(NES_DBG_CM, "Generating a CM Disconnect Event (status reset) for " - " QP%u, cm_id = %p. \n", - nesqp->hwqp.qp_id, cm_id); - } else { + nes_debug(NES_DBG_CM, "Generating a CM " + "Disconnect Event (status reset) for " + "QP%u, cm_id = %p. \n", + nesqp->hwqp.qp_id, cm_id); + } else cm_event.status = IW_CM_EVENT_STATUS_OK; - } cm_event.local_addr = cm_id->local_addr; cm_event.remote_addr = cm_id->remote_addr; cm_event.private_data = NULL; cm_event.private_data_len = 0; - nes_debug(NES_DBG_CM, "Generating a CM Disconnect Event for " - " QP%u, SQ Head = %u, SQ Tail = %u. cm_id = %p, refcount = %u.\n", - nesqp->hwqp.qp_id, - nesqp->hwqp.sq_head, nesqp->hwqp.sq_tail, cm_id, - atomic_read(&nesqp->refcount)); + nes_debug(NES_DBG_CM, "Generating a CM Disconnect Event" + " for QP%u, SQ Head = %u, SQ Tail = %u. " + "cm_id = %p, refcount = %u.\n", + nesqp->hwqp.qp_id, nesqp->hwqp.sq_head, + nesqp->hwqp.sq_tail, cm_id, + atomic_read(&nesqp->refcount)); spin_unlock_irqrestore(&nesqp->lock, flags); ret = cm_id->event_handler(cm_id, &cm_event); if (ret) - nes_debug(NES_DBG_CM, "OFA CM event_handler returned, ret=%d\n", ret); + nes_debug(NES_DBG_CM, "OFA CM event_handler " + "returned, ret=%d\n", ret); spin_lock_irqsave(&nesqp->lock, flags); } @@ -2247,31 +2449,24 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp) if (nesqp->flush_issued == 0) { nesqp->flush_issued = 1; spin_unlock_irqrestore(&nesqp->lock, flags); - flush_wqes(nesvnic->nesdev, nesqp, NES_CQP_FLUSH_RQ, 1); - } else { + flush_wqes(nesvnic->nesdev, nesqp, + NES_CQP_FLUSH_RQ, 1); + } else spin_unlock_irqrestore(&nesqp->lock, flags); - } - - /* This reference is from either ModifyQP or the AE processing, - there is still a race here with modifyqp */ - nes_rem_ref(&nesqp->ibqp); - } else { cm_id = nesqp->cm_id; spin_unlock_irqrestore(&nesqp->lock, flags); /* check to see if the inbound reset beat the outbound reset */ if ((!cm_id) && (last_ae==NES_AEQE_AEID_RESET_SENT)) { - nes_debug(NES_DBG_CM, "QP%u: Decing refcount due to inbound reset" - " beating the outbound reset.\n", - nesqp->hwqp.qp_id); - nes_rem_ref(&nesqp->ibqp); + nes_debug(NES_DBG_CM, "QP%u: Decing refcount " + "due to inbound reset beating the " + "outbound reset.\n", nesqp->hwqp.qp_id); } } } else { nesqp->disconn_pending = 0; spin_unlock_irqrestore(&nesqp->lock, flags); } - nes_rem_ref(&nesqp->ibqp); return 0; } @@ -2349,71 +2544,82 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) nesdev = nesvnic->nesdev; adapter = nesdev->nesadapter; - nes_debug(NES_DBG_CM, "nesvnic=%p, netdev=%p, %s\n", - nesvnic, nesvnic->netdev, nesvnic->netdev->name); - - /* since this is from a listen, we were able to put node handle into cm_id */ cm_node = (struct nes_cm_node *)cm_id->provider_data; + nes_debug(NES_DBG_CM, "nes_accept: cm_node= %p nesvnic=%p, netdev=%p," + "%s\n", cm_node, nesvnic, nesvnic->netdev, + nesvnic->netdev->name); /* associate the node with the QP */ nesqp->cm_node = (void *)cm_node; + cm_node->nesqp = nesqp; + nes_add_ref(&nesqp->ibqp); - nes_debug(NES_DBG_CM, "QP%u, cm_node=%p, jiffies = %lu\n", - nesqp->hwqp.qp_id, cm_node, jiffies); + nes_debug(NES_DBG_CM, "QP%u, cm_node=%p, jiffies = %lu listener = %p\n", + nesqp->hwqp.qp_id, cm_node, jiffies, cm_node->listener); atomic_inc(&cm_accepts); nes_debug(NES_DBG_CM, "netdev refcnt = %u.\n", atomic_read(&nesvnic->netdev->refcnt)); - /* allocate the ietf frame and space for private data */ - nesqp->ietf_frame = pci_alloc_consistent(nesdev->pcidev, - sizeof(struct ietf_mpa_frame) + conn_param->private_data_len, - &nesqp->ietf_frame_pbase); - - if (!nesqp->ietf_frame) { - nes_debug(NES_DBG_CM, "Unable to allocate memory for private data\n"); - return -ENOMEM; - } + /* allocate the ietf frame and space for private data */ + nesqp->ietf_frame = pci_alloc_consistent(nesdev->pcidev, + sizeof(struct ietf_mpa_frame) + conn_param->private_data_len, + &nesqp->ietf_frame_pbase); + if (!nesqp->ietf_frame) { + nes_debug(NES_DBG_CM, "Unable to allocate memory for private " + "data\n"); + return -ENOMEM; + } - /* setup the MPA frame */ - nesqp->private_data_len = conn_param->private_data_len; - memcpy(nesqp->ietf_frame->key, IEFT_MPA_KEY_REP, IETF_MPA_KEY_SIZE); - memcpy(nesqp->ietf_frame->priv_data, conn_param->private_data, - conn_param->private_data_len); + /* setup the MPA frame */ + nesqp->private_data_len = conn_param->private_data_len; + memcpy(nesqp->ietf_frame->key, IEFT_MPA_KEY_REP, IETF_MPA_KEY_SIZE); - nesqp->ietf_frame->priv_data_len = cpu_to_be16(conn_param->private_data_len); - nesqp->ietf_frame->rev = mpa_version; - nesqp->ietf_frame->flags = IETF_MPA_FLAGS_CRC; + memcpy(nesqp->ietf_frame->priv_data, conn_param->private_data, + conn_param->private_data_len); - /* setup our first outgoing iWarp send WQE (the IETF frame response) */ - wqe = &nesqp->hwqp.sq_vbase[0]; + nesqp->ietf_frame->priv_data_len = + cpu_to_be16(conn_param->private_data_len); + nesqp->ietf_frame->rev = mpa_version; + nesqp->ietf_frame->flags = IETF_MPA_FLAGS_CRC; - if (cm_id->remote_addr.sin_addr.s_addr != cm_id->local_addr.sin_addr.s_addr) { - u64temp = (unsigned long)nesqp; - u64temp |= NES_SW_CONTEXT_ALIGN>>1; - set_wqe_64bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX, - u64temp); - wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] = - cpu_to_le32(NES_IWARP_SQ_WQE_STREAMING | NES_IWARP_SQ_WQE_WRPDU); - wqe->wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX] = - cpu_to_le32(conn_param->private_data_len + sizeof(struct ietf_mpa_frame)); - wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_LOW_IDX] = - cpu_to_le32((u32)nesqp->ietf_frame_pbase); - wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_HIGH_IDX] = - cpu_to_le32((u32)((u64)nesqp->ietf_frame_pbase >> 32)); - wqe->wqe_words[NES_IWARP_SQ_WQE_LENGTH0_IDX] = - cpu_to_le32(conn_param->private_data_len + sizeof(struct ietf_mpa_frame)); - wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = 0; - - nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32( - NES_QPCONTEXT_ORDIRD_LSMM_PRESENT | NES_QPCONTEXT_ORDIRD_WRPDU); - } else { - nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32((NES_QPCONTEXT_ORDIRD_LSMM_PRESENT | - NES_QPCONTEXT_ORDIRD_WRPDU | NES_QPCONTEXT_ORDIRD_ALSMM)); - } - nesqp->skip_lsmm = 1; + /* setup our first outgoing iWarp send WQE (the IETF frame response) */ + wqe = &nesqp->hwqp.sq_vbase[0]; + + if (cm_id->remote_addr.sin_addr.s_addr != + cm_id->local_addr.sin_addr.s_addr) { + u64temp = (unsigned long)nesqp; + u64temp |= NES_SW_CONTEXT_ALIGN>>1; + set_wqe_64bit_value(wqe->wqe_words, + NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX, + u64temp); + wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] = + cpu_to_le32(NES_IWARP_SQ_WQE_STREAMING | + NES_IWARP_SQ_WQE_WRPDU); + wqe->wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX] = + cpu_to_le32(conn_param->private_data_len + + sizeof(struct ietf_mpa_frame)); + wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_LOW_IDX] = + cpu_to_le32((u32)nesqp->ietf_frame_pbase); + wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_HIGH_IDX] = + cpu_to_le32((u32)((u64)nesqp->ietf_frame_pbase >> 32)); + wqe->wqe_words[NES_IWARP_SQ_WQE_LENGTH0_IDX] = + cpu_to_le32(conn_param->private_data_len + + sizeof(struct ietf_mpa_frame)); + wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = 0; + + nesqp->nesqp_context->ird_ord_sizes |= + cpu_to_le32(NES_QPCONTEXT_ORDIRD_LSMM_PRESENT | + NES_QPCONTEXT_ORDIRD_WRPDU); + } else { + nesqp->nesqp_context->ird_ord_sizes |= + cpu_to_le32((NES_QPCONTEXT_ORDIRD_LSMM_PRESENT | + NES_QPCONTEXT_ORDIRD_WRPDU | + NES_QPCONTEXT_ORDIRD_ALSMM)); + } + nesqp->skip_lsmm = 1; /* Cache the cm_id in the qp */ @@ -2424,55 +2630,75 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) cm_id->provider_data = nesqp; nesqp->active_conn = 0; + if (cm_node->state == NES_CM_STATE_TSA) + nes_debug(NES_DBG_CM, "Already state = TSA for cm_node=%p\n", + cm_node); + nes_cm_init_tsa_conn(nesqp, cm_node); - nesqp->nesqp_context->tcpPorts[0] = cpu_to_le16(ntohs(cm_id->local_addr.sin_port)); - nesqp->nesqp_context->tcpPorts[1] = cpu_to_le16(ntohs(cm_id->remote_addr.sin_port)); - nesqp->nesqp_context->ip0 = cpu_to_le32(ntohl(cm_id->remote_addr.sin_addr.s_addr)); + nesqp->nesqp_context->tcpPorts[0] = + cpu_to_le16(ntohs(cm_id->local_addr.sin_port)); + nesqp->nesqp_context->tcpPorts[1] = + cpu_to_le16(ntohs(cm_id->remote_addr.sin_port)); + + if (ipv4_is_loopback(cm_id->remote_addr.sin_addr.s_addr)) + nesqp->nesqp_context->ip0 = + cpu_to_le32(ntohl(nesvnic->local_ipaddr)); + else + nesqp->nesqp_context->ip0 = + cpu_to_le32(ntohl(cm_id->remote_addr.sin_addr.s_addr)); nesqp->nesqp_context->misc2 |= cpu_to_le32( - (u32)PCI_FUNC(nesdev->pcidev->devfn) << NES_QPCONTEXT_MISC2_SRC_IP_SHIFT); + (u32)PCI_FUNC(nesdev->pcidev->devfn) << + NES_QPCONTEXT_MISC2_SRC_IP_SHIFT); - nesqp->nesqp_context->arp_index_vlan |= cpu_to_le32( - nes_arp_table(nesdev, le32_to_cpu(nesqp->nesqp_context->ip0), NULL, + nesqp->nesqp_context->arp_index_vlan |= + cpu_to_le32(nes_arp_table(nesdev, + le32_to_cpu(nesqp->nesqp_context->ip0), NULL, NES_ARP_RESOLVE) << 16); nesqp->nesqp_context->ts_val_delta = cpu_to_le32( - jiffies - nes_read_indexed(nesdev, NES_IDX_TCP_NOW)); + jiffies - nes_read_indexed(nesdev, NES_IDX_TCP_NOW)); nesqp->nesqp_context->ird_index = cpu_to_le32(nesqp->hwqp.qp_id); nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32( - ((u32)1 << NES_QPCONTEXT_ORDIRD_IWARP_MODE_SHIFT)); - nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32((u32)conn_param->ord); + ((u32)1 << NES_QPCONTEXT_ORDIRD_IWARP_MODE_SHIFT)); + nesqp->nesqp_context->ird_ord_sizes |= + cpu_to_le32((u32)conn_param->ord); memset(&nes_quad, 0, sizeof(nes_quad)); - nes_quad.DstIpAdrIndex = cpu_to_le32((u32)PCI_FUNC(nesdev->pcidev->devfn) << 24); - nes_quad.SrcIpadr = cm_id->remote_addr.sin_addr.s_addr; - nes_quad.TcpPorts[0] = cm_id->remote_addr.sin_port; - nes_quad.TcpPorts[1] = cm_id->local_addr.sin_port; + nes_quad.DstIpAdrIndex = + cpu_to_le32((u32)PCI_FUNC(nesdev->pcidev->devfn) << 24); + if (ipv4_is_loopback(cm_id->remote_addr.sin_addr.s_addr)) + nes_quad.SrcIpadr = nesvnic->local_ipaddr; + else + nes_quad.SrcIpadr = cm_id->remote_addr.sin_addr.s_addr; + nes_quad.TcpPorts[0] = cm_id->remote_addr.sin_port; + nes_quad.TcpPorts[1] = cm_id->local_addr.sin_port; /* Produce hash key */ crc_value = get_crc_value(&nes_quad); nesqp->hte_index = cpu_to_be32(crc_value ^ 0xffffffff); nes_debug(NES_DBG_CM, "HTE Index = 0x%08X, CRC = 0x%08X\n", - nesqp->hte_index, nesqp->hte_index & adapter->hte_index_mask); + nesqp->hte_index, nesqp->hte_index & adapter->hte_index_mask); nesqp->hte_index &= adapter->hte_index_mask; nesqp->nesqp_context->hte_index = cpu_to_le32(nesqp->hte_index); cm_node->cm_core->api->accelerated(cm_node->cm_core, cm_node); - nes_debug(NES_DBG_CM, "QP%u, Destination IP = 0x%08X:0x%04X, local = 0x%08X:0x%04X," - " rcv_nxt=0x%08X, snd_nxt=0x%08X, mpa + private data length=%zu.\n", - nesqp->hwqp.qp_id, + nes_debug(NES_DBG_CM, "QP%u, Destination IP = 0x%08X:0x%04X, local = " + "0x%08X:0x%04X, rcv_nxt=0x%08X, snd_nxt=0x%08X, mpa + " + "private data length=%zu.\n", nesqp->hwqp.qp_id, ntohl(cm_id->remote_addr.sin_addr.s_addr), ntohs(cm_id->remote_addr.sin_port), ntohl(cm_id->local_addr.sin_addr.s_addr), ntohs(cm_id->local_addr.sin_port), le32_to_cpu(nesqp->nesqp_context->rcv_nxt), le32_to_cpu(nesqp->nesqp_context->snd_nxt), - conn_param->private_data_len+sizeof(struct ietf_mpa_frame)); + conn_param->private_data_len + + sizeof(struct ietf_mpa_frame)); attr.qp_state = IB_QPS_RTS; nes_modify_qp(&nesqp->ibqp, &attr, IB_QP_STATE, NULL); @@ -2489,15 +2715,16 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) cm_event.private_data_len = 0; ret = cm_id->event_handler(cm_id, &cm_event); if (cm_node->loopbackpartner) { - cm_node->loopbackpartner->mpa_frame_size = nesqp->private_data_len; + cm_node->loopbackpartner->mpa_frame_size = + nesqp->private_data_len; /* copy entire MPA frame to our cm_node's frame */ - memcpy(cm_node->loopbackpartner->mpa_frame_buf, nesqp->ietf_frame->priv_data, - nesqp->private_data_len); + memcpy(cm_node->loopbackpartner->mpa_frame_buf, + nesqp->ietf_frame->priv_data, nesqp->private_data_len); create_event(cm_node->loopbackpartner, NES_CM_EVENT_CONNECTED); } if (ret) - printk("%s[%u] OFA CM event_handler returned, ret=%d\n", - __func__, __LINE__, ret); + printk(KERN_ERR "%s[%u] OFA CM event_handler returned, " + "ret=%d\n", __func__, __LINE__, ret); return 0; } @@ -2555,74 +2782,61 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) if (!nesdev) return -EINVAL; - atomic_inc(&cm_connects); - - nesqp->ietf_frame = kzalloc(sizeof(struct ietf_mpa_frame) + - conn_param->private_data_len, GFP_KERNEL); - if (!nesqp->ietf_frame) - return -ENOMEM; + nes_debug(NES_DBG_CM, "QP%u, current IP = 0x%08X, Destination IP = " + "0x%08X:0x%04X, local = 0x%08X:0x%04X.\n", nesqp->hwqp.qp_id, + ntohl(nesvnic->local_ipaddr), + ntohl(cm_id->remote_addr.sin_addr.s_addr), + ntohs(cm_id->remote_addr.sin_port), + ntohl(cm_id->local_addr.sin_addr.s_addr), + ntohs(cm_id->local_addr.sin_port)); - /* set qp as having an active connection */ + atomic_inc(&cm_connects); nesqp->active_conn = 1; - nes_debug(NES_DBG_CM, "QP%u, Destination IP = 0x%08X:0x%04X, local = 0x%08X:0x%04X.\n", - nesqp->hwqp.qp_id, - ntohl(cm_id->remote_addr.sin_addr.s_addr), - ntohs(cm_id->remote_addr.sin_port), - ntohl(cm_id->local_addr.sin_addr.s_addr), - ntohs(cm_id->local_addr.sin_port)); - /* cache the cm_id in the qp */ nesqp->cm_id = cm_id; cm_id->provider_data = nesqp; - /* copy the private data */ - if (conn_param->private_data_len) { - memcpy(nesqp->ietf_frame->priv_data, conn_param->private_data, - conn_param->private_data_len); - } - nesqp->private_data_len = conn_param->private_data_len; nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32((u32)conn_param->ord); nes_debug(NES_DBG_CM, "requested ord = 0x%08X.\n", (u32)conn_param->ord); - nes_debug(NES_DBG_CM, "mpa private data len =%u\n", conn_param->private_data_len); - - strcpy(&nesqp->ietf_frame->key[0], IEFT_MPA_KEY_REQ); - nesqp->ietf_frame->flags = IETF_MPA_FLAGS_CRC; - nesqp->ietf_frame->rev = IETF_MPA_VERSION; - nesqp->ietf_frame->priv_data_len = htons(conn_param->private_data_len); + nes_debug(NES_DBG_CM, "mpa private data len =%u\n", + conn_param->private_data_len); - if (cm_id->local_addr.sin_addr.s_addr != cm_id->remote_addr.sin_addr.s_addr) + if (cm_id->local_addr.sin_addr.s_addr != + cm_id->remote_addr.sin_addr.s_addr) nes_manage_apbvt(nesvnic, ntohs(cm_id->local_addr.sin_port), - PCI_FUNC(nesdev->pcidev->devfn), NES_MANAGE_APBVT_ADD); + PCI_FUNC(nesdev->pcidev->devfn), NES_MANAGE_APBVT_ADD); /* set up the connection params for the node */ - cm_info.loc_addr = (cm_id->local_addr.sin_addr.s_addr); - cm_info.loc_port = (cm_id->local_addr.sin_port); - cm_info.rem_addr = (cm_id->remote_addr.sin_addr.s_addr); - cm_info.rem_port = (cm_id->remote_addr.sin_port); + cm_info.loc_addr = htonl(cm_id->local_addr.sin_addr.s_addr); + cm_info.loc_port = htons(cm_id->local_addr.sin_port); + cm_info.rem_addr = htonl(cm_id->remote_addr.sin_addr.s_addr); + cm_info.rem_port = htons(cm_id->remote_addr.sin_port); cm_info.cm_id = cm_id; cm_info.conn_type = NES_CM_IWARP_CONN_TYPE; cm_id->add_ref(cm_id); - nes_add_ref(&nesqp->ibqp); /* create a connect CM node connection */ - cm_node = g_cm_core->api->connect(g_cm_core, nesvnic, nesqp->ietf_frame, &cm_info); + cm_node = g_cm_core->api->connect(g_cm_core, nesvnic, + conn_param->private_data_len, (void *)conn_param->private_data, + &cm_info); if (!cm_node) { - if (cm_id->local_addr.sin_addr.s_addr != cm_id->remote_addr.sin_addr.s_addr) + if (cm_id->local_addr.sin_addr.s_addr != + cm_id->remote_addr.sin_addr.s_addr) nes_manage_apbvt(nesvnic, ntohs(cm_id->local_addr.sin_port), - PCI_FUNC(nesdev->pcidev->devfn), NES_MANAGE_APBVT_DEL); - nes_rem_ref(&nesqp->ibqp); - kfree(nesqp->ietf_frame); - nesqp->ietf_frame = NULL; + PCI_FUNC(nesdev->pcidev->devfn), + NES_MANAGE_APBVT_DEL); + cm_id->rem_ref(cm_id); return -ENOMEM; } cm_node->apbvt_set = 1; nesqp->cm_node = cm_node; + cm_node->nesqp = nesqp; return 0; } @@ -2664,7 +2878,7 @@ int nes_create_listen(struct iw_cm_id *cm_id, int backlog) cm_node = g_cm_core->api->listen(g_cm_core, nesvnic, &cm_info); if (!cm_node) { - printk("%s[%u] Error returned from listen API call\n", + printk(KERN_ERR "%s[%u] Error returned from listen API call\n", __func__, __LINE__); return -ENOMEM; } @@ -2672,10 +2886,13 @@ int nes_create_listen(struct iw_cm_id *cm_id, int backlog) cm_id->provider_data = cm_node; if (!cm_node->reused_node) { - err = nes_manage_apbvt(nesvnic, ntohs(cm_id->local_addr.sin_port), - PCI_FUNC(nesvnic->nesdev->pcidev->devfn), NES_MANAGE_APBVT_ADD); + err = nes_manage_apbvt(nesvnic, + ntohs(cm_id->local_addr.sin_port), + PCI_FUNC(nesvnic->nesdev->pcidev->devfn), + NES_MANAGE_APBVT_ADD); if (err) { - printk("nes_manage_apbvt call returned %d.\n", err); + printk(KERN_ERR "nes_manage_apbvt call returned %d.\n", + err); g_cm_core->api->stop_listener(g_cm_core, (void *)cm_node); return err; } @@ -2795,53 +3012,70 @@ static void cm_event_connected(struct nes_cm_event *event) nes_cm_init_tsa_conn(nesqp, cm_node); /* set the QP tsa context */ - nesqp->nesqp_context->tcpPorts[0] = cpu_to_le16(ntohs(cm_id->local_addr.sin_port)); - nesqp->nesqp_context->tcpPorts[1] = cpu_to_le16(ntohs(cm_id->remote_addr.sin_port)); - nesqp->nesqp_context->ip0 = cpu_to_le32(ntohl(cm_id->remote_addr.sin_addr.s_addr)); + nesqp->nesqp_context->tcpPorts[0] = + cpu_to_le16(ntohs(cm_id->local_addr.sin_port)); + nesqp->nesqp_context->tcpPorts[1] = + cpu_to_le16(ntohs(cm_id->remote_addr.sin_port)); + if (ipv4_is_loopback(cm_id->remote_addr.sin_addr.s_addr)) + nesqp->nesqp_context->ip0 = + cpu_to_le32(ntohl(nesvnic->local_ipaddr)); + else + nesqp->nesqp_context->ip0 = + cpu_to_le32(ntohl(cm_id->remote_addr.sin_addr.s_addr)); nesqp->nesqp_context->misc2 |= cpu_to_le32( - (u32)PCI_FUNC(nesdev->pcidev->devfn) << NES_QPCONTEXT_MISC2_SRC_IP_SHIFT); + (u32)PCI_FUNC(nesdev->pcidev->devfn) << + NES_QPCONTEXT_MISC2_SRC_IP_SHIFT); nesqp->nesqp_context->arp_index_vlan |= cpu_to_le32( - nes_arp_table(nesdev, le32_to_cpu(nesqp->nesqp_context->ip0), + nes_arp_table(nesdev, + le32_to_cpu(nesqp->nesqp_context->ip0), NULL, NES_ARP_RESOLVE) << 16); nesqp->nesqp_context->ts_val_delta = cpu_to_le32( jiffies - nes_read_indexed(nesdev, NES_IDX_TCP_NOW)); nesqp->nesqp_context->ird_index = cpu_to_le32(nesqp->hwqp.qp_id); nesqp->nesqp_context->ird_ord_sizes |= - cpu_to_le32((u32)1 << NES_QPCONTEXT_ORDIRD_IWARP_MODE_SHIFT); + cpu_to_le32((u32)1 << + NES_QPCONTEXT_ORDIRD_IWARP_MODE_SHIFT); /* Adjust tail for not having a LSMM */ nesqp->hwqp.sq_tail = 1; #if defined(NES_SEND_FIRST_WRITE) - if (cm_node->send_write0) { - nes_debug(NES_DBG_CM, "Sending first write.\n"); - wqe = &nesqp->hwqp.sq_vbase[0]; - u64temp = (unsigned long)nesqp; - u64temp |= NES_SW_CONTEXT_ALIGN>>1; - set_wqe_64bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX, - u64temp); - wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] = cpu_to_le32(NES_IWARP_SQ_OP_RDMAW); - wqe->wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX] = 0; - wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_LOW_IDX] = 0; - wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_HIGH_IDX] = 0; - wqe->wqe_words[NES_IWARP_SQ_WQE_LENGTH0_IDX] = 0; - wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = 0; - - /* use the reserved spot on the WQ for the extra first WQE */ - nesqp->nesqp_context->ird_ord_sizes &= cpu_to_le32(~(NES_QPCONTEXT_ORDIRD_LSMM_PRESENT | - NES_QPCONTEXT_ORDIRD_WRPDU | NES_QPCONTEXT_ORDIRD_ALSMM)); - nesqp->skip_lsmm = 1; - nesqp->hwqp.sq_tail = 0; - nes_write32(nesdev->regs + NES_WQE_ALLOC, - (1 << 24) | 0x00800000 | nesqp->hwqp.qp_id); - } + if (cm_node->send_write0) { + nes_debug(NES_DBG_CM, "Sending first write.\n"); + wqe = &nesqp->hwqp.sq_vbase[0]; + u64temp = (unsigned long)nesqp; + u64temp |= NES_SW_CONTEXT_ALIGN>>1; + set_wqe_64bit_value(wqe->wqe_words, + NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX, u64temp); + wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] = + cpu_to_le32(NES_IWARP_SQ_OP_RDMAW); + wqe->wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX] = 0; + wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_LOW_IDX] = 0; + wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_HIGH_IDX] = 0; + wqe->wqe_words[NES_IWARP_SQ_WQE_LENGTH0_IDX] = 0; + wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = 0; + + /* use the reserved spot on the WQ for the extra first WQE */ + nesqp->nesqp_context->ird_ord_sizes &= + cpu_to_le32(~(NES_QPCONTEXT_ORDIRD_LSMM_PRESENT | + NES_QPCONTEXT_ORDIRD_WRPDU | + NES_QPCONTEXT_ORDIRD_ALSMM)); + nesqp->skip_lsmm = 1; + nesqp->hwqp.sq_tail = 0; + nes_write32(nesdev->regs + NES_WQE_ALLOC, + (1 << 24) | 0x00800000 | nesqp->hwqp.qp_id); + } #endif memset(&nes_quad, 0, sizeof(nes_quad)); - nes_quad.DstIpAdrIndex = cpu_to_le32((u32)PCI_FUNC(nesdev->pcidev->devfn) << 24); - nes_quad.SrcIpadr = cm_id->remote_addr.sin_addr.s_addr; + nes_quad.DstIpAdrIndex = + cpu_to_le32((u32)PCI_FUNC(nesdev->pcidev->devfn) << 24); + if (ipv4_is_loopback(cm_id->remote_addr.sin_addr.s_addr)) + nes_quad.SrcIpadr = nesvnic->local_ipaddr; + else + nes_quad.SrcIpadr = cm_id->remote_addr.sin_addr.s_addr; nes_quad.TcpPorts[0] = cm_id->remote_addr.sin_port; nes_quad.TcpPorts[1] = cm_id->local_addr.sin_port; @@ -2858,10 +3092,6 @@ static void cm_event_connected(struct nes_cm_event *event) nesqp->private_data_len = (u8) cm_node->mpa_frame_size; cm_node->cm_core->api->accelerated(cm_node->cm_core, cm_node); - /* modify QP state to rts */ - attr.qp_state = IB_QPS_RTS; - nes_modify_qp(&nesqp->ibqp, &attr, IB_QP_STATE, NULL); - /* notify OF layer we successfully created the requested connection */ cm_event.event = IW_CM_EVENT_CONNECT_REPLY; cm_event.status = IW_CM_EVENT_STATUS_ACCEPTED; @@ -2870,20 +3100,21 @@ static void cm_event_connected(struct nes_cm_event *event) cm_event.local_addr.sin_port = cm_id->local_addr.sin_port; cm_event.remote_addr = cm_id->remote_addr; - cm_event.private_data = (void *)event->cm_node->mpa_frame_buf; - cm_event.private_data_len = (u8) event->cm_node->mpa_frame_size; + cm_event.private_data = (void *)event->cm_node->mpa_frame_buf; + cm_event.private_data_len = (u8) event->cm_node->mpa_frame_size; cm_event.local_addr.sin_addr.s_addr = event->cm_info.rem_addr; ret = cm_id->event_handler(cm_id, &cm_event); nes_debug(NES_DBG_CM, "OFA CM event_handler returned, ret=%d\n", ret); if (ret) - printk("%s[%u] OFA CM event_handler returned, ret=%d\n", - __func__, __LINE__, ret); - nes_debug(NES_DBG_CM, "Exiting connect thread for QP%u. jiffies = %lu\n", - nesqp->hwqp.qp_id, jiffies ); + printk(KERN_ERR "%s[%u] OFA CM event_handler returned, " + "ret=%d\n", __func__, __LINE__, ret); + attr.qp_state = IB_QPS_RTS; + nes_modify_qp(&nesqp->ibqp, &attr, IB_QP_STATE, NULL); - nes_rem_ref(&nesqp->ibqp); + nes_debug(NES_DBG_CM, "Exiting connect thread for QP%u. jiffies = " + "%lu\n", nesqp->hwqp.qp_id, jiffies); return; } @@ -2927,17 +3158,19 @@ static void cm_event_connect_error(struct nes_cm_event *event) cm_event.private_data = NULL; cm_event.private_data_len = 0; - nes_debug(NES_DBG_CM, "call CM_EVENT REJECTED, local_addr=%08x, remove_addr=%08x\n", - cm_event.local_addr.sin_addr.s_addr, cm_event.remote_addr.sin_addr.s_addr); + nes_debug(NES_DBG_CM, "call CM_EVENT REJECTED, local_addr=%08x, " + "remove_addr=%08x\n", cm_event.local_addr.sin_addr.s_addr, + cm_event.remote_addr.sin_addr.s_addr); ret = cm_id->event_handler(cm_id, &cm_event); nes_debug(NES_DBG_CM, "OFA CM event_handler returned, ret=%d\n", ret); if (ret) - printk("%s[%u] OFA CM event_handler returned, ret=%d\n", - __func__, __LINE__, ret); + printk(KERN_ERR "%s[%u] OFA CM event_handler returned, " + "ret=%d\n", __func__, __LINE__, ret); nes_rem_ref(&nesqp->ibqp); - cm_id->rem_ref(cm_id); + cm_id->rem_ref(cm_id); + rem_ref_cm_node(event->cm_node->cm_core, event->cm_node); return; } @@ -3040,7 +3273,8 @@ static int nes_cm_post_event(struct nes_cm_event *event) add_ref_cm_node(event->cm_node); event->cm_info.cm_id->add_ref(event->cm_info.cm_id); INIT_WORK(&event->event_work, nes_cm_event_handler); - nes_debug(NES_DBG_CM, "queue_work, event=%p\n", event); + nes_debug(NES_DBG_CM, "cm_node=%p queue_work, event=%p\n", + event->cm_node, event); queue_work(event->cm_node->cm_core->event_wq, &event->event_work); @@ -3056,46 +3290,48 @@ static int nes_cm_post_event(struct nes_cm_event *event) */ static void nes_cm_event_handler(struct work_struct *work) { - struct nes_cm_event *event = container_of(work, struct nes_cm_event, event_work); + struct nes_cm_event *event = container_of(work, struct nes_cm_event, + event_work); struct nes_cm_core *cm_core; - if ((!event) || (!event->cm_node) || (!event->cm_node->cm_core)) { + if ((!event) || (!event->cm_node) || (!event->cm_node->cm_core)) return; - } + cm_core = event->cm_node->cm_core; nes_debug(NES_DBG_CM, "event=%p, event->type=%u, events posted=%u\n", - event, event->type, atomic_read(&cm_core->events_posted)); + event, event->type, atomic_read(&cm_core->events_posted)); switch (event->type) { - case NES_CM_EVENT_MPA_REQ: - cm_event_mpa_req(event); - nes_debug(NES_DBG_CM, "CM Event: MPA REQUEST\n"); - break; - case NES_CM_EVENT_RESET: - nes_debug(NES_DBG_CM, "CM Event: RESET\n"); - cm_event_reset(event); - break; - case NES_CM_EVENT_CONNECTED: - if ((!event->cm_node->cm_id) || - (event->cm_node->state != NES_CM_STATE_TSA)) { - break; - } - cm_event_connected(event); - nes_debug(NES_DBG_CM, "CM Event: CONNECTED\n"); + case NES_CM_EVENT_MPA_REQ: + cm_event_mpa_req(event); + nes_debug(NES_DBG_CM, "cm_node=%p CM Event: MPA REQUEST\n", + event->cm_node); + break; + case NES_CM_EVENT_RESET: + nes_debug(NES_DBG_CM, "cm_node = %p CM Event: RESET\n", + event->cm_node); + cm_event_reset(event); + break; + case NES_CM_EVENT_CONNECTED: + if ((!event->cm_node->cm_id) || + (event->cm_node->state != NES_CM_STATE_TSA)) break; - case NES_CM_EVENT_ABORTED: - if ((!event->cm_node->cm_id) || (event->cm_node->state == NES_CM_STATE_TSA)) { - break; - } - cm_event_connect_error(event); - nes_debug(NES_DBG_CM, "CM Event: ABORTED\n"); - break; - case NES_CM_EVENT_DROPPED_PKT: - nes_debug(NES_DBG_CM, "CM Event: DROPPED PKT\n"); - break; - default: - nes_debug(NES_DBG_CM, "CM Event: UNKNOWN EVENT TYPE\n"); + cm_event_connected(event); + nes_debug(NES_DBG_CM, "CM Event: CONNECTED\n"); + break; + case NES_CM_EVENT_ABORTED: + if ((!event->cm_node->cm_id) || + (event->cm_node->state == NES_CM_STATE_TSA)) break; + cm_event_connect_error(event); + nes_debug(NES_DBG_CM, "CM Event: ABORTED\n"); + break; + case NES_CM_EVENT_DROPPED_PKT: + nes_debug(NES_DBG_CM, "CM Event: DROPPED PKT\n"); + break; + default: + nes_debug(NES_DBG_CM, "CM Event: UNKNOWN EVENT TYPE\n"); + break; } atomic_dec(&cm_core->events_posted); diff --git a/drivers/infiniband/hw/nes/nes_cm.h b/drivers/infiniband/hw/nes/nes_cm.h index 7717cb2ab500..367b3d290140 100644 --- a/drivers/infiniband/hw/nes/nes_cm.h +++ b/drivers/infiniband/hw/nes/nes_cm.h @@ -83,6 +83,8 @@ enum nes_timer_type { #define SET_FIN 4 #define SET_RST 8 +#define TCP_OPTIONS_PADDING 3 + struct option_base { u8 optionnum; u8 length; @@ -177,6 +179,7 @@ enum nes_cm_node_state { NES_CM_STATE_ESTABLISHED, NES_CM_STATE_ACCEPTING, NES_CM_STATE_MPAREQ_SENT, + NES_CM_STATE_MPAREQ_RCVD, NES_CM_STATE_TSA, NES_CM_STATE_FIN_WAIT1, NES_CM_STATE_FIN_WAIT2, @@ -187,6 +190,16 @@ enum nes_cm_node_state { NES_CM_STATE_CLOSED }; +enum nes_tcpip_pkt_type { + NES_PKT_TYPE_UNKNOWN, + NES_PKT_TYPE_SYN, + NES_PKT_TYPE_SYNACK, + NES_PKT_TYPE_ACK, + NES_PKT_TYPE_FIN, + NES_PKT_TYPE_RST +}; + + /* type of nes connection */ enum nes_cm_conn_type { NES_CM_IWARP_CONN_TYPE, @@ -257,7 +270,9 @@ struct nes_cm_node { struct net_device *netdev; struct nes_cm_node *loopbackpartner; - struct list_head retrans_list; + + struct nes_timer_entry *send_entry; + spinlock_t retrans_list_lock; struct list_head recv_list; spinlock_t recv_list_lock; @@ -276,6 +291,8 @@ struct nes_cm_node { struct nes_vnic *nesvnic; int apbvt_set; int accept_pend; + int freed; + struct nes_qp *nesqp; }; /* structure for client or CM to fill when making CM api calls. */ @@ -366,14 +383,14 @@ struct nes_cm_ops { struct nes_cm_info *); int (*stop_listener)(struct nes_cm_core *, struct nes_cm_listener *); struct nes_cm_node * (*connect)(struct nes_cm_core *, - struct nes_vnic *, struct ietf_mpa_frame *, + struct nes_vnic *, u16, void *, struct nes_cm_info *); int (*close)(struct nes_cm_core *, struct nes_cm_node *); int (*accept)(struct nes_cm_core *, struct ietf_mpa_frame *, struct nes_cm_node *); int (*reject)(struct nes_cm_core *, struct ietf_mpa_frame *, struct nes_cm_node *); - int (*recv_pkt)(struct nes_cm_core *, struct nes_vnic *, + void (*recv_pkt)(struct nes_cm_core *, struct nes_vnic *, struct sk_buff *); int (*destroy_cm_core)(struct nes_cm_core *); int (*get)(struct nes_cm_core *); diff --git a/drivers/infiniband/hw/nes/nes_hw.c b/drivers/infiniband/hw/nes/nes_hw.c index 85f26d19a32b..1513d4066f1b 100644 --- a/drivers/infiniband/hw/nes/nes_hw.c +++ b/drivers/infiniband/hw/nes/nes_hw.c @@ -2814,7 +2814,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev, nesqp = *((struct nes_qp **)&context); if (atomic_inc_return(&nesqp->close_timer_started) == 1) { nesqp->cm_id->add_ref(nesqp->cm_id); - nes_add_ref(&nesqp->ibqp); schedule_nes_timer(nesqp->cm_node, (struct sk_buff *)nesqp, NES_TIMER_TYPE_CLOSE, 1, 0); nes_debug(NES_DBG_AEQ, "QP%u Not decrementing QP refcount (%d)," @@ -2838,7 +2837,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev, if (async_event_id == NES_AEQE_AEID_RESET_SENT) { tcp_state = NES_AEQE_TCP_STATE_CLOSED; } - nes_add_ref(&nesqp->ibqp); spin_lock_irqsave(&nesqp->lock, flags); nesqp->hw_iwarp_state = iwarp_state; nesqp->hw_tcp_state = tcp_state; @@ -2876,7 +2874,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev, } spin_unlock_irqrestore(&nesqp->lock, flags); if (next_iwarp_state) { - nes_add_ref(&nesqp->ibqp); nes_debug(NES_DBG_AEQ, "issuing hw modifyqp for QP%u. next state = 0x%08X," " also added another reference\n", nesqp->hwqp.qp_id, next_iwarp_state); @@ -2888,7 +2885,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev, /* FIN Received but ib state not RTS, close complete will be on its way */ spin_unlock_irqrestore(&nesqp->lock, flags); - nes_rem_ref(&nesqp->ibqp); return; } spin_unlock_irqrestore(&nesqp->lock, flags); @@ -2922,7 +2918,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev, if ((tcp_state == NES_AEQE_TCP_STATE_CLOSE_WAIT) || ((nesqp->ibqp_state == IB_QPS_RTS)&& (async_event_id == NES_AEQE_AEID_LLP_CONNECTION_RESET))) { - nes_add_ref(&nesqp->ibqp); nes_cm_disconn(nesqp); } else { nesqp->in_disconnect = 0; @@ -2931,7 +2926,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev, break; case NES_AEQE_AEID_LLP_TOO_MANY_RETRIES: nesqp = *((struct nes_qp **)&context); - nes_add_ref(&nesqp->ibqp); spin_lock_irqsave(&nesqp->lock, flags); nesqp->hw_iwarp_state = NES_AEQE_IWARP_STATE_ERROR; nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED; @@ -3042,7 +3036,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev, nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context); } /* tell cm to disconnect, cm will queue work to thread */ - nes_add_ref(&nesqp->ibqp); nes_cm_disconn(nesqp); break; case NES_AEQE_AEID_DDP_UBE_INVALID_MSN_NO_BUFFER_AVAILABLE: @@ -3062,7 +3055,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev, nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context); } /* tell cm to disconnect, cm will queue work to thread */ - nes_add_ref(&nesqp->ibqp); nes_cm_disconn(nesqp); break; case NES_AEQE_AEID_LLP_RECEIVED_MPA_CRC_ERROR: @@ -3082,7 +3074,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev, nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context); } /* tell cm to disconnect, cm will queue work to thread */ - nes_add_ref(&nesqp->ibqp); nes_cm_disconn(nesqp); break; /* TODO: additional AEs need to be here */ diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index e3939d13484e..d79942e84979 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -2867,7 +2867,6 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, nesqp->hwqp.qp_id, attr->qp_state, nesqp->ibqp_state, nesqp->iwarp_state, atomic_read(&nesqp->refcount)); - nes_add_ref(&nesqp->ibqp); spin_lock_irqsave(&nesqp->lock, qplockflags); nes_debug(NES_DBG_MOD_QP, "QP%u: hw_iwarp_state=0x%X, hw_tcp_state=0x%X," @@ -2882,7 +2881,6 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, nesqp->hwqp.qp_id); if (nesqp->iwarp_state > (u32)NES_CQP_QP_IWARP_STATE_IDLE) { spin_unlock_irqrestore(&nesqp->lock, qplockflags); - nes_rem_ref(&nesqp->ibqp); return -EINVAL; } next_iwarp_state = NES_CQP_QP_IWARP_STATE_IDLE; @@ -2893,7 +2891,6 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, nesqp->hwqp.qp_id); if (nesqp->iwarp_state>(u32)NES_CQP_QP_IWARP_STATE_IDLE) { spin_unlock_irqrestore(&nesqp->lock, qplockflags); - nes_rem_ref(&nesqp->ibqp); return -EINVAL; } next_iwarp_state = NES_CQP_QP_IWARP_STATE_IDLE; @@ -2904,14 +2901,12 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, nesqp->hwqp.qp_id); if (nesqp->iwarp_state>(u32)NES_CQP_QP_IWARP_STATE_RTS) { spin_unlock_irqrestore(&nesqp->lock, qplockflags); - nes_rem_ref(&nesqp->ibqp); return -EINVAL; } if (nesqp->cm_id == NULL) { nes_debug(NES_DBG_MOD_QP, "QP%u: Failing attempt to move QP to RTS without a CM_ID. \n", nesqp->hwqp.qp_id ); spin_unlock_irqrestore(&nesqp->lock, qplockflags); - nes_rem_ref(&nesqp->ibqp); return -EINVAL; } next_iwarp_state = NES_CQP_QP_IWARP_STATE_RTS; @@ -2929,7 +2924,6 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, nesqp->hwqp.qp_id, nesqp->hwqp.sq_head, nesqp->hwqp.sq_tail); if (nesqp->iwarp_state == (u32)NES_CQP_QP_IWARP_STATE_CLOSING) { spin_unlock_irqrestore(&nesqp->lock, qplockflags); - nes_rem_ref(&nesqp->ibqp); return 0; } else { if (nesqp->iwarp_state > (u32)NES_CQP_QP_IWARP_STATE_CLOSING) { @@ -2937,7 +2931,6 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, " ignored due to current iWARP state\n", nesqp->hwqp.qp_id); spin_unlock_irqrestore(&nesqp->lock, qplockflags); - nes_rem_ref(&nesqp->ibqp); return -EINVAL; } if (nesqp->hw_iwarp_state != NES_AEQE_IWARP_STATE_RTS) { @@ -2969,7 +2962,6 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, nesqp->hwqp.qp_id); if (nesqp->iwarp_state>=(u32)NES_CQP_QP_IWARP_STATE_TERMINATE) { spin_unlock_irqrestore(&nesqp->lock, qplockflags); - nes_rem_ref(&nesqp->ibqp); return -EINVAL; } /* next_iwarp_state = (NES_CQP_QP_IWARP_STATE_TERMINATE | 0x02000000); */ @@ -2982,7 +2974,6 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, case IB_QPS_RESET: if (nesqp->iwarp_state == (u32)NES_CQP_QP_IWARP_STATE_ERROR) { spin_unlock_irqrestore(&nesqp->lock, qplockflags); - nes_rem_ref(&nesqp->ibqp); return -EINVAL; } nes_debug(NES_DBG_MOD_QP, "QP%u: new state = error\n", @@ -3008,7 +2999,6 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, break; default: spin_unlock_irqrestore(&nesqp->lock, qplockflags); - nes_rem_ref(&nesqp->ibqp); return -EINVAL; break; } @@ -3088,7 +3078,6 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount), original_last_aeq, nesqp->last_aeq); /* this one is for the cm_disconnect thread */ - nes_add_ref(&nesqp->ibqp); spin_lock_irqsave(&nesqp->lock, qplockflags); nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED; nesqp->last_aeq = NES_AEQE_AEID_RESET_SENT; @@ -3097,14 +3086,12 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, } else { nes_debug(NES_DBG_MOD_QP, "QP%u No fake disconnect, QP refcount=%d\n", nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount)); - nes_rem_ref(&nesqp->ibqp); } } else { spin_lock_irqsave(&nesqp->lock, qplockflags); if (nesqp->cm_id) { /* These two are for the timer thread */ if (atomic_inc_return(&nesqp->close_timer_started) == 1) { - nes_add_ref(&nesqp->ibqp); nesqp->cm_id->add_ref(nesqp->cm_id); nes_debug(NES_DBG_MOD_QP, "QP%u Not decrementing QP refcount (%d)," " need ae to finish up, original_last_aeq = 0x%04X." @@ -3128,14 +3115,12 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, " original_last_aeq = 0x%04X. last_aeq = 0x%04X.\n", nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount), original_last_aeq, nesqp->last_aeq); - nes_rem_ref(&nesqp->ibqp); } } else { nes_debug(NES_DBG_MOD_QP, "QP%u Decrementing QP refcount (%d), No ae to finish up," " original_last_aeq = 0x%04X. last_aeq = 0x%04X.\n", nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount), original_last_aeq, nesqp->last_aeq); - nes_rem_ref(&nesqp->ibqp); } err = 0; diff --git a/drivers/infiniband/ulp/ipoib/Kconfig b/drivers/infiniband/ulp/ipoib/Kconfig index 691525cf394a..9d9a9dc51f18 100644 --- a/drivers/infiniband/ulp/ipoib/Kconfig +++ b/drivers/infiniband/ulp/ipoib/Kconfig @@ -11,16 +11,17 @@ config INFINIBAND_IPOIB config INFINIBAND_IPOIB_CM bool "IP-over-InfiniBand Connected Mode support" - depends on INFINIBAND_IPOIB && EXPERIMENTAL + depends on INFINIBAND_IPOIB default n ---help--- - This option enables experimental support for IPoIB connected mode. - After enabling this option, you need to switch to connected mode through - /sys/class/net/ibXXX/mode to actually create connections, and then increase - the interface MTU with e.g. ifconfig ib0 mtu 65520. + This option enables support for IPoIB connected mode. After + enabling this option, you need to switch to connected mode + through /sys/class/net/ibXXX/mode to actually create + connections, and then increase the interface MTU with + e.g. ifconfig ib0 mtu 65520. - WARNING: Enabling connected mode will trigger some - packet drops for multicast and UD mode traffic from this interface, + WARNING: Enabling connected mode will trigger some packet + drops for multicast and UD mode traffic from this interface, unless you limit mtu for these destinations to 2044. config INFINIBAND_IPOIB_DEBUG @@ -33,9 +34,10 @@ config INFINIBAND_IPOIB_DEBUG debug_level and mcast_debug_level module parameters (which can also be set after the driver is loaded through sysfs). - This option also creates an "ipoib_debugfs," which can be - mounted to expose debugging information about IB multicast - groups used by the IPoIB driver. + This option also creates a directory tree under ipoib/ in + debugfs, which contains files that expose debugging + information about IB multicast groups used by the IPoIB + driver. config INFINIBAND_IPOIB_DEBUG_DATA bool "IP-over-InfiniBand data path debugging" diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 8be9ea0436e6..f51201b17bfd 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -548,7 +548,7 @@ static int path_rec_start(struct net_device *dev, path_rec_completion, path, &path->query); if (path->query_id < 0) { - ipoib_warn(priv, "ib_sa_path_rec_get failed\n"); + ipoib_warn(priv, "ib_sa_path_rec_get failed: %d\n", path->query_id); path->query = NULL; return path->query_id; } diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c index 3a917c1f796f..63462ecca147 100644 --- a/drivers/infiniband/ulp/iser/iser_verbs.c +++ b/drivers/infiniband/ulp/iser/iser_verbs.c @@ -483,6 +483,7 @@ static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *eve break; case RDMA_CM_EVENT_DISCONNECTED: case RDMA_CM_EVENT_DEVICE_REMOVAL: + case RDMA_CM_EVENT_ADDR_CHANGE: iser_disconnected_handler(cma_id); break; default: diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c index adbf29f0169d..71c1971abf80 100644 --- a/drivers/input/keyboard/hil_kbd.c +++ b/drivers/input/keyboard/hil_kbd.c @@ -37,6 +37,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/semaphore.h> #include <linux/slab.h> #include <linux/pci_ids.h> diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c index 8486abc457ed..c600ab7f93e8 100644 --- a/drivers/input/keyboard/sh_keysc.c +++ b/drivers/input/keyboard/sh_keysc.c @@ -158,25 +158,18 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) memcpy(&priv->pdata, pdev->dev.platform_data, sizeof(priv->pdata)); pdata = &priv->pdata; - res = request_mem_region(res->start, res_size(res), pdev->name); - if (res == NULL) { - dev_err(&pdev->dev, "failed to request I/O memory\n"); - error = -EBUSY; - goto err1; - } - priv->iomem_base = ioremap_nocache(res->start, res_size(res)); if (priv->iomem_base == NULL) { dev_err(&pdev->dev, "failed to remap I/O memory\n"); error = -ENXIO; - goto err2; + goto err1; } priv->input = input_allocate_device(); if (!priv->input) { dev_err(&pdev->dev, "failed to allocate input device\n"); error = -ENOMEM; - goto err3; + goto err2; } input = priv->input; @@ -194,7 +187,7 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev); if (error) { dev_err(&pdev->dev, "failed to request IRQ\n"); - goto err4; + goto err3; } for (i = 0; i < SH_KEYSC_MAXKEYS; i++) { @@ -206,7 +199,7 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) error = input_register_device(input); if (error) { dev_err(&pdev->dev, "failed to register input device\n"); - goto err5; + goto err4; } iowrite16((sh_keysc_mode[pdata->mode].kymd << 8) | @@ -214,14 +207,12 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) iowrite16(0, priv->iomem_base + KYOUTDR_OFFS); iowrite16(KYCR2_IRQ_LEVEL, priv->iomem_base + KYCR2_OFFS); return 0; - err5: - free_irq(irq, pdev); err4: - input_free_device(input); + free_irq(irq, pdev); err3: - iounmap(priv->iomem_base); + input_free_device(input); err2: - release_mem_region(res->start, res_size(res)); + iounmap(priv->iomem_base); err1: platform_set_drvdata(pdev, NULL); kfree(priv); @@ -232,7 +223,6 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) static int __devexit sh_keysc_remove(struct platform_device *pdev) { struct sh_keysc_priv *priv = platform_get_drvdata(pdev); - struct resource *res; iowrite16(KYCR2_IRQ_DISABLED, priv->iomem_base + KYCR2_OFFS); @@ -240,9 +230,6 @@ static int __devexit sh_keysc_remove(struct platform_device *pdev) free_irq(platform_get_irq(pdev, 0), pdev); iounmap(priv->iomem_base); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, res_size(res)); - platform_set_drvdata(pdev, NULL); kfree(priv); return 0; diff --git a/drivers/input/keyboard/tosakbd.c b/drivers/input/keyboard/tosakbd.c index 94e444b4ee15..b12b7ee4b6aa 100644 --- a/drivers/input/keyboard/tosakbd.c +++ b/drivers/input/keyboard/tosakbd.c @@ -215,8 +215,6 @@ static int tosakbd_suspend(struct platform_device *dev, pm_message_t state) unsigned long flags; spin_lock_irqsave(&tosakbd->lock, flags); - PGSR1 = (PGSR1 & ~TOSA_GPIO_LOW_STROBE_BIT); - PGSR2 = (PGSR2 & ~TOSA_GPIO_HIGH_STROBE_BIT); tosakbd->suspended = 1; spin_unlock_irqrestore(&tosakbd->lock, flags); diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c index 49d8abfe38fe..daa9d4220331 100644 --- a/drivers/input/misc/hp_sdc_rtc.c +++ b/drivers/input/misc/hp_sdc_rtc.c @@ -44,6 +44,7 @@ #include <linux/proc_fs.h> #include <linux/poll.h> #include <linux/rtc.h> +#include <linux/semaphore.h> MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>"); MODULE_DESCRIPTION("HP i8042 SDC + MSM-58321 RTC Driver"); diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c index 7b233a492ad5..0d395979b2d1 100644 --- a/drivers/input/serio/hp_sdc.c +++ b/drivers/input/serio/hp_sdc.c @@ -67,9 +67,9 @@ #include <linux/module.h> #include <linux/ioport.h> #include <linux/time.h> +#include <linux/semaphore.h> #include <linux/slab.h> #include <linux/hil.h> -#include <linux/semaphore.h> #include <asm/io.h> #include <asm/system.h> diff --git a/drivers/input/touchscreen/corgi_ts.c b/drivers/input/touchscreen/corgi_ts.c index 4e9d8eece2e0..d0e13fc4a88c 100644 --- a/drivers/input/touchscreen/corgi_ts.c +++ b/drivers/input/touchscreen/corgi_ts.c @@ -195,7 +195,7 @@ static void ts_interrupt_main(struct corgi_ts *corgi_ts, int isTimer) { if ((GPLR(IRQ_TO_GPIO(corgi_ts->irq_gpio)) & GPIO_bit(IRQ_TO_GPIO(corgi_ts->irq_gpio))) == 0) { /* Disable Interrupt */ - set_irq_type(corgi_ts->irq_gpio, IRQT_NOEDGE); + set_irq_type(corgi_ts->irq_gpio, IRQ_TYPE_NONE); if (read_xydata(corgi_ts)) { corgi_ts->pendown = 1; new_data(corgi_ts); @@ -214,7 +214,7 @@ static void ts_interrupt_main(struct corgi_ts *corgi_ts, int isTimer) } /* Enable Falling Edge */ - set_irq_type(corgi_ts->irq_gpio, IRQT_FALLING); + set_irq_type(corgi_ts->irq_gpio, IRQ_TYPE_EDGE_FALLING); corgi_ts->pendown = 0; } } @@ -258,7 +258,7 @@ static int corgits_resume(struct platform_device *dev) corgi_ssp_ads7846_putget((4u << ADSCTRL_ADR_SH) | ADSCTRL_STS); /* Enable Falling Edge */ - set_irq_type(corgi_ts->irq_gpio, IRQT_FALLING); + set_irq_type(corgi_ts->irq_gpio, IRQ_TYPE_EDGE_FALLING); corgi_ts->power_mode = PWR_MODE_ACTIVE; return 0; @@ -333,7 +333,7 @@ static int __init corgits_probe(struct platform_device *pdev) corgi_ts->power_mode = PWR_MODE_ACTIVE; /* Enable Falling Edge */ - set_irq_type(corgi_ts->irq_gpio, IRQT_FALLING); + set_irq_type(corgi_ts->irq_gpio, IRQ_TYPE_EDGE_FALLING); return 0; diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c index a79f029b91c0..590a1379aa32 100644 --- a/drivers/input/touchscreen/mainstone-wm97xx.c +++ b/drivers/input/touchscreen/mainstone-wm97xx.c @@ -198,7 +198,7 @@ static int wm97xx_acc_startup(struct wm97xx *wm) switch (wm->id) { case WM9705_ID2: wm->pen_irq = IRQ_GPIO(4); - set_irq_type(IRQ_GPIO(4), IRQT_BOTHEDGE); + set_irq_type(IRQ_GPIO(4), IRQ_TYPE_EDGE_BOTH); break; case WM9712_ID2: case WM9713_ID2: diff --git a/drivers/isdn/Kconfig b/drivers/isdn/Kconfig index 66f946aa30b3..3d113c6e4a70 100644 --- a/drivers/isdn/Kconfig +++ b/drivers/isdn/Kconfig @@ -3,7 +3,7 @@ # menuconfig ISDN - tristate "ISDN support" + bool "ISDN support" depends on NET depends on !S390 ---help--- @@ -21,6 +21,8 @@ menuconfig ISDN if ISDN +source "drivers/isdn/mISDN/Kconfig" + menuconfig ISDN_I4L tristate "Old ISDN4Linux (deprecated)" ---help--- diff --git a/drivers/isdn/Makefile b/drivers/isdn/Makefile index 988142c30a6d..8380a4568d11 100644 --- a/drivers/isdn/Makefile +++ b/drivers/isdn/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_ISDN_I4L) += i4l/ obj-$(CONFIG_ISDN_CAPI) += capi/ +obj-$(CONFIG_MISDN) += mISDN/ obj-$(CONFIG_ISDN_CAPI) += hardware/ obj-$(CONFIG_ISDN_DIVERSION) += divert/ obj-$(CONFIG_ISDN_DRV_HISAX) += hisax/ diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index 8a35029caca0..871b0cbca5e4 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -1302,11 +1302,12 @@ static void capinc_tty_hangup(struct tty_struct *tty) #endif } -static void capinc_tty_break_ctl(struct tty_struct *tty, int state) +static int capinc_tty_break_ctl(struct tty_struct *tty, int state) { #ifdef _DEBUG_TTYFUNCS printk(KERN_DEBUG "capinc_tty_break_ctl(%d)\n", state); #endif + return 0; } static void capinc_tty_flush_buffer(struct tty_struct *tty) @@ -1552,7 +1553,8 @@ static int __init capi_init(void) return PTR_ERR(capi_class); } - device_create(capi_class, NULL, MKDEV(capi_major, 0), "capi"); + device_create_drvdata(capi_class, NULL, MKDEV(capi_major, 0), NULL, + "capi"); #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE if (capinc_tty_init() < 0) { diff --git a/drivers/isdn/gigaset/asyncdata.c b/drivers/isdn/gigaset/asyncdata.c index 091deb9d1c47..c2bd97d29273 100644 --- a/drivers/isdn/gigaset/asyncdata.c +++ b/drivers/isdn/gigaset/asyncdata.c @@ -575,7 +575,8 @@ int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb) else skb = iraw_encode(skb, HW_HDR_LEN, 0); if (!skb) { - err("unable to allocate memory for encoding!\n"); + dev_err(bcs->cs->dev, + "unable to allocate memory for encoding!\n"); return -ENOMEM; } diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c index 5255b5e20e13..3f11910c7ccd 100644 --- a/drivers/isdn/gigaset/bas-gigaset.c +++ b/drivers/isdn/gigaset/bas-gigaset.c @@ -1050,10 +1050,9 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx) } /* retrieve block of data to send */ - ifd->offset = gigaset_isowbuf_getbytes(ubc->isooutbuf, - ifd->length); - if (ifd->offset < 0) { - if (ifd->offset == -EBUSY) { + rc = gigaset_isowbuf_getbytes(ubc->isooutbuf, ifd->length); + if (rc < 0) { + if (rc == -EBUSY) { gig_dbg(DEBUG_ISO, "%s: buffer busy at frame %d", __func__, nframe); @@ -1062,11 +1061,12 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx) } else { dev_err(ucx->bcs->cs->dev, "%s: buffer error %d at frame %d\n", - __func__, ifd->offset, nframe); - return ifd->offset; + __func__, rc, nframe); + return rc; } break; } + ifd->offset = rc; ucx->limit = ubc->isooutbuf->nextread; ifd->status = 0; ifd->actual_length = 0; diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c index 827c32c16795..9d3ce7718e58 100644 --- a/drivers/isdn/gigaset/common.c +++ b/drivers/isdn/gigaset/common.c @@ -287,7 +287,7 @@ struct event_t *gigaset_add_event(struct cardstate *cs, tail = cs->ev_tail; next = (tail + 1) % MAX_EVENTS; if (unlikely(next == cs->ev_head)) - err("event queue full"); + dev_err(cs->dev, "event queue full\n"); else { event = cs->events + tail; event->type = type; diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h index f365993161fc..003752954993 100644 --- a/drivers/isdn/gigaset/gigaset.h +++ b/drivers/isdn/gigaset/gigaset.h @@ -106,7 +106,6 @@ enum debuglevel { #undef err #undef info #undef warn -#undef notice #define err(format, arg...) printk(KERN_ERR KBUILD_MODNAME ": " \ format "\n" , ## arg) @@ -114,8 +113,6 @@ enum debuglevel { format "\n" , ## arg) #define warn(format, arg...) printk(KERN_WARNING KBUILD_MODNAME ": " \ format "\n" , ## arg) -#define notice(format, arg...) printk(KERN_NOTICE KBUILD_MODNAME ": " \ - format "\n" , ## arg) #ifdef CONFIG_GIGASET_DEBUG diff --git a/drivers/isdn/gigaset/i4l.c b/drivers/isdn/gigaset/i4l.c index 9e089f06a942..3c127a8cbaf2 100644 --- a/drivers/isdn/gigaset/i4l.c +++ b/drivers/isdn/gigaset/i4l.c @@ -46,7 +46,8 @@ static int writebuf_from_LL(int driverID, int channel, int ack, return -ENODEV; } if (channel < 0 || channel >= cs->channels) { - err("%s: invalid channel ID (%d)", __func__, channel); + dev_err(cs->dev, "%s: invalid channel ID (%d)\n", + __func__, channel); return -ENODEV; } bcs = &cs->bcs[channel]; @@ -58,11 +59,13 @@ static int writebuf_from_LL(int driverID, int channel, int ack, if (!len) { if (ack) - notice("%s: not ACKing empty packet", __func__); + dev_notice(cs->dev, "%s: not ACKing empty packet\n", + __func__); return 0; } if (len > MAX_BUF_SIZE) { - err("%s: packet too large (%d bytes)", __func__, len); + dev_err(cs->dev, "%s: packet too large (%d bytes)\n", + __func__, len); return -EINVAL; } @@ -116,8 +119,7 @@ static int command_from_LL(isdn_ctrl *cntrl) gigaset_debugdrivers(); if (!cs) { - warn("LL tried to access unknown device with nr. %d", - cntrl->driver); + err("%s: invalid driver ID (%d)", __func__, cntrl->driver); return -ENODEV; } @@ -126,7 +128,7 @@ static int command_from_LL(isdn_ctrl *cntrl) gig_dbg(DEBUG_ANY, "ISDN_CMD_IOCTL (driver: %d, arg: %ld)", cntrl->driver, cntrl->arg); - warn("ISDN_CMD_IOCTL is not supported."); + dev_warn(cs->dev, "ISDN_CMD_IOCTL not supported\n"); return -EINVAL; case ISDN_CMD_DIAL: @@ -138,22 +140,23 @@ static int command_from_LL(isdn_ctrl *cntrl) cntrl->parm.setup.si1, cntrl->parm.setup.si2); if (cntrl->arg >= cs->channels) { - err("ISDN_CMD_DIAL: invalid channel (%d)", - (int) cntrl->arg); + dev_err(cs->dev, + "ISDN_CMD_DIAL: invalid channel (%d)\n", + (int) cntrl->arg); return -EINVAL; } bcs = cs->bcs + cntrl->arg; if (!gigaset_get_channel(bcs)) { - err("ISDN_CMD_DIAL: channel not free"); + dev_err(cs->dev, "ISDN_CMD_DIAL: channel not free\n"); return -EBUSY; } sp = kmalloc(sizeof *sp, GFP_ATOMIC); if (!sp) { gigaset_free_channel(bcs); - err("ISDN_CMD_DIAL: out of memory"); + dev_err(cs->dev, "ISDN_CMD_DIAL: out of memory\n"); return -ENOMEM; } *sp = cntrl->parm.setup; @@ -173,8 +176,9 @@ static int command_from_LL(isdn_ctrl *cntrl) gig_dbg(DEBUG_ANY, "ISDN_CMD_ACCEPTD"); if (cntrl->arg >= cs->channels) { - err("ISDN_CMD_ACCEPTD: invalid channel (%d)", - (int) cntrl->arg); + dev_err(cs->dev, + "ISDN_CMD_ACCEPTD: invalid channel (%d)\n", + (int) cntrl->arg); return -EINVAL; } @@ -196,8 +200,9 @@ static int command_from_LL(isdn_ctrl *cntrl) (int) cntrl->arg); if (cntrl->arg >= cs->channels) { - err("ISDN_CMD_HANGUP: invalid channel (%u)", - (unsigned) cntrl->arg); + dev_err(cs->dev, + "ISDN_CMD_HANGUP: invalid channel (%d)\n", + (int) cntrl->arg); return -EINVAL; } @@ -224,8 +229,9 @@ static int command_from_LL(isdn_ctrl *cntrl) cntrl->arg & 0xff, (cntrl->arg >> 8)); if ((cntrl->arg & 0xff) >= cs->channels) { - err("ISDN_CMD_SETL2: invalid channel (%u)", - (unsigned) cntrl->arg & 0xff); + dev_err(cs->dev, + "ISDN_CMD_SETL2: invalid channel (%d)\n", + (int) cntrl->arg & 0xff); return -EINVAL; } @@ -244,14 +250,16 @@ static int command_from_LL(isdn_ctrl *cntrl) cntrl->arg & 0xff, (cntrl->arg >> 8)); if ((cntrl->arg & 0xff) >= cs->channels) { - err("ISDN_CMD_SETL3: invalid channel (%u)", - (unsigned) cntrl->arg & 0xff); + dev_err(cs->dev, + "ISDN_CMD_SETL3: invalid channel (%d)\n", + (int) cntrl->arg & 0xff); return -EINVAL; } if (cntrl->arg >> 8 != ISDN_PROTO_L3_TRANS) { - err("ISDN_CMD_SETL3: invalid protocol %lu", - cntrl->arg >> 8); + dev_err(cs->dev, + "ISDN_CMD_SETL3: invalid protocol %lu\n", + cntrl->arg >> 8); return -EINVAL; } @@ -262,8 +270,9 @@ static int command_from_LL(isdn_ctrl *cntrl) case ISDN_CMD_ALERT: gig_dbg(DEBUG_ANY, "ISDN_CMD_ALERT"); //FIXME if (cntrl->arg >= cs->channels) { - err("ISDN_CMD_ALERT: invalid channel (%d)", - (int) cntrl->arg); + dev_err(cs->dev, + "ISDN_CMD_ALERT: invalid channel (%d)\n", + (int) cntrl->arg); return -EINVAL; } //bcs = cs->bcs + cntrl->arg; @@ -295,7 +304,8 @@ static int command_from_LL(isdn_ctrl *cntrl) gig_dbg(DEBUG_ANY, "ISDN_CMD_GETSIL"); break; default: - err("unknown command %d from LL", cntrl->command); + dev_err(cs->dev, "unknown command %d from LL\n", + cntrl->command); return -EINVAL; } diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c index af195b07c191..521951a898ec 100644 --- a/drivers/isdn/gigaset/interface.c +++ b/drivers/isdn/gigaset/interface.c @@ -197,7 +197,7 @@ static void if_close(struct tty_struct *tty, struct file *filp) mutex_lock(&cs->mutex); if (!cs->open_count) - warn("%s: device not opened", __func__); + dev_warn(cs->dev, "%s: device not opened\n", __func__); else { if (!--cs->open_count) { spin_lock_irqsave(&cs->lock, flags); @@ -232,7 +232,7 @@ static int if_ioctl(struct tty_struct *tty, struct file *file, return -ERESTARTSYS; // FIXME -EINTR? if (!cs->open_count) - warn("%s: device not opened", __func__); + dev_warn(cs->dev, "%s: device not opened\n", __func__); else { retval = 0; switch (cmd) { @@ -364,9 +364,9 @@ static int if_write(struct tty_struct *tty, const unsigned char *buf, int count) return -ERESTARTSYS; // FIXME -EINTR? if (!cs->open_count) - warn("%s: device not opened", __func__); + dev_warn(cs->dev, "%s: device not opened\n", __func__); else if (cs->mstate != MS_LOCKED) { - warn("can't write to unlocked device"); + dev_warn(cs->dev, "can't write to unlocked device\n"); retval = -EBUSY; } else if (!cs->connected) { gig_dbg(DEBUG_ANY, "can't write to unplugged device"); @@ -398,9 +398,9 @@ static int if_write_room(struct tty_struct *tty) return -ERESTARTSYS; // FIXME -EINTR? if (!cs->open_count) - warn("%s: device not opened", __func__); + dev_warn(cs->dev, "%s: device not opened\n", __func__); else if (cs->mstate != MS_LOCKED) { - warn("can't write to unlocked device"); + dev_warn(cs->dev, "can't write to unlocked device\n"); retval = -EBUSY; } else if (!cs->connected) { gig_dbg(DEBUG_ANY, "can't write to unplugged device"); @@ -430,9 +430,9 @@ static int if_chars_in_buffer(struct tty_struct *tty) return -ERESTARTSYS; // FIXME -EINTR? if (!cs->open_count) - warn("%s: device not opened", __func__); + dev_warn(cs->dev, "%s: device not opened\n", __func__); else if (cs->mstate != MS_LOCKED) { - warn("can't write to unlocked device"); + dev_warn(cs->dev, "can't write to unlocked device\n"); retval = -EBUSY; } else if (!cs->connected) { gig_dbg(DEBUG_ANY, "can't write to unplugged device"); @@ -460,7 +460,7 @@ static void if_throttle(struct tty_struct *tty) mutex_lock(&cs->mutex); if (!cs->open_count) - warn("%s: device not opened", __func__); + dev_warn(cs->dev, "%s: device not opened\n", __func__); else { //FIXME } @@ -483,7 +483,7 @@ static void if_unthrottle(struct tty_struct *tty) mutex_lock(&cs->mutex); if (!cs->open_count) - warn("%s: device not opened", __func__); + dev_warn(cs->dev, "%s: device not opened\n", __func__); else { //FIXME } @@ -510,7 +510,7 @@ static void if_set_termios(struct tty_struct *tty, struct ktermios *old) mutex_lock(&cs->mutex); if (!cs->open_count) { - warn("%s: device not opened", __func__); + dev_warn(cs->dev, "%s: device not opened\n", __func__); goto out; } @@ -623,7 +623,8 @@ void gigaset_if_init(struct cardstate *cs) if (!IS_ERR(cs->tty_dev)) dev_set_drvdata(cs->tty_dev, cs); else { - warn("could not register device to the tty subsystem"); + dev_warn(cs->dev, + "could not register device to the tty subsystem\n"); cs->tty_dev = NULL; } mutex_unlock(&cs->mutex); diff --git a/drivers/isdn/gigaset/usb-gigaset.c b/drivers/isdn/gigaset/usb-gigaset.c index 77d20ab0cd4d..4661830a49db 100644 --- a/drivers/isdn/gigaset/usb-gigaset.c +++ b/drivers/isdn/gigaset/usb-gigaset.c @@ -498,8 +498,9 @@ static int send_cb(struct cardstate *cs, struct cmdbuf_t *cb) if (status) { ucs->busy = 0; - err("could not submit urb (error %d)\n", - -status); + dev_err(cs->dev, + "could not submit urb (error %d)\n", + -status); cb->len = 0; /* skip urb => remove cb+wakeup in next loop cycle */ } @@ -670,7 +671,7 @@ static int write_modem(struct cardstate *cs) spin_unlock_irqrestore(&cs->lock, flags); if (ret) { - err("could not submit urb (error %d)\n", -ret); + dev_err(cs->dev, "could not submit urb (error %d)\n", -ret); ucs->busy = 0; } diff --git a/drivers/isdn/hardware/Makefile b/drivers/isdn/hardware/Makefile index 11c8a183948c..a5d8fce4c4c4 100644 --- a/drivers/isdn/hardware/Makefile +++ b/drivers/isdn/hardware/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_CAPI_AVM) += avm/ obj-$(CONFIG_CAPI_EICON) += eicon/ +obj-$(CONFIG_MISDN) += mISDN/ diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig new file mode 100644 index 000000000000..14793480c453 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/Kconfig @@ -0,0 +1,25 @@ +# +# Hardware for mISDN +# +comment "mISDN hardware drivers" + +config MISDN_HFCPCI + tristate "Support for HFC PCI cards" + depends on MISDN + depends on PCI + help + Enable support for cards with Cologne Chip AG's + HFC PCI chip. + +config MISDN_HFCMULTI + tristate "Support for HFC multiport cards (HFC-4S/8S/E1)" + depends on PCI + depends on MISDN + help + Enable support for cards with Cologne Chip AG's HFC multiport + chip. There are three types of chips that are quite similar, + but the interface is different: + * HFC-4S (4 S/T interfaces on one chip) + * HFC-8S (8 S/T interfaces on one chip) + * HFC-E1 (E1 interface for 2Mbit ISDN) + diff --git a/drivers/isdn/hardware/mISDN/Makefile b/drivers/isdn/hardware/mISDN/Makefile new file mode 100644 index 000000000000..1e7ca5332ad7 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the modular ISDN hardware drivers +# +# + +obj-$(CONFIG_MISDN_HFCPCI) += hfcpci.o +obj-$(CONFIG_MISDN_HFCMULTI) += hfcmulti.o diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h new file mode 100644 index 000000000000..a33d87afc843 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfc_multi.h @@ -0,0 +1,1204 @@ +/* + * see notice in hfc_multi.c + */ + +extern void ztdummy_extern_interrupt(void); +extern void ztdummy_register_interrupt(void); +extern int ztdummy_unregister_interrupt(void); + +#define DEBUG_HFCMULTI_FIFO 0x00010000 +#define DEBUG_HFCMULTI_CRC 0x00020000 +#define DEBUG_HFCMULTI_INIT 0x00040000 +#define DEBUG_HFCMULTI_PLXSD 0x00080000 +#define DEBUG_HFCMULTI_MODE 0x00100000 +#define DEBUG_HFCMULTI_MSG 0x00200000 +#define DEBUG_HFCMULTI_STATE 0x00400000 +#define DEBUG_HFCMULTI_SYNC 0x01000000 +#define DEBUG_HFCMULTI_DTMF 0x02000000 +#define DEBUG_HFCMULTI_LOCK 0x80000000 + +#define PCI_ENA_REGIO 0x01 +#define PCI_ENA_MEMIO 0x02 + +/* + * NOTE: some registers are assigned multiple times due to different modes + * also registers are assigned differen for HFC-4s/8s and HFC-E1 + */ + +/* +#define MAX_FRAME_SIZE 2048 +*/ + +struct hfc_chan { + struct dchannel *dch; /* link if channel is a D-channel */ + struct bchannel *bch; /* link if channel is a B-channel */ + int port; /* the interface port this */ + /* channel is associated with */ + int nt_timer; /* -1 if off, 0 if elapsed, >0 if running */ + int los, ais, slip_tx, slip_rx, rdi; /* current alarms */ + int jitter; + u_long cfg; /* port configuration */ + int sync; /* sync state (used by E1) */ + u_int protocol; /* current protocol */ + int slot_tx; /* current pcm slot */ + int bank_tx; /* current pcm bank */ + int slot_rx; + int bank_rx; + int conf; /* conference setting of TX slot */ + int txpending; /* if there is currently data in */ + /* the FIFO 0=no, 1=yes, 2=splloop */ + int rx_off; /* set to turn fifo receive off */ + int coeff_count; /* curren coeff block */ + s32 *coeff; /* memory pointer to 8 coeff blocks */ +}; + + +struct hfcm_hw { + u_char r_ctrl; + u_char r_irq_ctrl; + u_char r_cirm; + u_char r_ram_sz; + u_char r_pcm_md0; + u_char r_irqmsk_misc; + u_char r_dtmf; + u_char r_st_sync; + u_char r_sci_msk; + u_char r_tx0, r_tx1; + u_char a_st_ctrl0[8]; + timer_t timer; +}; + + +/* for each stack these flags are used (cfg) */ +#define HFC_CFG_NONCAP_TX 1 /* S/T TX interface has less capacity */ +#define HFC_CFG_DIS_ECHANNEL 2 /* disable E-channel processing */ +#define HFC_CFG_REG_ECHANNEL 3 /* register E-channel */ +#define HFC_CFG_OPTICAL 4 /* the E1 interface is optical */ +#define HFC_CFG_REPORT_LOS 5 /* the card should report loss of signal */ +#define HFC_CFG_REPORT_AIS 6 /* the card should report alarm ind. sign. */ +#define HFC_CFG_REPORT_SLIP 7 /* the card should report bit slips */ +#define HFC_CFG_REPORT_RDI 8 /* the card should report remote alarm */ +#define HFC_CFG_DTMF 9 /* enable DTMF-detection */ +#define HFC_CFG_CRC4 10 /* disable CRC-4 Multiframe mode, */ + /* use double frame instead. */ + +#define HFC_CHIP_EXRAM_128 0 /* external ram 128k */ +#define HFC_CHIP_EXRAM_512 1 /* external ram 256k */ +#define HFC_CHIP_REVISION0 2 /* old fifo handling */ +#define HFC_CHIP_PCM_SLAVE 3 /* PCM is slave */ +#define HFC_CHIP_PCM_MASTER 4 /* PCM is master */ +#define HFC_CHIP_RX_SYNC 5 /* disable pll sync for pcm */ +#define HFC_CHIP_DTMF 6 /* DTMF decoding is enabled */ +#define HFC_CHIP_ULAW 7 /* ULAW mode */ +#define HFC_CHIP_CLOCK2 8 /* double clock mode */ +#define HFC_CHIP_E1CLOCK_GET 9 /* always get clock from E1 interface */ +#define HFC_CHIP_E1CLOCK_PUT 10 /* always put clock from E1 interface */ +#define HFC_CHIP_WATCHDOG 11 /* whether we should send signals */ + /* to the watchdog */ +#define HFC_CHIP_B410P 12 /* whether we have a b410p with echocan in */ + /* hw */ +#define HFC_CHIP_PLXSD 13 /* whether we have a Speech-Design PLX */ + +#define HFC_IO_MODE_PCIMEM 0x00 /* normal memory mapped IO */ +#define HFC_IO_MODE_REGIO 0x01 /* PCI io access */ +#define HFC_IO_MODE_PLXSD 0x02 /* access HFC via PLX9030 */ + +/* table entry in the PCI devices list */ +struct hm_map { + char *vendor_name; + char *card_name; + int type; + int ports; + int clock2; + int leds; + int opticalsupport; + int dip_type; + int io_mode; +}; + +struct hfc_multi { + struct list_head list; + struct hm_map *mtyp; + int id; + int pcm; /* id of pcm bus */ + int type; + int ports; + + u_int irq; /* irq used by card */ + u_int irqcnt; + struct pci_dev *pci_dev; + int io_mode; /* selects mode */ +#ifdef HFC_REGISTER_DEBUG + void (*HFC_outb)(struct hfc_multi *hc, u_char reg, + u_char val, const char *function, int line); + void (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg, + u_char val, const char *function, int line); + u_char (*HFC_inb)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + u_char (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + u_short (*HFC_inw)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + u_short (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + void (*HFC_wait)(struct hfc_multi *hc, + const char *function, int line); + void (*HFC_wait_nodebug)(struct hfc_multi *hc, + const char *function, int line); +#else + void (*HFC_outb)(struct hfc_multi *hc, u_char reg, + u_char val); + void (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg, + u_char val); + u_char (*HFC_inb)(struct hfc_multi *hc, u_char reg); + u_char (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg); + u_short (*HFC_inw)(struct hfc_multi *hc, u_char reg); + u_short (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg); + void (*HFC_wait)(struct hfc_multi *hc); + void (*HFC_wait_nodebug)(struct hfc_multi *hc); +#endif + void (*read_fifo)(struct hfc_multi *hc, u_char *data, + int len); + void (*write_fifo)(struct hfc_multi *hc, u_char *data, + int len); + u_long pci_origmembase, plx_origmembase, dsp_origmembase; + u_char *pci_membase; /* PCI memory (MUST BE BYTE POINTER) */ + u_char *plx_membase; /* PLX memory */ + u_char *dsp_membase; /* DSP on PLX */ + u_long pci_iobase; /* PCI IO */ + struct hfcm_hw hw; /* remember data of write-only-registers */ + + u_long chip; /* chip configuration */ + int masterclk; /* port that provides master clock -1=off */ + int dtmf; /* flag that dtmf is currently in process */ + int Flen; /* F-buffer size */ + int Zlen; /* Z-buffer size (must be int for calculation)*/ + int max_trans; /* maximum transparent fifo fill */ + int Zmin; /* Z-buffer offset */ + int DTMFbase; /* base address of DTMF coefficients */ + + u_int slots; /* number of PCM slots */ + u_int leds; /* type of leds */ + u_int ledcount; /* used to animate leds */ + u_long ledstate; /* save last state of leds */ + int opticalsupport; /* has the e1 board */ + /* an optical Interface */ + int dslot; /* channel # of d-channel (E1) default 16 */ + + u_long wdcount; /* every 500 ms we need to */ + /* send the watchdog a signal */ + u_char wdbyte; /* watchdog toggle byte */ + u_int activity[8]; /* if there is any action on this */ + /* port (will be cleared after */ + /* showing led-states) */ + int e1_state; /* keep track of last state */ + int e1_getclock; /* if sync is retrieved from interface */ + int syncronized; /* keep track of existing sync interface */ + int e1_resync; /* resync jobs */ + + spinlock_t lock; /* the lock */ + + /* + * the channel index is counted from 0, regardless where the channel + * is located on the hfc-channel. + * the bch->channel is equvalent to the hfc-channel + */ + struct hfc_chan chan[32]; + u_char created[8]; /* what port is created */ + signed char slot_owner[256]; /* owner channel of slot */ +}; + +/* PLX GPIOs */ +#define PLX_GPIO4_DIR_BIT 13 +#define PLX_GPIO4_BIT 14 +#define PLX_GPIO5_DIR_BIT 16 +#define PLX_GPIO5_BIT 17 +#define PLX_GPIO6_DIR_BIT 19 +#define PLX_GPIO6_BIT 20 +#define PLX_GPIO7_DIR_BIT 22 +#define PLX_GPIO7_BIT 23 +#define PLX_GPIO8_DIR_BIT 25 +#define PLX_GPIO8_BIT 26 + +#define PLX_GPIO4 (1 << PLX_GPIO4_BIT) +#define PLX_GPIO5 (1 << PLX_GPIO5_BIT) +#define PLX_GPIO6 (1 << PLX_GPIO6_BIT) +#define PLX_GPIO7 (1 << PLX_GPIO7_BIT) +#define PLX_GPIO8 (1 << PLX_GPIO8_BIT) + +#define PLX_GPIO4_DIR (1 << PLX_GPIO4_DIR_BIT) +#define PLX_GPIO5_DIR (1 << PLX_GPIO5_DIR_BIT) +#define PLX_GPIO6_DIR (1 << PLX_GPIO6_DIR_BIT) +#define PLX_GPIO7_DIR (1 << PLX_GPIO7_DIR_BIT) +#define PLX_GPIO8_DIR (1 << PLX_GPIO8_DIR_BIT) + +#define PLX_TERM_ON PLX_GPIO7 +#define PLX_SLAVE_EN_N PLX_GPIO5 +#define PLX_MASTER_EN PLX_GPIO6 +#define PLX_SYNC_O_EN PLX_GPIO4 +#define PLX_DSP_RES_N PLX_GPIO8 +/* GPIO4..8 Enable & Set to OUT, SLAVE_EN_N = 1 */ +#define PLX_GPIOC_INIT (PLX_GPIO4_DIR | PLX_GPIO5_DIR | PLX_GPIO6_DIR \ + | PLX_GPIO7_DIR | PLX_GPIO8_DIR | PLX_SLAVE_EN_N) + +/* PLX Interrupt Control/STATUS */ +#define PLX_INTCSR_LINTI1_ENABLE 0x01 +#define PLX_INTCSR_LINTI1_STATUS 0x04 +#define PLX_INTCSR_LINTI2_ENABLE 0x08 +#define PLX_INTCSR_LINTI2_STATUS 0x20 +#define PLX_INTCSR_PCIINT_ENABLE 0x40 + +/* PLX Registers */ +#define PLX_INTCSR 0x4c +#define PLX_CNTRL 0x50 +#define PLX_GPIOC 0x54 + + +/* + * REGISTER SETTING FOR HFC-4S/8S AND HFC-E1 + */ + +/* write only registers */ +#define R_CIRM 0x00 +#define R_CTRL 0x01 +#define R_BRG_PCM_CFG 0x02 +#define R_RAM_ADDR0 0x08 +#define R_RAM_ADDR1 0x09 +#define R_RAM_ADDR2 0x0A +#define R_FIRST_FIFO 0x0B +#define R_RAM_SZ 0x0C +#define R_FIFO_MD 0x0D +#define R_INC_RES_FIFO 0x0E +#define R_FSM_IDX 0x0F +#define R_FIFO 0x0F +#define R_SLOT 0x10 +#define R_IRQMSK_MISC 0x11 +#define R_SCI_MSK 0x12 +#define R_IRQ_CTRL 0x13 +#define R_PCM_MD0 0x14 +#define R_PCM_MD1 0x15 +#define R_PCM_MD2 0x15 +#define R_SH0H 0x15 +#define R_SH1H 0x15 +#define R_SH0L 0x15 +#define R_SH1L 0x15 +#define R_SL_SEL0 0x15 +#define R_SL_SEL1 0x15 +#define R_SL_SEL2 0x15 +#define R_SL_SEL3 0x15 +#define R_SL_SEL4 0x15 +#define R_SL_SEL5 0x15 +#define R_SL_SEL6 0x15 +#define R_SL_SEL7 0x15 +#define R_ST_SEL 0x16 +#define R_ST_SYNC 0x17 +#define R_CONF_EN 0x18 +#define R_TI_WD 0x1A +#define R_BERT_WD_MD 0x1B +#define R_DTMF 0x1C +#define R_DTMF_N 0x1D +#define R_E1_WR_STA 0x20 +#define R_E1_RD_STA 0x20 +#define R_LOS0 0x22 +#define R_LOS1 0x23 +#define R_RX0 0x24 +#define R_RX_FR0 0x25 +#define R_RX_FR1 0x26 +#define R_TX0 0x28 +#define R_TX1 0x29 +#define R_TX_FR0 0x2C + +#define R_TX_FR1 0x2D +#define R_TX_FR2 0x2E +#define R_JATT_ATT 0x2F /* undocumented */ +#define A_ST_RD_STATE 0x30 +#define A_ST_WR_STATE 0x30 +#define R_RX_OFF 0x30 +#define A_ST_CTRL0 0x31 +#define R_SYNC_OUT 0x31 +#define A_ST_CTRL1 0x32 +#define A_ST_CTRL2 0x33 +#define A_ST_SQ_WR 0x34 +#define R_TX_OFF 0x34 +#define R_SYNC_CTRL 0x35 +#define A_ST_CLK_DLY 0x37 +#define R_PWM0 0x38 +#define R_PWM1 0x39 +#define A_ST_B1_TX 0x3C +#define A_ST_B2_TX 0x3D +#define A_ST_D_TX 0x3E +#define R_GPIO_OUT0 0x40 +#define R_GPIO_OUT1 0x41 +#define R_GPIO_EN0 0x42 +#define R_GPIO_EN1 0x43 +#define R_GPIO_SEL 0x44 +#define R_BRG_CTRL 0x45 +#define R_PWM_MD 0x46 +#define R_BRG_MD 0x47 +#define R_BRG_TIM0 0x48 +#define R_BRG_TIM1 0x49 +#define R_BRG_TIM2 0x4A +#define R_BRG_TIM3 0x4B +#define R_BRG_TIM_SEL01 0x4C +#define R_BRG_TIM_SEL23 0x4D +#define R_BRG_TIM_SEL45 0x4E +#define R_BRG_TIM_SEL67 0x4F +#define A_SL_CFG 0xD0 +#define A_CONF 0xD1 +#define A_CH_MSK 0xF4 +#define A_CON_HDLC 0xFA +#define A_SUBCH_CFG 0xFB +#define A_CHANNEL 0xFC +#define A_FIFO_SEQ 0xFD +#define A_IRQ_MSK 0xFF + +/* read only registers */ +#define A_Z12 0x04 +#define A_Z1L 0x04 +#define A_Z1 0x04 +#define A_Z1H 0x05 +#define A_Z2L 0x06 +#define A_Z2 0x06 +#define A_Z2H 0x07 +#define A_F1 0x0C +#define A_F12 0x0C +#define A_F2 0x0D +#define R_IRQ_OVIEW 0x10 +#define R_IRQ_MISC 0x11 +#define R_IRQ_STATECH 0x12 +#define R_CONF_OFLOW 0x14 +#define R_RAM_USE 0x15 +#define R_CHIP_ID 0x16 +#define R_BERT_STA 0x17 +#define R_F0_CNTL 0x18 +#define R_F0_CNTH 0x19 +#define R_BERT_EC 0x1A +#define R_BERT_ECL 0x1A +#define R_BERT_ECH 0x1B +#define R_STATUS 0x1C +#define R_CHIP_RV 0x1F +#define R_STATE 0x20 +#define R_SYNC_STA 0x24 +#define R_RX_SL0_0 0x25 +#define R_RX_SL0_1 0x26 +#define R_RX_SL0_2 0x27 +#define R_JATT_DIR 0x2b /* undocumented */ +#define R_SLIP 0x2c +#define A_ST_RD_STA 0x30 +#define R_FAS_EC 0x30 +#define R_FAS_ECL 0x30 +#define R_FAS_ECH 0x31 +#define R_VIO_EC 0x32 +#define R_VIO_ECL 0x32 +#define R_VIO_ECH 0x33 +#define A_ST_SQ_RD 0x34 +#define R_CRC_EC 0x34 +#define R_CRC_ECL 0x34 +#define R_CRC_ECH 0x35 +#define R_E_EC 0x36 +#define R_E_ECL 0x36 +#define R_E_ECH 0x37 +#define R_SA6_SA13_EC 0x38 +#define R_SA6_SA13_ECL 0x38 +#define R_SA6_SA13_ECH 0x39 +#define R_SA6_SA23_EC 0x3A +#define R_SA6_SA23_ECL 0x3A +#define R_SA6_SA23_ECH 0x3B +#define A_ST_B1_RX 0x3C +#define A_ST_B2_RX 0x3D +#define A_ST_D_RX 0x3E +#define A_ST_E_RX 0x3F +#define R_GPIO_IN0 0x40 +#define R_GPIO_IN1 0x41 +#define R_GPI_IN0 0x44 +#define R_GPI_IN1 0x45 +#define R_GPI_IN2 0x46 +#define R_GPI_IN3 0x47 +#define R_INT_DATA 0x88 +#define R_IRQ_FIFO_BL0 0xC8 +#define R_IRQ_FIFO_BL1 0xC9 +#define R_IRQ_FIFO_BL2 0xCA +#define R_IRQ_FIFO_BL3 0xCB +#define R_IRQ_FIFO_BL4 0xCC +#define R_IRQ_FIFO_BL5 0xCD +#define R_IRQ_FIFO_BL6 0xCE +#define R_IRQ_FIFO_BL7 0xCF + +/* read and write registers */ +#define A_FIFO_DATA0 0x80 +#define A_FIFO_DATA1 0x80 +#define A_FIFO_DATA2 0x80 +#define A_FIFO_DATA0_NOINC 0x84 +#define A_FIFO_DATA1_NOINC 0x84 +#define A_FIFO_DATA2_NOINC 0x84 +#define R_RAM_DATA 0xC0 + + +/* + * BIT SETTING FOR HFC-4S/8S AND HFC-E1 + */ + +/* chapter 2: universal bus interface */ +/* R_CIRM */ +#define V_IRQ_SEL 0x01 +#define V_SRES 0x08 +#define V_HFCRES 0x10 +#define V_PCMRES 0x20 +#define V_STRES 0x40 +#define V_ETRES 0x40 +#define V_RLD_EPR 0x80 +/* R_CTRL */ +#define V_FIFO_LPRIO 0x02 +#define V_SLOW_RD 0x04 +#define V_EXT_RAM 0x08 +#define V_CLK_OFF 0x20 +#define V_ST_CLK 0x40 +/* R_RAM_ADDR0 */ +#define V_RAM_ADDR2 0x01 +#define V_ADDR_RES 0x40 +#define V_ADDR_INC 0x80 +/* R_RAM_SZ */ +#define V_RAM_SZ 0x01 +#define V_PWM0_16KHZ 0x10 +#define V_PWM1_16KHZ 0x20 +#define V_FZ_MD 0x80 +/* R_CHIP_ID */ +#define V_PNP_IRQ 0x01 +#define V_CHIP_ID 0x10 + +/* chapter 3: data flow */ +/* R_FIRST_FIFO */ +#define V_FIRST_FIRO_DIR 0x01 +#define V_FIRST_FIFO_NUM 0x02 +/* R_FIFO_MD */ +#define V_FIFO_MD 0x01 +#define V_CSM_MD 0x04 +#define V_FSM_MD 0x08 +#define V_FIFO_SZ 0x10 +/* R_FIFO */ +#define V_FIFO_DIR 0x01 +#define V_FIFO_NUM 0x02 +#define V_REV 0x80 +/* R_SLOT */ +#define V_SL_DIR 0x01 +#define V_SL_NUM 0x02 +/* A_SL_CFG */ +#define V_CH_DIR 0x01 +#define V_CH_SEL 0x02 +#define V_ROUTING 0x40 +/* A_CON_HDLC */ +#define V_IFF 0x01 +#define V_HDLC_TRP 0x02 +#define V_TRP_IRQ 0x04 +#define V_DATA_FLOW 0x20 +/* A_SUBCH_CFG */ +#define V_BIT_CNT 0x01 +#define V_START_BIT 0x08 +#define V_LOOP_FIFO 0x40 +#define V_INV_DATA 0x80 +/* A_CHANNEL */ +#define V_CH_DIR0 0x01 +#define V_CH_NUM0 0x02 +/* A_FIFO_SEQ */ +#define V_NEXT_FIFO_DIR 0x01 +#define V_NEXT_FIFO_NUM 0x02 +#define V_SEQ_END 0x40 + +/* chapter 4: FIFO handling and HDLC controller */ +/* R_INC_RES_FIFO */ +#define V_INC_F 0x01 +#define V_RES_F 0x02 +#define V_RES_LOST 0x04 + +/* chapter 5: S/T interface */ +/* R_SCI_MSK */ +#define V_SCI_MSK_ST0 0x01 +#define V_SCI_MSK_ST1 0x02 +#define V_SCI_MSK_ST2 0x04 +#define V_SCI_MSK_ST3 0x08 +#define V_SCI_MSK_ST4 0x10 +#define V_SCI_MSK_ST5 0x20 +#define V_SCI_MSK_ST6 0x40 +#define V_SCI_MSK_ST7 0x80 +/* R_ST_SEL */ +#define V_ST_SEL 0x01 +#define V_MULT_ST 0x08 +/* R_ST_SYNC */ +#define V_SYNC_SEL 0x01 +#define V_AUTO_SYNC 0x08 +/* A_ST_WR_STA */ +#define V_ST_SET_STA 0x01 +#define V_ST_LD_STA 0x10 +#define V_ST_ACT 0x20 +#define V_SET_G2_G3 0x80 +/* A_ST_CTRL0 */ +#define V_B1_EN 0x01 +#define V_B2_EN 0x02 +#define V_ST_MD 0x04 +#define V_D_PRIO 0x08 +#define V_SQ_EN 0x10 +#define V_96KHZ 0x20 +#define V_TX_LI 0x40 +#define V_ST_STOP 0x80 +/* A_ST_CTRL1 */ +#define V_G2_G3_EN 0x01 +#define V_D_HI 0x04 +#define V_E_IGNO 0x08 +#define V_E_LO 0x10 +#define V_B12_SWAP 0x80 +/* A_ST_CTRL2 */ +#define V_B1_RX_EN 0x01 +#define V_B2_RX_EN 0x02 +#define V_ST_TRIS 0x40 +/* A_ST_CLK_DLY */ +#define V_ST_CK_DLY 0x01 +#define V_ST_SMPL 0x10 +/* A_ST_D_TX */ +#define V_ST_D_TX 0x40 +/* R_IRQ_STATECH */ +#define V_SCI_ST0 0x01 +#define V_SCI_ST1 0x02 +#define V_SCI_ST2 0x04 +#define V_SCI_ST3 0x08 +#define V_SCI_ST4 0x10 +#define V_SCI_ST5 0x20 +#define V_SCI_ST6 0x40 +#define V_SCI_ST7 0x80 +/* A_ST_RD_STA */ +#define V_ST_STA 0x01 +#define V_FR_SYNC_ST 0x10 +#define V_TI2_EXP 0x20 +#define V_INFO0 0x40 +#define V_G2_G3 0x80 +/* A_ST_SQ_RD */ +#define V_ST_SQ 0x01 +#define V_MF_RX_RDY 0x10 +#define V_MF_TX_RDY 0x80 +/* A_ST_D_RX */ +#define V_ST_D_RX 0x40 +/* A_ST_E_RX */ +#define V_ST_E_RX 0x40 + +/* chapter 5: E1 interface */ +/* R_E1_WR_STA */ +/* R_E1_RD_STA */ +#define V_E1_SET_STA 0x01 +#define V_E1_LD_STA 0x10 +/* R_RX0 */ +#define V_RX_CODE 0x01 +#define V_RX_FBAUD 0x04 +#define V_RX_CMI 0x08 +#define V_RX_INV_CMI 0x10 +#define V_RX_INV_CLK 0x20 +#define V_RX_INV_DATA 0x40 +#define V_AIS_ITU 0x80 +/* R_RX_FR0 */ +#define V_NO_INSYNC 0x01 +#define V_AUTO_RESYNC 0x02 +#define V_AUTO_RECO 0x04 +#define V_SWORD_COND 0x08 +#define V_SYNC_LOSS 0x10 +#define V_XCRC_SYNC 0x20 +#define V_MF_RESYNC 0x40 +#define V_RESYNC 0x80 +/* R_RX_FR1 */ +#define V_RX_MF 0x01 +#define V_RX_MF_SYNC 0x02 +#define V_RX_SL0_RAM 0x04 +#define V_ERR_SIM 0x20 +#define V_RES_NMF 0x40 +/* R_TX0 */ +#define V_TX_CODE 0x01 +#define V_TX_FBAUD 0x04 +#define V_TX_CMI_CODE 0x08 +#define V_TX_INV_CMI_CODE 0x10 +#define V_TX_INV_CLK 0x20 +#define V_TX_INV_DATA 0x40 +#define V_OUT_EN 0x80 +/* R_TX1 */ +#define V_INV_CLK 0x01 +#define V_EXCHG_DATA_LI 0x02 +#define V_AIS_OUT 0x04 +#define V_ATX 0x20 +#define V_NTRI 0x40 +#define V_AUTO_ERR_RES 0x80 +/* R_TX_FR0 */ +#define V_TRP_FAS 0x01 +#define V_TRP_NFAS 0x02 +#define V_TRP_RAL 0x04 +#define V_TRP_SA 0x08 +/* R_TX_FR1 */ +#define V_TX_FAS 0x01 +#define V_TX_NFAS 0x02 +#define V_TX_RAL 0x04 +#define V_TX_SA 0x08 +/* R_TX_FR2 */ +#define V_TX_MF 0x01 +#define V_TRP_SL0 0x02 +#define V_TX_SL0_RAM 0x04 +#define V_TX_E 0x10 +#define V_NEG_E 0x20 +#define V_XS12_ON 0x40 +#define V_XS15_ON 0x80 +/* R_RX_OFF */ +#define V_RX_SZ 0x01 +#define V_RX_INIT 0x04 +/* R_SYNC_OUT */ +#define V_SYNC_E1_RX 0x01 +#define V_IPATS0 0x20 +#define V_IPATS1 0x40 +#define V_IPATS2 0x80 +/* R_TX_OFF */ +#define V_TX_SZ 0x01 +#define V_TX_INIT 0x04 +/* R_SYNC_CTRL */ +#define V_EXT_CLK_SYNC 0x01 +#define V_SYNC_OFFS 0x02 +#define V_PCM_SYNC 0x04 +#define V_NEG_CLK 0x08 +#define V_HCLK 0x10 +/* +#define V_JATT_AUTO_DEL 0x20 +#define V_JATT_AUTO 0x40 +*/ +#define V_JATT_OFF 0x80 +/* R_STATE */ +#define V_E1_STA 0x01 +#define V_ALT_FR_RX 0x40 +#define V_ALT_FR_TX 0x80 +/* R_SYNC_STA */ +#define V_RX_STA 0x01 +#define V_FR_SYNC_E1 0x04 +#define V_SIG_LOS 0x08 +#define V_MFA_STA 0x10 +#define V_AIS 0x40 +#define V_NO_MF_SYNC 0x80 +/* R_RX_SL0_0 */ +#define V_SI_FAS 0x01 +#define V_SI_NFAS 0x02 +#define V_A 0x04 +#define V_CRC_OK 0x08 +#define V_TX_E1 0x10 +#define V_TX_E2 0x20 +#define V_RX_E1 0x40 +#define V_RX_E2 0x80 +/* R_SLIP */ +#define V_SLIP_RX 0x01 +#define V_FOSLIP_RX 0x08 +#define V_SLIP_TX 0x10 +#define V_FOSLIP_TX 0x80 + +/* chapter 6: PCM interface */ +/* R_PCM_MD0 */ +#define V_PCM_MD 0x01 +#define V_C4_POL 0x02 +#define V_F0_NEG 0x04 +#define V_F0_LEN 0x08 +#define V_PCM_ADDR 0x10 +/* R_SL_SEL0 */ +#define V_SL_SEL0 0x01 +#define V_SH_SEL0 0x80 +/* R_SL_SEL1 */ +#define V_SL_SEL1 0x01 +#define V_SH_SEL1 0x80 +/* R_SL_SEL2 */ +#define V_SL_SEL2 0x01 +#define V_SH_SEL2 0x80 +/* R_SL_SEL3 */ +#define V_SL_SEL3 0x01 +#define V_SH_SEL3 0x80 +/* R_SL_SEL4 */ +#define V_SL_SEL4 0x01 +#define V_SH_SEL4 0x80 +/* R_SL_SEL5 */ +#define V_SL_SEL5 0x01 +#define V_SH_SEL5 0x80 +/* R_SL_SEL6 */ +#define V_SL_SEL6 0x01 +#define V_SH_SEL6 0x80 +/* R_SL_SEL7 */ +#define V_SL_SEL7 0x01 +#define V_SH_SEL7 0x80 +/* R_PCM_MD1 */ +#define V_ODEC_CON 0x01 +#define V_PLL_ADJ 0x04 +#define V_PCM_DR 0x10 +#define V_PCM_LOOP 0x40 +/* R_PCM_MD2 */ +#define V_SYNC_PLL 0x02 +#define V_SYNC_SRC 0x04 +#define V_SYNC_OUT 0x08 +#define V_ICR_FR_TIME 0x40 +#define V_EN_PLL 0x80 + +/* chapter 7: pulse width modulation */ +/* R_PWM_MD */ +#define V_EXT_IRQ_EN 0x08 +#define V_PWM0_MD 0x10 +#define V_PWM1_MD 0x40 + +/* chapter 8: multiparty audio conferences */ +/* R_CONF_EN */ +#define V_CONF_EN 0x01 +#define V_ULAW 0x80 +/* A_CONF */ +#define V_CONF_NUM 0x01 +#define V_NOISE_SUPPR 0x08 +#define V_ATT_LEV 0x20 +#define V_CONF_SL 0x80 +/* R_CONF_OFLOW */ +#define V_CONF_OFLOW0 0x01 +#define V_CONF_OFLOW1 0x02 +#define V_CONF_OFLOW2 0x04 +#define V_CONF_OFLOW3 0x08 +#define V_CONF_OFLOW4 0x10 +#define V_CONF_OFLOW5 0x20 +#define V_CONF_OFLOW6 0x40 +#define V_CONF_OFLOW7 0x80 + +/* chapter 9: DTMF contoller */ +/* R_DTMF0 */ +#define V_DTMF_EN 0x01 +#define V_HARM_SEL 0x02 +#define V_DTMF_RX_CH 0x04 +#define V_DTMF_STOP 0x08 +#define V_CHBL_SEL 0x10 +#define V_RST_DTMF 0x40 +#define V_ULAW_SEL 0x80 + +/* chapter 10: BERT */ +/* R_BERT_WD_MD */ +#define V_PAT_SEQ 0x01 +#define V_BERT_ERR 0x08 +#define V_AUTO_WD_RES 0x20 +#define V_WD_RES 0x80 +/* R_BERT_STA */ +#define V_BERT_SYNC_SRC 0x01 +#define V_BERT_SYNC 0x10 +#define V_BERT_INV_DATA 0x20 + +/* chapter 11: auxiliary interface */ +/* R_BRG_PCM_CFG */ +#define V_BRG_EN 0x01 +#define V_BRG_MD 0x02 +#define V_PCM_CLK 0x20 +#define V_ADDR_WRDLY 0x40 +/* R_BRG_CTRL */ +#define V_BRG_CS 0x01 +#define V_BRG_ADDR 0x08 +#define V_BRG_CS_SRC 0x80 +/* R_BRG_MD */ +#define V_BRG_MD0 0x01 +#define V_BRG_MD1 0x02 +#define V_BRG_MD2 0x04 +#define V_BRG_MD3 0x08 +#define V_BRG_MD4 0x10 +#define V_BRG_MD5 0x20 +#define V_BRG_MD6 0x40 +#define V_BRG_MD7 0x80 +/* R_BRG_TIM0 */ +#define V_BRG_TIM0_IDLE 0x01 +#define V_BRG_TIM0_CLK 0x10 +/* R_BRG_TIM1 */ +#define V_BRG_TIM1_IDLE 0x01 +#define V_BRG_TIM1_CLK 0x10 +/* R_BRG_TIM2 */ +#define V_BRG_TIM2_IDLE 0x01 +#define V_BRG_TIM2_CLK 0x10 +/* R_BRG_TIM3 */ +#define V_BRG_TIM3_IDLE 0x01 +#define V_BRG_TIM3_CLK 0x10 +/* R_BRG_TIM_SEL01 */ +#define V_BRG_WR_SEL0 0x01 +#define V_BRG_RD_SEL0 0x04 +#define V_BRG_WR_SEL1 0x10 +#define V_BRG_RD_SEL1 0x40 +/* R_BRG_TIM_SEL23 */ +#define V_BRG_WR_SEL2 0x01 +#define V_BRG_RD_SEL2 0x04 +#define V_BRG_WR_SEL3 0x10 +#define V_BRG_RD_SEL3 0x40 +/* R_BRG_TIM_SEL45 */ +#define V_BRG_WR_SEL4 0x01 +#define V_BRG_RD_SEL4 0x04 +#define V_BRG_WR_SEL5 0x10 +#define V_BRG_RD_SEL5 0x40 +/* R_BRG_TIM_SEL67 */ +#define V_BRG_WR_SEL6 0x01 +#define V_BRG_RD_SEL6 0x04 +#define V_BRG_WR_SEL7 0x10 +#define V_BRG_RD_SEL7 0x40 + +/* chapter 12: clock, reset, interrupt, timer and watchdog */ +/* R_IRQMSK_MISC */ +#define V_STA_IRQMSK 0x01 +#define V_TI_IRQMSK 0x02 +#define V_PROC_IRQMSK 0x04 +#define V_DTMF_IRQMSK 0x08 +#define V_IRQ1S_MSK 0x10 +#define V_SA6_IRQMSK 0x20 +#define V_RX_EOMF_MSK 0x40 +#define V_TX_EOMF_MSK 0x80 +/* R_IRQ_CTRL */ +#define V_FIFO_IRQ 0x01 +#define V_GLOB_IRQ_EN 0x08 +#define V_IRQ_POL 0x10 +/* R_TI_WD */ +#define V_EV_TS 0x01 +#define V_WD_TS 0x10 +/* A_IRQ_MSK */ +#define V_IRQ 0x01 +#define V_BERT_EN 0x02 +#define V_MIX_IRQ 0x04 +/* R_IRQ_OVIEW */ +#define V_IRQ_FIFO_BL0 0x01 +#define V_IRQ_FIFO_BL1 0x02 +#define V_IRQ_FIFO_BL2 0x04 +#define V_IRQ_FIFO_BL3 0x08 +#define V_IRQ_FIFO_BL4 0x10 +#define V_IRQ_FIFO_BL5 0x20 +#define V_IRQ_FIFO_BL6 0x40 +#define V_IRQ_FIFO_BL7 0x80 +/* R_IRQ_MISC */ +#define V_STA_IRQ 0x01 +#define V_TI_IRQ 0x02 +#define V_IRQ_PROC 0x04 +#define V_DTMF_IRQ 0x08 +#define V_IRQ1S 0x10 +#define V_SA6_IRQ 0x20 +#define V_RX_EOMF 0x40 +#define V_TX_EOMF 0x80 +/* R_STATUS */ +#define V_BUSY 0x01 +#define V_PROC 0x02 +#define V_DTMF_STA 0x04 +#define V_LOST_STA 0x08 +#define V_SYNC_IN 0x10 +#define V_EXT_IRQSTA 0x20 +#define V_MISC_IRQSTA 0x40 +#define V_FR_IRQSTA 0x80 +/* R_IRQ_FIFO_BL0 */ +#define V_IRQ_FIFO0_TX 0x01 +#define V_IRQ_FIFO0_RX 0x02 +#define V_IRQ_FIFO1_TX 0x04 +#define V_IRQ_FIFO1_RX 0x08 +#define V_IRQ_FIFO2_TX 0x10 +#define V_IRQ_FIFO2_RX 0x20 +#define V_IRQ_FIFO3_TX 0x40 +#define V_IRQ_FIFO3_RX 0x80 +/* R_IRQ_FIFO_BL1 */ +#define V_IRQ_FIFO4_TX 0x01 +#define V_IRQ_FIFO4_RX 0x02 +#define V_IRQ_FIFO5_TX 0x04 +#define V_IRQ_FIFO5_RX 0x08 +#define V_IRQ_FIFO6_TX 0x10 +#define V_IRQ_FIFO6_RX 0x20 +#define V_IRQ_FIFO7_TX 0x40 +#define V_IRQ_FIFO7_RX 0x80 +/* R_IRQ_FIFO_BL2 */ +#define V_IRQ_FIFO8_TX 0x01 +#define V_IRQ_FIFO8_RX 0x02 +#define V_IRQ_FIFO9_TX 0x04 +#define V_IRQ_FIFO9_RX 0x08 +#define V_IRQ_FIFO10_TX 0x10 +#define V_IRQ_FIFO10_RX 0x20 +#define V_IRQ_FIFO11_TX 0x40 +#define V_IRQ_FIFO11_RX 0x80 +/* R_IRQ_FIFO_BL3 */ +#define V_IRQ_FIFO12_TX 0x01 +#define V_IRQ_FIFO12_RX 0x02 +#define V_IRQ_FIFO13_TX 0x04 +#define V_IRQ_FIFO13_RX 0x08 +#define V_IRQ_FIFO14_TX 0x10 +#define V_IRQ_FIFO14_RX 0x20 +#define V_IRQ_FIFO15_TX 0x40 +#define V_IRQ_FIFO15_RX 0x80 +/* R_IRQ_FIFO_BL4 */ +#define V_IRQ_FIFO16_TX 0x01 +#define V_IRQ_FIFO16_RX 0x02 +#define V_IRQ_FIFO17_TX 0x04 +#define V_IRQ_FIFO17_RX 0x08 +#define V_IRQ_FIFO18_TX 0x10 +#define V_IRQ_FIFO18_RX 0x20 +#define V_IRQ_FIFO19_TX 0x40 +#define V_IRQ_FIFO19_RX 0x80 +/* R_IRQ_FIFO_BL5 */ +#define V_IRQ_FIFO20_TX 0x01 +#define V_IRQ_FIFO20_RX 0x02 +#define V_IRQ_FIFO21_TX 0x04 +#define V_IRQ_FIFO21_RX 0x08 +#define V_IRQ_FIFO22_TX 0x10 +#define V_IRQ_FIFO22_RX 0x20 +#define V_IRQ_FIFO23_TX 0x40 +#define V_IRQ_FIFO23_RX 0x80 +/* R_IRQ_FIFO_BL6 */ +#define V_IRQ_FIFO24_TX 0x01 +#define V_IRQ_FIFO24_RX 0x02 +#define V_IRQ_FIFO25_TX 0x04 +#define V_IRQ_FIFO25_RX 0x08 +#define V_IRQ_FIFO26_TX 0x10 +#define V_IRQ_FIFO26_RX 0x20 +#define V_IRQ_FIFO27_TX 0x40 +#define V_IRQ_FIFO27_RX 0x80 +/* R_IRQ_FIFO_BL7 */ +#define V_IRQ_FIFO28_TX 0x01 +#define V_IRQ_FIFO28_RX 0x02 +#define V_IRQ_FIFO29_TX 0x04 +#define V_IRQ_FIFO29_RX 0x08 +#define V_IRQ_FIFO30_TX 0x10 +#define V_IRQ_FIFO30_RX 0x20 +#define V_IRQ_FIFO31_TX 0x40 +#define V_IRQ_FIFO31_RX 0x80 + +/* chapter 13: general purpose I/O pins (GPIO) and input pins (GPI) */ +/* R_GPIO_OUT0 */ +#define V_GPIO_OUT0 0x01 +#define V_GPIO_OUT1 0x02 +#define V_GPIO_OUT2 0x04 +#define V_GPIO_OUT3 0x08 +#define V_GPIO_OUT4 0x10 +#define V_GPIO_OUT5 0x20 +#define V_GPIO_OUT6 0x40 +#define V_GPIO_OUT7 0x80 +/* R_GPIO_OUT1 */ +#define V_GPIO_OUT8 0x01 +#define V_GPIO_OUT9 0x02 +#define V_GPIO_OUT10 0x04 +#define V_GPIO_OUT11 0x08 +#define V_GPIO_OUT12 0x10 +#define V_GPIO_OUT13 0x20 +#define V_GPIO_OUT14 0x40 +#define V_GPIO_OUT15 0x80 +/* R_GPIO_EN0 */ +#define V_GPIO_EN0 0x01 +#define V_GPIO_EN1 0x02 +#define V_GPIO_EN2 0x04 +#define V_GPIO_EN3 0x08 +#define V_GPIO_EN4 0x10 +#define V_GPIO_EN5 0x20 +#define V_GPIO_EN6 0x40 +#define V_GPIO_EN7 0x80 +/* R_GPIO_EN1 */ +#define V_GPIO_EN8 0x01 +#define V_GPIO_EN9 0x02 +#define V_GPIO_EN10 0x04 +#define V_GPIO_EN11 0x08 +#define V_GPIO_EN12 0x10 +#define V_GPIO_EN13 0x20 +#define V_GPIO_EN14 0x40 +#define V_GPIO_EN15 0x80 +/* R_GPIO_SEL */ +#define V_GPIO_SEL0 0x01 +#define V_GPIO_SEL1 0x02 +#define V_GPIO_SEL2 0x04 +#define V_GPIO_SEL3 0x08 +#define V_GPIO_SEL4 0x10 +#define V_GPIO_SEL5 0x20 +#define V_GPIO_SEL6 0x40 +#define V_GPIO_SEL7 0x80 +/* R_GPIO_IN0 */ +#define V_GPIO_IN0 0x01 +#define V_GPIO_IN1 0x02 +#define V_GPIO_IN2 0x04 +#define V_GPIO_IN3 0x08 +#define V_GPIO_IN4 0x10 +#define V_GPIO_IN5 0x20 +#define V_GPIO_IN6 0x40 +#define V_GPIO_IN7 0x80 +/* R_GPIO_IN1 */ +#define V_GPIO_IN8 0x01 +#define V_GPIO_IN9 0x02 +#define V_GPIO_IN10 0x04 +#define V_GPIO_IN11 0x08 +#define V_GPIO_IN12 0x10 +#define V_GPIO_IN13 0x20 +#define V_GPIO_IN14 0x40 +#define V_GPIO_IN15 0x80 +/* R_GPI_IN0 */ +#define V_GPI_IN0 0x01 +#define V_GPI_IN1 0x02 +#define V_GPI_IN2 0x04 +#define V_GPI_IN3 0x08 +#define V_GPI_IN4 0x10 +#define V_GPI_IN5 0x20 +#define V_GPI_IN6 0x40 +#define V_GPI_IN7 0x80 +/* R_GPI_IN1 */ +#define V_GPI_IN8 0x01 +#define V_GPI_IN9 0x02 +#define V_GPI_IN10 0x04 +#define V_GPI_IN11 0x08 +#define V_GPI_IN12 0x10 +#define V_GPI_IN13 0x20 +#define V_GPI_IN14 0x40 +#define V_GPI_IN15 0x80 +/* R_GPI_IN2 */ +#define V_GPI_IN16 0x01 +#define V_GPI_IN17 0x02 +#define V_GPI_IN18 0x04 +#define V_GPI_IN19 0x08 +#define V_GPI_IN20 0x10 +#define V_GPI_IN21 0x20 +#define V_GPI_IN22 0x40 +#define V_GPI_IN23 0x80 +/* R_GPI_IN3 */ +#define V_GPI_IN24 0x01 +#define V_GPI_IN25 0x02 +#define V_GPI_IN26 0x04 +#define V_GPI_IN27 0x08 +#define V_GPI_IN28 0x10 +#define V_GPI_IN29 0x20 +#define V_GPI_IN30 0x40 +#define V_GPI_IN31 0x80 + +/* map of all registers, used for debugging */ + +#ifdef HFC_REGISTER_DEBUG +struct hfc_register_names { + char *name; + u_char reg; +} hfc_register_names[] = { + /* write registers */ + {"R_CIRM", 0x00}, + {"R_CTRL", 0x01}, + {"R_BRG_PCM_CFG ", 0x02}, + {"R_RAM_ADDR0", 0x08}, + {"R_RAM_ADDR1", 0x09}, + {"R_RAM_ADDR2", 0x0A}, + {"R_FIRST_FIFO", 0x0B}, + {"R_RAM_SZ", 0x0C}, + {"R_FIFO_MD", 0x0D}, + {"R_INC_RES_FIFO", 0x0E}, + {"R_FIFO / R_FSM_IDX", 0x0F}, + {"R_SLOT", 0x10}, + {"R_IRQMSK_MISC", 0x11}, + {"R_SCI_MSK", 0x12}, + {"R_IRQ_CTRL", 0x13}, + {"R_PCM_MD0", 0x14}, + {"R_0x15", 0x15}, + {"R_ST_SEL", 0x16}, + {"R_ST_SYNC", 0x17}, + {"R_CONF_EN", 0x18}, + {"R_TI_WD", 0x1A}, + {"R_BERT_WD_MD", 0x1B}, + {"R_DTMF", 0x1C}, + {"R_DTMF_N", 0x1D}, + {"R_E1_XX_STA", 0x20}, + {"R_LOS0", 0x22}, + {"R_LOS1", 0x23}, + {"R_RX0", 0x24}, + {"R_RX_FR0", 0x25}, + {"R_RX_FR1", 0x26}, + {"R_TX0", 0x28}, + {"R_TX1", 0x29}, + {"R_TX_FR0", 0x2C}, + {"R_TX_FR1", 0x2D}, + {"R_TX_FR2", 0x2E}, + {"R_JATT_ATT", 0x2F}, + {"A_ST_xx_STA/R_RX_OFF", 0x30}, + {"A_ST_CTRL0/R_SYNC_OUT", 0x31}, + {"A_ST_CTRL1", 0x32}, + {"A_ST_CTRL2", 0x33}, + {"A_ST_SQ_WR", 0x34}, + {"R_TX_OFF", 0x34}, + {"R_SYNC_CTRL", 0x35}, + {"A_ST_CLK_DLY", 0x37}, + {"R_PWM0", 0x38}, + {"R_PWM1", 0x39}, + {"A_ST_B1_TX", 0x3C}, + {"A_ST_B2_TX", 0x3D}, + {"A_ST_D_TX", 0x3E}, + {"R_GPIO_OUT0", 0x40}, + {"R_GPIO_OUT1", 0x41}, + {"R_GPIO_EN0", 0x42}, + {"R_GPIO_EN1", 0x43}, + {"R_GPIO_SEL", 0x44}, + {"R_BRG_CTRL", 0x45}, + {"R_PWM_MD", 0x46}, + {"R_BRG_MD", 0x47}, + {"R_BRG_TIM0", 0x48}, + {"R_BRG_TIM1", 0x49}, + {"R_BRG_TIM2", 0x4A}, + {"R_BRG_TIM3", 0x4B}, + {"R_BRG_TIM_SEL01", 0x4C}, + {"R_BRG_TIM_SEL23", 0x4D}, + {"R_BRG_TIM_SEL45", 0x4E}, + {"R_BRG_TIM_SEL67", 0x4F}, + {"A_FIFO_DATA0-2", 0x80}, + {"A_FIFO_DATA0-2_NOINC", 0x84}, + {"R_RAM_DATA", 0xC0}, + {"A_SL_CFG", 0xD0}, + {"A_CONF", 0xD1}, + {"A_CH_MSK", 0xF4}, + {"A_CON_HDLC", 0xFA}, + {"A_SUBCH_CFG", 0xFB}, + {"A_CHANNEL", 0xFC}, + {"A_FIFO_SEQ", 0xFD}, + {"A_IRQ_MSK", 0xFF}, + {NULL, 0}, + + /* read registers */ + {"A_Z1", 0x04}, + {"A_Z1H", 0x05}, + {"A_Z2", 0x06}, + {"A_Z2H", 0x07}, + {"A_F1", 0x0C}, + {"A_F2", 0x0D}, + {"R_IRQ_OVIEW", 0x10}, + {"R_IRQ_MISC", 0x11}, + {"R_IRQ_STATECH", 0x12}, + {"R_CONF_OFLOW", 0x14}, + {"R_RAM_USE", 0x15}, + {"R_CHIP_ID", 0x16}, + {"R_BERT_STA", 0x17}, + {"R_F0_CNTL", 0x18}, + {"R_F0_CNTH", 0x19}, + {"R_BERT_ECL", 0x1A}, + {"R_BERT_ECH", 0x1B}, + {"R_STATUS", 0x1C}, + {"R_CHIP_RV", 0x1F}, + {"R_STATE", 0x20}, + {"R_SYNC_STA", 0x24}, + {"R_RX_SL0_0", 0x25}, + {"R_RX_SL0_1", 0x26}, + {"R_RX_SL0_2", 0x27}, + {"R_JATT_DIR", 0x2b}, + {"R_SLIP", 0x2c}, + {"A_ST_RD_STA", 0x30}, + {"R_FAS_ECL", 0x30}, + {"R_FAS_ECH", 0x31}, + {"R_VIO_ECL", 0x32}, + {"R_VIO_ECH", 0x33}, + {"R_CRC_ECL / A_ST_SQ_RD", 0x34}, + {"R_CRC_ECH", 0x35}, + {"R_E_ECL", 0x36}, + {"R_E_ECH", 0x37}, + {"R_SA6_SA13_ECL", 0x38}, + {"R_SA6_SA13_ECH", 0x39}, + {"R_SA6_SA23_ECL", 0x3A}, + {"R_SA6_SA23_ECH", 0x3B}, + {"A_ST_B1_RX", 0x3C}, + {"A_ST_B2_RX", 0x3D}, + {"A_ST_D_RX", 0x3E}, + {"A_ST_E_RX", 0x3F}, + {"R_GPIO_IN0", 0x40}, + {"R_GPIO_IN1", 0x41}, + {"R_GPI_IN0", 0x44}, + {"R_GPI_IN1", 0x45}, + {"R_GPI_IN2", 0x46}, + {"R_GPI_IN3", 0x47}, + {"A_FIFO_DATA0-2", 0x80}, + {"A_FIFO_DATA0-2_NOINC", 0x84}, + {"R_INT_DATA", 0x88}, + {"R_RAM_DATA", 0xC0}, + {"R_IRQ_FIFO_BL0", 0xC8}, + {"R_IRQ_FIFO_BL1", 0xC9}, + {"R_IRQ_FIFO_BL2", 0xCA}, + {"R_IRQ_FIFO_BL3", 0xCB}, + {"R_IRQ_FIFO_BL4", 0xCC}, + {"R_IRQ_FIFO_BL5", 0xCD}, + {"R_IRQ_FIFO_BL6", 0xCE}, + {"R_IRQ_FIFO_BL7", 0xCF}, +}; +#endif /* HFC_REGISTER_DEBUG */ + diff --git a/drivers/isdn/hardware/mISDN/hfc_pci.h b/drivers/isdn/hardware/mISDN/hfc_pci.h new file mode 100644 index 000000000000..fd2c9be6d849 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfc_pci.h @@ -0,0 +1,228 @@ +/* + * specific defines for CCD's HFC 2BDS0 PCI chips + * + * Author Werner Cornelius (werner@isdn4linux.de) + * + * Copyright 1999 by Werner Cornelius (werner@isdn4linux.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, 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. + * + */ + +/* + * thresholds for transparent B-channel mode + * change mask and threshold simultaneously + */ +#define HFCPCI_BTRANS_THRESHOLD 128 +#define HFCPCI_BTRANS_MAX 256 +#define HFCPCI_BTRANS_THRESMASK 0x00 + +/* defines for PCI config */ +#define PCI_ENA_MEMIO 0x02 +#define PCI_ENA_MASTER 0x04 + +/* GCI/IOM bus monitor registers */ +#define HCFPCI_C_I 0x08 +#define HFCPCI_TRxR 0x0C +#define HFCPCI_MON1_D 0x28 +#define HFCPCI_MON2_D 0x2C + +/* GCI/IOM bus timeslot registers */ +#define HFCPCI_B1_SSL 0x80 +#define HFCPCI_B2_SSL 0x84 +#define HFCPCI_AUX1_SSL 0x88 +#define HFCPCI_AUX2_SSL 0x8C +#define HFCPCI_B1_RSL 0x90 +#define HFCPCI_B2_RSL 0x94 +#define HFCPCI_AUX1_RSL 0x98 +#define HFCPCI_AUX2_RSL 0x9C + +/* GCI/IOM bus data registers */ +#define HFCPCI_B1_D 0xA0 +#define HFCPCI_B2_D 0xA4 +#define HFCPCI_AUX1_D 0xA8 +#define HFCPCI_AUX2_D 0xAC + +/* GCI/IOM bus configuration registers */ +#define HFCPCI_MST_EMOD 0xB4 +#define HFCPCI_MST_MODE 0xB8 +#define HFCPCI_CONNECT 0xBC + + +/* Interrupt and status registers */ +#define HFCPCI_FIFO_EN 0x44 +#define HFCPCI_TRM 0x48 +#define HFCPCI_B_MODE 0x4C +#define HFCPCI_CHIP_ID 0x58 +#define HFCPCI_CIRM 0x60 +#define HFCPCI_CTMT 0x64 +#define HFCPCI_INT_M1 0x68 +#define HFCPCI_INT_M2 0x6C +#define HFCPCI_INT_S1 0x78 +#define HFCPCI_INT_S2 0x7C +#define HFCPCI_STATUS 0x70 + +/* S/T section registers */ +#define HFCPCI_STATES 0xC0 +#define HFCPCI_SCTRL 0xC4 +#define HFCPCI_SCTRL_E 0xC8 +#define HFCPCI_SCTRL_R 0xCC +#define HFCPCI_SQ 0xD0 +#define HFCPCI_CLKDEL 0xDC +#define HFCPCI_B1_REC 0xF0 +#define HFCPCI_B1_SEND 0xF0 +#define HFCPCI_B2_REC 0xF4 +#define HFCPCI_B2_SEND 0xF4 +#define HFCPCI_D_REC 0xF8 +#define HFCPCI_D_SEND 0xF8 +#define HFCPCI_E_REC 0xFC + + +/* bits in status register (READ) */ +#define HFCPCI_PCI_PROC 0x02 +#define HFCPCI_NBUSY 0x04 +#define HFCPCI_TIMER_ELAP 0x10 +#define HFCPCI_STATINT 0x20 +#define HFCPCI_FRAMEINT 0x40 +#define HFCPCI_ANYINT 0x80 + +/* bits in CTMT (Write) */ +#define HFCPCI_CLTIMER 0x80 +#define HFCPCI_TIM3_125 0x04 +#define HFCPCI_TIM25 0x10 +#define HFCPCI_TIM50 0x14 +#define HFCPCI_TIM400 0x18 +#define HFCPCI_TIM800 0x1C +#define HFCPCI_AUTO_TIMER 0x20 +#define HFCPCI_TRANSB2 0x02 +#define HFCPCI_TRANSB1 0x01 + +/* bits in CIRM (Write) */ +#define HFCPCI_AUX_MSK 0x07 +#define HFCPCI_RESET 0x08 +#define HFCPCI_B1_REV 0x40 +#define HFCPCI_B2_REV 0x80 + +/* bits in INT_M1 and INT_S1 */ +#define HFCPCI_INTS_B1TRANS 0x01 +#define HFCPCI_INTS_B2TRANS 0x02 +#define HFCPCI_INTS_DTRANS 0x04 +#define HFCPCI_INTS_B1REC 0x08 +#define HFCPCI_INTS_B2REC 0x10 +#define HFCPCI_INTS_DREC 0x20 +#define HFCPCI_INTS_L1STATE 0x40 +#define HFCPCI_INTS_TIMER 0x80 + +/* bits in INT_M2 */ +#define HFCPCI_PROC_TRANS 0x01 +#define HFCPCI_GCI_I_CHG 0x02 +#define HFCPCI_GCI_MON_REC 0x04 +#define HFCPCI_IRQ_ENABLE 0x08 +#define HFCPCI_PMESEL 0x80 + +/* bits in STATES */ +#define HFCPCI_STATE_MSK 0x0F +#define HFCPCI_LOAD_STATE 0x10 +#define HFCPCI_ACTIVATE 0x20 +#define HFCPCI_DO_ACTION 0x40 +#define HFCPCI_NT_G2_G3 0x80 + +/* bits in HFCD_MST_MODE */ +#define HFCPCI_MASTER 0x01 +#define HFCPCI_SLAVE 0x00 +#define HFCPCI_F0IO_POSITIV 0x02 +#define HFCPCI_F0_NEGATIV 0x04 +#define HFCPCI_F0_2C4 0x08 +/* remaining bits are for codecs control */ + +/* bits in HFCD_SCTRL */ +#define SCTRL_B1_ENA 0x01 +#define SCTRL_B2_ENA 0x02 +#define SCTRL_MODE_TE 0x00 +#define SCTRL_MODE_NT 0x04 +#define SCTRL_LOW_PRIO 0x08 +#define SCTRL_SQ_ENA 0x10 +#define SCTRL_TEST 0x20 +#define SCTRL_NONE_CAP 0x40 +#define SCTRL_PWR_DOWN 0x80 + +/* bits in SCTRL_E */ +#define HFCPCI_AUTO_AWAKE 0x01 +#define HFCPCI_DBIT_1 0x04 +#define HFCPCI_IGNORE_COL 0x08 +#define HFCPCI_CHG_B1_B2 0x80 + +/* bits in FIFO_EN register */ +#define HFCPCI_FIFOEN_B1 0x03 +#define HFCPCI_FIFOEN_B2 0x0C +#define HFCPCI_FIFOEN_DTX 0x10 +#define HFCPCI_FIFOEN_B1TX 0x01 +#define HFCPCI_FIFOEN_B1RX 0x02 +#define HFCPCI_FIFOEN_B2TX 0x04 +#define HFCPCI_FIFOEN_B2RX 0x08 + + +/* definitions of fifo memory area */ +#define MAX_D_FRAMES 15 +#define MAX_B_FRAMES 31 +#define B_SUB_VAL 0x200 +#define B_FIFO_SIZE (0x2000 - B_SUB_VAL) +#define D_FIFO_SIZE 512 +#define D_FREG_MASK 0xF + +struct zt { + unsigned short z1; /* Z1 pointer 16 Bit */ + unsigned short z2; /* Z2 pointer 16 Bit */ +}; + +struct dfifo { + u_char data[D_FIFO_SIZE]; /* FIFO data space */ + u_char fill1[0x20A0-D_FIFO_SIZE]; /* reserved, do not use */ + u_char f1, f2; /* f pointers */ + u_char fill2[0x20C0-0x20A2]; /* reserved, do not use */ + /* mask index with D_FREG_MASK for access */ + struct zt za[MAX_D_FRAMES+1]; + u_char fill3[0x4000-0x2100]; /* align 16K */ +}; + +struct bzfifo { + struct zt za[MAX_B_FRAMES+1]; /* only range 0x0..0x1F allowed */ + u_char f1, f2; /* f pointers */ + u_char fill[0x2100-0x2082]; /* alignment */ +}; + + +union fifo_area { + struct { + struct dfifo d_tx; /* D-send channel */ + struct dfifo d_rx; /* D-receive channel */ + } d_chan; + struct { + u_char fill1[0x200]; + u_char txdat_b1[B_FIFO_SIZE]; + struct bzfifo txbz_b1; + struct bzfifo txbz_b2; + u_char txdat_b2[B_FIFO_SIZE]; + u_char fill2[D_FIFO_SIZE]; + u_char rxdat_b1[B_FIFO_SIZE]; + struct bzfifo rxbz_b1; + struct bzfifo rxbz_b2; + u_char rxdat_b2[B_FIFO_SIZE]; + } b_chans; + u_char fill[32768]; +}; + +#define Write_hfc(a, b, c) (writeb(c, (a->hw.pci_io)+b)) +#define Read_hfc(a, b) (readb((a->hw.pci_io)+b)) diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c new file mode 100644 index 000000000000..2649ea55a9e8 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -0,0 +1,5320 @@ +/* + * hfcmulti.c low level driver for hfc-4s/hfc-8s/hfc-e1 based cards + * + * Author Andreas Eversberg (jolly@eversberg.eu) + * ported to mqueue mechanism: + * Peter Sprenger (sprengermoving-bytes.de) + * + * inspired by existing hfc-pci driver: + * Copyright 1999 by Werner Cornelius (werner@isdn-development.de) + * Copyright 2008 by Karsten Keil (kkeil@suse.de) + * Copyright 2008 by Andreas Eversberg (jolly@eversberg.eu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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. + * + * + * Thanks to Cologne Chip AG for this great controller! + */ + +/* + * module parameters: + * type: + * By default (0), the card is automatically detected. + * Or use the following combinations: + * Bit 0-7 = 0x00001 = HFC-E1 (1 port) + * or Bit 0-7 = 0x00004 = HFC-4S (4 ports) + * or Bit 0-7 = 0x00008 = HFC-8S (8 ports) + * Bit 8 = 0x00100 = uLaw (instead of aLaw) + * Bit 9 = 0x00200 = Disable DTMF detect on all B-channels via hardware + * Bit 10 = spare + * Bit 11 = 0x00800 = Force PCM bus into slave mode. (otherwhise auto) + * or Bit 12 = 0x01000 = Force PCM bus into master mode. (otherwhise auto) + * Bit 13 = spare + * Bit 14 = 0x04000 = Use external ram (128K) + * Bit 15 = 0x08000 = Use external ram (512K) + * Bit 16 = 0x10000 = Use 64 timeslots instead of 32 + * or Bit 17 = 0x20000 = Use 128 timeslots instead of anything else + * Bit 18 = spare + * Bit 19 = 0x80000 = Send the Watchdog a Signal (Dual E1 with Watchdog) + * (all other bits are reserved and shall be 0) + * example: 0x20204 one HFC-4S with dtmf detection and 128 timeslots on PCM + * bus (PCM master) + * + * port: (optional or required for all ports on all installed cards) + * HFC-4S/HFC-8S only bits: + * Bit 0 = 0x001 = Use master clock for this S/T interface + * (ony once per chip). + * Bit 1 = 0x002 = transmitter line setup (non capacitive mode) + * Don't use this unless you know what you are doing! + * Bit 2 = 0x004 = Disable E-channel. (No E-channel processing) + * example: 0x0001,0x0000,0x0000,0x0000 one HFC-4S with master clock + * received from port 1 + * + * HFC-E1 only bits: + * Bit 0 = 0x0001 = interface: 0=copper, 1=optical + * Bit 1 = 0x0002 = reserved (later for 32 B-channels transparent mode) + * Bit 2 = 0x0004 = Report LOS + * Bit 3 = 0x0008 = Report AIS + * Bit 4 = 0x0010 = Report SLIP + * Bit 5 = 0x0020 = Report RDI + * Bit 8 = 0x0100 = Turn off CRC-4 Multiframe Mode, use double frame + * mode instead. + * Bit 9 = 0x0200 = Force get clock from interface, even in NT mode. + * or Bit 10 = 0x0400 = Force put clock to interface, even in TE mode. + * Bit 11 = 0x0800 = Use direct RX clock for PCM sync rather than PLL. + * (E1 only) + * Bit 12-13 = 0xX000 = elastic jitter buffer (1-3), Set both bits to 0 + * for default. + * (all other bits are reserved and shall be 0) + * + * debug: + * NOTE: only one debug value must be given for all cards + * enable debugging (see hfc_multi.h for debug options) + * + * poll: + * NOTE: only one poll value must be given for all cards + * Give the number of samples for each fifo process. + * By default 128 is used. Decrease to reduce delay, increase to + * reduce cpu load. If unsure, don't mess with it! + * Valid is 8, 16, 32, 64, 128, 256. + * + * pcm: + * NOTE: only one pcm value must be given for every card. + * The PCM bus id tells the mISDNdsp module about the connected PCM bus. + * By default (0), the PCM bus id is 100 for the card that is PCM master. + * If multiple cards are PCM master (because they are not interconnected), + * each card with PCM master will have increasing PCM id. + * All PCM busses with the same ID are expected to be connected and have + * common time slots slots. + * Only one chip of the PCM bus must be master, the others slave. + * -1 means no support of PCM bus not even. + * Omit this value, if all cards are interconnected or none is connected. + * If unsure, don't give this parameter. + * + * dslot: + * NOTE: only one poll value must be given for every card. + * Also this value must be given for non-E1 cards. If omitted, the E1 + * card has D-channel on time slot 16, which is default. + * If 1..15 or 17..31, an alternate time slot is used for D-channel. + * In this case, the application must be able to handle this. + * If -1 is given, the D-channel is disabled and all 31 slots can be used + * for B-channel. (only for specific applications) + * If you don't know how to use it, you don't need it! + * + * iomode: + * NOTE: only one mode value must be given for every card. + * -> See hfc_multi.h for HFC_IO_MODE_* values + * By default, the IO mode is pci memory IO (MEMIO). + * Some cards requre specific IO mode, so it cannot be changed. + * It may be usefull to set IO mode to register io (REGIO) to solve + * PCI bridge problems. + * If unsure, don't give this parameter. + * + * clockdelay_nt: + * NOTE: only one clockdelay_nt value must be given once for all cards. + * Give the value of the clock control register (A_ST_CLK_DLY) + * of the S/T interfaces in NT mode. + * This register is needed for the TBR3 certification, so don't change it. + * + * clockdelay_te: + * NOTE: only one clockdelay_te value must be given once + * Give the value of the clock control register (A_ST_CLK_DLY) + * of the S/T interfaces in TE mode. + * This register is needed for the TBR3 certification, so don't change it. + */ + +/* + * debug register access (never use this, it will flood your system log) + * #define HFC_REGISTER_DEBUG + */ + +static const char *hfcmulti_revision = "2.00"; + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/mISDNhw.h> +#include <linux/mISDNdsp.h> + +/* +#define IRQCOUNT_DEBUG +#define IRQ_DEBUG +*/ + +#include "hfc_multi.h" +#ifdef ECHOPREP +#include "gaintab.h" +#endif + +#define MAX_CARDS 8 +#define MAX_PORTS (8 * MAX_CARDS) + +static LIST_HEAD(HFClist); +static spinlock_t HFClock; /* global hfc list lock */ + +static void ph_state_change(struct dchannel *); +static void (*hfc_interrupt)(void); +static void (*register_interrupt)(void); +static int (*unregister_interrupt)(void); +static int interrupt_registered; + +static struct hfc_multi *syncmaster; +int plxsd_master; /* if we have a master card (yet) */ +static spinlock_t plx_lock; /* may not acquire other lock inside */ +EXPORT_SYMBOL(plx_lock); + +#define TYP_E1 1 +#define TYP_4S 4 +#define TYP_8S 8 + +static int poll_timer = 6; /* default = 128 samples = 16ms */ +/* number of POLL_TIMER interrupts for G2 timeout (ca 1s) */ +static int nt_t1_count[] = { 3840, 1920, 960, 480, 240, 120, 60, 30 }; +#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ +#define CLKDEL_NT 0x6c /* CLKDEL in NT mode + (0x60 MUST be included!) */ +static u_char silence = 0xff; /* silence by LAW */ + +#define DIP_4S 0x1 /* DIP Switches for Beronet 1S/2S/4S cards */ +#define DIP_8S 0x2 /* DIP Switches for Beronet 8S+ cards */ +#define DIP_E1 0x3 /* DIP Switches for Beronet E1 cards */ + +/* + * module stuff + */ + +static uint type[MAX_CARDS]; +static uint pcm[MAX_CARDS]; +static uint dslot[MAX_CARDS]; +static uint iomode[MAX_CARDS]; +static uint port[MAX_PORTS]; +static uint debug; +static uint poll; +static uint timer; +static uint clockdelay_te = CLKDEL_TE; +static uint clockdelay_nt = CLKDEL_NT; + +static int HFC_cnt, Port_cnt, PCM_cnt = 99; + +MODULE_AUTHOR("Andreas Eversberg"); +MODULE_LICENSE("GPL"); +module_param(debug, uint, S_IRUGO | S_IWUSR); +module_param(poll, uint, S_IRUGO | S_IWUSR); +module_param(timer, uint, S_IRUGO | S_IWUSR); +module_param(clockdelay_te, uint, S_IRUGO | S_IWUSR); +module_param(clockdelay_nt, uint, S_IRUGO | S_IWUSR); +module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(pcm, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(dslot, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(iomode, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR); + +#ifdef HFC_REGISTER_DEBUG +#define HFC_outb(hc, reg, val) \ + (hc->HFC_outb(hc, reg, val, __func__, __LINE__)) +#define HFC_outb_nodebug(hc, reg, val) \ + (hc->HFC_outb_nodebug(hc, reg, val, __func__, __LINE__)) +#define HFC_inb(hc, reg) \ + (hc->HFC_inb(hc, reg, __func__, __LINE__)) +#define HFC_inb_nodebug(hc, reg) \ + (hc->HFC_inb_nodebug(hc, reg, __func__, __LINE__)) +#define HFC_inw(hc, reg) \ + (hc->HFC_inw(hc, reg, __func__, __LINE__)) +#define HFC_inw_nodebug(hc, reg) \ + (hc->HFC_inw_nodebug(hc, reg, __func__, __LINE__)) +#define HFC_wait(hc) \ + (hc->HFC_wait(hc, __func__, __LINE__)) +#define HFC_wait_nodebug(hc) \ + (hc->HFC_wait_nodebug(hc, __func__, __LINE__)) +#else +#define HFC_outb(hc, reg, val) (hc->HFC_outb(hc, reg, val)) +#define HFC_outb_nodebug(hc, reg, val) (hc->HFC_outb_nodebug(hc, reg, val)) +#define HFC_inb(hc, reg) (hc->HFC_inb(hc, reg)) +#define HFC_inb_nodebug(hc, reg) (hc->HFC_inb_nodebug(hc, reg)) +#define HFC_inw(hc, reg) (hc->HFC_inw(hc, reg)) +#define HFC_inw_nodebug(hc, reg) (hc->HFC_inw_nodebug(hc, reg)) +#define HFC_wait(hc) (hc->HFC_wait(hc)) +#define HFC_wait_nodebug(hc) (hc->HFC_wait_nodebug(hc)) +#endif + +/* HFC_IO_MODE_PCIMEM */ +static void +#ifdef HFC_REGISTER_DEBUG +HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val, + const char *function, int line) +#else +HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val) +#endif +{ + writeb(val, (hc->pci_membase)+reg); +} +static u_char +#ifdef HFC_REGISTER_DEBUG +HFC_inb_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else +HFC_inb_pcimem(struct hfc_multi *hc, u_char reg) +#endif +{ + return readb((hc->pci_membase)+reg); +} +static u_short +#ifdef HFC_REGISTER_DEBUG +HFC_inw_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else +HFC_inw_pcimem(struct hfc_multi *hc, u_char reg) +#endif +{ + return readw((hc->pci_membase)+reg); +} +static void +#ifdef HFC_REGISTER_DEBUG +HFC_wait_pcimem(struct hfc_multi *hc, const char *function, int line) +#else +HFC_wait_pcimem(struct hfc_multi *hc) +#endif +{ + while (readb((hc->pci_membase)+R_STATUS) & V_BUSY); +} + +/* HFC_IO_MODE_REGIO */ +static void +#ifdef HFC_REGISTER_DEBUG +HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val, + const char *function, int line) +#else +HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val) +#endif +{ + outb(reg, (hc->pci_iobase)+4); + outb(val, hc->pci_iobase); +} +static u_char +#ifdef HFC_REGISTER_DEBUG +HFC_inb_regio(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else +HFC_inb_regio(struct hfc_multi *hc, u_char reg) +#endif +{ + outb(reg, (hc->pci_iobase)+4); + return inb(hc->pci_iobase); +} +static u_short +#ifdef HFC_REGISTER_DEBUG +HFC_inw_regio(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else +HFC_inw_regio(struct hfc_multi *hc, u_char reg) +#endif +{ + outb(reg, (hc->pci_iobase)+4); + return inw(hc->pci_iobase); +} +static void +#ifdef HFC_REGISTER_DEBUG +HFC_wait_regio(struct hfc_multi *hc, const char *function, int line) +#else +HFC_wait_regio(struct hfc_multi *hc) +#endif +{ + outb(R_STATUS, (hc->pci_iobase)+4); + while (inb(hc->pci_iobase) & V_BUSY); +} + +#ifdef HFC_REGISTER_DEBUG +static void +HFC_outb_debug(struct hfc_multi *hc, u_char reg, u_char val, + const char *function, int line) +{ + char regname[256] = "", bits[9] = "xxxxxxxx"; + int i; + + i = -1; + while (hfc_register_names[++i].name) { + if (hfc_register_names[i].reg == reg) + strcat(regname, hfc_register_names[i].name); + } + if (regname[0] == '\0') + strcpy(regname, "register"); + + bits[7] = '0'+(!!(val&1)); + bits[6] = '0'+(!!(val&2)); + bits[5] = '0'+(!!(val&4)); + bits[4] = '0'+(!!(val&8)); + bits[3] = '0'+(!!(val&16)); + bits[2] = '0'+(!!(val&32)); + bits[1] = '0'+(!!(val&64)); + bits[0] = '0'+(!!(val&128)); + printk(KERN_DEBUG + "HFC_outb(chip %d, %02x=%s, 0x%02x=%s); in %s() line %d\n", + hc->id, reg, regname, val, bits, function, line); + HFC_outb_nodebug(hc, reg, val); +} +static u_char +HFC_inb_debug(struct hfc_multi *hc, u_char reg, const char *function, int line) +{ + char regname[256] = "", bits[9] = "xxxxxxxx"; + u_char val = HFC_inb_nodebug(hc, reg); + int i; + + i = 0; + while (hfc_register_names[i++].name) + ; + while (hfc_register_names[++i].name) { + if (hfc_register_names[i].reg == reg) + strcat(regname, hfc_register_names[i].name); + } + if (regname[0] == '\0') + strcpy(regname, "register"); + + bits[7] = '0'+(!!(val&1)); + bits[6] = '0'+(!!(val&2)); + bits[5] = '0'+(!!(val&4)); + bits[4] = '0'+(!!(val&8)); + bits[3] = '0'+(!!(val&16)); + bits[2] = '0'+(!!(val&32)); + bits[1] = '0'+(!!(val&64)); + bits[0] = '0'+(!!(val&128)); + printk(KERN_DEBUG + "HFC_inb(chip %d, %02x=%s) = 0x%02x=%s; in %s() line %d\n", + hc->id, reg, regname, val, bits, function, line); + return val; +} +static u_short +HFC_inw_debug(struct hfc_multi *hc, u_char reg, const char *function, int line) +{ + char regname[256] = ""; + u_short val = HFC_inw_nodebug(hc, reg); + int i; + + i = 0; + while (hfc_register_names[i++].name) + ; + while (hfc_register_names[++i].name) { + if (hfc_register_names[i].reg == reg) + strcat(regname, hfc_register_names[i].name); + } + if (regname[0] == '\0') + strcpy(regname, "register"); + + printk(KERN_DEBUG + "HFC_inw(chip %d, %02x=%s) = 0x%04x; in %s() line %d\n", + hc->id, reg, regname, val, function, line); + return val; +} +static void +HFC_wait_debug(struct hfc_multi *hc, const char *function, int line) +{ + printk(KERN_DEBUG "HFC_wait(chip %d); in %s() line %d\n", + hc->id, function, line); + HFC_wait_nodebug(hc); +} +#endif + +/* write fifo data (REGIO) */ +void +write_fifo_regio(struct hfc_multi *hc, u_char *data, int len) +{ + outb(A_FIFO_DATA0, (hc->pci_iobase)+4); + while (len>>2) { + outl(*(u32 *)data, hc->pci_iobase); + data += 4; + len -= 4; + } + while (len>>1) { + outw(*(u16 *)data, hc->pci_iobase); + data += 2; + len -= 2; + } + while (len) { + outb(*data, hc->pci_iobase); + data++; + len--; + } +} +/* write fifo data (PCIMEM) */ +void +write_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len) +{ + while (len>>2) { + writel(*(u32 *)data, (hc->pci_membase)+A_FIFO_DATA0); + data += 4; + len -= 4; + } + while (len>>1) { + writew(*(u16 *)data, (hc->pci_membase)+A_FIFO_DATA0); + data += 2; + len -= 2; + } + while (len) { + writeb(*data, (hc->pci_membase)+A_FIFO_DATA0); + data++; + len--; + } +} +/* read fifo data (REGIO) */ +void +read_fifo_regio(struct hfc_multi *hc, u_char *data, int len) +{ + outb(A_FIFO_DATA0, (hc->pci_iobase)+4); + while (len>>2) { + *(u32 *)data = inl(hc->pci_iobase); + data += 4; + len -= 4; + } + while (len>>1) { + *(u16 *)data = inw(hc->pci_iobase); + data += 2; + len -= 2; + } + while (len) { + *data = inb(hc->pci_iobase); + data++; + len--; + } +} + +/* read fifo data (PCIMEM) */ +void +read_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len) +{ + while (len>>2) { + *(u32 *)data = + readl((hc->pci_membase)+A_FIFO_DATA0); + data += 4; + len -= 4; + } + while (len>>1) { + *(u16 *)data = + readw((hc->pci_membase)+A_FIFO_DATA0); + data += 2; + len -= 2; + } + while (len) { + *data = readb((hc->pci_membase)+A_FIFO_DATA0); + data++; + len--; + } +} + + +static void +enable_hwirq(struct hfc_multi *hc) +{ + hc->hw.r_irq_ctrl |= V_GLOB_IRQ_EN; + HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl); +} + +static void +disable_hwirq(struct hfc_multi *hc) +{ + hc->hw.r_irq_ctrl &= ~((u_char)V_GLOB_IRQ_EN); + HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl); +} + +#define NUM_EC 2 +#define MAX_TDM_CHAN 32 + + +inline void +enablepcibridge(struct hfc_multi *c) +{ + HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); /* was _io before */ +} + +inline void +disablepcibridge(struct hfc_multi *c) +{ + HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x2); /* was _io before */ +} + +inline unsigned char +readpcibridge(struct hfc_multi *hc, unsigned char address) +{ + unsigned short cipv; + unsigned char data; + + if (!hc->pci_iobase) + return 0; + + /* slow down a PCI read access by 1 PCI clock cycle */ + HFC_outb(hc, R_CTRL, 0x4); /*was _io before*/ + + if (address == 0) + cipv = 0x4000; + else + cipv = 0x5800; + + /* select local bridge port address by writing to CIP port */ + /* data = HFC_inb(c, cipv); * was _io before */ + outw(cipv, hc->pci_iobase + 4); + data = inb(hc->pci_iobase); + + /* restore R_CTRL for normal PCI read cycle speed */ + HFC_outb(hc, R_CTRL, 0x0); /* was _io before */ + + return data; +} + +inline void +writepcibridge(struct hfc_multi *hc, unsigned char address, unsigned char data) +{ + unsigned short cipv; + unsigned int datav; + + if (!hc->pci_iobase) + return; + + if (address == 0) + cipv = 0x4000; + else + cipv = 0x5800; + + /* select local bridge port address by writing to CIP port */ + outw(cipv, hc->pci_iobase + 4); + /* define a 32 bit dword with 4 identical bytes for write sequence */ + datav = data | ((__u32) data << 8) | ((__u32) data << 16) | + ((__u32) data << 24); + + /* + * write this 32 bit dword to the bridge data port + * this will initiate a write sequence of up to 4 writes to the same + * address on the local bus interface the number of write accesses + * is undefined but >=1 and depends on the next PCI transaction + * during write sequence on the local bus + */ + outl(datav, hc->pci_iobase); +} + +inline void +cpld_set_reg(struct hfc_multi *hc, unsigned char reg) +{ + /* Do data pin read low byte */ + HFC_outb(hc, R_GPIO_OUT1, reg); +} + +inline void +cpld_write_reg(struct hfc_multi *hc, unsigned char reg, unsigned char val) +{ + cpld_set_reg(hc, reg); + + enablepcibridge(hc); + writepcibridge(hc, 1, val); + disablepcibridge(hc); + + return; +} + +inline unsigned char +cpld_read_reg(struct hfc_multi *hc, unsigned char reg) +{ + unsigned char bytein; + + cpld_set_reg(hc, reg); + + /* Do data pin read low byte */ + HFC_outb(hc, R_GPIO_OUT1, reg); + + enablepcibridge(hc); + bytein = readpcibridge(hc, 1); + disablepcibridge(hc); + + return bytein; +} + +inline void +vpm_write_address(struct hfc_multi *hc, unsigned short addr) +{ + cpld_write_reg(hc, 0, 0xff & addr); + cpld_write_reg(hc, 1, 0x01 & (addr >> 8)); +} + +inline unsigned short +vpm_read_address(struct hfc_multi *c) +{ + unsigned short addr; + unsigned short highbit; + + addr = cpld_read_reg(c, 0); + highbit = cpld_read_reg(c, 1); + + addr = addr | (highbit << 8); + + return addr & 0x1ff; +} + +inline unsigned char +vpm_in(struct hfc_multi *c, int which, unsigned short addr) +{ + unsigned char res; + + vpm_write_address(c, addr); + + if (!which) + cpld_set_reg(c, 2); + else + cpld_set_reg(c, 3); + + enablepcibridge(c); + res = readpcibridge(c, 1); + disablepcibridge(c); + + cpld_set_reg(c, 0); + + return res; +} + +inline void +vpm_out(struct hfc_multi *c, int which, unsigned short addr, + unsigned char data) +{ + vpm_write_address(c, addr); + + enablepcibridge(c); + + if (!which) + cpld_set_reg(c, 2); + else + cpld_set_reg(c, 3); + + writepcibridge(c, 1, data); + + cpld_set_reg(c, 0); + + disablepcibridge(c); + + { + unsigned char regin; + regin = vpm_in(c, which, addr); + if (regin != data) + printk(KERN_DEBUG "Wrote 0x%x to register 0x%x but got back " + "0x%x\n", data, addr, regin); + } + +} + + +void +vpm_init(struct hfc_multi *wc) +{ + unsigned char reg; + unsigned int mask; + unsigned int i, x, y; + unsigned int ver; + + for (x = 0; x < NUM_EC; x++) { + /* Setup GPIO's */ + if (!x) { + ver = vpm_in(wc, x, 0x1a0); + printk(KERN_DEBUG "VPM: Chip %d: ver %02x\n", x, ver); + } + + for (y = 0; y < 4; y++) { + vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */ + vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */ + vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */ + } + + /* Setup TDM path - sets fsync and tdm_clk as inputs */ + reg = vpm_in(wc, x, 0x1a3); /* misc_con */ + vpm_out(wc, x, 0x1a3, reg & ~2); + + /* Setup Echo length (256 taps) */ + vpm_out(wc, x, 0x022, 1); + vpm_out(wc, x, 0x023, 0xff); + + /* Setup timeslots */ + vpm_out(wc, x, 0x02f, 0x00); + mask = 0x02020202 << (x * 4); + + /* Setup the tdm channel masks for all chips */ + for (i = 0; i < 4; i++) + vpm_out(wc, x, 0x33 - i, (mask >> (i << 3)) & 0xff); + + /* Setup convergence rate */ + printk(KERN_DEBUG "VPM: A-law mode\n"); + reg = 0x00 | 0x10 | 0x01; + vpm_out(wc, x, 0x20, reg); + printk(KERN_DEBUG "VPM reg 0x20 is %x\n", reg); + /*vpm_out(wc, x, 0x20, (0x00 | 0x08 | 0x20 | 0x10)); */ + + vpm_out(wc, x, 0x24, 0x02); + reg = vpm_in(wc, x, 0x24); + printk(KERN_DEBUG "NLP Thresh is set to %d (0x%x)\n", reg, reg); + + /* Initialize echo cans */ + for (i = 0; i < MAX_TDM_CHAN; i++) { + if (mask & (0x00000001 << i)) + vpm_out(wc, x, i, 0x00); + } + + /* + * ARM arch at least disallows a udelay of + * more than 2ms... it gives a fake "__bad_udelay" + * reference at link-time. + * long delays in kernel code are pretty sucky anyway + * for now work around it using 5 x 2ms instead of 1 x 10ms + */ + + udelay(2000); + udelay(2000); + udelay(2000); + udelay(2000); + udelay(2000); + + /* Put in bypass mode */ + for (i = 0; i < MAX_TDM_CHAN; i++) { + if (mask & (0x00000001 << i)) + vpm_out(wc, x, i, 0x01); + } + + /* Enable bypass */ + for (i = 0; i < MAX_TDM_CHAN; i++) { + if (mask & (0x00000001 << i)) + vpm_out(wc, x, 0x78 + i, 0x01); + } + + } +} + +void +vpm_check(struct hfc_multi *hctmp) +{ + unsigned char gpi2; + + gpi2 = HFC_inb(hctmp, R_GPI_IN2); + + if ((gpi2 & 0x3) != 0x3) + printk(KERN_DEBUG "Got interrupt 0x%x from VPM!\n", gpi2); +} + + +/* + * Interface to enable/disable the HW Echocan + * + * these functions are called within a spin_lock_irqsave on + * the channel instance lock, so we are not disturbed by irqs + * + * we can later easily change the interface to make other + * things configurable, for now we configure the taps + * + */ + +void +vpm_echocan_on(struct hfc_multi *hc, int ch, int taps) +{ + unsigned int timeslot; + unsigned int unit; + struct bchannel *bch = hc->chan[ch].bch; +#ifdef TXADJ + int txadj = -4; + struct sk_buff *skb; +#endif + if (hc->chan[ch].protocol != ISDN_P_B_RAW) + return; + + if (!bch) + return; + +#ifdef TXADJ + skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX, + sizeof(int), &txadj, GFP_ATOMIC); + if (skb) + recv_Bchannel_skb(bch, skb); +#endif + + timeslot = ((ch/4)*8) + ((ch%4)*4) + 1; + unit = ch % 4; + + printk(KERN_NOTICE "vpm_echocan_on called taps [%d] on timeslot %d\n", + taps, timeslot); + + vpm_out(hc, unit, timeslot, 0x7e); +} + +void +vpm_echocan_off(struct hfc_multi *hc, int ch) +{ + unsigned int timeslot; + unsigned int unit; + struct bchannel *bch = hc->chan[ch].bch; +#ifdef TXADJ + int txadj = 0; + struct sk_buff *skb; +#endif + + if (hc->chan[ch].protocol != ISDN_P_B_RAW) + return; + + if (!bch) + return; + +#ifdef TXADJ + skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX, + sizeof(int), &txadj, GFP_ATOMIC); + if (skb) + recv_Bchannel_skb(bch, skb); +#endif + + timeslot = ((ch/4)*8) + ((ch%4)*4) + 1; + unit = ch % 4; + + printk(KERN_NOTICE "vpm_echocan_off called on timeslot %d\n", + timeslot); + /* FILLME */ + vpm_out(hc, unit, timeslot, 0x01); +} + + +/* + * Speech Design resync feature + * NOTE: This is called sometimes outside interrupt handler. + * We must lock irqsave, so no other interrupt (other card) will occurr! + * Also multiple interrupts may nest, so must lock each access (lists, card)! + */ +static inline void +hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm) +{ + struct hfc_multi *hc, *next, *pcmmaster = 0; + u_int *plx_acc_32, pv; + u_long flags; + + spin_lock_irqsave(&HFClock, flags); + spin_lock(&plx_lock); /* must be locked inside other locks */ + + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: RESYNC(syncmaster=0x%p)\n", + __func__, syncmaster); + + /* select new master */ + if (newmaster) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "using provided controller\n"); + } else { + list_for_each_entry_safe(hc, next, &HFClist, list) { + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + if (hc->syncronized) { + newmaster = hc; + break; + } + } + } + } + + /* Disable sync of all cards */ + list_for_each_entry_safe(hc, next, &HFClist, list) { + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + pv = readl(plx_acc_32); + pv &= ~PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) { + pcmmaster = hc; + if (hc->type == 1) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "Schedule SYNC_I\n"); + hc->e1_resync |= 1; /* get SYNC_I */ + } + } + } + } + + if (newmaster) { + hc = newmaster; + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "id=%d (0x%p) = syncronized with " + "interface.\n", hc->id, hc); + /* Enable new sync master */ + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + pv = readl(plx_acc_32); + pv |= PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + /* switch to jatt PLL, if not disabled by RX_SYNC */ + if (hc->type == 1 && !test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "Schedule jatt PLL\n"); + hc->e1_resync |= 2; /* switch to jatt */ + } + } else { + if (pcmmaster) { + hc = pcmmaster; + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "id=%d (0x%p) = PCM master syncronized " + "with QUARTZ\n", hc->id, hc); + if (hc->type == 1) { + /* Use the crystal clock for the PCM + master card */ + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "Schedule QUARTZ for HFC-E1\n"); + hc->e1_resync |= 4; /* switch quartz */ + } else { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "QUARTZ is automatically " + "enabled by HFC-%dS\n", hc->type); + } + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + pv = readl(plx_acc_32); + pv |= PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + } else + if (!rm) + printk(KERN_ERR "%s no pcm master, this MUST " + "not happen!\n", __func__); + } + syncmaster = newmaster; + + spin_unlock(&plx_lock); + spin_unlock_irqrestore(&HFClock, flags); +} + +/* This must be called AND hc must be locked irqsave!!! */ +inline void +plxsd_checksync(struct hfc_multi *hc, int rm) +{ + if (hc->syncronized) { + if (syncmaster == NULL) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_WARNING "%s: GOT sync on card %d" + " (id=%d)\n", __func__, hc->id + 1, + hc->id); + hfcmulti_resync(hc, hc, rm); + } + } else { + if (syncmaster == hc) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_WARNING "%s: LOST sync on card %d" + " (id=%d)\n", __func__, hc->id + 1, + hc->id); + hfcmulti_resync(hc, NULL, rm); + } + } +} + + +/* + * free hardware resources used by driver + */ +static void +release_io_hfcmulti(struct hfc_multi *hc) +{ + u_int *plx_acc_32, pv; + u_long plx_flags; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + + /* soft reset also masks all interrupts */ + hc->hw.r_cirm |= V_SRES; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(1000); + hc->hw.r_cirm &= ~V_SRES; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(1000); /* instead of 'wait' that may cause locking */ + + /* release Speech Design card, if PLX was initialized */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) && hc->plx_membase) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: release PLXSD card %d\n", + __func__, hc->id + 1); + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + writel(PLX_GPIOC_INIT, plx_acc_32); + pv = readl(plx_acc_32); + /* Termination off */ + pv &= ~PLX_TERM_ON; + /* Disconnect the PCM */ + pv |= PLX_SLAVE_EN_N; + pv &= ~PLX_MASTER_EN; + pv &= ~PLX_SYNC_O_EN; + /* Put the DSP in Reset */ + pv &= ~PLX_DSP_RES_N; + writel(pv, plx_acc_32); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: PCM off: PLX_GPIO=%x\n", + __func__, pv); + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + /* disable memory mapped ports / io ports */ + test_and_clear_bit(HFC_CHIP_PLXSD, &hc->chip); /* prevent resync */ + pci_write_config_word(hc->pci_dev, PCI_COMMAND, 0); + if (hc->pci_membase) + iounmap((void *)hc->pci_membase); + if (hc->plx_membase) + iounmap((void *)hc->plx_membase); + if (hc->pci_iobase) + release_region(hc->pci_iobase, 8); + + if (hc->pci_dev) { + pci_disable_device(hc->pci_dev); + pci_set_drvdata(hc->pci_dev, NULL); + } + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done\n", __func__); +} + +/* + * function called to reset the HFC chip. A complete software reset of chip + * and fifos is done. All configuration of the chip is done. + */ + +static int +init_chip(struct hfc_multi *hc) +{ + u_long flags, val, val2 = 0, rev; + int i, err = 0; + u_char r_conf_en, rval; + u_int *plx_acc_32, pv; + u_long plx_flags, hfc_flags; + int plx_count; + struct hfc_multi *pos, *next, *plx_last_hc; + + spin_lock_irqsave(&hc->lock, flags); + /* reset all registers */ + memset(&hc->hw, 0, sizeof(struct hfcm_hw)); + + /* revision check */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + val = HFC_inb(hc, R_CHIP_ID)>>4; + if (val != 0x8 && val != 0xc && val != 0xe) { + printk(KERN_INFO "HFC_multi: unknown CHIP_ID:%x\n", (u_int)val); + err = -EIO; + goto out; + } + rev = HFC_inb(hc, R_CHIP_RV); + printk(KERN_INFO + "HFC_multi: detected HFC with chip ID=0x%lx revision=%ld%s\n", + val, rev, (rev == 0) ? " (old FIFO handling)" : ""); + if (rev == 0) { + test_and_set_bit(HFC_CHIP_REVISION0, &hc->chip); + printk(KERN_WARNING + "HFC_multi: NOTE: Your chip is revision 0, " + "ask Cologne Chip for update. Newer chips " + "have a better FIFO handling. Old chips " + "still work but may have slightly lower " + "HDLC transmit performance.\n"); + } + if (rev > 1) { + printk(KERN_WARNING "HFC_multi: WARNING: This driver doesn't " + "consider chip revision = %ld. The chip / " + "bridge may not work.\n", rev); + } + + /* set s-ram size */ + hc->Flen = 0x10; + hc->Zmin = 0x80; + hc->Zlen = 384; + hc->DTMFbase = 0x1000; + if (test_bit(HFC_CHIP_EXRAM_128, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: changing to 128K extenal RAM\n", + __func__); + hc->hw.r_ctrl |= V_EXT_RAM; + hc->hw.r_ram_sz = 1; + hc->Flen = 0x20; + hc->Zmin = 0xc0; + hc->Zlen = 1856; + hc->DTMFbase = 0x2000; + } + if (test_bit(HFC_CHIP_EXRAM_512, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: changing to 512K extenal RAM\n", + __func__); + hc->hw.r_ctrl |= V_EXT_RAM; + hc->hw.r_ram_sz = 2; + hc->Flen = 0x20; + hc->Zmin = 0xc0; + hc->Zlen = 8000; + hc->DTMFbase = 0x2000; + } + hc->max_trans = poll << 1; + if (hc->max_trans > hc->Zlen) + hc->max_trans = hc->Zlen; + + /* Speech Design PLX bridge */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: initializing PLXSD card %d\n", + __func__, hc->id + 1); + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + writel(PLX_GPIOC_INIT, plx_acc_32); + pv = readl(plx_acc_32); + /* The first and the last cards are terminating the PCM bus */ + pv |= PLX_TERM_ON; /* hc is currently the last */ + /* Disconnect the PCM */ + pv |= PLX_SLAVE_EN_N; + pv &= ~PLX_MASTER_EN; + pv &= ~PLX_SYNC_O_EN; + /* Put the DSP in Reset */ + pv &= ~PLX_DSP_RES_N; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: slave/term: PLX_GPIO=%x\n", + __func__, pv); + /* + * If we are the 3rd PLXSD card or higher, we must turn + * termination of last PLXSD card off. + */ + spin_lock_irqsave(&HFClock, hfc_flags); + plx_count = 0; + plx_last_hc = NULL; + list_for_each_entry_safe(pos, next, &HFClist, list) { + if (test_bit(HFC_CHIP_PLXSD, &pos->chip)) { + plx_count++; + if (pos != hc) + plx_last_hc = pos; + } + } + if (plx_count >= 3) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: card %d is between, so " + "we disable termination\n", + __func__, plx_last_hc->id + 1); + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = (u_int *)(plx_last_hc->plx_membase + + PLX_GPIOC); + pv = readl(plx_acc_32); + pv &= ~PLX_TERM_ON; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: term off: PLX_GPIO=%x\n", + __func__, pv); + } + spin_unlock_irqrestore(&HFClock, hfc_flags); + hc->hw.r_pcm_md0 = V_F0_LEN; /* shift clock for DSP */ + } + + /* we only want the real Z2 read-pointer for revision > 0 */ + if (!test_bit(HFC_CHIP_REVISION0, &hc->chip)) + hc->hw.r_ram_sz |= V_FZ_MD; + + /* select pcm mode */ + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: setting PCM into slave mode\n", + __func__); + } else + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip) && !plxsd_master) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: setting PCM into master mode\n", + __func__); + hc->hw.r_pcm_md0 |= V_PCM_MD; + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: performing PCM auto detect\n", + __func__); + } + + /* soft reset */ + HFC_outb(hc, R_CTRL, hc->hw.r_ctrl); + HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz); + HFC_outb(hc, R_FIFO_MD, 0); + hc->hw.r_cirm = V_SRES | V_HFCRES | V_PCMRES | V_STRES | V_RLD_EPR; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(100); + hc->hw.r_cirm = 0; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(100); + HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz); + + /* Speech Design PLX bridge pcm and sync mode */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + pv = readl(plx_acc_32); + /* Connect PCM */ + if (hc->hw.r_pcm_md0 & V_PCM_MD) { + pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N; + pv |= PLX_SYNC_O_EN; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: master: PLX_GPIO=%x\n", + __func__, pv); + } else { + pv &= ~(PLX_MASTER_EN | PLX_SLAVE_EN_N); + pv &= ~PLX_SYNC_O_EN; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: slave: PLX_GPIO=%x\n", + __func__, pv); + } + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + /* PCM setup */ + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x90); + if (hc->slots == 32) + HFC_outb(hc, R_PCM_MD1, 0x00); + if (hc->slots == 64) + HFC_outb(hc, R_PCM_MD1, 0x10); + if (hc->slots == 128) + HFC_outb(hc, R_PCM_MD1, 0x20); + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0xa0); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) + HFC_outb(hc, R_PCM_MD2, V_SYNC_SRC); /* sync via SYNC_I / O */ + else + HFC_outb(hc, R_PCM_MD2, 0x00); /* sync from interface */ + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00); + for (i = 0; i < 256; i++) { + HFC_outb_nodebug(hc, R_SLOT, i); + HFC_outb_nodebug(hc, A_SL_CFG, 0); + HFC_outb_nodebug(hc, A_CONF, 0); + hc->slot_owner[i] = -1; + } + + /* set clock speed */ + if (test_bit(HFC_CHIP_CLOCK2, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: setting double clock\n", __func__); + HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK); + } + + /* B410P GPIO */ + if (test_bit(HFC_CHIP_B410P, &hc->chip)) { + printk(KERN_NOTICE "Setting GPIOs\n"); + HFC_outb(hc, R_GPIO_SEL, 0x30); + HFC_outb(hc, R_GPIO_EN1, 0x3); + udelay(1000); + printk(KERN_NOTICE "calling vpm_init\n"); + vpm_init(hc); + } + + /* check if R_F0_CNT counts (8 kHz frame count) */ + val = HFC_inb(hc, R_F0_CNTL); + val += HFC_inb(hc, R_F0_CNTH) << 8; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "HFC_multi F0_CNT %ld after reset\n", val); + spin_unlock_irqrestore(&hc->lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ/100)?:1); /* Timeout minimum 10ms */ + spin_lock_irqsave(&hc->lock, flags); + val2 = HFC_inb(hc, R_F0_CNTL); + val2 += HFC_inb(hc, R_F0_CNTH) << 8; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "HFC_multi F0_CNT %ld after 10 ms (1st try)\n", + val2); + if (val2 >= val+8) { /* 1 ms */ + /* it counts, so we keep the pcm mode */ + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) + printk(KERN_INFO "controller is PCM bus MASTER\n"); + else + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) + printk(KERN_INFO "controller is PCM bus SLAVE\n"); + else { + test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + printk(KERN_INFO "controller is PCM bus SLAVE " + "(auto detected)\n"); + } + } else { + /* does not count */ + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) { +controller_fail: + printk(KERN_ERR "HFC_multi ERROR, getting no 125us " + "pulse. Seems that controller fails.\n"); + err = -EIO; + goto out; + } + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + printk(KERN_INFO "controller is PCM bus SLAVE " + "(ignoring missing PCM clock)\n"); + } else { + /* only one pcm master */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) + && plxsd_master) { + printk(KERN_ERR "HFC_multi ERROR, no clock " + "on another Speech Design card found. " + "Please be sure to connect PCM cable.\n"); + err = -EIO; + goto out; + } + /* retry with master clock */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = (u_int *)(hc->plx_membase + + PLX_GPIOC); + pv = readl(plx_acc_32); + pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N; + pv |= PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: master: PLX_GPIO" + "=%x\n", __func__, pv); + } + hc->hw.r_pcm_md0 |= V_PCM_MD; + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00); + spin_unlock_irqrestore(&hc->lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ/100)?:1); /* Timeout min. 10ms */ + spin_lock_irqsave(&hc->lock, flags); + val2 = HFC_inb(hc, R_F0_CNTL); + val2 += HFC_inb(hc, R_F0_CNTH) << 8; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "HFC_multi F0_CNT %ld after " + "10 ms (2nd try)\n", val2); + if (val2 >= val+8) { /* 1 ms */ + test_and_set_bit(HFC_CHIP_PCM_MASTER, + &hc->chip); + printk(KERN_INFO "controller is PCM bus MASTER " + "(auto detected)\n"); + } else + goto controller_fail; + } + } + + /* Release the DSP Reset */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) + plxsd_master = 1; + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + pv = readl(plx_acc_32); + pv |= PLX_DSP_RES_N; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: reset off: PLX_GPIO=%x\n", + __func__, pv); + } + + /* pcm id */ + if (hc->pcm) + printk(KERN_INFO "controller has given PCM BUS ID %d\n", + hc->pcm); + else { + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip) + || test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + PCM_cnt++; /* SD has proprietary bridging */ + } + hc->pcm = PCM_cnt; + printk(KERN_INFO "controller has PCM BUS ID %d " + "(auto selected)\n", hc->pcm); + } + + /* set up timer */ + HFC_outb(hc, R_TI_WD, poll_timer); + hc->hw.r_irqmsk_misc |= V_TI_IRQMSK; + + /* + * set up 125us interrupt, only if function pointer is available + * and module parameter timer is set + */ + if (timer && hfc_interrupt && register_interrupt) { + /* only one chip should use this interrupt */ + timer = 0; + interrupt_registered = 1; + hc->hw.r_irqmsk_misc |= V_PROC_IRQMSK; + /* deactivate other interrupts in ztdummy */ + register_interrupt(); + } + + /* set E1 state machine IRQ */ + if (hc->type == 1) + hc->hw.r_irqmsk_misc |= V_STA_IRQMSK; + + /* set DTMF detection */ + if (test_bit(HFC_CHIP_DTMF, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: enabling DTMF detection " + "for all B-channel\n", __func__); + hc->hw.r_dtmf = V_DTMF_EN | V_DTMF_STOP; + if (test_bit(HFC_CHIP_ULAW, &hc->chip)) + hc->hw.r_dtmf |= V_ULAW_SEL; + HFC_outb(hc, R_DTMF_N, 102 - 1); + hc->hw.r_irqmsk_misc |= V_DTMF_IRQMSK; + } + + /* conference engine */ + if (test_bit(HFC_CHIP_ULAW, &hc->chip)) + r_conf_en = V_CONF_EN | V_ULAW; + else + r_conf_en = V_CONF_EN; + HFC_outb(hc, R_CONF_EN, r_conf_en); + + /* setting leds */ + switch (hc->leds) { + case 1: /* HFC-E1 OEM */ + if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip)) + HFC_outb(hc, R_GPIO_SEL, 0x32); + else + HFC_outb(hc, R_GPIO_SEL, 0x30); + + HFC_outb(hc, R_GPIO_EN1, 0x0f); + HFC_outb(hc, R_GPIO_OUT1, 0x00); + + HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3); + break; + + case 2: /* HFC-4S OEM */ + case 3: + HFC_outb(hc, R_GPIO_SEL, 0xf0); + HFC_outb(hc, R_GPIO_EN1, 0xff); + HFC_outb(hc, R_GPIO_OUT1, 0x00); + break; + } + + /* set master clock */ + if (hc->masterclk >= 0) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: setting ST master clock " + "to port %d (0..%d)\n", + __func__, hc->masterclk, hc->ports-1); + hc->hw.r_st_sync = hc->masterclk | V_AUTO_SYNC; + HFC_outb(hc, R_ST_SYNC, hc->hw.r_st_sync); + } + + /* setting misc irq */ + HFC_outb(hc, R_IRQMSK_MISC, hc->hw.r_irqmsk_misc); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "r_irqmsk_misc.2: 0x%x\n", + hc->hw.r_irqmsk_misc); + + /* RAM access test */ + HFC_outb(hc, R_RAM_ADDR0, 0); + HFC_outb(hc, R_RAM_ADDR1, 0); + HFC_outb(hc, R_RAM_ADDR2, 0); + for (i = 0; i < 256; i++) { + HFC_outb_nodebug(hc, R_RAM_ADDR0, i); + HFC_outb_nodebug(hc, R_RAM_DATA, ((i*3)&0xff)); + } + for (i = 0; i < 256; i++) { + HFC_outb_nodebug(hc, R_RAM_ADDR0, i); + HFC_inb_nodebug(hc, R_RAM_DATA); + rval = HFC_inb_nodebug(hc, R_INT_DATA); + if (rval != ((i * 3) & 0xff)) { + printk(KERN_DEBUG + "addr:%x val:%x should:%x\n", i, rval, + (i * 3) & 0xff); + err++; + } + } + if (err) { + printk(KERN_DEBUG "aborting - %d RAM access errors\n", err); + err = -EIO; + goto out; + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done\n", __func__); +out: + spin_unlock_irqrestore(&hc->lock, flags); + return err; +} + + +/* + * control the watchdog + */ +static void +hfcmulti_watchdog(struct hfc_multi *hc) +{ + hc->wdcount++; + + if (hc->wdcount > 10) { + hc->wdcount = 0; + hc->wdbyte = hc->wdbyte == V_GPIO_OUT2 ? + V_GPIO_OUT3 : V_GPIO_OUT2; + + /* printk("Sending Watchdog Kill %x\n",hc->wdbyte); */ + HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3); + HFC_outb(hc, R_GPIO_OUT0, hc->wdbyte); + } +} + + + +/* + * output leds + */ +static void +hfcmulti_leds(struct hfc_multi *hc) +{ + unsigned long lled; + unsigned long leddw; + int i, state, active, leds; + struct dchannel *dch; + int led[4]; + + hc->ledcount += poll; + if (hc->ledcount > 4096) { + hc->ledcount -= 4096; + hc->ledstate = 0xAFFEAFFE; + } + + switch (hc->leds) { + case 1: /* HFC-E1 OEM */ + /* 2 red blinking: NT mode deactivate + * 2 red steady: TE mode deactivate + * left green: L1 active + * left red: frame sync, but no L1 + * right green: L2 active + */ + if (hc->chan[hc->dslot].sync != 2) { /* no frame sync */ + if (hc->chan[hc->dslot].dch->dev.D.protocol + != ISDN_P_NT_E1) { + led[0] = 1; + led[1] = 1; + } else if (hc->ledcount>>11) { + led[0] = 1; + led[1] = 1; + } else { + led[0] = 0; + led[1] = 0; + } + led[2] = 0; + led[3] = 0; + } else { /* with frame sync */ + /* TODO make it work */ + led[0] = 0; + led[1] = 0; + led[2] = 0; + led[3] = 1; + } + leds = (led[0] | (led[1]<<2) | (led[2]<<1) | (led[3]<<3))^0xF; + /* leds are inverted */ + if (leds != (int)hc->ledstate) { + HFC_outb_nodebug(hc, R_GPIO_OUT1, leds); + hc->ledstate = leds; + } + break; + + case 2: /* HFC-4S OEM */ + /* red blinking = PH_DEACTIVATE NT Mode + * red steady = PH_DEACTIVATE TE Mode + * green steady = PH_ACTIVATE + */ + for (i = 0; i < 4; i++) { + state = 0; + active = -1; + dch = hc->chan[(i << 2) | 2].dch; + if (dch) { + state = dch->state; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + } + if (state) { + if (state == active) { + led[i] = 1; /* led green */ + } else + if (dch->dev.D.protocol == ISDN_P_TE_S0) + /* TE mode: led red */ + led[i] = 2; + else + if (hc->ledcount>>11) + /* led red */ + led[i] = 2; + else + /* led off */ + led[i] = 0; + } else + led[i] = 0; /* led off */ + } + if (test_bit(HFC_CHIP_B410P, &hc->chip)) { + leds = 0; + for (i = 0; i < 4; i++) { + if (led[i] == 1) { + /*green*/ + leds |= (0x2 << (i * 2)); + } else if (led[i] == 2) { + /*red*/ + leds |= (0x1 << (i * 2)); + } + } + if (leds != (int)hc->ledstate) { + vpm_out(hc, 0, 0x1a8 + 3, leds); + hc->ledstate = leds; + } + } else { + leds = ((led[3] > 0) << 0) | ((led[1] > 0) << 1) | + ((led[0] > 0) << 2) | ((led[2] > 0) << 3) | + ((led[3] & 1) << 4) | ((led[1] & 1) << 5) | + ((led[0] & 1) << 6) | ((led[2] & 1) << 7); + if (leds != (int)hc->ledstate) { + HFC_outb_nodebug(hc, R_GPIO_EN1, leds & 0x0F); + HFC_outb_nodebug(hc, R_GPIO_OUT1, leds >> 4); + hc->ledstate = leds; + } + } + break; + + case 3: /* HFC 1S/2S Beronet */ + /* red blinking = PH_DEACTIVATE NT Mode + * red steady = PH_DEACTIVATE TE Mode + * green steady = PH_ACTIVATE + */ + for (i = 0; i < 2; i++) { + state = 0; + active = -1; + dch = hc->chan[(i << 2) | 2].dch; + if (dch) { + state = dch->state; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + } + if (state) { + if (state == active) { + led[i] = 1; /* led green */ + } else + if (dch->dev.D.protocol == ISDN_P_TE_S0) + /* TE mode: led red */ + led[i] = 2; + else + if (hc->ledcount >> 11) + /* led red */ + led[i] = 2; + else + /* led off */ + led[i] = 0; + } else + led[i] = 0; /* led off */ + } + + + leds = (led[0] > 0) | ((led[1] > 0)<<1) | ((led[0]&1)<<2) + | ((led[1]&1)<<3); + if (leds != (int)hc->ledstate) { + HFC_outb_nodebug(hc, R_GPIO_EN1, + ((led[0] > 0) << 2) | ((led[1] > 0) << 3)); + HFC_outb_nodebug(hc, R_GPIO_OUT1, + ((led[0] & 1) << 2) | ((led[1] & 1) << 3)); + hc->ledstate = leds; + } + break; + case 8: /* HFC 8S+ Beronet */ + lled = 0; + + for (i = 0; i < 8; i++) { + state = 0; + active = -1; + dch = hc->chan[(i << 2) | 2].dch; + if (dch) { + state = dch->state; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + } + if (state) { + if (state == active) { + lled |= 0 << i; + } else + if (hc->ledcount >> 11) + lled |= 0 << i; + else + lled |= 1 << i; + } else + lled |= 1 << i; + } + leddw = lled << 24 | lled << 16 | lled << 8 | lled; + if (leddw != hc->ledstate) { + /* HFC_outb(hc, R_BRG_PCM_CFG, 1); + HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); */ + /* was _io before */ + HFC_outb_nodebug(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK); + outw(0x4000, hc->pci_iobase + 4); + outl(leddw, hc->pci_iobase); + HFC_outb_nodebug(hc, R_BRG_PCM_CFG, V_PCM_CLK); + hc->ledstate = leddw; + } + break; + } +} +/* + * read dtmf coefficients + */ + +static void +hfcmulti_dtmf(struct hfc_multi *hc) +{ + s32 *coeff; + u_int mantissa; + int co, ch; + struct bchannel *bch = NULL; + u8 exponent; + int dtmf = 0; + int addr; + u16 w_float; + struct sk_buff *skb; + struct mISDNhead *hh; + + if (debug & DEBUG_HFCMULTI_DTMF) + printk(KERN_DEBUG "%s: dtmf detection irq\n", __func__); + for (ch = 0; ch <= 31; ch++) { + /* only process enabled B-channels */ + bch = hc->chan[ch].bch; + if (!bch) + continue; + if (!hc->created[hc->chan[ch].port]) + continue; + if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) + continue; + if (debug & DEBUG_HFCMULTI_DTMF) + printk(KERN_DEBUG "%s: dtmf channel %d:", + __func__, ch); + coeff = &(hc->chan[ch].coeff[hc->chan[ch].coeff_count * 16]); + dtmf = 1; + for (co = 0; co < 8; co++) { + /* read W(n-1) coefficient */ + addr = hc->DTMFbase + ((co<<7) | (ch<<2)); + HFC_outb_nodebug(hc, R_RAM_ADDR0, addr); + HFC_outb_nodebug(hc, R_RAM_ADDR1, addr>>8); + HFC_outb_nodebug(hc, R_RAM_ADDR2, (addr>>16) + | V_ADDR_INC); + w_float = HFC_inb_nodebug(hc, R_RAM_DATA); + w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8); + if (debug & DEBUG_HFCMULTI_DTMF) + printk(" %04x", w_float); + + /* decode float (see chip doc) */ + mantissa = w_float & 0x0fff; + if (w_float & 0x8000) + mantissa |= 0xfffff000; + exponent = (w_float>>12) & 0x7; + if (exponent) { + mantissa ^= 0x1000; + mantissa <<= (exponent-1); + } + + /* store coefficient */ + coeff[co<<1] = mantissa; + + /* read W(n) coefficient */ + w_float = HFC_inb_nodebug(hc, R_RAM_DATA); + w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8); + if (debug & DEBUG_HFCMULTI_DTMF) + printk(" %04x", w_float); + + /* decode float (see chip doc) */ + mantissa = w_float & 0x0fff; + if (w_float & 0x8000) + mantissa |= 0xfffff000; + exponent = (w_float>>12) & 0x7; + if (exponent) { + mantissa ^= 0x1000; + mantissa <<= (exponent-1); + } + + /* store coefficient */ + coeff[(co<<1)|1] = mantissa; + } + if (debug & DEBUG_HFCMULTI_DTMF) + printk("%s: DTMF ready %08x %08x %08x %08x " + "%08x %08x %08x %08x\n", __func__, + coeff[0], coeff[1], coeff[2], coeff[3], + coeff[4], coeff[5], coeff[6], coeff[7]); + hc->chan[ch].coeff_count++; + if (hc->chan[ch].coeff_count == 8) { + hc->chan[ch].coeff_count = 0; + skb = mI_alloc_skb(512, GFP_ATOMIC); + if (!skb) { + printk(KERN_WARNING "%s: No memory for skb\n", + __func__); + continue; + } + hh = mISDN_HEAD_P(skb); + hh->prim = PH_CONTROL_IND; + hh->id = DTMF_HFC_COEF; + memcpy(skb_put(skb, 512), hc->chan[ch].coeff, 512); + recv_Bchannel_skb(bch, skb); + } + } + + /* restart DTMF processing */ + hc->dtmf = dtmf; + if (dtmf) + HFC_outb_nodebug(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF); +} + + +/* + * fill fifo as much as possible + */ + +static void +hfcmulti_tx(struct hfc_multi *hc, int ch) +{ + int i, ii, temp, len = 0; + int Zspace, z1, z2; /* must be int for calculation */ + int Fspace, f1, f2; + u_char *d; + int *txpending, slot_tx; + struct bchannel *bch; + struct dchannel *dch; + struct sk_buff **sp = NULL; + int *idxp; + + bch = hc->chan[ch].bch; + dch = hc->chan[ch].dch; + if ((!dch) && (!bch)) + return; + + txpending = &hc->chan[ch].txpending; + slot_tx = hc->chan[ch].slot_tx; + if (dch) { + if (!test_bit(FLG_ACTIVE, &dch->Flags)) + return; + sp = &dch->tx_skb; + idxp = &dch->tx_idx; + } else { + if (!test_bit(FLG_ACTIVE, &bch->Flags)) + return; + sp = &bch->tx_skb; + idxp = &bch->tx_idx; + } + if (*sp) + len = (*sp)->len; + + if ((!len) && *txpending != 1) + return; /* no data */ + + if (test_bit(HFC_CHIP_B410P, &hc->chip) && + (hc->chan[ch].protocol == ISDN_P_B_RAW) && + (hc->chan[ch].slot_rx < 0) && + (hc->chan[ch].slot_tx < 0)) + HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch << 1)); + else + HFC_outb_nodebug(hc, R_FIFO, ch << 1); + HFC_wait_nodebug(hc); + + if (*txpending == 2) { + /* reset fifo */ + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + HFC_outb(hc, A_SUBCH_CFG, 0); + *txpending = 1; + } +next_frame: + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + f1 = HFC_inb_nodebug(hc, A_F1); + f2 = HFC_inb_nodebug(hc, A_F2); + while (f2 != (temp = HFC_inb_nodebug(hc, A_F2))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): reread f2 because %d!=%d\n", + __func__, hc->id + 1, temp, f2); + f2 = temp; /* repeat until F2 is equal */ + } + Fspace = f2 - f1 - 1; + if (Fspace < 0) + Fspace += hc->Flen; + /* + * Old FIFO handling doesn't give us the current Z2 read + * pointer, so we cannot send the next frame before the fifo + * is empty. It makes no difference except for a slightly + * lower performance. + */ + if (test_bit(HFC_CHIP_REVISION0, &hc->chip)) { + if (f1 != f2) + Fspace = 0; + else + Fspace = 1; + } + /* one frame only for ST D-channels, to allow resending */ + if (hc->type != 1 && dch) { + if (f1 != f2) + Fspace = 0; + } + /* F-counter full condition */ + if (Fspace == 0) + return; + } + z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin; + z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin; + while (z2 != (temp = (HFC_inw_nodebug(hc, A_Z2) - hc->Zmin))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): reread z2 because " + "%d!=%d\n", __func__, hc->id + 1, temp, z2); + z2 = temp; /* repeat unti Z2 is equal */ + } + Zspace = z2 - z1; + if (Zspace <= 0) + Zspace += hc->Zlen; + Zspace -= 4; /* keep not too full, so pointers will not overrun */ + /* fill transparent data only to maxinum transparent load (minus 4) */ + if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags)) + Zspace = Zspace - hc->Zlen + hc->max_trans; + if (Zspace <= 0) /* no space of 4 bytes */ + return; + + /* if no data */ + if (!len) { + if (z1 == z2) { /* empty */ + /* if done with FIFO audio data during PCM connection */ + if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && + *txpending && slot_tx >= 0) { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: reconnecting PCM due to no " + "more FIFO data: channel %d " + "slot_tx %d\n", + __func__, ch, slot_tx); + /* connect slot */ + HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | + V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch<<1 | 1); + HFC_wait_nodebug(hc); + HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | + V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch<<1); + HFC_wait_nodebug(hc); + } + *txpending = 0; + } + return; /* no data */ + } + + /* if audio data and connected slot */ + if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && (!*txpending) + && slot_tx >= 0) { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: disconnecting PCM due to " + "FIFO data: channel %d slot_tx %d\n", + __func__, ch, slot_tx); + /* disconnect slot */ + HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch<<1 | 1); + HFC_wait_nodebug(hc); + HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch<<1); + HFC_wait_nodebug(hc); + } + *txpending = 1; + + /* show activity */ + hc->activity[hc->chan[ch].port] = 1; + + /* fill fifo to what we have left */ + ii = len; + if (dch || test_bit(FLG_HDLC, &bch->Flags)) + temp = 1; + else + temp = 0; + i = *idxp; + d = (*sp)->data + i; + if (ii - i > Zspace) + ii = Zspace + i; + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): fifo(%d) has %d bytes space " + "left (z1=%04x, z2=%04x) sending %d of %d bytes %s\n", + __func__, hc->id + 1, ch, Zspace, z1, z2, ii-i, len-i, + temp ? "HDLC":"TRANS"); + + + /* Have to prep the audio data */ + hc->write_fifo(hc, d, ii - i); + *idxp = ii; + + /* if not all data has been written */ + if (ii != len) { + /* NOTE: fifo is started by the calling function */ + return; + } + + /* if all data has been written, terminate frame */ + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + /* increment f-counter */ + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F); + HFC_wait_nodebug(hc); + } + + /* send confirm, since get_net_bframe will not do it with trans */ + if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags)) + confirm_Bsend(bch); + + /* check for next frame */ + dev_kfree_skb(*sp); + if (bch && get_next_bframe(bch)) { /* hdlc is confirmed here */ + len = (*sp)->len; + goto next_frame; + } + if (dch && get_next_dframe(dch)) { + len = (*sp)->len; + goto next_frame; + } + + /* + * now we have no more data, so in case of transparent, + * we set the last byte in fifo to 'silence' in case we will get + * no more data at all. this prevents sending an undefined value. + */ + if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags)) + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence); +} + + +/* NOTE: only called if E1 card is in active state */ +static void +hfcmulti_rx(struct hfc_multi *hc, int ch) +{ + int temp; + int Zsize, z1, z2 = 0; /* = 0, to make GCC happy */ + int f1 = 0, f2 = 0; /* = 0, to make GCC happy */ + int again = 0; + struct bchannel *bch; + struct dchannel *dch; + struct sk_buff *skb, **sp = NULL; + int maxlen; + + bch = hc->chan[ch].bch; + dch = hc->chan[ch].dch; + if ((!dch) && (!bch)) + return; + if (dch) { + if (!test_bit(FLG_ACTIVE, &dch->Flags)) + return; + sp = &dch->rx_skb; + maxlen = dch->maxlen; + } else { + if (!test_bit(FLG_ACTIVE, &bch->Flags)) + return; + sp = &bch->rx_skb; + maxlen = bch->maxlen; + } +next_frame: + /* on first AND before getting next valid frame, R_FIFO must be written + to. */ + if (test_bit(HFC_CHIP_B410P, &hc->chip) && + (hc->chan[ch].protocol == ISDN_P_B_RAW) && + (hc->chan[ch].slot_rx < 0) && + (hc->chan[ch].slot_tx < 0)) + HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch<<1) | 1); + else + HFC_outb_nodebug(hc, R_FIFO, (ch<<1)|1); + HFC_wait_nodebug(hc); + + /* ignore if rx is off BUT change fifo (above) to start pending TX */ + if (hc->chan[ch].rx_off) + return; + + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + f1 = HFC_inb_nodebug(hc, A_F1); + while (f1 != (temp = HFC_inb_nodebug(hc, A_F1))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): reread f1 because %d!=%d\n", + __func__, hc->id + 1, temp, f1); + f1 = temp; /* repeat until F1 is equal */ + } + f2 = HFC_inb_nodebug(hc, A_F2); + } + z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin; + while (z1 != (temp = (HFC_inw_nodebug(hc, A_Z1) - hc->Zmin))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): reread z2 because " + "%d!=%d\n", __func__, hc->id + 1, temp, z2); + z1 = temp; /* repeat until Z1 is equal */ + } + z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin; + Zsize = z1 - z2; + if ((dch || test_bit(FLG_HDLC, &bch->Flags)) && f1 != f2) + /* complete hdlc frame */ + Zsize++; + if (Zsize < 0) + Zsize += hc->Zlen; + /* if buffer is empty */ + if (Zsize <= 0) + return; + + if (*sp == NULL) { + *sp = mI_alloc_skb(maxlen + 3, GFP_ATOMIC); + if (*sp == NULL) { + printk(KERN_DEBUG "%s: No mem for rx_skb\n", + __func__); + return; + } + } + /* show activity */ + hc->activity[hc->chan[ch].port] = 1; + + /* empty fifo with what we have */ + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): fifo(%d) reading %d " + "bytes (z1=%04x, z2=%04x) HDLC %s (f1=%d, f2=%d) " + "got=%d (again %d)\n", __func__, hc->id + 1, ch, + Zsize, z1, z2, (f1 == f2) ? "fragment" : "COMPLETE", + f1, f2, Zsize + (*sp)->len, again); + /* HDLC */ + if ((Zsize + (*sp)->len) > (maxlen + 3)) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): hdlc-frame too large.\n", + __func__, hc->id + 1); + skb_trim(*sp, 0); + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + return; + } + + hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize); + + if (f1 != f2) { + /* increment Z2,F2-counter */ + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F); + HFC_wait_nodebug(hc); + /* check size */ + if ((*sp)->len < 4) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): Frame below minimum " + "size\n", __func__, hc->id + 1); + skb_trim(*sp, 0); + goto next_frame; + } + /* there is at least one complete frame, check crc */ + if ((*sp)->data[(*sp)->len - 1]) { + if (debug & DEBUG_HFCMULTI_CRC) + printk(KERN_DEBUG + "%s: CRC-error\n", __func__); + skb_trim(*sp, 0); + goto next_frame; + } + skb_trim(*sp, (*sp)->len - 3); + if ((*sp)->len < MISDN_COPY_SIZE) { + skb = *sp; + *sp = mI_alloc_skb(skb->len, GFP_ATOMIC); + if (*sp) { + memcpy(skb_put(*sp, skb->len), + skb->data, skb->len); + skb_trim(skb, 0); + } else { + printk(KERN_DEBUG "%s: No mem\n", + __func__); + *sp = skb; + skb = NULL; + } + } else { + skb = NULL; + } + if (debug & DEBUG_HFCMULTI_FIFO) { + printk(KERN_DEBUG "%s(card %d):", + __func__, hc->id + 1); + temp = 0; + while (temp < (*sp)->len) + printk(" %02x", (*sp)->data[temp++]); + printk("\n"); + } + if (dch) + recv_Dchannel(dch); + else + recv_Bchannel(bch); + *sp = skb; + again++; + goto next_frame; + } + /* there is an incomplete frame */ + } else { + /* transparent */ + if (Zsize > skb_tailroom(*sp)) + Zsize = skb_tailroom(*sp); + hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize); + if (((*sp)->len) < MISDN_COPY_SIZE) { + skb = *sp; + *sp = mI_alloc_skb(skb->len, GFP_ATOMIC); + if (*sp) { + memcpy(skb_put(*sp, skb->len), + skb->data, skb->len); + skb_trim(skb, 0); + } else { + printk(KERN_DEBUG "%s: No mem\n", __func__); + *sp = skb; + skb = NULL; + } + } else { + skb = NULL; + } + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): fifo(%d) reading %d bytes " + "(z1=%04x, z2=%04x) TRANS\n", + __func__, hc->id + 1, ch, Zsize, z1, z2); + /* only bch is transparent */ + recv_Bchannel(bch); + *sp = skb; + } +} + + +/* + * Interrupt handler + */ +static void +signal_state_up(struct dchannel *dch, int info, char *msg) +{ + struct sk_buff *skb; + int id, data = info; + + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: %s\n", __func__, msg); + + id = TEI_SAPI | (GROUP_TEI << 8); /* manager address */ + + skb = _alloc_mISDN_skb(MPH_INFORMATION_IND, id, sizeof(data), &data, + GFP_ATOMIC); + if (!skb) + return; + recv_Dchannel_skb(dch, skb); +} + +static inline void +handle_timer_irq(struct hfc_multi *hc) +{ + int ch, temp; + struct dchannel *dch; + u_long flags; + + /* process queued resync jobs */ + if (hc->e1_resync) { + /* lock, so e1_resync gets not changed */ + spin_lock_irqsave(&HFClock, flags); + if (hc->e1_resync & 1) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "Enable SYNC_I\n"); + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC); + /* disable JATT, if RX_SYNC is set */ + if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) + HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX); + } + if (hc->e1_resync & 2) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "Enable jatt PLL\n"); + HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS); + } + if (hc->e1_resync & 4) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "Enable QUARTZ for HFC-E1\n"); + /* set jatt to quartz */ + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC + | V_JATT_OFF); + /* switch to JATT, in case it is not already */ + HFC_outb(hc, R_SYNC_OUT, 0); + } + hc->e1_resync = 0; + spin_unlock_irqrestore(&HFClock, flags); + } + + if (hc->type != 1 || hc->e1_state == 1) + for (ch = 0; ch <= 31; ch++) { + if (hc->created[hc->chan[ch].port]) { + hfcmulti_tx(hc, ch); + /* fifo is started when switching to rx-fifo */ + hfcmulti_rx(hc, ch); + if (hc->chan[ch].dch && + hc->chan[ch].nt_timer > -1) { + dch = hc->chan[ch].dch; + if (!(--hc->chan[ch].nt_timer)) { + schedule_event(dch, + FLG_PHCHANGE); + if (debug & + DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: nt_timer at " + "state %x\n", + __func__, + dch->state); + } + } + } + } + if (hc->type == 1 && hc->created[0]) { + dch = hc->chan[hc->dslot].dch; + if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dslot].cfg)) { + /* LOS */ + temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_SIG_LOS; + if (!temp && hc->chan[hc->dslot].los) + signal_state_up(dch, L1_SIGNAL_LOS_ON, + "LOS detected"); + if (temp && !hc->chan[hc->dslot].los) + signal_state_up(dch, L1_SIGNAL_LOS_OFF, + "LOS gone"); + hc->chan[hc->dslot].los = temp; + } + if (test_bit(HFC_CFG_REPORT_AIS, &hc->chan[hc->dslot].cfg)) { + /* AIS */ + temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_AIS; + if (!temp && hc->chan[hc->dslot].ais) + signal_state_up(dch, L1_SIGNAL_AIS_ON, + "AIS detected"); + if (temp && !hc->chan[hc->dslot].ais) + signal_state_up(dch, L1_SIGNAL_AIS_OFF, + "AIS gone"); + hc->chan[hc->dslot].ais = temp; + } + if (test_bit(HFC_CFG_REPORT_SLIP, &hc->chan[hc->dslot].cfg)) { + /* SLIP */ + temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_RX; + if (!temp && hc->chan[hc->dslot].slip_rx) + signal_state_up(dch, L1_SIGNAL_SLIP_RX, + " bit SLIP detected RX"); + hc->chan[hc->dslot].slip_rx = temp; + temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_TX; + if (!temp && hc->chan[hc->dslot].slip_tx) + signal_state_up(dch, L1_SIGNAL_SLIP_TX, + " bit SLIP detected TX"); + hc->chan[hc->dslot].slip_tx = temp; + } + if (test_bit(HFC_CFG_REPORT_RDI, &hc->chan[hc->dslot].cfg)) { + /* RDI */ + temp = HFC_inb_nodebug(hc, R_RX_SL0_0) & V_A; + if (!temp && hc->chan[hc->dslot].rdi) + signal_state_up(dch, L1_SIGNAL_RDI_ON, + "RDI detected"); + if (temp && !hc->chan[hc->dslot].rdi) + signal_state_up(dch, L1_SIGNAL_RDI_OFF, + "RDI gone"); + hc->chan[hc->dslot].rdi = temp; + } + temp = HFC_inb_nodebug(hc, R_JATT_DIR); + switch (hc->chan[hc->dslot].sync) { + case 0: + if ((temp & 0x60) == 0x60) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 now " + "in clock sync\n", + __func__, hc->id); + HFC_outb(hc, R_RX_OFF, + hc->chan[hc->dslot].jitter | V_RX_INIT); + HFC_outb(hc, R_TX_OFF, + hc->chan[hc->dslot].jitter | V_RX_INIT); + hc->chan[hc->dslot].sync = 1; + goto check_framesync; + } + break; + case 1: + if ((temp & 0x60) != 0x60) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 " + "lost clock sync\n", + __func__, hc->id); + hc->chan[hc->dslot].sync = 0; + break; + } +check_framesync: + temp = HFC_inb_nodebug(hc, R_SYNC_STA); + if (temp == 0x27) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 " + "now in frame sync\n", + __func__, hc->id); + hc->chan[hc->dslot].sync = 2; + } + break; + case 2: + if ((temp & 0x60) != 0x60) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 lost " + "clock & frame sync\n", + __func__, hc->id); + hc->chan[hc->dslot].sync = 0; + break; + } + temp = HFC_inb_nodebug(hc, R_SYNC_STA); + if (temp != 0x27) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 " + "lost frame sync\n", + __func__, hc->id); + hc->chan[hc->dslot].sync = 1; + } + break; + } + } + + if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip)) + hfcmulti_watchdog(hc); + + if (hc->leds) + hfcmulti_leds(hc); +} + +static void +ph_state_irq(struct hfc_multi *hc, u_char r_irq_statech) +{ + struct dchannel *dch; + int ch; + int active; + u_char st_status, temp; + + /* state machine */ + for (ch = 0; ch <= 31; ch++) { + if (hc->chan[ch].dch) { + dch = hc->chan[ch].dch; + if (r_irq_statech & 1) { + HFC_outb_nodebug(hc, R_ST_SEL, + hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + /* undocumented: status changes during read */ + st_status = HFC_inb_nodebug(hc, A_ST_RD_STATE); + while (st_status != (temp = + HFC_inb_nodebug(hc, A_ST_RD_STATE))) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: reread " + "STATE because %d!=%d\n", + __func__, temp, + st_status); + st_status = temp; /* repeat */ + } + + /* Speech Design TE-sync indication */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) && + dch->dev.D.protocol == ISDN_P_TE_S0) { + if (st_status & V_FR_SYNC_ST) + hc->syncronized |= + (1 << hc->chan[ch].port); + else + hc->syncronized &= + ~(1 << hc->chan[ch].port); + } + dch->state = st_status & 0x0f; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + if (dch->state == active) { + HFC_outb_nodebug(hc, R_FIFO, + (ch << 1) | 1); + HFC_wait_nodebug(hc); + HFC_outb_nodebug(hc, + R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + dch->tx_idx = 0; + } + schedule_event(dch, FLG_PHCHANGE); + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: S/T newstate %x port %d\n", + __func__, dch->state, + hc->chan[ch].port); + } + r_irq_statech >>= 1; + } + } + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) + plxsd_checksync(hc, 0); +} + +static void +fifo_irq(struct hfc_multi *hc, int block) +{ + int ch, j; + struct dchannel *dch; + struct bchannel *bch; + u_char r_irq_fifo_bl; + + r_irq_fifo_bl = HFC_inb_nodebug(hc, R_IRQ_FIFO_BL0 + block); + j = 0; + while (j < 8) { + ch = (block << 2) + (j >> 1); + dch = hc->chan[ch].dch; + bch = hc->chan[ch].bch; + if (((!dch) && (!bch)) || (!hc->created[hc->chan[ch].port])) { + j += 2; + continue; + } + if (dch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &dch->Flags)) { + hfcmulti_tx(hc, ch); + /* start fifo */ + HFC_outb_nodebug(hc, R_FIFO, 0); + HFC_wait_nodebug(hc); + } + if (bch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &bch->Flags)) { + hfcmulti_tx(hc, ch); + /* start fifo */ + HFC_outb_nodebug(hc, R_FIFO, 0); + HFC_wait_nodebug(hc); + } + j++; + if (dch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &dch->Flags)) { + hfcmulti_rx(hc, ch); + } + if (bch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &bch->Flags)) { + hfcmulti_rx(hc, ch); + } + j++; + } +} + +#ifdef IRQ_DEBUG +int irqsem; +#endif +static irqreturn_t +hfcmulti_interrupt(int intno, void *dev_id) +{ +#ifdef IRQCOUNT_DEBUG + static int iq1 = 0, iq2 = 0, iq3 = 0, iq4 = 0, + iq5 = 0, iq6 = 0, iqcnt = 0; +#endif + static int count; + struct hfc_multi *hc = dev_id; + struct dchannel *dch; + u_char r_irq_statech, status, r_irq_misc, r_irq_oview; + int i; + u_short *plx_acc, wval; + u_char e1_syncsta, temp; + u_long flags; + + if (!hc) { + printk(KERN_ERR "HFC-multi: Spurious interrupt!\n"); + return IRQ_NONE; + } + + spin_lock(&hc->lock); + +#ifdef IRQ_DEBUG + if (irqsem) + printk(KERN_ERR "irq for card %d during irq from " + "card %d, this is no bug.\n", hc->id + 1, irqsem); + irqsem = hc->id + 1; +#endif + + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, flags); + plx_acc = (u_short *)(hc->plx_membase + PLX_INTCSR); + wval = readw(plx_acc); + spin_unlock_irqrestore(&plx_lock, flags); + if (!(wval & PLX_INTCSR_LINTI1_STATUS)) + goto irq_notforus; + } + + status = HFC_inb_nodebug(hc, R_STATUS); + r_irq_statech = HFC_inb_nodebug(hc, R_IRQ_STATECH); +#ifdef IRQCOUNT_DEBUG + if (r_irq_statech) + iq1++; + if (status & V_DTMF_STA) + iq2++; + if (status & V_LOST_STA) + iq3++; + if (status & V_EXT_IRQSTA) + iq4++; + if (status & V_MISC_IRQSTA) + iq5++; + if (status & V_FR_IRQSTA) + iq6++; + if (iqcnt++ > 5000) { + printk(KERN_ERR "iq1:%x iq2:%x iq3:%x iq4:%x iq5:%x iq6:%x\n", + iq1, iq2, iq3, iq4, iq5, iq6); + iqcnt = 0; + } +#endif + if (!r_irq_statech && + !(status & (V_DTMF_STA | V_LOST_STA | V_EXT_IRQSTA | + V_MISC_IRQSTA | V_FR_IRQSTA))) { + /* irq is not for us */ + goto irq_notforus; + } + hc->irqcnt++; + if (r_irq_statech) { + if (hc->type != 1) + ph_state_irq(hc, r_irq_statech); + } + if (status & V_EXT_IRQSTA) + ; /* external IRQ */ + if (status & V_LOST_STA) { + /* LOST IRQ */ + HFC_outb(hc, R_INC_RES_FIFO, V_RES_LOST); /* clear irq! */ + } + if (status & V_MISC_IRQSTA) { + /* misc IRQ */ + r_irq_misc = HFC_inb_nodebug(hc, R_IRQ_MISC); + if (r_irq_misc & V_STA_IRQ) { + if (hc->type == 1) { + /* state machine */ + dch = hc->chan[hc->dslot].dch; + e1_syncsta = HFC_inb_nodebug(hc, R_SYNC_STA); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) + && hc->e1_getclock) { + if (e1_syncsta & V_FR_SYNC_E1) + hc->syncronized = 1; + else + hc->syncronized = 0; + } + /* undocumented: status changes during read */ + dch->state = HFC_inb_nodebug(hc, R_E1_RD_STA); + while (dch->state != (temp = + HFC_inb_nodebug(hc, R_E1_RD_STA))) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: reread " + "STATE because %d!=%d\n", + __func__, temp, + dch->state); + dch->state = temp; /* repeat */ + } + dch->state = HFC_inb_nodebug(hc, R_E1_RD_STA) + & 0x7; + schedule_event(dch, FLG_PHCHANGE); + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 (id=%d) newstate %x\n", + __func__, hc->id, dch->state); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) + plxsd_checksync(hc, 0); + } + } + if (r_irq_misc & V_TI_IRQ) + handle_timer_irq(hc); + + if (r_irq_misc & V_DTMF_IRQ) { + /* -> DTMF IRQ */ + hfcmulti_dtmf(hc); + } + /* TODO: REPLACE !!!! 125 us Interrupts are not acceptable */ + if (r_irq_misc & V_IRQ_PROC) { + /* IRQ every 125us */ + count++; + /* generate 1kHz signal */ + if (count == 8) { + if (hfc_interrupt) + hfc_interrupt(); + count = 0; + } + } + + } + if (status & V_FR_IRQSTA) { + /* FIFO IRQ */ + r_irq_oview = HFC_inb_nodebug(hc, R_IRQ_OVIEW); + for (i = 0; i < 8; i++) { + if (r_irq_oview & (1 << i)) + fifo_irq(hc, i); + } + } + +#ifdef IRQ_DEBUG + irqsem = 0; +#endif + spin_unlock(&hc->lock); + return IRQ_HANDLED; + +irq_notforus: +#ifdef IRQ_DEBUG + irqsem = 0; +#endif + spin_unlock(&hc->lock); + return IRQ_NONE; +} + + +/* + * timer callback for D-chan busy resolution. Currently no function + */ + +static void +hfcmulti_dbusy_timer(struct hfc_multi *hc) +{ +} + + +/* + * activate/deactivate hardware for selected channels and mode + * + * configure B-channel with the given protocol + * ch eqals to the HFC-channel (0-31) + * ch is the number of channel (0-4,4-7,8-11,12-15,16-19,20-23,24-27,28-31 + * for S/T, 1-31 for E1) + * the hdlc interrupts will be set/unset + */ +static int +mode_hfcmulti(struct hfc_multi *hc, int ch, int protocol, int slot_tx, + int bank_tx, int slot_rx, int bank_rx) +{ + int flow_tx = 0, flow_rx = 0, routing = 0; + int oslot_tx, oslot_rx; + int conf; + + if (ch < 0 || ch > 31) + return EINVAL; + oslot_tx = hc->chan[ch].slot_tx; + oslot_rx = hc->chan[ch].slot_rx; + conf = hc->chan[ch].conf; + + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: card %d channel %d protocol %x slot old=%d new=%d " + "bank new=%d (TX) slot old=%d new=%d bank new=%d (RX)\n", + __func__, hc->id, ch, protocol, oslot_tx, slot_tx, + bank_tx, oslot_rx, slot_rx, bank_rx); + + if (oslot_tx >= 0 && slot_tx != oslot_tx) { + /* remove from slot */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: remove from slot %d (TX)\n", + __func__, oslot_tx); + if (hc->slot_owner[oslot_tx<<1] == ch) { + HFC_outb(hc, R_SLOT, oslot_tx << 1); + HFC_outb(hc, A_SL_CFG, 0); + HFC_outb(hc, A_CONF, 0); + hc->slot_owner[oslot_tx<<1] = -1; + } else { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: we are not owner of this tx slot " + "anymore, channel %d is.\n", + __func__, hc->slot_owner[oslot_tx<<1]); + } + } + + if (oslot_rx >= 0 && slot_rx != oslot_rx) { + /* remove from slot */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: remove from slot %d (RX)\n", + __func__, oslot_rx); + if (hc->slot_owner[(oslot_rx << 1) | 1] == ch) { + HFC_outb(hc, R_SLOT, (oslot_rx << 1) | V_SL_DIR); + HFC_outb(hc, A_SL_CFG, 0); + hc->slot_owner[(oslot_rx << 1) | 1] = -1; + } else { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: we are not owner of this rx slot " + "anymore, channel %d is.\n", + __func__, + hc->slot_owner[(oslot_rx << 1) | 1]); + } + } + + if (slot_tx < 0) { + flow_tx = 0x80; /* FIFO->ST */ + /* disable pcm slot */ + hc->chan[ch].slot_tx = -1; + hc->chan[ch].bank_tx = 0; + } else { + /* set pcm slot */ + if (hc->chan[ch].txpending) + flow_tx = 0x80; /* FIFO->ST */ + else + flow_tx = 0xc0; /* PCM->ST */ + /* put on slot */ + routing = bank_tx ? 0xc0 : 0x80; + if (conf >= 0 || bank_tx > 1) + routing = 0x40; /* loop */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: put channel %d to slot %d bank" + " %d flow %02x routing %02x conf %d (TX)\n", + __func__, ch, slot_tx, bank_tx, + flow_tx, routing, conf); + HFC_outb(hc, R_SLOT, slot_tx << 1); + HFC_outb(hc, A_SL_CFG, (ch<<1) | routing); + HFC_outb(hc, A_CONF, (conf < 0) ? 0 : (conf | V_CONF_SL)); + hc->slot_owner[slot_tx << 1] = ch; + hc->chan[ch].slot_tx = slot_tx; + hc->chan[ch].bank_tx = bank_tx; + } + if (slot_rx < 0) { + /* disable pcm slot */ + flow_rx = 0x80; /* ST->FIFO */ + hc->chan[ch].slot_rx = -1; + hc->chan[ch].bank_rx = 0; + } else { + /* set pcm slot */ + if (hc->chan[ch].txpending) + flow_rx = 0x80; /* ST->FIFO */ + else + flow_rx = 0xc0; /* ST->(FIFO,PCM) */ + /* put on slot */ + routing = bank_rx?0x80:0xc0; /* reversed */ + if (conf >= 0 || bank_rx > 1) + routing = 0x40; /* loop */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: put channel %d to slot %d bank" + " %d flow %02x routing %02x conf %d (RX)\n", + __func__, ch, slot_rx, bank_rx, + flow_rx, routing, conf); + HFC_outb(hc, R_SLOT, (slot_rx<<1) | V_SL_DIR); + HFC_outb(hc, A_SL_CFG, (ch<<1) | V_CH_DIR | routing); + hc->slot_owner[(slot_rx<<1)|1] = ch; + hc->chan[ch].slot_rx = slot_rx; + hc->chan[ch].bank_rx = bank_rx; + } + + switch (protocol) { + case (ISDN_P_NONE): + /* disable TX fifo */ + HFC_outb(hc, R_FIFO, ch << 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + /* disable RX fifo */ + HFC_outb(hc, R_FIFO, (ch<<1)|1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + if (hc->chan[ch].bch && hc->type != 1) { + hc->hw.a_st_ctrl0[hc->chan[ch].port] &= + ((ch & 0x3) == 0)? ~V_B1_EN: ~V_B2_EN; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_CTRL0, + hc->hw.a_st_ctrl0[hc->chan[ch].port]); + } + if (hc->chan[ch].bch) { + test_and_clear_bit(FLG_HDLC, &hc->chan[ch].bch->Flags); + test_and_clear_bit(FLG_TRANSPARENT, + &hc->chan[ch].bch->Flags); + } + break; + case (ISDN_P_B_RAW): /* B-channel */ + + if (test_bit(HFC_CHIP_B410P, &hc->chip) && + (hc->chan[ch].slot_rx < 0) && + (hc->chan[ch].slot_tx < 0)) { + + printk(KERN_DEBUG + "Setting B-channel %d to echo cancelable " + "state on PCM slot %d\n", ch, + ((ch / 4) * 8) + ((ch % 4) * 4) + 1); + printk(KERN_DEBUG + "Enabling pass through for channel\n"); + vpm_out(hc, ch, ((ch / 4) * 8) + + ((ch % 4) * 4) + 1, 0x01); + /* rx path */ + /* S/T -> PCM */ + HFC_outb(hc, R_FIFO, (ch << 1)); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, R_SLOT, (((ch / 4) * 8) + + ((ch % 4) * 4) + 1) << 1); + HFC_outb(hc, A_SL_CFG, 0x80 | (ch << 1)); + + /* PCM -> FIFO */ + HFC_outb(hc, R_FIFO, 0x20 | (ch << 1) | 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) + + ((ch % 4) * 4) + 1) << 1) | 1); + HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1) | 1); + + /* tx path */ + /* PCM -> S/T */ + HFC_outb(hc, R_FIFO, (ch << 1) | 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) + + ((ch % 4) * 4)) << 1) | 1); + HFC_outb(hc, A_SL_CFG, 0x80 | 0x40 | (ch << 1) | 1); + + /* FIFO -> PCM */ + HFC_outb(hc, R_FIFO, 0x20 | (ch << 1)); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + /* tx silence */ + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence); + HFC_outb(hc, R_SLOT, (((ch / 4) * 8) + + ((ch % 4) * 4)) << 1); + HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1)); + } else { + /* enable TX fifo */ + HFC_outb(hc, R_FIFO, ch << 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | + V_HDLC_TRP | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + /* tx silence */ + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence); + /* enable RX fifo */ + HFC_outb(hc, R_FIFO, (ch<<1)|1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00 | V_HDLC_TRP); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + } + if (hc->type != 1) { + hc->hw.a_st_ctrl0[hc->chan[ch].port] |= + ((ch & 0x3) == 0) ? V_B1_EN : V_B2_EN; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_CTRL0, + hc->hw.a_st_ctrl0[hc->chan[ch].port]); + } + if (hc->chan[ch].bch) + test_and_set_bit(FLG_TRANSPARENT, + &hc->chan[ch].bch->Flags); + break; + case (ISDN_P_B_HDLC): /* B-channel */ + case (ISDN_P_TE_S0): /* D-channel */ + case (ISDN_P_NT_S0): + case (ISDN_P_TE_E1): + case (ISDN_P_NT_E1): + /* enable TX fifo */ + HFC_outb(hc, R_FIFO, ch<<1); + HFC_wait(hc); + if (hc->type == 1 || hc->chan[ch].bch) { + /* E1 or B-channel */ + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04); + HFC_outb(hc, A_SUBCH_CFG, 0); + } else { + /* D-Channel without HDLC fill flags */ + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04 | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 2); + } + HFC_outb(hc, A_IRQ_MSK, V_IRQ); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + /* enable RX fifo */ + HFC_outb(hc, R_FIFO, (ch<<1)|1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x04); + if (hc->type == 1 || hc->chan[ch].bch) + HFC_outb(hc, A_SUBCH_CFG, 0); /* full 8 bits */ + else + HFC_outb(hc, A_SUBCH_CFG, 2); /* 2 bits dchannel */ + HFC_outb(hc, A_IRQ_MSK, V_IRQ); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + if (hc->chan[ch].bch) { + test_and_set_bit(FLG_HDLC, &hc->chan[ch].bch->Flags); + if (hc->type != 1) { + hc->hw.a_st_ctrl0[hc->chan[ch].port] |= + ((ch&0x3) == 0) ? V_B1_EN : V_B2_EN; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_CTRL0, + hc->hw.a_st_ctrl0[hc->chan[ch].port]); + } + } + break; + default: + printk(KERN_DEBUG "%s: protocol not known %x\n", + __func__, protocol); + hc->chan[ch].protocol = ISDN_P_NONE; + return -ENOPROTOOPT; + } + hc->chan[ch].protocol = protocol; + return 0; +} + + +/* + * connect/disconnect PCM + */ + +static void +hfcmulti_pcm(struct hfc_multi *hc, int ch, int slot_tx, int bank_tx, + int slot_rx, int bank_rx) +{ + if (slot_rx < 0 || slot_rx < 0 || bank_tx < 0 || bank_rx < 0) { + /* disable PCM */ + mode_hfcmulti(hc, ch, hc->chan[ch].protocol, -1, 0, -1, 0); + return; + } + + /* enable pcm */ + mode_hfcmulti(hc, ch, hc->chan[ch].protocol, slot_tx, bank_tx, + slot_rx, bank_rx); +} + +/* + * set/disable conference + */ + +static void +hfcmulti_conf(struct hfc_multi *hc, int ch, int num) +{ + if (num >= 0 && num <= 7) + hc->chan[ch].conf = num; + else + hc->chan[ch].conf = -1; + mode_hfcmulti(hc, ch, hc->chan[ch].protocol, hc->chan[ch].slot_tx, + hc->chan[ch].bank_tx, hc->chan[ch].slot_rx, + hc->chan[ch].bank_rx); +} + + +/* + * set/disable sample loop + */ + +/* NOTE: this function is experimental and therefore disabled */ + +/* + * Layer 1 callback function + */ +static int +hfcm_l1callback(struct dchannel *dch, u_int cmd) +{ + struct hfc_multi *hc = dch->hw; + u_long flags; + + switch (cmd) { + case INFO3_P8: + case INFO3_P10: + break; + case HW_RESET_REQ: + /* start activation */ + spin_lock_irqsave(&hc->lock, flags); + if (hc->type == 1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HW_RESET_REQ no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 3); /* F3 */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 3); + HFC_outb(hc, A_ST_WR_STATE, 3 | (V_ST_ACT*3)); + /* activate */ + } + spin_unlock_irqrestore(&hc->lock, flags); + l1_event(dch->l1, HW_POWERUP_IND); + break; + case HW_DEACT_REQ: + /* start deactivation */ + spin_lock_irqsave(&hc->lock, flags); + if (hc->type == 1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HW_DEACT_REQ no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT*2); + /* deactivate */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized &= + ~(1 << hc->chan[dch->slot].port); + plxsd_checksync(hc, 0); + } + } + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) + del_timer(&dch->timer); + spin_unlock_irqrestore(&hc->lock, flags); + break; + case HW_POWERUP_REQ: + spin_lock_irqsave(&hc->lock, flags); + if (hc->type == 1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HW_POWERUP_REQ no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, 3 | 0x10); /* activate */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 3); /* activate */ + } + spin_unlock_irqrestore(&hc->lock, flags); + break; + case PH_ACTIVATE_IND: + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + case PH_DEACTIVATE_IND: + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: unknown command %x\n", + __func__, cmd); + return -1; + } + return 0; +} + +/* + * Layer2 -> Layer 1 Transfer + */ + +static int +handle_dmsg(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct hfc_multi *hc = dch->hw; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + int ret = -EINVAL; + unsigned int id; + u_long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + if (skb->len < 1) + break; + spin_lock_irqsave(&hc->lock, flags); + ret = dchannel_senddata(dch, skb); + if (ret > 0) { /* direct TX */ + id = hh->id; /* skb can be freed */ + hfcmulti_tx(hc, dch->slot); + ret = 0; + /* start fifo */ + HFC_outb(hc, R_FIFO, 0); + HFC_wait(hc); + spin_unlock_irqrestore(&hc->lock, flags); + queue_ch_frame(ch, PH_DATA_CNF, id, NULL); + } else + spin_unlock_irqrestore(&hc->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + if (dch->dev.D.protocol != ISDN_P_TE_S0) { + spin_lock_irqsave(&hc->lock, flags); + ret = 0; + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: PH_ACTIVATE port %d (0..%d)\n", + __func__, hc->chan[dch->slot].port, + hc->ports-1); + /* start activation */ + if (hc->type == 1) { + ph_state_change(dch); + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 report state %x \n", + __func__, dch->state); + } else { + HFC_outb(hc, R_ST_SEL, + hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 1); + /* G1 */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 1); + HFC_outb(hc, A_ST_WR_STATE, 1 | + (V_ST_ACT*3)); /* activate */ + dch->state = 1; + } + spin_unlock_irqrestore(&hc->lock, flags); + } else + ret = l1_event(dch->l1, hh->prim); + break; + case PH_DEACTIVATE_REQ: + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + if (dch->dev.D.protocol != ISDN_P_TE_S0) { + spin_lock_irqsave(&hc->lock, flags); + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: PH_DEACTIVATE port %d (0..%d)\n", + __func__, hc->chan[dch->slot].port, + hc->ports-1); + /* start deactivation */ + if (hc->type == 1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: PH_DEACTIVATE no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, + hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT * 2); + /* deactivate */ + dch->state = 1; + } + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) + del_timer(&dch->timer); +#ifdef FIXME + if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) + dchannel_sched_event(&hc->dch, D_CLEARBUSY); +#endif + ret = 0; + spin_unlock_irqrestore(&hc->lock, flags); + } else + ret = l1_event(dch->l1, hh->prim); + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static void +deactivate_bchannel(struct bchannel *bch) +{ + struct hfc_multi *hc = bch->hw; + u_long flags; + + spin_lock_irqsave(&hc->lock, flags); + if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) { + dev_kfree_skb(bch->next_skb); + bch->next_skb = NULL; + } + if (bch->tx_skb) { + dev_kfree_skb(bch->tx_skb); + bch->tx_skb = NULL; + } + bch->tx_idx = 0; + if (bch->rx_skb) { + dev_kfree_skb(bch->rx_skb); + bch->rx_skb = NULL; + } + hc->chan[bch->slot].coeff_count = 0; + test_and_clear_bit(FLG_ACTIVE, &bch->Flags); + test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); + hc->chan[bch->slot].rx_off = 0; + hc->chan[bch->slot].conf = -1; + mode_hfcmulti(hc, bch->slot, ISDN_P_NONE, -1, 0, -1, 0); + spin_unlock_irqrestore(&hc->lock, flags); +} + +static int +handle_bmsg(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfc_multi *hc = bch->hw; + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + unsigned int id; + u_long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + if (!skb->len) + break; + spin_lock_irqsave(&hc->lock, flags); + ret = bchannel_senddata(bch, skb); + if (ret > 0) { /* direct TX */ + id = hh->id; /* skb can be freed */ + hfcmulti_tx(hc, bch->slot); + ret = 0; + /* start fifo */ + HFC_outb_nodebug(hc, R_FIFO, 0); + HFC_wait_nodebug(hc); + if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) { + spin_unlock_irqrestore(&hc->lock, flags); + queue_ch_frame(ch, PH_DATA_CNF, id, NULL); + } else + spin_unlock_irqrestore(&hc->lock, flags); + } else + spin_unlock_irqrestore(&hc->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: PH_ACTIVATE ch %d (0..32)\n", + __func__, bch->slot); + spin_lock_irqsave(&hc->lock, flags); + /* activate B-channel if not already activated */ + if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { + hc->chan[bch->slot].txpending = 0; + ret = mode_hfcmulti(hc, bch->slot, + ch->protocol, + hc->chan[bch->slot].slot_tx, + hc->chan[bch->slot].bank_tx, + hc->chan[bch->slot].slot_rx, + hc->chan[bch->slot].bank_rx); + if (!ret) { + if (ch->protocol == ISDN_P_B_RAW && !hc->dtmf + && test_bit(HFC_CHIP_DTMF, &hc->chip)) { + /* start decoder */ + hc->dtmf = 1; + if (debug & DEBUG_HFCMULTI_DTMF) + printk(KERN_DEBUG + "%s: start dtmf decoder\n", + __func__); + HFC_outb(hc, R_DTMF, hc->hw.r_dtmf | + V_RST_DTMF); + } + } + } else + ret = 0; + spin_unlock_irqrestore(&hc->lock, flags); + if (!ret) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, NULL, + GFP_KERNEL); + break; + case PH_CONTROL_REQ: + spin_lock_irqsave(&hc->lock, flags); + switch (hh->id) { + case HFC_SPL_LOOP_ON: /* set sample loop */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HFC_SPL_LOOP_ON (len = %d)\n", + __func__, skb->len); + ret = 0; + break; + case HFC_SPL_LOOP_OFF: /* set silence */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_SPL_LOOP_OFF\n", + __func__); + ret = 0; + break; + default: + printk(KERN_ERR + "%s: unknown PH_CONTROL_REQ info %x\n", + __func__, hh->id); + ret = -EINVAL; + } + spin_unlock_irqrestore(&hc->lock, flags); + break; + case PH_DEACTIVATE_REQ: + deactivate_bchannel(bch); /* locked there */ + _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, NULL, + GFP_KERNEL); + ret = 0; + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +/* + * bchannel control function + */ +static int +channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + struct dsp_features *features = + (struct dsp_features *)(*((u_long *)&cq->p1)); + struct hfc_multi *hc = bch->hw; + int slot_tx; + int bank_tx; + int slot_rx; + int bank_rx; + int num; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_HFC_OP | MISDN_CTRL_HW_FEATURES_OP + | MISDN_CTRL_RX_OFF; + break; + case MISDN_CTRL_RX_OFF: /* turn off / on rx stream */ + hc->chan[bch->slot].rx_off = !!cq->p1; + if (!hc->chan[bch->slot].rx_off) { + /* reset fifo on rx on */ + HFC_outb_nodebug(hc, R_FIFO, (bch->slot << 1) | 1); + HFC_wait_nodebug(hc); + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + } + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: RX_OFF request (nr=%d off=%d)\n", + __func__, bch->nr, hc->chan[bch->slot].rx_off); + break; + case MISDN_CTRL_HW_FEATURES: /* fill features structure */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HW_FEATURE request\n", + __func__); + /* create confirm */ + features->hfc_id = hc->id; + if (test_bit(HFC_CHIP_DTMF, &hc->chip)) + features->hfc_dtmf = 1; + features->hfc_loops = 0; + if (test_bit(HFC_CHIP_B410P, &hc->chip)) { + features->hfc_echocanhw = 1; + } else { + features->pcm_id = hc->pcm; + features->pcm_slots = hc->slots; + features->pcm_banks = 2; + } + break; + case MISDN_CTRL_HFC_PCM_CONN: /* connect to pcm timeslot (0..N) */ + slot_tx = cq->p1 & 0xff; + bank_tx = cq->p1 >> 8; + slot_rx = cq->p2 & 0xff; + bank_rx = cq->p2 >> 8; + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HFC_PCM_CONN slot %d bank %d (TX) " + "slot %d bank %d (RX)\n", + __func__, slot_tx, bank_tx, + slot_rx, bank_rx); + if (slot_tx < hc->slots && bank_tx <= 2 && + slot_rx < hc->slots && bank_rx <= 2) + hfcmulti_pcm(hc, bch->slot, + slot_tx, bank_tx, slot_rx, bank_rx); + else { + printk(KERN_WARNING + "%s: HFC_PCM_CONN slot %d bank %d (TX) " + "slot %d bank %d (RX) out of range\n", + __func__, slot_tx, bank_tx, + slot_rx, bank_rx); + ret = -EINVAL; + } + break; + case MISDN_CTRL_HFC_PCM_DISC: /* release interface from pcm timeslot */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_PCM_DISC\n", + __func__); + hfcmulti_pcm(hc, bch->slot, -1, 0, -1, 0); + break; + case MISDN_CTRL_HFC_CONF_JOIN: /* join conference (0..7) */ + num = cq->p1 & 0xff; + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_CONF_JOIN conf %d\n", + __func__, num); + if (num <= 7) + hfcmulti_conf(hc, bch->slot, num); + else { + printk(KERN_WARNING + "%s: HW_CONF_JOIN conf %d out of range\n", + __func__, num); + ret = -EINVAL; + } + break; + case MISDN_CTRL_HFC_CONF_SPLIT: /* split conference */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_CONF_SPLIT\n", __func__); + hfcmulti_conf(hc, bch->slot, -1); + break; + case MISDN_CTRL_HFC_ECHOCAN_ON: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_ECHOCAN_ON\n", __func__); + if (test_bit(HFC_CHIP_B410P, &hc->chip)) + vpm_echocan_on(hc, bch->slot, cq->p1); + else + ret = -EINVAL; + break; + + case MISDN_CTRL_HFC_ECHOCAN_OFF: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_ECHOCAN_OFF\n", + __func__); + if (test_bit(HFC_CHIP_B410P, &hc->chip)) + vpm_echocan_off(hc, bch->slot); + else + ret = -EINVAL; + break; + default: + printk(KERN_WARNING "%s: unknown Op %x\n", + __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +static int +hfcm_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfc_multi *hc = bch->hw; + int err = -EINVAL; + u_long flags; + + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", + __func__, cmd, arg); + switch (cmd) { + case CLOSE_CHANNEL: + test_and_clear_bit(FLG_OPEN, &bch->Flags); + if (test_bit(FLG_ACTIVE, &bch->Flags)) + deactivate_bchannel(bch); /* locked there */ + ch->protocol = ISDN_P_NONE; + ch->peer = NULL; + module_put(THIS_MODULE); + err = 0; + break; + case CONTROL_CHANNEL: + spin_lock_irqsave(&hc->lock, flags); + err = channel_bctrl(bch, arg); + spin_unlock_irqrestore(&hc->lock, flags); + break; + default: + printk(KERN_WARNING "%s: unknown prim(%x)\n", + __func__, cmd); + } + return err; +} + +/* + * handle D-channel events + * + * handle state change event + */ +static void +ph_state_change(struct dchannel *dch) +{ + struct hfc_multi *hc = dch->hw; + int ch, i; + + if (!dch) { + printk(KERN_WARNING "%s: ERROR given dch is NULL\n", + __func__); + return; + } + ch = dch->slot; + + if (hc->type == 1) { + if (dch->dev.D.protocol == ISDN_P_TE_E1) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 TE (id=%d) newstate %x\n", + __func__, hc->id, dch->state); + } else { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 NT (id=%d) newstate %x\n", + __func__, hc->id, dch->state); + } + switch (dch->state) { + case (1): + if (hc->e1_state != 1) { + for (i = 1; i <= 31; i++) { + /* reset fifos on e1 activation */ + HFC_outb_nodebug(hc, R_FIFO, (i << 1) | 1); + HFC_wait_nodebug(hc); + HFC_outb_nodebug(hc, + R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + } + } + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + + default: + if (hc->e1_state != 1) + return; + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + } + hc->e1_state = dch->state; + } else { + if (dch->dev.D.protocol == ISDN_P_TE_S0) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: S/T TE newstate %x\n", + __func__, dch->state); + switch (dch->state) { + case (0): + l1_event(dch->l1, HW_RESET_IND); + break; + case (3): + l1_event(dch->l1, HW_DEACT_IND); + break; + case (5): + case (8): + l1_event(dch->l1, ANYSIGNAL); + break; + case (6): + l1_event(dch->l1, INFO2); + break; + case (7): + l1_event(dch->l1, INFO4_P8); + break; + } + } else { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: S/T NT newstate %x\n", + __func__, dch->state); + switch (dch->state) { + case (2): + if (hc->chan[ch].nt_timer == 0) { + hc->chan[ch].nt_timer = -1; + HFC_outb(hc, R_ST_SEL, + hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, 4 | + V_ST_LD_STA); /* G4 */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 4); + dch->state = 4; + } else { + /* one extra count for the next event */ + hc->chan[ch].nt_timer = + nt_t1_count[poll_timer] + 1; + HFC_outb(hc, R_ST_SEL, + hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + /* allow G2 -> G3 transition */ + HFC_outb(hc, A_ST_WR_STATE, 2 | + V_SET_G2_G3); + } + break; + case (1): + hc->chan[ch].nt_timer = -1; + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + case (4): + hc->chan[ch].nt_timer = -1; + break; + case (3): + hc->chan[ch].nt_timer = -1; + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + } + } + } +} + +/* + * called for card mode init message + */ + +static void +hfcmulti_initmode(struct dchannel *dch) +{ + struct hfc_multi *hc = dch->hw; + u_char a_st_wr_state, r_e1_wr_sta; + int i, pt; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + + if (hc->type == 1) { + hc->chan[hc->dslot].slot_tx = -1; + hc->chan[hc->dslot].slot_rx = -1; + hc->chan[hc->dslot].conf = -1; + if (hc->dslot) { + mode_hfcmulti(hc, hc->dslot, dch->dev.D.protocol, + -1, 0, -1, 0); + dch->timer.function = (void *) hfcmulti_dbusy_timer; + dch->timer.data = (long) dch; + init_timer(&dch->timer); + } + for (i = 1; i <= 31; i++) { + if (i == hc->dslot) + continue; + hc->chan[i].slot_tx = -1; + hc->chan[i].slot_rx = -1; + hc->chan[i].conf = -1; + mode_hfcmulti(hc, i, ISDN_P_NONE, -1, 0, -1, 0); + } + /* E1 */ + if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dslot].cfg)) { + HFC_outb(hc, R_LOS0, 255); /* 2 ms */ + HFC_outb(hc, R_LOS1, 255); /* 512 ms */ + } + if (test_bit(HFC_CFG_OPTICAL, &hc->chan[hc->dslot].cfg)) { + HFC_outb(hc, R_RX0, 0); + hc->hw.r_tx0 = 0 | V_OUT_EN; + } else { + HFC_outb(hc, R_RX0, 1); + hc->hw.r_tx0 = 1 | V_OUT_EN; + } + hc->hw.r_tx1 = V_ATX | V_NTRI; + HFC_outb(hc, R_TX0, hc->hw.r_tx0); + HFC_outb(hc, R_TX1, hc->hw.r_tx1); + HFC_outb(hc, R_TX_FR0, 0x00); + HFC_outb(hc, R_TX_FR1, 0xf8); + + if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dslot].cfg)) + HFC_outb(hc, R_TX_FR2, V_TX_MF | V_TX_E | V_NEG_E); + + HFC_outb(hc, R_RX_FR0, V_AUTO_RESYNC | V_AUTO_RECO | 0); + + if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dslot].cfg)) + HFC_outb(hc, R_RX_FR1, V_RX_MF | V_RX_MF_SYNC); + + if (dch->dev.D.protocol == ISDN_P_NT_E1) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: E1 port is NT-mode\n", + __func__); + r_e1_wr_sta = 0; /* G0 */ + hc->e1_getclock = 0; + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: E1 port is TE-mode\n", + __func__); + r_e1_wr_sta = 0; /* F0 */ + hc->e1_getclock = 1; + } + if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) + HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX); + else + HFC_outb(hc, R_SYNC_OUT, 0); + if (test_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip)) + hc->e1_getclock = 1; + if (test_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip)) + hc->e1_getclock = 0; + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + /* SLAVE (clock master) */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: E1 port is clock master " + "(clock from PCM)\n", __func__); + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | V_PCM_SYNC); + } else { + if (hc->e1_getclock) { + /* MASTER (clock slave) */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: E1 port is clock slave " + "(clock to PCM)\n", __func__); + HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS); + } else { + /* MASTER (clock master) */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: E1 port is " + "clock master " + "(clock from QUARTZ)\n", + __func__); + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | + V_PCM_SYNC | V_JATT_OFF); + HFC_outb(hc, R_SYNC_OUT, 0); + } + } + HFC_outb(hc, R_JATT_ATT, 0x9c); /* undoc register */ + HFC_outb(hc, R_PWM_MD, V_PWM0_MD); + HFC_outb(hc, R_PWM0, 0x50); + HFC_outb(hc, R_PWM1, 0xff); + /* state machine setup */ + HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta | V_E1_LD_STA); + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized = 0; + plxsd_checksync(hc, 0); + } + } else { + i = dch->slot; + hc->chan[i].slot_tx = -1; + hc->chan[i].slot_rx = -1; + hc->chan[i].conf = -1; + mode_hfcmulti(hc, i, dch->dev.D.protocol, -1, 0, -1, 0); + dch->timer.function = (void *)hfcmulti_dbusy_timer; + dch->timer.data = (long) dch; + init_timer(&dch->timer); + hc->chan[i - 2].slot_tx = -1; + hc->chan[i - 2].slot_rx = -1; + hc->chan[i - 2].conf = -1; + mode_hfcmulti(hc, i - 2, ISDN_P_NONE, -1, 0, -1, 0); + hc->chan[i - 1].slot_tx = -1; + hc->chan[i - 1].slot_rx = -1; + hc->chan[i - 1].conf = -1; + mode_hfcmulti(hc, i - 1, ISDN_P_NONE, -1, 0, -1, 0); + /* ST */ + pt = hc->chan[i].port; + /* select interface */ + HFC_outb(hc, R_ST_SEL, pt); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + if (dch->dev.D.protocol == ISDN_P_NT_S0) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: ST port %d is NT-mode\n", + __func__, pt); + /* clock delay */ + HFC_outb(hc, A_ST_CLK_DLY, clockdelay_nt); + a_st_wr_state = 1; /* G1 */ + hc->hw.a_st_ctrl0[pt] = V_ST_MD; + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: ST port %d is TE-mode\n", + __func__, pt); + /* clock delay */ + HFC_outb(hc, A_ST_CLK_DLY, clockdelay_te); + a_st_wr_state = 2; /* F2 */ + hc->hw.a_st_ctrl0[pt] = 0; + } + if (!test_bit(HFC_CFG_NONCAP_TX, &hc->chan[i].cfg)) + hc->hw.a_st_ctrl0[pt] |= V_TX_LI; + /* line setup */ + HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[pt]); + /* disable E-channel */ + if ((dch->dev.D.protocol == ISDN_P_NT_S0) || + test_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[i].cfg)) + HFC_outb(hc, A_ST_CTRL1, V_E_IGNO); + else + HFC_outb(hc, A_ST_CTRL1, 0); + /* enable B-channel receive */ + HFC_outb(hc, A_ST_CTRL2, V_B1_RX_EN | V_B2_RX_EN); + /* state machine setup */ + HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state | V_ST_LD_STA); + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state); + hc->hw.r_sci_msk |= 1 << pt; + /* state machine interrupts */ + HFC_outb(hc, R_SCI_MSK, hc->hw.r_sci_msk); + /* unset sync on port */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized &= + ~(1 << hc->chan[dch->slot].port); + plxsd_checksync(hc, 0); + } + } + if (debug & DEBUG_HFCMULTI_INIT) + printk("%s: done\n", __func__); +} + + +static int +open_dchannel(struct hfc_multi *hc, struct dchannel *dch, + struct channel_req *rq) +{ + int err = 0; + u_long flags; + + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__, + dch->dev.id, __builtin_return_address(0)); + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + if ((dch->dev.D.protocol != ISDN_P_NONE) && + (dch->dev.D.protocol != rq->protocol)) { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_WARNING "%s: change protocol %x to %x\n", + __func__, dch->dev.D.protocol, rq->protocol); + } + if ((dch->dev.D.protocol == ISDN_P_TE_S0) + && (rq->protocol != ISDN_P_TE_S0)) + l1_event(dch->l1, CLOSE_CHANNEL); + if (dch->dev.D.protocol != rq->protocol) { + if (rq->protocol == ISDN_P_TE_S0) { + err = create_l1(dch, hfcm_l1callback); + if (err) + return err; + } + dch->dev.D.protocol = rq->protocol; + spin_lock_irqsave(&hc->lock, flags); + hfcmulti_initmode(dch); + spin_unlock_irqrestore(&hc->lock, flags); + } + + if (((rq->protocol == ISDN_P_NT_S0) && (dch->state == 3)) || + ((rq->protocol == ISDN_P_TE_S0) && (dch->state == 7)) || + ((rq->protocol == ISDN_P_NT_E1) && (dch->state == 1)) || + ((rq->protocol == ISDN_P_TE_E1) && (dch->state == 1))) { + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + } + rq->ch = &dch->dev.D; + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", __func__); + return 0; +} + +static int +open_bchannel(struct hfc_multi *hc, struct dchannel *dch, + struct channel_req *rq) +{ + struct bchannel *bch; + int ch; + + if (!test_bit(rq->adr.channel, &dch->dev.channelmap[0])) + return -EINVAL; + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + if (hc->type == 1) + ch = rq->adr.channel; + else + ch = (rq->adr.channel - 1) + (dch->slot - 2); + bch = hc->chan[ch].bch; + if (!bch) { + printk(KERN_ERR "%s:internal error ch %d has no bch\n", + __func__, ch); + return -EINVAL; + } + if (test_and_set_bit(FLG_OPEN, &bch->Flags)) + return -EBUSY; /* b-channel can be only open once */ + bch->ch.protocol = rq->protocol; + hc->chan[ch].rx_off = 0; + rq->ch = &bch->ch; + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", __func__); + return 0; +} + +/* + * device control function + */ +static int +channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = 0; + break; + default: + printk(KERN_WARNING "%s: unknown Op %x\n", + __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +static int +hfcm_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct hfc_multi *hc = dch->hw; + struct channel_req *rq; + int err = 0; + u_long flags; + + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", + __func__, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + rq = arg; + switch (rq->protocol) { + case ISDN_P_TE_S0: + case ISDN_P_NT_S0: + if (hc->type == 1) { + err = -EINVAL; + break; + } + err = open_dchannel(hc, dch, rq); /* locked there */ + break; + case ISDN_P_TE_E1: + case ISDN_P_NT_E1: + if (hc->type != 1) { + err = -EINVAL; + break; + } + err = open_dchannel(hc, dch, rq); /* locked there */ + break; + default: + spin_lock_irqsave(&hc->lock, flags); + err = open_bchannel(hc, dch, rq); + spin_unlock_irqrestore(&hc->lock, flags); + } + break; + case CLOSE_CHANNEL: + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) close from %p\n", + __func__, dch->dev.id, + __builtin_return_address(0)); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: + spin_lock_irqsave(&hc->lock, flags); + err = channel_dctrl(dch, arg); + spin_unlock_irqrestore(&hc->lock, flags); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: unknown command %x\n", + __func__, cmd); + err = -EINVAL; + } + return err; +} + +/* + * initialize the card + */ + +/* + * start timer irq, wait some time and check if we have interrupts. + * if not, reset chip and try again. + */ +static int +init_card(struct hfc_multi *hc) +{ + int err = -EIO; + u_long flags; + u_short *plx_acc; + u_long plx_flags; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + + spin_lock_irqsave(&hc->lock, flags); + /* set interrupts but leave global interrupt disabled */ + hc->hw.r_irq_ctrl = V_FIFO_IRQ; + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + + if (request_irq(hc->pci_dev->irq, hfcmulti_interrupt, IRQF_SHARED, + "HFC-multi", hc)) { + printk(KERN_WARNING "mISDN: Could not get interrupt %d.\n", + hc->pci_dev->irq); + return -EIO; + } + hc->irq = hc->pci_dev->irq; + + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc = (u_short *)(hc->plx_membase+PLX_INTCSR); + writew((PLX_INTCSR_PCIINT_ENABLE | PLX_INTCSR_LINTI1_ENABLE), + plx_acc); /* enable PCI & LINT1 irq */ + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: IRQ %d count %d\n", + __func__, hc->irq, hc->irqcnt); + err = init_chip(hc); + if (err) + goto error; + /* + * Finally enable IRQ output + * this is only allowed, if an IRQ routine is allready + * established for this HFC, so don't do that earlier + */ + spin_lock_irqsave(&hc->lock, flags); + enable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + /* printk(KERN_DEBUG "no master irq set!!!\n"); */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100*HZ)/1000); /* Timeout 100ms */ + /* turn IRQ off until chip is completely initialized */ + spin_lock_irqsave(&hc->lock, flags); + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: IRQ %d count %d\n", + __func__, hc->irq, hc->irqcnt); + if (hc->irqcnt) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done\n", __func__); + + return 0; + } + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + printk(KERN_INFO "ignoring missing interrupts\n"); + return 0; + } + + printk(KERN_ERR "HFC PCI: IRQ(%d) getting no interrupts during init.\n", + hc->irq); + + err = -EIO; + +error: + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc = (u_short *)(hc->plx_membase+PLX_INTCSR); + writew(0x00, plx_acc); /*disable IRQs*/ + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: free irq %d\n", __func__, hc->irq); + if (hc->irq) { + free_irq(hc->irq, hc); + hc->irq = 0; + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done (err=%d)\n", __func__, err); + return err; +} + +/* + * find pci device and set it up + */ + +static int +setup_pci(struct hfc_multi *hc, struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct hm_map *m = (struct hm_map *)ent->driver_data; + + printk(KERN_INFO + "HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n", + m->vendor_name, m->card_name, m->clock2 ? "double" : "normal"); + + hc->pci_dev = pdev; + if (m->clock2) + test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip); + + if (ent->device == 0xB410) { + test_and_set_bit(HFC_CHIP_B410P, &hc->chip); + test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip); + test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + hc->slots = 32; + } + + if (hc->pci_dev->irq <= 0) { + printk(KERN_WARNING "HFC-multi: No IRQ for PCI card found.\n"); + return -EIO; + } + if (pci_enable_device(hc->pci_dev)) { + printk(KERN_WARNING "HFC-multi: Error enabling PCI card.\n"); + return -EIO; + } + hc->leds = m->leds; + hc->ledstate = 0xAFFEAFFE; + hc->opticalsupport = m->opticalsupport; + + /* set memory access methods */ + if (m->io_mode) /* use mode from card config */ + hc->io_mode = m->io_mode; + switch (hc->io_mode) { + case HFC_IO_MODE_PLXSD: + test_and_set_bit(HFC_CHIP_PLXSD, &hc->chip); + hc->slots = 128; /* required */ + /* fall through */ + case HFC_IO_MODE_PCIMEM: + hc->HFC_outb = HFC_outb_pcimem; + hc->HFC_inb = HFC_inb_pcimem; + hc->HFC_inw = HFC_inw_pcimem; + hc->HFC_wait = HFC_wait_pcimem; + hc->read_fifo = read_fifo_pcimem; + hc->write_fifo = write_fifo_pcimem; + break; + case HFC_IO_MODE_REGIO: + hc->HFC_outb = HFC_outb_regio; + hc->HFC_inb = HFC_inb_regio; + hc->HFC_inw = HFC_inw_regio; + hc->HFC_wait = HFC_wait_regio; + hc->read_fifo = read_fifo_regio; + hc->write_fifo = write_fifo_regio; + break; + default: + printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + hc->HFC_outb_nodebug = hc->HFC_outb; + hc->HFC_inb_nodebug = hc->HFC_inb; + hc->HFC_inw_nodebug = hc->HFC_inw; + hc->HFC_wait_nodebug = hc->HFC_wait; +#ifdef HFC_REGISTER_DEBUG + hc->HFC_outb = HFC_outb_debug; + hc->HFC_inb = HFC_inb_debug; + hc->HFC_inw = HFC_inw_debug; + hc->HFC_wait = HFC_wait_debug; +#endif + hc->pci_iobase = 0; + hc->pci_membase = NULL; + hc->plx_membase = NULL; + + switch (hc->io_mode) { + case HFC_IO_MODE_PLXSD: + hc->plx_origmembase = hc->pci_dev->resource[0].start; + /* MEMBASE 1 is PLX PCI Bridge */ + + if (!hc->plx_origmembase) { + printk(KERN_WARNING + "HFC-multi: No IO-Memory for PCI PLX bridge found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + hc->plx_membase = ioremap(hc->plx_origmembase, 0x80); + if (!hc->plx_membase) { + printk(KERN_WARNING + "HFC-multi: failed to remap plx address space. " + "(internal error)\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + printk(KERN_INFO + "HFC-multi: plx_membase:%#lx plx_origmembase:%#lx\n", + (u_long)hc->plx_membase, hc->plx_origmembase); + + hc->pci_origmembase = hc->pci_dev->resource[2].start; + /* MEMBASE 1 is PLX PCI Bridge */ + if (!hc->pci_origmembase) { + printk(KERN_WARNING + "HFC-multi: No IO-Memory for PCI card found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + hc->pci_membase = ioremap(hc->pci_origmembase, 0x400); + if (!hc->pci_membase) { + printk(KERN_WARNING "HFC-multi: failed to remap io " + "address space. (internal error)\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + printk(KERN_INFO + "card %d: defined at MEMBASE %#lx (%#lx) IRQ %d HZ %d " + "leds-type %d\n", + hc->id, (u_long)hc->pci_membase, hc->pci_origmembase, + hc->pci_dev->irq, HZ, hc->leds); + pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO); + break; + case HFC_IO_MODE_PCIMEM: + hc->pci_origmembase = hc->pci_dev->resource[1].start; + if (!hc->pci_origmembase) { + printk(KERN_WARNING + "HFC-multi: No IO-Memory for PCI card found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + hc->pci_membase = ioremap(hc->pci_origmembase, 256); + if (!hc->pci_membase) { + printk(KERN_WARNING + "HFC-multi: failed to remap io address space. " + "(internal error)\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + printk(KERN_INFO "card %d: defined at MEMBASE %#lx (%#lx) IRQ %d " + "HZ %d leds-type %d\n", hc->id, (u_long)hc->pci_membase, + hc->pci_origmembase, hc->pci_dev->irq, HZ, hc->leds); + pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO); + break; + case HFC_IO_MODE_REGIO: + hc->pci_iobase = (u_int) hc->pci_dev->resource[0].start; + if (!hc->pci_iobase) { + printk(KERN_WARNING + "HFC-multi: No IO for PCI card found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + if (!request_region(hc->pci_iobase, 8, "hfcmulti")) { + printk(KERN_WARNING "HFC-multi: failed to request " + "address space at 0x%08lx (internal error)\n", + hc->pci_iobase); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + printk(KERN_INFO + "%s %s: defined at IOBASE %#x IRQ %d HZ %d leds-type %d\n", + m->vendor_name, m->card_name, (u_int) hc->pci_iobase, + hc->pci_dev->irq, HZ, hc->leds); + pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_REGIO); + break; + default: + printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + pci_set_drvdata(hc->pci_dev, hc); + + /* At this point the needed PCI config is done */ + /* fifos are still not enabled */ + return 0; +} + + +/* + * remove port + */ + +static void +release_port(struct hfc_multi *hc, struct dchannel *dch) +{ + int pt, ci, i = 0; + u_long flags; + struct bchannel *pb; + + ci = dch->slot; + pt = hc->chan[ci].port; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered for port %d\n", + __func__, pt + 1); + + if (pt >= hc->ports) { + printk(KERN_WARNING "%s: ERROR port out of range (%d).\n", + __func__, pt + 1); + return; + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: releasing port=%d\n", + __func__, pt + 1); + + if (dch->dev.D.protocol == ISDN_P_TE_S0) + l1_event(dch->l1, CLOSE_CHANNEL); + + hc->chan[ci].dch = NULL; + + if (hc->created[pt]) { + hc->created[pt] = 0; + mISDN_unregister_device(&dch->dev); + } + + spin_lock_irqsave(&hc->lock, flags); + + if (dch->timer.function) { + del_timer(&dch->timer); + dch->timer.function = NULL; + } + + if (hc->type == 1) { /* E1 */ + /* remove sync */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized = 0; + plxsd_checksync(hc, 1); + } + /* free channels */ + for (i = 0; i <= 31; i++) { + if (hc->chan[i].bch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: free port %d channel %d\n", + __func__, hc->chan[i].port+1, i); + pb = hc->chan[i].bch; + hc->chan[i].bch = NULL; + spin_unlock_irqrestore(&hc->lock, flags); + mISDN_freebchannel(pb); + kfree(pb); + kfree(hc->chan[i].coeff); + spin_lock_irqsave(&hc->lock, flags); + } + } + } else { + /* remove sync */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized &= + ~(1 << hc->chan[ci].port); + plxsd_checksync(hc, 1); + } + /* free channels */ + if (hc->chan[ci - 2].bch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: free port %d channel %d\n", + __func__, hc->chan[ci - 2].port+1, + ci - 2); + pb = hc->chan[ci - 2].bch; + hc->chan[ci - 2].bch = NULL; + spin_unlock_irqrestore(&hc->lock, flags); + mISDN_freebchannel(pb); + kfree(pb); + kfree(hc->chan[ci - 2].coeff); + spin_lock_irqsave(&hc->lock, flags); + } + if (hc->chan[ci - 1].bch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: free port %d channel %d\n", + __func__, hc->chan[ci - 1].port+1, + ci - 1); + pb = hc->chan[ci - 1].bch; + hc->chan[ci - 1].bch = NULL; + spin_unlock_irqrestore(&hc->lock, flags); + mISDN_freebchannel(pb); + kfree(pb); + kfree(hc->chan[ci - 1].coeff); + spin_lock_irqsave(&hc->lock, flags); + } + } + + spin_unlock_irqrestore(&hc->lock, flags); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: free port %d channel D\n", __func__, pt); + mISDN_freedchannel(dch); + kfree(dch); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done!\n", __func__); +} + +static void +release_card(struct hfc_multi *hc) +{ + u_long flags; + int ch; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: release card (%d) entered\n", + __func__, hc->id); + + spin_lock_irqsave(&hc->lock, flags); + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + + udelay(1000); + + /* dimm leds */ + if (hc->leds) + hfcmulti_leds(hc); + + /* disable D-channels & B-channels */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: disable all channels (d and b)\n", + __func__); + for (ch = 0; ch <= 31; ch++) { + if (hc->chan[ch].dch) + release_port(hc, hc->chan[ch].dch); + } + + /* release hardware & irq */ + if (hc->irq) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: free irq %d\n", + __func__, hc->irq); + free_irq(hc->irq, hc); + hc->irq = 0; + + } + release_io_hfcmulti(hc); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: remove instance from list\n", + __func__); + list_del(&hc->list); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: delete instance\n", __func__); + if (hc == syncmaster) + syncmaster = NULL; + kfree(hc); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: card successfully removed\n", + __func__); +} + +static int +init_e1_port(struct hfc_multi *hc, struct hm_map *m) +{ + struct dchannel *dch; + struct bchannel *bch; + int ch, ret = 0; + char name[MISDN_MAX_IDLEN]; + + dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL); + if (!dch) + return -ENOMEM; + dch->debug = debug; + mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change); + dch->hw = hc; + dch->dev.Dprotocols = (1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1); + dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + dch->dev.D.send = handle_dmsg; + dch->dev.D.ctrl = hfcm_dctrl; + dch->dev.nrbchan = (hc->dslot)?30:31; + dch->slot = hc->dslot; + hc->chan[hc->dslot].dch = dch; + hc->chan[hc->dslot].port = 0; + hc->chan[hc->dslot].nt_timer = -1; + for (ch = 1; ch <= 31; ch++) { + if (ch == hc->dslot) /* skip dchannel */ + continue; + bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL); + if (!bch) { + printk(KERN_ERR "%s: no memory for bchannel\n", + __func__); + ret = -ENOMEM; + goto free_chan; + } + hc->chan[ch].coeff = kzalloc(512, GFP_KERNEL); + if (!hc->chan[ch].coeff) { + printk(KERN_ERR "%s: no memory for coeffs\n", + __func__); + ret = -ENOMEM; + goto free_chan; + } + bch->nr = ch; + bch->slot = ch; + bch->debug = debug; + mISDN_initbchannel(bch, MAX_DATA_MEM); + bch->hw = hc; + bch->ch.send = handle_bmsg; + bch->ch.ctrl = hfcm_bctrl; + bch->ch.nr = ch; + list_add(&bch->ch.list, &dch->dev.bchannels); + hc->chan[ch].bch = bch; + hc->chan[ch].port = 0; + test_and_set_bit(bch->nr, &dch->dev.channelmap[0]); + } + /* set optical line type */ + if (port[Port_cnt] & 0x001) { + if (!m->opticalsupport) { + printk(KERN_INFO + "This board has no optical " + "support\n"); + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set optical " + "interfacs: card(%d) " + "port(%d)\n", + __func__, + HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_OPTICAL, + &hc->chan[hc->dslot].cfg); + } + } + /* set LOS report */ + if (port[Port_cnt] & 0x004) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT set " + "LOS report: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_LOS, + &hc->chan[hc->dslot].cfg); + } + /* set AIS report */ + if (port[Port_cnt] & 0x008) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT set " + "AIS report: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_AIS, + &hc->chan[hc->dslot].cfg); + } + /* set SLIP report */ + if (port[Port_cnt] & 0x010) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set SLIP report: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_SLIP, + &hc->chan[hc->dslot].cfg); + } + /* set RDI report */ + if (port[Port_cnt] & 0x020) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set RDI report: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_RDI, + &hc->chan[hc->dslot].cfg); + } + /* set CRC-4 Mode */ + if (!(port[Port_cnt] & 0x100)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT turn on CRC4 report:" + " card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_CRC4, + &hc->chan[hc->dslot].cfg); + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT turn off CRC4" + " report: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + } + /* set forced clock */ + if (port[Port_cnt] & 0x0200) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT force getting clock from " + "E1: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip); + } else + if (port[Port_cnt] & 0x0400) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT force putting clock to " + "E1: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip); + } + /* set JATT PLL */ + if (port[Port_cnt] & 0x0800) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT disable JATT PLL on " + "E1: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CHIP_RX_SYNC, &hc->chip); + } + /* set elastic jitter buffer */ + if (port[Port_cnt] & 0x3000) { + hc->chan[hc->dslot].jitter = (port[Port_cnt]>>12) & 0x3; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set elastic " + "buffer to %d: card(%d) port(%d)\n", + __func__, hc->chan[hc->dslot].jitter, + HFC_cnt + 1, 1); + } else + hc->chan[hc->dslot].jitter = 2; /* default */ + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d", HFC_cnt + 1); + ret = mISDN_register_device(&dch->dev, name); + if (ret) + goto free_chan; + hc->created[0] = 1; + return ret; +free_chan: + release_port(hc, dch); + return ret; +} + +static int +init_multi_port(struct hfc_multi *hc, int pt) +{ + struct dchannel *dch; + struct bchannel *bch; + int ch, i, ret = 0; + char name[MISDN_MAX_IDLEN]; + + dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL); + if (!dch) + return -ENOMEM; + dch->debug = debug; + mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change); + dch->hw = hc; + dch->dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); + dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + dch->dev.D.send = handle_dmsg; + dch->dev.D.ctrl = hfcm_dctrl; + dch->dev.nrbchan = 2; + i = pt << 2; + dch->slot = i + 2; + hc->chan[i + 2].dch = dch; + hc->chan[i + 2].port = pt; + hc->chan[i + 2].nt_timer = -1; + for (ch = 0; ch < dch->dev.nrbchan; ch++) { + bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL); + if (!bch) { + printk(KERN_ERR "%s: no memory for bchannel\n", + __func__); + ret = -ENOMEM; + goto free_chan; + } + hc->chan[i + ch].coeff = kzalloc(512, GFP_KERNEL); + if (!hc->chan[i + ch].coeff) { + printk(KERN_ERR "%s: no memory for coeffs\n", + __func__); + ret = -ENOMEM; + goto free_chan; + } + bch->nr = ch + 1; + bch->slot = i + ch; + bch->debug = debug; + mISDN_initbchannel(bch, MAX_DATA_MEM); + bch->hw = hc; + bch->ch.send = handle_bmsg; + bch->ch.ctrl = hfcm_bctrl; + bch->ch.nr = ch + 1; + list_add(&bch->ch.list, &dch->dev.bchannels); + hc->chan[i + ch].bch = bch; + hc->chan[i + ch].port = pt; + test_and_set_bit(bch->nr, &dch->dev.channelmap[0]); + } + /* set master clock */ + if (port[Port_cnt] & 0x001) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PROTOCOL set master clock: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, pt + 1); + if (dch->dev.D.protocol != ISDN_P_TE_S0) { + printk(KERN_ERR "Error: Master clock " + "for port(%d) of card(%d) is only" + " possible with TE-mode\n", + pt + 1, HFC_cnt + 1); + ret = -EINVAL; + goto free_chan; + } + if (hc->masterclk >= 0) { + printk(KERN_ERR "Error: Master clock " + "for port(%d) of card(%d) already " + "defined for port(%d)\n", + pt + 1, HFC_cnt + 1, hc->masterclk+1); + ret = -EINVAL; + goto free_chan; + } + hc->masterclk = pt; + } + /* set transmitter line to non capacitive */ + if (port[Port_cnt] & 0x002) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PROTOCOL set non capacitive " + "transmitter: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, pt + 1); + test_and_set_bit(HFC_CFG_NONCAP_TX, + &hc->chan[i + 2].cfg); + } + /* disable E-channel */ + if (port[Port_cnt] & 0x004) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PROTOCOL disable E-channel: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, pt + 1); + test_and_set_bit(HFC_CFG_DIS_ECHANNEL, + &hc->chan[i + 2].cfg); + } + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-%ds.%d/%d", + hc->type, HFC_cnt + 1, pt + 1); + ret = mISDN_register_device(&dch->dev, name); + if (ret) + goto free_chan; + hc->created[pt] = 1; + return ret; +free_chan: + release_port(hc, dch); + return ret; +} + +static int +hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct hm_map *m = (struct hm_map *)ent->driver_data; + int ret_err = 0; + int pt; + struct hfc_multi *hc; + u_long flags; + u_char dips = 0, pmj = 0; /* dip settings, port mode Jumpers */ + + if (HFC_cnt >= MAX_CARDS) { + printk(KERN_ERR "too many cards (max=%d).\n", + MAX_CARDS); + return -EINVAL; + } + if ((type[HFC_cnt] & 0xff) && (type[HFC_cnt] & 0xff) != m->type) { + printk(KERN_WARNING "HFC-MULTI: Card '%s:%s' type %d found but " + "type[%d] %d was supplied as module parameter\n", + m->vendor_name, m->card_name, m->type, HFC_cnt, + type[HFC_cnt] & 0xff); + printk(KERN_WARNING "HFC-MULTI: Load module without parameters " + "first, to see cards and their types."); + return -EINVAL; + } + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: Registering %s:%s chip type %d (0x%x)\n", + __func__, m->vendor_name, m->card_name, m->type, + type[HFC_cnt]); + + /* allocate card+fifo structure */ + hc = kzalloc(sizeof(struct hfc_multi), GFP_KERNEL); + if (!hc) { + printk(KERN_ERR "No kmem for HFC-Multi card\n"); + return -ENOMEM; + } + spin_lock_init(&hc->lock); + hc->mtyp = m; + hc->type = m->type; + hc->ports = m->ports; + hc->id = HFC_cnt; + hc->pcm = pcm[HFC_cnt]; + hc->io_mode = iomode[HFC_cnt]; + if (dslot[HFC_cnt] < 0) { + hc->dslot = 0; + printk(KERN_INFO "HFC-E1 card has disabled D-channel, but " + "31 B-channels\n"); + } if (dslot[HFC_cnt] > 0 && dslot[HFC_cnt] < 32) { + hc->dslot = dslot[HFC_cnt]; + printk(KERN_INFO "HFC-E1 card has alternating D-channel on " + "time slot %d\n", dslot[HFC_cnt]); + } else + hc->dslot = 16; + + /* set chip specific features */ + hc->masterclk = -1; + if (type[HFC_cnt] & 0x100) { + test_and_set_bit(HFC_CHIP_ULAW, &hc->chip); + silence = 0xff; /* ulaw silence */ + } else + silence = 0x2a; /* alaw silence */ + if (!(type[HFC_cnt] & 0x200)) + test_and_set_bit(HFC_CHIP_DTMF, &hc->chip); + + if (type[HFC_cnt] & 0x800) + test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + if (type[HFC_cnt] & 0x1000) { + test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip); + test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + } + if (type[HFC_cnt] & 0x4000) + test_and_set_bit(HFC_CHIP_EXRAM_128, &hc->chip); + if (type[HFC_cnt] & 0x8000) + test_and_set_bit(HFC_CHIP_EXRAM_512, &hc->chip); + hc->slots = 32; + if (type[HFC_cnt] & 0x10000) + hc->slots = 64; + if (type[HFC_cnt] & 0x20000) + hc->slots = 128; + if (type[HFC_cnt] & 0x80000) { + test_and_set_bit(HFC_CHIP_WATCHDOG, &hc->chip); + hc->wdcount = 0; + hc->wdbyte = V_GPIO_OUT2; + printk(KERN_NOTICE "Watchdog enabled\n"); + } + + /* setup pci, hc->slots may change due to PLXSD */ + ret_err = setup_pci(hc, pdev, ent); + if (ret_err) { + if (hc == syncmaster) + syncmaster = NULL; + kfree(hc); + return ret_err; + } + + /* crate channels */ + for (pt = 0; pt < hc->ports; pt++) { + if (Port_cnt >= MAX_PORTS) { + printk(KERN_ERR "too many ports (max=%d).\n", + MAX_PORTS); + ret_err = -EINVAL; + goto free_card; + } + if (hc->type == 1) + ret_err = init_e1_port(hc, m); + else + ret_err = init_multi_port(hc, pt); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: Registering D-channel, card(%d) port(%d)" + "result %d\n", + __func__, HFC_cnt + 1, pt, ret_err); + + if (ret_err) { + while (pt) { /* release already registered ports */ + pt--; + release_port(hc, hc->chan[(pt << 2) + 2].dch); + } + goto free_card; + } + Port_cnt++; + } + + /* disp switches */ + switch (m->dip_type) { + case DIP_4S: + /* + * get DIP Setting for beroNet 1S/2S/4S cards + * check if Port Jumper config matches + * module param 'protocol' + * DIP Setting: (collect GPIO 13/14/15 (R_GPIO_IN1) + + * GPI 19/23 (R_GPI_IN2)) + */ + dips = ((~HFC_inb(hc, R_GPIO_IN1) & 0xE0) >> 5) | + ((~HFC_inb(hc, R_GPI_IN2) & 0x80) >> 3) | + (~HFC_inb(hc, R_GPI_IN2) & 0x08); + + /* Port mode (TE/NT) jumpers */ + pmj = ((HFC_inb(hc, R_GPI_IN3) >> 4) & 0xf); + + if (test_bit(HFC_CHIP_B410P, &hc->chip)) + pmj = ~pmj & 0xf; + + printk(KERN_INFO "%s: %s DIPs(0x%x) jumpers(0x%x)\n", + m->vendor_name, m->card_name, dips, pmj); + break; + case DIP_8S: + /* + * get DIP Setting for beroNet 8S0+ cards + * + * enable PCI auxbridge function + */ + HFC_outb(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK); + /* prepare access to auxport */ + outw(0x4000, hc->pci_iobase + 4); + /* + * some dummy reads are required to + * read valid DIP switch data + */ + dips = inb(hc->pci_iobase); + dips = inb(hc->pci_iobase); + dips = inb(hc->pci_iobase); + dips = ~inb(hc->pci_iobase) & 0x3F; + outw(0x0, hc->pci_iobase + 4); + /* disable PCI auxbridge function */ + HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK); + printk(KERN_INFO "%s: %s DIPs(0x%x)\n", + m->vendor_name, m->card_name, dips); + break; + case DIP_E1: + /* + * get DIP Setting for beroNet E1 cards + * DIP Setting: collect GPI 4/5/6/7 (R_GPI_IN0) + */ + dips = (~HFC_inb(hc, R_GPI_IN0) & 0xF0)>>4; + printk(KERN_INFO "%s: %s DIPs(0x%x)\n", + m->vendor_name, m->card_name, dips); + break; + } + + /* add to list */ + spin_lock_irqsave(&HFClock, flags); + list_add_tail(&hc->list, &HFClist); + spin_unlock_irqrestore(&HFClock, flags); + + /* initialize hardware */ + ret_err = init_card(hc); + if (ret_err) { + printk(KERN_ERR "init card returns %d\n", ret_err); + release_card(hc); + return ret_err; + } + + /* start IRQ and return */ + spin_lock_irqsave(&hc->lock, flags); + enable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + return 0; + +free_card: + release_io_hfcmulti(hc); + if (hc == syncmaster) + syncmaster = NULL; + kfree(hc); + return ret_err; +} + +static void __devexit hfc_remove_pci(struct pci_dev *pdev) +{ + struct hfc_multi *card = pci_get_drvdata(pdev); + u_long flags; + + if (debug) + printk(KERN_INFO "removing hfc_multi card vendor:%x " + "device:%x subvendor:%x subdevice:%x\n", + pdev->vendor, pdev->device, + pdev->subsystem_vendor, pdev->subsystem_device); + + if (card) { + spin_lock_irqsave(&HFClock, flags); + release_card(card); + spin_unlock_irqrestore(&HFClock, flags); + } else { + if (debug) + printk(KERN_WARNING "%s: drvdata allready removed\n", + __func__); + } +} + +#define VENDOR_CCD "Cologne Chip AG" +#define VENDOR_BN "beroNet GmbH" +#define VENDOR_DIG "Digium Inc." +#define VENDOR_JH "Junghanns.NET GmbH" +#define VENDOR_PRIM "PrimuX" + +static const struct hm_map hfcm_map[] = { +/*0*/ {VENDOR_BN, "HFC-1S Card (mini PCI)", 4, 1, 1, 3, 0, DIP_4S, 0}, +/*1*/ {VENDOR_BN, "HFC-2S Card", 4, 2, 1, 3, 0, DIP_4S}, +/*2*/ {VENDOR_BN, "HFC-2S Card (mini PCI)", 4, 2, 1, 3, 0, DIP_4S, 0}, +/*3*/ {VENDOR_BN, "HFC-4S Card", 4, 4, 1, 2, 0, DIP_4S, 0}, +/*4*/ {VENDOR_BN, "HFC-4S Card (mini PCI)", 4, 4, 1, 2, 0, 0, 0}, +/*5*/ {VENDOR_CCD, "HFC-4S Eval (old)", 4, 4, 0, 0, 0, 0, 0}, +/*6*/ {VENDOR_CCD, "HFC-4S IOB4ST", 4, 4, 1, 2, 0, 0, 0}, +/*7*/ {VENDOR_CCD, "HFC-4S", 4, 4, 1, 2, 0, 0, 0}, +/*8*/ {VENDOR_DIG, "HFC-4S Card", 4, 4, 0, 2, 0, 0, HFC_IO_MODE_REGIO}, +/*9*/ {VENDOR_CCD, "HFC-4S Swyx 4xS0 SX2 QuadBri", 4, 4, 1, 2, 0, 0, 0}, +/*10*/ {VENDOR_JH, "HFC-4S (junghanns 2.0)", 4, 4, 1, 2, 0, 0, 0}, +/*11*/ {VENDOR_PRIM, "HFC-2S Primux Card", 4, 2, 0, 0, 0, 0, 0}, + +/*12*/ {VENDOR_BN, "HFC-8S Card", 8, 8, 1, 0, 0, 0, 0}, +/*13*/ {VENDOR_BN, "HFC-8S Card (+)", 8, 8, 1, 8, 0, DIP_8S, + HFC_IO_MODE_REGIO}, +/*14*/ {VENDOR_CCD, "HFC-8S Eval (old)", 8, 8, 0, 0, 0, 0, 0}, +/*15*/ {VENDOR_CCD, "HFC-8S IOB4ST Recording", 8, 8, 1, 0, 0, 0, 0}, + +/*16*/ {VENDOR_CCD, "HFC-8S IOB8ST", 8, 8, 1, 0, 0, 0, 0}, +/*17*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0}, +/*18*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0}, + +/*19*/ {VENDOR_BN, "HFC-E1 Card", 1, 1, 0, 1, 0, DIP_E1, 0}, +/*20*/ {VENDOR_BN, "HFC-E1 Card (mini PCI)", 1, 1, 0, 1, 0, 0, 0}, +/*21*/ {VENDOR_BN, "HFC-E1+ Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0}, +/*22*/ {VENDOR_BN, "HFC-E1 Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0}, + +/*23*/ {VENDOR_CCD, "HFC-E1 Eval (old)", 1, 1, 0, 0, 0, 0, 0}, +/*24*/ {VENDOR_CCD, "HFC-E1 IOB1E1", 1, 1, 0, 1, 0, 0, 0}, +/*25*/ {VENDOR_CCD, "HFC-E1", 1, 1, 0, 1, 0, 0, 0}, + +/*26*/ {VENDOR_CCD, "HFC-4S Speech Design", 4, 4, 0, 0, 0, 0, + HFC_IO_MODE_PLXSD}, +/*27*/ {VENDOR_CCD, "HFC-E1 Speech Design", 1, 1, 0, 0, 0, 0, + HFC_IO_MODE_PLXSD}, +/*28*/ {VENDOR_CCD, "HFC-4S OpenVox", 4, 4, 1, 0, 0, 0, 0}, +/*29*/ {VENDOR_CCD, "HFC-2S OpenVox", 4, 2, 1, 0, 0, 0, 0}, +/*30*/ {VENDOR_CCD, "HFC-8S OpenVox", 8, 8, 1, 0, 0, 0, 0}, +}; + +#undef H +#define H(x) ((unsigned long)&hfcm_map[x]) +static struct pci_device_id hfmultipci_ids[] __devinitdata = { + + /* Cards with HFC-4S Chip */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN1SM, 0, 0, H(0)}, /* BN1S mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN2S, 0, 0, H(1)}, /* BN2S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN2SM, 0, 0, H(2)}, /* BN2S mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN4S, 0, 0, H(3)}, /* BN4S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN4SM, 0, 0, H(4)}, /* BN4S mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_DEVICE_ID_CCD_HFC4S, 0, 0, H(5)}, /* Old Eval */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB4ST, 0, 0, H(6)}, /* IOB4ST */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_HFC4S, 0, 0, H(7)}, /* 4S */ + { PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S, + PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S, 0, 0, H(8)}, + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_SWYX4S, 0, 0, H(9)}, /* 4S Swyx */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_JH4S20, 0, 0, H(10)}, + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_PMX2S, 0, 0, H(11)}, /* Primux */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_OV4S, 0, 0, H(28)}, /* OpenVox 4 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_OV2S, 0, 0, H(29)}, /* OpenVox 2 */ + + /* Cards with HFC-8S Chip */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN8S, 0, 0, H(12)}, /* BN8S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN8SP, 0, 0, H(13)}, /* BN8S+ */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_DEVICE_ID_CCD_HFC8S, 0, 0, H(14)}, /* old Eval */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB8STR, 0, 0, H(15)}, + /* IOB8ST Recording */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB8ST, 0, 0, H(16)}, /* IOB8ST */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB8ST_1, 0, 0, H(17)}, /* IOB8ST */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_HFC8S, 0, 0, H(18)}, /* 8S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_OV8S, 0, 0, H(30)}, /* OpenVox 8 */ + + + /* Cards with HFC-E1 Chip */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1, 0, 0, H(19)}, /* BNE1 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1M, 0, 0, H(20)}, /* BNE1 mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1DP, 0, 0, H(21)}, /* BNE1 + (Dual) */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1D, 0, 0, H(22)}, /* BNE1 (Dual) */ + + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_DEVICE_ID_CCD_HFCE1, 0, 0, H(23)}, /* Old Eval */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB1E1, 0, 0, H(24)}, /* IOB1E1 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_HFCE1, 0, 0, H(25)}, /* E1 */ + + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_SPD4S, 0, 0, H(26)}, /* PLX PCI Bridge */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_SPDE1, 0, 0, H(27)}, /* PLX PCI Bridge */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, 0}, + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, 0}, + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, 0}, + {0, } +}; +#undef H + +MODULE_DEVICE_TABLE(pci, hfmultipci_ids); + +static int +hfcmulti_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct hm_map *m = (struct hm_map *)ent->driver_data; + int ret; + + if (m == NULL) { + if (ent->vendor == PCI_VENDOR_ID_CCD) + if (ent->device == PCI_DEVICE_ID_CCD_HFC4S || + ent->device == PCI_DEVICE_ID_CCD_HFC8S || + ent->device == PCI_DEVICE_ID_CCD_HFCE1) + printk(KERN_ERR + "unknown HFC multiport controller " + "(vendor:%x device:%x subvendor:%x " + "subdevice:%x) Please contact the " + "driver maintainer for support.\n", + ent->vendor, ent->device, + ent->subvendor, ent->subdevice); + return -ENODEV; + } + ret = hfcmulti_init(pdev, ent); + if (ret) + return ret; + HFC_cnt++; + printk(KERN_INFO "%d devices registered\n", HFC_cnt); + return 0; +} + +static struct pci_driver hfcmultipci_driver = { + .name = "hfc_multi", + .probe = hfcmulti_probe, + .remove = __devexit_p(hfc_remove_pci), + .id_table = hfmultipci_ids, +}; + +static void __exit +HFCmulti_cleanup(void) +{ + struct hfc_multi *card, *next; + + /* unload interrupt function symbol */ + if (hfc_interrupt) + symbol_put(ztdummy_extern_interrupt); + if (register_interrupt) + symbol_put(ztdummy_register_interrupt); + if (unregister_interrupt) { + if (interrupt_registered) { + interrupt_registered = 0; + unregister_interrupt(); + } + symbol_put(ztdummy_unregister_interrupt); + } + + list_for_each_entry_safe(card, next, &HFClist, list) + release_card(card); + /* get rid of all devices of this driver */ + pci_unregister_driver(&hfcmultipci_driver); +} + +static int __init +HFCmulti_init(void) +{ + int err; + +#ifdef IRQ_DEBUG + printk(KERN_ERR "%s: IRQ_DEBUG IS ENABLED!\n", __func__); +#endif + + spin_lock_init(&HFClock); + spin_lock_init(&plx_lock); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: init entered\n", __func__); + +#ifdef __BIG_ENDIAN +#error "not running on big endian machines now" +#endif + hfc_interrupt = symbol_get(ztdummy_extern_interrupt); + register_interrupt = symbol_get(ztdummy_register_interrupt); + unregister_interrupt = symbol_get(ztdummy_unregister_interrupt); + printk(KERN_INFO "mISDN: HFC-multi driver %s\n", + hfcmulti_revision); + + switch (poll) { + case 0: + poll_timer = 6; + poll = 128; + break; + /* + * wenn dieses break nochmal verschwindet, + * gibt es heisse ohren :-) + * "without the break you will get hot ears ???" + */ + case 8: + poll_timer = 2; + break; + case 16: + poll_timer = 3; + break; + case 32: + poll_timer = 4; + break; + case 64: + poll_timer = 5; + break; + case 128: + poll_timer = 6; + break; + case 256: + poll_timer = 7; + break; + default: + printk(KERN_ERR + "%s: Wrong poll value (%d).\n", __func__, poll); + err = -EINVAL; + return err; + + } + + err = pci_register_driver(&hfcmultipci_driver); + if (err < 0) { + printk(KERN_ERR "error registering pci driver: %x\n", err); + if (hfc_interrupt) + symbol_put(ztdummy_extern_interrupt); + if (register_interrupt) + symbol_put(ztdummy_register_interrupt); + if (unregister_interrupt) { + if (interrupt_registered) { + interrupt_registered = 0; + unregister_interrupt(); + } + symbol_put(ztdummy_unregister_interrupt); + } + return err; + } + return 0; +} + + +module_init(HFCmulti_init); +module_exit(HFCmulti_cleanup); diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c new file mode 100644 index 000000000000..3231814e7efa --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -0,0 +1,2255 @@ +/* + * + * hfcpci.c low level driver for CCD's hfc-pci based cards + * + * Author Werner Cornelius (werner@isdn4linux.de) + * based on existing driver for CCD hfc ISA cards + * type approval valid for HFC-S PCI A based card + * + * Copyright 1999 by Werner Cornelius (werner@isdn-development.de) + * Copyright 2008 by Karsten Keil <kkeil@novell.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, 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/pci.h> +#include <linux/delay.h> +#include <linux/mISDNhw.h> + +#include "hfc_pci.h" + +static const char *hfcpci_revision = "2.0"; + +#define MAX_CARDS 8 +static int HFC_cnt; +static uint debug; + +MODULE_AUTHOR("Karsten Keil"); +MODULE_LICENSE("GPL"); +module_param(debug, uint, 0); + +static LIST_HEAD(HFClist); +DEFINE_RWLOCK(HFClock); + +enum { + HFC_CCD_2BD0, + HFC_CCD_B000, + HFC_CCD_B006, + HFC_CCD_B007, + HFC_CCD_B008, + HFC_CCD_B009, + HFC_CCD_B00A, + HFC_CCD_B00B, + HFC_CCD_B00C, + HFC_CCD_B100, + HFC_CCD_B700, + HFC_CCD_B701, + HFC_ASUS_0675, + HFC_BERKOM_A1T, + HFC_BERKOM_TCONCEPT, + HFC_ANIGMA_MC145575, + HFC_ZOLTRIX_2BD0, + HFC_DIGI_DF_M_IOM2_E, + HFC_DIGI_DF_M_E, + HFC_DIGI_DF_M_IOM2_A, + HFC_DIGI_DF_M_A, + HFC_ABOCOM_2BD1, + HFC_SITECOM_DC105V2, +}; + +struct hfcPCI_hw { + unsigned char cirm; + unsigned char ctmt; + unsigned char clkdel; + unsigned char states; + unsigned char conn; + unsigned char mst_m; + unsigned char int_m1; + unsigned char int_m2; + unsigned char sctrl; + unsigned char sctrl_r; + unsigned char sctrl_e; + unsigned char trm; + unsigned char fifo_en; + unsigned char bswapped; + unsigned char protocol; + int nt_timer; + unsigned char *pci_io; /* start of PCI IO memory */ + dma_addr_t dmahandle; + void *fifos; /* FIFO memory */ + int last_bfifo_cnt[2]; + /* marker saving last b-fifo frame count */ + struct timer_list timer; +}; + +#define HFC_CFG_MASTER 1 +#define HFC_CFG_SLAVE 2 +#define HFC_CFG_PCM 3 +#define HFC_CFG_2HFC 4 +#define HFC_CFG_SLAVEHFC 5 +#define HFC_CFG_NEG_F0 6 +#define HFC_CFG_SW_DD_DU 7 + +#define FLG_HFC_TIMER_T1 16 +#define FLG_HFC_TIMER_T3 17 + +#define NT_T1_COUNT 1120 /* number of 3.125ms interrupts (3.5s) */ +#define NT_T3_COUNT 31 /* number of 3.125ms interrupts (97 ms) */ +#define CLKDEL_TE 0x0e /* CLKDEL in TE mode */ +#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */ + + +struct hfc_pci { + struct list_head list; + u_char subtype; + u_char chanlimit; + u_char initdone; + u_long cfg; + u_int irq; + u_int irqcnt; + struct pci_dev *pdev; + struct hfcPCI_hw hw; + spinlock_t lock; /* card lock */ + struct dchannel dch; + struct bchannel bch[2]; +}; + +/* Interface functions */ +static void +enable_hwirq(struct hfc_pci *hc) +{ + hc->hw.int_m2 |= HFCPCI_IRQ_ENABLE; + Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2); +} + +static void +disable_hwirq(struct hfc_pci *hc) +{ + hc->hw.int_m2 &= ~((u_char)HFCPCI_IRQ_ENABLE); + Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2); +} + +/* + * free hardware resources used by driver + */ +static void +release_io_hfcpci(struct hfc_pci *hc) +{ + /* disable memory mapped ports + busmaster */ + pci_write_config_word(hc->pdev, PCI_COMMAND, 0); + del_timer(&hc->hw.timer); + pci_free_consistent(hc->pdev, 0x8000, hc->hw.fifos, hc->hw.dmahandle); + iounmap((void *)hc->hw.pci_io); +} + +/* + * set mode (NT or TE) + */ +static void +hfcpci_setmode(struct hfc_pci *hc) +{ + if (hc->hw.protocol == ISDN_P_NT_S0) { + hc->hw.clkdel = CLKDEL_NT; /* ST-Bit delay for NT-Mode */ + hc->hw.sctrl |= SCTRL_MODE_NT; /* NT-MODE */ + hc->hw.states = 1; /* G1 */ + } else { + hc->hw.clkdel = CLKDEL_TE; /* ST-Bit delay for TE-Mode */ + hc->hw.sctrl &= ~SCTRL_MODE_NT; /* TE-MODE */ + hc->hw.states = 2; /* F2 */ + } + Write_hfc(hc, HFCPCI_CLKDEL, hc->hw.clkdel); + Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | hc->hw.states); + udelay(10); + Write_hfc(hc, HFCPCI_STATES, hc->hw.states | 0x40); /* Deactivate */ + Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl); +} + +/* + * function called to reset the HFC PCI chip. A complete software reset of chip + * and fifos is done. + */ +static void +reset_hfcpci(struct hfc_pci *hc) +{ + u_char val; + int cnt = 0; + + printk(KERN_DEBUG "reset_hfcpci: entered\n"); + val = Read_hfc(hc, HFCPCI_CHIP_ID); + printk(KERN_INFO "HFC_PCI: resetting HFC ChipId(%x)\n", val); + /* enable memory mapped ports, disable busmaster */ + pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO); + disable_hwirq(hc); + /* enable memory ports + busmaster */ + pci_write_config_word(hc->pdev, PCI_COMMAND, + PCI_ENA_MEMIO + PCI_ENA_MASTER); + val = Read_hfc(hc, HFCPCI_STATUS); + printk(KERN_DEBUG "HFC-PCI status(%x) before reset\n", val); + hc->hw.cirm = HFCPCI_RESET; /* Reset On */ + Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); + set_current_state(TASK_UNINTERRUPTIBLE); + mdelay(10); /* Timeout 10ms */ + hc->hw.cirm = 0; /* Reset Off */ + Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); + val = Read_hfc(hc, HFCPCI_STATUS); + printk(KERN_DEBUG "HFC-PCI status(%x) after reset\n", val); + while (cnt < 50000) { /* max 50000 us */ + udelay(5); + cnt += 5; + val = Read_hfc(hc, HFCPCI_STATUS); + if (!(val & 2)) + break; + } + printk(KERN_DEBUG "HFC-PCI status(%x) after %dus\n", val, cnt); + + hc->hw.fifo_en = 0x30; /* only D fifos enabled */ + + hc->hw.bswapped = 0; /* no exchange */ + hc->hw.ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER; + hc->hw.trm = HFCPCI_BTRANS_THRESMASK; /* no echo connect , threshold */ + hc->hw.sctrl = 0x40; /* set tx_lo mode, error in datasheet ! */ + hc->hw.sctrl_r = 0; + hc->hw.sctrl_e = HFCPCI_AUTO_AWAKE; /* S/T Auto awake */ + hc->hw.mst_m = 0; + if (test_bit(HFC_CFG_MASTER, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_MASTER; /* HFC Master Mode */ + if (test_bit(HFC_CFG_NEG_F0, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_F0_NEGATIV; + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); + Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e); + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); + + hc->hw.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC | + HFCPCI_INTS_L1STATE | HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + + /* Clear already pending ints */ + if (Read_hfc(hc, HFCPCI_INT_S1)); + + /* set NT/TE mode */ + hfcpci_setmode(hc); + + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); + + /* + * Init GCI/IOM2 in master mode + * Slots 0 and 1 are set for B-chan 1 and 2 + * D- and monitor/CI channel are not enabled + * STIO1 is used as output for data, B1+B2 from ST->IOM+HFC + * STIO2 is used as data input, B1+B2 from IOM->ST + * ST B-channel send disabled -> continous 1s + * The IOM slots are always enabled + */ + if (test_bit(HFC_CFG_PCM, &hc->cfg)) { + /* set data flow directions: connect B1,B2: HFC to/from PCM */ + hc->hw.conn = 0x09; + } else { + hc->hw.conn = 0x36; /* set data flow directions */ + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) { + Write_hfc(hc, HFCPCI_B1_SSL, 0xC0); + Write_hfc(hc, HFCPCI_B2_SSL, 0xC1); + Write_hfc(hc, HFCPCI_B1_RSL, 0xC0); + Write_hfc(hc, HFCPCI_B2_RSL, 0xC1); + } else { + Write_hfc(hc, HFCPCI_B1_SSL, 0x80); + Write_hfc(hc, HFCPCI_B2_SSL, 0x81); + Write_hfc(hc, HFCPCI_B1_RSL, 0x80); + Write_hfc(hc, HFCPCI_B2_RSL, 0x81); + } + } + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + val = Read_hfc(hc, HFCPCI_INT_S2); +} + +/* + * Timer function called when kernel timer expires + */ +static void +hfcpci_Timer(struct hfc_pci *hc) +{ + hc->hw.timer.expires = jiffies + 75; + /* WD RESET */ +/* + * WriteReg(hc, HFCD_DATA, HFCD_CTMT, hc->hw.ctmt | 0x80); + * add_timer(&hc->hw.timer); + */ +} + + +/* + * select a b-channel entry matching and active + */ +static struct bchannel * +Sel_BCS(struct hfc_pci *hc, int channel) +{ + if (test_bit(FLG_ACTIVE, &hc->bch[0].Flags) && + (hc->bch[0].nr & channel)) + return &hc->bch[0]; + else if (test_bit(FLG_ACTIVE, &hc->bch[1].Flags) && + (hc->bch[1].nr & channel)) + return &hc->bch[1]; + else + return NULL; +} + +/* + * clear the desired B-channel rx fifo + */ +static void +hfcpci_clear_fifo_rx(struct hfc_pci *hc, int fifo) +{ + u_char fifo_state; + struct bzfifo *bzr; + + if (fifo) { + bzr = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2; + fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2RX; + } else { + bzr = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b1; + fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1RX; + } + if (fifo_state) + hc->hw.fifo_en ^= fifo_state; + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + hc->hw.last_bfifo_cnt[fifo] = 0; + bzr->f1 = MAX_B_FRAMES; + bzr->f2 = bzr->f1; /* init F pointers to remain constant */ + bzr->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1); + bzr->za[MAX_B_FRAMES].z2 = cpu_to_le16( + le16_to_cpu(bzr->za[MAX_B_FRAMES].z1)); + if (fifo_state) + hc->hw.fifo_en |= fifo_state; + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); +} + +/* + * clear the desired B-channel tx fifo + */ +static void hfcpci_clear_fifo_tx(struct hfc_pci *hc, int fifo) +{ + u_char fifo_state; + struct bzfifo *bzt; + + if (fifo) { + bzt = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2; + fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2TX; + } else { + bzt = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1; + fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1TX; + } + if (fifo_state) + hc->hw.fifo_en ^= fifo_state; + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + if (hc->bch[fifo].debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) " + "z1(%x) z2(%x) state(%x)\n", + fifo, bzt->f1, bzt->f2, + le16_to_cpu(bzt->za[MAX_B_FRAMES].z1), + le16_to_cpu(bzt->za[MAX_B_FRAMES].z2), + fifo_state); + bzt->f2 = MAX_B_FRAMES; + bzt->f1 = bzt->f2; /* init F pointers to remain constant */ + bzt->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1); + bzt->za[MAX_B_FRAMES].z2 = cpu_to_le16( + le16_to_cpu(bzt->za[MAX_B_FRAMES].z1 - 1)); + if (fifo_state) + hc->hw.fifo_en |= fifo_state; + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + if (hc->bch[fifo].debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG + "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) z1(%x) z2(%x)\n", + fifo, bzt->f1, bzt->f2, + le16_to_cpu(bzt->za[MAX_B_FRAMES].z1), + le16_to_cpu(bzt->za[MAX_B_FRAMES].z2)); +} + +/* + * read a complete B-frame out of the buffer + */ +static void +hfcpci_empty_bfifo(struct bchannel *bch, struct bzfifo *bz, + u_char *bdata, int count) +{ + u_char *ptr, *ptr1, new_f2; + int total, maxlen, new_z2; + struct zt *zp; + + if ((bch->debug & DEBUG_HW_BCHANNEL) && !(bch->debug & DEBUG_HW_BFIFO)) + printk(KERN_DEBUG "hfcpci_empty_fifo\n"); + zp = &bz->za[bz->f2]; /* point to Z-Regs */ + new_z2 = le16_to_cpu(zp->z2) + count; /* new position in fifo */ + if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z2 -= B_FIFO_SIZE; /* buffer wrap */ + new_f2 = (bz->f2 + 1) & MAX_B_FRAMES; + if ((count > MAX_DATA_SIZE + 3) || (count < 4) || + (*(bdata + (le16_to_cpu(zp->z1) - B_SUB_VAL)))) { + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "hfcpci_empty_fifo: incoming packet " + "invalid length %d or crc\n", count); +#ifdef ERROR_STATISTIC + bch->err_inv++; +#endif + bz->za[new_f2].z2 = cpu_to_le16(new_z2); + bz->f2 = new_f2; /* next buffer */ + } else { + bch->rx_skb = mI_alloc_skb(count - 3, GFP_ATOMIC); + if (!bch->rx_skb) { + printk(KERN_WARNING "HFCPCI: receive out of memory\n"); + return; + } + total = count; + count -= 3; + ptr = skb_put(bch->rx_skb, count); + + if (le16_to_cpu(zp->z2) + count <= B_FIFO_SIZE + B_SUB_VAL) + maxlen = count; /* complete transfer */ + else + maxlen = B_FIFO_SIZE + B_SUB_VAL - + le16_to_cpu(zp->z2); /* maximum */ + + ptr1 = bdata + (le16_to_cpu(zp->z2) - B_SUB_VAL); + /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + count -= maxlen; + + if (count) { /* rest remaining */ + ptr += maxlen; + ptr1 = bdata; /* start of buffer */ + memcpy(ptr, ptr1, count); /* rest */ + } + bz->za[new_f2].z2 = cpu_to_le16(new_z2); + bz->f2 = new_f2; /* next buffer */ + recv_Bchannel(bch); + } +} + +/* + * D-channel receive procedure + */ +static int +receive_dmsg(struct hfc_pci *hc) +{ + struct dchannel *dch = &hc->dch; + int maxlen; + int rcnt, total; + int count = 5; + u_char *ptr, *ptr1; + struct dfifo *df; + struct zt *zp; + + df = &((union fifo_area *)(hc->hw.fifos))->d_chan.d_rx; + while (((df->f1 & D_FREG_MASK) != (df->f2 & D_FREG_MASK)) && count--) { + zp = &df->za[df->f2 & D_FREG_MASK]; + rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2); + if (rcnt < 0) + rcnt += D_FIFO_SIZE; + rcnt++; + if (dch->debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG + "hfcpci recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)\n", + df->f1, df->f2, + le16_to_cpu(zp->z1), + le16_to_cpu(zp->z2), + rcnt); + + if ((rcnt > MAX_DFRAME_LEN + 3) || (rcnt < 4) || + (df->data[le16_to_cpu(zp->z1)])) { + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG + "empty_fifo hfcpci paket inv. len " + "%d or crc %d\n", + rcnt, + df->data[le16_to_cpu(zp->z1)]); +#ifdef ERROR_STATISTIC + cs->err_rx++; +#endif + df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | + (MAX_D_FRAMES + 1); /* next buffer */ + df->za[df->f2 & D_FREG_MASK].z2 = + cpu_to_le16((zp->z2 + rcnt) & (D_FIFO_SIZE - 1)); + } else { + dch->rx_skb = mI_alloc_skb(rcnt - 3, GFP_ATOMIC); + if (!dch->rx_skb) { + printk(KERN_WARNING + "HFC-PCI: D receive out of memory\n"); + break; + } + total = rcnt; + rcnt -= 3; + ptr = skb_put(dch->rx_skb, rcnt); + + if (le16_to_cpu(zp->z2) + rcnt <= D_FIFO_SIZE) + maxlen = rcnt; /* complete transfer */ + else + maxlen = D_FIFO_SIZE - le16_to_cpu(zp->z2); + /* maximum */ + + ptr1 = df->data + le16_to_cpu(zp->z2); + /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + rcnt -= maxlen; + + if (rcnt) { /* rest remaining */ + ptr += maxlen; + ptr1 = df->data; /* start of buffer */ + memcpy(ptr, ptr1, rcnt); /* rest */ + } + df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | + (MAX_D_FRAMES + 1); /* next buffer */ + df->za[df->f2 & D_FREG_MASK].z2 = cpu_to_le16(( + le16_to_cpu(zp->z2) + total) & (D_FIFO_SIZE - 1)); + recv_Dchannel(dch); + } + } + return 1; +} + +/* + * check for transparent receive data and read max one threshold size if avail + */ +int +hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *bz, u_char *bdata) +{ + unsigned short *z1r, *z2r; + int new_z2, fcnt, maxlen; + u_char *ptr, *ptr1; + + z1r = &bz->za[MAX_B_FRAMES].z1; /* pointer to z reg */ + z2r = z1r + 1; + + fcnt = le16_to_cpu(*z1r) - le16_to_cpu(*z2r); + if (!fcnt) + return 0; /* no data avail */ + + if (fcnt <= 0) + fcnt += B_FIFO_SIZE; /* bytes actually buffered */ + if (fcnt > HFCPCI_BTRANS_THRESHOLD) + fcnt = HFCPCI_BTRANS_THRESHOLD; /* limit size */ + + new_z2 = le16_to_cpu(*z2r) + fcnt; /* new position in fifo */ + if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z2 -= B_FIFO_SIZE; /* buffer wrap */ + + bch->rx_skb = mI_alloc_skb(fcnt, GFP_ATOMIC); + if (bch->rx_skb) { + ptr = skb_put(bch->rx_skb, fcnt); + if (le16_to_cpu(*z2r) + fcnt <= B_FIFO_SIZE + B_SUB_VAL) + maxlen = fcnt; /* complete transfer */ + else + maxlen = B_FIFO_SIZE + B_SUB_VAL - le16_to_cpu(*z2r); + /* maximum */ + + ptr1 = bdata + (le16_to_cpu(*z2r) - B_SUB_VAL); + /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + fcnt -= maxlen; + + if (fcnt) { /* rest remaining */ + ptr += maxlen; + ptr1 = bdata; /* start of buffer */ + memcpy(ptr, ptr1, fcnt); /* rest */ + } + recv_Bchannel(bch); + } else + printk(KERN_WARNING "HFCPCI: receive out of memory\n"); + + *z2r = cpu_to_le16(new_z2); /* new position */ + return 1; +} + +/* + * B-channel main receive routine + */ +void +main_rec_hfcpci(struct bchannel *bch) +{ + struct hfc_pci *hc = bch->hw; + int rcnt, real_fifo; + int receive, count = 5; + struct bzfifo *bz; + u_char *bdata; + struct zt *zp; + + + if ((bch->nr & 2) && (!hc->hw.bswapped)) { + bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2; + bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b2; + real_fifo = 1; + } else { + bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b1; + bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b1; + real_fifo = 0; + } +Begin: + count--; + if (bz->f1 != bz->f2) { + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG "hfcpci rec ch(%x) f1(%d) f2(%d)\n", + bch->nr, bz->f1, bz->f2); + zp = &bz->za[bz->f2]; + + rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2); + if (rcnt < 0) + rcnt += B_FIFO_SIZE; + rcnt++; + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG + "hfcpci rec ch(%x) z1(%x) z2(%x) cnt(%d)\n", + bch->nr, le16_to_cpu(zp->z1), + le16_to_cpu(zp->z2), rcnt); + hfcpci_empty_bfifo(bch, bz, bdata, rcnt); + rcnt = bz->f1 - bz->f2; + if (rcnt < 0) + rcnt += MAX_B_FRAMES + 1; + if (hc->hw.last_bfifo_cnt[real_fifo] > rcnt + 1) { + rcnt = 0; + hfcpci_clear_fifo_rx(hc, real_fifo); + } + hc->hw.last_bfifo_cnt[real_fifo] = rcnt; + if (rcnt > 1) + receive = 1; + else + receive = 0; + } else if (test_bit(FLG_TRANSPARENT, &bch->Flags)) + receive = hfcpci_empty_fifo_trans(bch, bz, bdata); + else + receive = 0; + if (count && receive) + goto Begin; + +} + +/* + * D-channel send routine + */ +static void +hfcpci_fill_dfifo(struct hfc_pci *hc) +{ + struct dchannel *dch = &hc->dch; + int fcnt; + int count, new_z1, maxlen; + struct dfifo *df; + u_char *src, *dst, new_f1; + + if ((dch->debug & DEBUG_HW_DCHANNEL) && !(dch->debug & DEBUG_HW_DFIFO)) + printk(KERN_DEBUG "%s\n", __func__); + + if (!dch->tx_skb) + return; + count = dch->tx_skb->len - dch->tx_idx; + if (count <= 0) + return; + df = &((union fifo_area *) (hc->hw.fifos))->d_chan.d_tx; + + if (dch->debug & DEBUG_HW_DFIFO) + printk(KERN_DEBUG "%s:f1(%d) f2(%d) z1(f1)(%x)\n", __func__, + df->f1, df->f2, + le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1)); + fcnt = df->f1 - df->f2; /* frame count actually buffered */ + if (fcnt < 0) + fcnt += (MAX_D_FRAMES + 1); /* if wrap around */ + if (fcnt > (MAX_D_FRAMES - 1)) { + if (dch->debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG + "hfcpci_fill_Dfifo more as 14 frames\n"); +#ifdef ERROR_STATISTIC + cs->err_tx++; +#endif + return; + } + /* now determine free bytes in FIFO buffer */ + maxlen = le16_to_cpu(df->za[df->f2 & D_FREG_MASK].z2) - + le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) - 1; + if (maxlen <= 0) + maxlen += D_FIFO_SIZE; /* count now contains available bytes */ + + if (dch->debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG "hfcpci_fill_Dfifo count(%d/%d)\n", + count, maxlen); + if (count > maxlen) { + if (dch->debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG "hfcpci_fill_Dfifo no fifo mem\n"); + return; + } + new_z1 = (le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) + count) & + (D_FIFO_SIZE - 1); + new_f1 = ((df->f1 + 1) & D_FREG_MASK) | (D_FREG_MASK + 1); + src = dch->tx_skb->data + dch->tx_idx; /* source pointer */ + dst = df->data + le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1); + maxlen = D_FIFO_SIZE - le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1); + /* end fifo */ + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + + count -= maxlen; /* remaining bytes */ + if (count) { + dst = df->data; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + df->za[new_f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1); + /* for next buffer */ + df->za[df->f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1); + /* new pos actual buffer */ + df->f1 = new_f1; /* next frame */ + dch->tx_idx = dch->tx_skb->len; +} + +/* + * B-channel send routine + */ +static void +hfcpci_fill_fifo(struct bchannel *bch) +{ + struct hfc_pci *hc = bch->hw; + int maxlen, fcnt; + int count, new_z1; + struct bzfifo *bz; + u_char *bdata; + u_char new_f1, *src, *dst; + unsigned short *z1t, *z2t; + + if ((bch->debug & DEBUG_HW_BCHANNEL) && !(bch->debug & DEBUG_HW_BFIFO)) + printk(KERN_DEBUG "%s\n", __func__); + if ((!bch->tx_skb) || bch->tx_skb->len <= 0) + return; + count = bch->tx_skb->len - bch->tx_idx; + if ((bch->nr & 2) && (!hc->hw.bswapped)) { + bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2; + bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.txdat_b2; + } else { + bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1; + bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.txdat_b1; + } + + if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { + z1t = &bz->za[MAX_B_FRAMES].z1; + z2t = z1t + 1; + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG "hfcpci_fill_fifo_trans ch(%x) " + "cnt(%d) z1(%x) z2(%x)\n", bch->nr, count, + le16_to_cpu(*z1t), le16_to_cpu(*z2t)); + fcnt = le16_to_cpu(*z2t) - le16_to_cpu(*z1t); + if (fcnt <= 0) + fcnt += B_FIFO_SIZE; + /* fcnt contains available bytes in fifo */ + fcnt = B_FIFO_SIZE - fcnt; + /* remaining bytes to send (bytes in fifo) */ +next_t_frame: + count = bch->tx_skb->len - bch->tx_idx; + /* maximum fill shall be HFCPCI_BTRANS_MAX */ + if (count > HFCPCI_BTRANS_MAX - fcnt) + count = HFCPCI_BTRANS_MAX - fcnt; + if (count <= 0) + return; + /* data is suitable for fifo */ + new_z1 = le16_to_cpu(*z1t) + count; + /* new buffer Position */ + if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z1 -= B_FIFO_SIZE; /* buffer wrap */ + src = bch->tx_skb->data + bch->tx_idx; + /* source pointer */ + dst = bdata + (le16_to_cpu(*z1t) - B_SUB_VAL); + maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(*z1t); + /* end of fifo */ + if (bch->debug & DEBUG_HW_BFIFO) + printk(KERN_DEBUG "hfcpci_FFt fcnt(%d) " + "maxl(%d) nz1(%x) dst(%p)\n", + fcnt, maxlen, new_z1, dst); + fcnt += count; + bch->tx_idx += count; + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + count -= maxlen; /* remaining bytes */ + if (count) { + dst = bdata; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + *z1t = cpu_to_le16(new_z1); /* now send data */ + if (bch->tx_idx < bch->tx_skb->len) + return; + /* send confirm, on trans, free on hdlc. */ + if (test_bit(FLG_TRANSPARENT, &bch->Flags)) + confirm_Bsend(bch); + dev_kfree_skb(bch->tx_skb); + if (get_next_bframe(bch)) + goto next_t_frame; + return; + } + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG + "%s: ch(%x) f1(%d) f2(%d) z1(f1)(%x)\n", + __func__, bch->nr, bz->f1, bz->f2, + bz->za[bz->f1].z1); + fcnt = bz->f1 - bz->f2; /* frame count actually buffered */ + if (fcnt < 0) + fcnt += (MAX_B_FRAMES + 1); /* if wrap around */ + if (fcnt > (MAX_B_FRAMES - 1)) { + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG + "hfcpci_fill_Bfifo more as 14 frames\n"); + return; + } + /* now determine free bytes in FIFO buffer */ + maxlen = le16_to_cpu(bz->za[bz->f2].z2) - + le16_to_cpu(bz->za[bz->f1].z1) - 1; + if (maxlen <= 0) + maxlen += B_FIFO_SIZE; /* count now contains available bytes */ + + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG "hfcpci_fill_fifo ch(%x) count(%d/%d)\n", + bch->nr, count, maxlen); + + if (maxlen < count) { + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG "hfcpci_fill_fifo no fifo mem\n"); + return; + } + new_z1 = le16_to_cpu(bz->za[bz->f1].z1) + count; + /* new buffer Position */ + if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z1 -= B_FIFO_SIZE; /* buffer wrap */ + + new_f1 = ((bz->f1 + 1) & MAX_B_FRAMES); + src = bch->tx_skb->data + bch->tx_idx; /* source pointer */ + dst = bdata + (le16_to_cpu(bz->za[bz->f1].z1) - B_SUB_VAL); + maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(bz->za[bz->f1].z1); + /* end fifo */ + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + + count -= maxlen; /* remaining bytes */ + if (count) { + dst = bdata; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + bz->za[new_f1].z1 = cpu_to_le16(new_z1); /* for next buffer */ + bz->f1 = new_f1; /* next frame */ + dev_kfree_skb(bch->tx_skb); + get_next_bframe(bch); +} + + + +/* + * handle L1 state changes TE + */ + +static void +ph_state_te(struct dchannel *dch) +{ + if (dch->debug) + printk(KERN_DEBUG "%s: TE newstate %x\n", + __func__, dch->state); + switch (dch->state) { + case 0: + l1_event(dch->l1, HW_RESET_IND); + break; + case 3: + l1_event(dch->l1, HW_DEACT_IND); + break; + case 5: + case 8: + l1_event(dch->l1, ANYSIGNAL); + break; + case 6: + l1_event(dch->l1, INFO2); + break; + case 7: + l1_event(dch->l1, INFO4_P8); + break; + } +} + +/* + * handle L1 state changes NT + */ + +static void +handle_nt_timer3(struct dchannel *dch) { + struct hfc_pci *hc = dch->hw; + + test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + hc->hw.nt_timer = 0; + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + if (test_bit(HFC_CFG_MASTER, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); +} + +static void +ph_state_nt(struct dchannel *dch) +{ + struct hfc_pci *hc = dch->hw; + + if (dch->debug) + printk(KERN_DEBUG "%s: NT newstate %x\n", + __func__, dch->state); + switch (dch->state) { + case 2: + if (hc->hw.nt_timer < 0) { + hc->hw.nt_timer = 0; + test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); + test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + /* Clear already pending ints */ + if (Read_hfc(hc, HFCPCI_INT_S1)); + Write_hfc(hc, HFCPCI_STATES, 4 | HFCPCI_LOAD_STATE); + udelay(10); + Write_hfc(hc, HFCPCI_STATES, 4); + dch->state = 4; + } else if (hc->hw.nt_timer == 0) { + hc->hw.int_m1 |= HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + hc->hw.nt_timer = NT_T1_COUNT; + hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER; + hc->hw.ctmt |= HFCPCI_TIM3_125; + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | + HFCPCI_CLTIMER); + test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); + test_and_set_bit(FLG_HFC_TIMER_T1, &dch->Flags); + /* allow G2 -> G3 transition */ + Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3); + } else { + Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3); + } + break; + case 1: + hc->hw.nt_timer = 0; + test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); + test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + hc->hw.mst_m &= ~HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + case 4: + hc->hw.nt_timer = 0; + test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); + test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + break; + case 3: + if (!test_and_set_bit(FLG_HFC_TIMER_T3, &dch->Flags)) { + if (!test_and_clear_bit(FLG_L2_ACTIVATED, + &dch->Flags)) { + handle_nt_timer3(dch); + break; + } + test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); + hc->hw.int_m1 |= HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + hc->hw.nt_timer = NT_T3_COUNT; + hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER; + hc->hw.ctmt |= HFCPCI_TIM3_125; + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | + HFCPCI_CLTIMER); + } + break; + } +} + +static void +ph_state(struct dchannel *dch) +{ + struct hfc_pci *hc = dch->hw; + + if (hc->hw.protocol == ISDN_P_NT_S0) { + if (test_bit(FLG_HFC_TIMER_T3, &dch->Flags) && + hc->hw.nt_timer < 0) + handle_nt_timer3(dch); + else + ph_state_nt(dch); + } else + ph_state_te(dch); +} + +/* + * Layer 1 callback function + */ +static int +hfc_l1callback(struct dchannel *dch, u_int cmd) +{ + struct hfc_pci *hc = dch->hw; + + switch (cmd) { + case INFO3_P8: + case INFO3_P10: + if (test_bit(HFC_CFG_MASTER, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + break; + case HW_RESET_REQ: + Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 3); + /* HFC ST 3 */ + udelay(6); + Write_hfc(hc, HFCPCI_STATES, 3); /* HFC ST 2 */ + if (test_bit(HFC_CFG_MASTER, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE | + HFCPCI_DO_ACTION); + l1_event(dch->l1, HW_POWERUP_IND); + break; + case HW_DEACT_REQ: + hc->hw.mst_m &= ~HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) + del_timer(&dch->timer); + break; + case HW_POWERUP_REQ: + Write_hfc(hc, HFCPCI_STATES, HFCPCI_DO_ACTION); + break; + case PH_ACTIVATE_IND: + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + case PH_DEACTIVATE_IND: + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: unknown command %x\n", + __func__, cmd); + return -1; + } + return 0; +} + +/* + * Interrupt handler + */ +static inline void +tx_birq(struct bchannel *bch) +{ + if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len) + hfcpci_fill_fifo(bch); + else { + if (bch->tx_skb) + dev_kfree_skb(bch->tx_skb); + if (get_next_bframe(bch)) + hfcpci_fill_fifo(bch); + } +} + +static inline void +tx_dirq(struct dchannel *dch) +{ + if (dch->tx_skb && dch->tx_idx < dch->tx_skb->len) + hfcpci_fill_dfifo(dch->hw); + else { + if (dch->tx_skb) + dev_kfree_skb(dch->tx_skb); + if (get_next_dframe(dch)) + hfcpci_fill_dfifo(dch->hw); + } +} + +static irqreturn_t +hfcpci_int(int intno, void *dev_id) +{ + struct hfc_pci *hc = dev_id; + u_char exval; + struct bchannel *bch; + u_char val, stat; + + spin_lock(&hc->lock); + if (!(hc->hw.int_m2 & 0x08)) { + spin_unlock(&hc->lock); + return IRQ_NONE; /* not initialised */ + } + stat = Read_hfc(hc, HFCPCI_STATUS); + if (HFCPCI_ANYINT & stat) { + val = Read_hfc(hc, HFCPCI_INT_S1); + if (hc->dch.debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG + "HFC-PCI: stat(%02x) s1(%02x)\n", stat, val); + } else { + /* shared */ + spin_unlock(&hc->lock); + return IRQ_NONE; + } + hc->irqcnt++; + + if (hc->dch.debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG "HFC-PCI irq %x\n", val); + val &= hc->hw.int_m1; + if (val & 0x40) { /* state machine irq */ + exval = Read_hfc(hc, HFCPCI_STATES) & 0xf; + if (hc->dch.debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG "ph_state chg %d->%d\n", + hc->dch.state, exval); + hc->dch.state = exval; + schedule_event(&hc->dch, FLG_PHCHANGE); + val &= ~0x40; + } + if (val & 0x80) { /* timer irq */ + if (hc->hw.protocol == ISDN_P_NT_S0) { + if ((--hc->hw.nt_timer) < 0) + schedule_event(&hc->dch, FLG_PHCHANGE); + } + val &= ~0x80; + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER); + } + if (val & 0x08) { + bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); + if (bch) + main_rec_hfcpci(bch); + else if (hc->dch.debug) + printk(KERN_DEBUG "hfcpci spurious 0x08 IRQ\n"); + } + if (val & 0x10) { + bch = Sel_BCS(hc, 2); + if (bch) + main_rec_hfcpci(bch); + else if (hc->dch.debug) + printk(KERN_DEBUG "hfcpci spurious 0x10 IRQ\n"); + } + if (val & 0x01) { + bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); + if (bch) + tx_birq(bch); + else if (hc->dch.debug) + printk(KERN_DEBUG "hfcpci spurious 0x01 IRQ\n"); + } + if (val & 0x02) { + bch = Sel_BCS(hc, 2); + if (bch) + tx_birq(bch); + else if (hc->dch.debug) + printk(KERN_DEBUG "hfcpci spurious 0x02 IRQ\n"); + } + if (val & 0x20) + receive_dmsg(hc); + if (val & 0x04) { /* dframe transmitted */ + if (test_and_clear_bit(FLG_BUSY_TIMER, &hc->dch.Flags)) + del_timer(&hc->dch.timer); + tx_dirq(&hc->dch); + } + spin_unlock(&hc->lock); + return IRQ_HANDLED; +} + +/* + * timer callback for D-chan busy resolution. Currently no function + */ +static void +hfcpci_dbusy_timer(struct hfc_pci *hc) +{ +} + +/* + * activate/deactivate hardware for selected channels and mode + */ +static int +mode_hfcpci(struct bchannel *bch, int bc, int protocol) +{ + struct hfc_pci *hc = bch->hw; + int fifo2; + u_char rx_slot = 0, tx_slot = 0, pcm_mode; + + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG + "HFCPCI bchannel protocol %x-->%x ch %x-->%x\n", + bch->state, protocol, bch->nr, bc); + + fifo2 = bc; + pcm_mode = (bc>>24) & 0xff; + if (pcm_mode) { /* PCM SLOT USE */ + if (!test_bit(HFC_CFG_PCM, &hc->cfg)) + printk(KERN_WARNING + "%s: pcm channel id without HFC_CFG_PCM\n", + __func__); + rx_slot = (bc>>8) & 0xff; + tx_slot = (bc>>16) & 0xff; + bc = bc & 0xff; + } else if (test_bit(HFC_CFG_PCM, &hc->cfg) && + (protocol > ISDN_P_NONE)) + printk(KERN_WARNING "%s: no pcm channel id but HFC_CFG_PCM\n", + __func__); + if (hc->chanlimit > 1) { + hc->hw.bswapped = 0; /* B1 and B2 normal mode */ + hc->hw.sctrl_e &= ~0x80; + } else { + if (bc & 2) { + if (protocol != ISDN_P_NONE) { + hc->hw.bswapped = 1; /* B1 and B2 exchanged */ + hc->hw.sctrl_e |= 0x80; + } else { + hc->hw.bswapped = 0; /* B1 and B2 normal mode */ + hc->hw.sctrl_e &= ~0x80; + } + fifo2 = 1; + } else { + hc->hw.bswapped = 0; /* B1 and B2 normal mode */ + hc->hw.sctrl_e &= ~0x80; + } + } + switch (protocol) { + case (-1): /* used for init */ + bch->state = -1; + bch->nr = bc; + case (ISDN_P_NONE): + if (bch->state == ISDN_P_NONE) + return 0; + if (bc & 2) { + hc->hw.sctrl &= ~SCTRL_B2_ENA; + hc->hw.sctrl_r &= ~SCTRL_B2_ENA; + } else { + hc->hw.sctrl &= ~SCTRL_B1_ENA; + hc->hw.sctrl_r &= ~SCTRL_B1_ENA; + } + if (fifo2 & 2) { + hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B2; + hc->hw.int_m1 &= ~(HFCPCI_INTS_B2TRANS + + HFCPCI_INTS_B2REC); + } else { + hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B1; + hc->hw.int_m1 &= ~(HFCPCI_INTS_B1TRANS + + HFCPCI_INTS_B1REC); + } +#ifdef REVERSE_BITORDER + if (bch->nr & 2) + hc->hw.cirm &= 0x7f; + else + hc->hw.cirm &= 0xbf; +#endif + bch->state = ISDN_P_NONE; + bch->nr = bc; + test_and_clear_bit(FLG_HDLC, &bch->Flags); + test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags); + break; + case (ISDN_P_B_RAW): + bch->state = protocol; + bch->nr = bc; + hfcpci_clear_fifo_rx(hc, (fifo2 & 2)?1:0); + hfcpci_clear_fifo_tx(hc, (fifo2 & 2)?1:0); + if (bc & 2) { + hc->hw.sctrl |= SCTRL_B2_ENA; + hc->hw.sctrl_r |= SCTRL_B2_ENA; +#ifdef REVERSE_BITORDER + hc->hw.cirm |= 0x80; +#endif + } else { + hc->hw.sctrl |= SCTRL_B1_ENA; + hc->hw.sctrl_r |= SCTRL_B1_ENA; +#ifdef REVERSE_BITORDER + hc->hw.cirm |= 0x40; +#endif + } + if (fifo2 & 2) { + hc->hw.fifo_en |= HFCPCI_FIFOEN_B2; + hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS + + HFCPCI_INTS_B2REC); + hc->hw.ctmt |= 2; + hc->hw.conn &= ~0x18; + } else { + hc->hw.fifo_en |= HFCPCI_FIFOEN_B1; + hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS + + HFCPCI_INTS_B1REC); + hc->hw.ctmt |= 1; + hc->hw.conn &= ~0x03; + } + test_and_set_bit(FLG_TRANSPARENT, &bch->Flags); + break; + case (ISDN_P_B_HDLC): + bch->state = protocol; + bch->nr = bc; + hfcpci_clear_fifo_rx(hc, (fifo2 & 2)?1:0); + hfcpci_clear_fifo_tx(hc, (fifo2 & 2)?1:0); + if (bc & 2) { + hc->hw.sctrl |= SCTRL_B2_ENA; + hc->hw.sctrl_r |= SCTRL_B2_ENA; + } else { + hc->hw.sctrl |= SCTRL_B1_ENA; + hc->hw.sctrl_r |= SCTRL_B1_ENA; + } + if (fifo2 & 2) { + hc->hw.last_bfifo_cnt[1] = 0; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B2; + hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS + + HFCPCI_INTS_B2REC); + hc->hw.ctmt &= ~2; + hc->hw.conn &= ~0x18; + } else { + hc->hw.last_bfifo_cnt[0] = 0; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B1; + hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS + + HFCPCI_INTS_B1REC); + hc->hw.ctmt &= ~1; + hc->hw.conn &= ~0x03; + } + test_and_set_bit(FLG_HDLC, &bch->Flags); + break; + default: + printk(KERN_DEBUG "prot not known %x\n", protocol); + return -ENOPROTOOPT; + } + if (test_bit(HFC_CFG_PCM, &hc->cfg)) { + if ((protocol == ISDN_P_NONE) || + (protocol == -1)) { /* init case */ + rx_slot = 0; + tx_slot = 0; + } else { + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) { + rx_slot |= 0xC0; + tx_slot |= 0xC0; + } else { + rx_slot |= 0x80; + tx_slot |= 0x80; + } + } + if (bc & 2) { + hc->hw.conn &= 0xc7; + hc->hw.conn |= 0x08; + printk(KERN_DEBUG "%s: Write_hfc: B2_SSL 0x%x\n", + __func__, tx_slot); + printk(KERN_DEBUG "%s: Write_hfc: B2_RSL 0x%x\n", + __func__, rx_slot); + Write_hfc(hc, HFCPCI_B2_SSL, tx_slot); + Write_hfc(hc, HFCPCI_B2_RSL, rx_slot); + } else { + hc->hw.conn &= 0xf8; + hc->hw.conn |= 0x01; + printk(KERN_DEBUG "%s: Write_hfc: B1_SSL 0x%x\n", + __func__, tx_slot); + printk(KERN_DEBUG "%s: Write_hfc: B1_RSL 0x%x\n", + __func__, rx_slot); + Write_hfc(hc, HFCPCI_B1_SSL, tx_slot); + Write_hfc(hc, HFCPCI_B1_RSL, rx_slot); + } + } + Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e); + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl); + Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); +#ifdef REVERSE_BITORDER + Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); +#endif + return 0; +} + +static int +set_hfcpci_rxtest(struct bchannel *bch, int protocol, int chan) +{ + struct hfc_pci *hc = bch->hw; + + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG + "HFCPCI bchannel test rx protocol %x-->%x ch %x-->%x\n", + bch->state, protocol, bch->nr, chan); + if (bch->nr != chan) { + printk(KERN_DEBUG + "HFCPCI rxtest wrong channel parameter %x/%x\n", + bch->nr, chan); + return -EINVAL; + } + switch (protocol) { + case (ISDN_P_B_RAW): + bch->state = protocol; + hfcpci_clear_fifo_rx(hc, (chan & 2)?1:0); + if (chan & 2) { + hc->hw.sctrl_r |= SCTRL_B2_ENA; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX; + hc->hw.int_m1 |= HFCPCI_INTS_B2REC; + hc->hw.ctmt |= 2; + hc->hw.conn &= ~0x18; +#ifdef REVERSE_BITORDER + hc->hw.cirm |= 0x80; +#endif + } else { + hc->hw.sctrl_r |= SCTRL_B1_ENA; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX; + hc->hw.int_m1 |= HFCPCI_INTS_B1REC; + hc->hw.ctmt |= 1; + hc->hw.conn &= ~0x03; +#ifdef REVERSE_BITORDER + hc->hw.cirm |= 0x40; +#endif + } + break; + case (ISDN_P_B_HDLC): + bch->state = protocol; + hfcpci_clear_fifo_rx(hc, (chan & 2)?1:0); + if (chan & 2) { + hc->hw.sctrl_r |= SCTRL_B2_ENA; + hc->hw.last_bfifo_cnt[1] = 0; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX; + hc->hw.int_m1 |= HFCPCI_INTS_B2REC; + hc->hw.ctmt &= ~2; + hc->hw.conn &= ~0x18; + } else { + hc->hw.sctrl_r |= SCTRL_B1_ENA; + hc->hw.last_bfifo_cnt[0] = 0; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX; + hc->hw.int_m1 |= HFCPCI_INTS_B1REC; + hc->hw.ctmt &= ~1; + hc->hw.conn &= ~0x03; + } + break; + default: + printk(KERN_DEBUG "prot not known %x\n", protocol); + return -ENOPROTOOPT; + } + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); +#ifdef REVERSE_BITORDER + Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); +#endif + return 0; +} + +static void +deactivate_bchannel(struct bchannel *bch) +{ + struct hfc_pci *hc = bch->hw; + u_long flags; + + spin_lock_irqsave(&hc->lock, flags); + if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) { + dev_kfree_skb(bch->next_skb); + bch->next_skb = NULL; + } + if (bch->tx_skb) { + dev_kfree_skb(bch->tx_skb); + bch->tx_skb = NULL; + } + bch->tx_idx = 0; + if (bch->rx_skb) { + dev_kfree_skb(bch->rx_skb); + bch->rx_skb = NULL; + } + mode_hfcpci(bch, bch->nr, ISDN_P_NONE); + test_and_clear_bit(FLG_ACTIVE, &bch->Flags); + test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); + spin_unlock_irqrestore(&hc->lock, flags); +} + +/* + * Layer 1 B-channel hardware access + */ +static int +channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = 0; + break; + default: + printk(KERN_WARNING "%s: unknown Op %x\n", __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} +static int +hfc_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfc_pci *hc = bch->hw; + int ret = -EINVAL; + u_long flags; + + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", __func__, cmd, arg); + switch (cmd) { + case HW_TESTRX_RAW: + spin_lock_irqsave(&hc->lock, flags); + ret = set_hfcpci_rxtest(bch, ISDN_P_B_RAW, (int)(long)arg); + spin_unlock_irqrestore(&hc->lock, flags); + break; + case HW_TESTRX_HDLC: + spin_lock_irqsave(&hc->lock, flags); + ret = set_hfcpci_rxtest(bch, ISDN_P_B_HDLC, (int)(long)arg); + spin_unlock_irqrestore(&hc->lock, flags); + break; + case HW_TESTRX_OFF: + spin_lock_irqsave(&hc->lock, flags); + mode_hfcpci(bch, bch->nr, ISDN_P_NONE); + spin_unlock_irqrestore(&hc->lock, flags); + ret = 0; + break; + case CLOSE_CHANNEL: + test_and_clear_bit(FLG_OPEN, &bch->Flags); + if (test_bit(FLG_ACTIVE, &bch->Flags)) + deactivate_bchannel(bch); + ch->protocol = ISDN_P_NONE; + ch->peer = NULL; + module_put(THIS_MODULE); + ret = 0; + break; + case CONTROL_CHANNEL: + ret = channel_bctrl(bch, arg); + break; + default: + printk(KERN_WARNING "%s: unknown prim(%x)\n", + __func__, cmd); + } + return ret; +} + +/* + * Layer2 -> Layer 1 Dchannel data + */ +static int +hfcpci_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct hfc_pci *hc = dch->hw; + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + unsigned int id; + u_long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + spin_lock_irqsave(&hc->lock, flags); + ret = dchannel_senddata(dch, skb); + if (ret > 0) { /* direct TX */ + id = hh->id; /* skb can be freed */ + hfcpci_fill_dfifo(dch->hw); + ret = 0; + spin_unlock_irqrestore(&hc->lock, flags); + queue_ch_frame(ch, PH_DATA_CNF, id, NULL); + } else + spin_unlock_irqrestore(&hc->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + spin_lock_irqsave(&hc->lock, flags); + if (hc->hw.protocol == ISDN_P_NT_S0) { + ret = 0; + if (test_bit(HFC_CFG_MASTER, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + if (test_bit(FLG_ACTIVE, &dch->Flags)) { + spin_unlock_irqrestore(&hc->lock, flags); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + } + test_and_set_bit(FLG_L2_ACTIVATED, &dch->Flags); + Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE | + HFCPCI_DO_ACTION | 1); + } else + ret = l1_event(dch->l1, hh->prim); + spin_unlock_irqrestore(&hc->lock, flags); + break; + case PH_DEACTIVATE_REQ: + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + spin_lock_irqsave(&hc->lock, flags); + if (hc->hw.protocol == ISDN_P_NT_S0) { + /* prepare deactivation */ + Write_hfc(hc, HFCPCI_STATES, 0x40); + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) + del_timer(&dch->timer); +#ifdef FIXME + if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) + dchannel_sched_event(&hc->dch, D_CLEARBUSY); +#endif + hc->hw.mst_m &= ~HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + ret = 0; + } else { + ret = l1_event(dch->l1, hh->prim); + } + spin_unlock_irqrestore(&hc->lock, flags); + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +/* + * Layer2 -> Layer 1 Bchannel data + */ +static int +hfcpci_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfc_pci *hc = bch->hw; + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + unsigned int id; + u_long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + spin_lock_irqsave(&hc->lock, flags); + ret = bchannel_senddata(bch, skb); + if (ret > 0) { /* direct TX */ + id = hh->id; /* skb can be freed */ + hfcpci_fill_fifo(bch); + ret = 0; + spin_unlock_irqrestore(&hc->lock, flags); + if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) + queue_ch_frame(ch, PH_DATA_CNF, id, NULL); + } else + spin_unlock_irqrestore(&hc->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + spin_lock_irqsave(&hc->lock, flags); + if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) + ret = mode_hfcpci(bch, bch->nr, ch->protocol); + else + ret = 0; + spin_unlock_irqrestore(&hc->lock, flags); + if (!ret) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + break; + case PH_DEACTIVATE_REQ: + deactivate_bchannel(bch); + _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + ret = 0; + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +/* + * called for card init message + */ + +void +inithfcpci(struct hfc_pci *hc) +{ + printk(KERN_DEBUG "inithfcpci: entered\n"); + hc->dch.timer.function = (void *) hfcpci_dbusy_timer; + hc->dch.timer.data = (long) &hc->dch; + init_timer(&hc->dch.timer); + hc->chanlimit = 2; + mode_hfcpci(&hc->bch[0], 1, -1); + mode_hfcpci(&hc->bch[1], 2, -1); +} + + +static int +init_card(struct hfc_pci *hc) +{ + int cnt = 3; + u_long flags; + + printk(KERN_DEBUG "init_card: entered\n"); + + + spin_lock_irqsave(&hc->lock, flags); + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + if (request_irq(hc->irq, hfcpci_int, IRQF_SHARED, "HFC PCI", hc)) { + printk(KERN_WARNING + "mISDN: couldn't get interrupt %d\n", hc->irq); + return -EIO; + } + spin_lock_irqsave(&hc->lock, flags); + reset_hfcpci(hc); + while (cnt) { + inithfcpci(hc); + /* + * Finally enable IRQ output + * this is only allowed, if an IRQ routine is allready + * established for this HFC, so don't do that earlier + */ + enable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + /* Timeout 80ms */ + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout((80*HZ)/1000); + printk(KERN_INFO "HFC PCI: IRQ %d count %d\n", + hc->irq, hc->irqcnt); + /* now switch timer interrupt off */ + spin_lock_irqsave(&hc->lock, flags); + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + /* reinit mode reg */ + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + if (!hc->irqcnt) { + printk(KERN_WARNING + "HFC PCI: IRQ(%d) getting no interrupts " + "during init %d\n", hc->irq, 4 - cnt); + if (cnt == 1) { + spin_unlock_irqrestore(&hc->lock, flags); + return -EIO; + } else { + reset_hfcpci(hc); + cnt--; + } + } else { + spin_unlock_irqrestore(&hc->lock, flags); + hc->initdone = 1; + return 0; + } + } + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + free_irq(hc->irq, hc); + return -EIO; +} + +static int +channel_ctrl(struct hfc_pci *hc, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + u_char slot; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_CONNECT | + MISDN_CTRL_DISCONNECT; + break; + case MISDN_CTRL_LOOP: + /* channel 0 disabled loop */ + if (cq->channel < 0 || cq->channel > 2) { + ret = -EINVAL; + break; + } + if (cq->channel & 1) { + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) + slot = 0xC0; + else + slot = 0x80; + printk(KERN_DEBUG "%s: Write_hfc: B1_SSL/RSL 0x%x\n", + __func__, slot); + Write_hfc(hc, HFCPCI_B1_SSL, slot); + Write_hfc(hc, HFCPCI_B1_RSL, slot); + hc->hw.conn = (hc->hw.conn & ~7) | 6; + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + } + if (cq->channel & 2) { + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) + slot = 0xC1; + else + slot = 0x81; + printk(KERN_DEBUG "%s: Write_hfc: B2_SSL/RSL 0x%x\n", + __func__, slot); + Write_hfc(hc, HFCPCI_B2_SSL, slot); + Write_hfc(hc, HFCPCI_B2_RSL, slot); + hc->hw.conn = (hc->hw.conn & ~0x38) | 0x30; + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + } + if (cq->channel & 3) + hc->hw.trm |= 0x80; /* enable IOM-loop */ + else { + hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x09; + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + hc->hw.trm &= 0x7f; /* disable IOM-loop */ + } + Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); + break; + case MISDN_CTRL_CONNECT: + if (cq->channel == cq->p1) { + ret = -EINVAL; + break; + } + if (cq->channel < 1 || cq->channel > 2 || + cq->p1 < 1 || cq->p1 > 2) { + ret = -EINVAL; + break; + } + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) + slot = 0xC0; + else + slot = 0x80; + printk(KERN_DEBUG "%s: Write_hfc: B1_SSL/RSL 0x%x\n", + __func__, slot); + Write_hfc(hc, HFCPCI_B1_SSL, slot); + Write_hfc(hc, HFCPCI_B2_RSL, slot); + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) + slot = 0xC1; + else + slot = 0x81; + printk(KERN_DEBUG "%s: Write_hfc: B2_SSL/RSL 0x%x\n", + __func__, slot); + Write_hfc(hc, HFCPCI_B2_SSL, slot); + Write_hfc(hc, HFCPCI_B1_RSL, slot); + hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x36; + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + hc->hw.trm |= 0x80; + Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); + break; + case MISDN_CTRL_DISCONNECT: + hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x09; + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + hc->hw.trm &= 0x7f; /* disable IOM-loop */ + break; + default: + printk(KERN_WARNING "%s: unknown Op %x\n", + __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +static int +open_dchannel(struct hfc_pci *hc, struct mISDNchannel *ch, + struct channel_req *rq) +{ + int err = 0; + + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__, + hc->dch.dev.id, __builtin_return_address(0)); + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + if (!hc->initdone) { + if (rq->protocol == ISDN_P_TE_S0) { + err = create_l1(&hc->dch, hfc_l1callback); + if (err) + return err; + } + hc->hw.protocol = rq->protocol; + ch->protocol = rq->protocol; + err = init_card(hc); + if (err) + return err; + } else { + if (rq->protocol != ch->protocol) { + if (hc->hw.protocol == ISDN_P_TE_S0) + l1_event(hc->dch.l1, CLOSE_CHANNEL); + hc->hw.protocol = rq->protocol; + ch->protocol = rq->protocol; + hfcpci_setmode(hc); + } + } + + if (((ch->protocol == ISDN_P_NT_S0) && (hc->dch.state == 3)) || + ((ch->protocol == ISDN_P_TE_S0) && (hc->dch.state == 7))) { + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + } + rq->ch = ch; + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", __func__); + return 0; +} + +static int +open_bchannel(struct hfc_pci *hc, struct channel_req *rq) +{ + struct bchannel *bch; + + if (rq->adr.channel > 2) + return -EINVAL; + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + bch = &hc->bch[rq->adr.channel - 1]; + if (test_and_set_bit(FLG_OPEN, &bch->Flags)) + return -EBUSY; /* b-channel can be only open once */ + bch->ch.protocol = rq->protocol; + rq->ch = &bch->ch; /* TODO: E-channel */ + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", __func__); + return 0; +} + +/* + * device control function + */ +static int +hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct hfc_pci *hc = dch->hw; + struct channel_req *rq; + int err = 0; + + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", + __func__, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + rq = arg; + if (rq->adr.channel == 0) + err = open_dchannel(hc, ch, rq); + else + err = open_bchannel(hc, rq); + break; + case CLOSE_CHANNEL: + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) close from %p\n", + __func__, hc->dch.dev.id, + __builtin_return_address(0)); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: + err = channel_ctrl(hc, arg); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: unknown command %x\n", + __func__, cmd); + return -EINVAL; + } + return err; +} + +static int +setup_hw(struct hfc_pci *hc) +{ + void *buffer; + + printk(KERN_INFO "mISDN: HFC-PCI driver %s\n", hfcpci_revision); + hc->hw.cirm = 0; + hc->dch.state = 0; + pci_set_master(hc->pdev); + if (!hc->irq) { + printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n"); + return 1; + } + hc->hw.pci_io = (char *)(ulong)hc->pdev->resource[1].start; + + if (!hc->hw.pci_io) { + printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n"); + return 1; + } + /* Allocate memory for FIFOS */ + /* the memory needs to be on a 32k boundary within the first 4G */ + pci_set_dma_mask(hc->pdev, 0xFFFF8000); + buffer = pci_alloc_consistent(hc->pdev, 0x8000, &hc->hw.dmahandle); + /* We silently assume the address is okay if nonzero */ + if (!buffer) { + printk(KERN_WARNING + "HFC-PCI: Error allocating memory for FIFO!\n"); + return 1; + } + hc->hw.fifos = buffer; + pci_write_config_dword(hc->pdev, 0x80, hc->hw.dmahandle); + hc->hw.pci_io = ioremap((ulong) hc->hw.pci_io, 256); + printk(KERN_INFO + "HFC-PCI: defined at mem %#lx fifo %#lx(%#lx) IRQ %d HZ %d\n", + (u_long) hc->hw.pci_io, (u_long) hc->hw.fifos, + (u_long) hc->hw.dmahandle, hc->irq, HZ); + /* enable memory mapped ports, disable busmaster */ + pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO); + hc->hw.int_m2 = 0; + disable_hwirq(hc); + hc->hw.int_m1 = 0; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + /* At this point the needed PCI config is done */ + /* fifos are still not enabled */ + hc->hw.timer.function = (void *) hfcpci_Timer; + hc->hw.timer.data = (long) hc; + init_timer(&hc->hw.timer); + /* default PCM master */ + test_and_set_bit(HFC_CFG_MASTER, &hc->cfg); + return 0; +} + +static void +release_card(struct hfc_pci *hc) { + u_long flags; + + spin_lock_irqsave(&hc->lock, flags); + hc->hw.int_m2 = 0; /* interrupt output off ! */ + disable_hwirq(hc); + mode_hfcpci(&hc->bch[0], 1, ISDN_P_NONE); + mode_hfcpci(&hc->bch[1], 2, ISDN_P_NONE); + if (hc->dch.timer.function != NULL) { + del_timer(&hc->dch.timer); + hc->dch.timer.function = NULL; + } + spin_unlock_irqrestore(&hc->lock, flags); + if (hc->hw.protocol == ISDN_P_TE_S0) + l1_event(hc->dch.l1, CLOSE_CHANNEL); + if (hc->initdone) + free_irq(hc->irq, hc); + release_io_hfcpci(hc); /* must release after free_irq! */ + mISDN_unregister_device(&hc->dch.dev); + mISDN_freebchannel(&hc->bch[1]); + mISDN_freebchannel(&hc->bch[0]); + mISDN_freedchannel(&hc->dch); + list_del(&hc->list); + pci_set_drvdata(hc->pdev, NULL); + kfree(hc); +} + +static int +setup_card(struct hfc_pci *card) +{ + int err = -EINVAL; + u_int i; + u_long flags; + char name[MISDN_MAX_IDLEN]; + + if (HFC_cnt >= MAX_CARDS) + return -EINVAL; /* maybe better value */ + + card->dch.debug = debug; + spin_lock_init(&card->lock); + mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, ph_state); + card->dch.hw = card; + card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); + card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + card->dch.dev.D.send = hfcpci_l2l1D; + card->dch.dev.D.ctrl = hfc_dctrl; + card->dch.dev.nrbchan = 2; + for (i = 0; i < 2; i++) { + card->bch[i].nr = i + 1; + test_and_set_bit(i + 1, &card->dch.dev.channelmap[0]); + card->bch[i].debug = debug; + mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM); + card->bch[i].hw = card; + card->bch[i].ch.send = hfcpci_l2l1B; + card->bch[i].ch.ctrl = hfc_bctrl; + card->bch[i].ch.nr = i + 1; + list_add(&card->bch[i].ch.list, &card->dch.dev.bchannels); + } + err = setup_hw(card); + if (err) + goto error; + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-pci.%d", HFC_cnt + 1); + err = mISDN_register_device(&card->dch.dev, name); + if (err) + goto error; + HFC_cnt++; + write_lock_irqsave(&HFClock, flags); + list_add_tail(&card->list, &HFClist); + write_unlock_irqrestore(&HFClock, flags); + printk(KERN_INFO "HFC %d cards installed\n", HFC_cnt); + return 0; +error: + mISDN_freebchannel(&card->bch[1]); + mISDN_freebchannel(&card->bch[0]); + mISDN_freedchannel(&card->dch); + kfree(card); + return err; +} + +/* private data in the PCI devices list */ +struct _hfc_map { + u_int subtype; + u_int flag; + char *name; +}; + +static const struct _hfc_map hfc_map[] = +{ + {HFC_CCD_2BD0, 0, "CCD/Billion/Asuscom 2BD0"}, + {HFC_CCD_B000, 0, "Billion B000"}, + {HFC_CCD_B006, 0, "Billion B006"}, + {HFC_CCD_B007, 0, "Billion B007"}, + {HFC_CCD_B008, 0, "Billion B008"}, + {HFC_CCD_B009, 0, "Billion B009"}, + {HFC_CCD_B00A, 0, "Billion B00A"}, + {HFC_CCD_B00B, 0, "Billion B00B"}, + {HFC_CCD_B00C, 0, "Billion B00C"}, + {HFC_CCD_B100, 0, "Seyeon B100"}, + {HFC_CCD_B700, 0, "Primux II S0 B700"}, + {HFC_CCD_B701, 0, "Primux II S0 NT B701"}, + {HFC_ABOCOM_2BD1, 0, "Abocom/Magitek 2BD1"}, + {HFC_ASUS_0675, 0, "Asuscom/Askey 675"}, + {HFC_BERKOM_TCONCEPT, 0, "German telekom T-Concept"}, + {HFC_BERKOM_A1T, 0, "German telekom A1T"}, + {HFC_ANIGMA_MC145575, 0, "Motorola MC145575"}, + {HFC_ZOLTRIX_2BD0, 0, "Zoltrix 2BD0"}, + {HFC_DIGI_DF_M_IOM2_E, 0, + "Digi International DataFire Micro V IOM2 (Europe)"}, + {HFC_DIGI_DF_M_E, 0, + "Digi International DataFire Micro V (Europe)"}, + {HFC_DIGI_DF_M_IOM2_A, 0, + "Digi International DataFire Micro V IOM2 (North America)"}, + {HFC_DIGI_DF_M_A, 0, + "Digi International DataFire Micro V (North America)"}, + {HFC_SITECOM_DC105V2, 0, "Sitecom Connectivity DC-105 ISDN TA"}, + {}, +}; + +static struct pci_device_id hfc_ids[] = +{ + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[0]}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[1]}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[2]}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[3]}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[4]}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[5]}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[6]}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[7]}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[8]}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[9]}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B700, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[10]}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B701, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[11]}, + {PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[12]}, + {PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[13]}, + {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[14]}, + {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[15]}, + {PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[16]}, + {PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[17]}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[18]}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[19]}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[20]}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[21]}, + {PCI_VENDOR_ID_SITECOM, PCI_DEVICE_ID_SITECOM_DC105V2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[22]}, + {}, +}; + +static int __devinit +hfc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err = -ENOMEM; + struct hfc_pci *card; + struct _hfc_map *m = (struct _hfc_map *)ent->driver_data; + + card = kzalloc(sizeof(struct hfc_pci), GFP_ATOMIC); + if (!card) { + printk(KERN_ERR "No kmem for HFC card\n"); + return err; + } + card->pdev = pdev; + card->subtype = m->subtype; + err = pci_enable_device(pdev); + if (err) { + kfree(card); + return err; + } + + printk(KERN_INFO "mISDN_hfcpci: found adapter %s at %s\n", + m->name, pci_name(pdev)); + + card->irq = pdev->irq; + pci_set_drvdata(pdev, card); + err = setup_card(card); + if (err) + pci_set_drvdata(pdev, NULL); + return err; +} + +static void __devexit +hfc_remove_pci(struct pci_dev *pdev) +{ + struct hfc_pci *card = pci_get_drvdata(pdev); + u_long flags; + + if (card) { + write_lock_irqsave(&HFClock, flags); + release_card(card); + write_unlock_irqrestore(&HFClock, flags); + } else + if (debug) + printk(KERN_WARNING "%s: drvdata allready removed\n", + __func__); +} + + +static struct pci_driver hfc_driver = { + .name = "hfcpci", + .probe = hfc_probe, + .remove = __devexit_p(hfc_remove_pci), + .id_table = hfc_ids, +}; + +static int __init +HFC_init(void) +{ + int err; + + err = pci_register_driver(&hfc_driver); + return err; +} + +static void __exit +HFC_cleanup(void) +{ + struct hfc_pci *card, *next; + + list_for_each_entry_safe(card, next, &HFClist, list) { + release_card(card); + } + pci_unregister_driver(&hfc_driver); +} + +module_init(HFC_init); +module_exit(HFC_cleanup); diff --git a/drivers/isdn/hisax/hisax_fcpcipnp.c b/drivers/isdn/hisax/hisax_fcpcipnp.c index c0b4db2f8364..1925118122f8 100644 --- a/drivers/isdn/hisax/hisax_fcpcipnp.c +++ b/drivers/isdn/hisax/hisax_fcpcipnp.c @@ -974,8 +974,6 @@ static struct pnp_driver fcpnp_driver = { .remove = __devexit_p(fcpnp_remove), .id_table = fcpnp_ids, }; -#else -static struct pnp_driver fcpnp_driver; #endif static void __devexit fcpci_remove(struct pci_dev *pdev) diff --git a/drivers/isdn/hisax/st5481.h b/drivers/isdn/hisax/st5481.h index 2044e7173ab4..cff7a6354334 100644 --- a/drivers/isdn/hisax/st5481.h +++ b/drivers/isdn/hisax/st5481.h @@ -220,7 +220,7 @@ enum { #define ERR(format, arg...) \ printk(KERN_ERR "%s:%s: " format "\n" , __FILE__, __func__ , ## arg) -#define WARN(format, arg...) \ +#define WARNING(format, arg...) \ printk(KERN_WARNING "%s:%s: " format "\n" , __FILE__, __func__ , ## arg) #define INFO(format, arg...) \ @@ -412,7 +412,7 @@ struct st5481_adapter { ({ \ int status; \ if ((status = usb_submit_urb(urb, mem_flags)) < 0) { \ - WARN("usb_submit_urb failed,status=%d", status); \ + WARNING("usb_submit_urb failed,status=%d", status); \ } \ status; \ }) diff --git a/drivers/isdn/hisax/st5481_b.c b/drivers/isdn/hisax/st5481_b.c index fa64115cd7c7..0074b600a0ef 100644 --- a/drivers/isdn/hisax/st5481_b.c +++ b/drivers/isdn/hisax/st5481_b.c @@ -180,7 +180,7 @@ static void usb_b_out_complete(struct urb *urb) DBG(4,"urb killed status %d", urb->status); return; // Give up default: - WARN("urb status %d",urb->status); + WARNING("urb status %d",urb->status); if (b_out->busy == 0) { st5481_usb_pipe_reset(adapter, (bcs->channel+1)*2 | USB_DIR_OUT, NULL, NULL); } @@ -372,6 +372,6 @@ void st5481_b_l2l1(struct hisax_if *ifc, int pr, void *arg) B_L1L2(bcs, PH_DEACTIVATE | INDICATION, NULL); break; default: - WARN("pr %#x\n", pr); + WARNING("pr %#x\n", pr); } } diff --git a/drivers/isdn/hisax/st5481_d.c b/drivers/isdn/hisax/st5481_d.c index b8c4855cc889..077991c1cd05 100644 --- a/drivers/isdn/hisax/st5481_d.c +++ b/drivers/isdn/hisax/st5481_d.c @@ -389,7 +389,7 @@ static void usb_d_out_complete(struct urb *urb) DBG(1,"urb killed status %d", urb->status); break; default: - WARN("urb status %d",urb->status); + WARNING("urb status %d",urb->status); if (d_out->busy == 0) { st5481_usb_pipe_reset(adapter, EP_D_OUT | USB_DIR_OUT, fifo_reseted, adapter); } @@ -420,7 +420,7 @@ static void dout_start_xmit(struct FsmInst *fsm, int event, void *arg) isdnhdlc_out_init(&d_out->hdlc_state, 1, 0); if (test_and_set_bit(buf_nr, &d_out->busy)) { - WARN("ep %d urb %d busy %#lx", EP_D_OUT, buf_nr, d_out->busy); + WARNING("ep %d urb %d busy %#lx", EP_D_OUT, buf_nr, d_out->busy); return; } urb = d_out->urb[buf_nr]; @@ -601,7 +601,7 @@ void st5481_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg) FsmEvent(&adapter->d_out.fsm, EV_DOUT_START_XMIT, NULL); break; default: - WARN("pr %#x\n", pr); + WARNING("pr %#x\n", pr); break; } } diff --git a/drivers/isdn/hisax/st5481_usb.c b/drivers/isdn/hisax/st5481_usb.c index 427a8b0520f5..ec3c0e507669 100644 --- a/drivers/isdn/hisax/st5481_usb.c +++ b/drivers/isdn/hisax/st5481_usb.c @@ -66,7 +66,7 @@ static void usb_ctrl_msg(struct st5481_adapter *adapter, struct ctrl_msg *ctrl_msg; if ((w_index = fifo_add(&ctrl->msg_fifo.f)) < 0) { - WARN("control msg FIFO full"); + WARNING("control msg FIFO full"); return; } ctrl_msg = &ctrl->msg_fifo.data[w_index]; @@ -139,7 +139,7 @@ static void usb_ctrl_complete(struct urb *urb) DBG(1,"urb killed status %d", urb->status); return; // Give up default: - WARN("urb status %d",urb->status); + WARNING("urb status %d",urb->status); break; } } @@ -198,7 +198,7 @@ static void usb_int_complete(struct urb *urb) DBG(2, "urb shutting down with status: %d", urb->status); return; default: - WARN("nonzero urb status received: %d", urb->status); + WARNING("nonzero urb status received: %d", urb->status); goto exit; } @@ -235,7 +235,7 @@ static void usb_int_complete(struct urb *urb) exit: status = usb_submit_urb (urb, GFP_ATOMIC); if (status) - WARN("usb_submit_urb failed with result %d", status); + WARNING("usb_submit_urb failed with result %d", status); } /* ====================================================================== @@ -257,7 +257,7 @@ int st5481_setup_usb(struct st5481_adapter *adapter) DBG(2,""); if ((status = usb_reset_configuration (dev)) < 0) { - WARN("reset_configuration failed,status=%d",status); + WARNING("reset_configuration failed,status=%d",status); return status; } @@ -269,7 +269,7 @@ int st5481_setup_usb(struct st5481_adapter *adapter) // Check if the config is sane if ( altsetting->desc.bNumEndpoints != 7 ) { - WARN("expecting 7 got %d endpoints!", altsetting->desc.bNumEndpoints); + WARNING("expecting 7 got %d endpoints!", altsetting->desc.bNumEndpoints); return -EINVAL; } @@ -279,7 +279,7 @@ int st5481_setup_usb(struct st5481_adapter *adapter) // Use alternative setting 3 on interface 0 to have 2B+D if ((status = usb_set_interface (dev, 0, 3)) < 0) { - WARN("usb_set_interface failed,status=%d",status); + WARNING("usb_set_interface failed,status=%d",status); return status; } @@ -497,7 +497,7 @@ static void usb_in_complete(struct urb *urb) DBG(1,"urb killed status %d", urb->status); return; // Give up default: - WARN("urb status %d",urb->status); + WARNING("urb status %d",urb->status); break; } } @@ -523,7 +523,7 @@ static void usb_in_complete(struct urb *urb) DBG(4,"count=%d",status); DBG_PACKET(0x400, in->rcvbuf, status); if (!(skb = dev_alloc_skb(status))) { - WARN("receive out of memory\n"); + WARNING("receive out of memory\n"); break; } memcpy(skb_put(skb, status), in->rcvbuf, status); diff --git a/drivers/isdn/mISDN/Kconfig b/drivers/isdn/mISDN/Kconfig new file mode 100644 index 000000000000..4938355c4072 --- /dev/null +++ b/drivers/isdn/mISDN/Kconfig @@ -0,0 +1,44 @@ +# +# modularer ISDN driver +# + +menuconfig MISDN + tristate "Modular ISDN driver" + help + Enable support for the modular ISDN driver. + +if MISDN != n + +config MISDN_DSP + tristate "Digital Audio Processing of transparent data" + depends on MISDN + help + Enable support for digital audio processing capability. + This module may be used for special applications that require + cross connecting of bchannels, conferencing, dtmf decoding + echo cancelation, tone generation, and Blowfish encryption and + decryption. + It may use hardware features if available. + E.g. it is required for PBX4Linux. Go to http://isdn.eversberg.eu + and get more informations about this module and it's usage. + If unsure, say 'N'. + +config MISDN_L1OIP + tristate "ISDN over IP tunnel" + depends on MISDN + help + Enable support for ISDN over IP tunnel. + + It features: + - dynamic IP exchange, if one or both peers have dynamic IPs + - BRI (S0) and PRI (S2M) interface + - layer 1 control via network keepalive frames + - direct tunneling of physical interface via IP + + NOTE: This protocol is called 'Layer 1 over IP' and is not + compatible with ISDNoIP (Agfeo) or TDMoIP. Protocol description is + provided in the source code. + +source "drivers/isdn/hardware/mISDN/Kconfig" + +endif #MISDN diff --git a/drivers/isdn/mISDN/Makefile b/drivers/isdn/mISDN/Makefile new file mode 100644 index 000000000000..1cb5e633cf75 --- /dev/null +++ b/drivers/isdn/mISDN/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for the modular ISDN driver +# + +obj-$(CONFIG_MISDN) += mISDN_core.o +obj-$(CONFIG_MISDN_DSP) += mISDN_dsp.o +obj-$(CONFIG_MISDN_L1OIP) += l1oip.o + +# multi objects + +mISDN_core-objs := core.o fsm.o socket.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o +mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_pipeline.o dsp_hwec.o +l1oip-objs := l1oip_core.o l1oip_codec.o diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c new file mode 100644 index 000000000000..33068177b7c9 --- /dev/null +++ b/drivers/isdn/mISDN/core.c @@ -0,0 +1,244 @@ +/* + * Copyright 2008 by Karsten Keil <kkeil@novell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + */ + +#include <linux/types.h> +#include <linux/stddef.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/mISDNif.h> +#include "core.h" + +static u_int debug; + +MODULE_AUTHOR("Karsten Keil"); +MODULE_LICENSE("GPL"); +module_param(debug, uint, S_IRUGO | S_IWUSR); + +static LIST_HEAD(devices); +DEFINE_RWLOCK(device_lock); +static u64 device_ids; +#define MAX_DEVICE_ID 63 + +static LIST_HEAD(Bprotocols); +DEFINE_RWLOCK(bp_lock); + +struct mISDNdevice +*get_mdevice(u_int id) +{ + struct mISDNdevice *dev; + + read_lock(&device_lock); + list_for_each_entry(dev, &devices, D.list) + if (dev->id == id) { + read_unlock(&device_lock); + return dev; + } + read_unlock(&device_lock); + return NULL; +} + +int +get_mdevice_count(void) +{ + struct mISDNdevice *dev; + int cnt = 0; + + read_lock(&device_lock); + list_for_each_entry(dev, &devices, D.list) + cnt++; + read_unlock(&device_lock); + return cnt; +} + +static int +get_free_devid(void) +{ + u_int i; + + for (i = 0; i <= MAX_DEVICE_ID; i++) + if (!test_and_set_bit(i, (u_long *)&device_ids)) + return i; + return -1; +} + +int +mISDN_register_device(struct mISDNdevice *dev, char *name) +{ + u_long flags; + int err; + + dev->id = get_free_devid(); + if (dev->id < 0) + return -EBUSY; + if (name && name[0]) + strcpy(dev->name, name); + else + sprintf(dev->name, "mISDN%d", dev->id); + if (debug & DEBUG_CORE) + printk(KERN_DEBUG "mISDN_register %s %d\n", + dev->name, dev->id); + err = create_stack(dev); + if (err) + return err; + write_lock_irqsave(&device_lock, flags); + list_add_tail(&dev->D.list, &devices); + write_unlock_irqrestore(&device_lock, flags); + return 0; +} +EXPORT_SYMBOL(mISDN_register_device); + +void +mISDN_unregister_device(struct mISDNdevice *dev) { + u_long flags; + + if (debug & DEBUG_CORE) + printk(KERN_DEBUG "mISDN_unregister %s %d\n", + dev->name, dev->id); + write_lock_irqsave(&device_lock, flags); + list_del(&dev->D.list); + write_unlock_irqrestore(&device_lock, flags); + test_and_clear_bit(dev->id, (u_long *)&device_ids); + delete_stack(dev); +} +EXPORT_SYMBOL(mISDN_unregister_device); + +u_int +get_all_Bprotocols(void) +{ + struct Bprotocol *bp; + u_int m = 0; + + read_lock(&bp_lock); + list_for_each_entry(bp, &Bprotocols, list) + m |= bp->Bprotocols; + read_unlock(&bp_lock); + return m; +} + +struct Bprotocol * +get_Bprotocol4mask(u_int m) +{ + struct Bprotocol *bp; + + read_lock(&bp_lock); + list_for_each_entry(bp, &Bprotocols, list) + if (bp->Bprotocols & m) { + read_unlock(&bp_lock); + return bp; + } + read_unlock(&bp_lock); + return NULL; +} + +struct Bprotocol * +get_Bprotocol4id(u_int id) +{ + u_int m; + + if (id < ISDN_P_B_START || id > 63) { + printk(KERN_WARNING "%s id not in range %d\n", + __func__, id); + return NULL; + } + m = 1 << (id & ISDN_P_B_MASK); + return get_Bprotocol4mask(m); +} + +int +mISDN_register_Bprotocol(struct Bprotocol *bp) +{ + u_long flags; + struct Bprotocol *old; + + if (debug & DEBUG_CORE) + printk(KERN_DEBUG "%s: %s/%x\n", __func__, + bp->name, bp->Bprotocols); + old = get_Bprotocol4mask(bp->Bprotocols); + if (old) { + printk(KERN_WARNING + "register duplicate protocol old %s/%x new %s/%x\n", + old->name, old->Bprotocols, bp->name, bp->Bprotocols); + return -EBUSY; + } + write_lock_irqsave(&bp_lock, flags); + list_add_tail(&bp->list, &Bprotocols); + write_unlock_irqrestore(&bp_lock, flags); + return 0; +} +EXPORT_SYMBOL(mISDN_register_Bprotocol); + +void +mISDN_unregister_Bprotocol(struct Bprotocol *bp) +{ + u_long flags; + + if (debug & DEBUG_CORE) + printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name, + bp->Bprotocols); + write_lock_irqsave(&bp_lock, flags); + list_del(&bp->list); + write_unlock_irqrestore(&bp_lock, flags); +} +EXPORT_SYMBOL(mISDN_unregister_Bprotocol); + +int +mISDNInit(void) +{ + int err; + + printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n", + MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE); + mISDN_initstack(&debug); + err = mISDN_inittimer(&debug); + if (err) + goto error; + err = l1_init(&debug); + if (err) { + mISDN_timer_cleanup(); + goto error; + } + err = Isdnl2_Init(&debug); + if (err) { + mISDN_timer_cleanup(); + l1_cleanup(); + goto error; + } + err = misdn_sock_init(&debug); + if (err) { + mISDN_timer_cleanup(); + l1_cleanup(); + Isdnl2_cleanup(); + } +error: + return err; +} + +void mISDN_cleanup(void) +{ + misdn_sock_cleanup(); + mISDN_timer_cleanup(); + l1_cleanup(); + Isdnl2_cleanup(); + + if (!list_empty(&devices)) + printk(KERN_ERR "%s devices still registered\n", __func__); + + if (!list_empty(&Bprotocols)) + printk(KERN_ERR "%s Bprotocols still registered\n", __func__); + printk(KERN_DEBUG "mISDNcore unloaded\n"); +} + +module_init(mISDNInit); +module_exit(mISDN_cleanup); + diff --git a/drivers/isdn/mISDN/core.h b/drivers/isdn/mISDN/core.h new file mode 100644 index 000000000000..7da7233b4c1a --- /dev/null +++ b/drivers/isdn/mISDN/core.h @@ -0,0 +1,77 @@ +/* + * Copyright 2008 by Karsten Keil <kkeil@novell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef mISDN_CORE_H +#define mISDN_CORE_H + +extern struct mISDNdevice *get_mdevice(u_int); +extern int get_mdevice_count(void); + +/* stack status flag */ +#define mISDN_STACK_ACTION_MASK 0x0000ffff +#define mISDN_STACK_COMMAND_MASK 0x000f0000 +#define mISDN_STACK_STATUS_MASK 0xfff00000 +/* action bits 0-15 */ +#define mISDN_STACK_WORK 0 +#define mISDN_STACK_SETUP 1 +#define mISDN_STACK_CLEARING 2 +#define mISDN_STACK_RESTART 3 +#define mISDN_STACK_WAKEUP 4 +#define mISDN_STACK_ABORT 15 +/* command bits 16-19 */ +#define mISDN_STACK_STOPPED 16 +#define mISDN_STACK_INIT 17 +#define mISDN_STACK_THREADSTART 18 +/* status bits 20-31 */ +#define mISDN_STACK_BCHANNEL 20 +#define mISDN_STACK_ACTIVE 29 +#define mISDN_STACK_RUNNING 30 +#define mISDN_STACK_KILLED 31 + + +/* manager options */ +#define MGR_OPT_USER 24 +#define MGR_OPT_NETWORK 25 + +extern int connect_Bstack(struct mISDNdevice *, struct mISDNchannel *, + u_int, struct sockaddr_mISDN *); +extern int connect_layer1(struct mISDNdevice *, struct mISDNchannel *, + u_int, struct sockaddr_mISDN *); +extern int create_l2entity(struct mISDNdevice *, struct mISDNchannel *, + u_int, struct sockaddr_mISDN *); + +extern int create_stack(struct mISDNdevice *); +extern int create_teimanager(struct mISDNdevice *); +extern void delete_teimanager(struct mISDNchannel *); +extern void delete_channel(struct mISDNchannel *); +extern void delete_stack(struct mISDNdevice *); +extern void mISDN_initstack(u_int *); +extern int misdn_sock_init(u_int *); +extern void misdn_sock_cleanup(void); +extern void add_layer2(struct mISDNchannel *, struct mISDNstack *); +extern void __add_layer2(struct mISDNchannel *, struct mISDNstack *); + +extern u_int get_all_Bprotocols(void); +struct Bprotocol *get_Bprotocol4mask(u_int); +struct Bprotocol *get_Bprotocol4id(u_int); + +extern int mISDN_inittimer(u_int *); +extern void mISDN_timer_cleanup(void); + +extern int l1_init(u_int *); +extern void l1_cleanup(void); +extern int Isdnl2_Init(u_int *); +extern void Isdnl2_cleanup(void); + +#endif diff --git a/drivers/isdn/mISDN/dsp.h b/drivers/isdn/mISDN/dsp.h new file mode 100644 index 000000000000..6c3fed6b8d4f --- /dev/null +++ b/drivers/isdn/mISDN/dsp.h @@ -0,0 +1,263 @@ +/* + * Audio support data for ISDN4Linux. + * + * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#define DEBUG_DSP_CTRL 0x0001 +#define DEBUG_DSP_CORE 0x0002 +#define DEBUG_DSP_DTMF 0x0004 +#define DEBUG_DSP_CMX 0x0010 +#define DEBUG_DSP_TONE 0x0020 +#define DEBUG_DSP_BLOWFISH 0x0040 +#define DEBUG_DSP_DELAY 0x0100 +#define DEBUG_DSP_DTMFCOEFF 0x8000 /* heavy output */ + +/* options may be: + * + * bit 0 = use ulaw instead of alaw + * bit 1 = enable hfc hardware accelleration for all channels + * + */ +#define DSP_OPT_ULAW (1<<0) +#define DSP_OPT_NOHARDWARE (1<<1) + +#include <linux/timer.h> +#include <linux/workqueue.h> + +#include "dsp_ecdis.h" + +extern int dsp_options; +extern int dsp_debug; +extern int dsp_poll; +extern int dsp_tics; +extern spinlock_t dsp_lock; +extern struct work_struct dsp_workq; +extern u32 dsp_poll_diff; /* calculated fix-comma corrected poll value */ + +/*************** + * audio stuff * + ***************/ + +extern s32 dsp_audio_alaw_to_s32[256]; +extern s32 dsp_audio_ulaw_to_s32[256]; +extern s32 *dsp_audio_law_to_s32; +extern u8 dsp_audio_s16_to_law[65536]; +extern u8 dsp_audio_alaw_to_ulaw[256]; +extern u8 dsp_audio_mix_law[65536]; +extern u8 dsp_audio_seven2law[128]; +extern u8 dsp_audio_law2seven[256]; +extern void dsp_audio_generate_law_tables(void); +extern void dsp_audio_generate_s2law_table(void); +extern void dsp_audio_generate_seven(void); +extern void dsp_audio_generate_mix_table(void); +extern void dsp_audio_generate_ulaw_samples(void); +extern void dsp_audio_generate_volume_changes(void); +extern u8 dsp_silence; + + +/************* + * cmx stuff * + *************/ + +#define MAX_POLL 256 /* maximum number of send-chunks */ + +#define CMX_BUFF_SIZE 0x8000 /* must be 2**n (0x1000 about 1/2 second) */ +#define CMX_BUFF_HALF 0x4000 /* CMX_BUFF_SIZE / 2 */ +#define CMX_BUFF_MASK 0x7fff /* CMX_BUFF_SIZE - 1 */ + +/* how many seconds will we check the lowest delay until the jitter buffer + is reduced by that delay */ +#define MAX_SECONDS_JITTER_CHECK 5 + +extern struct timer_list dsp_spl_tl; +extern u32 dsp_spl_jiffies; + +/* the structure of conferences: + * + * each conference has a unique number, given by user space. + * the conferences are linked in a chain. + * each conference has members linked in a chain. + * each dsplayer points to a member, each member points to a dsplayer. + */ + +/* all members within a conference (this is linked 1:1 with the dsp) */ +struct dsp; +struct dsp_conf_member { + struct list_head list; + struct dsp *dsp; +}; + +/* the list of all conferences */ +struct dsp_conf { + struct list_head list; + u32 id; + /* all cmx stacks with the same ID are + connected */ + struct list_head mlist; + int software; /* conf is processed by software */ + int hardware; /* conf is processed by hardware */ + /* note: if both unset, has only one member */ +}; + + +/************** + * DTMF stuff * + **************/ + +#define DSP_DTMF_NPOINTS 102 + +#define ECHOCAN_BUFLEN (4*128) + +struct dsp_dtmf { + int treshold; /* above this is dtmf (square of) */ + int software; /* dtmf uses software decoding */ + int hardware; /* dtmf uses hardware decoding */ + int size; /* number of bytes in buffer */ + signed short buffer[DSP_DTMF_NPOINTS]; + /* buffers one full dtmf frame */ + u8 lastwhat, lastdigit; + int count; + u8 digits[16]; /* just the dtmf result */ +}; + + +/****************** + * pipeline stuff * + ******************/ +struct dsp_pipeline { + rwlock_t lock; + struct list_head list; + int inuse; +}; + +/*************** + * tones stuff * + ***************/ + +struct dsp_tone { + int software; /* tones are generated by software */ + int hardware; /* tones are generated by hardware */ + int tone; + void *pattern; + int count; + int index; + struct timer_list tl; +}; + +/***************** + * general stuff * + *****************/ + +struct dsp { + struct list_head list; + struct mISDNchannel ch; + struct mISDNchannel *up; + unsigned char name[64]; + int b_active; + int echo; /* echo is enabled */ + int rx_disabled; /* what the user wants */ + int rx_is_off; /* what the card is */ + int tx_mix; + struct dsp_tone tone; + struct dsp_dtmf dtmf; + int tx_volume, rx_volume; + + /* queue for sending frames */ + struct work_struct workq; + struct sk_buff_head sendq; + int hdlc; /* if mode is hdlc */ + int data_pending; /* currently an unconfirmed frame */ + + /* conference stuff */ + u32 conf_id; + struct dsp_conf *conf; + struct dsp_conf_member + *member; + + /* buffer stuff */ + int rx_W; /* current write pos for data without timestamp */ + int rx_R; /* current read pos for transmit clock */ + int rx_init; /* if set, pointers will be adjusted first */ + int tx_W; /* current write pos for transmit data */ + int tx_R; /* current read pos for transmit clock */ + int rx_delay[MAX_SECONDS_JITTER_CHECK]; + int tx_delay[MAX_SECONDS_JITTER_CHECK]; + u8 tx_buff[CMX_BUFF_SIZE]; + u8 rx_buff[CMX_BUFF_SIZE]; + int last_tx; /* if set, we transmitted last poll interval */ + int cmx_delay; /* initial delay of buffers, + or 0 for dynamic jitter buffer */ + int tx_dejitter; /* if set, dejitter tx buffer */ + int tx_data; /* enables tx-data of CMX to upper layer */ + + /* hardware stuff */ + struct dsp_features features; + int features_rx_off; /* set if rx_off is featured */ + int pcm_slot_rx; /* current PCM slot (or -1) */ + int pcm_bank_rx; + int pcm_slot_tx; + int pcm_bank_tx; + int hfc_conf; /* unique id of current conference (or -1) */ + + /* encryption stuff */ + int bf_enable; + u32 bf_p[18]; + u32 bf_s[1024]; + int bf_crypt_pos; + u8 bf_data_in[9]; + u8 bf_crypt_out[9]; + int bf_decrypt_in_pos; + int bf_decrypt_out_pos; + u8 bf_crypt_inring[16]; + u8 bf_data_out[9]; + int bf_sync; + + struct dsp_pipeline + pipeline; +}; + +/* functions */ + +extern void dsp_change_volume(struct sk_buff *skb, int volume); + +extern struct list_head dsp_ilist; +extern struct list_head conf_ilist; +extern void dsp_cmx_debug(struct dsp *dsp); +extern void dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp); +extern int dsp_cmx_conf(struct dsp *dsp, u32 conf_id); +extern void dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb); +extern void dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb); +extern void dsp_cmx_send(void *arg); +extern void dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb); +extern int dsp_cmx_del_conf_member(struct dsp *dsp); +extern int dsp_cmx_del_conf(struct dsp_conf *conf); + +extern void dsp_dtmf_goertzel_init(struct dsp *dsp); +extern void dsp_dtmf_hardware(struct dsp *dsp); +extern u8 *dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len, + int fmt); + +extern int dsp_tone(struct dsp *dsp, int tone); +extern void dsp_tone_copy(struct dsp *dsp, u8 *data, int len); +extern void dsp_tone_timeout(void *arg); + +extern void dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len); +extern void dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len); +extern int dsp_bf_init(struct dsp *dsp, const u8 *key, unsigned int keylen); +extern void dsp_bf_cleanup(struct dsp *dsp); + +extern int dsp_pipeline_module_init(void); +extern void dsp_pipeline_module_exit(void); +extern int dsp_pipeline_init(struct dsp_pipeline *pipeline); +extern void dsp_pipeline_destroy(struct dsp_pipeline *pipeline); +extern int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg); +extern void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, + int len); +extern void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, + int len); + diff --git a/drivers/isdn/mISDN/dsp_audio.c b/drivers/isdn/mISDN/dsp_audio.c new file mode 100644 index 000000000000..1c2dd5694773 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_audio.c @@ -0,0 +1,434 @@ +/* + * Audio support data for mISDN_dsp. + * + * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu) + * Rewritten by Peter + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/delay.h> +#include <linux/mISDNif.h> +#include <linux/mISDNdsp.h> +#include "core.h" +#include "dsp.h" + +/* ulaw[unsigned char] -> signed 16-bit */ +s32 dsp_audio_ulaw_to_s32[256]; +/* alaw[unsigned char] -> signed 16-bit */ +s32 dsp_audio_alaw_to_s32[256]; + +s32 *dsp_audio_law_to_s32; +EXPORT_SYMBOL(dsp_audio_law_to_s32); + +/* signed 16-bit -> law */ +u8 dsp_audio_s16_to_law[65536]; +EXPORT_SYMBOL(dsp_audio_s16_to_law); + +/* alaw -> ulaw */ +u8 dsp_audio_alaw_to_ulaw[256]; +/* ulaw -> alaw */ +u8 dsp_audio_ulaw_to_alaw[256]; +u8 dsp_silence; + + +/***************************************************** + * generate table for conversion of s16 to alaw/ulaw * + *****************************************************/ + +#define AMI_MASK 0x55 + +static inline unsigned char linear2alaw(short int linear) +{ + int mask; + int seg; + int pcm_val; + static int seg_end[8] = { + 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF + }; + + pcm_val = linear; + if (pcm_val >= 0) { + /* Sign (7th) bit = 1 */ + mask = AMI_MASK | 0x80; + } else { + /* Sign bit = 0 */ + mask = AMI_MASK; + pcm_val = -pcm_val; + } + + /* Convert the scaled magnitude to segment number. */ + for (seg = 0; seg < 8; seg++) { + if (pcm_val <= seg_end[seg]) + break; + } + /* Combine the sign, segment, and quantization bits. */ + return ((seg << 4) | + ((pcm_val >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask; +} + + +static inline short int alaw2linear(unsigned char alaw) +{ + int i; + int seg; + + alaw ^= AMI_MASK; + i = ((alaw & 0x0F) << 4) + 8 /* rounding error */; + seg = (((int) alaw & 0x70) >> 4); + if (seg) + i = (i + 0x100) << (seg - 1); + return (short int) ((alaw & 0x80) ? i : -i); +} + +static inline short int ulaw2linear(unsigned char ulaw) +{ + short mu, e, f, y; + static short etab[] = {0, 132, 396, 924, 1980, 4092, 8316, 16764}; + + mu = 255 - ulaw; + e = (mu & 0x70) / 16; + f = mu & 0x0f; + y = f * (1 << (e + 3)); + y += etab[e]; + if (mu & 0x80) + y = -y; + return y; +} + +#define BIAS 0x84 /*!< define the add-in bias for 16 bit samples */ + +static unsigned char linear2ulaw(short sample) +{ + static int exp_lut[256] = { + 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; + int sign, exponent, mantissa; + unsigned char ulawbyte; + + /* Get the sample into sign-magnitude. */ + sign = (sample >> 8) & 0x80; /* set aside the sign */ + if (sign != 0) + sample = -sample; /* get magnitude */ + + /* Convert from 16 bit linear to ulaw. */ + sample = sample + BIAS; + exponent = exp_lut[(sample >> 7) & 0xFF]; + mantissa = (sample >> (exponent + 3)) & 0x0F; + ulawbyte = ~(sign | (exponent << 4) | mantissa); + + return ulawbyte; +} + +static int reverse_bits(int i) +{ + int z, j; + z = 0; + + for (j = 0; j < 8; j++) { + if ((i & (1 << j)) != 0) + z |= 1 << (7 - j); + } + return z; +} + + +void dsp_audio_generate_law_tables(void) +{ + int i; + for (i = 0; i < 256; i++) + dsp_audio_alaw_to_s32[i] = alaw2linear(reverse_bits(i)); + + for (i = 0; i < 256; i++) + dsp_audio_ulaw_to_s32[i] = ulaw2linear(reverse_bits(i)); + + for (i = 0; i < 256; i++) { + dsp_audio_alaw_to_ulaw[i] = + linear2ulaw(dsp_audio_alaw_to_s32[i]); + dsp_audio_ulaw_to_alaw[i] = + linear2alaw(dsp_audio_ulaw_to_s32[i]); + } +} + +void +dsp_audio_generate_s2law_table(void) +{ + int i; + + if (dsp_options & DSP_OPT_ULAW) { + /* generating ulaw-table */ + for (i = -32768; i < 32768; i++) { + dsp_audio_s16_to_law[i & 0xffff] = + reverse_bits(linear2ulaw(i)); + } + } else { + /* generating alaw-table */ + for (i = -32768; i < 32768; i++) { + dsp_audio_s16_to_law[i & 0xffff] = + reverse_bits(linear2alaw(i)); + } + } +} + + +/* + * the seven bit sample is the number of every second alaw-sample ordered by + * aplitude. 0x00 is negative, 0x7f is positive amplitude. + */ +u8 dsp_audio_seven2law[128]; +u8 dsp_audio_law2seven[256]; + +/******************************************************************** + * generate table for conversion law from/to 7-bit alaw-like sample * + ********************************************************************/ + +void +dsp_audio_generate_seven(void) +{ + int i, j, k; + u8 spl; + u8 sorted_alaw[256]; + + /* generate alaw table, sorted by the linear value */ + for (i = 0; i < 256; i++) { + j = 0; + for (k = 0; k < 256; k++) { + if (dsp_audio_alaw_to_s32[k] + < dsp_audio_alaw_to_s32[i]) { + j++; + } + } + sorted_alaw[j] = i; + } + + /* generate tabels */ + for (i = 0; i < 256; i++) { + /* spl is the source: the law-sample (converted to alaw) */ + spl = i; + if (dsp_options & DSP_OPT_ULAW) + spl = dsp_audio_ulaw_to_alaw[i]; + /* find the 7-bit-sample */ + for (j = 0; j < 256; j++) { + if (sorted_alaw[j] == spl) + break; + } + /* write 7-bit audio value */ + dsp_audio_law2seven[i] = j >> 1; + } + for (i = 0; i < 128; i++) { + spl = sorted_alaw[i << 1]; + if (dsp_options & DSP_OPT_ULAW) + spl = dsp_audio_alaw_to_ulaw[spl]; + dsp_audio_seven2law[i] = spl; + } +} + + +/* mix 2*law -> law */ +u8 dsp_audio_mix_law[65536]; + +/****************************************************** + * generate mix table to mix two law samples into one * + ******************************************************/ + +void +dsp_audio_generate_mix_table(void) +{ + int i, j; + s32 sample; + + i = 0; + while (i < 256) { + j = 0; + while (j < 256) { + sample = dsp_audio_law_to_s32[i]; + sample += dsp_audio_law_to_s32[j]; + if (sample > 32767) + sample = 32767; + if (sample < -32768) + sample = -32768; + dsp_audio_mix_law[(i<<8)|j] = + dsp_audio_s16_to_law[sample & 0xffff]; + j++; + } + i++; + } +} + + +/************************************* + * generate different volume changes * + *************************************/ + +static u8 dsp_audio_reduce8[256]; +static u8 dsp_audio_reduce7[256]; +static u8 dsp_audio_reduce6[256]; +static u8 dsp_audio_reduce5[256]; +static u8 dsp_audio_reduce4[256]; +static u8 dsp_audio_reduce3[256]; +static u8 dsp_audio_reduce2[256]; +static u8 dsp_audio_reduce1[256]; +static u8 dsp_audio_increase1[256]; +static u8 dsp_audio_increase2[256]; +static u8 dsp_audio_increase3[256]; +static u8 dsp_audio_increase4[256]; +static u8 dsp_audio_increase5[256]; +static u8 dsp_audio_increase6[256]; +static u8 dsp_audio_increase7[256]; +static u8 dsp_audio_increase8[256]; + +static u8 *dsp_audio_volume_change[16] = { + dsp_audio_reduce8, + dsp_audio_reduce7, + dsp_audio_reduce6, + dsp_audio_reduce5, + dsp_audio_reduce4, + dsp_audio_reduce3, + dsp_audio_reduce2, + dsp_audio_reduce1, + dsp_audio_increase1, + dsp_audio_increase2, + dsp_audio_increase3, + dsp_audio_increase4, + dsp_audio_increase5, + dsp_audio_increase6, + dsp_audio_increase7, + dsp_audio_increase8, +}; + +void +dsp_audio_generate_volume_changes(void) +{ + register s32 sample; + int i; + int num[] = { 110, 125, 150, 175, 200, 300, 400, 500 }; + int denum[] = { 100, 100, 100, 100, 100, 100, 100, 100 }; + + i = 0; + while (i < 256) { + dsp_audio_reduce8[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[7] / num[7]) & 0xffff]; + dsp_audio_reduce7[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[6] / num[6]) & 0xffff]; + dsp_audio_reduce6[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[5] / num[5]) & 0xffff]; + dsp_audio_reduce5[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[4] / num[4]) & 0xffff]; + dsp_audio_reduce4[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[3] / num[3]) & 0xffff]; + dsp_audio_reduce3[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[2] / num[2]) & 0xffff]; + dsp_audio_reduce2[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[1] / num[1]) & 0xffff]; + dsp_audio_reduce1[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[0] / num[0]) & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[0] / denum[0]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase1[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[1] / denum[1]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase2[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[2] / denum[2]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase3[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[3] / denum[3]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase4[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[4] / denum[4]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase5[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[5] / denum[5]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase6[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[6] / denum[6]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase7[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[7] / denum[7]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase8[i] = dsp_audio_s16_to_law[sample & 0xffff]; + + i++; + } +} + + +/************************************** + * change the volume of the given skb * + **************************************/ + +/* this is a helper function for changing volume of skb. the range may be + * -8 to 8, which is a shift to the power of 2. 0 == no volume, 3 == volume*8 + */ +void +dsp_change_volume(struct sk_buff *skb, int volume) +{ + u8 *volume_change; + int i, ii; + u8 *p; + int shift; + + if (volume == 0) + return; + + /* get correct conversion table */ + if (volume < 0) { + shift = volume + 8; + if (shift < 0) + shift = 0; + } else { + shift = volume + 7; + if (shift > 15) + shift = 15; + } + volume_change = dsp_audio_volume_change[shift]; + i = 0; + ii = skb->len; + p = skb->data; + /* change volume */ + while (i < ii) { + *p = volume_change[*p]; + p++; + i++; + } +} + diff --git a/drivers/isdn/mISDN/dsp_biquad.h b/drivers/isdn/mISDN/dsp_biquad.h new file mode 100644 index 000000000000..038191bc45f5 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_biquad.h @@ -0,0 +1,65 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * biquad.h - General telephony bi-quad section routines (currently this just + * handles canonic/type 2 form) + * + * Written by Steve Underwood <steveu@coppice.org> + * + * Copyright (C) 2001 Steve Underwood + * + * All rights reserved. + * + * 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. + * + */ + +struct biquad2_state { + int32_t gain; + int32_t a1; + int32_t a2; + int32_t b1; + int32_t b2; + + int32_t z1; + int32_t z2; +}; + +static inline void biquad2_init(struct biquad2_state *bq, + int32_t gain, int32_t a1, int32_t a2, int32_t b1, int32_t b2) +{ + bq->gain = gain; + bq->a1 = a1; + bq->a2 = a2; + bq->b1 = b1; + bq->b2 = b2; + + bq->z1 = 0; + bq->z2 = 0; +} + +static inline int16_t biquad2(struct biquad2_state *bq, int16_t sample) +{ + int32_t y; + int32_t z0; + + z0 = sample*bq->gain + bq->z1*bq->a1 + bq->z2*bq->a2; + y = z0 + bq->z1*bq->b1 + bq->z2*bq->b2; + + bq->z2 = bq->z1; + bq->z1 = z0 >> 15; + y >>= 15; + return y; +} diff --git a/drivers/isdn/mISDN/dsp_blowfish.c b/drivers/isdn/mISDN/dsp_blowfish.c new file mode 100644 index 000000000000..18e411e95bba --- /dev/null +++ b/drivers/isdn/mISDN/dsp_blowfish.c @@ -0,0 +1,672 @@ +/* + * Blowfish encryption/decryption for mISDN_dsp. + * + * Copyright Andreas Eversberg (jolly@eversberg.eu) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/mISDNif.h> +#include <linux/mISDNdsp.h> +#include "core.h" +#include "dsp.h" + +/* + * how to encode a sample stream to 64-bit blocks that will be encryped + * + * first of all, data is collected until a block of 9 samples are received. + * of course, a packet may have much more than 9 sample, but is may have + * not excacly the multiple of 9 samples. if there is a rest, the next + * received data will complete the block. + * + * the block is then converted to 9 uLAW samples without the least sigificant + * bit. the result is a 7-bit encoded sample. + * + * the samples will be reoganised to form 8 bytes of data: + * (5(6) means: encoded sample no. 5, bit 6) + * + * 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) 0(0) 1(6) + * 1(5) 1(4) 1(3) 1(2) 1(1) 1(0) 2(6) 2(5) + * 2(4) 2(3) 2(2) 2(1) 2(0) 3(6) 3(5) 3(4) + * 3(3) 3(2) 3(1) 3(0) 4(6) 4(5) 4(4) 4(3) + * 4(2) 4(1) 4(0) 5(6) 5(5) 5(4) 5(3) 5(2) + * 5(1) 5(0) 6(6) 6(5) 6(4) 6(3) 6(2) 6(1) + * 6(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0) + * 8(6) 8(5) 8(4) 8(3) 8(2) 8(1) 8(0) + * + * the missing bit 0 of the last byte is filled with some + * random noise, to fill all 8 bytes. + * + * the 8 bytes will be encrypted using blowfish. + * + * the result will be converted into 9 bytes. the bit 7 is used for + * checksumme (CS) for sync (0, 1) and for the last bit: + * (5(6) means: crypted byte 5, bit 6) + * + * 1 0(7) 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) + * 0 0(0) 1(7) 1(6) 1(5) 1(4) 1(3) 1(2) + * 0 1(1) 1(0) 2(7) 2(6) 2(5) 2(4) 2(3) + * 0 2(2) 2(1) 2(0) 3(7) 3(6) 3(5) 3(4) + * 0 3(3) 3(2) 3(1) 3(0) 4(7) 4(6) 4(5) + * CS 4(4) 4(3) 4(2) 4(1) 4(0) 5(7) 5(6) + * CS 5(5) 5(4) 5(3) 5(2) 5(1) 5(0) 6(7) + * CS 6(6) 6(5) 6(4) 6(3) 6(2) 6(1) 6(0) + * 7(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0) + * + * the checksum is used to detect transmission errors and frame drops. + * + * synchronisation of received block is done by shifting the upper bit of each + * byte (bit 7) to a shift register. if the rigister has the first five bits + * (10000), this is used to find the sync. only if sync has been found, the + * current block of 9 received bytes are decrypted. before that the check + * sum is calculated. if it is incorrect the block is dropped. + * this will avoid loud noise due to corrupt encrypted data. + * + * if the last block is corrupt, the current decoded block is repeated + * until a valid block has been received. + */ + +/* + * some blowfish parts are taken from the + * crypto-api for faster implementation + */ + +struct bf_ctx { + u32 p[18]; + u32 s[1024]; +}; + +static const u32 bf_pbox[16 + 2] = { + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b, +}; + +static const u32 bf_sbox[256 * 4] = { + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, +}; + +/* + * Round loop unrolling macros, S is a pointer to a S-Box array + * organized in 4 unsigned longs at a row. + */ +#define GET32_3(x) (((x) & 0xff)) +#define GET32_2(x) (((x) >> (8)) & (0xff)) +#define GET32_1(x) (((x) >> (16)) & (0xff)) +#define GET32_0(x) (((x) >> (24)) & (0xff)) + +#define bf_F(x) (((S[GET32_0(x)] + S[256 + GET32_1(x)]) ^ \ + S[512 + GET32_2(x)]) + S[768 + GET32_3(x)]) + +#define EROUND(a, b, n) do { b ^= P[n]; a ^= bf_F(b); } while (0) +#define DROUND(a, b, n) do { a ^= bf_F(b); b ^= P[n]; } while (0) + + +/* + * encrypt isdn data frame + * every block with 9 samples is encrypted + */ +void +dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len) +{ + int i = 0, j = dsp->bf_crypt_pos; + u8 *bf_data_in = dsp->bf_data_in; + u8 *bf_crypt_out = dsp->bf_crypt_out; + u32 *P = dsp->bf_p; + u32 *S = dsp->bf_s; + u32 yl, yr; + u32 cs; + u8 nibble; + + while (i < len) { + /* collect a block of 9 samples */ + if (j < 9) { + bf_data_in[j] = *data; + *data++ = bf_crypt_out[j++]; + i++; + continue; + } + j = 0; + /* transcode 9 samples xlaw to 8 bytes */ + yl = dsp_audio_law2seven[bf_data_in[0]]; + yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[1]]; + yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[2]]; + yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[3]]; + nibble = dsp_audio_law2seven[bf_data_in[4]]; + yr = nibble; + yl = (yl<<4) | (nibble>>3); + yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[5]]; + yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[6]]; + yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[7]]; + yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[8]]; + yr = (yr<<1) | (bf_data_in[0] & 1); + + /* fill unused bit with random noise of audio input */ + /* encrypt */ + + EROUND(yr, yl, 0); + EROUND(yl, yr, 1); + EROUND(yr, yl, 2); + EROUND(yl, yr, 3); + EROUND(yr, yl, 4); + EROUND(yl, yr, 5); + EROUND(yr, yl, 6); + EROUND(yl, yr, 7); + EROUND(yr, yl, 8); + EROUND(yl, yr, 9); + EROUND(yr, yl, 10); + EROUND(yl, yr, 11); + EROUND(yr, yl, 12); + EROUND(yl, yr, 13); + EROUND(yr, yl, 14); + EROUND(yl, yr, 15); + yl ^= P[16]; + yr ^= P[17]; + + /* calculate 3-bit checksumme */ + cs = yl ^ (yl>>3) ^ (yl>>6) ^ (yl>>9) ^ (yl>>12) ^ (yl>>15) + ^ (yl>>18) ^ (yl>>21) ^ (yl>>24) ^ (yl>>27) ^ (yl>>30) + ^ (yr<<2) ^ (yr>>1) ^ (yr>>4) ^ (yr>>7) ^ (yr>>10) + ^ (yr>>13) ^ (yr>>16) ^ (yr>>19) ^ (yr>>22) ^ (yr>>25) + ^ (yr>>28) ^ (yr>>31); + + /* + * transcode 8 crypted bytes to 9 data bytes with sync + * and checksum information + */ + bf_crypt_out[0] = (yl>>25) | 0x80; + bf_crypt_out[1] = (yl>>18) & 0x7f; + bf_crypt_out[2] = (yl>>11) & 0x7f; + bf_crypt_out[3] = (yl>>4) & 0x7f; + bf_crypt_out[4] = ((yl<<3) & 0x78) | ((yr>>29) & 0x07); + bf_crypt_out[5] = ((yr>>22) & 0x7f) | ((cs<<5) & 0x80); + bf_crypt_out[6] = ((yr>>15) & 0x7f) | ((cs<<6) & 0x80); + bf_crypt_out[7] = ((yr>>8) & 0x7f) | (cs<<7); + bf_crypt_out[8] = yr; + } + + /* write current count */ + dsp->bf_crypt_pos = j; + +} + + +/* + * decrypt isdn data frame + * every block with 9 bytes is decrypted + */ +void +dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len) +{ + int i = 0; + u8 j = dsp->bf_decrypt_in_pos; + u8 k = dsp->bf_decrypt_out_pos; + u8 *bf_crypt_inring = dsp->bf_crypt_inring; + u8 *bf_data_out = dsp->bf_data_out; + u16 sync = dsp->bf_sync; + u32 *P = dsp->bf_p; + u32 *S = dsp->bf_s; + u32 yl, yr; + u8 nibble; + u8 cs, cs0, cs1, cs2; + + while (i < len) { + /* + * shift upper bit and rotate data to buffer ring + * send current decrypted data + */ + sync = (sync<<1) | ((*data)>>7); + bf_crypt_inring[j++ & 15] = *data; + *data++ = bf_data_out[k++]; + i++; + if (k == 9) + k = 0; /* repeat if no sync has been found */ + /* check if not in sync */ + if ((sync&0x1f0) != 0x100) + continue; + j -= 9; + /* transcode receive data to 64 bit block of encrypted data */ + yl = bf_crypt_inring[j++ & 15]; + yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ + yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ + yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ + nibble = bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ + yr = nibble; + yl = (yl<<4) | (nibble>>3); + cs2 = bf_crypt_inring[j++ & 15]; + yr = (yr<<7) | (cs2 & 0x7f); + cs1 = bf_crypt_inring[j++ & 15]; + yr = (yr<<7) | (cs1 & 0x7f); + cs0 = bf_crypt_inring[j++ & 15]; + yr = (yr<<7) | (cs0 & 0x7f); + yr = (yr<<8) | bf_crypt_inring[j++ & 15]; + + /* calculate 3-bit checksumme */ + cs = yl ^ (yl>>3) ^ (yl>>6) ^ (yl>>9) ^ (yl>>12) ^ (yl>>15) + ^ (yl>>18) ^ (yl>>21) ^ (yl>>24) ^ (yl>>27) ^ (yl>>30) + ^ (yr<<2) ^ (yr>>1) ^ (yr>>4) ^ (yr>>7) ^ (yr>>10) + ^ (yr>>13) ^ (yr>>16) ^ (yr>>19) ^ (yr>>22) ^ (yr>>25) + ^ (yr>>28) ^ (yr>>31); + + /* check if frame is valid */ + if ((cs&0x7) != (((cs2>>5)&4) | ((cs1>>6)&2) | (cs0 >> 7))) { + if (dsp_debug & DEBUG_DSP_BLOWFISH) + printk(KERN_DEBUG + "DSP BLOWFISH: received corrupt frame, " + "checksumme is not correct\n"); + continue; + } + + /* decrypt */ + yr ^= P[17]; + yl ^= P[16]; + DROUND(yl, yr, 15); + DROUND(yr, yl, 14); + DROUND(yl, yr, 13); + DROUND(yr, yl, 12); + DROUND(yl, yr, 11); + DROUND(yr, yl, 10); + DROUND(yl, yr, 9); + DROUND(yr, yl, 8); + DROUND(yl, yr, 7); + DROUND(yr, yl, 6); + DROUND(yl, yr, 5); + DROUND(yr, yl, 4); + DROUND(yl, yr, 3); + DROUND(yr, yl, 2); + DROUND(yl, yr, 1); + DROUND(yr, yl, 0); + + /* transcode 8 crypted bytes to 9 sample bytes */ + bf_data_out[0] = dsp_audio_seven2law[(yl>>25) & 0x7f]; + bf_data_out[1] = dsp_audio_seven2law[(yl>>18) & 0x7f]; + bf_data_out[2] = dsp_audio_seven2law[(yl>>11) & 0x7f]; + bf_data_out[3] = dsp_audio_seven2law[(yl>>4) & 0x7f]; + bf_data_out[4] = dsp_audio_seven2law[((yl<<3) & 0x78) | + ((yr>>29) & 0x07)]; + + bf_data_out[5] = dsp_audio_seven2law[(yr>>22) & 0x7f]; + bf_data_out[6] = dsp_audio_seven2law[(yr>>15) & 0x7f]; + bf_data_out[7] = dsp_audio_seven2law[(yr>>8) & 0x7f]; + bf_data_out[8] = dsp_audio_seven2law[(yr>>1) & 0x7f]; + k = 0; /* start with new decoded frame */ + } + + /* write current count and sync */ + dsp->bf_decrypt_in_pos = j; + dsp->bf_decrypt_out_pos = k; + dsp->bf_sync = sync; +} + + +/* used to encrypt S and P boxes */ +static inline void +encrypt_block(const u32 *P, const u32 *S, u32 *dst, u32 *src) +{ + u32 yl = src[0]; + u32 yr = src[1]; + + EROUND(yr, yl, 0); + EROUND(yl, yr, 1); + EROUND(yr, yl, 2); + EROUND(yl, yr, 3); + EROUND(yr, yl, 4); + EROUND(yl, yr, 5); + EROUND(yr, yl, 6); + EROUND(yl, yr, 7); + EROUND(yr, yl, 8); + EROUND(yl, yr, 9); + EROUND(yr, yl, 10); + EROUND(yl, yr, 11); + EROUND(yr, yl, 12); + EROUND(yl, yr, 13); + EROUND(yr, yl, 14); + EROUND(yl, yr, 15); + + yl ^= P[16]; + yr ^= P[17]; + + dst[0] = yr; + dst[1] = yl; +} + +/* + * initialize the dsp for encryption and decryption using the same key + * Calculates the blowfish S and P boxes for encryption and decryption. + * The margin of keylen must be 4-56 bytes. + * returns 0 if ok. + */ +int +dsp_bf_init(struct dsp *dsp, const u8 *key, uint keylen) +{ + short i, j, count; + u32 data[2], temp; + u32 *P = (u32 *)dsp->bf_p; + u32 *S = (u32 *)dsp->bf_s; + + if (keylen < 4 || keylen > 56) + return 1; + + /* Set dsp states */ + i = 0; + while (i < 9) { + dsp->bf_crypt_out[i] = 0xff; + dsp->bf_data_out[i] = dsp_silence; + i++; + } + dsp->bf_crypt_pos = 0; + dsp->bf_decrypt_in_pos = 0; + dsp->bf_decrypt_out_pos = 0; + dsp->bf_sync = 0x1ff; + dsp->bf_enable = 1; + + /* Copy the initialization s-boxes */ + for (i = 0, count = 0; i < 256; i++) + for (j = 0; j < 4; j++, count++) + S[count] = bf_sbox[count]; + + /* Set the p-boxes */ + for (i = 0; i < 16 + 2; i++) + P[i] = bf_pbox[i]; + + /* Actual subkey generation */ + for (j = 0, i = 0; i < 16 + 2; i++) { + temp = (((u32)key[j] << 24) | + ((u32)key[(j + 1) % keylen] << 16) | + ((u32)key[(j + 2) % keylen] << 8) | + ((u32)key[(j + 3) % keylen])); + + P[i] = P[i] ^ temp; + j = (j + 4) % keylen; + } + + data[0] = 0x00000000; + data[1] = 0x00000000; + + for (i = 0; i < 16 + 2; i += 2) { + encrypt_block(P, S, data, data); + + P[i] = data[0]; + P[i + 1] = data[1]; + } + + for (i = 0; i < 4; i++) { + for (j = 0, count = i * 256; j < 256; j += 2, count += 2) { + encrypt_block(P, S, data, data); + + S[count] = data[0]; + S[count + 1] = data[1]; + } + } + + return 0; +} + + +/* + * turn encryption off + */ +void +dsp_bf_cleanup(struct dsp *dsp) +{ + dsp->bf_enable = 0; +} diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c new file mode 100644 index 000000000000..e92b1ba4b45e --- /dev/null +++ b/drivers/isdn/mISDN/dsp_cmx.c @@ -0,0 +1,1886 @@ +/* + * Audio crossconnecting/conferrencing (hardware level). + * + * Copyright 2002 by Andreas Eversberg (jolly@eversberg.eu) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +/* + * The process of adding and removing parties to/from a conference: + * + * There is a chain of struct dsp_conf which has one or more members in a chain + * of struct dsp_conf_member. + * + * After a party is added, the conference is checked for hardware capability. + * Also if a party is removed, the conference is checked again. + * + * There are 3 different solutions: -1 = software, 0 = hardware-crossconnect + * 1-n = hardware-conference. The n will give the conference number. + * + * Depending on the change after removal or insertion of a party, hardware + * commands are given. + * + * The current solution is stored within the struct dsp_conf entry. + */ + +/* + * HOW THE CMX WORKS: + * + * There are 3 types of interaction: One member is alone, in this case only + * data flow from upper to lower layer is done. + * Two members will also exchange their data so they are crossconnected. + * Three or more members will be added in a conference and will hear each + * other but will not receive their own speech (echo) if not enabled. + * + * Features of CMX are: + * - Crossconnecting or even conference, if more than two members are together. + * - Force mixing of transmit data with other crossconnect/conference members. + * - Echo generation to benchmark the delay of audio processing. + * - Use hardware to minimize cpu load, disable FIFO load and minimize delay. + * - Dejittering and clock generation. + * + * There are 2 buffers: + * + * + * RX-Buffer + * R W + * | | + * ----------------+-------------+------------------- + * + * The rx-buffer is a ring buffer used to store the received data for each + * individual member. This is only the case if data needs to be dejittered + * or in case of a conference where different clocks require reclocking. + * The transmit-clock (R) will read the buffer. + * If the clock overruns the write-pointer, we will have a buffer underrun. + * If the write pointer always has a certain distance from the transmit- + * clock, we will have a delay. The delay will dynamically be increased and + * reduced. + * + * + * TX-Buffer + * R W + * | | + * -----------------+--------+----------------------- + * + * The tx-buffer is a ring buffer to queue the transmit data from user space + * until it will be mixed or sent. There are two pointers, R and W. If the write + * pointer W would reach or overrun R, the buffer would overrun. In this case + * (some) data is dropped so that it will not overrun. + * Additionally a dynamic dejittering can be enabled. this allows data from + * user space that have jitter and different clock source. + * + * + * Clock: + * + * A Clock is not required, if the data source has exactly one clock. In this + * case the data source is forwarded to the destination. + * + * A Clock is required, because the data source + * - has multiple clocks. + * - has no usable clock due to jitter or packet loss (VoIP). + * In this case the system's clock is used. The clock resolution depends on + * the jiffie resolution. + * + * If a member joins a conference: + * + * - If a member joins, its rx_buff is set to silence and change read pointer + * to transmit clock. + * + * The procedure of received data from card is explained in cmx_receive. + * The procedure of received data from user space is explained in cmx_transmit. + * The procedure of transmit data to card is cmx_send. + * + * + * Interaction with other features: + * + * DTMF: + * DTMF decoding is done before the data is crossconnected. + * + * Volume change: + * Changing rx-volume is done before the data is crossconnected. The tx-volume + * must be changed whenever data is transmitted to the card by the cmx. + * + * Tones: + * If a tone is enabled, it will be processed whenever data is transmitted to + * the card. It will replace the tx-data from the user space. + * If tones are generated by hardware, this conference member is removed for + * this time. + * + * Disable rx-data: + * If cmx is realized in hardware, rx data will be disabled if requested by + * the upper layer. If dtmf decoding is done by software and enabled, rx data + * will not be diabled but blocked to the upper layer. + * + * HFC conference engine: + * If it is possible to realize all features using hardware, hardware will be + * used if not forbidden by control command. Disabling rx-data provides + * absolutely traffic free audio processing. (except for the quick 1-frame + * upload of a tone loop, only once for a new tone) + * + */ + +/* delay.h is required for hw_lock.h */ + +#include <linux/delay.h> +#include <linux/mISDNif.h> +#include <linux/mISDNdsp.h> +#include "core.h" +#include "dsp.h" +/* + * debugging of multi party conference, + * by using conference even with two members + */ + +/* #define CMX_CONF_DEBUG */ + +/*#define CMX_DEBUG * massive read/write pointer output */ +/*#define CMX_TX_DEBUG * massive read/write on tx-buffer with content */ + +static inline int +count_list_member(struct list_head *head) +{ + int cnt = 0; + struct list_head *m; + + list_for_each(m, head) + cnt++; + return cnt; +} + +/* + * debug cmx memory structure + */ +void +dsp_cmx_debug(struct dsp *dsp) +{ + struct dsp_conf *conf; + struct dsp_conf_member *member; + struct dsp *odsp; + + printk(KERN_DEBUG "-----Current DSP\n"); + list_for_each_entry(odsp, &dsp_ilist, list) { + printk(KERN_DEBUG "* %s echo=%d txmix=%d", + odsp->name, odsp->echo, odsp->tx_mix); + if (odsp->conf) + printk(" (Conf %d)", odsp->conf->id); + if (dsp == odsp) + printk(" *this*"); + printk("\n"); + } + printk(KERN_DEBUG "-----Current Conf:\n"); + list_for_each_entry(conf, &conf_ilist, list) { + printk(KERN_DEBUG "* Conf %d (%p)\n", conf->id, conf); + list_for_each_entry(member, &conf->mlist, list) { + printk(KERN_DEBUG + " - member = %s (slot_tx %d, bank_tx %d, " + "slot_rx %d, bank_rx %d hfc_conf %d)%s\n", + member->dsp->name, member->dsp->pcm_slot_tx, + member->dsp->pcm_bank_tx, member->dsp->pcm_slot_rx, + member->dsp->pcm_bank_rx, member->dsp->hfc_conf, + (member->dsp == dsp) ? " *this*" : ""); + } + } + printk(KERN_DEBUG "-----end\n"); +} + +/* + * search conference + */ +static struct dsp_conf * +dsp_cmx_search_conf(u32 id) +{ + struct dsp_conf *conf; + + if (!id) { + printk(KERN_WARNING "%s: conference ID is 0.\n", __func__); + return NULL; + } + + /* search conference */ + list_for_each_entry(conf, &conf_ilist, list) + if (conf->id == id) + return conf; + + return NULL; +} + + +/* + * add member to conference + */ +static int +dsp_cmx_add_conf_member(struct dsp *dsp, struct dsp_conf *conf) +{ + struct dsp_conf_member *member; + + if (!conf || !dsp) { + printk(KERN_WARNING "%s: conf or dsp is 0.\n", __func__); + return -EINVAL; + } + if (dsp->member) { + printk(KERN_WARNING "%s: dsp is already member in a conf.\n", + __func__); + return -EINVAL; + } + + if (dsp->conf) { + printk(KERN_WARNING "%s: dsp is already in a conf.\n", + __func__); + return -EINVAL; + } + + member = kzalloc(sizeof(struct dsp_conf_member), GFP_ATOMIC); + if (!member) { + printk(KERN_ERR "kmalloc struct dsp_conf_member failed\n"); + return -ENOMEM; + } + member->dsp = dsp; + /* clear rx buffer */ + memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); + dsp->rx_init = 1; /* rx_W and rx_R will be adjusted on first frame */ + dsp->rx_W = 0; + dsp->rx_R = 0; + + list_add_tail(&member->list, &conf->mlist); + + dsp->conf = conf; + dsp->member = member; + + return 0; +} + + +/* + * del member from conference + */ +int +dsp_cmx_del_conf_member(struct dsp *dsp) +{ + struct dsp_conf_member *member; + + if (!dsp) { + printk(KERN_WARNING "%s: dsp is 0.\n", + __func__); + return -EINVAL; + } + + if (!dsp->conf) { + printk(KERN_WARNING "%s: dsp is not in a conf.\n", + __func__); + return -EINVAL; + } + + if (list_empty(&dsp->conf->mlist)) { + printk(KERN_WARNING "%s: dsp has linked an empty conf.\n", + __func__); + return -EINVAL; + } + + /* find us in conf */ + list_for_each_entry(member, &dsp->conf->mlist, list) { + if (member->dsp == dsp) { + list_del(&member->list); + dsp->conf = NULL; + dsp->member = NULL; + kfree(member); + return 0; + } + } + printk(KERN_WARNING + "%s: dsp is not present in its own conf_meber list.\n", + __func__); + + return -EINVAL; +} + + +/* + * new conference + */ +static struct dsp_conf +*dsp_cmx_new_conf(u32 id) +{ + struct dsp_conf *conf; + + if (!id) { + printk(KERN_WARNING "%s: id is 0.\n", + __func__); + return NULL; + } + + conf = kzalloc(sizeof(struct dsp_conf), GFP_ATOMIC); + if (!conf) { + printk(KERN_ERR "kmalloc struct dsp_conf failed\n"); + return NULL; + } + INIT_LIST_HEAD(&conf->mlist); + conf->id = id; + + list_add_tail(&conf->list, &conf_ilist); + + return conf; +} + + +/* + * del conference + */ +int +dsp_cmx_del_conf(struct dsp_conf *conf) +{ + if (!conf) { + printk(KERN_WARNING "%s: conf is null.\n", + __func__); + return -EINVAL; + } + + if (!list_empty(&conf->mlist)) { + printk(KERN_WARNING "%s: conf not empty.\n", + __func__); + return -EINVAL; + } + list_del(&conf->list); + kfree(conf); + + return 0; +} + + +/* + * send HW message to hfc card + */ +static void +dsp_cmx_hw_message(struct dsp *dsp, u32 message, u32 param1, u32 param2, + u32 param3, u32 param4) +{ + struct mISDN_ctrl_req cq; + + memset(&cq, 0, sizeof(cq)); + cq.op = message; + cq.p1 = param1 | (param2 << 8); + cq.p2 = param3 | (param4 << 8); + if (dsp->ch.peer) + dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq); +} + + +/* + * do hardware update and set the software/hardware flag + * + * either a conference or a dsp instance can be given + * if only dsp instance is given, the instance is not associated with a conf + * and therefore removed. if a conference is given, the dsp is expected to + * be member of that conference. + */ +void +dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp) +{ + struct dsp_conf_member *member, *nextm; + struct dsp *finddsp; + int memb = 0, i, ii, i1, i2; + int freeunits[8]; + u_char freeslots[256]; + int same_hfc = -1, same_pcm = -1, current_conf = -1, + all_conf = 1; + + /* dsp gets updated (no conf) */ + if (!conf) { + if (!dsp) + return; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s checking dsp %s\n", + __func__, dsp->name); +one_member: + /* remove HFC conference if enabled */ + if (dsp->hfc_conf >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s removing %s from HFC conf %d " + "because dsp is split\n", __func__, + dsp->name, dsp->hfc_conf); + dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_CONF_SPLIT, + 0, 0, 0, 0); + dsp->hfc_conf = -1; + } + /* process hw echo */ + if (dsp->features.pcm_banks < 1) + return; + if (!dsp->echo) { + /* NO ECHO: remove PCM slot if assigned */ + if (dsp->pcm_slot_tx >= 0 || dsp->pcm_slot_rx >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s removing %s from" + " PCM slot %d (TX) %d (RX) because" + " dsp is split (no echo)\n", + __func__, dsp->name, + dsp->pcm_slot_tx, dsp->pcm_slot_rx); + dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_DISC, + 0, 0, 0, 0); + dsp->pcm_slot_tx = -1; + dsp->pcm_bank_tx = -1; + dsp->pcm_slot_rx = -1; + dsp->pcm_bank_rx = -1; + } + return; + } + /* ECHO: already echo */ + if (dsp->pcm_slot_tx >= 0 && dsp->pcm_slot_rx < 0 && + dsp->pcm_bank_tx == 2 && dsp->pcm_bank_rx == 2) + return; + /* ECHO: if slot already assigned */ + if (dsp->pcm_slot_tx >= 0) { + dsp->pcm_slot_rx = dsp->pcm_slot_tx; + dsp->pcm_bank_tx = 2; /* 2 means loop */ + dsp->pcm_bank_rx = 2; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s refresh %s for echo using slot %d\n", + __func__, dsp->name, + dsp->pcm_slot_tx); + dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN, + dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2); + return; + } + /* ECHO: find slot */ + dsp->pcm_slot_tx = -1; + dsp->pcm_slot_rx = -1; + memset(freeslots, 1, sizeof(freeslots)); + list_for_each_entry(finddsp, &dsp_ilist, list) { + if (finddsp->features.pcm_id == dsp->features.pcm_id) { + if (finddsp->pcm_slot_rx >= 0 && + finddsp->pcm_slot_rx < sizeof(freeslots)) + freeslots[finddsp->pcm_slot_tx] = 0; + if (finddsp->pcm_slot_tx >= 0 && + finddsp->pcm_slot_tx < sizeof(freeslots)) + freeslots[finddsp->pcm_slot_rx] = 0; + } + } + i = 0; + ii = dsp->features.pcm_slots; + while (i < ii) { + if (freeslots[i]) + break; + i++; + } + if (i == ii) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s no slot available for echo\n", + __func__); + /* no more slots available */ + return; + } + /* assign free slot */ + dsp->pcm_slot_tx = i; + dsp->pcm_slot_rx = i; + dsp->pcm_bank_tx = 2; /* loop */ + dsp->pcm_bank_rx = 2; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s assign echo for %s using slot %d\n", + __func__, dsp->name, dsp->pcm_slot_tx); + dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN, + dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2); + return; + } + + /* conf gets updated (all members) */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s checking conference %d\n", + __func__, conf->id); + + if (list_empty(&conf->mlist)) { + printk(KERN_ERR "%s: conference whithout members\n", + __func__); + return; + } + member = list_entry(conf->mlist.next, struct dsp_conf_member, list); + same_hfc = member->dsp->features.hfc_id; + same_pcm = member->dsp->features.pcm_id; + /* check all members in our conference */ + list_for_each_entry(member, &conf->mlist, list) { + /* check if member uses mixing */ + if (member->dsp->tx_mix) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "tx_mix is turned on\n", __func__, + member->dsp->name); +conf_software: + list_for_each_entry(member, &conf->mlist, list) { + dsp = member->dsp; + /* remove HFC conference if enabled */ + if (dsp->hfc_conf >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s removing %s from HFC " + "conf %d because not " + "possible with hardware\n", + __func__, + dsp->name, + dsp->hfc_conf); + dsp_cmx_hw_message(dsp, + MISDN_CTRL_HFC_CONF_SPLIT, + 0, 0, 0, 0); + dsp->hfc_conf = -1; + } + /* remove PCM slot if assigned */ + if (dsp->pcm_slot_tx >= 0 || + dsp->pcm_slot_rx >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s removing " + "%s from PCM slot %d (TX)" + " slot %d (RX) because not" + " possible with hardware\n", + __func__, + dsp->name, + dsp->pcm_slot_tx, + dsp->pcm_slot_rx); + dsp_cmx_hw_message(dsp, + MISDN_CTRL_HFC_PCM_DISC, + 0, 0, 0, 0); + dsp->pcm_slot_tx = -1; + dsp->pcm_bank_tx = -1; + dsp->pcm_slot_rx = -1; + dsp->pcm_bank_rx = -1; + } + } + conf->hardware = 0; + conf->software = 1; + return; + } + /* check if member has echo turned on */ + if (member->dsp->echo) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "echo is turned on\n", __func__, + member->dsp->name); + goto conf_software; + } + /* check if member has tx_mix turned on */ + if (member->dsp->tx_mix) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "tx_mix is turned on\n", + __func__, member->dsp->name); + goto conf_software; + } + /* check if member changes volume at an not suppoted level */ + if (member->dsp->tx_volume) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "tx_volume is changed\n", + __func__, member->dsp->name); + goto conf_software; + } + if (member->dsp->rx_volume) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "rx_volume is changed\n", + __func__, member->dsp->name); + goto conf_software; + } + /* check if tx-data turned on */ + if (member->dsp->tx_data) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "tx_data is turned on\n", + __func__, member->dsp->name); + goto conf_software; + } + /* check if pipeline exists */ + if (member->dsp->pipeline.inuse) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "pipeline exists\n", __func__, + member->dsp->name); + goto conf_software; + } + /* check if encryption is enabled */ + if (member->dsp->bf_enable) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s dsp %s cannot form a " + "conf, because encryption is enabled\n", + __func__, member->dsp->name); + goto conf_software; + } + /* check if member is on a card with PCM support */ + if (member->dsp->features.pcm_id < 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "dsp has no PCM bus\n", + __func__, member->dsp->name); + goto conf_software; + } + /* check if relations are on the same PCM bus */ + if (member->dsp->features.pcm_id != same_pcm) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "dsp is on a different PCM bus than the " + "first dsp\n", + __func__, member->dsp->name); + goto conf_software; + } + /* determine if members are on the same hfc chip */ + if (same_hfc != member->dsp->features.hfc_id) + same_hfc = -1; + /* if there are members already in a conference */ + if (current_conf < 0 && member->dsp->hfc_conf >= 0) + current_conf = member->dsp->hfc_conf; + /* if any member is not in a conference */ + if (member->dsp->hfc_conf < 0) + all_conf = 0; + + memb++; + } + + /* if no member, this is an error */ + if (memb < 1) + return; + + /* one member */ + if (memb == 1) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s conf %d cannot form a HW conference, " + "because dsp is alone\n", __func__, conf->id); + conf->hardware = 0; + conf->software = 0; + member = list_entry(conf->mlist.next, struct dsp_conf_member, + list); + dsp = member->dsp; + goto one_member; + } + + /* + * ok, now we are sure that all members are on the same pcm. + * now we will see if we have only two members, so we can do + * crossconnections, which don't have any limitations. + */ + + /* if we have only two members */ + if (memb == 2) { + member = list_entry(conf->mlist.next, struct dsp_conf_member, + list); + nextm = list_entry(member->list.next, struct dsp_conf_member, + list); + /* remove HFC conference if enabled */ + if (member->dsp->hfc_conf >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s removing %s from HFC conf %d because " + "two parties require only a PCM slot\n", + __func__, member->dsp->name, + member->dsp->hfc_conf); + dsp_cmx_hw_message(member->dsp, + MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0); + member->dsp->hfc_conf = -1; + } + if (nextm->dsp->hfc_conf >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s removing %s from HFC conf %d because " + "two parties require only a PCM slot\n", + __func__, nextm->dsp->name, + nextm->dsp->hfc_conf); + dsp_cmx_hw_message(nextm->dsp, + MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0); + nextm->dsp->hfc_conf = -1; + } + /* if members have two banks (and not on the same chip) */ + if (member->dsp->features.pcm_banks > 1 && + nextm->dsp->features.pcm_banks > 1 && + member->dsp->features.hfc_id != + nextm->dsp->features.hfc_id) { + /* if both members have same slots with crossed banks */ + if (member->dsp->pcm_slot_tx >= 0 && + member->dsp->pcm_slot_rx >= 0 && + nextm->dsp->pcm_slot_tx >= 0 && + nextm->dsp->pcm_slot_rx >= 0 && + nextm->dsp->pcm_slot_tx == + member->dsp->pcm_slot_rx && + nextm->dsp->pcm_slot_rx == + member->dsp->pcm_slot_tx && + nextm->dsp->pcm_slot_tx == + member->dsp->pcm_slot_tx && + member->dsp->pcm_bank_tx != + member->dsp->pcm_bank_rx && + nextm->dsp->pcm_bank_tx != + nextm->dsp->pcm_bank_rx) { + /* all members have same slot */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s & %s stay joined on " + "PCM slot %d bank %d (TX) bank %d " + "(RX) (on different chips)\n", + __func__, + member->dsp->name, + nextm->dsp->name, + member->dsp->pcm_slot_tx, + member->dsp->pcm_bank_tx, + member->dsp->pcm_bank_rx); + conf->hardware = 0; + conf->software = 1; + return; + } + /* find a new slot */ + memset(freeslots, 1, sizeof(freeslots)); + list_for_each_entry(dsp, &dsp_ilist, list) { + if (dsp != member->dsp && + dsp != nextm->dsp && + member->dsp->features.pcm_id == + dsp->features.pcm_id) { + if (dsp->pcm_slot_rx >= 0 && + dsp->pcm_slot_rx < + sizeof(freeslots)) + freeslots[dsp->pcm_slot_tx] = 0; + if (dsp->pcm_slot_tx >= 0 && + dsp->pcm_slot_tx < + sizeof(freeslots)) + freeslots[dsp->pcm_slot_rx] = 0; + } + } + i = 0; + ii = member->dsp->features.pcm_slots; + while (i < ii) { + if (freeslots[i]) + break; + i++; + } + if (i == ii) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s no slot available for " + "%s & %s\n", __func__, + member->dsp->name, + nextm->dsp->name); + /* no more slots available */ + goto conf_software; + } + /* assign free slot */ + member->dsp->pcm_slot_tx = i; + member->dsp->pcm_slot_rx = i; + nextm->dsp->pcm_slot_tx = i; + nextm->dsp->pcm_slot_rx = i; + member->dsp->pcm_bank_rx = 0; + member->dsp->pcm_bank_tx = 1; + nextm->dsp->pcm_bank_rx = 1; + nextm->dsp->pcm_bank_tx = 0; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s adding %s & %s to new PCM slot %d " + "(TX and RX on different chips) because " + "both members have not same slots\n", + __func__, + member->dsp->name, + nextm->dsp->name, + member->dsp->pcm_slot_tx); + dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, + member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, + member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx); + dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN, + nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx, + nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx); + conf->hardware = 1; + conf->software = 0; + return; + /* if members have one bank (or on the same chip) */ + } else { + /* if both members have different crossed slots */ + if (member->dsp->pcm_slot_tx >= 0 && + member->dsp->pcm_slot_rx >= 0 && + nextm->dsp->pcm_slot_tx >= 0 && + nextm->dsp->pcm_slot_rx >= 0 && + nextm->dsp->pcm_slot_tx == + member->dsp->pcm_slot_rx && + nextm->dsp->pcm_slot_rx == + member->dsp->pcm_slot_tx && + member->dsp->pcm_slot_tx != + member->dsp->pcm_slot_rx && + member->dsp->pcm_bank_tx == 0 && + member->dsp->pcm_bank_rx == 0 && + nextm->dsp->pcm_bank_tx == 0 && + nextm->dsp->pcm_bank_rx == 0) { + /* all members have same slot */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s & %s stay joined on PCM " + "slot %d (TX) %d (RX) on same chip " + "or one bank PCM)\n", __func__, + member->dsp->name, + nextm->dsp->name, + member->dsp->pcm_slot_tx, + member->dsp->pcm_slot_rx); + conf->hardware = 0; + conf->software = 1; + return; + } + /* find two new slot */ + memset(freeslots, 1, sizeof(freeslots)); + list_for_each_entry(dsp, &dsp_ilist, list) { + if (dsp != member->dsp && + dsp != nextm->dsp && + member->dsp->features.pcm_id == + dsp->features.pcm_id) { + if (dsp->pcm_slot_rx >= 0 && + dsp->pcm_slot_rx < + sizeof(freeslots)) + freeslots[dsp->pcm_slot_tx] = 0; + if (dsp->pcm_slot_tx >= 0 && + dsp->pcm_slot_tx < + sizeof(freeslots)) + freeslots[dsp->pcm_slot_rx] = 0; + } + } + i1 = 0; + ii = member->dsp->features.pcm_slots; + while (i1 < ii) { + if (freeslots[i1]) + break; + i1++; + } + if (i1 == ii) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s no slot available " + "for %s & %s\n", __func__, + member->dsp->name, + nextm->dsp->name); + /* no more slots available */ + goto conf_software; + } + i2 = i1+1; + while (i2 < ii) { + if (freeslots[i2]) + break; + i2++; + } + if (i2 == ii) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s no slot available " + "for %s & %s\n", + __func__, + member->dsp->name, + nextm->dsp->name); + /* no more slots available */ + goto conf_software; + } + /* assign free slots */ + member->dsp->pcm_slot_tx = i1; + member->dsp->pcm_slot_rx = i2; + nextm->dsp->pcm_slot_tx = i2; + nextm->dsp->pcm_slot_rx = i1; + member->dsp->pcm_bank_rx = 0; + member->dsp->pcm_bank_tx = 0; + nextm->dsp->pcm_bank_rx = 0; + nextm->dsp->pcm_bank_tx = 0; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s adding %s & %s to new PCM slot %d " + "(TX) %d (RX) on same chip or one bank " + "PCM, because both members have not " + "crossed slots\n", __func__, + member->dsp->name, + nextm->dsp->name, + member->dsp->pcm_slot_tx, + member->dsp->pcm_slot_rx); + dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, + member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, + member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx); + dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN, + nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx, + nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx); + conf->hardware = 1; + conf->software = 0; + return; + } + } + + /* + * if we have more than two, we may check if we have a conference + * unit available on the chip. also all members must be on the same + */ + + /* if not the same HFC chip */ + if (same_hfc < 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s conference %d cannot be formed, because " + "members are on different chips or not " + "on HFC chip\n", + __func__, conf->id); + goto conf_software; + } + + /* for more than two members.. */ + + /* in case of hdlc, we change to software */ + if (dsp->hdlc) + goto conf_software; + + /* if all members already have the same conference */ + if (all_conf) + return; + + /* + * if there is an existing conference, but not all members have joined + */ + if (current_conf >= 0) { +join_members: + list_for_each_entry(member, &conf->mlist, list) { + /* join to current conference */ + if (member->dsp->hfc_conf == current_conf) + continue; + /* get a free timeslot first */ + memset(freeslots, 1, sizeof(freeslots)); + list_for_each_entry(dsp, &dsp_ilist, list) { + /* + * not checking current member, because + * slot will be overwritten. + */ + if ( + dsp != member->dsp && + /* dsp must be on the same PCM */ + member->dsp->features.pcm_id == + dsp->features.pcm_id) { + /* dsp must be on a slot */ + if (dsp->pcm_slot_tx >= 0 && + dsp->pcm_slot_tx < + sizeof(freeslots)) + freeslots[dsp->pcm_slot_tx] = 0; + if (dsp->pcm_slot_rx >= 0 && + dsp->pcm_slot_rx < + sizeof(freeslots)) + freeslots[dsp->pcm_slot_rx] = 0; + } + } + i = 0; + ii = member->dsp->features.pcm_slots; + while (i < ii) { + if (freeslots[i]) + break; + i++; + } + if (i == ii) { + /* no more slots available */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s conference %d cannot be formed," + " because no slot free\n", + __func__, conf->id); + goto conf_software; + } + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s changing dsp %s to HW conference " + "%d slot %d\n", __func__, + member->dsp->name, current_conf, i); + /* assign free slot & set PCM & join conf */ + member->dsp->pcm_slot_tx = i; + member->dsp->pcm_slot_rx = i; + member->dsp->pcm_bank_tx = 2; /* loop */ + member->dsp->pcm_bank_rx = 2; + member->dsp->hfc_conf = current_conf; + dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, + i, 2, i, 2); + dsp_cmx_hw_message(member->dsp, + MISDN_CTRL_HFC_CONF_JOIN, current_conf, 0, 0, 0); + } + return; + } + + /* + * no member is in a conference yet, so we find a free one + */ + memset(freeunits, 1, sizeof(freeunits)); + list_for_each_entry(dsp, &dsp_ilist, list) { + /* dsp must be on the same chip */ + if (dsp->features.hfc_id == same_hfc && + /* dsp must have joined a HW conference */ + dsp->hfc_conf >= 0 && + /* slot must be within range */ + dsp->hfc_conf < 8) + freeunits[dsp->hfc_conf] = 0; + } + i = 0; + ii = 8; + while (i < ii) { + if (freeunits[i]) + break; + i++; + } + if (i == ii) { + /* no more conferences available */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s conference %d cannot be formed, because " + "no conference number free\n", + __func__, conf->id); + goto conf_software; + } + /* join all members */ + current_conf = i; + goto join_members; +} + + +/* + * conf_id != 0: join or change conference + * conf_id == 0: split from conference if not already + */ +int +dsp_cmx_conf(struct dsp *dsp, u32 conf_id) +{ + int err; + struct dsp_conf *conf; + struct dsp_conf_member *member; + + /* if conference doesn't change */ + if (dsp->conf_id == conf_id) + return 0; + + /* first remove us from current conf */ + if (dsp->conf_id) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "removing us from conference %d\n", + dsp->conf->id); + /* remove us from conf */ + conf = dsp->conf; + err = dsp_cmx_del_conf_member(dsp); + if (err) + return err; + dsp->conf_id = 0; + + /* update hardware */ + dsp_cmx_hardware(NULL, dsp); + + /* conf now empty? */ + if (list_empty(&conf->mlist)) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "conference is empty, so we remove it.\n"); + err = dsp_cmx_del_conf(conf); + if (err) + return err; + } else { + /* update members left on conf */ + dsp_cmx_hardware(conf, NULL); + } + } + + /* if split */ + if (!conf_id) + return 0; + + /* now add us to conf */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "searching conference %d\n", + conf_id); + conf = dsp_cmx_search_conf(conf_id); + if (!conf) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "conference doesn't exist yet, creating.\n"); + /* the conference doesn't exist, so we create */ + conf = dsp_cmx_new_conf(conf_id); + if (!conf) + return -EINVAL; + } else if (!list_empty(&conf->mlist)) { + member = list_entry(conf->mlist.next, struct dsp_conf_member, + list); + if (dsp->hdlc && !member->dsp->hdlc) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "cannot join transparent conference.\n"); + return -EINVAL; + } + if (!dsp->hdlc && member->dsp->hdlc) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "cannot join hdlc conference.\n"); + return -EINVAL; + } + } + /* add conference member */ + err = dsp_cmx_add_conf_member(dsp, conf); + if (err) + return err; + dsp->conf_id = conf_id; + + /* if we are alone, we do nothing! */ + if (list_empty(&conf->mlist)) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "we are alone in this conference, so exit.\n"); + /* update hardware */ + dsp_cmx_hardware(NULL, dsp); + return 0; + } + + /* update members on conf */ + dsp_cmx_hardware(conf, NULL); + + return 0; +} + + +/* + * audio data is received from card + */ +void +dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb) +{ + u8 *d, *p; + int len = skb->len; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + int w, i, ii; + + /* check if we have sompen */ + if (len < 1) + return; + + /* half of the buffer should be larger than maximum packet size */ + if (len >= CMX_BUFF_HALF) { + printk(KERN_ERR + "%s line %d: packet from card is too large (%d bytes). " + "please make card send smaller packets OR increase " + "CMX_BUFF_SIZE\n", __FILE__, __LINE__, len); + return; + } + + /* + * initialize pointers if not already - + * also add delay if requested by PH_SIGNAL + */ + if (dsp->rx_init) { + dsp->rx_init = 0; + if (dsp->features.unordered) { + dsp->rx_R = (hh->id & CMX_BUFF_MASK); + dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) + & CMX_BUFF_MASK; + } else { + dsp->rx_R = 0; + dsp->rx_W = dsp->cmx_delay; + } + } + /* if frame contains time code, write directly */ + if (dsp->features.unordered) { + dsp->rx_W = (hh->id & CMX_BUFF_MASK); + /* printk(KERN_DEBUG "%s %08x\n", dsp->name, hh->id); */ + } + /* + * if we underrun (or maybe overrun), + * we set our new read pointer, and write silence to buffer + */ + if (((dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "cmx_receive(dsp=%lx): UNDERRUN (or overrun the " + "maximum delay), adjusting read pointer! " + "(inst %s)\n", (u_long)dsp, dsp->name); + /* flush buffer */ + if (dsp->features.unordered) { + dsp->rx_R = (hh->id & CMX_BUFF_MASK); + dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) + & CMX_BUFF_MASK; + } else { + dsp->rx_R = 0; + dsp->rx_W = dsp->cmx_delay; + } + memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); + } + /* if we have reached double delay, jump back to middle */ + if (dsp->cmx_delay) + if (((dsp->rx_W - dsp->rx_R) & CMX_BUFF_MASK) >= + (dsp->cmx_delay << 1)) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "cmx_receive(dsp=%lx): OVERRUN (because " + "twice the delay is reached), adjusting " + "read pointer! (inst %s)\n", + (u_long)dsp, dsp->name); + /* flush buffer */ + if (dsp->features.unordered) { + dsp->rx_R = (hh->id & CMX_BUFF_MASK); + dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) + & CMX_BUFF_MASK; + } else { + dsp->rx_R = 0; + dsp->rx_W = dsp->cmx_delay; + } + memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); + } + + /* show where to write */ +#ifdef CMX_DEBUG + printk(KERN_DEBUG + "cmx_receive(dsp=%lx): rx_R(dsp)=%05x rx_W(dsp)=%05x len=%d %s\n", + (u_long)dsp, dsp->rx_R, dsp->rx_W, len, dsp->name); +#endif + + /* write data into rx_buffer */ + p = skb->data; + d = dsp->rx_buff; + w = dsp->rx_W; + i = 0; + ii = len; + while (i < ii) { + d[w++ & CMX_BUFF_MASK] = *p++; + i++; + } + + /* increase write-pointer */ + dsp->rx_W = ((dsp->rx_W+len) & CMX_BUFF_MASK); +} + + +/* + * send (mixed) audio data to card and control jitter + */ +static void +dsp_cmx_send_member(struct dsp *dsp, int len, s32 *c, int members) +{ + struct dsp_conf *conf = dsp->conf; + struct dsp *member, *other; + register s32 sample; + u8 *d, *p, *q, *o_q; + struct sk_buff *nskb, *txskb; + int r, rr, t, tt, o_r, o_rr; + int preload = 0; + struct mISDNhead *hh, *thh; + + /* don't process if: */ + if (!dsp->b_active) { /* if not active */ + dsp->last_tx = 0; + return; + } + if (dsp->pcm_slot_tx >= 0 && /* connected to pcm slot */ + dsp->tx_R == dsp->tx_W && /* AND no tx-data */ + !(dsp->tone.tone && dsp->tone.software)) { /* AND not soft tones */ + dsp->last_tx = 0; + return; + } + +#ifdef CMX_DEBUG + printk(KERN_DEBUG + "SEND members=%d dsp=%s, conf=%p, rx_R=%05x rx_W=%05x\n", + members, dsp->name, conf, dsp->rx_R, dsp->rx_W); +#endif + + /* preload if we have delay set */ + if (dsp->cmx_delay && !dsp->last_tx) { + preload = len; + if (preload < 128) + preload = 128; + } + + /* PREPARE RESULT */ + nskb = mI_alloc_skb(len + preload, GFP_ATOMIC); + if (!nskb) { + printk(KERN_ERR + "FATAL ERROR in mISDN_dsp.o: cannot alloc %d bytes\n", + len + preload); + return; + } + hh = mISDN_HEAD_P(nskb); + hh->prim = PH_DATA_REQ; + hh->id = 0; + dsp->last_tx = 1; + + /* set pointers, indexes and stuff */ + member = dsp; + p = dsp->tx_buff; /* transmit data */ + q = dsp->rx_buff; /* received data */ + d = skb_put(nskb, preload + len); /* result */ + t = dsp->tx_R; /* tx-pointers */ + tt = dsp->tx_W; + r = dsp->rx_R; /* rx-pointers */ + rr = (r + len) & CMX_BUFF_MASK; + + /* preload with silence, if required */ + if (preload) { + memset(d, dsp_silence, preload); + d += preload; + } + + /* PROCESS TONES/TX-DATA ONLY */ + if (dsp->tone.tone && dsp->tone.software) { + /* -> copy tone */ + dsp_tone_copy(dsp, d, len); + dsp->tx_R = 0; /* clear tx buffer */ + dsp->tx_W = 0; + goto send_packet; + } + /* if we have tx-data but do not use mixing */ + if (!dsp->tx_mix && t != tt) { + /* -> send tx-data and continue when not enough */ +#ifdef CMX_TX_DEBUG + sprintf(debugbuf, "TX sending (%04x-%04x)%p: ", t, tt, p); +#endif + while (r != rr && t != tt) { +#ifdef CMX_TX_DEBUG + if (strlen(debugbuf) < 48) + sprintf(debugbuf+strlen(debugbuf), " %02x", p[t]); +#endif + *d++ = p[t]; /* write tx_buff */ + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + } + if (r == rr) { + dsp->tx_R = t; +#ifdef CMX_TX_DEBUG + printk(KERN_DEBUG "%s\n", debugbuf); +#endif + goto send_packet; + } + } +#ifdef CMX_TX_DEBUG + printk(KERN_DEBUG "%s\n", debugbuf); +#endif + + /* PROCESS DATA (one member / no conf) */ + if (!conf || members <= 1) { + /* -> if echo is NOT enabled */ + if (!dsp->echo) { + /* -> send tx-data if available or use 0-volume */ + while (r != rr && t != tt) { + *d++ = p[t]; /* write tx_buff */ + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + } + if (r != rr) + memset(d, dsp_silence, (rr-r)&CMX_BUFF_MASK); + /* -> if echo is enabled */ + } else { + /* + * -> mix tx-data with echo if available, + * or use echo only + */ + while (r != rr && t != tt) { + *d++ = dsp_audio_mix_law[(p[t]<<8)|q[r]]; + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + } + while (r != rr) { + *d++ = q[r]; /* echo */ + r = (r+1) & CMX_BUFF_MASK; + } + } + dsp->tx_R = t; + goto send_packet; + } + /* PROCESS DATA (two members) */ +#ifdef CMX_CONF_DEBUG + if (0) { +#else + if (members == 2) { +#endif + /* "other" becomes other party */ + other = (list_entry(conf->mlist.next, + struct dsp_conf_member, list))->dsp; + if (other == member) + other = (list_entry(conf->mlist.prev, + struct dsp_conf_member, list))->dsp; + o_q = other->rx_buff; /* received data */ + o_rr = (other->rx_R + len) & CMX_BUFF_MASK; + /* end of rx-pointer */ + o_r = (o_rr - rr + r) & CMX_BUFF_MASK; + /* start rx-pointer at current read position*/ + /* -> if echo is NOT enabled */ + if (!dsp->echo) { + /* + * -> copy other member's rx-data, + * if tx-data is available, mix + */ + while (o_r != o_rr && t != tt) { + *d++ = dsp_audio_mix_law[(p[t]<<8)|o_q[o_r]]; + t = (t+1) & CMX_BUFF_MASK; + o_r = (o_r+1) & CMX_BUFF_MASK; + } + while (o_r != o_rr) { + *d++ = o_q[o_r]; + o_r = (o_r+1) & CMX_BUFF_MASK; + } + /* -> if echo is enabled */ + } else { + /* + * -> mix other member's rx-data with echo, + * if tx-data is available, mix + */ + while (r != rr && t != tt) { + sample = dsp_audio_law_to_s32[p[t]] + + dsp_audio_law_to_s32[q[r]] + + dsp_audio_law_to_s32[o_q[o_r]]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; + /* tx-data + rx_data + echo */ + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + o_r = (o_r+1) & CMX_BUFF_MASK; + } + while (r != rr) { + *d++ = dsp_audio_mix_law[(q[r]<<8)|o_q[o_r]]; + r = (r+1) & CMX_BUFF_MASK; + o_r = (o_r+1) & CMX_BUFF_MASK; + } + } + dsp->tx_R = t; + goto send_packet; + } +#ifdef DSP_NEVER_DEFINED + } +#endif + /* PROCESS DATA (three or more members) */ + /* -> if echo is NOT enabled */ + if (!dsp->echo) { + /* + * -> substract rx-data from conf-data, + * if tx-data is available, mix + */ + while (r != rr && t != tt) { + sample = dsp_audio_law_to_s32[p[t]] + *c++ - + dsp_audio_law_to_s32[q[r]]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; + /* conf-rx+tx */ + r = (r+1) & CMX_BUFF_MASK; + t = (t+1) & CMX_BUFF_MASK; + } + while (r != rr) { + sample = *c++ - dsp_audio_law_to_s32[q[r]]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; + /* conf-rx */ + r = (r+1) & CMX_BUFF_MASK; + } + /* -> if echo is enabled */ + } else { + /* + * -> encode conf-data, if tx-data + * is available, mix + */ + while (r != rr && t != tt) { + sample = dsp_audio_law_to_s32[p[t]] + *c++; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; + /* conf(echo)+tx */ + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + } + while (r != rr) { + sample = *c++; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; + /* conf(echo) */ + r = (r+1) & CMX_BUFF_MASK; + } + } + dsp->tx_R = t; + goto send_packet; + +send_packet: + /* + * send tx-data if enabled - don't filter, + * becuase we want what we send, not what we filtered + */ + if (dsp->tx_data) { + /* PREPARE RESULT */ + txskb = mI_alloc_skb(len, GFP_ATOMIC); + if (!txskb) { + printk(KERN_ERR + "FATAL ERROR in mISDN_dsp.o: " + "cannot alloc %d bytes\n", len); + } else { + thh = mISDN_HEAD_P(txskb); + thh->prim = DL_DATA_REQ; + thh->id = 0; + memcpy(skb_put(txskb, len), nskb->data+preload, len); + /* queue (trigger later) */ + skb_queue_tail(&dsp->sendq, txskb); + } + } + /* adjust volume */ + if (dsp->tx_volume) + dsp_change_volume(nskb, dsp->tx_volume); + /* pipeline */ + if (dsp->pipeline.inuse) + dsp_pipeline_process_tx(&dsp->pipeline, nskb->data, nskb->len); + /* crypt */ + if (dsp->bf_enable) + dsp_bf_encrypt(dsp, nskb->data, nskb->len); + /* queue and trigger */ + skb_queue_tail(&dsp->sendq, nskb); + schedule_work(&dsp->workq); +} + +u32 samplecount; +struct timer_list dsp_spl_tl; +u32 dsp_spl_jiffies; /* calculate the next time to fire */ +u32 dsp_start_jiffies; /* jiffies at the time, the calculation begins */ +struct timeval dsp_start_tv; /* time at start of calculation */ + +void +dsp_cmx_send(void *arg) +{ + struct dsp_conf *conf; + struct dsp_conf_member *member; + struct dsp *dsp; + int mustmix, members; + s32 mixbuffer[MAX_POLL+100], *c; + u8 *p, *q; + int r, rr; + int jittercheck = 0, delay, i; + u_long flags; + struct timeval tv; + u32 elapsed; + s16 length; + + /* lock */ + spin_lock_irqsave(&dsp_lock, flags); + + if (!dsp_start_tv.tv_sec) { + do_gettimeofday(&dsp_start_tv); + length = dsp_poll; + } else { + do_gettimeofday(&tv); + elapsed = ((tv.tv_sec - dsp_start_tv.tv_sec) * 8000) + + ((s32)(tv.tv_usec / 125) - (dsp_start_tv.tv_usec / 125)); + dsp_start_tv.tv_sec = tv.tv_sec; + dsp_start_tv.tv_usec = tv.tv_usec; + length = elapsed; + } + if (length > MAX_POLL + 100) + length = MAX_POLL + 100; +/* printk(KERN_DEBUG "len=%d dsp_count=0x%x.%04x dsp_poll_diff=0x%x.%04x\n", + length, dsp_count >> 16, dsp_count & 0xffff, dsp_poll_diff >> 16, + dsp_poll_diff & 0xffff); + */ + + /* + * check if jitter needs to be checked + * (this is about every second = 8192 samples) + */ + samplecount += length; + if ((samplecount & 8191) < length) + jittercheck = 1; + + /* loop all members that do not require conference mixing */ + list_for_each_entry(dsp, &dsp_ilist, list) { + if (dsp->hdlc) + continue; + conf = dsp->conf; + mustmix = 0; + members = 0; + if (conf) { + members = count_list_member(&conf->mlist); +#ifdef CMX_CONF_DEBUG + if (conf->software && members > 1) +#else + if (conf->software && members > 2) +#endif + mustmix = 1; + } + + /* transmission required */ + if (!mustmix) { + dsp_cmx_send_member(dsp, length, mixbuffer, members); + + /* + * unused mixbuffer is given to prevent a + * potential null-pointer-bug + */ + } + } + + /* loop all members that require conference mixing */ + list_for_each_entry(conf, &conf_ilist, list) { + /* count members and check hardware */ + members = count_list_member(&conf->mlist); +#ifdef CMX_CONF_DEBUG + if (conf->software && members > 1) { +#else + if (conf->software && members > 2) { +#endif + /* check for hdlc conf */ + member = list_entry(conf->mlist.next, + struct dsp_conf_member, list); + if (member->dsp->hdlc) + continue; + /* mix all data */ + memset(mixbuffer, 0, length*sizeof(s32)); + list_for_each_entry(member, &conf->mlist, list) { + dsp = member->dsp; + /* get range of data to mix */ + c = mixbuffer; + q = dsp->rx_buff; + r = dsp->rx_R; + rr = (r + length) & CMX_BUFF_MASK; + /* add member's data */ + while (r != rr) { + *c++ += dsp_audio_law_to_s32[q[r]]; + r = (r+1) & CMX_BUFF_MASK; + } + } + + /* process each member */ + list_for_each_entry(member, &conf->mlist, list) { + /* transmission */ + dsp_cmx_send_member(member->dsp, length, + mixbuffer, members); + } + } + } + + /* delete rx-data, increment buffers, change pointers */ + list_for_each_entry(dsp, &dsp_ilist, list) { + if (dsp->hdlc) + continue; + p = dsp->rx_buff; + q = dsp->tx_buff; + r = dsp->rx_R; + /* move receive pointer when receiving */ + if (!dsp->rx_is_off) { + rr = (r + length) & CMX_BUFF_MASK; + /* delete rx-data */ + while (r != rr) { + p[r] = dsp_silence; + r = (r+1) & CMX_BUFF_MASK; + } + /* increment rx-buffer pointer */ + dsp->rx_R = r; /* write incremented read pointer */ + } + + /* check current rx_delay */ + delay = (dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK; + if (delay >= CMX_BUFF_HALF) + delay = 0; /* will be the delay before next write */ + /* check for lower delay */ + if (delay < dsp->rx_delay[0]) + dsp->rx_delay[0] = delay; + /* check current tx_delay */ + delay = (dsp->tx_W-dsp->tx_R) & CMX_BUFF_MASK; + if (delay >= CMX_BUFF_HALF) + delay = 0; /* will be the delay before next write */ + /* check for lower delay */ + if (delay < dsp->tx_delay[0]) + dsp->tx_delay[0] = delay; + if (jittercheck) { + /* find the lowest of all rx_delays */ + delay = dsp->rx_delay[0]; + i = 1; + while (i < MAX_SECONDS_JITTER_CHECK) { + if (delay > dsp->rx_delay[i]) + delay = dsp->rx_delay[i]; + i++; + } + /* + * remove rx_delay only if we have delay AND we + * have not preset cmx_delay + */ + if (delay && !dsp->cmx_delay) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s lowest rx_delay of %d bytes for" + " dsp %s are now removed.\n", + __func__, delay, + dsp->name); + r = dsp->rx_R; + rr = (r + delay) & CMX_BUFF_MASK; + /* delete rx-data */ + while (r != rr) { + p[r] = dsp_silence; + r = (r+1) & CMX_BUFF_MASK; + } + /* increment rx-buffer pointer */ + dsp->rx_R = r; + /* write incremented read pointer */ + } + /* find the lowest of all tx_delays */ + delay = dsp->tx_delay[0]; + i = 1; + while (i < MAX_SECONDS_JITTER_CHECK) { + if (delay > dsp->tx_delay[i]) + delay = dsp->tx_delay[i]; + i++; + } + /* + * remove delay only if we have delay AND we + * have enabled tx_dejitter + */ + if (delay && dsp->tx_dejitter) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s lowest tx_delay of %d bytes for" + " dsp %s are now removed.\n", + __func__, delay, + dsp->name); + r = dsp->tx_R; + rr = (r + delay) & CMX_BUFF_MASK; + /* delete tx-data */ + while (r != rr) { + q[r] = dsp_silence; + r = (r+1) & CMX_BUFF_MASK; + } + /* increment rx-buffer pointer */ + dsp->tx_R = r; + /* write incremented read pointer */ + } + /* scroll up delays */ + i = MAX_SECONDS_JITTER_CHECK - 1; + while (i) { + dsp->rx_delay[i] = dsp->rx_delay[i-1]; + dsp->tx_delay[i] = dsp->tx_delay[i-1]; + i--; + } + dsp->tx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */ + dsp->rx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */ + } + } + + /* if next event would be in the past ... */ + if ((s32)(dsp_spl_jiffies+dsp_tics-jiffies) <= 0) + dsp_spl_jiffies = jiffies + 1; + else + dsp_spl_jiffies += dsp_tics; + + dsp_spl_tl.expires = dsp_spl_jiffies; + add_timer(&dsp_spl_tl); + + /* unlock */ + spin_unlock_irqrestore(&dsp_lock, flags); +} + +/* + * audio data is transmitted from upper layer to the dsp + */ +void +dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb) +{ + u_int w, ww; + u8 *d, *p; + int space; /* todo: , l = skb->len; */ +#ifdef CMX_TX_DEBUG + char debugbuf[256] = ""; +#endif + + /* check if there is enough space, and then copy */ + w = dsp->tx_W; + ww = dsp->tx_R; + p = dsp->tx_buff; + d = skb->data; + space = ww-w; + if (space <= 0) + space += CMX_BUFF_SIZE; + /* write-pointer should not overrun nor reach read pointer */ + if (space-1 < skb->len) + /* write to the space we have left */ + ww = (ww - 1) & CMX_BUFF_MASK; + else + /* write until all byte are copied */ + ww = (w + skb->len) & CMX_BUFF_MASK; + dsp->tx_W = ww; + + /* show current buffer */ +#ifdef CMX_DEBUG + printk(KERN_DEBUG + "cmx_transmit(dsp=%lx) %d bytes to 0x%x-0x%x. %s\n", + (u_long)dsp, (ww-w)&CMX_BUFF_MASK, w, ww, dsp->name); +#endif + + /* copy transmit data to tx-buffer */ +#ifdef CMX_TX_DEBUG + sprintf(debugbuf, "TX getting (%04x-%04x)%p: ", w, ww, p); +#endif + while (w != ww) { +#ifdef CMX_TX_DEBUG + if (strlen(debugbuf) < 48) + sprintf(debugbuf+strlen(debugbuf), " %02x", *d); +#endif + p[w] = *d++; + w = (w+1) & CMX_BUFF_MASK; + } +#ifdef CMX_TX_DEBUG + printk(KERN_DEBUG "%s\n", debugbuf); +#endif + +} + +/* + * hdlc data is received from card and sent to all members. + */ +void +dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb) +{ + struct sk_buff *nskb = NULL; + struct dsp_conf_member *member; + struct mISDNhead *hh; + + /* not if not active */ + if (!dsp->b_active) + return; + + /* check if we have sompen */ + if (skb->len < 1) + return; + + /* no conf */ + if (!dsp->conf) { + /* in case of hardware (echo) */ + if (dsp->pcm_slot_tx >= 0) + return; + if (dsp->echo) + nskb = skb_clone(skb, GFP_ATOMIC); + if (nskb) { + hh = mISDN_HEAD_P(nskb); + hh->prim = PH_DATA_REQ; + hh->id = 0; + skb_queue_tail(&dsp->sendq, nskb); + schedule_work(&dsp->workq); + } + return; + } + /* in case of hardware conference */ + if (dsp->conf->hardware) + return; + list_for_each_entry(member, &dsp->conf->mlist, list) { + if (dsp->echo || member->dsp != dsp) { + nskb = skb_clone(skb, GFP_ATOMIC); + if (nskb) { + hh = mISDN_HEAD_P(nskb); + hh->prim = PH_DATA_REQ; + hh->id = 0; + skb_queue_tail(&member->dsp->sendq, nskb); + schedule_work(&member->dsp->workq); + } + } + } +} + + diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c new file mode 100644 index 000000000000..2f10ed82c0db --- /dev/null +++ b/drivers/isdn/mISDN/dsp_core.c @@ -0,0 +1,1191 @@ +/* + * Author Andreas Eversberg (jolly@eversberg.eu) + * Based on source code structure by + * Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/mISDN.cert + * + * Thanks to Karsten Keil (great drivers) + * Cologne Chip (great chips) + * + * This module does: + * Real-time tone generation + * DTMF detection + * Real-time cross-connection and conferrence + * Compensate jitter due to system load and hardware fault. + * All features are done in kernel space and will be realized + * using hardware, if available and supported by chip set. + * Blowfish encryption/decryption + */ + +/* STRUCTURE: + * + * The dsp module provides layer 2 for b-channels (64kbit). It provides + * transparent audio forwarding with special digital signal processing: + * + * - (1) generation of tones + * - (2) detection of dtmf tones + * - (3) crossconnecting and conferences (clocking) + * - (4) echo generation for delay test + * - (5) volume control + * - (6) disable receive data + * - (7) pipeline + * - (8) encryption/decryption + * + * Look: + * TX RX + * ------upper layer------ + * | ^ + * | |(6) + * v | + * +-----+-------------+-----+ + * |(3)(4) | + * | CMX | + * | | + * | +-------------+ + * | | ^ + * | | | + * |+---------+| +----+----+ + * ||(1) || |(2) | + * || || | | + * || Tones || | DTMF | + * || || | | + * || || | | + * |+----+----+| +----+----+ + * +-----+-----+ ^ + * | | + * v | + * +----+----+ +----+----+ + * |(5) | |(5) | + * | | | | + * |TX Volume| |RX Volume| + * | | | | + * | | | | + * +----+----+ +----+----+ + * | ^ + * | | + * v | + * +----+-------------+----+ + * |(7) | + * | | + * | Pipeline Processing | + * | | + * | | + * +----+-------------+----+ + * | ^ + * | | + * v | + * +----+----+ +----+----+ + * |(8) | |(8) | + * | | | | + * | Encrypt | | Decrypt | + * | | | | + * | | | | + * +----+----+ +----+----+ + * | ^ + * | | + * v | + * ------card layer------ + * TX RX + * + * Above you can see the logical data flow. If software is used to do the + * process, it is actually the real data flow. If hardware is used, data + * may not flow, but hardware commands to the card, to provide the data flow + * as shown. + * + * NOTE: The channel must be activated in order to make dsp work, even if + * no data flow to the upper layer is intended. Activation can be done + * after and before controlling the setting using PH_CONTROL requests. + * + * DTMF: Will be detected by hardware if possible. It is done before CMX + * processing. + * + * Tones: Will be generated via software if endless looped audio fifos are + * not supported by hardware. Tones will override all data from CMX. + * It is not required to join a conference to use tones at any time. + * + * CMX: Is transparent when not used. When it is used, it will do + * crossconnections and conferences via software if not possible through + * hardware. If hardware capability is available, hardware is used. + * + * Echo: Is generated by CMX and is used to check performane of hard and + * software CMX. + * + * The CMX has special functions for conferences with one, two and more + * members. It will allow different types of data flow. Receive and transmit + * data to/form upper layer may be swithed on/off individually without loosing + * features of CMX, Tones and DTMF. + * + * Echo Cancellation: Sometimes we like to cancel echo from the interface. + * Note that a VoIP call may not have echo caused by the IP phone. The echo + * is generated by the telephone line connected to it. Because the delay + * is high, it becomes an echo. RESULT: Echo Cachelation is required if + * both echo AND delay is applied to an interface. + * Remember that software CMX always generates a more or less delay. + * + * If all used features can be realized in hardware, and if transmit and/or + * receive data ist disabled, the card may not send/receive any data at all. + * Not receiving is usefull if only announcements are played. Not sending is + * usefull if an answering machine records audio. Not sending and receiving is + * usefull during most states of the call. If supported by hardware, tones + * will be played without cpu load. Small PBXs and NT-Mode applications will + * not need expensive hardware when processing calls. + * + * + * LOCKING: + * + * When data is received from upper or lower layer (card), the complete dsp + * module is locked by a global lock. This lock MUST lock irq, because it + * must lock timer events by DSP poll timer. + * When data is ready to be transmitted down, the data is queued and sent + * outside lock and timer event. + * PH_CONTROL must not change any settings, join or split conference members + * during process of data. + * + * HDLC: + * + * It works quite the same as transparent, except that HDLC data is forwarded + * to all other conference members if no hardware bridging is possible. + * Send data will be writte to sendq. Sendq will be sent if confirm is received. + * Conference cannot join, if one member is not hdlc. + * + */ + +#include <linux/delay.h> +#include <linux/mISDNif.h> +#include <linux/mISDNdsp.h> +#include <linux/module.h> +#include <linux/vmalloc.h> +#include "core.h" +#include "dsp.h" + +const char *mISDN_dsp_revision = "2.0"; + +static int debug; +static int options; +static int poll; +static int dtmfthreshold = 100; + +MODULE_AUTHOR("Andreas Eversberg"); +module_param(debug, uint, S_IRUGO | S_IWUSR); +module_param(options, uint, S_IRUGO | S_IWUSR); +module_param(poll, uint, S_IRUGO | S_IWUSR); +module_param(dtmfthreshold, uint, S_IRUGO | S_IWUSR); +MODULE_LICENSE("GPL"); + +/*int spinnest = 0;*/ + +spinlock_t dsp_lock; /* global dsp lock */ +struct list_head dsp_ilist; +struct list_head conf_ilist; +int dsp_debug; +int dsp_options; +int dsp_poll, dsp_tics; + +/* check if rx may be turned off or must be turned on */ +static void +dsp_rx_off_member(struct dsp *dsp) +{ + struct mISDN_ctrl_req cq; + int rx_off = 1; + + if (!dsp->features_rx_off) + return; + + /* not disabled */ + if (!dsp->rx_disabled) + rx_off = 0; + /* software dtmf */ + else if (dsp->dtmf.software) + rx_off = 0; + /* echo in software */ + else if (dsp->echo && dsp->pcm_slot_tx < 0) + rx_off = 0; + /* bridge in software */ + else if (dsp->conf) { + if (dsp->conf->software) + rx_off = 0; + } + + if (rx_off == dsp->rx_is_off) + return; + + if (!dsp->ch.peer) { + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: no peer, no rx_off\n", + __func__); + return; + } + cq.op = MISDN_CTRL_RX_OFF; + cq.p1 = rx_off; + if (dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq)) { + printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n", + __func__); + return; + } + dsp->rx_is_off = rx_off; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: %s set rx_off = %d\n", + __func__, dsp->name, rx_off); +} +static void +dsp_rx_off(struct dsp *dsp) +{ + struct dsp_conf_member *member; + + if (dsp_options & DSP_OPT_NOHARDWARE) + return; + + /* no conf */ + if (!dsp->conf) { + dsp_rx_off_member(dsp); + return; + } + /* check all members in conf */ + list_for_each_entry(member, &dsp->conf->mlist, list) { + dsp_rx_off_member(member->dsp); + } +} + +static int +dsp_control_req(struct dsp *dsp, struct mISDNhead *hh, struct sk_buff *skb) +{ + struct sk_buff *nskb; + int ret = 0; + int cont; + u8 *data; + int len; + + if (skb->len < sizeof(int)) + printk(KERN_ERR "%s: PH_CONTROL message too short\n", __func__); + cont = *((int *)skb->data); + len = skb->len - sizeof(int); + data = skb->data + sizeof(int); + + switch (cont) { + case DTMF_TONE_START: /* turn on DTMF */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: start dtmf\n", __func__); + if (len == sizeof(int)) { + printk(KERN_NOTICE "changing DTMF Threshold " + "to %d\n", *((int *)data)); + dsp->dtmf.treshold = (*(int *)data) * 10000; + } + /* init goertzel */ + dsp_dtmf_goertzel_init(dsp); + + /* check dtmf hardware */ + dsp_dtmf_hardware(dsp); + break; + case DTMF_TONE_STOP: /* turn off DTMF */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: stop dtmf\n", __func__); + dsp->dtmf.hardware = 0; + dsp->dtmf.software = 0; + break; + case DSP_CONF_JOIN: /* join / update conference */ + if (len < sizeof(int)) { + ret = -EINVAL; + break; + } + if (*((u32 *)data) == 0) + goto conf_split; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: join conference %d\n", + __func__, *((u32 *)data)); + ret = dsp_cmx_conf(dsp, *((u32 *)data)); + /* dsp_cmx_hardware will also be called here */ + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_CONF_SPLIT: /* remove from conference */ +conf_split: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: release conference\n", __func__); + ret = dsp_cmx_conf(dsp, 0); + /* dsp_cmx_hardware will also be called here */ + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + dsp_rx_off(dsp); + break; + case DSP_TONE_PATT_ON: /* play tone */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (len < sizeof(int)) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn tone 0x%x on\n", + __func__, *((int *)skb->data)); + ret = dsp_tone(dsp, *((int *)data)); + if (!ret) { + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + } + if (!dsp->tone.tone) + goto tone_off; + break; + case DSP_TONE_PATT_OFF: /* stop tone */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn tone off\n", __func__); + dsp_tone(dsp, 0); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + /* reset tx buffers (user space data) */ +tone_off: + dsp->rx_W = 0; + dsp->rx_R = 0; + break; + case DSP_VOL_CHANGE_TX: /* change volume */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (len < sizeof(int)) { + ret = -EINVAL; + break; + } + dsp->tx_volume = *((int *)data); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: change tx vol to %d\n", + __func__, dsp->tx_volume); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_dtmf_hardware(dsp); + dsp_rx_off(dsp); + break; + case DSP_VOL_CHANGE_RX: /* change volume */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (len < sizeof(int)) { + ret = -EINVAL; + break; + } + dsp->rx_volume = *((int *)data); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: change rx vol to %d\n", + __func__, dsp->tx_volume); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_dtmf_hardware(dsp); + dsp_rx_off(dsp); + break; + case DSP_ECHO_ON: /* enable echo */ + dsp->echo = 1; /* soft echo */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: enable cmx-echo\n", __func__); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_ECHO_OFF: /* disable echo */ + dsp->echo = 0; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: disable cmx-echo\n", __func__); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_RECEIVE_ON: /* enable receive to user space */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: enable receive to user " + "space\n", __func__); + dsp->rx_disabled = 0; + dsp_rx_off(dsp); + break; + case DSP_RECEIVE_OFF: /* disable receive to user space */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: disable receive to " + "user space\n", __func__); + dsp->rx_disabled = 1; + dsp_rx_off(dsp); + break; + case DSP_MIX_ON: /* enable mixing of tx data */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: enable mixing of " + "tx-data with conf mebers\n", __func__); + dsp->tx_mix = 1; + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_MIX_OFF: /* disable mixing of tx data */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: disable mixing of " + "tx-data with conf mebers\n", __func__); + dsp->tx_mix = 0; + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_TXDATA_ON: /* enable txdata */ + dsp->tx_data = 1; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: enable tx-data\n", __func__); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_TXDATA_OFF: /* disable txdata */ + dsp->tx_data = 0; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: disable tx-data\n", __func__); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_DELAY: /* use delay algorithm instead of dynamic + jitter algorithm */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (len < sizeof(int)) { + ret = -EINVAL; + break; + } + dsp->cmx_delay = (*((int *)data)) << 3; + /* miliseconds to samples */ + if (dsp->cmx_delay >= (CMX_BUFF_HALF>>1)) + /* clip to half of maximum usable buffer + (half of half buffer) */ + dsp->cmx_delay = (CMX_BUFF_HALF>>1) - 1; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: use delay algorithm to " + "compensate jitter (%d samples)\n", + __func__, dsp->cmx_delay); + break; + case DSP_JITTER: /* use dynamic jitter algorithm instead of + delay algorithm */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + dsp->cmx_delay = 0; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: use jitter algorithm to " + "compensate jitter\n", __func__); + break; + case DSP_TX_DEJITTER: /* use dynamic jitter algorithm for tx-buffer */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + dsp->tx_dejitter = 1; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: use dejitter on TX " + "buffer\n", __func__); + break; + case DSP_TX_DEJ_OFF: /* use tx-buffer without dejittering*/ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + dsp->tx_dejitter = 0; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: use TX buffer without " + "dejittering\n", __func__); + break; + case DSP_PIPELINE_CFG: + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (len > 0 && ((char *)data)[len - 1]) { + printk(KERN_DEBUG "%s: pipeline config string " + "is not NULL terminated!\n", __func__); + ret = -EINVAL; + } else { + dsp->pipeline.inuse = 1; + dsp_cmx_hardware(dsp->conf, dsp); + ret = dsp_pipeline_build(&dsp->pipeline, + len > 0 ? (char *)data : NULL); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + } + break; + case DSP_BF_ENABLE_KEY: /* turn blowfish on */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (len < 4 || len > 56) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn blowfish on (key " + "not shown)\n", __func__); + ret = dsp_bf_init(dsp, (u8 *)data, len); + /* set new cont */ + if (!ret) + cont = DSP_BF_ACCEPT; + else + cont = DSP_BF_REJECT; + /* send indication if it worked to set it */ + nskb = _alloc_mISDN_skb(PH_CONTROL_IND, MISDN_ID_ANY, + sizeof(int), &cont, GFP_ATOMIC); + if (nskb) { + if (dsp->up) { + if (dsp->up->send(dsp->up, nskb)) + dev_kfree_skb(nskb); + } else + dev_kfree_skb(nskb); + } + if (!ret) { + dsp_cmx_hardware(dsp->conf, dsp); + dsp_dtmf_hardware(dsp); + dsp_rx_off(dsp); + } + break; + case DSP_BF_DISABLE: /* turn blowfish off */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn blowfish off\n", __func__); + dsp_bf_cleanup(dsp); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_dtmf_hardware(dsp); + dsp_rx_off(dsp); + break; + default: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: ctrl req %x unhandled\n", + __func__, cont); + ret = -EINVAL; + } + return ret; +} + +static void +get_features(struct mISDNchannel *ch) +{ + struct dsp *dsp = container_of(ch, struct dsp, ch); + struct mISDN_ctrl_req cq; + + if (dsp_options & DSP_OPT_NOHARDWARE) + return; + if (!ch->peer) { + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: no peer, no features\n", + __func__); + return; + } + memset(&cq, 0, sizeof(cq)); + cq.op = MISDN_CTRL_GETOP; + if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq) < 0) { + printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", + __func__); + return; + } + if (cq.op & MISDN_CTRL_RX_OFF) + dsp->features_rx_off = 1; + if ((cq.op & MISDN_CTRL_HW_FEATURES_OP)) { + cq.op = MISDN_CTRL_HW_FEATURES; + *((u_long *)&cq.p1) = (u_long)&dsp->features; + if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq)) { + printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n", + __func__); + } + } else + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: features not supported for %s\n", + __func__, dsp->name); +} + +static int +dsp_function(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct dsp *dsp = container_of(ch, struct dsp, ch); + struct mISDNhead *hh; + int ret = 0; + u8 *digits; + int cont; + struct sk_buff *nskb; + u_long flags; + + hh = mISDN_HEAD_P(skb); + switch (hh->prim) { + /* FROM DOWN */ + case (PH_DATA_CNF): + dsp->data_pending = 0; + /* trigger next hdlc frame, if any */ + if (dsp->hdlc) { + spin_lock_irqsave(&dsp_lock, flags); + if (dsp->b_active) + schedule_work(&dsp->workq); + spin_unlock_irqrestore(&dsp_lock, flags); + } + break; + case (PH_DATA_IND): + case (DL_DATA_IND): + if (skb->len < 1) { + ret = -EINVAL; + break; + } + if (dsp->rx_is_off) { + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: rx-data during rx_off" + " for %s\n", + __func__, dsp->name); + } + if (dsp->hdlc) { + /* hdlc */ + spin_lock_irqsave(&dsp_lock, flags); + dsp_cmx_hdlc(dsp, skb); + spin_unlock_irqrestore(&dsp_lock, flags); + if (dsp->rx_disabled) { + /* if receive is not allowed */ + break; + } + hh->prim = DL_DATA_IND; + if (dsp->up) + return dsp->up->send(dsp->up, skb); + break; + } + + /* decrypt if enabled */ + if (dsp->bf_enable) + dsp_bf_decrypt(dsp, skb->data, skb->len); + /* pipeline */ + if (dsp->pipeline.inuse) + dsp_pipeline_process_rx(&dsp->pipeline, skb->data, + skb->len); + /* change volume if requested */ + if (dsp->rx_volume) + dsp_change_volume(skb, dsp->rx_volume); + + /* check if dtmf soft decoding is turned on */ + if (dsp->dtmf.software) { + digits = dsp_dtmf_goertzel_decode(dsp, skb->data, + skb->len, (dsp_options&DSP_OPT_ULAW)?1:0); + while (*digits) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s: digit" + "(%c) to layer %s\n", + __func__, *digits, dsp->name); + cont = DTMF_TONE_VAL | *digits; + nskb = _alloc_mISDN_skb(PH_CONTROL_IND, + MISDN_ID_ANY, sizeof(int), &cont, + GFP_ATOMIC); + if (nskb) { + if (dsp->up) { + if (dsp->up->send( + dsp->up, nskb)) + dev_kfree_skb(nskb); + } else + dev_kfree_skb(nskb); + } + digits++; + } + } + /* we need to process receive data if software */ + spin_lock_irqsave(&dsp_lock, flags); + if (dsp->pcm_slot_tx < 0 && dsp->pcm_slot_rx < 0) { + /* process data from card at cmx */ + dsp_cmx_receive(dsp, skb); + } + spin_unlock_irqrestore(&dsp_lock, flags); + + if (dsp->rx_disabled) { + /* if receive is not allowed */ + break; + } + hh->prim = DL_DATA_IND; + if (dsp->up) + return dsp->up->send(dsp->up, skb); + break; + case (PH_CONTROL_IND): + if (dsp_debug & DEBUG_DSP_DTMFCOEFF) + printk(KERN_DEBUG "%s: PH_CONTROL INDICATION " + "received: %x (len %d) %s\n", __func__, + hh->id, skb->len, dsp->name); + switch (hh->id) { + case (DTMF_HFC_COEF): /* getting coefficients */ + if (!dsp->dtmf.hardware) { + if (dsp_debug & DEBUG_DSP_DTMFCOEFF) + printk(KERN_DEBUG "%s: ignoring DTMF " + "coefficients from HFC\n", + __func__); + break; + } + digits = dsp_dtmf_goertzel_decode(dsp, skb->data, + skb->len, 2); + while (*digits) { + int k; + struct sk_buff *nskb; + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s: digit" + "(%c) to layer %s\n", + __func__, *digits, dsp->name); + k = *digits | DTMF_TONE_VAL; + nskb = _alloc_mISDN_skb(PH_CONTROL_IND, + MISDN_ID_ANY, sizeof(int), &k, + GFP_ATOMIC); + if (nskb) { + if (dsp->up) { + if (dsp->up->send( + dsp->up, nskb)) + dev_kfree_skb(nskb); + } else + dev_kfree_skb(nskb); + } + digits++; + } + break; + case (HFC_VOL_CHANGE_TX): /* change volume */ + if (skb->len != sizeof(int)) { + ret = -EINVAL; + break; + } + spin_lock_irqsave(&dsp_lock, flags); + dsp->tx_volume = *((int *)skb->data); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: change tx volume to " + "%d\n", __func__, dsp->tx_volume); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_dtmf_hardware(dsp); + dsp_rx_off(dsp); + spin_unlock_irqrestore(&dsp_lock, flags); + break; + default: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: ctrl ind %x unhandled " + "%s\n", __func__, hh->id, dsp->name); + ret = -EINVAL; + } + break; + case (PH_ACTIVATE_IND): + case (PH_ACTIVATE_CNF): + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: b_channel is now active %s\n", + __func__, dsp->name); + /* bchannel now active */ + spin_lock_irqsave(&dsp_lock, flags); + dsp->b_active = 1; + dsp->data_pending = 0; + dsp->rx_init = 1; + /* rx_W and rx_R will be adjusted on first frame */ + dsp->rx_W = 0; + dsp->rx_R = 0; + memset(dsp->rx_buff, 0, sizeof(dsp->rx_buff)); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_dtmf_hardware(dsp); + dsp_rx_off(dsp); + spin_unlock_irqrestore(&dsp_lock, flags); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: done with activation, sending " + "confirm to user space. %s\n", __func__, + dsp->name); + /* send activation to upper layer */ + hh->prim = DL_ESTABLISH_CNF; + if (dsp->up) + return dsp->up->send(dsp->up, skb); + break; + case (PH_DEACTIVATE_IND): + case (PH_DEACTIVATE_CNF): + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: b_channel is now inactive %s\n", + __func__, dsp->name); + /* bchannel now inactive */ + spin_lock_irqsave(&dsp_lock, flags); + dsp->b_active = 0; + dsp->data_pending = 0; + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + spin_unlock_irqrestore(&dsp_lock, flags); + hh->prim = DL_RELEASE_CNF; + if (dsp->up) + return dsp->up->send(dsp->up, skb); + break; + /* FROM UP */ + case (DL_DATA_REQ): + case (PH_DATA_REQ): + if (skb->len < 1) { + ret = -EINVAL; + break; + } + if (dsp->hdlc) { + /* hdlc */ + spin_lock_irqsave(&dsp_lock, flags); + if (dsp->b_active) { + skb_queue_tail(&dsp->sendq, skb); + schedule_work(&dsp->workq); + } + spin_unlock_irqrestore(&dsp_lock, flags); + return 0; + } + /* send data to tx-buffer (if no tone is played) */ + if (!dsp->tone.tone) { + spin_lock_irqsave(&dsp_lock, flags); + dsp_cmx_transmit(dsp, skb); + spin_unlock_irqrestore(&dsp_lock, flags); + } + break; + case (PH_CONTROL_REQ): + spin_lock_irqsave(&dsp_lock, flags); + ret = dsp_control_req(dsp, hh, skb); + spin_unlock_irqrestore(&dsp_lock, flags); + break; + case (DL_ESTABLISH_REQ): + case (PH_ACTIVATE_REQ): + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: activating b_channel %s\n", + __func__, dsp->name); + if (dsp->dtmf.hardware || dsp->dtmf.software) + dsp_dtmf_goertzel_init(dsp); + get_features(ch); + /* send ph_activate */ + hh->prim = PH_ACTIVATE_REQ; + if (ch->peer) + return ch->recv(ch->peer, skb); + break; + case (DL_RELEASE_REQ): + case (PH_DEACTIVATE_REQ): + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: releasing b_channel %s\n", + __func__, dsp->name); + spin_lock_irqsave(&dsp_lock, flags); + dsp->tone.tone = 0; + dsp->tone.hardware = 0; + dsp->tone.software = 0; + if (timer_pending(&dsp->tone.tl)) + del_timer(&dsp->tone.tl); + if (dsp->conf) + dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be + called here */ + skb_queue_purge(&dsp->sendq); + spin_unlock_irqrestore(&dsp_lock, flags); + hh->prim = PH_DEACTIVATE_REQ; + if (ch->peer) + return ch->recv(ch->peer, skb); + break; + default: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: msg %x unhandled %s\n", + __func__, hh->prim, dsp->name); + ret = -EINVAL; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static int +dsp_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct dsp *dsp = container_of(ch, struct dsp, ch); + u_long flags; + int err = 0; + + if (debug & DEBUG_DSP_CTRL) + printk(KERN_DEBUG "%s:(%x)\n", __func__, cmd); + + switch (cmd) { + case OPEN_CHANNEL: + break; + case CLOSE_CHANNEL: + if (dsp->ch.peer) + dsp->ch.peer->ctrl(dsp->ch.peer, CLOSE_CHANNEL, NULL); + + /* wait until workqueue has finished, + * must lock here, or we may hit send-process currently + * queueing. */ + spin_lock_irqsave(&dsp_lock, flags); + dsp->b_active = 0; + spin_unlock_irqrestore(&dsp_lock, flags); + /* MUST not be locked, because it waits until queue is done. */ + cancel_work_sync(&dsp->workq); + spin_lock_irqsave(&dsp_lock, flags); + if (timer_pending(&dsp->tone.tl)) + del_timer(&dsp->tone.tl); + skb_queue_purge(&dsp->sendq); + if (dsp_debug & DEBUG_DSP_CTRL) + printk(KERN_DEBUG "%s: releasing member %s\n", + __func__, dsp->name); + dsp->b_active = 0; + dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be called + here */ + dsp_pipeline_destroy(&dsp->pipeline); + + if (dsp_debug & DEBUG_DSP_CTRL) + printk(KERN_DEBUG "%s: remove & destroy object %s\n", + __func__, dsp->name); + list_del(&dsp->list); + spin_unlock_irqrestore(&dsp_lock, flags); + + if (dsp_debug & DEBUG_DSP_CTRL) + printk(KERN_DEBUG "%s: dsp instance released\n", + __func__); + vfree(dsp); + module_put(THIS_MODULE); + break; + } + return err; +} + +static void +dsp_send_bh(struct work_struct *work) +{ + struct dsp *dsp = container_of(work, struct dsp, workq); + struct sk_buff *skb; + struct mISDNhead *hh; + + if (dsp->hdlc && dsp->data_pending) + return; /* wait until data has been acknowledged */ + + /* send queued data */ + while ((skb = skb_dequeue(&dsp->sendq))) { + /* in locked date, we must have still data in queue */ + if (dsp->data_pending) { + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: fifo full %s, this is " + "no bug!\n", __func__, dsp->name); + /* flush transparent data, if not acked */ + dev_kfree_skb(skb); + continue; + } + hh = mISDN_HEAD_P(skb); + if (hh->prim == DL_DATA_REQ) { + /* send packet up */ + if (dsp->up) { + if (dsp->up->send(dsp->up, skb)) + dev_kfree_skb(skb); + } else + dev_kfree_skb(skb); + } else { + /* send packet down */ + if (dsp->ch.peer) { + dsp->data_pending = 1; + if (dsp->ch.recv(dsp->ch.peer, skb)) { + dev_kfree_skb(skb); + dsp->data_pending = 0; + } + } else + dev_kfree_skb(skb); + } + } +} + +static int +dspcreate(struct channel_req *crq) +{ + struct dsp *ndsp; + u_long flags; + + if (crq->protocol != ISDN_P_B_L2DSP + && crq->protocol != ISDN_P_B_L2DSPHDLC) + return -EPROTONOSUPPORT; + ndsp = vmalloc(sizeof(struct dsp)); + if (!ndsp) { + printk(KERN_ERR "%s: vmalloc struct dsp failed\n", __func__); + return -ENOMEM; + } + memset(ndsp, 0, sizeof(struct dsp)); + if (dsp_debug & DEBUG_DSP_CTRL) + printk(KERN_DEBUG "%s: creating new dsp instance\n", __func__); + + /* default enabled */ + INIT_WORK(&ndsp->workq, (void *)dsp_send_bh); + skb_queue_head_init(&ndsp->sendq); + ndsp->ch.send = dsp_function; + ndsp->ch.ctrl = dsp_ctrl; + ndsp->up = crq->ch; + crq->ch = &ndsp->ch; + if (crq->protocol == ISDN_P_B_L2DSP) { + crq->protocol = ISDN_P_B_RAW; + ndsp->hdlc = 0; + } else { + crq->protocol = ISDN_P_B_HDLC; + ndsp->hdlc = 1; + } + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", + __func__); + + sprintf(ndsp->name, "DSP_C%x(0x%p)", + ndsp->up->st->dev->id + 1, ndsp); + /* set frame size to start */ + ndsp->features.hfc_id = -1; /* current PCM id */ + ndsp->features.pcm_id = -1; /* current PCM id */ + ndsp->pcm_slot_rx = -1; /* current CPM slot */ + ndsp->pcm_slot_tx = -1; + ndsp->pcm_bank_rx = -1; + ndsp->pcm_bank_tx = -1; + ndsp->hfc_conf = -1; /* current conference number */ + /* set tone timer */ + ndsp->tone.tl.function = (void *)dsp_tone_timeout; + ndsp->tone.tl.data = (long) ndsp; + init_timer(&ndsp->tone.tl); + + if (dtmfthreshold < 20 || dtmfthreshold > 500) + dtmfthreshold = 200; + ndsp->dtmf.treshold = dtmfthreshold*10000; + + /* init pipeline append to list */ + spin_lock_irqsave(&dsp_lock, flags); + dsp_pipeline_init(&ndsp->pipeline); + list_add_tail(&ndsp->list, &dsp_ilist); + spin_unlock_irqrestore(&dsp_lock, flags); + + return 0; +} + + +static struct Bprotocol DSP = { + .Bprotocols = (1 << (ISDN_P_B_L2DSP & ISDN_P_B_MASK)) + | (1 << (ISDN_P_B_L2DSPHDLC & ISDN_P_B_MASK)), + .name = "dsp", + .create = dspcreate +}; + +static int dsp_init(void) +{ + int err; + int tics; + + printk(KERN_INFO "DSP modul %s\n", mISDN_dsp_revision); + + dsp_options = options; + dsp_debug = debug; + + /* set packet size */ + dsp_poll = poll; + if (dsp_poll) { + if (dsp_poll > MAX_POLL) { + printk(KERN_ERR "%s: Wrong poll value (%d), use %d " + "maximum.\n", __func__, poll, MAX_POLL); + err = -EINVAL; + return err; + } + if (dsp_poll < 8) { + printk(KERN_ERR "%s: Wrong poll value (%d), use 8 " + "minimum.\n", __func__, dsp_poll); + err = -EINVAL; + return err; + } + dsp_tics = poll * HZ / 8000; + if (dsp_tics * 8000 != poll * HZ) { + printk(KERN_INFO "mISDN_dsp: Cannot clock every %d " + "samples (0,125 ms). It is not a multiple of " + "%d HZ.\n", poll, HZ); + err = -EINVAL; + return err; + } + } else { + poll = 8; + while (poll <= MAX_POLL) { + tics = poll * HZ / 8000; + if (tics * 8000 == poll * HZ) { + dsp_tics = tics; + dsp_poll = poll; + if (poll >= 64) + break; + } + poll++; + } + } + if (dsp_poll == 0) { + printk(KERN_INFO "mISDN_dsp: There is no multiple of kernel " + "clock that equals exactly the duration of 8-256 " + "samples. (Choose kernel clock speed like 100, 250, " + "300, 1000)\n"); + err = -EINVAL; + return err; + } + printk(KERN_INFO "mISDN_dsp: DSP clocks every %d samples. This equals " + "%d jiffies.\n", dsp_poll, dsp_tics); + + spin_lock_init(&dsp_lock); + INIT_LIST_HEAD(&dsp_ilist); + INIT_LIST_HEAD(&conf_ilist); + + /* init conversion tables */ + dsp_audio_generate_law_tables(); + dsp_silence = (dsp_options&DSP_OPT_ULAW)?0xff:0x2a; + dsp_audio_law_to_s32 = (dsp_options&DSP_OPT_ULAW)?dsp_audio_ulaw_to_s32: + dsp_audio_alaw_to_s32; + dsp_audio_generate_s2law_table(); + dsp_audio_generate_seven(); + dsp_audio_generate_mix_table(); + if (dsp_options & DSP_OPT_ULAW) + dsp_audio_generate_ulaw_samples(); + dsp_audio_generate_volume_changes(); + + err = dsp_pipeline_module_init(); + if (err) { + printk(KERN_ERR "mISDN_dsp: Can't initialize pipeline, " + "error(%d)\n", err); + return err; + } + + err = mISDN_register_Bprotocol(&DSP); + if (err) { + printk(KERN_ERR "Can't register %s error(%d)\n", DSP.name, err); + return err; + } + + /* set sample timer */ + dsp_spl_tl.function = (void *)dsp_cmx_send; + dsp_spl_tl.data = 0; + init_timer(&dsp_spl_tl); + dsp_spl_tl.expires = jiffies + dsp_tics; + dsp_spl_jiffies = dsp_spl_tl.expires; + add_timer(&dsp_spl_tl); + + return 0; +} + + +static void dsp_cleanup(void) +{ + mISDN_unregister_Bprotocol(&DSP); + + if (timer_pending(&dsp_spl_tl)) + del_timer(&dsp_spl_tl); + + if (!list_empty(&dsp_ilist)) { + printk(KERN_ERR "mISDN_dsp: Audio DSP object inst list not " + "empty.\n"); + } + if (!list_empty(&conf_ilist)) { + printk(KERN_ERR "mISDN_dsp: Conference list not empty. Not " + "all memory freed.\n"); + } + + dsp_pipeline_module_exit(); +} + +module_init(dsp_init); +module_exit(dsp_cleanup); + diff --git a/drivers/isdn/mISDN/dsp_dtmf.c b/drivers/isdn/mISDN/dsp_dtmf.c new file mode 100644 index 000000000000..efc371c1f0dc --- /dev/null +++ b/drivers/isdn/mISDN/dsp_dtmf.c @@ -0,0 +1,303 @@ +/* + * DTMF decoder. + * + * Copyright by Andreas Eversberg (jolly@eversberg.eu) + * based on different decoders such as ISDN4Linux + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/mISDNif.h> +#include <linux/mISDNdsp.h> +#include "core.h" +#include "dsp.h" + +#define NCOEFF 8 /* number of frequencies to be analyzed */ + +/* For DTMF recognition: + * 2 * cos(2 * PI * k / N) precalculated for all k + */ +static u64 cos2pik[NCOEFF] = +{ + /* k << 15 (source: hfc-4s/8s documentation (www.colognechip.de)) */ + 55960, 53912, 51402, 48438, 38146, 32650, 26170, 18630 +}; + +/* digit matrix */ +static char dtmf_matrix[4][4] = +{ + {'1', '2', '3', 'A'}, + {'4', '5', '6', 'B'}, + {'7', '8', '9', 'C'}, + {'*', '0', '#', 'D'} +}; + +/* dtmf detection using goertzel algorithm + * init function + */ +void dsp_dtmf_goertzel_init(struct dsp *dsp) +{ + dsp->dtmf.size = 0; + dsp->dtmf.lastwhat = '\0'; + dsp->dtmf.lastdigit = '\0'; + dsp->dtmf.count = 0; +} + +/* check for hardware or software features + */ +void dsp_dtmf_hardware(struct dsp *dsp) +{ + int hardware = 1; + + if (!dsp->features.hfc_dtmf) + hardware = 0; + + /* check for volume change */ + if (dsp->tx_volume) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " + "because tx_volume is changed\n", + __func__, dsp->name); + hardware = 0; + } + if (dsp->rx_volume) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " + "because rx_volume is changed\n", + __func__, dsp->name); + hardware = 0; + } + /* check if encryption is enabled */ + if (dsp->bf_enable) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " + "because encryption is enabled\n", + __func__, dsp->name); + hardware = 0; + } + /* check if pipeline exists */ + if (dsp->pipeline.inuse) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " + "because pipeline exists.\n", + __func__, dsp->name); + hardware = 0; + } + + dsp->dtmf.hardware = hardware; + dsp->dtmf.software = !hardware; +} + + +/************************************************************* + * calculate the coefficients of the given sample and decode * + *************************************************************/ + +/* the given sample is decoded. if the sample is not long enough for a + * complete frame, the decoding is finished and continued with the next + * call of this function. + * + * the algorithm is very good for detection with a minimum of errors. i + * tested it allot. it even works with very short tones (40ms). the only + * disadvantage is, that it doesn't work good with different volumes of both + * tones. this will happen, if accoustically coupled dialers are used. + * it sometimes detects tones during speach, which is normal for decoders. + * use sequences to given commands during calls. + * + * dtmf - points to a structure of the current dtmf state + * spl and len - the sample + * fmt - 0 = alaw, 1 = ulaw, 2 = coefficients from HFC DTMF hw-decoder + */ + +u8 +*dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len, int fmt) +{ + u8 what; + int size; + signed short *buf; + s32 sk, sk1, sk2; + int k, n, i; + s32 *hfccoeff; + s32 result[NCOEFF], tresh, treshl; + int lowgroup, highgroup; + s64 cos2pik_; + + dsp->dtmf.digits[0] = '\0'; + + /* Note: The function will loop until the buffer has not enough samples + * left to decode a full frame. + */ +again: + /* convert samples */ + size = dsp->dtmf.size; + buf = dsp->dtmf.buffer; + switch (fmt) { + case 0: /* alaw */ + case 1: /* ulaw */ + while (size < DSP_DTMF_NPOINTS && len) { + buf[size++] = dsp_audio_law_to_s32[*data++]; + len--; + } + break; + + case 2: /* HFC coefficients */ + default: + if (len < 64) { + if (len > 0) + printk(KERN_ERR "%s: coefficients have invalid " + "size. (is=%d < must=%d)\n", + __func__, len, 64); + return dsp->dtmf.digits; + } + hfccoeff = (s32 *)data; + for (k = 0; k < NCOEFF; k++) { + sk2 = (*hfccoeff++)>>4; + sk = (*hfccoeff++)>>4; + if (sk > 32767 || sk < -32767 || sk2 > 32767 + || sk2 < -32767) + printk(KERN_WARNING + "DTMF-Detection overflow\n"); + /* compute |X(k)|**2 */ + result[k] = + (sk * sk) - + (((cos2pik[k] * sk) >> 15) * sk2) + + (sk2 * sk2); + } + data += 64; + len -= 64; + goto coefficients; + break; + } + dsp->dtmf.size = size; + + if (size < DSP_DTMF_NPOINTS) + return dsp->dtmf.digits; + + dsp->dtmf.size = 0; + + /* now we have a full buffer of signed long samples - we do goertzel */ + for (k = 0; k < NCOEFF; k++) { + sk = 0; + sk1 = 0; + sk2 = 0; + buf = dsp->dtmf.buffer; + cos2pik_ = cos2pik[k]; + for (n = 0; n < DSP_DTMF_NPOINTS; n++) { + sk = ((cos2pik_*sk1)>>15) - sk2 + (*buf++); + sk2 = sk1; + sk1 = sk; + } + sk >>= 8; + sk2 >>= 8; + if (sk > 32767 || sk < -32767 || sk2 > 32767 || sk2 < -32767) + printk(KERN_WARNING "DTMF-Detection overflow\n"); + /* compute |X(k)|**2 */ + result[k] = + (sk * sk) - + (((cos2pik[k] * sk) >> 15) * sk2) + + (sk2 * sk2); + } + + /* our (squared) coefficients have been calculated, we need to process + * them. + */ +coefficients: + tresh = 0; + for (i = 0; i < NCOEFF; i++) { + if (result[i] < 0) + result[i] = 0; + if (result[i] > dsp->dtmf.treshold) { + if (result[i] > tresh) + tresh = result[i]; + } + } + + if (tresh == 0) { + what = 0; + goto storedigit; + } + + if (dsp_debug & DEBUG_DSP_DTMFCOEFF) + printk(KERN_DEBUG "a %3d %3d %3d %3d %3d %3d %3d %3d" + " tr:%3d r %3d %3d %3d %3d %3d %3d %3d %3d\n", + result[0]/10000, result[1]/10000, result[2]/10000, + result[3]/10000, result[4]/10000, result[5]/10000, + result[6]/10000, result[7]/10000, tresh/10000, + result[0]/(tresh/100), result[1]/(tresh/100), + result[2]/(tresh/100), result[3]/(tresh/100), + result[4]/(tresh/100), result[5]/(tresh/100), + result[6]/(tresh/100), result[7]/(tresh/100)); + + /* calc digit (lowgroup/highgroup) */ + lowgroup = -1; + highgroup = -1; + treshl = tresh >> 3; /* tones which are not on, must be below 9 dB */ + tresh = tresh >> 2; /* touchtones must match within 6 dB */ + for (i = 0; i < NCOEFF; i++) { + if (result[i] < treshl) + continue; /* ignore */ + if (result[i] < tresh) { + lowgroup = -1; + highgroup = -1; + break; /* noise inbetween */ + } + /* good level found. This is allowed only one time per group */ + if (i < NCOEFF/2) { + /* lowgroup */ + if (lowgroup >= 0) { + /* Bad. Another tone found. */ + lowgroup = -1; + break; + } else + lowgroup = i; + } else { + /* higroup */ + if (highgroup >= 0) { + /* Bad. Another tone found. */ + highgroup = -1; + break; + } else + highgroup = i-(NCOEFF/2); + } + } + + /* get digit or null */ + what = 0; + if (lowgroup >= 0 && highgroup >= 0) + what = dtmf_matrix[lowgroup][highgroup]; + +storedigit: + if (what && (dsp_debug & DEBUG_DSP_DTMF)) + printk(KERN_DEBUG "DTMF what: %c\n", what); + + if (dsp->dtmf.lastwhat != what) + dsp->dtmf.count = 0; + + /* the tone (or no tone) must remain 3 times without change */ + if (dsp->dtmf.count == 2) { + if (dsp->dtmf.lastdigit != what) { + dsp->dtmf.lastdigit = what; + if (what) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "DTMF digit: %c\n", + what); + if ((strlen(dsp->dtmf.digits)+1) + < sizeof(dsp->dtmf.digits)) { + dsp->dtmf.digits[strlen( + dsp->dtmf.digits)+1] = '\0'; + dsp->dtmf.digits[strlen( + dsp->dtmf.digits)] = what; + } + } + } + } else + dsp->dtmf.count++; + + dsp->dtmf.lastwhat = what; + + goto again; +} + + diff --git a/drivers/isdn/mISDN/dsp_ecdis.h b/drivers/isdn/mISDN/dsp_ecdis.h new file mode 100644 index 000000000000..8a20af43308b --- /dev/null +++ b/drivers/isdn/mISDN/dsp_ecdis.h @@ -0,0 +1,110 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * ec_disable_detector.h - A detector which should eventually meet the + * G.164/G.165 requirements for detecting the + * 2100Hz echo cancellor disable tone. + * + * Written by Steve Underwood <steveu@coppice.org> + * + * Copyright (C) 2001 Steve Underwood + * + * All rights reserved. + * + * 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 "dsp_biquad.h" + +struct ec_disable_detector_state { + struct biquad2_state notch; + int notch_level; + int channel_level; + int tone_present; + int tone_cycle_duration; + int good_cycles; + int hit; +}; + + +#define FALSE 0 +#define TRUE (!FALSE) + +static inline void +echo_can_disable_detector_init(struct ec_disable_detector_state *det) +{ + /* Elliptic notch */ + /* This is actually centred at 2095Hz, but gets the balance we want, due + to the asymmetric walls of the notch */ + biquad2_init(&det->notch, + (int32_t) (-0.7600000*32768.0), + (int32_t) (-0.1183852*32768.0), + (int32_t) (-0.5104039*32768.0), + (int32_t) (0.1567596*32768.0), + (int32_t) (1.0000000*32768.0)); + + det->channel_level = 0; + det->notch_level = 0; + det->tone_present = FALSE; + det->tone_cycle_duration = 0; + det->good_cycles = 0; + det->hit = 0; +} +/*- End of function --------------------------------------------------------*/ + +static inline int +echo_can_disable_detector_update(struct ec_disable_detector_state *det, +int16_t amp) +{ + int16_t notched; + + notched = biquad2(&det->notch, amp); + /* Estimate the overall energy in the channel, and the energy in + the notch (i.e. overall channel energy - tone energy => noise). + Use abs instead of multiply for speed (is it really faster?). + Damp the overall energy a little more for a stable result. + Damp the notch energy a little less, so we don't damp out the + blip every time the phase reverses */ + det->channel_level += ((abs(amp) - det->channel_level) >> 5); + det->notch_level += ((abs(notched) - det->notch_level) >> 4); + if (det->channel_level > 280) { + /* There is adequate energy in the channel. + Is it mostly at 2100Hz? */ + if (det->notch_level*6 < det->channel_level) { + /* The notch says yes, so we have the tone. */ + if (!det->tone_present) { + /* Do we get a kick every 450+-25ms? */ + if (det->tone_cycle_duration >= 425*8 + && det->tone_cycle_duration <= 475*8) { + det->good_cycles++; + if (det->good_cycles > 2) + det->hit = TRUE; + } + det->tone_cycle_duration = 0; + } + det->tone_present = TRUE; + } else + det->tone_present = FALSE; + det->tone_cycle_duration++; + } else { + det->tone_present = FALSE; + det->tone_cycle_duration = 0; + det->good_cycles = 0; + } + return det->hit; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ diff --git a/drivers/isdn/mISDN/dsp_hwec.c b/drivers/isdn/mISDN/dsp_hwec.c new file mode 100644 index 000000000000..eb892d9dd5c6 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_hwec.c @@ -0,0 +1,138 @@ +/* + * dsp_hwec.c: + * builtin mISDN dsp pipeline element for enabling the hw echocanceller + * + * Copyright (C) 2007, Nadi Sarrar + * + * Nadi Sarrar <nadi@beronet.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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + */ + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/mISDNdsp.h> +#include <linux/mISDNif.h> +#include "core.h" +#include "dsp.h" +#include "dsp_hwec.h" + +static struct mISDN_dsp_element_arg args[] = { + { "deftaps", "128", "Set the number of taps of cancellation." }, +}; + +static struct mISDN_dsp_element dsp_hwec_p = { + .name = "hwec", + .new = NULL, + .free = NULL, + .process_tx = NULL, + .process_rx = NULL, + .num_args = sizeof(args) / sizeof(struct mISDN_dsp_element_arg), + .args = args, +}; +struct mISDN_dsp_element *dsp_hwec = &dsp_hwec_p; + +void dsp_hwec_enable(struct dsp *dsp, const char *arg) +{ + int deftaps = 128, + len; + struct mISDN_ctrl_req cq; + + if (!dsp) { + printk(KERN_ERR "%s: failed to enable hwec: dsp is NULL\n", + __func__); + return; + } + + if (!arg) + goto _do; + + len = strlen(arg); + if (!len) + goto _do; + + { + char _dup[len + 1]; + char *dup, *tok, *name, *val; + int tmp; + + strcpy(_dup, arg); + dup = _dup; + + while ((tok = strsep(&dup, ","))) { + if (!strlen(tok)) + continue; + name = strsep(&tok, "="); + val = tok; + + if (!val) + continue; + + if (!strcmp(name, "deftaps")) { + if (sscanf(val, "%d", &tmp) == 1) + deftaps = tmp; + } + } + } + +_do: + printk(KERN_DEBUG "%s: enabling hwec with deftaps=%d\n", + __func__, deftaps); + memset(&cq, 0, sizeof(cq)); + cq.op = MISDN_CTRL_HFC_ECHOCAN_ON; + cq.p1 = deftaps; + if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) { + printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", + __func__); + return; + } +} + +void dsp_hwec_disable(struct dsp *dsp) +{ + struct mISDN_ctrl_req cq; + + if (!dsp) { + printk(KERN_ERR "%s: failed to disable hwec: dsp is NULL\n", + __func__); + return; + } + + printk(KERN_DEBUG "%s: disabling hwec\n", __func__); + memset(&cq, 0, sizeof(cq)); + cq.op = MISDN_CTRL_HFC_ECHOCAN_OFF; + if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) { + printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", + __func__); + return; + } +} + +int dsp_hwec_init(void) +{ + mISDN_dsp_element_register(dsp_hwec); + + return 0; +} + +void dsp_hwec_exit(void) +{ + mISDN_dsp_element_unregister(dsp_hwec); +} + diff --git a/drivers/isdn/mISDN/dsp_hwec.h b/drivers/isdn/mISDN/dsp_hwec.h new file mode 100644 index 000000000000..eebe80c3f713 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_hwec.h @@ -0,0 +1,10 @@ +/* + * dsp_hwec.h + */ + +extern struct mISDN_dsp_element *dsp_hwec; +extern void dsp_hwec_enable(struct dsp *dsp, const char *arg); +extern void dsp_hwec_disable(struct dsp *dsp); +extern int dsp_hwec_init(void); +extern void dsp_hwec_exit(void); + diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c new file mode 100644 index 000000000000..850260ab57d0 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_pipeline.c @@ -0,0 +1,348 @@ +/* + * dsp_pipeline.c: pipelined audio processing + * + * Copyright (C) 2007, Nadi Sarrar + * + * Nadi Sarrar <nadi@beronet.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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + */ + +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/string.h> +#include <linux/mISDNif.h> +#include <linux/mISDNdsp.h> +#include "dsp.h" +#include "dsp_hwec.h" + +/* uncomment for debugging */ +/*#define PIPELINE_DEBUG*/ + +struct dsp_pipeline_entry { + struct mISDN_dsp_element *elem; + void *p; + struct list_head list; +}; +struct dsp_element_entry { + struct mISDN_dsp_element *elem; + struct device dev; + struct list_head list; +}; + +static LIST_HEAD(dsp_elements); + +/* sysfs */ +static struct class *elements_class; + +static ssize_t +attr_show_args(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mISDN_dsp_element *elem = dev_get_drvdata(dev); + ssize_t len = 0; + int i = 0; + + *buf = 0; + for (; i < elem->num_args; ++i) + len = sprintf(buf, "%sName: %s\n%s%s%sDescription: %s\n" + "\n", buf, + elem->args[i].name, + elem->args[i].def ? "Default: " : "", + elem->args[i].def ? elem->args[i].def : "", + elem->args[i].def ? "\n" : "", + elem->args[i].desc); + + return len; +} + +static struct device_attribute element_attributes[] = { + __ATTR(args, 0444, attr_show_args, NULL), +}; + +int mISDN_dsp_element_register(struct mISDN_dsp_element *elem) +{ + struct dsp_element_entry *entry; + int ret, i; + + if (!elem) + return -EINVAL; + + entry = kzalloc(sizeof(struct dsp_element_entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->elem = elem; + + entry->dev.class = elements_class; + dev_set_drvdata(&entry->dev, elem); + snprintf(entry->dev.bus_id, BUS_ID_SIZE, elem->name); + ret = device_register(&entry->dev); + if (ret) { + printk(KERN_ERR "%s: failed to register %s\n", + __func__, elem->name); + goto err1; + } + + for (i = 0; i < (sizeof(element_attributes) + / sizeof(struct device_attribute)); ++i) + ret = device_create_file(&entry->dev, + &element_attributes[i]); + if (ret) { + printk(KERN_ERR "%s: failed to create device file\n", + __func__); + goto err2; + } + + list_add_tail(&entry->list, &dsp_elements); + + printk(KERN_DEBUG "%s: %s registered\n", __func__, elem->name); + + return 0; + +err2: + device_unregister(&entry->dev); +err1: + kfree(entry); + return ret; +} +EXPORT_SYMBOL(mISDN_dsp_element_register); + +void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem) +{ + struct dsp_element_entry *entry, *n; + + if (!elem) + return; + + list_for_each_entry_safe(entry, n, &dsp_elements, list) + if (entry->elem == elem) { + list_del(&entry->list); + device_unregister(&entry->dev); + kfree(entry); + printk(KERN_DEBUG "%s: %s unregistered\n", + __func__, elem->name); + return; + } + printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name); +} +EXPORT_SYMBOL(mISDN_dsp_element_unregister); + +int dsp_pipeline_module_init(void) +{ + elements_class = class_create(THIS_MODULE, "dsp_pipeline"); + if (IS_ERR(elements_class)) + return PTR_ERR(elements_class); + +#ifdef PIPELINE_DEBUG + printk(KERN_DEBUG "%s: dsp pipeline module initialized\n", __func__); +#endif + + dsp_hwec_init(); + + return 0; +} + +void dsp_pipeline_module_exit(void) +{ + struct dsp_element_entry *entry, *n; + + dsp_hwec_exit(); + + class_destroy(elements_class); + + list_for_each_entry_safe(entry, n, &dsp_elements, list) { + list_del(&entry->list); + printk(KERN_WARNING "%s: element was still registered: %s\n", + __func__, entry->elem->name); + kfree(entry); + } + + printk(KERN_DEBUG "%s: dsp pipeline module exited\n", __func__); +} + +int dsp_pipeline_init(struct dsp_pipeline *pipeline) +{ + if (!pipeline) + return -EINVAL; + + INIT_LIST_HEAD(&pipeline->list); + +#ifdef PIPELINE_DEBUG + printk(KERN_DEBUG "%s: dsp pipeline ready\n", __func__); +#endif + + return 0; +} + +static inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline) +{ + struct dsp_pipeline_entry *entry, *n; + + list_for_each_entry_safe(entry, n, &pipeline->list, list) { + list_del(&entry->list); + if (entry->elem == dsp_hwec) + dsp_hwec_disable(container_of(pipeline, struct dsp, + pipeline)); + else + entry->elem->free(entry->p); + kfree(entry); + } +} + +void dsp_pipeline_destroy(struct dsp_pipeline *pipeline) +{ + + if (!pipeline) + return; + + _dsp_pipeline_destroy(pipeline); + +#ifdef PIPELINE_DEBUG + printk(KERN_DEBUG "%s: dsp pipeline destroyed\n", __func__); +#endif +} + +int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) +{ + int len, incomplete = 0, found = 0; + char *dup, *tok, *name, *args; + struct dsp_element_entry *entry, *n; + struct dsp_pipeline_entry *pipeline_entry; + struct mISDN_dsp_element *elem; + + if (!pipeline) + return -EINVAL; + + if (!list_empty(&pipeline->list)) + _dsp_pipeline_destroy(pipeline); + + if (!cfg) + return 0; + + len = strlen(cfg); + if (!len) + return 0; + + dup = kmalloc(len + 1, GFP_KERNEL); + if (!dup) + return 0; + strcpy(dup, cfg); + while ((tok = strsep(&dup, "|"))) { + if (!strlen(tok)) + continue; + name = strsep(&tok, "("); + args = strsep(&tok, ")"); + if (args && !*args) + args = 0; + + list_for_each_entry_safe(entry, n, &dsp_elements, list) + if (!strcmp(entry->elem->name, name)) { + elem = entry->elem; + + pipeline_entry = kmalloc(sizeof(struct + dsp_pipeline_entry), GFP_KERNEL); + if (!pipeline_entry) { + printk(KERN_DEBUG "%s: failed to add " + "entry to pipeline: %s (out of " + "memory)\n", __func__, elem->name); + incomplete = 1; + goto _out; + } + pipeline_entry->elem = elem; + + if (elem == dsp_hwec) { + /* This is a hack to make the hwec + available as a pipeline module */ + dsp_hwec_enable(container_of(pipeline, + struct dsp, pipeline), args); + list_add_tail(&pipeline_entry->list, + &pipeline->list); + } else { + pipeline_entry->p = elem->new(args); + if (pipeline_entry->p) { + list_add_tail(&pipeline_entry-> + list, &pipeline->list); +#ifdef PIPELINE_DEBUG + printk(KERN_DEBUG "%s: created " + "instance of %s%s%s\n", + __func__, name, args ? + " with args " : "", args ? + args : ""); +#endif + } else { + printk(KERN_DEBUG "%s: failed " + "to add entry to pipeline: " + "%s (new() returned NULL)\n", + __func__, elem->name); + kfree(pipeline_entry); + incomplete = 1; + } + } + found = 1; + break; + } + + if (found) + found = 0; + else { + printk(KERN_DEBUG "%s: element not found, skipping: " + "%s\n", __func__, name); + incomplete = 1; + } + } + +_out: + if (!list_empty(&pipeline->list)) + pipeline->inuse = 1; + else + pipeline->inuse = 0; + +#ifdef PIPELINE_DEBUG + printk(KERN_DEBUG "%s: dsp pipeline built%s: %s\n", + __func__, incomplete ? " incomplete" : "", cfg); +#endif + kfree(dup); + return 0; +} + +void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len) +{ + struct dsp_pipeline_entry *entry; + + if (!pipeline) + return; + + list_for_each_entry(entry, &pipeline->list, list) + if (entry->elem->process_tx) + entry->elem->process_tx(entry->p, data, len); +} + +void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len) +{ + struct dsp_pipeline_entry *entry; + + if (!pipeline) + return; + + list_for_each_entry_reverse(entry, &pipeline->list, list) + if (entry->elem->process_rx) + entry->elem->process_rx(entry->p, data, len); +} + + diff --git a/drivers/isdn/mISDN/dsp_tones.c b/drivers/isdn/mISDN/dsp_tones.c new file mode 100644 index 000000000000..23dd0dd21524 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_tones.c @@ -0,0 +1,551 @@ +/* + * Audio support data for ISDN4Linux. + * + * Copyright Andreas Eversberg (jolly@eversberg.eu) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/mISDNif.h> +#include <linux/mISDNdsp.h> +#include "core.h" +#include "dsp.h" + + +#define DATA_S sample_silence +#define SIZE_S (&sizeof_silence) +#define DATA_GA sample_german_all +#define SIZE_GA (&sizeof_german_all) +#define DATA_GO sample_german_old +#define SIZE_GO (&sizeof_german_old) +#define DATA_DT sample_american_dialtone +#define SIZE_DT (&sizeof_american_dialtone) +#define DATA_RI sample_american_ringing +#define SIZE_RI (&sizeof_american_ringing) +#define DATA_BU sample_american_busy +#define SIZE_BU (&sizeof_american_busy) +#define DATA_S1 sample_special1 +#define SIZE_S1 (&sizeof_special1) +#define DATA_S2 sample_special2 +#define SIZE_S2 (&sizeof_special2) +#define DATA_S3 sample_special3 +#define SIZE_S3 (&sizeof_special3) + +/***************/ +/* tones loops */ +/***************/ + +/* all tones are alaw encoded */ +/* the last sample+1 is in phase with the first sample. the error is low */ + +static u8 sample_german_all[] = { + 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, + 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, + 0xdc, 0xfc, 0x6c, + 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, + 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, + 0xdc, 0xfc, 0x6c, + 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, + 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, + 0xdc, 0xfc, 0x6c, + 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, + 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, + 0xdc, 0xfc, 0x6c, +}; +static u32 sizeof_german_all = sizeof(sample_german_all); + +static u8 sample_german_old[] = { + 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, + 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, + 0x8c, + 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, + 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, + 0x8c, + 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, + 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, + 0x8c, + 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, + 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, + 0x8c, +}; +static u32 sizeof_german_old = sizeof(sample_german_old); + +static u8 sample_american_dialtone[] = { + 0x2a, 0x18, 0x90, 0x6c, 0x4c, 0xbc, 0x4c, 0x6c, + 0x10, 0x58, 0x32, 0xb9, 0x31, 0x2d, 0x8d, 0x0d, + 0x8d, 0x2d, 0x31, 0x99, 0x0f, 0x28, 0x60, 0xf0, + 0xd0, 0x50, 0xd0, 0x30, 0x60, 0x08, 0x8e, 0x67, + 0x09, 0x19, 0x21, 0xe1, 0xd9, 0xb9, 0x29, 0x67, + 0x83, 0x02, 0xce, 0xbe, 0xee, 0x1a, 0x1b, 0xef, + 0xbf, 0xcf, 0x03, 0x82, 0x66, 0x28, 0xb8, 0xd8, + 0xe0, 0x20, 0x18, 0x08, 0x66, 0x8f, 0x09, 0x61, + 0x31, 0xd1, 0x51, 0xd1, 0xf1, 0x61, 0x29, 0x0e, + 0x98, 0x30, 0x2c, 0x8c, 0x0c, 0x8c, 0x2c, 0x30, + 0xb8, 0x33, 0x59, 0x11, 0x6d, 0x4d, 0xbd, 0x4d, + 0x6d, 0x91, 0x19, +}; +static u32 sizeof_american_dialtone = sizeof(sample_american_dialtone); + +static u8 sample_american_ringing[] = { + 0x2a, 0xe0, 0xac, 0x0c, 0xbc, 0x4c, 0x8c, 0x90, + 0x48, 0xc7, 0xc1, 0xed, 0xcd, 0x4d, 0xcd, 0xed, + 0xc1, 0xb7, 0x08, 0x30, 0xec, 0xcc, 0xcc, 0x8c, + 0x10, 0x58, 0x1a, 0x99, 0x71, 0xed, 0x8d, 0x8d, + 0x2d, 0x41, 0x89, 0x9e, 0x20, 0x70, 0x2c, 0xec, + 0x2c, 0x70, 0x20, 0x86, 0x77, 0xe1, 0x31, 0x11, + 0xd1, 0xf1, 0x81, 0x09, 0xa3, 0x56, 0x58, 0x00, + 0x40, 0xc0, 0x60, 0x38, 0x46, 0x43, 0x57, 0x39, + 0xd9, 0x59, 0x99, 0xc9, 0x77, 0x2f, 0x2e, 0xc6, + 0xd6, 0x28, 0xd6, 0x36, 0x26, 0x2e, 0x8a, 0xa3, + 0x43, 0x63, 0x4b, 0x4a, 0x62, 0x42, 0xa2, 0x8b, + 0x2f, 0x27, 0x37, 0xd7, 0x29, 0xd7, 0xc7, 0x2f, + 0x2e, 0x76, 0xc8, 0x98, 0x58, 0xd8, 0x38, 0x56, + 0x42, 0x47, 0x39, 0x61, 0xc1, 0x41, 0x01, 0x59, + 0x57, 0xa2, 0x08, 0x80, 0xf0, 0xd0, 0x10, 0x30, + 0xe0, 0x76, 0x87, 0x21, 0x71, 0x2d, 0xed, 0x2d, + 0x71, 0x21, 0x9f, 0x88, 0x40, 0x2c, 0x8c, 0x8c, + 0xec, 0x70, 0x98, 0x1b, 0x59, 0x11, 0x8d, 0xcd, + 0xcd, 0xed, 0x31, 0x09, 0xb6, 0xc0, 0xec, 0xcc, + 0x4c, 0xcc, 0xec, 0xc0, 0xc6, 0x49, 0x91, 0x8d, + 0x4d, 0xbd, 0x0d, 0xad, 0xe1, +}; +static u32 sizeof_american_ringing = sizeof(sample_american_ringing); + +static u8 sample_american_busy[] = { + 0x2a, 0x00, 0x6c, 0x4c, 0x4c, 0x6c, 0xb0, 0x66, + 0x99, 0x11, 0x6d, 0x8d, 0x2d, 0x41, 0xd7, 0x96, + 0x60, 0xf0, 0x70, 0x40, 0x58, 0xf6, 0x53, 0x57, + 0x09, 0x89, 0xd7, 0x5f, 0xe3, 0x2a, 0xe3, 0x5f, + 0xd7, 0x89, 0x09, 0x57, 0x53, 0xf6, 0x58, 0x40, + 0x70, 0xf0, 0x60, 0x96, 0xd7, 0x41, 0x2d, 0x8d, + 0x6d, 0x11, 0x99, 0x66, 0xb0, 0x6c, 0x4c, 0x4c, + 0x6c, 0x00, 0x2a, 0x01, 0x6d, 0x4d, 0x4d, 0x6d, + 0xb1, 0x67, 0x98, 0x10, 0x6c, 0x8c, 0x2c, 0x40, + 0xd6, 0x97, 0x61, 0xf1, 0x71, 0x41, 0x59, 0xf7, + 0x52, 0x56, 0x08, 0x88, 0xd6, 0x5e, 0xe2, 0x2a, + 0xe2, 0x5e, 0xd6, 0x88, 0x08, 0x56, 0x52, 0xf7, + 0x59, 0x41, 0x71, 0xf1, 0x61, 0x97, 0xd6, 0x40, + 0x2c, 0x8c, 0x6c, 0x10, 0x98, 0x67, 0xb1, 0x6d, + 0x4d, 0x4d, 0x6d, 0x01, +}; +static u32 sizeof_american_busy = sizeof(sample_american_busy); + +static u8 sample_special1[] = { + 0x2a, 0x2c, 0xbc, 0x6c, 0xd6, 0x71, 0xbd, 0x0d, + 0xd9, 0x80, 0xcc, 0x4c, 0x40, 0x39, 0x0d, 0xbd, + 0x11, 0x86, 0xec, 0xbc, 0xec, 0x0e, 0x51, 0xbd, + 0x8d, 0x89, 0x30, 0x4c, 0xcc, 0xe0, 0xe1, 0xcd, + 0x4d, 0x31, 0x88, 0x8c, 0xbc, 0x50, 0x0f, 0xed, + 0xbd, 0xed, 0x87, 0x10, 0xbc, 0x0c, 0x38, 0x41, + 0x4d, 0xcd, 0x81, 0xd8, 0x0c, 0xbc, 0x70, 0xd7, + 0x6d, 0xbd, 0x2d, +}; +static u32 sizeof_special1 = sizeof(sample_special1); + +static u8 sample_special2[] = { + 0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc, + 0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d, + 0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6, + 0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0, + 0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd, + 0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc, + 0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d, + 0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6, + 0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0, + 0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd, +}; +static u32 sizeof_special2 = sizeof(sample_special2); + +static u8 sample_special3[] = { + 0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1, + 0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c, + 0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc, + 0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7, + 0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd, + 0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1, + 0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c, + 0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc, + 0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7, + 0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd, +}; +static u32 sizeof_special3 = sizeof(sample_special3); + +static u8 sample_silence[] = { + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, +}; +static u32 sizeof_silence = sizeof(sample_silence); + +struct tones_samples { + u32 *len; + u8 *data; +}; +static struct +tones_samples samples[] = { + {&sizeof_german_all, sample_german_all}, + {&sizeof_german_old, sample_german_old}, + {&sizeof_american_dialtone, sample_american_dialtone}, + {&sizeof_american_ringing, sample_american_ringing}, + {&sizeof_american_busy, sample_american_busy}, + {&sizeof_special1, sample_special1}, + {&sizeof_special2, sample_special2}, + {&sizeof_special3, sample_special3}, + {NULL, NULL}, +}; + +/*********************************** + * generate ulaw from alaw samples * + ***********************************/ + +void +dsp_audio_generate_ulaw_samples(void) +{ + int i, j; + + i = 0; + while (samples[i].len) { + j = 0; + while (j < (*samples[i].len)) { + samples[i].data[j] = + dsp_audio_alaw_to_ulaw[samples[i].data[j]]; + j++; + } + i++; + } +} + + +/**************************** + * tone sequence definition * + ****************************/ + +struct pattern { + int tone; + u8 *data[10]; + u32 *siz[10]; + u32 seq[10]; +} pattern[] = { + {TONE_GERMAN_DIALTONE, + {DATA_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1900, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_OLDDIALTONE, + {DATA_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1998, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_AMERICAN_DIALTONE, + {DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_DIALPBX, + {DATA_GA, DATA_S, DATA_GA, DATA_S, DATA_GA, DATA_S, 0, 0, 0, 0}, + {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0}, + {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, + + {TONE_GERMAN_OLDDIALPBX, + {DATA_GO, DATA_S, DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0}, + {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0}, + {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, + + {TONE_AMERICAN_DIALPBX, + {DATA_DT, DATA_S, DATA_DT, DATA_S, DATA_DT, DATA_S, 0, 0, 0, 0}, + {SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, 0, 0, 0, 0}, + {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, + + {TONE_GERMAN_RINGING, + {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_OLDRINGING, + {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {8000, 40000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_AMERICAN_RINGING, + {DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_RINGPBX, + {DATA_GA, DATA_S, DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0}, + {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0}, + {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_OLDRINGPBX, + {DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0}, + {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0}, + {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, + + {TONE_AMERICAN_RINGPBX, + {DATA_RI, DATA_S, DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0}, + {SIZE_RI, SIZE_S, SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0}, + {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_BUSY, + {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_OLDBUSY, + {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_AMERICAN_BUSY, + {DATA_BU, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_BU, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_HANGUP, + {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_OLDHANGUP, + {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_AMERICAN_HANGUP, + {DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_SPECIAL_INFO, + {DATA_S1, DATA_S2, DATA_S3, DATA_S, 0, 0, 0, 0, 0, 0}, + {SIZE_S1, SIZE_S2, SIZE_S3, SIZE_S, 0, 0, 0, 0, 0, 0}, + {2666, 2666, 2666, 8002, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_GASSENBESETZT, + {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {2000, 2000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_AUFSCHALTTON, + {DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0}, + {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0}, + {1000, 5000, 1000, 17000, 0, 0, 0, 0, 0, 0} }, + + {0, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, +}; + +/****************** + * copy tone data * + ******************/ + +/* an sk_buff is generated from the number of samples needed. + * the count will be changed and may begin from 0 each pattern period. + * the clue is to precalculate the pointers and legths to use only one + * memcpy per function call, or two memcpy if the tone sequence changes. + * + * pattern - the type of the pattern + * count - the sample from the beginning of the pattern (phase) + * len - the number of bytes + * + * return - the sk_buff with the sample + * + * if tones has finished (e.g. knocking tone), dsp->tones is turned off + */ +void dsp_tone_copy(struct dsp *dsp, u8 *data, int len) +{ + int index, count, start, num; + struct pattern *pat; + struct dsp_tone *tone = &dsp->tone; + + /* if we have no tone, we copy silence */ + if (!tone->tone) { + memset(data, dsp_silence, len); + return; + } + + /* process pattern */ + pat = (struct pattern *)tone->pattern; + /* points to the current pattern */ + index = tone->index; /* gives current sequence index */ + count = tone->count; /* gives current sample */ + + /* copy sample */ + while (len) { + /* find sample to start with */ + while (42) { + /* warp arround */ + if (!pat->seq[index]) { + count = 0; + index = 0; + } + /* check if we are currently playing this tone */ + if (count < pat->seq[index]) + break; + if (dsp_debug & DEBUG_DSP_TONE) + printk(KERN_DEBUG "%s: reaching next sequence " + "(index=%d)\n", __func__, index); + count -= pat->seq[index]; + index++; + } + /* calculate start and number of samples */ + start = count % (*(pat->siz[index])); + num = len; + if (num+count > pat->seq[index]) + num = pat->seq[index] - count; + if (num+start > (*(pat->siz[index]))) + num = (*(pat->siz[index])) - start; + /* copy memory */ + memcpy(data, pat->data[index]+start, num); + /* reduce length */ + data += num; + count += num; + len -= num; + } + tone->index = index; + tone->count = count; + + /* return sk_buff */ + return; +} + + +/******************************* + * send HW message to hfc card * + *******************************/ + +static void +dsp_tone_hw_message(struct dsp *dsp, u8 *sample, int len) +{ + struct sk_buff *nskb; + + /* unlocking is not required, because we don't expect a response */ + nskb = _alloc_mISDN_skb(PH_CONTROL_REQ, + (len)?HFC_SPL_LOOP_ON:HFC_SPL_LOOP_OFF, len, sample, + GFP_ATOMIC); + if (nskb) { + if (dsp->ch.peer) { + if (dsp->ch.recv(dsp->ch.peer, nskb)) + dev_kfree_skb(nskb); + } else + dev_kfree_skb(nskb); + } +} + + +/***************** + * timer expires * + *****************/ +void +dsp_tone_timeout(void *arg) +{ + struct dsp *dsp = arg; + struct dsp_tone *tone = &dsp->tone; + struct pattern *pat = (struct pattern *)tone->pattern; + int index = tone->index; + + if (!tone->tone) + return; + + index++; + if (!pat->seq[index]) + index = 0; + tone->index = index; + + /* set next tone */ + if (pat->data[index] == DATA_S) + dsp_tone_hw_message(dsp, 0, 0); + else + dsp_tone_hw_message(dsp, pat->data[index], *(pat->siz[index])); + /* set timer */ + init_timer(&tone->tl); + tone->tl.expires = jiffies + (pat->seq[index] * HZ) / 8000; + add_timer(&tone->tl); +} + + +/******************** + * set/release tone * + ********************/ + +/* + * tones are relaized by streaming or by special loop commands if supported + * by hardware. when hardware is used, the patterns will be controlled by + * timers. + */ +int +dsp_tone(struct dsp *dsp, int tone) +{ + struct pattern *pat; + int i; + struct dsp_tone *tonet = &dsp->tone; + + tonet->software = 0; + tonet->hardware = 0; + + /* we turn off the tone */ + if (!tone) { + if (dsp->features.hfc_loops) + if (timer_pending(&tonet->tl)) + del_timer(&tonet->tl); + if (dsp->features.hfc_loops) + dsp_tone_hw_message(dsp, NULL, 0); + tonet->tone = 0; + return 0; + } + + pat = NULL; + i = 0; + while (pattern[i].tone) { + if (pattern[i].tone == tone) { + pat = &pattern[i]; + break; + } + i++; + } + if (!pat) { + printk(KERN_WARNING "dsp: given tone 0x%x is invalid\n", tone); + return -EINVAL; + } + if (dsp_debug & DEBUG_DSP_TONE) + printk(KERN_DEBUG "%s: now starting tone %d (index=%d)\n", + __func__, tone, 0); + tonet->tone = tone; + tonet->pattern = pat; + tonet->index = 0; + tonet->count = 0; + + if (dsp->features.hfc_loops) { + tonet->hardware = 1; + /* set first tone */ + dsp_tone_hw_message(dsp, pat->data[0], *(pat->siz[0])); + /* set timer */ + if (timer_pending(&tonet->tl)) + del_timer(&tonet->tl); + init_timer(&tonet->tl); + tonet->tl.expires = jiffies + (pat->seq[0] * HZ) / 8000; + add_timer(&tonet->tl); + } else { + tonet->software = 1; + } + + return 0; +} + + + + + diff --git a/drivers/isdn/mISDN/fsm.c b/drivers/isdn/mISDN/fsm.c new file mode 100644 index 000000000000..b5d6553f2dc8 --- /dev/null +++ b/drivers/isdn/mISDN/fsm.c @@ -0,0 +1,183 @@ +/* + * finite state machine implementation + * + * Author Karsten Keil <kkeil@novell.com> + * + * Thanks to Jan den Ouden + * Fritz Elfert + * Copyright 2008 by Karsten Keil <kkeil@novell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/string.h> +#include "fsm.h" + +#define FSM_TIMER_DEBUG 0 + +void +mISDN_FsmNew(struct Fsm *fsm, + struct FsmNode *fnlist, int fncount) +{ + int i; + + fsm->jumpmatrix = kzalloc(sizeof(FSMFNPTR) * fsm->state_count * + fsm->event_count, GFP_KERNEL); + + for (i = 0; i < fncount; i++) + if ((fnlist[i].state >= fsm->state_count) || + (fnlist[i].event >= fsm->event_count)) { + printk(KERN_ERR + "mISDN_FsmNew Error: %d st(%ld/%ld) ev(%ld/%ld)\n", + i, (long)fnlist[i].state, (long)fsm->state_count, + (long)fnlist[i].event, (long)fsm->event_count); + } else + fsm->jumpmatrix[fsm->state_count * fnlist[i].event + + fnlist[i].state] = (FSMFNPTR) fnlist[i].routine; +} +EXPORT_SYMBOL(mISDN_FsmNew); + +void +mISDN_FsmFree(struct Fsm *fsm) +{ + kfree((void *) fsm->jumpmatrix); +} +EXPORT_SYMBOL(mISDN_FsmFree); + +int +mISDN_FsmEvent(struct FsmInst *fi, int event, void *arg) +{ + FSMFNPTR r; + + if ((fi->state >= fi->fsm->state_count) || + (event >= fi->fsm->event_count)) { + printk(KERN_ERR + "mISDN_FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n", + (long)fi->state, (long)fi->fsm->state_count, event, + (long)fi->fsm->event_count); + return 1; + } + r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state]; + if (r) { + if (fi->debug) + fi->printdebug(fi, "State %s Event %s", + fi->fsm->strState[fi->state], + fi->fsm->strEvent[event]); + r(fi, event, arg); + return 0; + } else { + if (fi->debug) + fi->printdebug(fi, "State %s Event %s no action", + fi->fsm->strState[fi->state], + fi->fsm->strEvent[event]); + return 1; + } +} +EXPORT_SYMBOL(mISDN_FsmEvent); + +void +mISDN_FsmChangeState(struct FsmInst *fi, int newstate) +{ + fi->state = newstate; + if (fi->debug) + fi->printdebug(fi, "ChangeState %s", + fi->fsm->strState[newstate]); +} +EXPORT_SYMBOL(mISDN_FsmChangeState); + +static void +FsmExpireTimer(struct FsmTimer *ft) +{ +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft); +#endif + mISDN_FsmEvent(ft->fi, ft->event, ft->arg); +} + +void +mISDN_FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft) +{ + ft->fi = fi; + ft->tl.function = (void *) FsmExpireTimer; + ft->tl.data = (long) ft; +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "mISDN_FsmInitTimer %lx", (long) ft); +#endif + init_timer(&ft->tl); +} +EXPORT_SYMBOL(mISDN_FsmInitTimer); + +void +mISDN_FsmDelTimer(struct FsmTimer *ft, int where) +{ +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "mISDN_FsmDelTimer %lx %d", + (long) ft, where); +#endif + del_timer(&ft->tl); +} +EXPORT_SYMBOL(mISDN_FsmDelTimer); + +int +mISDN_FsmAddTimer(struct FsmTimer *ft, + int millisec, int event, void *arg, int where) +{ + +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "mISDN_FsmAddTimer %lx %d %d", + (long) ft, millisec, where); +#endif + + if (timer_pending(&ft->tl)) { + if (ft->fi->debug) { + printk(KERN_WARNING + "mISDN_FsmAddTimer: timer already active!\n"); + ft->fi->printdebug(ft->fi, + "mISDN_FsmAddTimer already active!"); + } + return -1; + } + init_timer(&ft->tl); + ft->event = event; + ft->arg = arg; + ft->tl.expires = jiffies + (millisec * HZ) / 1000; + add_timer(&ft->tl); + return 0; +} +EXPORT_SYMBOL(mISDN_FsmAddTimer); + +void +mISDN_FsmRestartTimer(struct FsmTimer *ft, + int millisec, int event, void *arg, int where) +{ + +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "mISDN_FsmRestartTimer %lx %d %d", + (long) ft, millisec, where); +#endif + + if (timer_pending(&ft->tl)) + del_timer(&ft->tl); + init_timer(&ft->tl); + ft->event = event; + ft->arg = arg; + ft->tl.expires = jiffies + (millisec * HZ) / 1000; + add_timer(&ft->tl); +} +EXPORT_SYMBOL(mISDN_FsmRestartTimer); diff --git a/drivers/isdn/mISDN/fsm.h b/drivers/isdn/mISDN/fsm.h new file mode 100644 index 000000000000..928f5be192c1 --- /dev/null +++ b/drivers/isdn/mISDN/fsm.h @@ -0,0 +1,67 @@ +/* + * + * Author Karsten Keil <kkeil@novell.com> + * + * Thanks to Jan den Ouden + * Fritz Elfert + * Copyright 2008 by Karsten Keil <kkeil@novell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef _MISDN_FSM_H +#define _MISDN_FSM_H + +#include <linux/timer.h> + +/* Statemachine */ + +struct FsmInst; + +typedef void (*FSMFNPTR)(struct FsmInst *, int, void *); + +struct Fsm { + FSMFNPTR *jumpmatrix; + int state_count, event_count; + char **strEvent, **strState; +}; + +struct FsmInst { + struct Fsm *fsm; + int state; + int debug; + void *userdata; + int userint; + void (*printdebug) (struct FsmInst *, char *, ...); +}; + +struct FsmNode { + int state, event; + void (*routine) (struct FsmInst *, int, void *); +}; + +struct FsmTimer { + struct FsmInst *fi; + struct timer_list tl; + int event; + void *arg; +}; + +extern void mISDN_FsmNew(struct Fsm *, struct FsmNode *, int); +extern void mISDN_FsmFree(struct Fsm *); +extern int mISDN_FsmEvent(struct FsmInst *, int , void *); +extern void mISDN_FsmChangeState(struct FsmInst *, int); +extern void mISDN_FsmInitTimer(struct FsmInst *, struct FsmTimer *); +extern int mISDN_FsmAddTimer(struct FsmTimer *, int, int, void *, int); +extern void mISDN_FsmRestartTimer(struct FsmTimer *, int, int, void *, int); +extern void mISDN_FsmDelTimer(struct FsmTimer *, int); + +#endif diff --git a/drivers/isdn/mISDN/hwchannel.c b/drivers/isdn/mISDN/hwchannel.c new file mode 100644 index 000000000000..2596fba4e614 --- /dev/null +++ b/drivers/isdn/mISDN/hwchannel.c @@ -0,0 +1,365 @@ +/* + * + * Author Karsten Keil <kkeil@novell.com> + * + * Copyright 2008 by Karsten Keil <kkeil@novell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/mISDNhw.h> + +static void +dchannel_bh(struct work_struct *ws) +{ + struct dchannel *dch = container_of(ws, struct dchannel, workq); + struct sk_buff *skb; + int err; + + if (test_and_clear_bit(FLG_RECVQUEUE, &dch->Flags)) { + while ((skb = skb_dequeue(&dch->rqueue))) { + if (likely(dch->dev.D.peer)) { + err = dch->dev.D.recv(dch->dev.D.peer, skb); + if (err) + dev_kfree_skb(skb); + } else + dev_kfree_skb(skb); + } + } + if (test_and_clear_bit(FLG_PHCHANGE, &dch->Flags)) { + if (dch->phfunc) + dch->phfunc(dch); + } +} + +static void +bchannel_bh(struct work_struct *ws) +{ + struct bchannel *bch = container_of(ws, struct bchannel, workq); + struct sk_buff *skb; + int err; + + if (test_and_clear_bit(FLG_RECVQUEUE, &bch->Flags)) { + while ((skb = skb_dequeue(&bch->rqueue))) { + if (bch->rcount >= 64) + printk(KERN_WARNING "B-channel %p receive " + "queue if full, but empties...\n", bch); + bch->rcount--; + if (likely(bch->ch.peer)) { + err = bch->ch.recv(bch->ch.peer, skb); + if (err) + dev_kfree_skb(skb); + } else + dev_kfree_skb(skb); + } + } +} + +int +mISDN_initdchannel(struct dchannel *ch, int maxlen, void *phf) +{ + test_and_set_bit(FLG_HDLC, &ch->Flags); + ch->maxlen = maxlen; + ch->hw = NULL; + ch->rx_skb = NULL; + ch->tx_skb = NULL; + ch->tx_idx = 0; + ch->phfunc = phf; + skb_queue_head_init(&ch->squeue); + skb_queue_head_init(&ch->rqueue); + INIT_LIST_HEAD(&ch->dev.bchannels); + INIT_WORK(&ch->workq, dchannel_bh); + return 0; +} +EXPORT_SYMBOL(mISDN_initdchannel); + +int +mISDN_initbchannel(struct bchannel *ch, int maxlen) +{ + ch->Flags = 0; + ch->maxlen = maxlen; + ch->hw = NULL; + ch->rx_skb = NULL; + ch->tx_skb = NULL; + ch->tx_idx = 0; + skb_queue_head_init(&ch->rqueue); + ch->rcount = 0; + ch->next_skb = NULL; + INIT_WORK(&ch->workq, bchannel_bh); + return 0; +} +EXPORT_SYMBOL(mISDN_initbchannel); + +int +mISDN_freedchannel(struct dchannel *ch) +{ + if (ch->tx_skb) { + dev_kfree_skb(ch->tx_skb); + ch->tx_skb = NULL; + } + if (ch->rx_skb) { + dev_kfree_skb(ch->rx_skb); + ch->rx_skb = NULL; + } + skb_queue_purge(&ch->squeue); + skb_queue_purge(&ch->rqueue); + flush_scheduled_work(); + return 0; +} +EXPORT_SYMBOL(mISDN_freedchannel); + +int +mISDN_freebchannel(struct bchannel *ch) +{ + if (ch->tx_skb) { + dev_kfree_skb(ch->tx_skb); + ch->tx_skb = NULL; + } + if (ch->rx_skb) { + dev_kfree_skb(ch->rx_skb); + ch->rx_skb = NULL; + } + if (ch->next_skb) { + dev_kfree_skb(ch->next_skb); + ch->next_skb = NULL; + } + skb_queue_purge(&ch->rqueue); + ch->rcount = 0; + flush_scheduled_work(); + return 0; +} +EXPORT_SYMBOL(mISDN_freebchannel); + +static inline u_int +get_sapi_tei(u_char *p) +{ + u_int sapi, tei; + + sapi = *p >> 2; + tei = p[1] >> 1; + return sapi | (tei << 8); +} + +void +recv_Dchannel(struct dchannel *dch) +{ + struct mISDNhead *hh; + + if (dch->rx_skb->len < 2) { /* at least 2 for sapi / tei */ + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + return; + } + hh = mISDN_HEAD_P(dch->rx_skb); + hh->prim = PH_DATA_IND; + hh->id = get_sapi_tei(dch->rx_skb->data); + skb_queue_tail(&dch->rqueue, dch->rx_skb); + dch->rx_skb = NULL; + schedule_event(dch, FLG_RECVQUEUE); +} +EXPORT_SYMBOL(recv_Dchannel); + +void +recv_Bchannel(struct bchannel *bch) +{ + struct mISDNhead *hh; + + hh = mISDN_HEAD_P(bch->rx_skb); + hh->prim = PH_DATA_IND; + hh->id = MISDN_ID_ANY; + if (bch->rcount >= 64) { + dev_kfree_skb(bch->rx_skb); + bch->rx_skb = NULL; + return; + } + bch->rcount++; + skb_queue_tail(&bch->rqueue, bch->rx_skb); + bch->rx_skb = NULL; + schedule_event(bch, FLG_RECVQUEUE); +} +EXPORT_SYMBOL(recv_Bchannel); + +void +recv_Dchannel_skb(struct dchannel *dch, struct sk_buff *skb) +{ + skb_queue_tail(&dch->rqueue, skb); + schedule_event(dch, FLG_RECVQUEUE); +} +EXPORT_SYMBOL(recv_Dchannel_skb); + +void +recv_Bchannel_skb(struct bchannel *bch, struct sk_buff *skb) +{ + if (bch->rcount >= 64) { + dev_kfree_skb(skb); + return; + } + bch->rcount++; + skb_queue_tail(&bch->rqueue, skb); + schedule_event(bch, FLG_RECVQUEUE); +} +EXPORT_SYMBOL(recv_Bchannel_skb); + +static void +confirm_Dsend(struct dchannel *dch) +{ + struct sk_buff *skb; + + skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(dch->tx_skb), + 0, NULL, GFP_ATOMIC); + if (!skb) { + printk(KERN_ERR "%s: no skb id %x\n", __func__, + mISDN_HEAD_ID(dch->tx_skb)); + return; + } + skb_queue_tail(&dch->rqueue, skb); + schedule_event(dch, FLG_RECVQUEUE); +} + +int +get_next_dframe(struct dchannel *dch) +{ + dch->tx_idx = 0; + dch->tx_skb = skb_dequeue(&dch->squeue); + if (dch->tx_skb) { + confirm_Dsend(dch); + return 1; + } + dch->tx_skb = NULL; + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + return 0; +} +EXPORT_SYMBOL(get_next_dframe); + +void +confirm_Bsend(struct bchannel *bch) +{ + struct sk_buff *skb; + + if (bch->rcount >= 64) + return; + skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(bch->tx_skb), + 0, NULL, GFP_ATOMIC); + if (!skb) { + printk(KERN_ERR "%s: no skb id %x\n", __func__, + mISDN_HEAD_ID(bch->tx_skb)); + return; + } + bch->rcount++; + skb_queue_tail(&bch->rqueue, skb); + schedule_event(bch, FLG_RECVQUEUE); +} +EXPORT_SYMBOL(confirm_Bsend); + +int +get_next_bframe(struct bchannel *bch) +{ + bch->tx_idx = 0; + if (test_bit(FLG_TX_NEXT, &bch->Flags)) { + bch->tx_skb = bch->next_skb; + if (bch->tx_skb) { + bch->next_skb = NULL; + test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); + if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) + confirm_Bsend(bch); /* not for transparent */ + return 1; + } else { + test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); + printk(KERN_WARNING "B TX_NEXT without skb\n"); + } + } + bch->tx_skb = NULL; + test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); + return 0; +} +EXPORT_SYMBOL(get_next_bframe); + +void +queue_ch_frame(struct mISDNchannel *ch, u_int pr, int id, struct sk_buff *skb) +{ + struct mISDNhead *hh; + + if (!skb) { + _queue_data(ch, pr, id, 0, NULL, GFP_ATOMIC); + } else { + if (ch->peer) { + hh = mISDN_HEAD_P(skb); + hh->prim = pr; + hh->id = id; + if (!ch->recv(ch->peer, skb)) + return; + } + dev_kfree_skb(skb); + } +} +EXPORT_SYMBOL(queue_ch_frame); + +int +dchannel_senddata(struct dchannel *ch, struct sk_buff *skb) +{ + /* check oversize */ + if (skb->len <= 0) { + printk(KERN_WARNING "%s: skb too small\n", __func__); + return -EINVAL; + } + if (skb->len > ch->maxlen) { + printk(KERN_WARNING "%s: skb too large(%d/%d)\n", + __func__, skb->len, ch->maxlen); + return -EINVAL; + } + /* HW lock must be obtained */ + if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) { + skb_queue_tail(&ch->squeue, skb); + return 0; + } else { + /* write to fifo */ + ch->tx_skb = skb; + ch->tx_idx = 0; + return 1; + } +} +EXPORT_SYMBOL(dchannel_senddata); + +int +bchannel_senddata(struct bchannel *ch, struct sk_buff *skb) +{ + + /* check oversize */ + if (skb->len <= 0) { + printk(KERN_WARNING "%s: skb too small\n", __func__); + return -EINVAL; + } + if (skb->len > ch->maxlen) { + printk(KERN_WARNING "%s: skb too large(%d/%d)\n", + __func__, skb->len, ch->maxlen); + return -EINVAL; + } + /* HW lock must be obtained */ + /* check for pending next_skb */ + if (ch->next_skb) { + printk(KERN_WARNING + "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n", + __func__, skb->len, ch->next_skb->len); + return -EBUSY; + } + if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) { + test_and_set_bit(FLG_TX_NEXT, &ch->Flags); + ch->next_skb = skb; + return 0; + } else { + /* write to fifo */ + ch->tx_skb = skb; + ch->tx_idx = 0; + return 1; + } +} +EXPORT_SYMBOL(bchannel_senddata); diff --git a/drivers/isdn/mISDN/l1oip.h b/drivers/isdn/mISDN/l1oip.h new file mode 100644 index 000000000000..a23d575449f6 --- /dev/null +++ b/drivers/isdn/mISDN/l1oip.h @@ -0,0 +1,91 @@ +/* + * see notice in l1oip.c + */ + +/* debugging */ +#define DEBUG_L1OIP_INIT 0x00010000 +#define DEBUG_L1OIP_SOCKET 0x00020000 +#define DEBUG_L1OIP_MGR 0x00040000 +#define DEBUG_L1OIP_MSG 0x00080000 + +/* enable to disorder received bchannels by sequence 2143658798... */ +/* +#define REORDER_DEBUG +*/ + +/* frames */ +#define L1OIP_MAX_LEN 2048 /* max packet size form l2 */ +#define L1OIP_MAX_PERFRAME 1400 /* max data size in one frame */ + + +/* timers */ +#define L1OIP_KEEPALIVE 15 +#define L1OIP_TIMEOUT 65 + + +/* socket */ +#define L1OIP_DEFAULTPORT 931 + + +/* channel structure */ +struct l1oip_chan { + struct dchannel *dch; + struct bchannel *bch; + u32 tx_counter; /* counts xmit bytes/packets */ + u32 rx_counter; /* counts recv bytes/packets */ + u32 codecstate; /* used by codec to save data */ +#ifdef REORDER_DEBUG + int disorder_flag; + struct sk_buff *disorder_skb; + u32 disorder_cnt; +#endif +}; + + +/* card structure */ +struct l1oip { + struct list_head list; + + /* card */ + int registered; /* if registered with mISDN */ + char name[MISDN_MAX_IDLEN]; + int idx; /* card index */ + int pri; /* 1=pri, 0=bri */ + int d_idx; /* current dchannel number */ + int b_num; /* number of bchannels */ + u32 id; /* id of connection */ + int ondemand; /* if transmis. is on demand */ + int bundle; /* bundle channels in one frm */ + int codec; /* codec to use for transmis. */ + int limit; /* limit number of bchannels */ + + /* timer */ + struct timer_list keep_tl; + struct timer_list timeout_tl; + int timeout_on; + struct work_struct workq; + + /* socket */ + struct socket *socket; /* if set, socket is created */ + struct completion socket_complete;/* completion of sock thread */ + struct task_struct *socket_thread; + spinlock_t socket_lock; /* access sock outside thread */ + u32 remoteip; /* if all set, ip is assigned */ + u16 localport; /* must always be set */ + u16 remoteport; /* must always be set */ + struct sockaddr_in sin_local; /* local socket name */ + struct sockaddr_in sin_remote; /* remote socket name */ + struct msghdr sendmsg; /* ip message to send */ + struct iovec sendiov; /* iov for message */ + + /* frame */ + struct l1oip_chan chan[128]; /* channel instances */ +}; + +extern int l1oip_law_to_4bit(u8 *data, int len, u8 *result, u32 *state); +extern int l1oip_4bit_to_law(u8 *data, int len, u8 *result); +extern int l1oip_alaw_to_ulaw(u8 *data, int len, u8 *result); +extern int l1oip_ulaw_to_alaw(u8 *data, int len, u8 *result); +extern void l1oip_4bit_free(void); +extern int l1oip_4bit_alloc(int ulaw); + diff --git a/drivers/isdn/mISDN/l1oip_codec.c b/drivers/isdn/mISDN/l1oip_codec.c new file mode 100644 index 000000000000..a2dc4570ef43 --- /dev/null +++ b/drivers/isdn/mISDN/l1oip_codec.c @@ -0,0 +1,374 @@ +/* + + * l1oip_codec.c generic codec using lookup table + * -> conversion from a-Law to u-Law + * -> conversion from u-Law to a-Law + * -> compression by reducing the number of sample resolution to 4 + * + * NOTE: It is not compatible with any standard codec like ADPCM. + * + * Author Andreas Eversberg (jolly@eversberg.eu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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. + + */ + +/* + +How the codec works: +-------------------- + +The volume is increased to increase the dynamic range of the audio signal. +Each sample is converted to a-LAW with only 16 steps of level resolution. +A pair of two samples are stored in one byte. + +The first byte is stored in the upper bits, the second byte is stored in the +lower bits. + +To speed up compression and decompression, two lookup tables are formed: + +- 16 bits index for two samples (law encoded) with 8 bit compressed result. +- 8 bits index for one compressed data with 16 bits decompressed result. + +NOTE: The bytes are handled as they are law-encoded. + +*/ + +#include <linux/vmalloc.h> +#include <linux/mISDNif.h> +#include "core.h" + +/* definitions of codec. don't use calculations, code may run slower. */ + +static u8 *table_com; +static u16 *table_dec; + + +/* alaw -> ulaw */ +static u8 alaw_to_ulaw[256] = +{ + 0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49, + 0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57, + 0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41, + 0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f, + 0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d, + 0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b, + 0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45, + 0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53, + 0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47, + 0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55, + 0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f, + 0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e, + 0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b, + 0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59, + 0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43, + 0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51, + 0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a, + 0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58, + 0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42, + 0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50, + 0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e, + 0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c, + 0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46, + 0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54, + 0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48, + 0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56, + 0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40, + 0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f, + 0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c, + 0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a, + 0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44, + 0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52 +}; + +/* ulaw -> alaw */ +static u8 ulaw_to_alaw[256] = +{ + 0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35, + 0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25, + 0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d, + 0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d, + 0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31, + 0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21, + 0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9, + 0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9, + 0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47, + 0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf, + 0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f, + 0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33, + 0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23, + 0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b, + 0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b, + 0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b, + 0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34, + 0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24, + 0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c, + 0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c, + 0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30, + 0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20, + 0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8, + 0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8, + 0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46, + 0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde, + 0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e, + 0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32, + 0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22, + 0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a, + 0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a, + 0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a +}; + +/* alaw -> 4bit compression */ +static u8 alaw_to_4bit[256] = { + 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, + 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, + 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, + 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, + 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, + 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, + 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, + 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, + 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, + 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, + 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0d, 0x02, + 0x0e, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, + 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, + 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, + 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, + 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, + 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, + 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, + 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, + 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, + 0x0e, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, + 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x01, 0x0a, 0x05, + 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, + 0x0d, 0x02, 0x09, 0x07, 0x0f, 0x00, 0x0b, 0x04, + 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, + 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, + 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, + 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, + 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, + 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04, + 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03, + 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04, +}; + +/* 4bit -> alaw decompression */ +static u8 _4bit_to_alaw[16] = { + 0x5d, 0x51, 0xd9, 0xd7, 0x5f, 0x53, 0xa3, 0x4b, + 0x2a, 0x3a, 0x22, 0x2e, 0x26, 0x56, 0x20, 0x2c, +}; + +/* ulaw -> 4bit compression */ +static u8 ulaw_to_4bit[256] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, +}; + +/* 4bit -> ulaw decompression */ +static u8 _4bit_to_ulaw[16] = { + 0x11, 0x21, 0x31, 0x40, 0x4e, 0x5c, 0x68, 0x71, + 0xfe, 0xef, 0xe7, 0xdb, 0xcd, 0xbf, 0xaf, 0x9f, +}; + + +/* + * Compresses data to the result buffer + * The result size must be at least half of the input buffer. + * The number of samples also must be even! + */ +int +l1oip_law_to_4bit(u8 *data, int len, u8 *result, u32 *state) +{ + int ii, i = 0, o = 0; + + if (!len) + return 0; + + /* send saved byte and first input byte */ + if (*state) { + *result++ = table_com[(((*state)<<8)&0xff00) | (*data++)]; + len--; + o++; + } + + ii = len >> 1; + + while (i < ii) { + *result++ = table_com[(data[0]<<8) | (data[1])]; + data += 2; + i++; + o++; + } + + /* if len has an odd number, we save byte for next call */ + if (len & 1) + *state = 0x100 + *data; + else + *state = 0; + + return o; +} + +/* Decompress data to the result buffer + * The result size must be the number of sample in packet. (2 * input data) + * The number of samples in the result are even! + */ +int +l1oip_4bit_to_law(u8 *data, int len, u8 *result) +{ + int i = 0; + u16 r; + + while (i < len) { + r = table_dec[*data++]; + *result++ = r>>8; + *result++ = r; + i++; + } + + return len << 1; +} + + +/* + * law conversion + */ +int +l1oip_alaw_to_ulaw(u8 *data, int len, u8 *result) +{ + int i = 0; + + while (i < len) { + *result++ = alaw_to_ulaw[*data++]; + i++; + } + + return len; +} + +int +l1oip_ulaw_to_alaw(u8 *data, int len, u8 *result) +{ + int i = 0; + + while (i < len) { + *result++ = ulaw_to_alaw[*data++]; + i++; + } + + return len; +} + + +/* + * generate/free compression and decompression table + */ +void +l1oip_4bit_free(void) +{ + if (table_dec) + vfree(table_dec); + if (table_com) + vfree(table_com); + table_com = NULL; + table_dec = NULL; +} + +int +l1oip_4bit_alloc(int ulaw) +{ + int i1, i2, c, sample; + + /* in case, it is called again */ + if (table_dec) + return 0; + + /* alloc conversion tables */ + table_com = vmalloc(65536); + table_dec = vmalloc(512); + if (!table_com | !table_dec) { + l1oip_4bit_free(); + return -ENOMEM; + } + memset(table_com, 0, 65536); + memset(table_dec, 0, 512); + /* generate compression table */ + i1 = 0; + while (i1 < 256) { + if (ulaw) + c = ulaw_to_4bit[i1]; + else + c = alaw_to_4bit[i1]; + i2 = 0; + while (i2 < 256) { + table_com[(i1<<8) | i2] |= (c<<4); + table_com[(i2<<8) | i1] |= c; + i2++; + } + i1++; + } + + /* generate decompression table */ + i1 = 0; + while (i1 < 16) { + if (ulaw) + sample = _4bit_to_ulaw[i1]; + else + sample = _4bit_to_alaw[i1]; + i2 = 0; + while (i2 < 16) { + table_dec[(i1<<4) | i2] |= (sample<<8); + table_dec[(i2<<4) | i1] |= sample; + i2++; + } + i1++; + } + + return 0; +} + + diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c new file mode 100644 index 000000000000..155b99780c4f --- /dev/null +++ b/drivers/isdn/mISDN/l1oip_core.c @@ -0,0 +1,1518 @@ +/* + + * l1oip.c low level driver for tunneling layer 1 over IP + * + * NOTE: It is not compatible with TDMoIP nor "ISDN over IP". + * + * Author Andreas Eversberg (jolly@eversberg.eu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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. + * + */ + +/* module parameters: + * type: + Value 1 = BRI + Value 2 = PRI + Value 3 = BRI (multi channel frame, not supported yet) + Value 4 = PRI (multi channel frame, not supported yet) + A multi channel frame reduces overhead to a single frame for all + b-channels, but increases delay. + (NOTE: Multi channel frames are not implemented yet.) + + * codec: + Value 0 = transparent (default) + Value 1 = transfer ALAW + Value 2 = transfer ULAW + Value 3 = transfer generic 4 bit compression. + + * ulaw: + 0 = we use a-Law (default) + 1 = we use u-Law + + * limit: + limitation of B-channels to control bandwidth (1...126) + BRI: 1 or 2 + PRI: 1-30, 31-126 (126, because dchannel ist not counted here) + Also limited ressources are used for stack, resulting in less channels. + It is possible to have more channels than 30 in PRI mode, this must + be supported by the application. + + * ip: + byte representation of remote ip address (127.0.0.1 -> 127,0,0,1) + If not given or four 0, no remote address is set. + For multiple interfaces, concat ip addresses. (127,0,0,1,127,0,0,1) + + * port: + port number (local interface) + If not given or 0, port 931 is used for fist instance, 932 for next... + For multiple interfaces, different ports must be given. + + * remoteport: + port number (remote interface) + If not given or 0, remote port equals local port + For multiple interfaces on equal sites, different ports must be given. + + * ondemand: + 0 = fixed (always transmit packets, even when remote side timed out) + 1 = on demand (only transmit packets, when remote side is detected) + the default is 0 + NOTE: ID must also be set for on demand. + + * id: + optional value to identify frames. This value must be equal on both + peers and should be random. If omitted or 0, no ID is transmitted. + + * debug: + NOTE: only one debug value must be given for all cards + enable debugging (see l1oip.h for debug options) + + +Special mISDN controls: + + op = MISDN_CTRL_SETPEER* + p1 = bytes 0-3 : remote IP address in network order (left element first) + p2 = bytes 1-2 : remote port in network order (high byte first) + optional: + p2 = bytes 3-4 : local port in network order (high byte first) + + op = MISDN_CTRL_UNSETPEER* + + * Use l1oipctrl for comfortable setting or removing ip address. + (Layer 1 Over IP CTRL) + + +L1oIP-Protocol +-------------- + +Frame Header: + + 7 6 5 4 3 2 1 0 ++---------------+ +|Ver|T|I|Coding | ++---------------+ +| ID byte 3 * | ++---------------+ +| ID byte 2 * | ++---------------+ +| ID byte 1 * | ++---------------+ +| ID byte 0 * | ++---------------+ +|M| Channel | ++---------------+ +| Length * | ++---------------+ +| Time Base MSB | ++---------------+ +| Time Base LSB | ++---------------+ +| Data.... | + +... + +| | ++---------------+ +|M| Channel | ++---------------+ +| Length * | ++---------------+ +| Time Base MSB | ++---------------+ +| Time Base LSB | ++---------------+ +| Data.... | + +... + + +* Only included in some cases. + +- Ver = Version +If version is missmatch, the frame must be ignored. + +- T = Type of interface +Must be 0 for S0 or 1 for E1. + +- I = Id present +If bit is set, four ID bytes are included in frame. + +- ID = Connection ID +Additional ID to prevent Denial of Service attacs. Also it prevents hijacking +connections with dynamic IP. The ID should be random and must not be 0. + +- Coding = Type of codec +Must be 0 for no transcoding. Also for D-channel and other HDLC frames. + 1 and 2 are reserved for explicitly use of a-LAW or u-LAW codec. + 3 is used for generic table compressor. + +- M = More channels to come. If this flag is 1, the following byte contains +the length of the channel data. After the data block, the next channel will +be defined. The flag for the last channel block (or if only one channel is +transmitted), must be 0 and no length is given. + +- Channel = Channel number +0 reserved +1-3 channel data for S0 (3 is D-channel) +1-31 channel data for E1 (16 is D-channel) +32-127 channel data for extended E1 (16 is D-channel) + +- The length is used if the M-flag is 1. It is used to find the next channel +inside frame. +NOTE: A value of 0 equals 256 bytes of data. + -> For larger data blocks, a single frame must be used. + -> For larger streams, a single frame or multiple blocks with same channel ID + must be used. + +- Time Base = Timestamp of first sample in frame +The "Time Base" is used to rearange packets and to detect packet loss. +The 16 bits are sent in network order (MSB first) and count 1/8000 th of a +second. This causes a wrap arround each 8,192 seconds. There is no requirement +for the initial "Time Base", but 0 should be used for the first packet. +In case of HDLC data, this timestamp counts the packet or byte number. + + +Two Timers: + +After initialisation, a timer of 15 seconds is started. Whenever a packet is +transmitted, the timer is reset to 15 seconds again. If the timer expires, an +empty packet is transmitted. This keep the connection alive. + +When a valid packet is received, a timer 65 seconds is started. The interface +become ACTIVE. If the timer expires, the interface becomes INACTIVE. + + +Dynamic IP handling: + +To allow dynamic IP, the ID must be non 0. In this case, any packet with the +correct port number and ID will be accepted. If the remote side changes its IP +the new IP is used for all transmitted packets until it changes again. + + +On Demand: + +If the ondemand parameter is given, the remote IP is set to 0 on timeout. +This will stop keepalive traffic to remote. If the remote is online again, +traffic will continue to the remote address. This is usefull for road warriors. +This feature only works with ID set, otherwhise it is highly unsecure. + + +Socket and Thread +----------------- + +The complete socket opening and closing is done by a thread. +When the thread opened a socket, the hc->socket descriptor is set. Whenever a +packet shall be sent to the socket, the hc->socket must be checked wheter not +NULL. To prevent change in socket descriptor, the hc->socket_lock must be used. +To change the socket, a recall of l1oip_socket_open() will safely kill the +socket process and create a new one. + +*/ + +#define L1OIP_VERSION 0 /* 0...3 */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/mISDNif.h> +#include <linux/mISDNhw.h> +#include <linux/mISDNdsp.h> +#include <linux/init.h> +#include <linux/in.h> +#include <linux/inet.h> +#include <linux/workqueue.h> +#include <linux/kthread.h> +#include <net/sock.h> +#include "core.h" +#include "l1oip.h" + +static const char *l1oip_revision = "2.00"; + +static int l1oip_cnt; +static spinlock_t l1oip_lock; +static struct list_head l1oip_ilist; + +#define MAX_CARDS 16 +static u_int type[MAX_CARDS]; +static u_int codec[MAX_CARDS]; +static u_int ip[MAX_CARDS*4]; +static u_int port[MAX_CARDS]; +static u_int remoteport[MAX_CARDS]; +static u_int ondemand[MAX_CARDS]; +static u_int limit[MAX_CARDS]; +static u_int id[MAX_CARDS]; +static int debug; +static int ulaw; + +MODULE_AUTHOR("Andreas Eversberg"); +MODULE_LICENSE("GPL"); +module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(codec, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(ip, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(remoteport, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(ondemand, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(limit, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(id, uint, NULL, S_IRUGO | S_IWUSR); +module_param(ulaw, uint, S_IRUGO | S_IWUSR); +module_param(debug, uint, S_IRUGO | S_IWUSR); + +/* + * send a frame via socket, if open and restart timer + */ +static int +l1oip_socket_send(struct l1oip *hc, u8 localcodec, u8 channel, u32 chanmask, + u16 timebase, u8 *buf, int len) +{ + u8 *p; + int multi = 0; + u8 frame[len+32]; + struct socket *socket = NULL; + mm_segment_t oldfs; + + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: sending data to socket (len = %d)\n", + __func__, len); + + p = frame; + + /* restart timer */ + if ((int)(hc->keep_tl.expires-jiffies) < 5*HZ) { + del_timer(&hc->keep_tl); + hc->keep_tl.expires = jiffies + L1OIP_KEEPALIVE*HZ; + add_timer(&hc->keep_tl); + } else + hc->keep_tl.expires = jiffies + L1OIP_KEEPALIVE*HZ; + + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: resetting timer\n", __func__); + + /* drop if we have no remote ip or port */ + if (!hc->sin_remote.sin_addr.s_addr || !hc->sin_remote.sin_port) { + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: dropping frame, because remote " + "IP is not set.\n", __func__); + return len; + } + + /* assemble frame */ + *p++ = (L1OIP_VERSION<<6) /* version and coding */ + | (hc->pri?0x20:0x00) /* type */ + | (hc->id?0x10:0x00) /* id */ + | localcodec; + if (hc->id) { + *p++ = hc->id>>24; /* id */ + *p++ = hc->id>>16; + *p++ = hc->id>>8; + *p++ = hc->id; + } + *p++ = (multi == 1)?0x80:0x00 + channel; /* m-flag, channel */ + if (multi == 1) + *p++ = len; /* length */ + *p++ = timebase>>8; /* time base */ + *p++ = timebase; + + if (buf && len) { /* add data to frame */ + if (localcodec == 1 && ulaw) + l1oip_ulaw_to_alaw(buf, len, p); + else if (localcodec == 2 && !ulaw) + l1oip_alaw_to_ulaw(buf, len, p); + else if (localcodec == 3) + len = l1oip_law_to_4bit(buf, len, p, + &hc->chan[channel].codecstate); + else + memcpy(p, buf, len); + } + len += p - frame; + + /* check for socket in safe condition */ + spin_lock(&hc->socket_lock); + if (!hc->socket) { + spin_unlock(&hc->socket_lock); + return 0; + } + /* seize socket */ + socket = hc->socket; + hc->socket = NULL; + spin_unlock(&hc->socket_lock); + /* send packet */ + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: sending packet to socket (len " + "= %d)\n", __func__, len); + hc->sendiov.iov_base = frame; + hc->sendiov.iov_len = len; + oldfs = get_fs(); + set_fs(KERNEL_DS); + len = sock_sendmsg(socket, &hc->sendmsg, len); + set_fs(oldfs); + /* give socket back */ + hc->socket = socket; /* no locking required */ + + return len; +} + + +/* + * receive channel data from socket + */ +static void +l1oip_socket_recv(struct l1oip *hc, u8 remotecodec, u8 channel, u16 timebase, + u8 *buf, int len) +{ + struct sk_buff *nskb; + struct bchannel *bch; + struct dchannel *dch; + u8 *p; + u32 rx_counter; + + if (len == 0) { + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: received empty keepalive data, " + "ignoring\n", __func__); + return; + } + + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: received data, sending to mISDN (%d)\n", + __func__, len); + + if (channel < 1 || channel > 127) { + printk(KERN_WARNING "%s: packet error - channel %d out of " + "range\n", __func__, channel); + return; + } + dch = hc->chan[channel].dch; + bch = hc->chan[channel].bch; + if (!dch && !bch) { + printk(KERN_WARNING "%s: packet error - channel %d not in " + "stack\n", __func__, channel); + return; + } + + /* prepare message */ + nskb = mI_alloc_skb((remotecodec == 3)?(len<<1):len, GFP_ATOMIC); + if (!nskb) { + printk(KERN_ERR "%s: No mem for skb.\n", __func__); + return; + } + p = skb_put(nskb, (remotecodec == 3)?(len<<1):len); + + if (remotecodec == 1 && ulaw) + l1oip_alaw_to_ulaw(buf, len, p); + else if (remotecodec == 2 && !ulaw) + l1oip_ulaw_to_alaw(buf, len, p); + else if (remotecodec == 3) + len = l1oip_4bit_to_law(buf, len, p); + else + memcpy(p, buf, len); + + /* send message up */ + if (dch && len >= 2) { + dch->rx_skb = nskb; + recv_Dchannel(dch); + } + if (bch) { + /* expand 16 bit sequence number to 32 bit sequence number */ + rx_counter = hc->chan[channel].rx_counter; + if (((s16)(timebase - rx_counter)) >= 0) { + /* time has changed forward */ + if (timebase >= (rx_counter & 0xffff)) + rx_counter = + (rx_counter & 0xffff0000) | timebase; + else + rx_counter = ((rx_counter & 0xffff0000)+0x10000) + | timebase; + } else { + /* time has changed backwards */ + if (timebase < (rx_counter & 0xffff)) + rx_counter = + (rx_counter & 0xffff0000) | timebase; + else + rx_counter = ((rx_counter & 0xffff0000)-0x10000) + | timebase; + } + hc->chan[channel].rx_counter = rx_counter; + +#ifdef REORDER_DEBUG + if (hc->chan[channel].disorder_flag) { + struct sk_buff *skb; + int cnt; + skb = hc->chan[channel].disorder_skb; + hc->chan[channel].disorder_skb = nskb; + nskb = skb; + cnt = hc->chan[channel].disorder_cnt; + hc->chan[channel].disorder_cnt = rx_counter; + rx_counter = cnt; + } + hc->chan[channel].disorder_flag ^= 1; + if (nskb) +#endif + queue_ch_frame(&bch->ch, PH_DATA_IND, rx_counter, nskb); + } +} + + +/* + * parse frame and extract channel data + */ +static void +l1oip_socket_parse(struct l1oip *hc, struct sockaddr_in *sin, u8 *buf, int len) +{ + u32 id; + u8 channel; + u8 remotecodec; + u16 timebase; + int m, mlen; + int len_start = len; /* initial frame length */ + struct dchannel *dch = hc->chan[hc->d_idx].dch; + + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: received frame, parsing... (%d)\n", + __func__, len); + + /* check lenght */ + if (len < 1+1+2) { + printk(KERN_WARNING "%s: packet error - length %d below " + "4 bytes\n", __func__, len); + return; + } + + /* check version */ + if (((*buf)>>6) != L1OIP_VERSION) { + printk(KERN_WARNING "%s: packet error - unknown version %d\n", + __func__, buf[0]>>6); + return; + } + + /* check type */ + if (((*buf)&0x20) && !hc->pri) { + printk(KERN_WARNING "%s: packet error - received E1 packet " + "on S0 interface\n", __func__); + return; + } + if (!((*buf)&0x20) && hc->pri) { + printk(KERN_WARNING "%s: packet error - received S0 packet " + "on E1 interface\n", __func__); + return; + } + + /* get id flag */ + id = (*buf>>4)&1; + + /* check coding */ + remotecodec = (*buf) & 0x0f; + if (remotecodec > 3) { + printk(KERN_WARNING "%s: packet error - remotecodec %d " + "unsupported\n", __func__, remotecodec); + return; + } + buf++; + len--; + + /* check id */ + if (id) { + if (!hc->id) { + printk(KERN_WARNING "%s: packet error - packet has id " + "0x%x, but we have not\n", __func__, id); + return; + } + if (len < 4) { + printk(KERN_WARNING "%s: packet error - packet too " + "short for ID value\n", __func__); + return; + } + id = (*buf++) << 24; + id += (*buf++) << 16; + id += (*buf++) << 8; + id += (*buf++); + len -= 4; + + if (id != hc->id) { + printk(KERN_WARNING "%s: packet error - ID mismatch, " + "got 0x%x, we 0x%x\n", + __func__, id, hc->id); + return; + } + } else { + if (hc->id) { + printk(KERN_WARNING "%s: packet error - packet has no " + "ID, but we have\n", __func__); + return; + } + } + +multiframe: + if (len < 1) { + printk(KERN_WARNING "%s: packet error - packet too short, " + "channel expected at position %d.\n", + __func__, len-len_start+1); + return; + } + + /* get channel and multiframe flag */ + channel = *buf&0x7f; + m = *buf >> 7; + buf++; + len--; + + /* check length on multiframe */ + if (m) { + if (len < 1) { + printk(KERN_WARNING "%s: packet error - packet too " + "short, length expected at position %d.\n", + __func__, len_start-len-1); + return; + } + + mlen = *buf++; + len--; + if (mlen == 0) + mlen = 256; + if (len < mlen+3) { + printk(KERN_WARNING "%s: packet error - length %d at " + "position %d exceeds total length %d.\n", + __func__, mlen, len_start-len-1, len_start); + return; + } + if (len == mlen+3) { + printk(KERN_WARNING "%s: packet error - length %d at " + "position %d will not allow additional " + "packet.\n", + __func__, mlen, len_start-len+1); + return; + } + } else + mlen = len-2; /* single frame, substract timebase */ + + if (len < 2) { + printk(KERN_WARNING "%s: packet error - packet too short, time " + "base expected at position %d.\n", + __func__, len-len_start+1); + return; + } + + /* get time base */ + timebase = (*buf++) << 8; + timebase |= (*buf++); + len -= 2; + + /* if inactive, we send up a PH_ACTIVATE and activate */ + if (!test_bit(FLG_ACTIVE, &dch->Flags)) { + if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET)) + printk(KERN_DEBUG "%s: interface become active due to " + "received packet\n", __func__); + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_ATOMIC); + } + + /* distribute packet */ + l1oip_socket_recv(hc, remotecodec, channel, timebase, buf, mlen); + buf += mlen; + len -= mlen; + + /* multiframe */ + if (m) + goto multiframe; + + /* restart timer */ + if ((int)(hc->timeout_tl.expires-jiffies) < 5*HZ || !hc->timeout_on) { + hc->timeout_on = 1; + del_timer(&hc->timeout_tl); + hc->timeout_tl.expires = jiffies + L1OIP_TIMEOUT*HZ; + add_timer(&hc->timeout_tl); + } else /* only adjust timer */ + hc->timeout_tl.expires = jiffies + L1OIP_TIMEOUT*HZ; + + /* if ip or source port changes */ + if ((hc->sin_remote.sin_addr.s_addr != sin->sin_addr.s_addr) + || (hc->sin_remote.sin_port != sin->sin_port)) { + if (debug & DEBUG_L1OIP_SOCKET) + printk(KERN_DEBUG "%s: remote address changes from " + "0x%08x to 0x%08x (port %d to %d)\n", __func__, + ntohl(hc->sin_remote.sin_addr.s_addr), + ntohl(sin->sin_addr.s_addr), + ntohs(hc->sin_remote.sin_port), + ntohs(sin->sin_port)); + hc->sin_remote.sin_addr.s_addr = sin->sin_addr.s_addr; + hc->sin_remote.sin_port = sin->sin_port; + } +} + + +/* + * socket stuff + */ +static int +l1oip_socket_thread(void *data) +{ + struct l1oip *hc = (struct l1oip *)data; + int ret = 0; + struct msghdr msg; + struct iovec iov; + mm_segment_t oldfs; + struct sockaddr_in sin_rx; + unsigned char recvbuf[1500]; + int recvlen; + struct socket *socket = NULL; + DECLARE_COMPLETION(wait); + + /* make daemon */ + allow_signal(SIGTERM); + + /* create socket */ + if (sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &socket)) { + printk(KERN_ERR "%s: Failed to create socket.\n", __func__); + return -EIO; + } + + /* set incoming address */ + hc->sin_local.sin_family = AF_INET; + hc->sin_local.sin_addr.s_addr = INADDR_ANY; + hc->sin_local.sin_port = htons((unsigned short)hc->localport); + + /* set outgoing address */ + hc->sin_remote.sin_family = AF_INET; + hc->sin_remote.sin_addr.s_addr = htonl(hc->remoteip); + hc->sin_remote.sin_port = htons((unsigned short)hc->remoteport); + + /* bind to incomming port */ + if (socket->ops->bind(socket, (struct sockaddr *)&hc->sin_local, + sizeof(hc->sin_local))) { + printk(KERN_ERR "%s: Failed to bind socket to port %d.\n", + __func__, hc->localport); + ret = -EINVAL; + goto fail; + } + + /* check sk */ + if (socket->sk == NULL) { + printk(KERN_ERR "%s: socket->sk == NULL\n", __func__); + ret = -EIO; + goto fail; + } + + /* build receive message */ + msg.msg_name = &sin_rx; + msg.msg_namelen = sizeof(sin_rx); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + /* build send message */ + hc->sendmsg.msg_name = &hc->sin_remote; + hc->sendmsg.msg_namelen = sizeof(hc->sin_remote); + hc->sendmsg.msg_control = NULL; + hc->sendmsg.msg_controllen = 0; + hc->sendmsg.msg_iov = &hc->sendiov; + hc->sendmsg.msg_iovlen = 1; + + /* give away socket */ + spin_lock(&hc->socket_lock); + hc->socket = socket; + spin_unlock(&hc->socket_lock); + + /* read loop */ + if (debug & DEBUG_L1OIP_SOCKET) + printk(KERN_DEBUG "%s: socket created and open\n", + __func__); + while (!signal_pending(current)) { + iov.iov_base = recvbuf; + iov.iov_len = sizeof(recvbuf); + oldfs = get_fs(); + set_fs(KERNEL_DS); + recvlen = sock_recvmsg(socket, &msg, sizeof(recvbuf), 0); + set_fs(oldfs); + if (recvlen > 0) { + l1oip_socket_parse(hc, &sin_rx, recvbuf, recvlen); + } else { + if (debug & DEBUG_L1OIP_SOCKET) + printk(KERN_WARNING "%s: broken pipe on socket\n", + __func__); + } + } + + /* get socket back, check first if in use, maybe by send function */ + spin_lock(&hc->socket_lock); + /* if hc->socket is NULL, it is in use until it is given back */ + while (!hc->socket) { + spin_unlock(&hc->socket_lock); + schedule_timeout(HZ/10); + spin_lock(&hc->socket_lock); + } + hc->socket = NULL; + spin_unlock(&hc->socket_lock); + + if (debug & DEBUG_L1OIP_SOCKET) + printk(KERN_DEBUG "%s: socket thread terminating\n", + __func__); + +fail: + /* close socket */ + if (socket) + sock_release(socket); + + /* if we got killed, signal completion */ + complete(&hc->socket_complete); + hc->socket_thread = NULL; /* show termination of thread */ + + if (debug & DEBUG_L1OIP_SOCKET) + printk(KERN_DEBUG "%s: socket thread terminated\n", + __func__); + return ret; +} + +static void +l1oip_socket_close(struct l1oip *hc) +{ + /* kill thread */ + if (hc->socket_thread) { + if (debug & DEBUG_L1OIP_SOCKET) + printk(KERN_DEBUG "%s: socket thread exists, " + "killing...\n", __func__); + send_sig(SIGTERM, hc->socket_thread, 0); + wait_for_completion(&hc->socket_complete); + } +} + +static int +l1oip_socket_open(struct l1oip *hc) +{ + /* in case of reopen, we need to close first */ + l1oip_socket_close(hc); + + init_completion(&hc->socket_complete); + + /* create receive process */ + hc->socket_thread = kthread_run(l1oip_socket_thread, hc, "l1oip_%s", + hc->name); + if (IS_ERR(hc->socket_thread)) { + int err = PTR_ERR(hc->socket_thread); + printk(KERN_ERR "%s: Failed (%d) to create socket process.\n", + __func__, err); + hc->socket_thread = NULL; + sock_release(hc->socket); + return err; + } + if (debug & DEBUG_L1OIP_SOCKET) + printk(KERN_DEBUG "%s: socket thread created\n", __func__); + + return 0; +} + + +static void +l1oip_send_bh(struct work_struct *work) +{ + struct l1oip *hc = container_of(work, struct l1oip, workq); + + if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET)) + printk(KERN_DEBUG "%s: keepalive timer expired, sending empty " + "frame on dchannel\n", __func__); + + /* send an empty l1oip frame at D-channel */ + l1oip_socket_send(hc, 0, hc->d_idx, 0, 0, NULL, 0); +} + + +/* + * timer stuff + */ +static void +l1oip_keepalive(void *data) +{ + struct l1oip *hc = (struct l1oip *)data; + + schedule_work(&hc->workq); +} + +static void +l1oip_timeout(void *data) +{ + struct l1oip *hc = (struct l1oip *)data; + struct dchannel *dch = hc->chan[hc->d_idx].dch; + + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: timeout timer expired, turn layer one " + "down.\n", __func__); + + hc->timeout_on = 0; /* state that timer must be initialized next time */ + + /* if timeout, we send up a PH_DEACTIVATE and deactivate */ + if (test_bit(FLG_ACTIVE, &dch->Flags)) { + if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET)) + printk(KERN_DEBUG "%s: interface become deactivated " + "due to timeout\n", __func__); + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_ATOMIC); + } + + /* if we have ondemand set, we remove ip address */ + if (hc->ondemand) { + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: on demand causes ip address to " + "be removed\n", __func__); + hc->sin_remote.sin_addr.s_addr = 0; + } +} + + +/* + * message handling + */ +static int +handle_dmsg(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct l1oip *hc = dch->hw; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + int ret = -EINVAL; + int l, ll; + unsigned char *p; + + switch (hh->prim) { + case PH_DATA_REQ: + if (skb->len < 1) { + printk(KERN_WARNING "%s: skb too small\n", + __func__); + break; + } + if (skb->len > MAX_DFRAME_LEN_L1 || skb->len > L1OIP_MAX_LEN) { + printk(KERN_WARNING "%s: skb too large\n", + __func__); + break; + } + /* send frame */ + p = skb->data; + l = skb->len; + while (l) { + ll = (l < L1OIP_MAX_PERFRAME)?l:L1OIP_MAX_PERFRAME; + l1oip_socket_send(hc, 0, dch->slot, 0, + hc->chan[dch->slot].tx_counter++, p, ll); + p += ll; + l -= ll; + } + skb_trim(skb, 0); + queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb); + return 0; + case PH_ACTIVATE_REQ: + if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET)) + printk(KERN_DEBUG "%s: PH_ACTIVATE channel %d (1..%d)\n" + , __func__, dch->slot, hc->b_num+1); + skb_trim(skb, 0); + if (test_bit(FLG_ACTIVE, &dch->Flags)) + queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb); + else + queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb); + return 0; + case PH_DEACTIVATE_REQ: + if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET)) + printk(KERN_DEBUG "%s: PH_DEACTIVATE channel %d " + "(1..%d)\n", __func__, dch->slot, + hc->b_num+1); + skb_trim(skb, 0); + if (test_bit(FLG_ACTIVE, &dch->Flags)) + queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb); + else + queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb); + return 0; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static int +channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + struct l1oip *hc = dch->hw; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_SETPEER | MISDN_CTRL_UNSETPEER; + break; + case MISDN_CTRL_SETPEER: + hc->remoteip = (u32)cq->p1; + hc->remoteport = cq->p2 & 0xffff; + hc->localport = cq->p2 >> 16; + if (!hc->remoteport) + hc->remoteport = hc->localport; + if (debug & DEBUG_L1OIP_SOCKET) + printk(KERN_DEBUG "%s: got new ip address from user " + "space.\n", __func__); + l1oip_socket_open(hc); + break; + case MISDN_CTRL_UNSETPEER: + if (debug & DEBUG_L1OIP_SOCKET) + printk(KERN_DEBUG "%s: removing ip address.\n", + __func__); + hc->remoteip = 0; + l1oip_socket_open(hc); + break; + default: + printk(KERN_WARNING "%s: unknown Op %x\n", + __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +static int +open_dchannel(struct l1oip *hc, struct dchannel *dch, struct channel_req *rq) +{ + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__, + dch->dev.id, __builtin_return_address(0)); + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + if ((dch->dev.D.protocol != ISDN_P_NONE) && + (dch->dev.D.protocol != rq->protocol)) { + if (debug & DEBUG_HW_OPEN) + printk(KERN_WARNING "%s: change protocol %x to %x\n", + __func__, dch->dev.D.protocol, rq->protocol); + } + if (dch->dev.D.protocol != rq->protocol) + dch->dev.D.protocol = rq->protocol; + + if (test_bit(FLG_ACTIVE, &dch->Flags)) { + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + } + rq->ch = &dch->dev.D; + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", __func__); + return 0; +} + +static int +open_bchannel(struct l1oip *hc, struct dchannel *dch, struct channel_req *rq) +{ + struct bchannel *bch; + int ch; + + if (!test_bit(rq->adr.channel & 0x1f, + &dch->dev.channelmap[rq->adr.channel >> 5])) + return -EINVAL; + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + ch = rq->adr.channel; /* BRI: 1=B1 2=B2 PRI: 1..15,17.. */ + bch = hc->chan[ch].bch; + if (!bch) { + printk(KERN_ERR "%s:internal error ch %d has no bch\n", + __func__, ch); + return -EINVAL; + } + if (test_and_set_bit(FLG_OPEN, &bch->Flags)) + return -EBUSY; /* b-channel can be only open once */ + bch->ch.protocol = rq->protocol; + rq->ch = &bch->ch; + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", __func__); + return 0; +} + +static int +l1oip_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct l1oip *hc = dch->hw; + struct channel_req *rq; + int err = 0; + + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", + __func__, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + rq = arg; + switch (rq->protocol) { + case ISDN_P_TE_S0: + case ISDN_P_NT_S0: + if (hc->pri) { + err = -EINVAL; + break; + } + err = open_dchannel(hc, dch, rq); + break; + case ISDN_P_TE_E1: + case ISDN_P_NT_E1: + if (!hc->pri) { + err = -EINVAL; + break; + } + err = open_dchannel(hc, dch, rq); + break; + default: + err = open_bchannel(hc, dch, rq); + } + break; + case CLOSE_CHANNEL: + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) close from %p\n", + __func__, dch->dev.id, + __builtin_return_address(0)); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: + err = channel_dctrl(dch, arg); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: unknown command %x\n", + __func__, cmd); + err = -EINVAL; + } + return err; +} + +static int +handle_bmsg(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct l1oip *hc = bch->hw; + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + int l, ll, i; + unsigned char *p; + + switch (hh->prim) { + case PH_DATA_REQ: + if (skb->len <= 0) { + printk(KERN_WARNING "%s: skb too small\n", + __func__); + break; + } + if (skb->len > MAX_DFRAME_LEN_L1 || skb->len > L1OIP_MAX_LEN) { + printk(KERN_WARNING "%s: skb too large\n", + __func__); + break; + } + /* check for AIS / ulaw-silence */ + p = skb->data; + l = skb->len; + for (i = 0; i < l; i++) { + if (*p++ != 0xff) + break; + } + if (i == l) { + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: got AIS, not sending, " + "but counting\n", __func__); + hc->chan[bch->slot].tx_counter += l; + skb_trim(skb, 0); + queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb); + return 0; + } + /* check for silence */ + p = skb->data; + l = skb->len; + for (i = 0; i < l; i++) { + if (*p++ != 0x2a) + break; + } + if (i == l) { + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: got silence, not sending" + ", but counting\n", __func__); + hc->chan[bch->slot].tx_counter += l; + skb_trim(skb, 0); + queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb); + return 0; + } + + /* send frame */ + p = skb->data; + l = skb->len; + while (l) { + ll = (l < L1OIP_MAX_PERFRAME)?l:L1OIP_MAX_PERFRAME; + l1oip_socket_send(hc, hc->codec, bch->slot, 0, + hc->chan[bch->slot].tx_counter, p, ll); + hc->chan[bch->slot].tx_counter += ll; + p += ll; + l -= ll; + } + skb_trim(skb, 0); + queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb); + return 0; + case PH_ACTIVATE_REQ: + if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET)) + printk(KERN_DEBUG "%s: PH_ACTIVATE channel %d (1..%d)\n" + , __func__, bch->slot, hc->b_num+1); + hc->chan[bch->slot].codecstate = 0; + test_and_set_bit(FLG_ACTIVE, &bch->Flags); + skb_trim(skb, 0); + queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb); + return 0; + case PH_DEACTIVATE_REQ: + if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET)) + printk(KERN_DEBUG "%s: PH_DEACTIVATE channel %d " + "(1..%d)\n", __func__, bch->slot, + hc->b_num+1); + test_and_clear_bit(FLG_ACTIVE, &bch->Flags); + skb_trim(skb, 0); + queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb); + return 0; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static int +channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + struct dsp_features *features = + (struct dsp_features *)(*((u_long *)&cq->p1)); + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_HW_FEATURES_OP; + break; + case MISDN_CTRL_HW_FEATURES: /* fill features structure */ + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: HW_FEATURE request\n", + __func__); + /* create confirm */ + features->unclocked = 1; + features->unordered = 1; + break; + default: + printk(KERN_WARNING "%s: unknown Op %x\n", + __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +static int +l1oip_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + int err = -EINVAL; + + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", + __func__, cmd, arg); + switch (cmd) { + case CLOSE_CHANNEL: + test_and_clear_bit(FLG_OPEN, &bch->Flags); + test_and_clear_bit(FLG_ACTIVE, &bch->Flags); + ch->protocol = ISDN_P_NONE; + ch->peer = NULL; + module_put(THIS_MODULE); + err = 0; + break; + case CONTROL_CHANNEL: + err = channel_bctrl(bch, arg); + break; + default: + printk(KERN_WARNING "%s: unknown prim(%x)\n", + __func__, cmd); + } + return err; +} + + +/* + * cleanup module and stack + */ +static void +release_card(struct l1oip *hc) +{ + int ch; + + if (timer_pending(&hc->keep_tl)) + del_timer(&hc->keep_tl); + + if (timer_pending(&hc->timeout_tl)) + del_timer(&hc->timeout_tl); + + if (hc->socket_thread) + l1oip_socket_close(hc); + + if (hc->registered && hc->chan[hc->d_idx].dch) + mISDN_unregister_device(&hc->chan[hc->d_idx].dch->dev); + for (ch = 0; ch < 128; ch++) { + if (hc->chan[ch].dch) { + mISDN_freedchannel(hc->chan[ch].dch); + kfree(hc->chan[ch].dch); + } + if (hc->chan[ch].bch) { + mISDN_freebchannel(hc->chan[ch].bch); + kfree(hc->chan[ch].bch); +#ifdef REORDER_DEBUG + if (hc->chan[ch].disorder_skb) + dev_kfree_skb(hc->chan[ch].disorder_skb); +#endif + } + } + + spin_lock(&l1oip_lock); + list_del(&hc->list); + spin_unlock(&l1oip_lock); + + kfree(hc); +} + +static void +l1oip_cleanup(void) +{ + struct l1oip *hc, *next; + + list_for_each_entry_safe(hc, next, &l1oip_ilist, list) + release_card(hc); + + l1oip_4bit_free(); +} + + +/* + * module and stack init + */ +static int +init_card(struct l1oip *hc, int pri, int bundle) +{ + struct dchannel *dch; + struct bchannel *bch; + int ret; + int i, ch; + + spin_lock_init(&hc->socket_lock); + hc->idx = l1oip_cnt; + hc->pri = pri; + hc->d_idx = pri?16:3; + hc->b_num = pri?30:2; + hc->bundle = bundle; + if (hc->pri) + sprintf(hc->name, "l1oip-e1.%d", l1oip_cnt + 1); + else + sprintf(hc->name, "l1oip-s0.%d", l1oip_cnt + 1); + + switch (codec[l1oip_cnt]) { + case 0: /* as is */ + case 1: /* alaw */ + case 2: /* ulaw */ + case 3: /* 4bit */ + break; + default: + printk(KERN_ERR "Codec(%d) not supported.\n", + codec[l1oip_cnt]); + return -EINVAL; + } + hc->codec = codec[l1oip_cnt]; + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: using codec %d\n", + __func__, hc->codec); + + if (id[l1oip_cnt] == 0) { + printk(KERN_WARNING "Warning: No 'id' value given or " + "0, this is highly unsecure. Please use 32 " + "bit randmom number 0x...\n"); + } + hc->id = id[l1oip_cnt]; + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: using id 0x%x\n", __func__, hc->id); + + hc->ondemand = ondemand[l1oip_cnt]; + if (hc->ondemand && !hc->id) { + printk(KERN_ERR "%s: ondemand option only allowed in " + "conjunction with non 0 ID\n", __func__); + return -EINVAL; + } + + if (limit[l1oip_cnt]) + hc->b_num = limit[l1oip_cnt]; + if (!pri && hc->b_num > 2) { + printk(KERN_ERR "Maximum limit for BRI interface is 2 " + "channels.\n"); + return -EINVAL; + } + if (pri && hc->b_num > 126) { + printk(KERN_ERR "Maximum limit for PRI interface is 126 " + "channels.\n"); + return -EINVAL; + } + if (pri && hc->b_num > 30) { + printk(KERN_WARNING "Maximum limit for BRI interface is 30 " + "channels.\n"); + printk(KERN_WARNING "Your selection of %d channels must be " + "supported by application.\n", hc->limit); + } + + hc->remoteip = ip[l1oip_cnt<<2] << 24 + | ip[(l1oip_cnt<<2)+1] << 16 + | ip[(l1oip_cnt<<2)+2] << 8 + | ip[(l1oip_cnt<<2)+3]; + hc->localport = port[l1oip_cnt]?:(L1OIP_DEFAULTPORT+l1oip_cnt); + if (remoteport[l1oip_cnt]) + hc->remoteport = remoteport[l1oip_cnt]; + else + hc->remoteport = hc->localport; + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: using local port %d remote ip " + "%d.%d.%d.%d port %d ondemand %d\n", __func__, + hc->localport, hc->remoteip >> 24, + (hc->remoteip >> 16) & 0xff, + (hc->remoteip >> 8) & 0xff, hc->remoteip & 0xff, + hc->remoteport, hc->ondemand); + + dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL); + if (!dch) + return -ENOMEM; + dch->debug = debug; + mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, NULL); + dch->hw = hc; + if (pri) + dch->dev.Dprotocols = (1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1); + else + dch->dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); + dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + dch->dev.D.send = handle_dmsg; + dch->dev.D.ctrl = l1oip_dctrl; + dch->dev.nrbchan = hc->b_num; + dch->slot = hc->d_idx; + hc->chan[hc->d_idx].dch = dch; + i = 1; + for (ch = 0; ch < dch->dev.nrbchan; ch++) { + if (ch == 15) + i++; + bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL); + if (!bch) { + printk(KERN_ERR "%s: no memory for bchannel\n", + __func__); + return -ENOMEM; + } + bch->nr = i + ch; + bch->slot = i + ch; + bch->debug = debug; + mISDN_initbchannel(bch, MAX_DATA_MEM); + bch->hw = hc; + bch->ch.send = handle_bmsg; + bch->ch.ctrl = l1oip_bctrl; + bch->ch.nr = i + ch; + list_add(&bch->ch.list, &dch->dev.bchannels); + hc->chan[i + ch].bch = bch; + test_and_set_bit(bch->nr & 0x1f, + &dch->dev.channelmap[bch->nr >> 5]); + } + ret = mISDN_register_device(&dch->dev, hc->name); + if (ret) + return ret; + hc->registered = 1; + + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: Setting up network card(%d)\n", + __func__, l1oip_cnt + 1); + ret = l1oip_socket_open(hc); + if (ret) + return ret; + + hc->keep_tl.function = (void *)l1oip_keepalive; + hc->keep_tl.data = (ulong)hc; + init_timer(&hc->keep_tl); + hc->keep_tl.expires = jiffies + 2*HZ; /* two seconds first time */ + add_timer(&hc->keep_tl); + + hc->timeout_tl.function = (void *)l1oip_timeout; + hc->timeout_tl.data = (ulong)hc; + init_timer(&hc->timeout_tl); + hc->timeout_on = 0; /* state that we have timer off */ + + return 0; +} + +static int __init +l1oip_init(void) +{ + int pri, bundle; + struct l1oip *hc; + int ret; + + printk(KERN_INFO "mISDN: Layer-1-over-IP driver Rev. %s\n", + l1oip_revision); + + INIT_LIST_HEAD(&l1oip_ilist); + spin_lock_init(&l1oip_lock); + + if (l1oip_4bit_alloc(ulaw)) + return -ENOMEM; + + l1oip_cnt = 0; + while (type[l1oip_cnt] && l1oip_cnt < MAX_CARDS) { + switch (type[l1oip_cnt] & 0xff) { + case 1: + pri = 0; + bundle = 0; + break; + case 2: + pri = 1; + bundle = 0; + break; + case 3: + pri = 0; + bundle = 1; + break; + case 4: + pri = 1; + bundle = 1; + break; + default: + printk(KERN_ERR "Card type(%d) not supported.\n", + type[l1oip_cnt] & 0xff); + l1oip_cleanup(); + return -EINVAL; + } + + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: interface %d is %s with %s.\n", + __func__, l1oip_cnt, pri?"PRI":"BRI", + bundle?"bundled IP packet for all B-channels" + :"seperate IP packets for every B-channel"); + + hc = kzalloc(sizeof(struct l1oip), GFP_ATOMIC); + if (!hc) { + printk(KERN_ERR "No kmem for L1-over-IP driver.\n"); + l1oip_cleanup(); + return -ENOMEM; + } + INIT_WORK(&hc->workq, (void *)l1oip_send_bh); + + spin_lock(&l1oip_lock); + list_add_tail(&hc->list, &l1oip_ilist); + spin_unlock(&l1oip_lock); + + ret = init_card(hc, pri, bundle); + if (ret) { + l1oip_cleanup(); + return ret; + } + + l1oip_cnt++; + } + printk(KERN_INFO "%d virtual devices registered\n", l1oip_cnt); + return 0; +} + +module_init(l1oip_init); +module_exit(l1oip_cleanup); + diff --git a/drivers/isdn/mISDN/layer1.c b/drivers/isdn/mISDN/layer1.c new file mode 100644 index 000000000000..fced1a2755f8 --- /dev/null +++ b/drivers/isdn/mISDN/layer1.c @@ -0,0 +1,403 @@ +/* + * + * Author Karsten Keil <kkeil@novell.com> + * + * Copyright 2008 by Karsten Keil <kkeil@novell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + */ + + +#include <linux/module.h> +#include <linux/mISDNhw.h> +#include "layer1.h" +#include "fsm.h" + +static int *debug; + +struct layer1 { + u_long Flags; + struct FsmInst l1m; + struct FsmTimer timer; + int delay; + struct dchannel *dch; + dchannel_l1callback *dcb; +}; + +#define TIMER3_VALUE 7000 + +static +struct Fsm l1fsm_s = {NULL, 0, 0, NULL, NULL}; + +enum { + ST_L1_F2, + ST_L1_F3, + ST_L1_F4, + ST_L1_F5, + ST_L1_F6, + ST_L1_F7, + ST_L1_F8, +}; + +#define L1S_STATE_COUNT (ST_L1_F8+1) + +static char *strL1SState[] = +{ + "ST_L1_F2", + "ST_L1_F3", + "ST_L1_F4", + "ST_L1_F5", + "ST_L1_F6", + "ST_L1_F7", + "ST_L1_F8", +}; + +enum { + EV_PH_ACTIVATE, + EV_PH_DEACTIVATE, + EV_RESET_IND, + EV_DEACT_CNF, + EV_DEACT_IND, + EV_POWER_UP, + EV_ANYSIG_IND, + EV_INFO2_IND, + EV_INFO4_IND, + EV_TIMER_DEACT, + EV_TIMER_ACT, + EV_TIMER3, +}; + +#define L1_EVENT_COUNT (EV_TIMER3 + 1) + +static char *strL1Event[] = +{ + "EV_PH_ACTIVATE", + "EV_PH_DEACTIVATE", + "EV_RESET_IND", + "EV_DEACT_CNF", + "EV_DEACT_IND", + "EV_POWER_UP", + "EV_ANYSIG_IND", + "EV_INFO2_IND", + "EV_INFO4_IND", + "EV_TIMER_DEACT", + "EV_TIMER_ACT", + "EV_TIMER3", +}; + +static void +l1m_debug(struct FsmInst *fi, char *fmt, ...) +{ + struct layer1 *l1 = fi->userdata; + va_list va; + + va_start(va, fmt); + printk(KERN_DEBUG "%s: ", l1->dch->dev.name); + vprintk(fmt, va); + printk("\n"); + va_end(va); +} + +static void +l1_reset(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L1_F3); +} + +static void +l1_deact_cnf(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L1_F3); + if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) + l1->dcb(l1->dch, HW_POWERUP_REQ); +} + +static void +l1_deact_req_s(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L1_F3); + mISDN_FsmRestartTimer(&l1->timer, 550, EV_TIMER_DEACT, NULL, 2); + test_and_set_bit(FLG_L1_DEACTTIMER, &l1->Flags); +} + +static void +l1_power_up_s(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) { + mISDN_FsmChangeState(fi, ST_L1_F4); + l1->dcb(l1->dch, INFO3_P8); + } else + mISDN_FsmChangeState(fi, ST_L1_F3); +} + +static void +l1_go_F5(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L1_F5); +} + +static void +l1_go_F8(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L1_F8); +} + +static void +l1_info2_ind(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L1_F6); + l1->dcb(l1->dch, INFO3_P8); +} + +static void +l1_info4_ind(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L1_F7); + l1->dcb(l1->dch, INFO3_P8); + if (test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags)) + mISDN_FsmDelTimer(&l1->timer, 4); + if (!test_bit(FLG_L1_ACTIVATED, &l1->Flags)) { + if (test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags)) + mISDN_FsmDelTimer(&l1->timer, 3); + mISDN_FsmRestartTimer(&l1->timer, 110, EV_TIMER_ACT, NULL, 2); + test_and_set_bit(FLG_L1_ACTTIMER, &l1->Flags); + } +} + +static void +l1_timer3(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags); + if (test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags)) { + if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) + l1->dcb(l1->dch, HW_D_NOBLOCKED); + l1->dcb(l1->dch, PH_DEACTIVATE_IND); + } + if (l1->l1m.state != ST_L1_F6) { + mISDN_FsmChangeState(fi, ST_L1_F3); + l1->dcb(l1->dch, HW_POWERUP_REQ); + } +} + +static void +l1_timer_act(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + test_and_clear_bit(FLG_L1_ACTTIMER, &l1->Flags); + test_and_set_bit(FLG_L1_ACTIVATED, &l1->Flags); + l1->dcb(l1->dch, PH_ACTIVATE_IND); +} + +static void +l1_timer_deact(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags); + test_and_clear_bit(FLG_L1_ACTIVATED, &l1->Flags); + if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) + l1->dcb(l1->dch, HW_D_NOBLOCKED); + l1->dcb(l1->dch, PH_DEACTIVATE_IND); + l1->dcb(l1->dch, HW_DEACT_REQ); +} + +static void +l1_activate_s(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + mISDN_FsmRestartTimer(&l1->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2); + test_and_set_bit(FLG_L1_T3RUN, &l1->Flags); + l1->dcb(l1->dch, HW_RESET_REQ); +} + +static void +l1_activate_no(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + if ((!test_bit(FLG_L1_DEACTTIMER, &l1->Flags)) && + (!test_bit(FLG_L1_T3RUN, &l1->Flags))) { + test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags); + if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) + l1->dcb(l1->dch, HW_D_NOBLOCKED); + l1->dcb(l1->dch, PH_DEACTIVATE_IND); + } +} + +static struct FsmNode L1SFnList[] = +{ + {ST_L1_F3, EV_PH_ACTIVATE, l1_activate_s}, + {ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no}, + {ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no}, + {ST_L1_F3, EV_RESET_IND, l1_reset}, + {ST_L1_F4, EV_RESET_IND, l1_reset}, + {ST_L1_F5, EV_RESET_IND, l1_reset}, + {ST_L1_F6, EV_RESET_IND, l1_reset}, + {ST_L1_F7, EV_RESET_IND, l1_reset}, + {ST_L1_F8, EV_RESET_IND, l1_reset}, + {ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F6, EV_DEACT_IND, l1_deact_req_s}, + {ST_L1_F7, EV_DEACT_IND, l1_deact_req_s}, + {ST_L1_F8, EV_DEACT_IND, l1_deact_req_s}, + {ST_L1_F3, EV_POWER_UP, l1_power_up_s}, + {ST_L1_F4, EV_ANYSIG_IND, l1_go_F5}, + {ST_L1_F6, EV_ANYSIG_IND, l1_go_F8}, + {ST_L1_F7, EV_ANYSIG_IND, l1_go_F8}, + {ST_L1_F3, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F4, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F5, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F7, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F8, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F3, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F4, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F5, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F6, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F8, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F3, EV_TIMER3, l1_timer3}, + {ST_L1_F4, EV_TIMER3, l1_timer3}, + {ST_L1_F5, EV_TIMER3, l1_timer3}, + {ST_L1_F6, EV_TIMER3, l1_timer3}, + {ST_L1_F8, EV_TIMER3, l1_timer3}, + {ST_L1_F7, EV_TIMER_ACT, l1_timer_act}, + {ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact}, +}; + +static void +release_l1(struct layer1 *l1) { + mISDN_FsmDelTimer(&l1->timer, 0); + if (l1->dch) + l1->dch->l1 = NULL; + module_put(THIS_MODULE); + kfree(l1); +} + +int +l1_event(struct layer1 *l1, u_int event) +{ + int err = 0; + + if (!l1) + return -EINVAL; + switch (event) { + case HW_RESET_IND: + mISDN_FsmEvent(&l1->l1m, EV_RESET_IND, NULL); + break; + case HW_DEACT_IND: + mISDN_FsmEvent(&l1->l1m, EV_DEACT_IND, NULL); + break; + case HW_POWERUP_IND: + mISDN_FsmEvent(&l1->l1m, EV_POWER_UP, NULL); + break; + case HW_DEACT_CNF: + mISDN_FsmEvent(&l1->l1m, EV_DEACT_CNF, NULL); + break; + case ANYSIGNAL: + mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL); + break; + case LOSTFRAMING: + mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL); + break; + case INFO2: + mISDN_FsmEvent(&l1->l1m, EV_INFO2_IND, NULL); + break; + case INFO4_P8: + mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL); + break; + case INFO4_P10: + mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL); + break; + case PH_ACTIVATE_REQ: + if (test_bit(FLG_L1_ACTIVATED, &l1->Flags)) + l1->dcb(l1->dch, PH_ACTIVATE_IND); + else { + test_and_set_bit(FLG_L1_ACTIVATING, &l1->Flags); + mISDN_FsmEvent(&l1->l1m, EV_PH_ACTIVATE, NULL); + } + break; + case CLOSE_CHANNEL: + release_l1(l1); + break; + default: + if (*debug & DEBUG_L1) + printk(KERN_DEBUG "%s %x unhandled\n", + __func__, event); + err = -EINVAL; + } + return err; +} +EXPORT_SYMBOL(l1_event); + +int +create_l1(struct dchannel *dch, dchannel_l1callback *dcb) { + struct layer1 *nl1; + + nl1 = kzalloc(sizeof(struct layer1), GFP_ATOMIC); + if (!nl1) { + printk(KERN_ERR "kmalloc struct layer1 failed\n"); + return -ENOMEM; + } + nl1->l1m.fsm = &l1fsm_s; + nl1->l1m.state = ST_L1_F3; + nl1->Flags = 0; + nl1->l1m.debug = *debug & DEBUG_L1_FSM; + nl1->l1m.userdata = nl1; + nl1->l1m.userint = 0; + nl1->l1m.printdebug = l1m_debug; + nl1->dch = dch; + nl1->dcb = dcb; + mISDN_FsmInitTimer(&nl1->l1m, &nl1->timer); + __module_get(THIS_MODULE); + dch->l1 = nl1; + return 0; +} +EXPORT_SYMBOL(create_l1); + +int +l1_init(u_int *deb) +{ + debug = deb; + l1fsm_s.state_count = L1S_STATE_COUNT; + l1fsm_s.event_count = L1_EVENT_COUNT; + l1fsm_s.strEvent = strL1Event; + l1fsm_s.strState = strL1SState; + mISDN_FsmNew(&l1fsm_s, L1SFnList, ARRAY_SIZE(L1SFnList)); + return 0; +} + +void +l1_cleanup(void) +{ + mISDN_FsmFree(&l1fsm_s); +} diff --git a/drivers/isdn/mISDN/layer1.h b/drivers/isdn/mISDN/layer1.h new file mode 100644 index 000000000000..9c8125fd89af --- /dev/null +++ b/drivers/isdn/mISDN/layer1.h @@ -0,0 +1,26 @@ +/* + * + * Layer 1 defines + * + * Copyright 2008 by Karsten Keil <kkeil@novell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + */ + +#define FLG_L1_ACTIVATING 1 +#define FLG_L1_ACTIVATED 2 +#define FLG_L1_DEACTTIMER 3 +#define FLG_L1_ACTTIMER 4 +#define FLG_L1_T3RUN 5 +#define FLG_L1_PULL_REQ 6 +#define FLG_L1_UINT 7 +#define FLG_L1_DBLOCKED 8 + diff --git a/drivers/isdn/mISDN/layer2.c b/drivers/isdn/mISDN/layer2.c new file mode 100644 index 000000000000..a7915a156c04 --- /dev/null +++ b/drivers/isdn/mISDN/layer2.c @@ -0,0 +1,2216 @@ +/* + * + * Author Karsten Keil <kkeil@novell.com> + * + * Copyright 2008 by Karsten Keil <kkeil@novell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + */ + +#include "fsm.h" +#include "layer2.h" + +static int *debug; + +static +struct Fsm l2fsm = {NULL, 0, 0, NULL, NULL}; + +static char *strL2State[] = +{ + "ST_L2_1", + "ST_L2_2", + "ST_L2_3", + "ST_L2_4", + "ST_L2_5", + "ST_L2_6", + "ST_L2_7", + "ST_L2_8", +}; + +enum { + EV_L2_UI, + EV_L2_SABME, + EV_L2_DISC, + EV_L2_DM, + EV_L2_UA, + EV_L2_FRMR, + EV_L2_SUPER, + EV_L2_I, + EV_L2_DL_DATA, + EV_L2_ACK_PULL, + EV_L2_DL_UNITDATA, + EV_L2_DL_ESTABLISH_REQ, + EV_L2_DL_RELEASE_REQ, + EV_L2_MDL_ASSIGN, + EV_L2_MDL_REMOVE, + EV_L2_MDL_ERROR, + EV_L1_DEACTIVATE, + EV_L2_T200, + EV_L2_T203, + EV_L2_SET_OWN_BUSY, + EV_L2_CLEAR_OWN_BUSY, + EV_L2_FRAME_ERROR, +}; + +#define L2_EVENT_COUNT (EV_L2_FRAME_ERROR+1) + +static char *strL2Event[] = +{ + "EV_L2_UI", + "EV_L2_SABME", + "EV_L2_DISC", + "EV_L2_DM", + "EV_L2_UA", + "EV_L2_FRMR", + "EV_L2_SUPER", + "EV_L2_I", + "EV_L2_DL_DATA", + "EV_L2_ACK_PULL", + "EV_L2_DL_UNITDATA", + "EV_L2_DL_ESTABLISH_REQ", + "EV_L2_DL_RELEASE_REQ", + "EV_L2_MDL_ASSIGN", + "EV_L2_MDL_REMOVE", + "EV_L2_MDL_ERROR", + "EV_L1_DEACTIVATE", + "EV_L2_T200", + "EV_L2_T203", + "EV_L2_SET_OWN_BUSY", + "EV_L2_CLEAR_OWN_BUSY", + "EV_L2_FRAME_ERROR", +}; + +static void +l2m_debug(struct FsmInst *fi, char *fmt, ...) +{ + struct layer2 *l2 = fi->userdata; + va_list va; + + if (!(*debug & DEBUG_L2_FSM)) + return; + va_start(va, fmt); + printk(KERN_DEBUG "l2 (tei %d): ", l2->tei); + vprintk(fmt, va); + printk("\n"); + va_end(va); +} + +inline u_int +l2headersize(struct layer2 *l2, int ui) +{ + return ((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) + + (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1); +} + +inline u_int +l2addrsize(struct layer2 *l2) +{ + return test_bit(FLG_LAPD, &l2->flag) ? 2 : 1; +} + +static u_int +l2_newid(struct layer2 *l2) +{ + u_int id; + + id = l2->next_id++; + if (id == 0x7fff) + l2->next_id = 1; + id <<= 16; + id |= l2->tei << 8; + id |= l2->sapi; + return id; +} + +static void +l2up(struct layer2 *l2, u_int prim, struct sk_buff *skb) +{ + int err; + + if (!l2->up) + return; + mISDN_HEAD_PRIM(skb) = prim; + mISDN_HEAD_ID(skb) = (l2->ch.nr << 16) | l2->ch.addr; + err = l2->up->send(l2->up, skb); + if (err) { + printk(KERN_WARNING "%s: err=%d\n", __func__, err); + dev_kfree_skb(skb); + } +} + +static void +l2up_create(struct layer2 *l2, u_int prim, int len, void *arg) +{ + struct sk_buff *skb; + struct mISDNhead *hh; + int err; + + if (!l2->up) + return; + skb = mI_alloc_skb(len, GFP_ATOMIC); + if (!skb) + return; + hh = mISDN_HEAD_P(skb); + hh->prim = prim; + hh->id = (l2->ch.nr << 16) | l2->ch.addr; + if (len) + memcpy(skb_put(skb, len), arg, len); + err = l2->up->send(l2->up, skb); + if (err) { + printk(KERN_WARNING "%s: err=%d\n", __func__, err); + dev_kfree_skb(skb); + } +} + +static int +l2down_skb(struct layer2 *l2, struct sk_buff *skb) { + int ret; + + ret = l2->ch.recv(l2->ch.peer, skb); + if (ret && (*debug & DEBUG_L2_RECV)) + printk(KERN_DEBUG "l2down_skb: ret(%d)\n", ret); + return ret; +} + +static int +l2down_raw(struct layer2 *l2, struct sk_buff *skb) +{ + struct mISDNhead *hh = mISDN_HEAD_P(skb); + + if (hh->prim == PH_DATA_REQ) { + if (test_and_set_bit(FLG_L1_NOTREADY, &l2->flag)) { + skb_queue_tail(&l2->down_queue, skb); + return 0; + } + l2->down_id = mISDN_HEAD_ID(skb); + } + return l2down_skb(l2, skb); +} + +static int +l2down(struct layer2 *l2, u_int prim, u_int id, struct sk_buff *skb) +{ + struct mISDNhead *hh = mISDN_HEAD_P(skb); + + hh->prim = prim; + hh->id = id; + return l2down_raw(l2, skb); +} + +static int +l2down_create(struct layer2 *l2, u_int prim, u_int id, int len, void *arg) +{ + struct sk_buff *skb; + int err; + struct mISDNhead *hh; + + skb = mI_alloc_skb(len, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + hh = mISDN_HEAD_P(skb); + hh->prim = prim; + hh->id = id; + if (len) + memcpy(skb_put(skb, len), arg, len); + err = l2down_raw(l2, skb); + if (err) + dev_kfree_skb(skb); + return err; +} + +static int +ph_data_confirm(struct layer2 *l2, struct mISDNhead *hh, struct sk_buff *skb) { + struct sk_buff *nskb = skb; + int ret = -EAGAIN; + + if (test_bit(FLG_L1_NOTREADY, &l2->flag)) { + if (hh->id == l2->down_id) { + nskb = skb_dequeue(&l2->down_queue); + if (nskb) { + l2->down_id = mISDN_HEAD_ID(nskb); + if (l2down_skb(l2, nskb)) { + dev_kfree_skb(nskb); + l2->down_id = MISDN_ID_NONE; + } + } else + l2->down_id = MISDN_ID_NONE; + if (ret) { + dev_kfree_skb(skb); + ret = 0; + } + if (l2->down_id == MISDN_ID_NONE) { + test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag); + mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL); + } + } + } + if (!test_and_set_bit(FLG_L1_NOTREADY, &l2->flag)) { + nskb = skb_dequeue(&l2->down_queue); + if (nskb) { + l2->down_id = mISDN_HEAD_ID(nskb); + if (l2down_skb(l2, nskb)) { + dev_kfree_skb(nskb); + l2->down_id = MISDN_ID_NONE; + test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag); + } + } else + test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag); + } + return ret; +} + +static int +l2mgr(struct layer2 *l2, u_int prim, void *arg) { + long c = (long)arg; + + printk(KERN_WARNING + "l2mgr: addr:%x prim %x %c\n", l2->id, prim, (char)c); + if (test_bit(FLG_LAPD, &l2->flag) && + !test_bit(FLG_FIXED_TEI, &l2->flag)) { + switch (c) { + case 'C': + case 'D': + case 'G': + case 'H': + l2_tei(l2, prim, (u_long)arg); + break; + } + } + return 0; +} + +static void +set_peer_busy(struct layer2 *l2) { + test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + if (skb_queue_len(&l2->i_queue) || skb_queue_len(&l2->ui_queue)) + test_and_set_bit(FLG_L2BLOCK, &l2->flag); +} + +static void +clear_peer_busy(struct layer2 *l2) { + if (test_and_clear_bit(FLG_PEER_BUSY, &l2->flag)) + test_and_clear_bit(FLG_L2BLOCK, &l2->flag); +} + +static void +InitWin(struct layer2 *l2) +{ + int i; + + for (i = 0; i < MAX_WINDOW; i++) + l2->windowar[i] = NULL; +} + +static int +freewin(struct layer2 *l2) +{ + int i, cnt = 0; + + for (i = 0; i < MAX_WINDOW; i++) { + if (l2->windowar[i]) { + cnt++; + dev_kfree_skb(l2->windowar[i]); + l2->windowar[i] = NULL; + } + } + return cnt; +} + +static void +ReleaseWin(struct layer2 *l2) +{ + int cnt = freewin(l2); + + if (cnt) + printk(KERN_WARNING + "isdnl2 freed %d skbuffs in release\n", cnt); +} + +inline unsigned int +cansend(struct layer2 *l2) +{ + unsigned int p1; + + if (test_bit(FLG_MOD128, &l2->flag)) + p1 = (l2->vs - l2->va) % 128; + else + p1 = (l2->vs - l2->va) % 8; + return (p1 < l2->window) && !test_bit(FLG_PEER_BUSY, &l2->flag); +} + +inline void +clear_exception(struct layer2 *l2) +{ + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + test_and_clear_bit(FLG_REJEXC, &l2->flag); + test_and_clear_bit(FLG_OWN_BUSY, &l2->flag); + clear_peer_busy(l2); +} + +static int +sethdraddr(struct layer2 *l2, u_char *header, int rsp) +{ + u_char *ptr = header; + int crbit = rsp; + + if (test_bit(FLG_LAPD, &l2->flag)) { + if (test_bit(FLG_LAPD_NET, &l2->flag)) + crbit = !crbit; + *ptr++ = (l2->sapi << 2) | (crbit ? 2 : 0); + *ptr++ = (l2->tei << 1) | 1; + return 2; + } else { + if (test_bit(FLG_ORIG, &l2->flag)) + crbit = !crbit; + if (crbit) + *ptr++ = l2->addr.B; + else + *ptr++ = l2->addr.A; + return 1; + } +} + +static inline void +enqueue_super(struct layer2 *l2, struct sk_buff *skb) +{ + if (l2down(l2, PH_DATA_REQ, l2_newid(l2), skb)) + dev_kfree_skb(skb); +} + +static inline void +enqueue_ui(struct layer2 *l2, struct sk_buff *skb) +{ + if (l2->tm) + l2_tei(l2, MDL_STATUS_UI_IND, 0); + if (l2down(l2, PH_DATA_REQ, l2_newid(l2), skb)) + dev_kfree_skb(skb); +} + +inline int +IsUI(u_char *data) +{ + return (data[0] & 0xef) == UI; +} + +inline int +IsUA(u_char *data) +{ + return (data[0] & 0xef) == UA; +} + +inline int +IsDM(u_char *data) +{ + return (data[0] & 0xef) == DM; +} + +inline int +IsDISC(u_char *data) +{ + return (data[0] & 0xef) == DISC; +} + +inline int +IsRR(u_char *data, struct layer2 *l2) +{ + if (test_bit(FLG_MOD128, &l2->flag)) + return data[0] == RR; + else + return (data[0] & 0xf) == 1; +} + +inline int +IsSFrame(u_char *data, struct layer2 *l2) +{ + register u_char d = *data; + + if (!test_bit(FLG_MOD128, &l2->flag)) + d &= 0xf; + return ((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c); +} + +inline int +IsSABME(u_char *data, struct layer2 *l2) +{ + u_char d = data[0] & ~0x10; + + return test_bit(FLG_MOD128, &l2->flag) ? d == SABME : d == SABM; +} + +inline int +IsREJ(u_char *data, struct layer2 *l2) +{ + return test_bit(FLG_MOD128, &l2->flag) ? + data[0] == REJ : (data[0] & 0xf) == REJ; +} + +inline int +IsFRMR(u_char *data) +{ + return (data[0] & 0xef) == FRMR; +} + +inline int +IsRNR(u_char *data, struct layer2 *l2) +{ + return test_bit(FLG_MOD128, &l2->flag) ? + data[0] == RNR : (data[0] & 0xf) == RNR; +} + +int +iframe_error(struct layer2 *l2, struct sk_buff *skb) +{ + u_int i; + int rsp = *skb->data & 0x2; + + i = l2addrsize(l2) + (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1); + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (rsp) + return 'L'; + if (skb->len < i) + return 'N'; + if ((skb->len - i) > l2->maxlen) + return 'O'; + return 0; +} + +int +super_error(struct layer2 *l2, struct sk_buff *skb) +{ + if (skb->len != l2addrsize(l2) + + (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1)) + return 'N'; + return 0; +} + +int +unnum_error(struct layer2 *l2, struct sk_buff *skb, int wantrsp) +{ + int rsp = (*skb->data & 0x2) >> 1; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (rsp != wantrsp) + return 'L'; + if (skb->len != l2addrsize(l2) + 1) + return 'N'; + return 0; +} + +int +UI_error(struct layer2 *l2, struct sk_buff *skb) +{ + int rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (rsp) + return 'L'; + if (skb->len > l2->maxlen + l2addrsize(l2) + 1) + return 'O'; + return 0; +} + +int +FRMR_error(struct layer2 *l2, struct sk_buff *skb) +{ + u_int headers = l2addrsize(l2) + 1; + u_char *datap = skb->data + headers; + int rsp = *skb->data & 0x2; + + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (!rsp) + return 'L'; + if (test_bit(FLG_MOD128, &l2->flag)) { + if (skb->len < headers + 5) + return 'N'; + else if (*debug & DEBUG_L2) + l2m_debug(&l2->l2m, + "FRMR information %2x %2x %2x %2x %2x", + datap[0], datap[1], datap[2], datap[3], datap[4]); + } else { + if (skb->len < headers + 3) + return 'N'; + else if (*debug & DEBUG_L2) + l2m_debug(&l2->l2m, + "FRMR information %2x %2x %2x", + datap[0], datap[1], datap[2]); + } + return 0; +} + +static unsigned int +legalnr(struct layer2 *l2, unsigned int nr) +{ + if (test_bit(FLG_MOD128, &l2->flag)) + return ((nr - l2->va) % 128) <= ((l2->vs - l2->va) % 128); + else + return ((nr - l2->va) % 8) <= ((l2->vs - l2->va) % 8); +} + +static void +setva(struct layer2 *l2, unsigned int nr) +{ + struct sk_buff *skb; + + while (l2->va != nr) { + l2->va++; + if (test_bit(FLG_MOD128, &l2->flag)) + l2->va %= 128; + else + l2->va %= 8; + if (l2->windowar[l2->sow]) { + skb_trim(l2->windowar[l2->sow], 0); + skb_queue_tail(&l2->tmp_queue, l2->windowar[l2->sow]); + l2->windowar[l2->sow] = NULL; + } + l2->sow = (l2->sow + 1) % l2->window; + } + skb = skb_dequeue(&l2->tmp_queue); + while (skb) { + dev_kfree_skb(skb); + skb = skb_dequeue(&l2->tmp_queue); + } +} + +static void +send_uframe(struct layer2 *l2, struct sk_buff *skb, u_char cmd, u_char cr) +{ + u_char tmp[MAX_L2HEADER_LEN]; + int i; + + i = sethdraddr(l2, tmp, cr); + tmp[i++] = cmd; + if (skb) + skb_trim(skb, 0); + else { + skb = mI_alloc_skb(i, GFP_ATOMIC); + if (!skb) { + printk(KERN_WARNING "%s: can't alloc skbuff\n", + __func__); + return; + } + } + memcpy(skb_put(skb, i), tmp, i); + enqueue_super(l2, skb); +} + + +inline u_char +get_PollFlag(struct layer2 *l2, struct sk_buff *skb) +{ + return skb->data[l2addrsize(l2)] & 0x10; +} + +inline u_char +get_PollFlagFree(struct layer2 *l2, struct sk_buff *skb) +{ + u_char PF; + + PF = get_PollFlag(l2, skb); + dev_kfree_skb(skb); + return PF; +} + +inline void +start_t200(struct layer2 *l2, int i) +{ + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i); + test_and_set_bit(FLG_T200_RUN, &l2->flag); +} + +inline void +restart_t200(struct layer2 *l2, int i) +{ + mISDN_FsmRestartTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i); + test_and_set_bit(FLG_T200_RUN, &l2->flag); +} + +inline void +stop_t200(struct layer2 *l2, int i) +{ + if (test_and_clear_bit(FLG_T200_RUN, &l2->flag)) + mISDN_FsmDelTimer(&l2->t200, i); +} + +inline void +st5_dl_release_l2l3(struct layer2 *l2) +{ + int pr; + + if (test_and_clear_bit(FLG_PEND_REL, &l2->flag)) + pr = DL_RELEASE_CNF; + else + pr = DL_RELEASE_IND; + l2up_create(l2, pr, 0, NULL); +} + +inline void +lapb_dl_release_l2l3(struct layer2 *l2, int f) +{ + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE_REQ, l2_newid(l2), 0, NULL); + l2up_create(l2, f, 0, NULL); +} + +static void +establishlink(struct FsmInst *fi) +{ + struct layer2 *l2 = fi->userdata; + u_char cmd; + + clear_exception(l2); + l2->rc = 0; + cmd = (test_bit(FLG_MOD128, &l2->flag) ? SABME : SABM) | 0x10; + send_uframe(l2, NULL, cmd, CMD); + mISDN_FsmDelTimer(&l2->t203, 1); + restart_t200(l2, 1); + test_and_clear_bit(FLG_PEND_REL, &l2->flag); + freewin(l2); + mISDN_FsmChangeState(fi, ST_L2_5); +} + +static void +l2_mdl_error_ua(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + if (get_PollFlagFree(l2, skb)) + l2mgr(l2, MDL_ERROR_IND, (void *) 'C'); + else + l2mgr(l2, MDL_ERROR_IND, (void *) 'D'); + +} + +static void +l2_mdl_error_dm(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + if (get_PollFlagFree(l2, skb)) + l2mgr(l2, MDL_ERROR_IND, (void *) 'B'); + else { + l2mgr(l2, MDL_ERROR_IND, (void *) 'E'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); + } +} + +static void +l2_st8_mdl_error_dm(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + if (get_PollFlagFree(l2, skb)) + l2mgr(l2, MDL_ERROR_IND, (void *) 'B'); + else + l2mgr(l2, MDL_ERROR_IND, (void *) 'E'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); +} + +static void +l2_go_st3(struct FsmInst *fi, int event, void *arg) +{ + dev_kfree_skb((struct sk_buff *)arg); + mISDN_FsmChangeState(fi, ST_L2_3); +} + +static void +l2_mdl_assign(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L2_3); + dev_kfree_skb((struct sk_buff *)arg); + l2_tei(l2, MDL_ASSIGN_IND, 0); +} + +static void +l2_queue_ui_assign(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->ui_queue, skb); + mISDN_FsmChangeState(fi, ST_L2_2); + l2_tei(l2, MDL_ASSIGN_IND, 0); +} + +static void +l2_queue_ui(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->ui_queue, skb); +} + +static void +tx_ui(struct layer2 *l2) +{ + struct sk_buff *skb; + u_char header[MAX_L2HEADER_LEN]; + int i; + + i = sethdraddr(l2, header, CMD); + if (test_bit(FLG_LAPD_NET, &l2->flag)) + header[1] = 0xff; /* tei 127 */ + header[i++] = UI; + while ((skb = skb_dequeue(&l2->ui_queue))) { + memcpy(skb_push(skb, i), header, i); + enqueue_ui(l2, skb); + } +} + +static void +l2_send_ui(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->ui_queue, skb); + tx_ui(l2); +} + +static void +l2_got_ui(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_pull(skb, l2headersize(l2, 1)); +/* + * in states 1-3 for broadcast + */ + + if (l2->tm) + l2_tei(l2, MDL_STATUS_UI_IND, 0); + l2up(l2, DL_UNITDATA_IND, skb); +} + +static void +l2_establish(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + dev_kfree_skb(skb); +} + +static void +l2_discard_i_setl3(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->i_queue); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + test_and_clear_bit(FLG_PEND_REL, &l2->flag); + dev_kfree_skb(skb); +} + +static void +l2_l3_reestablish(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->i_queue); + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + dev_kfree_skb(skb); +} + +static void +l2_release(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_trim(skb, 0); + l2up(l2, DL_RELEASE_CNF, skb); +} + +static void +l2_pend_rel(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + test_and_set_bit(FLG_PEND_REL, &l2->flag); + dev_kfree_skb(skb); +} + +static void +l2_disconnect(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_purge(&l2->i_queue); + freewin(l2); + mISDN_FsmChangeState(fi, ST_L2_6); + l2->rc = 0; + send_uframe(l2, NULL, DISC | 0x10, CMD); + mISDN_FsmDelTimer(&l2->t203, 1); + restart_t200(l2, 2); + if (skb) + dev_kfree_skb(skb); +} + +static void +l2_start_multi(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + l2->vs = 0; + l2->va = 0; + l2->vr = 0; + l2->sow = 0; + clear_exception(l2); + send_uframe(l2, NULL, UA | get_PollFlag(l2, skb), RSP); + mISDN_FsmChangeState(fi, ST_L2_7); + mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3); + skb_trim(skb, 0); + l2up(l2, DL_ESTABLISH_IND, skb); + if (l2->tm) + l2_tei(l2, MDL_STATUS_UP_IND, 0); +} + +static void +l2_send_UA(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); +} + +static void +l2_send_DM(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + send_uframe(l2, skb, DM | get_PollFlag(l2, skb), RSP); +} + +static void +l2_restart_multi(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + int est = 0; + + send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); + + l2mgr(l2, MDL_ERROR_IND, (void *) 'F'); + + if (l2->vs != l2->va) { + skb_queue_purge(&l2->i_queue); + est = 1; + } + + clear_exception(l2); + l2->vs = 0; + l2->va = 0; + l2->vr = 0; + l2->sow = 0; + mISDN_FsmChangeState(fi, ST_L2_7); + stop_t200(l2, 3); + mISDN_FsmRestartTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3); + + if (est) + l2up_create(l2, DL_ESTABLISH_IND, 0, NULL); +/* mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST, + * MGR_SHORTSTATUS | INDICATION, SSTATUS_L2_ESTABLISHED, + * 0, NULL, 0); + */ + if (skb_queue_len(&l2->i_queue) && cansend(l2)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); +} + +static void +l2_stop_multi(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + mISDN_FsmChangeState(fi, ST_L2_4); + mISDN_FsmDelTimer(&l2->t203, 3); + stop_t200(l2, 4); + + send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); + skb_queue_purge(&l2->i_queue); + freewin(l2); + lapb_dl_release_l2l3(l2, DL_RELEASE_IND); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); +} + +static void +l2_connected(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + int pr = -1; + + if (!get_PollFlag(l2, skb)) { + l2_mdl_error_ua(fi, event, arg); + return; + } + dev_kfree_skb(skb); + if (test_and_clear_bit(FLG_PEND_REL, &l2->flag)) + l2_disconnect(fi, event, NULL); + if (test_and_clear_bit(FLG_L3_INIT, &l2->flag)) { + pr = DL_ESTABLISH_CNF; + } else if (l2->vs != l2->va) { + skb_queue_purge(&l2->i_queue); + pr = DL_ESTABLISH_IND; + } + stop_t200(l2, 5); + l2->vr = 0; + l2->vs = 0; + l2->va = 0; + l2->sow = 0; + mISDN_FsmChangeState(fi, ST_L2_7); + mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 4); + if (pr != -1) + l2up_create(l2, pr, 0, NULL); + + if (skb_queue_len(&l2->i_queue) && cansend(l2)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); + + if (l2->tm) + l2_tei(l2, MDL_STATUS_UP_IND, 0); +} + +static void +l2_released(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (!get_PollFlag(l2, skb)) { + l2_mdl_error_ua(fi, event, arg); + return; + } + dev_kfree_skb(skb); + stop_t200(l2, 6); + lapb_dl_release_l2l3(l2, DL_RELEASE_CNF); + mISDN_FsmChangeState(fi, ST_L2_4); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); +} + +static void +l2_reestablish(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (!get_PollFlagFree(l2, skb)) { + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + } +} + +static void +l2_st5_dm_release(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (get_PollFlagFree(l2, skb)) { + stop_t200(l2, 7); + if (!test_bit(FLG_L3_INIT, &l2->flag)) + skb_queue_purge(&l2->i_queue); + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE_REQ, + l2_newid(l2), 0, NULL); + st5_dl_release_l2l3(l2); + mISDN_FsmChangeState(fi, ST_L2_4); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); + } +} + +static void +l2_st6_dm_release(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (get_PollFlagFree(l2, skb)) { + stop_t200(l2, 8); + lapb_dl_release_l2l3(l2, DL_RELEASE_CNF); + mISDN_FsmChangeState(fi, ST_L2_4); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); + } +} + +void +enquiry_cr(struct layer2 *l2, u_char typ, u_char cr, u_char pf) +{ + struct sk_buff *skb; + u_char tmp[MAX_L2HEADER_LEN]; + int i; + + i = sethdraddr(l2, tmp, cr); + if (test_bit(FLG_MOD128, &l2->flag)) { + tmp[i++] = typ; + tmp[i++] = (l2->vr << 1) | (pf ? 1 : 0); + } else + tmp[i++] = (l2->vr << 5) | typ | (pf ? 0x10 : 0); + skb = mI_alloc_skb(i, GFP_ATOMIC); + if (!skb) { + printk(KERN_WARNING + "isdnl2 can't alloc sbbuff for enquiry_cr\n"); + return; + } + memcpy(skb_put(skb, i), tmp, i); + enqueue_super(l2, skb); +} + +inline void +enquiry_response(struct layer2 *l2) +{ + if (test_bit(FLG_OWN_BUSY, &l2->flag)) + enquiry_cr(l2, RNR, RSP, 1); + else + enquiry_cr(l2, RR, RSP, 1); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); +} + +inline void +transmit_enquiry(struct layer2 *l2) +{ + if (test_bit(FLG_OWN_BUSY, &l2->flag)) + enquiry_cr(l2, RNR, CMD, 1); + else + enquiry_cr(l2, RR, CMD, 1); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + start_t200(l2, 9); +} + + +static void +nrerrorrecovery(struct FsmInst *fi) +{ + struct layer2 *l2 = fi->userdata; + + l2mgr(l2, MDL_ERROR_IND, (void *) 'J'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); +} + +static void +invoke_retransmission(struct layer2 *l2, unsigned int nr) +{ + u_int p1; + + if (l2->vs != nr) { + while (l2->vs != nr) { + (l2->vs)--; + if (test_bit(FLG_MOD128, &l2->flag)) { + l2->vs %= 128; + p1 = (l2->vs - l2->va) % 128; + } else { + l2->vs %= 8; + p1 = (l2->vs - l2->va) % 8; + } + p1 = (p1 + l2->sow) % l2->window; + if (l2->windowar[p1]) + skb_queue_head(&l2->i_queue, l2->windowar[p1]); + else + printk(KERN_WARNING + "%s: windowar[%d] is NULL\n", + __func__, p1); + l2->windowar[p1] = NULL; + } + mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL); + } +} + +static void +l2_st7_got_super(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + int PollFlag, rsp, typ = RR; + unsigned int nr; + + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + + skb_pull(skb, l2addrsize(l2)); + if (IsRNR(skb->data, l2)) { + set_peer_busy(l2); + typ = RNR; + } else + clear_peer_busy(l2); + if (IsREJ(skb->data, l2)) + typ = REJ; + + if (test_bit(FLG_MOD128, &l2->flag)) { + PollFlag = (skb->data[1] & 0x1) == 0x1; + nr = skb->data[1] >> 1; + } else { + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; + } + dev_kfree_skb(skb); + + if (PollFlag) { + if (rsp) + l2mgr(l2, MDL_ERROR_IND, (void *) 'A'); + else + enquiry_response(l2); + } + if (legalnr(l2, nr)) { + if (typ == REJ) { + setva(l2, nr); + invoke_retransmission(l2, nr); + stop_t200(l2, 10); + if (mISDN_FsmAddTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 6)) + l2m_debug(&l2->l2m, "Restart T203 ST7 REJ"); + } else if ((nr == l2->vs) && (typ == RR)) { + setva(l2, nr); + stop_t200(l2, 11); + mISDN_FsmRestartTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 7); + } else if ((l2->va != nr) || (typ == RNR)) { + setva(l2, nr); + if (typ != RR) + mISDN_FsmDelTimer(&l2->t203, 9); + restart_t200(l2, 12); + } + if (skb_queue_len(&l2->i_queue) && (typ == RR)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); + } else + nrerrorrecovery(fi); +} + +static void +l2_feed_i_if_reest(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (!test_bit(FLG_L3_INIT, &l2->flag)) + skb_queue_tail(&l2->i_queue, skb); + else + dev_kfree_skb(skb); +} + +static void +l2_feed_i_pull(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->i_queue, skb); + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); +} + +static void +l2_feed_iqueue(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->i_queue, skb); +} + +static void +l2_got_iframe(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + int PollFlag, i; + u_int ns, nr; + + i = l2addrsize(l2); + if (test_bit(FLG_MOD128, &l2->flag)) { + PollFlag = ((skb->data[i + 1] & 0x1) == 0x1); + ns = skb->data[i] >> 1; + nr = (skb->data[i + 1] >> 1) & 0x7f; + } else { + PollFlag = (skb->data[i] & 0x10); + ns = (skb->data[i] >> 1) & 0x7; + nr = (skb->data[i] >> 5) & 0x7; + } + if (test_bit(FLG_OWN_BUSY, &l2->flag)) { + dev_kfree_skb(skb); + if (PollFlag) + enquiry_response(l2); + } else { + if (l2->vr == ns) { + l2->vr++; + if (test_bit(FLG_MOD128, &l2->flag)) + l2->vr %= 128; + else + l2->vr %= 8; + test_and_clear_bit(FLG_REJEXC, &l2->flag); + if (PollFlag) + enquiry_response(l2); + else + test_and_set_bit(FLG_ACK_PEND, &l2->flag); + skb_pull(skb, l2headersize(l2, 0)); + l2up(l2, DL_DATA_IND, skb); + } else { + /* n(s)!=v(r) */ + dev_kfree_skb(skb); + if (test_and_set_bit(FLG_REJEXC, &l2->flag)) { + if (PollFlag) + enquiry_response(l2); + } else { + enquiry_cr(l2, REJ, RSP, PollFlag); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + } + } + } + if (legalnr(l2, nr)) { + if (!test_bit(FLG_PEER_BUSY, &l2->flag) && + (fi->state == ST_L2_7)) { + if (nr == l2->vs) { + stop_t200(l2, 13); + mISDN_FsmRestartTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 7); + } else if (nr != l2->va) + restart_t200(l2, 14); + } + setva(l2, nr); + } else { + nrerrorrecovery(fi); + return; + } + if (skb_queue_len(&l2->i_queue) && (fi->state == ST_L2_7)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); + if (test_and_clear_bit(FLG_ACK_PEND, &l2->flag)) + enquiry_cr(l2, RR, RSP, 0); +} + +static void +l2_got_tei(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + u_int info; + + l2->tei = (signed char)(long)arg; + set_channel_address(&l2->ch, l2->sapi, l2->tei); + info = DL_INFO_L2_CONNECT; + l2up_create(l2, DL_INFORMATION_IND, sizeof(info), &info); + if (fi->state == ST_L2_3) { + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + } else + mISDN_FsmChangeState(fi, ST_L2_4); + if (skb_queue_len(&l2->ui_queue)) + tx_ui(l2); +} + +static void +l2_st5_tout_200(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + } else if (l2->rc == l2->N200) { + mISDN_FsmChangeState(fi, ST_L2_4); + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + skb_queue_purge(&l2->i_queue); + l2mgr(l2, MDL_ERROR_IND, (void *) 'G'); + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE_REQ, + l2_newid(l2), 0, NULL); + st5_dl_release_l2l3(l2); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); + } else { + l2->rc++; + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + send_uframe(l2, NULL, (test_bit(FLG_MOD128, &l2->flag) ? + SABME : SABM) | 0x10, CMD); + } +} + +static void +l2_st6_tout_200(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + } else if (l2->rc == l2->N200) { + mISDN_FsmChangeState(fi, ST_L2_4); + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + l2mgr(l2, MDL_ERROR_IND, (void *) 'H'); + lapb_dl_release_l2l3(l2, DL_RELEASE_CNF); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); + } else { + l2->rc++; + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, + NULL, 9); + send_uframe(l2, NULL, DISC | 0x10, CMD); + } +} + +static void +l2_st7_tout_200(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + return; + } + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + l2->rc = 0; + mISDN_FsmChangeState(fi, ST_L2_8); + transmit_enquiry(l2); + l2->rc++; +} + +static void +l2_st8_tout_200(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + return; + } + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + if (l2->rc == l2->N200) { + l2mgr(l2, MDL_ERROR_IND, (void *) 'I'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); + } else { + transmit_enquiry(l2); + l2->rc++; + } +} + +static void +l2_st7_tout_203(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 9); + return; + } + mISDN_FsmChangeState(fi, ST_L2_8); + transmit_enquiry(l2); + l2->rc = 0; +} + +static void +l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb, *nskb, *oskb; + u_char header[MAX_L2HEADER_LEN]; + u_int i, p1; + + if (!cansend(l2)) + return; + + skb = skb_dequeue(&l2->i_queue); + if (!skb) + return; + + if (test_bit(FLG_MOD128, &l2->flag)) + p1 = (l2->vs - l2->va) % 128; + else + p1 = (l2->vs - l2->va) % 8; + p1 = (p1 + l2->sow) % l2->window; + if (l2->windowar[p1]) { + printk(KERN_WARNING "isdnl2 try overwrite ack queue entry %d\n", + p1); + dev_kfree_skb(l2->windowar[p1]); + } + l2->windowar[p1] = skb; + i = sethdraddr(l2, header, CMD); + if (test_bit(FLG_MOD128, &l2->flag)) { + header[i++] = l2->vs << 1; + header[i++] = l2->vr << 1; + l2->vs = (l2->vs + 1) % 128; + } else { + header[i++] = (l2->vr << 5) | (l2->vs << 1); + l2->vs = (l2->vs + 1) % 8; + } + + nskb = skb_clone(skb, GFP_ATOMIC); + p1 = skb_headroom(nskb); + if (p1 >= i) + memcpy(skb_push(nskb, i), header, i); + else { + printk(KERN_WARNING + "isdnl2 pull_iqueue skb header(%d/%d) too short\n", i, p1); + oskb = nskb; + nskb = mI_alloc_skb(oskb->len + i, GFP_ATOMIC); + if (!nskb) { + dev_kfree_skb(oskb); + printk(KERN_WARNING "%s: no skb mem\n", __func__); + return; + } + memcpy(skb_put(nskb, i), header, i); + memcpy(skb_put(nskb, oskb->len), oskb->data, oskb->len); + dev_kfree_skb(oskb); + } + l2down(l2, PH_DATA_REQ, l2_newid(l2), nskb); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + if (!test_and_set_bit(FLG_T200_RUN, &l2->flag)) { + mISDN_FsmDelTimer(&l2->t203, 13); + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 11); + } +} + +static void +l2_st8_got_super(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + int PollFlag, rsp, rnr = 0; + unsigned int nr; + + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + + skb_pull(skb, l2addrsize(l2)); + + if (IsRNR(skb->data, l2)) { + set_peer_busy(l2); + rnr = 1; + } else + clear_peer_busy(l2); + + if (test_bit(FLG_MOD128, &l2->flag)) { + PollFlag = (skb->data[1] & 0x1) == 0x1; + nr = skb->data[1] >> 1; + } else { + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; + } + dev_kfree_skb(skb); + if (rsp && PollFlag) { + if (legalnr(l2, nr)) { + if (rnr) { + restart_t200(l2, 15); + } else { + stop_t200(l2, 16); + mISDN_FsmAddTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 5); + setva(l2, nr); + } + invoke_retransmission(l2, nr); + mISDN_FsmChangeState(fi, ST_L2_7); + if (skb_queue_len(&l2->i_queue) && cansend(l2)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); + } else + nrerrorrecovery(fi); + } else { + if (!rsp && PollFlag) + enquiry_response(l2); + if (legalnr(l2, nr)) + setva(l2, nr); + else + nrerrorrecovery(fi); + } +} + +static void +l2_got_FRMR(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_pull(skb, l2addrsize(l2) + 1); + + if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) || /* I or S */ + (IsUA(skb->data) && (fi->state == ST_L2_7))) { + l2mgr(l2, MDL_ERROR_IND, (void *) 'K'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); + } + dev_kfree_skb(skb); +} + +static void +l2_st24_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->ui_queue); + l2->tei = GROUP_TEI; + mISDN_FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st3_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->ui_queue); + l2->tei = GROUP_TEI; + l2up_create(l2, DL_RELEASE_IND, 0, NULL); + mISDN_FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st5_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->i_queue); + skb_queue_purge(&l2->ui_queue); + freewin(l2); + l2->tei = GROUP_TEI; + stop_t200(l2, 17); + st5_dl_release_l2l3(l2); + mISDN_FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st6_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->ui_queue); + l2->tei = GROUP_TEI; + stop_t200(l2, 18); + l2up_create(l2, DL_RELEASE_IND, 0, NULL); + mISDN_FsmChangeState(fi, ST_L2_1); +} + +static void +l2_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->i_queue); + skb_queue_purge(&l2->ui_queue); + freewin(l2); + l2->tei = GROUP_TEI; + stop_t200(l2, 17); + mISDN_FsmDelTimer(&l2->t203, 19); + l2up_create(l2, DL_RELEASE_IND, 0, NULL); +/* mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST, + * MGR_SHORTSTATUS_IND, SSTATUS_L2_RELEASED, + * 0, NULL, 0); + */ + mISDN_FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st14_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_purge(&l2->i_queue); + skb_queue_purge(&l2->ui_queue); + if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag)) + l2up(l2, DL_RELEASE_IND, skb); + else + dev_kfree_skb(skb); +} + +static void +l2_st5_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_purge(&l2->i_queue); + skb_queue_purge(&l2->ui_queue); + freewin(l2); + stop_t200(l2, 19); + st5_dl_release_l2l3(l2); + mISDN_FsmChangeState(fi, ST_L2_4); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); + dev_kfree_skb(skb); +} + +static void +l2_st6_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_purge(&l2->ui_queue); + stop_t200(l2, 20); + l2up(l2, DL_RELEASE_CNF, skb); + mISDN_FsmChangeState(fi, ST_L2_4); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); +} + +static void +l2_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_purge(&l2->i_queue); + skb_queue_purge(&l2->ui_queue); + freewin(l2); + stop_t200(l2, 19); + mISDN_FsmDelTimer(&l2->t203, 19); + l2up(l2, DL_RELEASE_IND, skb); + mISDN_FsmChangeState(fi, ST_L2_4); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); +} + +static void +l2_set_own_busy(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (!test_and_set_bit(FLG_OWN_BUSY, &l2->flag)) { + enquiry_cr(l2, RNR, RSP, 0); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + } + if (skb) + dev_kfree_skb(skb); +} + +static void +l2_clear_own_busy(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (!test_and_clear_bit(FLG_OWN_BUSY, &l2->flag)) { + enquiry_cr(l2, RR, RSP, 0); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + } + if (skb) + dev_kfree_skb(skb); +} + +static void +l2_frame_error(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + l2mgr(l2, MDL_ERROR_IND, arg); +} + +static void +l2_frame_error_reest(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + l2mgr(l2, MDL_ERROR_IND, arg); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); +} + +static struct FsmNode L2FnList[] = +{ + {ST_L2_1, EV_L2_DL_ESTABLISH_REQ, l2_mdl_assign}, + {ST_L2_2, EV_L2_DL_ESTABLISH_REQ, l2_go_st3}, + {ST_L2_4, EV_L2_DL_ESTABLISH_REQ, l2_establish}, + {ST_L2_5, EV_L2_DL_ESTABLISH_REQ, l2_discard_i_setl3}, + {ST_L2_7, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, + {ST_L2_8, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, + {ST_L2_4, EV_L2_DL_RELEASE_REQ, l2_release}, + {ST_L2_5, EV_L2_DL_RELEASE_REQ, l2_pend_rel}, + {ST_L2_7, EV_L2_DL_RELEASE_REQ, l2_disconnect}, + {ST_L2_8, EV_L2_DL_RELEASE_REQ, l2_disconnect}, + {ST_L2_5, EV_L2_DL_DATA, l2_feed_i_if_reest}, + {ST_L2_7, EV_L2_DL_DATA, l2_feed_i_pull}, + {ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue}, + {ST_L2_1, EV_L2_DL_UNITDATA, l2_queue_ui_assign}, + {ST_L2_2, EV_L2_DL_UNITDATA, l2_queue_ui}, + {ST_L2_3, EV_L2_DL_UNITDATA, l2_queue_ui}, + {ST_L2_4, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_5, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_6, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_7, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_8, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_1, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_2, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_2, EV_L2_MDL_ERROR, l2_st24_tei_remove}, + {ST_L2_3, EV_L2_MDL_ERROR, l2_st3_tei_remove}, + {ST_L2_4, EV_L2_MDL_REMOVE, l2_st24_tei_remove}, + {ST_L2_5, EV_L2_MDL_REMOVE, l2_st5_tei_remove}, + {ST_L2_6, EV_L2_MDL_REMOVE, l2_st6_tei_remove}, + {ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_4, EV_L2_SABME, l2_start_multi}, + {ST_L2_5, EV_L2_SABME, l2_send_UA}, + {ST_L2_6, EV_L2_SABME, l2_send_DM}, + {ST_L2_7, EV_L2_SABME, l2_restart_multi}, + {ST_L2_8, EV_L2_SABME, l2_restart_multi}, + {ST_L2_4, EV_L2_DISC, l2_send_DM}, + {ST_L2_5, EV_L2_DISC, l2_send_DM}, + {ST_L2_6, EV_L2_DISC, l2_send_UA}, + {ST_L2_7, EV_L2_DISC, l2_stop_multi}, + {ST_L2_8, EV_L2_DISC, l2_stop_multi}, + {ST_L2_4, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_5, EV_L2_UA, l2_connected}, + {ST_L2_6, EV_L2_UA, l2_released}, + {ST_L2_7, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_8, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_4, EV_L2_DM, l2_reestablish}, + {ST_L2_5, EV_L2_DM, l2_st5_dm_release}, + {ST_L2_6, EV_L2_DM, l2_st6_dm_release}, + {ST_L2_7, EV_L2_DM, l2_mdl_error_dm}, + {ST_L2_8, EV_L2_DM, l2_st8_mdl_error_dm}, + {ST_L2_1, EV_L2_UI, l2_got_ui}, + {ST_L2_2, EV_L2_UI, l2_got_ui}, + {ST_L2_3, EV_L2_UI, l2_got_ui}, + {ST_L2_4, EV_L2_UI, l2_got_ui}, + {ST_L2_5, EV_L2_UI, l2_got_ui}, + {ST_L2_6, EV_L2_UI, l2_got_ui}, + {ST_L2_7, EV_L2_UI, l2_got_ui}, + {ST_L2_8, EV_L2_UI, l2_got_ui}, + {ST_L2_7, EV_L2_FRMR, l2_got_FRMR}, + {ST_L2_8, EV_L2_FRMR, l2_got_FRMR}, + {ST_L2_7, EV_L2_SUPER, l2_st7_got_super}, + {ST_L2_8, EV_L2_SUPER, l2_st8_got_super}, + {ST_L2_7, EV_L2_I, l2_got_iframe}, + {ST_L2_8, EV_L2_I, l2_got_iframe}, + {ST_L2_5, EV_L2_T200, l2_st5_tout_200}, + {ST_L2_6, EV_L2_T200, l2_st6_tout_200}, + {ST_L2_7, EV_L2_T200, l2_st7_tout_200}, + {ST_L2_8, EV_L2_T200, l2_st8_tout_200}, + {ST_L2_7, EV_L2_T203, l2_st7_tout_203}, + {ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue}, + {ST_L2_7, EV_L2_SET_OWN_BUSY, l2_set_own_busy}, + {ST_L2_8, EV_L2_SET_OWN_BUSY, l2_set_own_busy}, + {ST_L2_7, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, + {ST_L2_8, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, + {ST_L2_4, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_5, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_6, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_7, EV_L2_FRAME_ERROR, l2_frame_error_reest}, + {ST_L2_8, EV_L2_FRAME_ERROR, l2_frame_error_reest}, + {ST_L2_1, EV_L1_DEACTIVATE, l2_st14_persistant_da}, + {ST_L2_2, EV_L1_DEACTIVATE, l2_st24_tei_remove}, + {ST_L2_3, EV_L1_DEACTIVATE, l2_st3_tei_remove}, + {ST_L2_4, EV_L1_DEACTIVATE, l2_st14_persistant_da}, + {ST_L2_5, EV_L1_DEACTIVATE, l2_st5_persistant_da}, + {ST_L2_6, EV_L1_DEACTIVATE, l2_st6_persistant_da}, + {ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da}, +}; + +#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode)) + +static int +ph_data_indication(struct layer2 *l2, struct mISDNhead *hh, struct sk_buff *skb) +{ + u_char *datap = skb->data; + int ret = -EINVAL; + int psapi, ptei; + u_int l; + int c = 0; + + l = l2addrsize(l2); + if (skb->len <= l) { + mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *) 'N'); + return ret; + } + if (test_bit(FLG_LAPD, &l2->flag)) { /* Maybe not needed */ + psapi = *datap++; + ptei = *datap++; + if ((psapi & 1) || !(ptei & 1)) { + printk(KERN_WARNING + "l2 D-channel frame wrong EA0/EA1\n"); + return ret; + } + psapi >>= 2; + ptei >>= 1; + if (psapi != l2->sapi) { + /* not our bussiness + * printk(KERN_DEBUG "%s: sapi %d/%d sapi mismatch\n", + * __func__, + * psapi, l2->sapi); + */ + dev_kfree_skb(skb); + return 0; + } + if ((ptei != l2->tei) && (ptei != GROUP_TEI)) { + /* not our bussiness + * printk(KERN_DEBUG "%s: tei %d/%d sapi %d mismatch\n", + * __func__, + * ptei, l2->tei, psapi); + */ + dev_kfree_skb(skb); + return 0; + } + } else + datap += l; + if (!(*datap & 1)) { /* I-Frame */ + c = iframe_error(l2, skb); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_I, skb); + } else if (IsSFrame(datap, l2)) { /* S-Frame */ + c = super_error(l2, skb); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SUPER, skb); + } else if (IsUI(datap)) { + c = UI_error(l2, skb); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UI, skb); + } else if (IsSABME(datap, l2)) { + c = unnum_error(l2, skb, CMD); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SABME, skb); + } else if (IsUA(datap)) { + c = unnum_error(l2, skb, RSP); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UA, skb); + } else if (IsDISC(datap)) { + c = unnum_error(l2, skb, CMD); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DISC, skb); + } else if (IsDM(datap)) { + c = unnum_error(l2, skb, RSP); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DM, skb); + } else if (IsFRMR(datap)) { + c = FRMR_error(l2, skb); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_FRMR, skb); + } else + c = 'L'; + if (c) { + printk(KERN_WARNING "l2 D-channel frame error %c\n", c); + mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *)(long)c); + } + return ret; +} + +static int +l2_send(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct layer2 *l2 = container_of(ch, struct layer2, ch); + struct mISDNhead *hh = mISDN_HEAD_P(skb); + int ret = -EINVAL; + + if (*debug & DEBUG_L2_RECV) + printk(KERN_DEBUG "%s: prim(%x) id(%x) tei(%d)\n", + __func__, hh->prim, hh->id, l2->tei); + switch (hh->prim) { + case PH_DATA_IND: + ret = ph_data_indication(l2, hh, skb); + break; + case PH_DATA_CNF: + ret = ph_data_confirm(l2, hh, skb); + break; + case PH_ACTIVATE_IND: + test_and_set_bit(FLG_L1_ACTIV, &l2->flag); + l2up_create(l2, MPH_ACTIVATE_IND, 0, NULL); + if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag)) + ret = mISDN_FsmEvent(&l2->l2m, + EV_L2_DL_ESTABLISH_REQ, skb); + break; + case PH_DEACTIVATE_IND: + test_and_clear_bit(FLG_L1_ACTIV, &l2->flag); + l2up_create(l2, MPH_DEACTIVATE_IND, 0, NULL); + ret = mISDN_FsmEvent(&l2->l2m, EV_L1_DEACTIVATE, skb); + break; + case MPH_INFORMATION_IND: + if (!l2->up) + break; + ret = l2->up->send(l2->up, skb); + break; + case DL_DATA_REQ: + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_DATA, skb); + break; + case DL_UNITDATA_REQ: + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_UNITDATA, skb); + break; + case DL_ESTABLISH_REQ: + if (test_bit(FLG_LAPB, &l2->flag)) + test_and_set_bit(FLG_ORIG, &l2->flag); + if (test_bit(FLG_L1_ACTIV, &l2->flag)) { + if (test_bit(FLG_LAPD, &l2->flag) || + test_bit(FLG_ORIG, &l2->flag)) + ret = mISDN_FsmEvent(&l2->l2m, + EV_L2_DL_ESTABLISH_REQ, skb); + } else { + if (test_bit(FLG_LAPD, &l2->flag) || + test_bit(FLG_ORIG, &l2->flag)) { + test_and_set_bit(FLG_ESTAB_PEND, + &l2->flag); + } + ret = l2down(l2, PH_ACTIVATE_REQ, l2_newid(l2), + skb); + } + break; + case DL_RELEASE_REQ: + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE_REQ, + l2_newid(l2), 0, NULL); + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_RELEASE_REQ, + skb); + break; + default: + if (*debug & DEBUG_L2) + l2m_debug(&l2->l2m, "l2 unknown pr %04x", + hh->prim); + } + if (ret) { + dev_kfree_skb(skb); + ret = 0; + } + return ret; +} + +int +tei_l2(struct layer2 *l2, u_int cmd, u_long arg) +{ + int ret = -EINVAL; + + if (*debug & DEBUG_L2_TEI) + printk(KERN_DEBUG "%s: cmd(%x)\n", __func__, cmd); + switch (cmd) { + case (MDL_ASSIGN_REQ): + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ASSIGN, (void *)arg); + break; + case (MDL_REMOVE_REQ): + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_REMOVE, NULL); + break; + case (MDL_ERROR_IND): + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, NULL); + break; + case (MDL_ERROR_RSP): + /* ETS 300-125 5.3.2.1 Test: TC13010 */ + printk(KERN_NOTICE "MDL_ERROR|REQ (tei_l2)\n"); + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, NULL); + break; + } + return ret; +} + +static void +release_l2(struct layer2 *l2) +{ + mISDN_FsmDelTimer(&l2->t200, 21); + mISDN_FsmDelTimer(&l2->t203, 16); + skb_queue_purge(&l2->i_queue); + skb_queue_purge(&l2->ui_queue); + skb_queue_purge(&l2->down_queue); + ReleaseWin(l2); + if (test_bit(FLG_LAPD, &l2->flag)) { + TEIrelease(l2); + if (l2->ch.st) + l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, + CLOSE_CHANNEL, NULL); + } + kfree(l2); +} + +static int +l2_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct layer2 *l2 = container_of(ch, struct layer2, ch); + u_int info; + + if (*debug & DEBUG_L2_CTRL) + printk(KERN_DEBUG "%s:(%x)\n", __func__, cmd); + + switch (cmd) { + case OPEN_CHANNEL: + if (test_bit(FLG_LAPD, &l2->flag)) { + set_channel_address(&l2->ch, l2->sapi, l2->tei); + info = DL_INFO_L2_CONNECT; + l2up_create(l2, DL_INFORMATION_IND, + sizeof(info), &info); + } + break; + case CLOSE_CHANNEL: + if (l2->ch.peer) + l2->ch.peer->ctrl(l2->ch.peer, CLOSE_CHANNEL, NULL); + release_l2(l2); + break; + } + return 0; +} + +struct layer2 * +create_l2(struct mISDNchannel *ch, u_int protocol, u_long options, u_long arg) +{ + struct layer2 *l2; + struct channel_req rq; + + l2 = kzalloc(sizeof(struct layer2), GFP_KERNEL); + if (!l2) { + printk(KERN_ERR "kzalloc layer2 failed\n"); + return NULL; + } + l2->next_id = 1; + l2->down_id = MISDN_ID_NONE; + l2->up = ch; + l2->ch.st = ch->st; + l2->ch.send = l2_send; + l2->ch.ctrl = l2_ctrl; + switch (protocol) { + case ISDN_P_LAPD_NT: + test_and_set_bit(FLG_LAPD, &l2->flag); + test_and_set_bit(FLG_LAPD_NET, &l2->flag); + test_and_set_bit(FLG_MOD128, &l2->flag); + l2->sapi = 0; + l2->maxlen = MAX_DFRAME_LEN; + if (test_bit(OPTION_L2_PMX, &options)) + l2->window = 7; + else + l2->window = 1; + if (test_bit(OPTION_L2_PTP, &options)) + test_and_set_bit(FLG_PTP, &l2->flag); + if (test_bit(OPTION_L2_FIXEDTEI, &options)) + test_and_set_bit(FLG_FIXED_TEI, &l2->flag); + l2->tei = (u_int)arg; + l2->T200 = 1000; + l2->N200 = 3; + l2->T203 = 10000; + if (test_bit(OPTION_L2_PMX, &options)) + rq.protocol = ISDN_P_NT_E1; + else + rq.protocol = ISDN_P_NT_S0; + rq.adr.channel = 0; + l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, OPEN_CHANNEL, &rq); + break; + case ISDN_P_LAPD_TE: + test_and_set_bit(FLG_LAPD, &l2->flag); + test_and_set_bit(FLG_MOD128, &l2->flag); + test_and_set_bit(FLG_ORIG, &l2->flag); + l2->sapi = 0; + l2->maxlen = MAX_DFRAME_LEN; + if (test_bit(OPTION_L2_PMX, &options)) + l2->window = 7; + else + l2->window = 1; + if (test_bit(OPTION_L2_PTP, &options)) + test_and_set_bit(FLG_PTP, &l2->flag); + if (test_bit(OPTION_L2_FIXEDTEI, &options)) + test_and_set_bit(FLG_FIXED_TEI, &l2->flag); + l2->tei = (u_int)arg; + l2->T200 = 1000; + l2->N200 = 3; + l2->T203 = 10000; + if (test_bit(OPTION_L2_PMX, &options)) + rq.protocol = ISDN_P_TE_E1; + else + rq.protocol = ISDN_P_TE_S0; + rq.adr.channel = 0; + l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, OPEN_CHANNEL, &rq); + break; + case ISDN_P_B_X75SLP: + test_and_set_bit(FLG_LAPB, &l2->flag); + l2->window = 7; + l2->maxlen = MAX_DATA_SIZE; + l2->T200 = 1000; + l2->N200 = 4; + l2->T203 = 5000; + l2->addr.A = 3; + l2->addr.B = 1; + break; + default: + printk(KERN_ERR "layer2 create failed prt %x\n", + protocol); + kfree(l2); + return NULL; + } + skb_queue_head_init(&l2->i_queue); + skb_queue_head_init(&l2->ui_queue); + skb_queue_head_init(&l2->down_queue); + skb_queue_head_init(&l2->tmp_queue); + InitWin(l2); + l2->l2m.fsm = &l2fsm; + if (test_bit(FLG_LAPB, &l2->flag) || + test_bit(FLG_PTP, &l2->flag) || + test_bit(FLG_LAPD_NET, &l2->flag)) + l2->l2m.state = ST_L2_4; + else + l2->l2m.state = ST_L2_1; + l2->l2m.debug = *debug; + l2->l2m.userdata = l2; + l2->l2m.userint = 0; + l2->l2m.printdebug = l2m_debug; + + mISDN_FsmInitTimer(&l2->l2m, &l2->t200); + mISDN_FsmInitTimer(&l2->l2m, &l2->t203); + return l2; +} + +static int +x75create(struct channel_req *crq) +{ + struct layer2 *l2; + + if (crq->protocol != ISDN_P_B_X75SLP) + return -EPROTONOSUPPORT; + l2 = create_l2(crq->ch, crq->protocol, 0, 0); + if (!l2) + return -ENOMEM; + crq->ch = &l2->ch; + crq->protocol = ISDN_P_B_HDLC; + return 0; +} + +static struct Bprotocol X75SLP = { + .Bprotocols = (1 << (ISDN_P_B_X75SLP & ISDN_P_B_MASK)), + .name = "X75SLP", + .create = x75create +}; + +int +Isdnl2_Init(u_int *deb) +{ + debug = deb; + mISDN_register_Bprotocol(&X75SLP); + l2fsm.state_count = L2_STATE_COUNT; + l2fsm.event_count = L2_EVENT_COUNT; + l2fsm.strEvent = strL2Event; + l2fsm.strState = strL2State; + mISDN_FsmNew(&l2fsm, L2FnList, ARRAY_SIZE(L2FnList)); + TEIInit(deb); + return 0; +} + +void +Isdnl2_cleanup(void) +{ + mISDN_unregister_Bprotocol(&X75SLP); + TEIFree(); + mISDN_FsmFree(&l2fsm); +} + diff --git a/drivers/isdn/mISDN/layer2.h b/drivers/isdn/mISDN/layer2.h new file mode 100644 index 000000000000..6293f80dc2d3 --- /dev/null +++ b/drivers/isdn/mISDN/layer2.h @@ -0,0 +1,140 @@ +/* + * Layer 2 defines + * + * Copyright 2008 by Karsten Keil <kkeil@novell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + */ + +#include <linux/mISDNif.h> +#include <linux/skbuff.h> +#include "fsm.h" + +#define MAX_WINDOW 8 + +struct manager { + struct mISDNchannel ch; + struct mISDNchannel bcast; + u_long options; + struct list_head layer2; + rwlock_t lock; + struct FsmInst deact; + struct FsmTimer datimer; + struct sk_buff_head sendq; + struct mISDNchannel *up; + u_int nextid; + u_int lastid; +}; + +struct teimgr { + int ri; + int rcnt; + struct FsmInst tei_m; + struct FsmTimer timer; + int tval, nval; + struct layer2 *l2; + struct manager *mgr; +}; + +struct laddr { + u_char A; + u_char B; +}; + +struct layer2 { + struct list_head list; + struct mISDNchannel ch; + u_long flag; + int id; + struct mISDNchannel *up; + signed char sapi; + signed char tei; + struct laddr addr; + u_int maxlen; + struct teimgr *tm; + u_int vs, va, vr; + int rc; + u_int window; + u_int sow; + struct FsmInst l2m; + struct FsmTimer t200, t203; + int T200, N200, T203; + u_int next_id; + u_int down_id; + struct sk_buff *windowar[MAX_WINDOW]; + struct sk_buff_head i_queue; + struct sk_buff_head ui_queue; + struct sk_buff_head down_queue; + struct sk_buff_head tmp_queue; +}; + +enum { + ST_L2_1, + ST_L2_2, + ST_L2_3, + ST_L2_4, + ST_L2_5, + ST_L2_6, + ST_L2_7, + ST_L2_8, +}; + +#define L2_STATE_COUNT (ST_L2_8+1) + +extern struct layer2 *create_l2(struct mISDNchannel *, u_int, + u_long, u_long); +extern int tei_l2(struct layer2 *, u_int, u_long arg); + + +/* from tei.c */ +extern int l2_tei(struct layer2 *, u_int, u_long arg); +extern void TEIrelease(struct layer2 *); +extern int TEIInit(u_int *); +extern void TEIFree(void); + +#define MAX_L2HEADER_LEN 4 + +#define RR 0x01 +#define RNR 0x05 +#define REJ 0x09 +#define SABME 0x6f +#define SABM 0x2f +#define DM 0x0f +#define UI 0x03 +#define DISC 0x43 +#define UA 0x63 +#define FRMR 0x87 +#define XID 0xaf + +#define CMD 0 +#define RSP 1 + +#define LC_FLUSH_WAIT 1 + +#define FLG_LAPB 0 +#define FLG_LAPD 1 +#define FLG_ORIG 2 +#define FLG_MOD128 3 +#define FLG_PEND_REL 4 +#define FLG_L3_INIT 5 +#define FLG_T200_RUN 6 +#define FLG_ACK_PEND 7 +#define FLG_REJEXC 8 +#define FLG_OWN_BUSY 9 +#define FLG_PEER_BUSY 10 +#define FLG_DCHAN_BUSY 11 +#define FLG_L1_ACTIV 12 +#define FLG_ESTAB_PEND 13 +#define FLG_PTP 14 +#define FLG_FIXED_TEI 15 +#define FLG_L2BLOCK 16 +#define FLG_L1_NOTREADY 17 +#define FLG_LAPD_NET 18 diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c new file mode 100644 index 000000000000..4ba4cc364c9e --- /dev/null +++ b/drivers/isdn/mISDN/socket.c @@ -0,0 +1,781 @@ +/* + * + * Author Karsten Keil <kkeil@novell.com> + * + * Copyright 2008 by Karsten Keil <kkeil@novell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + */ + +#include <linux/mISDNif.h> +#include "core.h" + +static int *debug; + +static struct proto mISDN_proto = { + .name = "misdn", + .owner = THIS_MODULE, + .obj_size = sizeof(struct mISDN_sock) +}; + +#define _pms(sk) ((struct mISDN_sock *)sk) + +static struct mISDN_sock_list data_sockets = { + .lock = __RW_LOCK_UNLOCKED(data_sockets.lock) +}; + +static struct mISDN_sock_list base_sockets = { + .lock = __RW_LOCK_UNLOCKED(base_sockets.lock) +}; + +#define L2_HEADER_LEN 4 + +static inline struct sk_buff * +_l2_alloc_skb(unsigned int len, gfp_t gfp_mask) +{ + struct sk_buff *skb; + + skb = alloc_skb(len + L2_HEADER_LEN, gfp_mask); + if (likely(skb)) + skb_reserve(skb, L2_HEADER_LEN); + return skb; +} + +static void +mISDN_sock_link(struct mISDN_sock_list *l, struct sock *sk) +{ + write_lock_bh(&l->lock); + sk_add_node(sk, &l->head); + write_unlock_bh(&l->lock); +} + +static void mISDN_sock_unlink(struct mISDN_sock_list *l, struct sock *sk) +{ + write_lock_bh(&l->lock); + sk_del_node_init(sk); + write_unlock_bh(&l->lock); +} + +static int +mISDN_send(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct mISDN_sock *msk; + int err; + + msk = container_of(ch, struct mISDN_sock, ch); + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s len %d %p\n", __func__, skb->len, skb); + if (msk->sk.sk_state == MISDN_CLOSED) + return -EUNATCH; + __net_timestamp(skb); + err = sock_queue_rcv_skb(&msk->sk, skb); + if (err) + printk(KERN_WARNING "%s: error %d\n", __func__, err); + return err; +} + +static int +mISDN_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct mISDN_sock *msk; + + msk = container_of(ch, struct mISDN_sock, ch); + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s(%p, %x, %p)\n", __func__, ch, cmd, arg); + switch (cmd) { + case CLOSE_CHANNEL: + msk->sk.sk_state = MISDN_CLOSED; + break; + } + return 0; +} + +static inline void +mISDN_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) +{ + struct timeval tv; + + if (_pms(sk)->cmask & MISDN_TIME_STAMP) { + skb_get_timestamp(skb, &tv); + put_cmsg(msg, SOL_MISDN, MISDN_TIME_STAMP, sizeof(tv), &tv); + } +} + +static int +mISDN_sock_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len, int flags) +{ + struct sk_buff *skb; + struct sock *sk = sock->sk; + struct sockaddr_mISDN *maddr; + + int copied, err; + + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s: len %d, flags %x ch.nr %d, proto %x\n", + __func__, (int)len, flags, _pms(sk)->ch.nr, + sk->sk_protocol); + if (flags & (MSG_OOB)) + return -EOPNOTSUPP; + + if (sk->sk_state == MISDN_CLOSED) + return 0; + + skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err); + if (!skb) + return err; + + if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) { + msg->msg_namelen = sizeof(struct sockaddr_mISDN); + maddr = (struct sockaddr_mISDN *)msg->msg_name; + maddr->family = AF_ISDN; + maddr->dev = _pms(sk)->dev->id; + if ((sk->sk_protocol == ISDN_P_LAPD_TE) || + (sk->sk_protocol == ISDN_P_LAPD_NT)) { + maddr->channel = (mISDN_HEAD_ID(skb) >> 16) & 0xff; + maddr->tei = (mISDN_HEAD_ID(skb) >> 8) & 0xff; + maddr->sapi = mISDN_HEAD_ID(skb) & 0xff; + } else { + maddr->channel = _pms(sk)->ch.nr; + maddr->sapi = _pms(sk)->ch.addr & 0xFF; + maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xFF; + } + } else { + if (msg->msg_namelen) + printk(KERN_WARNING "%s: too small namelen %d\n", + __func__, msg->msg_namelen); + msg->msg_namelen = 0; + } + + copied = skb->len + MISDN_HEADER_LEN; + if (len < copied) { + if (flags & MSG_PEEK) + atomic_dec(&skb->users); + else + skb_queue_head(&sk->sk_receive_queue, skb); + return -ENOSPC; + } + memcpy(skb_push(skb, MISDN_HEADER_LEN), mISDN_HEAD_P(skb), + MISDN_HEADER_LEN); + + err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + + mISDN_sock_cmsg(sk, msg, skb); + + skb_free_datagram(sk, skb); + + return err ? : copied; +} + +static int +mISDN_sock_sendmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len) +{ + struct sock *sk = sock->sk; + struct sk_buff *skb; + int err = -ENOMEM; + struct sockaddr_mISDN *maddr; + + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s: len %d flags %x ch %d proto %x\n", + __func__, (int)len, msg->msg_flags, _pms(sk)->ch.nr, + sk->sk_protocol); + + if (msg->msg_flags & MSG_OOB) + return -EOPNOTSUPP; + + if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE)) + return -EINVAL; + + if (len < MISDN_HEADER_LEN) + return -EINVAL; + + if (sk->sk_state != MISDN_BOUND) + return -EBADFD; + + lock_sock(sk); + + skb = _l2_alloc_skb(len, GFP_KERNEL); + if (!skb) + goto done; + + if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { + err = -EFAULT; + goto drop; + } + + memcpy(mISDN_HEAD_P(skb), skb->data, MISDN_HEADER_LEN); + skb_pull(skb, MISDN_HEADER_LEN); + + if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) { + /* if we have a address, we use it */ + maddr = (struct sockaddr_mISDN *)msg->msg_name; + mISDN_HEAD_ID(skb) = maddr->channel; + } else { /* use default for L2 messages */ + if ((sk->sk_protocol == ISDN_P_LAPD_TE) || + (sk->sk_protocol == ISDN_P_LAPD_NT)) + mISDN_HEAD_ID(skb) = _pms(sk)->ch.nr; + } + + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s: ID:%x\n", + __func__, mISDN_HEAD_ID(skb)); + + err = -ENODEV; + if (!_pms(sk)->ch.peer || + (err = _pms(sk)->ch.recv(_pms(sk)->ch.peer, skb))) + goto drop; + + err = len; + +done: + release_sock(sk); + return err; + +drop: + kfree_skb(skb); + goto done; +} + +static int +data_sock_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk); + if (!sk) + return 0; + switch (sk->sk_protocol) { + case ISDN_P_TE_S0: + case ISDN_P_NT_S0: + case ISDN_P_TE_E1: + case ISDN_P_NT_E1: + if (sk->sk_state == MISDN_BOUND) + delete_channel(&_pms(sk)->ch); + else + mISDN_sock_unlink(&data_sockets, sk); + break; + case ISDN_P_LAPD_TE: + case ISDN_P_LAPD_NT: + case ISDN_P_B_RAW: + case ISDN_P_B_HDLC: + case ISDN_P_B_X75SLP: + case ISDN_P_B_L2DTMF: + case ISDN_P_B_L2DSP: + case ISDN_P_B_L2DSPHDLC: + delete_channel(&_pms(sk)->ch); + mISDN_sock_unlink(&data_sockets, sk); + break; + } + + lock_sock(sk); + + sock_orphan(sk); + skb_queue_purge(&sk->sk_receive_queue); + + release_sock(sk); + sock_put(sk); + + return 0; +} + +static int +data_sock_ioctl_bound(struct sock *sk, unsigned int cmd, void __user *p) +{ + struct mISDN_ctrl_req cq; + int err = -EINVAL, val; + struct mISDNchannel *bchan, *next; + + lock_sock(sk); + if (!_pms(sk)->dev) { + err = -ENODEV; + goto done; + } + switch (cmd) { + case IMCTRLREQ: + if (copy_from_user(&cq, p, sizeof(cq))) { + err = -EFAULT; + break; + } + if ((sk->sk_protocol & ~ISDN_P_B_MASK) == ISDN_P_B_START) { + list_for_each_entry_safe(bchan, next, + &_pms(sk)->dev->bchannels, list) { + if (bchan->nr == cq.channel) { + err = bchan->ctrl(bchan, + CONTROL_CHANNEL, &cq); + break; + } + } + } else + err = _pms(sk)->dev->D.ctrl(&_pms(sk)->dev->D, + CONTROL_CHANNEL, &cq); + if (err) + break; + if (copy_to_user(p, &cq, sizeof(cq))) + err = -EFAULT; + break; + case IMCLEAR_L2: + if (sk->sk_protocol != ISDN_P_LAPD_NT) { + err = -EINVAL; + break; + } + if (get_user(val, (int __user *)p)) { + err = -EFAULT; + break; + } + err = _pms(sk)->dev->teimgr->ctrl(_pms(sk)->dev->teimgr, + CONTROL_CHANNEL, &val); + break; + default: + err = -EINVAL; + break; + } +done: + release_sock(sk); + return err; +} + +static int +data_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + int err = 0, id; + struct sock *sk = sock->sk; + struct mISDNdevice *dev; + struct mISDNversion ver; + + switch (cmd) { + case IMGETVERSION: + ver.major = MISDN_MAJOR_VERSION; + ver.minor = MISDN_MINOR_VERSION; + ver.release = MISDN_RELEASE; + if (copy_to_user((void __user *)arg, &ver, sizeof(ver))) + err = -EFAULT; + break; + case IMGETCOUNT: + id = get_mdevice_count(); + if (put_user(id, (int __user *)arg)) + err = -EFAULT; + break; + case IMGETDEVINFO: + if (get_user(id, (int __user *)arg)) { + err = -EFAULT; + break; + } + dev = get_mdevice(id); + if (dev) { + struct mISDN_devinfo di; + + di.id = dev->id; + di.Dprotocols = dev->Dprotocols; + di.Bprotocols = dev->Bprotocols | get_all_Bprotocols(); + di.protocol = dev->D.protocol; + memcpy(di.channelmap, dev->channelmap, + MISDN_CHMAP_SIZE * 4); + di.nrbchan = dev->nrbchan; + strcpy(di.name, dev->name); + if (copy_to_user((void __user *)arg, &di, sizeof(di))) + err = -EFAULT; + } else + err = -ENODEV; + break; + default: + if (sk->sk_state == MISDN_BOUND) + err = data_sock_ioctl_bound(sk, cmd, + (void __user *)arg); + else + err = -ENOTCONN; + } + return err; +} + +static int data_sock_setsockopt(struct socket *sock, int level, int optname, + char __user *optval, int len) +{ + struct sock *sk = sock->sk; + int err = 0, opt = 0; + + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s(%p, %d, %x, %p, %d)\n", __func__, sock, + level, optname, optval, len); + + lock_sock(sk); + + switch (optname) { + case MISDN_TIME_STAMP: + if (get_user(opt, (int __user *)optval)) { + err = -EFAULT; + break; + } + + if (opt) + _pms(sk)->cmask |= MISDN_TIME_STAMP; + else + _pms(sk)->cmask &= ~MISDN_TIME_STAMP; + break; + default: + err = -ENOPROTOOPT; + break; + } + release_sock(sk); + return err; +} + +static int data_sock_getsockopt(struct socket *sock, int level, int optname, + char __user *optval, int __user *optlen) +{ + struct sock *sk = sock->sk; + int len, opt; + + if (get_user(len, optlen)) + return -EFAULT; + + switch (optname) { + case MISDN_TIME_STAMP: + if (_pms(sk)->cmask & MISDN_TIME_STAMP) + opt = 1; + else + opt = 0; + + if (put_user(opt, optval)) + return -EFAULT; + break; + default: + return -ENOPROTOOPT; + } + + return 0; +} + +static int +data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) +{ + struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; + struct sock *sk = sock->sk; + int err = 0; + + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk); + if (addr_len != sizeof(struct sockaddr_mISDN)) + return -EINVAL; + if (!maddr || maddr->family != AF_ISDN) + return -EINVAL; + + lock_sock(sk); + + if (_pms(sk)->dev) { + err = -EALREADY; + goto done; + } + _pms(sk)->dev = get_mdevice(maddr->dev); + if (!_pms(sk)->dev) { + err = -ENODEV; + goto done; + } + _pms(sk)->ch.send = mISDN_send; + _pms(sk)->ch.ctrl = mISDN_ctrl; + + switch (sk->sk_protocol) { + case ISDN_P_TE_S0: + case ISDN_P_NT_S0: + case ISDN_P_TE_E1: + case ISDN_P_NT_E1: + mISDN_sock_unlink(&data_sockets, sk); + err = connect_layer1(_pms(sk)->dev, &_pms(sk)->ch, + sk->sk_protocol, maddr); + if (err) + mISDN_sock_link(&data_sockets, sk); + break; + case ISDN_P_LAPD_TE: + case ISDN_P_LAPD_NT: + err = create_l2entity(_pms(sk)->dev, &_pms(sk)->ch, + sk->sk_protocol, maddr); + break; + case ISDN_P_B_RAW: + case ISDN_P_B_HDLC: + case ISDN_P_B_X75SLP: + case ISDN_P_B_L2DTMF: + case ISDN_P_B_L2DSP: + case ISDN_P_B_L2DSPHDLC: + err = connect_Bstack(_pms(sk)->dev, &_pms(sk)->ch, + sk->sk_protocol, maddr); + break; + default: + err = -EPROTONOSUPPORT; + } + if (err) + goto done; + sk->sk_state = MISDN_BOUND; + _pms(sk)->ch.protocol = sk->sk_protocol; + +done: + release_sock(sk); + return err; +} + +static int +data_sock_getname(struct socket *sock, struct sockaddr *addr, + int *addr_len, int peer) +{ + struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; + struct sock *sk = sock->sk; + + if (!_pms(sk)->dev) + return -EBADFD; + + lock_sock(sk); + + *addr_len = sizeof(*maddr); + maddr->dev = _pms(sk)->dev->id; + maddr->channel = _pms(sk)->ch.nr; + maddr->sapi = _pms(sk)->ch.addr & 0xff; + maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xff; + release_sock(sk); + return 0; +} + +static const struct proto_ops data_sock_ops = { + .family = PF_ISDN, + .owner = THIS_MODULE, + .release = data_sock_release, + .ioctl = data_sock_ioctl, + .bind = data_sock_bind, + .getname = data_sock_getname, + .sendmsg = mISDN_sock_sendmsg, + .recvmsg = mISDN_sock_recvmsg, + .poll = datagram_poll, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = data_sock_setsockopt, + .getsockopt = data_sock_getsockopt, + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .mmap = sock_no_mmap +}; + +static int +data_sock_create(struct net *net, struct socket *sock, int protocol) +{ + struct sock *sk; + + if (sock->type != SOCK_DGRAM) + return -ESOCKTNOSUPPORT; + + sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto); + if (!sk) + return -ENOMEM; + + sock_init_data(sock, sk); + + sock->ops = &data_sock_ops; + sock->state = SS_UNCONNECTED; + sock_reset_flag(sk, SOCK_ZAPPED); + + sk->sk_protocol = protocol; + sk->sk_state = MISDN_OPEN; + mISDN_sock_link(&data_sockets, sk); + + return 0; +} + +static int +base_sock_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk); + if (!sk) + return 0; + + mISDN_sock_unlink(&base_sockets, sk); + sock_orphan(sk); + sock_put(sk); + + return 0; +} + +static int +base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + int err = 0, id; + struct mISDNdevice *dev; + struct mISDNversion ver; + + switch (cmd) { + case IMGETVERSION: + ver.major = MISDN_MAJOR_VERSION; + ver.minor = MISDN_MINOR_VERSION; + ver.release = MISDN_RELEASE; + if (copy_to_user((void __user *)arg, &ver, sizeof(ver))) + err = -EFAULT; + break; + case IMGETCOUNT: + id = get_mdevice_count(); + if (put_user(id, (int __user *)arg)) + err = -EFAULT; + break; + case IMGETDEVINFO: + if (get_user(id, (int __user *)arg)) { + err = -EFAULT; + break; + } + dev = get_mdevice(id); + if (dev) { + struct mISDN_devinfo di; + + di.id = dev->id; + di.Dprotocols = dev->Dprotocols; + di.Bprotocols = dev->Bprotocols | get_all_Bprotocols(); + di.protocol = dev->D.protocol; + memcpy(di.channelmap, dev->channelmap, + MISDN_CHMAP_SIZE * 4); + di.nrbchan = dev->nrbchan; + strcpy(di.name, dev->name); + if (copy_to_user((void __user *)arg, &di, sizeof(di))) + err = -EFAULT; + } else + err = -ENODEV; + break; + default: + err = -EINVAL; + } + return err; +} + +static int +base_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) +{ + struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; + struct sock *sk = sock->sk; + int err = 0; + + if (!maddr || maddr->family != AF_ISDN) + return -EINVAL; + + lock_sock(sk); + + if (_pms(sk)->dev) { + err = -EALREADY; + goto done; + } + + _pms(sk)->dev = get_mdevice(maddr->dev); + if (!_pms(sk)->dev) { + err = -ENODEV; + goto done; + } + sk->sk_state = MISDN_BOUND; + +done: + release_sock(sk); + return err; +} + +static const struct proto_ops base_sock_ops = { + .family = PF_ISDN, + .owner = THIS_MODULE, + .release = base_sock_release, + .ioctl = base_sock_ioctl, + .bind = base_sock_bind, + .getname = sock_no_getname, + .sendmsg = sock_no_sendmsg, + .recvmsg = sock_no_recvmsg, + .poll = sock_no_poll, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .mmap = sock_no_mmap +}; + + +static int +base_sock_create(struct net *net, struct socket *sock, int protocol) +{ + struct sock *sk; + + if (sock->type != SOCK_RAW) + return -ESOCKTNOSUPPORT; + + sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto); + if (!sk) + return -ENOMEM; + + sock_init_data(sock, sk); + sock->ops = &base_sock_ops; + sock->state = SS_UNCONNECTED; + sock_reset_flag(sk, SOCK_ZAPPED); + sk->sk_protocol = protocol; + sk->sk_state = MISDN_OPEN; + mISDN_sock_link(&base_sockets, sk); + + return 0; +} + +static int +mISDN_sock_create(struct net *net, struct socket *sock, int proto) +{ + int err = -EPROTONOSUPPORT; + + switch (proto) { + case ISDN_P_BASE: + err = base_sock_create(net, sock, proto); + break; + case ISDN_P_TE_S0: + case ISDN_P_NT_S0: + case ISDN_P_TE_E1: + case ISDN_P_NT_E1: + case ISDN_P_LAPD_TE: + case ISDN_P_LAPD_NT: + case ISDN_P_B_RAW: + case ISDN_P_B_HDLC: + case ISDN_P_B_X75SLP: + case ISDN_P_B_L2DTMF: + case ISDN_P_B_L2DSP: + case ISDN_P_B_L2DSPHDLC: + err = data_sock_create(net, sock, proto); + break; + default: + return err; + } + + return err; +} + +static struct +net_proto_family mISDN_sock_family_ops = { + .owner = THIS_MODULE, + .family = PF_ISDN, + .create = mISDN_sock_create, +}; + +int +misdn_sock_init(u_int *deb) +{ + int err; + + debug = deb; + err = sock_register(&mISDN_sock_family_ops); + if (err) + printk(KERN_ERR "%s: error(%d)\n", __func__, err); + return err; +} + +void +misdn_sock_cleanup(void) +{ + sock_unregister(PF_ISDN); +} + diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c new file mode 100644 index 000000000000..54cfddcc4784 --- /dev/null +++ b/drivers/isdn/mISDN/stack.c @@ -0,0 +1,674 @@ +/* + * + * Author Karsten Keil <kkeil@novell.com> + * + * Copyright 2008 by Karsten Keil <kkeil@novell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + */ + +#include <linux/mISDNif.h> +#include <linux/kthread.h> +#include "core.h" + +static u_int *debug; + +static inline void +_queue_message(struct mISDNstack *st, struct sk_buff *skb) +{ + struct mISDNhead *hh = mISDN_HEAD_P(skb); + + if (*debug & DEBUG_QUEUE_FUNC) + printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n", + __func__, hh->prim, hh->id, skb); + skb_queue_tail(&st->msgq, skb); + if (likely(!test_bit(mISDN_STACK_STOPPED, &st->status))) { + test_and_set_bit(mISDN_STACK_WORK, &st->status); + wake_up_interruptible(&st->workq); + } +} + +int +mISDN_queue_message(struct mISDNchannel *ch, struct sk_buff *skb) +{ + _queue_message(ch->st, skb); + return 0; +} + +static struct mISDNchannel * +get_channel4id(struct mISDNstack *st, u_int id) +{ + struct mISDNchannel *ch; + + mutex_lock(&st->lmutex); + list_for_each_entry(ch, &st->layer2, list) { + if (id == ch->nr) + goto unlock; + } + ch = NULL; +unlock: + mutex_unlock(&st->lmutex); + return ch; +} + +static void +send_socklist(struct mISDN_sock_list *sl, struct sk_buff *skb) +{ + struct hlist_node *node; + struct sock *sk; + struct sk_buff *cskb = NULL; + + read_lock(&sl->lock); + sk_for_each(sk, node, &sl->head) { + if (sk->sk_state != MISDN_BOUND) + continue; + if (!cskb) + cskb = skb_copy(skb, GFP_KERNEL); + if (!cskb) { + printk(KERN_WARNING "%s no skb\n", __func__); + break; + } + if (!sock_queue_rcv_skb(sk, cskb)) + cskb = NULL; + } + read_unlock(&sl->lock); + if (cskb) + dev_kfree_skb(cskb); +} + +static void +send_layer2(struct mISDNstack *st, struct sk_buff *skb) +{ + struct sk_buff *cskb; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + struct mISDNchannel *ch; + int ret; + + if (!st) + return; + mutex_lock(&st->lmutex); + if ((hh->id & MISDN_ID_ADDR_MASK) == MISDN_ID_ANY) { /* L2 for all */ + list_for_each_entry(ch, &st->layer2, list) { + if (list_is_last(&ch->list, &st->layer2)) { + cskb = skb; + skb = NULL; + } else { + cskb = skb_copy(skb, GFP_KERNEL); + } + if (cskb) { + ret = ch->send(ch, cskb); + if (ret) { + if (*debug & DEBUG_SEND_ERR) + printk(KERN_DEBUG + "%s ch%d prim(%x) addr(%x)" + " err %d\n", + __func__, ch->nr, + hh->prim, ch->addr, ret); + dev_kfree_skb(cskb); + } + } else { + printk(KERN_WARNING "%s ch%d addr %x no mem\n", + __func__, ch->nr, ch->addr); + goto out; + } + } + } else { + list_for_each_entry(ch, &st->layer2, list) { + if ((hh->id & MISDN_ID_ADDR_MASK) == ch->addr) { + ret = ch->send(ch, skb); + if (!ret) + skb = NULL; + goto out; + } + } + ret = st->dev->teimgr->ctrl(st->dev->teimgr, CHECK_DATA, skb); + if (!ret) + skb = NULL; + else if (*debug & DEBUG_SEND_ERR) + printk(KERN_DEBUG + "%s ch%d mgr prim(%x) addr(%x) err %d\n", + __func__, ch->nr, hh->prim, ch->addr, ret); + } +out: + mutex_unlock(&st->lmutex); + if (skb) + dev_kfree_skb(skb); +} + +static inline int +send_msg_to_layer(struct mISDNstack *st, struct sk_buff *skb) +{ + struct mISDNhead *hh = mISDN_HEAD_P(skb); + struct mISDNchannel *ch; + int lm; + + lm = hh->prim & MISDN_LAYERMASK; + if (*debug & DEBUG_QUEUE_FUNC) + printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n", + __func__, hh->prim, hh->id, skb); + if (lm == 0x1) { + if (!hlist_empty(&st->l1sock.head)) { + __net_timestamp(skb); + send_socklist(&st->l1sock, skb); + } + return st->layer1->send(st->layer1, skb); + } else if (lm == 0x2) { + if (!hlist_empty(&st->l1sock.head)) + send_socklist(&st->l1sock, skb); + send_layer2(st, skb); + return 0; + } else if (lm == 0x4) { + ch = get_channel4id(st, hh->id); + if (ch) + return ch->send(ch, skb); + else + printk(KERN_WARNING + "%s: dev(%s) prim(%x) id(%x) no channel\n", + __func__, st->dev->name, hh->prim, hh->id); + } else if (lm == 0x8) { + WARN_ON(lm == 0x8); + ch = get_channel4id(st, hh->id); + if (ch) + return ch->send(ch, skb); + else + printk(KERN_WARNING + "%s: dev(%s) prim(%x) id(%x) no channel\n", + __func__, st->dev->name, hh->prim, hh->id); + } else { + /* broadcast not handled yet */ + printk(KERN_WARNING "%s: dev(%s) prim %x not delivered\n", + __func__, st->dev->name, hh->prim); + } + return -ESRCH; +} + +static void +do_clear_stack(struct mISDNstack *st) +{ +} + +static int +mISDNStackd(void *data) +{ + struct mISDNstack *st = data; + int err = 0; + +#ifdef CONFIG_SMP + lock_kernel(); +#endif + sigfillset(¤t->blocked); +#ifdef CONFIG_SMP + unlock_kernel(); +#endif + if (*debug & DEBUG_MSG_THREAD) + printk(KERN_DEBUG "mISDNStackd %s started\n", st->dev->name); + + if (st->notify != NULL) { + complete(st->notify); + st->notify = NULL; + } + + for (;;) { + struct sk_buff *skb; + + if (unlikely(test_bit(mISDN_STACK_STOPPED, &st->status))) { + test_and_clear_bit(mISDN_STACK_WORK, &st->status); + test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); + } else + test_and_set_bit(mISDN_STACK_RUNNING, &st->status); + while (test_bit(mISDN_STACK_WORK, &st->status)) { + skb = skb_dequeue(&st->msgq); + if (!skb) { + test_and_clear_bit(mISDN_STACK_WORK, + &st->status); + /* test if a race happens */ + skb = skb_dequeue(&st->msgq); + if (!skb) + continue; + test_and_set_bit(mISDN_STACK_WORK, + &st->status); + } +#ifdef MISDN_MSG_STATS + st->msg_cnt++; +#endif + err = send_msg_to_layer(st, skb); + if (unlikely(err)) { + if (*debug & DEBUG_SEND_ERR) + printk(KERN_DEBUG + "%s: %s prim(%x) id(%x) " + "send call(%d)\n", + __func__, st->dev->name, + mISDN_HEAD_PRIM(skb), + mISDN_HEAD_ID(skb), err); + dev_kfree_skb(skb); + continue; + } + if (unlikely(test_bit(mISDN_STACK_STOPPED, + &st->status))) { + test_and_clear_bit(mISDN_STACK_WORK, + &st->status); + test_and_clear_bit(mISDN_STACK_RUNNING, + &st->status); + break; + } + } + if (test_bit(mISDN_STACK_CLEARING, &st->status)) { + test_and_set_bit(mISDN_STACK_STOPPED, &st->status); + test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); + do_clear_stack(st); + test_and_clear_bit(mISDN_STACK_CLEARING, &st->status); + test_and_set_bit(mISDN_STACK_RESTART, &st->status); + } + if (test_and_clear_bit(mISDN_STACK_RESTART, &st->status)) { + test_and_clear_bit(mISDN_STACK_STOPPED, &st->status); + test_and_set_bit(mISDN_STACK_RUNNING, &st->status); + if (!skb_queue_empty(&st->msgq)) + test_and_set_bit(mISDN_STACK_WORK, + &st->status); + } + if (test_bit(mISDN_STACK_ABORT, &st->status)) + break; + if (st->notify != NULL) { + complete(st->notify); + st->notify = NULL; + } +#ifdef MISDN_MSG_STATS + st->sleep_cnt++; +#endif + test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status); + wait_event_interruptible(st->workq, (st->status & + mISDN_STACK_ACTION_MASK)); + if (*debug & DEBUG_MSG_THREAD) + printk(KERN_DEBUG "%s: %s wake status %08lx\n", + __func__, st->dev->name, st->status); + test_and_set_bit(mISDN_STACK_ACTIVE, &st->status); + + test_and_clear_bit(mISDN_STACK_WAKEUP, &st->status); + + if (test_bit(mISDN_STACK_STOPPED, &st->status)) { + test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); +#ifdef MISDN_MSG_STATS + st->stopped_cnt++; +#endif + } + } +#ifdef MISDN_MSG_STATS + printk(KERN_DEBUG "mISDNStackd daemon for %s proceed %d " + "msg %d sleep %d stopped\n", + st->dev->name, st->msg_cnt, st->sleep_cnt, st->stopped_cnt); + printk(KERN_DEBUG + "mISDNStackd daemon for %s utime(%ld) stime(%ld)\n", + st->dev->name, st->thread->utime, st->thread->stime); + printk(KERN_DEBUG + "mISDNStackd daemon for %s nvcsw(%ld) nivcsw(%ld)\n", + st->dev->name, st->thread->nvcsw, st->thread->nivcsw); + printk(KERN_DEBUG "mISDNStackd daemon for %s killed now\n", + st->dev->name); +#endif + test_and_set_bit(mISDN_STACK_KILLED, &st->status); + test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); + test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status); + test_and_clear_bit(mISDN_STACK_ABORT, &st->status); + skb_queue_purge(&st->msgq); + st->thread = NULL; + if (st->notify != NULL) { + complete(st->notify); + st->notify = NULL; + } + return 0; +} + +static int +l1_receive(struct mISDNchannel *ch, struct sk_buff *skb) +{ + if (!ch->st) + return -ENODEV; + __net_timestamp(skb); + _queue_message(ch->st, skb); + return 0; +} + +void +set_channel_address(struct mISDNchannel *ch, u_int sapi, u_int tei) +{ + ch->addr = sapi | (tei << 8); +} + +void +__add_layer2(struct mISDNchannel *ch, struct mISDNstack *st) +{ + list_add_tail(&ch->list, &st->layer2); +} + +void +add_layer2(struct mISDNchannel *ch, struct mISDNstack *st) +{ + mutex_lock(&st->lmutex); + __add_layer2(ch, st); + mutex_unlock(&st->lmutex); +} + +static int +st_own_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + if (!ch->st || ch->st->layer1) + return -EINVAL; + return ch->st->layer1->ctrl(ch->st->layer1, cmd, arg); +} + +int +create_stack(struct mISDNdevice *dev) +{ + struct mISDNstack *newst; + int err; + DECLARE_COMPLETION_ONSTACK(done); + + newst = kzalloc(sizeof(struct mISDNstack), GFP_KERNEL); + if (!newst) { + printk(KERN_ERR "kmalloc mISDN_stack failed\n"); + return -ENOMEM; + } + newst->dev = dev; + INIT_LIST_HEAD(&newst->layer2); + INIT_HLIST_HEAD(&newst->l1sock.head); + rwlock_init(&newst->l1sock.lock); + init_waitqueue_head(&newst->workq); + skb_queue_head_init(&newst->msgq); + mutex_init(&newst->lmutex); + dev->D.st = newst; + err = create_teimanager(dev); + if (err) { + printk(KERN_ERR "kmalloc teimanager failed\n"); + kfree(newst); + return err; + } + dev->teimgr->peer = &newst->own; + dev->teimgr->recv = mISDN_queue_message; + dev->teimgr->st = newst; + newst->layer1 = &dev->D; + dev->D.recv = l1_receive; + dev->D.peer = &newst->own; + newst->own.st = newst; + newst->own.ctrl = st_own_ctrl; + newst->own.send = mISDN_queue_message; + newst->own.recv = mISDN_queue_message; + if (*debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: st(%s)\n", __func__, newst->dev->name); + newst->notify = &done; + newst->thread = kthread_run(mISDNStackd, (void *)newst, "mISDN_%s", + newst->dev->name); + if (IS_ERR(newst->thread)) { + err = PTR_ERR(newst->thread); + printk(KERN_ERR + "mISDN:cannot create kernel thread for %s (%d)\n", + newst->dev->name, err); + delete_teimanager(dev->teimgr); + kfree(newst); + } else + wait_for_completion(&done); + return err; +} + +int +connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch, + u_int protocol, struct sockaddr_mISDN *adr) +{ + struct mISDN_sock *msk = container_of(ch, struct mISDN_sock, ch); + struct channel_req rq; + int err; + + + if (*debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", + __func__, dev->name, protocol, adr->dev, adr->channel, + adr->sapi, adr->tei); + switch (protocol) { + case ISDN_P_NT_S0: + case ISDN_P_NT_E1: + case ISDN_P_TE_S0: + case ISDN_P_TE_E1: +#ifdef PROTOCOL_CHECK + /* this should be enhanced */ + if (!list_empty(&dev->D.st->layer2) + && dev->D.protocol != protocol) + return -EBUSY; + if (!hlist_empty(&dev->D.st->l1sock.head) + && dev->D.protocol != protocol) + return -EBUSY; +#endif + ch->recv = mISDN_queue_message; + ch->peer = &dev->D.st->own; + ch->st = dev->D.st; + rq.protocol = protocol; + rq.adr.channel = 0; + err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); + printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err); + if (err) + return err; + write_lock_bh(&dev->D.st->l1sock.lock); + sk_add_node(&msk->sk, &dev->D.st->l1sock.head); + write_unlock_bh(&dev->D.st->l1sock.lock); + break; + default: + return -ENOPROTOOPT; + } + return 0; +} + +int +connect_Bstack(struct mISDNdevice *dev, struct mISDNchannel *ch, + u_int protocol, struct sockaddr_mISDN *adr) +{ + struct channel_req rq, rq2; + int pmask, err; + struct Bprotocol *bp; + + if (*debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", + __func__, dev->name, protocol, + adr->dev, adr->channel, adr->sapi, + adr->tei); + ch->st = dev->D.st; + pmask = 1 << (protocol & ISDN_P_B_MASK); + if (pmask & dev->Bprotocols) { + rq.protocol = protocol; + rq.adr = *adr; + err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); + if (err) + return err; + ch->recv = rq.ch->send; + ch->peer = rq.ch; + rq.ch->recv = ch->send; + rq.ch->peer = ch; + rq.ch->st = dev->D.st; + } else { + bp = get_Bprotocol4mask(pmask); + if (!bp) + return -ENOPROTOOPT; + rq2.protocol = protocol; + rq2.adr = *adr; + rq2.ch = ch; + err = bp->create(&rq2); + if (err) + return err; + ch->recv = rq2.ch->send; + ch->peer = rq2.ch; + rq2.ch->st = dev->D.st; + rq.protocol = rq2.protocol; + rq.adr = *adr; + err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); + if (err) { + rq2.ch->ctrl(rq2.ch, CLOSE_CHANNEL, NULL); + return err; + } + rq2.ch->recv = rq.ch->send; + rq2.ch->peer = rq.ch; + rq.ch->recv = rq2.ch->send; + rq.ch->peer = rq2.ch; + rq.ch->st = dev->D.st; + } + ch->protocol = protocol; + ch->nr = rq.ch->nr; + return 0; +} + +int +create_l2entity(struct mISDNdevice *dev, struct mISDNchannel *ch, + u_int protocol, struct sockaddr_mISDN *adr) +{ + struct channel_req rq; + int err; + + if (*debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", + __func__, dev->name, protocol, + adr->dev, adr->channel, adr->sapi, + adr->tei); + rq.protocol = ISDN_P_TE_S0; + if (dev->Dprotocols & (1 << ISDN_P_TE_E1)) + rq.protocol = ISDN_P_TE_E1; + switch (protocol) { + case ISDN_P_LAPD_NT: + rq.protocol = ISDN_P_NT_S0; + if (dev->Dprotocols & (1 << ISDN_P_NT_E1)) + rq.protocol = ISDN_P_NT_E1; + case ISDN_P_LAPD_TE: +#ifdef PROTOCOL_CHECK + /* this should be enhanced */ + if (!list_empty(&dev->D.st->layer2) + && dev->D.protocol != protocol) + return -EBUSY; + if (!hlist_empty(&dev->D.st->l1sock.head) + && dev->D.protocol != protocol) + return -EBUSY; +#endif + ch->recv = mISDN_queue_message; + ch->peer = &dev->D.st->own; + ch->st = dev->D.st; + rq.adr.channel = 0; + err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); + printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err); + if (err) + break; + rq.protocol = protocol; + rq.adr = *adr; + rq.ch = ch; + err = dev->teimgr->ctrl(dev->teimgr, OPEN_CHANNEL, &rq); + printk(KERN_DEBUG "%s: ret 2 %d\n", __func__, err); + if (!err) { + if ((protocol == ISDN_P_LAPD_NT) && !rq.ch) + break; + add_layer2(rq.ch, dev->D.st); + rq.ch->recv = mISDN_queue_message; + rq.ch->peer = &dev->D.st->own; + rq.ch->ctrl(rq.ch, OPEN_CHANNEL, NULL); /* can't fail */ + } + break; + default: + err = -EPROTONOSUPPORT; + } + return err; +} + +void +delete_channel(struct mISDNchannel *ch) +{ + struct mISDN_sock *msk = container_of(ch, struct mISDN_sock, ch); + struct mISDNchannel *pch; + + if (!ch->st) { + printk(KERN_WARNING "%s: no stack\n", __func__); + return; + } + if (*debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: st(%s) protocol(%x)\n", __func__, + ch->st->dev->name, ch->protocol); + if (ch->protocol >= ISDN_P_B_START) { + if (ch->peer) { + ch->peer->ctrl(ch->peer, CLOSE_CHANNEL, NULL); + ch->peer = NULL; + } + return; + } + switch (ch->protocol) { + case ISDN_P_NT_S0: + case ISDN_P_TE_S0: + case ISDN_P_NT_E1: + case ISDN_P_TE_E1: + write_lock_bh(&ch->st->l1sock.lock); + sk_del_node_init(&msk->sk); + write_unlock_bh(&ch->st->l1sock.lock); + ch->st->dev->D.ctrl(&ch->st->dev->D, CLOSE_CHANNEL, NULL); + break; + case ISDN_P_LAPD_TE: + pch = get_channel4id(ch->st, ch->nr); + if (pch) { + mutex_lock(&ch->st->lmutex); + list_del(&pch->list); + mutex_unlock(&ch->st->lmutex); + pch->ctrl(pch, CLOSE_CHANNEL, NULL); + pch = ch->st->dev->teimgr; + pch->ctrl(pch, CLOSE_CHANNEL, NULL); + } else + printk(KERN_WARNING "%s: no l2 channel\n", + __func__); + break; + case ISDN_P_LAPD_NT: + pch = ch->st->dev->teimgr; + if (pch) { + pch->ctrl(pch, CLOSE_CHANNEL, NULL); + } else + printk(KERN_WARNING "%s: no l2 channel\n", + __func__); + break; + default: + break; + } + return; +} + +void +delete_stack(struct mISDNdevice *dev) +{ + struct mISDNstack *st = dev->D.st; + DECLARE_COMPLETION_ONSTACK(done); + + if (*debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: st(%s)\n", __func__, + st->dev->name); + if (dev->teimgr) + delete_teimanager(dev->teimgr); + if (st->thread) { + if (st->notify) { + printk(KERN_WARNING "%s: notifier in use\n", + __func__); + complete(st->notify); + } + st->notify = &done; + test_and_set_bit(mISDN_STACK_ABORT, &st->status); + test_and_set_bit(mISDN_STACK_WAKEUP, &st->status); + wake_up_interruptible(&st->workq); + wait_for_completion(&done); + } + if (!list_empty(&st->layer2)) + printk(KERN_WARNING "%s: layer2 list not empty\n", + __func__); + if (!hlist_empty(&st->l1sock.head)) + printk(KERN_WARNING "%s: layer1 list not empty\n", + __func__); + kfree(st); +} + +void +mISDN_initstack(u_int *dp) +{ + debug = dp; +} diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c new file mode 100644 index 000000000000..6fbae42127bf --- /dev/null +++ b/drivers/isdn/mISDN/tei.c @@ -0,0 +1,1340 @@ +/* + * + * Author Karsten Keil <kkeil@novell.com> + * + * Copyright 2008 by Karsten Keil <kkeil@novell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + */ +#include "layer2.h" +#include <linux/random.h> +#include "core.h" + +#define ID_REQUEST 1 +#define ID_ASSIGNED 2 +#define ID_DENIED 3 +#define ID_CHK_REQ 4 +#define ID_CHK_RES 5 +#define ID_REMOVE 6 +#define ID_VERIFY 7 + +#define TEI_ENTITY_ID 0xf + +#define MGR_PH_ACTIVE 16 +#define MGR_PH_NOTREADY 17 + +#define DATIMER_VAL 10000 + +static u_int *debug; + +static struct Fsm deactfsm = {NULL, 0, 0, NULL, NULL}; +static struct Fsm teifsmu = {NULL, 0, 0, NULL, NULL}; +static struct Fsm teifsmn = {NULL, 0, 0, NULL, NULL}; + +enum { + ST_L1_DEACT, + ST_L1_DEACT_PENDING, + ST_L1_ACTIV, +}; +#define DEACT_STATE_COUNT (ST_L1_ACTIV+1) + +static char *strDeactState[] = +{ + "ST_L1_DEACT", + "ST_L1_DEACT_PENDING", + "ST_L1_ACTIV", +}; + +enum { + EV_ACTIVATE, + EV_ACTIVATE_IND, + EV_DEACTIVATE, + EV_DEACTIVATE_IND, + EV_UI, + EV_DATIMER, +}; + +#define DEACT_EVENT_COUNT (EV_DATIMER+1) + +static char *strDeactEvent[] = +{ + "EV_ACTIVATE", + "EV_ACTIVATE_IND", + "EV_DEACTIVATE", + "EV_DEACTIVATE_IND", + "EV_UI", + "EV_DATIMER", +}; + +static void +da_debug(struct FsmInst *fi, char *fmt, ...) +{ + struct manager *mgr = fi->userdata; + va_list va; + + if (!(*debug & DEBUG_L2_TEIFSM)) + return; + va_start(va, fmt); + printk(KERN_DEBUG "mgr(%d): ", mgr->ch.st->dev->id); + vprintk(fmt, va); + printk("\n"); + va_end(va); +} + +static void +da_activate(struct FsmInst *fi, int event, void *arg) +{ + struct manager *mgr = fi->userdata; + + if (fi->state == ST_L1_DEACT_PENDING) + mISDN_FsmDelTimer(&mgr->datimer, 1); + mISDN_FsmChangeState(fi, ST_L1_ACTIV); +} + +static void +da_deactivate_ind(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L1_DEACT); +} + +static void +da_deactivate(struct FsmInst *fi, int event, void *arg) +{ + struct manager *mgr = fi->userdata; + struct layer2 *l2; + u_long flags; + + read_lock_irqsave(&mgr->lock, flags); + list_for_each_entry(l2, &mgr->layer2, list) { + if (l2->l2m.state > ST_L2_4) { + /* have still activ TEI */ + read_unlock_irqrestore(&mgr->lock, flags); + return; + } + } + read_unlock_irqrestore(&mgr->lock, flags); + /* All TEI are inactiv */ + mISDN_FsmAddTimer(&mgr->datimer, DATIMER_VAL, EV_DATIMER, NULL, 1); + mISDN_FsmChangeState(fi, ST_L1_DEACT_PENDING); +} + +static void +da_ui(struct FsmInst *fi, int event, void *arg) +{ + struct manager *mgr = fi->userdata; + + /* restart da timer */ + mISDN_FsmDelTimer(&mgr->datimer, 2); + mISDN_FsmAddTimer(&mgr->datimer, DATIMER_VAL, EV_DATIMER, NULL, 2); + +} + +static void +da_timer(struct FsmInst *fi, int event, void *arg) +{ + struct manager *mgr = fi->userdata; + struct layer2 *l2; + u_long flags; + + /* check again */ + read_lock_irqsave(&mgr->lock, flags); + list_for_each_entry(l2, &mgr->layer2, list) { + if (l2->l2m.state > ST_L2_4) { + /* have still activ TEI */ + read_unlock_irqrestore(&mgr->lock, flags); + mISDN_FsmChangeState(fi, ST_L1_ACTIV); + return; + } + } + read_unlock_irqrestore(&mgr->lock, flags); + /* All TEI are inactiv */ + mISDN_FsmChangeState(fi, ST_L1_DEACT); + _queue_data(&mgr->ch, PH_DEACTIVATE_REQ, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); +} + +static struct FsmNode DeactFnList[] = +{ + {ST_L1_DEACT, EV_ACTIVATE_IND, da_activate}, + {ST_L1_ACTIV, EV_DEACTIVATE_IND, da_deactivate_ind}, + {ST_L1_ACTIV, EV_DEACTIVATE, da_deactivate}, + {ST_L1_DEACT_PENDING, EV_ACTIVATE, da_activate}, + {ST_L1_DEACT_PENDING, EV_UI, da_ui}, + {ST_L1_DEACT_PENDING, EV_DATIMER, da_timer}, +}; + +enum { + ST_TEI_NOP, + ST_TEI_IDREQ, + ST_TEI_IDVERIFY, +}; + +#define TEI_STATE_COUNT (ST_TEI_IDVERIFY+1) + +static char *strTeiState[] = +{ + "ST_TEI_NOP", + "ST_TEI_IDREQ", + "ST_TEI_IDVERIFY", +}; + +enum { + EV_IDREQ, + EV_ASSIGN, + EV_ASSIGN_REQ, + EV_DENIED, + EV_CHKREQ, + EV_CHKRESP, + EV_REMOVE, + EV_VERIFY, + EV_TIMER, +}; + +#define TEI_EVENT_COUNT (EV_TIMER+1) + +static char *strTeiEvent[] = +{ + "EV_IDREQ", + "EV_ASSIGN", + "EV_ASSIGN_REQ", + "EV_DENIED", + "EV_CHKREQ", + "EV_CHKRESP", + "EV_REMOVE", + "EV_VERIFY", + "EV_TIMER", +}; + +static void +tei_debug(struct FsmInst *fi, char *fmt, ...) +{ + struct teimgr *tm = fi->userdata; + va_list va; + + if (!(*debug & DEBUG_L2_TEIFSM)) + return; + va_start(va, fmt); + printk(KERN_DEBUG "tei(%d): ", tm->l2->tei); + vprintk(fmt, va); + printk("\n"); + va_end(va); +} + + + +static int +get_free_id(struct manager *mgr) +{ + u64 ids = 0; + int i; + struct layer2 *l2; + + list_for_each_entry(l2, &mgr->layer2, list) { + if (l2->ch.nr > 63) { + printk(KERN_WARNING + "%s: more as 63 layer2 for one device\n", + __func__); + return -EBUSY; + } + test_and_set_bit(l2->ch.nr, (u_long *)&ids); + } + for (i = 1; i < 64; i++) + if (!test_bit(i, (u_long *)&ids)) + return i; + printk(KERN_WARNING "%s: more as 63 layer2 for one device\n", + __func__); + return -EBUSY; +} + +static int +get_free_tei(struct manager *mgr) +{ + u64 ids = 0; + int i; + struct layer2 *l2; + + list_for_each_entry(l2, &mgr->layer2, list) { + if (l2->ch.nr == 0) + continue; + if ((l2->ch.addr & 0xff) != 0) + continue; + i = l2->ch.addr >> 8; + if (i < 64) + continue; + i -= 64; + + test_and_set_bit(i, (u_long *)&ids); + } + for (i = 0; i < 64; i++) + if (!test_bit(i, (u_long *)&ids)) + return i + 64; + printk(KERN_WARNING "%s: more as 63 dynamic tei for one device\n", + __func__); + return -1; +} + +static void +teiup_create(struct manager *mgr, u_int prim, int len, void *arg) +{ + struct sk_buff *skb; + struct mISDNhead *hh; + int err; + + skb = mI_alloc_skb(len, GFP_ATOMIC); + if (!skb) + return; + hh = mISDN_HEAD_P(skb); + hh->prim = prim; + hh->id = (mgr->ch.nr << 16) | mgr->ch.addr; + if (len) + memcpy(skb_put(skb, len), arg, len); + err = mgr->up->send(mgr->up, skb); + if (err) { + printk(KERN_WARNING "%s: err=%d\n", __func__, err); + dev_kfree_skb(skb); + } +} + +static u_int +new_id(struct manager *mgr) +{ + u_int id; + + id = mgr->nextid++; + if (id == 0x7fff) + mgr->nextid = 1; + id <<= 16; + id |= GROUP_TEI << 8; + id |= TEI_SAPI; + return id; +} + +static void +do_send(struct manager *mgr) +{ + if (!test_bit(MGR_PH_ACTIVE, &mgr->options)) + return; + + if (!test_and_set_bit(MGR_PH_NOTREADY, &mgr->options)) { + struct sk_buff *skb = skb_dequeue(&mgr->sendq); + + if (!skb) { + test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options); + return; + } + mgr->lastid = mISDN_HEAD_ID(skb); + mISDN_FsmEvent(&mgr->deact, EV_UI, NULL); + if (mgr->ch.recv(mgr->ch.peer, skb)) { + dev_kfree_skb(skb); + test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options); + mgr->lastid = MISDN_ID_NONE; + } + } +} + +static void +do_ack(struct manager *mgr, u_int id) +{ + if (test_bit(MGR_PH_NOTREADY, &mgr->options)) { + if (id == mgr->lastid) { + if (test_bit(MGR_PH_ACTIVE, &mgr->options)) { + struct sk_buff *skb; + + skb = skb_dequeue(&mgr->sendq); + if (skb) { + mgr->lastid = mISDN_HEAD_ID(skb); + if (!mgr->ch.recv(mgr->ch.peer, skb)) + return; + dev_kfree_skb(skb); + } + } + mgr->lastid = MISDN_ID_NONE; + test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options); + } + } +} + +static void +mgr_send_down(struct manager *mgr, struct sk_buff *skb) +{ + skb_queue_tail(&mgr->sendq, skb); + if (!test_bit(MGR_PH_ACTIVE, &mgr->options)) { + _queue_data(&mgr->ch, PH_ACTIVATE_REQ, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + } else { + do_send(mgr); + } +} + +static int +dl_unit_data(struct manager *mgr, struct sk_buff *skb) +{ + if (!test_bit(MGR_OPT_NETWORK, &mgr->options)) /* only net send UI */ + return -EINVAL; + if (!test_bit(MGR_PH_ACTIVE, &mgr->options)) + _queue_data(&mgr->ch, PH_ACTIVATE_REQ, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + skb_push(skb, 3); + skb->data[0] = 0x02; /* SAPI 0 C/R = 1 */ + skb->data[1] = 0xff; /* TEI 127 */ + skb->data[2] = UI; /* UI frame */ + mISDN_HEAD_PRIM(skb) = PH_DATA_REQ; + mISDN_HEAD_ID(skb) = new_id(mgr); + skb_queue_tail(&mgr->sendq, skb); + do_send(mgr); + return 0; +} + +unsigned int +random_ri(void) +{ + u16 x; + + get_random_bytes(&x, sizeof(x)); + return x; +} + +static struct layer2 * +findtei(struct manager *mgr, int tei) +{ + struct layer2 *l2; + u_long flags; + + read_lock_irqsave(&mgr->lock, flags); + list_for_each_entry(l2, &mgr->layer2, list) { + if ((l2->sapi == 0) && (l2->tei > 0) && + (l2->tei != GROUP_TEI) && (l2->tei == tei)) + goto done; + } + l2 = NULL; +done: + read_unlock_irqrestore(&mgr->lock, flags); + return l2; +} + +static void +put_tei_msg(struct manager *mgr, u_char m_id, unsigned int ri, u_char tei) +{ + struct sk_buff *skb; + u_char bp[8]; + + bp[0] = (TEI_SAPI << 2); + if (test_bit(MGR_OPT_NETWORK, &mgr->options)) + bp[0] |= 2; /* CR:=1 for net command */ + bp[1] = (GROUP_TEI << 1) | 0x1; + bp[2] = UI; + bp[3] = TEI_ENTITY_ID; + bp[4] = ri >> 8; + bp[5] = ri & 0xff; + bp[6] = m_id; + bp[7] = (tei << 1) | 1; + skb = _alloc_mISDN_skb(PH_DATA_REQ, new_id(mgr), + 8, bp, GFP_ATOMIC); + if (!skb) { + printk(KERN_WARNING "%s: no skb for tei msg\n", __func__); + return; + } + mgr_send_down(mgr, skb); +} + +static void +tei_id_request(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + + if (tm->l2->tei != GROUP_TEI) { + tm->tei_m.printdebug(&tm->tei_m, + "assign request for allready assigned tei %d", + tm->l2->tei); + return; + } + tm->ri = random_ri(); + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(&tm->tei_m, + "assign request ri %d", tm->ri); + put_tei_msg(tm->mgr, ID_REQUEST, tm->ri, GROUP_TEI); + mISDN_FsmChangeState(fi, ST_TEI_IDREQ); + mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 1); + tm->nval = 3; +} + +static void +tei_id_assign(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + struct layer2 *l2; + u_char *dp = arg; + int ri, tei; + + ri = ((unsigned int) *dp++ << 8); + ri += *dp++; + dp++; + tei = *dp >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "identity assign ri %d tei %d", + ri, tei); + l2 = findtei(tm->mgr, tei); + if (l2) { /* same tei is in use */ + if (ri != l2->tm->ri) { + tm->tei_m.printdebug(fi, + "possible duplicate assignment tei %d", tei); + tei_l2(l2, MDL_ERROR_RSP, 0); + } + } else if (ri == tm->ri) { + mISDN_FsmDelTimer(&tm->timer, 1); + mISDN_FsmChangeState(fi, ST_TEI_NOP); + tei_l2(tm->l2, MDL_ASSIGN_REQ, tei); + } +} + +static void +tei_id_test_dup(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + struct layer2 *l2; + u_char *dp = arg; + int tei, ri; + + ri = ((unsigned int) *dp++ << 8); + ri += *dp++; + dp++; + tei = *dp >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "foreign identity assign ri %d tei %d", + ri, tei); + l2 = findtei(tm->mgr, tei); + if (l2) { /* same tei is in use */ + if (ri != l2->tm->ri) { /* and it wasn't our request */ + tm->tei_m.printdebug(fi, + "possible duplicate assignment tei %d", tei); + mISDN_FsmEvent(&l2->tm->tei_m, EV_VERIFY, NULL); + } + } +} + +static void +tei_id_denied(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + u_char *dp = arg; + int ri, tei; + + ri = ((unsigned int) *dp++ << 8); + ri += *dp++; + dp++; + tei = *dp >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "identity denied ri %d tei %d", + ri, tei); +} + +static void +tei_id_chk_req(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + u_char *dp = arg; + int tei; + + tei = *(dp+3) >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "identity check req tei %d", tei); + if ((tm->l2->tei != GROUP_TEI) && ((tei == GROUP_TEI) || + (tei == tm->l2->tei))) { + mISDN_FsmDelTimer(&tm->timer, 4); + mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP); + put_tei_msg(tm->mgr, ID_CHK_RES, random_ri(), tm->l2->tei); + } +} + +static void +tei_id_remove(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + u_char *dp = arg; + int tei; + + tei = *(dp+3) >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "identity remove tei %d", tei); + if ((tm->l2->tei != GROUP_TEI) && + ((tei == GROUP_TEI) || (tei == tm->l2->tei))) { + mISDN_FsmDelTimer(&tm->timer, 5); + mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP); + tei_l2(tm->l2, MDL_REMOVE_REQ, 0); + } +} + +static void +tei_id_verify(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "id verify request for tei %d", + tm->l2->tei); + put_tei_msg(tm->mgr, ID_VERIFY, 0, tm->l2->tei); + mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY); + mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 2); + tm->nval = 2; +} + +static void +tei_id_req_tout(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + + if (--tm->nval) { + tm->ri = random_ri(); + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "assign req(%d) ri %d", + 4 - tm->nval, tm->ri); + put_tei_msg(tm->mgr, ID_REQUEST, tm->ri, GROUP_TEI); + mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 3); + } else { + tm->tei_m.printdebug(fi, "assign req failed"); + tei_l2(tm->l2, MDL_ERROR_RSP, 0); + mISDN_FsmChangeState(fi, ST_TEI_NOP); + } +} + +static void +tei_id_ver_tout(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + + if (--tm->nval) { + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, + "id verify req(%d) for tei %d", + 3 - tm->nval, tm->l2->tei); + put_tei_msg(tm->mgr, ID_VERIFY, 0, tm->l2->tei); + mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 4); + } else { + tm->tei_m.printdebug(fi, "verify req for tei %d failed", + tm->l2->tei); + tei_l2(tm->l2, MDL_REMOVE_REQ, 0); + mISDN_FsmChangeState(fi, ST_TEI_NOP); + } +} + +static struct FsmNode TeiFnListUser[] = +{ + {ST_TEI_NOP, EV_IDREQ, tei_id_request}, + {ST_TEI_NOP, EV_ASSIGN, tei_id_test_dup}, + {ST_TEI_NOP, EV_VERIFY, tei_id_verify}, + {ST_TEI_NOP, EV_REMOVE, tei_id_remove}, + {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req}, + {ST_TEI_IDREQ, EV_TIMER, tei_id_req_tout}, + {ST_TEI_IDREQ, EV_ASSIGN, tei_id_assign}, + {ST_TEI_IDREQ, EV_DENIED, tei_id_denied}, + {ST_TEI_IDVERIFY, EV_TIMER, tei_id_ver_tout}, + {ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove}, + {ST_TEI_IDVERIFY, EV_CHKREQ, tei_id_chk_req}, +}; + +static void +tei_l2remove(struct layer2 *l2) +{ + put_tei_msg(l2->tm->mgr, ID_REMOVE, 0, l2->tei); + tei_l2(l2, MDL_REMOVE_REQ, 0); + list_del(&l2->ch.list); + l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); +} + +static void +tei_assign_req(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + u_char *dp = arg; + + if (tm->l2->tei == GROUP_TEI) { + tm->tei_m.printdebug(&tm->tei_m, + "net tei assign request without tei"); + return; + } + tm->ri = ((unsigned int) *dp++ << 8); + tm->ri += *dp++; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(&tm->tei_m, + "net assign request ri %d teim %d", tm->ri, *dp); + put_tei_msg(tm->mgr, ID_ASSIGNED, tm->ri, tm->l2->tei); + mISDN_FsmChangeState(fi, ST_TEI_NOP); +} + +static void +tei_id_chk_req_net(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "id check request for tei %d", + tm->l2->tei); + tm->rcnt = 0; + put_tei_msg(tm->mgr, ID_CHK_REQ, 0, tm->l2->tei); + mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY); + mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 2); + tm->nval = 2; +} + +static void +tei_id_chk_resp(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + u_char *dp = arg; + int tei; + + tei = dp[3] >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "identity check resp tei %d", tei); + if (tei == tm->l2->tei) + tm->rcnt++; +} + +static void +tei_id_verify_net(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + u_char *dp = arg; + int tei; + + tei = dp[3] >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "identity verify req tei %d/%d", + tei, tm->l2->tei); + if (tei == tm->l2->tei) + tei_id_chk_req_net(fi, event, arg); +} + +static void +tei_id_ver_tout_net(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + + if (tm->rcnt == 1) { + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, + "check req for tei %d sucessful\n", tm->l2->tei); + mISDN_FsmChangeState(fi, ST_TEI_NOP); + } else if (tm->rcnt > 1) { + /* duplicate assignment; remove */ + tei_l2remove(tm->l2); + } else if (--tm->nval) { + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, + "id check req(%d) for tei %d", + 3 - tm->nval, tm->l2->tei); + put_tei_msg(tm->mgr, ID_CHK_REQ, 0, tm->l2->tei); + mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 4); + } else { + tm->tei_m.printdebug(fi, "check req for tei %d failed", + tm->l2->tei); + mISDN_FsmChangeState(fi, ST_TEI_NOP); + tei_l2remove(tm->l2); + } +} + +static struct FsmNode TeiFnListNet[] = +{ + {ST_TEI_NOP, EV_ASSIGN_REQ, tei_assign_req}, + {ST_TEI_NOP, EV_VERIFY, tei_id_verify_net}, + {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req_net}, + {ST_TEI_IDVERIFY, EV_TIMER, tei_id_ver_tout_net}, + {ST_TEI_IDVERIFY, EV_CHKRESP, tei_id_chk_resp}, +}; + +static void +tei_ph_data_ind(struct teimgr *tm, u_int mt, u_char *dp, int len) +{ + if (test_bit(FLG_FIXED_TEI, &tm->l2->flag)) + return; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(&tm->tei_m, "tei handler mt %x", mt); + if (mt == ID_ASSIGNED) + mISDN_FsmEvent(&tm->tei_m, EV_ASSIGN, dp); + else if (mt == ID_DENIED) + mISDN_FsmEvent(&tm->tei_m, EV_DENIED, dp); + else if (mt == ID_CHK_REQ) + mISDN_FsmEvent(&tm->tei_m, EV_CHKREQ, dp); + else if (mt == ID_REMOVE) + mISDN_FsmEvent(&tm->tei_m, EV_REMOVE, dp); + else if (mt == ID_VERIFY) + mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, dp); + else if (mt == ID_CHK_RES) + mISDN_FsmEvent(&tm->tei_m, EV_CHKRESP, dp); +} + +static struct layer2 * +create_new_tei(struct manager *mgr, int tei) +{ + u_long opt = 0; + u_long flags; + int id; + struct layer2 *l2; + + if (!mgr->up) + return NULL; + if (tei < 64) + test_and_set_bit(OPTION_L2_FIXEDTEI, &opt); + if (mgr->ch.st->dev->Dprotocols + & ((1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1))) + test_and_set_bit(OPTION_L2_PMX, &opt); + l2 = create_l2(mgr->up, ISDN_P_LAPD_NT, (u_int)opt, (u_long)tei); + if (!l2) { + printk(KERN_WARNING "%s:no memory for layer2\n", __func__); + return NULL; + } + l2->tm = kzalloc(sizeof(struct teimgr), GFP_KERNEL); + if (!l2->tm) { + kfree(l2); + printk(KERN_WARNING "%s:no memory for teimgr\n", __func__); + return NULL; + } + l2->tm->mgr = mgr; + l2->tm->l2 = l2; + l2->tm->tei_m.debug = *debug & DEBUG_L2_TEIFSM; + l2->tm->tei_m.userdata = l2->tm; + l2->tm->tei_m.printdebug = tei_debug; + l2->tm->tei_m.fsm = &teifsmn; + l2->tm->tei_m.state = ST_TEI_NOP; + l2->tm->tval = 2000; /* T202 2 sec */ + mISDN_FsmInitTimer(&l2->tm->tei_m, &l2->tm->timer); + write_lock_irqsave(&mgr->lock, flags); + id = get_free_id(mgr); + list_add_tail(&l2->list, &mgr->layer2); + write_unlock_irqrestore(&mgr->lock, flags); + if (id < 0) { + l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); + printk(KERN_WARNING "%s:no free id\n", __func__); + return NULL; + } else { + l2->ch.nr = id; + __add_layer2(&l2->ch, mgr->ch.st); + l2->ch.recv = mgr->ch.recv; + l2->ch.peer = mgr->ch.peer; + l2->ch.ctrl(&l2->ch, OPEN_CHANNEL, NULL); + } + return l2; +} + +static void +new_tei_req(struct manager *mgr, u_char *dp) +{ + int tei, ri; + struct layer2 *l2; + + ri = dp[0] << 8; + ri += dp[1]; + if (!mgr->up) + goto denied; + tei = get_free_tei(mgr); + if (tei < 0) { + printk(KERN_WARNING "%s:No free tei\n", __func__); + goto denied; + } + l2 = create_new_tei(mgr, tei); + if (!l2) + goto denied; + else + mISDN_FsmEvent(&l2->tm->tei_m, EV_ASSIGN_REQ, dp); + return; +denied: + put_tei_msg(mgr, ID_DENIED, ri, GROUP_TEI); +} + +static int +ph_data_ind(struct manager *mgr, struct sk_buff *skb) +{ + int ret = -EINVAL; + struct layer2 *l2; + u_long flags; + u_char mt; + + if (skb->len < 8) { + if (*debug & DEBUG_L2_TEI) + printk(KERN_DEBUG "%s: short mgr frame %d/8\n", + __func__, skb->len); + goto done; + } + if (*debug & DEBUG_L2_TEI) + + if ((skb->data[0] >> 2) != TEI_SAPI) /* not for us */ + goto done; + if (skb->data[0] & 1) /* EA0 formal error */ + goto done; + if (!(skb->data[1] & 1)) /* EA1 formal error */ + goto done; + if ((skb->data[1] >> 1) != GROUP_TEI) /* not for us */ + goto done; + if ((skb->data[2] & 0xef) != UI) /* not UI */ + goto done; + if (skb->data[3] != TEI_ENTITY_ID) /* not tei entity */ + goto done; + mt = skb->data[6]; + switch (mt) { + case ID_REQUEST: + case ID_CHK_RES: + case ID_VERIFY: + if (!test_bit(MGR_OPT_NETWORK, &mgr->options)) + goto done; + break; + case ID_ASSIGNED: + case ID_DENIED: + case ID_CHK_REQ: + case ID_REMOVE: + if (test_bit(MGR_OPT_NETWORK, &mgr->options)) + goto done; + break; + default: + goto done; + } + ret = 0; + if (mt == ID_REQUEST) { + new_tei_req(mgr, &skb->data[4]); + goto done; + } + read_lock_irqsave(&mgr->lock, flags); + list_for_each_entry(l2, &mgr->layer2, list) { + tei_ph_data_ind(l2->tm, mt, &skb->data[4], skb->len - 4); + } + read_unlock_irqrestore(&mgr->lock, flags); +done: + return ret; +} + +int +l2_tei(struct layer2 *l2, u_int cmd, u_long arg) +{ + struct teimgr *tm = l2->tm; + + if (test_bit(FLG_FIXED_TEI, &l2->flag)) + return 0; + if (*debug & DEBUG_L2_TEI) + printk(KERN_DEBUG "%s: cmd(%x)\n", __func__, cmd); + switch (cmd) { + case MDL_ASSIGN_IND: + mISDN_FsmEvent(&tm->tei_m, EV_IDREQ, NULL); + break; + case MDL_ERROR_IND: + if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) + mISDN_FsmEvent(&tm->tei_m, EV_CHKREQ, &l2->tei); + if (test_bit(MGR_OPT_USER, &tm->mgr->options)) + mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, NULL); + break; + case MDL_STATUS_UP_IND: + if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) + mISDN_FsmEvent(&tm->mgr->deact, EV_ACTIVATE, NULL); + break; + case MDL_STATUS_DOWN_IND: + if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) + mISDN_FsmEvent(&tm->mgr->deact, EV_DEACTIVATE, NULL); + break; + case MDL_STATUS_UI_IND: + if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) + mISDN_FsmEvent(&tm->mgr->deact, EV_UI, NULL); + break; + } + return 0; +} + +void +TEIrelease(struct layer2 *l2) +{ + struct teimgr *tm = l2->tm; + u_long flags; + + mISDN_FsmDelTimer(&tm->timer, 1); + write_lock_irqsave(&tm->mgr->lock, flags); + list_del(&l2->list); + write_unlock_irqrestore(&tm->mgr->lock, flags); + l2->tm = NULL; + kfree(tm); +} + +static int +create_teimgr(struct manager *mgr, struct channel_req *crq) +{ + struct layer2 *l2; + u_long opt = 0; + u_long flags; + int id; + + if (*debug & DEBUG_L2_TEI) + printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", + __func__, mgr->ch.st->dev->name, crq->protocol, + crq->adr.dev, crq->adr.channel, crq->adr.sapi, + crq->adr.tei); + if (crq->adr.sapi != 0) /* not supported yet */ + return -EINVAL; + if (crq->adr.tei > GROUP_TEI) + return -EINVAL; + if (crq->adr.tei < 64) + test_and_set_bit(OPTION_L2_FIXEDTEI, &opt); + if (crq->adr.tei == 0) + test_and_set_bit(OPTION_L2_PTP, &opt); + if (test_bit(MGR_OPT_NETWORK, &mgr->options)) { + if (crq->protocol == ISDN_P_LAPD_TE) + return -EPROTONOSUPPORT; + if ((crq->adr.tei != 0) && (crq->adr.tei != 127)) + return -EINVAL; + if (mgr->up) { + printk(KERN_WARNING + "%s: only one network manager is allowed\n", + __func__); + return -EBUSY; + } + } else if (test_bit(MGR_OPT_USER, &mgr->options)) { + if (crq->protocol == ISDN_P_LAPD_NT) + return -EPROTONOSUPPORT; + if ((crq->adr.tei >= 64) && (crq->adr.tei < GROUP_TEI)) + return -EINVAL; /* dyn tei */ + } else { + if (crq->protocol == ISDN_P_LAPD_NT) + test_and_set_bit(MGR_OPT_NETWORK, &mgr->options); + if (crq->protocol == ISDN_P_LAPD_TE) + test_and_set_bit(MGR_OPT_USER, &mgr->options); + } + if (mgr->ch.st->dev->Dprotocols + & ((1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1))) + test_and_set_bit(OPTION_L2_PMX, &opt); + if ((crq->protocol == ISDN_P_LAPD_NT) && (crq->adr.tei == 127)) { + mgr->up = crq->ch; + id = DL_INFO_L2_CONNECT; + teiup_create(mgr, DL_INFORMATION_IND, sizeof(id), &id); + crq->ch = NULL; + if (!list_empty(&mgr->layer2)) { + read_lock_irqsave(&mgr->lock, flags); + list_for_each_entry(l2, &mgr->layer2, list) { + l2->up = mgr->up; + l2->ch.ctrl(&l2->ch, OPEN_CHANNEL, NULL); + } + read_unlock_irqrestore(&mgr->lock, flags); + } + return 0; + } + l2 = create_l2(crq->ch, crq->protocol, (u_int)opt, + (u_long)crq->adr.tei); + if (!l2) + return -ENOMEM; + l2->tm = kzalloc(sizeof(struct teimgr), GFP_KERNEL); + if (!l2->tm) { + kfree(l2); + printk(KERN_ERR "kmalloc teimgr failed\n"); + return -ENOMEM; + } + l2->tm->mgr = mgr; + l2->tm->l2 = l2; + l2->tm->tei_m.debug = *debug & DEBUG_L2_TEIFSM; + l2->tm->tei_m.userdata = l2->tm; + l2->tm->tei_m.printdebug = tei_debug; + if (crq->protocol == ISDN_P_LAPD_TE) { + l2->tm->tei_m.fsm = &teifsmu; + l2->tm->tei_m.state = ST_TEI_NOP; + l2->tm->tval = 1000; /* T201 1 sec */ + } else { + l2->tm->tei_m.fsm = &teifsmn; + l2->tm->tei_m.state = ST_TEI_NOP; + l2->tm->tval = 2000; /* T202 2 sec */ + } + mISDN_FsmInitTimer(&l2->tm->tei_m, &l2->tm->timer); + write_lock_irqsave(&mgr->lock, flags); + id = get_free_id(mgr); + list_add_tail(&l2->list, &mgr->layer2); + write_unlock_irqrestore(&mgr->lock, flags); + if (id < 0) { + l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); + } else { + l2->ch.nr = id; + l2->up->nr = id; + crq->ch = &l2->ch; + id = 0; + } + return id; +} + +static int +mgr_send(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct manager *mgr; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + int ret = -EINVAL; + + mgr = container_of(ch, struct manager, ch); + if (*debug & DEBUG_L2_RECV) + printk(KERN_DEBUG "%s: prim(%x) id(%x)\n", + __func__, hh->prim, hh->id); + switch (hh->prim) { + case PH_DATA_IND: + mISDN_FsmEvent(&mgr->deact, EV_UI, NULL); + ret = ph_data_ind(mgr, skb); + break; + case PH_DATA_CNF: + do_ack(mgr, hh->id); + ret = 0; + break; + case PH_ACTIVATE_IND: + test_and_set_bit(MGR_PH_ACTIVE, &mgr->options); + mISDN_FsmEvent(&mgr->deact, EV_ACTIVATE_IND, NULL); + do_send(mgr); + ret = 0; + break; + case PH_DEACTIVATE_IND: + test_and_clear_bit(MGR_PH_ACTIVE, &mgr->options); + mISDN_FsmEvent(&mgr->deact, EV_DEACTIVATE_IND, NULL); + ret = 0; + break; + case DL_UNITDATA_REQ: + return dl_unit_data(mgr, skb); + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static int +free_teimanager(struct manager *mgr) +{ + struct layer2 *l2, *nl2; + + if (test_bit(MGR_OPT_NETWORK, &mgr->options)) { + /* not locked lock is taken in release tei */ + mgr->up = NULL; + if (test_bit(OPTION_L2_CLEANUP, &mgr->options)) { + list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) { + put_tei_msg(mgr, ID_REMOVE, 0, l2->tei); + mutex_lock(&mgr->ch.st->lmutex); + list_del(&l2->ch.list); + mutex_unlock(&mgr->ch.st->lmutex); + l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); + } + test_and_clear_bit(MGR_OPT_NETWORK, &mgr->options); + } else { + list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) { + l2->up = NULL; + } + } + } + if (test_bit(MGR_OPT_USER, &mgr->options)) { + if (list_empty(&mgr->layer2)) + test_and_clear_bit(MGR_OPT_USER, &mgr->options); + } + mgr->ch.st->dev->D.ctrl(&mgr->ch.st->dev->D, CLOSE_CHANNEL, NULL); + return 0; +} + +static int +ctrl_teimanager(struct manager *mgr, void *arg) +{ + /* currently we only have one option */ + int clean = *((int *)arg); + + if (clean) + test_and_set_bit(OPTION_L2_CLEANUP, &mgr->options); + else + test_and_clear_bit(OPTION_L2_CLEANUP, &mgr->options); + return 0; +} + +/* This function does create a L2 for fixed TEI in NT Mode */ +static int +check_data(struct manager *mgr, struct sk_buff *skb) +{ + struct mISDNhead *hh = mISDN_HEAD_P(skb); + int ret, tei; + struct layer2 *l2; + + if (*debug & DEBUG_L2_CTRL) + printk(KERN_DEBUG "%s: prim(%x) id(%x)\n", + __func__, hh->prim, hh->id); + if (test_bit(MGR_OPT_USER, &mgr->options)) + return -ENOTCONN; + if (hh->prim != PH_DATA_IND) + return -ENOTCONN; + if (skb->len != 3) + return -ENOTCONN; + if (skb->data[0] != 0) + /* only SAPI 0 command */ + return -ENOTCONN; + if (!(skb->data[1] & 1)) /* invalid EA1 */ + return -EINVAL; + tei = skb->data[1] >> 0; + if (tei > 63) /* not a fixed tei */ + return -ENOTCONN; + if ((skb->data[2] & ~0x10) != SABME) + return -ENOTCONN; + /* We got a SABME for a fixed TEI */ + l2 = create_new_tei(mgr, tei); + if (!l2) + return -ENOMEM; + ret = l2->ch.send(&l2->ch, skb); + return ret; +} + +void +delete_teimanager(struct mISDNchannel *ch) +{ + struct manager *mgr; + struct layer2 *l2, *nl2; + + mgr = container_of(ch, struct manager, ch); + /* not locked lock is taken in release tei */ + list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) { + mutex_lock(&mgr->ch.st->lmutex); + list_del(&l2->ch.list); + mutex_unlock(&mgr->ch.st->lmutex); + l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); + } + list_del(&mgr->ch.list); + list_del(&mgr->bcast.list); + skb_queue_purge(&mgr->sendq); + kfree(mgr); +} + +static int +mgr_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct manager *mgr; + int ret = -EINVAL; + + mgr = container_of(ch, struct manager, ch); + if (*debug & DEBUG_L2_CTRL) + printk(KERN_DEBUG "%s(%x, %p)\n", __func__, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + ret = create_teimgr(mgr, arg); + break; + case CLOSE_CHANNEL: + ret = free_teimanager(mgr); + break; + case CONTROL_CHANNEL: + ret = ctrl_teimanager(mgr, arg); + break; + case CHECK_DATA: + ret = check_data(mgr, arg); + break; + } + return ret; +} + +static int +mgr_bcast(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct manager *mgr = container_of(ch, struct manager, bcast); + struct mISDNhead *hh = mISDN_HEAD_P(skb); + struct sk_buff *cskb = NULL; + struct layer2 *l2; + u_long flags; + int ret; + + read_lock_irqsave(&mgr->lock, flags); + list_for_each_entry(l2, &mgr->layer2, list) { + if ((hh->id & MISDN_ID_SAPI_MASK) == + (l2->ch.addr & MISDN_ID_SAPI_MASK)) { + if (list_is_last(&l2->list, &mgr->layer2)) { + cskb = skb; + skb = NULL; + } else { + if (!cskb) + cskb = skb_copy(skb, GFP_KERNEL); + } + if (cskb) { + ret = l2->ch.send(&l2->ch, cskb); + if (ret) { + if (*debug & DEBUG_SEND_ERR) + printk(KERN_DEBUG + "%s ch%d prim(%x) addr(%x)" + " err %d\n", + __func__, l2->ch.nr, + hh->prim, l2->ch.addr, ret); + } else + cskb = NULL; + } else { + printk(KERN_WARNING "%s ch%d addr %x no mem\n", + __func__, ch->nr, ch->addr); + goto out; + } + } + } +out: + read_unlock_irqrestore(&mgr->lock, flags); + if (cskb) + dev_kfree_skb(cskb); + if (skb) + dev_kfree_skb(skb); + return 0; +} + +static int +mgr_bcast_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + + return -EINVAL; +} + +int +create_teimanager(struct mISDNdevice *dev) +{ + struct manager *mgr; + + mgr = kzalloc(sizeof(struct manager), GFP_KERNEL); + if (!mgr) + return -ENOMEM; + INIT_LIST_HEAD(&mgr->layer2); + mgr->lock = __RW_LOCK_UNLOCKED(mgr->lock); + skb_queue_head_init(&mgr->sendq); + mgr->nextid = 1; + mgr->lastid = MISDN_ID_NONE; + mgr->ch.send = mgr_send; + mgr->ch.ctrl = mgr_ctrl; + mgr->ch.st = dev->D.st; + set_channel_address(&mgr->ch, TEI_SAPI, GROUP_TEI); + add_layer2(&mgr->ch, dev->D.st); + mgr->bcast.send = mgr_bcast; + mgr->bcast.ctrl = mgr_bcast_ctrl; + mgr->bcast.st = dev->D.st; + set_channel_address(&mgr->bcast, 0, GROUP_TEI); + add_layer2(&mgr->bcast, dev->D.st); + mgr->deact.debug = *debug & DEBUG_MANAGER; + mgr->deact.userdata = mgr; + mgr->deact.printdebug = da_debug; + mgr->deact.fsm = &deactfsm; + mgr->deact.state = ST_L1_DEACT; + mISDN_FsmInitTimer(&mgr->deact, &mgr->datimer); + dev->teimgr = &mgr->ch; + return 0; +} + +int TEIInit(u_int *deb) +{ + debug = deb; + teifsmu.state_count = TEI_STATE_COUNT; + teifsmu.event_count = TEI_EVENT_COUNT; + teifsmu.strEvent = strTeiEvent; + teifsmu.strState = strTeiState; + mISDN_FsmNew(&teifsmu, TeiFnListUser, ARRAY_SIZE(TeiFnListUser)); + teifsmn.state_count = TEI_STATE_COUNT; + teifsmn.event_count = TEI_EVENT_COUNT; + teifsmn.strEvent = strTeiEvent; + teifsmn.strState = strTeiState; + mISDN_FsmNew(&teifsmn, TeiFnListNet, ARRAY_SIZE(TeiFnListNet)); + deactfsm.state_count = DEACT_STATE_COUNT; + deactfsm.event_count = DEACT_EVENT_COUNT; + deactfsm.strEvent = strDeactEvent; + deactfsm.strState = strDeactState; + mISDN_FsmNew(&deactfsm, DeactFnList, ARRAY_SIZE(DeactFnList)); + return 0; +} + +void TEIFree(void) +{ + mISDN_FsmFree(&teifsmu); + mISDN_FsmFree(&teifsmn); + mISDN_FsmFree(&deactfsm); +} diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c new file mode 100644 index 000000000000..b5fabc7019d8 --- /dev/null +++ b/drivers/isdn/mISDN/timerdev.c @@ -0,0 +1,301 @@ +/* + * + * general timer device for using in ISDN stacks + * + * Author Karsten Keil <kkeil@novell.com> + * + * Copyright 2008 by Karsten Keil <kkeil@novell.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + */ + +#include <linux/poll.h> +#include <linux/vmalloc.h> +#include <linux/timer.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/mISDNif.h> + +static int *debug; + + +struct mISDNtimerdev { + int next_id; + struct list_head pending; + struct list_head expired; + wait_queue_head_t wait; + u_int work; + spinlock_t lock; /* protect lists */ +}; + +struct mISDNtimer { + struct list_head list; + struct mISDNtimerdev *dev; + struct timer_list tl; + int id; +}; + +static int +mISDN_open(struct inode *ino, struct file *filep) +{ + struct mISDNtimerdev *dev; + + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep); + dev = kmalloc(sizeof(struct mISDNtimerdev) , GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->next_id = 1; + INIT_LIST_HEAD(&dev->pending); + INIT_LIST_HEAD(&dev->expired); + spin_lock_init(&dev->lock); + dev->work = 0; + init_waitqueue_head(&dev->wait); + filep->private_data = dev; + __module_get(THIS_MODULE); + return 0; +} + +static int +mISDN_close(struct inode *ino, struct file *filep) +{ + struct mISDNtimerdev *dev = filep->private_data; + struct mISDNtimer *timer, *next; + + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep); + list_for_each_entry_safe(timer, next, &dev->pending, list) { + del_timer(&timer->tl); + kfree(timer); + } + list_for_each_entry_safe(timer, next, &dev->expired, list) { + kfree(timer); + } + kfree(dev); + module_put(THIS_MODULE); + return 0; +} + +static ssize_t +mISDN_read(struct file *filep, char *buf, size_t count, loff_t *off) +{ + struct mISDNtimerdev *dev = filep->private_data; + struct mISDNtimer *timer; + u_long flags; + int ret = 0; + + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s(%p, %p, %d, %p)\n", __func__, + filep, buf, (int)count, off); + if (*off != filep->f_pos) + return -ESPIPE; + + if (list_empty(&dev->expired) && (dev->work == 0)) { + if (filep->f_flags & O_NONBLOCK) + return -EAGAIN; + wait_event_interruptible(dev->wait, (dev->work || + !list_empty(&dev->expired))); + if (signal_pending(current)) + return -ERESTARTSYS; + } + if (count < sizeof(int)) + return -ENOSPC; + if (dev->work) + dev->work = 0; + if (!list_empty(&dev->expired)) { + spin_lock_irqsave(&dev->lock, flags); + timer = (struct mISDNtimer *)dev->expired.next; + list_del(&timer->list); + spin_unlock_irqrestore(&dev->lock, flags); + if (put_user(timer->id, (int *)buf)) + ret = -EFAULT; + else + ret = sizeof(int); + kfree(timer); + } + return ret; +} + +static loff_t +mISDN_llseek(struct file *filep, loff_t offset, int orig) +{ + return -ESPIPE; +} + +static ssize_t +mISDN_write(struct file *filep, const char *buf, size_t count, loff_t *off) +{ + return -EOPNOTSUPP; +} + +static unsigned int +mISDN_poll(struct file *filep, poll_table *wait) +{ + struct mISDNtimerdev *dev = filep->private_data; + unsigned int mask = POLLERR; + + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s(%p, %p)\n", __func__, filep, wait); + if (dev) { + poll_wait(filep, &dev->wait, wait); + mask = 0; + if (dev->work || !list_empty(&dev->expired)) + mask |= (POLLIN | POLLRDNORM); + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s work(%d) empty(%d)\n", __func__, + dev->work, list_empty(&dev->expired)); + } + return mask; +} + +static void +dev_expire_timer(struct mISDNtimer *timer) +{ + u_long flags; + + spin_lock_irqsave(&timer->dev->lock, flags); + list_del(&timer->list); + list_add_tail(&timer->list, &timer->dev->expired); + spin_unlock_irqrestore(&timer->dev->lock, flags); + wake_up_interruptible(&timer->dev->wait); +} + +static int +misdn_add_timer(struct mISDNtimerdev *dev, int timeout) +{ + int id; + u_long flags; + struct mISDNtimer *timer; + + if (!timeout) { + dev->work = 1; + wake_up_interruptible(&dev->wait); + id = 0; + } else { + timer = kzalloc(sizeof(struct mISDNtimer), GFP_KERNEL); + if (!timer) + return -ENOMEM; + spin_lock_irqsave(&dev->lock, flags); + timer->id = dev->next_id++; + if (dev->next_id < 0) + dev->next_id = 1; + list_add_tail(&timer->list, &dev->pending); + spin_unlock_irqrestore(&dev->lock, flags); + timer->dev = dev; + timer->tl.data = (long)timer; + timer->tl.function = (void *) dev_expire_timer; + init_timer(&timer->tl); + timer->tl.expires = jiffies + ((HZ * (u_long)timeout) / 1000); + add_timer(&timer->tl); + id = timer->id; + } + return id; +} + +static int +misdn_del_timer(struct mISDNtimerdev *dev, int id) +{ + u_long flags; + struct mISDNtimer *timer; + int ret = 0; + + spin_lock_irqsave(&dev->lock, flags); + list_for_each_entry(timer, &dev->pending, list) { + if (timer->id == id) { + list_del_init(&timer->list); + del_timer(&timer->tl); + ret = timer->id; + kfree(timer); + goto unlock; + } + } +unlock: + spin_unlock_irqrestore(&dev->lock, flags); + return ret; +} + +static int +mISDN_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct mISDNtimerdev *dev = filep->private_data; + int id, tout, ret = 0; + + + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s(%p, %x, %lx)\n", __func__, + filep, cmd, arg); + switch (cmd) { + case IMADDTIMER: + if (get_user(tout, (int __user *)arg)) { + ret = -EFAULT; + break; + } + id = misdn_add_timer(dev, tout); + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s add %d id %d\n", __func__, + tout, id); + if (id < 0) { + ret = id; + break; + } + if (put_user(id, (int __user *)arg)) + ret = -EFAULT; + break; + case IMDELTIMER: + if (get_user(id, (int __user *)arg)) { + ret = -EFAULT; + break; + } + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s del id %d\n", __func__, id); + id = misdn_del_timer(dev, id); + if (put_user(id, (int __user *)arg)) + ret = -EFAULT; + break; + default: + ret = -EINVAL; + } + return ret; +} + +static struct file_operations mISDN_fops = { + .llseek = mISDN_llseek, + .read = mISDN_read, + .write = mISDN_write, + .poll = mISDN_poll, + .ioctl = mISDN_ioctl, + .open = mISDN_open, + .release = mISDN_close, +}; + +static struct miscdevice mISDNtimer = { + .minor = MISC_DYNAMIC_MINOR, + .name = "mISDNtimer", + .fops = &mISDN_fops, +}; + +int +mISDN_inittimer(int *deb) +{ + int err; + + debug = deb; + err = misc_register(&mISDNtimer); + if (err) + printk(KERN_WARNING "mISDN: Could not register timer device\n"); + return err; +} + +void mISDN_timer_cleanup(void) +{ + misc_deregister(&mISDNtimer); +} diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 86a369bc57d6..9556262dda5a 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -103,6 +103,14 @@ config LEDS_HP6XX This option enables led support for the handheld HP Jornada 620/660/680/690. +config LEDS_PCA9532 + tristate "LED driver for PCA9532 dimmer" + depends on LEDS_CLASS && I2C && INPUT && EXPERIMENTAL + help + This option enables support for NXP pca9532 + led controller. It is generally only usefull + as a platform driver + config LEDS_GPIO tristate "LED Support for GPIO connected LEDs" depends on LEDS_CLASS && GENERIC_GPIO @@ -147,6 +155,14 @@ config LEDS_CLEVO_MAIL To compile this driver as a module, choose M here: the module will be called leds-clevo-mail. +config LEDS_PCA955X + tristate "LED Support for PCA955x I2C chips" + depends on LEDS_CLASS && I2C + help + This option enables support for LEDs connected to PCA955x + LED driver chips accessed via the I2C bus. Supported + devices include PCA9550, PCA9551, PCA9552, and PCA9553. + comment "LED Triggers" config LEDS_TRIGGERS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 973d626f5f4a..ff7982b44565 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -16,11 +16,13 @@ obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o obj-$(CONFIG_LEDS_H1940) += leds-h1940.o obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o +obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o obj-$(CONFIG_LEDS_CM_X270) += leds-cm-x270.o obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o +obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o # LED Triggers obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index 0f242b3f09b6..f910eaffe3a6 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -111,16 +111,17 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger) flags); if (led_cdev->trigger->deactivate) led_cdev->trigger->deactivate(led_cdev); + led_cdev->trigger = NULL; led_set_brightness(led_cdev, LED_OFF); } if (trigger) { write_lock_irqsave(&trigger->leddev_list_lock, flags); list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs); write_unlock_irqrestore(&trigger->leddev_list_lock, flags); + led_cdev->trigger = trigger; if (trigger->activate) trigger->activate(led_cdev); } - led_cdev->trigger = trigger; } EXPORT_SYMBOL_GPL(led_trigger_set); diff --git a/drivers/leds/leds-atmel-pwm.c b/drivers/leds/leds-atmel-pwm.c index 28db6c1444ed..52297c3ab246 100644 --- a/drivers/leds/leds-atmel-pwm.c +++ b/drivers/leds/leds-atmel-pwm.c @@ -37,7 +37,7 @@ static int __init pwmled_probe(struct platform_device *pdev) { const struct gpio_led_platform_data *pdata; struct pwmled *leds; - unsigned i; + int i; int status; pdata = pdev->dev.platform_data; diff --git a/drivers/leds/leds-h1940.c b/drivers/leds/leds-h1940.c index bcec42230389..73c705021686 100644 --- a/drivers/leds/leds-h1940.c +++ b/drivers/leds/leds-h1940.c @@ -23,7 +23,8 @@ /* * Green led. */ -void h1940_greenled_set(struct led_classdev *led_dev, enum led_brightness value) +static void h1940_greenled_set(struct led_classdev *led_dev, + enum led_brightness value) { switch (value) { case LED_HALF: @@ -52,7 +53,8 @@ static struct led_classdev h1940_greenled = { /* * Red led. */ -void h1940_redled_set(struct led_classdev *led_dev, enum led_brightness value) +static void h1940_redled_set(struct led_classdev *led_dev, + enum led_brightness value) { switch (value) { case LED_HALF: @@ -82,7 +84,8 @@ static struct led_classdev h1940_redled = { * Blue led. * (it can only be blue flashing led) */ -void h1940_blueled_set(struct led_classdev *led_dev, enum led_brightness value) +static void h1940_blueled_set(struct led_classdev *led_dev, + enum led_brightness value) { if (value) { /* flashing Blue */ diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c new file mode 100644 index 000000000000..4064d4f6b33b --- /dev/null +++ b/drivers/leds/leds-pca9532.c @@ -0,0 +1,337 @@ +/* + * pca9532.c - 16-bit Led dimmer + * + * Copyright (C) 2008 Riku Voipio <riku.voipio@movial.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 of the License. + * + * Datasheet: http://www.nxp.com/acrobat/datasheets/PCA9532_3.pdf + * + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/leds.h> +#include <linux/input.h> +#include <linux/mutex.h> +#include <linux/leds-pca9532.h> + +static const unsigned short normal_i2c[] = { /*0x60,*/ I2C_CLIENT_END}; +I2C_CLIENT_INSMOD_1(pca9532); + +#define PCA9532_REG_PSC(i) (0x2+(i)*2) +#define PCA9532_REG_PWM(i) (0x3+(i)*2) +#define PCA9532_REG_LS0 0x6 +#define LED_REG(led) ((led>>2)+PCA9532_REG_LS0) +#define LED_NUM(led) (led & 0x3) + +#define ldev_to_led(c) container_of(c, struct pca9532_led, ldev) + +struct pca9532_data { + struct i2c_client *client; + struct pca9532_led leds[16]; + struct mutex update_lock; + struct input_dev *idev; + u8 pwm[2]; + u8 psc[2]; +}; + +static int pca9532_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int pca9532_remove(struct i2c_client *client); + +static const struct i2c_device_id pca9532_id[] = { + { "pca9532", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, pca9532_id); + +static struct i2c_driver pca9532_driver = { + .driver = { + .name = "pca9532", + }, + .probe = pca9532_probe, + .remove = pca9532_remove, + .id_table = pca9532_id, +}; + +/* We have two pwm/blinkers, but 16 possible leds to drive. Additionaly, + * the clever Thecus people are using one pwm to drive the beeper. So, + * as a compromise we average one pwm to the values requested by all + * leds that are not ON/OFF. + * */ +static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink, + enum led_brightness value) +{ + int a = 0, b = 0, i = 0; + struct pca9532_data *data = i2c_get_clientdata(client); + for (i = 0; i < 16; i++) { + if (data->leds[i].type == PCA9532_TYPE_LED && + data->leds[i].state == PCA9532_PWM0+pwm) { + a++; + b += data->leds[i].ldev.brightness; + } + } + if (a == 0) { + dev_err(&client->dev, + "fear of division by zero %d/%d, wanted %d\n", + b, a, value); + return -EINVAL; + } + b = b/a; + if (b > 0xFF) + return -EINVAL; + mutex_lock(&data->update_lock); + data->pwm[pwm] = b; + i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm), + data->pwm[pwm]); + data->psc[pwm] = blink; + i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm), + data->psc[pwm]); + mutex_unlock(&data->update_lock); + return 0; +} + +/* Set LED routing */ +static void pca9532_setled(struct pca9532_led *led) +{ + struct i2c_client *client = led->client; + struct pca9532_data *data = i2c_get_clientdata(client); + char reg; + + mutex_lock(&data->update_lock); + reg = i2c_smbus_read_byte_data(client, LED_REG(led->id)); + /* zero led bits */ + reg = reg & ~(0x3<<LED_NUM(led->id)*2); + /* set the new value */ + reg = reg | (led->state << LED_NUM(led->id)*2); + i2c_smbus_write_byte_data(client, LED_REG(led->id), reg); + mutex_unlock(&data->update_lock); +} + +static void pca9532_set_brightness(struct led_classdev *led_cdev, + enum led_brightness value) +{ + int err = 0; + struct pca9532_led *led = ldev_to_led(led_cdev); + + if (value == LED_OFF) + led->state = PCA9532_OFF; + else if (value == LED_FULL) + led->state = PCA9532_ON; + else { + led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */ + err = pca9532_setpwm(led->client, 0, 0, value); + if (err) + return; /* XXX: led api doesn't allow error code? */ + } + pca9532_setled(led); +} + +static int pca9532_set_blink(struct led_classdev *led_cdev, + unsigned long *delay_on, unsigned long *delay_off) +{ + struct pca9532_led *led = ldev_to_led(led_cdev); + struct i2c_client *client = led->client; + int psc; + + if (*delay_on == 0 && *delay_off == 0) { + /* led subsystem ask us for a blink rate */ + *delay_on = 1000; + *delay_off = 1000; + } + if (*delay_on != *delay_off || *delay_on > 1690 || *delay_on < 6) + return -EINVAL; + + /* Thecus specific: only use PSC/PWM 0 */ + psc = (*delay_on * 152-1)/1000; + return pca9532_setpwm(client, 0, psc, led_cdev->brightness); +} + +int pca9532_event(struct input_dev *dev, unsigned int type, unsigned int code, + int value) +{ + struct pca9532_data *data = input_get_drvdata(dev); + + if (type != EV_SND && (code != SND_BELL || code != SND_TONE)) + return -1; + + /* XXX: allow different kind of beeps with psc/pwm modifications */ + if (value > 1 && value < 32767) + data->pwm[1] = 127; + else + data->pwm[1] = 0; + + dev_info(&dev->dev, "setting beep to %d \n", data->pwm[1]); + mutex_lock(&data->update_lock); + i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1), + data->pwm[1]); + mutex_unlock(&data->update_lock); + + return 0; +} + +static int pca9532_configure(struct i2c_client *client, + struct pca9532_data *data, struct pca9532_platform_data *pdata) +{ + int i, err = 0; + + for (i = 0; i < 2; i++) { + data->pwm[i] = pdata->pwm[i]; + data->psc[i] = pdata->psc[i]; + i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(i), + data->pwm[i]); + i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(i), + data->psc[i]); + } + + for (i = 0; i < 16; i++) { + struct pca9532_led *led = &data->leds[i]; + struct pca9532_led *pled = &pdata->leds[i]; + led->client = client; + led->id = i; + led->type = pled->type; + switch (led->type) { + case PCA9532_TYPE_NONE: + break; + case PCA9532_TYPE_LED: + led->state = pled->state; + led->name = pled->name; + led->ldev.name = led->name; + led->ldev.brightness = LED_OFF; + led->ldev.brightness_set = pca9532_set_brightness; + led->ldev.blink_set = pca9532_set_blink; + if (led_classdev_register(&client->dev, + &led->ldev) < 0) { + dev_err(&client->dev, + "couldn't register LED %s\n", + led->name); + goto exit; + } + pca9532_setled(led); + break; + case PCA9532_TYPE_N2100_BEEP: + BUG_ON(data->idev); + led->state = PCA9532_PWM1; + pca9532_setled(led); + data->idev = input_allocate_device(); + if (data->idev == NULL) { + err = -ENOMEM; + goto exit; + } + data->idev->name = pled->name; + data->idev->phys = "i2c/pca9532"; + data->idev->id.bustype = BUS_HOST; + data->idev->id.vendor = 0x001f; + data->idev->id.product = 0x0001; + data->idev->id.version = 0x0100; + data->idev->evbit[0] = BIT_MASK(EV_SND); + data->idev->sndbit[0] = BIT_MASK(SND_BELL) | + BIT_MASK(SND_TONE); + data->idev->event = pca9532_event; + input_set_drvdata(data->idev, data); + err = input_register_device(data->idev); + if (err) { + input_free_device(data->idev); + data->idev = NULL; + goto exit; + } + break; + } + } + return 0; + +exit: + if (i > 0) + for (i = i - 1; i >= 0; i--) + switch (data->leds[i].type) { + case PCA9532_TYPE_NONE: + break; + case PCA9532_TYPE_LED: + led_classdev_unregister(&data->leds[i].ldev); + break; + case PCA9532_TYPE_N2100_BEEP: + if (data->idev != NULL) { + input_unregister_device(data->idev); + input_free_device(data->idev); + data->idev = NULL; + } + break; + } + + return err; + +} + +static int pca9532_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pca9532_data *data = i2c_get_clientdata(client); + struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + data = kzalloc(sizeof(struct pca9532_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + dev_info(&client->dev, "setting platform data\n"); + i2c_set_clientdata(client, data); + data->client = client; + mutex_init(&data->update_lock); + + if (pca9532_pdata == NULL) + return -EIO; + + pca9532_configure(client, data, pca9532_pdata); + return 0; + +} + +static int pca9532_remove(struct i2c_client *client) +{ + struct pca9532_data *data = i2c_get_clientdata(client); + int i; + for (i = 0; i < 16; i++) + switch (data->leds[i].type) { + case PCA9532_TYPE_NONE: + break; + case PCA9532_TYPE_LED: + led_classdev_unregister(&data->leds[i].ldev); + break; + case PCA9532_TYPE_N2100_BEEP: + if (data->idev != NULL) { + input_unregister_device(data->idev); + input_free_device(data->idev); + data->idev = NULL; + } + break; + } + + kfree(data); + i2c_set_clientdata(client, NULL); + return 0; +} + +static int __init pca9532_init(void) +{ + return i2c_add_driver(&pca9532_driver); +} + +static void __exit pca9532_exit(void) +{ + i2c_del_driver(&pca9532_driver); +} + +MODULE_AUTHOR("Riku Voipio <riku.voipio@movial.fi>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PCA 9532 LED dimmer"); + +module_init(pca9532_init); +module_exit(pca9532_exit); + diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c new file mode 100644 index 000000000000..146c06972863 --- /dev/null +++ b/drivers/leds/leds-pca955x.c @@ -0,0 +1,384 @@ +/* + * Copyright 2007-2008 Extreme Engineering Solutions, Inc. + * + * Author: Nate Case <ncase@xes-inc.com> + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * LED driver for various PCA955x I2C LED drivers + * + * Supported devices: + * + * Device Description 7-bit slave address + * ------ ----------- ------------------- + * PCA9550 2-bit driver 0x60 .. 0x61 + * PCA9551 8-bit driver 0x60 .. 0x67 + * PCA9552 16-bit driver 0x60 .. 0x67 + * PCA9553/01 4-bit driver 0x62 + * PCA9553/02 4-bit driver 0x63 + * + * Philips PCA955x LED driver chips follow a register map as shown below: + * + * Control Register Description + * ---------------- ----------- + * 0x0 Input register 0 + * .. + * NUM_INPUT_REGS - 1 Last Input register X + * + * NUM_INPUT_REGS Frequency prescaler 0 + * NUM_INPUT_REGS + 1 PWM register 0 + * NUM_INPUT_REGS + 2 Frequency prescaler 1 + * NUM_INPUT_REGS + 3 PWM register 1 + * + * NUM_INPUT_REGS + 4 LED selector 0 + * NUM_INPUT_REGS + 4 + * + NUM_LED_REGS - 1 Last LED selector + * + * where NUM_INPUT_REGS and NUM_LED_REGS vary depending on how many + * bits the chip supports. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/leds.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/workqueue.h> + +/* LED select registers determine the source that drives LED outputs */ +#define PCA955X_LS_LED_ON 0x0 /* Output LOW */ +#define PCA955X_LS_LED_OFF 0x1 /* Output HI-Z */ +#define PCA955X_LS_BLINK0 0x2 /* Blink at PWM0 rate */ +#define PCA955X_LS_BLINK1 0x3 /* Blink at PWM1 rate */ + +enum pca955x_type { + pca9550, + pca9551, + pca9552, + pca9553, +}; + +struct pca955x_chipdef { + int bits; + u8 slv_addr; /* 7-bit slave address mask */ + int slv_addr_shift; /* Number of bits to ignore */ +}; + +static struct pca955x_chipdef pca955x_chipdefs[] = { + [pca9550] = { + .bits = 2, + .slv_addr = /* 110000x */ 0x60, + .slv_addr_shift = 1, + }, + [pca9551] = { + .bits = 8, + .slv_addr = /* 1100xxx */ 0x60, + .slv_addr_shift = 3, + }, + [pca9552] = { + .bits = 16, + .slv_addr = /* 1100xxx */ 0x60, + .slv_addr_shift = 3, + }, + [pca9553] = { + .bits = 4, + .slv_addr = /* 110001x */ 0x62, + .slv_addr_shift = 1, + }, +}; + +static const struct i2c_device_id pca955x_id[] = { + { "pca9550", pca9550 }, + { "pca9551", pca9551 }, + { "pca9552", pca9552 }, + { "pca9553", pca9553 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pca955x_id); + +struct pca955x_led { + struct pca955x_chipdef *chipdef; + struct i2c_client *client; + struct work_struct work; + spinlock_t lock; + enum led_brightness brightness; + struct led_classdev led_cdev; + int led_num; /* 0 .. 15 potentially */ + char name[32]; +}; + +/* 8 bits per input register */ +static inline int pca95xx_num_input_regs(int bits) +{ + return (bits + 7) / 8; +} + +/* 4 bits per LED selector register */ +static inline int pca95xx_num_led_regs(int bits) +{ + return (bits + 3) / 4; +} + +/* + * Return an LED selector register value based on an existing one, with + * the appropriate 2-bit state value set for the given LED number (0-3). + */ +static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state) +{ + return (oldval & (~(0x3 << (led_num << 1)))) | + ((state & 0x3) << (led_num << 1)); +} + +/* + * Write to frequency prescaler register, used to program the + * period of the PWM output. period = (PSCx + 1) / 38 + */ +static void pca955x_write_psc(struct i2c_client *client, int n, u8 val) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + + i2c_smbus_write_byte_data(client, + pca95xx_num_input_regs(pca955x->chipdef->bits) + 2*n, + val); +} + +/* + * Write to PWM register, which determines the duty cycle of the + * output. LED is OFF when the count is less than the value of this + * register, and ON when it is greater. If PWMx == 0, LED is always OFF. + * + * Duty cycle is (256 - PWMx) / 256 + */ +static void pca955x_write_pwm(struct i2c_client *client, int n, u8 val) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + + i2c_smbus_write_byte_data(client, + pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + 2*n, + val); +} + +/* + * Write to LED selector register, which determines the source that + * drives the LED output. + */ +static void pca955x_write_ls(struct i2c_client *client, int n, u8 val) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + + i2c_smbus_write_byte_data(client, + pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n, + val); +} + +/* + * Read the LED selector register, which determines the source that + * drives the LED output. + */ +static u8 pca955x_read_ls(struct i2c_client *client, int n) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + + return (u8) i2c_smbus_read_byte_data(client, + pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n); +} + +static void pca955x_led_work(struct work_struct *work) +{ + struct pca955x_led *pca955x; + u8 ls; + int chip_ls; /* which LSx to use (0-3 potentially) */ + int ls_led; /* which set of bits within LSx to use (0-3) */ + + pca955x = container_of(work, struct pca955x_led, work); + chip_ls = pca955x->led_num / 4; + ls_led = pca955x->led_num % 4; + + ls = pca955x_read_ls(pca955x->client, chip_ls); + + switch (pca955x->brightness) { + case LED_FULL: + ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_ON); + break; + case LED_OFF: + ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_OFF); + break; + case LED_HALF: + ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK0); + break; + default: + /* + * Use PWM1 for all other values. This has the unwanted + * side effect of making all LEDs on the chip share the + * same brightness level if set to a value other than + * OFF, HALF, or FULL. But, this is probably better than + * just turning off for all other values. + */ + pca955x_write_pwm(pca955x->client, 1, 255-pca955x->brightness); + ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1); + break; + } + + pca955x_write_ls(pca955x->client, chip_ls, ls); +} + +void pca955x_led_set(struct led_classdev *led_cdev, enum led_brightness value) +{ + struct pca955x_led *pca955x; + + pca955x = container_of(led_cdev, struct pca955x_led, led_cdev); + + spin_lock(&pca955x->lock); + pca955x->brightness = value; + + /* + * Must use workqueue for the actual I/O since I2C operations + * can sleep. + */ + schedule_work(&pca955x->work); + + spin_unlock(&pca955x->lock); +} + +static int __devinit pca955x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pca955x_led *pca955x; + int i; + int err = -ENODEV; + struct pca955x_chipdef *chip; + struct i2c_adapter *adapter; + struct led_platform_data *pdata; + + chip = &pca955x_chipdefs[id->driver_data]; + adapter = to_i2c_adapter(client->dev.parent); + pdata = client->dev.platform_data; + + /* Make sure the slave address / chip type combo given is possible */ + if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) != + chip->slv_addr) { + dev_err(&client->dev, "invalid slave address %02x\n", + client->addr); + return -ENODEV; + } + + printk(KERN_INFO "leds-pca955x: Using %s %d-bit LED driver at " + "slave address 0x%02x\n", + id->name, chip->bits, client->addr); + + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) + return -EIO; + + if (pdata) { + if (pdata->num_leds != chip->bits) { + dev_err(&client->dev, "board info claims %d LEDs" + " on a %d-bit chip\n", + pdata->num_leds, chip->bits); + return -ENODEV; + } + } + + for (i = 0; i < chip->bits; i++) { + pca955x = kzalloc(sizeof(struct pca955x_led), GFP_KERNEL); + if (!pca955x) { + err = -ENOMEM; + goto exit; + } + + pca955x->chipdef = chip; + pca955x->client = client; + pca955x->led_num = i; + /* Platform data can specify LED names and default triggers */ + if (pdata) { + if (pdata->leds[i].name) + snprintf(pca955x->name, 32, "pca955x:%s", + pdata->leds[i].name); + if (pdata->leds[i].default_trigger) + pca955x->led_cdev.default_trigger = + pdata->leds[i].default_trigger; + } else { + snprintf(pca955x->name, 32, "pca955x:%d", i); + } + spin_lock_init(&pca955x->lock); + + pca955x->led_cdev.name = pca955x->name; + pca955x->led_cdev.brightness_set = + pca955x_led_set; + + /* + * Client data is a pointer to the _first_ pca955x_led + * struct + */ + if (i == 0) + i2c_set_clientdata(client, pca955x); + + INIT_WORK(&(pca955x->work), pca955x_led_work); + + led_classdev_register(&client->dev, &(pca955x->led_cdev)); + } + + /* Turn off LEDs */ + for (i = 0; i < pca95xx_num_led_regs(chip->bits); i++) + pca955x_write_ls(client, i, 0x55); + + /* PWM0 is used for half brightness or 50% duty cycle */ + pca955x_write_pwm(client, 0, 255-LED_HALF); + + /* PWM1 is used for variable brightness, default to OFF */ + pca955x_write_pwm(client, 1, 0); + + /* Set to fast frequency so we do not see flashing */ + pca955x_write_psc(client, 0, 0); + pca955x_write_psc(client, 1, 0); + + return 0; +exit: + return err; +} + +static int __devexit pca955x_remove(struct i2c_client *client) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + int leds = pca955x->chipdef->bits; + int i; + + for (i = 0; i < leds; i++) { + led_classdev_unregister(&(pca955x->led_cdev)); + cancel_work_sync(&(pca955x->work)); + kfree(pca955x); + pca955x = pca955x + 1; + } + + return 0; +} + +static struct i2c_driver pca955x_driver = { + .driver = { + .name = "leds-pca955x", + .owner = THIS_MODULE, + }, + .probe = pca955x_probe, + .remove = __devexit_p(pca955x_remove), + .id_table = pca955x_id, +}; + +static int __init pca955x_leds_init(void) +{ + return i2c_add_driver(&pca955x_driver); +} + +static void __exit pca955x_leds_exit(void) +{ + i2c_del_driver(&pca955x_driver); +} + +module_init(pca955x_leds_init); +module_exit(pca955x_leds_exit); + +MODULE_AUTHOR("Nate Case <ncase@xes-inc.com>"); +MODULE_DESCRIPTION("PCA955x LED driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c index 5eea4356d703..90663e01a56e 100644 --- a/drivers/lguest/core.c +++ b/drivers/lguest/core.c @@ -135,6 +135,7 @@ static void unmap_switcher(void) /* Now we just need to free the pages we copied the switcher into */ for (i = 0; i < TOTAL_SWITCHER_PAGES; i++) __free_pages(switcher_page[i], 0); + kfree(switcher_page); } /*H:032 diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c index 0414ddf87587..a1039068f95c 100644 --- a/drivers/lguest/interrupts_and_traps.c +++ b/drivers/lguest/interrupts_and_traps.c @@ -406,7 +406,8 @@ void load_guest_idt_entry(struct lg_cpu *cpu, unsigned int num, u32 lo, u32 hi) * deliver_trap() to bounce it back into the Guest. */ static void default_idt_entry(struct desc_struct *idt, int trap, - const unsigned long handler) + const unsigned long handler, + const struct desc_struct *base) { /* A present interrupt gate. */ u32 flags = 0x8e00; @@ -415,6 +416,10 @@ static void default_idt_entry(struct desc_struct *idt, * the Guest to use the "int" instruction to trigger it. */ if (trap == LGUEST_TRAP_ENTRY) flags |= (GUEST_PL << 13); + else if (base) + /* Copy priv. level from what Guest asked for. This allows + * debug (int 3) traps from Guest userspace, for example. */ + flags |= (base->b & 0x6000); /* Now pack it into the IDT entry in its weird format. */ idt->a = (LGUEST_CS<<16) | (handler&0x0000FFFF); @@ -428,7 +433,7 @@ void setup_default_idt_entries(struct lguest_ro_state *state, unsigned int i; for (i = 0; i < ARRAY_SIZE(state->guest_idt); i++) - default_idt_entry(&state->guest_idt[i], i, def[i]); + default_idt_entry(&state->guest_idt[i], i, def[i], NULL); } /*H:240 We don't use the IDT entries in the "struct lguest" directly, instead @@ -442,6 +447,8 @@ void copy_traps(const struct lg_cpu *cpu, struct desc_struct *idt, /* We can simply copy the direct traps, otherwise we use the default * ones in the Switcher: they will return to the Host. */ for (i = 0; i < ARRAY_SIZE(cpu->arch.idt); i++) { + const struct desc_struct *gidt = &cpu->arch.idt[i]; + /* If no Guest can ever override this trap, leave it alone. */ if (!direct_trap(i)) continue; @@ -449,12 +456,15 @@ void copy_traps(const struct lg_cpu *cpu, struct desc_struct *idt, /* Only trap gates (type 15) can go direct to the Guest. * Interrupt gates (type 14) disable interrupts as they are * entered, which we never let the Guest do. Not present - * entries (type 0x0) also can't go direct, of course. */ - if (idt_type(cpu->arch.idt[i].a, cpu->arch.idt[i].b) == 0xF) - idt[i] = cpu->arch.idt[i]; + * entries (type 0x0) also can't go direct, of course. + * + * If it can't go direct, we still need to copy the priv. level: + * they might want to give userspace access to a software + * interrupt. */ + if (idt_type(gidt->a, gidt->b) == 0xF) + idt[i] = *gidt; else - /* Reset it to the default. */ - default_idt_entry(&idt[i], i, def[i]); + default_idt_entry(&idt[i], i, def[i], gidt); } } diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c index 1a8de57289eb..37344aaee22f 100644 --- a/drivers/lguest/lguest_device.c +++ b/drivers/lguest/lguest_device.c @@ -98,16 +98,20 @@ static u32 lg_get_features(struct virtio_device *vdev) return features; } -static void lg_set_features(struct virtio_device *vdev, u32 features) +static void lg_finalize_features(struct virtio_device *vdev) { - unsigned int i; + unsigned int i, bits; struct lguest_device_desc *desc = to_lgdev(vdev)->desc; /* Second half of bitmap is features we accept. */ u8 *out_features = lg_features(desc) + desc->feature_len; + /* Give virtio_ring a chance to accept features. */ + vring_transport_features(vdev); + memset(out_features, 0, desc->feature_len); - for (i = 0; i < min(desc->feature_len * 8, 32); i++) { - if (features & (1 << i)) + bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8; + for (i = 0; i < bits; i++) { + if (test_bit(i, vdev->features)) out_features[i / 8] |= (1 << (i % 8)); } } @@ -297,7 +301,7 @@ static void lg_del_vq(struct virtqueue *vq) /* The ops structure which hooks everything together. */ static struct virtio_config_ops lguest_config_ops = { .get_features = lg_get_features, - .set_features = lg_set_features, + .finalize_features = lg_finalize_features, .get = lg_get, .set = lg_set, .get_status = lg_get_status, diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index 95dfda52b4f9..bf7942327bda 100644 --- a/drivers/lguest/x86/core.c +++ b/drivers/lguest/x86/core.c @@ -480,7 +480,7 @@ void __init lguest_arch_host_init(void) * bit on its CPU, depending on the argument (0 == unset). */ on_each_cpu(adjust_pge, (void *)0, 1); /* Turn off the feature in the global feature set. */ - clear_bit(X86_FEATURE_PGE, boot_cpu_data.x86_capability); + clear_cpu_cap(&boot_cpu_data, X86_FEATURE_PGE); } put_online_cpus(); }; @@ -491,7 +491,7 @@ void __exit lguest_arch_host_fini(void) /* If we had PGE before we started, turn it back on now. */ get_online_cpus(); if (cpu_had_pge) { - set_bit(X86_FEATURE_PGE, boot_cpu_data.x86_capability); + set_cpu_cap(&boot_cpu_data, X86_FEATURE_PGE); /* adjust_pge's argument "1" means set PGE. */ on_each_cpu(adjust_pge, (void *)1, 1); } diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index e5d446804d32..cae52485208a 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -862,7 +862,8 @@ adbdev_init(void) adb_dev_class = class_create(THIS_MODULE, "adb"); if (IS_ERR(adb_dev_class)) return; - device_create(adb_dev_class, NULL, MKDEV(ADB_MAJOR, 0), "adb"); + device_create_drvdata(adb_dev_class, NULL, MKDEV(ADB_MAJOR, 0), NULL, + "adb"); platform_device_register(&adb_pfdev); platform_driver_probe(&adb_pfdrv, adb_dummy_probe); diff --git a/drivers/mca/mca-bus.c b/drivers/mca/mca-bus.c index 67b8e9453b19..ef2dbfe74714 100644 --- a/drivers/mca/mca-bus.c +++ b/drivers/mca/mca-bus.c @@ -40,7 +40,7 @@ static struct mca_bus *mca_root_busses[MAX_MCA_BUSSES]; struct mca_device_info { short pos_id; /* the 2 byte pos id for this card */ - char name[DEVICE_NAME_SIZE]; + char name[50]; }; static int mca_bus_match (struct device *dev, struct device_driver *drv) diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index b26927ce889c..621a272a2c74 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -225,7 +225,7 @@ static struct page *read_sb_page(mddev_t *mddev, long offset, unsigned long inde || test_bit(Faulty, &rdev->flags)) continue; - target = (rdev->sb_offset << 1) + offset + index * (PAGE_SIZE/512); + target = rdev->sb_start + offset + index * (PAGE_SIZE/512); if (sync_page_io(rdev->bdev, target, PAGE_SIZE, page, READ)) { page->index = index; @@ -241,10 +241,10 @@ static struct page *read_sb_page(mddev_t *mddev, long offset, unsigned long inde static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) { mdk_rdev_t *rdev; - struct list_head *tmp; mddev_t *mddev = bitmap->mddev; - rdev_for_each(rdev, tmp, mddev) + rcu_read_lock(); + rdev_for_each_rcu(rdev, mddev) if (test_bit(In_sync, &rdev->flags) && !test_bit(Faulty, &rdev->flags)) { int size = PAGE_SIZE; @@ -260,32 +260,37 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) + (long)(page->index * (PAGE_SIZE/512)) + size/512 > 0) /* bitmap runs in to metadata */ - return -EINVAL; + goto bad_alignment; if (rdev->data_offset + mddev->size*2 - > rdev->sb_offset*2 + bitmap->offset) + > rdev->sb_start + bitmap->offset) /* data runs in to bitmap */ - return -EINVAL; - } else if (rdev->sb_offset*2 < rdev->data_offset) { + goto bad_alignment; + } else if (rdev->sb_start < rdev->data_offset) { /* METADATA BITMAP DATA */ - if (rdev->sb_offset*2 + if (rdev->sb_start + bitmap->offset + page->index*(PAGE_SIZE/512) + size/512 > rdev->data_offset) /* bitmap runs in to data */ - return -EINVAL; + goto bad_alignment; } else { /* DATA METADATA BITMAP - no problems */ } md_super_write(mddev, rdev, - (rdev->sb_offset<<1) + bitmap->offset + rdev->sb_start + bitmap->offset + page->index * (PAGE_SIZE/512), size, page); } + rcu_read_unlock(); if (wait) md_super_wait(mddev); return 0; + + bad_alignment: + rcu_read_unlock(); + return -EINVAL; } static void bitmap_file_kick(struct bitmap *bitmap); @@ -454,8 +459,11 @@ void bitmap_update_sb(struct bitmap *bitmap) spin_unlock_irqrestore(&bitmap->lock, flags); sb = (bitmap_super_t *)kmap_atomic(bitmap->sb_page, KM_USER0); sb->events = cpu_to_le64(bitmap->mddev->events); - if (!bitmap->mddev->degraded) - sb->events_cleared = cpu_to_le64(bitmap->mddev->events); + if (bitmap->mddev->events < bitmap->events_cleared) { + /* rocking back to read-only */ + bitmap->events_cleared = bitmap->mddev->events; + sb->events_cleared = cpu_to_le64(bitmap->events_cleared); + } kunmap_atomic(sb, KM_USER0); write_page(bitmap, bitmap->sb_page, 1); } @@ -1085,9 +1093,19 @@ void bitmap_daemon_work(struct bitmap *bitmap) } else spin_unlock_irqrestore(&bitmap->lock, flags); lastpage = page; -/* - printk("bitmap clean at page %lu\n", j); -*/ + + /* We are possibly going to clear some bits, so make + * sure that events_cleared is up-to-date. + */ + if (bitmap->need_sync) { + bitmap_super_t *sb; + bitmap->need_sync = 0; + sb = kmap_atomic(bitmap->sb_page, KM_USER0); + sb->events_cleared = + cpu_to_le64(bitmap->events_cleared); + kunmap_atomic(sb, KM_USER0); + write_page(bitmap, bitmap->sb_page, 1); + } spin_lock_irqsave(&bitmap->lock, flags); clear_page_attr(bitmap, page, BITMAP_PAGE_CLEAN); } @@ -1257,6 +1275,12 @@ void bitmap_endwrite(struct bitmap *bitmap, sector_t offset, unsigned long secto return; } + if (success && + bitmap->events_cleared < bitmap->mddev->events) { + bitmap->events_cleared = bitmap->mddev->events; + bitmap->need_sync = 1; + } + if (!success && ! (*bmc & NEEDED_MASK)) *bmc |= NEEDED_MASK; diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index ab6a61db63ce..13956437bc81 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -1216,9 +1216,24 @@ error: return -EINVAL; } +static int crypt_merge(struct dm_target *ti, struct bvec_merge_data *bvm, + struct bio_vec *biovec, int max_size) +{ + struct crypt_config *cc = ti->private; + struct request_queue *q = bdev_get_queue(cc->dev->bdev); + + if (!q->merge_bvec_fn) + return max_size; + + bvm->bi_bdev = cc->dev->bdev; + bvm->bi_sector = cc->start + bvm->bi_sector - ti->begin; + + return min(max_size, q->merge_bvec_fn(q, bvm, biovec)); +} + static struct target_type crypt_target = { .name = "crypt", - .version= {1, 5, 0}, + .version= {1, 6, 0}, .module = THIS_MODULE, .ctr = crypt_ctr, .dtr = crypt_dtr, @@ -1228,6 +1243,7 @@ static struct target_type crypt_target = { .preresume = crypt_preresume, .resume = crypt_resume, .message = crypt_message, + .merge = crypt_merge, }; static int __init dm_crypt_init(void) diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c index 17753d80ad22..6449bcdf84ca 100644 --- a/drivers/md/dm-linear.c +++ b/drivers/md/dm-linear.c @@ -69,13 +69,25 @@ static void linear_dtr(struct dm_target *ti) kfree(lc); } -static int linear_map(struct dm_target *ti, struct bio *bio, - union map_info *map_context) +static sector_t linear_map_sector(struct dm_target *ti, sector_t bi_sector) { - struct linear_c *lc = (struct linear_c *) ti->private; + struct linear_c *lc = ti->private; + + return lc->start + (bi_sector - ti->begin); +} + +static void linear_map_bio(struct dm_target *ti, struct bio *bio) +{ + struct linear_c *lc = ti->private; bio->bi_bdev = lc->dev->bdev; - bio->bi_sector = lc->start + (bio->bi_sector - ti->begin); + bio->bi_sector = linear_map_sector(ti, bio->bi_sector); +} + +static int linear_map(struct dm_target *ti, struct bio *bio, + union map_info *map_context) +{ + linear_map_bio(ti, bio); return DM_MAPIO_REMAPPED; } @@ -114,15 +126,31 @@ static int linear_ioctl(struct dm_target *ti, struct inode *inode, return blkdev_driver_ioctl(bdev->bd_inode, &fake_file, bdev->bd_disk, cmd, arg); } +static int linear_merge(struct dm_target *ti, struct bvec_merge_data *bvm, + struct bio_vec *biovec, int max_size) +{ + struct linear_c *lc = ti->private; + struct request_queue *q = bdev_get_queue(lc->dev->bdev); + + if (!q->merge_bvec_fn) + return max_size; + + bvm->bi_bdev = lc->dev->bdev; + bvm->bi_sector = linear_map_sector(ti, bvm->bi_sector); + + return min(max_size, q->merge_bvec_fn(q, bvm, biovec)); +} + static struct target_type linear_target = { .name = "linear", - .version= {1, 0, 2}, + .version= {1, 0, 3}, .module = THIS_MODULE, .ctr = linear_ctr, .dtr = linear_dtr, .map = linear_map, .status = linear_status, .ioctl = linear_ioctl, + .merge = linear_merge, }; int __init dm_linear_init(void) diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index 67a6f31b7fc3..5b48478c79f5 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c @@ -831,7 +831,7 @@ static struct dm_dirty_log_type _disk_type = { .status = disk_status, }; -int __init dm_dirty_log_init(void) +static int __init dm_dirty_log_init(void) { int r; @@ -848,7 +848,7 @@ int __init dm_dirty_log_init(void) return r; } -void __exit dm_dirty_log_exit(void) +static void __exit dm_dirty_log_exit(void) { dm_dirty_log_type_unregister(&_disk_type); dm_dirty_log_type_unregister(&_core_type); diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 9f7302d4878d..71dd65aa31b6 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -147,9 +147,12 @@ static struct priority_group *alloc_priority_group(void) static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti) { struct pgpath *pgpath, *tmp; + struct multipath *m = ti->private; list_for_each_entry_safe(pgpath, tmp, pgpaths, list) { list_del(&pgpath->list); + if (m->hw_handler_name) + scsi_dh_detach(bdev_get_queue(pgpath->path.dev->bdev)); dm_put_device(ti, pgpath->path.dev); free_pgpath(pgpath); } @@ -525,8 +528,10 @@ static int parse_path_selector(struct arg_set *as, struct priority_group *pg, } r = read_param(_params, shift(as), &ps_argc, &ti->error); - if (r) + if (r) { + dm_put_path_selector(pst); return -EINVAL; + } r = pst->create(&pg->ps, ps_argc, as->argv); if (r) { @@ -546,6 +551,7 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps, { int r; struct pgpath *p; + struct multipath *m = ti->private; /* we need at least a path arg */ if (as->argc < 1) { @@ -564,6 +570,15 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps, goto bad; } + if (m->hw_handler_name) { + r = scsi_dh_attach(bdev_get_queue(p->path.dev->bdev), + m->hw_handler_name); + if (r < 0) { + dm_put_device(ti, p->path.dev); + goto bad; + } + } + r = ps->type->add_path(ps, &p->path, as->argc, as->argv, &ti->error); if (r) { dm_put_device(ti, p->path.dev); @@ -623,8 +638,10 @@ static struct priority_group *parse_priority_group(struct arg_set *as, struct pgpath *pgpath; struct arg_set path_args; - if (as->argc < nr_params) + if (as->argc < nr_params) { + ti->error = "not enough path parameters"; goto bad; + } path_args.argc = nr_params; path_args.argv = as->argv; @@ -867,7 +884,7 @@ static int reinstate_path(struct pgpath *pgpath) if (pgpath->path.is_active) goto out; - if (!pgpath->pg->ps.type) { + if (!pgpath->pg->ps.type->reinstate_path) { DMWARN("Reinstate path not supported by path selector %s", pgpath->pg->ps.type->name); r = -EINVAL; diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 1ba8a47d61b1..6e5528aecc98 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -40,6 +40,11 @@ */ #define SNAPSHOT_PAGES (((1UL << 20) >> PAGE_SHIFT) ? : 1) +/* + * The size of the mempool used to track chunks in use. + */ +#define MIN_IOS 256 + static struct workqueue_struct *ksnapd; static void flush_queued_bios(struct work_struct *work); @@ -91,7 +96,63 @@ struct dm_snap_pending_exception { */ static struct kmem_cache *exception_cache; static struct kmem_cache *pending_cache; -static mempool_t *pending_pool; + +struct dm_snap_tracked_chunk { + struct hlist_node node; + chunk_t chunk; +}; + +static struct kmem_cache *tracked_chunk_cache; + +static struct dm_snap_tracked_chunk *track_chunk(struct dm_snapshot *s, + chunk_t chunk) +{ + struct dm_snap_tracked_chunk *c = mempool_alloc(s->tracked_chunk_pool, + GFP_NOIO); + unsigned long flags; + + c->chunk = chunk; + + spin_lock_irqsave(&s->tracked_chunk_lock, flags); + hlist_add_head(&c->node, + &s->tracked_chunk_hash[DM_TRACKED_CHUNK_HASH(chunk)]); + spin_unlock_irqrestore(&s->tracked_chunk_lock, flags); + + return c; +} + +static void stop_tracking_chunk(struct dm_snapshot *s, + struct dm_snap_tracked_chunk *c) +{ + unsigned long flags; + + spin_lock_irqsave(&s->tracked_chunk_lock, flags); + hlist_del(&c->node); + spin_unlock_irqrestore(&s->tracked_chunk_lock, flags); + + mempool_free(c, s->tracked_chunk_pool); +} + +static int __chunk_is_tracked(struct dm_snapshot *s, chunk_t chunk) +{ + struct dm_snap_tracked_chunk *c; + struct hlist_node *hn; + int found = 0; + + spin_lock_irq(&s->tracked_chunk_lock); + + hlist_for_each_entry(c, hn, + &s->tracked_chunk_hash[DM_TRACKED_CHUNK_HASH(chunk)], node) { + if (c->chunk == chunk) { + found = 1; + break; + } + } + + spin_unlock_irq(&s->tracked_chunk_lock); + + return found; +} /* * One of these per registered origin, held in the snapshot_origins hash @@ -302,14 +363,19 @@ static void free_exception(struct dm_snap_exception *e) kmem_cache_free(exception_cache, e); } -static struct dm_snap_pending_exception *alloc_pending_exception(void) +static struct dm_snap_pending_exception *alloc_pending_exception(struct dm_snapshot *s) { - return mempool_alloc(pending_pool, GFP_NOIO); + struct dm_snap_pending_exception *pe = mempool_alloc(s->pending_pool, + GFP_NOIO); + + pe->snap = s; + + return pe; } static void free_pending_exception(struct dm_snap_pending_exception *pe) { - mempool_free(pe, pending_pool); + mempool_free(pe, pe->snap->pending_pool); } static void insert_completed_exception(struct dm_snapshot *s, @@ -482,6 +548,7 @@ static int set_chunk_size(struct dm_snapshot *s, const char *chunk_size_arg, static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) { struct dm_snapshot *s; + int i; int r = -EINVAL; char persistent; char *origin_path; @@ -564,11 +631,30 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) goto bad5; } + s->pending_pool = mempool_create_slab_pool(MIN_IOS, pending_cache); + if (!s->pending_pool) { + ti->error = "Could not allocate mempool for pending exceptions"; + goto bad6; + } + + s->tracked_chunk_pool = mempool_create_slab_pool(MIN_IOS, + tracked_chunk_cache); + if (!s->tracked_chunk_pool) { + ti->error = "Could not allocate tracked_chunk mempool for " + "tracking reads"; + goto bad_tracked_chunk_pool; + } + + for (i = 0; i < DM_TRACKED_CHUNK_HASH_SIZE; i++) + INIT_HLIST_HEAD(&s->tracked_chunk_hash[i]); + + spin_lock_init(&s->tracked_chunk_lock); + /* Metadata must only be loaded into one table at once */ r = s->store.read_metadata(&s->store); if (r < 0) { ti->error = "Failed to read snapshot metadata"; - goto bad6; + goto bad_load_and_register; } else if (r > 0) { s->valid = 0; DMWARN("Snapshot is marked invalid."); @@ -582,7 +668,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) if (register_snapshot(s)) { r = -EINVAL; ti->error = "Cannot register snapshot origin"; - goto bad6; + goto bad_load_and_register; } ti->private = s; @@ -590,6 +676,12 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) return 0; + bad_load_and_register: + mempool_destroy(s->tracked_chunk_pool); + + bad_tracked_chunk_pool: + mempool_destroy(s->pending_pool); + bad6: dm_kcopyd_client_destroy(s->kcopyd_client); @@ -624,6 +716,9 @@ static void __free_exceptions(struct dm_snapshot *s) static void snapshot_dtr(struct dm_target *ti) { +#ifdef CONFIG_DM_DEBUG + int i; +#endif struct dm_snapshot *s = ti->private; flush_workqueue(ksnapd); @@ -632,8 +727,17 @@ static void snapshot_dtr(struct dm_target *ti) /* After this returns there can be no new kcopyd jobs. */ unregister_snapshot(s); +#ifdef CONFIG_DM_DEBUG + for (i = 0; i < DM_TRACKED_CHUNK_HASH_SIZE; i++) + BUG_ON(!hlist_empty(&s->tracked_chunk_hash[i])); +#endif + + mempool_destroy(s->tracked_chunk_pool); + __free_exceptions(s); + mempool_destroy(s->pending_pool); + dm_put_device(ti, s->origin); dm_put_device(ti, s->cow); @@ -772,6 +876,13 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success) } /* + * Check for conflicting reads. This is extremely improbable, + * so yield() is sufficient and there is no need for a wait queue. + */ + while (__chunk_is_tracked(s, pe->e.old_chunk)) + yield(); + + /* * Add a proper exception, and remove the * in-flight exception from the list. */ @@ -873,7 +984,7 @@ __find_pending_exception(struct dm_snapshot *s, struct bio *bio) * to hold the lock while we do this. */ up_write(&s->lock); - pe = alloc_pending_exception(); + pe = alloc_pending_exception(s); down_write(&s->lock); if (!s->valid) { @@ -893,7 +1004,6 @@ __find_pending_exception(struct dm_snapshot *s, struct bio *bio) bio_list_init(&pe->snapshot_bios); pe->primary_pe = NULL; atomic_set(&pe->ref_count, 0); - pe->snap = s; pe->started = 0; if (s->store.prepare_exception(&s->store, &pe->e)) { @@ -974,14 +1084,10 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio, start_copy(pe); goto out; } - } else - /* - * FIXME: this read path scares me because we - * always use the origin when we have a pending - * exception. However I can't think of a - * situation where this is wrong - ejt. - */ + } else { bio->bi_bdev = s->origin->bdev; + map_context->ptr = track_chunk(s, chunk); + } out_unlock: up_write(&s->lock); @@ -989,6 +1095,18 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio, return r; } +static int snapshot_end_io(struct dm_target *ti, struct bio *bio, + int error, union map_info *map_context) +{ + struct dm_snapshot *s = ti->private; + struct dm_snap_tracked_chunk *c = map_context->ptr; + + if (c) + stop_tracking_chunk(s, c); + + return 0; +} + static void snapshot_resume(struct dm_target *ti) { struct dm_snapshot *s = ti->private; @@ -1266,6 +1384,7 @@ static struct target_type snapshot_target = { .ctr = snapshot_ctr, .dtr = snapshot_dtr, .map = snapshot_map, + .end_io = snapshot_end_io, .resume = snapshot_resume, .status = snapshot_status, }; @@ -1306,9 +1425,9 @@ static int __init dm_snapshot_init(void) goto bad4; } - pending_pool = mempool_create_slab_pool(128, pending_cache); - if (!pending_pool) { - DMERR("Couldn't create pending pool."); + tracked_chunk_cache = KMEM_CACHE(dm_snap_tracked_chunk, 0); + if (!tracked_chunk_cache) { + DMERR("Couldn't create cache to track chunks in use."); r = -ENOMEM; goto bad5; } @@ -1317,13 +1436,13 @@ static int __init dm_snapshot_init(void) if (!ksnapd) { DMERR("Failed to create ksnapd workqueue."); r = -ENOMEM; - goto bad6; + goto bad_pending_pool; } return 0; - bad6: - mempool_destroy(pending_pool); + bad_pending_pool: + kmem_cache_destroy(tracked_chunk_cache); bad5: kmem_cache_destroy(pending_cache); bad4: @@ -1352,9 +1471,9 @@ static void __exit dm_snapshot_exit(void) DMERR("origin unregister failed %d", r); exit_origin_hash(); - mempool_destroy(pending_pool); kmem_cache_destroy(pending_cache); kmem_cache_destroy(exception_cache); + kmem_cache_destroy(tracked_chunk_cache); } /* Module hooks */ diff --git a/drivers/md/dm-snap.h b/drivers/md/dm-snap.h index 24f9fb73b982..292c15609ae3 100644 --- a/drivers/md/dm-snap.h +++ b/drivers/md/dm-snap.h @@ -130,6 +130,10 @@ struct exception_store { void *context; }; +#define DM_TRACKED_CHUNK_HASH_SIZE 16 +#define DM_TRACKED_CHUNK_HASH(x) ((unsigned long)(x) & \ + (DM_TRACKED_CHUNK_HASH_SIZE - 1)) + struct dm_snapshot { struct rw_semaphore lock; struct dm_target *ti; @@ -157,6 +161,8 @@ struct dm_snapshot { /* The last percentage we notified */ int last_percent; + mempool_t *pending_pool; + struct exception_table pending; struct exception_table complete; @@ -174,6 +180,11 @@ struct dm_snapshot { /* Queue of snapshot writes for ksnapd to flush */ struct bio_list queued_bios; struct work_struct queued_bios_work; + + /* Chunks with outstanding reads */ + mempool_t *tracked_chunk_pool; + spinlock_t tracked_chunk_lock; + struct hlist_head tracked_chunk_hash[DM_TRACKED_CHUNK_HASH_SIZE]; }; /* diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 94116eaf4709..798e468103b8 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -506,14 +506,13 @@ void dm_set_device_limits(struct dm_target *ti, struct block_device *bdev) rs->max_sectors = min_not_zero(rs->max_sectors, q->max_sectors); - /* FIXME: Device-Mapper on top of RAID-0 breaks because DM - * currently doesn't honor MD's merge_bvec_fn routine. - * In this case, we'll force DM to use PAGE_SIZE or - * smaller I/O, just to be safe. A better fix is in the - * works, but add this for the time being so it will at - * least operate correctly. + /* + * Check if merge fn is supported. + * If not we'll force DM to use PAGE_SIZE or + * smaller I/O, just to be safe. */ - if (q->merge_bvec_fn) + + if (q->merge_bvec_fn && !ti->type->merge) rs->max_sectors = min_not_zero(rs->max_sectors, (unsigned int) (PAGE_SIZE >> 9)); diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 372369b1cc20..bca448e11878 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -37,8 +37,8 @@ static DEFINE_SPINLOCK(_minor_lock); struct dm_io { struct mapped_device *md; int error; - struct bio *bio; atomic_t io_count; + struct bio *bio; unsigned long start_time; }; @@ -829,6 +829,49 @@ static int __split_bio(struct mapped_device *md, struct bio *bio) * CRUD END *---------------------------------------------------------------*/ +static int dm_merge_bvec(struct request_queue *q, + struct bvec_merge_data *bvm, + struct bio_vec *biovec) +{ + struct mapped_device *md = q->queuedata; + struct dm_table *map = dm_get_table(md); + struct dm_target *ti; + sector_t max_sectors; + int max_size; + + if (unlikely(!map)) + return 0; + + ti = dm_table_find_target(map, bvm->bi_sector); + + /* + * Find maximum amount of I/O that won't need splitting + */ + max_sectors = min(max_io_len(md, bvm->bi_sector, ti), + (sector_t) BIO_MAX_SECTORS); + max_size = (max_sectors << SECTOR_SHIFT) - bvm->bi_size; + if (max_size < 0) + max_size = 0; + + /* + * merge_bvec_fn() returns number of bytes + * it can accept at this offset + * max is precomputed maximal io size + */ + if (max_size && ti->type->merge) + max_size = ti->type->merge(ti, bvm, biovec, max_size); + + /* + * Always allow an entire first page + */ + if (max_size <= biovec->bv_len && !(bvm->bi_size >> SECTOR_SHIFT)) + max_size = biovec->bv_len; + + dm_table_put(map); + + return max_size; +} + /* * The request function that just remaps the bio built up by * dm_merge_bvec. @@ -1032,6 +1075,7 @@ static struct mapped_device *alloc_dev(int minor) blk_queue_make_request(md->queue, dm_request); blk_queue_bounce_limit(md->queue, BLK_BOUNCE_ANY); md->queue->unplug_fn = dm_unplug_all; + blk_queue_merge_bvec(md->queue, dm_merge_bvec); md->io_pool = mempool_create_slab_pool(MIN_IOS, _io_cache); if (!md->io_pool) diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 8c03b634e62e..1e59a0b0a78a 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h @@ -100,12 +100,6 @@ int dm_lock_for_deletion(struct mapped_device *md); void dm_kobject_uevent(struct mapped_device *md); -/* - * Dirty log - */ -int dm_dirty_log_init(void); -void dm_dirty_log_exit(void); - int dm_kcopyd_init(void); void dm_kcopyd_exit(void); diff --git a/drivers/md/faulty.c b/drivers/md/faulty.c index d107ddceefcd..268547dbfbd3 100644 --- a/drivers/md/faulty.c +++ b/drivers/md/faulty.c @@ -297,7 +297,7 @@ static int run(mddev_t *mddev) rdev_for_each(rdev, tmp, mddev) conf->rdev = rdev; - mddev->array_size = mddev->size; + mddev->array_sectors = mddev->size * 2; mddev->private = conf; reconfig(mddev, mddev->layout, -1); diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 6a866d7c8ae5..b1eebf88c209 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -122,13 +122,13 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) return NULL; cnt = 0; - conf->array_size = 0; + conf->array_sectors = 0; rdev_for_each(rdev, tmp, mddev) { int j = rdev->raid_disk; dev_info_t *disk = conf->disks + j; - if (j < 0 || j > raid_disks || disk->rdev) { + if (j < 0 || j >= raid_disks || disk->rdev) { printk("linear: disk numbering problem. Aborting!\n"); goto out; } @@ -146,7 +146,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9); disk->size = rdev->size; - conf->array_size += rdev->size; + conf->array_sectors += rdev->size * 2; cnt++; } @@ -155,7 +155,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) goto out; } - min_spacing = conf->array_size; + min_spacing = conf->array_sectors / 2; sector_div(min_spacing, PAGE_SIZE/sizeof(struct dev_info *)); /* min_spacing is the minimum spacing that will fit the hash @@ -164,7 +164,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) * that is larger than min_spacing as use the size of that as * the actual spacing */ - conf->hash_spacing = conf->array_size; + conf->hash_spacing = conf->array_sectors / 2; for (i=0; i < cnt-1 ; i++) { sector_t sz = 0; int j; @@ -194,7 +194,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) unsigned round; unsigned long base; - sz = conf->array_size >> conf->preshift; + sz = conf->array_sectors >> (conf->preshift + 1); sz += 1; /* force round-up */ base = conf->hash_spacing >> conf->preshift; round = sector_div(sz, base); @@ -221,7 +221,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) curr_offset = 0; i = 0; for (curr_offset = 0; - curr_offset < conf->array_size; + curr_offset < conf->array_sectors / 2; curr_offset += conf->hash_spacing) { while (i < raid_disks-1 && @@ -258,7 +258,7 @@ static int linear_run (mddev_t *mddev) if (!conf) return 1; mddev->private = conf; - mddev->array_size = conf->array_size; + mddev->array_sectors = conf->array_sectors; blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec); mddev->queue->unplug_fn = linear_unplug; @@ -292,8 +292,8 @@ static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev) newconf->prev = mddev_to_conf(mddev); mddev->private = newconf; mddev->raid_disks++; - mddev->array_size = newconf->array_size; - set_capacity(mddev->gendisk, mddev->array_size << 1); + mddev->array_sectors = newconf->array_sectors; + set_capacity(mddev->gendisk, mddev->array_sectors); return 0; } diff --git a/drivers/md/md.c b/drivers/md/md.c index 2580ac1b9b0f..c2ff77ccec50 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -169,7 +169,6 @@ void md_new_event(mddev_t *mddev) { atomic_inc(&md_event_count); wake_up(&md_event_waiters); - sysfs_notify(&mddev->kobj, NULL, "sync_action"); } EXPORT_SYMBOL_GPL(md_new_event); @@ -274,10 +273,12 @@ static mddev_t * mddev_find(dev_t unit) INIT_LIST_HEAD(&new->all_mddevs); init_timer(&new->safemode_timer); atomic_set(&new->active, 1); + atomic_set(&new->openers, 0); spin_lock_init(&new->write_lock); init_waitqueue_head(&new->sb_wait); init_waitqueue_head(&new->recovery_wait); new->reshape_position = MaxSector; + new->resync_min = 0; new->resync_max = MaxSector; new->level = LEVEL_NONE; @@ -347,21 +348,20 @@ static struct mdk_personality *find_pers(int level, char *clevel) return NULL; } +/* return the offset of the super block in 512byte sectors */ static inline sector_t calc_dev_sboffset(struct block_device *bdev) { - sector_t size = bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; - return MD_NEW_SIZE_BLOCKS(size); + sector_t num_sectors = bdev->bd_inode->i_size / 512; + return MD_NEW_SIZE_SECTORS(num_sectors); } -static sector_t calc_dev_size(mdk_rdev_t *rdev, unsigned chunk_size) +static sector_t calc_num_sectors(mdk_rdev_t *rdev, unsigned chunk_size) { - sector_t size; - - size = rdev->sb_offset; + sector_t num_sectors = rdev->sb_start; if (chunk_size) - size &= ~((sector_t)chunk_size/1024 - 1); - return size; + num_sectors &= ~((sector_t)chunk_size/512 - 1); + return num_sectors; } static int alloc_disk_sb(mdk_rdev_t * rdev) @@ -372,7 +372,7 @@ static int alloc_disk_sb(mdk_rdev_t * rdev) rdev->sb_page = alloc_page(GFP_KERNEL); if (!rdev->sb_page) { printk(KERN_ALERT "md: out of memory.\n"); - return -EINVAL; + return -ENOMEM; } return 0; @@ -384,7 +384,7 @@ static void free_disk_sb(mdk_rdev_t * rdev) put_page(rdev->sb_page); rdev->sb_loaded = 0; rdev->sb_page = NULL; - rdev->sb_offset = 0; + rdev->sb_start = 0; rdev->size = 0; } } @@ -530,7 +530,7 @@ static int read_disk_sb(mdk_rdev_t * rdev, int size) return 0; - if (!sync_page_io(rdev->bdev, rdev->sb_offset<<1, size, rdev->sb_page, READ)) + if (!sync_page_io(rdev->bdev, rdev->sb_start, size, rdev->sb_page, READ)) goto fail; rdev->sb_loaded = 1; return 0; @@ -543,17 +543,12 @@ fail: static int uuid_equal(mdp_super_t *sb1, mdp_super_t *sb2) { - if ( (sb1->set_uuid0 == sb2->set_uuid0) && - (sb1->set_uuid1 == sb2->set_uuid1) && - (sb1->set_uuid2 == sb2->set_uuid2) && - (sb1->set_uuid3 == sb2->set_uuid3)) - - return 1; - - return 0; + return sb1->set_uuid0 == sb2->set_uuid0 && + sb1->set_uuid1 == sb2->set_uuid1 && + sb1->set_uuid2 == sb2->set_uuid2 && + sb1->set_uuid3 == sb2->set_uuid3; } - static int sb_equal(mdp_super_t *sb1, mdp_super_t *sb2) { int ret; @@ -564,7 +559,7 @@ static int sb_equal(mdp_super_t *sb1, mdp_super_t *sb2) if (!tmp1 || !tmp2) { ret = 0; - printk(KERN_INFO "md.c: sb1 is not equal to sb2!\n"); + printk(KERN_INFO "md.c sb_equal(): failed to allocate memory!\n"); goto abort; } @@ -577,11 +572,7 @@ static int sb_equal(mdp_super_t *sb1, mdp_super_t *sb2) tmp1->nr_disks = 0; tmp2->nr_disks = 0; - if (memcmp(tmp1, tmp2, MD_SB_GENERIC_CONSTANT_WORDS * 4)) - ret = 0; - else - ret = 1; - + ret = (memcmp(tmp1, tmp2, MD_SB_GENERIC_CONSTANT_WORDS * 4) == 0); abort: kfree(tmp1); kfree(tmp2); @@ -658,11 +649,14 @@ static unsigned int calc_sb_csum(mdp_super_t * sb) */ struct super_type { - char *name; - struct module *owner; - int (*load_super)(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version); - int (*validate_super)(mddev_t *mddev, mdk_rdev_t *rdev); - void (*sync_super)(mddev_t *mddev, mdk_rdev_t *rdev); + char *name; + struct module *owner; + int (*load_super)(mdk_rdev_t *rdev, mdk_rdev_t *refdev, + int minor_version); + int (*validate_super)(mddev_t *mddev, mdk_rdev_t *rdev); + void (*sync_super)(mddev_t *mddev, mdk_rdev_t *rdev); + unsigned long long (*rdev_size_change)(mdk_rdev_t *rdev, + sector_t num_sectors); }; /* @@ -673,16 +667,14 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE]; mdp_super_t *sb; int ret; - sector_t sb_offset; /* - * Calculate the position of the superblock, + * Calculate the position of the superblock (512byte sectors), * it's at the end of the disk. * * It also happens to be a multiple of 4Kb. */ - sb_offset = calc_dev_sboffset(rdev->bdev); - rdev->sb_offset = sb_offset; + rdev->sb_start = calc_dev_sboffset(rdev->bdev); ret = read_disk_sb(rdev, MD_SB_BYTES); if (ret) return ret; @@ -759,7 +751,7 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version else ret = 0; } - rdev->size = calc_dev_size(rdev, sb->chunk_size); + rdev->size = calc_num_sectors(rdev, sb->chunk_size) / 2; if (rdev->size < sb->size && sb->level > 1) /* "this cannot possibly happen" ... */ @@ -1004,6 +996,26 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) } /* + * rdev_size_change for 0.90.0 + */ +static unsigned long long +super_90_rdev_size_change(mdk_rdev_t *rdev, sector_t num_sectors) +{ + if (num_sectors && num_sectors < rdev->mddev->size * 2) + return 0; /* component must fit device */ + if (rdev->mddev->bitmap_offset) + return 0; /* can't move bitmap */ + rdev->sb_start = calc_dev_sboffset(rdev->bdev); + if (!num_sectors || num_sectors > rdev->sb_start) + num_sectors = rdev->sb_start; + md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size, + rdev->sb_page); + md_super_wait(rdev->mddev); + return num_sectors / 2; /* kB for sysfs */ +} + + +/* * version 1 superblock */ @@ -1034,12 +1046,12 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) { struct mdp_superblock_1 *sb; int ret; - sector_t sb_offset; + sector_t sb_start; char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE]; int bmask; /* - * Calculate the position of the superblock. + * Calculate the position of the superblock in 512byte sectors. * It is always aligned to a 4K boundary and * depeding on minor_version, it can be: * 0: At least 8K, but less than 12K, from end of device @@ -1048,22 +1060,20 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) */ switch(minor_version) { case 0: - sb_offset = rdev->bdev->bd_inode->i_size >> 9; - sb_offset -= 8*2; - sb_offset &= ~(sector_t)(4*2-1); - /* convert from sectors to K */ - sb_offset /= 2; + sb_start = rdev->bdev->bd_inode->i_size >> 9; + sb_start -= 8*2; + sb_start &= ~(sector_t)(4*2-1); break; case 1: - sb_offset = 0; + sb_start = 0; break; case 2: - sb_offset = 4; + sb_start = 8; break; default: return -EINVAL; } - rdev->sb_offset = sb_offset; + rdev->sb_start = sb_start; /* superblock is rarely larger than 1K, but it can be larger, * and it is safe to read 4k, so we do that @@ -1077,7 +1087,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) if (sb->magic != cpu_to_le32(MD_SB_MAGIC) || sb->major_version != cpu_to_le32(1) || le32_to_cpu(sb->max_dev) > (4096-256)/2 || - le64_to_cpu(sb->super_offset) != (rdev->sb_offset<<1) || + le64_to_cpu(sb->super_offset) != rdev->sb_start || (le32_to_cpu(sb->feature_map) & ~MD_FEATURE_ALL) != 0) return -EINVAL; @@ -1113,7 +1123,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) rdev->sb_size = (rdev->sb_size | bmask) + 1; if (minor_version - && rdev->data_offset < sb_offset + (rdev->sb_size/512)) + && rdev->data_offset < sb_start + (rdev->sb_size/512)) return -EINVAL; if (sb->level == cpu_to_le32(LEVEL_MULTIPATH)) @@ -1149,7 +1159,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) if (minor_version) rdev->size = ((rdev->bdev->bd_inode->i_size>>9) - le64_to_cpu(sb->data_offset)) / 2; else - rdev->size = rdev->sb_offset; + rdev->size = rdev->sb_start / 2; if (rdev->size < le64_to_cpu(sb->data_size)/2) return -EINVAL; rdev->size = le64_to_cpu(sb->data_size)/2; @@ -1328,35 +1338,74 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev) sb->sb_csum = calc_sb_1_csum(sb); } +static unsigned long long +super_1_rdev_size_change(mdk_rdev_t *rdev, sector_t num_sectors) +{ + struct mdp_superblock_1 *sb; + sector_t max_sectors; + if (num_sectors && num_sectors < rdev->mddev->size * 2) + return 0; /* component must fit device */ + if (rdev->sb_start < rdev->data_offset) { + /* minor versions 1 and 2; superblock before data */ + max_sectors = rdev->bdev->bd_inode->i_size >> 9; + max_sectors -= rdev->data_offset; + if (!num_sectors || num_sectors > max_sectors) + num_sectors = max_sectors; + } else if (rdev->mddev->bitmap_offset) { + /* minor version 0 with bitmap we can't move */ + return 0; + } else { + /* minor version 0; superblock after data */ + sector_t sb_start; + sb_start = (rdev->bdev->bd_inode->i_size >> 9) - 8*2; + sb_start &= ~(sector_t)(4*2 - 1); + max_sectors = rdev->size * 2 + sb_start - rdev->sb_start; + if (!num_sectors || num_sectors > max_sectors) + num_sectors = max_sectors; + rdev->sb_start = sb_start; + } + sb = (struct mdp_superblock_1 *) page_address(rdev->sb_page); + sb->data_size = cpu_to_le64(num_sectors); + sb->super_offset = rdev->sb_start; + sb->sb_csum = calc_sb_1_csum(sb); + md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size, + rdev->sb_page); + md_super_wait(rdev->mddev); + return num_sectors / 2; /* kB for sysfs */ +} static struct super_type super_types[] = { [0] = { .name = "0.90.0", .owner = THIS_MODULE, - .load_super = super_90_load, - .validate_super = super_90_validate, - .sync_super = super_90_sync, + .load_super = super_90_load, + .validate_super = super_90_validate, + .sync_super = super_90_sync, + .rdev_size_change = super_90_rdev_size_change, }, [1] = { .name = "md-1", .owner = THIS_MODULE, - .load_super = super_1_load, - .validate_super = super_1_validate, - .sync_super = super_1_sync, + .load_super = super_1_load, + .validate_super = super_1_validate, + .sync_super = super_1_sync, + .rdev_size_change = super_1_rdev_size_change, }, }; static int match_mddev_units(mddev_t *mddev1, mddev_t *mddev2) { - struct list_head *tmp, *tmp2; mdk_rdev_t *rdev, *rdev2; - rdev_for_each(rdev, tmp, mddev1) - rdev_for_each(rdev2, tmp2, mddev2) + rcu_read_lock(); + rdev_for_each_rcu(rdev, mddev1) + rdev_for_each_rcu(rdev2, mddev2) if (rdev->bdev->bd_contains == - rdev2->bdev->bd_contains) + rdev2->bdev->bd_contains) { + rcu_read_unlock(); return 1; - + } + rcu_read_unlock(); return 0; } @@ -1423,7 +1472,7 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) kobject_del(&rdev->kobj); goto fail; } - list_add(&rdev->same_set, &mddev->disks); + list_add_rcu(&rdev->same_set, &mddev->disks); bd_claim_by_disk(rdev->bdev, rdev->bdev->bd_holder, mddev->gendisk); return 0; @@ -1448,14 +1497,16 @@ static void unbind_rdev_from_array(mdk_rdev_t * rdev) return; } bd_release_from_disk(rdev->bdev, rdev->mddev->gendisk); - list_del_init(&rdev->same_set); + list_del_rcu(&rdev->same_set); printk(KERN_INFO "md: unbind<%s>\n", bdevname(rdev->bdev,b)); rdev->mddev = NULL; sysfs_remove_link(&rdev->kobj, "block"); /* We need to delay this, otherwise we can deadlock when - * writing to 'remove' to "dev/state" + * writing to 'remove' to "dev/state". We also need + * to delay it due to rcu usage. */ + synchronize_rcu(); INIT_WORK(&rdev->del_work, md_delayed_delete); kobject_get(&rdev->kobj); schedule_work(&rdev->del_work); @@ -1511,7 +1562,6 @@ static void export_rdev(mdk_rdev_t * rdev) if (rdev->mddev) MD_BUG(); free_disk_sb(rdev); - list_del_init(&rdev->same_set); #ifndef MODULE if (test_bit(AutoDetected, &rdev->flags)) md_autodetect_dev(rdev->bdev->bd_dev); @@ -1758,11 +1808,11 @@ repeat: dprintk("%s ", bdevname(rdev->bdev,b)); if (!test_bit(Faulty, &rdev->flags)) { md_super_write(mddev,rdev, - rdev->sb_offset<<1, rdev->sb_size, + rdev->sb_start, rdev->sb_size, rdev->sb_page); dprintk(KERN_INFO "(write) %s's sb offset: %llu\n", bdevname(rdev->bdev,b), - (unsigned long long)rdev->sb_offset); + (unsigned long long)rdev->sb_start); rdev->sb_events = mddev->events; } else @@ -1787,7 +1837,7 @@ repeat: } -/* words written to sysfs files may, or my not, be \n terminated. +/* words written to sysfs files may, or may not, be \n terminated. * We want to accept with case. For this we use cmd_match. */ static int cmd_match(const char *cmd, const char *str) @@ -1886,6 +1936,8 @@ state_store(mdk_rdev_t *rdev, const char *buf, size_t len) err = 0; } + if (!err) + sysfs_notify(&rdev->kobj, NULL, "state"); return err ? err : len; } static struct rdev_sysfs_entry rdev_state = @@ -1931,7 +1983,7 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len) slot = -1; else if (e==buf || (*e && *e!= '\n')) return -EINVAL; - if (rdev->mddev->pers) { + if (rdev->mddev->pers && slot == -1) { /* Setting 'slot' on an active array requires also * updating the 'rd%d' link, and communicating * with the personality with ->hot_*_disk. @@ -1939,8 +1991,6 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len) * failed/spare devices. This normally happens automatically, * but not when the metadata is externally managed. */ - if (slot != -1) - return -EBUSY; if (rdev->raid_disk == -1) return -EEXIST; /* personality does all needed checks */ @@ -1954,6 +2004,43 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len) sysfs_remove_link(&rdev->mddev->kobj, nm); set_bit(MD_RECOVERY_NEEDED, &rdev->mddev->recovery); md_wakeup_thread(rdev->mddev->thread); + } else if (rdev->mddev->pers) { + mdk_rdev_t *rdev2; + struct list_head *tmp; + /* Activating a spare .. or possibly reactivating + * if we every get bitmaps working here. + */ + + if (rdev->raid_disk != -1) + return -EBUSY; + + if (rdev->mddev->pers->hot_add_disk == NULL) + return -EINVAL; + + rdev_for_each(rdev2, tmp, rdev->mddev) + if (rdev2->raid_disk == slot) + return -EEXIST; + + rdev->raid_disk = slot; + if (test_bit(In_sync, &rdev->flags)) + rdev->saved_raid_disk = slot; + else + rdev->saved_raid_disk = -1; + err = rdev->mddev->pers-> + hot_add_disk(rdev->mddev, rdev); + if (err) { + rdev->raid_disk = -1; + return err; + } else + sysfs_notify(&rdev->kobj, NULL, "state"); + sprintf(nm, "rd%d", rdev->raid_disk); + if (sysfs_create_link(&rdev->mddev->kobj, &rdev->kobj, nm)) + printk(KERN_WARNING + "md: cannot register " + "%s for %s\n", + nm, mdname(rdev->mddev)); + + /* don't wakeup anyone, leave that to userspace. */ } else { if (slot >= rdev->mddev->raid_disks) return -ENOSPC; @@ -1962,6 +2049,7 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len) clear_bit(Faulty, &rdev->flags); clear_bit(WriteMostly, &rdev->flags); set_bit(In_sync, &rdev->flags); + sysfs_notify(&rdev->kobj, NULL, "state"); } return len; } @@ -1983,7 +2071,7 @@ offset_store(mdk_rdev_t *rdev, const char *buf, size_t len) unsigned long long offset = simple_strtoull(buf, &e, 10); if (e==buf || (*e && *e != '\n')) return -EINVAL; - if (rdev->mddev->pers) + if (rdev->mddev->pers && rdev->raid_disk >= 0) return -EBUSY; if (rdev->size && rdev->mddev->external) /* Must set offset before size, so overlap checks @@ -2015,17 +2103,30 @@ static int overlaps(sector_t s1, sector_t l1, sector_t s2, sector_t l2) static ssize_t rdev_size_store(mdk_rdev_t *rdev, const char *buf, size_t len) { - char *e; - unsigned long long size = simple_strtoull(buf, &e, 10); + unsigned long long size; unsigned long long oldsize = rdev->size; mddev_t *my_mddev = rdev->mddev; - if (e==buf || (*e && *e != '\n')) + if (strict_strtoull(buf, 10, &size) < 0) return -EINVAL; - if (my_mddev->pers) - return -EBUSY; + if (size < my_mddev->size) + return -EINVAL; + if (my_mddev->pers && rdev->raid_disk >= 0) { + if (my_mddev->persistent) { + size = super_types[my_mddev->major_version]. + rdev_size_change(rdev, size * 2); + if (!size) + return -EBUSY; + } else if (!size) { + size = (rdev->bdev->bd_inode->i_size >> 10); + size -= rdev->data_offset/2; + } + if (size < my_mddev->size) + return -EINVAL; /* component must fit device */ + } + rdev->size = size; - if (size > oldsize && rdev->mddev->external) { + if (size > oldsize && my_mddev->external) { /* need to check that all other rdevs with the same ->bdev * do not overlap. We need to unlock the mddev to avoid * a deadlock. We have already changed rdev->size, and if @@ -2044,8 +2145,9 @@ rdev_size_store(mdk_rdev_t *rdev, const char *buf, size_t len) if (test_bit(AllReserved, &rdev2->flags) || (rdev->bdev == rdev2->bdev && rdev != rdev2 && - overlaps(rdev->data_offset, rdev->size, - rdev2->data_offset, rdev2->size))) { + overlaps(rdev->data_offset, rdev->size * 2, + rdev2->data_offset, + rdev2->size * 2))) { overlap = 1; break; } @@ -2067,8 +2169,6 @@ rdev_size_store(mdk_rdev_t *rdev, const char *buf, size_t len) return -EBUSY; } } - if (size < my_mddev->size || my_mddev->size == 0) - my_mddev->size = size; return len; } @@ -2512,7 +2612,7 @@ __ATTR(resync_start, S_IRUGO|S_IWUSR, resync_start_show, resync_start_store); * When written, doesn't tear down array, but just stops it * suspended (not supported yet) * All IO requests will block. The array can be reconfigured. - * Writing this, if accepted, will block until array is quiessent + * Writing this, if accepted, will block until array is quiescent * readonly * no resync can happen. no superblocks get written. * write requests fail @@ -2585,7 +2685,7 @@ array_state_show(mddev_t *mddev, char *page) return sprintf(page, "%s\n", array_states[st]); } -static int do_md_stop(mddev_t * mddev, int ro); +static int do_md_stop(mddev_t * mddev, int ro, int is_open); static int do_md_run(mddev_t * mddev); static int restart_array(mddev_t *mddev); @@ -2599,16 +2699,16 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len) break; case clear: /* stopping an active array */ - if (atomic_read(&mddev->active) > 1) + if (atomic_read(&mddev->openers) > 0) return -EBUSY; - err = do_md_stop(mddev, 0); + err = do_md_stop(mddev, 0, 0); break; case inactive: /* stopping an active array */ if (mddev->pers) { - if (atomic_read(&mddev->active) > 1) + if (atomic_read(&mddev->openers) > 0) return -EBUSY; - err = do_md_stop(mddev, 2); + err = do_md_stop(mddev, 2, 0); } else err = 0; /* already inactive */ break; @@ -2616,7 +2716,7 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len) break; /* not supported yet */ case readonly: if (mddev->pers) - err = do_md_stop(mddev, 1); + err = do_md_stop(mddev, 1, 0); else { mddev->ro = 1; set_disk_ro(mddev->gendisk, 1); @@ -2626,7 +2726,7 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len) case read_auto: if (mddev->pers) { if (mddev->ro != 1) - err = do_md_stop(mddev, 1); + err = do_md_stop(mddev, 1, 0); else err = restart_array(mddev); if (err == 0) { @@ -2681,8 +2781,10 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len) } if (err) return err; - else + else { + sysfs_notify(&mddev->kobj, NULL, "array_state"); return len; + } } static struct md_sysfs_entry md_array_state = __ATTR(array_state, S_IRUGO|S_IWUSR, array_state_show, array_state_store); @@ -2785,7 +2887,7 @@ size_show(mddev_t *mddev, char *page) return sprintf(page, "%llu\n", (unsigned long long)mddev->size); } -static int update_size(mddev_t *mddev, unsigned long size); +static int update_size(mddev_t *mddev, sector_t num_sectors); static ssize_t size_store(mddev_t *mddev, const char *buf, size_t len) @@ -2802,7 +2904,7 @@ size_store(mddev_t *mddev, const char *buf, size_t len) return -EINVAL; if (mddev->pers) { - err = update_size(mddev, size); + err = update_size(mddev, size * 2); md_update_sb(mddev, 1); } else { if (mddev->size == 0 || @@ -2899,7 +3001,7 @@ action_show(mddev_t *mddev, char *page) type = "check"; else type = "repair"; - } else + } else if (test_bit(MD_RECOVERY_RECOVER, &mddev->recovery)) type = "recover"; } return sprintf(page, "%s\n", type); @@ -2921,15 +3023,19 @@ action_store(mddev_t *mddev, const char *page, size_t len) } else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) || test_bit(MD_RECOVERY_NEEDED, &mddev->recovery)) return -EBUSY; - else if (cmd_match(page, "resync") || cmd_match(page, "recover")) + else if (cmd_match(page, "resync")) + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + else if (cmd_match(page, "recover")) { + set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); - else if (cmd_match(page, "reshape")) { + } else if (cmd_match(page, "reshape")) { int err; if (mddev->pers->start_reshape == NULL) return -EINVAL; err = mddev->pers->start_reshape(mddev); if (err) return err; + sysfs_notify(&mddev->kobj, NULL, "degraded"); } else { if (cmd_match(page, "check")) set_bit(MD_RECOVERY_CHECK, &mddev->recovery); @@ -2940,6 +3046,7 @@ action_store(mddev_t *mddev, const char *page, size_t len) } set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); + sysfs_notify(&mddev->kobj, NULL, "sync_action"); return len; } @@ -3049,11 +3156,11 @@ static ssize_t sync_speed_show(mddev_t *mddev, char *page) { unsigned long resync, dt, db; - resync = (mddev->curr_mark_cnt - atomic_read(&mddev->recovery_active)); - dt = ((jiffies - mddev->resync_mark) / HZ); + resync = mddev->curr_mark_cnt - atomic_read(&mddev->recovery_active); + dt = (jiffies - mddev->resync_mark) / HZ; if (!dt) dt++; - db = resync - (mddev->resync_mark_cnt); - return sprintf(page, "%ld\n", db/dt/2); /* K/sec */ + db = resync - mddev->resync_mark_cnt; + return sprintf(page, "%lu\n", db/dt/2); /* K/sec */ } static struct md_sysfs_entry md_sync_speed = __ATTR_RO(sync_speed); @@ -3075,6 +3182,36 @@ sync_completed_show(mddev_t *mddev, char *page) static struct md_sysfs_entry md_sync_completed = __ATTR_RO(sync_completed); static ssize_t +min_sync_show(mddev_t *mddev, char *page) +{ + return sprintf(page, "%llu\n", + (unsigned long long)mddev->resync_min); +} +static ssize_t +min_sync_store(mddev_t *mddev, const char *buf, size_t len) +{ + unsigned long long min; + if (strict_strtoull(buf, 10, &min)) + return -EINVAL; + if (min > mddev->resync_max) + return -EINVAL; + if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) + return -EBUSY; + + /* Must be a multiple of chunk_size */ + if (mddev->chunk_size) { + if (min & (sector_t)((mddev->chunk_size>>9)-1)) + return -EINVAL; + } + mddev->resync_min = min; + + return len; +} + +static struct md_sysfs_entry md_min_sync = +__ATTR(sync_min, S_IRUGO|S_IWUSR, min_sync_show, min_sync_store); + +static ssize_t max_sync_show(mddev_t *mddev, char *page) { if (mddev->resync_max == MaxSector) @@ -3089,9 +3226,10 @@ max_sync_store(mddev_t *mddev, const char *buf, size_t len) if (strncmp(buf, "max", 3) == 0) mddev->resync_max = MaxSector; else { - char *ep; - unsigned long long max = simple_strtoull(buf, &ep, 10); - if (ep == buf || (*ep != 0 && *ep != '\n')) + unsigned long long max; + if (strict_strtoull(buf, 10, &max)) + return -EINVAL; + if (max < mddev->resync_min) return -EINVAL; if (max < mddev->resync_max && test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) @@ -3222,6 +3360,7 @@ static struct attribute *md_redundancy_attrs[] = { &md_sync_speed.attr, &md_sync_force_parallel.attr, &md_sync_completed.attr, + &md_min_sync.attr, &md_max_sync.attr, &md_suspend_lo.attr, &md_suspend_hi.attr, @@ -3326,9 +3465,9 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data) disk->queue = mddev->queue; add_disk(disk); mddev->gendisk = disk; - mutex_unlock(&disks_mutex); error = kobject_init_and_add(&mddev->kobj, &md_ktype, &disk->dev.kobj, "%s", "md"); + mutex_unlock(&disks_mutex); if (error) printk(KERN_WARNING "md: cannot register %s/md - name in use\n", disk->disk_name); @@ -3341,7 +3480,11 @@ static void md_safemode_timeout(unsigned long data) { mddev_t *mddev = (mddev_t *) data; - mddev->safemode = 1; + if (!atomic_read(&mddev->writes_pending)) { + mddev->safemode = 1; + if (mddev->external) + sysfs_notify(&mddev->kobj, NULL, "array_state"); + } md_wakeup_thread(mddev->thread); } @@ -3432,22 +3575,23 @@ static int do_md_run(mddev_t * mddev) * We don't want the data to overlap the metadata, * Internal Bitmap issues has handled elsewhere. */ - if (rdev->data_offset < rdev->sb_offset) { + if (rdev->data_offset < rdev->sb_start) { if (mddev->size && rdev->data_offset + mddev->size*2 - > rdev->sb_offset*2) { + > rdev->sb_start) { printk("md: %s: data overlaps metadata\n", mdname(mddev)); return -EINVAL; } } else { - if (rdev->sb_offset*2 + rdev->sb_size/512 + if (rdev->sb_start + rdev->sb_size/512 > rdev->data_offset) { printk("md: %s: metadata overlaps data\n", mdname(mddev)); return -EINVAL; } } + sysfs_notify(&rdev->kobj, NULL, "state"); } md_probe(mddev->unit, NULL, NULL); @@ -3519,7 +3663,9 @@ static int do_md_run(mddev_t * mddev) mddev->ro = 2; /* read-only, but switch on first write */ err = mddev->pers->run(mddev); - if (!err && mddev->pers->sync_request) { + if (err) + printk(KERN_ERR "md: pers->run() failed ...\n"); + else if (mddev->pers->sync_request) { err = bitmap_create(mddev); if (err) { printk(KERN_ERR "%s: failed to create bitmap (%d)\n", @@ -3528,7 +3674,6 @@ static int do_md_run(mddev_t * mddev) } } if (err) { - printk(KERN_ERR "md: pers->run() failed ...\n"); module_put(mddev->pers->owner); mddev->pers = NULL; bitmap_destroy(mddev); @@ -3563,7 +3708,7 @@ static int do_md_run(mddev_t * mddev) if (mddev->flags) md_update_sb(mddev, 0); - set_capacity(disk, mddev->array_size<<1); + set_capacity(disk, mddev->array_sectors); /* If we call blk_queue_make_request here, it will * re-initialise max_sectors etc which may have been @@ -3608,6 +3753,9 @@ static int do_md_run(mddev_t * mddev) mddev->changed = 1; md_new_event(mddev); + sysfs_notify(&mddev->kobj, NULL, "array_state"); + sysfs_notify(&mddev->kobj, NULL, "sync_action"); + sysfs_notify(&mddev->kobj, NULL, "degraded"); kobject_uevent(&mddev->gendisk->dev.kobj, KOBJ_CHANGE); return 0; } @@ -3615,38 +3763,25 @@ static int do_md_run(mddev_t * mddev) static int restart_array(mddev_t *mddev) { struct gendisk *disk = mddev->gendisk; - int err; - /* - * Complain if it has no devices - */ - err = -ENXIO; + /* Complain if it has no devices */ if (list_empty(&mddev->disks)) - goto out; - - if (mddev->pers) { - err = -EBUSY; - if (!mddev->ro) - goto out; - - mddev->safemode = 0; - mddev->ro = 0; - set_disk_ro(disk, 0); - - printk(KERN_INFO "md: %s switched to read-write mode.\n", - mdname(mddev)); - /* - * Kick recovery or resync if necessary - */ - set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); - md_wakeup_thread(mddev->thread); - md_wakeup_thread(mddev->sync_thread); - err = 0; - } else - err = -EINVAL; - -out: - return err; + return -ENXIO; + if (!mddev->pers) + return -EINVAL; + if (!mddev->ro) + return -EBUSY; + mddev->safemode = 0; + mddev->ro = 0; + set_disk_ro(disk, 0); + printk(KERN_INFO "md: %s switched to read-write mode.\n", + mdname(mddev)); + /* Kick recovery or resync if necessary */ + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); + md_wakeup_thread(mddev->sync_thread); + sysfs_notify(&mddev->kobj, NULL, "array_state"); + return 0; } /* similar to deny_write_access, but accounts for our holding a reference @@ -3680,16 +3815,17 @@ static void restore_bitmap_write_access(struct file *file) * 1 - switch to readonly * 2 - stop but do not disassemble array */ -static int do_md_stop(mddev_t * mddev, int mode) +static int do_md_stop(mddev_t * mddev, int mode, int is_open) { int err = 0; struct gendisk *disk = mddev->gendisk; + if (atomic_read(&mddev->openers) > is_open) { + printk("md: %s still in use.\n",mdname(mddev)); + return -EBUSY; + } + if (mddev->pers) { - if (atomic_read(&mddev->active)>2) { - printk("md: %s still in use.\n",mdname(mddev)); - return -EBUSY; - } if (mddev->sync_thread) { set_bit(MD_RECOVERY_FROZEN, &mddev->recovery); @@ -3773,10 +3909,11 @@ static int do_md_stop(mddev_t * mddev, int mode) export_array(mddev); - mddev->array_size = 0; + mddev->array_sectors = 0; mddev->size = 0; mddev->raid_disks = 0; mddev->recovery_cp = 0; + mddev->resync_min = 0; mddev->resync_max = MaxSector; mddev->reshape_position = MaxSector; mddev->external = 0; @@ -3811,6 +3948,7 @@ static int do_md_stop(mddev_t * mddev, int mode) mdname(mddev)); err = 0; md_new_event(mddev); + sysfs_notify(&mddev->kobj, NULL, "array_state"); out: return err; } @@ -3836,7 +3974,7 @@ static void autorun_array(mddev_t *mddev) err = do_md_run (mddev); if (err) { printk(KERN_WARNING "md: do_md_run() returned %d\n", err); - do_md_stop (mddev, 0); + do_md_stop (mddev, 0, 0); } } @@ -3927,8 +4065,10 @@ static void autorun_devices(int part) /* on success, candidates will be empty, on error * it won't... */ - rdev_for_each_list(rdev, tmp, candidates) + rdev_for_each_list(rdev, tmp, candidates) { + list_del_init(&rdev->same_set); export_rdev(rdev); + } mddev_put(mddev); } printk(KERN_INFO "md: ... autorun DONE.\n"); @@ -4009,9 +4149,11 @@ static int get_bitmap_file(mddev_t * mddev, void __user * arg) char *ptr, *buf = NULL; int err = -ENOMEM; - md_allow_write(mddev); + if (md_allow_write(mddev)) + file = kmalloc(sizeof(*file), GFP_NOIO); + else + file = kmalloc(sizeof(*file), GFP_KERNEL); - file = kmalloc(sizeof(*file), GFP_KERNEL); if (!file) goto out; @@ -4044,15 +4186,12 @@ out: static int get_disk_info(mddev_t * mddev, void __user * arg) { mdu_disk_info_t info; - unsigned int nr; mdk_rdev_t *rdev; if (copy_from_user(&info, arg, sizeof(info))) return -EFAULT; - nr = info.number; - - rdev = find_rdev_nr(mddev, nr); + rdev = find_rdev_nr(mddev, info.number); if (rdev) { info.major = MAJOR(rdev->bdev->bd_dev); info.minor = MINOR(rdev->bdev->bd_dev); @@ -4172,8 +4311,12 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) } if (err) export_rdev(rdev); + else + sysfs_notify(&rdev->kobj, NULL, "state"); md_update_sb(mddev, 1); + if (mddev->degraded) + set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); return err; @@ -4212,10 +4355,10 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) if (!mddev->persistent) { printk(KERN_INFO "md: nonpersistent superblock ...\n"); - rdev->sb_offset = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; + rdev->sb_start = rdev->bdev->bd_inode->i_size / 512; } else - rdev->sb_offset = calc_dev_sboffset(rdev->bdev); - rdev->size = calc_dev_size(rdev, mddev->chunk_size); + rdev->sb_start = calc_dev_sboffset(rdev->bdev); + rdev->size = calc_num_sectors(rdev, mddev->chunk_size) / 2; err = bind_rdev_to_array(rdev, mddev); if (err) { @@ -4232,9 +4375,6 @@ static int hot_remove_disk(mddev_t * mddev, dev_t dev) char b[BDEVNAME_SIZE]; mdk_rdev_t *rdev; - if (!mddev->pers) - return -ENODEV; - rdev = find_rdev(mddev, dev); if (!rdev) return -ENXIO; @@ -4257,7 +4397,6 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev) { char b[BDEVNAME_SIZE]; int err; - unsigned int size; mdk_rdev_t *rdev; if (!mddev->pers) @@ -4285,13 +4424,11 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev) } if (mddev->persistent) - rdev->sb_offset = calc_dev_sboffset(rdev->bdev); + rdev->sb_start = calc_dev_sboffset(rdev->bdev); else - rdev->sb_offset = - rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; + rdev->sb_start = rdev->bdev->bd_inode->i_size / 512; - size = calc_dev_size(rdev, mddev->chunk_size); - rdev->size = size; + rdev->size = calc_num_sectors(rdev, mddev->chunk_size) / 2; if (test_bit(Faulty, &rdev->flags)) { printk(KERN_WARNING @@ -4476,24 +4613,24 @@ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info) return 0; } -static int update_size(mddev_t *mddev, unsigned long size) +static int update_size(mddev_t *mddev, sector_t num_sectors) { mdk_rdev_t * rdev; int rv; struct list_head *tmp; - int fit = (size == 0); + int fit = (num_sectors == 0); if (mddev->pers->resize == NULL) return -EINVAL; - /* The "size" is the amount of each device that is used. - * This can only make sense for arrays with redundancy. - * linear and raid0 always use whatever space is available - * We can only consider changing the size if no resync - * or reconstruction is happening, and if the new size - * is acceptable. It must fit before the sb_offset or, - * if that is <data_offset, it must fit before the - * size of each device. - * If size is zero, we find the largest size that fits. + /* The "num_sectors" is the number of sectors of each device that + * is used. This can only make sense for arrays with redundancy. + * linear and raid0 always use whatever space is available. We can only + * consider changing this number if no resync or reconstruction is + * happening, and if the new size is acceptable. It must fit before the + * sb_start or, if that is <data_offset, it must fit before the size + * of each device. If num_sectors is zero, we find the largest size + * that fits. + */ if (mddev->sync_thread) return -EBUSY; @@ -4501,19 +4638,20 @@ static int update_size(mddev_t *mddev, unsigned long size) sector_t avail; avail = rdev->size * 2; - if (fit && (size == 0 || size > avail/2)) - size = avail/2; - if (avail < ((sector_t)size << 1)) + if (fit && (num_sectors == 0 || num_sectors > avail)) + num_sectors = avail; + if (avail < num_sectors) return -ENOSPC; } - rv = mddev->pers->resize(mddev, (sector_t)size *2); + rv = mddev->pers->resize(mddev, num_sectors); if (!rv) { struct block_device *bdev; bdev = bdget_disk(mddev->gendisk, 0); if (bdev) { mutex_lock(&bdev->bd_inode->i_mutex); - i_size_write(bdev->bd_inode, (loff_t)mddev->array_size << 10); + i_size_write(bdev->bd_inode, + (loff_t)mddev->array_sectors << 9); mutex_unlock(&bdev->bd_inode->i_mutex); bdput(bdev); } @@ -4588,7 +4726,7 @@ static int update_array_info(mddev_t *mddev, mdu_array_info_t *info) return mddev->pers->reconfig(mddev, info->layout, -1); } if (info->size >= 0 && mddev->size != info->size) - rv = update_size(mddev, info->size); + rv = update_size(mddev, (sector_t)info->size * 2); if (mddev->raid_disks != info->raid_disks) rv = update_raid_disks(mddev, info->raid_disks); @@ -4641,6 +4779,12 @@ static int set_disk_faulty(mddev_t *mddev, dev_t dev) return 0; } +/* + * We have a problem here : there is no easy way to give a CHS + * virtual geometry. We currently pretend that we have a 2 heads + * 4 sectors (with a BIG number of cylinders...). This drives + * dosfs just mad... ;-) + */ static int md_getgeo(struct block_device *bdev, struct hd_geometry *geo) { mddev_t *mddev = bdev->bd_disk->private_data; @@ -4785,19 +4929,13 @@ static int md_ioctl(struct inode *inode, struct file *file, goto done_unlock; case STOP_ARRAY: - err = do_md_stop (mddev, 0); + err = do_md_stop (mddev, 0, 1); goto done_unlock; case STOP_ARRAY_RO: - err = do_md_stop (mddev, 1); + err = do_md_stop (mddev, 1, 1); goto done_unlock; - /* - * We have a problem here : there is no easy way to give a CHS - * virtual geometry. We currently pretend that we have a 2 heads - * 4 sectors (with a BIG number of cylinders...). This drives - * dosfs just mad... ;-) - */ } /* @@ -4807,13 +4945,12 @@ static int md_ioctl(struct inode *inode, struct file *file, * here and hit the 'default' below, so only disallow * 'md' ioctls, and switch to rw mode if started auto-readonly. */ - if (_IOC_TYPE(cmd) == MD_MAJOR && - mddev->ro && mddev->pers) { + if (_IOC_TYPE(cmd) == MD_MAJOR && mddev->ro && mddev->pers) { if (mddev->ro == 2) { mddev->ro = 0; - set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); - md_wakeup_thread(mddev->thread); - + sysfs_notify(&mddev->kobj, NULL, "array_state"); + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); } else { err = -EROFS; goto abort_unlock; @@ -4883,6 +5020,7 @@ static int md_open(struct inode *inode, struct file *file) err = 0; mddev_get(mddev); + atomic_inc(&mddev->openers); mddev_unlock(mddev); check_disk_change(inode->i_bdev); @@ -4895,6 +5033,7 @@ static int md_release(struct inode *inode, struct file * file) mddev_t *mddev = inode->i_bdev->bd_disk->private_data; BUG_ON(!mddev); + atomic_dec(&mddev->openers); mddev_put(mddev); return 0; @@ -5029,6 +5168,9 @@ void md_error(mddev_t *mddev, mdk_rdev_t *rdev) if (!mddev->pers->error_handler) return; mddev->pers->error_handler(mddev,rdev); + if (mddev->degraded) + set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); + set_bit(StateChanged, &rdev->flags); set_bit(MD_RECOVERY_INTR, &mddev->recovery); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); @@ -5258,10 +5400,11 @@ static int md_seq_show(struct seq_file *seq, void *v) if (!list_empty(&mddev->disks)) { if (mddev->pers) seq_printf(seq, "\n %llu blocks", - (unsigned long long)mddev->array_size); + (unsigned long long) + mddev->array_sectors / 2); else seq_printf(seq, "\n %llu blocks", - (unsigned long long)size); + (unsigned long long)size); } if (mddev->persistent) { if (mddev->major_version != 0 || @@ -5391,12 +5534,12 @@ int unregister_md_personality(struct mdk_personality *p) static int is_mddev_idle(mddev_t *mddev) { mdk_rdev_t * rdev; - struct list_head *tmp; int idle; long curr_events; idle = 1; - rdev_for_each(rdev, tmp, mddev) { + rcu_read_lock(); + rdev_for_each_rcu(rdev, mddev) { struct gendisk *disk = rdev->bdev->bd_contains->bd_disk; curr_events = disk_stat_read(disk, sectors[0]) + disk_stat_read(disk, sectors[1]) - @@ -5428,6 +5571,7 @@ static int is_mddev_idle(mddev_t *mddev) idle = 0; } } + rcu_read_unlock(); return idle; } @@ -5451,6 +5595,7 @@ void md_done_sync(mddev_t *mddev, int blocks, int ok) */ void md_write_start(mddev_t *mddev, struct bio *bi) { + int did_change = 0; if (bio_data_dir(bi) != WRITE) return; @@ -5461,6 +5606,7 @@ void md_write_start(mddev_t *mddev, struct bio *bi) set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); md_wakeup_thread(mddev->sync_thread); + did_change = 1; } atomic_inc(&mddev->writes_pending); if (mddev->safemode == 1) @@ -5471,10 +5617,12 @@ void md_write_start(mddev_t *mddev, struct bio *bi) mddev->in_sync = 0; set_bit(MD_CHANGE_CLEAN, &mddev->flags); md_wakeup_thread(mddev->thread); + did_change = 1; } spin_unlock_irq(&mddev->write_lock); - sysfs_notify(&mddev->kobj, NULL, "array_state"); } + if (did_change) + sysfs_notify(&mddev->kobj, NULL, "array_state"); wait_event(mddev->sb_wait, !test_bit(MD_CHANGE_CLEAN, &mddev->flags) && !test_bit(MD_CHANGE_PENDING, &mddev->flags)); @@ -5495,13 +5643,18 @@ void md_write_end(mddev_t *mddev) * may proceed without blocking. It is important to call this before * attempting a GFP_KERNEL allocation while holding the mddev lock. * Must be called with mddev_lock held. + * + * In the ->external case MD_CHANGE_CLEAN can not be cleared until mddev->lock + * is dropped, so return -EAGAIN after notifying userspace. */ -void md_allow_write(mddev_t *mddev) +int md_allow_write(mddev_t *mddev) { if (!mddev->pers) - return; + return 0; if (mddev->ro) - return; + return 0; + if (!mddev->pers->sync_request) + return 0; spin_lock_irq(&mddev->write_lock); if (mddev->in_sync) { @@ -5512,14 +5665,14 @@ void md_allow_write(mddev_t *mddev) mddev->safemode = 1; spin_unlock_irq(&mddev->write_lock); md_update_sb(mddev, 0); - sysfs_notify(&mddev->kobj, NULL, "array_state"); - /* wait for the dirty state to be recorded in the metadata */ - wait_event(mddev->sb_wait, - !test_bit(MD_CHANGE_CLEAN, &mddev->flags) && - !test_bit(MD_CHANGE_PENDING, &mddev->flags)); } else spin_unlock_irq(&mddev->write_lock); + + if (test_bit(MD_CHANGE_CLEAN, &mddev->flags)) + return -EAGAIN; + else + return 0; } EXPORT_SYMBOL_GPL(md_allow_write); @@ -5625,9 +5778,11 @@ void md_do_sync(mddev_t *mddev) max_sectors = mddev->resync_max_sectors; mddev->resync_mismatches = 0; /* we don't use the checkpoint if there's a bitmap */ - if (!mddev->bitmap && - !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) + if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) + j = mddev->resync_min; + else if (!mddev->bitmap) j = mddev->recovery_cp; + } else if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) max_sectors = mddev->size << 1; else { @@ -5796,6 +5951,7 @@ void md_do_sync(mddev_t *mddev) skip: mddev->curr_resync = 0; + mddev->resync_min = 0; mddev->resync_max = MaxSector; sysfs_notify(&mddev->kobj, NULL, "sync_completed"); wake_up(&resync_wait); @@ -5845,7 +6001,8 @@ static int remove_and_add_spares(mddev_t *mddev) if (rdev->raid_disk < 0 && !test_bit(Faulty, &rdev->flags)) { rdev->recovery_offset = 0; - if (mddev->pers->hot_add_disk(mddev,rdev)) { + if (mddev->pers-> + hot_add_disk(mddev, rdev) == 0) { char nm[20]; sprintf(nm, "rd%d", rdev->raid_disk); if (sysfs_create_link(&mddev->kobj, @@ -5920,23 +6077,31 @@ void md_check_recovery(mddev_t *mddev) int spares = 0; if (!mddev->external) { + int did_change = 0; spin_lock_irq(&mddev->write_lock); if (mddev->safemode && !atomic_read(&mddev->writes_pending) && !mddev->in_sync && mddev->recovery_cp == MaxSector) { mddev->in_sync = 1; + did_change = 1; if (mddev->persistent) set_bit(MD_CHANGE_CLEAN, &mddev->flags); } if (mddev->safemode == 1) mddev->safemode = 0; spin_unlock_irq(&mddev->write_lock); + if (did_change) + sysfs_notify(&mddev->kobj, NULL, "array_state"); } if (mddev->flags) md_update_sb(mddev, 0); + rdev_for_each(rdev, rtmp, mddev) + if (test_and_clear_bit(StateChanged, &rdev->flags)) + sysfs_notify(&rdev->kobj, NULL, "state"); + if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) && !test_bit(MD_RECOVERY_DONE, &mddev->recovery)) { @@ -5951,7 +6116,9 @@ void md_check_recovery(mddev_t *mddev) if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery)) { /* success...*/ /* activate any spares */ - mddev->pers->spare_active(mddev); + if (mddev->pers->spare_active(mddev)) + sysfs_notify(&mddev->kobj, NULL, + "degraded"); } md_update_sb(mddev, 1); @@ -5965,13 +6132,18 @@ void md_check_recovery(mddev_t *mddev) mddev->recovery = 0; /* flag recovery needed just to double check */ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + sysfs_notify(&mddev->kobj, NULL, "sync_action"); md_new_event(mddev); goto unlock; } + /* Set RUNNING before clearing NEEDED to avoid + * any transients in the value of "sync_action". + */ + set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); + clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery); /* Clear some bits that don't mean anything, but * might be left set */ - clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery); clear_bit(MD_RECOVERY_INTR, &mddev->recovery); clear_bit(MD_RECOVERY_DONE, &mddev->recovery); @@ -5989,17 +6161,19 @@ void md_check_recovery(mddev_t *mddev) /* Cannot proceed */ goto unlock; set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery); + clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery); } else if ((spares = remove_and_add_spares(mddev))) { clear_bit(MD_RECOVERY_SYNC, &mddev->recovery); clear_bit(MD_RECOVERY_CHECK, &mddev->recovery); + set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); } else if (mddev->recovery_cp < MaxSector) { set_bit(MD_RECOVERY_SYNC, &mddev->recovery); + clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery); } else if (!test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) /* nothing to be done ... */ goto unlock; if (mddev->pers->sync_request) { - set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); if (spares && mddev->bitmap && ! mddev->bitmap->file) { /* We are adding a device or devices to an array * which has the bitmap stored on all devices. @@ -6018,9 +6192,16 @@ void md_check_recovery(mddev_t *mddev) mddev->recovery = 0; } else md_wakeup_thread(mddev->sync_thread); + sysfs_notify(&mddev->kobj, NULL, "sync_action"); md_new_event(mddev); } unlock: + if (!mddev->sync_thread) { + clear_bit(MD_RECOVERY_RUNNING, &mddev->recovery); + if (test_and_clear_bit(MD_RECOVERY_RECOVER, + &mddev->recovery)) + sysfs_notify(&mddev->kobj, NULL, "sync_action"); + } mddev_unlock(mddev); } } @@ -6047,7 +6228,7 @@ static int md_notify_reboot(struct notifier_block *this, for_each_mddev(mddev, tmp) if (mddev_trylock(mddev)) { - do_md_stop (mddev, 1); + do_md_stop (mddev, 1, 0); mddev_unlock(mddev); } /* diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index e968116e0de9..c4779ccba1c3 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -281,13 +281,18 @@ static int multipath_add_disk(mddev_t *mddev, mdk_rdev_t *rdev) { multipath_conf_t *conf = mddev->private; struct request_queue *q; - int found = 0; + int err = -EEXIST; int path; struct multipath_info *p; + int first = 0; + int last = mddev->raid_disks - 1; + + if (rdev->raid_disk >= 0) + first = last = rdev->raid_disk; print_multipath_conf(conf); - for (path=0; path<mddev->raid_disks; path++) + for (path = first; path <= last; path++) if ((p=conf->multipaths+path)->rdev == NULL) { q = rdev->bdev->bd_disk->queue; blk_queue_stack_limits(mddev->queue, q); @@ -307,11 +312,13 @@ static int multipath_add_disk(mddev_t *mddev, mdk_rdev_t *rdev) rdev->raid_disk = path; set_bit(In_sync, &rdev->flags); rcu_assign_pointer(p->rdev, rdev); - found = 1; + err = 0; + break; } print_multipath_conf(conf); - return found; + + return err; } static int multipath_remove_disk(mddev_t *mddev, int number) @@ -497,7 +504,7 @@ static int multipath_run (mddev_t *mddev) /* * Ok, everything is just fine now */ - mddev->array_size = mddev->size; + mddev->array_sectors = mddev->size * 2; mddev->queue->unplug_fn = multipath_unplug; mddev->queue->backing_dev_info.congested_fn = multipath_congested; diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index bcbb82594a19..183610635661 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -295,16 +295,16 @@ static int raid0_run (mddev_t *mddev) goto out_free_conf; /* calculate array device size */ - mddev->array_size = 0; + mddev->array_sectors = 0; rdev_for_each(rdev, tmp, mddev) - mddev->array_size += rdev->size; + mddev->array_sectors += rdev->size * 2; printk("raid0 : md_size is %llu blocks.\n", - (unsigned long long)mddev->array_size); + (unsigned long long)mddev->array_sectors / 2); printk("raid0 : conf->hash_spacing is %llu blocks.\n", (unsigned long long)conf->hash_spacing); { - sector_t s = mddev->array_size; + sector_t s = mddev->array_sectors / 2; sector_t space = conf->hash_spacing; int round; conf->preshift = 0; diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index c610b947218a..03a5ab705c20 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1100,11 +1100,16 @@ static int raid1_spare_active(mddev_t *mddev) static int raid1_add_disk(mddev_t *mddev, mdk_rdev_t *rdev) { conf_t *conf = mddev->private; - int found = 0; + int err = -EEXIST; int mirror = 0; mirror_info_t *p; + int first = 0; + int last = mddev->raid_disks - 1; - for (mirror=0; mirror < mddev->raid_disks; mirror++) + if (rdev->raid_disk >= 0) + first = last = rdev->raid_disk; + + for (mirror = first; mirror <= last; mirror++) if ( !(p=conf->mirrors+mirror)->rdev) { blk_queue_stack_limits(mddev->queue, @@ -1119,7 +1124,7 @@ static int raid1_add_disk(mddev_t *mddev, mdk_rdev_t *rdev) p->head_position = 0; rdev->raid_disk = mirror; - found = 1; + err = 0; /* As all devices are equivalent, we don't need a full recovery * if this was recently any drive of the array */ @@ -1130,7 +1135,7 @@ static int raid1_add_disk(mddev_t *mddev, mdk_rdev_t *rdev) } print_conf(conf); - return found; + return err; } static int raid1_remove_disk(mddev_t *mddev, int number) @@ -2038,7 +2043,7 @@ static int run(mddev_t *mddev) /* * Ok, everything is just fine now */ - mddev->array_size = mddev->size; + mddev->array_sectors = mddev->size * 2; mddev->queue->unplug_fn = raid1_unplug; mddev->queue->backing_dev_info.congested_fn = raid1_congested; @@ -2100,14 +2105,15 @@ static int raid1_resize(mddev_t *mddev, sector_t sectors) * any io in the removed space completes, but it hardly seems * worth it. */ - mddev->array_size = sectors>>1; - set_capacity(mddev->gendisk, mddev->array_size << 1); + mddev->array_sectors = sectors; + set_capacity(mddev->gendisk, mddev->array_sectors); mddev->changed = 1; - if (mddev->array_size > mddev->size && mddev->recovery_cp == MaxSector) { + if (mddev->array_sectors / 2 > mddev->size && + mddev->recovery_cp == MaxSector) { mddev->recovery_cp = mddev->size << 1; set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); } - mddev->size = mddev->array_size; + mddev->size = mddev->array_sectors / 2; mddev->resync_max_sectors = sectors; return 0; } @@ -2131,7 +2137,7 @@ static int raid1_reshape(mddev_t *mddev) conf_t *conf = mddev_to_conf(mddev); int cnt, raid_disks; unsigned long flags; - int d, d2; + int d, d2, err; /* Cannot change chunk_size, layout, or level */ if (mddev->chunk_size != mddev->new_chunk || @@ -2143,7 +2149,9 @@ static int raid1_reshape(mddev_t *mddev) return -EINVAL; } - md_allow_write(mddev); + err = md_allow_write(mddev); + if (err) + return err; raid_disks = mddev->raid_disks + mddev->delta_disks; diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 22bb2b1b886d..159535d73567 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1114,24 +1114,30 @@ static int raid10_spare_active(mddev_t *mddev) static int raid10_add_disk(mddev_t *mddev, mdk_rdev_t *rdev) { conf_t *conf = mddev->private; - int found = 0; + int err = -EEXIST; int mirror; mirror_info_t *p; + int first = 0; + int last = mddev->raid_disks - 1; if (mddev->recovery_cp < MaxSector) /* only hot-add to in-sync arrays, as recovery is * very different from resync */ - return 0; + return -EBUSY; if (!enough(conf)) - return 0; + return -EINVAL; + + if (rdev->raid_disk) + first = last = rdev->raid_disk; if (rdev->saved_raid_disk >= 0 && + rdev->saved_raid_disk >= first && conf->mirrors[rdev->saved_raid_disk].rdev == NULL) mirror = rdev->saved_raid_disk; else - mirror = 0; - for ( ; mirror < mddev->raid_disks; mirror++) + mirror = first; + for ( ; mirror <= last ; mirror++) if ( !(p=conf->mirrors+mirror)->rdev) { blk_queue_stack_limits(mddev->queue, @@ -1146,7 +1152,7 @@ static int raid10_add_disk(mddev_t *mddev, mdk_rdev_t *rdev) p->head_position = 0; rdev->raid_disk = mirror; - found = 1; + err = 0; if (rdev->saved_raid_disk != mirror) conf->fullsync = 1; rcu_assign_pointer(p->rdev, rdev); @@ -1154,7 +1160,7 @@ static int raid10_add_disk(mddev_t *mddev, mdk_rdev_t *rdev) } print_conf(conf); - return found; + return err; } static int raid10_remove_disk(mddev_t *mddev, int number) @@ -2159,7 +2165,7 @@ static int run(mddev_t *mddev) /* * Ok, everything is just fine now */ - mddev->array_size = size << (conf->chunk_shift-1); + mddev->array_sectors = size << conf->chunk_shift; mddev->resync_max_sectors = size << conf->chunk_shift; mddev->queue->unplug_fn = raid10_unplug; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 9ce7154845c6..55e7c56045a0 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -115,15 +115,20 @@ static void return_io(struct bio *return_bi) return_bi = bi->bi_next; bi->bi_next = NULL; bi->bi_size = 0; - bi->bi_end_io(bi, - test_bit(BIO_UPTODATE, &bi->bi_flags) - ? 0 : -EIO); + bio_endio(bi, 0); bi = return_bi; } } static void print_raid5_conf (raid5_conf_t *conf); +static int stripe_operations_active(struct stripe_head *sh) +{ + return sh->check_state || sh->reconstruct_state || + test_bit(STRIPE_BIOFILL_RUN, &sh->state) || + test_bit(STRIPE_COMPUTE_RUN, &sh->state); +} + static void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh) { if (atomic_dec_and_test(&sh->count)) { @@ -143,7 +148,7 @@ static void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh) } md_wakeup_thread(conf->mddev->thread); } else { - BUG_ON(sh->ops.pending); + BUG_ON(stripe_operations_active(sh)); if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) { atomic_dec(&conf->preread_active_stripes); if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD) @@ -245,7 +250,7 @@ static void init_stripe(struct stripe_head *sh, sector_t sector, int pd_idx, int BUG_ON(atomic_read(&sh->count) != 0); BUG_ON(test_bit(STRIPE_HANDLE, &sh->state)); - BUG_ON(sh->ops.pending || sh->ops.ack || sh->ops.complete); + BUG_ON(stripe_operations_active(sh)); CHECK_DEVLOCK(); pr_debug("init_stripe called, stripe %llu\n", @@ -346,62 +351,18 @@ static struct stripe_head *get_active_stripe(raid5_conf_t *conf, sector_t sector return sh; } -/* test_and_ack_op() ensures that we only dequeue an operation once */ -#define test_and_ack_op(op, pend) \ -do { \ - if (test_bit(op, &sh->ops.pending) && \ - !test_bit(op, &sh->ops.complete)) { \ - if (test_and_set_bit(op, &sh->ops.ack)) \ - clear_bit(op, &pend); \ - else \ - ack++; \ - } else \ - clear_bit(op, &pend); \ -} while (0) - -/* find new work to run, do not resubmit work that is already - * in flight - */ -static unsigned long get_stripe_work(struct stripe_head *sh) -{ - unsigned long pending; - int ack = 0; - - pending = sh->ops.pending; - - test_and_ack_op(STRIPE_OP_BIOFILL, pending); - test_and_ack_op(STRIPE_OP_COMPUTE_BLK, pending); - test_and_ack_op(STRIPE_OP_PREXOR, pending); - test_and_ack_op(STRIPE_OP_BIODRAIN, pending); - test_and_ack_op(STRIPE_OP_POSTXOR, pending); - test_and_ack_op(STRIPE_OP_CHECK, pending); - if (test_and_clear_bit(STRIPE_OP_IO, &sh->ops.pending)) - ack++; - - sh->ops.count -= ack; - if (unlikely(sh->ops.count < 0)) { - printk(KERN_ERR "pending: %#lx ops.pending: %#lx ops.ack: %#lx " - "ops.complete: %#lx\n", pending, sh->ops.pending, - sh->ops.ack, sh->ops.complete); - BUG(); - } - - return pending; -} - static void raid5_end_read_request(struct bio *bi, int error); static void raid5_end_write_request(struct bio *bi, int error); -static void ops_run_io(struct stripe_head *sh) +static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s) { raid5_conf_t *conf = sh->raid_conf; int i, disks = sh->disks; might_sleep(); - set_bit(STRIPE_IO_STARTED, &sh->state); for (i = disks; i--; ) { int rw; struct bio *bi; @@ -430,11 +391,11 @@ static void ops_run_io(struct stripe_head *sh) rcu_read_unlock(); if (rdev) { - if (test_bit(STRIPE_SYNCING, &sh->state) || - test_bit(STRIPE_EXPAND_SOURCE, &sh->state) || - test_bit(STRIPE_EXPAND_READY, &sh->state)) + if (s->syncing || s->expanding || s->expanded) md_sync_acct(rdev->bdev, STRIPE_SECTORS); + set_bit(STRIPE_IO_STARTED, &sh->state); + bi->bi_bdev = rdev->bdev; pr_debug("%s: for %llu schedule op %ld on disc %d\n", __func__, (unsigned long long)sh->sector, @@ -528,38 +489,34 @@ static void ops_complete_biofill(void *stripe_head_ref) (unsigned long long)sh->sector); /* clear completed biofills */ + spin_lock_irq(&conf->device_lock); for (i = sh->disks; i--; ) { struct r5dev *dev = &sh->dev[i]; /* acknowledge completion of a biofill operation */ /* and check if we need to reply to a read request, * new R5_Wantfill requests are held off until - * !test_bit(STRIPE_OP_BIOFILL, &sh->ops.pending) + * !STRIPE_BIOFILL_RUN */ if (test_and_clear_bit(R5_Wantfill, &dev->flags)) { struct bio *rbi, *rbi2; - /* The access to dev->read is outside of the - * spin_lock_irq(&conf->device_lock), but is protected - * by the STRIPE_OP_BIOFILL pending bit - */ BUG_ON(!dev->read); rbi = dev->read; dev->read = NULL; while (rbi && rbi->bi_sector < dev->sector + STRIPE_SECTORS) { rbi2 = r5_next_bio(rbi, dev->sector); - spin_lock_irq(&conf->device_lock); if (--rbi->bi_phys_segments == 0) { rbi->bi_next = return_bi; return_bi = rbi; } - spin_unlock_irq(&conf->device_lock); rbi = rbi2; } } } - set_bit(STRIPE_OP_BIOFILL, &sh->ops.complete); + spin_unlock_irq(&conf->device_lock); + clear_bit(STRIPE_BIOFILL_RUN, &sh->state); return_io(return_bi); @@ -610,13 +567,14 @@ static void ops_complete_compute5(void *stripe_head_ref) set_bit(R5_UPTODATE, &tgt->flags); BUG_ON(!test_bit(R5_Wantcompute, &tgt->flags)); clear_bit(R5_Wantcompute, &tgt->flags); - set_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete); + clear_bit(STRIPE_COMPUTE_RUN, &sh->state); + if (sh->check_state == check_state_compute_run) + sh->check_state = check_state_compute_result; set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); } -static struct dma_async_tx_descriptor * -ops_run_compute5(struct stripe_head *sh, unsigned long pending) +static struct dma_async_tx_descriptor *ops_run_compute5(struct stripe_head *sh) { /* kernel stack size limits the total number of disks */ int disks = sh->disks; @@ -646,10 +604,6 @@ ops_run_compute5(struct stripe_head *sh, unsigned long pending) ASYNC_TX_XOR_ZERO_DST, NULL, ops_complete_compute5, sh); - /* ack now if postxor is not set to be run */ - if (tx && !test_bit(STRIPE_OP_POSTXOR, &pending)) - async_tx_ack(tx); - return tx; } @@ -659,8 +613,6 @@ static void ops_complete_prexor(void *stripe_head_ref) pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); - - set_bit(STRIPE_OP_PREXOR, &sh->ops.complete); } static struct dma_async_tx_descriptor * @@ -680,7 +632,7 @@ ops_run_prexor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; /* Only process blocks that are known to be uptodate */ - if (dev->towrite && test_bit(R5_Wantprexor, &dev->flags)) + if (test_bit(R5_Wantdrain, &dev->flags)) xor_srcs[count++] = dev->page; } @@ -692,16 +644,10 @@ ops_run_prexor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) } static struct dma_async_tx_descriptor * -ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, - unsigned long pending) +ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) { int disks = sh->disks; - int pd_idx = sh->pd_idx, i; - - /* check if prexor is active which means only process blocks - * that are part of a read-modify-write (Wantprexor) - */ - int prexor = test_bit(STRIPE_OP_PREXOR, &pending); + int i; pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); @@ -709,20 +655,8 @@ ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; struct bio *chosen; - int towrite; - - towrite = 0; - if (prexor) { /* rmw */ - if (dev->towrite && - test_bit(R5_Wantprexor, &dev->flags)) - towrite = 1; - } else { /* rcw */ - if (i != pd_idx && dev->towrite && - test_bit(R5_LOCKED, &dev->flags)) - towrite = 1; - } - if (towrite) { + if (test_and_clear_bit(R5_Wantdrain, &dev->flags)) { struct bio *wbi; spin_lock(&sh->lock); @@ -747,18 +681,6 @@ ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, static void ops_complete_postxor(void *stripe_head_ref) { struct stripe_head *sh = stripe_head_ref; - - pr_debug("%s: stripe %llu\n", __func__, - (unsigned long long)sh->sector); - - set_bit(STRIPE_OP_POSTXOR, &sh->ops.complete); - set_bit(STRIPE_HANDLE, &sh->state); - release_stripe(sh); -} - -static void ops_complete_write(void *stripe_head_ref) -{ - struct stripe_head *sh = stripe_head_ref; int disks = sh->disks, i, pd_idx = sh->pd_idx; pr_debug("%s: stripe %llu\n", __func__, @@ -770,16 +692,21 @@ static void ops_complete_write(void *stripe_head_ref) set_bit(R5_UPTODATE, &dev->flags); } - set_bit(STRIPE_OP_BIODRAIN, &sh->ops.complete); - set_bit(STRIPE_OP_POSTXOR, &sh->ops.complete); + if (sh->reconstruct_state == reconstruct_state_drain_run) + sh->reconstruct_state = reconstruct_state_drain_result; + else if (sh->reconstruct_state == reconstruct_state_prexor_drain_run) + sh->reconstruct_state = reconstruct_state_prexor_drain_result; + else { + BUG_ON(sh->reconstruct_state != reconstruct_state_run); + sh->reconstruct_state = reconstruct_state_result; + } set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); } static void -ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, - unsigned long pending) +ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) { /* kernel stack size limits the total number of disks */ int disks = sh->disks; @@ -787,9 +714,8 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, int count = 0, pd_idx = sh->pd_idx, i; struct page *xor_dest; - int prexor = test_bit(STRIPE_OP_PREXOR, &pending); + int prexor = 0; unsigned long flags; - dma_async_tx_callback callback; pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); @@ -797,7 +723,8 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, /* check if prexor is active which means only process blocks * that are part of a read-modify-write (written) */ - if (prexor) { + if (sh->reconstruct_state == reconstruct_state_prexor_drain_run) { + prexor = 1; xor_dest = xor_srcs[count++] = sh->dev[pd_idx].page; for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; @@ -813,10 +740,6 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, } } - /* check whether this postxor is part of a write */ - callback = test_bit(STRIPE_OP_BIODRAIN, &pending) ? - ops_complete_write : ops_complete_postxor; - /* 1/ if we prexor'd then the dest is reused as a source * 2/ if we did not prexor then we are redoing the parity * set ASYNC_TX_XOR_DROP_DST and ASYNC_TX_XOR_ZERO_DST @@ -830,25 +753,20 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, if (unlikely(count == 1)) { flags &= ~(ASYNC_TX_XOR_DROP_DST | ASYNC_TX_XOR_ZERO_DST); tx = async_memcpy(xor_dest, xor_srcs[0], 0, 0, STRIPE_SIZE, - flags, tx, callback, sh); + flags, tx, ops_complete_postxor, sh); } else tx = async_xor(xor_dest, xor_srcs, 0, count, STRIPE_SIZE, - flags, tx, callback, sh); + flags, tx, ops_complete_postxor, sh); } static void ops_complete_check(void *stripe_head_ref) { struct stripe_head *sh = stripe_head_ref; - int pd_idx = sh->pd_idx; pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); - if (test_and_clear_bit(STRIPE_OP_MOD_DMA_CHECK, &sh->ops.pending) && - sh->ops.zero_sum_result == 0) - set_bit(R5_UPTODATE, &sh->dev[pd_idx].flags); - - set_bit(STRIPE_OP_CHECK, &sh->ops.complete); + sh->check_state = check_state_check_result; set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); } @@ -875,46 +793,42 @@ static void ops_run_check(struct stripe_head *sh) tx = async_xor_zero_sum(xor_dest, xor_srcs, 0, count, STRIPE_SIZE, &sh->ops.zero_sum_result, 0, NULL, NULL, NULL); - if (tx) - set_bit(STRIPE_OP_MOD_DMA_CHECK, &sh->ops.pending); - else - clear_bit(STRIPE_OP_MOD_DMA_CHECK, &sh->ops.pending); - atomic_inc(&sh->count); tx = async_trigger_callback(ASYNC_TX_DEP_ACK | ASYNC_TX_ACK, tx, ops_complete_check, sh); } -static void raid5_run_ops(struct stripe_head *sh, unsigned long pending) +static void raid5_run_ops(struct stripe_head *sh, unsigned long ops_request) { int overlap_clear = 0, i, disks = sh->disks; struct dma_async_tx_descriptor *tx = NULL; - if (test_bit(STRIPE_OP_BIOFILL, &pending)) { + if (test_bit(STRIPE_OP_BIOFILL, &ops_request)) { ops_run_biofill(sh); overlap_clear++; } - if (test_bit(STRIPE_OP_COMPUTE_BLK, &pending)) - tx = ops_run_compute5(sh, pending); + if (test_bit(STRIPE_OP_COMPUTE_BLK, &ops_request)) { + tx = ops_run_compute5(sh); + /* terminate the chain if postxor is not set to be run */ + if (tx && !test_bit(STRIPE_OP_POSTXOR, &ops_request)) + async_tx_ack(tx); + } - if (test_bit(STRIPE_OP_PREXOR, &pending)) + if (test_bit(STRIPE_OP_PREXOR, &ops_request)) tx = ops_run_prexor(sh, tx); - if (test_bit(STRIPE_OP_BIODRAIN, &pending)) { - tx = ops_run_biodrain(sh, tx, pending); + if (test_bit(STRIPE_OP_BIODRAIN, &ops_request)) { + tx = ops_run_biodrain(sh, tx); overlap_clear++; } - if (test_bit(STRIPE_OP_POSTXOR, &pending)) - ops_run_postxor(sh, tx, pending); + if (test_bit(STRIPE_OP_POSTXOR, &ops_request)) + ops_run_postxor(sh, tx); - if (test_bit(STRIPE_OP_CHECK, &pending)) + if (test_bit(STRIPE_OP_CHECK, &ops_request)) ops_run_check(sh); - if (test_bit(STRIPE_OP_IO, &pending)) - ops_run_io(sh); - if (overlap_clear) for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; @@ -997,14 +911,16 @@ static int resize_stripes(raid5_conf_t *conf, int newsize) struct stripe_head *osh, *nsh; LIST_HEAD(newstripes); struct disk_info *ndisks; - int err = 0; + int err; struct kmem_cache *sc; int i; if (newsize <= conf->pool_size) return 0; /* never bother to shrink */ - md_allow_write(conf->mddev); + err = md_allow_write(conf->mddev); + if (err) + return err; /* Step 1 */ sc = kmem_cache_create(conf->cache_name[1-conf->active_name], @@ -1703,11 +1619,11 @@ static void compute_block_2(struct stripe_head *sh, int dd_idx1, int dd_idx2) } } -static int -handle_write_operations5(struct stripe_head *sh, int rcw, int expand) +static void +schedule_reconstruction5(struct stripe_head *sh, struct stripe_head_state *s, + int rcw, int expand) { int i, pd_idx = sh->pd_idx, disks = sh->disks; - int locked = 0; if (rcw) { /* if we are not expanding this is a proper write request, and @@ -1715,53 +1631,48 @@ handle_write_operations5(struct stripe_head *sh, int rcw, int expand) * stripe cache */ if (!expand) { - set_bit(STRIPE_OP_BIODRAIN, &sh->ops.pending); - sh->ops.count++; - } + sh->reconstruct_state = reconstruct_state_drain_run; + set_bit(STRIPE_OP_BIODRAIN, &s->ops_request); + } else + sh->reconstruct_state = reconstruct_state_run; - set_bit(STRIPE_OP_POSTXOR, &sh->ops.pending); - sh->ops.count++; + set_bit(STRIPE_OP_POSTXOR, &s->ops_request); for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; if (dev->towrite) { set_bit(R5_LOCKED, &dev->flags); + set_bit(R5_Wantdrain, &dev->flags); if (!expand) clear_bit(R5_UPTODATE, &dev->flags); - locked++; + s->locked++; } } - if (locked + 1 == disks) + if (s->locked + 1 == disks) if (!test_and_set_bit(STRIPE_FULL_WRITE, &sh->state)) atomic_inc(&sh->raid_conf->pending_full_writes); } else { BUG_ON(!(test_bit(R5_UPTODATE, &sh->dev[pd_idx].flags) || test_bit(R5_Wantcompute, &sh->dev[pd_idx].flags))); - set_bit(STRIPE_OP_PREXOR, &sh->ops.pending); - set_bit(STRIPE_OP_BIODRAIN, &sh->ops.pending); - set_bit(STRIPE_OP_POSTXOR, &sh->ops.pending); - - sh->ops.count += 3; + sh->reconstruct_state = reconstruct_state_prexor_drain_run; + set_bit(STRIPE_OP_PREXOR, &s->ops_request); + set_bit(STRIPE_OP_BIODRAIN, &s->ops_request); + set_bit(STRIPE_OP_POSTXOR, &s->ops_request); for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; if (i == pd_idx) continue; - /* For a read-modify write there may be blocks that are - * locked for reading while others are ready to be - * written so we distinguish these blocks by the - * R5_Wantprexor bit - */ if (dev->towrite && (test_bit(R5_UPTODATE, &dev->flags) || - test_bit(R5_Wantcompute, &dev->flags))) { - set_bit(R5_Wantprexor, &dev->flags); + test_bit(R5_Wantcompute, &dev->flags))) { + set_bit(R5_Wantdrain, &dev->flags); set_bit(R5_LOCKED, &dev->flags); clear_bit(R5_UPTODATE, &dev->flags); - locked++; + s->locked++; } } } @@ -1771,13 +1682,11 @@ handle_write_operations5(struct stripe_head *sh, int rcw, int expand) */ set_bit(R5_LOCKED, &sh->dev[pd_idx].flags); clear_bit(R5_UPTODATE, &sh->dev[pd_idx].flags); - locked++; + s->locked++; - pr_debug("%s: stripe %llu locked: %d pending: %lx\n", + pr_debug("%s: stripe %llu locked: %d ops_request: %lx\n", __func__, (unsigned long long)sh->sector, - locked, sh->ops.pending); - - return locked; + s->locked, s->ops_request); } /* @@ -1876,7 +1785,7 @@ static int stripe_to_pdidx(sector_t stripe, raid5_conf_t *conf, int disks) } static void -handle_requests_to_failed_array(raid5_conf_t *conf, struct stripe_head *sh, +handle_failed_stripe(raid5_conf_t *conf, struct stripe_head *sh, struct stripe_head_state *s, int disks, struct bio **return_bi) { @@ -1967,48 +1876,38 @@ handle_requests_to_failed_array(raid5_conf_t *conf, struct stripe_head *sh, md_wakeup_thread(conf->mddev->thread); } -/* __handle_issuing_new_read_requests5 - returns 0 if there are no more disks - * to process +/* fetch_block5 - checks the given member device to see if its data needs + * to be read or computed to satisfy a request. + * + * Returns 1 when no more member devices need to be checked, otherwise returns + * 0 to tell the loop in handle_stripe_fill5 to continue */ -static int __handle_issuing_new_read_requests5(struct stripe_head *sh, - struct stripe_head_state *s, int disk_idx, int disks) +static int fetch_block5(struct stripe_head *sh, struct stripe_head_state *s, + int disk_idx, int disks) { struct r5dev *dev = &sh->dev[disk_idx]; struct r5dev *failed_dev = &sh->dev[s->failed_num]; - /* don't schedule compute operations or reads on the parity block while - * a check is in flight - */ - if ((disk_idx == sh->pd_idx) && - test_bit(STRIPE_OP_CHECK, &sh->ops.pending)) - return ~0; - /* is the data in this block needed, and can we get it? */ if (!test_bit(R5_LOCKED, &dev->flags) && - !test_bit(R5_UPTODATE, &dev->flags) && (dev->toread || - (dev->towrite && !test_bit(R5_OVERWRITE, &dev->flags)) || - s->syncing || s->expanding || (s->failed && - (failed_dev->toread || (failed_dev->towrite && - !test_bit(R5_OVERWRITE, &failed_dev->flags) - ))))) { - /* 1/ We would like to get this block, possibly by computing it, - * but we might not be able to. - * - * 2/ Since parity check operations potentially make the parity - * block !uptodate it will need to be refreshed before any - * compute operations on data disks are scheduled. - * - * 3/ We hold off parity block re-reads until check operations - * have quiesced. + !test_bit(R5_UPTODATE, &dev->flags) && + (dev->toread || + (dev->towrite && !test_bit(R5_OVERWRITE, &dev->flags)) || + s->syncing || s->expanding || + (s->failed && + (failed_dev->toread || + (failed_dev->towrite && + !test_bit(R5_OVERWRITE, &failed_dev->flags)))))) { + /* We would like to get this block, possibly by computing it, + * otherwise read it if the backing disk is insync */ if ((s->uptodate == disks - 1) && - (s->failed && disk_idx == s->failed_num) && - !test_bit(STRIPE_OP_CHECK, &sh->ops.pending)) { - set_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending); + (s->failed && disk_idx == s->failed_num)) { + set_bit(STRIPE_COMPUTE_RUN, &sh->state); + set_bit(STRIPE_OP_COMPUTE_BLK, &s->ops_request); set_bit(R5_Wantcompute, &dev->flags); sh->ops.target = disk_idx; s->req_compute = 1; - sh->ops.count++; /* Careful: from this point on 'uptodate' is in the eye * of raid5_run_ops which services 'compute' operations * before writes. R5_Wantcompute flags a block that will @@ -2016,53 +1915,40 @@ static int __handle_issuing_new_read_requests5(struct stripe_head *sh, * subsequent operation. */ s->uptodate++; - return 0; /* uptodate + compute == disks */ + return 1; /* uptodate + compute == disks */ } else if (test_bit(R5_Insync, &dev->flags)) { set_bit(R5_LOCKED, &dev->flags); set_bit(R5_Wantread, &dev->flags); - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; s->locked++; pr_debug("Reading block %d (sync=%d)\n", disk_idx, s->syncing); } } - return ~0; + return 0; } -static void handle_issuing_new_read_requests5(struct stripe_head *sh, +/** + * handle_stripe_fill5 - read or compute data to satisfy pending requests. + */ +static void handle_stripe_fill5(struct stripe_head *sh, struct stripe_head_state *s, int disks) { int i; - /* Clear completed compute operations. Parity recovery - * (STRIPE_OP_MOD_REPAIR_PD) implies a write-back which is handled - * later on in this routine - */ - if (test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete) && - !test_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending)) { - clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete); - clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.ack); - clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending); - } - /* look for blocks to read/compute, skip this if a compute * is already in flight, or if the stripe contents are in the * midst of changing due to a write */ - if (!test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending) && - !test_bit(STRIPE_OP_PREXOR, &sh->ops.pending) && - !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending)) { + if (!test_bit(STRIPE_COMPUTE_RUN, &sh->state) && !sh->check_state && + !sh->reconstruct_state) for (i = disks; i--; ) - if (__handle_issuing_new_read_requests5( - sh, s, i, disks) == 0) + if (fetch_block5(sh, s, i, disks)) break; - } set_bit(STRIPE_HANDLE, &sh->state); } -static void handle_issuing_new_read_requests6(struct stripe_head *sh, +static void handle_stripe_fill6(struct stripe_head *sh, struct stripe_head_state *s, struct r6_state *r6s, int disks) { @@ -2121,12 +2007,12 @@ static void handle_issuing_new_read_requests6(struct stripe_head *sh, } -/* handle_completed_write_requests +/* handle_stripe_clean_event * any written block on an uptodate or failed drive can be returned. * Note that if we 'wrote' to a failed drive, it will be UPTODATE, but * never LOCKED, so we don't need to test 'failed' directly. */ -static void handle_completed_write_requests(raid5_conf_t *conf, +static void handle_stripe_clean_event(raid5_conf_t *conf, struct stripe_head *sh, int disks, struct bio **return_bi) { int i; @@ -2171,7 +2057,7 @@ static void handle_completed_write_requests(raid5_conf_t *conf, md_wakeup_thread(conf->mddev->thread); } -static void handle_issuing_new_write_requests5(raid5_conf_t *conf, +static void handle_stripe_dirtying5(raid5_conf_t *conf, struct stripe_head *sh, struct stripe_head_state *s, int disks) { int rmw = 0, rcw = 0, i; @@ -2215,9 +2101,6 @@ static void handle_issuing_new_write_requests5(raid5_conf_t *conf, "%d for r-m-w\n", i); set_bit(R5_LOCKED, &dev->flags); set_bit(R5_Wantread, &dev->flags); - if (!test_and_set_bit( - STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; s->locked++; } else { set_bit(STRIPE_DELAYED, &sh->state); @@ -2241,9 +2124,6 @@ static void handle_issuing_new_write_requests5(raid5_conf_t *conf, "%d for Reconstruct\n", i); set_bit(R5_LOCKED, &dev->flags); set_bit(R5_Wantread, &dev->flags); - if (!test_and_set_bit( - STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; s->locked++; } else { set_bit(STRIPE_DELAYED, &sh->state); @@ -2261,14 +2141,13 @@ static void handle_issuing_new_write_requests5(raid5_conf_t *conf, * simultaneously. If this is not the case then new writes need to be * held off until the compute completes. */ - if ((s->req_compute || - !test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending)) && - (s->locked == 0 && (rcw == 0 || rmw == 0) && - !test_bit(STRIPE_BIT_DELAY, &sh->state))) - s->locked += handle_write_operations5(sh, rcw == 0, 0); + if ((s->req_compute || !test_bit(STRIPE_COMPUTE_RUN, &sh->state)) && + (s->locked == 0 && (rcw == 0 || rmw == 0) && + !test_bit(STRIPE_BIT_DELAY, &sh->state))) + schedule_reconstruction5(sh, s, rcw == 0, 0); } -static void handle_issuing_new_write_requests6(raid5_conf_t *conf, +static void handle_stripe_dirtying6(raid5_conf_t *conf, struct stripe_head *sh, struct stripe_head_state *s, struct r6_state *r6s, int disks) { @@ -2371,92 +2250,86 @@ static void handle_issuing_new_write_requests6(raid5_conf_t *conf, static void handle_parity_checks5(raid5_conf_t *conf, struct stripe_head *sh, struct stripe_head_state *s, int disks) { - int canceled_check = 0; + struct r5dev *dev = NULL; set_bit(STRIPE_HANDLE, &sh->state); - /* complete a check operation */ - if (test_and_clear_bit(STRIPE_OP_CHECK, &sh->ops.complete)) { - clear_bit(STRIPE_OP_CHECK, &sh->ops.ack); - clear_bit(STRIPE_OP_CHECK, &sh->ops.pending); + switch (sh->check_state) { + case check_state_idle: + /* start a new check operation if there are no failures */ if (s->failed == 0) { - if (sh->ops.zero_sum_result == 0) - /* parity is correct (on disc, - * not in buffer any more) - */ - set_bit(STRIPE_INSYNC, &sh->state); - else { - conf->mddev->resync_mismatches += - STRIPE_SECTORS; - if (test_bit( - MD_RECOVERY_CHECK, &conf->mddev->recovery)) - /* don't try to repair!! */ - set_bit(STRIPE_INSYNC, &sh->state); - else { - set_bit(STRIPE_OP_COMPUTE_BLK, - &sh->ops.pending); - set_bit(STRIPE_OP_MOD_REPAIR_PD, - &sh->ops.pending); - set_bit(R5_Wantcompute, - &sh->dev[sh->pd_idx].flags); - sh->ops.target = sh->pd_idx; - sh->ops.count++; - s->uptodate++; - } - } - } else - canceled_check = 1; /* STRIPE_INSYNC is not set */ - } - - /* start a new check operation if there are no failures, the stripe is - * not insync, and a repair is not in flight - */ - if (s->failed == 0 && - !test_bit(STRIPE_INSYNC, &sh->state) && - !test_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending)) { - if (!test_and_set_bit(STRIPE_OP_CHECK, &sh->ops.pending)) { BUG_ON(s->uptodate != disks); + sh->check_state = check_state_run; + set_bit(STRIPE_OP_CHECK, &s->ops_request); clear_bit(R5_UPTODATE, &sh->dev[sh->pd_idx].flags); - sh->ops.count++; s->uptodate--; + break; } - } - - /* check if we can clear a parity disk reconstruct */ - if (test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete) && - test_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending)) { - - clear_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending); - clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete); - clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.ack); - clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending); - } - + dev = &sh->dev[s->failed_num]; + /* fall through */ + case check_state_compute_result: + sh->check_state = check_state_idle; + if (!dev) + dev = &sh->dev[sh->pd_idx]; + + /* check that a write has not made the stripe insync */ + if (test_bit(STRIPE_INSYNC, &sh->state)) + break; - /* Wait for check parity and compute block operations to complete - * before write-back. If a failure occurred while the check operation - * was in flight we need to cycle this stripe through handle_stripe - * since the parity block may not be uptodate - */ - if (!canceled_check && !test_bit(STRIPE_INSYNC, &sh->state) && - !test_bit(STRIPE_OP_CHECK, &sh->ops.pending) && - !test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending)) { - struct r5dev *dev; /* either failed parity check, or recovery is happening */ - if (s->failed == 0) - s->failed_num = sh->pd_idx; - dev = &sh->dev[s->failed_num]; BUG_ON(!test_bit(R5_UPTODATE, &dev->flags)); BUG_ON(s->uptodate != disks); set_bit(R5_LOCKED, &dev->flags); + s->locked++; set_bit(R5_Wantwrite, &dev->flags); - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; clear_bit(STRIPE_DEGRADED, &sh->state); - s->locked++; set_bit(STRIPE_INSYNC, &sh->state); + break; + case check_state_run: + break; /* we will be called again upon completion */ + case check_state_check_result: + sh->check_state = check_state_idle; + + /* if a failure occurred during the check operation, leave + * STRIPE_INSYNC not set and let the stripe be handled again + */ + if (s->failed) + break; + + /* handle a successful check operation, if parity is correct + * we are done. Otherwise update the mismatch count and repair + * parity if !MD_RECOVERY_CHECK + */ + if (sh->ops.zero_sum_result == 0) + /* parity is correct (on disc, + * not in buffer any more) + */ + set_bit(STRIPE_INSYNC, &sh->state); + else { + conf->mddev->resync_mismatches += STRIPE_SECTORS; + if (test_bit(MD_RECOVERY_CHECK, &conf->mddev->recovery)) + /* don't try to repair!! */ + set_bit(STRIPE_INSYNC, &sh->state); + else { + sh->check_state = check_state_compute_run; + set_bit(STRIPE_COMPUTE_RUN, &sh->state); + set_bit(STRIPE_OP_COMPUTE_BLK, &s->ops_request); + set_bit(R5_Wantcompute, + &sh->dev[sh->pd_idx].flags); + sh->ops.target = sh->pd_idx; + s->uptodate++; + } + } + break; + case check_state_compute_run: + break; + default: + printk(KERN_ERR "%s: unknown check_state: %d sector: %llu\n", + __func__, sh->check_state, + (unsigned long long) sh->sector); + BUG(); } } @@ -2641,15 +2514,14 @@ static void handle_stripe5(struct stripe_head *sh) struct bio *return_bi = NULL; struct stripe_head_state s; struct r5dev *dev; - unsigned long pending = 0; mdk_rdev_t *blocked_rdev = NULL; int prexor; memset(&s, 0, sizeof(s)); - pr_debug("handling stripe %llu, state=%#lx cnt=%d, pd_idx=%d " - "ops=%lx:%lx:%lx\n", (unsigned long long)sh->sector, sh->state, - atomic_read(&sh->count), sh->pd_idx, - sh->ops.pending, sh->ops.ack, sh->ops.complete); + pr_debug("handling stripe %llu, state=%#lx cnt=%d, pd_idx=%d check:%d " + "reconstruct:%d\n", (unsigned long long)sh->sector, sh->state, + atomic_read(&sh->count), sh->pd_idx, sh->check_state, + sh->reconstruct_state); spin_lock(&sh->lock); clear_bit(STRIPE_HANDLE, &sh->state); @@ -2658,15 +2530,8 @@ static void handle_stripe5(struct stripe_head *sh) s.syncing = test_bit(STRIPE_SYNCING, &sh->state); s.expanding = test_bit(STRIPE_EXPAND_SOURCE, &sh->state); s.expanded = test_bit(STRIPE_EXPAND_READY, &sh->state); - /* Now to look around and see what can be done */ - - /* clean-up completed biofill operations */ - if (test_bit(STRIPE_OP_BIOFILL, &sh->ops.complete)) { - clear_bit(STRIPE_OP_BIOFILL, &sh->ops.pending); - clear_bit(STRIPE_OP_BIOFILL, &sh->ops.ack); - clear_bit(STRIPE_OP_BIOFILL, &sh->ops.complete); - } + /* Now to look around and see what can be done */ rcu_read_lock(); for (i=disks; i--; ) { mdk_rdev_t *rdev; @@ -2680,10 +2545,10 @@ static void handle_stripe5(struct stripe_head *sh) /* maybe we can request a biofill operation * * new wantfill requests are only permitted while - * STRIPE_OP_BIOFILL is clear + * ops_complete_biofill is guaranteed to be inactive */ if (test_bit(R5_UPTODATE, &dev->flags) && dev->toread && - !test_bit(STRIPE_OP_BIOFILL, &sh->ops.pending)) + !test_bit(STRIPE_BIOFILL_RUN, &sh->state)) set_bit(R5_Wantfill, &dev->flags); /* now count some things */ @@ -2727,8 +2592,10 @@ static void handle_stripe5(struct stripe_head *sh) goto unlock; } - if (s.to_fill && !test_and_set_bit(STRIPE_OP_BIOFILL, &sh->ops.pending)) - sh->ops.count++; + if (s.to_fill && !test_bit(STRIPE_BIOFILL_RUN, &sh->state)) { + set_bit(STRIPE_OP_BIOFILL, &s.ops_request); + set_bit(STRIPE_BIOFILL_RUN, &sh->state); + } pr_debug("locked=%d uptodate=%d to_read=%d" " to_write=%d failed=%d failed_num=%d\n", @@ -2738,8 +2605,7 @@ static void handle_stripe5(struct stripe_head *sh) * need to be failed */ if (s.failed > 1 && s.to_read+s.to_write+s.written) - handle_requests_to_failed_array(conf, sh, &s, disks, - &return_bi); + handle_failed_stripe(conf, sh, &s, disks, &return_bi); if (s.failed > 1 && s.syncing) { md_done_sync(conf->mddev, STRIPE_SECTORS,0); clear_bit(STRIPE_SYNCING, &sh->state); @@ -2755,48 +2621,25 @@ static void handle_stripe5(struct stripe_head *sh) !test_bit(R5_LOCKED, &dev->flags) && test_bit(R5_UPTODATE, &dev->flags)) || (s.failed == 1 && s.failed_num == sh->pd_idx))) - handle_completed_write_requests(conf, sh, disks, &return_bi); + handle_stripe_clean_event(conf, sh, disks, &return_bi); /* Now we might consider reading some blocks, either to check/generate * parity, or to satisfy requests * or to load a block that is being partially written. */ if (s.to_read || s.non_overwrite || - (s.syncing && (s.uptodate + s.compute < disks)) || s.expanding || - test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending)) - handle_issuing_new_read_requests5(sh, &s, disks); + (s.syncing && (s.uptodate + s.compute < disks)) || s.expanding) + handle_stripe_fill5(sh, &s, disks); /* Now we check to see if any write operations have recently * completed */ - - /* leave prexor set until postxor is done, allows us to distinguish - * a rmw from a rcw during biodrain - */ prexor = 0; - if (test_bit(STRIPE_OP_PREXOR, &sh->ops.complete) && - test_bit(STRIPE_OP_POSTXOR, &sh->ops.complete)) { - + if (sh->reconstruct_state == reconstruct_state_prexor_drain_result) prexor = 1; - clear_bit(STRIPE_OP_PREXOR, &sh->ops.complete); - clear_bit(STRIPE_OP_PREXOR, &sh->ops.ack); - clear_bit(STRIPE_OP_PREXOR, &sh->ops.pending); - - for (i = disks; i--; ) - clear_bit(R5_Wantprexor, &sh->dev[i].flags); - } - - /* if only POSTXOR is set then this is an 'expand' postxor */ - if (test_bit(STRIPE_OP_BIODRAIN, &sh->ops.complete) && - test_bit(STRIPE_OP_POSTXOR, &sh->ops.complete)) { - - clear_bit(STRIPE_OP_BIODRAIN, &sh->ops.complete); - clear_bit(STRIPE_OP_BIODRAIN, &sh->ops.ack); - clear_bit(STRIPE_OP_BIODRAIN, &sh->ops.pending); - - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.complete); - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.ack); - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.pending); + if (sh->reconstruct_state == reconstruct_state_drain_result || + sh->reconstruct_state == reconstruct_state_prexor_drain_result) { + sh->reconstruct_state = reconstruct_state_idle; /* All the 'written' buffers and the parity block are ready to * be written back to disk @@ -2808,9 +2651,6 @@ static void handle_stripe5(struct stripe_head *sh) (i == sh->pd_idx || dev->written)) { pr_debug("Writing block %d\n", i); set_bit(R5_Wantwrite, &dev->flags); - if (!test_and_set_bit( - STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; if (prexor) continue; if (!test_bit(R5_Insync, &dev->flags) || @@ -2832,20 +2672,18 @@ static void handle_stripe5(struct stripe_head *sh) * 2/ A 'check' operation is in flight, as it may clobber the parity * block. */ - if (s.to_write && !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending) && - !test_bit(STRIPE_OP_CHECK, &sh->ops.pending)) - handle_issuing_new_write_requests5(conf, sh, &s, disks); + if (s.to_write && !sh->reconstruct_state && !sh->check_state) + handle_stripe_dirtying5(conf, sh, &s, disks); /* maybe we need to check and possibly fix the parity for this stripe * Any reads will already have been scheduled, so we just see if enough * data is available. The parity check is held off while parity * dependent operations are in flight. */ - if ((s.syncing && s.locked == 0 && - !test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending) && - !test_bit(STRIPE_INSYNC, &sh->state)) || - test_bit(STRIPE_OP_CHECK, &sh->ops.pending) || - test_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending)) + if (sh->check_state || + (s.syncing && s.locked == 0 && + !test_bit(STRIPE_COMPUTE_RUN, &sh->state) && + !test_bit(STRIPE_INSYNC, &sh->state))) handle_parity_checks5(conf, sh, &s, disks); if (s.syncing && s.locked == 0 && test_bit(STRIPE_INSYNC, &sh->state)) { @@ -2864,52 +2702,35 @@ static void handle_stripe5(struct stripe_head *sh) dev = &sh->dev[s.failed_num]; if (!test_bit(R5_ReWrite, &dev->flags)) { set_bit(R5_Wantwrite, &dev->flags); - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; set_bit(R5_ReWrite, &dev->flags); set_bit(R5_LOCKED, &dev->flags); s.locked++; } else { /* let's read it back */ set_bit(R5_Wantread, &dev->flags); - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; set_bit(R5_LOCKED, &dev->flags); s.locked++; } } - /* Finish postxor operations initiated by the expansion - * process - */ - if (test_bit(STRIPE_OP_POSTXOR, &sh->ops.complete) && - !test_bit(STRIPE_OP_BIODRAIN, &sh->ops.pending)) { - + /* Finish reconstruct operations initiated by the expansion process */ + if (sh->reconstruct_state == reconstruct_state_result) { + sh->reconstruct_state = reconstruct_state_idle; clear_bit(STRIPE_EXPANDING, &sh->state); - - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.pending); - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.ack); - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.complete); - - for (i = conf->raid_disks; i--; ) { + for (i = conf->raid_disks; i--; ) set_bit(R5_Wantwrite, &sh->dev[i].flags); set_bit(R5_LOCKED, &dev->flags); s.locked++; - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; - } } if (s.expanded && test_bit(STRIPE_EXPANDING, &sh->state) && - !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending)) { + !sh->reconstruct_state) { /* Need to write out all blocks after computing parity */ sh->disks = conf->raid_disks; sh->pd_idx = stripe_to_pdidx(sh->sector, conf, conf->raid_disks); - s.locked += handle_write_operations5(sh, 1, 1); - } else if (s.expanded && - s.locked == 0 && - !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending)) { + schedule_reconstruction5(sh, &s, 1, 1); + } else if (s.expanded && !sh->reconstruct_state && s.locked == 0) { clear_bit(STRIPE_EXPAND_READY, &sh->state); atomic_dec(&conf->reshape_stripes); wake_up(&conf->wait_for_overlap); @@ -2917,12 +2738,9 @@ static void handle_stripe5(struct stripe_head *sh) } if (s.expanding && s.locked == 0 && - !test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending)) + !test_bit(STRIPE_COMPUTE_RUN, &sh->state)) handle_stripe_expansion(conf, sh, NULL); - if (sh->ops.count) - pending = get_stripe_work(sh); - unlock: spin_unlock(&sh->lock); @@ -2930,11 +2748,12 @@ static void handle_stripe5(struct stripe_head *sh) if (unlikely(blocked_rdev)) md_wait_for_blocked_rdev(blocked_rdev, conf->mddev); - if (pending) - raid5_run_ops(sh, pending); + if (s.ops_request) + raid5_run_ops(sh, s.ops_request); - return_io(return_bi); + ops_run_io(sh, &s); + return_io(return_bi); } static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page) @@ -3042,8 +2861,7 @@ static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page) * might need to be failed */ if (s.failed > 2 && s.to_read+s.to_write+s.written) - handle_requests_to_failed_array(conf, sh, &s, disks, - &return_bi); + handle_failed_stripe(conf, sh, &s, disks, &return_bi); if (s.failed > 2 && s.syncing) { md_done_sync(conf->mddev, STRIPE_SECTORS,0); clear_bit(STRIPE_SYNCING, &sh->state); @@ -3068,7 +2886,7 @@ static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page) ( r6s.q_failed || ((test_bit(R5_Insync, &qdev->flags) && !test_bit(R5_LOCKED, &qdev->flags) && test_bit(R5_UPTODATE, &qdev->flags))))) - handle_completed_write_requests(conf, sh, disks, &return_bi); + handle_stripe_clean_event(conf, sh, disks, &return_bi); /* Now we might consider reading some blocks, either to check/generate * parity, or to satisfy requests @@ -3076,11 +2894,11 @@ static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page) */ if (s.to_read || s.non_overwrite || (s.to_write && s.failed) || (s.syncing && (s.uptodate < disks)) || s.expanding) - handle_issuing_new_read_requests6(sh, &s, &r6s, disks); + handle_stripe_fill6(sh, &s, &r6s, disks); /* now to consider writing and what else, if anything should be read */ if (s.to_write) - handle_issuing_new_write_requests6(conf, sh, &s, &r6s, disks); + handle_stripe_dirtying6(conf, sh, &s, &r6s, disks); /* maybe we need to check and possibly fix the parity for this stripe * Any reads will already have been scheduled, so we just see if enough @@ -3136,7 +2954,7 @@ static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page) } if (s.expanding && s.locked == 0 && - !test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending)) + !test_bit(STRIPE_COMPUTE_RUN, &sh->state)) handle_stripe_expansion(conf, sh, &r6s); unlock: @@ -3146,68 +2964,9 @@ static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page) if (unlikely(blocked_rdev)) md_wait_for_blocked_rdev(blocked_rdev, conf->mddev); - return_io(return_bi); - - for (i=disks; i-- ;) { - int rw; - struct bio *bi; - mdk_rdev_t *rdev; - if (test_and_clear_bit(R5_Wantwrite, &sh->dev[i].flags)) - rw = WRITE; - else if (test_and_clear_bit(R5_Wantread, &sh->dev[i].flags)) - rw = READ; - else - continue; - - set_bit(STRIPE_IO_STARTED, &sh->state); - - bi = &sh->dev[i].req; - - bi->bi_rw = rw; - if (rw == WRITE) - bi->bi_end_io = raid5_end_write_request; - else - bi->bi_end_io = raid5_end_read_request; - - rcu_read_lock(); - rdev = rcu_dereference(conf->disks[i].rdev); - if (rdev && test_bit(Faulty, &rdev->flags)) - rdev = NULL; - if (rdev) - atomic_inc(&rdev->nr_pending); - rcu_read_unlock(); + ops_run_io(sh, &s); - if (rdev) { - if (s.syncing || s.expanding || s.expanded) - md_sync_acct(rdev->bdev, STRIPE_SECTORS); - - bi->bi_bdev = rdev->bdev; - pr_debug("for %llu schedule op %ld on disc %d\n", - (unsigned long long)sh->sector, bi->bi_rw, i); - atomic_inc(&sh->count); - bi->bi_sector = sh->sector + rdev->data_offset; - bi->bi_flags = 1 << BIO_UPTODATE; - bi->bi_vcnt = 1; - bi->bi_max_vecs = 1; - bi->bi_idx = 0; - bi->bi_io_vec = &sh->dev[i].vec; - bi->bi_io_vec[0].bv_len = STRIPE_SIZE; - bi->bi_io_vec[0].bv_offset = 0; - bi->bi_size = STRIPE_SIZE; - bi->bi_next = NULL; - if (rw == WRITE && - test_bit(R5_ReWrite, &sh->dev[i].flags)) - atomic_add(STRIPE_SECTORS, &rdev->corrected_errors); - generic_make_request(bi); - } else { - if (rw == WRITE) - set_bit(STRIPE_DEGRADED, &sh->state); - pr_debug("skip op %ld on disc %d for sector %llu\n", - bi->bi_rw, i, (unsigned long long)sh->sector); - clear_bit(R5_LOCKED, &sh->dev[i].flags); - set_bit(STRIPE_HANDLE, &sh->state); - } - } + return_io(return_bi); } static void handle_stripe(struct stripe_head *sh, struct page *tmp_page) @@ -3697,9 +3456,7 @@ static int make_request(struct request_queue *q, struct bio * bi) if ( rw == WRITE ) md_write_end(mddev); - bi->bi_end_io(bi, - test_bit(BIO_UPTODATE, &bi->bi_flags) - ? 0 : -EIO); + bio_endio(bi, 0); } return 0; } @@ -3785,7 +3542,7 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped j == raid6_next_disk(sh->pd_idx, sh->disks)) continue; s = compute_blocknr(sh, j); - if (s < (mddev->array_size<<1)) { + if (s < mddev->array_sectors) { skipped = 1; continue; } @@ -4002,12 +3759,8 @@ static int retry_aligned_read(raid5_conf_t *conf, struct bio *raid_bio) spin_lock_irq(&conf->device_lock); remaining = --raid_bio->bi_phys_segments; spin_unlock_irq(&conf->device_lock); - if (remaining == 0) { - - raid_bio->bi_end_io(raid_bio, - test_bit(BIO_UPTODATE, &raid_bio->bi_flags) - ? 0 : -EIO); - } + if (remaining == 0) + bio_endio(raid_bio, 0); if (atomic_dec_and_test(&conf->active_aligned_reads)) wake_up(&conf->wait_for_stripe); return handled; @@ -4094,6 +3847,8 @@ raid5_store_stripe_cache_size(mddev_t *mddev, const char *page, size_t len) { raid5_conf_t *conf = mddev_to_conf(mddev); unsigned long new; + int err; + if (len >= PAGE_SIZE) return -EINVAL; if (!conf) @@ -4109,7 +3864,9 @@ raid5_store_stripe_cache_size(mddev_t *mddev, const char *page, size_t len) else break; } - md_allow_write(mddev); + err = md_allow_write(mddev); + if (err) + return err; while (new > conf->max_nr_stripes) { if (grow_one_stripe(conf)) conf->max_nr_stripes++; @@ -4434,7 +4191,7 @@ static int run(mddev_t *mddev) mddev->queue->backing_dev_info.congested_data = mddev; mddev->queue->backing_dev_info.congested_fn = raid5_congested; - mddev->array_size = mddev->size * (conf->previous_raid_disks - + mddev->array_sectors = 2 * mddev->size * (conf->previous_raid_disks - conf->max_degraded); blk_queue_merge_bvec(mddev->queue, raid5_mergeable_bvec); @@ -4609,35 +4366,41 @@ abort: static int raid5_add_disk(mddev_t *mddev, mdk_rdev_t *rdev) { raid5_conf_t *conf = mddev->private; - int found = 0; + int err = -EEXIST; int disk; struct disk_info *p; + int first = 0; + int last = conf->raid_disks - 1; if (mddev->degraded > conf->max_degraded) /* no point adding a device */ - return 0; + return -EINVAL; + + if (rdev->raid_disk >= 0) + first = last = rdev->raid_disk; /* * find the disk ... but prefer rdev->saved_raid_disk * if possible. */ if (rdev->saved_raid_disk >= 0 && + rdev->saved_raid_disk >= first && conf->disks[rdev->saved_raid_disk].rdev == NULL) disk = rdev->saved_raid_disk; else - disk = 0; - for ( ; disk < conf->raid_disks; disk++) + disk = first; + for ( ; disk <= last ; disk++) if ((p=conf->disks + disk)->rdev == NULL) { clear_bit(In_sync, &rdev->flags); rdev->raid_disk = disk; - found = 1; + err = 0; if (rdev->saved_raid_disk != disk) conf->fullsync = 1; rcu_assign_pointer(p->rdev, rdev); break; } print_raid5_conf(conf); - return found; + return err; } static int raid5_resize(mddev_t *mddev, sector_t sectors) @@ -4652,8 +4415,9 @@ static int raid5_resize(mddev_t *mddev, sector_t sectors) raid5_conf_t *conf = mddev_to_conf(mddev); sectors &= ~((sector_t)mddev->chunk_size/512 - 1); - mddev->array_size = (sectors * (mddev->raid_disks-conf->max_degraded))>>1; - set_capacity(mddev->gendisk, mddev->array_size << 1); + mddev->array_sectors = sectors * (mddev->raid_disks + - conf->max_degraded); + set_capacity(mddev->gendisk, mddev->array_sectors); mddev->changed = 1; if (sectors/2 > mddev->size && mddev->recovery_cp == MaxSector) { mddev->recovery_cp = mddev->size << 1; @@ -4738,7 +4502,7 @@ static int raid5_start_reshape(mddev_t *mddev) rdev_for_each(rdev, rtmp, mddev) if (rdev->raid_disk < 0 && !test_bit(Faulty, &rdev->flags)) { - if (raid5_add_disk(mddev, rdev)) { + if (raid5_add_disk(mddev, rdev) == 0) { char nm[20]; set_bit(In_sync, &rdev->flags); added_devices++; @@ -4786,15 +4550,16 @@ static void end_reshape(raid5_conf_t *conf) struct block_device *bdev; if (!test_bit(MD_RECOVERY_INTR, &conf->mddev->recovery)) { - conf->mddev->array_size = conf->mddev->size * + conf->mddev->array_sectors = 2 * conf->mddev->size * (conf->raid_disks - conf->max_degraded); - set_capacity(conf->mddev->gendisk, conf->mddev->array_size << 1); + set_capacity(conf->mddev->gendisk, conf->mddev->array_sectors); conf->mddev->changed = 1; bdev = bdget_disk(conf->mddev->gendisk, 0); if (bdev) { mutex_lock(&bdev->bd_inode->i_mutex); - i_size_write(bdev->bd_inode, (loff_t)conf->mddev->array_size << 10); + i_size_write(bdev->bd_inode, + (loff_t)conf->mddev->array_sectors << 9); mutex_unlock(&bdev->bd_inode->i_mutex); bdput(bdev); } diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index 171afe7da6b6..cf6a817d5059 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -563,7 +563,7 @@ int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev DEB_EE(("dev:%p\n",dev)); - if( VFL_TYPE_GRABBER == (*vid)->type ) { + if ((*vid)->vfl_type == VFL_TYPE_GRABBER) { vv->video_minor = -1; } else { vv->vbi_minor = -1; diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index a5e62750eea3..e8bc7abf2409 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -656,7 +656,7 @@ static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *bu /* if we have a user buffer, the first page may not be aligned to a page boundary. */ - pt1->offset = list->offset; + pt1->offset = dma->sglist->offset; pt2->offset = pt1->offset+o1; pt3->offset = pt1->offset+o2; @@ -958,21 +958,18 @@ int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int case VIDIOC_ENUM_FMT: { struct v4l2_fmtdesc *f = arg; - int index; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - case V4L2_BUF_TYPE_VIDEO_OVERLAY: { - index = f->index; - if (index < 0 || index >= NUM_FORMATS) { + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (f->index >= NUM_FORMATS) return -EINVAL; - } - memset(f,0,sizeof(*f)); - f->index = index; - strlcpy((char *)f->description,formats[index].name,sizeof(f->description)); - f->pixelformat = formats[index].pixelformat; + strlcpy((char *)f->description, formats[f->index].name, + sizeof(f->description)); + f->pixelformat = formats[f->index].pixelformat; + f->flags = 0; + memset(f->reserved, 0, sizeof(f->reserved)); break; - } default: return -EINVAL; } diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig index 850d5689b14d..6f92beaa5ac8 100644 --- a/drivers/media/common/tuners/Kconfig +++ b/drivers/media/common/tuners/Kconfig @@ -21,9 +21,8 @@ config MEDIA_TUNER tristate default VIDEO_MEDIA && I2C depends on VIDEO_MEDIA && I2C - select FW_LOADER if !MEDIA_TUNER_CUSTOMIZE && HOTPLUG - select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMIZE && HOTPLUG - select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMIZE && HOTPLUG + select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMIZE select MEDIA_TUNER_MT20XX if !MEDIA_TUNER_CUSTOMIZE select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMIZE select MEDIA_TUNER_TEA5761 if !MEDIA_TUNER_CUSTOMIZE @@ -138,8 +137,6 @@ config MEDIA_TUNER_QT1010 config MEDIA_TUNER_XC2028 tristate "XCeive xc2028/xc3028 tuners" depends on VIDEO_MEDIA && I2C - depends on HOTPLUG - select FW_LOADER default m if MEDIA_TUNER_CUSTOMIZE help Say Y here to include support for the xc2028/xc3028 tuners. @@ -147,8 +144,6 @@ config MEDIA_TUNER_XC2028 config MEDIA_TUNER_XC5000 tristate "Xceive XC5000 silicon tuner" depends on VIDEO_MEDIA && I2C - depends on HOTPLUG - select FW_LOADER default m if DVB_FE_CUSTOMISE help A driver for the silicon tuner XC5000 from Xceive. @@ -162,4 +157,11 @@ config MEDIA_TUNER_MXL5005S help A driver for the silicon tuner MXL5005S from MaxLinear. +config MEDIA_TUNER_MXL5007T + tristate "MaxLinear MxL5007T silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if DVB_FE_CUSTOMISE + help + A driver for the silicon tuner MxL5007T from MaxLinear. + endif # MEDIA_TUNER_CUSTOMIZE diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile index 55f7e6706297..4dfbe5b8264f 100644 --- a/drivers/media/common/tuners/Makefile +++ b/drivers/media/common/tuners/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_MEDIA_TUNER_MT2266) += mt2266.o obj-$(CONFIG_MEDIA_TUNER_QT1010) += qt1010.o obj-$(CONFIG_MEDIA_TUNER_MT2131) += mt2131.o obj-$(CONFIG_MEDIA_TUNER_MXL5005S) += mxl5005s.o +obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends diff --git a/drivers/media/common/tuners/mt20xx.c b/drivers/media/common/tuners/mt20xx.c index fbcb28233737..35b763a16d53 100644 --- a/drivers/media/common/tuners/mt20xx.c +++ b/drivers/media/common/tuners/mt20xx.c @@ -148,7 +148,8 @@ static int mt2032_compute_freq(struct dvb_frontend *fe, tuner_dbg("mt2032: rfin=%d lo2=%d lo2n=%d lo2a=%d num=%d lo2freq=%d\n", rfin,lo2,lo2n,lo2a,lo2num,lo2freq); - if(lo1a<0 || lo1a>7 || lo1n<17 ||lo1n>48 || lo2a<0 ||lo2a >7 ||lo2n<17 || lo2n>30) { + if (lo1a > 7 || lo1n < 17 || lo1n > 48 || lo2a > 7 || lo2n < 17 || + lo2n > 30) { tuner_info("mt2032: frequency parameters out of range: %d %d %d %d\n", lo1a, lo1n, lo2a,lo2n); return(-1); diff --git a/drivers/media/common/tuners/mxl5007t.c b/drivers/media/common/tuners/mxl5007t.c new file mode 100644 index 000000000000..cb25e43502fe --- /dev/null +++ b/drivers/media/common/tuners/mxl5007t.c @@ -0,0 +1,1030 @@ +/* + * mxl5007t.c - driver for the MaxLinear MxL5007T silicon tuner + * + * Copyright (C) 2008 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, 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/i2c.h> +#include <linux/types.h> +#include <linux/videodev2.h> +#include "tuner-i2c.h" +#include "mxl5007t.h" + +static DEFINE_MUTEX(mxl5007t_list_mutex); +static LIST_HEAD(hybrid_tuner_instance_list); + +static int mxl5007t_debug; +module_param_named(debug, mxl5007t_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debug level"); + +/* ------------------------------------------------------------------------- */ + +#define mxl_printk(kern, fmt, arg...) \ + printk(kern "%s: " fmt "\n", __func__, ##arg) + +#define mxl_err(fmt, arg...) \ + mxl_printk(KERN_ERR, "%d: " fmt, __LINE__, ##arg) + +#define mxl_warn(fmt, arg...) \ + mxl_printk(KERN_WARNING, fmt, ##arg) + +#define mxl_info(fmt, arg...) \ + mxl_printk(KERN_INFO, fmt, ##arg) + +#define mxl_debug(fmt, arg...) \ +({ \ + if (mxl5007t_debug) \ + mxl_printk(KERN_DEBUG, fmt, ##arg); \ +}) + +#define mxl_fail(ret) \ +({ \ + int __ret; \ + __ret = (ret < 0); \ + if (__ret) \ + mxl_printk(KERN_ERR, "error %d on line %d", \ + ret, __LINE__); \ + __ret; \ +}) + +/* ------------------------------------------------------------------------- */ + +#define MHz 1000000 + +enum mxl5007t_mode { + MxL_MODE_OTA_DVBT_ATSC = 0, + MxL_MODE_OTA_NTSC_PAL_GH = 1, + MxL_MODE_OTA_PAL_IB = 2, + MxL_MODE_OTA_PAL_D_SECAM_KL = 3, + MxL_MODE_OTA_ISDBT = 4, + MxL_MODE_CABLE_DIGITAL = 0x10, + MxL_MODE_CABLE_NTSC_PAL_GH = 0x11, + MxL_MODE_CABLE_PAL_IB = 0x12, + MxL_MODE_CABLE_PAL_D_SECAM_KL = 0x13, + MxL_MODE_CABLE_SCTE40 = 0x14, +}; + +enum mxl5007t_chip_version { + MxL_UNKNOWN_ID = 0x00, + MxL_5007_V1_F1 = 0x11, + MxL_5007_V1_F2 = 0x12, + MxL_5007_V2_100_F1 = 0x21, + MxL_5007_V2_100_F2 = 0x22, + MxL_5007_V2_200_F1 = 0x23, + MxL_5007_V2_200_F2 = 0x24, +}; + +struct reg_pair_t { + u8 reg; + u8 val; +}; + +/* ------------------------------------------------------------------------- */ + +static struct reg_pair_t init_tab[] = { + { 0x0b, 0x44 }, /* XTAL */ + { 0x0c, 0x60 }, /* IF */ + { 0x10, 0x00 }, /* MISC */ + { 0x12, 0xca }, /* IDAC */ + { 0x16, 0x90 }, /* MODE */ + { 0x32, 0x38 }, /* MODE Analog/Digital */ + { 0xd8, 0x18 }, /* CLK_OUT_ENABLE */ + { 0x2c, 0x34 }, /* OVERRIDE */ + { 0x4d, 0x40 }, /* OVERRIDE */ + { 0x7f, 0x02 }, /* OVERRIDE */ + { 0x9a, 0x52 }, /* OVERRIDE */ + { 0x48, 0x5a }, /* OVERRIDE */ + { 0x76, 0x1a }, /* OVERRIDE */ + { 0x6a, 0x48 }, /* OVERRIDE */ + { 0x64, 0x28 }, /* OVERRIDE */ + { 0x66, 0xe6 }, /* OVERRIDE */ + { 0x35, 0x0e }, /* OVERRIDE */ + { 0x7e, 0x01 }, /* OVERRIDE */ + { 0x83, 0x00 }, /* OVERRIDE */ + { 0x04, 0x0b }, /* OVERRIDE */ + { 0x05, 0x01 }, /* TOP_MASTER_ENABLE */ + { 0, 0 } +}; + +static struct reg_pair_t init_tab_cable[] = { + { 0x0b, 0x44 }, /* XTAL */ + { 0x0c, 0x60 }, /* IF */ + { 0x10, 0x00 }, /* MISC */ + { 0x12, 0xca }, /* IDAC */ + { 0x16, 0x90 }, /* MODE */ + { 0x32, 0x38 }, /* MODE A/D */ + { 0x71, 0x3f }, /* TOP1 */ + { 0x72, 0x3f }, /* TOP2 */ + { 0x74, 0x3f }, /* TOP3 */ + { 0xd8, 0x18 }, /* CLK_OUT_ENABLE */ + { 0x2c, 0x34 }, /* OVERRIDE */ + { 0x4d, 0x40 }, /* OVERRIDE */ + { 0x7f, 0x02 }, /* OVERRIDE */ + { 0x9a, 0x52 }, /* OVERRIDE */ + { 0x48, 0x5a }, /* OVERRIDE */ + { 0x76, 0x1a }, /* OVERRIDE */ + { 0x6a, 0x48 }, /* OVERRIDE */ + { 0x64, 0x28 }, /* OVERRIDE */ + { 0x66, 0xe6 }, /* OVERRIDE */ + { 0x35, 0x0e }, /* OVERRIDE */ + { 0x7e, 0x01 }, /* OVERRIDE */ + { 0x04, 0x0b }, /* OVERRIDE */ + { 0x68, 0xb4 }, /* OVERRIDE */ + { 0x36, 0x00 }, /* OVERRIDE */ + { 0x05, 0x01 }, /* TOP_MASTER_ENABLE */ + { 0, 0 } +}; + +/* ------------------------------------------------------------------------- */ + +static struct reg_pair_t reg_pair_rftune[] = { + { 0x11, 0x00 }, /* abort tune */ + { 0x13, 0x15 }, + { 0x14, 0x40 }, + { 0x15, 0x0e }, + { 0x11, 0x02 }, /* start tune */ + { 0, 0 } +}; + +/* ------------------------------------------------------------------------- */ + +struct mxl5007t_state { + struct list_head hybrid_tuner_instance_list; + struct tuner_i2c_props i2c_props; + + struct mutex lock; + + struct mxl5007t_config *config; + + enum mxl5007t_chip_version chip_id; + + struct reg_pair_t tab_init[ARRAY_SIZE(init_tab)]; + struct reg_pair_t tab_init_cable[ARRAY_SIZE(init_tab_cable)]; + struct reg_pair_t tab_rftune[ARRAY_SIZE(reg_pair_rftune)]; + + u32 frequency; + u32 bandwidth; +}; + +/* ------------------------------------------------------------------------- */ + +/* called by _init and _rftun to manipulate the register arrays */ + +static void set_reg_bits(struct reg_pair_t *reg_pair, u8 reg, u8 mask, u8 val) +{ + unsigned int i = 0; + + while (reg_pair[i].reg || reg_pair[i].val) { + if (reg_pair[i].reg == reg) { + reg_pair[i].val &= ~mask; + reg_pair[i].val |= val; + } + i++; + + } + return; +} + +static void copy_reg_bits(struct reg_pair_t *reg_pair1, + struct reg_pair_t *reg_pair2) +{ + unsigned int i, j; + + i = j = 0; + + while (reg_pair1[i].reg || reg_pair1[i].val) { + while (reg_pair2[j].reg || reg_pair2[j].reg) { + if (reg_pair1[i].reg != reg_pair2[j].reg) { + j++; + continue; + } + reg_pair2[j].val = reg_pair1[i].val; + break; + } + i++; + } + return; +} + +/* ------------------------------------------------------------------------- */ + +static void mxl5007t_set_mode_bits(struct mxl5007t_state *state, + enum mxl5007t_mode mode, + s32 if_diff_out_level) +{ + switch (mode) { + case MxL_MODE_OTA_DVBT_ATSC: + set_reg_bits(state->tab_init, 0x32, 0x0f, 0x06); + set_reg_bits(state->tab_init, 0x35, 0xff, 0x0e); + break; + case MxL_MODE_OTA_ISDBT: + set_reg_bits(state->tab_init, 0x32, 0x0f, 0x06); + set_reg_bits(state->tab_init, 0x35, 0xff, 0x12); + break; + case MxL_MODE_OTA_NTSC_PAL_GH: + set_reg_bits(state->tab_init, 0x16, 0x70, 0x00); + set_reg_bits(state->tab_init, 0x32, 0xff, 0x85); + break; + case MxL_MODE_OTA_PAL_IB: + set_reg_bits(state->tab_init, 0x16, 0x70, 0x10); + set_reg_bits(state->tab_init, 0x32, 0xff, 0x85); + break; + case MxL_MODE_OTA_PAL_D_SECAM_KL: + set_reg_bits(state->tab_init, 0x16, 0x70, 0x20); + set_reg_bits(state->tab_init, 0x32, 0xff, 0x85); + break; + case MxL_MODE_CABLE_DIGITAL: + set_reg_bits(state->tab_init_cable, 0x71, 0xff, 0x01); + set_reg_bits(state->tab_init_cable, 0x72, 0xff, + 8 - if_diff_out_level); + set_reg_bits(state->tab_init_cable, 0x74, 0xff, 0x17); + break; + case MxL_MODE_CABLE_NTSC_PAL_GH: + set_reg_bits(state->tab_init, 0x16, 0x70, 0x00); + set_reg_bits(state->tab_init, 0x32, 0xff, 0x85); + set_reg_bits(state->tab_init_cable, 0x71, 0xff, 0x01); + set_reg_bits(state->tab_init_cable, 0x72, 0xff, + 8 - if_diff_out_level); + set_reg_bits(state->tab_init_cable, 0x74, 0xff, 0x17); + break; + case MxL_MODE_CABLE_PAL_IB: + set_reg_bits(state->tab_init, 0x16, 0x70, 0x10); + set_reg_bits(state->tab_init, 0x32, 0xff, 0x85); + set_reg_bits(state->tab_init_cable, 0x71, 0xff, 0x01); + set_reg_bits(state->tab_init_cable, 0x72, 0xff, + 8 - if_diff_out_level); + set_reg_bits(state->tab_init_cable, 0x74, 0xff, 0x17); + break; + case MxL_MODE_CABLE_PAL_D_SECAM_KL: + set_reg_bits(state->tab_init, 0x16, 0x70, 0x20); + set_reg_bits(state->tab_init, 0x32, 0xff, 0x85); + set_reg_bits(state->tab_init_cable, 0x71, 0xff, 0x01); + set_reg_bits(state->tab_init_cable, 0x72, 0xff, + 8 - if_diff_out_level); + set_reg_bits(state->tab_init_cable, 0x74, 0xff, 0x17); + break; + case MxL_MODE_CABLE_SCTE40: + set_reg_bits(state->tab_init_cable, 0x36, 0xff, 0x08); + set_reg_bits(state->tab_init_cable, 0x68, 0xff, 0xbc); + set_reg_bits(state->tab_init_cable, 0x71, 0xff, 0x01); + set_reg_bits(state->tab_init_cable, 0x72, 0xff, + 8 - if_diff_out_level); + set_reg_bits(state->tab_init_cable, 0x74, 0xff, 0x17); + break; + default: + mxl_fail(-EINVAL); + } + return; +} + +static void mxl5007t_set_if_freq_bits(struct mxl5007t_state *state, + enum mxl5007t_if_freq if_freq, + int invert_if) +{ + u8 val; + + switch (if_freq) { + case MxL_IF_4_MHZ: + val = 0x00; + break; + case MxL_IF_4_5_MHZ: + val = 0x20; + break; + case MxL_IF_4_57_MHZ: + val = 0x30; + break; + case MxL_IF_5_MHZ: + val = 0x40; + break; + case MxL_IF_5_38_MHZ: + val = 0x50; + break; + case MxL_IF_6_MHZ: + val = 0x60; + break; + case MxL_IF_6_28_MHZ: + val = 0x70; + break; + case MxL_IF_9_1915_MHZ: + val = 0x80; + break; + case MxL_IF_35_25_MHZ: + val = 0x90; + break; + case MxL_IF_36_15_MHZ: + val = 0xa0; + break; + case MxL_IF_44_MHZ: + val = 0xb0; + break; + default: + mxl_fail(-EINVAL); + return; + } + set_reg_bits(state->tab_init, 0x0c, 0xf0, val); + + /* set inverted IF or normal IF */ + set_reg_bits(state->tab_init, 0x0c, 0x08, invert_if ? 0x08 : 0x00); + + return; +} + +static void mxl5007t_set_xtal_freq_bits(struct mxl5007t_state *state, + enum mxl5007t_xtal_freq xtal_freq) +{ + u8 val; + + switch (xtal_freq) { + case MxL_XTAL_16_MHZ: + val = 0x00; /* select xtal freq & Ref Freq */ + break; + case MxL_XTAL_20_MHZ: + val = 0x11; + break; + case MxL_XTAL_20_25_MHZ: + val = 0x22; + break; + case MxL_XTAL_20_48_MHZ: + val = 0x33; + break; + case MxL_XTAL_24_MHZ: + val = 0x44; + break; + case MxL_XTAL_25_MHZ: + val = 0x55; + break; + case MxL_XTAL_25_14_MHZ: + val = 0x66; + break; + case MxL_XTAL_27_MHZ: + val = 0x77; + break; + case MxL_XTAL_28_8_MHZ: + val = 0x88; + break; + case MxL_XTAL_32_MHZ: + val = 0x99; + break; + case MxL_XTAL_40_MHZ: + val = 0xaa; + break; + case MxL_XTAL_44_MHZ: + val = 0xbb; + break; + case MxL_XTAL_48_MHZ: + val = 0xcc; + break; + case MxL_XTAL_49_3811_MHZ: + val = 0xdd; + break; + default: + mxl_fail(-EINVAL); + return; + } + set_reg_bits(state->tab_init, 0x0b, 0xff, val); + + return; +} + +static struct reg_pair_t *mxl5007t_calc_init_regs(struct mxl5007t_state *state, + enum mxl5007t_mode mode) +{ + struct mxl5007t_config *cfg = state->config; + + memcpy(&state->tab_init, &init_tab, sizeof(init_tab)); + memcpy(&state->tab_init_cable, &init_tab_cable, sizeof(init_tab_cable)); + + mxl5007t_set_mode_bits(state, mode, cfg->if_diff_out_level); + mxl5007t_set_if_freq_bits(state, cfg->if_freq_hz, cfg->invert_if); + mxl5007t_set_xtal_freq_bits(state, cfg->xtal_freq_hz); + + set_reg_bits(state->tab_init, 0x10, 0x40, cfg->loop_thru_enable << 6); + + set_reg_bits(state->tab_init, 0xd8, 0x08, cfg->clk_out_enable << 3); + + set_reg_bits(state->tab_init, 0x10, 0x07, cfg->clk_out_amp); + + /* set IDAC to automatic mode control by AGC */ + set_reg_bits(state->tab_init, 0x12, 0x80, 0x00); + + if (mode >= MxL_MODE_CABLE_DIGITAL) { + copy_reg_bits(state->tab_init, state->tab_init_cable); + return state->tab_init_cable; + } else + return state->tab_init; +} + +/* ------------------------------------------------------------------------- */ + +enum mxl5007t_bw_mhz { + MxL_BW_6MHz = 6, + MxL_BW_7MHz = 7, + MxL_BW_8MHz = 8, +}; + +static void mxl5007t_set_bw_bits(struct mxl5007t_state *state, + enum mxl5007t_bw_mhz bw) +{ + u8 val; + + switch (bw) { + case MxL_BW_6MHz: + val = 0x15; /* set DIG_MODEINDEX, DIG_MODEINDEX_A, + * and DIG_MODEINDEX_CSF */ + break; + case MxL_BW_7MHz: + val = 0x21; + break; + case MxL_BW_8MHz: + val = 0x3f; + break; + default: + mxl_fail(-EINVAL); + return; + } + set_reg_bits(state->tab_rftune, 0x13, 0x3f, val); + + return; +} + +static struct +reg_pair_t *mxl5007t_calc_rf_tune_regs(struct mxl5007t_state *state, + u32 rf_freq, enum mxl5007t_bw_mhz bw) +{ + u32 dig_rf_freq = 0; + u32 temp; + u32 frac_divider = 1000000; + unsigned int i; + + memcpy(&state->tab_rftune, ®_pair_rftune, sizeof(reg_pair_rftune)); + + mxl5007t_set_bw_bits(state, bw); + + /* Convert RF frequency into 16 bits => + * 10 bit integer (MHz) + 6 bit fraction */ + dig_rf_freq = rf_freq / MHz; + + temp = rf_freq % MHz; + + for (i = 0; i < 6; i++) { + dig_rf_freq <<= 1; + frac_divider /= 2; + if (temp > frac_divider) { + temp -= frac_divider; + dig_rf_freq++; + } + } + + /* add to have shift center point by 7.8124 kHz */ + if (temp > 7812) + dig_rf_freq++; + + set_reg_bits(state->tab_rftune, 0x14, 0xff, (u8)dig_rf_freq); + set_reg_bits(state->tab_rftune, 0x15, 0xff, (u8)(dig_rf_freq >> 8)); + + return state->tab_rftune; +} + +/* ------------------------------------------------------------------------- */ + +static int mxl5007t_write_reg(struct mxl5007t_state *state, u8 reg, u8 val) +{ + u8 buf[] = { reg, val }; + struct i2c_msg msg = { .addr = state->i2c_props.addr, .flags = 0, + .buf = buf, .len = 2 }; + int ret; + + ret = i2c_transfer(state->i2c_props.adap, &msg, 1); + if (ret != 1) { + mxl_err("failed!"); + return -EREMOTEIO; + } + return 0; +} + +static int mxl5007t_write_regs(struct mxl5007t_state *state, + struct reg_pair_t *reg_pair) +{ + unsigned int i = 0; + int ret = 0; + + while ((ret == 0) && (reg_pair[i].reg || reg_pair[i].val)) { + ret = mxl5007t_write_reg(state, + reg_pair[i].reg, reg_pair[i].val); + i++; + } + return ret; +} + +static int mxl5007t_read_reg(struct mxl5007t_state *state, u8 reg, u8 *val) +{ + struct i2c_msg msg[] = { + { .addr = state->i2c_props.addr, .flags = 0, + .buf = ®, .len = 1 }, + { .addr = state->i2c_props.addr, .flags = I2C_M_RD, + .buf = val, .len = 1 }, + }; + int ret; + + ret = i2c_transfer(state->i2c_props.adap, msg, 2); + if (ret != 2) { + mxl_err("failed!"); + return -EREMOTEIO; + } + return 0; +} + +static int mxl5007t_soft_reset(struct mxl5007t_state *state) +{ + u8 d = 0xff; + struct i2c_msg msg = { .addr = state->i2c_props.addr, .flags = 0, + .buf = &d, .len = 1 }; + + int ret = i2c_transfer(state->i2c_props.adap, &msg, 1); + + if (ret != 1) { + mxl_err("failed!"); + return -EREMOTEIO; + } + return 0; +} + +static int mxl5007t_tuner_init(struct mxl5007t_state *state, + enum mxl5007t_mode mode) +{ + struct reg_pair_t *init_regs; + int ret; + + ret = mxl5007t_soft_reset(state); + if (mxl_fail(ret)) + goto fail; + + /* calculate initialization reg array */ + init_regs = mxl5007t_calc_init_regs(state, mode); + + ret = mxl5007t_write_regs(state, init_regs); + if (mxl_fail(ret)) + goto fail; + mdelay(1); + + ret = mxl5007t_write_reg(state, 0x2c, 0x35); + mxl_fail(ret); +fail: + return ret; +} + +static int mxl5007t_tuner_rf_tune(struct mxl5007t_state *state, u32 rf_freq_hz, + enum mxl5007t_bw_mhz bw) +{ + struct reg_pair_t *rf_tune_regs; + int ret; + + /* calculate channel change reg array */ + rf_tune_regs = mxl5007t_calc_rf_tune_regs(state, rf_freq_hz, bw); + + ret = mxl5007t_write_regs(state, rf_tune_regs); + if (mxl_fail(ret)) + goto fail; + msleep(3); +fail: + return ret; +} + +/* ------------------------------------------------------------------------- */ + +static int mxl5007t_synth_lock_status(struct mxl5007t_state *state, + int *rf_locked, int *ref_locked) +{ + u8 d; + int ret; + + *rf_locked = 0; + *ref_locked = 0; + + ret = mxl5007t_read_reg(state, 0xcf, &d); + if (mxl_fail(ret)) + goto fail; + + if ((d & 0x0c) == 0x0c) + *rf_locked = 1; + + if ((d & 0x03) == 0x03) + *ref_locked = 1; +fail: + return ret; +} + +static int mxl5007t_check_rf_input_power(struct mxl5007t_state *state, + s32 *rf_input_level) +{ + u8 d1, d2; + int ret; + + ret = mxl5007t_read_reg(state, 0xb7, &d1); + if (mxl_fail(ret)) + goto fail; + + ret = mxl5007t_read_reg(state, 0xbf, &d2); + if (mxl_fail(ret)) + goto fail; + + d2 = d2 >> 4; + if (d2 > 7) + d2 += 0xf0; + + *rf_input_level = (s32)(d1 + d2 - 113); +fail: + return ret; +} + +/* ------------------------------------------------------------------------- */ + +static int mxl5007t_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct mxl5007t_state *state = fe->tuner_priv; + int rf_locked, ref_locked; + s32 rf_input_level; + int ret; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = mxl5007t_synth_lock_status(state, &rf_locked, &ref_locked); + if (mxl_fail(ret)) + goto fail; + mxl_debug("%s%s", rf_locked ? "rf locked " : "", + ref_locked ? "ref locked" : ""); + + ret = mxl5007t_check_rf_input_power(state, &rf_input_level); + if (mxl_fail(ret)) + goto fail; + mxl_debug("rf input power: %d", rf_input_level); +fail: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return ret; +} + +/* ------------------------------------------------------------------------- */ + +static int mxl5007t_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct mxl5007t_state *state = fe->tuner_priv; + enum mxl5007t_bw_mhz bw; + enum mxl5007t_mode mode; + int ret; + u32 freq = params->frequency; + + if (fe->ops.info.type == FE_ATSC) { + switch (params->u.vsb.modulation) { + case VSB_8: + case VSB_16: + mode = MxL_MODE_OTA_DVBT_ATSC; + break; + case QAM_64: + case QAM_256: + mode = MxL_MODE_CABLE_DIGITAL; + break; + default: + mxl_err("modulation not set!"); + return -EINVAL; + } + bw = MxL_BW_6MHz; + } else if (fe->ops.info.type == FE_OFDM) { + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_6_MHZ: + bw = MxL_BW_6MHz; + break; + case BANDWIDTH_7_MHZ: + bw = MxL_BW_7MHz; + break; + case BANDWIDTH_8_MHZ: + bw = MxL_BW_8MHz; + break; + default: + mxl_err("bandwidth not set!"); + return -EINVAL; + } + mode = MxL_MODE_OTA_DVBT_ATSC; + } else { + mxl_err("modulation type not supported!"); + return -EINVAL; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + mutex_lock(&state->lock); + + ret = mxl5007t_tuner_init(state, mode); + if (mxl_fail(ret)) + goto fail; + + ret = mxl5007t_tuner_rf_tune(state, freq, bw); + if (mxl_fail(ret)) + goto fail; + + state->frequency = freq; + state->bandwidth = (fe->ops.info.type == FE_OFDM) ? + params->u.ofdm.bandwidth : 0; +fail: + mutex_unlock(&state->lock); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return ret; +} + +static int mxl5007t_set_analog_params(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct mxl5007t_state *state = fe->tuner_priv; + enum mxl5007t_bw_mhz bw = 0; /* FIXME */ + enum mxl5007t_mode cbl_mode; + enum mxl5007t_mode ota_mode; + char *mode_name; + int ret; + u32 freq = params->frequency * 62500; + +#define cable 1 + if (params->std & V4L2_STD_MN) { + cbl_mode = MxL_MODE_CABLE_NTSC_PAL_GH; + ota_mode = MxL_MODE_OTA_NTSC_PAL_GH; + mode_name = "MN"; + } else if (params->std & V4L2_STD_B) { + cbl_mode = MxL_MODE_CABLE_PAL_IB; + ota_mode = MxL_MODE_OTA_PAL_IB; + mode_name = "B"; + } else if (params->std & V4L2_STD_GH) { + cbl_mode = MxL_MODE_CABLE_NTSC_PAL_GH; + ota_mode = MxL_MODE_OTA_NTSC_PAL_GH; + mode_name = "GH"; + } else if (params->std & V4L2_STD_PAL_I) { + cbl_mode = MxL_MODE_CABLE_PAL_IB; + ota_mode = MxL_MODE_OTA_PAL_IB; + mode_name = "I"; + } else if (params->std & V4L2_STD_DK) { + cbl_mode = MxL_MODE_CABLE_PAL_D_SECAM_KL; + ota_mode = MxL_MODE_OTA_PAL_D_SECAM_KL; + mode_name = "DK"; + } else if (params->std & V4L2_STD_SECAM_L) { + cbl_mode = MxL_MODE_CABLE_PAL_D_SECAM_KL; + ota_mode = MxL_MODE_OTA_PAL_D_SECAM_KL; + mode_name = "L"; + } else if (params->std & V4L2_STD_SECAM_LC) { + cbl_mode = MxL_MODE_CABLE_PAL_D_SECAM_KL; + ota_mode = MxL_MODE_OTA_PAL_D_SECAM_KL; + mode_name = "L'"; + } else { + mode_name = "xx"; + /* FIXME */ + cbl_mode = MxL_MODE_CABLE_NTSC_PAL_GH; + ota_mode = MxL_MODE_OTA_NTSC_PAL_GH; + } + mxl_debug("setting mxl5007 to system %s", mode_name); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + mutex_lock(&state->lock); + + ret = mxl5007t_tuner_init(state, cable ? cbl_mode : ota_mode); + if (mxl_fail(ret)) + goto fail; + + ret = mxl5007t_tuner_rf_tune(state, freq, bw); + if (mxl_fail(ret)) + goto fail; + + state->frequency = freq; + state->bandwidth = 0; +fail: + mutex_unlock(&state->lock); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return ret; +} + +/* ------------------------------------------------------------------------- */ + +static int mxl5007t_init(struct dvb_frontend *fe) +{ + struct mxl5007t_state *state = fe->tuner_priv; + int ret; + u8 d; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = mxl5007t_read_reg(state, 0x05, &d); + if (mxl_fail(ret)) + goto fail; + + ret = mxl5007t_write_reg(state, 0x05, d | 0x01); + mxl_fail(ret); +fail: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return ret; +} + +static int mxl5007t_sleep(struct dvb_frontend *fe) +{ + struct mxl5007t_state *state = fe->tuner_priv; + int ret; + u8 d; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = mxl5007t_read_reg(state, 0x05, &d); + if (mxl_fail(ret)) + goto fail; + + ret = mxl5007t_write_reg(state, 0x05, d & ~0x01); + mxl_fail(ret); +fail: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return ret; +} + +/* ------------------------------------------------------------------------- */ + +static int mxl5007t_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct mxl5007t_state *state = fe->tuner_priv; + *frequency = state->frequency; + return 0; +} + +static int mxl5007t_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct mxl5007t_state *state = fe->tuner_priv; + *bandwidth = state->bandwidth; + return 0; +} + +static int mxl5007t_release(struct dvb_frontend *fe) +{ + struct mxl5007t_state *state = fe->tuner_priv; + + mutex_lock(&mxl5007t_list_mutex); + + if (state) + hybrid_tuner_release_state(state); + + mutex_unlock(&mxl5007t_list_mutex); + + fe->tuner_priv = NULL; + + return 0; +} + +/* ------------------------------------------------------------------------- */ + +static struct dvb_tuner_ops mxl5007t_tuner_ops = { + .info = { + .name = "MaxLinear MxL5007T", + }, + .init = mxl5007t_init, + .sleep = mxl5007t_sleep, + .set_params = mxl5007t_set_params, + .set_analog_params = mxl5007t_set_analog_params, + .get_status = mxl5007t_get_status, + .get_frequency = mxl5007t_get_frequency, + .get_bandwidth = mxl5007t_get_bandwidth, + .release = mxl5007t_release, +}; + +static int mxl5007t_get_chip_id(struct mxl5007t_state *state) +{ + char *name; + int ret; + u8 id; + + ret = mxl5007t_read_reg(state, 0xd3, &id); + if (mxl_fail(ret)) + goto fail; + + switch (id) { + case MxL_5007_V1_F1: + name = "MxL5007.v1.f1"; + break; + case MxL_5007_V1_F2: + name = "MxL5007.v1.f2"; + break; + case MxL_5007_V2_100_F1: + name = "MxL5007.v2.100.f1"; + break; + case MxL_5007_V2_100_F2: + name = "MxL5007.v2.100.f2"; + break; + case MxL_5007_V2_200_F1: + name = "MxL5007.v2.200.f1"; + break; + case MxL_5007_V2_200_F2: + name = "MxL5007.v2.200.f2"; + break; + default: + name = "MxL5007T"; + id = MxL_UNKNOWN_ID; + } + state->chip_id = id; + mxl_info("%s detected @ %d-%04x", name, + i2c_adapter_id(state->i2c_props.adap), + state->i2c_props.addr); + return 0; +fail: + mxl_warn("unable to identify device @ %d-%04x", + i2c_adapter_id(state->i2c_props.adap), + state->i2c_props.addr); + + state->chip_id = MxL_UNKNOWN_ID; + return ret; +} + +struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 addr, + struct mxl5007t_config *cfg) +{ + struct mxl5007t_state *state = NULL; + int instance, ret; + + mutex_lock(&mxl5007t_list_mutex); + instance = hybrid_tuner_request_state(struct mxl5007t_state, state, + hybrid_tuner_instance_list, + i2c, addr, "mxl5007"); + switch (instance) { + case 0: + goto fail; + break; + case 1: + /* new tuner instance */ + state->config = cfg; + + mutex_init(&state->lock); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = mxl5007t_get_chip_id(state); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + /* check return value of mxl5007t_get_chip_id */ + if (mxl_fail(ret)) + goto fail; + break; + default: + /* existing tuner instance */ + break; + } + fe->tuner_priv = state; + mutex_unlock(&mxl5007t_list_mutex); + + memcpy(&fe->ops.tuner_ops, &mxl5007t_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + return fe; +fail: + mutex_unlock(&mxl5007t_list_mutex); + + mxl5007t_release(fe); + return NULL; +} +EXPORT_SYMBOL_GPL(mxl5007t_attach); +MODULE_DESCRIPTION("MaxLinear MxL5007T Silicon IC tuner driver"); +MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/common/tuners/mxl5007t.h b/drivers/media/common/tuners/mxl5007t.h new file mode 100644 index 000000000000..aa3eea0b5262 --- /dev/null +++ b/drivers/media/common/tuners/mxl5007t.h @@ -0,0 +1,104 @@ +/* + * mxl5007t.h - driver for the MaxLinear MxL5007T silicon tuner + * + * Copyright (C) 2008 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, 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 __MXL5007T_H__ +#define __MXL5007T_H__ + +#include "dvb_frontend.h" + +/* ------------------------------------------------------------------------- */ + +enum mxl5007t_if_freq { + MxL_IF_4_MHZ, /* 4000000 */ + MxL_IF_4_5_MHZ, /* 4500000 */ + MxL_IF_4_57_MHZ, /* 4570000 */ + MxL_IF_5_MHZ, /* 5000000 */ + MxL_IF_5_38_MHZ, /* 5380000 */ + MxL_IF_6_MHZ, /* 6000000 */ + MxL_IF_6_28_MHZ, /* 6280000 */ + MxL_IF_9_1915_MHZ, /* 9191500 */ + MxL_IF_35_25_MHZ, /* 35250000 */ + MxL_IF_36_15_MHZ, /* 36150000 */ + MxL_IF_44_MHZ, /* 44000000 */ +}; + +enum mxl5007t_xtal_freq { + MxL_XTAL_16_MHZ, /* 16000000 */ + MxL_XTAL_20_MHZ, /* 20000000 */ + MxL_XTAL_20_25_MHZ, /* 20250000 */ + MxL_XTAL_20_48_MHZ, /* 20480000 */ + MxL_XTAL_24_MHZ, /* 24000000 */ + MxL_XTAL_25_MHZ, /* 25000000 */ + MxL_XTAL_25_14_MHZ, /* 25140000 */ + MxL_XTAL_27_MHZ, /* 27000000 */ + MxL_XTAL_28_8_MHZ, /* 28800000 */ + MxL_XTAL_32_MHZ, /* 32000000 */ + MxL_XTAL_40_MHZ, /* 40000000 */ + MxL_XTAL_44_MHZ, /* 44000000 */ + MxL_XTAL_48_MHZ, /* 48000000 */ + MxL_XTAL_49_3811_MHZ, /* 49381100 */ +}; + +enum mxl5007t_clkout_amp { + MxL_CLKOUT_AMP_0_94V = 0, + MxL_CLKOUT_AMP_0_53V = 1, + MxL_CLKOUT_AMP_0_37V = 2, + MxL_CLKOUT_AMP_0_28V = 3, + MxL_CLKOUT_AMP_0_23V = 4, + MxL_CLKOUT_AMP_0_20V = 5, + MxL_CLKOUT_AMP_0_17V = 6, + MxL_CLKOUT_AMP_0_15V = 7, +}; + +struct mxl5007t_config { + s32 if_diff_out_level; + enum mxl5007t_clkout_amp clk_out_amp; + enum mxl5007t_xtal_freq xtal_freq_hz; + enum mxl5007t_if_freq if_freq_hz; + unsigned int invert_if:1; + unsigned int loop_thru_enable:1; + unsigned int clk_out_enable:1; +}; + +#if defined(CONFIG_MEDIA_TUNER_MXL5007T) || (defined(CONFIG_MEDIA_TUNER_MXL5007T_MODULE) && defined(MODULE)) +extern struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 addr, + struct mxl5007t_config *cfg); +#else +static inline struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + u8 addr, + struct mxl5007t_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* __MXL5007T_H__ */ + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ + diff --git a/drivers/media/common/tuners/tda9887.c b/drivers/media/common/tuners/tda9887.c index a0545ba957b0..72abf0b73486 100644 --- a/drivers/media/common/tuners/tda9887.c +++ b/drivers/media/common/tuners/tda9887.c @@ -6,7 +6,7 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/delay.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> #include <media/tuner.h> #include "tuner-i2c.h" diff --git a/drivers/media/common/tuners/tuner-simple.c b/drivers/media/common/tuners/tuner-simple.c index 266c255cf0d8..597e47f5d69c 100644 --- a/drivers/media/common/tuners/tuner-simple.c +++ b/drivers/media/common/tuners/tuner-simple.c @@ -6,7 +6,7 @@ */ #include <linux/delay.h> #include <linux/i2c.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/tuner.h> #include <media/v4l2-common.h> #include <media/tuner-types.h> diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig index 7588db1319d0..7e9c090fc04e 100644 --- a/drivers/media/dvb/bt8xx/Kconfig +++ b/drivers/media/dvb/bt8xx/Kconfig @@ -1,7 +1,6 @@ config DVB_BT8XX tristate "BT8xx based PCI cards" depends on DVB_CORE && PCI && I2C && VIDEO_BT848 - depends on HOTPLUG # due to FW_LOADER select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_SP887X if !DVB_FE_CUSTOMISE select DVB_NXT6000 if !DVB_FE_CUSTOMISE @@ -10,7 +9,6 @@ config DVB_BT8XX select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE - select FW_LOADER help Support for PCI cards based on the Bt8xx PCI bridge. Examples are the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards, diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c index e208a60c048a..e7132770a3bf 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.c +++ b/drivers/media/dvb/dvb-core/dvbdev.c @@ -233,9 +233,9 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, mutex_unlock(&dvbdev_register_lock); - clsdev = device_create(dvb_class, adap->device, + clsdev = device_create_drvdata(dvb_class, adap->device, MKDEV(DVB_MAJOR, nums2minor(adap->num, type, id)), - "dvb%d.%s%d", adap->num, dnames[type], id); + NULL, "dvb%d.%s%d", adap->num, dnames[type], id); if (IS_ERR(clsdev)) { printk(KERN_ERR "%s: failed to create device dvb%d.%s%d (%ld)\n", __func__, adap->num, dnames[type], id, PTR_ERR(clsdev)); diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index a577c0f89f67..e84152b7576d 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -1,8 +1,6 @@ config DVB_USB tristate "Support for various USB DVB devices" depends on DVB_CORE && USB && I2C && INPUT - depends on HOTPLUG # due to FW_LOADER - select FW_LOADER help By enabling this you will be able to choose the various supported USB1.1 and USB2.0 DVB devices. @@ -246,6 +244,14 @@ config DVB_USB_AF9005_REMOTE Say Y here to support the default remote control decoding for the Afatech AF9005 based receiver. +config DVB_USB_DW2102 + tristate "DvbWorld 2102 DVB-S USB2.0 receiver" + depends on DVB_USB + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_PLL if !DVB_FE_CUSTOMISE + help + Say Y here to support the DvbWorld 2102 DVB-S USB2.0 receiver. + config DVB_USB_ANYSEE tristate "Anysee DVB-T/C USB2.0 support" depends on DVB_USB diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index 44c11e45e564..e206f1ea0027 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -64,6 +64,9 @@ obj-$(CONFIG_DVB_USB_AF9005_REMOTE) += dvb-usb-af9005-remote.o dvb-usb-anysee-objs = anysee.o obj-$(CONFIG_DVB_USB_ANYSEE) += dvb-usb-anysee.o +dvb-usb-dw2102-objs = dw2102.o +obj-$(CONFIG_DVB_USB_DW2102) += dvb-usb-dw2102.o + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ # due to tuner-xc3028 EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c index adfd4fc82efd..2f408d2e1ef3 100644 --- a/drivers/media/dvb/dvb-usb/anysee.c +++ b/drivers/media/dvb/dvb-usb/anysee.c @@ -43,7 +43,7 @@ module_param_named(debug, dvb_usb_anysee_debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -struct mutex anysee_usb_mutex; +static struct mutex anysee_usb_mutex; static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen, u8 *rbuf, u8 rlen) diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index e5238b31e946..029b437caf9a 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -204,5 +204,6 @@ #define USB_PID_ASUS_U3000 0x171f #define USB_PID_ASUS_U3100 0x173f #define USB_PID_YUAN_EC372S 0x1edc +#define USB_PID_DW2102 0x2102 #endif diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c new file mode 100644 index 000000000000..a4d898b44e55 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -0,0 +1,425 @@ +/* DVB USB framework compliant Linux driver for the DVBWorld DVB-S 2102 Card +* +* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) +* +* 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 <linux/version.h> +#include "dw2102.h" +#include "stv0299.h" +#include "z0194a.h" + +#ifndef USB_PID_DW2102 +#define USB_PID_DW2102 0x2102 +#endif + +#define DW2102_READ_MSG 0 +#define DW2102_WRITE_MSG 1 + +#define REG_1F_SYMBOLRATE_BYTE0 0x1f +#define REG_20_SYMBOLRATE_BYTE1 0x20 +#define REG_21_SYMBOLRATE_BYTE2 0x21 + +#define DW2102_VOLTAGE_CTRL (0x1800) +#define DW2102_RC_QUERY (0x1a00) + +struct dw2102_state { + u32 last_key_pressed; +}; +struct dw2102_rc_keys { + u32 keycode; + u32 event; +}; + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int dw2102_op_rw(struct usb_device *dev, u8 request, u16 value, + u8 *data, u16 len, int flags) +{ + int ret; + u8 u8buf[len]; + + unsigned int pipe = (flags == DW2102_READ_MSG) ? + usb_rcvctrlpipe(dev, 0) : usb_sndctrlpipe(dev, 0); + u8 request_type = (flags == DW2102_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT; + + if (flags == DW2102_WRITE_MSG) + memcpy(u8buf, data, len); + ret = usb_control_msg(dev, pipe, request, + request_type | USB_TYPE_VENDOR, value, 0 , u8buf, len, 2000); + + if (flags == DW2102_READ_MSG) + memcpy(data, u8buf, len); + return ret; +} + +/* I2C */ + +static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ +struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i = 0, ret = 0; + u8 buf6[] = {0x2c, 0x05, 0xc0, 0, 0, 0, 0}; + u8 request; + u16 value; + + if (!d) + return -ENODEV; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + switch (num) { + case 2: + /* read stv0299 register */ + request = 0xb5; + value = msg[0].buf[0];/* register */ + for (i = 0; i < msg[1].len; i++) { + value = value + i; + ret = dw2102_op_rw(d->udev, 0xb5, + value, buf6, 2, DW2102_READ_MSG); + msg[1].buf[i] = buf6[0]; + + } + break; + case 1: + switch (msg[0].addr) { + case 0x68: + /* write to stv0299 register */ + buf6[0] = 0x2a; + buf6[1] = msg[0].buf[0]; + buf6[2] = msg[0].buf[1]; + ret = dw2102_op_rw(d->udev, 0xb2, + 0, buf6, 3, DW2102_WRITE_MSG); + break; + case 0x60: + if (msg[0].flags == 0) { + /* write to tuner pll */ + buf6[0] = 0x2c; + buf6[1] = 5; + buf6[2] = 0xc0; + buf6[3] = msg[0].buf[0]; + buf6[4] = msg[0].buf[1]; + buf6[5] = msg[0].buf[2]; + buf6[6] = msg[0].buf[3]; + ret = dw2102_op_rw(d->udev, 0xb2, + 0, buf6, 7, DW2102_WRITE_MSG); + } else { + /* write to tuner pll */ + ret = dw2102_op_rw(d->udev, 0xb5, + 0, buf6, 1, DW2102_READ_MSG); + msg[0].buf[0] = buf6[0]; + } + break; + case (DW2102_RC_QUERY): + ret = dw2102_op_rw(d->udev, 0xb8, + 0, buf6, 2, DW2102_READ_MSG); + msg[0].buf[0] = buf6[0]; + msg[0].buf[1] = buf6[1]; + break; + case (DW2102_VOLTAGE_CTRL): + buf6[0] = 0x30; + buf6[1] = msg[0].buf[0]; + ret = dw2102_op_rw(d->udev, 0xb2, + 0, buf6, 2, DW2102_WRITE_MSG); + break; + } + + break; + } + + mutex_unlock(&d->i2c_mutex); + return num; +} + +static u32 dw2102_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm dw2102_i2c_algo = { + .master_xfer = dw2102_i2c_transfer, + .functionality = dw2102_i2c_func, +}; + +static int dw2102_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 = DW2102_VOLTAGE_CTRL, .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].buf = command_18v; + i2c_transfer(&udev_adap->dev->i2c_adap, msg, 1); + return 0; +} + +static int dw2102_frontend_attach(struct dvb_usb_adapter *d) +{ + d->fe = dvb_attach(stv0299_attach, &sharp_z0194a_config, + &d->dev->i2c_adap); + if (d->fe != NULL) { + d->fe->ops.set_voltage = dw2102_set_voltage; + info("Attached stv0299!\n"); + return 0; + } + return -EIO; +} + +static int dw2102_tuner_attach(struct dvb_usb_adapter *adap) +{ + dvb_attach(dvb_pll_attach, adap->fe, 0x60, + &adap->dev->i2c_adap, DVB_PLL_OPERA1); + return 0; +} + +static struct dvb_usb_rc_key dw2102_rc_keys[] = { + { 0xf8, 0x0a, KEY_Q }, /*power*/ + { 0xf8, 0x0c, KEY_M }, /*mute*/ + { 0xf8, 0x11, KEY_1 }, + { 0xf8, 0x12, KEY_2 }, + { 0xf8, 0x13, KEY_3 }, + { 0xf8, 0x14, KEY_4 }, + { 0xf8, 0x15, KEY_5 }, + { 0xf8, 0x16, KEY_6 }, + { 0xf8, 0x17, KEY_7 }, + { 0xf8, 0x18, KEY_8 }, + { 0xf8, 0x19, KEY_9 }, + { 0xf8, 0x10, KEY_0 }, + { 0xf8, 0x1c, KEY_PAGEUP }, /*ch+*/ + { 0xf8, 0x0f, KEY_PAGEDOWN }, /*ch-*/ + { 0xf8, 0x1a, KEY_O }, /*vol+*/ + { 0xf8, 0x0e, KEY_Z }, /*vol-*/ + { 0xf8, 0x04, KEY_R }, /*rec*/ + { 0xf8, 0x09, KEY_D }, /*fav*/ + { 0xf8, 0x08, KEY_BACKSPACE }, /*rewind*/ + { 0xf8, 0x07, KEY_A }, /*fast*/ + { 0xf8, 0x0b, KEY_P }, /*pause*/ + { 0xf8, 0x02, KEY_ESC }, /*cancel*/ + { 0xf8, 0x03, KEY_G }, /*tab*/ + { 0xf8, 0x00, KEY_UP }, /*up*/ + { 0xf8, 0x1f, KEY_ENTER }, /*ok*/ + { 0xf8, 0x01, KEY_DOWN }, /*down*/ + { 0xf8, 0x05, KEY_C }, /*cap*/ + { 0xf8, 0x06, KEY_S }, /*stop*/ + { 0xf8, 0x40, KEY_F }, /*full*/ + { 0xf8, 0x1e, KEY_W }, /*tvmode*/ + { 0xf8, 0x1b, KEY_B }, /*recall*/ + +}; + + + +static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + struct dw2102_state *st = d->priv; + u8 key[2]; + struct i2c_msg msg[] = { + {.addr = DW2102_RC_QUERY, .flags = I2C_M_RD, .buf = key, + .len = 2}, + }; + int i; + + *state = REMOTE_NO_KEY_PRESSED; + if (dw2102_i2c_transfer(&d->i2c_adap, msg, 1) == 1) { + for (i = 0; i < ARRAY_SIZE(dw2102_rc_keys); i++) { + if (dw2102_rc_keys[i].data == msg[0].buf[0]) { + *state = REMOTE_KEY_PRESSED; + *event = dw2102_rc_keys[i].event; + st->last_key_pressed = + dw2102_rc_keys[i].event; + break; + } + st->last_key_pressed = 0; + } + } + /* info("key: %x %x\n",key[0],key[1]); */ + return 0; +} + +static struct usb_device_id dw2102_table[] = { + {USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW2102)}, + {USB_DEVICE(USB_VID_CYPRESS, 0x2101)}, + { } +}; + +MODULE_DEVICE_TABLE(usb, dw2102_table); + +static int dw2102_load_firmware(struct usb_device *dev, + const struct firmware *frmwr) +{ + u8 *b, *p; + int ret = 0, i; + u8 reset; + u8 reset16 [] = {0, 0, 0, 0, 0, 0, 0}; + const struct firmware *fw; + const char *filename = "dvb-usb-dw2101.fw"; + switch (dev->descriptor.idProduct) { + case 0x2101: + ret = request_firmware(&fw, filename, &dev->dev); + if (ret != 0) { + err("did not find the firmware file. (%s) " + "Please see linux/Documentation/dvb/ for more details " + "on firmware-problems.", filename); + return ret; + } + break; + case USB_PID_DW2102: + fw = frmwr; + break; + } + info("start downloading DW2102 firmware"); + p = kmalloc(fw->size, GFP_KERNEL); + reset = 1; + /*stop the CPU*/ + dw2102_op_rw(dev, 0xa0, 0x7f92, &reset, 1, DW2102_WRITE_MSG); + dw2102_op_rw(dev, 0xa0, 0xe600, &reset, 1, DW2102_WRITE_MSG); + + if (p != NULL) { + memcpy(p, fw->data, fw->size); + for (i = 0; i < fw->size; i += 0x40) { + b = (u8 *) p + i; + if (dw2102_op_rw + (dev, 0xa0, i, b , 0x40, + DW2102_WRITE_MSG) != 0x40 + ) { + err("error while transferring firmware"); + ret = -EINVAL; + break; + } + } + /* restart the CPU */ + reset = 0; + if (ret || dw2102_op_rw + (dev, 0xa0, 0x7f92, &reset, 1, + DW2102_WRITE_MSG) != 1) { + err("could not restart the USB controller CPU."); + ret = -EINVAL; + } + if (ret || dw2102_op_rw + (dev, 0xa0, 0xe600, &reset, 1, + DW2102_WRITE_MSG) != 1) { + err("could not restart the USB controller CPU."); + ret = -EINVAL; + } + /* init registers */ + switch (dev->descriptor.idProduct) { + case USB_PID_DW2102: + dw2102_op_rw + (dev, 0xbf, 0x0040, &reset, 0, + DW2102_WRITE_MSG); + dw2102_op_rw + (dev, 0xb9, 0x0000, &reset16[0], 2, + DW2102_READ_MSG); + break; + case 0x2101: + dw2102_op_rw + (dev, 0xbc, 0x0030, &reset16[0], 2, + DW2102_READ_MSG); + dw2102_op_rw + (dev, 0xba, 0x0000, &reset16[0], 7, + DW2102_READ_MSG); + dw2102_op_rw + (dev, 0xba, 0x0000, &reset16[0], 7, + DW2102_READ_MSG); + dw2102_op_rw + (dev, 0xb9, 0x0000, &reset16[0], 2, + DW2102_READ_MSG); + break; + } + kfree(p); + } + return ret; +} + +static struct dvb_usb_device_properties dw2102_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-dw2102.fw", + .size_of_priv = sizeof(struct dw2102_state), + .no_reconnect = 1, + + .i2c_algo = &dw2102_i2c_algo, + .rc_key_map = dw2102_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dw2102_rc_keys), + .rc_interval = 150, + .rc_query = dw2102_rc_query, + + .generic_bulk_ctrl_endpoint = 0x81, + /* parameter for the MPEG2-data transfer */ + .num_adapters = 1, + .download_firmware = dw2102_load_firmware, + .adapter = { + { + .frontend_attach = dw2102_frontend_attach, + .streaming_ctrl = NULL, + .tuner_attach = dw2102_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + } + }, + .num_device_descs = 2, + .devices = { + {"DVBWorld DVB-S 2102 USB2.0", + {&dw2102_table[0], NULL}, + {NULL}, + }, + {"DVBWorld DVB-S 2101 USB2.0", + {&dw2102_table[1], NULL}, + {NULL}, + }, + } +}; + +static int dw2102_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf, &dw2102_properties, + THIS_MODULE, NULL, adapter_nr); +} + +static struct usb_driver dw2102_driver = { + .name = "dw2102", + .probe = dw2102_probe, + .disconnect = dvb_usb_device_exit, + .id_table = dw2102_table, +}; + +static int __init dw2102_module_init(void) +{ + int ret = usb_register(&dw2102_driver); + if (ret) + err("usb_register failed. Error number %d", ret); + + return ret; +} + +static void __exit dw2102_module_exit(void) +{ + usb_deregister(&dw2102_driver); +} + +module_init(dw2102_module_init); +module_exit(dw2102_module_exit); + +MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by"); +MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101 2102 USB2.0 device"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/dw2102.h b/drivers/media/dvb/dvb-usb/dw2102.h new file mode 100644 index 000000000000..7a310f906837 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/dw2102.h @@ -0,0 +1,9 @@ +#ifndef _DW2102_H_ +#define _DW2102_H_ + +#define DVB_USB_LOG_PREFIX "dw2102" +#include "dvb-usb.h" + +extern int dvb_usb_dw2102_debug; +#define deb_xfer(args...) dprintk(dvb_usb_dw2102_debug, 0x02, args) +#endif diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index c20553c4da1f..574dffe91b68 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -97,9 +97,8 @@ comment "DVB-T (terrestrial) frontends" config DVB_SP8870 tristate "Spase sp8870 based" - depends on DVB_CORE && I2C && HOTPLUG + depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE - select FW_LOADER help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -110,9 +109,8 @@ config DVB_SP8870 config DVB_SP887X tristate "Spase sp887x based" - depends on DVB_CORE && I2C && HOTPLUG + depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE - select FW_LOADER help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -135,6 +133,20 @@ config DVB_CX22702 help A DVB-T tuner module. Say Y when you want to support this frontend. +config DVB_DRX397XD + tristate "Micronas DRX3975D/DRX3977D based" + depends on DVB_CORE && I2C && HOTPLUG + default m if DVB_FE_CUSTOMISE + select FW_LOADER + help + A DVB-T tuner module. Say Y when you want to support this frontend. + + TODO: + This driver needs external firmware. Please use the command + "<kerneldir>/Documentation/dvb/get_dvb_firmware drx397xD" to + download/extract them, and then copy them to /usr/lib/hotplug/firmware + or /lib/firmware (depending on configuration of firmware hotplug). + config DVB_L64781 tristate "LSI L64781" depends on DVB_CORE && I2C @@ -144,9 +156,8 @@ config DVB_L64781 config DVB_TDA1004X tristate "Philips TDA10045H/TDA10046H based" - depends on DVB_CORE && I2C && HOTPLUG + depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE - select FW_LOADER help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -211,9 +222,8 @@ config DVB_DIB7000P config DVB_TDA10048 tristate "Philips TDA10048HN based" - depends on DVB_CORE && I2C && HOTPLUG + depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE - select FW_LOADER help A DVB-T tuner module. Say Y when you want to support this frontend. @@ -253,9 +263,8 @@ comment "ATSC (North American/Korean Terrestrial/Cable DTV) frontends" config DVB_NXT200X tristate "NxtWave Communications NXT2002/NXT2004 based" - depends on DVB_CORE && I2C && HOTPLUG + depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE - select FW_LOADER help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. @@ -268,9 +277,8 @@ config DVB_NXT200X config DVB_OR51211 tristate "Oren OR51211 based" - depends on DVB_CORE && I2C && HOTPLUG + depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE - select FW_LOADER help An ATSC 8VSB tuner module. Say Y when you want to support this frontend. @@ -281,9 +289,8 @@ config DVB_OR51211 config DVB_OR51132 tristate "Oren OR51132 based" - depends on DVB_CORE && I2C && HOTPLUG + depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE - select FW_LOADER help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. @@ -297,9 +304,8 @@ config DVB_OR51132 config DVB_BCM3510 tristate "Broadcom BCM3510" - depends on DVB_CORE && I2C && HOTPLUG + depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE - select FW_LOADER help An ATSC 8VSB/16VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index a89dc0fc4c6f..028da55611c0 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_DVB_NXT6000) += nxt6000.o obj-$(CONFIG_DVB_MT352) += mt352.o obj-$(CONFIG_DVB_ZL10353) += zl10353.o obj-$(CONFIG_DVB_CX22702) += cx22702.o +obj-$(CONFIG_DVB_DRX397XD) += drx397xD.o obj-$(CONFIG_DVB_TDA10021) += tda10021.o obj-$(CONFIG_DVB_TDA10023) += tda10023.o obj-$(CONFIG_DVB_STV0297) += stv0297.o diff --git a/drivers/media/dvb/frontends/drx397xD.c b/drivers/media/dvb/frontends/drx397xD.c new file mode 100644 index 000000000000..3cbed874a6f8 --- /dev/null +++ b/drivers/media/dvb/frontends/drx397xD.c @@ -0,0 +1,1504 @@ +/* + * Driver for Micronas drx397xD demodulator + * + * Copyright (C) 2007 Henk Vergonet <Henk.Vergonet@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#define DEBUG /* uncomment if you want debugging output */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/firmware.h> +#include <asm/div64.h> + +#include "dvb_frontend.h" +#include "drx397xD.h" + +static const char mod_name[] = "drx397xD"; + +#define MAX_CLOCK_DRIFT 200 /* maximal 200 PPM allowed */ + +#define F_SET_0D0h 1 +#define F_SET_0D4h 2 + +typedef enum fw_ix { +#define _FW_ENTRY(a, b) b +#include "drx397xD_fw.h" +} fw_ix_t; + +/* chip specifics */ +struct drx397xD_state { + struct i2c_adapter *i2c; + struct dvb_frontend frontend; + struct drx397xD_config config; + fw_ix_t chip_rev; + int flags; + u32 bandwidth_parm; /* internal bandwidth conversions */ + u32 f_osc; /* w90: actual osc frequency [Hz] */ +}; + +/******************************************************************************* + * Firmware + ******************************************************************************/ + +static const char *blob_name[] = { +#define _BLOB_ENTRY(a, b) a +#include "drx397xD_fw.h" +}; + +typedef enum blob_ix { +#define _BLOB_ENTRY(a, b) b +#include "drx397xD_fw.h" +} blob_ix_t; + +static struct { + const char *name; + const struct firmware *file; + rwlock_t lock; + int refcnt; + const u8 *data[ARRAY_SIZE(blob_name)]; +} fw[] = { +#define _FW_ENTRY(a, b) { \ + .name = a, \ + .file = 0, \ + .lock = RW_LOCK_UNLOCKED, \ + .refcnt = 0, \ + .data = { } } +#include "drx397xD_fw.h" +}; + +/* use only with writer lock aquired */ +static void _drx_release_fw(struct drx397xD_state *s, fw_ix_t ix) +{ + memset(&fw[ix].data[0], 0, sizeof(fw[0].data)); + if (fw[ix].file) + release_firmware(fw[ix].file); +} + +static void drx_release_fw(struct drx397xD_state *s) +{ + fw_ix_t ix = s->chip_rev; + + pr_debug("%s\n", __FUNCTION__); + + write_lock(&fw[ix].lock); + if (fw[ix].refcnt) { + fw[ix].refcnt--; + if (fw[ix].refcnt == 0) + _drx_release_fw(s, ix); + } + write_unlock(&fw[ix].lock); +} + +static int drx_load_fw(struct drx397xD_state *s, fw_ix_t ix) +{ + const u8 *data; + size_t size, len; + int i = 0, j, rc = -EINVAL; + + pr_debug("%s\n", __FUNCTION__); + + if (ix < 0 || ix >= ARRAY_SIZE(fw)) + return -EINVAL; + s->chip_rev = ix; + + write_lock(&fw[ix].lock); + if (fw[ix].file) { + rc = 0; + goto exit_ok; + } + memset(&fw[ix].data[0], 0, sizeof(fw[0].data)); + + if (request_firmware(&fw[ix].file, fw[ix].name, &s->i2c->dev) != 0) { + printk(KERN_ERR "%s: Firmware \"%s\" not available\n", + mod_name, fw[ix].name); + rc = -ENOENT; + goto exit_err; + } + + if (!fw[ix].file->data || fw[ix].file->size < 10) + goto exit_corrupt; + + data = fw[ix].file->data; + size = fw[ix].file->size; + + if (data[i++] != 2) /* check firmware version */ + goto exit_corrupt; + + do { + switch (data[i++]) { + case 0x00: /* bytecode */ + if (i >= size) + break; + i += data[i]; + case 0x01: /* reset */ + case 0x02: /* sleep */ + i++; + break; + case 0xfe: /* name */ + len = strnlen(&data[i], size - i); + if (i + len + 1 >= size) + goto exit_corrupt; + if (data[i + len + 1] != 0) + goto exit_corrupt; + for (j = 0; j < ARRAY_SIZE(blob_name); j++) { + if (strcmp(blob_name[j], &data[i]) == 0) { + fw[ix].data[j] = &data[i + len + 1]; + pr_debug("Loading %s\n", blob_name[j]); + } + } + i += len + 1; + break; + case 0xff: /* file terminator */ + if (i == size) { + rc = 0; + goto exit_ok; + } + default: + goto exit_corrupt; + } + } while (i < size); + exit_corrupt: + printk(KERN_ERR "%s: Firmware is corrupt\n", mod_name); + exit_err: + _drx_release_fw(s, ix); + fw[ix].refcnt--; + exit_ok: + fw[ix].refcnt++; + write_unlock(&fw[ix].lock); + return rc; +} + +/******************************************************************************* + * i2c bus IO + ******************************************************************************/ + +static int write_fw(struct drx397xD_state *s, blob_ix_t ix) +{ + struct i2c_msg msg = {.addr = s->config.demod_address,.flags = 0 }; + const u8 *data; + int len, rc = 0, i = 0; + + if (ix < 0 || ix >= ARRAY_SIZE(blob_name)) { + pr_debug("%s drx_fw_ix_t out of range\n", __FUNCTION__); + return -EINVAL; + } + pr_debug("%s %s\n", __FUNCTION__, blob_name[ix]); + + read_lock(&fw[s->chip_rev].lock); + data = fw[s->chip_rev].data[ix]; + if (!data) { + rc = -EINVAL; + goto exit_rc; + } + + for (;;) { + switch (data[i++]) { + case 0: /* bytecode */ + len = data[i++]; + msg.len = len; + msg.buf = (__u8 *) &data[i]; + if (i2c_transfer(s->i2c, &msg, 1) != 1) { + rc = -EIO; + goto exit_rc; + } + i += len; + break; + case 1: /* reset */ + case 2: /* sleep */ + i++; + break; + default: + goto exit_rc; + } + } + exit_rc: + read_unlock(&fw[s->chip_rev].lock); + return 0; +} + +/* Function is not endian safe, use the RD16 wrapper below */ +static int _read16(struct drx397xD_state *s, u32 i2c_adr) +{ + int rc; + u8 a[4]; + u16 v; + struct i2c_msg msg[2] = { + { + .addr = s->config.demod_address, + .flags = 0, + .buf = a, + .len = sizeof(a) + } + , { + .addr = s->config.demod_address, + .flags = I2C_M_RD, + .buf = (u8 *) & v, + .len = sizeof(v) + } + }; + + *(u32 *) a = i2c_adr; + + rc = i2c_transfer(s->i2c, msg, 2); + if (rc != 2) + return -EIO; + + return le16_to_cpu(v); +} + +/* Function is not endian safe, use the WR16.. wrappers below */ +static int _write16(struct drx397xD_state *s, u32 i2c_adr, u16 val) +{ + u8 a[6]; + int rc; + struct i2c_msg msg = { + .addr = s->config.demod_address, + .flags = 0, + .buf = a, + .len = sizeof(a) + }; + + *(u32 *) a = i2c_adr; + *(u16 *) & a[4] = val; + + rc = i2c_transfer(s->i2c, &msg, 1); + if (rc != 1) + return -EIO; + return 0; +} + +#define WR16(ss,adr, val) \ + _write16(ss, I2C_ADR_C0(adr), cpu_to_le16(val)) +#define WR16_E0(ss,adr, val) \ + _write16(ss, I2C_ADR_E0(adr), cpu_to_le16(val)) +#define RD16(ss,adr) \ + _read16(ss, I2C_ADR_C0(adr)) + +#define EXIT_RC( cmd ) if ( (rc = (cmd)) < 0) goto exit_rc + +/******************************************************************************* + * Tuner callback + ******************************************************************************/ + +static int PLL_Set(struct drx397xD_state *s, + struct dvb_frontend_parameters *fep, int *df_tuner) +{ + struct dvb_frontend *fe = &s->frontend; + u32 f_tuner, f = fep->frequency; + int rc; + + pr_debug("%s\n", __FUNCTION__); + + if ((f > s->frontend.ops.tuner_ops.info.frequency_max) || + (f < s->frontend.ops.tuner_ops.info.frequency_min)) + return -EINVAL; + + *df_tuner = 0; + if (!s->frontend.ops.tuner_ops.set_params || + !s->frontend.ops.tuner_ops.get_frequency) + return -ENOSYS; + + rc = s->frontend.ops.tuner_ops.set_params(fe, fep); + if (rc < 0) + return rc; + + rc = s->frontend.ops.tuner_ops.get_frequency(fe, &f_tuner); + if (rc < 0) + return rc; + + *df_tuner = f_tuner - f; + pr_debug("%s requested %d [Hz] tuner %d [Hz]\n", __FUNCTION__, f, + f_tuner); + + return 0; +} + +/******************************************************************************* + * Demodulator helper functions + ******************************************************************************/ + +static int SC_WaitForReady(struct drx397xD_state *s) +{ + int cnt = 1000; + int rc; + + pr_debug("%s\n", __FUNCTION__); + + while (cnt--) { + rc = RD16(s, 0x820043); + if (rc == 0) + return 0; + } + return -1; +} + +static int SC_SendCommand(struct drx397xD_state *s, int cmd) +{ + int rc; + + pr_debug("%s\n", __FUNCTION__); + + WR16(s, 0x820043, cmd); + SC_WaitForReady(s); + rc = RD16(s, 0x820042); + if ((rc & 0xffff) == 0xffff) + return -1; + return 0; +} + +static int HI_Command(struct drx397xD_state *s, u16 cmd) +{ + int rc, cnt = 1000; + + pr_debug("%s\n", __FUNCTION__); + + rc = WR16(s, 0x420032, cmd); + if (rc < 0) + return rc; + + do { + rc = RD16(s, 0x420032); + if (rc == 0) { + rc = RD16(s, 0x420031); + return rc; + } + if (rc < 0) + return rc; + } while (--cnt); + return rc; +} + +static int HI_CfgCommand(struct drx397xD_state *s) +{ + + pr_debug("%s\n", __FUNCTION__); + + WR16(s, 0x420033, 0x3973); + WR16(s, 0x420034, s->config.w50); // code 4, log 4 + WR16(s, 0x420035, s->config.w52); // code 15, log 9 + WR16(s, 0x420036, s->config.demod_address << 1); + WR16(s, 0x420037, s->config.w56); // code (set_i2c ?? initX 1 ), log 1 +// WR16(s, 0x420033, 0x3973); + if ((s->config.w56 & 8) == 0) + return HI_Command(s, 3); + return WR16(s, 0x420032, 0x3); +} + +static const u8 fastIncrDecLUT_15273[] = { + 0x0e, 0x0f, 0x0f, 0x10, 0x11, 0x12, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1d, 0x1f +}; + +static const u8 slowIncrDecLUT_15272[] = { + 3, 4, 4, 5, 6 +}; + +static int SetCfgIfAgc(struct drx397xD_state *s, struct drx397xD_CfgIfAgc *agc) +{ + u16 w06 = agc->w06; + u16 w08 = agc->w08; + u16 w0A = agc->w0A; + u16 w0C = agc->w0C; + int quot, rem, i, rc = -EINVAL; + + pr_debug("%s\n", __FUNCTION__); + + if (agc->w04 > 0x3ff) + goto exit_rc; + + if (agc->d00 == 1) { + EXIT_RC(RD16(s, 0x0c20010)); + rc &= ~0x10; + EXIT_RC(WR16(s, 0x0c20010, rc)); + return WR16(s, 0x0c20030, agc->w04 & 0x7ff); + } + + if (agc->d00 != 0) + goto exit_rc; + if (w0A < w08) + goto exit_rc; + if (w0A > 0x3ff) + goto exit_rc; + if (w0C > 0x3ff) + goto exit_rc; + if (w06 > 0x3ff) + goto exit_rc; + + EXIT_RC(RD16(s, 0x0c20010)); + rc |= 0x10; + EXIT_RC(WR16(s, 0x0c20010, rc)); + + EXIT_RC(WR16(s, 0x0c20025, (w06 >> 1) & 0x1ff)); + EXIT_RC(WR16(s, 0x0c20031, (w0A - w08) >> 1)); + EXIT_RC(WR16(s, 0x0c20032, ((w0A + w08) >> 1) - 0x1ff)); + + quot = w0C / 113; + rem = w0C % 113; + if (quot <= 8) { + quot = 8 - quot; + } else { + quot = 0; + rem += 113; + } + + EXIT_RC(WR16(s, 0x0c20024, quot)); + + i = fastIncrDecLUT_15273[rem / 8]; + EXIT_RC(WR16(s, 0x0c2002d, i)); + EXIT_RC(WR16(s, 0x0c2002e, i)); + + i = slowIncrDecLUT_15272[rem / 28]; + EXIT_RC(WR16(s, 0x0c2002b, i)); + rc = WR16(s, 0x0c2002c, i); + exit_rc: + return rc; +} + +static int SetCfgRfAgc(struct drx397xD_state *s, struct drx397xD_CfgRfAgc *agc) +{ + u16 w04 = agc->w04; + u16 w06 = agc->w06; + int rc = -1; + + pr_debug("%s %d 0x%x 0x%x\n", __FUNCTION__, agc->d00, w04, w06); + + if (w04 > 0x3ff) + goto exit_rc; + + switch (agc->d00) { + case 1: + if (w04 == 0x3ff) + w04 = 0x400; + + EXIT_RC(WR16(s, 0x0c20036, w04)); + s->config.w9C &= ~2; + EXIT_RC(WR16(s, 0x0c20015, s->config.w9C)); + EXIT_RC(RD16(s, 0x0c20010)); + rc &= 0xbfdf; + EXIT_RC(WR16(s, 0x0c20010, rc)); + EXIT_RC(RD16(s, 0x0c20013)); + rc &= ~2; + break; + case 0: + // loc_8000659 + s->config.w9C &= ~2; + EXIT_RC(WR16(s, 0x0c20015, s->config.w9C)); + EXIT_RC(RD16(s, 0x0c20010)); + rc &= 0xbfdf; + rc |= 0x4000; + EXIT_RC(WR16(s, 0x0c20010, rc)); + EXIT_RC(WR16(s, 0x0c20051, (w06 >> 4) & 0x3f)); + EXIT_RC(RD16(s, 0x0c20013)); + rc &= ~2; + break; + default: + s->config.w9C |= 2; + EXIT_RC(WR16(s, 0x0c20015, s->config.w9C)); + EXIT_RC(RD16(s, 0x0c20010)); + rc &= 0xbfdf; + EXIT_RC(WR16(s, 0x0c20010, rc)); + + EXIT_RC(WR16(s, 0x0c20036, 0)); + + EXIT_RC(RD16(s, 0x0c20013)); + rc |= 2; + } + rc = WR16(s, 0x0c20013, rc); + exit_rc: + return rc; +} + +static int GetLockStatus(struct drx397xD_state *s, int *lockstat) +{ + int rc; + + *lockstat = 0; + + rc = RD16(s, 0x082004b); + if (rc < 0) + return rc; + + if (s->config.d60 != 2) + return 0; + + if ((rc & 7) == 7) + *lockstat |= 1; + if ((rc & 3) == 3) + *lockstat |= 2; + if (rc & 1) + *lockstat |= 4; + return 0; +} + +static int CorrectSysClockDeviation(struct drx397xD_state *s) +{ + int rc = -EINVAL; + int lockstat; + u32 clk, clk_limit; + + pr_debug("%s\n", __FUNCTION__); + + if (s->config.d5C == 0) { + EXIT_RC(WR16(s, 0x08200e8, 0x010)); + EXIT_RC(WR16(s, 0x08200e9, 0x113)); + s->config.d5C = 1; + return rc; + } + if (s->config.d5C != 1) + goto exit_rc; + + rc = RD16(s, 0x0820048); + + rc = GetLockStatus(s, &lockstat); + if (rc < 0) + goto exit_rc; + if ((lockstat & 1) == 0) + goto exit_rc; + + EXIT_RC(WR16(s, 0x0420033, 0x200)); + EXIT_RC(WR16(s, 0x0420034, 0xc5)); + EXIT_RC(WR16(s, 0x0420035, 0x10)); + EXIT_RC(WR16(s, 0x0420036, 0x1)); + EXIT_RC(WR16(s, 0x0420037, 0xa)); + EXIT_RC(HI_Command(s, 6)); + EXIT_RC(RD16(s, 0x0420040)); + clk = rc; + EXIT_RC(RD16(s, 0x0420041)); + clk |= rc << 16; + + if (clk <= 0x26ffff) + goto exit_rc; + if (clk > 0x610000) + goto exit_rc; + + if (!s->bandwidth_parm) + return -EINVAL; + + /* round & convert to Hz */ + clk = ((u64) (clk + 0x800000) * s->bandwidth_parm + (1 << 20)) >> 21; + clk_limit = s->config.f_osc * MAX_CLOCK_DRIFT / 1000; + + if (clk - s->config.f_osc * 1000 + clk_limit <= 2 * clk_limit) { + s->f_osc = clk; + pr_debug("%s: osc %d %d [Hz]\n", __FUNCTION__, + s->config.f_osc * 1000, clk - s->config.f_osc * 1000); + } + rc = WR16(s, 0x08200e8, 0); + exit_rc: + return rc; +} + +static int ConfigureMPEGOutput(struct drx397xD_state *s, int type) +{ + int rc, si, bp; + + pr_debug("%s\n", __FUNCTION__); + + si = s->config.wA0; + if (s->config.w98 == 0) { + si |= 1; + bp = 0; + } else { + si &= ~1; + bp = 0x200; + } + if (s->config.w9A == 0) { + si |= 0x80; + } else { + si &= ~0x80; + } + + EXIT_RC(WR16(s, 0x2150045, 0)); + EXIT_RC(WR16(s, 0x2150010, si)); + EXIT_RC(WR16(s, 0x2150011, bp)); + rc = WR16(s, 0x2150012, (type == 0 ? 0xfff : 0)); + exit_rc: + return rc; +} + +static int drx_tune(struct drx397xD_state *s, + struct dvb_frontend_parameters *fep) +{ + u16 v22 = 0; + u16 v1C = 0; + u16 v1A = 0; + u16 v18 = 0; + u32 edi = 0, ebx = 0, ebp = 0, edx = 0; + u16 v20 = 0, v1E = 0, v16 = 0, v14 = 0, v12 = 0, v10 = 0, v0E = 0; + + int rc, df_tuner; + int a, b, c, d; + pr_debug("%s %d\n", __FUNCTION__, s->config.d60); + + if (s->config.d60 != 2) + goto set_tuner; + rc = CorrectSysClockDeviation(s); + if (rc < 0) + goto set_tuner; + + s->config.d60 = 1; + rc = ConfigureMPEGOutput(s, 0); + if (rc < 0) + goto set_tuner; + set_tuner: + + rc = PLL_Set(s, fep, &df_tuner); + if (rc < 0) { + printk(KERN_ERR "Error in pll_set\n"); + goto exit_rc; + } + msleep(200); + + a = rc = RD16(s, 0x2150016); + if (rc < 0) + goto exit_rc; + b = rc = RD16(s, 0x2150010); + if (rc < 0) + goto exit_rc; + c = rc = RD16(s, 0x2150034); + if (rc < 0) + goto exit_rc; + d = rc = RD16(s, 0x2150035); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2150014, c); + rc = WR16(s, 0x2150015, d); + rc = WR16(s, 0x2150010, 0); + rc = WR16(s, 0x2150000, 2); + rc = WR16(s, 0x2150036, 0x0fff); + rc = WR16(s, 0x2150016, a); + + rc = WR16(s, 0x2150010, 2); + rc = WR16(s, 0x2150007, 0); + rc = WR16(s, 0x2150000, 1); + rc = WR16(s, 0x2110000, 0); + rc = WR16(s, 0x0800000, 0); + rc = WR16(s, 0x2800000, 0); + rc = WR16(s, 0x2110010, 0x664); + + rc = write_fw(s, DRXD_ResetECRAM); + rc = WR16(s, 0x2110000, 1); + + rc = write_fw(s, DRXD_InitSC); + if (rc < 0) + goto exit_rc; + + rc = SetCfgIfAgc(s, &s->config.ifagc); + if (rc < 0) + goto exit_rc; + + rc = SetCfgRfAgc(s, &s->config.rfagc); + if (rc < 0) + goto exit_rc; + + if (fep->u.ofdm.transmission_mode != TRANSMISSION_MODE_2K) + v22 = 1; + switch (fep->u.ofdm.transmission_mode) { + case TRANSMISSION_MODE_8K: + edi = 1; + if (s->chip_rev == DRXD_FW_B1) + break; + + rc = WR16(s, 0x2010010, 0); + if (rc < 0) + break; + v1C = 0x63; + v1A = 0x53; + v18 = 0x43; + break; + default: + edi = 0; + if (s->chip_rev == DRXD_FW_B1) + break; + + rc = WR16(s, 0x2010010, 1); + if (rc < 0) + break; + + v1C = 0x61; + v1A = 0x47; + v18 = 0x41; + } + + switch (fep->u.ofdm.guard_interval) { + case GUARD_INTERVAL_1_4: + edi |= 0x0c; + break; + case GUARD_INTERVAL_1_8: + edi |= 0x08; + break; + case GUARD_INTERVAL_1_16: + edi |= 0x04; + break; + case GUARD_INTERVAL_1_32: + break; + default: + v22 |= 2; + } + + ebx = 0; + ebp = 0; + v20 = 0; + v1E = 0; + v16 = 0; + v14 = 0; + v12 = 0; + v10 = 0; + v0E = 0; + + switch (fep->u.ofdm.hierarchy_information) { + case HIERARCHY_1: + edi |= 0x40; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x1c10047, 1); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010012, 1); + if (rc < 0) + goto exit_rc; + ebx = 0x19f; + ebp = 0x1fb; + v20 = 0x0c0; + v1E = 0x195; + v16 = 0x1d6; + v14 = 0x1ef; + v12 = 4; + v10 = 5; + v0E = 5; + break; + case HIERARCHY_2: + edi |= 0x80; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x1c10047, 2); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010012, 2); + if (rc < 0) + goto exit_rc; + ebx = 0x08f; + ebp = 0x12f; + v20 = 0x0c0; + v1E = 0x11e; + v16 = 0x1d6; + v14 = 0x15e; + v12 = 4; + v10 = 5; + v0E = 5; + break; + case HIERARCHY_4: + edi |= 0xc0; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x1c10047, 3); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010012, 3); + if (rc < 0) + goto exit_rc; + ebx = 0x14d; + ebp = 0x197; + v20 = 0x0c0; + v1E = 0x1ce; + v16 = 0x1d6; + v14 = 0x11a; + v12 = 4; + v10 = 6; + v0E = 5; + break; + default: + v22 |= 8; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x1c10047, 0); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010012, 0); + if (rc < 0) + goto exit_rc; + // QPSK QAM16 QAM64 + ebx = 0x19f; // 62 + ebp = 0x1fb; // 15 + v20 = 0x16a; // 62 + v1E = 0x195; // 62 + v16 = 0x1bb; // 15 + v14 = 0x1ef; // 15 + v12 = 5; // 16 + v10 = 5; // 16 + v0E = 5; // 16 + } + + switch (fep->u.ofdm.constellation) { + default: + v22 |= 4; + case QPSK: + if (s->chip_rev == DRXD_FW_B1) + break; + + rc = WR16(s, 0x1c10046, 0); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010011, 0); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001a, 0x10); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001b, 0); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001c, 0); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10062, v20); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c1002a, v1C); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10015, v16); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10016, v12); + if (rc < 0) + goto exit_rc; + break; + case QAM_16: + edi |= 0x10; + if (s->chip_rev == DRXD_FW_B1) + break; + + rc = WR16(s, 0x1c10046, 1); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010011, 1); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001a, 0x10); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001b, 4); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001c, 0); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10062, v1E); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c1002a, v1A); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10015, v14); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10016, v10); + if (rc < 0) + goto exit_rc; + break; + case QAM_64: + edi |= 0x20; + rc = WR16(s, 0x1c10046, 2); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x2010011, 2); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001a, 0x20); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001b, 8); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x201001c, 2); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10062, ebx); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c1002a, v18); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10015, ebp); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x1c10016, v0E); + if (rc < 0) + goto exit_rc; + break; + } + + if (s->config.s20d24 == 1) { + rc = WR16(s, 0x2010013, 0); + } else { + rc = WR16(s, 0x2010013, 1); + edi |= 0x1000; + } + + switch (fep->u.ofdm.code_rate_HP) { + default: + v22 |= 0x10; + case FEC_1_2: + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x2090011, 0); + break; + case FEC_2_3: + edi |= 0x200; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x2090011, 1); + break; + case FEC_3_4: + edi |= 0x400; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x2090011, 2); + break; + case FEC_5_6: /* 5 */ + edi |= 0x600; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x2090011, 3); + break; + case FEC_7_8: /* 7 */ + edi |= 0x800; + if (s->chip_rev == DRXD_FW_B1) + break; + rc = WR16(s, 0x2090011, 4); + break; + }; + if (rc < 0) + goto exit_rc; + + switch (fep->u.ofdm.bandwidth) { + default: + rc = -EINVAL; + goto exit_rc; + case BANDWIDTH_8_MHZ: /* 0 */ + case BANDWIDTH_AUTO: + rc = WR16(s, 0x0c2003f, 0x32); + s->bandwidth_parm = ebx = 0x8b8249; // 9142857 + edx = 0; + break; + case BANDWIDTH_7_MHZ: + rc = WR16(s, 0x0c2003f, 0x3b); + s->bandwidth_parm = ebx = 0x7a1200; // 8000000 + edx = 0x4807; + break; + case BANDWIDTH_6_MHZ: + rc = WR16(s, 0x0c2003f, 0x47); + s->bandwidth_parm = ebx = 0x68a1b6; // 6857142 + edx = 0x0f07; + break; + }; + + if (rc < 0) + goto exit_rc; + + rc = WR16(s, 0x08200ec, edx); + if (rc < 0) + goto exit_rc; + + rc = RD16(s, 0x0820050); + if (rc < 0) + goto exit_rc; + rc = WR16(s, 0x0820050, rc); + + { + /* Configure bandwidth specific factor */ + ebx = div64_u64(((u64) (s->f_osc) << 21) + (ebx >> 1), + (u64)ebx) - 0x800000; + EXIT_RC(WR16(s, 0x0c50010, ebx & 0xffff)); + EXIT_RC(WR16(s, 0x0c50011, ebx >> 16)); + + /* drx397xD oscillator calibration */ + ebx = div64_u64(((u64) (s->config.f_if + df_tuner) << 28) + + (s->f_osc >> 1), (u64)s->f_osc); + } + ebx &= 0xfffffff; + if (fep->inversion == INVERSION_ON) + ebx = 0x10000000 - ebx; + + EXIT_RC(WR16(s, 0x0c30010, ebx & 0xffff)); + EXIT_RC(WR16(s, 0x0c30011, ebx >> 16)); + + EXIT_RC(WR16(s, 0x0800000, 1)); + EXIT_RC(RD16(s, 0x0800000)); + + + EXIT_RC(SC_WaitForReady(s)); + EXIT_RC(WR16(s, 0x0820042, 0)); + EXIT_RC(WR16(s, 0x0820041, v22)); + EXIT_RC(WR16(s, 0x0820040, edi)); + EXIT_RC(SC_SendCommand(s, 3)); + + rc = RD16(s, 0x0800000); + + SC_WaitForReady(s); + WR16(s, 0x0820042, 0); + WR16(s, 0x0820041, 1); + WR16(s, 0x0820040, 1); + SC_SendCommand(s, 1); + +// rc = WR16(s, 0x2150000, 1); +// if (rc < 0) goto exit_rc; + + rc = WR16(s, 0x2150000, 2); + rc = WR16(s, 0x2150016, a); + rc = WR16(s, 0x2150010, 4); + rc = WR16(s, 0x2150036, 0); + rc = WR16(s, 0x2150000, 1); + s->config.d60 = 2; + exit_rc: + return rc; +} + +/******************************************************************************* + * DVB interface + ******************************************************************************/ + +static int drx397x_init(struct dvb_frontend *fe) +{ + struct drx397xD_state *s = fe->demodulator_priv; + int rc; + + pr_debug("%s\n", __FUNCTION__); + + s->config.rfagc.d00 = 2; /* 0x7c */ + s->config.rfagc.w04 = 0; + s->config.rfagc.w06 = 0x3ff; + + s->config.ifagc.d00 = 0; /* 0x68 */ + s->config.ifagc.w04 = 0; + s->config.ifagc.w06 = 140; + s->config.ifagc.w08 = 0; + s->config.ifagc.w0A = 0x3ff; + s->config.ifagc.w0C = 0x388; + + /* for signal strenght calculations */ + s->config.ss76 = 820; + s->config.ss78 = 2200; + s->config.ss7A = 150; + + /* HI_CfgCommand */ + s->config.w50 = 4; + s->config.w52 = 9; // 0xf; + + s->config.f_if = 42800000; /* d14: intermediate frequency [Hz] */ + s->config.f_osc = 48000; /* s66 : oscillator frequency [kHz] */ + s->config.w92 = 12000; // 20000; + + s->config.w9C = 0x000e; + s->config.w9E = 0x0000; + + /* ConfigureMPEGOutput params */ + s->config.wA0 = 4; + s->config.w98 = 1; // 0; + s->config.w9A = 1; + + /* get chip revision */ + rc = RD16(s, 0x2410019); + if (rc < 0) + return -ENODEV; + + if (rc == 0) { + printk(KERN_INFO "%s: chip revision A2\n", mod_name); + rc = drx_load_fw(s, DRXD_FW_A2); + } else { + + rc = (rc >> 12) - 3; + switch (rc) { + case 1: + s->flags |= F_SET_0D4h; + case 0: + case 4: + s->flags |= F_SET_0D0h; + break; + case 2: + case 5: + break; + case 3: + s->flags |= F_SET_0D4h; + break; + default: + return -ENODEV; + }; + printk(KERN_INFO "%s: chip revision B1.%d\n", mod_name, rc); + rc = drx_load_fw(s, DRXD_FW_B1); + } + if (rc < 0) + goto error; + + rc = WR16(s, 0x0420033, 0x3973); + if (rc < 0) + goto error; + + rc = HI_Command(s, 2); + + msleep(1); + + if (s->chip_rev == DRXD_FW_A2) { + rc = WR16(s, 0x043012d, 0x47F); + if (rc < 0) + goto error; + } + rc = WR16_E0(s, 0x0400000, 0); + if (rc < 0) + goto error; + + if (s->config.w92 > 20000 || s->config.w92 % 4000) { + printk(KERN_ERR "%s: invalid osc frequency\n", mod_name); + rc = -1; + goto error; + } + + rc = WR16(s, 0x2410010, 1); + if (rc < 0) + goto error; + rc = WR16(s, 0x2410011, 0x15); + if (rc < 0) + goto error; + rc = WR16(s, 0x2410012, s->config.w92 / 4000); + if (rc < 0) + goto error; +#ifdef ORIG_FW + rc = WR16(s, 0x2410015, 2); + if (rc < 0) + goto error; +#endif + rc = WR16(s, 0x2410017, 0x3973); + if (rc < 0) + goto error; + + s->f_osc = s->config.f_osc * 1000; /* initial estimator */ + + s->config.w56 = 1; + + rc = HI_CfgCommand(s); + if (rc < 0) + goto error; + + rc = write_fw(s, DRXD_InitAtomicRead); + if (rc < 0) + goto error; + + if (s->chip_rev == DRXD_FW_A2) { + rc = WR16(s, 0x2150013, 0); + if (rc < 0) + goto error; + } + + rc = WR16_E0(s, 0x0400002, 0); + if (rc < 0) + goto error; + rc = WR16(s, 0x0400002, 0); + if (rc < 0) + goto error; + + if (s->chip_rev == DRXD_FW_A2) { + rc = write_fw(s, DRXD_ResetCEFR); + if (rc < 0) + goto error; + } + rc = write_fw(s, DRXD_microcode); + if (rc < 0) + goto error; + + s->config.w9C = 0x0e; + if (s->flags & F_SET_0D0h) { + s->config.w9C = 0; + rc = RD16(s, 0x0c20010); + if (rc < 0) + goto write_DRXD_InitFE_1; + + rc &= ~0x1000; + rc = WR16(s, 0x0c20010, rc); + if (rc < 0) + goto write_DRXD_InitFE_1; + + rc = RD16(s, 0x0c20011); + if (rc < 0) + goto write_DRXD_InitFE_1; + + rc &= ~0x8; + rc = WR16(s, 0x0c20011, rc); + if (rc < 0) + goto write_DRXD_InitFE_1; + + rc = WR16(s, 0x0c20012, 1); + } + + write_DRXD_InitFE_1: + + rc = write_fw(s, DRXD_InitFE_1); + if (rc < 0) + goto error; + + rc = 1; + if (s->chip_rev == DRXD_FW_B1) { + if (s->flags & F_SET_0D0h) + rc = 0; + } else { + if (s->flags & F_SET_0D0h) + rc = 4; + } + + rc = WR16(s, 0x0C20012, rc); + if (rc < 0) + goto error; + + rc = WR16(s, 0x0C20013, s->config.w9E); + if (rc < 0) + goto error; + rc = WR16(s, 0x0C20015, s->config.w9C); + if (rc < 0) + goto error; + + rc = write_fw(s, DRXD_InitFE_2); + if (rc < 0) + goto error; + rc = write_fw(s, DRXD_InitFT); + if (rc < 0) + goto error; + rc = write_fw(s, DRXD_InitCP); + if (rc < 0) + goto error; + rc = write_fw(s, DRXD_InitCE); + if (rc < 0) + goto error; + rc = write_fw(s, DRXD_InitEQ); + if (rc < 0) + goto error; + rc = write_fw(s, DRXD_InitEC); + if (rc < 0) + goto error; + rc = write_fw(s, DRXD_InitSC); + if (rc < 0) + goto error; + + rc = SetCfgIfAgc(s, &s->config.ifagc); + if (rc < 0) + goto error; + + rc = SetCfgRfAgc(s, &s->config.rfagc); + if (rc < 0) + goto error; + + rc = ConfigureMPEGOutput(s, 1); + rc = WR16(s, 0x08201fe, 0x0017); + rc = WR16(s, 0x08201ff, 0x0101); + + s->config.d5C = 0; + s->config.d60 = 1; + s->config.d48 = 1; + error: + return rc; +} + +static int drx397x_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + return 0; +} + +static int drx397x_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct drx397xD_state *s = fe->demodulator_priv; + + s->config.s20d24 = 1; // 0; + return drx_tune(s, params); +} + +static int drx397x_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings + *fe_tune_settings) +{ + fe_tune_settings->min_delay_ms = 10000; + fe_tune_settings->step_size = 0; + fe_tune_settings->max_drift = 0; + return 0; +} + +static int drx397x_read_status(struct dvb_frontend *fe, fe_status_t * status) +{ + struct drx397xD_state *s = fe->demodulator_priv; + int lockstat; + + GetLockStatus(s, &lockstat); + /* TODO */ +// if (lockstat & 1) +// CorrectSysClockDeviation(s); + + *status = 0; + if (lockstat & 2) { + CorrectSysClockDeviation(s); + ConfigureMPEGOutput(s, 1); + *status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI; + } + if (lockstat & 4) { + *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + } + + return 0; +} + +static int drx397x_read_ber(struct dvb_frontend *fe, unsigned int *ber) +{ + *ber = 0; + return 0; +} + +static int drx397x_read_snr(struct dvb_frontend *fe, u16 * snr) +{ + *snr = 0; + return 0; +} + +static int drx397x_read_signal_strength(struct dvb_frontend *fe, u16 * strength) +{ + struct drx397xD_state *s = fe->demodulator_priv; + int rc; + + if (s->config.ifagc.d00 == 2) { + *strength = 0xffff; + return 0; + } + rc = RD16(s, 0x0c20035); + if (rc < 0) { + *strength = 0; + return 0; + } + rc &= 0x3ff; + /* Signal strength is calculated using the following formula: + * + * a = 2200 * 150 / (2200 + 150); + * a = a * 3300 / (a + 820); + * b = 2200 * 3300 / (2200 + 820); + * c = (((b-a) * rc) >> 10 + a) << 4; + * strength = ~c & 0xffff; + * + * The following does the same but with less rounding errors: + */ + *strength = ~(7720 + (rc * 30744 >> 10)); + return 0; +} + +static int drx397x_read_ucblocks(struct dvb_frontend *fe, + unsigned int *ucblocks) +{ + *ucblocks = 0; + return 0; +} + +static int drx397x_sleep(struct dvb_frontend *fe) +{ + return 0; +} + +static void drx397x_release(struct dvb_frontend *fe) +{ + struct drx397xD_state *s = fe->demodulator_priv; + printk(KERN_INFO "%s: release demodulator\n", mod_name); + if (s) { + drx_release_fw(s); + kfree(s); + } + +} + +static struct dvb_frontend_ops drx397x_ops = { + + .info = { + .name = "Micronas DRX397xD DVB-T Frontend", + .type = FE_OFDM, + .frequency_min = 47125000, + .frequency_max = 855250000, + .frequency_stepsize = 166667, + .frequency_tolerance = 0, + .caps = /* 0x0C01B2EAE */ + FE_CAN_FEC_1_2 | // = 0x2, + FE_CAN_FEC_2_3 | // = 0x4, + FE_CAN_FEC_3_4 | // = 0x8, + FE_CAN_FEC_5_6 | // = 0x20, + FE_CAN_FEC_7_8 | // = 0x80, + FE_CAN_FEC_AUTO | // = 0x200, + FE_CAN_QPSK | // = 0x400, + FE_CAN_QAM_16 | // = 0x800, + FE_CAN_QAM_64 | // = 0x2000, + FE_CAN_QAM_AUTO | // = 0x10000, + FE_CAN_TRANSMISSION_MODE_AUTO | // = 0x20000, + FE_CAN_GUARD_INTERVAL_AUTO | // = 0x80000, + FE_CAN_HIERARCHY_AUTO | // = 0x100000, + FE_CAN_RECOVER | // = 0x40000000, + FE_CAN_MUTE_TS // = 0x80000000 + }, + + .release = drx397x_release, + .init = drx397x_init, + .sleep = drx397x_sleep, + + .set_frontend = drx397x_set_frontend, + .get_tune_settings = drx397x_get_tune_settings, + .get_frontend = drx397x_get_frontend, + + .read_status = drx397x_read_status, + .read_snr = drx397x_read_snr, + .read_signal_strength = drx397x_read_signal_strength, + .read_ber = drx397x_read_ber, + .read_ucblocks = drx397x_read_ucblocks, +}; + +struct dvb_frontend *drx397xD_attach(const struct drx397xD_config *config, + struct i2c_adapter *i2c) +{ + struct drx397xD_state *s = NULL; + + /* allocate memory for the internal state */ + s = kzalloc(sizeof(struct drx397xD_state), GFP_KERNEL); + if (s == NULL) + goto error; + + /* setup the state */ + s->i2c = i2c; + memcpy(&s->config, config, sizeof(struct drx397xD_config)); + + /* check if the demod is there */ + if (RD16(s, 0x2410019) < 0) + goto error; + + /* create dvb_frontend */ + memcpy(&s->frontend.ops, &drx397x_ops, sizeof(struct dvb_frontend_ops)); + s->frontend.demodulator_priv = s; + + return &s->frontend; + error: + kfree(s); + return NULL; +} + +MODULE_DESCRIPTION("Micronas DRX397xD DVB-T Frontend"); +MODULE_AUTHOR("Henk Vergonet"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(drx397xD_attach); diff --git a/drivers/media/dvb/frontends/drx397xD.h b/drivers/media/dvb/frontends/drx397xD.h new file mode 100644 index 000000000000..ddc7a07971b7 --- /dev/null +++ b/drivers/media/dvb/frontends/drx397xD.h @@ -0,0 +1,130 @@ +/* + * Driver for Micronas DVB-T drx397xD demodulator + * + * Copyright (C) 2007 Henk vergonet <Henk.Vergonet@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 _DRX397XD_H_INCLUDED +#define _DRX397XD_H_INCLUDED + +#include <linux/dvb/frontend.h> + +#define DRX_F_STEPSIZE 166667 +#define DRX_F_OFFSET 36000000 + +#define I2C_ADR_C0(x) \ +( (u32)cpu_to_le32( \ + (u32)( \ + (((u32)(x) & (u32)0x000000ffUL) ) | \ + (((u32)(x) & (u32)0x0000ff00UL) << 16) | \ + (((u32)(x) & (u32)0x0fff0000UL) >> 8) | \ + ( (u32)0x00c00000UL) \ + )) \ +) + +#define I2C_ADR_E0(x) \ +( (u32)cpu_to_le32( \ + (u32)( \ + (((u32)(x) & (u32)0x000000ffUL) ) | \ + (((u32)(x) & (u32)0x0000ff00UL) << 16) | \ + (((u32)(x) & (u32)0x0fff0000UL) >> 8) | \ + ( (u32)0x00e00000UL) \ + )) \ +) + +struct drx397xD_CfgRfAgc /* 0x7c */ +{ + int d00; /* 2 */ + u16 w04; + u16 w06; +}; + +struct drx397xD_CfgIfAgc /* 0x68 */ +{ + int d00; /* 0 */ + u16 w04; /* 0 */ + u16 w06; + u16 w08; + u16 w0A; + u16 w0C; +}; + +struct drx397xD_s20 { + int d04; + u32 d18; + u32 d1C; + u32 d20; + u32 d14; + u32 d24; + u32 d0C; + u32 d08; +}; + +struct drx397xD_config +{ + /* demodulator's I2C address */ + u8 demod_address; /* 0x0f */ + + struct drx397xD_CfgIfAgc ifagc; /* 0x68 */ + struct drx397xD_CfgRfAgc rfagc; /* 0x7c */ + u32 s20d24; + + /* HI_CfgCommand parameters */ + u16 w50, w52, /* w54, */ w56; + + int d5C; + int d60; + int d48; + int d28; + + u32 f_if; /* d14: intermediate frequency [Hz] */ + /* 36000000 on Cinergy 2400i DT */ + /* 42800000 on Pinnacle Hybrid PRO 330e */ + + u16 f_osc; /* s66: 48000 oscillator frequency [kHz] */ + + u16 w92; /* 20000 */ + + u16 wA0; + u16 w98; + u16 w9A; + + u16 w9C; /* 0xe0 */ + u16 w9E; /* 0x00 */ + + /* used for signal strength calculations in + drx397x_read_signal_strength + */ + u16 ss78; // 2200 + u16 ss7A; // 150 + u16 ss76; // 820 +}; + +#if defined(CONFIG_DVB_DRX397XD) || (defined(CONFIG_DVB_DRX397XD_MODULE) && defined(MODULE)) +extern struct dvb_frontend* drx397xD_attach(const struct drx397xD_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend* drx397xD_attach(const struct drx397xD_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif /* CONFIG_DVB_DRX397XD */ + +#endif /* _DRX397XD_H_INCLUDED */ diff --git a/drivers/media/dvb/frontends/drx397xD_fw.h b/drivers/media/dvb/frontends/drx397xD_fw.h new file mode 100644 index 000000000000..01de02a81cd4 --- /dev/null +++ b/drivers/media/dvb/frontends/drx397xD_fw.h @@ -0,0 +1,40 @@ +/* + * Firmware definitions for Micronas drx397xD + * + * Copyright (C) 2007 Henk Vergonet <Henk.Vergonet@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifdef _FW_ENTRY + _FW_ENTRY("drx397xD.A2.fw", DRXD_FW_A2 = 0 ), + _FW_ENTRY("drx397xD.B1.fw", DRXD_FW_B1 ), +#undef _FW_ENTRY +#endif /* _FW_ENTRY */ + +#ifdef _BLOB_ENTRY + _BLOB_ENTRY("InitAtomicRead", DRXD_InitAtomicRead = 0 ), + _BLOB_ENTRY("InitCE", DRXD_InitCE ), + _BLOB_ENTRY("InitCP", DRXD_InitCP ), + _BLOB_ENTRY("InitEC", DRXD_InitEC ), + _BLOB_ENTRY("InitEQ", DRXD_InitEQ ), + _BLOB_ENTRY("InitFE_1", DRXD_InitFE_1 ), + _BLOB_ENTRY("InitFE_2", DRXD_InitFE_2 ), + _BLOB_ENTRY("InitFT", DRXD_InitFT ), + _BLOB_ENTRY("InitSC", DRXD_InitSC ), + _BLOB_ENTRY("ResetCEFR", DRXD_ResetCEFR ), + _BLOB_ENTRY("ResetECRAM", DRXD_ResetECRAM ), + _BLOB_ENTRY("microcode", DRXD_microcode ), +#undef _BLOB_ENTRY +#endif /* _BLOB_ENTRY */ diff --git a/drivers/media/dvb/frontends/z0194a.h b/drivers/media/dvb/frontends/z0194a.h new file mode 100644 index 000000000000..d2876d2e1769 --- /dev/null +++ b/drivers/media/dvb/frontends/z0194a.h @@ -0,0 +1,97 @@ +/* z0194a.h Sharp z0194a tuner support +* +* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation, version 2. +* +* see Documentation/dvb/README.dvb-usb for more information +*/ + +#ifndef Z0194A +#define Z0194A + +static int sharp_z0194a__set_symbol_rate(struct dvb_frontend *fe, + u32 srate, u32 ratio) +{ + u8 aclk = 0; + u8 bclk = 0; + + if (srate < 1500000) { + aclk = 0xb7; bclk = 0x47; } + else if (srate < 3000000) { + aclk = 0xb7; bclk = 0x4b; } + else if (srate < 7000000) { + aclk = 0xb7; bclk = 0x4f; } + else if (srate < 14000000) { + aclk = 0xb7; bclk = 0x53; } + else if (srate < 30000000) { + aclk = 0xb6; bclk = 0x53; } + else if (srate < 45000000) { + aclk = 0xb4; bclk = 0x51; } + + stv0299_writereg(fe, 0x13, aclk); + stv0299_writereg(fe, 0x14, bclk); + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, (ratio) & 0xf0); + + return 0; +} + +static u8 sharp_z0194a__inittab[] = { + 0x01, 0x15, + 0x02, 0x00, + 0x03, 0x00, + 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ + 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ + 0x06, 0x40, /* DAC not used, set to high impendance mode */ + 0x07, 0x00, /* DAC LSB */ + 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ + 0x09, 0x00, /* FIFO */ + 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ + 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ + 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ + 0x10, 0x3f, /* AGC2 0x3d */ + 0x11, 0x84, + 0x12, 0xb9, + 0x15, 0xc9, /* lock detector threshold */ + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0x00, + 0x19, 0x00, + 0x1a, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, /* out imp: normal out type: parallel FEC mode:0 */ + 0x29, 0x1e, /* 1/2 threshold */ + 0x2a, 0x14, /* 2/3 threshold */ + 0x2b, 0x0f, /* 3/4 threshold */ + 0x2c, 0x09, /* 5/6 threshold */ + 0x2d, 0x05, /* 7/8 threshold */ + 0x2e, 0x01, + 0x31, 0x1f, /* test all FECs */ + 0x32, 0x19, /* viterbi and synchro search */ + 0x33, 0xfc, /* rs control */ + 0x34, 0x93, /* error control */ + 0x0f, 0x52, + 0xff, 0xff +}; + +static struct stv0299_config sharp_z0194a_config = { + .demod_address = 0x68, + .inittab = sharp_z0194a__inittab, + .mclk = 88000000UL, + .invert = 1, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 100, + .set_symbol_rate = sharp_z0194a__set_symbol_rate, +}; + +#endif diff --git a/drivers/media/dvb/pluto2/pluto2.c b/drivers/media/dvb/pluto2/pluto2.c index 1360403b88b6..a9653c63f4db 100644 --- a/drivers/media/dvb/pluto2/pluto2.c +++ b/drivers/media/dvb/pluto2/pluto2.c @@ -242,7 +242,7 @@ static int __devinit pluto_dma_map(struct pluto *pluto) pluto->dma_addr = pci_map_single(pluto->pdev, pluto->dma_buf, TS_DMA_BYTES, PCI_DMA_FROMDEVICE); - return pci_dma_mapping_error(pluto->dma_addr); + return pci_dma_mapping_error(pluto->pdev, pluto->dma_addr); } static void pluto_dma_unmap(struct pluto *pluto) diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c index b4b8ed795c95..c5f45fed69dc 100644 --- a/drivers/media/dvb/siano/smscoreapi.c +++ b/drivers/media/dvb/siano/smscoreapi.c @@ -110,12 +110,12 @@ struct smscore_registry_entry_t { enum sms_device_type_st type; }; -struct list_head g_smscore_notifyees; -struct list_head g_smscore_devices; -struct mutex g_smscore_deviceslock; +static struct list_head g_smscore_notifyees; +static struct list_head g_smscore_devices; +static struct mutex g_smscore_deviceslock; -struct list_head g_smscore_registry; -struct mutex g_smscore_registrylock; +static struct list_head g_smscore_registry; +static struct mutex g_smscore_registrylock; static int default_mode = 4; @@ -1187,7 +1187,7 @@ int smsclient_sendrequest(struct smscore_client_t *client, } -int smscore_module_init(void) +static int __init smscore_module_init(void) { int rc = 0; @@ -1209,7 +1209,7 @@ int smscore_module_init(void) return rc; } -void smscore_module_exit(void) +static void __exit smscore_module_exit(void) { kmutex_lock(&g_smscore_deviceslock); diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c index 6f9c18563867..229274a14110 100644 --- a/drivers/media/dvb/siano/smsdvb.c +++ b/drivers/media/dvb/siano/smsdvb.c @@ -27,8 +27,8 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -struct list_head g_smsdvb_clients; -struct mutex g_smsdvb_clientslock; +static struct list_head g_smsdvb_clients; +static struct mutex g_smsdvb_clientslock; static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) { diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig index 87c973ac668b..41b5a988b619 100644 --- a/drivers/media/dvb/ttpci/Kconfig +++ b/drivers/media/dvb/ttpci/Kconfig @@ -5,8 +5,6 @@ config TTPCI_EEPROM config DVB_AV7110 tristate "AV7110 cards" depends on DVB_CORE && PCI && I2C - depends on HOTPLUG - select FW_LOADER if !DVB_AV7110_FIRMWARE select TTPCI_EEPROM select VIDEO_SAA7146_VV depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV @@ -127,14 +125,12 @@ config DVB_BUDGET_AV depends on DVB_BUDGET_CORE && I2C select VIDEO_SAA7146_VV depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV - depends on HOTPLUG # dependency of FW_LOADER select DVB_PLL if !DVB_FE_CUSTOMISE 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 Support for simple SAA7146 based DVB cards (so called Budget- or Nova-PCI cards) without onboard diff --git a/drivers/media/dvb/ttusb-dec/Kconfig b/drivers/media/dvb/ttusb-dec/Kconfig index a23cc0aa17d3..d5f48a3102bd 100644 --- a/drivers/media/dvb/ttusb-dec/Kconfig +++ b/drivers/media/dvb/ttusb-dec/Kconfig @@ -1,8 +1,6 @@ config DVB_TTUSB_DEC tristate "Technotrend/Hauppauge USB DEC devices" depends on DVB_CORE && USB && INPUT - depends on HOTPLUG # due to FW_LOADER - select FW_LOADER select CRC32 help Support for external USB adapters designed by Technotrend and diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c index 4e3f83e4e48f..1ed88f3abe61 100644 --- a/drivers/media/radio/dsbr100.c +++ b/drivers/media/radio/dsbr100.c @@ -85,6 +85,7 @@ #include <linux/input.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/usb.h> /* @@ -444,14 +445,7 @@ static const struct file_operations usb_dsbr100_fops = { .llseek = no_llseek, }; -/* V4L2 interface */ -static struct video_device dsbr100_videodev_template = -{ - .owner = THIS_MODULE, - .name = "D-Link DSB-R 100", - .type = VID_TYPE_TUNER, - .fops = &usb_dsbr100_fops, - .release = video_device_release, +static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, @@ -466,6 +460,14 @@ static struct video_device dsbr100_videodev_template = .vidioc_s_input = vidioc_s_input, }; +/* V4L2 interface */ +static struct video_device dsbr100_videodev_template = { + .name = "D-Link DSB-R 100", + .fops = &usb_dsbr100_fops, + .ioctl_ops = &usb_dsbr100_ioctl_ops, + .release = video_device_release, +}; + /* check if the device is present and register with v4l and usb if it is */ static int usb_dsbr100_probe(struct usb_interface *intf, diff --git a/drivers/media/radio/miropcm20-radio.c b/drivers/media/radio/miropcm20-radio.c index 09fe6f1cdf14..7fd7ee2d32c1 100644 --- a/drivers/media/radio/miropcm20-radio.c +++ b/drivers/media/radio/miropcm20-radio.c @@ -23,6 +23,7 @@ #include <linux/init.h> #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include "oss/aci.h" #include "miropcm20-rds-core.h" @@ -228,9 +229,7 @@ static const struct file_operations pcm20_fops = { }; static struct video_device pcm20_radio = { - .owner = THIS_MODULE, .name = "Miro PCM 20 radio", - .type = VID_TYPE_TUNER, .fops = &pcm20_fops, .priv = &pcm20_unit }; diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c index 1ec18ed1a733..eba9209b3024 100644 --- a/drivers/media/radio/radio-aimslab.c +++ b/drivers/media/radio/radio-aimslab.c @@ -36,6 +36,7 @@ #include <asm/uaccess.h> /* copy to/from user */ #include <linux/videodev2.h> /* kernel radio structs */ #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/version.h> /* for KERNEL_VERSION MACRO */ #define RADIO_VERSION KERNEL_VERSION(0,0,2) @@ -388,12 +389,7 @@ static const struct file_operations rtrack_fops = { .llseek = no_llseek, }; -static struct video_device rtrack_radio= -{ - .owner = THIS_MODULE, - .name = "RadioTrack radio", - .type = VID_TYPE_TUNER, - .fops = &rtrack_fops, +static const struct v4l2_ioctl_ops rtrack_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, @@ -408,6 +404,12 @@ static struct video_device rtrack_radio= .vidioc_s_ctrl = vidioc_s_ctrl, }; +static struct video_device rtrack_radio = { + .name = "RadioTrack radio", + .fops = &rtrack_fops, + .ioctl_ops = &rtrack_ioctl_ops, +}; + static int __init rtrack_init(void) { if(io==-1) diff --git a/drivers/media/radio/radio-aztech.c b/drivers/media/radio/radio-aztech.c index 46cdb549eac7..3fe5504428c5 100644 --- a/drivers/media/radio/radio-aztech.c +++ b/drivers/media/radio/radio-aztech.c @@ -33,6 +33,7 @@ #include <asm/uaccess.h> /* copy to/from user */ #include <linux/videodev2.h> /* kernel radio structs */ #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/version.h> /* for KERNEL_VERSION MACRO */ #define RADIO_VERSION KERNEL_VERSION(0,0,2) @@ -352,12 +353,7 @@ static const struct file_operations aztech_fops = { .llseek = no_llseek, }; -static struct video_device aztech_radio= -{ - .owner = THIS_MODULE, - .name = "Aztech radio", - .type = VID_TYPE_TUNER, - .fops = &aztech_fops, +static const struct v4l2_ioctl_ops aztech_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, @@ -372,6 +368,12 @@ static struct video_device aztech_radio= .vidioc_s_ctrl = vidioc_s_ctrl, }; +static struct video_device aztech_radio = { + .name = "Aztech radio", + .fops = &aztech_fops, + .ioctl_ops = &aztech_ioctl_ops, +}; + module_param_named(debug,aztech_radio.debug, int, 0644); MODULE_PARM_DESC(debug,"activates debug info"); diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c index b14db53ea456..6166e726ed72 100644 --- a/drivers/media/radio/radio-cadet.c +++ b/drivers/media/radio/radio-cadet.c @@ -39,6 +39,7 @@ #include <asm/uaccess.h> /* copy to/from user */ #include <linux/videodev2.h> /* V4L2 API defs */ #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/param.h> #include <linux/pnp.h> @@ -569,12 +570,7 @@ static const struct file_operations cadet_fops = { .llseek = no_llseek, }; -static struct video_device cadet_radio= -{ - .owner = THIS_MODULE, - .name = "Cadet radio", - .type = VID_TYPE_TUNER, - .fops = &cadet_fops, +static const struct v4l2_ioctl_ops cadet_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, @@ -589,6 +585,12 @@ static struct video_device cadet_radio= .vidioc_s_input = vidioc_s_input, }; +static struct video_device cadet_radio = { + .name = "Cadet radio", + .fops = &cadet_fops, + .ioctl_ops = &cadet_ioctl_ops, +}; + #ifdef CONFIG_PNP static struct pnp_device_id cadet_pnp_devices[] = { diff --git a/drivers/media/radio/radio-gemtek-pci.c b/drivers/media/radio/radio-gemtek-pci.c index de49be971480..36e754e3ffb2 100644 --- a/drivers/media/radio/radio-gemtek-pci.c +++ b/drivers/media/radio/radio-gemtek-pci.c @@ -46,6 +46,7 @@ #include <linux/pci.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/errno.h> #include <linux/version.h> /* for KERNEL_VERSION MACRO */ @@ -374,11 +375,7 @@ static const struct file_operations gemtek_pci_fops = { .llseek = no_llseek, }; -static struct video_device vdev_template = { - .owner = THIS_MODULE, - .name = "Gemtek PCI Radio", - .type = VID_TYPE_TUNER, - .fops = &gemtek_pci_fops, +static const struct v4l2_ioctl_ops gemtek_pci_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, @@ -393,6 +390,12 @@ static struct video_device vdev_template = { .vidioc_s_ctrl = vidioc_s_ctrl, }; +static struct video_device vdev_template = { + .name = "Gemtek PCI Radio", + .fops = &gemtek_pci_fops, + .ioctl_ops = &gemtek_pci_ioctl_ops, +}; + static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci_device_id *pci_id ) { struct gemtek_pci_card *card; diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c index 81f6aeb1cd11..2b1a6221de6d 100644 --- a/drivers/media/radio/radio-gemtek.c +++ b/drivers/media/radio/radio-gemtek.c @@ -23,6 +23,7 @@ #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ #include <linux/videodev2.h> /* kernel radio structs */ +#include <media/v4l2-ioctl.h> #include <media/v4l2-common.h> #include <linux/spinlock.h> @@ -552,11 +553,7 @@ static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) return 0; } -static struct video_device gemtek_radio = { - .owner = THIS_MODULE, - .name = "GemTek Radio card", - .type = VID_TYPE_TUNER, - .fops = &gemtek_fops, +static const struct v4l2_ioctl_ops gemtek_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, @@ -571,6 +568,12 @@ static struct video_device gemtek_radio = { .vidioc_s_ctrl = vidioc_s_ctrl }; +static struct video_device gemtek_radio = { + .name = "GemTek Radio card", + .fops = &gemtek_fops, + .ioctl_ops = &gemtek_ioctl_ops, +}; + /* * Initialization / cleanup related stuff. */ diff --git a/drivers/media/radio/radio-maestro.c b/drivers/media/radio/radio-maestro.c index bddd3c409aa9..0ada1c697e8a 100644 --- a/drivers/media/radio/radio-maestro.c +++ b/drivers/media/radio/radio-maestro.c @@ -27,6 +27,7 @@ #include <linux/pci.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/version.h> /* for KERNEL_VERSION MACRO */ #define RADIO_VERSION KERNEL_VERSION(0,0,6) @@ -354,10 +355,7 @@ 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, +static const struct v4l2_ioctl_ops maestro_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, @@ -372,6 +370,12 @@ static struct video_device maestro_radio = { .vidioc_s_ctrl = vidioc_s_ctrl, }; +static struct video_device maestro_radio = { + .name = "Maestro radio", + .fops = &maestro_fops, + .ioctl_ops = &maestro_ioctl_ops, +}; + 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 0133ecf3e040..43c75497dc49 100644 --- a/drivers/media/radio/radio-maxiradio.c +++ b/drivers/media/radio/radio-maxiradio.c @@ -44,6 +44,7 @@ #include <linux/pci.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #define DRIVER_VERSION "0.77" @@ -373,13 +374,7 @@ static int vidioc_s_ctrl (struct file *file, void *priv, return -EINVAL; } -static struct video_device maxiradio_radio = -{ - .owner = THIS_MODULE, - .name = "Maxi Radio FM2000 radio", - .type = VID_TYPE_TUNER, - .fops = &maxiradio_fops, - +static const struct v4l2_ioctl_ops maxiradio_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, @@ -394,6 +389,12 @@ static struct video_device maxiradio_radio = .vidioc_s_ctrl = vidioc_s_ctrl, }; +static struct video_device maxiradio_radio = { + .name = "Maxi Radio FM2000 radio", + .fops = &maxiradio_fops, + .ioctl_ops = &maxiradio_ioctl_ops, +}; + static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { if(!request_region(pci_resource_start(pdev, 0), diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c index 070802103dc3..e2dde0807268 100644 --- a/drivers/media/radio/radio-rtrack2.c +++ b/drivers/media/radio/radio-rtrack2.c @@ -17,6 +17,7 @@ #include <asm/uaccess.h> /* copy to/from user */ #include <linux/videodev2.h> /* kernel radio structs */ #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/spinlock.h> #include <linux/version.h> /* for KERNEL_VERSION MACRO */ @@ -294,12 +295,7 @@ static const struct file_operations rtrack2_fops = { .llseek = no_llseek, }; -static struct video_device rtrack2_radio= -{ - .owner = THIS_MODULE, - .name = "RadioTrack II radio", - .type = VID_TYPE_TUNER, - .fops = &rtrack2_fops, +static const struct v4l2_ioctl_ops rtrack2_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, @@ -314,6 +310,12 @@ static struct video_device rtrack2_radio= .vidioc_s_input = vidioc_s_input, }; +static struct video_device rtrack2_radio = { + .name = "RadioTrack II radio", + .fops = &rtrack2_fops, + .ioctl_ops = &rtrack2_ioctl_ops, +}; + static int __init rtrack2_init(void) { if(io==-1) diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index 66e052fd3909..bb5d92f104af 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c @@ -24,6 +24,7 @@ #include <linux/delay.h> /* udelay */ #include <linux/videodev2.h> /* kernel radio structs */ #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/isapnp.h> #include <asm/io.h> /* outb, outb_p */ #include <asm/uaccess.h> /* copy to/from user */ @@ -294,12 +295,7 @@ static const struct file_operations fmi_fops = { .llseek = no_llseek, }; -static struct video_device fmi_radio= -{ - .owner = THIS_MODULE, - .name = "SF16FMx radio", - .type = VID_TYPE_TUNER, - .fops = &fmi_fops, +static const struct v4l2_ioctl_ops fmi_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, @@ -314,6 +310,12 @@ static struct video_device fmi_radio= .vidioc_s_ctrl = vidioc_s_ctrl, }; +static struct video_device fmi_radio = { + .name = "SF16FMx radio", + .fops = &fmi_fops, + .ioctl_ops = &fmi_ioctl_ops, +}; + /* ladis: this is my card. does any other types exist? */ static struct isapnp_device_id id_table[] __devinitdata = { { ISAPNP_ANY_ID, ISAPNP_ANY_ID, diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c index b0ccf7cb5952..6290553d24be 100644 --- a/drivers/media/radio/radio-sf16fmr2.c +++ b/drivers/media/radio/radio-sf16fmr2.c @@ -22,6 +22,7 @@ #include <asm/uaccess.h> /* copy to/from user */ #include <linux/videodev2.h> /* kernel radio structs */ #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/mutex.h> static struct mutex lock; @@ -410,12 +411,7 @@ static const struct file_operations fmr2_fops = { .llseek = no_llseek, }; -static struct video_device fmr2_radio= -{ - .owner = THIS_MODULE, - .name = "SF16FMR2 radio", - . type = VID_TYPE_TUNER, - .fops = &fmr2_fops, +static const struct v4l2_ioctl_ops fmr2_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, @@ -430,6 +426,12 @@ static struct video_device fmr2_radio= .vidioc_s_ctrl = vidioc_s_ctrl, }; +static struct video_device fmr2_radio = { + .name = "SF16FMR2 radio", + .fops = &fmr2_fops, + .ioctl_ops = &fmr2_ioctl_ops, +}; + static int __init fmr2_init(void) { fmr2_unit.port = io; diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c index dc93a882b385..a4984ff87c9c 100644 --- a/drivers/media/radio/radio-si470x.c +++ b/drivers/media/radio/radio-si470x.c @@ -133,6 +133,7 @@ #include <linux/videodev2.h> #include <linux/mutex.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <media/rds.h> #include <asm/unaligned.h> @@ -1585,15 +1586,7 @@ done: return retval; } - -/* - * si470x_viddev_tamples - video device interface - */ -static struct video_device si470x_viddev_template = { - .fops = &si470x_fops, - .name = DRIVER_NAME, - .type = VID_TYPE_TUNER, - .release = video_device_release, +static const struct v4l2_ioctl_ops si470x_ioctl_ops = { .vidioc_querycap = si470x_vidioc_querycap, .vidioc_g_input = si470x_vidioc_g_input, .vidioc_s_input = si470x_vidioc_s_input, @@ -1607,7 +1600,16 @@ static struct video_device si470x_viddev_template = { .vidioc_g_frequency = si470x_vidioc_g_frequency, .vidioc_s_frequency = si470x_vidioc_s_frequency, .vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek, - .owner = THIS_MODULE, +}; + +/* + * si470x_viddev_tamples - video device interface + */ +static struct video_device si470x_viddev_template = { + .fops = &si470x_fops, + .ioctl_ops = &si470x_ioctl_ops, + .name = DRIVER_NAME, + .release = video_device_release, }; diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c index acc32080e9bd..cefa44fc5aed 100644 --- a/drivers/media/radio/radio-terratec.c +++ b/drivers/media/radio/radio-terratec.c @@ -32,6 +32,7 @@ #include <asm/uaccess.h> /* copy to/from user */ #include <linux/videodev2.h> /* kernel radio structs */ #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/spinlock.h> #include <linux/version.h> /* for KERNEL_VERSION MACRO */ @@ -366,12 +367,7 @@ static const struct file_operations terratec_fops = { .llseek = no_llseek, }; -static struct video_device terratec_radio= -{ - .owner = THIS_MODULE, - .name = "TerraTec ActiveRadio", - .type = VID_TYPE_TUNER, - .fops = &terratec_fops, +static const struct v4l2_ioctl_ops terratec_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, @@ -386,6 +382,12 @@ static struct video_device terratec_radio= .vidioc_s_input = vidioc_s_input, }; +static struct video_device terratec_radio = { + .name = "TerraTec ActiveRadio", + .fops = &terratec_fops, + .ioctl_ops = &terratec_ioctl_ops, +}; + static int __init terratec_init(void) { if(io==-1) diff --git a/drivers/media/radio/radio-trust.c b/drivers/media/radio/radio-trust.c index 4ebdfbadeb9c..d70172d23edb 100644 --- a/drivers/media/radio/radio-trust.c +++ b/drivers/media/radio/radio-trust.c @@ -23,6 +23,7 @@ #include <asm/uaccess.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/version.h> /* for KERNEL_VERSION MACRO */ #define RADIO_VERSION KERNEL_VERSION(0,0,2) @@ -346,12 +347,7 @@ static const struct file_operations trust_fops = { .llseek = no_llseek, }; -static struct video_device trust_radio= -{ - .owner = THIS_MODULE, - .name = "Trust FM Radio", - .type = VID_TYPE_TUNER, - .fops = &trust_fops, +static const struct v4l2_ioctl_ops trust_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, @@ -366,6 +362,12 @@ static struct video_device trust_radio= .vidioc_s_input = vidioc_s_input, }; +static struct video_device trust_radio = { + .name = "Trust FM Radio", + .fops = &trust_fops, + .ioctl_ops = &trust_ioctl_ops, +}; + static int __init trust_init(void) { if(io == -1) { diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c index 18f2abd7e255..f8d62cfea774 100644 --- a/drivers/media/radio/radio-typhoon.c +++ b/drivers/media/radio/radio-typhoon.c @@ -40,6 +40,7 @@ #include <asm/uaccess.h> /* copy to/from user */ #include <linux/videodev2.h> /* kernel radio structs */ #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/version.h> /* for KERNEL_VERSION MACRO */ #define RADIO_VERSION KERNEL_VERSION(0,1,1) @@ -344,12 +345,7 @@ static const struct file_operations typhoon_fops = { .llseek = no_llseek, }; -static struct video_device typhoon_radio = -{ - .owner = THIS_MODULE, - .name = "Typhoon Radio", - .type = VID_TYPE_TUNER, - .fops = &typhoon_fops, +static const struct v4l2_ioctl_ops typhoon_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, @@ -364,6 +360,12 @@ static struct video_device typhoon_radio = .vidioc_s_ctrl = vidioc_s_ctrl, }; +static struct video_device typhoon_radio = { + .name = "Typhoon Radio", + .fops = &typhoon_fops, + .ioctl_ops = &typhoon_ioctl_ops, +}; + #ifdef CONFIG_RADIO_TYPHOON_PROC_FS static int typhoon_proc_show(struct seq_file *m, void *v) diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c index 43773c56c62f..9f17a332fa11 100644 --- a/drivers/media/radio/radio-zoltrix.c +++ b/drivers/media/radio/radio-zoltrix.c @@ -37,6 +37,7 @@ #include <asm/uaccess.h> /* copy to/from user */ #include <linux/videodev2.h> /* kernel radio structs */ #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/version.h> /* for KERNEL_VERSION MACRO */ #define RADIO_VERSION KERNEL_VERSION(0,0,2) @@ -407,12 +408,7 @@ static const struct file_operations zoltrix_fops = .llseek = no_llseek, }; -static struct video_device zoltrix_radio = -{ - .owner = THIS_MODULE, - .name = "Zoltrix Radio Plus", - .type = VID_TYPE_TUNER, - .fops = &zoltrix_fops, +static const struct v4l2_ioctl_ops zoltrix_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, @@ -427,6 +423,12 @@ static struct video_device zoltrix_radio = .vidioc_s_ctrl = vidioc_s_ctrl, }; +static struct video_device zoltrix_radio = { + .name = "Zoltrix Radio Plus", + .fops = &zoltrix_fops, + .ioctl_ops = &zoltrix_ioctl_ops, +}; + static int __init zoltrix_init(void) { if (io == -1) { diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index f606d2951fde..d4a6e56a7135 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -487,17 +487,6 @@ config VIDEO_PMS To compile this driver as a module, choose M here: the module will be called pms. -config VIDEO_PLANB - tristate "PlanB Video-In on PowerMac" - depends on PPC_PMAC && VIDEO_V4L1 && BROKEN - help - PlanB is the V4L driver for the PowerMac 7x00/8x00 series video - input hardware. If you want to experiment with this, say Y. - Otherwise, or if you don't understand a word, say N. See - <http://www.cpu.lu/~mlan/linux/dev/planb.html> for more info. - - Saying M will compile this driver as a module (planb). - config VIDEO_BWQCAM tristate "Quickcam BW Video For Linux" depends on PARPORT && VIDEO_V4L1 @@ -806,13 +795,7 @@ menuconfig V4L_USB_DRIVERS if V4L_USB_DRIVERS && USB -config USB_VIDEO_CLASS - tristate "USB Video Class (UVC)" - ---help--- - Support for the USB Video Class (UVC). Currently only video - input devices, such as webcams, are supported. - - For more information see: <http://linux-uvc.berlios.de/> +source "drivers/media/video/uvc/Kconfig" source "drivers/media/video/gspca/Kconfig" diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 45d5db5abb1e..bbc6f8b82297 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -10,6 +10,8 @@ msp3400-objs := msp3400-driver.o msp3400-kthreads.o stkwebcam-objs := stk-webcam.o stk-sensor.o +videodev-objs := v4l2-dev.o v4l2-ioctl.o + obj-$(CONFIG_VIDEO_DEV) += videodev.o compat_ioctl32.o v4l2-int-device.o obj-$(CONFIG_VIDEO_V4L2_COMMON) += v4l2-common.o @@ -55,7 +57,6 @@ obj-$(CONFIG_VIDEO_ZORAN_DC30) += zr36050.o zr36016.o obj-$(CONFIG_VIDEO_ZORAN_ZR36060) += zr36060.o obj-$(CONFIG_VIDEO_PMS) += pms.o -obj-$(CONFIG_VIDEO_PLANB) += planb.o obj-$(CONFIG_VIDEO_VINO) += vino.o indycam.o obj-$(CONFIG_VIDEO_STRADIS) += stradis.o obj-$(CONFIG_VIDEO_CPIA) += cpia.o diff --git a/drivers/media/video/arv.c b/drivers/media/video/arv.c index 8c7d1958856b..56ebfd5ef6fa 100644 --- a/drivers/media/video/arv.c +++ b/drivers/media/video/arv.c @@ -754,7 +754,6 @@ static const struct file_operations ar_fops = { }; static struct video_device ar_template = { - .owner = THIS_MODULE, .name = "Colour AR VGA", .type = VID_TYPE_CAPTURE, .fops = &ar_fops, diff --git a/drivers/media/video/au0828/Kconfig b/drivers/media/video/au0828/Kconfig index 52b2491581a8..ed9a50f189fc 100644 --- a/drivers/media/video/au0828/Kconfig +++ b/drivers/media/video/au0828/Kconfig @@ -6,6 +6,7 @@ config VIDEO_AU0828 select VIDEO_TVEEPROM select DVB_AU8522 if !DVB_FE_CUSTOMIZE select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE + select MEDIA_TUNER_MXL5007T if !DVB_FE_CUSTOMIZE ---help--- This is a video4linux driver for Auvitek's USB device. diff --git a/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c index 898e12395e7c..443e59009762 100644 --- a/drivers/media/video/au0828/au0828-cards.c +++ b/drivers/media/video/au0828/au0828-cards.c @@ -32,6 +32,9 @@ struct au0828_board au0828_boards[] = { [AU0828_BOARD_HAUPPAUGE_HVR950Q] = { .name = "Hauppauge HVR950Q", }, + [AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL] = { + .name = "Hauppauge HVR950Q rev xxF8", + }, [AU0828_BOARD_DVICO_FUSIONHDTV7] = { .name = "DViCO FusionHDTV USB", }, @@ -49,6 +52,7 @@ int au0828_tuner_callback(void *priv, int command, int arg) switch (dev->board) { case AU0828_BOARD_HAUPPAUGE_HVR850: case AU0828_BOARD_HAUPPAUGE_HVR950Q: + case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: case AU0828_BOARD_DVICO_FUSIONHDTV7: if (command == 0) { /* Tuner Reset Command from xc5000 */ @@ -110,6 +114,7 @@ void au0828_card_setup(struct au0828_dev *dev) switch (dev->board) { case AU0828_BOARD_HAUPPAUGE_HVR850: case AU0828_BOARD_HAUPPAUGE_HVR950Q: + case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: if (dev->i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xa0); break; @@ -128,6 +133,7 @@ void au0828_gpio_setup(struct au0828_dev *dev) switch (dev->board) { case AU0828_BOARD_HAUPPAUGE_HVR850: case AU0828_BOARD_HAUPPAUGE_HVR950Q: + case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: /* GPIO's * 4 - CS5340 * 5 - AU8522 Demodulator @@ -193,6 +199,12 @@ struct usb_device_id au0828_usb_id_table [] = { .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, { USB_DEVICE(0x0fd9, 0x0008), .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x7201), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, + { USB_DEVICE(0x2040, 0x7211), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, + { USB_DEVICE(0x2040, 0x7281), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, { }, }; diff --git a/drivers/media/video/au0828/au0828-cards.h b/drivers/media/video/au0828/au0828-cards.h index e26f54a961d0..c37f5fd0fa80 100644 --- a/drivers/media/video/au0828/au0828-cards.h +++ b/drivers/media/video/au0828/au0828-cards.h @@ -23,3 +23,4 @@ #define AU0828_BOARD_HAUPPAUGE_HVR950Q 1 #define AU0828_BOARD_HAUPPAUGE_HVR850 2 #define AU0828_BOARD_DVICO_FUSIONHDTV7 3 +#define AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL 4 diff --git a/drivers/media/video/au0828/au0828-dvb.c b/drivers/media/video/au0828/au0828-dvb.c index c6d470590380..584a83a94a2a 100644 --- a/drivers/media/video/au0828/au0828-dvb.c +++ b/drivers/media/video/au0828/au0828-dvb.c @@ -28,6 +28,7 @@ #include "au0828.h" #include "au8522.h" #include "xc5000.h" +#include "mxl5007t.h" DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); @@ -45,6 +46,11 @@ static struct xc5000_config hauppauge_hvr950q_tunerconfig = { .tuner_callback = au0828_tuner_callback }; +static struct mxl5007t_config mxl5007t_hvr950q_config = { + .xtal_freq_hz = MxL_XTAL_24_MHZ, + .if_freq_hz = MxL_IF_6_MHZ, +}; + /*-------------------------------------------------------------------*/ static void urb_completion(struct urb *purb) { @@ -342,6 +348,15 @@ int au0828_dvb_register(struct au0828_dev *dev) &dev->i2c_adap, &hauppauge_hvr950q_tunerconfig, dev); break; + case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: + dvb->frontend = dvb_attach(au8522_attach, + &hauppauge_hvr950q_config, + &dev->i2c_adap); + if (dvb->frontend != NULL) + dvb_attach(mxl5007t_attach, dvb->frontend, + &dev->i2c_adap, 0x60, + &mxl5007t_hvr950q_config); + break; default: printk(KERN_WARNING "The frontend of your DVB/ATSC card " "isn't supported yet\n"); diff --git a/drivers/media/video/bt8xx/Kconfig b/drivers/media/video/bt8xx/Kconfig index 24a34fc1f2b3..ce71e8e7b835 100644 --- a/drivers/media/video/bt8xx/Kconfig +++ b/drivers/media/video/bt8xx/Kconfig @@ -1,9 +1,7 @@ config VIDEO_BT848 tristate "BT848 Video For Linux" depends on VIDEO_DEV && PCI && I2C && VIDEO_V4L2 && INPUT - depends on HOTPLUG # due to FW_LOADER select I2C_ALGOBIT - select FW_LOADER select VIDEO_BTCX select VIDEOBUF_DMA_SG select VIDEO_IR diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 0ea559a7fe59..85bf31ab8789 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -45,6 +45,7 @@ #include <linux/kdev_t.h> #include "bttvp.h" #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <media/tvaudio.h> #include <media/msp3400.h> @@ -163,8 +164,8 @@ MODULE_LICENSE("GPL"); static ssize_t show_card(struct device *cd, struct device_attribute *attr, char *buf) { - struct video_device *vfd = container_of(cd, struct video_device, class_dev); - struct bttv *btv = dev_get_drvdata(vfd->dev); + struct video_device *vfd = container_of(cd, struct video_device, dev); + struct bttv *btv = dev_get_drvdata(vfd->parent); return sprintf(buf, "%d\n", btv ? btv->c.type : UNSET); } static DEVICE_ATTR(card, S_IRUGO, show_card, NULL); @@ -3357,10 +3358,7 @@ static const struct file_operations bttv_fops = .poll = bttv_poll, }; -static struct video_device bttv_video_template = -{ - .fops = &bttv_fops, - .minor = -1, +static const struct v4l2_ioctl_ops bttv_ioctl_ops = { .vidioc_querycap = bttv_querycap, .vidioc_enum_fmt_vid_cap = bttv_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = bttv_g_fmt_vid_cap, @@ -3411,8 +3409,14 @@ static struct video_device bttv_video_template = .vidioc_g_register = bttv_g_register, .vidioc_s_register = bttv_s_register, #endif - .tvnorms = BTTV_NORMS, - .current_norm = V4L2_STD_PAL, +}; + +static struct video_device bttv_video_template = { + .fops = &bttv_fops, + .minor = -1, + .ioctl_ops = &bttv_ioctl_ops, + .tvnorms = BTTV_NORMS, + .current_norm = V4L2_STD_PAL, }; /* ----------------------------------------------------------------------- */ @@ -3635,10 +3639,7 @@ static const struct file_operations radio_fops = .poll = radio_poll, }; -static struct video_device radio_template = -{ - .fops = &radio_fops, - .minor = -1, +static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_querycap = radio_querycap, .vidioc_g_tuner = radio_g_tuner, .vidioc_enum_input = radio_enum_input, @@ -3655,6 +3656,12 @@ static struct video_device radio_template = .vidioc_s_frequency = bttv_s_frequency, }; +static struct video_device radio_template = { + .fops = &radio_fops, + .minor = -1, + .ioctl_ops = &radio_ioctl_ops, +}; + /* ----------------------------------------------------------------------- */ /* some debug code */ @@ -4175,8 +4182,7 @@ static irqreturn_t bttv_irq(int irq, void *dev_id) static struct video_device *vdev_init(struct bttv *btv, const struct video_device *template, - const char *type_name, - const int type) + const char *type_name) { struct video_device *vfd; @@ -4185,9 +4191,8 @@ static struct video_device *vdev_init(struct bttv *btv, return NULL; *vfd = *template; vfd->minor = -1; - vfd->dev = &btv->c.pci->dev; + vfd->parent = &btv->c.pci->dev; vfd->release = video_device_release; - vfd->type = type; vfd->debug = bttv_debug; snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)", btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "", @@ -4223,20 +4228,11 @@ static void bttv_unregister_video(struct bttv *btv) /* register video4linux devices */ static int __devinit bttv_register_video(struct bttv *btv) { - int video_type = VID_TYPE_CAPTURE | - VID_TYPE_TUNER | - VID_TYPE_CLIPPING| - VID_TYPE_SCALES; - - if (no_overlay <= 0) { - bttv_video_template.type |= VID_TYPE_OVERLAY; - } else { + if (no_overlay > 0) printk("bttv: Overlay support disabled.\n"); - } /* video */ - btv->video_dev = vdev_init(btv, &bttv_video_template, - "video", video_type); + btv->video_dev = vdev_init(btv, &bttv_video_template, "video"); if (NULL == btv->video_dev) goto err; @@ -4244,7 +4240,7 @@ static int __devinit bttv_register_video(struct bttv *btv) goto err; printk(KERN_INFO "bttv%d: registered device video%d\n", btv->c.nr,btv->video_dev->minor & 0x1f); - if (device_create_file(&btv->video_dev->class_dev, + if (device_create_file(&btv->video_dev->dev, &dev_attr_card)<0) { printk(KERN_ERR "bttv%d: device_create_file 'card' " "failed\n", btv->c.nr); @@ -4252,8 +4248,7 @@ static int __devinit bttv_register_video(struct bttv *btv) } /* vbi */ - btv->vbi_dev = vdev_init(btv, &bttv_video_template, - "vbi", VID_TYPE_TUNER | VID_TYPE_TELETEXT); + btv->vbi_dev = vdev_init(btv, &bttv_video_template, "vbi"); if (NULL == btv->vbi_dev) goto err; @@ -4265,8 +4260,7 @@ static int __devinit bttv_register_video(struct bttv *btv) if (!btv->has_radio) return 0; /* radio */ - btv->radio_dev = vdev_init(btv, &radio_template, - "radio", VID_TYPE_TUNER); + btv->radio_dev = vdev_init(btv, &radio_template, "radio"); if (NULL == btv->radio_dev) goto err; if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,radio_nr)<0) diff --git a/drivers/media/video/bt8xx/bttv-risc.c b/drivers/media/video/bt8xx/bttv-risc.c index 0af586876e72..649682aac1ac 100644 --- a/drivers/media/video/bt8xx/bttv-risc.c +++ b/drivers/media/video/bt8xx/bttv-risc.c @@ -31,6 +31,7 @@ #include <linux/interrupt.h> #include <asm/page.h> #include <asm/pgtable.h> +#include <media/v4l2-ioctl.h> #include "bttvp.h" diff --git a/drivers/media/video/bt8xx/bttv-vbi.c b/drivers/media/video/bt8xx/bttv-vbi.c index 68f28e5fa040..6819e21a3773 100644 --- a/drivers/media/video/bt8xx/bttv-vbi.c +++ b/drivers/media/video/bt8xx/bttv-vbi.c @@ -29,6 +29,7 @@ #include <linux/kernel.h> #include <linux/interrupt.h> #include <linux/kdev_t.h> +#include <media/v4l2-ioctl.h> #include <asm/io.h> #include "bttvp.h" diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c index b364adaae78d..d3b3268bace8 100644 --- a/drivers/media/video/bw-qcam.c +++ b/drivers/media/video/bw-qcam.c @@ -74,6 +74,7 @@ OTHER DEALINGS IN THE SOFTWARE. #include <linux/sched.h> #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/mutex.h> #include <asm/uaccess.h> @@ -906,9 +907,7 @@ static const struct file_operations qcam_fops = { }; static struct video_device qcam_template= { - .owner = THIS_MODULE, .name = "Connectix Quickcam", - .type = VID_TYPE_CAPTURE, .fops = &qcam_fops, }; diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c index fe1e67bb1ca8..fe9379b282d3 100644 --- a/drivers/media/video/c-qcam.c +++ b/drivers/media/video/c-qcam.c @@ -35,6 +35,7 @@ #include <linux/sched.h> #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/mutex.h> #include <linux/jiffies.h> @@ -701,9 +702,7 @@ static const struct file_operations qcam_fops = { static struct video_device qcam_template= { - .owner = THIS_MODULE, .name = "Colour QuickCam", - .type = VID_TYPE_CAPTURE, .fops = &qcam_fops, }; diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index d99453faaab7..c149b7d712e5 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -25,6 +25,7 @@ #include <linux/spinlock.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <media/v4l2-chip-ident.h> #include <linux/device.h> #include <linux/wait.h> @@ -1768,17 +1769,7 @@ static const struct file_operations cafe_v4l_fops = { .llseek = no_llseek, }; -static struct video_device cafe_v4l_template = { - .name = "cafe", - .type = VFL_TYPE_GRABBER, - .type2 = VID_TYPE_CAPTURE, - .minor = -1, /* Get one dynamically */ - .tvnorms = V4L2_STD_NTSC_M, - .current_norm = V4L2_STD_NTSC_M, /* make mplayer happy */ - - .fops = &cafe_v4l_fops, - .release = cafe_v4l_dev_release, - +static const struct v4l2_ioctl_ops cafe_v4l_ioctl_ops = { .vidioc_querycap = cafe_vidioc_querycap, .vidioc_enum_fmt_vid_cap = cafe_vidioc_enum_fmt_vid_cap, .vidioc_try_fmt_vid_cap = cafe_vidioc_try_fmt_vid_cap, @@ -1801,6 +1792,17 @@ static struct video_device cafe_v4l_template = { .vidioc_s_parm = cafe_vidioc_s_parm, }; +static struct video_device cafe_v4l_template = { + .name = "cafe", + .minor = -1, /* Get one dynamically */ + .tvnorms = V4L2_STD_NTSC_M, + .current_norm = V4L2_STD_NTSC_M, /* make mplayer happy */ + + .fops = &cafe_v4l_fops, + .ioctl_ops = &cafe_v4l_ioctl_ops, + .release = cafe_v4l_dev_release, +}; + @@ -2157,7 +2159,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; + cam->v4ldev.parent = &pdev->dev; ret = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, -1); if (ret) goto out_smbus; diff --git a/drivers/media/video/compat_ioctl32.c b/drivers/media/video/compat_ioctl32.c index 54de0cd482e9..bd5d9de5a008 100644 --- a/drivers/media/video/compat_ioctl32.c +++ b/drivers/media/video/compat_ioctl32.c @@ -17,7 +17,7 @@ #include <linux/videodev2.h> #include <linux/module.h> #include <linux/smp_lock.h> -#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #ifdef CONFIG_COMPAT diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c index 2a81376ef503..dc8cc6115e2f 100644 --- a/drivers/media/video/cpia.c +++ b/drivers/media/video/cpia.c @@ -3799,9 +3799,7 @@ static const struct file_operations cpia_fops = { }; static struct video_device cpia_template = { - .owner = THIS_MODULE, .name = "CPiA Camera", - .type = VID_TYPE_CAPTURE, .fops = &cpia_fops, }; diff --git a/drivers/media/video/cpia.h b/drivers/media/video/cpia.h index 5096058bf579..8f0cfee4b8a1 100644 --- a/drivers/media/video/cpia.h +++ b/drivers/media/video/cpia.h @@ -46,6 +46,7 @@ #include <asm/uaccess.h> #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/list.h> #include <linux/mutex.h> diff --git a/drivers/media/video/cpia2/cpia2_core.c b/drivers/media/video/cpia2/cpia2_core.c index f2e8b1c82c66..af8b9ec8e358 100644 --- a/drivers/media/video/cpia2/cpia2_core.c +++ b/drivers/media/video/cpia2/cpia2_core.c @@ -32,6 +32,7 @@ #include "cpia2.h" #include <linux/slab.h> +#include <linux/mm.h> #include <linux/vmalloc.h> #include <linux/firmware.h> diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index 7ce2789fa976..515c8b57a60d 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -37,6 +37,7 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/init.h> +#include <media/v4l2-ioctl.h> #include "cpia2.h" #include "cpia2dev.h" @@ -1935,11 +1936,7 @@ static const struct file_operations fops_template = { static struct video_device cpia2_template = { /* I could not find any place for the old .initialize initializer?? */ - .owner= THIS_MODULE, .name= "CPiA2 Camera", - .type= VID_TYPE_CAPTURE, - .type2 = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING, .minor= -1, .fops= &fops_template, .release= video_device_release, diff --git a/drivers/media/video/cs5345.c b/drivers/media/video/cs5345.c index 1c3fa3a7470a..61d14d26686f 100644 --- a/drivers/media/video/cs5345.c +++ b/drivers/media/video/cs5345.c @@ -111,7 +111,7 @@ static int cs5345_command(struct i2c_client *client, unsigned cmd, void *arg) if (cmd == VIDIOC_DBG_G_REGISTER) reg->val = cs5345_read(client, reg->reg & 0x1f); else - cs5345_write(client, reg->reg & 0x1f, reg->val & 0x1f); + cs5345_write(client, reg->reg & 0x1f, reg->val & 0xff); break; } #endif diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c index 645b339152d3..e30a589c0e18 100644 --- a/drivers/media/video/cs53l32a.c +++ b/drivers/media/video/cs53l32a.c @@ -26,7 +26,7 @@ #include <asm/uaccess.h> #include <linux/i2c.h> #include <linux/i2c-id.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv-legacy.h> diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig index 9aefdc5ea79a..ef48565de7f1 100644 --- a/drivers/media/video/cx18/Kconfig +++ b/drivers/media/video/cx18/Kconfig @@ -2,9 +2,7 @@ config VIDEO_CX18 tristate "Conexant cx23418 MPEG encoder support" depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C && EXPERIMENTAL depends on INPUT # due to VIDEO_IR - depends on HOTPLUG # due to FW_LOADER select I2C_ALGOBIT - select FW_LOADER select VIDEO_IR select VIDEO_TUNER select VIDEO_TVEEPROM diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c index c40a286de1b9..0b55837880a7 100644 --- a/drivers/media/video/cx18/cx18-av-audio.c +++ b/drivers/media/video/cx18/cx18-av-audio.c @@ -30,7 +30,6 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) if (freq != 32000 && freq != 44100 && freq != 48000) return -EINVAL; - /* common for all inputs and rates */ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */ cx18_av_write(cx, 0x127, 0x50); @@ -38,15 +37,30 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) switch (freq) { case 32000: /* VID_PLL and AUX_PLL */ - cx18_av_write4(cx, 0x108, 0x1006040f); + cx18_av_write4(cx, 0x108, 0x1408040f); /* AUX_PLL_FRAC */ - cx18_av_write4(cx, 0x110, 0x01bb39ee); + /* 0x8.9504318a * 28,636,363.636 / 0x14 = 32000 * 384 */ + cx18_av_write4(cx, 0x110, 0x012a0863); - /* src3/4/6_ctl = 0x0801f77f */ + /* src3/4/6_ctl */ + /* 0x1.f77f = (4 * 15734.26) / 32000 */ cx18_av_write4(cx, 0x900, 0x0801f77f); cx18_av_write4(cx, 0x904, 0x0801f77f); cx18_av_write4(cx, 0x90c, 0x0801f77f); + + /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */ + cx18_av_write(cx, 0x127, 0x54); + + /* AUD_COUNT = 0x2fff = 8 samples * 4 * 384 - 1 */ + cx18_av_write4(cx, 0x12c, 0x11202fff); + + /* + * EN_AV_LOCK = 1 + * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 = + * ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8 + */ + cx18_av_write4(cx, 0x128, 0xa10d2ef8); break; case 44100: @@ -54,12 +68,24 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) cx18_av_write4(cx, 0x108, 0x1009040f); /* AUX_PLL_FRAC */ - cx18_av_write4(cx, 0x110, 0x00ec6bd6); + /* 0x9.7635e7 * 28,636,363.63 / 0x10 = 44100 * 384 */ + cx18_av_write4(cx, 0x110, 0x00ec6bce); - /* src3/4/6_ctl = 0x08016d59 */ + /* src3/4/6_ctl */ + /* 0x1.6d59 = (4 * 15734.26) / 44100 */ cx18_av_write4(cx, 0x900, 0x08016d59); cx18_av_write4(cx, 0x904, 0x08016d59); cx18_av_write4(cx, 0x90c, 0x08016d59); + + /* AUD_COUNT = 0x92ff = 49 samples * 2 * 384 - 1 */ + cx18_av_write4(cx, 0x12c, 0x112092ff); + + /* + * EN_AV_LOCK = 1 + * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 = + * ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8 + */ + cx18_av_write4(cx, 0x128, 0xa11d4bf8); break; case 48000: @@ -67,12 +93,24 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) cx18_av_write4(cx, 0x108, 0x100a040f); /* AUX_PLL_FRAC */ - cx18_av_write4(cx, 0x110, 0x0098d6e5); + /* 0xa.4c6b6ea * 28,636,363.63 / 0x10 = 48000 * 384 */ + cx18_av_write4(cx, 0x110, 0x0098d6dd); - /* src3/4/6_ctl = 0x08014faa */ + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 15734.26) / 48000 */ cx18_av_write4(cx, 0x900, 0x08014faa); cx18_av_write4(cx, 0x904, 0x08014faa); cx18_av_write4(cx, 0x90c, 0x08014faa); + + /* AUD_COUNT = 0x5fff = 4 samples * 16 * 384 - 1 */ + cx18_av_write4(cx, 0x12c, 0x11205fff); + + /* + * EN_AV_LOCK = 1 + * VID_COUNT = 0x1193f8 = 143999.000 * 8 = + * ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8 + */ + cx18_av_write4(cx, 0x128, 0xa11193f8); break; } } else { @@ -82,18 +120,31 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) cx18_av_write4(cx, 0x108, 0x1e08040f); /* AUX_PLL_FRAC */ - cx18_av_write4(cx, 0x110, 0x012a0869); + /* 0x8.9504318 * 28,636,363.63 / 0x1e = 32000 * 256 */ + cx18_av_write4(cx, 0x110, 0x012a0863); - /* src1_ctl = 0x08010000 */ + /* src1_ctl */ + /* 0x1.0000 = 32000/32000 */ cx18_av_write4(cx, 0x8f8, 0x08010000); - /* src3/4/6_ctl = 0x08020000 */ + /* src3/4/6_ctl */ + /* 0x2.0000 = 2 * (32000/32000) */ cx18_av_write4(cx, 0x900, 0x08020000); cx18_av_write4(cx, 0x904, 0x08020000); cx18_av_write4(cx, 0x90c, 0x08020000); /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */ cx18_av_write(cx, 0x127, 0x54); + + /* AUD_COUNT = 0x1fff = 8 samples * 4 * 256 - 1 */ + cx18_av_write4(cx, 0x12c, 0x11201fff); + + /* + * EN_AV_LOCK = 1 + * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 = + * ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8 + */ + cx18_av_write4(cx, 0x128, 0xa10d2ef8); break; case 44100: @@ -101,15 +152,28 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) cx18_av_write4(cx, 0x108, 0x1809040f); /* AUX_PLL_FRAC */ - cx18_av_write4(cx, 0x110, 0x00ec6bd6); + /* 0x9.7635e74 * 28,636,363.63 / 0x18 = 44100 * 256 */ + cx18_av_write4(cx, 0x110, 0x00ec6bce); - /* src1_ctl = 0x08010000 */ + /* src1_ctl */ + /* 0x1.60cd = 44100/32000 */ cx18_av_write4(cx, 0x8f8, 0x080160cd); - /* src3/4/6_ctl = 0x08020000 */ + /* src3/4/6_ctl */ + /* 0x1.7385 = 2 * (32000/44100) */ cx18_av_write4(cx, 0x900, 0x08017385); cx18_av_write4(cx, 0x904, 0x08017385); cx18_av_write4(cx, 0x90c, 0x08017385); + + /* AUD_COUNT = 0x61ff = 49 samples * 2 * 256 - 1 */ + cx18_av_write4(cx, 0x12c, 0x112061ff); + + /* + * EN_AV_LOCK = 1 + * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 = + * ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8 + */ + cx18_av_write4(cx, 0x128, 0xa11d4bf8); break; case 48000: @@ -117,15 +181,28 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) cx18_av_write4(cx, 0x108, 0x180a040f); /* AUX_PLL_FRAC */ - cx18_av_write4(cx, 0x110, 0x0098d6e5); + /* 0xa.4c6b6ea * 28,636,363.63 / 0x18 = 48000 * 256 */ + cx18_av_write4(cx, 0x110, 0x0098d6dd); - /* src1_ctl = 0x08010000 */ + /* src1_ctl */ + /* 0x1.8000 = 48000/32000 */ cx18_av_write4(cx, 0x8f8, 0x08018000); - /* src3/4/6_ctl = 0x08020000 */ + /* src3/4/6_ctl */ + /* 0x1.5555 = 2 * (32000/48000) */ cx18_av_write4(cx, 0x900, 0x08015555); cx18_av_write4(cx, 0x904, 0x08015555); cx18_av_write4(cx, 0x90c, 0x08015555); + + /* AUD_COUNT = 0x3fff = 4 samples * 16 * 256 - 1 */ + cx18_av_write4(cx, 0x12c, 0x11203fff); + + /* + * EN_AV_LOCK = 1 + * VID_COUNT = 0x1193f8 = 143999.000 * 8 = + * ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8 + */ + cx18_av_write4(cx, 0x128, 0xa11193f8); break; } } diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index 45e31b04730e..4801bc7fb5b2 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -46,6 +46,7 @@ #include <linux/dvb/video.h> #include <linux/dvb/audio.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <media/tuner.h> #include "cx18-mailbox.h" #include "cx18-av-core.h" diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c index 2d630d9f7496..78fadd2ada5d 100644 --- a/drivers/media/video/cx18/cx18-firmware.c +++ b/drivers/media/video/cx18/cx18-firmware.c @@ -86,10 +86,6 @@ #define CX18_DSP0_INTERRUPT_MASK 0xd0004C -/* Encoder/decoder firmware sizes */ -#define CX18_FW_CPU_SIZE (158332) -#define CX18_FW_APU_SIZE (141200) - #define APU_ROM_SYNC1 0x6D676553 /* "mgeS" */ #define APU_ROM_SYNC2 0x72646548 /* "rdeH" */ @@ -100,35 +96,22 @@ struct cx18_apu_rom_seghdr { u32 size; }; -static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx, long size) +static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx) { const struct firmware *fw = NULL; - int retries = 3; int i, j; + unsigned size; u32 __iomem *dst = (u32 __iomem *)mem; const u32 *src; -retry: - if (!retries || request_firmware(&fw, fn, &cx->dev->dev)) { - CX18_ERR("Unable to open firmware %s (must be %ld bytes)\n", - fn, size); + if (request_firmware(&fw, fn, &cx->dev->dev)) { + CX18_ERR("Unable to open firmware %s\n", fn); CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n"); return -ENOMEM; } src = (const u32 *)fw->data; - if (fw->size != size) { - /* 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. */ - CX18_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 += 4096) { setup_page(i); for (j = i; j < fw->size && j < i + 4096; j += 4) { @@ -145,15 +128,16 @@ retry: } if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size); + size = fw->size; release_firmware(fw); return size; } -static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx, long size) +static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx) { const struct firmware *fw = NULL; - int retries = 3; int i, j; + unsigned size; const u32 *src; struct cx18_apu_rom_seghdr seghdr; const u8 *vers; @@ -161,10 +145,8 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx, u32 apu_version = 0; int sz; -retry: - if (!retries || request_firmware(&fw, fn, &cx->dev->dev)) { - CX18_ERR("unable to open firmware %s (must be %ld bytes)\n", - fn, size); + if (request_firmware(&fw, fn, &cx->dev->dev)) { + CX18_ERR("unable to open firmware %s\n", fn); CX18_ERR("did you put the firmware in the hotplug firmware directory?\n"); return -ENOMEM; } @@ -173,19 +155,8 @@ retry: vers = fw->data + sizeof(seghdr); sz = fw->size; - if (fw->size != size) { - /* 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. */ - CX18_INFO("retry: file loaded was not %s (expected size %ld, got %zd)\n", - fn, size, fw->size); - release_firmware(fw); - retries--; - goto retry; - } apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32]; - while (offset + sizeof(seghdr) < size) { + while (offset + sizeof(seghdr) < fw->size) { /* TODO: byteswapping */ memcpy(&seghdr, src + offset / 4, sizeof(seghdr)); offset += sizeof(seghdr); @@ -215,6 +186,7 @@ retry: if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) CX18_INFO("loaded %s firmware V%08x (%zd bytes)\n", fn, apu_version, fw->size); + size = fw->size; release_firmware(fw); /* Clear bit0 for APU to start from 0 */ write_reg(read_reg(0xc72030) & ~1, 0xc72030); @@ -340,7 +312,7 @@ int cx18_firmware_init(struct cx18 *cx) /* Only if the processor is not running */ if (read_reg(CX18_PROC_SOFT_RESET) & 8) { int sz = load_apu_fw_direct("v4l-cx23418-apu.fw", - cx->enc_mem, cx, CX18_FW_APU_SIZE); + cx->enc_mem, cx); write_enc(0xE51FF004, 0); write_enc(0xa00000, 4); /* todo: not hardcoded */ @@ -348,7 +320,7 @@ int cx18_firmware_init(struct cx18 *cx) cx18_msleep_timeout(500, 0); sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw", - cx->enc_mem, cx, CX18_FW_CPU_SIZE); + cx->enc_mem, cx); if (sz > 0) { int retries = 0; diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 0d74e59e503e..a7f839631d6a 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -787,50 +787,54 @@ int cx18_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, return res; } -void cx18_set_funcs(struct video_device *vdev) -{ - vdev->vidioc_querycap = cx18_querycap; - vdev->vidioc_g_priority = cx18_g_priority; - vdev->vidioc_s_priority = cx18_s_priority; - vdev->vidioc_s_audio = cx18_s_audio; - vdev->vidioc_g_audio = cx18_g_audio; - vdev->vidioc_enumaudio = cx18_enumaudio; - vdev->vidioc_enum_input = cx18_enum_input; - vdev->vidioc_cropcap = cx18_cropcap; - vdev->vidioc_s_crop = cx18_s_crop; - vdev->vidioc_g_crop = cx18_g_crop; - vdev->vidioc_g_input = cx18_g_input; - vdev->vidioc_s_input = cx18_s_input; - vdev->vidioc_g_frequency = cx18_g_frequency; - vdev->vidioc_s_frequency = cx18_s_frequency; - vdev->vidioc_s_tuner = cx18_s_tuner; - vdev->vidioc_g_tuner = cx18_g_tuner; - vdev->vidioc_g_enc_index = cx18_g_enc_index; - vdev->vidioc_g_std = cx18_g_std; - vdev->vidioc_s_std = cx18_s_std; - vdev->vidioc_log_status = cx18_log_status; - vdev->vidioc_enum_fmt_vid_cap = cx18_enum_fmt_vid_cap; - vdev->vidioc_encoder_cmd = cx18_encoder_cmd; - vdev->vidioc_try_encoder_cmd = cx18_try_encoder_cmd; - vdev->vidioc_g_fmt_vid_cap = cx18_g_fmt_vid_cap; - vdev->vidioc_g_fmt_vbi_cap = cx18_g_fmt_vbi_cap; - vdev->vidioc_g_fmt_sliced_vbi_cap = cx18_g_fmt_sliced_vbi_cap; - vdev->vidioc_s_fmt_vid_cap = cx18_s_fmt_vid_cap; - vdev->vidioc_s_fmt_vbi_cap = cx18_s_fmt_vbi_cap; - vdev->vidioc_s_fmt_sliced_vbi_cap = cx18_s_fmt_sliced_vbi_cap; - vdev->vidioc_try_fmt_vid_cap = cx18_try_fmt_vid_cap; - vdev->vidioc_try_fmt_vbi_cap = cx18_try_fmt_vbi_cap; - vdev->vidioc_try_fmt_sliced_vbi_cap = cx18_try_fmt_sliced_vbi_cap; - vdev->vidioc_g_sliced_vbi_cap = cx18_g_sliced_vbi_cap; - vdev->vidioc_g_chip_ident = cx18_g_chip_ident; +static const struct v4l2_ioctl_ops cx18_ioctl_ops = { + .vidioc_querycap = cx18_querycap, + .vidioc_g_priority = cx18_g_priority, + .vidioc_s_priority = cx18_s_priority, + .vidioc_s_audio = cx18_s_audio, + .vidioc_g_audio = cx18_g_audio, + .vidioc_enumaudio = cx18_enumaudio, + .vidioc_enum_input = cx18_enum_input, + .vidioc_cropcap = cx18_cropcap, + .vidioc_s_crop = cx18_s_crop, + .vidioc_g_crop = cx18_g_crop, + .vidioc_g_input = cx18_g_input, + .vidioc_s_input = cx18_s_input, + .vidioc_g_frequency = cx18_g_frequency, + .vidioc_s_frequency = cx18_s_frequency, + .vidioc_s_tuner = cx18_s_tuner, + .vidioc_g_tuner = cx18_g_tuner, + .vidioc_g_enc_index = cx18_g_enc_index, + .vidioc_g_std = cx18_g_std, + .vidioc_s_std = cx18_s_std, + .vidioc_log_status = cx18_log_status, + .vidioc_enum_fmt_vid_cap = cx18_enum_fmt_vid_cap, + .vidioc_encoder_cmd = cx18_encoder_cmd, + .vidioc_try_encoder_cmd = cx18_try_encoder_cmd, + .vidioc_g_fmt_vid_cap = cx18_g_fmt_vid_cap, + .vidioc_g_fmt_vbi_cap = cx18_g_fmt_vbi_cap, + .vidioc_g_fmt_sliced_vbi_cap = cx18_g_fmt_sliced_vbi_cap, + .vidioc_s_fmt_vid_cap = cx18_s_fmt_vid_cap, + .vidioc_s_fmt_vbi_cap = cx18_s_fmt_vbi_cap, + .vidioc_s_fmt_sliced_vbi_cap = cx18_s_fmt_sliced_vbi_cap, + .vidioc_try_fmt_vid_cap = cx18_try_fmt_vid_cap, + .vidioc_try_fmt_vbi_cap = cx18_try_fmt_vbi_cap, + .vidioc_try_fmt_sliced_vbi_cap = cx18_try_fmt_sliced_vbi_cap, + .vidioc_g_sliced_vbi_cap = cx18_g_sliced_vbi_cap, + .vidioc_g_chip_ident = cx18_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG - vdev->vidioc_g_register = cx18_g_register; - vdev->vidioc_s_register = cx18_s_register; + .vidioc_g_register = cx18_g_register, + .vidioc_s_register = cx18_s_register, #endif - vdev->vidioc_default = cx18_default; - vdev->vidioc_queryctrl = cx18_queryctrl; - vdev->vidioc_querymenu = cx18_querymenu; - vdev->vidioc_g_ext_ctrls = cx18_g_ext_ctrls; - vdev->vidioc_s_ext_ctrls = cx18_s_ext_ctrls; - vdev->vidioc_try_ext_ctrls = cx18_try_ext_ctrls; + .vidioc_default = cx18_default, + .vidioc_queryctrl = cx18_queryctrl, + .vidioc_querymenu = cx18_querymenu, + .vidioc_g_ext_ctrls = cx18_g_ext_ctrls, + .vidioc_s_ext_ctrls = cx18_s_ext_ctrls, + .vidioc_try_ext_ctrls = cx18_try_ext_ctrls, +}; + +void cx18_set_funcs(struct video_device *vdev) +{ + vdev->ioctl_ops = &cx18_ioctl_ops; } diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 1728b1d832a9..0da57f583bf7 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -187,14 +187,11 @@ static int cx18_prep_dev(struct cx18 *cx, int type) return -ENOMEM; } - s->v4l2dev->type = - VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_TELETEXT | - VID_TYPE_CLIPPING | VID_TYPE_SCALES | VID_TYPE_MPEG_ENCODER; snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "cx18-%d", cx->num); s->v4l2dev->minor = minor; - s->v4l2dev->dev = &cx->dev->dev; + s->v4l2dev->parent = &cx->dev->dev; s->v4l2dev->fops = cx18_stream_info[type].fops; s->v4l2dev->release = video_device_release; s->v4l2dev->tvnorms = V4L2_STD_ALL; diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig index 5cfb46bbdaa9..e60bd31b51a3 100644 --- a/drivers/media/video/cx23885/Kconfig +++ b/drivers/media/video/cx23885/Kconfig @@ -1,9 +1,7 @@ config VIDEO_CX23885 tristate "Conexant cx23885 (2388x successor) support" depends on DVB_CORE && VIDEO_DEV && PCI && I2C && INPUT - depends on HOTPLUG # due to FW_LOADER select I2C_ALGOBIT - select FW_LOADER select VIDEO_BTCX select VIDEO_TUNER select VIDEO_TVEEPROM diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c index e7ef093265af..8118091568fc 100644 --- a/drivers/media/video/cx23885/cx23885-417.c +++ b/drivers/media/video/cx23885/cx23885-417.c @@ -32,6 +32,7 @@ #include <linux/device.h> #include <linux/firmware.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <media/cx2341x.h> #include "cx23885.h" @@ -1699,14 +1700,7 @@ static struct file_operations mpeg_fops = { .llseek = no_llseek, }; -static struct video_device cx23885_mpeg_template = { - .name = "cx23885", - .type = VID_TYPE_CAPTURE | - VID_TYPE_TUNER | - VID_TYPE_SCALES | - VID_TYPE_MPEG_ENCODER, - .fops = &mpeg_fops, - .minor = -1, +static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { .vidioc_s_std = vidioc_s_std, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, @@ -1735,6 +1729,13 @@ static struct video_device cx23885_mpeg_template = { .vidioc_queryctrl = vidioc_queryctrl, }; +static struct video_device cx23885_mpeg_template = { + .name = "cx23885", + .fops = &mpeg_fops, + .ioctl_ops = &mpeg_ioctl_ops, + .minor = -1, +}; + void cx23885_417_unregister(struct cx23885_dev *dev) { dprintk(1, "%s()\n", __func__); @@ -1766,7 +1767,7 @@ static struct video_device *cx23885_video_dev_alloc( vfd->minor = -1; snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, type, cx23885_boards[tsport->dev->board].name); - vfd->dev = &pci->dev; + vfd->parent = &pci->dev; vfd->release = video_device_release; return vfd; } diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index fd7112c11d35..a19de850955d 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -145,6 +145,7 @@ struct cx23885_board cx23885_boards[] = { }, [CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP] = { .name = "DViCO FusionHDTV7 Dual Express", + .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, }, }; @@ -325,25 +326,41 @@ int cx23885_tuner_callback(void *priv, int command, int arg) { struct cx23885_i2c *bus = priv; struct cx23885_dev *dev = bus->dev; + u32 bitmask = 0; + + if (command != 0) { + printk(KERN_ERR "%s(): Unknown command 0x%x.\n", + __func__, command); + return -EINVAL; + } switch(dev->board) { case CX23885_BOARD_HAUPPAUGE_HVR1500Q: - if(command == 0) { /* Tuner Reset Command from xc5000 */ - /* Drive the tuner into reset and out */ - cx_clear(GP0_IO, 0x00000004); - mdelay(200); - cx_set(GP0_IO, 0x00000004); - return 0; - } - else { - printk(KERN_ERR - "%s(): Unknow command.\n", __func__); - return -EINVAL; + /* Tuner Reset Command from xc5000 */ + if (command == 0) + bitmask = 0x04; + break; + case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP: + if (command == 0) { + + /* Two identical tuners on two different i2c buses, + * we need to reset the correct gpio. */ + if (bus->nr == 0) + bitmask = 0x01; + else if (bus->nr == 1) + bitmask = 0x04; } break; } - return 0; /* Should never be here */ + if (bitmask) { + /* Drive the tuner into reset and back out */ + cx_clear(GP0_IO, bitmask); + mdelay(200); + cx_set(GP0_IO, bitmask); + } + + return 0; } void cx23885_gpio_setup(struct cx23885_dev *dev) @@ -435,6 +452,19 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) mdelay(20); cx_set(GP0_IO, 0x00050005); break; + case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP: + /* GPIO-0 xc5000 tuner reset i2c bus 0 */ + /* GPIO-1 s5h1409 demod reset i2c bus 0 */ + /* GPIO-2 xc5000 tuner reset i2c bus 1 */ + /* GPIO-3 s5h1409 demod reset i2c bus 0 */ + + /* Put the parts into reset and back */ + cx_set(GP0_IO, 0x000f0000); + mdelay(20); + cx_clear(GP0_IO, 0x0000000f); + mdelay(20); + cx_set(GP0_IO, 0x000f000f); + break; } } diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index d17343ea0d33..6286a9cf957e 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -76,6 +76,117 @@ LIST_HEAD(cx23885_devlist); * 0x00010ea0 0x00010xxx Free */ +static struct sram_channel cx23885_sram_channels[] = { + [SRAM_CH01] = { + .name = "VID A", + .cmds_start = 0x10000, + .ctrl_start = 0x10380, + .cdt = 0x104c0, + .fifo_start = 0x40, + .fifo_size = 0x2800, + .ptr1_reg = DMA1_PTR1, + .ptr2_reg = DMA1_PTR2, + .cnt1_reg = DMA1_CNT1, + .cnt2_reg = DMA1_CNT2, + }, + [SRAM_CH02] = { + .name = "ch2", + .cmds_start = 0x0, + .ctrl_start = 0x0, + .cdt = 0x0, + .fifo_start = 0x0, + .fifo_size = 0x0, + .ptr1_reg = DMA2_PTR1, + .ptr2_reg = DMA2_PTR2, + .cnt1_reg = DMA2_CNT1, + .cnt2_reg = DMA2_CNT2, + }, + [SRAM_CH03] = { + .name = "TS1 B", + .cmds_start = 0x100A0, + .ctrl_start = 0x10400, + .cdt = 0x10580, + .fifo_start = 0x5000, + .fifo_size = 0x1000, + .ptr1_reg = DMA3_PTR1, + .ptr2_reg = DMA3_PTR2, + .cnt1_reg = DMA3_CNT1, + .cnt2_reg = DMA3_CNT2, + }, + [SRAM_CH04] = { + .name = "ch4", + .cmds_start = 0x0, + .ctrl_start = 0x0, + .cdt = 0x0, + .fifo_start = 0x0, + .fifo_size = 0x0, + .ptr1_reg = DMA4_PTR1, + .ptr2_reg = DMA4_PTR2, + .cnt1_reg = DMA4_CNT1, + .cnt2_reg = DMA4_CNT2, + }, + [SRAM_CH05] = { + .name = "ch5", + .cmds_start = 0x0, + .ctrl_start = 0x0, + .cdt = 0x0, + .fifo_start = 0x0, + .fifo_size = 0x0, + .ptr1_reg = DMA5_PTR1, + .ptr2_reg = DMA5_PTR2, + .cnt1_reg = DMA5_CNT1, + .cnt2_reg = DMA5_CNT2, + }, + [SRAM_CH06] = { + .name = "TS2 C", + .cmds_start = 0x10140, + .ctrl_start = 0x10440, + .cdt = 0x105e0, + .fifo_start = 0x6000, + .fifo_size = 0x1000, + .ptr1_reg = DMA5_PTR1, + .ptr2_reg = DMA5_PTR2, + .cnt1_reg = DMA5_CNT1, + .cnt2_reg = DMA5_CNT2, + }, + [SRAM_CH07] = { + .name = "ch7", + .cmds_start = 0x0, + .ctrl_start = 0x0, + .cdt = 0x0, + .fifo_start = 0x0, + .fifo_size = 0x0, + .ptr1_reg = DMA6_PTR1, + .ptr2_reg = DMA6_PTR2, + .cnt1_reg = DMA6_CNT1, + .cnt2_reg = DMA6_CNT2, + }, + [SRAM_CH08] = { + .name = "ch8", + .cmds_start = 0x0, + .ctrl_start = 0x0, + .cdt = 0x0, + .fifo_start = 0x0, + .fifo_size = 0x0, + .ptr1_reg = DMA7_PTR1, + .ptr2_reg = DMA7_PTR2, + .cnt1_reg = DMA7_CNT1, + .cnt2_reg = DMA7_CNT2, + }, + [SRAM_CH09] = { + .name = "ch9", + .cmds_start = 0x0, + .ctrl_start = 0x0, + .cdt = 0x0, + .fifo_start = 0x0, + .fifo_size = 0x0, + .ptr1_reg = DMA8_PTR1, + .ptr2_reg = DMA8_PTR2, + .cnt1_reg = DMA8_CNT1, + .cnt2_reg = DMA8_CNT2, + }, +}; + static struct sram_channel cx23887_sram_channels[] = { [SRAM_CH01] = { .name = "VID A", @@ -104,8 +215,8 @@ static struct sram_channel cx23887_sram_channels[] = { [SRAM_CH03] = { .name = "TS1 B", .cmds_start = 0x100A0, - .ctrl_start = 0x10780, - .cdt = 0x10400, + .ctrl_start = 0x10630, + .cdt = 0x10870, .fifo_start = 0x5000, .fifo_size = 0x1000, .ptr1_reg = DMA3_PTR1, @@ -140,7 +251,7 @@ static struct sram_channel cx23887_sram_channels[] = { [SRAM_CH06] = { .name = "TS2 C", .cmds_start = 0x10140, - .ctrl_start = 0x10680, + .ctrl_start = 0x10670, .cdt = 0x108d0, .fifo_start = 0x6000, .fifo_size = 0x1000, @@ -460,6 +571,7 @@ static void cx23885_reset(struct cx23885_dev *dev) cx_write(AUDIO_INT_INT_STAT, 0xffffffff); cx_write(AUDIO_EXT_INT_STAT, 0xffffffff); cx_write(CLK_DELAY, cx_read(CLK_DELAY) & 0x80000000); + cx_write(PAD_CTRL, 0x00500300); mdelay(100); @@ -625,7 +737,6 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) atomic_inc(&dev->refcount); dev->nr = cx23885_devcount++; - dev->sram_channels = cx23887_sram_channels; sprintf(dev->name, "cx23885[%d]", dev->nr); mutex_lock(&devlist); @@ -637,11 +748,13 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) dev->bridge = CX23885_BRIDGE_887; /* Apply a sensible clock frequency for the PCIe bridge */ dev->clk_freq = 25000000; + dev->sram_channels = cx23887_sram_channels; } else if(dev->pci->device == 0x8852) { dev->bridge = CX23885_BRIDGE_885; /* Apply a sensible clock frequency for the PCIe bridge */ dev->clk_freq = 28000000; + dev->sram_channels = cx23885_sram_channels; } else BUG(); @@ -1010,8 +1123,9 @@ static void cx23885_tsport_reg_dump(struct cx23885_tsport *port) port->reg_gpcnt_ctl, cx_read(port->reg_gpcnt_ctl)); dprintk(1, "%s() dma_ctl(0x%08X) 0x%08x\n", __func__, port->reg_dma_ctl, cx_read(port->reg_dma_ctl)); - dprintk(1, "%s() src_sel(0x%08X) 0x%08x\n", __func__, - port->reg_src_sel, cx_read(port->reg_src_sel)); + if (port->reg_src_sel) + dprintk(1, "%s() src_sel(0x%08X) 0x%08x\n", __func__, + port->reg_src_sel, cx_read(port->reg_src_sel)); dprintk(1, "%s() lngth(0x%08X) 0x%08x\n", __func__, port->reg_lngth, cx_read(port->reg_lngth)); dprintk(1, "%s() hw_sop_ctrl(0x%08X) 0x%08x\n", __func__, @@ -1042,6 +1156,9 @@ static int cx23885_start_dma(struct cx23885_tsport *port, dprintk(1, "%s() w: %d, h: %d, f: %d\n", __func__, buf->vb.width, buf->vb.height, buf->vb.field); + /* Stop the fifo and risc engine for this port */ + cx_clear(port->reg_dma_ctl, port->dma_ctl_val); + /* setup fifo + format */ cx23885_sram_channel_setup(dev, &dev->sram_channels[ port->sram_chno ], @@ -1083,7 +1200,21 @@ static int cx23885_start_dma(struct cx23885_tsport *port, cx_write(port->reg_gpcnt_ctl, 3); q->count = 1; - if (cx23885_boards[dev->board].portb & CX23885_MPEG_ENCODER) { + /* Set VIDB pins to input */ + if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) { + reg = cx_read(PAD_CTRL); + reg &= ~0x3; /* Clear TS1_OE & TS1_SOP_OE */ + cx_write(PAD_CTRL, reg); + } + + /* Set VIDC pins to input */ + if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) { + reg = cx_read(PAD_CTRL); + reg &= ~0x4; /* Clear TS2_SOP_OE */ + cx_write(PAD_CTRL, reg); + } + + if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) { reg = cx_read(PAD_CTRL); reg = reg & ~0x1; /* Clear TS1_OE */ @@ -1133,7 +1264,7 @@ static int cx23885_stop_dma(struct cx23885_tsport *port) cx_clear(port->reg_ts_int_msk, port->ts_int_msk_val); cx_clear(port->reg_dma_ctl, port->dma_ctl_val); - if (cx23885_boards[dev->board].portb & CX23885_MPEG_ENCODER) { + if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) { reg = cx_read(PAD_CTRL); diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index 043fc4e5c586..ad2235dab5b1 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -33,6 +33,7 @@ #include "cx23885.h" #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #ifdef CONFIG_VIDEO_V4L1_COMPAT /* Include V4L1 specific functions. Should be removed soon */ @@ -326,7 +327,7 @@ struct video_device *cx23885_vdev_init(struct cx23885_dev *dev, return NULL; *vfd = *template; vfd->minor = -1; - vfd->dev = &pci->dev; + vfd->parent = &pci->dev; vfd->release = video_device_release; snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, type, cx23885_boards[dev->board].name); @@ -1433,12 +1434,7 @@ static const struct file_operations video_fops = { .llseek = no_llseek, }; -static struct video_device cx23885_vbi_template; -static struct video_device cx23885_video_template = { - .name = "cx23885-video", - .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_SCALES, - .fops = &video_fops, - .minor = -1, +static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, @@ -1471,6 +1467,14 @@ static struct video_device cx23885_video_template = { .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, #endif +}; + +static struct video_device cx23885_vbi_template; +static struct video_device cx23885_video_template = { + .name = "cx23885-video", + .fops = &video_fops, + .minor = -1, + .ioctl_ops = &video_ioctl_ops, .tvnorms = CX23885_NORMS, .current_norm = V4L2_STD_NTSC_M, }; @@ -1512,7 +1516,6 @@ int cx23885_video_register(struct cx23885_dev *dev) memcpy(&cx23885_vbi_template, &cx23885_video_template, sizeof(cx23885_vbi_template)); strcpy(cx23885_vbi_template.name, "cx23885-vbi"); - cx23885_vbi_template.type = VID_TYPE_TELETEXT|VID_TYPE_TUNER; dev->tvnorm = cx23885_video_template.current_norm; diff --git a/drivers/media/video/cx25840/Kconfig b/drivers/media/video/cx25840/Kconfig index 448f4cd0ce34..de515dadadc2 100644 --- a/drivers/media/video/cx25840/Kconfig +++ b/drivers/media/video/cx25840/Kconfig @@ -1,8 +1,6 @@ config VIDEO_CX25840 tristate "Conexant CX2584x audio/video decoders" depends on VIDEO_V4L2 && I2C && EXPERIMENTAL - depends on HOTPLUG # due to FW_LOADER - select FW_LOADER ---help--- Support for the Conexant CX2584x audio/video decoders. diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index e7bf4f4c1319..209d3bcb5dbb 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -50,7 +50,7 @@ MODULE_LICENSE("GPL"); static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END }; -int cx25840_debug; +static int cx25840_debug; module_param_named(debug,cx25840_debug, int, 0644); diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h index 72916ba975a8..b87337e590b4 100644 --- a/drivers/media/video/cx25840/cx25840-core.h +++ b/drivers/media/video/cx25840/cx25840-core.h @@ -24,8 +24,6 @@ #include <linux/videodev2.h> #include <linux/i2c.h> -extern int cx25840_debug; - /* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is present in Hauppauge PVR-150 (and possibly PVR-500) cards that have certain NTSC tuners (tveeprom tuner model numbers 85, 99 and 112). The diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig index 10e20d8196dc..9dd7bdf659b9 100644 --- a/drivers/media/video/cx88/Kconfig +++ b/drivers/media/video/cx88/Kconfig @@ -33,9 +33,8 @@ config VIDEO_CX88_ALSA config VIDEO_CX88_BLACKBIRD tristate "Blackbird MPEG encoder support (cx2388x + cx23416)" - depends on VIDEO_CX88 && HOTPLUG + depends on VIDEO_CX88 select VIDEO_CX2341X - select FW_LOADER ---help--- This adds support for MPEG encoder cards based on the Blackbird reference design, using the Conexant 2388x diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index bfdca5847764..9a1374a38ec7 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -33,6 +33,7 @@ #include <linux/device.h> #include <linux/firmware.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <media/cx2341x.h> #include "cx88.h" @@ -1174,12 +1175,7 @@ static const struct file_operations mpeg_fops = .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, - .fops = &mpeg_fops, - .minor = -1, +static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { .vidioc_querymenu = vidioc_querymenu, .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, @@ -1207,6 +1203,13 @@ static struct video_device cx8802_mpeg_template = .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_s_std = vidioc_s_std, +}; + +static struct video_device cx8802_mpeg_template = { + .name = "cx8802", + .fops = &mpeg_fops, + .ioctl_ops = &mpeg_ioctl_ops, + .minor = -1, .tvnorms = CX88_NORMS, .current_norm = V4L2_STD_NTSC_M, }; diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index fa6d398e97b9..de199a206a15 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -1348,7 +1348,7 @@ static const struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .audio_chip = AUDIO_CHIP_WM8775, + .audio_chip = V4L2_IDENT_WM8775, .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c index 60eeda3057e9..d656fec59010 100644 --- a/drivers/media/video/cx88/cx88-core.c +++ b/drivers/media/video/cx88/cx88-core.c @@ -40,6 +40,7 @@ #include "cx88.h" #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards"); MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); @@ -1006,7 +1007,7 @@ struct video_device *cx88_vdev_init(struct cx88_core *core, return NULL; *vfd = *template; vfd->minor = -1; - vfd->dev = &pci->dev; + vfd->parent = &pci->dev; vfd->release = video_device_release; snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", core->name, type, core->board.name); diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 0fed5cd2ccea..ef4d56ea0027 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -39,6 +39,7 @@ #include "cx88.h" #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #ifdef CONFIG_VIDEO_V4L1_COMPAT /* Include V4L1 specific functions. Should be removed soon */ @@ -447,7 +448,7 @@ int cx88_video_mux(struct cx88_core *core, unsigned int input) the initialization. Some boards may use different routes for different inputs. HVR-1300 surely does */ if (core->board.audio_chip && - core->board.audio_chip == AUDIO_CHIP_WM8775) { + core->board.audio_chip == V4L2_IDENT_WM8775) { struct v4l2_routing route; route.input = INPUT(input).audioroute; @@ -1682,13 +1683,7 @@ static const struct file_operations video_fops = .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, - .fops = &video_fops, - .minor = -1, +static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, @@ -1721,6 +1716,15 @@ static struct video_device cx8800_video_template = .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, #endif +}; + +static struct video_device cx8800_vbi_template; + +static struct video_device cx8800_video_template = { + .name = "cx8800-video", + .fops = &video_fops, + .minor = -1, + .ioctl_ops = &video_ioctl_ops, .tvnorms = CX88_NORMS, .current_norm = V4L2_STD_NTSC_M, }; @@ -1735,12 +1739,7 @@ static const struct file_operations radio_fops = .llseek = no_llseek, }; -static struct video_device cx8800_radio_template = -{ - .name = "cx8800-radio", - .type = VID_TYPE_TUNER, - .fops = &radio_fops, - .minor = -1, +static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_querycap = radio_querycap, .vidioc_g_tuner = radio_g_tuner, .vidioc_enum_input = radio_enum_input, @@ -1759,6 +1758,13 @@ static struct video_device cx8800_radio_template = #endif }; +static struct video_device cx8800_radio_template = { + .name = "cx8800-radio", + .fops = &radio_fops, + .minor = -1, + .ioctl_ops = &radio_ioctl_ops, +}; + /* ----------------------------------------------------------- */ static void cx8800_unregister_video(struct cx8800_dev *dev) @@ -1830,7 +1836,6 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, 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); @@ -1866,7 +1871,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, /* load and configure helper modules */ - if (core->board.audio_chip == AUDIO_CHIP_WM8775) + if (core->board.audio_chip == V4L2_IDENT_WM8775) request_module("wm8775"); switch (core->boardnr) { diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index 14ac173f4071..54fe65094711 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -29,8 +29,8 @@ #include <media/tuner.h> #include <media/tveeprom.h> #include <media/videobuf-dma-sg.h> +#include <media/v4l2-chip-ident.h> #include <media/cx2341x.h> -#include <media/audiochip.h> #if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE) #include <media/videobuf-dvb.h> #endif @@ -252,7 +252,7 @@ struct cx88_board { struct cx88_input input[MAX_CX88_INPUT]; struct cx88_input radio; enum cx88_board_type mpeg; - enum audiochip audio_chip; + unsigned int audio_chip; }; struct cx88_subid { diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 05f0d5a15058..476ae44a62d2 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -32,8 +32,8 @@ #include <media/saa7115.h> #include <media/tvp5150.h> #include <media/tveeprom.h> -#include <media/audiochip.h> #include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> #include "em28xx.h" @@ -52,6 +52,15 @@ struct em28xx_hash_table { }; struct em28xx_board em28xx_boards[] = { + [EM2750_BOARD_UNKNOWN] = { + .name = "Unknown EM2750/EM2751 webcam grabber", + .vchannels = 1, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = 0, + .amux = 0, + } }, + }, [EM2800_BOARD_UNKNOWN] = { .name = "Unknown EM2800 video grabber", .is_em2800 = 1, @@ -73,6 +82,39 @@ struct em28xx_board em28xx_boards[] = { .is_em2800 = 0, .tuner_type = TUNER_ABSENT, }, + [EM2750_BOARD_DLCW_130] = { + /* Beijing Huaqi Information Digital Technology Co., Ltd */ + .name = "Huaqi DLCW-130", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 1, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = 0, + .amux = 0, + } }, + }, + [EM2800_BOARD_KWORLD_USB2800] = { + .name = "Kworld USB2800", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .is_em2800 = 1, + .vchannels = 3, + .tuner_type = TUNER_PHILIPS_FCV1236D, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA7113, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = 1, + } }, + }, [EM2820_BOARD_KWORLD_PVRTV2800RF] = { .name = "Kworld PVR TV 2800 RF", .is_em2800 = 0, @@ -151,6 +193,376 @@ struct em28xx_board em28xx_boards[] = { MSP_DSP_IN_SCART, MSP_DSP_IN_SCART), } }, }, + [EM2820_BOARD_DLINK_USB_TV] = { + .name = "D-Link DUB-T210 TV Tuner", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .is_em2800 = 0, + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA7113, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = 1, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = 1, + } }, + }, + [EM2820_BOARD_HERCULES_SMART_TV_USB2] = { + .name = "Hercules Smart TV USB 2.0", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA7113, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = 1, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = 1, + } }, + }, + [EM2820_BOARD_PINNACLE_USB_2_FM1216ME] = { + .name = "Pinnacle PCTV USB 2 (Philips FM1216ME)", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .is_em2800 = 0, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA7113, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = 1, + } }, + }, + [EM2820_BOARD_GADMEI_UTV310] = { + .name = "Gadmei UTV310", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .tuner_type = TUNER_TNF_5335MF, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA7113, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE1, + .amux = 1, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = 1, + } }, + }, + [EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE] = { + .name = "Leadtek Winfast USB II Deluxe", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA7114, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = 2, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = 0, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = 9, + .amux = 1, + } }, + }, + [EM2820_BOARD_PINNACLE_DVC_100] = { + .name = "Pinnacle Dazzle DVC 100", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .decoder = EM28XX_SAA7113, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = 1, + } }, + }, + [EM2820_BOARD_VIDEOLOGY_20K14XUSB] = { + .name = "Videology 20K14XUSB USB2.0", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 1, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = 0, + .amux = 0, + } }, + }, + [EM2821_BOARD_PROLINK_PLAYTV_USB2] = { + .name = "SIIG AVTuner-PVR/Prolink PlayTV USB 2.0", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .is_em2800 = 0, + .tuner_type = TUNER_LG_PAL_NEW_TAPC, /* unknown? */ + .tda9887_conf = TDA9887_PRESENT, /* unknown? */ + .decoder = EM28XX_SAA7113, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = 1, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = 1, + } }, + }, + [EM2821_BOARD_SUPERCOMP_USB_2] = { + .name = "Supercomp USB 2.0 TV", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .is_em2800 = 0, + .tuner_type = TUNER_PHILIPS_FM1236_MK3, + .tda9887_conf = TDA9887_PRESENT | + TDA9887_PORT1_ACTIVE | + TDA9887_PORT2_ACTIVE, + .decoder = EM28XX_SAA7113, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = 1, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = 0, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = 1, + } }, + }, + [EM2821_BOARD_USBGEAR_VD204] = { + .name = "Usbgear VD204v9", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 2, + .decoder = EM28XX_SAA7113, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = 1, + } }, + }, + [EM2860_BOARD_NETGMBH_CAM] = { + /* Beijing Huaqi Information Digital Technology Co., Ltd */ + .name = "NetGMBH Cam", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 1, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = 0, + .amux = 0, + } }, + }, + [EM2860_BOARD_TYPHOON_DVD_MAKER] = { + .name = "Typhoon DVD Maker", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 2, + .decoder = EM28XX_SAA7113, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = 1, + } }, + }, + [EM2860_BOARD_GADMEI_UTV330] = { + .name = "Gadmei UTV330", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .tuner_type = TUNER_TNF_5335MF, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA7113, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = 1, + } }, + }, + [EM2860_BOARD_TERRATEC_HYBRID_XS] = { + .name = "Terratec Cinergy A Hybrid XS", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .tuner_type = TUNER_XC2028, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = 1, + } }, + }, + [EM2861_BOARD_KWORLD_PVRTV_300U] = { + .name = "KWorld PVRTV 300U", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .tuner_type = TUNER_XC2028, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = 1, + } }, + }, + [EM2861_BOARD_YAKUMO_MOVIE_MIXER] = { + .name = "Yakumo MovieMixer", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 1, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = 1, + } }, + }, + [EM2861_BOARD_PLEXTOR_PX_TV100U] = { + .name = "Plextor ConvertX PX-TV100U", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .tuner_type = TUNER_TNF_5335MF, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = 1, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = 1, + } }, + }, + [EM2870_BOARD_TERRATEC_XS] = { + .name = "Terratec Cinergy T XS", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_XC2028, + }, + [EM2870_BOARD_TERRATEC_XS_MT2060] = { + .name = "Terratec Cinergy T XS (MT2060)", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_ABSENT, /* MT2060 */ + }, + [EM2870_BOARD_KWORLD_350U] = { + .name = "Kworld 350 U DVB-T", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_XC2028, + }, + [EM2870_BOARD_KWORLD_355U] = { + .name = "Kworld 355 U DVB-T", + .valid = EM28XX_BOARD_NOT_VALIDATED, + }, + [EM2870_BOARD_PINNACLE_PCTV_DVB] = { + .name = "Pinnacle PCTV DVB-T", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_ABSENT, /* MT2060 */ + }, + [EM2870_BOARD_COMPRO_VIDEOMATE] = { + .name = "Compro, VideoMate U3", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_ABSENT, /* MT2060 */ + }, + [EM2880_BOARD_TERRATEC_HYBRID_XS_FR] = { + .name = "Terratec Hybrid XS Secam", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .has_msp34xx = 1, + .tuner_type = TUNER_XC2028, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = 1, + } }, + }, [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = { .name = "Hauppauge WinTV HVR 900", .vchannels = 3, @@ -194,7 +606,7 @@ struct em28xx_board em28xx_boards[] = { .amux = 1, } }, }, - [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950] = { + [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950] = { .name = "Hauppauge WinTV HVR 950", .vchannels = 3, .tda9887_conf = TDA9887_PRESENT, @@ -240,12 +652,36 @@ struct em28xx_board em28xx_boards[] = { .amux = 1, } }, }, + [EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600] = { + .name = "AMD ATI TV Wonder HD 600", + .vchannels = 3, + .tda9887_conf = TDA9887_PRESENT, + .tuner_type = TUNER_XC2028, + .mts_firmware = 1, + .has_12mhz_i2s = 1, + .has_dvb = 1, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = 1, + } }, + }, [EM2880_BOARD_TERRATEC_HYBRID_XS] = { .name = "Terratec Hybrid XS", .vchannels = 3, .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, + .has_dvb = 1, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -328,6 +764,21 @@ struct em28xx_board em28xx_boards[] = { .amux = 1, } }, }, + [EM2800_BOARD_GRABBEEX_USB2800] = { + .name = "eMPIA Technology, Inc. GrabBeeX+ Video Encoder", + .is_em2800 = 1, + .vchannels = 2, + .decoder = EM28XX_SAA7113, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = 1, + } }, + }, [EM2800_BOARD_LEADTEK_WINFAST_USBII] = { .name = "Leadtek Winfast USB II", .is_em2800 = 1, @@ -439,13 +890,232 @@ struct em28xx_board em28xx_boards[] = { .amux = 0, } }, }, + [EM2880_BOARD_MSI_DIGIVOX_AD] = { + .name = "MSI DigiVox A/D", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .tuner_type = TUNER_XC2028, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = 1, + } }, + }, + [EM2880_BOARD_MSI_DIGIVOX_AD_II] = { + .name = "MSI DigiVox A/D II", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .tuner_type = TUNER_XC2028, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = 1, + } }, + }, + [EM2880_BOARD_KWORLD_DVB_305U] = { + .name = "KWorld DVB-T 305U", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .tuner_type = TUNER_XC2028, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = 1, + } }, + }, + [EM2880_BOARD_KWORLD_DVB_310U] = { + .name = "KWorld DVB-T 310U", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .tuner_type = TUNER_XC2028, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = 1, + } }, + }, + [EM2881_BOARD_DNT_DA2_HYBRID] = { + .name = "DNT DA2 Hybrid", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .tuner_type = TUNER_XC2028, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = 1, + } }, + }, + [EM2881_BOARD_PINNACLE_HYBRID_PRO] = { + .name = "Pinnacle Hybrid Pro", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .tuner_type = TUNER_XC2028, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = 1, + } }, + }, + [EM2882_BOARD_PINNACLE_HYBRID_PRO] = { + .name = "Pinnacle Hybrid Pro (2)", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .tuner_type = TUNER_XC2028, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = 1, + } }, + }, + [EM2882_BOARD_KWORLD_VS_DVBT] = { + .name = "Kworld VS-DVB-T 323UR", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .tuner_type = TUNER_XC2028, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = 1, + } }, + }, + [EM2882_BOARD_TERRATEC_HYBRID_XS] = { + .name = "Terratec Hybrid XS (em2882)", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .tuner_type = TUNER_XC2028, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = 1, + } }, + }, + [EM2883_BOARD_KWORLD_HYBRID_A316] = { + .name = "Kworld PlusTV HD Hybrid 330", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .vchannels = 3, + .is_em2800 = 0, + .tuner_type = TUNER_XC2028, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = 1, + } }, + }, + [EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU] = { + .name = "Compro VideoMate ForYou/Stereo", + .vchannels = 2, + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); /* table of devices that work with this driver */ struct usb_device_id em28xx_id_table [] = { { USB_DEVICE(0xeb1a, 0x2750), - .driver_info = EM2820_BOARD_UNKNOWN }, + .driver_info = EM2750_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2751), + .driver_info = EM2750_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2800), .driver_info = EM2800_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2820), @@ -462,36 +1132,78 @@ struct usb_device_id em28xx_id_table [] = { .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2883), .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0xe300), + .driver_info = EM2861_BOARD_KWORLD_PVRTV_300U }, + { USB_DEVICE(0xeb1a, 0xe305), + .driver_info = EM2880_BOARD_KWORLD_DVB_305U }, + { USB_DEVICE(0xeb1a, 0xe310), + .driver_info = EM2880_BOARD_MSI_DIGIVOX_AD }, + { USB_DEVICE(0xeb1a, 0xa316), + .driver_info = EM2883_BOARD_KWORLD_HYBRID_A316 }, + { USB_DEVICE(0xeb1a, 0xe320), + .driver_info = EM2880_BOARD_MSI_DIGIVOX_AD_II }, + { USB_DEVICE(0xeb1a, 0xe323), + .driver_info = EM2882_BOARD_KWORLD_VS_DVBT }, + { USB_DEVICE(0xeb1a, 0xe350), + .driver_info = EM2870_BOARD_KWORLD_350U }, + { USB_DEVICE(0xeb1a, 0xe355), + .driver_info = EM2870_BOARD_KWORLD_355U }, + { USB_DEVICE(0xeb1a, 0x2801), + .driver_info = EM2800_BOARD_GRABBEEX_USB2800 }, + { USB_DEVICE(0xeb1a, 0xe357), + .driver_info = EM2870_BOARD_KWORLD_355U }, { USB_DEVICE(0x0ccd, 0x0036), .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 }, - { USB_DEVICE(0x2304, 0x0208), - .driver_info = EM2820_BOARD_PINNACLE_USB_2 }, + { USB_DEVICE(0x0ccd, 0x004c), + .driver_info = EM2880_BOARD_TERRATEC_HYBRID_XS_FR }, + { USB_DEVICE(0x0ccd, 0x004f), + .driver_info = EM2860_BOARD_TERRATEC_HYBRID_XS }, + { USB_DEVICE(0x0ccd, 0x005e), + .driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS }, + { USB_DEVICE(0x0ccd, 0x0042), + .driver_info = EM2880_BOARD_TERRATEC_HYBRID_XS }, + { USB_DEVICE(0x0ccd, 0x0043), + .driver_info = EM2870_BOARD_TERRATEC_XS }, + { USB_DEVICE(0x0ccd, 0x0047), + .driver_info = EM2880_BOARD_TERRATEC_PRODIGY_XS }, + { USB_DEVICE(0x185b, 0x2870), + .driver_info = EM2870_BOARD_COMPRO_VIDEOMATE }, + { USB_DEVICE(0x185b, 0x2041), + .driver_info = EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU }, { USB_DEVICE(0x2040, 0x4200), .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 }, { USB_DEVICE(0x2040, 0x4201), .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 }, - { USB_DEVICE(0x2304, 0x0207), - .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, - { USB_DEVICE(0x2304, 0x021a), - .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, - { USB_DEVICE(0x2304, 0x0227), - .driver_info = EM2880_BOARD_PINNACLE_PCTV_HD_PRO }, { USB_DEVICE(0x2040, 0x6500), .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 }, { USB_DEVICE(0x2040, 0x6502), .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 }, { USB_DEVICE(0x2040, 0x6513), /* HCW HVR-980 */ - .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 }, + .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 }, { USB_DEVICE(0x2040, 0x6517), /* HP HVR-950 */ - .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 }, + .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 }, { USB_DEVICE(0x2040, 0x651b), /* RP HVR-950 */ - .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 }, + .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 }, { USB_DEVICE(0x2040, 0x651f), /* HCW HVR-850 */ - .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 }, - { USB_DEVICE(0x0ccd, 0x0042), - .driver_info = EM2880_BOARD_TERRATEC_HYBRID_XS }, - { USB_DEVICE(0x0ccd, 0x0047), - .driver_info = EM2880_BOARD_TERRATEC_PRODIGY_XS }, + .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 }, + { USB_DEVICE(0x0438, 0xb002), + .driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 }, + { USB_DEVICE(0x2001, 0xf112), + .driver_info = EM2820_BOARD_DLINK_USB_TV }, + { USB_DEVICE(0x2304, 0x0207), + .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, + { USB_DEVICE(0x2304, 0x0208), + .driver_info = EM2820_BOARD_PINNACLE_USB_2 }, + { USB_DEVICE(0x2304, 0x021a), + .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, + { USB_DEVICE(0x2304, 0x0226), + .driver_info = EM2882_BOARD_PINNACLE_HYBRID_PRO }, + { USB_DEVICE(0x2304, 0x0227), + .driver_info = EM2880_BOARD_PINNACLE_PCTV_HD_PRO }, + { USB_DEVICE(0x0413, 0x6023), + .driver_info = EM2800_BOARD_LEADTEK_WINFAST_USBII }, + { USB_DEVICE(0x093b, 0xa005), + .driver_info = EM2861_BOARD_PLEXTOR_PX_TV100U }, { }, }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); @@ -500,6 +1212,18 @@ MODULE_DEVICE_TABLE(usb, em28xx_id_table); * Reset sequences for analog/digital modes */ +/* Reset for the most [analog] boards */ +static struct em28xx_reg_seq default_analog[] = { + {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +/* Reset for the most [digital] boards */ +static struct em28xx_reg_seq default_digital[] = { + {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + /* Board Hauppauge WinTV HVR 900 analog */ static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = { {EM28XX_R08_GPIO, 0x2d, ~EM_GPIO_4, 10}, @@ -515,14 +1239,42 @@ static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = { { -1, -1, -1, -1}, }; -/* Board Hauppauge WinTV HVR 900 tuner_callback */ -static struct em28xx_reg_seq hauppauge_wintv_hvr_900_tuner_callback[] = { +/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */ +static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = { + {EM28XX_R08_GPIO, 0x69, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */ +static struct em28xx_reg_seq em2880_msi_digivox_ad_digital[] = { + {EM28XX_R08_GPIO, 0x6a, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +/* Board - EM2870 Kworld 355u + Analog - No input analog */ +static struct em28xx_reg_seq em2870_kworld_355u_digital[] = { + {EM2880_R04_GPO, 0x01, 0xff, 10}, + { -1, -1, -1, -1}, +}; + +/* Callback for the most boards */ +static struct em28xx_reg_seq default_callback[] = { {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, {EM28XX_R08_GPIO, 0, EM_GPIO_4, 10}, {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, { -1, -1, -1, -1}, }; +/* Callback for EM2882 TERRATEC HYBRID XS */ +static struct em28xx_reg_seq em2882_terratec_hybrid_xs_digital[] = { + {EM28XX_R08_GPIO, 0x2e, 0xff, 6}, + {EM28XX_R08_GPIO, 0x3e, ~EM_GPIO_4, 6}, + {EM2880_R04_GPO, 0x04, 0xff, 10}, + {EM2880_R04_GPO, 0x0c, 0xff, 10}, + { -1, -1, -1, -1}, +}; + /* * EEPROM hash table for devices with generic USB IDs */ @@ -569,6 +1321,7 @@ static void em28xx_set_model(struct em28xx *dev) dev->max_range_640_480 = em28xx_boards[dev->model].max_range_640_480; dev->has_dvb = em28xx_boards[dev->model].has_dvb; dev->has_snapshot_button = em28xx_boards[dev->model].has_snapshot_button; + dev->valid = em28xx_boards[dev->model].valid; } /* Since em28xx_pre_card_setup() requires a proper dev->model, @@ -604,19 +1357,171 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2880_BOARD_TERRATEC_PRODIGY_XS: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: - case EM2880_BOARD_TERRATEC_HYBRID_XS: - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: + case EM2860_BOARD_TERRATEC_HYBRID_XS: + case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: + case EM2882_BOARD_PINNACLE_HYBRID_PRO: + case EM2883_BOARD_KWORLD_HYBRID_A316: + case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: + em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + msleep(50); + + /* Sets GPO/GPIO sequences for this device */ + dev->analog_gpio = hauppauge_wintv_hvr_900_analog; + dev->digital_gpio = hauppauge_wintv_hvr_900_digital; + dev->tun_analog_gpio = default_callback; + dev->tun_digital_gpio = default_callback; + break; + + case EM2882_BOARD_TERRATEC_HYBRID_XS: em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); msleep(50); + /* should be added ir_codes here */ + /* Sets GPO/GPIO sequences for this device */ dev->analog_gpio = hauppauge_wintv_hvr_900_analog; dev->digital_gpio = hauppauge_wintv_hvr_900_digital; - dev->tun_analog_gpio = hauppauge_wintv_hvr_900_tuner_callback; - dev->tun_digital_gpio = hauppauge_wintv_hvr_900_tuner_callback; + dev->tun_analog_gpio = default_callback; + dev->tun_digital_gpio = em2882_terratec_hybrid_xs_digital; + break; + + case EM2880_BOARD_TERRATEC_HYBRID_XS_FR: + case EM2880_BOARD_TERRATEC_HYBRID_XS: + case EM2870_BOARD_TERRATEC_XS: + case EM2881_BOARD_PINNACLE_HYBRID_PRO: + case EM2880_BOARD_KWORLD_DVB_310U: + case EM2870_BOARD_KWORLD_350U: + case EM2881_BOARD_DNT_DA2_HYBRID: + em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + msleep(50); + + /* NOTE: EM2881_DNT_DA2_HYBRID spend 140 msleep for digital + and analog commands. If this commands doesn't work, + add this timer. */ + /* Sets GPO/GPIO sequences for this device */ + dev->analog_gpio = default_analog; + dev->digital_gpio = default_digital; + dev->tun_analog_gpio = default_callback; + dev->tun_digital_gpio = default_callback; + break; + + case EM2880_BOARD_MSI_DIGIVOX_AD: + case EM2880_BOARD_MSI_DIGIVOX_AD_II: + em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + msleep(50); + + /* Sets GPO/GPIO sequences for this device */ + dev->analog_gpio = em2880_msi_digivox_ad_analog; + dev->digital_gpio = em2880_msi_digivox_ad_digital; + dev->tun_analog_gpio = default_callback; + dev->tun_digital_gpio = default_callback; + break; + + case EM2750_BOARD_UNKNOWN: + case EM2750_BOARD_DLCW_130: + em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x0a", 1); + break; + + case EM2861_BOARD_PLEXTOR_PX_TV100U: + em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + /* FIXME guess */ + /* Turn on analog audio output */ + em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1); + break; + + case EM2861_BOARD_KWORLD_PVRTV_300U: + case EM2880_BOARD_KWORLD_DVB_305U: + em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x4c", 1); + msleep(10); + em28xx_write_regs(dev, 0x08, "\x6d", 1); + msleep(10); + em28xx_write_regs(dev, 0x08, "\x7d", 1); + msleep(10); + break; + + case EM2870_BOARD_KWORLD_355U: + em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + msleep(50); + + /* Sets GPO/GPIO sequences for this device */ + dev->digital_gpio = em2870_kworld_355u_digital; + break; + + case EM2870_BOARD_COMPRO_VIDEOMATE: + em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + /* TODO: someone can do some cleanup here... + not everything's needed */ + em28xx_write_regs(dev, 0x04, "\x00", 1); + msleep(10); + em28xx_write_regs(dev, 0x04, "\x01", 1); + msleep(10); + em28xx_write_regs(dev, 0x08, "\xfd", 1); + mdelay(70); + em28xx_write_regs(dev, 0x08, "\xfc", 1); + mdelay(70); + em28xx_write_regs(dev, 0x08, "\xdc", 1); + mdelay(70); + em28xx_write_regs(dev, 0x08, "\xfc", 1); + mdelay(70); + break; + + case EM2870_BOARD_TERRATEC_XS_MT2060: + em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + /* this device needs some gpio writes to get the DVB-T + demod work */ + em28xx_write_regs(dev, 0x08, "\xfe", 1); + mdelay(70); + em28xx_write_regs(dev, 0x08, "\xde", 1); + mdelay(70); + dev->em28xx_write_regs(dev, 0x08, "\xfe", 1); + mdelay(70); + break; + + case EM2870_BOARD_PINNACLE_PCTV_DVB: + em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + /* this device needs some gpio writes to get the + DVB-T demod work */ + em28xx_write_regs(dev, 0x08, "\xfe", 1); + mdelay(70); + em28xx_write_regs(dev, 0x08, "\xde", 1); + mdelay(70); + em28xx_write_regs(dev, 0x08, "\xfe", 1); + mdelay(70); + /* switch em2880 rc protocol */ + em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x22", 1); + /* should be added ir_codes here */ + break; + + case EM2820_BOARD_GADMEI_UTV310: + em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + /* Turn on analog audio output */ + em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1); + break; + + case EM2860_BOARD_GADMEI_UTV330: + /* Turn on IR */ + em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x07", 1); + em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + /* should be added ir_codes here */ + break; + + case EM2820_BOARD_MSI_VOX_USB_2: + em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + /* enables audio for that device */ + em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1); break; } @@ -639,12 +1544,16 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: ctl->demod = XC3028_FE_ZARLINK456; break; + case EM2880_BOARD_TERRATEC_HYBRID_XS: + ctl->demod = XC3028_FE_ZARLINK456; + break; case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: /* djh - Not sure which demod we need here */ ctl->demod = XC3028_FE_DEFAULT; break; - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: + case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: + case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: /* FIXME: Better to specify the needed IF */ ctl->demod = XC3028_FE_DEFAULT; break; @@ -809,6 +1718,8 @@ void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir) break; case (EM2800_BOARD_KWORLD_USB2800): break; + case (EM2800_BOARD_GRABBEEX_USB2800): + break; } } @@ -823,7 +1734,7 @@ void em28xx_card_setup(struct em28xx *dev) case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: + case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: { struct tveeprom tv; #ifdef CONFIG_MODULES @@ -836,7 +1747,7 @@ void em28xx_card_setup(struct em28xx *dev) dev->tuner_type = tv.tuner_type; - if (tv.audio_processor == AUDIO_CHIP_MSP34XX) { + if (tv.audio_processor == V4L2_IDENT_MSPX4XX) { dev->i2s_speed = 2048000; dev->has_msp34xx = 1; } @@ -854,11 +1765,21 @@ void em28xx_card_setup(struct em28xx *dev) case EM2800_BOARD_UNKNOWN: if (!em28xx_hint_board(dev)) em28xx_set_model(dev); + break; } if (dev->has_snapshot_button) em28xx_register_snapshot_button(dev); + if (dev->valid == EM28XX_BOARD_NOT_VALIDATED) { + em28xx_errdev("\n\n"); + em28xx_errdev("The support for this board weren't " + "valid yet.\n"); + em28xx_errdev("Please send a report of having this working\n"); + em28xx_errdev("not to V4L mailing list (and/or to other " + "addresses)\n\n"); + } + /* Allow override tuner type by a module parameter */ if (tuner >= 0) dev->tuner_type = tuner; diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index cc61cfb23a4a..4b992bc0083c 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -6,6 +6,7 @@ (c) 2008 Devin Heitmueller <devin.heitmueller@gmail.com> - Fixes for the driver to properly work with HVR-950 - Fixes for the driver to properly work with Pinnacle PCTV HD Pro Stick + - Fixes for the driver to properly work with AMD ATI TV Wonder HD 600 (c) 2008 Aidan Thornton <makosoft@googlemail.com> @@ -409,8 +410,9 @@ static int dvb_init(struct em28xx *dev) em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); /* init frontend */ switch (dev->model) { - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: + case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: + case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: dvb->frontend = dvb_attach(lgdt330x_attach, &em2880_lgdt3303_dev, &dev->i2c_adap); @@ -441,6 +443,15 @@ static int dvb_init(struct em28xx *dev) } break; #endif + case EM2880_BOARD_TERRATEC_HYBRID_XS: + dvb->frontend = dvb_attach(zl10353_attach, + &em28xx_zl10353_with_xc3028, + &dev->i2c_adap); + if (attach_xc3028(0x61, dev) < 0) { + result = -EINVAL; + goto out_free; + } + break; default: printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card" " isn't supported yet\n", diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 2d9f14d2a00b..49ab0629702e 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -38,6 +38,7 @@ #include "em28xx.h" #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <media/msp3400.h> #include <media/tuner.h> @@ -1763,20 +1764,7 @@ static const struct file_operations em28xx_v4l_fops = { .compat_ioctl = v4l_compat_ioctl32, }; -static const struct file_operations radio_fops = { - .owner = THIS_MODULE, - .open = em28xx_v4l2_open, - .release = em28xx_v4l2_close, - .ioctl = video_ioctl2, - .compat_ioctl = v4l_compat_ioctl32, - .llseek = no_llseek, -}; - -static const struct video_device em28xx_video_template = { - .fops = &em28xx_v4l_fops, - .release = video_device_release, - - .minor = -1, +static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, @@ -1814,16 +1802,29 @@ static const struct video_device em28xx_video_template = { #ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidiocgmbuf, #endif +}; + +static const struct video_device em28xx_video_template = { + .fops = &em28xx_v4l_fops, + .release = video_device_release, + .ioctl_ops = &video_ioctl_ops, + + .minor = -1, .tvnorms = V4L2_STD_ALL, .current_norm = V4L2_STD_PAL, }; -static struct video_device em28xx_radio_template = { - .name = "em28xx-radio", - .type = VID_TYPE_TUNER, - .fops = &radio_fops, - .minor = -1, +static const struct file_operations radio_fops = { + .owner = THIS_MODULE, + .open = em28xx_v4l2_open, + .release = em28xx_v4l2_close, + .ioctl = video_ioctl2, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_querycap = radio_querycap, .vidioc_g_tuner = radio_g_tuner, .vidioc_enum_input = radio_enum_input, @@ -1842,6 +1843,13 @@ static struct video_device em28xx_radio_template = { #endif }; +static struct video_device em28xx_radio_template = { + .name = "em28xx-radio", + .fops = &radio_fops, + .ioctl_ops = &radio_ioctl_ops, + .minor = -1, +}; + /******************************** usb interface ******************************/ @@ -1882,7 +1890,6 @@ EXPORT_SYMBOL(em28xx_unregister_extension); static struct video_device *em28xx_vdev_init(struct em28xx *dev, const struct video_device *template, - const int type, const char *type_name) { struct video_device *vfd; @@ -1892,9 +1899,8 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, return NULL; *vfd = *template; vfd->minor = -1; - vfd->dev = &dev->udev->dev; + vfd->parent = &dev->udev->dev; vfd->release = video_device_release; - vfd->type = type; vfd->debug = video_debug; snprintf(vfd->name, sizeof(vfd->name), "%s %s", @@ -1972,14 +1978,11 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, list_add_tail(&dev->devlist, &em28xx_devlist); /* allocate and fill video video_device struct */ - dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, - VID_TYPE_CAPTURE, "video"); + dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video"); if (NULL == dev->vdev) { em28xx_errdev("cannot allocate video_device.\n"); goto fail_unreg; } - if (dev->tuner_type != TUNER_ABSENT) - dev->vdev->type |= VID_TYPE_TUNER; /* register v4l2 video video_device */ retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, @@ -1991,8 +1994,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, } /* Allocate and fill vbi video_device struct */ - dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, - VFL_TYPE_VBI, "vbi"); + dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, "vbi"); /* register v4l2 vbi video_device */ if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI, vbi_nr[dev->devno]) < 0) { @@ -2002,8 +2004,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, } if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) { - dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template, - VFL_TYPE_RADIO, "radio"); + dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template, "radio"); if (NULL == dev->radio_dev) { em28xx_errdev("cannot allocate video_device.\n"); goto fail_unreg; diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 89842c5d64a1..9a3310748685 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -54,15 +54,58 @@ #define EM2880_BOARD_TERRATEC_PRODIGY_XS 13 #define EM2820_BOARD_PROLINK_PLAYTV_USB2 14 #define EM2800_BOARD_VGEAR_POCKETTV 15 -#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 16 +#define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 16 #define EM2880_BOARD_PINNACLE_PCTV_HD_PRO 17 #define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 18 #define EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA 19 +#define EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 20 +#define EM2800_BOARD_GRABBEEX_USB2800 21 +#define EM2750_BOARD_UNKNOWN 22 +#define EM2750_BOARD_DLCW_130 23 +#define EM2820_BOARD_DLINK_USB_TV 24 +#define EM2820_BOARD_GADMEI_UTV310 25 +#define EM2820_BOARD_HERCULES_SMART_TV_USB2 26 +#define EM2820_BOARD_PINNACLE_USB_2_FM1216ME 27 +#define EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE 28 +#define EM2820_BOARD_PINNACLE_DVC_100 29 +#define EM2820_BOARD_VIDEOLOGY_20K14XUSB 30 +#define EM2821_BOARD_USBGEAR_VD204 31 +#define EM2821_BOARD_SUPERCOMP_USB_2 32 +#define EM2821_BOARD_PROLINK_PLAYTV_USB2 33 +#define EM2860_BOARD_TERRATEC_HYBRID_XS 34 +#define EM2860_BOARD_TYPHOON_DVD_MAKER 35 +#define EM2860_BOARD_NETGMBH_CAM 36 +#define EM2860_BOARD_GADMEI_UTV330 37 +#define EM2861_BOARD_YAKUMO_MOVIE_MIXER 38 +#define EM2861_BOARD_KWORLD_PVRTV_300U 39 +#define EM2861_BOARD_PLEXTOR_PX_TV100U 40 +#define EM2870_BOARD_KWORLD_350U 41 +#define EM2870_BOARD_KWORLD_355U 42 +#define EM2870_BOARD_TERRATEC_XS 43 +#define EM2870_BOARD_TERRATEC_XS_MT2060 44 +#define EM2870_BOARD_PINNACLE_PCTV_DVB 45 +#define EM2870_BOARD_COMPRO_VIDEOMATE 46 +#define EM2880_BOARD_KWORLD_DVB_305U 47 +#define EM2880_BOARD_KWORLD_DVB_310U 48 +#define EM2880_BOARD_MSI_DIGIVOX_AD 49 +#define EM2880_BOARD_MSI_DIGIVOX_AD_II 50 +#define EM2880_BOARD_TERRATEC_HYBRID_XS_FR 51 +#define EM2881_BOARD_DNT_DA2_HYBRID 52 +#define EM2881_BOARD_PINNACLE_HYBRID_PRO 53 +#define EM2882_BOARD_KWORLD_VS_DVBT 54 +#define EM2882_BOARD_TERRATEC_HYBRID_XS 55 +#define EM2882_BOARD_PINNACLE_HYBRID_PRO 56 +#define EM2883_BOARD_KWORLD_HYBRID_A316 57 +#define EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU 58 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 #define EM28XX_DEF_BUF 8 +/* Params for validated field */ +#define EM28XX_BOARD_NOT_VALIDATED 1 +#define EM28XX_BOARD_VALIDATED 0 + /* maximum number of em28xx boards */ #define EM28XX_MAXBOARDS 4 /*FIXME: should be bigger */ @@ -251,6 +294,7 @@ struct em28xx_board { unsigned int max_range_640_480:1; unsigned int has_dvb:1; unsigned int has_snapshot_button:1; + unsigned int valid:1; enum em28xx_decoder decoder; @@ -331,6 +375,7 @@ struct em28xx { unsigned int max_range_640_480:1; unsigned int has_dvb:1; unsigned int has_snapshot_button:1; + unsigned int valid:1; /* report for validated boards */ /* Some older em28xx chips needs a waiting time after writing */ unsigned int wait_after_write; @@ -360,7 +405,7 @@ struct em28xx { v4l2_std_id norm; /* selected tv norm */ int ctl_freq; /* selected frequency */ unsigned int ctl_input; /* selected input */ - unsigned int ctl_ainput; /* slected audio input */ + unsigned int ctl_ainput;/* selected audio input */ int mute; int volume; /* frame properties */ diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c index 15d037ae25c5..2d170d101c21 100644 --- a/drivers/media/video/et61x251/et61x251_core.c +++ b/drivers/media/video/et61x251/et61x251_core.c @@ -34,6 +34,7 @@ #include <linux/mm.h> #include <linux/vmalloc.h> #include <linux/page-flags.h> +#include <media/v4l2-ioctl.h> #include <asm/byteorder.h> #include <asm/page.h> #include <asm/uaccess.h> @@ -985,7 +986,7 @@ static DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, static int et61x251_create_sysfs(struct et61x251_device* cam) { - struct device *classdev = &(cam->v4ldev->class_dev); + struct device *classdev = &(cam->v4ldev->dev); int err = 0; if ((err = device_create_file(classdev, &dev_attr_reg))) @@ -2584,8 +2585,6 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) } strcpy(cam->v4ldev->name, "ET61X[12]51 PC Camera"); - cam->v4ldev->owner = THIS_MODULE; - cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; cam->v4ldev->fops = &et61x251_fops; cam->v4ldev->minor = video_nr[dev_nr]; cam->v4ldev->release = video_device_release; diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index 013d593b0c67..44b0bffeb20e 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -25,9 +25,6 @@ #define CONEX_CAM 1 /* special JPEG header */ #include "jpeg.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) -static const char version[] = "2.1.7"; - MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); MODULE_DESCRIPTION("GSPCA USB Conexant Camera Driver"); MODULE_LICENSE("GPL"); @@ -818,7 +815,6 @@ static int sd_config(struct gspca_dev *gspca_dev, struct cam *cam; cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 0x01; cam->cam_mode = vga_mode; cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; @@ -1011,9 +1007,8 @@ static struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define DVNM(name) .driver_info = (kernel_ulong_t) name static __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x0572, 0x0041), DVNM("Creative Notebook cx11646")}, + {USB_DEVICE(0x0572, 0x0041)}, {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -1038,7 +1033,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - PDEBUG(D_PROBE, "v%s registered", version); + PDEBUG(D_PROBE, "registered"); return 0; } static void __exit sd_mod_exit(void) diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c index 8ab4ea7201a9..c8c2f02fcf00 100644 --- a/drivers/media/video/gspca/etoms.c +++ b/drivers/media/video/gspca/etoms.c @@ -22,9 +22,6 @@ #include "gspca.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) -static const char version[] = "2.1.7"; - MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); MODULE_DESCRIPTION("Etoms USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -602,26 +599,10 @@ static int sd_config(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; - __u16 vendor; - __u16 product; - - vendor = id->idVendor; - product = id->idProduct; -/* switch (vendor) { */ -/* case 0x102c: * Etoms */ - switch (product) { - case 0x6151: - sd->sensor = SENSOR_PAS106; /* Etoms61x151 */ - break; - case 0x6251: - sd->sensor = SENSOR_TAS5130CXX; /* Etoms61x251 */ - break; -/* } */ -/* break; */ - } + cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 1; + sd->sensor = id->driver_info; if (sd->sensor == SENSOR_PAS106) { cam->cam_mode = sif_mode; cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; @@ -911,12 +892,11 @@ static struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define DVNM(name) .driver_info = (kernel_ulong_t) name static __devinitdata struct usb_device_id device_table[] = { #ifndef CONFIG_USB_ET61X251 - {USB_DEVICE(0x102c, 0x6151), DVNM("Qcam Sangha CIF")}, + {USB_DEVICE(0x102c, 0x6151), .driver_info = SENSOR_PAS106}, #endif - {USB_DEVICE(0x102c, 0x6251), DVNM("Qcam xxxxxx VGA")}, + {USB_DEVICE(0x102c, 0x6251), .driver_info = SENSOR_TAS5130CXX}, {} }; @@ -942,7 +922,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - PDEBUG(D_PROBE, "v%s registered", version); + PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 16e367cec760..3a051c925ff6 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -32,6 +32,7 @@ #include <asm/page.h> #include <linux/uaccess.h> #include <linux/jiffies.h> +#include <media/v4l2-ioctl.h> #include "gspca.h" @@ -42,8 +43,7 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("GSPCA USB Camera Driver"); MODULE_LICENSE("GPL"); -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) -static const char version[] = "2.1.7"; +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 2, 0) static int video_nr = -1; @@ -209,6 +209,8 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, &frame->v4l2_buf.timestamp); frame->v4l2_buf.sequence = ++gspca_dev->sequence; } else if (gspca_dev->last_packet_type == DISCARD_PACKET) { + if (packet_type == LAST_PACKET) + gspca_dev->last_packet_type = packet_type; return frame; } @@ -399,7 +401,7 @@ static struct usb_host_endpoint *alt_isoc(struct usb_host_interface *alt, * This routine may be called many times when the bandwidth is too small * (the bandwidth is checked on urb submit). */ -struct usb_host_endpoint *get_isoc_ep(struct gspca_dev *gspca_dev) +static struct usb_host_endpoint *get_isoc_ep(struct gspca_dev *gspca_dev) { struct usb_interface *intf; struct usb_host_endpoint *ep; @@ -832,7 +834,16 @@ static int vidioc_querycap(struct file *file, void *priv, memset(cap, 0, sizeof *cap); strncpy(cap->driver, gspca_dev->sd_desc->name, sizeof cap->driver); - strncpy(cap->card, gspca_dev->cam.dev_name, sizeof cap->card); +/* strncpy(cap->card, gspca_dev->cam.dev_name, sizeof cap->card); */ + if (gspca_dev->dev->product != NULL) { + strncpy(cap->card, gspca_dev->dev->product, + sizeof cap->card); + } else { + snprintf(cap->card, sizeof cap->card, + "USB Camera (%04x:%04x)", + le16_to_cpu(gspca_dev->dev->descriptor.idVendor), + le16_to_cpu(gspca_dev->dev->descriptor.idProduct)); + } strncpy(cap->bus_info, gspca_dev->dev->bus->bus_name, sizeof cap->bus_info); cap->version = DRIVER_VERSION_NUMBER; @@ -1649,12 +1660,7 @@ static struct file_operations dev_fops = { .poll = dev_poll, }; -static struct video_device gspca_template = { - .name = "gspca main driver", - .type = VID_TYPE_CAPTURE, - .fops = &dev_fops, - .release = dev_release, /* mandatory */ - .minor = -1, +static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_dqbuf = vidioc_dqbuf, .vidioc_qbuf = vidioc_qbuf, @@ -1683,6 +1689,14 @@ static struct video_device gspca_template = { #endif }; +static struct video_device gspca_template = { + .name = "gspca main driver", + .fops = &dev_fops, + .ioctl_ops = &dev_ioctl_ops, + .release = dev_release, /* mandatory */ + .minor = -1, +}; + /* * probe and create a new gspca device * @@ -1740,10 +1754,11 @@ int gspca_dev_probe(struct usb_interface *intf, /* init video stuff */ memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template); - gspca_dev->vdev.dev = &dev->dev; + gspca_dev->vdev.parent = &dev->dev; memcpy(&gspca_dev->fops, &dev_fops, sizeof gspca_dev->fops); gspca_dev->vdev.fops = &gspca_dev->fops; gspca_dev->fops.owner = module; /* module protection */ + gspca_dev->present = 1; ret = video_register_device(&gspca_dev->vdev, VFL_TYPE_GRABBER, video_nr); @@ -1752,7 +1767,6 @@ int gspca_dev_probe(struct usb_interface *intf, goto out; } - gspca_dev->present = 1; usb_set_intfdata(intf, gspca_dev); PDEBUG(D_PROBE, "probe ok"); return 0; @@ -1885,7 +1899,10 @@ EXPORT_SYMBOL(gspca_auto_gain_n_exposure); /* -- module insert / remove -- */ static int __init gspca_init(void) { - info("main v%s registered", version); + info("main v%d.%d.%d registered", + (DRIVER_VERSION_NUMBER >> 16) & 0xff, + (DRIVER_VERSION_NUMBER >> 8) & 0xff, + DRIVER_VERSION_NUMBER & 0xff); return 0; } static void __exit gspca_exit(void) diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c index 88c2b02f380a..21c4ee56a10a 100644 --- a/drivers/media/video/gspca/mars.c +++ b/drivers/media/video/gspca/mars.c @@ -24,9 +24,6 @@ #include "gspca.h" #include "jpeg.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) -static const char version[] = "2.1.7"; - MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); MODULE_DESCRIPTION("GSPCA/Mars USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -140,7 +137,6 @@ static int sd_config(struct gspca_dev *gspca_dev, struct cam *cam; cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 0x01; cam->cam_mode = vga_mode; cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; @@ -424,9 +420,8 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define DVNM(name) .driver_info = (kernel_ulong_t) name static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x093a, 0x050f), DVNM("Mars-Semi Pc-Camera")}, + {USB_DEVICE(0x093a, 0x050f)}, {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -451,7 +446,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - PDEBUG(D_PROBE, "v%s registered", version); + PDEBUG(D_PROBE, "registered"); return 0; } static void __exit sd_mod_exit(void) diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index 08d99c3b78e2..83139efc4629 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -24,9 +24,6 @@ #include "gspca.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) -static const char version[] = "2.1.7"; - MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("OV519 USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -1375,7 +1372,6 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = sif_mode; cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; } - cam->dev_name = (char *) id->driver_info; sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; @@ -2129,21 +2125,20 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define DVNM(name) .driver_info = (kernel_ulong_t) name static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x041e, 0x4052), DVNM("Creative Live! VISTA IM")}, - {USB_DEVICE(0x041e, 0x405f), DVNM("Creative Live! VISTA VF0330")}, - {USB_DEVICE(0x041e, 0x4060), DVNM("Creative Live! VISTA VF0350")}, - {USB_DEVICE(0x041e, 0x4061), DVNM("Creative Live! VISTA VF0400")}, - {USB_DEVICE(0x041e, 0x4064), DVNM("Creative Live! VISTA VF0420")}, - {USB_DEVICE(0x041e, 0x4068), DVNM("Creative Live! VISTA VF0470")}, - {USB_DEVICE(0x045e, 0x028c), DVNM("Microsoft xbox cam")}, - {USB_DEVICE(0x054c, 0x0154), DVNM("Sonny toy4")}, - {USB_DEVICE(0x054c, 0x0155), DVNM("Sonny toy5")}, - {USB_DEVICE(0x05a9, 0x0519), DVNM("OmniVision")}, - {USB_DEVICE(0x05a9, 0x0530), DVNM("OmniVision")}, - {USB_DEVICE(0x05a9, 0x4519), DVNM("OmniVision")}, - {USB_DEVICE(0x05a9, 0x8519), DVNM("OmniVision")}, + {USB_DEVICE(0x041e, 0x4052)}, + {USB_DEVICE(0x041e, 0x405f)}, + {USB_DEVICE(0x041e, 0x4060)}, + {USB_DEVICE(0x041e, 0x4061)}, + {USB_DEVICE(0x041e, 0x4064)}, + {USB_DEVICE(0x041e, 0x4068)}, + {USB_DEVICE(0x045e, 0x028c)}, + {USB_DEVICE(0x054c, 0x0154)}, + {USB_DEVICE(0x054c, 0x0155)}, + {USB_DEVICE(0x05a9, 0x0519)}, + {USB_DEVICE(0x05a9, 0x0530)}, + {USB_DEVICE(0x05a9, 0x4519)}, + {USB_DEVICE(0x05a9, 0x8519)}, {} }; #undef DVNAME @@ -2169,7 +2164,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - PDEBUG(D_PROBE, "v%s registered", version); + PDEBUG(D_PROBE, "registered"); return 0; } static void __exit sd_mod_exit(void) diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index fa7abc411090..7ef18d578811 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -27,9 +27,6 @@ #include "gspca.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) -static const char version[] = "2.1.7"; - MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>"); MODULE_DESCRIPTION("Pixart PAC207"); MODULE_LICENSE("GPL"); @@ -208,7 +205,7 @@ static int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index, } -int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value) +static int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value) { struct usb_device *udev = gspca_dev->dev; int err; @@ -223,8 +220,7 @@ int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value) return err; } - -int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) +static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) { struct usb_device *udev = gspca_dev->dev; int res; @@ -574,17 +570,16 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define DVNM(name) .driver_info = (kernel_ulong_t) name static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x041e, 0x4028), DVNM("Creative Webcam Vista Plus")}, - {USB_DEVICE(0x093a, 0x2460), DVNM("Q-Tec Webcam 100")}, - {USB_DEVICE(0x093a, 0x2463), DVNM("Philips spc200nc pac207")}, - {USB_DEVICE(0x093a, 0x2464), DVNM("Labtec Webcam 1200")}, - {USB_DEVICE(0x093a, 0x2468), DVNM("PAC207")}, - {USB_DEVICE(0x093a, 0x2470), DVNM("Genius GF112")}, - {USB_DEVICE(0x093a, 0x2471), DVNM("Genius VideoCam GE111")}, - {USB_DEVICE(0x093a, 0x2472), DVNM("Genius VideoCam GE110")}, - {USB_DEVICE(0x2001, 0xf115), DVNM("D-Link DSB-C120")}, + {USB_DEVICE(0x041e, 0x4028)}, + {USB_DEVICE(0x093a, 0x2460)}, + {USB_DEVICE(0x093a, 0x2463)}, + {USB_DEVICE(0x093a, 0x2464)}, + {USB_DEVICE(0x093a, 0x2468)}, + {USB_DEVICE(0x093a, 0x2470)}, + {USB_DEVICE(0x093a, 0x2471)}, + {USB_DEVICE(0x093a, 0x2472)}, + {USB_DEVICE(0x2001, 0xf115)}, {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -609,7 +604,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - PDEBUG(D_PROBE, "v%s registered", version); + PDEBUG(D_PROBE, "registered"); return 0; } static void __exit sd_mod_exit(void) diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 5c052e31be4a..ea3d7021f401 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -23,9 +23,6 @@ #include "gspca.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) -static const char version[] = "2.1.7"; - MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); MODULE_DESCRIPTION("Pixart PAC7311"); MODULE_LICENSE("GPL"); @@ -266,7 +263,6 @@ static int sd_config(struct gspca_dev *gspca_dev, reg_w(gspca_dev, 0x3e, 0x20); cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 0x05; cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); @@ -713,16 +709,14 @@ static struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define DVNM(name) .driver_info = (kernel_ulong_t) name static __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x093a, 0x2600), DVNM("Typhoon")}, - {USB_DEVICE(0x093a, 0x2601), DVNM("Philips SPC610NC")}, - {USB_DEVICE(0x093a, 0x2603), DVNM("PAC7312")}, - {USB_DEVICE(0x093a, 0x2608), DVNM("Trust WB-3300p")}, - {USB_DEVICE(0x093a, 0x260e), DVNM("Gigaware VGA PC Camera")}, - /* and also ', Trust WB-3350p, SIGMA cam 2350' */ - {USB_DEVICE(0x093a, 0x260f), DVNM("SnakeCam")}, - {USB_DEVICE(0x093a, 0x2621), DVNM("PAC731x")}, + {USB_DEVICE(0x093a, 0x2600)}, + {USB_DEVICE(0x093a, 0x2601)}, + {USB_DEVICE(0x093a, 0x2603)}, + {USB_DEVICE(0x093a, 0x2608)}, + {USB_DEVICE(0x093a, 0x260e)}, + {USB_DEVICE(0x093a, 0x260f)}, + {USB_DEVICE(0x093a, 0x2621)}, {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -747,7 +741,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - PDEBUG(D_PROBE, "v%s registered", version); + PDEBUG(D_PROBE, "registered"); return 0; } static void __exit sd_mod_exit(void) diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index dbeebe8625c5..e18748c5a14d 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -24,9 +24,6 @@ #include "gspca.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 8) -static const char version[] = "2.1.8"; - MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); MODULE_DESCRIPTION("GSPCA/SN9C102 USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -44,25 +41,29 @@ struct sd { unsigned char brightness; unsigned char autogain; unsigned char autogain_ignore_frames; + unsigned char frames_to_drop; unsigned char freq; /* light freq filter setting */ - unsigned char saturation; - unsigned char hue; - unsigned char contrast; unsigned char fr_h_sz; /* size of frame header */ char sensor; /* Type of image sensor chip */ #define SENSOR_HV7131R 0 #define SENSOR_OV6650 1 #define SENSOR_OV7630 2 -#define SENSOR_OV7630_3 3 -#define SENSOR_PAS106 4 -#define SENSOR_PAS202 5 -#define SENSOR_TAS5110 6 -#define SENSOR_TAS5130CXX 7 +#define SENSOR_PAS106 3 +#define SENSOR_PAS202 4 +#define SENSOR_TAS5110 5 +#define SENSOR_TAS5130CXX 6 char sensor_has_gain; __u8 sensor_addr; + __u8 reg11; }; +/* flags used in the device id table */ +#define F_GAIN 0x01 /* has gain */ +#define F_AUTO 0x02 /* has autogain */ +#define F_SIF 0x04 /* sif or vga */ +#define F_H18 0x08 /* long (18 b) or short (12 b) frame header */ + #define COMP2 0x8f #define COMP 0xc7 /* 0x87 //0x07 */ #define COMP1 0xc9 /* 0x89 //0x09 */ @@ -92,12 +93,6 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val); -static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { { @@ -174,48 +169,6 @@ static struct ctrl sd_ctrls[] = { .set = sd_setfreq, .get = sd_getfreq, }, - { - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 255, - .step = 1, -#define SATURATION_DEF 127 - .default_value = SATURATION_DEF, - }, - .set = sd_setsaturation, - .get = sd_getsaturation, - }, - { - { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = 0, - .maximum = 255, - .step = 1, -#define HUE_DEF 127 - .default_value = HUE_DEF, - }, - .set = sd_sethue, - .get = sd_gethue, - }, - { - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 1, -#define CONTRAST_DEF 127 - .default_value = CONTRAST_DEF, - }, - .set = sd_setcontrast, - .get = sd_getcontrast, - }, }; static struct v4l2_pix_format vga_mode[] = { @@ -248,8 +201,6 @@ static struct v4l2_pix_format sif_mode[] = { .priv = 0}, }; -static const __u8 probe_ov7630[] = {0x08, 0x44}; - static const __u8 initHv7131[] = { 0x46, 0x77, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -321,7 +272,7 @@ static const __u8 initOv7630_3[] = { 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, /* r21 .. r28 */ 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xff /* r29 .. r30 */ }; -static const __u8 ov7630_sensor_init_com[][8] = { +static const __u8 ov7630_sensor_init[][8] = { {0xa0, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, {0xb0, 0x21, 0x01, 0x77, 0x3a, 0x00, 0x00, 0x10}, /* {0xd0, 0x21, 0x12, 0x7c, 0x01, 0x80, 0x34, 0x10}, jfm */ @@ -342,17 +293,6 @@ static const __u8 ov7630_sensor_init_com[][8] = { {0xa0, 0x21, 0x7d, 0xf7, 0x8e, 0x00, 0x30, 0x10}, {0xd0, 0x21, 0x17, 0x1c, 0xbd, 0x06, 0xf6, 0x10}, }; -static const __u8 ov7630_sensor_init[][8] = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 200ms */ - {0xa0, 0x21, 0x11, 0x01, 0xbd, 0x06, 0xf6, 0x10}, /* jfm */ - {0xa0, 0x21, 0x10, 0x57, 0xbd, 0x06, 0xf6, 0x16}, - {0xa0, 0x21, 0x76, 0x02, 0xbd, 0x06, 0xf6, 0x16}, - {0xa0, 0x21, 0x00, 0x10, 0xbd, 0x06, 0xf6, 0x15}, /* gain */ -}; -static const __u8 ov7630_sensor_init_3[][8] = { - {0xa0, 0x21, 0x2a, 0xa0, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x21, 0x2a, 0x80, 0x00, 0x00, 0x00, 0x10}, -}; static const __u8 initPas106[] = { 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x00, 0x00, @@ -542,7 +482,6 @@ static void setbrightness(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_OV6650: - case SENSOR_OV7630_3: case SENSOR_OV7630: { __u8 i2cOV[] = {0xa0, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}; @@ -635,7 +574,7 @@ static void setsensorgain(struct gspca_dev *gspca_dev) case SENSOR_OV6650: gain >>= 1; /* fall thru */ - case SENSOR_OV7630_3: { + case SENSOR_OV7630: { __u8 i2c[] = {0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; i2c[1] = sd->sensor_addr; @@ -690,7 +629,7 @@ static void setexposure(struct gspca_dev *gspca_dev) break; } case SENSOR_OV6650: - case SENSOR_OV7630_3: { + case SENSOR_OV7630: { /* The ov6650 / ov7630 have 2 registers which both influence exposure, register 11, whose low nibble sets the nr off fps according to: fps = 30 / (low_nibble + 1) @@ -705,16 +644,20 @@ static void setexposure(struct gspca_dev *gspca_dev) The code maps our 0 - 510 ms exposure ctrl to these 2 registers, trying to keep fps as high as possible. */ - __u8 i2c[] = {0xb0, 0x00, 0x10, 0x00, 0xc0, 0x00, 0x00, 0x10}; - int reg10, reg11; + __u8 i2c[] = {0xb0, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}; + int reg10, reg11, reg10_max; + /* ov6645 datasheet says reg10_max is 9a, but that uses tline * 2 * reg10 as formula for calculating texpo, the ov6650 probably uses the same formula as the 7730 which uses tline * 4 * reg10, which explains why the reg10max we've found experimentally for the ov6650 is exactly half that of the ov6645. The ov7630 datasheet says the max is 0x41. */ - const int reg10_max = (sd->sensor == SENSOR_OV6650) - ? 0x4d : 0x41; + if (sd->sensor == SENSOR_OV6650) { + reg10_max = 0x4d; + i2c[4] = 0xc0; /* OV6650 needs non default vsync pol */ + } else + reg10_max = 0x41; reg11 = (60 * sd->exposure + 999) / 1000; if (reg11 < 1) @@ -735,20 +678,23 @@ static void setexposure(struct gspca_dev *gspca_dev) else if (reg10 > reg10_max) reg10 = reg10_max; + /* In 640x480, if the reg11 has less than 3, the image is + unstable (not enough bandwidth). */ + if (gspca_dev->width == 640 && reg11 < 3) + reg11 = 3; + /* Write reg 10 and reg11 low nibble */ i2c[1] = sd->sensor_addr; i2c[3] = reg10; i2c[4] |= reg11 - 1; - if (sd->sensor == SENSOR_OV7630_3) { - __u8 reg76 = reg10 & 0x03; - __u8 i2c_reg76[] = {0xa0, 0x21, 0x76, 0x00, - 0x00, 0x00, 0x00, 0x10}; - reg10 >>= 2; - i2c_reg76[3] = reg76; - if (i2c_w(gspca_dev, i2c_reg76) < 0) - PDEBUG(D_ERR, "i2c error exposure"); - } - if (i2c_w(gspca_dev, i2c) < 0) + + /* If register 11 didn't change, don't change it */ + if (sd->reg11 == reg11 ) + i2c[0] = 0xa0; + + if (i2c_w(gspca_dev, i2c) == 0) + sd->reg11 = reg11; + else PDEBUG(D_ERR, "i2c error exposure"); break; } @@ -761,11 +707,11 @@ static void setfreq(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_OV6650: - case SENSOR_OV7630_3: { + case SENSOR_OV7630: { /* Framerate adjust register for artificial light 50 hz flicker - compensation, identical to ov6630 0x2b register, see ov6630 - datasheet. - 0x4f -> (30 fps -> 25 fps), 0x00 -> no adjustment */ + compensation, for the ov6650 this is identical to ov6630 + 0x2b register, see ov6630 datasheet. + 0x4f / 0x8a -> (30 fps -> 25 fps), 0x00 -> no adjustment */ __u8 i2c[] = {0xa0, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}; switch (sd->freq) { default: @@ -786,69 +732,6 @@ static void setfreq(struct gspca_dev *gspca_dev) } } -static void setsaturation(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->sensor) { -/* case SENSOR_OV6650: */ - case SENSOR_OV7630_3: - case SENSOR_OV7630: { - __u8 i2c[] = {0xa0, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}; - i2c[1] = sd->sensor_addr; - i2c[3] = sd->saturation & 0xf0; - if (i2c_w(gspca_dev, i2c) < 0) - PDEBUG(D_ERR, "i2c error setsaturation"); - else - PDEBUG(D_CONF, "saturation set to: %d", - (int)sd->saturation); - break; - } - } -} - -static void sethue(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->sensor) { -/* case SENSOR_OV6650: */ - case SENSOR_OV7630_3: - case SENSOR_OV7630: { - __u8 i2c[] = {0xa0, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10}; - i2c[1] = sd->sensor_addr; - i2c[3] = 0x20 | (sd->hue >> 3); - if (i2c_w(gspca_dev, i2c) < 0) - PDEBUG(D_ERR, "i2c error setsaturation"); - else - PDEBUG(D_CONF, "hue set to: %d", (int)sd->hue); - break; - } - } -} - -static void setcontrast(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->sensor) { -/* case SENSOR_OV6650: */ - case SENSOR_OV7630_3: - case SENSOR_OV7630: { - __u8 i2c[] = {0xa0, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10}; - i2c[1] = sd->sensor_addr; - i2c[3] = 0x20 | (sd->contrast >> 3); - if (i2c_w(gspca_dev, i2c) < 0) - PDEBUG(D_ERR, "i2c error setcontrast"); - else - PDEBUG(D_CONF, "contrast set to: %d", - (int)sd->contrast); - break; - } - } -} - - static void do_autogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -874,88 +757,32 @@ static int sd_config(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; - __u16 product; int sif = 0; /* nctrls depends upon the sensor, so we use a per cam copy */ memcpy(&sd->sd_desc, gspca_dev->sd_desc, sizeof(struct sd_desc)); gspca_dev->sd_desc = &sd->sd_desc; - sd->fr_h_sz = 12; /* default size of the frame header */ - sd->sd_desc.nctrls = 2; /* default nb of ctrls */ - sd->autogain = AUTOGAIN_DEF; /* default is autogain active */ - - product = id->idProduct; -/* switch (id->idVendor) { */ -/* case 0x0c45: * Sonix */ - switch (product) { - case 0x6001: /* SN9C102 */ - case 0x6005: /* SN9C101 */ - case 0x6007: /* SN9C101 */ - sd->sensor = SENSOR_TAS5110; - sd->sensor_has_gain = 1; - sd->sd_desc.nctrls = 4; - sd->sd_desc.dq_callback = do_autogain; - sif = 1; - break; - case 0x6009: /* SN9C101 */ - case 0x600d: /* SN9C101 */ - case 0x6029: /* SN9C101 */ - sd->sensor = SENSOR_PAS106; - sif = 1; - break; - case 0x6011: /* SN9C101 - SN9C101G */ - sd->sensor = SENSOR_OV6650; - sd->sensor_has_gain = 1; - sd->sensor_addr = 0x60; - sd->sd_desc.nctrls = 5; - sd->sd_desc.dq_callback = do_autogain; - sif = 1; - break; - case 0x6019: /* SN9C101 */ - case 0x602c: /* SN9C102 */ - case 0x602e: /* SN9C102 */ - sd->sensor = SENSOR_OV7630; - sd->sensor_addr = 0x21; - break; - case 0x60b0: /* SN9C103 */ - sd->sensor = SENSOR_OV7630_3; - sd->sensor_addr = 0x21; - sd->fr_h_sz = 18; /* size of frame header */ - sd->sensor_has_gain = 1; - sd->sd_desc.nctrls = 8; - sd->sd_desc.dq_callback = do_autogain; - sd->autogain = 0; - break; - case 0x6024: /* SN9C102 */ - case 0x6025: /* SN9C102 */ - sd->sensor = SENSOR_TAS5130CXX; - break; - case 0x6028: /* SN9C102 */ - sd->sensor = SENSOR_PAS202; - break; - case 0x602d: /* SN9C102 */ - sd->sensor = SENSOR_HV7131R; - break; - case 0x60af: /* SN9C103 */ - sd->sensor = SENSOR_PAS202; - sd->fr_h_sz = 18; /* size of frame header (?) */ - break; - } -/* break; */ -/* } */ + /* copy the webcam info from the device id */ + sd->sensor = (id->driver_info >> 24) & 0xff; + if (id->driver_info & (F_GAIN << 16)) + sd->sensor_has_gain = 1; + if (id->driver_info & (F_AUTO << 16)) + sd->sd_desc.dq_callback = do_autogain; + if (id->driver_info & (F_SIF << 16)) + sif = 1; + if (id->driver_info & (F_H18 << 16)) + sd->fr_h_sz = 18; /* size of frame header */ + else + sd->fr_h_sz = 12; + sd->sd_desc.nctrls = (id->driver_info >> 8) & 0xff; + sd->sensor_addr = id->driver_info & 0xff; cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 0x01; if (!sif) { cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); - if (sd->sensor == SENSOR_OV7630_3) { - /* We only have 320x240 & 640x480 */ - cam->cam_mode++; - cam->nmodes--; - } } else { cam->cam_mode = sif_mode; cam->nmodes = ARRAY_SIZE(sif_mode); @@ -963,12 +790,9 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->brightness = BRIGHTNESS_DEF; sd->gain = GAIN_DEF; sd->exposure = EXPOSURE_DEF; + sd->autogain = AUTOGAIN_DEF; sd->freq = FREQ_DEF; - sd->contrast = CONTRAST_DEF; - sd->saturation = SATURATION_DEF; - sd->hue = HUE_DEF; - if (sd->sensor == SENSOR_OV7630_3) /* jfm: from win trace */ - reg_w(gspca_dev, 0x01, probe_ov7630, sizeof probe_ov7630); + return 0; } @@ -1002,9 +826,8 @@ static void pas106_i2cinit(struct gspca_dev *gspca_dev) static void sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int mode, l; + int mode, l = 0x1f; const __u8 *sn9c10x; - __u8 reg01, reg17; __u8 reg17_19[3]; mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; @@ -1022,13 +845,11 @@ static void sd_start(struct gspca_dev *gspca_dev) reg17_19[2] = 0x20; break; case SENSOR_OV7630: - sn9c10x = initOv7630; - reg17_19[0] = 0x68; - reg17_19[1] = (mode << 4) | COMP2; - reg17_19[2] = MCK_INIT1; - break; - case SENSOR_OV7630_3: - sn9c10x = initOv7630_3; + if (sd->fr_h_sz == 18) { /* SN9C103 */ + sn9c10x = initOv7630_3; + l = sizeof initOv7630_3; + } else + sn9c10x = initOv7630; reg17_19[0] = 0x68; reg17_19[1] = (mode << 4) | COMP2; reg17_19[2] = MCK_INIT1; @@ -1059,30 +880,11 @@ static void sd_start(struct gspca_dev *gspca_dev) reg17_19[2] = mode ? 0x23 : 0x43; break; } - switch (sd->sensor) { - case SENSOR_OV7630: - reg01 = 0x06; - reg17 = 0x29; - l = sizeof initOv7630; - break; - case SENSOR_OV7630_3: - reg01 = 0x44; - reg17 = 0x68; - l = sizeof initOv7630_3; - break; - default: - reg01 = sn9c10x[0]; - reg17 = sn9c10x[0x17 - 1]; - l = 0x1f; - break; - } /* reg 0x01 bit 2 video transfert on */ - reg_w(gspca_dev, 0x01, ®01, 1); + reg_w(gspca_dev, 0x01, &sn9c10x[0x01 - 1], 1); /* reg 0x17 SensorClk enable inv Clk 0x60 */ - reg_w(gspca_dev, 0x17, ®17, 1); -/*fixme: for ov7630 102 - reg_w(gspca_dev, 0x01, {0x06, sn9c10x[1]}, 2); */ + reg_w(gspca_dev, 0x17, &sn9c10x[0x17 - 1], 1); /* Set the registers from the template */ reg_w_big(gspca_dev, 0x01, sn9c10x, l); switch (sd->sensor) { @@ -1095,17 +897,13 @@ static void sd_start(struct gspca_dev *gspca_dev) sizeof ov6650_sensor_init); break; case SENSOR_OV7630: - i2c_w_vector(gspca_dev, ov7630_sensor_init_com, - sizeof ov7630_sensor_init_com); - msleep(200); i2c_w_vector(gspca_dev, ov7630_sensor_init, sizeof ov7630_sensor_init); - break; - case SENSOR_OV7630_3: - i2c_w_vector(gspca_dev, ov7630_sensor_init_com, - sizeof ov7630_sensor_init_com); - msleep(200); - i2c_w(gspca_dev, ov7630_sensor_init_3[mode]); + if (sd->fr_h_sz == 18) { /* SN9C103 */ + const __u8 i2c[] = { 0xa0, 0x21, 0x13, 0x80, 0x00, + 0x00, 0x00, 0x10 }; + i2c_w(gspca_dev, i2c); + } break; case SENSOR_PAS106: pas106_i2cinit(gspca_dev); @@ -1145,14 +943,14 @@ static void sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x18, ®17_19[1], 2); msleep(20); + sd->reg11 = -1; + setgain(gspca_dev); setbrightness(gspca_dev); setexposure(gspca_dev); setfreq(gspca_dev); - setsaturation(gspca_dev); - sethue(gspca_dev); - setcontrast(gspca_dev); + sd->frames_to_drop = 0; sd->autogain_ignore_frames = 0; atomic_set(&sd->avg_lum, -1); } @@ -1198,21 +996,31 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, && data[3 + i] == 0xc4 && data[4 + i] == 0xc4 && data[5 + i] == 0x96) { /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, - frame, data, 0); + int lum = -1; + int pkt_type = LAST_PACKET; + if (len - i < sd->fr_h_sz) { - atomic_set(&sd->avg_lum, -1); PDEBUG(D_STREAM, "packet too short to" " get avg brightness"); } else if (sd->fr_h_sz == 12) { - atomic_set(&sd->avg_lum, - data[i + 8] + - (data[i + 9] << 8)); + lum = data[i + 8] + (data[i + 9] << 8); } else { - atomic_set(&sd->avg_lum, - data[i + 9] + - (data[i + 10] << 8)); + lum = data[i + 9] + + (data[i + 10] << 8); + } + if (lum == 0) { + lum = -1; + sd->frames_to_drop = 2; + } + atomic_set(&sd->avg_lum, lum); + + if (sd->frames_to_drop) { + sd->frames_to_drop--; + pkt_type = DISCARD_PACKET; } + + frame = gspca_frame_add(gspca_dev, pkt_type, + frame, data, 0); data += i + sd->fr_h_sz; len -= i + sd->fr_h_sz; gspca_frame_add(gspca_dev, FIRST_PACKET, @@ -1327,60 +1135,6 @@ static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) return 0; } -static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->saturation = val; - if (gspca_dev->streaming) - setsaturation(gspca_dev); - return 0; -} - -static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->saturation; - return 0; -} - -static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->hue = val; - if (gspca_dev->streaming) - sethue(gspca_dev); - return 0; -} - -static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->hue; - return 0; -} - -static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->contrast = val; - if (gspca_dev->streaming) - setcontrast(gspca_dev); - return 0; -} - -static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->contrast; - return 0; -} - static int sd_querymenu(struct gspca_dev *gspca_dev, struct v4l2_querymenu *menu) { @@ -1418,27 +1172,47 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define DVNM(name) .driver_info = (kernel_ulong_t) name +#define SFCI(sensor, flags, nctrls, i2c_addr) \ + .driver_info = (SENSOR_ ## sensor << 24) \ + | ((flags) << 16) \ + | ((nctrls) << 8) \ + | (i2c_addr) static __devinitdata struct usb_device_id device_table[] = { #ifndef CONFIG_USB_SN9C102 - {USB_DEVICE(0x0c45, 0x6001), DVNM("Genius VideoCAM NB")}, - {USB_DEVICE(0x0c45, 0x6005), DVNM("Sweex Tas5110")}, - {USB_DEVICE(0x0c45, 0x6007), DVNM("Sonix sn9c101 + Tas5110D")}, - {USB_DEVICE(0x0c45, 0x6009), DVNM("spcaCam@120")}, - {USB_DEVICE(0x0c45, 0x600d), DVNM("spcaCam@120")}, + {USB_DEVICE(0x0c45, 0x6001), /* SN9C102 */ + SFCI(TAS5110, F_GAIN|F_AUTO|F_SIF, 4, 0)}, + {USB_DEVICE(0x0c45, 0x6005), /* SN9C101 */ + SFCI(TAS5110, F_GAIN|F_AUTO|F_SIF, 4, 0)}, + {USB_DEVICE(0x0c45, 0x6007), /* SN9C101 */ + SFCI(TAS5110, F_GAIN|F_AUTO|F_SIF, 4, 0)}, + {USB_DEVICE(0x0c45, 0x6009), /* SN9C101 */ + SFCI(PAS106, F_SIF, 2, 0)}, + {USB_DEVICE(0x0c45, 0x600d), /* SN9C101 */ + SFCI(PAS106, F_SIF, 2, 0)}, #endif - {USB_DEVICE(0x0c45, 0x6011), DVNM("MAX Webcam Microdia")}, + {USB_DEVICE(0x0c45, 0x6011), /* SN9C101 - SN9C101G */ + SFCI(OV6650, F_GAIN|F_AUTO|F_SIF, 5, 0x60)}, #ifndef CONFIG_USB_SN9C102 - {USB_DEVICE(0x0c45, 0x6019), DVNM("Generic Sonix OV7630")}, - {USB_DEVICE(0x0c45, 0x6024), DVNM("Generic Sonix Tas5130c")}, - {USB_DEVICE(0x0c45, 0x6025), DVNM("Xcam Shanga")}, - {USB_DEVICE(0x0c45, 0x6028), DVNM("Sonix Btc Pc380")}, - {USB_DEVICE(0x0c45, 0x6029), DVNM("spcaCam@150")}, - {USB_DEVICE(0x0c45, 0x602c), DVNM("Generic Sonix OV7630")}, - {USB_DEVICE(0x0c45, 0x602d), DVNM("LIC-200 LG")}, - {USB_DEVICE(0x0c45, 0x602e), DVNM("Genius VideoCam Messenger")}, - {USB_DEVICE(0x0c45, 0x60af), DVNM("Trust WB3100P")}, - {USB_DEVICE(0x0c45, 0x60b0), DVNM("Genius VideoCam Look")}, + {USB_DEVICE(0x0c45, 0x6019), /* SN9C101 */ + SFCI(OV7630, F_GAIN|F_AUTO, 5, 0x21)}, + {USB_DEVICE(0x0c45, 0x6024), /* SN9C102 */ + SFCI(TAS5130CXX, 0, 2, 0)}, + {USB_DEVICE(0x0c45, 0x6025), /* SN9C102 */ + SFCI(TAS5130CXX, 0, 2, 0)}, + {USB_DEVICE(0x0c45, 0x6028), /* SN9C102 */ + SFCI(PAS202, 0, 2, 0)}, + {USB_DEVICE(0x0c45, 0x6029), /* SN9C101 */ + SFCI(PAS106, F_SIF, 2, 0)}, + {USB_DEVICE(0x0c45, 0x602c), /* SN9C102 */ + SFCI(OV7630, F_GAIN|F_AUTO, 5, 0x21)}, + {USB_DEVICE(0x0c45, 0x602d), /* SN9C102 */ + SFCI(HV7131R, 0, 2, 0)}, + {USB_DEVICE(0x0c45, 0x602e), /* SN9C102 */ + SFCI(OV7630, F_GAIN|F_AUTO, 5, 0x21)}, + {USB_DEVICE(0x0c45, 0x60af), /* SN9C103 */ + SFCI(PAS202, F_H18, 2, 0)}, + {USB_DEVICE(0x0c45, 0x60b0), /* SN9C103 */ + SFCI(OV7630, F_GAIN|F_AUTO|F_H18, 5, 0x21)}, #endif {} }; @@ -1464,7 +1238,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - PDEBUG(D_PROBE, "v%s registered", version); + PDEBUG(D_PROBE, "registered"); return 0; } static void __exit sd_mod_exit(void) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 3e68b9926956..33a3df1f6915 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -24,9 +24,6 @@ #include "gspca.h" #include "jpeg.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) -static const char version[] = "2.1.7"; - MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -361,6 +358,7 @@ static const __u8 mo4000_sensor_init[][8] = { }; static const __u8 ov7660_sensor_init[][8] = { {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */ +/* (delay 20ms) */ {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10}, /* Outformat ?? rawRGB */ {0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */ @@ -539,13 +537,31 @@ static void reg_r(struct gspca_dev *gspca_dev, value, 0, gspca_dev->usb_buf, len, 500); + PDEBUG(D_USBI, "reg_r [%02x] -> %02x", value, gspca_dev->usb_buf[0]); } +static void reg_w1(struct gspca_dev *gspca_dev, + __u16 value, + __u8 data) +{ + PDEBUG(D_USBO, "reg_w1 [%02x] = %02x", value, data); + gspca_dev->usb_buf[0] = data; + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, + 0, + gspca_dev->usb_buf, 1, + 500); +} static void reg_w(struct gspca_dev *gspca_dev, __u16 value, const __u8 *buffer, int len) { + PDEBUG(D_USBO, "reg_w [%02x] = %02x %02x ..", + value, buffer[0], buffer[1]); if (len <= sizeof gspca_dev->usb_buf) { memcpy(gspca_dev->usb_buf, buffer, len); usb_control_msg(gspca_dev->dev, @@ -571,31 +587,42 @@ static void reg_w(struct gspca_dev *gspca_dev, } } -/* I2C write 2 bytes */ -static void i2c_w2(struct gspca_dev *gspca_dev, - const __u8 *buffer) +/* I2C write 1 byte */ +static void i2c_w1(struct gspca_dev *gspca_dev, __u8 reg, __u8 val) { struct sd *sd = (struct sd *) gspca_dev; - __u8 mode[8]; - /* is i2c ready */ - mode[0] = 0x81 | (2 << 4); - mode[1] = sd->i2c_base; - mode[2] = buffer[0]; - mode[3] = buffer[1]; - mode[4] = 0; - mode[5] = 0; - mode[6] = 0; - mode[7] = 0x10; - reg_w(gspca_dev, 0x08, mode, 8); + PDEBUG(D_USBO, "i2c_w2 [%02x] = %02x", reg, val); + gspca_dev->usb_buf[0] = 0x81 | (2 << 4); /* = a1 */ + gspca_dev->usb_buf[1] = sd->i2c_base; + gspca_dev->usb_buf[2] = reg; + gspca_dev->usb_buf[3] = val; + gspca_dev->usb_buf[4] = 0; + gspca_dev->usb_buf[5] = 0; + gspca_dev->usb_buf[6] = 0; + gspca_dev->usb_buf[7] = 0x10; + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x08, /* value = i2c */ + 0, + gspca_dev->usb_buf, 8, + 500); } /* I2C write 8 bytes */ static void i2c_w8(struct gspca_dev *gspca_dev, const __u8 *buffer) { - reg_w(gspca_dev, 0x08, buffer, 8); - msleep(1); + memcpy(gspca_dev->usb_buf, buffer, 8); + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x08, 0, /* value, index */ + gspca_dev->usb_buf, 8, + 500); } /* read 5 bytes in gspca_dev->usb_buf */ @@ -613,24 +640,21 @@ static void i2c_r5(struct gspca_dev *gspca_dev, __u8 reg) mode[6] = 0; mode[7] = 0x10; i2c_w8(gspca_dev, mode); + msleep(2); mode[0] = 0x81 | (5 << 4) | 0x02; mode[2] = 0; i2c_w8(gspca_dev, mode); + msleep(2); reg_r(gspca_dev, 0x0a, 5); } static int probesensor(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 reg02; - static const __u8 datasend[] = { 2, 0 }; - /* reg val1 val2 val3 val4 */ - i2c_w2(gspca_dev, datasend); -/* should write 0xa1 0x11 0x02 0x00 0x00 0x00 0x00 the 0x10 is add by i2cw */ + i2c_w1(gspca_dev, 0x02, 0); /* sensor wakeup */ msleep(10); - reg02 = 0x66; - reg_w(gspca_dev, 0x02, ®02, 1); /* Gpio on */ + reg_w1(gspca_dev, 0x02, 0x66); /* Gpio on */ msleep(10); i2c_r5(gspca_dev, 0); /* read sensor id */ if (gspca_dev->usb_buf[0] == 0x02 @@ -642,7 +666,7 @@ static int probesensor(struct gspca_dev *gspca_dev) sd->sensor = SENSOR_HV7131R; return SENSOR_HV7131R; } - PDEBUG(D_PROBE, "Find Sensor %d %d %d", + PDEBUG(D_PROBE, "Find Sensor 0x%02x 0x%02x 0x%02x", gspca_dev->usb_buf[0], gspca_dev->usb_buf[1], gspca_dev->usb_buf[2]); PDEBUG(D_PROBE, "Sensor sn9c102P Not found"); @@ -653,8 +677,6 @@ static int configure_gpio(struct gspca_dev *gspca_dev, const __u8 *sn9c1xx) { struct sd *sd = (struct sd *) gspca_dev; - __u8 data; - __u8 regF1; const __u8 *reg9a; static const __u8 reg9a_def[] = {0x08, 0x40, 0x20, 0x10, 0x00, 0x04}; @@ -663,15 +685,13 @@ static int configure_gpio(struct gspca_dev *gspca_dev, static const __u8 reg9a_sn9c325[] = {0x0a, 0x40, 0x38, 0x30, 0x00, 0x20}; - - regF1 = 0x00; - reg_w(gspca_dev, 0xf1, ®F1, 1); - reg_w(gspca_dev, 0x01, &sn9c1xx[0], 1); /*fixme:jfm was [1] en v1*/ + reg_w1(gspca_dev, 0xf1, 0x00); + reg_w1(gspca_dev, 0x01, sn9c1xx[0]); /*fixme:jfm was [1] en v1*/ /* configure gpio */ reg_w(gspca_dev, 0x01, &sn9c1xx[1], 2); reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2); - reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5); /* jfm was 3 */ + reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5); /* jfm len was 3 */ switch (sd->bridge) { case BRIDGE_SN9C325: reg9a = reg9a_sn9c325; @@ -685,35 +705,25 @@ static int configure_gpio(struct gspca_dev *gspca_dev, } reg_w(gspca_dev, 0x9a, reg9a, 6); - data = 0x60; /*fixme:jfm 60 00 00 (3) */ - reg_w(gspca_dev, 0xd4, &data, 1); + reg_w1(gspca_dev, 0xd4, 0x60); /*fixme:jfm 60 00 00 (3) ? */ reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f); switch (sd->bridge) { case BRIDGE_SN9C120: /* from win trace */ - data = 0x61; - reg_w(gspca_dev, 0x01, &data, 1); - data = 0x20; - reg_w(gspca_dev, 0x17, &data, 1); - data = 0x60; - reg_w(gspca_dev, 0x01, &data, 1); + reg_w1(gspca_dev, 0x01, 0x61); + reg_w1(gspca_dev, 0x17, 0x20); + reg_w1(gspca_dev, 0x01, 0x60); break; case BRIDGE_SN9C325: - data = 0x43; - reg_w(gspca_dev, 0x01, &data, 1); - data = 0xae; - reg_w(gspca_dev, 0x17, &data, 1); - data = 0x42; - reg_w(gspca_dev, 0x01, &data, 1); + reg_w1(gspca_dev, 0x01, 0x43); + reg_w1(gspca_dev, 0x17, 0xae); + reg_w1(gspca_dev, 0x01, 0x42); break; default: - data = 0x43; - reg_w(gspca_dev, 0x01, &data, 1); - data = 0x61; - reg_w(gspca_dev, 0x17, &data, 1); - data = 0x42; - reg_w(gspca_dev, 0x01, &data, 1); + reg_w1(gspca_dev, 0x01, 0x43); + reg_w1(gspca_dev, 0x17, 0x61); + reg_w1(gspca_dev, 0x01, 0x42); } if (sd->sensor == SENSOR_HV7131R) { @@ -770,6 +780,9 @@ static void ov7660_InitSensor(struct gspca_dev *gspca_dev) { int i = 0; + i2c_w8(gspca_dev, ov7660_sensor_init[i]); /* reset SCCB */ + i++; + msleep(20); while (ov7660_sensor_init[i][0]) { i2c_w8(gspca_dev, ov7660_sensor_init[i]); i++; @@ -782,194 +795,16 @@ static int sd_config(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; - __u16 vendor; - __u16 product; - - vendor = id->idVendor; - product = id->idProduct; - sd->sensor = -1; - switch (vendor) { - case 0x0458: /* Genius */ -/* switch (product) { - case 0x7025: */ - sd->bridge = BRIDGE_SN9C120; - sd->sensor = SENSOR_MI0360; - sd->i2c_base = 0x5d; -/* break; - } */ - break; - case 0x045e: -/* switch (product) { - case 0x00f5: - case 0x00f7: */ - sd->bridge = BRIDGE_SN9C105; - sd->sensor = SENSOR_OV7660; - sd->i2c_base = 0x21; -/* break; - } */ - break; - case 0x0471: /* Philips */ -/* switch (product) { - case 0x0327: - case 0x0328: - case 0x0330: */ - sd->bridge = BRIDGE_SN9C105; - sd->sensor = SENSOR_MI0360; - sd->i2c_base = 0x5d; -/* break; - } */ - break; - case 0x0c45: /* Sonix */ - switch (product) { - case 0x6040: - sd->bridge = BRIDGE_SN9C102P; -/* sd->sensor = SENSOR_MI0360; * from BW600.inf */ -/*fixme: MI0360 base=5d ? */ - sd->sensor = SENSOR_HV7131R; /* gspcav1 value */ - sd->i2c_base = 0x11; - break; -/* case 0x607a: * from BW600.inf - sd->bridge = BRIDGE_SN9C102P; - sd->sensor = SENSOR_OV7648; - sd->i2c_base = 0x??; - break; */ - case 0x607c: - sd->bridge = BRIDGE_SN9C102P; - sd->sensor = SENSOR_HV7131R; - sd->i2c_base = 0x11; - break; -/* case 0x607e: * from BW600.inf - sd->bridge = BRIDGE_SN9C102P; - sd->sensor = SENSOR_OV7630; - sd->i2c_base = 0x??; - break; */ - case 0x60c0: - sd->bridge = BRIDGE_SN9C105; - sd->sensor = SENSOR_MI0360; - sd->i2c_base = 0x5d; - break; -/* case 0x60c8: * from BW600.inf - sd->bridge = BRIDGE_SN9C105; - sd->sensor = SENSOR_OM6801; - sd->i2c_base = 0x??; - break; */ -/* case 0x60cc: * from BW600.inf - sd->bridge = BRIDGE_SN9C105; - sd->sensor = SENSOR_HV7131GP; - sd->i2c_base = 0x??; - break; */ - case 0x60ec: - sd->bridge = BRIDGE_SN9C105; - sd->sensor = SENSOR_MO4000; - sd->i2c_base = 0x21; - break; -/* case 0x60ef: * from BW600.inf - sd->bridge = BRIDGE_SN9C105; - sd->sensor = SENSOR_ICM105C; - sd->i2c_base = 0x??; - break; */ -/* case 0x60fa: * from BW600.inf - sd->bridge = BRIDGE_SN9C105; - sd->sensor = SENSOR_OV7648; - sd->i2c_base = 0x??; - break; */ - case 0x60fb: - sd->bridge = BRIDGE_SN9C105; - sd->sensor = SENSOR_OV7660; - sd->i2c_base = 0x21; - break; - case 0x60fc: - sd->bridge = BRIDGE_SN9C105; - sd->sensor = SENSOR_HV7131R; - sd->i2c_base = 0x11; - break; -/* case 0x60fe: * from BW600.inf - sd->bridge = BRIDGE_SN9C105; - sd->sensor = SENSOR_OV7630; - sd->i2c_base = 0x??; - break; */ -/* case 0x6108: * from BW600.inf - sd->bridge = BRIDGE_SN9C120; - sd->sensor = SENSOR_OM6801; - sd->i2c_base = 0x??; - break; */ -/* case 0x6122: * from BW600.inf - sd->bridge = BRIDGE_SN9C110; - sd->sensor = SENSOR_ICM105C; - sd->i2c_base = 0x??; - break; */ - case 0x612a: -/* sd->bridge = BRIDGE_SN9C110; * in BW600.inf */ - sd->bridge = BRIDGE_SN9C325; - sd->sensor = SENSOR_OV7648; - sd->i2c_base = 0x21; -/*fixme: sensor_init has base = 00 et 6e!*/ - break; -/* case 0x6123: * from BW600.inf - sd->bridge = BRIDGE_SN9C110; - sd->sensor = SENSOR_SanyoCCD; - sd->i2c_base = 0x??; - break; */ - case 0x612c: - sd->bridge = BRIDGE_SN9C110; - sd->sensor = SENSOR_MO4000; - sd->i2c_base = 0x21; - break; -/* case 0x612e: * from BW600.inf - sd->bridge = BRIDGE_SN9C110; - sd->sensor = SENSOR_OV7630; - sd->i2c_base = 0x??; - break; */ -/* case 0x612f: * from BW600.inf - sd->bridge = BRIDGE_SN9C110; - sd->sensor = SENSOR_ICM105C; - sd->i2c_base = 0x??; - break; */ - case 0x6130: - sd->bridge = BRIDGE_SN9C120; - sd->sensor = SENSOR_MI0360; - sd->i2c_base = 0x5d; - break; - case 0x6138: - sd->bridge = BRIDGE_SN9C120; - sd->sensor = SENSOR_MO4000; - sd->i2c_base = 0x21; - break; -/* case 0x613a: * from BW600.inf - sd->bridge = BRIDGE_SN9C120; - sd->sensor = SENSOR_OV7648; - sd->i2c_base = 0x??; - break; */ - case 0x613b: - sd->bridge = BRIDGE_SN9C120; - sd->sensor = SENSOR_OV7660; - sd->i2c_base = 0x21; - break; - case 0x613c: - sd->bridge = BRIDGE_SN9C120; - sd->sensor = SENSOR_HV7131R; - sd->i2c_base = 0x11; - break; -/* case 0x613e: * from BW600.inf - sd->bridge = BRIDGE_SN9C120; - sd->sensor = SENSOR_OV7630; - sd->i2c_base = 0x??; - break; */ - } - break; - } - if (sd->sensor < 0) { - PDEBUG(D_ERR, "Invalid vendor/product %04x:%04x", - vendor, product); - return -EINVAL; - } cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 0x01; cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); + sd->bridge = id->driver_info >> 16; + sd->sensor = id->driver_info >> 8; + sd->i2c_base = id->driver_info; + sd->qindex = 4; /* set the quantization table */ sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; @@ -983,34 +818,26 @@ static int sd_open(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; /* const __u8 *sn9c1xx; */ - __u8 regF1; __u8 regGpio[] = { 0x29, 0x74 }; + __u8 regF1; /* setup a selector by bridge */ - regF1 = 0x01; - reg_w(gspca_dev, 0xf1, ®F1, 1); + reg_w1(gspca_dev, 0xf1, 0x01); reg_r(gspca_dev, 0x00, 1); /* -> regF1 = 0x00 */ - regF1 = gspca_dev->usb_buf[0]; - reg_w(gspca_dev, 0xf1, ®F1, 1); + reg_w1(gspca_dev, 0xf1, gspca_dev->usb_buf[0]); reg_r(gspca_dev, 0x00, 1); regF1 = gspca_dev->usb_buf[0]; switch (sd->bridge) { case BRIDGE_SN9C102P: if (regF1 != 0x11) return -ENODEV; - reg_w(gspca_dev, 0x02, ®Gpio[1], 1); + reg_w1(gspca_dev, 0x02, regGpio[1]); break; case BRIDGE_SN9C105: if (regF1 != 0x11) return -ENODEV; reg_w(gspca_dev, 0x02, regGpio, 2); break; - case BRIDGE_SN9C110: - if (regF1 != 0x12) - return -ENODEV; - regGpio[1] = 0x62; - reg_w(gspca_dev, 0x02, ®Gpio[1], 1); - break; case BRIDGE_SN9C120: if (regF1 != 0x12) return -ENODEV; @@ -1018,16 +845,15 @@ static int sd_open(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x02, regGpio, 2); break; default: +/* case BRIDGE_SN9C110: */ /* case BRIDGE_SN9C325: */ if (regF1 != 0x12) return -ENODEV; - regGpio[1] = 0x62; - reg_w(gspca_dev, 0x02, ®Gpio[1], 1); + reg_w1(gspca_dev, 0x02, 0x62); break; } - regF1 = 0x01; - reg_w(gspca_dev, 0xf1, ®F1, 1); + reg_w1(gspca_dev, 0xf1, 0x01); return 0; } @@ -1123,7 +949,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) } k2 = sd->brightness >> 10; - reg_w(gspca_dev, 0x96, &k2, 1); + reg_w1(gspca_dev, 0x96, k2); } static void setcontrast(struct gspca_dev *gspca_dev) @@ -1152,7 +978,7 @@ static void setcolors(struct gspca_dev *gspca_dev) data = (colour + 32) & 0x7f; /* blue */ else data = (-colour + 32) & 0x7f; /* red */ - reg_w(gspca_dev, 0x05, &data, 1); + reg_w1(gspca_dev, 0x05, data); } /* -- start the camera -- */ @@ -1165,7 +991,6 @@ static void sd_start(struct gspca_dev *gspca_dev) __u8 reg17; const __u8 *sn9c1xx; int mode; - static const __u8 DC29[] = { 0x6a, 0x50, 0x00, 0x00, 0x50, 0x3c }; static const __u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f }; static const __u8 CA[] = { 0x28, 0xd8, 0x14, 0xec }; static const __u8 CA_sn9c120[] = @@ -1179,21 +1004,20 @@ static void sd_start(struct gspca_dev *gspca_dev) /*fixme:jfm this sequence should appear at end of sd_start */ /* with - data = 0x44; - reg_w(gspca_dev, 0x01, &data, 1); */ - reg_w(gspca_dev, 0x15, &sn9c1xx[0x15], 1); - reg_w(gspca_dev, 0x16, &sn9c1xx[0x16], 1); - reg_w(gspca_dev, 0x12, &sn9c1xx[0x12], 1); - reg_w(gspca_dev, 0x13, &sn9c1xx[0x13], 1); - reg_w(gspca_dev, 0x18, &sn9c1xx[0x18], 1); - reg_w(gspca_dev, 0xd2, &DC29[0], 1); - reg_w(gspca_dev, 0xd3, &DC29[1], 1); - reg_w(gspca_dev, 0xc6, &DC29[2], 1); - reg_w(gspca_dev, 0xc7, &DC29[3], 1); - reg_w(gspca_dev, 0xc8, &DC29[4], 1); - reg_w(gspca_dev, 0xc9, &DC29[5], 1); + reg_w1(gspca_dev, 0x01, 0x44); */ + reg_w1(gspca_dev, 0x15, sn9c1xx[0x15]); + reg_w1(gspca_dev, 0x16, sn9c1xx[0x16]); + reg_w1(gspca_dev, 0x12, sn9c1xx[0x12]); + reg_w1(gspca_dev, 0x13, sn9c1xx[0x13]); + reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]); + reg_w1(gspca_dev, 0xd2, 0x6a); /* DC29 */ + reg_w1(gspca_dev, 0xd3, 0x50); + reg_w1(gspca_dev, 0xc6, 0x00); + reg_w1(gspca_dev, 0xc7, 0x00); + reg_w1(gspca_dev, 0xc8, 0x50); + reg_w1(gspca_dev, 0xc9, 0x3c); /*fixme:jfm end of ending sequence */ - reg_w(gspca_dev, 0x18, &sn9c1xx[0x18], 1); + reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]); switch (sd->bridge) { case BRIDGE_SN9C325: data = 0xae; @@ -1205,11 +1029,11 @@ static void sd_start(struct gspca_dev *gspca_dev) data = 0x60; break; } - reg_w(gspca_dev, 0x17, &data, 1); - reg_w(gspca_dev, 0x05, &sn9c1xx[5], 1); - reg_w(gspca_dev, 0x07, &sn9c1xx[7], 1); - reg_w(gspca_dev, 0x06, &sn9c1xx[6], 1); - reg_w(gspca_dev, 0x14, &sn9c1xx[0x14], 1); + reg_w1(gspca_dev, 0x17, data); + reg_w1(gspca_dev, 0x05, sn9c1xx[5]); + reg_w1(gspca_dev, 0x07, sn9c1xx[7]); + reg_w1(gspca_dev, 0x06, sn9c1xx[6]); + reg_w1(gspca_dev, 0x14, sn9c1xx[0x14]); switch (sd->bridge) { case BRIDGE_SN9C325: reg_w(gspca_dev, 0x20, regsn20_sn9c325, @@ -1217,10 +1041,8 @@ static void sd_start(struct gspca_dev *gspca_dev) for (i = 0; i < 8; i++) reg_w(gspca_dev, 0x84, reg84_sn9c325, sizeof reg84_sn9c325); - data = 0x0a; - reg_w(gspca_dev, 0x9a, &data, 1); - data = 0x60; - reg_w(gspca_dev, 0x99, &data, 1); + reg_w1(gspca_dev, 0x9a, 0x0a); + reg_w1(gspca_dev, 0x99, 0x60); break; case BRIDGE_SN9C120: reg_w(gspca_dev, 0x20, regsn20_sn9c120, @@ -1233,39 +1055,30 @@ static void sd_start(struct gspca_dev *gspca_dev) sizeof reg84_sn9c120_2); reg_w(gspca_dev, 0x84, reg84_sn9c120_3, sizeof reg84_sn9c120_3); - data = 0x05; - reg_w(gspca_dev, 0x9a, &data, 1); - data = 0x5b; - reg_w(gspca_dev, 0x99, &data, 1); + reg_w1(gspca_dev, 0x9a, 0x05); + reg_w1(gspca_dev, 0x99, 0x5b); break; default: reg_w(gspca_dev, 0x20, regsn20, sizeof regsn20); for (i = 0; i < 8; i++) reg_w(gspca_dev, 0x84, reg84, sizeof reg84); - data = 0x08; - reg_w(gspca_dev, 0x9a, &data, 1); - data = 0x59; - reg_w(gspca_dev, 0x99, &data, 1); + reg_w1(gspca_dev, 0x9a, 0x08); + reg_w1(gspca_dev, 0x99, 0x59); break; } mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; - reg1 = 0x02; + if (mode) + reg1 = 0x46; /* 320 clk 48Mhz */ + else + reg1 = 0x06; /* 640 clk 24Mz */ reg17 = 0x61; switch (sd->sensor) { case SENSOR_HV7131R: hv7131R_InitSensor(gspca_dev); - if (mode) - reg1 = 0x46; /* 320 clk 48Mhz */ - else - reg1 = 0x06; /* 640 clk 24Mz */ break; case SENSOR_MI0360: mi0360_InitSensor(gspca_dev); - if (mode) - reg1 = 0x46; /* 320 clk 48Mhz */ - else - reg1 = 0x06; /* 640 clk 24Mz */ break; case SENSOR_MO4000: mo4000_InitSensor(gspca_dev); @@ -1274,13 +1087,13 @@ static void sd_start(struct gspca_dev *gspca_dev) reg1 = 0x06; /* clk 24Mz */ } else { reg17 = 0x22; /* 640 MCKSIZE */ - reg1 = 0x06; /* 640 clk 24Mz */ +/* reg1 = 0x06; * 640 clk 24Mz (done) */ } break; case SENSOR_OV7648: + ov7648_InitSensor(gspca_dev); reg17 = 0xa2; reg1 = 0x44; - ov7648_InitSensor(gspca_dev); /* if (mode) ; * 320x2... else @@ -1292,7 +1105,7 @@ static void sd_start(struct gspca_dev *gspca_dev) if (mode) { /* reg17 = 0x21; * 320 */ /* reg1 = 0x44; */ - reg1 = 0x46; +/* reg1 = 0x46; (done) */ } else { reg17 = 0xa2; /* 640 */ reg1 = 0x40; @@ -1321,16 +1134,16 @@ static void sd_start(struct gspca_dev *gspca_dev) /* here change size mode 0 -> VGA; 1 -> CIF */ data = 0x40 | sn9c1xx[0x18] | (mode << 4); - reg_w(gspca_dev, 0x18, &data, 1); + reg_w1(gspca_dev, 0x18, data); reg_w(gspca_dev, 0x100, qtable4, 0x40); reg_w(gspca_dev, 0x140, qtable4 + 0x40, 0x40); data = sn9c1xx[0x18] | (mode << 4); - reg_w(gspca_dev, 0x18, &data, 1); + reg_w1(gspca_dev, 0x18, data); - reg_w(gspca_dev, 0x17, ®17, 1); - reg_w(gspca_dev, 0x01, ®1, 1); + reg_w1(gspca_dev, 0x17, reg17); + reg_w1(gspca_dev, 0x01, reg1); setbrightness(gspca_dev); setcontrast(gspca_dev); } @@ -1342,7 +1155,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) { 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10 }; static const __u8 stopmi0360[] = { 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 }; - __u8 regF1; __u8 data; const __u8 *sn9c1xx; @@ -1366,12 +1178,11 @@ static void sd_stopN(struct gspca_dev *gspca_dev) break; } sn9c1xx = sn_tb[(int) sd->sensor]; - reg_w(gspca_dev, 0x01, &sn9c1xx[1], 1); - reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 1); - reg_w(gspca_dev, 0x01, &sn9c1xx[1], 1); - reg_w(gspca_dev, 0x01, &data, 1); - regF1 = 0x01; - reg_w(gspca_dev, 0xf1, ®F1, 1); + reg_w1(gspca_dev, 0x01, sn9c1xx[1]); + reg_w1(gspca_dev, 0x17, sn9c1xx[0x17]); + reg_w1(gspca_dev, 0x01, sn9c1xx[1]); + reg_w1(gspca_dev, 0x01, data); + reg_w1(gspca_dev, 0xf1, 0x01); } static void sd_stop0(struct gspca_dev *gspca_dev) @@ -1610,30 +1421,53 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define DVNM(name) .driver_info = (kernel_ulong_t) name +#define BSI(bridge, sensor, i2c_addr) \ + .driver_info = (BRIDGE_ ## bridge << 16) \ + | (SENSOR_ ## sensor << 8) \ + | (i2c_addr) static const __devinitdata struct usb_device_id device_table[] = { #ifndef CONFIG_USB_SN9C102 - {USB_DEVICE(0x0458, 0x7025), DVNM("Genius Eye 311Q")}, - {USB_DEVICE(0x045e, 0x00f5), DVNM("MicroSoft VX3000")}, - {USB_DEVICE(0x045e, 0x00f7), DVNM("MicroSoft VX1000")}, - {USB_DEVICE(0x0471, 0x0327), DVNM("Philips SPC 600 NC")}, - {USB_DEVICE(0x0471, 0x0328), DVNM("Philips SPC 700 NC")}, + {USB_DEVICE(0x0458, 0x7025), BSI(SN9C120, MI0360, 0x5d)}, + {USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)}, + {USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)}, + {USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)}, + {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)}, #endif - {USB_DEVICE(0x0471, 0x0330), DVNM("Philips SPC 710NC")}, - {USB_DEVICE(0x0c45, 0x6040), DVNM("Speed NVC 350K")}, - {USB_DEVICE(0x0c45, 0x607c), DVNM("Sonix sn9c102p Hv7131R")}, - {USB_DEVICE(0x0c45, 0x60c0), DVNM("Sangha Sn535")}, - {USB_DEVICE(0x0c45, 0x60ec), DVNM("SN9C105+MO4000")}, - {USB_DEVICE(0x0c45, 0x60fb), DVNM("Surfer NoName")}, - {USB_DEVICE(0x0c45, 0x60fc), DVNM("LG-LIC300")}, - {USB_DEVICE(0x0c45, 0x612a), DVNM("Avant Camera")}, - {USB_DEVICE(0x0c45, 0x612c), DVNM("Typhoon Rasy Cam 1.3MPix")}, + {USB_DEVICE(0x0471, 0x0330), BSI(SN9C105, MI0360, 0x5d)}, + {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, HV7131R, 0x11)}, +/* bw600.inf: + {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, MI0360, 0x5d)}, */ +/* {USB_DEVICE(0x0c45, 0x603a), BSI(SN9C102P, OV7648, 0x??)}, */ +/* {USB_DEVICE(0x0c45, 0x607a), BSI(SN9C102P, OV7648, 0x??)}, */ + {USB_DEVICE(0x0c45, 0x607c), BSI(SN9C102P, HV7131R, 0x11)}, +/* {USB_DEVICE(0x0c45, 0x607e), BSI(SN9C102P, OV7630, 0x??)}, */ + {USB_DEVICE(0x0c45, 0x60c0), BSI(SN9C105, MI0360, 0x5d)}, +/* {USB_DEVICE(0x0c45, 0x60c8), BSI(SN9C105, OM6801, 0x??)}, */ +/* {USB_DEVICE(0x0c45, 0x60cc), BSI(SN9C105, HV7131GP, 0x??)}, */ + {USB_DEVICE(0x0c45, 0x60ec), BSI(SN9C105, MO4000, 0x21)}, +/* {USB_DEVICE(0x0c45, 0x60ef), BSI(SN9C105, ICM105C, 0x??)}, */ +/* {USB_DEVICE(0x0c45, 0x60fa), BSI(SN9C105, OV7648, 0x??)}, */ + {USB_DEVICE(0x0c45, 0x60fb), BSI(SN9C105, OV7660, 0x21)}, + {USB_DEVICE(0x0c45, 0x60fc), BSI(SN9C105, HV7131R, 0x11)}, +/* {USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x??)}, */ +/* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6801, 0x??)}, */ +/* {USB_DEVICE(0x0c45, 0x6122), BSI(SN9C110, ICM105C, 0x??)}, */ +/* {USB_DEVICE(0x0c45, 0x6123), BSI(SN9C110, SanyoCCD, 0x??)}, */ + {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C325, OV7648, 0x21)}, +/* bw600.inf: + {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C110, OV7648, 0x21)}, */ + {USB_DEVICE(0x0c45, 0x612c), BSI(SN9C110, MO4000, 0x21)}, +/* {USB_DEVICE(0x0c45, 0x612e), BSI(SN9C110, OV7630, 0x??)}, */ +/* {USB_DEVICE(0x0c45, 0x612f), BSI(SN9C110, ICM105C, 0x??)}, */ #ifndef CONFIG_USB_SN9C102 - {USB_DEVICE(0x0c45, 0x6130), DVNM("Sonix Pccam")}, - {USB_DEVICE(0x0c45, 0x6138), DVNM("Sn9c120 Mo4000")}, - {USB_DEVICE(0x0c45, 0x613b), DVNM("Surfer SN-206")}, - {USB_DEVICE(0x0c45, 0x613c), DVNM("Sonix Pccam168")}, + {USB_DEVICE(0x0c45, 0x6130), BSI(SN9C120, MI0360, 0x5d)}, + {USB_DEVICE(0x0c45, 0x6138), BSI(SN9C120, MO4000, 0x21)}, +/* {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x??)}, */ + {USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)}, + {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)}, +/* {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x??)}, */ #endif + {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, MI0360, 0x5d)}, {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -1658,7 +1492,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - info("v%s registered", version); + info("registered"); return 0; } static void __exit sd_mod_exit(void) diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c index 156206118795..17fe2c2a440d 100644 --- a/drivers/media/video/gspca/spca500.c +++ b/drivers/media/video/gspca/spca500.c @@ -24,9 +24,6 @@ #include "gspca.h" #include "jpeg.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) -static const char version[] = "2.1.7"; - MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); MODULE_DESCRIPTION("GSPCA/SPCA500 USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -630,109 +627,10 @@ static int sd_config(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; - __u16 vendor; - __u16 product; - - vendor = id->idVendor; - product = id->idProduct; - switch (vendor) { - case 0x040a: /* Kodak cameras */ -/* switch (product) { */ -/* case 0x0300: */ - sd->subtype = KodakEZ200; -/* break; */ -/* } */ - break; - case 0x041e: /* Creative cameras */ -/* switch (product) { */ -/* case 0x400a: */ - sd->subtype = CreativePCCam300; -/* break; */ -/* } */ - break; - case 0x046d: /* Logitech Labtec */ - switch (product) { - case 0x0890: - sd->subtype = LogitechTraveler; - break; - case 0x0900: - sd->subtype = LogitechClickSmart310; - break; - case 0x0901: - sd->subtype = LogitechClickSmart510; - break; - } - break; - case 0x04a5: /* Benq */ -/* switch (product) { */ -/* case 0x300c: */ - sd->subtype = BenqDC1016; -/* break; */ -/* } */ - break; - case 0x04fc: /* SunPlus */ -/* switch (product) { */ -/* case 0x7333: */ - sd->subtype = PalmPixDC85; -/* break; */ -/* } */ - break; - case 0x055f: /* Mustek cameras */ - switch (product) { - case 0xc200: - sd->subtype = MustekGsmart300; - break; - case 0xc220: - sd->subtype = Gsmartmini; - break; - } - break; - case 0x06bd: /* Agfa Cl20 */ -/* switch (product) { */ -/* case 0x0404: */ - sd->subtype = AgfaCl20; -/* break; */ -/* } */ - break; - case 0x06be: /* Optimedia */ -/* switch (product) { */ -/* case 0x0800: */ - sd->subtype = Optimedia; -/* break; */ -/* } */ - break; - case 0x084d: /* D-Link / Minton */ -/* switch (product) { */ -/* case 0x0003: * DSC-350 / S-Cam F5 */ - sd->subtype = DLinkDSC350; -/* break; */ -/* } */ - break; - case 0x08ca: /* Aiptek */ -/* switch (product) { */ -/* case 0x0103: */ - sd->subtype = AiptekPocketDV; -/* break; */ -/* } */ - break; - case 0x2899: /* ToptroIndustrial */ -/* switch (product) { */ -/* case 0x012c: */ - sd->subtype = ToptroIndus; -/* break; */ -/* } */ - break; - case 0x8086: /* Intel */ -/* switch (product) { */ -/* case 0x0630: * Pocket PC Camera */ - sd->subtype = IntelPocketPCCamera; -/* break; */ -/* } */ - break; - } + cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 0x01; + sd->subtype = id->driver_info; if (sd->subtype != LogitechClickSmart310) { cam->cam_mode = vga_mode; cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; @@ -1162,23 +1060,22 @@ static struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define DVNM(name) .driver_info = (kernel_ulong_t) name static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x040a, 0x0300), DVNM("Kodak EZ200")}, - {USB_DEVICE(0x041e, 0x400a), DVNM("Creative PC-CAM 300")}, - {USB_DEVICE(0x046d, 0x0890), DVNM("Logitech QuickCam traveler")}, - {USB_DEVICE(0x046d, 0x0900), DVNM("Logitech Inc. ClickSmart 310")}, - {USB_DEVICE(0x046d, 0x0901), DVNM("Logitech Inc. ClickSmart 510")}, - {USB_DEVICE(0x04a5, 0x300c), DVNM("Benq DC1016")}, - {USB_DEVICE(0x04fc, 0x7333), DVNM("PalmPixDC85")}, - {USB_DEVICE(0x055f, 0xc200), DVNM("Mustek Gsmart 300")}, - {USB_DEVICE(0x055f, 0xc220), DVNM("Gsmart Mini")}, - {USB_DEVICE(0x06bd, 0x0404), DVNM("Agfa CL20")}, - {USB_DEVICE(0x06be, 0x0800), DVNM("Optimedia")}, - {USB_DEVICE(0x084d, 0x0003), DVNM("D-Link DSC-350")}, - {USB_DEVICE(0x08ca, 0x0103), DVNM("Aiptek PocketDV")}, - {USB_DEVICE(0x2899, 0x012c), DVNM("Toptro Industrial")}, - {USB_DEVICE(0x8086, 0x0630), DVNM("Intel Pocket PC Camera")}, + {USB_DEVICE(0x040a, 0x0300), .driver_info = KodakEZ200}, + {USB_DEVICE(0x041e, 0x400a), .driver_info = CreativePCCam300}, + {USB_DEVICE(0x046d, 0x0890), .driver_info = LogitechTraveler}, + {USB_DEVICE(0x046d, 0x0900), .driver_info = LogitechClickSmart310}, + {USB_DEVICE(0x046d, 0x0901), .driver_info = LogitechClickSmart510}, + {USB_DEVICE(0x04a5, 0x300c), .driver_info = BenqDC1016}, + {USB_DEVICE(0x04fc, 0x7333), .driver_info = PalmPixDC85}, + {USB_DEVICE(0x055f, 0xc200), .driver_info = MustekGsmart300}, + {USB_DEVICE(0x055f, 0xc220), .driver_info = Gsmartmini}, + {USB_DEVICE(0x06bd, 0x0404), .driver_info = AgfaCl20}, + {USB_DEVICE(0x06be, 0x0800), .driver_info = Optimedia}, + {USB_DEVICE(0x084d, 0x0003), .driver_info = DLinkDSC350}, + {USB_DEVICE(0x08ca, 0x0103), .driver_info = AiptekPocketDV}, + {USB_DEVICE(0x2899, 0x012c), .driver_info = ToptroIndus}, + {USB_DEVICE(0x8086, 0x0630), .driver_info = IntelPocketPCCamera}, {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -1203,7 +1100,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - PDEBUG(D_PROBE, "v%s registered", version); + PDEBUG(D_PROBE, "registered"); return 0; } static void __exit sd_mod_exit(void) diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c index 50e929de0203..51a3c3429ef0 100644 --- a/drivers/media/video/gspca/spca501.c +++ b/drivers/media/video/gspca/spca501.c @@ -23,9 +23,6 @@ #include "gspca.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) -static const char version[] = "2.1.7"; - MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); MODULE_DESCRIPTION("GSPCA/SPCA501 USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -1923,63 +1920,12 @@ static int sd_config(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; - __u16 vendor; - __u16 product; - - vendor = id->idVendor; - product = id->idProduct; - switch (vendor) { - case 0x0000: /* Unknow Camera */ -/* switch (product) { */ -/* case 0x0000: */ - sd->subtype = MystFromOriUnknownCamera; -/* break; */ -/* } */ - break; - case 0x040a: /* Kodak cameras */ -/* switch (product) { */ -/* case 0x0002: */ - sd->subtype = KodakDVC325; -/* break; */ -/* } */ - break; - case 0x0497: /* Smile International */ -/* switch (product) { */ -/* case 0xc001: */ - sd->subtype = SmileIntlCamera; -/* break; */ -/* } */ - break; - case 0x0506: /* 3COM cameras */ -/* switch (product) { */ -/* case 0x00df: */ - sd->subtype = ThreeComHomeConnectLite; -/* break; */ -/* } */ - break; - case 0x0733: /* Rebadged ViewQuest (Intel) and ViewQuest cameras */ - switch (product) { - case 0x0401: - sd->subtype = IntelCreateAndShare; - break; - case 0x0402: - sd->subtype = ViewQuestM318B; - break; - } - break; - case 0x1776: /* Arowana */ -/* switch (product) { */ -/* case 0x501c: */ - sd->subtype = Arowana300KCMOSCamera; -/* break; */ -/* } */ - break; - } + cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 0x01; cam->cam_mode = vga_mode; cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + sd->subtype = id->driver_info; sd->brightness = sd_ctrls[MY_BRIGHTNESS].qctrl.default_value; sd->contrast = sd_ctrls[MY_CONTRAST].qctrl.default_value; sd->colors = sd_ctrls[MY_COLOR].qctrl.default_value; @@ -2183,15 +2129,14 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define DVNM(name) .driver_info = (kernel_ulong_t) name static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x040a, 0x0002), DVNM("Kodak DVC-325")}, - {USB_DEVICE(0x0497, 0xc001), DVNM("Smile International")}, - {USB_DEVICE(0x0506, 0x00df), DVNM("3Com HomeConnect Lite")}, - {USB_DEVICE(0x0733, 0x0401), DVNM("Intel Create and Share")}, - {USB_DEVICE(0x0733, 0x0402), DVNM("ViewQuest M318B")}, - {USB_DEVICE(0x1776, 0x501c), DVNM("Arowana 300K CMOS Camera")}, - {USB_DEVICE(0x0000, 0x0000), DVNM("MystFromOri Unknow Camera")}, + {USB_DEVICE(0x040a, 0x0002), .driver_info = KodakDVC325}, + {USB_DEVICE(0x0497, 0xc001), .driver_info = SmileIntlCamera}, + {USB_DEVICE(0x0506, 0x00df), .driver_info = ThreeComHomeConnectLite}, + {USB_DEVICE(0x0733, 0x0401), .driver_info = IntelCreateAndShare}, + {USB_DEVICE(0x0733, 0x0402), .driver_info = ViewQuestM318B}, + {USB_DEVICE(0x1776, 0x501c), .driver_info = Arowana300KCMOSCamera}, + {USB_DEVICE(0x0000, 0x0000), .driver_info = MystFromOriUnknownCamera}, {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -2216,7 +2161,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - PDEBUG(D_PROBE, "v%s registered", version); + PDEBUG(D_PROBE, "registered"); return 0; } static void __exit sd_mod_exit(void) diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c index ddea6e140aa8..3c2be80cbd65 100644 --- a/drivers/media/video/gspca/spca505.c +++ b/drivers/media/video/gspca/spca505.c @@ -23,9 +23,6 @@ #include "gspca.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) -static const char version[] = "2.1.7"; - MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); MODULE_DESCRIPTION("GSPCA/SPCA505 USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -34,10 +31,6 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - int buflen; - unsigned char tmpbuf[640 * 480 * 3 / 2]; /* YYUV per line */ - unsigned char tmpbuf2[640 * 480 * 2]; /* YUYV */ - unsigned char brightness; char subtype; @@ -67,29 +60,29 @@ static struct ctrl sd_ctrls[] = { }; static struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 160 * 2, - .sizeimage = 160 * 120 * 2, + {160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 160 * 3, + .sizeimage = 160 * 120 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 5}, - {176, 144, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 176 * 2, - .sizeimage = 176 * 144 * 2, + {176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 176 * 3, + .sizeimage = 176 * 144 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 4}, - {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 320 * 2, - .sizeimage = 320 * 240 * 2, + {320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 320 * 3, + .sizeimage = 320 * 240 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 2}, - {352, 288, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 352 * 2, - .sizeimage = 352 * 288 * 2, + {352, 288, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 352 * 3, + .sizeimage = 352 * 288 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1}, - {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 640 * 2, - .sizeimage = 640 * 480 * 2, + {640, 480, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 640 * 3, + .sizeimage = 640 * 480 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, }; @@ -641,33 +634,11 @@ static int sd_config(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; - __u16 vendor; - __u16 product; - - vendor = id->idVendor; - product = id->idProduct; - switch (vendor) { - case 0x041e: /* Creative cameras */ -/* switch (product) { */ -/* case 0x401d: * here505b */ - sd->subtype = Nxultra; -/* break; */ -/* } */ - break; - case 0x0733: /* Rebadged ViewQuest (Intel) and ViewQuest cameras */ -/* switch (product) { */ -/* case 0x0430: */ -/* fixme: may be UsbGrabberPV321 BRIDGE_SPCA506 SENSOR_SAA7113 */ - sd->subtype = IntelPCCameraPro; -/* break; */ -/* } */ - break; - } cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 0x01; cam->cam_mode = vga_mode; + sd->subtype = id->driver_info; if (sd->subtype != IntelPCCameraPro) cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; else /* no 640x480 for IntelPCCameraPro */ @@ -785,77 +756,30 @@ static void sd_close(struct gspca_dev *gspca_dev) reg_write(gspca_dev->dev, 0x05, 0x11, 0xf); } -/* convert YYUV per line to YUYV (YUV 4:2:2) */ -static void yyuv_decode(unsigned char *out, - unsigned char *in, - int width, - int height) -{ - unsigned char *Ui, *Vi, *yi, *yi1; - unsigned char *out1; - int i, j; - - yi = in; - for (i = height / 2; --i >= 0; ) { - out1 = out + width * 2; /* next line */ - yi1 = yi + width; - Ui = yi1 + width; - Vi = Ui + width / 2; - for (j = width / 2; --j >= 0; ) { - *out++ = 128 + *yi++; - *out++ = 128 + *Ui; - *out++ = 128 + *yi++; - *out++ = 128 + *Vi; - - *out1++ = 128 + *yi1++; - *out1++ = 128 + *Ui++; - *out1++ = 128 + *yi1++; - *out1++ = 128 + *Vi++; - } - yi += width * 2; - out = out1; - } -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ int len) /* iso packet length */ { - struct sd *sd = (struct sd *) gspca_dev; - switch (data[0]) { case 0: /* start of frame */ - if (gspca_dev->last_packet_type == FIRST_PACKET) { - yyuv_decode(sd->tmpbuf2, sd->tmpbuf, - gspca_dev->width, - gspca_dev->height); - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, - sd->tmpbuf2, - gspca_dev->width - * gspca_dev->height - * 2); - } - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, 0); + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); data += SPCA50X_OFFSET_DATA; len -= SPCA50X_OFFSET_DATA; - if (len > 0) - memcpy(sd->tmpbuf, data, len); - else - len = 0; - sd->buflen = len; - return; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + break; case 0xff: /* drop */ /* gspca_dev->last_packet_type = DISCARD_PACKET; */ - return; + break; + default: + data += 1; + len -= 1; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + break; } - data += 1; - len -= 1; - memcpy(&sd->tmpbuf[sd->buflen], data, len); - sd->buflen += len; } static void setbrightness(struct gspca_dev *gspca_dev) @@ -910,10 +834,10 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define DVNM(name) .driver_info = (kernel_ulong_t) name static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x041e, 0x401d), DVNM("Creative Webcam NX ULTRA")}, - {USB_DEVICE(0x0733, 0x0430), DVNM("Intel PC Camera Pro")}, + {USB_DEVICE(0x041e, 0x401d), .driver_info = Nxultra}, + {USB_DEVICE(0x0733, 0x0430), .driver_info = IntelPCCameraPro}, +/*fixme: may be UsbGrabberPV321 BRIDGE_SPCA506 SENSOR_SAA7113 */ {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -938,7 +862,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - PDEBUG(D_PROBE, "v%s registered", version); + PDEBUG(D_PROBE, "registered"); return 0; } static void __exit sd_mod_exit(void) diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c index 143203c1fd9f..6fe715c80ad2 100644 --- a/drivers/media/video/gspca/spca506.c +++ b/drivers/media/video/gspca/spca506.c @@ -25,9 +25,6 @@ #include "gspca.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) -static const char version[] = "2.1.7"; - MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); MODULE_DESCRIPTION("GSPCA/SPCA506 USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -36,10 +33,6 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - int buflen; - __u8 tmpbuf[640 * 480 * 3]; /* YYUV per line */ - __u8 tmpbuf2[640 * 480 * 2]; /* YUYV */ - unsigned char brightness; unsigned char contrast; unsigned char colors; @@ -118,29 +111,29 @@ static struct ctrl sd_ctrls[] = { }; static struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 160 * 2, - .sizeimage = 160 * 120 * 2, + {160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 160 * 3, + .sizeimage = 160 * 120 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 5}, - {176, 144, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 176 * 2, - .sizeimage = 176 * 144 * 2, + {176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 176 * 3, + .sizeimage = 176 * 144 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 4}, - {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 320 * 2, - .sizeimage = 320 * 240 * 2, + {320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 320 * 3, + .sizeimage = 320 * 240 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 2}, - {352, 288, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 352 * 2, - .sizeimage = 352 * 288 * 2, + {352, 288, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 352 * 3, + .sizeimage = 352 * 288 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1}, - {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 640 * 2, - .sizeimage = 640 * 480 * 2, + {640, 480, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 640 * 3, + .sizeimage = 640 * 480 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, }; @@ -310,7 +303,6 @@ static int sd_config(struct gspca_dev *gspca_dev, struct cam *cam; cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 0x01; cam->cam_mode = vga_mode; cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; @@ -576,77 +568,30 @@ static void sd_close(struct gspca_dev *gspca_dev) { } -/* convert YYUV per line to YUYV (YUV 4:2:2) */ -static void yyuv_decode(unsigned char *out, - unsigned char *in, - int width, - int height) -{ - unsigned char *Ui, *Vi, *yi, *yi1; - unsigned char *out1; - int i, j; - - yi = in; - for (i = height / 2; --i >= 0; ) { - out1 = out + width * 2; /* next line */ - yi1 = yi + width; - Ui = yi1 + width; - Vi = Ui + width / 2; - for (j = width / 2; --j >= 0; ) { - *out++ = 128 + *yi++; - *out++ = 128 + *Ui; - *out++ = 128 + *yi++; - *out++ = 128 + *Vi; - - *out1++ = 128 + *yi1++; - *out1++ = 128 + *Ui++; - *out1++ = 128 + *yi1++; - *out1++ = 128 + *Vi++; - } - yi += width * 2; - out = out1; - } -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ int len) /* iso packet length */ { - struct sd *sd = (struct sd *) gspca_dev; - switch (data[0]) { case 0: /* start of frame */ - if (gspca_dev->last_packet_type == FIRST_PACKET) { - yyuv_decode(sd->tmpbuf2, sd->tmpbuf, - gspca_dev->width, - gspca_dev->height); - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, - sd->tmpbuf2, - gspca_dev->width - * gspca_dev->height - * 2); - } - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, 0); + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); data += SPCA50X_OFFSET_DATA; len -= SPCA50X_OFFSET_DATA; - if (len > 0) - memcpy(sd->tmpbuf, data, len); - else - len = 0; - sd->buflen = len; - return; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + break; case 0xff: /* drop */ /* gspca_dev->last_packet_type = DISCARD_PACKET; */ - return; + break; + default: + data += 1; + len -= 1; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + break; } - data += 1; - len -= 1; - memcpy(&sd->tmpbuf[sd->buflen], data, len); - sd->buflen += len; } static void setbrightness(struct gspca_dev *gspca_dev) @@ -804,12 +749,12 @@ static struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define DVNM(name) .driver_info = (kernel_ulong_t) name static __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x06e1, 0xa190), DVNM("ADS Instant VCD")}, -/* {USB_DEVICE(0x0733, 0x0430), DVNM("UsbGrabber PV321c")}, */ - {USB_DEVICE(0x0734, 0x043b), DVNM("3DeMon USB Capture aka")}, - {USB_DEVICE(0x99fa, 0x8988), DVNM("Grandtec V.cap")}, + {USB_DEVICE(0x06e1, 0xa190)}, +/*fixme: may be IntelPCCameraPro BRIDGE_SPCA505 + {USB_DEVICE(0x0733, 0x0430)}, */ + {USB_DEVICE(0x0734, 0x043b)}, + {USB_DEVICE(0x99fa, 0x8988)}, {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -834,7 +779,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - PDEBUG(D_PROBE, "v%s registered", version); + PDEBUG(D_PROBE, "registered"); return 0; } static void __exit sd_mod_exit(void) diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c index d8cd93866a4a..b608a27ad115 100644 --- a/drivers/media/video/gspca/spca508.c +++ b/drivers/media/video/gspca/spca508.c @@ -22,9 +22,6 @@ #include "gspca.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) -static const char version[] = "2.1.7"; - MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); MODULE_DESCRIPTION("GSPCA/SPCA508 USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -33,10 +30,6 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - int buflen; - unsigned char tmpbuf[352 * 288 * 3 / 2]; /* YUVY per line */ - unsigned char tmpbuf2[352 * 288 * 2]; /* YUYV */ - unsigned char brightness; char subtype; @@ -71,23 +64,23 @@ static struct ctrl sd_ctrls[] = { static struct v4l2_pix_format sif_mode[] = { {160, 120, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 160 * 2, - .sizeimage = 160 * 120 * 2, + .bytesperline = 160 * 3, + .sizeimage = 160 * 120 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 3}, {176, 144, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 176 * 2, - .sizeimage = 176 * 144 * 2, + .bytesperline = 176 * 3, + .sizeimage = 176 * 144 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 2}, {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 320 * 2, - .sizeimage = 320 * 240 * 2, + .bytesperline = 320 * 3, + .sizeimage = 320 * 240 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1}, {352, 288, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 352 * 2, - .sizeimage = 352 * 288 * 2, + .bytesperline = 352 * 3, + .sizeimage = 352 * 288 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, }; @@ -1476,58 +1469,8 @@ static int sd_config(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; - __u16 product; int data1, data2; - product = id->idProduct; - switch (id->idVendor) { - case 0x0130: /* Clone webcam */ -/* switch (product) { */ -/* case 0x0130: */ - sd->subtype = HamaUSBSightcam; /* same as Hama 0010 */ -/* break; */ -/* } */ - break; - case 0x041e: /* Creative cameras */ -/* switch (product) { */ -/* case 0x4018: */ - sd->subtype = CreativeVista; -/* break; */ -/* } */ - break; - case 0x0461: /* MicroInnovation */ -/* switch (product) { */ -/* case 0x0815: */ - sd->subtype = MicroInnovationIC200; -/* break; */ -/* } */ - break; - case 0x0733: /* Rebadged ViewQuest (Intel) and ViewQuest cameras */ -/* switch (product) { */ -/* case 0x110: */ - sd->subtype = ViewQuestVQ110; -/* break; */ -/* } */ - break; - case 0x0af9: /* Hama cameras */ - switch (product) { - case 0x0010: - sd->subtype = HamaUSBSightcam; - break; - case 0x0011: - sd->subtype = HamaUSBSightcam2; - break; - } - break; - case 0x8086: /* Intel */ -/* switch (product) { */ -/* case 0x0110: */ - sd->subtype = IntelEasyPCCamera; -/* break; */ -/* } */ - break; - } - /* Read from global register the USB product and vendor IDs, just to * prove that we can communicate with the device. This works, which * confirms at we are communicating properly and that the device @@ -1544,10 +1487,11 @@ static int sd_config(struct gspca_dev *gspca_dev, PDEBUG(D_PROBE, "Window 1 average luminance: %d", data1); cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 0x01; cam->cam_mode = sif_mode; cam->nmodes = ARRAY_SIZE(sif_mode); + + sd->subtype = id->driver_info; sd->brightness = BRIGHTNESS_DEF; switch (sd->subtype) { @@ -1619,77 +1563,30 @@ static void sd_close(struct gspca_dev *gspca_dev) { } -/* convert YUVY per line to YUYV (YUV 4:2:2) */ -static void yuvy_decode(unsigned char *out, - unsigned char *in, - int width, - int height) -{ - unsigned char *Ui, *Vi, *yi, *yi1; - unsigned char *out1; - int i, j; - - yi = in; - for (i = height / 2; --i >= 0; ) { - out1 = out + width * 2; /* next line */ - Ui = yi + width; - Vi = Ui + width / 2; - yi1 = Vi + width / 2; - for (j = width / 2; --j >= 0; ) { - *out++ = 128 + *yi++; - *out++ = 128 + *Ui; - *out++ = 128 + *yi++; - *out++ = 128 + *Vi; - - *out1++ = 128 + *yi1++; - *out1++ = 128 + *Ui++; - *out1++ = 128 + *yi1++; - *out1++ = 128 + *Vi++; - } - yi += width * 2; - out = out1; - } -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ int len) /* iso packet length */ { - struct sd *sd = (struct sd *) gspca_dev; - switch (data[0]) { case 0: /* start of frame */ - if (gspca_dev->last_packet_type == FIRST_PACKET) { - yuvy_decode(sd->tmpbuf2, sd->tmpbuf, - gspca_dev->width, - gspca_dev->height); - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, - sd->tmpbuf2, - gspca_dev->width - * gspca_dev->height - * 2); - } - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, 0); + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); data += SPCA508_OFFSET_DATA; len -= SPCA508_OFFSET_DATA; - if (len > 0) - memcpy(sd->tmpbuf, data, len); - else - len = 0; - sd->buflen = len; - return; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + break; case 0xff: /* drop */ /* gspca_dev->last_packet_type = DISCARD_PACKET; */ - return; + break; + default: + data += 1; + len -= 1; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + break; } - data += 1; - len -= 1; - memcpy(&sd->tmpbuf[sd->buflen], data, len); - sd->buflen += len; } static void setbrightness(struct gspca_dev *gspca_dev) @@ -1745,15 +1642,14 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define DVNM(name) .driver_info = (kernel_ulong_t) name static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x0130, 0x0130), DVNM("Clone Digital Webcam 11043")}, - {USB_DEVICE(0x041e, 0x4018), DVNM("Creative Webcam Vista (PD1100)")}, - {USB_DEVICE(0x0461, 0x0815), DVNM("Micro Innovation IC200")}, - {USB_DEVICE(0x0733, 0x0110), DVNM("ViewQuest VQ110")}, - {USB_DEVICE(0x0af9, 0x0010), DVNM("Hama USB Sightcam 100")}, - {USB_DEVICE(0x0af9, 0x0011), DVNM("Hama USB Sightcam 100")}, - {USB_DEVICE(0x8086, 0x0110), DVNM("Intel Easy PC Camera")}, + {USB_DEVICE(0x0130, 0x0130), .driver_info = HamaUSBSightcam}, + {USB_DEVICE(0x041e, 0x4018), .driver_info = CreativeVista}, + {USB_DEVICE(0x0461, 0x0815), .driver_info = MicroInnovationIC200}, + {USB_DEVICE(0x0733, 0x0110), .driver_info = ViewQuestVQ110}, + {USB_DEVICE(0x0af9, 0x0010), .driver_info = HamaUSBSightcam}, + {USB_DEVICE(0x0af9, 0x0011), .driver_info = HamaUSBSightcam2}, + {USB_DEVICE(0x8086, 0x0110), .driver_info = IntelEasyPCCamera}, {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -1778,7 +1674,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - PDEBUG(D_PROBE, "v%s registered", version); + PDEBUG(D_PROBE, "registered"); return 0; } static void __exit sd_mod_exit(void) diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c index b659bd0f788d..a26174508cb9 100644 --- a/drivers/media/video/gspca/spca561.c +++ b/drivers/media/video/gspca/spca561.c @@ -24,9 +24,6 @@ #include "gspca.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) -static const char version[] = "2.1.7"; - MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); MODULE_DESCRIPTION("GSPCA/SPCA561 USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -582,35 +579,15 @@ static int sd_config(struct gspca_dev *gspca_dev, PDEBUG(D_PROBE, "Bad vendor / product from device"); return -EINVAL; } - switch (product) { - case 0x0928: - case 0x0929: - case 0x092a: - case 0x092b: - case 0x092c: - case 0x092d: - case 0x092e: - case 0x092f: - case 0x403b: - sd->chip_revision = Rev012A; - break; - default: -/* case 0x0561: - case 0x0815: * ?? in spca508.c - case 0x401a: - case 0x7004: - case 0x7e50: - case 0xa001: - case 0xcdee: */ - sd->chip_revision = Rev072A; - break; - } + cam = &gspca_dev->cam; cam->dev_name = (char *) id->driver_info; cam->epaddr = 0x01; gspca_dev->nbalt = 7 + 1; /* choose alternate 7 first */ cam->cam_mode = sif_mode; cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + + sd->chip_revision = id->driver_info; sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; @@ -997,23 +974,22 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define DVNM(name) .driver_info = (kernel_ulong_t) name static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x041e, 0x401a), DVNM("Creative Webcam Vista (PD1100)")}, - {USB_DEVICE(0x041e, 0x403b), DVNM("Creative Webcam Vista (VF0010)")}, - {USB_DEVICE(0x0458, 0x7004), DVNM("Genius VideoCAM Express V2")}, - {USB_DEVICE(0x046d, 0x0928), DVNM("Logitech QC Express Etch2")}, - {USB_DEVICE(0x046d, 0x0929), DVNM("Labtec Webcam Elch2")}, - {USB_DEVICE(0x046d, 0x092a), DVNM("Logitech QC for Notebook")}, - {USB_DEVICE(0x046d, 0x092b), DVNM("Labtec Webcam Plus")}, - {USB_DEVICE(0x046d, 0x092c), DVNM("Logitech QC chat Elch2")}, - {USB_DEVICE(0x046d, 0x092d), DVNM("Logitech QC Elch2")}, - {USB_DEVICE(0x046d, 0x092e), DVNM("Logitech QC Elch2")}, - {USB_DEVICE(0x046d, 0x092f), DVNM("Logitech QC Elch2")}, - {USB_DEVICE(0x04fc, 0x0561), DVNM("Flexcam 100")}, - {USB_DEVICE(0x060b, 0xa001), DVNM("Maxell Compact Pc PM3")}, - {USB_DEVICE(0x10fd, 0x7e50), DVNM("FlyCam Usb 100")}, - {USB_DEVICE(0xabcd, 0xcdee), DVNM("Petcam")}, + {USB_DEVICE(0x041e, 0x401a), .driver_info = Rev072A}, + {USB_DEVICE(0x041e, 0x403b), .driver_info = Rev012A}, + {USB_DEVICE(0x0458, 0x7004), .driver_info = Rev072A}, + {USB_DEVICE(0x046d, 0x0928), .driver_info = Rev012A}, + {USB_DEVICE(0x046d, 0x0929), .driver_info = Rev012A}, + {USB_DEVICE(0x046d, 0x092a), .driver_info = Rev012A}, + {USB_DEVICE(0x046d, 0x092b), .driver_info = Rev012A}, + {USB_DEVICE(0x046d, 0x092c), .driver_info = Rev012A}, + {USB_DEVICE(0x046d, 0x092d), .driver_info = Rev012A}, + {USB_DEVICE(0x046d, 0x092e), .driver_info = Rev012A}, + {USB_DEVICE(0x046d, 0x092f), .driver_info = Rev012A}, + {USB_DEVICE(0x04fc, 0x0561), .driver_info = Rev072A}, + {USB_DEVICE(0x060b, 0xa001), .driver_info = Rev072A}, + {USB_DEVICE(0x10fd, 0x7e50), .driver_info = Rev072A}, + {USB_DEVICE(0xabcd, 0xcdee), .driver_info = Rev072A}, {} }; @@ -1039,7 +1015,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - PDEBUG(D_PROBE, "v%s registered", version); + PDEBUG(D_PROBE, "registered"); return 0; } static void __exit sd_mod_exit(void) diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c index c78ee0d3e59b..16219cf6a6d5 100644 --- a/drivers/media/video/gspca/stk014.c +++ b/drivers/media/video/gspca/stk014.c @@ -23,9 +23,6 @@ #include "gspca.h" #include "jpeg.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) -static const char version[] = "2.1.7"; - MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("Syntek DV4000 (STK014) USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -299,7 +296,6 @@ static int sd_config(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; struct cam *cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 0x02; gspca_dev->cam.cam_mode = vga_mode; gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); @@ -549,9 +545,8 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define DVNM(name) .driver_info = (kernel_ulong_t) name static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x05e1, 0x0893), DVNM("Syntek DV4000")}, + {USB_DEVICE(0x05e1, 0x0893)}, {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -576,7 +571,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - info("v%s registered", version); + info("registered"); return 0; } static void __exit sd_mod_exit(void) diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index abd7bef9b3d1..54efa48bee01 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -24,9 +24,6 @@ #include "gspca.h" #include "jpeg.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 8) -static const char version[] = "2.1.8"; - MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); MODULE_DESCRIPTION("GSPCA/SPCA5xx USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -804,229 +801,29 @@ static int sd_config(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; struct cam *cam; - __u16 vendor; - __u16 product; - __u8 fw; - - vendor = id->idVendor; - product = id->idProduct; - switch (vendor) { - case 0x041e: /* Creative cameras */ -/* switch (product) { */ -/* case 0x400b: */ -/* case 0x4012: */ -/* case 0x4013: */ -/* sd->bridge = BRIDGE_SPCA504C; */ -/* break; */ -/* } */ - break; - case 0x0458: /* Genius KYE cameras */ -/* switch (product) { */ -/* case 0x7006: */ - sd->bridge = BRIDGE_SPCA504B; -/* break; */ -/* } */ - break; - case 0x0461: /* MicroInnovation */ -/* switch (product) { */ -/* case 0x0821: */ - sd->bridge = BRIDGE_SPCA533; -/* break; */ -/* } */ - break; - case 0x046d: /* Logitech Labtec */ - switch (product) { - case 0x0905: - sd->subtype = LogitechClickSmart820; - sd->bridge = BRIDGE_SPCA533; - break; - case 0x0960: - sd->subtype = LogitechClickSmart420; - sd->bridge = BRIDGE_SPCA504C; - break; - } - break; - case 0x0471: /* Philips */ -/* switch (product) { */ -/* case 0x0322: */ - sd->bridge = BRIDGE_SPCA504B; -/* break; */ -/* } */ - break; - case 0x04a5: /* Benq */ - switch (product) { - case 0x3003: - sd->bridge = BRIDGE_SPCA504B; - break; - case 0x3008: - case 0x300a: - sd->bridge = BRIDGE_SPCA533; - break; - } - break; - case 0x04f1: /* JVC */ -/* switch (product) { */ -/* case 0x1001: */ - sd->bridge = BRIDGE_SPCA504B; -/* break; */ -/* } */ - break; - case 0x04fc: /* SunPlus */ - switch (product) { - case 0x500c: - sd->bridge = BRIDGE_SPCA504B; - break; - case 0x504a: + + cam = &gspca_dev->cam; + cam->epaddr = 0x01; + + sd->bridge = id->driver_info >> 8; + sd->subtype = id->driver_info; + + if (sd->subtype == AiptekMiniPenCam13) { /* try to get the firmware as some cam answer 2.0.1.2.2 * and should be a spca504b then overwrite that setting */ - reg_r(dev, 0x20, 0, gspca_dev->usb_buf, 1); - fw = gspca_dev->usb_buf[0]; - if (fw == 1) { - sd->subtype = AiptekMiniPenCam13; - sd->bridge = BRIDGE_SPCA504; - } else if (fw == 2) { - sd->bridge = BRIDGE_SPCA504B; - } else - return -ENODEV; - break; - case 0x504b: - sd->bridge = BRIDGE_SPCA504B; - break; - case 0x5330: - sd->bridge = BRIDGE_SPCA533; - break; - case 0x5360: - sd->bridge = BRIDGE_SPCA536; - break; - case 0xffff: - sd->bridge = BRIDGE_SPCA504B; - break; - } - break; - case 0x052b: /* ?? Megapix */ -/* switch (product) { */ -/* case 0x1513: */ - sd->subtype = MegapixV4; - sd->bridge = BRIDGE_SPCA533; -/* break; */ -/* } */ - break; - case 0x0546: /* Polaroid */ - switch (product) { - case 0x3155: - sd->bridge = BRIDGE_SPCA533; - break; - case 0x3191: - case 0x3273: - sd->bridge = BRIDGE_SPCA504B; - break; - } - break; - case 0x055f: /* Mustek cameras */ - switch (product) { - case 0xc211: - sd->bridge = BRIDGE_SPCA536; - break; - case 0xc230: - case 0xc232: - sd->bridge = BRIDGE_SPCA533; - break; - case 0xc360: - sd->bridge = BRIDGE_SPCA536; - break; - case 0xc420: - sd->bridge = BRIDGE_SPCA504; - break; - case 0xc430: - case 0xc440: - sd->bridge = BRIDGE_SPCA533; - break; - case 0xc520: - sd->bridge = BRIDGE_SPCA504; - break; - case 0xc530: - case 0xc540: - case 0xc630: - case 0xc650: - sd->bridge = BRIDGE_SPCA533; - break; - } - break; - case 0x05da: /* Digital Dream cameras */ -/* switch (product) { */ -/* case 0x1018: */ + reg_r(dev, 0x20, 0, gspca_dev->usb_buf, 1); + switch (gspca_dev->usb_buf[0]) { + case 1: + break; /* (right bridge/subtype) */ + case 2: sd->bridge = BRIDGE_SPCA504B; -/* break; */ -/* } */ - break; - case 0x06d6: /* Trust */ -/* switch (product) { */ -/* case 0x0031: */ - sd->bridge = BRIDGE_SPCA533; /* SPCA533A */ -/* break; */ -/* } */ - break; - case 0x0733: /* Rebadged ViewQuest (Intel) and ViewQuest cameras */ - switch (product) { - case 0x1311: - case 0x1314: - case 0x2211: - case 0x2221: - sd->bridge = BRIDGE_SPCA533; - break; - case 0x3261: - case 0x3281: - sd->bridge = BRIDGE_SPCA536; - break; - } - break; - case 0x08ca: /* Aiptek */ - switch (product) { - case 0x0104: - case 0x0106: - sd->bridge = BRIDGE_SPCA533; - break; - case 0x2008: - sd->bridge = BRIDGE_SPCA504B; - break; - case 0x2010: - sd->bridge = BRIDGE_SPCA533; - break; - case 0x2016: - case 0x2018: - sd->bridge = BRIDGE_SPCA504B; - break; - case 0x2020: - case 0x2022: - sd->bridge = BRIDGE_SPCA533; - break; - case 0x2024: - sd->bridge = BRIDGE_SPCA536; - break; - case 0x2028: - sd->bridge = BRIDGE_SPCA533; - break; - case 0x2040: - case 0x2042: - case 0x2050: - case 0x2060: - sd->bridge = BRIDGE_SPCA536; + sd->subtype = 0; break; + default: + return -ENODEV; } - break; - case 0x0d64: /* SunPlus */ -/* switch (product) { */ -/* case 0x0303: */ - sd->bridge = BRIDGE_SPCA536; -/* break; */ -/* } */ - break; } - cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; - cam->epaddr = 0x01; - switch (sd->bridge) { default: /* case BRIDGE_SPCA504B: */ @@ -1581,65 +1378,67 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define DVNM(name) .driver_info = (kernel_ulong_t) name +#define BS(bridge, subtype) \ + .driver_info = (BRIDGE_ ## bridge << 8) \ + | (subtype) static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x041e, 0x400b), DVNM("Creative PC-CAM 600")}, - {USB_DEVICE(0x041e, 0x4012), DVNM("PC-Cam350")}, - {USB_DEVICE(0x041e, 0x4013), DVNM("Creative Pccam750")}, - {USB_DEVICE(0x0458, 0x7006), DVNM("Genius Dsc 1.3 Smart")}, - {USB_DEVICE(0x0461, 0x0821), DVNM("Fujifilm MV-1")}, - {USB_DEVICE(0x046d, 0x0905), DVNM("Logitech ClickSmart 820")}, - {USB_DEVICE(0x046d, 0x0960), DVNM("Logitech ClickSmart 420")}, - {USB_DEVICE(0x0471, 0x0322), DVNM("Philips DMVC1300K")}, - {USB_DEVICE(0x04a5, 0x3003), DVNM("Benq DC 1300")}, - {USB_DEVICE(0x04a5, 0x3008), DVNM("Benq DC 1500")}, - {USB_DEVICE(0x04a5, 0x300a), DVNM("Benq DC3410")}, - {USB_DEVICE(0x04f1, 0x1001), DVNM("JVC GC A50")}, - {USB_DEVICE(0x04fc, 0x500c), DVNM("Sunplus CA500C")}, - {USB_DEVICE(0x04fc, 0x504a), DVNM("Aiptek Mini PenCam 1.3")}, - {USB_DEVICE(0x04fc, 0x504b), DVNM("Maxell MaxPocket LE 1.3")}, - {USB_DEVICE(0x04fc, 0x5330), DVNM("Digitrex 2110")}, - {USB_DEVICE(0x04fc, 0x5360), DVNM("Sunplus Generic")}, - {USB_DEVICE(0x04fc, 0xffff), DVNM("Pure DigitalDakota")}, - {USB_DEVICE(0x052b, 0x1513), DVNM("Megapix V4")}, - {USB_DEVICE(0x0546, 0x3155), DVNM("Polaroid PDC3070")}, - {USB_DEVICE(0x0546, 0x3191), DVNM("Polaroid Ion 80")}, - {USB_DEVICE(0x0546, 0x3273), DVNM("Polaroid PDC2030")}, - {USB_DEVICE(0x055f, 0xc211), DVNM("Kowa Bs888e Microcamera")}, - {USB_DEVICE(0x055f, 0xc230), DVNM("Mustek Digicam 330K")}, - {USB_DEVICE(0x055f, 0xc232), DVNM("Mustek MDC3500")}, - {USB_DEVICE(0x055f, 0xc360), DVNM("Mustek DV4000 Mpeg4 ")}, - {USB_DEVICE(0x055f, 0xc420), DVNM("Mustek gSmart Mini 2")}, - {USB_DEVICE(0x055f, 0xc430), DVNM("Mustek Gsmart LCD 2")}, - {USB_DEVICE(0x055f, 0xc440), DVNM("Mustek DV 3000")}, - {USB_DEVICE(0x055f, 0xc520), DVNM("Mustek gSmart Mini 3")}, - {USB_DEVICE(0x055f, 0xc530), DVNM("Mustek Gsmart LCD 3")}, - {USB_DEVICE(0x055f, 0xc540), DVNM("Gsmart D30")}, - {USB_DEVICE(0x055f, 0xc630), DVNM("Mustek MDC4000")}, - {USB_DEVICE(0x055f, 0xc650), DVNM("Mustek MDC5500Z")}, - {USB_DEVICE(0x05da, 0x1018), DVNM("Digital Dream Enigma 1.3")}, - {USB_DEVICE(0x06d6, 0x0031), DVNM("Trust 610 LCD PowerC@m Zoom")}, - {USB_DEVICE(0x0733, 0x1311), DVNM("Digital Dream Epsilon 1.3")}, - {USB_DEVICE(0x0733, 0x1314), DVNM("Mercury 2.1MEG Deluxe Classic Cam")}, - {USB_DEVICE(0x0733, 0x2211), DVNM("Jenoptik jdc 21 LCD")}, - {USB_DEVICE(0x0733, 0x2221), DVNM("Mercury Digital Pro 3.1p")}, - {USB_DEVICE(0x0733, 0x3261), DVNM("Concord 3045 spca536a")}, - {USB_DEVICE(0x0733, 0x3281), DVNM("Cyberpix S550V")}, - {USB_DEVICE(0x08ca, 0x0104), DVNM("Aiptek PocketDVII 1.3")}, - {USB_DEVICE(0x08ca, 0x0106), DVNM("Aiptek Pocket DV3100+")}, - {USB_DEVICE(0x08ca, 0x2008), DVNM("Aiptek Mini PenCam 2 M")}, - {USB_DEVICE(0x08ca, 0x2010), DVNM("Aiptek PocketCam 3M")}, - {USB_DEVICE(0x08ca, 0x2016), DVNM("Aiptek PocketCam 2 Mega")}, - {USB_DEVICE(0x08ca, 0x2018), DVNM("Aiptek Pencam SD 2M")}, - {USB_DEVICE(0x08ca, 0x2020), DVNM("Aiptek Slim 3000F")}, - {USB_DEVICE(0x08ca, 0x2022), DVNM("Aiptek Slim 3200")}, - {USB_DEVICE(0x08ca, 0x2024), DVNM("Aiptek DV3500 Mpeg4 ")}, - {USB_DEVICE(0x08ca, 0x2028), DVNM("Aiptek PocketCam4M")}, - {USB_DEVICE(0x08ca, 0x2040), DVNM("Aiptek PocketDV4100M")}, - {USB_DEVICE(0x08ca, 0x2042), DVNM("Aiptek PocketDV5100")}, - {USB_DEVICE(0x08ca, 0x2050), DVNM("Medion MD 41437")}, - {USB_DEVICE(0x08ca, 0x2060), DVNM("Aiptek PocketDV5300")}, - {USB_DEVICE(0x0d64, 0x0303), DVNM("Sunplus FashionCam DXG")}, + {USB_DEVICE(0x041e, 0x400b), BS(SPCA504C, 0)}, + {USB_DEVICE(0x041e, 0x4012), BS(SPCA504C, 0)}, + {USB_DEVICE(0x041e, 0x4013), BS(SPCA504C, 0)}, + {USB_DEVICE(0x0458, 0x7006), BS(SPCA504B, 0)}, + {USB_DEVICE(0x0461, 0x0821), BS(SPCA533, 0)}, + {USB_DEVICE(0x046d, 0x0905), BS(SPCA533, LogitechClickSmart820)}, + {USB_DEVICE(0x046d, 0x0960), BS(SPCA504C, LogitechClickSmart420)}, + {USB_DEVICE(0x0471, 0x0322), BS(SPCA504B, 0)}, + {USB_DEVICE(0x04a5, 0x3003), BS(SPCA504B, 0)}, + {USB_DEVICE(0x04a5, 0x3008), BS(SPCA533, 0)}, + {USB_DEVICE(0x04a5, 0x300a), BS(SPCA533, 0)}, + {USB_DEVICE(0x04f1, 0x1001), BS(SPCA504B, 0)}, + {USB_DEVICE(0x04fc, 0x500c), BS(SPCA504B, 0)}, + {USB_DEVICE(0x04fc, 0x504a), BS(SPCA504, AiptekMiniPenCam13)}, + {USB_DEVICE(0x04fc, 0x504b), BS(SPCA504B, 0)}, + {USB_DEVICE(0x04fc, 0x5330), BS(SPCA533, 0)}, + {USB_DEVICE(0x04fc, 0x5360), BS(SPCA536, 0)}, + {USB_DEVICE(0x04fc, 0xffff), BS(SPCA504B, 0)}, + {USB_DEVICE(0x052b, 0x1513), BS(SPCA533, MegapixV4)}, + {USB_DEVICE(0x0546, 0x3155), BS(SPCA533, 0)}, + {USB_DEVICE(0x0546, 0x3191), BS(SPCA504B, 0)}, + {USB_DEVICE(0x0546, 0x3273), BS(SPCA504B, 0)}, + {USB_DEVICE(0x055f, 0xc211), BS(SPCA536, 0)}, + {USB_DEVICE(0x055f, 0xc230), BS(SPCA533, 0)}, + {USB_DEVICE(0x055f, 0xc232), BS(SPCA533, 0)}, + {USB_DEVICE(0x055f, 0xc360), BS(SPCA536, 0)}, + {USB_DEVICE(0x055f, 0xc420), BS(SPCA504, 0)}, + {USB_DEVICE(0x055f, 0xc430), BS(SPCA533, 0)}, + {USB_DEVICE(0x055f, 0xc440), BS(SPCA533, 0)}, + {USB_DEVICE(0x055f, 0xc520), BS(SPCA504, 0)}, + {USB_DEVICE(0x055f, 0xc530), BS(SPCA533, 0)}, + {USB_DEVICE(0x055f, 0xc540), BS(SPCA533, 0)}, + {USB_DEVICE(0x055f, 0xc630), BS(SPCA533, 0)}, + {USB_DEVICE(0x055f, 0xc650), BS(SPCA533, 0)}, + {USB_DEVICE(0x05da, 0x1018), BS(SPCA504B, 0)}, + {USB_DEVICE(0x06d6, 0x0031), BS(SPCA533, 0)}, + {USB_DEVICE(0x0733, 0x1311), BS(SPCA533, 0)}, + {USB_DEVICE(0x0733, 0x1314), BS(SPCA533, 0)}, + {USB_DEVICE(0x0733, 0x2211), BS(SPCA533, 0)}, + {USB_DEVICE(0x0733, 0x2221), BS(SPCA533, 0)}, + {USB_DEVICE(0x0733, 0x3261), BS(SPCA536, 0)}, + {USB_DEVICE(0x0733, 0x3281), BS(SPCA536, 0)}, + {USB_DEVICE(0x08ca, 0x0104), BS(SPCA533, 0)}, + {USB_DEVICE(0x08ca, 0x0106), BS(SPCA533, 0)}, + {USB_DEVICE(0x08ca, 0x2008), BS(SPCA504B, 0)}, + {USB_DEVICE(0x08ca, 0x2010), BS(SPCA533, 0)}, + {USB_DEVICE(0x08ca, 0x2016), BS(SPCA504B, 0)}, + {USB_DEVICE(0x08ca, 0x2018), BS(SPCA504B, 0)}, + {USB_DEVICE(0x08ca, 0x2020), BS(SPCA533, 0)}, + {USB_DEVICE(0x08ca, 0x2022), BS(SPCA533, 0)}, + {USB_DEVICE(0x08ca, 0x2024), BS(SPCA536, 0)}, + {USB_DEVICE(0x08ca, 0x2028), BS(SPCA533, 0)}, + {USB_DEVICE(0x08ca, 0x2040), BS(SPCA536, 0)}, + {USB_DEVICE(0x08ca, 0x2042), BS(SPCA536, 0)}, + {USB_DEVICE(0x08ca, 0x2050), BS(SPCA536, 0)}, + {USB_DEVICE(0x08ca, 0x2060), BS(SPCA536, 0)}, + {USB_DEVICE(0x0d64, 0x0303), BS(SPCA536, 0)}, {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -1664,7 +1463,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - PDEBUG(D_PROBE, "v%s registered", version); + PDEBUG(D_PROBE, "registered"); return 0; } static void __exit sd_mod_exit(void) diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c index 00f47e463a05..91b555c34c68 100644 --- a/drivers/media/video/gspca/t613.c +++ b/drivers/media/video/gspca/t613.c @@ -1,12 +1,4 @@ /* - *Notes: * t613 + tas5130A - * * Focus to light do not balance well as in win. - * Quality in win is not good, but its kinda better. - * * Fix some "extraneous bytes", most of apps will show the image anyway - * * Gamma table, is there, but its really doing something? - * * 7~8 Fps, its ok, max on win its 10. - * Costantino Leandro - * * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> * * This program is free software; you can redistribute it and/or modify @@ -22,16 +14,22 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + *Notes: * t613 + tas5130A + * * Focus to light do not balance well as in win. + * Quality in win is not good, but its kinda better. + * * Fix some "extraneous bytes", most of apps will show the image anyway + * * Gamma table, is there, but its really doing something? + * * 7~8 Fps, its ok, max on win its 10. + * Costantino Leandro */ #define MODULE_NAME "t613" + #include "gspca.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) -static const char version[] = "2.1.7"; #define MAX_GAMMA 0x10 /* 0 to 15 */ -/* From LUVCVIEW */ #define V4L2_CID_EFFECTS (V4L2_CID_PRIVATE_BASE + 3) MODULE_AUTHOR("Leandro Costantino <le_costantino@pixartargentina.com.ar>"); @@ -424,7 +422,6 @@ static int sd_config(struct gspca_dev *gspca_dev, struct cam *cam; cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 0x01; cam->cam_mode = vga_mode_t16; @@ -998,9 +995,8 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define DVNM(name) .driver_info = (kernel_ulong_t) name static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x17a1, 0x0128), DVNM("XPX Webcam")}, + {USB_DEVICE(0x17a1, 0x0128)}, {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -1025,7 +1021,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - PDEBUG(D_PROBE, "v%s registered", version); + PDEBUG(D_PROBE, "registered"); return 0; } static void __exit sd_mod_exit(void) diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c index 0b793899095f..1ff8ba2f7fe5 100644 --- a/drivers/media/video/gspca/tv8532.c +++ b/drivers/media/video/gspca/tv8532.c @@ -22,9 +22,6 @@ #include "gspca.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) -static const char version[] = "2.1.7"; - MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); MODULE_DESCRIPTION("TV8532 USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -249,7 +246,6 @@ static int sd_config(struct gspca_dev *gspca_dev, tv_8532WriteEEprom(gspca_dev); cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 1; cam->cam_mode = sif_mode; cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; @@ -624,13 +620,12 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define DVNM(name) .driver_info = (kernel_ulong_t) name static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x046d, 0x0920), DVNM("QC Express")}, - {USB_DEVICE(0x046d, 0x0921), DVNM("Labtec Webcam")}, - {USB_DEVICE(0x0545, 0x808b), DVNM("Veo Stingray")}, - {USB_DEVICE(0x0545, 0x8333), DVNM("Veo Stingray")}, - {USB_DEVICE(0x0923, 0x010f), DVNM("ICM532 cams")}, + {USB_DEVICE(0x046d, 0x0920)}, + {USB_DEVICE(0x046d, 0x0921)}, + {USB_DEVICE(0x0545, 0x808b)}, + {USB_DEVICE(0x0545, 0x8333)}, + {USB_DEVICE(0x0923, 0x010f)}, {} }; @@ -656,7 +651,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - PDEBUG(D_PROBE, "v%s registered", version); + PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index fcf2c9e32573..a4221753e1bf 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -24,9 +24,6 @@ #include "gspca.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) -static const char version[] = "2.1.7"; - MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); MODULE_DESCRIPTION("GSPCA/VC032X USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -1419,30 +1416,10 @@ static int sd_config(struct gspca_dev *gspca_dev, struct usb_device *dev = gspca_dev->dev; struct cam *cam; int sensor; - __u16 product; - - product = id->idProduct; - sd->bridge = BRIDGE_VC0321; - switch (id->idVendor) { - case 0x0ac8: /* Vimicro z-star */ - switch (product) { - case 0x0323: - sd->bridge = BRIDGE_VC0323; - break; - } - break; - case 0x17ef: /* Lenovo */ -/* switch (product) { */ -/* case 0x4802: * Lenovo MI1310_SOC */ - sd->bridge = BRIDGE_VC0323; -/* break; */ -/* } */ - break; - } cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 0x02; + sd->bridge = id->driver_info; if (sd->bridge == BRIDGE_VC0321) { cam->cam_mode = vc0321_mode; cam->nmodes = ARRAY_SIZE(vc0321_mode); @@ -1771,16 +1748,15 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define DVNM(name) .driver_info = (kernel_ulong_t) name static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x046d, 0x0892), DVNM("Logitech Orbicam")}, - {USB_DEVICE(0x046d, 0x0896), DVNM("Logitech Orbicam")}, - {USB_DEVICE(0x0ac8, 0x0321), DVNM("Vimicro generic vc0321")}, - {USB_DEVICE(0x0ac8, 0x0323), DVNM("Vimicro Vc0323")}, - {USB_DEVICE(0x0ac8, 0x0328), DVNM("A4Tech PK-130MG")}, - {USB_DEVICE(0x0ac8, 0xc001), DVNM("Sony embedded vimicro")}, - {USB_DEVICE(0x0ac8, 0xc002), DVNM("Sony embedded vimicro")}, - {USB_DEVICE(0x17ef, 0x4802), DVNM("Lenovo Vc0323+MI1310_SOC")}, + {USB_DEVICE(0x046d, 0x0892), .driver_info = BRIDGE_VC0321}, + {USB_DEVICE(0x046d, 0x0896), .driver_info = BRIDGE_VC0321}, + {USB_DEVICE(0x0ac8, 0x0321), .driver_info = BRIDGE_VC0321}, + {USB_DEVICE(0x0ac8, 0x0323), .driver_info = BRIDGE_VC0323}, + {USB_DEVICE(0x0ac8, 0x0328), .driver_info = BRIDGE_VC0321}, + {USB_DEVICE(0x0ac8, 0xc001), .driver_info = BRIDGE_VC0321}, + {USB_DEVICE(0x0ac8, 0xc002), .driver_info = BRIDGE_VC0321}, + {USB_DEVICE(0x17ef, 0x4802), .driver_info = BRIDGE_VC0323}, {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -1805,7 +1781,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - PDEBUG(D_PROBE, "v%s registered", version); + PDEBUG(D_PROBE, "registered"); return 0; } static void __exit sd_mod_exit(void) diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index b761b11c5c6a..22a994ccb1d5 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -24,9 +24,6 @@ #include "gspca.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) -static const char version[] = "2.1.7"; - MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>, " "Serge A. Suchkov <Serge.A.S@tochka.ru>"); MODULE_DESCRIPTION("GSPCA ZC03xx/VC3xx USB Camera Driver"); @@ -49,7 +46,7 @@ struct sd { __u8 sharpness; char qindex; - char sensor; /* Type of image sensor chip */ + signed char sensor; /* Type of image sensor chip */ /* !! values used in different tables */ #define SENSOR_CS2102 0 #define SENSOR_CS2102K 1 @@ -2205,10 +2202,10 @@ static const struct usb_action hdcs2020xb_InitialScale[] = { }; static const struct usb_action hdcs2020b_50HZ[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x13, 0x0018}, /* 00,13,18,aa */ - {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */ - {0xaa, 0x0e, 0x0005}, /* 00,0e,05,aa */ - {0xaa, 0x19, 0x001f}, /* 00,19,1f,aa */ + {0xaa, 0x13, 0x0018}, /* 00,13,18,aa */ + {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */ + {0xaa, 0x0e, 0x0005}, /* 00,0e,05,aa */ + {0xaa, 0x19, 0x001f}, /* 00,19,1f,aa */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */ {0xa0, 0x76, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,76,cc */ @@ -2226,10 +2223,10 @@ static const struct usb_action hdcs2020b_50HZ[] = { }; static const struct usb_action hdcs2020b_60HZ[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x13, 0x0031}, /* 00,13,31,aa */ - {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */ - {0xaa, 0x0e, 0x0004}, /* 00,0e,04,aa */ - {0xaa, 0x19, 0x00cd}, /* 00,19,cd,aa */ + {0xaa, 0x13, 0x0031}, /* 00,13,31,aa */ + {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */ + {0xaa, 0x0e, 0x0004}, /* 00,0e,04,aa */ + {0xaa, 0x19, 0x00cd}, /* 00,19,cd,aa */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */ {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,62,cc */ @@ -2247,10 +2244,10 @@ static const struct usb_action hdcs2020b_60HZ[] = { }; static const struct usb_action hdcs2020b_NoFliker[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x13, 0x0010}, /* 00,13,10,aa */ - {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */ - {0xaa, 0x0e, 0x0004}, /* 00,0e,04,aa */ - {0xaa, 0x19, 0x0000}, /* 00,19,00,aa */ + {0xaa, 0x13, 0x0010}, /* 00,13,10,aa */ + {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */ + {0xaa, 0x0e, 0x0004}, /* 00,0e,04,aa */ + {0xaa, 0x19, 0x0000}, /* 00,19,00,aa */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */ {0xa0, 0x70, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,70,cc */ @@ -4102,27 +4099,27 @@ static const struct usb_action pas106b_Initial_com[] = { static const struct usb_action pas106b_Initial[] = { /* 176x144 */ /* JPEG control */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* ClockSetting */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* Sream and Sensor specific */ - {0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT}, /* CMOSSensorSelect */ + {0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT}, /* Picture size */ - {0xa0, 0x00, ZC3XX_R003_FRAMEWIDTHHIGH}, /* FrameWidthHigh 00 */ - {0xa0, 0xb0, ZC3XX_R004_FRAMEWIDTHLOW}, /* FrameWidthLow B0 */ - {0xa0, 0x00, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* FrameHeightHigh 00 */ - {0xa0, 0x90, ZC3XX_R006_FRAMEHEIGHTLOW}, /* FrameHightLow 90 */ + {0xa0, 0x00, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0xb0, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x00, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0x90, ZC3XX_R006_FRAMEHEIGHTLOW}, /* System */ - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* SystemOperating */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* Sream and Sensor specific */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* VideoControlFunction */ - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* VideoControlFunction */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* Sensor Interface */ - {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* Compatibily Mode */ + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* Window inside sensor array */ - {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, /* WinXStartLow */ - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* FirstYLow */ - {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, /* FirstxLow */ - {0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW}, /* WinHeightLow */ - {0xa0, 0x68, ZC3XX_R09E_WINWIDTHLOW}, /* WinWidthLow */ + {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x68, ZC3XX_R09E_WINWIDTHLOW}, /* Init the sensor */ {0xaa, 0x02, 0x0004}, {0xaa, 0x08, 0x0000}, @@ -4135,40 +4132,40 @@ static const struct usb_action pas106b_Initial[] = { /* 176x144 */ {0xaa, 0x14, 0x0081}, /* Other registors */ - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* SensorCorrection */ + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* Frame retreiving */ - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* AutoAdjustFPS */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* Gains */ - {0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN}, /* DigitalGain */ + {0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN}, /* Unknown */ {0xa0, 0x00, 0x01ad}, /* Sharpness */ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* SharpnessMode */ - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* Sharpness05 */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* Other registors */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* OperationMode */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* Auto exposure and white balance */ - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* AWBStatus */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /*Dead pixels */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* DeadPixelsMode */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* EEPROM */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* EEPROMAccess */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* JPEG control */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* ClockSetting */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* Other registers */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* OperationMode */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* Auto exposure and white balance */ - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* AWBStatus */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /*Dead pixels */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* DeadPixelsMode */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* EEPROM */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* EEPROMAccess */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* JPEG control */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* ClockSetting */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ {0xa0, 0xf4, ZC3XX_R10B_RGB01}, @@ -4180,67 +4177,67 @@ static const struct usb_action pas106b_Initial[] = { /* 176x144 */ {0xa0, 0xf4, ZC3XX_R111_RGB21}, {0xa0, 0x58, ZC3XX_R112_RGB22}, /* Auto correction */ - {0xa0, 0x03, ZC3XX_R181_WINXSTART}, /* WinXstart */ - {0xa0, 0x08, ZC3XX_R182_WINXWIDTH}, /* WinXWidth */ - {0xa0, 0x16, ZC3XX_R183_WINXCENTER}, /* WinXCenter */ - {0xa0, 0x03, ZC3XX_R184_WINYSTART}, /* WinYStart */ - {0xa0, 0x05, ZC3XX_R185_WINYWIDTH}, /* WinYWidth */ - {0xa0, 0x14, ZC3XX_R186_WINYCENTER}, /* WinYCenter */ - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, /* AutoCorrectEnable */ + {0xa0, 0x03, ZC3XX_R181_WINXSTART}, + {0xa0, 0x08, ZC3XX_R182_WINXWIDTH}, + {0xa0, 0x16, ZC3XX_R183_WINXCENTER}, + {0xa0, 0x03, ZC3XX_R184_WINYSTART}, + {0xa0, 0x05, ZC3XX_R185_WINYWIDTH}, + {0xa0, 0x14, ZC3XX_R186_WINYCENTER}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, /* Auto exposure and white balance */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* ExposureLimitHigh */ - {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* ExposureLimitMid */ - {0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW}, /* ExposureLimitLow */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* AntiFlickerHigh */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* AntiFlickerLow */ - {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW}, /* AntiFlickerLow */ - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* AEBFreeze */ - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* AEBUnfreeze */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* sensor on */ {0xaa, 0x07, 0x00b1}, {0xaa, 0x05, 0x0003}, {0xaa, 0x04, 0x0001}, {0xaa, 0x03, 0x003b}, /* Gains */ - {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* DigitalLimitDiff */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* DigitalGainStep */ - {0xa0, 0xa0, ZC3XX_R11D_GLOBALGAIN}, /* GlobalGain */ - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* GlobalGain */ + {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xa0, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* Auto correction */ - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, /* AutoCorrectEnable */ + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, {0xa1, 0x01, 0x0180}, /* AutoCorrectEnable */ - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* AutoCorrectEnable */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* Gains */ - {0xa0, 0x40, ZC3XX_R116_RGAIN}, /* RGain */ - {0xa0, 0x40, ZC3XX_R117_GGAIN}, /* GGain */ - {0xa0, 0x40, ZC3XX_R118_BGAIN}, /* BGain */ + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, {} }; static const struct usb_action pas106b_InitialScale[] = { /* 352x288 */ /* JPEG control */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* ClockSetting */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* Sream and Sensor specific */ - {0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT}, /* CMOSSensorSelect */ + {0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT}, /* Picture size */ - {0xa0, 0x01, ZC3XX_R003_FRAMEWIDTHHIGH}, /* FrameWidthHigh */ - {0xa0, 0x60, ZC3XX_R004_FRAMEWIDTHLOW}, /* FrameWidthLow */ - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* FrameHeightHigh */ - {0xa0, 0x20, ZC3XX_R006_FRAMEHEIGHTLOW}, /* FrameHightLow */ + {0xa0, 0x01, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x60, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0x20, ZC3XX_R006_FRAMEHEIGHTLOW}, /* System */ - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* SystemOperating */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* Sream and Sensor specific */ - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* VideoControlFunction */ - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* VideoControlFunction */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* Sensor Interface */ - {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* Compatibily Mode */ + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* Window inside sensor array */ - {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, /* WinXStartLow */ - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* FirstYLow */ - {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, /* FirstxLow */ - {0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW}, /* WinHeightLow */ - {0xa0, 0x68, ZC3XX_R09E_WINWIDTHLOW}, /* WinWidthLow */ + {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x68, ZC3XX_R09E_WINWIDTHLOW}, /* Init the sensor */ {0xaa, 0x02, 0x0004}, {0xaa, 0x08, 0x0000}, @@ -4253,41 +4250,41 @@ static const struct usb_action pas106b_InitialScale[] = { /* 352x288 */ {0xaa, 0x14, 0x0081}, /* Other registors */ - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* SensorCorrection */ + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* Frame retreiving */ - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* AutoAdjustFPS */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* Gains */ - {0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN}, /* DigitalGain */ + {0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN}, /* Unknown */ {0xa0, 0x00, 0x01ad}, /* Sharpness */ - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* SharpnessMode */ - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* Sharpness05 */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* Other registors */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* OperationMode */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* Auto exposure and white balance */ - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* AWBStatus */ - {0xa0, 0x80, ZC3XX_R18D_YTARGET}, /* ????????? */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x80, ZC3XX_R18D_YTARGET}, /*Dead pixels */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* DeadPixelsMode */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* EEPROM */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* EEPROMAccess */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* JPEG control */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* ClockSetting */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* Other registers */ - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* OperationMode */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* Auto exposure and white balance */ - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* AWBStatus */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /*Dead pixels */ - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* DeadPixelsMode */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* EEPROM */ - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* EEPROMAccess */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* JPEG control */ - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* ClockSetting */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ {0xa0, 0xf4, ZC3XX_R10B_RGB01}, @@ -4299,43 +4296,43 @@ static const struct usb_action pas106b_InitialScale[] = { /* 352x288 */ {0xa0, 0xf4, ZC3XX_R111_RGB21}, {0xa0, 0x58, ZC3XX_R112_RGB22}, /* Auto correction */ - {0xa0, 0x03, ZC3XX_R181_WINXSTART}, /* WinXstart */ - {0xa0, 0x08, ZC3XX_R182_WINXWIDTH}, /* WinXWidth */ - {0xa0, 0x16, ZC3XX_R183_WINXCENTER}, /* WinXCenter */ - {0xa0, 0x03, ZC3XX_R184_WINYSTART}, /* WinYStart */ - {0xa0, 0x05, ZC3XX_R185_WINYWIDTH}, /* WinYWidth */ - {0xa0, 0x14, ZC3XX_R186_WINYCENTER}, /* WinYCenter */ - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, /* AutoCorrectEnable */ + {0xa0, 0x03, ZC3XX_R181_WINXSTART}, + {0xa0, 0x08, ZC3XX_R182_WINXWIDTH}, + {0xa0, 0x16, ZC3XX_R183_WINXCENTER}, + {0xa0, 0x03, ZC3XX_R184_WINYSTART}, + {0xa0, 0x05, ZC3XX_R185_WINYWIDTH}, + {0xa0, 0x14, ZC3XX_R186_WINYCENTER}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, /* Auto exposure and white balance */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* ExposureLimitHigh 0 */ - {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* ExposureLimitMid */ - {0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW}, /* ExposureLimitLow 0xb1 */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* AntiFlickerHigh 0x00 */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* AntiFlickerLow 0x00 */ - {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW}, /* AntiFlickerLow 0x87 */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* AEBFreeze 0x10 0x0c */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* AEBUnfreeze 0x30 0x18 */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* sensor on */ {0xaa, 0x07, 0x00b1}, {0xaa, 0x05, 0x0003}, {0xaa, 0x04, 0x0001}, {0xaa, 0x03, 0x003b}, /* Gains */ - {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* DigitalLimitDiff */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* DigitalGainStep */ - {0xa0, 0xa0, ZC3XX_R11D_GLOBALGAIN}, /* GlobalGain */ - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* GlobalGain */ + {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xa0, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* Auto correction */ - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, /* AutoCorrectEnable */ + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, {0xa1, 0x01, 0x0180}, /* AutoCorrectEnable */ - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* AutoCorrectEnable */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* Gains */ - {0xa0, 0x40, ZC3XX_R116_RGAIN}, /* RGain */ - {0xa0, 0x40, ZC3XX_R117_GGAIN}, /* GGain */ - {0xa0, 0x40, ZC3XX_R118_BGAIN}, /* BGain */ + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, {0xa0, 0x00, 0x0007}, /* AutoCorrectEnable */ {0xa0, 0xff, ZC3XX_R018_FRAMELOST}, /* Frame adjust */ @@ -4459,8 +4456,8 @@ static const struct usb_action pb03303x_Initial[] = { {0xa0, 0x50, ZC3XX_R112_RGB22}, {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, @@ -5984,7 +5981,7 @@ static const struct usb_action tas5130c_vf0250_Initial[] = { {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa, */ {0xaa, 0x13, 0x0002}, /* 00,13,02,aa, */ {0xaa, 0x15, 0x0004}, /* 00,15,04,aa */ - {0xaa, 0x01, 0x0000}, +/*?? {0xaa, 0x01, 0x0000}, */ {0xaa, 0x01, 0x0000}, {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa, */ {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa, */ @@ -6000,8 +5997,8 @@ static const struct usb_action tas5130c_vf0250_Initial[] = { {0xaa, 0x0f, 0x00a0}, /* 00,0f,a0,aa, */ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa, */ {0xaa, 0x11, 0x00a0}, /* 00,11,a0,aa, */ - {0xa0, 0x00, 0x0039}, - {0xa1, 0x01, 0x0037}, +/*?? {0xa0, 0x00, 0x0039}, + {0xa1, 0x01, 0x0037}, */ {0xaa, 0x16, 0x0001}, /* 00,16,01,aa, */ {0xaa, 0x17, 0x00e8}, /* 00,17,e6,aa, (e6 -> e8) */ {0xaa, 0x18, 0x0002}, /* 00,18,02,aa, */ @@ -6272,7 +6269,7 @@ static void reg_w(struct usb_device *dev, __u8 value, __u16 index) { - PDEBUG(D_USBO, "reg w %02x -> [%04x]", value, index); + PDEBUG(D_USBO, "reg w [%04x] = %02x", index, value); reg_w_i(dev, value, index); } @@ -6280,17 +6277,17 @@ static __u16 i2c_read(struct gspca_dev *gspca_dev, __u8 reg) { __u8 retbyte; - __u8 retval[2]; + __u16 retval; reg_w_i(gspca_dev->dev, reg, 0x92); reg_w_i(gspca_dev->dev, 0x02, 0x90); /* <- read command */ msleep(25); retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ - retval[0] = reg_r_i(gspca_dev, 0x0095); /* read Lowbyte */ - retval[1] = reg_r_i(gspca_dev, 0x0096); /* read Hightbyte */ - PDEBUG(D_USBO, "i2c r [%02x] -> (%02x) %02x%02x", - reg, retbyte, retval[1], retval[0]); - return (retval[1] << 8) | retval[0]; + retval = reg_r_i(gspca_dev, 0x0095); /* read Lowbyte */ + retval |= reg_r_i(gspca_dev, 0x0096) << 8; /* read Hightbyte */ + PDEBUG(D_USBO, "i2c r [%02x] -> %04x (%02x)", + reg, retval, retbyte); + return retval; } static __u8 i2c_write(struct gspca_dev *gspca_dev, @@ -6306,7 +6303,7 @@ static __u8 i2c_write(struct gspca_dev *gspca_dev, reg_w_i(gspca_dev->dev, 0x01, 0x90); /* <- write command */ msleep(5); retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ - PDEBUG(D_USBO, "i2c w [%02x] %02x%02x (%02x)", + PDEBUG(D_USBO, "i2c w [%02x] = %02x%02x (%02x)", reg, valH, valL, retbyte); return retbyte; } @@ -6349,6 +6346,8 @@ static void setmatrix(struct gspca_dev *gspca_dev) {0x58, 0xf4, 0xf4, 0xf4, 0x58, 0xf4, 0xf4, 0xf4, 0x58}; static const __u8 po2030_matrix[9] = {0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60}; + static const __u8 vf0250_matrix[9] = + {0x7b, 0xea, 0xea, 0xea, 0x7b, 0xea, 0xea, 0xea, 0x7b}; switch (sd->sensor) { case SENSOR_GC0305: @@ -6363,8 +6362,9 @@ static void setmatrix(struct gspca_dev *gspca_dev) case SENSOR_PO2030: matrix = po2030_matrix; break; - case SENSOR_TAS5130C_VF0250: /* no matrix? */ - return; + case SENSOR_TAS5130C_VF0250: + matrix = vf0250_matrix; + break; default: /* matrix already loaded */ return; } @@ -6744,7 +6744,7 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev) return 0x04; /* CS2102 */ start_2wr_probe(dev, 0x06); /* OmniVision */ - reg_w(dev, 0x08, 0x8d); + reg_w(dev, 0x08, 0x008d); i2c_write(gspca_dev, 0x11, 0xaa, 0x00); retbyte = i2c_read(gspca_dev, 0x11); if (retbyte != 0) { @@ -6778,7 +6778,7 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev) return 0x0c; /* ICM105A */ start_2wr_probe(dev, 0x0e); /* PAS202BCB */ - reg_w(dev, 0x08, 0x8d); + reg_w(dev, 0x08, 0x008d); i2c_write(gspca_dev, 0x03, 0xaa, 0x00); msleep(500); retbyte = i2c_read(gspca_dev, 0x03); @@ -6830,7 +6830,6 @@ static const struct sensor_by_chipset_revision chipset_revision_sensor[] = { {0x8001, 0x13}, {0x8000, 0x14}, /* CS2102K */ {0x8400, 0x15}, /* TAS5130K */ - {0, 0} }; static int vga_3wr_probe(struct gspca_dev *gspca_dev) @@ -6843,7 +6842,7 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) /*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/ reg_w(dev, 0x02, 0x0010); - reg_r(gspca_dev, 0x10); + reg_r(gspca_dev, 0x0010); reg_w(dev, 0x01, 0x0000); reg_w(dev, 0x00, 0x0010); reg_w(dev, 0x01, 0x0001); @@ -6869,17 +6868,15 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) PDEBUG(D_PROBE, "probe 3wr vga 1 0x%04x", checkword); reg_r(gspca_dev, 0x0010); /* this is tested only once anyway */ - i = 0; - while (chipset_revision_sensor[i].revision) { + for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) { if (chipset_revision_sensor[i].revision == checkword) { sd->chip_revision = checkword; send_unknown(dev, SENSOR_PB0330); return chipset_revision_sensor[i].internal_sensor_id; } - i++; } - reg_w(dev, 0x01, 0x0000); + reg_w(dev, 0x01, 0x0000); /* check ?? */ reg_w(dev, 0x01, 0x0001); reg_w(dev, 0xdd, 0x008b); reg_w(dev, 0x0a, 0x0010); @@ -6901,8 +6898,11 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) retbyte = i2c_read(gspca_dev, 0x00); if (retbyte != 0) { PDEBUG(D_PROBE, "probe 3wr vga type %02x", retbyte); - send_unknown(dev, SENSOR_GC0305); - return retbyte; /* 0x29 = gc0305 - should continue? */ + if (retbyte == 0x11) /* VF0250 */ + return 0x0250; + if (retbyte == 0x29) /* gc0305 */ + send_unknown(dev, SENSOR_GC0305); + return retbyte; } reg_w(dev, 0x01, 0x0000); /* check OmniVision */ @@ -6918,18 +6918,18 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) return 0x06; /* OmniVision confirm ? */ } - reg_w(dev, 0x01, 0x00); - reg_w(dev, 0x00, 0x02); - reg_w(dev, 0x01, 0x10); - reg_w(dev, 0x01, 0x01); - reg_w(dev, 0xee, 0x8b); - reg_w(dev, 0x03, 0x12); + reg_w(dev, 0x01, 0x0000); + reg_w(dev, 0x00, 0x0002); + reg_w(dev, 0x01, 0x0010); + reg_w(dev, 0x01, 0x0001); + reg_w(dev, 0xee, 0x008b); + reg_w(dev, 0x03, 0x0012); /* msleep(150); */ - reg_w(dev, 0x01, 0x12); - reg_w(dev, 0x05, 0x12); - retbyte = i2c_read(gspca_dev, 0x00); /* ID 0 */ + reg_w(dev, 0x01, 0x0012); + reg_w(dev, 0x05, 0x0012); + retbyte = i2c_read(gspca_dev, 0x0000); /* ID 0 */ checkword = retbyte << 8; - retbyte = i2c_read(gspca_dev, 0x01); /* ID 1 */ + retbyte = i2c_read(gspca_dev, 0x0001); /* ID 1 */ checkword |= retbyte; PDEBUG(D_PROBE, "probe 3wr vga 2 0x%04x", checkword); if (checkword == 0x2030) { @@ -6939,14 +6939,14 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) return checkword; } - reg_w(dev, 0x01, 0x00); - reg_w(dev, 0x0a, 0x10); - reg_w(dev, 0xd3, 0x8b); - reg_w(dev, 0x01, 0x01); - reg_w(dev, 0x03, 0x12); - reg_w(dev, 0x01, 0x12); - reg_w(dev, 0x05, 0x01); - reg_w(dev, 0xd3, 0x8b); + reg_w(dev, 0x01, 0x0000); + reg_w(dev, 0x0a, 0x0010); + reg_w(dev, 0xd3, 0x008b); + reg_w(dev, 0x01, 0x0001); + reg_w(dev, 0x03, 0x0012); + reg_w(dev, 0x01, 0x0012); + reg_w(dev, 0x05, 0x0001); + reg_w(dev, 0xd3, 0x008b); retbyte = i2c_read(gspca_dev, 0x01); if (retbyte != 0) { PDEBUG(D_PROBE, "probe 3wr vga type 0a ?"); @@ -6962,7 +6962,9 @@ static int zcxx_probeSensor(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_MC501CB: + return -1; /* don't probe */ case SENSOR_TAS5130C_VF0250: + /* may probe but with write in reg 0x0010 */ return -1; /* don't probe */ } sensor = vga_2wr_probe(gspca_dev); @@ -7010,30 +7012,7 @@ static int sd_config(struct gspca_dev *gspca_dev, /* define some sensors from the vendor/product */ sd->sharpness = 2; - switch (id->idVendor) { - case 0x041e: /* Creative */ - switch (id->idProduct) { - case 0x4051: /* zc301 chips */ - case 0x4053: - sd->sensor = SENSOR_TAS5130C_VF0250; - break; - } - break; - case 0x046d: /* Logitech Labtec */ - switch (id->idProduct) { - case 0x08dd: - sd->sensor = SENSOR_MC501CB; - break; - } - break; - case 0x0ac8: /* Vimicro z-star */ - switch (id->idProduct) { - case 0x305b: - sd->sensor = SENSOR_TAS5130C_VF0250; - break; - } - break; - } + sd->sensor = id->driver_info; sensor = zcxx_probeSensor(gspca_dev); if (sensor >= 0) PDEBUG(D_PROBE, "probe sensor -> %02x", sensor); @@ -7119,6 +7098,10 @@ static int sd_config(struct gspca_dev *gspca_dev, PDEBUG(D_PROBE, "Find Sensor GC0305"); sd->sensor = SENSOR_GC0305; break; + case 0x0250: + PDEBUG(D_PROBE, "Sensor Tas5130 (VF0250)"); + sd->sensor = SENSOR_TAS5130C_VF0250; + break; case 0x2030: PDEBUG(D_PROBE, "Find Sensor PO2030"); sd->sensor = SENSOR_PO2030; @@ -7146,7 +7129,6 @@ static int sd_config(struct gspca_dev *gspca_dev, } cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 0x01; /*fixme:test*/ gspca_dev->nbalt--; @@ -7235,6 +7217,7 @@ static void sd_start(struct gspca_dev *gspca_dev) case SENSOR_GC0305: case SENSOR_OV7620: case SENSOR_PO2030: + case SENSOR_TAS5130C_VF0250: msleep(100); /* ?? */ reg_r(gspca_dev, 0x0002); /* --> 0x40 */ reg_w(dev, 0x09, 0x01ad); /* (from win traces) */ @@ -7515,70 +7498,69 @@ static const struct sd_desc sd_desc = { .querymenu = sd_querymenu, }; -#define DVNM(name) .driver_info = (kernel_ulong_t) name static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x041e, 0x041e), DVNM("Creative WebCam Live!")}, + {USB_DEVICE(0x041e, 0x041e)}, #ifndef CONFIG_USB_ZC0301 - {USB_DEVICE(0x041e, 0x4017), DVNM("Creative Webcam Mobile PD1090")}, - {USB_DEVICE(0x041e, 0x401c), DVNM("Creative NX")}, - {USB_DEVICE(0x041e, 0x401e), DVNM("Creative Nx Pro")}, - {USB_DEVICE(0x041e, 0x401f), DVNM("Creative Webcam Notebook PD1171")}, + {USB_DEVICE(0x041e, 0x4017)}, + {USB_DEVICE(0x041e, 0x401c)}, + {USB_DEVICE(0x041e, 0x401e)}, + {USB_DEVICE(0x041e, 0x401f)}, #endif - {USB_DEVICE(0x041e, 0x4029), DVNM("Creative WebCam Vista Pro")}, + {USB_DEVICE(0x041e, 0x4029)}, #ifndef CONFIG_USB_ZC0301 - {USB_DEVICE(0x041e, 0x4034), DVNM("Creative Instant P0620")}, - {USB_DEVICE(0x041e, 0x4035), DVNM("Creative Instant P0620D")}, - {USB_DEVICE(0x041e, 0x4036), DVNM("Creative Live !")}, - {USB_DEVICE(0x041e, 0x403a), DVNM("Creative Nx Pro 2")}, + {USB_DEVICE(0x041e, 0x4034)}, + {USB_DEVICE(0x041e, 0x4035)}, + {USB_DEVICE(0x041e, 0x4036)}, + {USB_DEVICE(0x041e, 0x403a)}, #endif - {USB_DEVICE(0x041e, 0x4051), DVNM("Creative Notebook Pro (VF0250)")}, - {USB_DEVICE(0x041e, 0x4053), DVNM("Creative Live!Cam Video IM")}, + {USB_DEVICE(0x041e, 0x4051), .driver_info = SENSOR_TAS5130C_VF0250}, + {USB_DEVICE(0x041e, 0x4053), .driver_info = SENSOR_TAS5130C_VF0250}, #ifndef CONFIG_USB_ZC0301 - {USB_DEVICE(0x0458, 0x7007), DVNM("Genius VideoCam V2")}, - {USB_DEVICE(0x0458, 0x700c), DVNM("Genius VideoCam V3")}, - {USB_DEVICE(0x0458, 0x700f), DVNM("Genius VideoCam Web V2")}, + {USB_DEVICE(0x0458, 0x7007)}, + {USB_DEVICE(0x0458, 0x700c)}, + {USB_DEVICE(0x0458, 0x700f)}, #endif - {USB_DEVICE(0x0461, 0x0a00), DVNM("MicroInnovation WebCam320")}, - {USB_DEVICE(0x046d, 0x08a0), DVNM("Logitech QC IM")}, - {USB_DEVICE(0x046d, 0x08a1), DVNM("Logitech QC IM 0x08A1 +sound")}, - {USB_DEVICE(0x046d, 0x08a2), DVNM("Labtec Webcam Pro")}, - {USB_DEVICE(0x046d, 0x08a3), DVNM("Logitech QC Chat")}, - {USB_DEVICE(0x046d, 0x08a6), DVNM("Logitech QCim")}, - {USB_DEVICE(0x046d, 0x08a7), DVNM("Logitech QuickCam Image")}, - {USB_DEVICE(0x046d, 0x08a9), DVNM("Logitech Notebook Deluxe")}, - {USB_DEVICE(0x046d, 0x08aa), DVNM("Labtec Webcam Notebook")}, - {USB_DEVICE(0x046d, 0x08ac), DVNM("Logitech QuickCam Cool")}, - {USB_DEVICE(0x046d, 0x08ad), DVNM("Logitech QCCommunicate STX")}, + {USB_DEVICE(0x0461, 0x0a00)}, + {USB_DEVICE(0x046d, 0x08a0)}, + {USB_DEVICE(0x046d, 0x08a1)}, + {USB_DEVICE(0x046d, 0x08a2)}, + {USB_DEVICE(0x046d, 0x08a3)}, + {USB_DEVICE(0x046d, 0x08a6)}, + {USB_DEVICE(0x046d, 0x08a7)}, + {USB_DEVICE(0x046d, 0x08a9)}, + {USB_DEVICE(0x046d, 0x08aa)}, + {USB_DEVICE(0x046d, 0x08ac)}, + {USB_DEVICE(0x046d, 0x08ad)}, #ifndef CONFIG_USB_ZC0301 - {USB_DEVICE(0x046d, 0x08ae), DVNM("Logitech QuickCam for Notebooks")}, + {USB_DEVICE(0x046d, 0x08ae)}, #endif - {USB_DEVICE(0x046d, 0x08af), DVNM("Logitech QuickCam Cool")}, - {USB_DEVICE(0x046d, 0x08b9), DVNM("Logitech QC IM ???")}, - {USB_DEVICE(0x046d, 0x08d7), DVNM("Logitech QCam STX")}, - {USB_DEVICE(0x046d, 0x08d9), DVNM("Logitech QuickCam IM/Connect")}, - {USB_DEVICE(0x046d, 0x08d8), DVNM("Logitech Notebook Deluxe")}, - {USB_DEVICE(0x046d, 0x08da), DVNM("Logitech QuickCam Messenger")}, - {USB_DEVICE(0x046d, 0x08dd), DVNM("Logitech QuickCam for Notebooks")}, - {USB_DEVICE(0x0471, 0x0325), DVNM("Philips SPC 200 NC")}, - {USB_DEVICE(0x0471, 0x0326), DVNM("Philips SPC 300 NC")}, - {USB_DEVICE(0x0471, 0x032d), DVNM("Philips spc210nc")}, - {USB_DEVICE(0x0471, 0x032e), DVNM("Philips spc315nc")}, - {USB_DEVICE(0x055f, 0xc005), DVNM("Mustek Wcam300A")}, + {USB_DEVICE(0x046d, 0x08af)}, + {USB_DEVICE(0x046d, 0x08b9)}, + {USB_DEVICE(0x046d, 0x08d7)}, + {USB_DEVICE(0x046d, 0x08d9)}, + {USB_DEVICE(0x046d, 0x08d8)}, + {USB_DEVICE(0x046d, 0x08da)}, + {USB_DEVICE(0x046d, 0x08dd), .driver_info = SENSOR_MC501CB}, + {USB_DEVICE(0x0471, 0x0325)}, + {USB_DEVICE(0x0471, 0x0326)}, + {USB_DEVICE(0x0471, 0x032d)}, + {USB_DEVICE(0x0471, 0x032e)}, + {USB_DEVICE(0x055f, 0xc005)}, #ifndef CONFIG_USB_ZC0301 - {USB_DEVICE(0x055f, 0xd003), DVNM("Mustek WCam300A")}, - {USB_DEVICE(0x055f, 0xd004), DVNM("Mustek WCam300 AN")}, + {USB_DEVICE(0x055f, 0xd003)}, + {USB_DEVICE(0x055f, 0xd004)}, #endif - {USB_DEVICE(0x0698, 0x2003), DVNM("CTX M730V built in")}, - {USB_DEVICE(0x0ac8, 0x0302), DVNM("Z-star Vimicro zc0302")}, + {USB_DEVICE(0x0698, 0x2003)}, + {USB_DEVICE(0x0ac8, 0x0302)}, #ifndef CONFIG_USB_ZC0301 - {USB_DEVICE(0x0ac8, 0x301b), DVNM("Z-Star zc301b")}, - {USB_DEVICE(0x0ac8, 0x303b), DVNM("Vimicro 0x303b")}, + {USB_DEVICE(0x0ac8, 0x301b)}, + {USB_DEVICE(0x0ac8, 0x303b)}, #endif - {USB_DEVICE(0x0ac8, 0x305b), DVNM("Z-star Vimicro zc0305b")}, + {USB_DEVICE(0x0ac8, 0x305b), .driver_info = SENSOR_TAS5130C_VF0250}, #ifndef CONFIG_USB_ZC0301 - {USB_DEVICE(0x0ac8, 0x307b), DVNM("Z-Star 307b")}, - {USB_DEVICE(0x10fd, 0x0128), DVNM("Typhoon Webshot II 300k 0x0128")}, - {USB_DEVICE(0x10fd, 0x8050), DVNM("Typhoon Webshot II USB 300k")}, + {USB_DEVICE(0x0ac8, 0x307b)}, + {USB_DEVICE(0x10fd, 0x0128)}, + {USB_DEVICE(0x10fd, 0x8050)}, #endif {} /* end of entry */ }; @@ -7605,7 +7587,7 @@ static int __init sd_mod_init(void) { if (usb_register(&sd_driver) < 0) return -1; - PDEBUG(D_PROBE, "v%s registered", version); + PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/ivtv/Kconfig b/drivers/media/video/ivtv/Kconfig index 5d7ee8fcdd50..0069898bddab 100644 --- a/drivers/media/video/ivtv/Kconfig +++ b/drivers/media/video/ivtv/Kconfig @@ -2,9 +2,7 @@ config VIDEO_IVTV tristate "Conexant cx23416/cx23415 MPEG encoder/decoder support" depends on VIDEO_V4L1 && VIDEO_V4L2 && PCI && I2C && EXPERIMENTAL depends on INPUT # due to VIDEO_IR - depends on HOTPLUG # due to FW_LOADER select I2C_ALGOBIT - select FW_LOADER select VIDEO_IR select VIDEO_TUNER select VIDEO_TVEEPROM diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 41fd79279bb5..aea1664948ce 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -465,9 +465,8 @@ static void ivtv_process_eeprom(struct ivtv *itv) 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 == -1 && tv.has_ir) { + itv->options.newi2c = (tv.has_ir & 4) ? 1 : 0; if (itv->options.newi2c) { IVTV_INFO("Reopen i2c bus for IR-blaster support\n"); exit_ivtv_i2c(itv); diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h index a08bb3331cfb..ab287b48fc2b 100644 --- a/drivers/media/video/ivtv/ivtv-driver.h +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -60,6 +60,7 @@ #include <linux/dvb/video.h> #include <linux/dvb/audio.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <media/tuner.h> #include <media/cx2341x.h> diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index 52e00a7f3110..61030309d0ad 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -1842,69 +1842,73 @@ int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, return res; } -void ivtv_set_funcs(struct video_device *vdev) -{ - vdev->vidioc_querycap = ivtv_querycap; - vdev->vidioc_g_priority = ivtv_g_priority; - vdev->vidioc_s_priority = ivtv_s_priority; - vdev->vidioc_s_audio = ivtv_s_audio; - vdev->vidioc_g_audio = ivtv_g_audio; - vdev->vidioc_enumaudio = ivtv_enumaudio; - vdev->vidioc_s_audout = ivtv_s_audout; - vdev->vidioc_g_audout = ivtv_g_audout; - vdev->vidioc_enum_input = ivtv_enum_input; - vdev->vidioc_enum_output = ivtv_enum_output; - vdev->vidioc_enumaudout = ivtv_enumaudout; - vdev->vidioc_cropcap = ivtv_cropcap; - vdev->vidioc_s_crop = ivtv_s_crop; - vdev->vidioc_g_crop = ivtv_g_crop; - vdev->vidioc_g_input = ivtv_g_input; - vdev->vidioc_s_input = ivtv_s_input; - vdev->vidioc_g_output = ivtv_g_output; - vdev->vidioc_s_output = ivtv_s_output; - vdev->vidioc_g_frequency = ivtv_g_frequency; - vdev->vidioc_s_frequency = ivtv_s_frequency; - vdev->vidioc_s_tuner = ivtv_s_tuner; - vdev->vidioc_g_tuner = ivtv_g_tuner; - vdev->vidioc_g_enc_index = ivtv_g_enc_index; - vdev->vidioc_g_fbuf = ivtv_g_fbuf; - vdev->vidioc_s_fbuf = ivtv_s_fbuf; - vdev->vidioc_g_std = ivtv_g_std; - vdev->vidioc_s_std = ivtv_s_std; - vdev->vidioc_overlay = ivtv_overlay; - vdev->vidioc_log_status = ivtv_log_status; - vdev->vidioc_enum_fmt_vid_cap = ivtv_enum_fmt_vid_cap; - vdev->vidioc_encoder_cmd = ivtv_encoder_cmd; - vdev->vidioc_try_encoder_cmd = ivtv_try_encoder_cmd; - vdev->vidioc_enum_fmt_vid_out = ivtv_enum_fmt_vid_out; - vdev->vidioc_g_fmt_vid_cap = ivtv_g_fmt_vid_cap; - vdev->vidioc_g_fmt_vbi_cap = ivtv_g_fmt_vbi_cap; - vdev->vidioc_g_fmt_sliced_vbi_cap = ivtv_g_fmt_sliced_vbi_cap; - vdev->vidioc_g_fmt_vid_out = ivtv_g_fmt_vid_out; - vdev->vidioc_g_fmt_vid_out_overlay = ivtv_g_fmt_vid_out_overlay; - vdev->vidioc_g_fmt_sliced_vbi_out = ivtv_g_fmt_sliced_vbi_out; - vdev->vidioc_s_fmt_vid_cap = ivtv_s_fmt_vid_cap; - vdev->vidioc_s_fmt_vbi_cap = ivtv_s_fmt_vbi_cap; - vdev->vidioc_s_fmt_sliced_vbi_cap = ivtv_s_fmt_sliced_vbi_cap; - vdev->vidioc_s_fmt_vid_out = ivtv_s_fmt_vid_out; - vdev->vidioc_s_fmt_vid_out_overlay = ivtv_s_fmt_vid_out_overlay; - vdev->vidioc_s_fmt_sliced_vbi_out = ivtv_s_fmt_sliced_vbi_out; - vdev->vidioc_try_fmt_vid_cap = ivtv_try_fmt_vid_cap; - vdev->vidioc_try_fmt_vbi_cap = ivtv_try_fmt_vbi_cap; - vdev->vidioc_try_fmt_sliced_vbi_cap = ivtv_try_fmt_sliced_vbi_cap; - vdev->vidioc_try_fmt_vid_out = ivtv_try_fmt_vid_out; - vdev->vidioc_try_fmt_vid_out_overlay = ivtv_try_fmt_vid_out_overlay; - vdev->vidioc_try_fmt_sliced_vbi_out = ivtv_try_fmt_sliced_vbi_out; - vdev->vidioc_g_sliced_vbi_cap = ivtv_g_sliced_vbi_cap; - vdev->vidioc_g_chip_ident = ivtv_g_chip_ident; +static const struct v4l2_ioctl_ops ivtv_ioctl_ops = { + .vidioc_querycap = ivtv_querycap, + .vidioc_g_priority = ivtv_g_priority, + .vidioc_s_priority = ivtv_s_priority, + .vidioc_s_audio = ivtv_s_audio, + .vidioc_g_audio = ivtv_g_audio, + .vidioc_enumaudio = ivtv_enumaudio, + .vidioc_s_audout = ivtv_s_audout, + .vidioc_g_audout = ivtv_g_audout, + .vidioc_enum_input = ivtv_enum_input, + .vidioc_enum_output = ivtv_enum_output, + .vidioc_enumaudout = ivtv_enumaudout, + .vidioc_cropcap = ivtv_cropcap, + .vidioc_s_crop = ivtv_s_crop, + .vidioc_g_crop = ivtv_g_crop, + .vidioc_g_input = ivtv_g_input, + .vidioc_s_input = ivtv_s_input, + .vidioc_g_output = ivtv_g_output, + .vidioc_s_output = ivtv_s_output, + .vidioc_g_frequency = ivtv_g_frequency, + .vidioc_s_frequency = ivtv_s_frequency, + .vidioc_s_tuner = ivtv_s_tuner, + .vidioc_g_tuner = ivtv_g_tuner, + .vidioc_g_enc_index = ivtv_g_enc_index, + .vidioc_g_fbuf = ivtv_g_fbuf, + .vidioc_s_fbuf = ivtv_s_fbuf, + .vidioc_g_std = ivtv_g_std, + .vidioc_s_std = ivtv_s_std, + .vidioc_overlay = ivtv_overlay, + .vidioc_log_status = ivtv_log_status, + .vidioc_enum_fmt_vid_cap = ivtv_enum_fmt_vid_cap, + .vidioc_encoder_cmd = ivtv_encoder_cmd, + .vidioc_try_encoder_cmd = ivtv_try_encoder_cmd, + .vidioc_enum_fmt_vid_out = ivtv_enum_fmt_vid_out, + .vidioc_g_fmt_vid_cap = ivtv_g_fmt_vid_cap, + .vidioc_g_fmt_vbi_cap = ivtv_g_fmt_vbi_cap, + .vidioc_g_fmt_sliced_vbi_cap = ivtv_g_fmt_sliced_vbi_cap, + .vidioc_g_fmt_vid_out = ivtv_g_fmt_vid_out, + .vidioc_g_fmt_vid_out_overlay = ivtv_g_fmt_vid_out_overlay, + .vidioc_g_fmt_sliced_vbi_out = ivtv_g_fmt_sliced_vbi_out, + .vidioc_s_fmt_vid_cap = ivtv_s_fmt_vid_cap, + .vidioc_s_fmt_vbi_cap = ivtv_s_fmt_vbi_cap, + .vidioc_s_fmt_sliced_vbi_cap = ivtv_s_fmt_sliced_vbi_cap, + .vidioc_s_fmt_vid_out = ivtv_s_fmt_vid_out, + .vidioc_s_fmt_vid_out_overlay = ivtv_s_fmt_vid_out_overlay, + .vidioc_s_fmt_sliced_vbi_out = ivtv_s_fmt_sliced_vbi_out, + .vidioc_try_fmt_vid_cap = ivtv_try_fmt_vid_cap, + .vidioc_try_fmt_vbi_cap = ivtv_try_fmt_vbi_cap, + .vidioc_try_fmt_sliced_vbi_cap = ivtv_try_fmt_sliced_vbi_cap, + .vidioc_try_fmt_vid_out = ivtv_try_fmt_vid_out, + .vidioc_try_fmt_vid_out_overlay = ivtv_try_fmt_vid_out_overlay, + .vidioc_try_fmt_sliced_vbi_out = ivtv_try_fmt_sliced_vbi_out, + .vidioc_g_sliced_vbi_cap = ivtv_g_sliced_vbi_cap, + .vidioc_g_chip_ident = ivtv_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG - vdev->vidioc_g_register = ivtv_g_register; - vdev->vidioc_s_register = ivtv_s_register; + .vidioc_g_register = ivtv_g_register, + .vidioc_s_register = ivtv_s_register, #endif - vdev->vidioc_default = ivtv_default; - vdev->vidioc_queryctrl = ivtv_queryctrl; - vdev->vidioc_querymenu = ivtv_querymenu; - vdev->vidioc_g_ext_ctrls = ivtv_g_ext_ctrls; - vdev->vidioc_s_ext_ctrls = ivtv_s_ext_ctrls; - vdev->vidioc_try_ext_ctrls = ivtv_try_ext_ctrls; + .vidioc_default = ivtv_default, + .vidioc_queryctrl = ivtv_queryctrl, + .vidioc_querymenu = ivtv_querymenu, + .vidioc_g_ext_ctrls = ivtv_g_ext_ctrls, + .vidioc_s_ext_ctrls = ivtv_s_ext_ctrls, + .vidioc_try_ext_ctrls = ivtv_try_ext_ctrls, +}; + +void ivtv_set_funcs(struct video_device *vdev) +{ + vdev->ioctl_ops = &ivtv_ioctl_ops; } diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index f8883b487f4a..54d2023b26c4 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -208,16 +208,11 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) 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->parent = &itv->dev->dev; s->v4l2dev->fops = ivtv_stream_info[type].fops; s->v4l2dev->release = video_device_release; s->v4l2dev->tvnorms = V4L2_STD_ALL; diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c index 39bf6b114d50..89a781c6929d 100644 --- a/drivers/media/video/m52790.c +++ b/drivers/media/video/m52790.c @@ -26,7 +26,7 @@ #include <asm/uaccess.h> #include <linux/i2c.h> #include <linux/i2c-id.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/m52790.h> #include <media/v4l2-common.h> #include <media/v4l2-chip-ident.h> diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index 2fb5854cf6f0..7c8ef6ac6c39 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -31,6 +31,7 @@ #include <linux/init.h> #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <asm/uaccess.h> #include <asm/io.h> #include <linux/delay.h> @@ -1697,13 +1698,7 @@ static const struct file_operations meye_fops = { .llseek = no_llseek, }; -static struct video_device meye_template = { - .owner = THIS_MODULE, - .name = "meye", - .type = VID_TYPE_CAPTURE, - .fops = &meye_fops, - .release = video_device_release, - .minor = -1, +static const struct v4l2_ioctl_ops meye_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, @@ -1724,6 +1719,14 @@ static struct video_device meye_template = { .vidioc_default = vidioc_default, }; +static struct video_device meye_template = { + .name = "meye", + .fops = &meye_fops, + .ioctl_ops = &meye_ioctl_ops, + .release = video_device_release, + .minor = -1, +}; + #ifdef CONFIG_PM static int meye_suspend(struct pci_dev *pdev, pm_message_t state) { @@ -1801,7 +1804,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; + meye.video_dev->parent = &meye.mchip_dev->dev; if ((ret = sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 1))) { printk(KERN_ERR "meye: unable to power on the camera\n"); diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index 5691e019d195..3da74dcee902 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -51,9 +51,9 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/i2c.h> -#include <linux/videodev.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <media/v4l2-i2c-drv-legacy.h> #include <media/tvaudio.h> #include <media/msp3400.h> diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c index 1622f70e4dd0..846a14a61fd1 100644 --- a/drivers/media/video/msp3400-kthreads.c +++ b/drivers/media/video/msp3400-kthreads.c @@ -25,7 +25,6 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/freezer.h> -#include <linux/videodev.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> #include <media/msp3400.h> diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index ee43499544c1..554d2295484e 100644 --- a/drivers/media/video/mt9m001.c +++ b/drivers/media/video/mt9m001.c @@ -120,7 +120,7 @@ static int mt9m001_init(struct soc_camera_device *icd) int ret; /* Disable chip, synchronous option update */ - dev_dbg(icd->vdev->dev, "%s\n", __func__); + dev_dbg(icd->vdev->parent, "%s\n", __func__); ret = reg_write(icd, MT9M001_RESET, 1); if (ret >= 0) diff --git a/drivers/media/video/ov511.c b/drivers/media/video/ov511.c index eafb0c7736e6..9edaca4371d7 100644 --- a/drivers/media/video/ov511.c +++ b/drivers/media/video/ov511.c @@ -4666,9 +4666,7 @@ static const struct file_operations ov511_fops = { }; static struct video_device vdev_template = { - .owner = THIS_MODULE, .name = "OV511 USB Camera", - .type = VID_TYPE_CAPTURE, .fops = &ov511_fops, .release = video_device_release, .minor = -1, @@ -5661,43 +5659,43 @@ static int ov_create_sysfs(struct video_device *vdev) { int rc; - rc = video_device_create_file(vdev, &dev_attr_custom_id); + rc = device_create_file(&vdev->dev, &dev_attr_custom_id); if (rc) goto err; - rc = video_device_create_file(vdev, &dev_attr_model); + rc = device_create_file(&vdev->dev, &dev_attr_model); if (rc) goto err_id; - rc = video_device_create_file(vdev, &dev_attr_bridge); + rc = device_create_file(&vdev->dev, &dev_attr_bridge); if (rc) goto err_model; - rc = video_device_create_file(vdev, &dev_attr_sensor); + rc = device_create_file(&vdev->dev, &dev_attr_sensor); if (rc) goto err_bridge; - rc = video_device_create_file(vdev, &dev_attr_brightness); + rc = device_create_file(&vdev->dev, &dev_attr_brightness); if (rc) goto err_sensor; - rc = video_device_create_file(vdev, &dev_attr_saturation); + rc = device_create_file(&vdev->dev, &dev_attr_saturation); if (rc) goto err_bright; - rc = video_device_create_file(vdev, &dev_attr_contrast); + rc = device_create_file(&vdev->dev, &dev_attr_contrast); if (rc) goto err_sat; - rc = video_device_create_file(vdev, &dev_attr_hue); + rc = device_create_file(&vdev->dev, &dev_attr_hue); if (rc) goto err_contrast; - rc = video_device_create_file(vdev, &dev_attr_exposure); + rc = device_create_file(&vdev->dev, &dev_attr_exposure); if (rc) goto err_hue; return 0; err_hue: - video_device_remove_file(vdev, &dev_attr_hue); + device_remove_file(&vdev->dev, &dev_attr_hue); err_contrast: - video_device_remove_file(vdev, &dev_attr_contrast); + device_remove_file(&vdev->dev, &dev_attr_contrast); err_sat: - video_device_remove_file(vdev, &dev_attr_saturation); + device_remove_file(&vdev->dev, &dev_attr_saturation); err_bright: - video_device_remove_file(vdev, &dev_attr_brightness); + device_remove_file(&vdev->dev, &dev_attr_brightness); err_sensor: - video_device_remove_file(vdev, &dev_attr_sensor); + device_remove_file(&vdev->dev, &dev_attr_sensor); err_bridge: - video_device_remove_file(vdev, &dev_attr_bridge); + device_remove_file(&vdev->dev, &dev_attr_bridge); err_model: - video_device_remove_file(vdev, &dev_attr_model); + device_remove_file(&vdev->dev, &dev_attr_model); err_id: - video_device_remove_file(vdev, &dev_attr_custom_id); + device_remove_file(&vdev->dev, &dev_attr_custom_id); err: return rc; } @@ -5833,7 +5831,7 @@ ov51x_probe(struct usb_interface *intf, const struct usb_device_id *id) goto error; memcpy(ov->vdev, &vdev_template, sizeof(*ov->vdev)); - ov->vdev->dev = &intf->dev; + ov->vdev->parent = &intf->dev; video_set_drvdata(ov->vdev, ov); for (i = 0; i < OV511_MAX_UNIT_VIDEO; i++) { diff --git a/drivers/media/video/ov511.h b/drivers/media/video/ov511.h index 1010e51189b7..baded1262ca9 100644 --- a/drivers/media/video/ov511.h +++ b/drivers/media/video/ov511.h @@ -4,6 +4,7 @@ #include <asm/uaccess.h> #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/usb.h> #include <linux/mutex.h> diff --git a/drivers/media/video/planb.c b/drivers/media/video/planb.c index 36047d4e70f6..e69de29bb2d1 100644 --- a/drivers/media/video/planb.c +++ b/drivers/media/video/planb.c @@ -1,2309 +0,0 @@ -/* - planb - PlanB frame grabber driver - - PlanB is used in the 7x00/8x00 series of PowerMacintosh - Computers as video input DMA controller. - - Copyright (C) 1998 Michel Lanners (mlan@cpu.lu) - - Based largely on the bttv driver by Ralph Metzler (rjkm@thp.uni-koeln.de) - - Additional debugging and coding by Takashi Oe (toe@unlserve.unl.edu) - - 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. -*/ - -/* $Id: planb.c,v 1.18 1999/05/02 17:36:34 mlan Exp $ */ - -#include <linux/init.h> -#include <linux/errno.h> -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/major.h> -#include <linux/slab.h> -#include <linux/types.h> -#include <linux/pci.h> -#include <linux/delay.h> -#include <linux/vmalloc.h> -#include <linux/mm.h> -#include <linux/sched.h> -#include <linux/videodev.h> -#include <media/v4l2-common.h> -#include <linux/wait.h> -#include <asm/uaccess.h> -#include <asm/io.h> -#include <asm/prom.h> -#include <asm/dbdma.h> -#include <asm/pgtable.h> -#include <asm/page.h> -#include <asm/irq.h> -#include <linux/mutex.h> - -#include "planb.h" -#include "saa7196.h" - -/* Would you mind for some ugly debugging? */ -#if 0 -#define DEBUG(x...) printk(KERN_DEBUG ## x) /* Debug driver */ -#else -#define DEBUG(x...) /* Don't debug driver */ -#endif - -#if 0 -#define IDEBUG(x...) printk(KERN_DEBUG ## x) /* Debug interrupt part */ -#else -#define IDEBUG(x...) /* Don't debug interrupt part */ -#endif - -/* Ever seen a Mac with more than 1 of these? */ -#define PLANB_MAX 1 - -static int planb_num; -static struct planb planbs[PLANB_MAX]; -static volatile struct planb_registers *planb_regs; - -static int def_norm = PLANB_DEF_NORM; /* default norm */ -static int video_nr = -1; - -module_param(def_norm, int, 0); -MODULE_PARM_DESC(def_norm, "Default startup norm (0=PAL, 1=NTSC, 2=SECAM)"); -module_param(video_nr, int, 0); -MODULE_LICENSE("GPL"); - - -/* ------------------ PlanB Exported Functions ------------------ */ -static long planb_write(struct video_device *, const char *, unsigned long, int); -static long planb_read(struct video_device *, char *, unsigned long, int); -static int planb_open(struct video_device *, int); -static void planb_close(struct video_device *); -static int planb_ioctl(struct video_device *, unsigned int, void *); -static int planb_init_done(struct video_device *); -static int planb_mmap(struct video_device *, const char *, unsigned long); -static void release_planb(void); -int init_planbs(struct video_init *); - -/* ------------------ PlanB Internal Functions ------------------ */ -static int planb_prepare_open(struct planb *); -static void planb_prepare_close(struct planb *); -static void saa_write_reg(unsigned char, unsigned char); -static unsigned char saa_status(int, struct planb *); -static void saa_set(unsigned char, unsigned char, struct planb *); -static void saa_init_regs(struct planb *); -static int grabbuf_alloc(struct planb *); -static int vgrab(struct planb *, struct video_mmap *); -static void add_clip(struct planb *, struct video_clip *); -static void fill_cmd_buff(struct planb *); -static void cmd_buff(struct planb *); -static volatile struct dbdma_cmd *setup_grab_cmd(int, struct planb *); -static void overlay_start(struct planb *); -static void overlay_stop(struct planb *); -static inline void tab_cmd_dbdma(volatile struct dbdma_cmd *, unsigned short, - unsigned int); -static inline void tab_cmd_store(volatile struct dbdma_cmd *, unsigned int, - unsigned int); -static inline void tab_cmd_gen(volatile struct dbdma_cmd *, unsigned short, - unsigned short, unsigned int, unsigned int); -static int init_planb(struct planb *); -static int find_planb(void); -static void planb_pre_capture(int, int, struct planb *); -static volatile struct dbdma_cmd *cmd_geo_setup(volatile struct dbdma_cmd *, - int, int, int, int, int, struct planb *); -static inline void planb_dbdma_stop(volatile struct dbdma_regs *); -static unsigned int saa_geo_setup(int, int, int, int, struct planb *); -static inline int overlay_is_active(struct planb *); - -/*******************************/ -/* Memory management functions */ -/*******************************/ - -static int grabbuf_alloc(struct planb *pb) -{ - int i, npage; - - npage = MAX_GBUFFERS * ((PLANB_MAX_FBUF / PAGE_SIZE + 1) -#ifndef PLANB_GSCANLINE - + MAX_LNUM -#endif /* PLANB_GSCANLINE */ - ); - if ((pb->rawbuf = kmalloc(npage - * sizeof(unsigned long), GFP_KERNEL)) == 0) - return -ENOMEM; - for (i = 0; i < npage; i++) { - pb->rawbuf[i] = (unsigned char *)__get_free_pages(GFP_KERNEL - |GFP_DMA, 0); - if (!pb->rawbuf[i]) - break; - SetPageReserved(virt_to_page(pb->rawbuf[i])); - } - if (i-- < npage) { - printk(KERN_DEBUG "PlanB: init_grab: grab buffer not allocated\n"); - for (; i > 0; i--) { - ClearPageReserved(virt_to_page(pb->rawbuf[i])); - free_pages((unsigned long)pb->rawbuf[i], 0); - } - kfree(pb->rawbuf); - return -ENOBUFS; - } - pb->rawbuf_size = npage; - return 0; -} - -/*****************************/ -/* Hardware access functions */ -/*****************************/ - -static void saa_write_reg(unsigned char addr, unsigned char val) -{ - planb_regs->saa_addr = addr; eieio(); - planb_regs->saa_regval = val; eieio(); - return; -} - -/* return status byte 0 or 1: */ -static unsigned char saa_status(int byte, struct planb *pb) -{ - saa_regs[pb->win.norm][SAA7196_STDC] = - (saa_regs[pb->win.norm][SAA7196_STDC] & ~2) | ((byte & 1) << 1); - saa_write_reg (SAA7196_STDC, saa_regs[pb->win.norm][SAA7196_STDC]); - - /* Let's wait 30msec for this one */ - msleep_interruptible(30); - - return (unsigned char)in_8 (&planb_regs->saa_status); -} - -static void saa_set(unsigned char addr, unsigned char val, struct planb *pb) -{ - if(saa_regs[pb->win.norm][addr] != val) { - saa_regs[pb->win.norm][addr] = val; - saa_write_reg (addr, val); - } - return; -} - -static void saa_init_regs(struct planb *pb) -{ - int i; - - for (i = 0; i < SAA7196_NUMREGS; i++) - saa_write_reg (i, saa_regs[pb->win.norm][i]); -} - -static unsigned int saa_geo_setup(int width, int height, int interlace, int bpp, - struct planb *pb) -{ - int ht, norm = pb->win.norm; - - switch(bpp) { - case 2: - /* RGB555+a 1x16-bit + 16-bit transparent */ - saa_regs[norm][SAA7196_FMTS] &= ~0x3; - break; - case 1: - case 4: - /* RGB888 1x24-bit + 8-bit transparent */ - saa_regs[norm][SAA7196_FMTS] &= ~0x1; - saa_regs[norm][SAA7196_FMTS] |= 0x2; - break; - default: - return -EINVAL; - } - ht = (interlace ? height / 2 : height); - saa_regs[norm][SAA7196_OUTPIX] = (unsigned char) (width & 0x00ff); - saa_regs[norm][SAA7196_HFILT] = (saa_regs[norm][SAA7196_HFILT] & ~0x3) - | (width >> 8 & 0x3); - saa_regs[norm][SAA7196_OUTLINE] = (unsigned char) (ht & 0xff); - saa_regs[norm][SAA7196_VYP] = (saa_regs[norm][SAA7196_VYP] & ~0x3) - | (ht >> 8 & 0x3); - /* feed both fields if interlaced, or else feed only even fields */ - saa_regs[norm][SAA7196_FMTS] = (interlace) ? - (saa_regs[norm][SAA7196_FMTS] & ~0x60) - : (saa_regs[norm][SAA7196_FMTS] | 0x60); - /* transparent mode; extended format enabled */ - saa_regs[norm][SAA7196_DPATH] |= 0x3; - - return 0; -} - -/***************************/ -/* DBDMA support functions */ -/***************************/ - -static inline void planb_dbdma_restart(volatile struct dbdma_regs *ch) -{ - out_le32(&ch->control, PLANB_CLR(RUN)); - out_le32(&ch->control, PLANB_SET(RUN|WAKE) | PLANB_CLR(PAUSE)); -} - -static inline void planb_dbdma_stop(volatile struct dbdma_regs *ch) -{ - int i = 0; - - out_le32(&ch->control, PLANB_CLR(RUN) | PLANB_SET(FLUSH)); - while((in_le32(&ch->status) == (ACTIVE | FLUSH)) && (i < 999)) { - IDEBUG("PlanB: waiting for DMA to stop\n"); - i++; - } -} - -static inline void tab_cmd_dbdma(volatile struct dbdma_cmd *ch, - unsigned short command, unsigned int cmd_dep) -{ - st_le16(&ch->command, command); - st_le32(&ch->cmd_dep, cmd_dep); -} - -static inline void tab_cmd_store(volatile struct dbdma_cmd *ch, - unsigned int phy_addr, unsigned int cmd_dep) -{ - st_le16(&ch->command, STORE_WORD | KEY_SYSTEM); - st_le16(&ch->req_count, 4); - st_le32(&ch->phy_addr, phy_addr); - st_le32(&ch->cmd_dep, cmd_dep); -} - -static inline void tab_cmd_gen(volatile struct dbdma_cmd *ch, - unsigned short command, unsigned short req_count, - unsigned int phy_addr, unsigned int cmd_dep) -{ - st_le16(&ch->command, command); - st_le16(&ch->req_count, req_count); - st_le32(&ch->phy_addr, phy_addr); - st_le32(&ch->cmd_dep, cmd_dep); -} - -static volatile struct dbdma_cmd *cmd_geo_setup( - volatile struct dbdma_cmd *c1, int width, int height, int interlace, - int bpp, int clip, struct planb *pb) -{ - int norm = pb->win.norm; - - if((saa_geo_setup(width, height, interlace, bpp, pb)) != 0) - return (volatile struct dbdma_cmd *)NULL; - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), - SAA7196_FMTS); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), - saa_regs[norm][SAA7196_FMTS]); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), - SAA7196_DPATH); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), - saa_regs[norm][SAA7196_DPATH]); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->even), - bpp | ((clip)? PLANB_CLIPMASK: 0)); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->odd), - bpp | ((clip)? PLANB_CLIPMASK: 0)); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), - SAA7196_OUTPIX); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), - saa_regs[norm][SAA7196_OUTPIX]); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), - SAA7196_HFILT); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), - saa_regs[norm][SAA7196_HFILT]); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), - SAA7196_OUTLINE); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), - saa_regs[norm][SAA7196_OUTLINE]); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), - SAA7196_VYP); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), - saa_regs[norm][SAA7196_VYP]); - return c1; -} - -/******************************/ -/* misc. supporting functions */ -/******************************/ - -static inline void planb_lock(struct planb *pb) -{ - mutex_lock(&pb->lock); -} - -static inline void planb_unlock(struct planb *pb) -{ - mutex_unlock(&pb->lock); -} - -/***************/ -/* Driver Core */ -/***************/ - -static int planb_prepare_open(struct planb *pb) -{ - int i, size; - - /* allocate memory for two plus alpha command buffers (size: max lines, - plus 40 commands handling, plus 1 alignment), plus dummy command buf, - plus clipmask buffer, plus frame grabbing status */ - size = (pb->tab_size*(2+MAX_GBUFFERS*TAB_FACTOR)+1+MAX_GBUFFERS - * PLANB_DUMMY)*sizeof(struct dbdma_cmd) - +(PLANB_MAXLINES*((PLANB_MAXPIXELS+7)& ~7))/8 - +MAX_GBUFFERS*sizeof(unsigned int); - if ((pb->priv_space = kzalloc (size, GFP_KERNEL)) == 0) - return -ENOMEM; - pb->overlay_last1 = pb->ch1_cmd = (volatile struct dbdma_cmd *) - DBDMA_ALIGN (pb->priv_space); - pb->overlay_last2 = pb->ch2_cmd = pb->ch1_cmd + pb->tab_size; - pb->ch1_cmd_phys = virt_to_bus(pb->ch1_cmd); - pb->cap_cmd[0] = pb->ch2_cmd + pb->tab_size; - pb->pre_cmd[0] = pb->cap_cmd[0] + pb->tab_size * TAB_FACTOR; - for (i = 1; i < MAX_GBUFFERS; i++) { - pb->cap_cmd[i] = pb->pre_cmd[i-1] + PLANB_DUMMY; - pb->pre_cmd[i] = pb->cap_cmd[i] + pb->tab_size * TAB_FACTOR; - } - pb->frame_stat=(volatile unsigned int *)(pb->pre_cmd[MAX_GBUFFERS-1] - + PLANB_DUMMY); - pb->mask = (unsigned char *)(pb->frame_stat+MAX_GBUFFERS); - - pb->rawbuf = NULL; - pb->rawbuf_size = 0; - pb->grabbing = 0; - for (i = 0; i < MAX_GBUFFERS; i++) { - pb->frame_stat[i] = GBUFFER_UNUSED; - pb->gwidth[i] = 0; - pb->gheight[i] = 0; - pb->gfmt[i] = 0; - pb->gnorm_switch[i] = 0; -#ifndef PLANB_GSCANLINE - pb->lsize[i] = 0; - pb->lnum[i] = 0; -#endif /* PLANB_GSCANLINE */ - } - pb->gcount = 0; - pb->suspend = 0; - pb->last_fr = -999; - pb->prev_last_fr = -999; - - /* Reset DMA controllers */ - planb_dbdma_stop(&pb->planb_base->ch2); - planb_dbdma_stop(&pb->planb_base->ch1); - - return 0; -} - -static void planb_prepare_close(struct planb *pb) -{ - int i; - - /* make sure the dma's are idle */ - planb_dbdma_stop(&pb->planb_base->ch2); - planb_dbdma_stop(&pb->planb_base->ch1); - /* free kernel memory of command buffers */ - if(pb->priv_space != 0) { - kfree (pb->priv_space); - pb->priv_space = 0; - pb->cmd_buff_inited = 0; - } - if(pb->rawbuf) { - for (i = 0; i < pb->rawbuf_size; i++) { - ClearPageReserved(virt_to_page(pb->rawbuf[i])); - free_pages((unsigned long)pb->rawbuf[i], 0); - } - kfree(pb->rawbuf); - } - pb->rawbuf = NULL; -} - -/*****************************/ -/* overlay support functions */ -/*****************************/ - -static inline int overlay_is_active(struct planb *pb) -{ - unsigned int size = pb->tab_size * sizeof(struct dbdma_cmd); - unsigned int caddr = (unsigned)in_le32(&pb->planb_base->ch1.cmdptr); - - return (in_le32(&pb->overlay_last1->cmd_dep) == pb->ch1_cmd_phys) - && (caddr < (pb->ch1_cmd_phys + size)) - && (caddr >= (unsigned)pb->ch1_cmd_phys); -} - -static void overlay_start(struct planb *pb) -{ - - DEBUG("PlanB: overlay_start()\n"); - - if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) { - - DEBUG("PlanB: presumably, grabbing is in progress...\n"); - - planb_dbdma_stop(&pb->planb_base->ch2); - out_le32 (&pb->planb_base->ch2.cmdptr, - virt_to_bus(pb->ch2_cmd)); - planb_dbdma_restart(&pb->planb_base->ch2); - st_le16 (&pb->ch1_cmd->command, DBDMA_NOP); - tab_cmd_dbdma(pb->last_cmd[pb->last_fr], - DBDMA_NOP | BR_ALWAYS, - virt_to_bus(pb->ch1_cmd)); - eieio(); - pb->prev_last_fr = pb->last_fr; - pb->last_fr = -2; - if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) { - IDEBUG("PlanB: became inactive " - "in the mean time... reactivating\n"); - planb_dbdma_stop(&pb->planb_base->ch1); - out_le32 (&pb->planb_base->ch1.cmdptr, - virt_to_bus(pb->ch1_cmd)); - planb_dbdma_restart(&pb->planb_base->ch1); - } - } else { - - DEBUG("PlanB: currently idle, so can do whatever\n"); - - planb_dbdma_stop(&pb->planb_base->ch2); - planb_dbdma_stop(&pb->planb_base->ch1); - st_le32 (&pb->planb_base->ch2.cmdptr, - virt_to_bus(pb->ch2_cmd)); - st_le32 (&pb->planb_base->ch1.cmdptr, - virt_to_bus(pb->ch1_cmd)); - out_le16 (&pb->ch1_cmd->command, DBDMA_NOP); - planb_dbdma_restart(&pb->planb_base->ch2); - planb_dbdma_restart(&pb->planb_base->ch1); - pb->last_fr = -1; - } - return; -} - -static void overlay_stop(struct planb *pb) -{ - DEBUG("PlanB: overlay_stop()\n"); - - if(pb->last_fr == -1) { - - DEBUG("PlanB: no grabbing, it seems...\n"); - - planb_dbdma_stop(&pb->planb_base->ch2); - planb_dbdma_stop(&pb->planb_base->ch1); - pb->last_fr = -999; - } else if(pb->last_fr == -2) { - unsigned int cmd_dep; - tab_cmd_dbdma(pb->cap_cmd[pb->prev_last_fr], DBDMA_STOP, 0); - eieio(); - cmd_dep = (unsigned int)in_le32(&pb->overlay_last1->cmd_dep); - if(overlay_is_active(pb)) { - - DEBUG("PlanB: overlay is currently active\n"); - - planb_dbdma_stop(&pb->planb_base->ch2); - planb_dbdma_stop(&pb->planb_base->ch1); - if(cmd_dep != pb->ch1_cmd_phys) { - out_le32(&pb->planb_base->ch1.cmdptr, - virt_to_bus(pb->overlay_last1)); - planb_dbdma_restart(&pb->planb_base->ch1); - } - } - pb->last_fr = pb->prev_last_fr; - pb->prev_last_fr = -999; - } - return; -} - -static void suspend_overlay(struct planb *pb) -{ - int fr = -1; - struct dbdma_cmd last; - - DEBUG("PlanB: suspend_overlay: %d\n", pb->suspend); - - if(pb->suspend++) - return; - if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) { - if(pb->last_fr == -2) { - fr = pb->prev_last_fr; - memcpy(&last, (void*)pb->last_cmd[fr], sizeof(last)); - tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0); - } - if(overlay_is_active(pb)) { - planb_dbdma_stop(&pb->planb_base->ch2); - planb_dbdma_stop(&pb->planb_base->ch1); - pb->suspended.overlay = 1; - pb->suspended.frame = fr; - memcpy(&pb->suspended.cmd, &last, sizeof(last)); - return; - } - } - pb->suspended.overlay = 0; - pb->suspended.frame = fr; - memcpy(&pb->suspended.cmd, &last, sizeof(last)); - return; -} - -static void resume_overlay(struct planb *pb) -{ - - DEBUG("PlanB: resume_overlay: %d\n", pb->suspend); - - if(pb->suspend > 1) - return; - if(pb->suspended.frame != -1) { - memcpy((void*)pb->last_cmd[pb->suspended.frame], - &pb->suspended.cmd, sizeof(pb->suspended.cmd)); - } - if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) { - goto finish; - } - if(pb->suspended.overlay) { - - DEBUG("PlanB: overlay being resumed\n"); - - st_le16 (&pb->ch1_cmd->command, DBDMA_NOP); - st_le16 (&pb->ch2_cmd->command, DBDMA_NOP); - /* Set command buffer addresses */ - st_le32(&pb->planb_base->ch1.cmdptr, - virt_to_bus(pb->overlay_last1)); - out_le32(&pb->planb_base->ch2.cmdptr, - virt_to_bus(pb->overlay_last2)); - /* Start the DMA controller */ - out_le32 (&pb->planb_base->ch2.control, - PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE)); - out_le32 (&pb->planb_base->ch1.control, - PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE)); - } else if(pb->suspended.frame != -1) { - out_le32(&pb->planb_base->ch1.cmdptr, - virt_to_bus(pb->last_cmd[pb->suspended.frame])); - out_le32 (&pb->planb_base->ch1.control, - PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE)); - } - -finish: - pb->suspend--; - wake_up_interruptible(&pb->suspendq); -} - -static void add_clip(struct planb *pb, struct video_clip *clip) -{ - volatile unsigned char *base; - int xc = clip->x, yc = clip->y; - int wc = clip->width, hc = clip->height; - int ww = pb->win.width, hw = pb->win.height; - int x, y, xtmp1, xtmp2; - - DEBUG("PlanB: clip %dx%d+%d+%d\n", wc, hc, xc, yc); - - if(xc < 0) { - wc += xc; - xc = 0; - } - if(yc < 0) { - hc += yc; - yc = 0; - } - if(xc + wc > ww) - wc = ww - xc; - if(wc <= 0) /* Nothing to do */ - return; - if(yc + hc > hw) - hc = hw - yc; - - for (y = yc; y < yc+hc; y++) { - xtmp1=xc>>3; - xtmp2=(xc+wc)>>3; - base = pb->mask + y*96; - if(xc != 0 || wc >= 8) - *(base + xtmp1) &= (unsigned char)(0x00ff & - (0xff00 >> (xc&7))); - for (x = xtmp1 + 1; x < xtmp2; x++) { - *(base + x) = 0; - } - if(xc < (ww & ~0x7)) - *(base + xtmp2) &= (unsigned char)(0x00ff >> - ((xc+wc) & 7)); - } - - return; -} - -static void fill_cmd_buff(struct planb *pb) -{ - int restore = 0; - volatile struct dbdma_cmd last; - - DEBUG("PlanB: fill_cmd_buff()\n"); - - if(pb->overlay_last1 != pb->ch1_cmd) { - restore = 1; - last = *(pb->overlay_last1); - } - memset ((void *) pb->ch1_cmd, 0, 2 * pb->tab_size - * sizeof(struct dbdma_cmd)); - cmd_buff (pb); - if(restore) - *(pb->overlay_last1) = last; - if(pb->suspended.overlay) { - unsigned long jump_addr = in_le32(&pb->overlay_last1->cmd_dep); - if(jump_addr != pb->ch1_cmd_phys) { - int i; - - DEBUG("PlanB: adjusting ch1's jump address\n"); - - for(i = 0; i < MAX_GBUFFERS; i++) { - if(pb->need_pre_capture[i]) { - if(jump_addr == virt_to_bus(pb->pre_cmd[i])) - goto found; - } else { - if(jump_addr == virt_to_bus(pb->cap_cmd[i])) - goto found; - } - } - - DEBUG("PlanB: not found...\n"); - - goto out; -found: - if(pb->need_pre_capture[i]) - out_le32(&pb->pre_cmd[i]->phy_addr, - virt_to_bus(pb->overlay_last1)); - else - out_le32(&pb->cap_cmd[i]->phy_addr, - virt_to_bus(pb->overlay_last1)); - } - } -out: - pb->cmd_buff_inited = 1; - - return; -} - -static void cmd_buff(struct planb *pb) -{ - int i, bpp, count, nlines, stepsize, interlace; - unsigned long base, jump, addr_com, addr_dep; - volatile struct dbdma_cmd *c1 = pb->ch1_cmd; - volatile struct dbdma_cmd *c2 = pb->ch2_cmd; - - interlace = pb->win.interlace; - bpp = pb->win.bpp; - count = (bpp * ((pb->win.x + pb->win.width > pb->win.swidth) ? - (pb->win.swidth - pb->win.x) : pb->win.width)); - nlines = ((pb->win.y + pb->win.height > pb->win.sheight) ? - (pb->win.sheight - pb->win.y) : pb->win.height); - - /* Do video in: */ - - /* Preamble commands: */ - addr_com = virt_to_bus(c1); - addr_dep = virt_to_bus(&c1->cmd_dep); - tab_cmd_dbdma(c1++, DBDMA_NOP, 0); - jump = virt_to_bus(c1+16); /* 14 by cmd_geo_setup() and 2 for padding */ - if((c1 = cmd_geo_setup(c1, pb->win.width, pb->win.height, interlace, - bpp, 1, pb)) == NULL) { - printk(KERN_WARNING "PlanB: encountered serious problems\n"); - tab_cmd_dbdma(pb->ch1_cmd + 1, DBDMA_STOP, 0); - tab_cmd_dbdma(pb->ch2_cmd + 1, DBDMA_STOP, 0); - return; - } - tab_cmd_store(c1++, addr_com, (unsigned)(DBDMA_NOP | BR_ALWAYS) << 16); - tab_cmd_store(c1++, addr_dep, jump); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel), - PLANB_SET(FIELD_SYNC)); - /* (1) wait for field sync to be set */ - tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), - PLANB_SET(ODD_FIELD)); - /* wait for field sync to be cleared */ - tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0); - /* if not odd field, wait until field sync is set again */ - tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++; - /* assert ch_sync to ch2 */ - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch2.control), - PLANB_SET(CH_SYNC)); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), - PLANB_SET(DMA_ABORT)); - - base = (pb->frame_buffer_phys + pb->offset + pb->win.y * (pb->win.bpl - + pb->win.pad) + pb->win.x * bpp); - - if (interlace) { - stepsize = 2; - jump = virt_to_bus(c1 + (nlines + 1) / 2); - } else { - stepsize = 1; - jump = virt_to_bus(c1 + nlines); - } - - /* even field data: */ - for (i=0; i < nlines; i += stepsize, c1++) - tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, - count, base + i * (pb->win.bpl + pb->win.pad), jump); - - /* For non-interlaced, we use even fields only */ - if (!interlace) - goto cmd_tab_data_end; - - /* Resync to odd field */ - /* (2) wait for field sync to be set */ - tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), - PLANB_SET(ODD_FIELD)); - /* wait for field sync to be cleared */ - tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0); - /* if not odd field, wait until field sync is set again */ - tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++; - /* assert ch_sync to ch2 */ - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch2.control), - PLANB_SET(CH_SYNC)); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), - PLANB_SET(DMA_ABORT)); - - /* odd field data: */ - jump = virt_to_bus(c1 + nlines / 2); - for (i=1; i < nlines; i += stepsize, c1++) - tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count, - base + i * (pb->win.bpl + pb->win.pad), jump); - - /* And jump back to the start */ -cmd_tab_data_end: - pb->overlay_last1 = c1; /* keep a pointer to the last command */ - tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->ch1_cmd)); - - /* Clipmask command buffer */ - - /* Preamble commands: */ - tab_cmd_dbdma(c2++, DBDMA_NOP, 0); - tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.wait_sel), - PLANB_SET(CH_SYNC)); - /* wait until ch1 asserts ch_sync */ - tab_cmd_dbdma(c2++, DBDMA_NOP | WAIT_IFCLR, 0); - /* clear ch_sync asserted by ch1 */ - tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.control), - PLANB_CLR(CH_SYNC)); - tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.wait_sel), - PLANB_SET(FIELD_SYNC)); - tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel), - PLANB_SET(ODD_FIELD)); - - /* jump to end of even field if appropriate */ - /* this points to (interlace)? pos. C: pos. B */ - jump = (interlace) ? virt_to_bus(c2 + (nlines + 1) / 2 + 2): - virt_to_bus(c2 + nlines + 2); - /* if odd field, skip over to odd field clipmasking */ - tab_cmd_dbdma(c2++, DBDMA_NOP | BR_IFSET, jump); - - /* even field mask: */ - tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel), - PLANB_SET(DMA_ABORT)); - /* this points to pos. B */ - jump = (interlace) ? virt_to_bus(c2 + nlines + 1): - virt_to_bus(c2 + nlines); - base = virt_to_bus(pb->mask); - for (i=0; i < nlines; i += stepsize, c2++) - tab_cmd_gen(c2, OUTPUT_MORE | KEY_STREAM0 | BR_IFSET, 96, - base + i * 96, jump); - - /* For non-interlaced, we use only even fields */ - if(!interlace) - goto cmd_tab_mask_end; - - /* odd field mask: */ -/* C */ tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel), - PLANB_SET(DMA_ABORT)); - /* this points to pos. B */ - jump = virt_to_bus(c2 + nlines / 2); - base = virt_to_bus(pb->mask); - for (i=1; i < nlines; i += 2, c2++) /* abort if set */ - tab_cmd_gen(c2, OUTPUT_MORE | KEY_STREAM0 | BR_IFSET, 96, - base + i * 96, jump); - - /* Inform channel 1 and jump back to start */ -cmd_tab_mask_end: - /* ok, I just realized this is kind of flawed. */ - /* this part is reached only after odd field clipmasking. */ - /* wanna clean up? */ - /* wait for field sync to be set */ - /* corresponds to fsync (1) of ch1 */ -/* B */ tab_cmd_dbdma(c2++, DBDMA_NOP | WAIT_IFCLR, 0); - /* restart ch1, meant to clear any dead bit or something */ - tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch1.control), - PLANB_CLR(RUN)); - tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch1.control), - PLANB_SET(RUN)); - pb->overlay_last2 = c2; /* keep a pointer to the last command */ - /* start over even field clipmasking */ - tab_cmd_dbdma(c2, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->ch2_cmd)); - - eieio(); - return; -} - -/*********************************/ -/* grabdisplay support functions */ -/*********************************/ - -static int palette2fmt[] = { - 0, - PLANB_GRAY, - 0, - 0, - 0, - PLANB_COLOUR32, - PLANB_COLOUR15, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, -}; - -#define PLANB_PALETTE_MAX 15 - -static int vgrab(struct planb *pb, struct video_mmap *mp) -{ - unsigned int fr = mp->frame; - unsigned int format; - - if(pb->rawbuf==NULL) { - int err; - if((err=grabbuf_alloc(pb))) - return err; - } - - IDEBUG("PlanB: grab %d: %dx%d(%u)\n", pb->grabbing, - mp->width, mp->height, fr); - - if(pb->grabbing >= MAX_GBUFFERS) - return -ENOBUFS; - if(fr > (MAX_GBUFFERS - 1) || fr < 0) - return -EINVAL; - if(mp->height <= 0 || mp->width <= 0) - return -EINVAL; - if(mp->format < 0 || mp->format >= PLANB_PALETTE_MAX) - return -EINVAL; - if((format = palette2fmt[mp->format]) == 0) - return -EINVAL; - if (mp->height * mp->width * format > PLANB_MAX_FBUF) /* format = bpp */ - return -EINVAL; - - planb_lock(pb); - if(mp->width != pb->gwidth[fr] || mp->height != pb->gheight[fr] || - format != pb->gfmt[fr] || (pb->gnorm_switch[fr])) { - int i; -#ifndef PLANB_GSCANLINE - unsigned int osize = pb->gwidth[fr] * pb->gheight[fr] - * pb->gfmt[fr]; - unsigned int nsize = mp->width * mp->height * format; -#endif - - IDEBUG("PlanB: gwidth = %d, gheight = %d, mp->format = %u\n", - mp->width, mp->height, mp->format); - -#ifndef PLANB_GSCANLINE - if(pb->gnorm_switch[fr]) - nsize = 0; - if (nsize < osize) { - for(i = pb->gbuf_idx[fr]; osize > 0; i++) { - memset((void *)pb->rawbuf[i], 0, PAGE_SIZE); - osize -= PAGE_SIZE; - } - } - for(i = pb->l_fr_addr_idx[fr]; i < pb->l_fr_addr_idx[fr] - + pb->lnum[fr]; i++) - memset((void *)pb->rawbuf[i], 0, PAGE_SIZE); -#else -/* XXX TODO */ -/* - if(pb->gnorm_switch[fr]) - memset((void *)pb->gbuffer[fr], 0, - pb->gbytes_per_line * pb->gheight[fr]); - else { - if(mp-> - for(i = 0; i < pb->gheight[fr]; i++) { - memset((void *)(pb->gbuffer[fr] - + pb->gbytes_per_line * i - } - } -*/ -#endif - pb->gwidth[fr] = mp->width; - pb->gheight[fr] = mp->height; - pb->gfmt[fr] = format; - pb->last_cmd[fr] = setup_grab_cmd(fr, pb); - planb_pre_capture(fr, pb->gfmt[fr], pb); /* gfmt = bpp */ - pb->need_pre_capture[fr] = 1; - pb->gnorm_switch[fr] = 0; - } else - pb->need_pre_capture[fr] = 0; - pb->frame_stat[fr] = GBUFFER_GRABBING; - if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) { - - IDEBUG("PlanB: ch1 inactive, initiating grabbing\n"); - - planb_dbdma_stop(&pb->planb_base->ch1); - if(pb->need_pre_capture[fr]) { - - IDEBUG("PlanB: padding pre-capture sequence\n"); - - out_le32 (&pb->planb_base->ch1.cmdptr, - virt_to_bus(pb->pre_cmd[fr])); - } else { - tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0); - tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0); - /* let's be on the safe side. here is not timing critical. */ - tab_cmd_dbdma((pb->cap_cmd[fr] + 1), DBDMA_NOP, 0); - out_le32 (&pb->planb_base->ch1.cmdptr, - virt_to_bus(pb->cap_cmd[fr])); - } - planb_dbdma_restart(&pb->planb_base->ch1); - pb->last_fr = fr; - } else { - int i; - - IDEBUG("PlanB: ch1 active, grabbing being queued\n"); - - if((pb->last_fr == -1) || ((pb->last_fr == -2) && - overlay_is_active(pb))) { - - IDEBUG("PlanB: overlay is active, grabbing defered\n"); - - tab_cmd_dbdma(pb->last_cmd[fr], - DBDMA_NOP | BR_ALWAYS, - virt_to_bus(pb->ch1_cmd)); - if(pb->need_pre_capture[fr]) { - - IDEBUG("PlanB: padding pre-capture sequence\n"); - - tab_cmd_store(pb->pre_cmd[fr], - virt_to_bus(&pb->overlay_last1->cmd_dep), - virt_to_bus(pb->ch1_cmd)); - eieio(); - out_le32 (&pb->overlay_last1->cmd_dep, - virt_to_bus(pb->pre_cmd[fr])); - } else { - tab_cmd_store(pb->cap_cmd[fr], - virt_to_bus(&pb->overlay_last1->cmd_dep), - virt_to_bus(pb->ch1_cmd)); - tab_cmd_dbdma((pb->cap_cmd[fr] + 1), - DBDMA_NOP, 0); - eieio(); - out_le32 (&pb->overlay_last1->cmd_dep, - virt_to_bus(pb->cap_cmd[fr])); - } - for(i = 0; overlay_is_active(pb) && i < 999; i++) - IDEBUG("PlanB: waiting for overlay done\n"); - tab_cmd_dbdma(pb->ch1_cmd, DBDMA_NOP, 0); - pb->prev_last_fr = fr; - pb->last_fr = -2; - } else if(pb->last_fr == -2) { - - IDEBUG("PlanB: mixed mode detected, grabbing" - " will be done before activating overlay\n"); - - tab_cmd_dbdma(pb->ch1_cmd, DBDMA_NOP, 0); - if(pb->need_pre_capture[fr]) { - - IDEBUG("PlanB: padding pre-capture sequence\n"); - - tab_cmd_dbdma(pb->last_cmd[pb->prev_last_fr], - DBDMA_NOP | BR_ALWAYS, - virt_to_bus(pb->pre_cmd[fr])); - eieio(); - } else { - tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0); - if(pb->gwidth[pb->prev_last_fr] != - pb->gwidth[fr] - || pb->gheight[pb->prev_last_fr] != - pb->gheight[fr] - || pb->gfmt[pb->prev_last_fr] != - pb->gfmt[fr]) - tab_cmd_dbdma((pb->cap_cmd[fr] + 1), - DBDMA_NOP, 0); - else - tab_cmd_dbdma((pb->cap_cmd[fr] + 1), - DBDMA_NOP | BR_ALWAYS, - virt_to_bus(pb->cap_cmd[fr] + 16)); - tab_cmd_dbdma(pb->last_cmd[pb->prev_last_fr], - DBDMA_NOP | BR_ALWAYS, - virt_to_bus(pb->cap_cmd[fr])); - eieio(); - } - tab_cmd_dbdma(pb->last_cmd[fr], - DBDMA_NOP | BR_ALWAYS, - virt_to_bus(pb->ch1_cmd)); - eieio(); - pb->prev_last_fr = fr; - pb->last_fr = -2; - } else { - - IDEBUG("PlanB: active grabbing session detected\n"); - - if(pb->need_pre_capture[fr]) { - - IDEBUG("PlanB: padding pre-capture sequence\n"); - - tab_cmd_dbdma(pb->last_cmd[pb->last_fr], - DBDMA_NOP | BR_ALWAYS, - virt_to_bus(pb->pre_cmd[fr])); - eieio(); - } else { - tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0); - tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0); - if(pb->gwidth[pb->last_fr] != pb->gwidth[fr] - || pb->gheight[pb->last_fr] != - pb->gheight[fr] - || pb->gfmt[pb->last_fr] != - pb->gfmt[fr]) - tab_cmd_dbdma((pb->cap_cmd[fr] + 1), - DBDMA_NOP, 0); - else - tab_cmd_dbdma((pb->cap_cmd[fr] + 1), - DBDMA_NOP | BR_ALWAYS, - virt_to_bus(pb->cap_cmd[fr] + 16)); - tab_cmd_dbdma(pb->last_cmd[pb->last_fr], - DBDMA_NOP | BR_ALWAYS, - virt_to_bus(pb->cap_cmd[fr])); - eieio(); - } - pb->last_fr = fr; - } - if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) { - - IDEBUG("PlanB: became inactive in the mean time..." - "reactivating\n"); - - planb_dbdma_stop(&pb->planb_base->ch1); - out_le32 (&pb->planb_base->ch1.cmdptr, - virt_to_bus(pb->cap_cmd[fr])); - planb_dbdma_restart(&pb->planb_base->ch1); - } - } - pb->grabbing++; - planb_unlock(pb); - - return 0; -} - -static void planb_pre_capture(int fr, int bpp, struct planb *pb) -{ - volatile struct dbdma_cmd *c1 = pb->pre_cmd[fr]; - int interlace = (pb->gheight[fr] > pb->maxlines/2)? 1: 0; - - tab_cmd_dbdma(c1++, DBDMA_NOP, 0); - if((c1 = cmd_geo_setup(c1, pb->gwidth[fr], pb->gheight[fr], interlace, - bpp, 0, pb)) == NULL) { - printk(KERN_WARNING "PlanB: encountered some problems\n"); - tab_cmd_dbdma(pb->pre_cmd[fr] + 1, DBDMA_STOP, 0); - return; - } - /* Sync to even field */ - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel), - PLANB_SET(FIELD_SYNC)); - tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), - PLANB_SET(ODD_FIELD)); - tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0); - tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++; - tab_cmd_dbdma(c1++, DBDMA_NOP | INTR_ALWAYS, 0); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), - PLANB_SET(DMA_ABORT)); - /* For non-interlaced, we use even fields only */ - if (pb->gheight[fr] <= pb->maxlines/2) - goto cmd_tab_data_end; - /* Sync to odd field */ - tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), - PLANB_SET(ODD_FIELD)); - tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0); - tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++; - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), - PLANB_SET(DMA_ABORT)); -cmd_tab_data_end: - tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->cap_cmd[fr])); - - eieio(); -} - -static volatile struct dbdma_cmd *setup_grab_cmd(int fr, struct planb *pb) -{ - int i, bpp, count, nlines, stepsize, interlace; -#ifdef PLANB_GSCANLINE - int scanline; -#else - int nlpp, leftover1; - unsigned long base; -#endif - unsigned long jump; - int pagei; - volatile struct dbdma_cmd *c1; - volatile struct dbdma_cmd *jump_addr; - - c1 = pb->cap_cmd[fr]; - interlace = (pb->gheight[fr] > pb->maxlines/2)? 1: 0; - bpp = pb->gfmt[fr]; /* gfmt = bpp */ - count = bpp * pb->gwidth[fr]; - nlines = pb->gheight[fr]; -#ifdef PLANB_GSCANLINE - scanline = pb->gbytes_per_line; -#else - pb->lsize[fr] = count; - pb->lnum[fr] = 0; -#endif - - /* Do video in: */ - - /* Preamble commands: */ - tab_cmd_dbdma(c1++, DBDMA_NOP, 0); - tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(c1 + 16)); c1++; - if((c1 = cmd_geo_setup(c1, pb->gwidth[fr], pb->gheight[fr], interlace, - bpp, 0, pb)) == NULL) { - printk(KERN_WARNING "PlanB: encountered serious problems\n"); - tab_cmd_dbdma(pb->cap_cmd[fr] + 1, DBDMA_STOP, 0); - return (pb->cap_cmd[fr] + 2); - } - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel), - PLANB_SET(FIELD_SYNC)); - tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), - PLANB_SET(ODD_FIELD)); - tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0); - tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++; - tab_cmd_dbdma(c1++, DBDMA_NOP | INTR_ALWAYS, 0); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), - PLANB_SET(DMA_ABORT)); - - if (interlace) { - stepsize = 2; - jump_addr = c1 + TAB_FACTOR * (nlines + 1) / 2; - } else { - stepsize = 1; - jump_addr = c1 + TAB_FACTOR * nlines; - } - jump = virt_to_bus(jump_addr); - - /* even field data: */ - - pagei = pb->gbuf_idx[fr]; -#ifdef PLANB_GSCANLINE - for (i = 0; i < nlines; i += stepsize) { - tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, - virt_to_bus(pb->rawbuf[pagei - + i * scanline / PAGE_SIZE]), jump); - } -#else - i = 0; - leftover1 = 0; - do { - int j; - - base = virt_to_bus(pb->rawbuf[pagei]); - nlpp = (PAGE_SIZE - leftover1) / count / stepsize; - for(j = 0; j < nlpp && i < nlines; j++, i += stepsize, c1++) - tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, - count, base + count * j * stepsize + leftover1, jump); - if(i < nlines) { - int lov0 = PAGE_SIZE - count * nlpp * stepsize - leftover1; - - if(lov0 == 0) - leftover1 = 0; - else { - if(lov0 >= count) { - tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, base - + count * nlpp * stepsize + leftover1, jump); - } else { - pb->l_to_addr[fr][pb->lnum[fr]] = pb->rawbuf[pagei] - + count * nlpp * stepsize + leftover1; - pb->l_to_next_idx[fr][pb->lnum[fr]] = pagei + 1; - pb->l_to_next_size[fr][pb->lnum[fr]] = count - lov0; - tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, - virt_to_bus(pb->rawbuf[pb->l_fr_addr_idx[fr] - + pb->lnum[fr]]), jump); - if(++pb->lnum[fr] > MAX_LNUM) - pb->lnum[fr]--; - } - leftover1 = count * stepsize - lov0; - i += stepsize; - } - } - pagei++; - } while(i < nlines); - tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, jump); - c1 = jump_addr; -#endif /* PLANB_GSCANLINE */ - - /* For non-interlaced, we use even fields only */ - if (!interlace) - goto cmd_tab_data_end; - - /* Sync to odd field */ - tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0); - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), - PLANB_SET(ODD_FIELD)); - tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0); - tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++; - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), - PLANB_SET(DMA_ABORT)); - - /* odd field data: */ - jump_addr = c1 + TAB_FACTOR * nlines / 2; - jump = virt_to_bus(jump_addr); -#ifdef PLANB_GSCANLINE - for (i = 1; i < nlines; i += stepsize) { - tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, - virt_to_bus(pb->rawbuf[pagei - + i * scanline / PAGE_SIZE]), jump); - } -#else - i = 1; - leftover1 = 0; - pagei = pb->gbuf_idx[fr]; - if(nlines <= 1) - goto skip; - do { - int j; - - base = virt_to_bus(pb->rawbuf[pagei]); - nlpp = (PAGE_SIZE - leftover1) / count / stepsize; - if(leftover1 >= count) { - tab_cmd_gen(c1++, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count, - base + leftover1 - count, jump); - i += stepsize; - } - for(j = 0; j < nlpp && i < nlines; j++, i += stepsize, c1++) - tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count, - base + count * (j * stepsize + 1) + leftover1, jump); - if(i < nlines) { - int lov0 = PAGE_SIZE - count * nlpp * stepsize - leftover1; - - if(lov0 == 0) - leftover1 = 0; - else { - if(lov0 > count) { - pb->l_to_addr[fr][pb->lnum[fr]] = pb->rawbuf[pagei] - + count * (nlpp * stepsize + 1) + leftover1; - pb->l_to_next_idx[fr][pb->lnum[fr]] = pagei + 1; - pb->l_to_next_size[fr][pb->lnum[fr]] = count * stepsize - - lov0; - tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, - virt_to_bus(pb->rawbuf[pb->l_fr_addr_idx[fr] - + pb->lnum[fr]]), jump); - if(++pb->lnum[fr] > MAX_LNUM) - pb->lnum[fr]--; - i += stepsize; - } - leftover1 = count * stepsize - lov0; - } - } - pagei++; - } while(i < nlines); -skip: - tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, jump); - c1 = jump_addr; -#endif /* PLANB_GSCANLINE */ - -cmd_tab_data_end: - tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->intr_stat), - (fr << 9) | PLANB_FRM_IRQ | PLANB_GEN_IRQ); - /* stop it */ - tab_cmd_dbdma(c1, DBDMA_STOP, 0); - - eieio(); - return c1; -} - -static irqreturn_t planb_irq(int irq, void *dev_id) -{ - unsigned int stat, astat; - struct planb *pb = (struct planb *)dev_id; - - IDEBUG("PlanB: planb_irq()\n"); - - /* get/clear interrupt status bits */ - eieio(); - stat = in_le32(&pb->planb_base->intr_stat); - astat = stat & pb->intr_mask; - out_le32(&pb->planb_base->intr_stat, PLANB_FRM_IRQ - & ~astat & stat & ~PLANB_GEN_IRQ); - IDEBUG("PlanB: stat = %X, astat = %X\n", stat, astat); - - if(astat & PLANB_FRM_IRQ) { - unsigned int fr = stat >> 9; -#ifndef PLANB_GSCANLINE - int i; -#endif - IDEBUG("PlanB: PLANB_FRM_IRQ\n"); - - pb->gcount++; - - IDEBUG("PlanB: grab %d: fr = %d, gcount = %d\n", - pb->grabbing, fr, pb->gcount); -#ifndef PLANB_GSCANLINE - IDEBUG("PlanB: %d * %d bytes are being copied over\n", - pb->lnum[fr], pb->lsize[fr]); - for(i = 0; i < pb->lnum[fr]; i++) { - int first = pb->lsize[fr] - pb->l_to_next_size[fr][i]; - - memcpy(pb->l_to_addr[fr][i], - pb->rawbuf[pb->l_fr_addr_idx[fr] + i], - first); - memcpy(pb->rawbuf[pb->l_to_next_idx[fr][i]], - pb->rawbuf[pb->l_fr_addr_idx[fr] + i] + first, - pb->l_to_next_size[fr][i]); - } -#endif - pb->frame_stat[fr] = GBUFFER_DONE; - pb->grabbing--; - wake_up_interruptible(&pb->capq); - return IRQ_HANDLED; - } - /* incorrect interrupts? */ - pb->intr_mask = PLANB_CLR_IRQ; - out_le32(&pb->planb_base->intr_stat, PLANB_CLR_IRQ); - printk(KERN_ERR "PlanB: IRQ lockup, cleared intrrupts" - " unconditionally\n"); - return IRQ_HANDLED; -} - -/******************************* - * Device Operations functions * - *******************************/ - -static int planb_open(struct video_device *dev, int mode) -{ - struct planb *pb = (struct planb *)dev; - - if (pb->user == 0) { - int err; - if((err = planb_prepare_open(pb)) != 0) - return err; - } - pb->user++; - - DEBUG("PlanB: device opened\n"); - return 0; -} - -static void planb_close(struct video_device *dev) -{ - struct planb *pb = (struct planb *)dev; - - if(pb->user < 1) /* ??? */ - return; - planb_lock(pb); - if (pb->user == 1) { - if (pb->overlay) { - planb_dbdma_stop(&pb->planb_base->ch2); - planb_dbdma_stop(&pb->planb_base->ch1); - pb->overlay = 0; - } - planb_prepare_close(pb); - } - pb->user--; - planb_unlock(pb); - - DEBUG("PlanB: device closed\n"); -} - -static long planb_read(struct video_device *v, char *buf, unsigned long count, - int nonblock) -{ - DEBUG("planb: read request\n"); - return -EINVAL; -} - -static long planb_write(struct video_device *v, const char *buf, - unsigned long count, int nonblock) -{ - DEBUG("planb: write request\n"); - return -EINVAL; -} - -static int planb_ioctl(struct video_device *dev, unsigned int cmd, void *arg) -{ - struct planb *pb=(struct planb *)dev; - - switch (cmd) - { - case VIDIOCGCAP: - { - struct video_capability b; - - DEBUG("PlanB: IOCTL VIDIOCGCAP\n"); - - strcpy (b.name, pb->video_dev.name); - b.type = VID_TYPE_OVERLAY | VID_TYPE_CLIPPING | - VID_TYPE_FRAMERAM | VID_TYPE_SCALES | - VID_TYPE_CAPTURE; - b.channels = 2; /* composite & svhs */ - b.audios = 0; - b.maxwidth = PLANB_MAXPIXELS; - b.maxheight = PLANB_MAXLINES; - b.minwidth = 32; /* wild guess */ - b.minheight = 32; - if (copy_to_user(arg,&b,sizeof(b))) - return -EFAULT; - return 0; - } - case VIDIOCSFBUF: - { - struct video_buffer v; - unsigned short bpp; - unsigned int fmt; - - DEBUG("PlanB: IOCTL VIDIOCSFBUF\n"); - - if (!capable(CAP_SYS_ADMIN) - || !capable(CAP_SYS_RAWIO)) - return -EPERM; - if (copy_from_user(&v, arg,sizeof(v))) - return -EFAULT; - planb_lock(pb); - switch(v.depth) { - case 8: - bpp = 1; - fmt = PLANB_GRAY; - break; - case 15: - case 16: - bpp = 2; - fmt = PLANB_COLOUR15; - break; - case 24: - case 32: - bpp = 4; - fmt = PLANB_COLOUR32; - break; - default: - planb_unlock(pb); - return -EINVAL; - } - if (bpp * v.width > v.bytesperline) { - planb_unlock(pb); - return -EINVAL; - } - pb->win.bpp = bpp; - pb->win.color_fmt = fmt; - pb->frame_buffer_phys = (unsigned long) v.base; - pb->win.sheight = v.height; - pb->win.swidth = v.width; - pb->picture.depth = pb->win.depth = v.depth; - pb->win.bpl = pb->win.bpp * pb->win.swidth; - pb->win.pad = v.bytesperline - pb->win.bpl; - - DEBUG("PlanB: Display at %p is %d by %d, bytedepth %d," - " bpl %d (+ %d)\n", v.base, v.width,v.height, - pb->win.bpp, pb->win.bpl, pb->win.pad); - - pb->cmd_buff_inited = 0; - if(pb->overlay) { - suspend_overlay(pb); - fill_cmd_buff(pb); - resume_overlay(pb); - } - planb_unlock(pb); - return 0; - } - case VIDIOCGFBUF: - { - struct video_buffer v; - - DEBUG("PlanB: IOCTL VIDIOCGFBUF\n"); - - v.base = (void *)pb->frame_buffer_phys; - v.height = pb->win.sheight; - v.width = pb->win.swidth; - v.depth = pb->win.depth; - v.bytesperline = pb->win.bpl + pb->win.pad; - if (copy_to_user(arg, &v, sizeof(v))) - return -EFAULT; - return 0; - } - case VIDIOCCAPTURE: - { - int i; - - if(copy_from_user(&i, arg, sizeof(i))) - return -EFAULT; - if(i==0) { - DEBUG("PlanB: IOCTL VIDIOCCAPTURE Stop\n"); - - if (!(pb->overlay)) - return 0; - planb_lock(pb); - pb->overlay = 0; - overlay_stop(pb); - planb_unlock(pb); - } else { - DEBUG("PlanB: IOCTL VIDIOCCAPTURE Start\n"); - - if (pb->frame_buffer_phys == 0 || - pb->win.width == 0 || - pb->win.height == 0) - return -EINVAL; - if (pb->overlay) - return 0; - planb_lock(pb); - pb->overlay = 1; - if(!(pb->cmd_buff_inited)) - fill_cmd_buff(pb); - overlay_start(pb); - planb_unlock(pb); - } - return 0; - } - case VIDIOCGCHAN: - { - struct video_channel v; - - DEBUG("PlanB: IOCTL VIDIOCGCHAN\n"); - - if(copy_from_user(&v, arg,sizeof(v))) - return -EFAULT; - v.flags = 0; - v.tuners = 0; - v.type = VIDEO_TYPE_CAMERA; - v.norm = pb->win.norm; - switch(v.channel) - { - case 0: - strcpy(v.name,"Composite"); - break; - case 1: - strcpy(v.name,"SVHS"); - break; - default: - return -EINVAL; - break; - } - if(copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; - - return 0; - } - case VIDIOCSCHAN: - { - struct video_channel v; - - DEBUG("PlanB: IOCTL VIDIOCSCHAN\n"); - - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - - if (v.norm != pb->win.norm) { - int i, maxlines; - - switch (v.norm) - { - case VIDEO_MODE_PAL: - case VIDEO_MODE_SECAM: - maxlines = PLANB_MAXLINES; - break; - case VIDEO_MODE_NTSC: - maxlines = PLANB_NTSC_MAXLINES; - break; - default: - return -EINVAL; - break; - } - planb_lock(pb); - /* empty the grabbing queue */ - wait_event(pb->capq, !pb->grabbing); - pb->maxlines = maxlines; - pb->win.norm = v.norm; - /* Stop overlay if running */ - suspend_overlay(pb); - for(i = 0; i < MAX_GBUFFERS; i++) - pb->gnorm_switch[i] = 1; - /* I know it's an overkill, but.... */ - fill_cmd_buff(pb); - /* ok, now init it accordingly */ - saa_init_regs (pb); - /* restart overlay if it was running */ - resume_overlay(pb); - planb_unlock(pb); - } - - switch(v.channel) - { - case 0: /* Composite */ - saa_set (SAA7196_IOCC, - ((saa_regs[pb->win.norm][SAA7196_IOCC] & - ~7) | 3), pb); - break; - case 1: /* SVHS */ - saa_set (SAA7196_IOCC, - ((saa_regs[pb->win.norm][SAA7196_IOCC] & - ~7) | 4), pb); - break; - default: - return -EINVAL; - break; - } - - return 0; - } - case VIDIOCGPICT: - { - struct video_picture vp = pb->picture; - - DEBUG("PlanB: IOCTL VIDIOCGPICT\n"); - - switch(pb->win.color_fmt) { - case PLANB_GRAY: - vp.palette = VIDEO_PALETTE_GREY; - case PLANB_COLOUR15: - vp.palette = VIDEO_PALETTE_RGB555; - break; - case PLANB_COLOUR32: - vp.palette = VIDEO_PALETTE_RGB32; - break; - default: - vp.palette = 0; - break; - } - - if(copy_to_user(arg,&vp,sizeof(vp))) - return -EFAULT; - return 0; - } - case VIDIOCSPICT: - { - struct video_picture vp; - - DEBUG("PlanB: IOCTL VIDIOCSPICT\n"); - - if(copy_from_user(&vp,arg,sizeof(vp))) - return -EFAULT; - pb->picture = vp; - /* Should we do sanity checks here? */ - saa_set (SAA7196_BRIG, (unsigned char) - ((pb->picture.brightness) >> 8), pb); - saa_set (SAA7196_HUEC, (unsigned char) - ((pb->picture.hue) >> 8) ^ 0x80, pb); - saa_set (SAA7196_CSAT, (unsigned char) - ((pb->picture.colour) >> 9), pb); - saa_set (SAA7196_CONT, (unsigned char) - ((pb->picture.contrast) >> 9), pb); - - return 0; - } - case VIDIOCSWIN: - { - struct video_window vw; - struct video_clip clip; - int i; - - DEBUG("PlanB: IOCTL VIDIOCSWIN\n"); - - if(copy_from_user(&vw,arg,sizeof(vw))) - return -EFAULT; - - planb_lock(pb); - /* Stop overlay if running */ - suspend_overlay(pb); - pb->win.interlace = (vw.height > pb->maxlines/2)? 1: 0; - if (pb->win.x != vw.x || - pb->win.y != vw.y || - pb->win.width != vw.width || - pb->win.height != vw.height || - !pb->cmd_buff_inited) { - pb->win.x = vw.x; - pb->win.y = vw.y; - pb->win.width = vw.width; - pb->win.height = vw.height; - fill_cmd_buff(pb); - } - /* Reset clip mask */ - memset ((void *) pb->mask, 0xff, (pb->maxlines - * ((PLANB_MAXPIXELS + 7) & ~7)) / 8); - /* Add any clip rects */ - for (i = 0; i < vw.clipcount; i++) { - if (copy_from_user(&clip, vw.clips + i, - sizeof(struct video_clip))) - return -EFAULT; - add_clip(pb, &clip); - } - /* restart overlay if it was running */ - resume_overlay(pb); - planb_unlock(pb); - return 0; - } - case VIDIOCGWIN: - { - struct video_window vw; - - DEBUG("PlanB: IOCTL VIDIOCGWIN\n"); - - vw.x=pb->win.x; - vw.y=pb->win.y; - vw.width=pb->win.width; - vw.height=pb->win.height; - vw.chromakey=0; - vw.flags=0; - if(pb->win.interlace) - vw.flags|=VIDEO_WINDOW_INTERLACE; - if(copy_to_user(arg,&vw,sizeof(vw))) - return -EFAULT; - return 0; - } - case VIDIOCSYNC: { - int i; - - IDEBUG("PlanB: IOCTL VIDIOCSYNC\n"); - - if(copy_from_user((void *)&i,arg,sizeof(int))) - return -EFAULT; - - IDEBUG("PlanB: sync to frame %d\n", i); - - if(i > (MAX_GBUFFERS - 1) || i < 0) - return -EINVAL; -chk_grab: - switch (pb->frame_stat[i]) { - case GBUFFER_UNUSED: - return -EINVAL; - case GBUFFER_GRABBING: - IDEBUG("PlanB: waiting for grab" - " done (%d)\n", i); - interruptible_sleep_on(&pb->capq); - if(signal_pending(current)) - return -EINTR; - goto chk_grab; - case GBUFFER_DONE: - pb->frame_stat[i] = GBUFFER_UNUSED; - break; - } - return 0; - } - - case VIDIOCMCAPTURE: - { - struct video_mmap vm; - volatile unsigned int status; - - IDEBUG("PlanB: IOCTL VIDIOCMCAPTURE\n"); - - if(copy_from_user((void *) &vm,(void *)arg,sizeof(vm))) - return -EFAULT; - status = pb->frame_stat[vm.frame]; - if (status != GBUFFER_UNUSED) - return -EBUSY; - - return vgrab(pb, &vm); - } - - case VIDIOCGMBUF: - { - int i; - struct video_mbuf vm; - - DEBUG("PlanB: IOCTL VIDIOCGMBUF\n"); - - memset(&vm, 0 , sizeof(vm)); - vm.size = PLANB_MAX_FBUF * MAX_GBUFFERS; - vm.frames = MAX_GBUFFERS; - for(i = 0; i<MAX_GBUFFERS; i++) - vm.offsets[i] = PLANB_MAX_FBUF * i; - if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) - return -EFAULT; - return 0; - } - - case PLANBIOCGSAAREGS: - { - struct planb_saa_regs preg; - - DEBUG("PlanB: IOCTL PLANBIOCGSAAREGS\n"); - - if(copy_from_user(&preg, arg, sizeof(preg))) - return -EFAULT; - if(preg.addr >= SAA7196_NUMREGS) - return -EINVAL; - preg.val = saa_regs[pb->win.norm][preg.addr]; - if(copy_to_user((void *)arg, (void *)&preg, - sizeof(preg))) - return -EFAULT; - return 0; - } - - case PLANBIOCSSAAREGS: - { - struct planb_saa_regs preg; - - DEBUG("PlanB: IOCTL PLANBIOCSSAAREGS\n"); - - if(copy_from_user(&preg, arg, sizeof(preg))) - return -EFAULT; - if(preg.addr >= SAA7196_NUMREGS) - return -EINVAL; - saa_set (preg.addr, preg.val, pb); - return 0; - } - - case PLANBIOCGSTAT: - { - struct planb_stat_regs pstat; - - DEBUG("PlanB: IOCTL PLANBIOCGSTAT\n"); - - pstat.ch1_stat = in_le32(&pb->planb_base->ch1.status); - pstat.ch2_stat = in_le32(&pb->planb_base->ch2.status); - pstat.saa_stat0 = saa_status(0, pb); - pstat.saa_stat1 = saa_status(1, pb); - - if(copy_to_user((void *)arg, (void *)&pstat, - sizeof(pstat))) - return -EFAULT; - return 0; - } - - case PLANBIOCSMODE: { - int v; - - DEBUG("PlanB: IOCTL PLANBIOCSMODE\n"); - - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - - switch(v) - { - case PLANB_TV_MODE: - saa_set (SAA7196_STDC, - (saa_regs[pb->win.norm][SAA7196_STDC] & - 0x7f), pb); - break; - case PLANB_VTR_MODE: - saa_set (SAA7196_STDC, - (saa_regs[pb->win.norm][SAA7196_STDC] | - 0x80), pb); - break; - default: - return -EINVAL; - break; - } - pb->win.mode = v; - return 0; - } - case PLANBIOCGMODE: { - int v=pb->win.mode; - - DEBUG("PlanB: IOCTL PLANBIOCGMODE\n"); - - if(copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; - return 0; - } -#ifdef PLANB_GSCANLINE - case PLANBG_GRAB_BPL: { - int v=pb->gbytes_per_line; - - DEBUG("PlanB: IOCTL PLANBG_GRAB_BPL\n"); - - if(copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; - return 0; - } -#endif /* PLANB_GSCANLINE */ - case PLANB_INTR_DEBUG: { - int i; - - DEBUG("PlanB: IOCTL PLANB_INTR_DEBUG\n"); - - if(copy_from_user(&i, arg, sizeof(i))) - return -EFAULT; - - /* avoid hang ups all together */ - for (i = 0; i < MAX_GBUFFERS; i++) { - if(pb->frame_stat[i] == GBUFFER_GRABBING) { - pb->frame_stat[i] = GBUFFER_DONE; - } - } - if(pb->grabbing) - pb->grabbing--; - wake_up_interruptible(&pb->capq); - return 0; - } - case PLANB_INV_REGS: { - int i; - struct planb_any_regs any; - - DEBUG("PlanB: IOCTL PLANB_INV_REGS\n"); - - if(copy_from_user(&any, arg, sizeof(any))) - return -EFAULT; - if(any.offset < 0 || any.offset + any.bytes > 0x400) - return -EINVAL; - if(any.bytes > 128) - return -EINVAL; - for (i = 0; i < any.bytes; i++) { - any.data[i] = - in_8((unsigned char *)pb->planb_base - + any.offset + i); - } - if(copy_to_user(arg,&any,sizeof(any))) - return -EFAULT; - return 0; - } - default: - { - DEBUG("PlanB: Unimplemented IOCTL\n"); - return -ENOIOCTLCMD; - } - /* Some IOCTLs are currently unsupported on PlanB */ - case VIDIOCGTUNER: { - DEBUG("PlanB: IOCTL VIDIOCGTUNER\n"); - goto unimplemented; } - case VIDIOCSTUNER: { - DEBUG("PlanB: IOCTL VIDIOCSTUNER\n"); - goto unimplemented; } - case VIDIOCSFREQ: { - DEBUG("PlanB: IOCTL VIDIOCSFREQ\n"); - goto unimplemented; } - case VIDIOCGFREQ: { - DEBUG("PlanB: IOCTL VIDIOCGFREQ\n"); - goto unimplemented; } - case VIDIOCKEY: { - DEBUG("PlanB: IOCTL VIDIOCKEY\n"); - goto unimplemented; } - case VIDIOCSAUDIO: { - DEBUG("PlanB: IOCTL VIDIOCSAUDIO\n"); - goto unimplemented; } - case VIDIOCGAUDIO: { - DEBUG("PlanB: IOCTL VIDIOCGAUDIO\n"); - goto unimplemented; } -unimplemented: - DEBUG(" Unimplemented\n"); - return -ENOIOCTLCMD; - } - return 0; -} - -static int planb_mmap(struct vm_area_struct *vma, struct video_device *dev, const char *adr, unsigned long size) -{ - int i; - struct planb *pb = (struct planb *)dev; - unsigned long start = (unsigned long)adr; - - if (size > MAX_GBUFFERS * PLANB_MAX_FBUF) - return -EINVAL; - if (!pb->rawbuf) { - int err; - if((err=grabbuf_alloc(pb))) - return err; - } - for (i = 0; i < pb->rawbuf_size; i++) { - unsigned long pfn; - - pfn = virt_to_phys((void *)pb->rawbuf[i]) >> PAGE_SHIFT; - if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - start += PAGE_SIZE; - if (size <= PAGE_SIZE) - break; - size -= PAGE_SIZE; - } - return 0; -} - -static struct video_device planb_template= -{ - .owner = THIS_MODULE, - .name = PLANB_DEVICE_NAME, - .type = VID_TYPE_OVERLAY, - .open = planb_open, - .close = planb_close, - .read = planb_read, - .write = planb_write, - .ioctl = planb_ioctl, - .mmap = planb_mmap, /* mmap? */ -}; - -static int init_planb(struct planb *pb) -{ - unsigned char saa_rev; - int i, result; - - memset ((void *) &pb->win, 0, sizeof (struct planb_window)); - /* Simple sanity check */ - if(def_norm >= NUM_SUPPORTED_NORM || def_norm < 0) { - printk(KERN_ERR "PlanB: Option(s) invalid\n"); - return -2; - } - pb->win.norm = def_norm; - pb->win.mode = PLANB_TV_MODE; /* TV mode */ - pb->win.interlace=1; - pb->win.x=0; - pb->win.y=0; - pb->win.width=768; /* 640 */ - pb->win.height=576; /* 480 */ - pb->maxlines=576; -#if 0 - btv->win.cropwidth=768; /* 640 */ - btv->win.cropheight=576; /* 480 */ - btv->win.cropx=0; - btv->win.cropy=0; -#endif - pb->win.pad=0; - pb->win.bpp=4; - pb->win.depth=32; - pb->win.color_fmt=PLANB_COLOUR32; - pb->win.bpl=1024*pb->win.bpp; - pb->win.swidth=1024; - pb->win.sheight=768; -#ifdef PLANB_GSCANLINE - if((pb->gbytes_per_line = PLANB_MAXPIXELS * 4) > PAGE_SIZE - || (pb->gbytes_per_line <= 0)) - return -3; - else { - /* page align pb->gbytes_per_line for DMA purpose */ - for(i = PAGE_SIZE; pb->gbytes_per_line < (i>>1);) - i>>=1; - pb->gbytes_per_line = i; - } -#endif - pb->tab_size = PLANB_MAXLINES + 40; - pb->suspend = 0; - mutex_init(&pb->lock); - pb->ch1_cmd = 0; - pb->ch2_cmd = 0; - pb->mask = 0; - pb->priv_space = 0; - pb->offset = 0; - pb->user = 0; - pb->overlay = 0; - init_waitqueue_head(&pb->suspendq); - pb->cmd_buff_inited = 0; - pb->frame_buffer_phys = 0; - - /* Reset DMA controllers */ - planb_dbdma_stop(&pb->planb_base->ch2); - planb_dbdma_stop(&pb->planb_base->ch1); - - saa_rev = (saa_status(0, pb) & 0xf0) >> 4; - printk(KERN_INFO "PlanB: SAA7196 video processor rev. %d\n", saa_rev); - /* Initialize the SAA registers in memory and on chip */ - saa_init_regs (pb); - - /* clear interrupt mask */ - pb->intr_mask = PLANB_CLR_IRQ; - - result = request_irq(pb->irq, planb_irq, 0, "PlanB", pb); - if (result < 0) { - if (result==-EINVAL) - printk(KERN_ERR "PlanB: Bad irq number (%d) " - "or handler\n", (int)pb->irq); - else if (result==-EBUSY) - printk(KERN_ERR "PlanB: I don't know why, " - "but IRQ %d is busy\n", (int)pb->irq); - return result; - } - disable_irq(pb->irq); - - /* Now add the template and register the device unit. */ - memcpy(&pb->video_dev,&planb_template,sizeof(planb_template)); - - pb->picture.brightness=0x90<<8; - pb->picture.contrast = 0x70 << 8; - pb->picture.colour = 0x70<<8; - pb->picture.hue = 0x8000; - pb->picture.whiteness = 0; - pb->picture.depth = pb->win.depth; - - pb->frame_stat=NULL; - init_waitqueue_head(&pb->capq); - for(i=0; i<MAX_GBUFFERS; i++) { - pb->gbuf_idx[i] = PLANB_MAX_FBUF * i / PAGE_SIZE; - pb->gwidth[i]=0; - pb->gheight[i]=0; - pb->gfmt[i]=0; - pb->cap_cmd[i]=NULL; -#ifndef PLANB_GSCANLINE - pb->l_fr_addr_idx[i] = MAX_GBUFFERS * (PLANB_MAX_FBUF - / PAGE_SIZE + 1) + MAX_LNUM * i; - pb->lsize[i] = 0; - pb->lnum[i] = 0; -#endif - } - pb->rawbuf=NULL; - pb->grabbing=0; - - /* enable interrupts */ - out_le32(&pb->planb_base->intr_stat, PLANB_CLR_IRQ); - pb->intr_mask = PLANB_FRM_IRQ; - enable_irq(pb->irq); - - if(video_register_device(&pb->video_dev, VFL_TYPE_GRABBER, video_nr)<0) - return -1; - - return 0; -} - -/* - * Scan for a PlanB controller, request the irq and map the io memory - */ - -static int find_planb(void) -{ - struct planb *pb; - struct device_node *planb_devices; - unsigned char dev_fn, confreg, bus; - unsigned int old_base, new_base; - unsigned int irq; - struct pci_dev *pdev; - int rc; - - if (!machine_is(powermac)) - return 0; - - planb_devices = of_find_node_by_name(NULL, "planb"); - if (planb_devices == 0) { - planb_num=0; - printk(KERN_WARNING "PlanB: no device found!\n"); - return planb_num; - } - - if (planb_devices->next != NULL) - printk(KERN_ERR "Warning: only using first PlanB device!\n"); - pb = &planbs[0]; - planb_num = 1; - - 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; - } - - /* Initialize PlanB's PCI registers */ - - /* There is a bug with the way OF assigns addresses - to the devices behind the chaos bridge. - control needs only 0x1000 of space, but decodes only - the upper 16 bits. It therefore occupies a full 64K. - OF assigns the planb controller memory within this space; - so we need to change that here in order to access planb. */ - - /* We remap to 0xf1000000 in hope that nobody uses it ! */ - - bus = (planb_devices->addrs[0].space >> 16) & 0xff; - dev_fn = (planb_devices->addrs[0].space >> 8) & 0xff; - 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_get_bus_and_slot(bus, dev_fn); - if (!pdev) { - printk(KERN_ERR "planb: cannot find slot\n"); - goto err_out; - } - - /* Enable response in memory space, bus mastering, - use memory write and invalidate */ - rc = pci_enable_device(pdev); - if (rc) { - printk(KERN_ERR "planb: cannot enable PCI device %s\n", - pci_name(pdev)); - goto err_out; - } - rc = pci_set_mwi(pdev); - if (rc) { - printk(KERN_ERR "planb: cannot enable MWI on PCI device %s\n", - pci_name(pdev)); - goto err_out_disable; - } - pci_set_master(pdev); - - /* Set the new base address */ - pci_write_config_dword (pdev, confreg, new_base); - - planb_regs = (volatile struct planb_registers *) - ioremap (new_base, 0x400); - pb->planb_base = planb_regs; - pb->planb_base_phys = (struct planb_registers *)new_base; - pb->irq = irq; - pb->dev = pdev; - - return planb_num; - -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; -} - -static void release_planb(void) -{ - int i; - struct planb *pb; - - for (i=0;i<planb_num; i++) - { - pb=&planbs[i]; - - /* stop and flash DMAs unconditionally */ - planb_dbdma_stop(&pb->planb_base->ch2); - planb_dbdma_stop(&pb->planb_base->ch1); - - /* clear and free interrupts */ - pb->intr_mask = PLANB_CLR_IRQ; - out_le32 (&pb->planb_base->intr_stat, PLANB_CLR_IRQ); - free_irq(pb->irq, pb); - - /* make sure all allocated memory are freed */ - planb_prepare_close(pb); - - 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); - } -} - -static int __init init_planbs(void) -{ - int i; - - if (find_planb()<=0) - return -EIO; - - for (i=0; i<planb_num; i++) { - if (init_planb(&planbs[i])<0) { - printk(KERN_ERR "PlanB: error registering device %d" - " with v4l\n", i); - release_planb(); - return -EIO; - } - printk(KERN_INFO "PlanB: registered device %d with v4l\n", i); - } - return 0; -} - -static void __exit exit_planbs(void) -{ - release_planb(); -} - -module_init(init_planbs); -module_exit(exit_planbs); diff --git a/drivers/media/video/planb.h b/drivers/media/video/planb.h index e21b5735c103..e69de29bb2d1 100644 --- a/drivers/media/video/planb.h +++ b/drivers/media/video/planb.h @@ -1,232 +0,0 @@ -/* - planb - PlanB frame grabber driver - - PlanB is used in the 7x00/8x00 series of PowerMacintosh - Computers as video input DMA controller. - - Copyright (C) 1998 Michel Lanners (mlan@cpu.lu) - - Based largely on the bttv driver by Ralph Metzler (rjkm@thp.uni-koeln.de) - - Additional debugging and coding by Takashi Oe (toe@unlserve.unl.edu) - - - 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. -*/ - -/* $Id: planb.h,v 1.13 1999/05/03 19:28:56 mlan Exp $ */ - -#ifndef _PLANB_H_ -#define _PLANB_H_ - -#ifdef __KERNEL__ -#include <asm/dbdma.h> -#include "saa7196.h" -#endif /* __KERNEL__ */ - -#define PLANB_DEVICE_NAME "Apple PlanB Video-In" -#define PLANB_REV "1.0" - -#ifdef __KERNEL__ -//#define PLANB_GSCANLINE /* use this if apps have the notion of */ - /* grab buffer scanline */ -/* This should be safe for both PAL and NTSC */ -#define PLANB_MAXPIXELS 768 -#define PLANB_MAXLINES 576 -#define PLANB_NTSC_MAXLINES 480 - -/* Uncomment your preferred norm ;-) */ -#define PLANB_DEF_NORM VIDEO_MODE_PAL -//#define PLANB_DEF_NORM VIDEO_MODE_NTSC -//#define PLANB_DEF_NORM VIDEO_MODE_SECAM - -/* fields settings */ -#define PLANB_GRAY 0x1 /* 8-bit mono? */ -#define PLANB_COLOUR15 0x2 /* 16-bit mode */ -#define PLANB_COLOUR32 0x4 /* 32-bit mode */ -#define PLANB_CLIPMASK 0x8 /* hardware clipmasking */ - -/* misc. flags for PlanB DMA operation */ -#define CH_SYNC 0x1 /* synchronize channels (set by ch1; - cleared by ch2) */ -#define FIELD_SYNC 0x2 /* used for the start of each field - (0 -> 1 -> 0 for ch1; 0 -> 1 for ch2) */ -#define EVEN_FIELD 0x0 /* even field is detected if unset */ -#define DMA_ABORT 0x2 /* error or just out of sync if set */ -#define ODD_FIELD 0x4 /* odd field is detected if set */ - -/* for capture operations */ -#define MAX_GBUFFERS 2 -/* note PLANB_MAX_FBUF must be divisible by PAGE_SIZE */ -#ifdef PLANB_GSCANLINE -#define PLANB_MAX_FBUF 0x240000 /* 576 * 1024 * 4 */ -#define TAB_FACTOR (1) -#else -#define PLANB_MAX_FBUF 0x1b0000 /* 576 * 768 * 4 */ -#define TAB_FACTOR (2) -#endif -#endif /* __KERNEL__ */ - -struct planb_saa_regs { - unsigned char addr; - unsigned char val; -}; - -struct planb_stat_regs { - unsigned int ch1_stat; - unsigned int ch2_stat; - unsigned char saa_stat0; - unsigned char saa_stat1; -}; - -struct planb_any_regs { - unsigned int offset; - unsigned int bytes; - unsigned char data[128]; -}; - -/* planb private ioctls */ -#define PLANBIOCGSAAREGS _IOWR('v', BASE_VIDIOCPRIVATE, struct planb_saa_regs) /* Read a saa7196 reg value */ -#define PLANBIOCSSAAREGS _IOW('v', BASE_VIDIOCPRIVATE + 1, struct planb_saa_regs) /* Set a saa7196 reg value */ -#define PLANBIOCGSTAT _IOR('v', BASE_VIDIOCPRIVATE + 2, struct planb_stat_regs) /* Read planb status */ -#define PLANB_TV_MODE 1 -#define PLANB_VTR_MODE 2 -#define PLANBIOCGMODE _IOR('v', BASE_VIDIOCPRIVATE + 3, int) /* Get TV/VTR mode */ -#define PLANBIOCSMODE _IOW('v', BASE_VIDIOCPRIVATE + 4, int) /* Set TV/VTR mode */ - -#ifdef PLANB_GSCANLINE -#define PLANBG_GRAB_BPL _IOR('v', BASE_VIDIOCPRIVATE + 5, int) /* # of bytes per scanline in grab buffer */ -#endif - -/* call wake_up_interruptible() with appropriate actions */ -#define PLANB_INTR_DEBUG _IOW('v', BASE_VIDIOCPRIVATE + 20, int) -/* investigate which reg does what */ -#define PLANB_INV_REGS _IOWR('v', BASE_VIDIOCPRIVATE + 21, struct planb_any_regs) - -#ifdef __KERNEL__ - -/* Potentially useful macros */ -#define PLANB_SET(x) ((x) << 16 | (x)) -#define PLANB_CLR(x) ((x) << 16) - -/* This represents the physical register layout */ -struct planb_registers { - volatile struct dbdma_regs ch1; /* 0x00: video in */ - volatile unsigned int even; /* 0x40: even field setting */ - volatile unsigned int odd; /* 0x44; odd field setting */ - unsigned int pad1[14]; /* empty? */ - volatile struct dbdma_regs ch2; /* 0x80: clipmask out */ - unsigned int pad2[16]; /* 0xc0: empty? */ - volatile unsigned int reg3; /* 0x100: ???? */ - volatile unsigned int intr_stat; /* 0x104: irq status */ -#define PLANB_CLR_IRQ 0x00 /* clear Plan B interrupt */ -#define PLANB_GEN_IRQ 0x01 /* assert Plan B interrupt */ -#define PLANB_FRM_IRQ 0x0100 /* end of frame */ - unsigned int pad3[1]; /* empty? */ - volatile unsigned int reg5; /* 0x10c: ??? */ - unsigned int pad4[60]; /* empty? */ - volatile unsigned char saa_addr; /* 0x200: SAA subadr */ - char pad5[3]; - volatile unsigned char saa_regval; /* SAA7196 write reg. val */ - char pad6[3]; - volatile unsigned char saa_status; /* SAA7196 status byte */ - /* There is more unused stuff here */ -}; - -struct planb_window { - int x, y; - ushort width, height; - ushort bpp, bpl, depth, pad; - ushort swidth, sheight; - int norm; - int interlace; - u32 color_fmt; - int chromakey; - int mode; /* used to switch between TV/VTR modes */ -}; - -struct planb_suspend { - int overlay; - int frame; - struct dbdma_cmd cmd; -}; - -struct planb { - struct video_device video_dev; - struct video_picture picture; /* Current picture params */ - struct video_audio audio_dev; /* Current audio params */ - - volatile struct planb_registers *planb_base; /* virt base of planb */ - struct planb_registers *planb_base_phys; /* phys base of planb */ - void *priv_space; /* Org. alloc. mem for kfree */ - int user; - unsigned int tab_size; - int maxlines; - 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; - unsigned long frame_buffer_phys; /* We need phys for DMA */ - int offset; /* offset of pixel 1 */ - volatile struct dbdma_cmd *ch1_cmd; /* Video In DMA cmd buffer */ - volatile struct dbdma_cmd *ch2_cmd; /* Clip Out DMA cmd buffer */ - volatile struct dbdma_cmd *overlay_last1; - volatile struct dbdma_cmd *overlay_last2; - unsigned long ch1_cmd_phys; - volatile unsigned char *mask; /* Clipmask buffer */ - int suspend; - wait_queue_head_t suspendq; - struct planb_suspend suspended; - int cmd_buff_inited; /* cmd buffer inited? */ - - int grabbing; - unsigned int gcount; - wait_queue_head_t capq; - int last_fr; - int prev_last_fr; - unsigned char **rawbuf; - int rawbuf_size; - int gbuf_idx[MAX_GBUFFERS]; - volatile struct dbdma_cmd *cap_cmd[MAX_GBUFFERS]; - volatile struct dbdma_cmd *last_cmd[MAX_GBUFFERS]; - volatile struct dbdma_cmd *pre_cmd[MAX_GBUFFERS]; - int need_pre_capture[MAX_GBUFFERS]; -#define PLANB_DUMMY 40 /* # of command buf's allocated for pre-capture seq. */ - int gwidth[MAX_GBUFFERS], gheight[MAX_GBUFFERS]; - unsigned int gfmt[MAX_GBUFFERS]; - int gnorm_switch[MAX_GBUFFERS]; - volatile unsigned int *frame_stat; -#define GBUFFER_UNUSED 0x00U -#define GBUFFER_GRABBING 0x01U -#define GBUFFER_DONE 0x02U -#ifdef PLANB_GSCANLINE - int gbytes_per_line; -#else -#define MAX_LNUM 431 /* change this if PLANB_MAXLINES or */ - /* PLANB_MAXPIXELS changes */ - int l_fr_addr_idx[MAX_GBUFFERS]; - unsigned char *l_to_addr[MAX_GBUFFERS][MAX_LNUM]; - int l_to_next_idx[MAX_GBUFFERS][MAX_LNUM]; - int l_to_next_size[MAX_GBUFFERS][MAX_LNUM]; - int lsize[MAX_GBUFFERS], lnum[MAX_GBUFFERS]; -#endif -}; - -#endif /* __KERNEL__ */ - -#endif /* _PLANB_H_ */ diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c index 51b1461d8fb6..00425d743656 100644 --- a/drivers/media/video/pms.c +++ b/drivers/media/video/pms.c @@ -30,6 +30,7 @@ #include <asm/io.h> #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/mutex.h> #include <asm/uaccess.h> @@ -894,9 +895,7 @@ static const struct file_operations pms_fops = { static struct video_device pms_template= { - .owner = THIS_MODULE, .name = "Mediavision PMS", - .type = VID_TYPE_CAPTURE, .fops = &pms_fops, }; diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig index 4482b2c72ced..19eb274c9cd0 100644 --- a/drivers/media/video/pvrusb2/Kconfig +++ b/drivers/media/video/pvrusb2/Kconfig @@ -2,8 +2,6 @@ config VIDEO_PVRUSB2 tristate "Hauppauge WinTV-PVR USB2 support" depends on VIDEO_V4L2 && I2C depends on VIDEO_MEDIA # Avoids pvrusb = Y / DVB = M - depends on HOTPLUG # due to FW_LOADER - select FW_LOADER select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_CX2341X diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.h b/drivers/media/video/pvrusb2/pvrusb2-context.h index 61801291c2af..d657e53bbfa3 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-context.h +++ b/drivers/media/video/pvrusb2/pvrusb2-context.h @@ -16,8 +16,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#ifndef __PVRUSB2_BASE_H -#define __PVRUSB2_BASE_H +#ifndef __PVRUSB2_CONTEXT_H +#define __PVRUSB2_CONTEXT_H #include <linux/mutex.h> #include <linux/usb.h> diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c index 5d036e7e3f07..88e175168438 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -97,13 +97,13 @@ static const struct pvr2_device_desc pvr2_device_24xxx = { .flag_has_cx25840 = !0, .flag_has_wm8775 = !0, .flag_has_hauppauge_rom = !0, - .flag_has_hauppauge_custom_ir = !0, .flag_has_analogtuner = !0, .flag_has_fmradio = !0, .flag_has_composite = !0, .flag_has_svideo = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, + .ir_scheme = PVR2_IR_SCHEME_24XXX, }; @@ -330,7 +330,7 @@ static const char *pvr2_fw1_names_73xxx[] = { }; static const struct pvr2_device_desc pvr2_device_73xxx = { - .description = "WinTV PVR USB2 Model Category 73xxx", + .description = "WinTV HVR-1900 Model Category 73xxx", .shortname = "73xxx", .client_modules.lst = pvr2_client_73xxx, .client_modules.cnt = ARRAY_SIZE(pvr2_client_73xxx), @@ -344,6 +344,7 @@ static const struct pvr2_device_desc pvr2_device_73xxx = { .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, + .ir_scheme = PVR2_IR_SCHEME_ZILOG, #ifdef CONFIG_VIDEO_PVRUSB2_DVB .dvb_props = &pvr2_73xxx_dvb_props, #endif @@ -438,7 +439,7 @@ static const char *pvr2_fw1_names_75xxx[] = { }; static const struct pvr2_device_desc pvr2_device_750xx = { - .description = "WinTV PVR USB2 Model Category 750xx", + .description = "WinTV HVR-1950 Model Category 750xx", .shortname = "750xx", .client_modules.lst = pvr2_client_75xxx, .client_modules.cnt = ARRAY_SIZE(pvr2_client_75xxx), @@ -453,13 +454,14 @@ static const struct pvr2_device_desc pvr2_device_750xx = { .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, .default_std_mask = V4L2_STD_NTSC_M, .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, + .ir_scheme = PVR2_IR_SCHEME_ZILOG, #ifdef CONFIG_VIDEO_PVRUSB2_DVB .dvb_props = &pvr2_750xx_dvb_props, #endif }; static const struct pvr2_device_desc pvr2_device_751xx = { - .description = "WinTV PVR USB2 Model Category 751xx", + .description = "WinTV HVR-1950 Model Category 751xx", .shortname = "751xx", .client_modules.lst = pvr2_client_75xxx, .client_modules.cnt = ARRAY_SIZE(pvr2_client_75xxx), @@ -474,6 +476,7 @@ static const struct pvr2_device_desc pvr2_device_751xx = { .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, .default_std_mask = V4L2_STD_NTSC_M, .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, + .ir_scheme = PVR2_IR_SCHEME_ZILOG, #ifdef CONFIG_VIDEO_PVRUSB2_DVB .dvb_props = &pvr2_751xx_dvb_props, #endif diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/drivers/media/video/pvrusb2/pvrusb2-devattr.h index e23ce1d2edd7..cb3a33eb0276 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.h +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -48,6 +48,10 @@ struct pvr2_string_table { #define PVR2_LED_SCHEME_NONE 0 #define PVR2_LED_SCHEME_HAUPPAUGE 1 +#define PVR2_IR_SCHEME_NONE 0 +#define PVR2_IR_SCHEME_24XXX 1 +#define PVR2_IR_SCHEME_ZILOG 2 + /* This describes a particular hardware type (except for the USB device ID which must live in a separate structure due to environmental constraints). See the top of pvrusb2-hdw.c for where this is @@ -126,15 +130,19 @@ struct pvr2_device_desc { ensure that it is found. */ unsigned int flag_has_wm8775:1; - /* Device has IR hardware that can be faked into looking like a - normal Hauppauge i2c IR receiver. This is currently very - specific to the 24xxx device, where Hauppauge had replaced their - 'standard' I2C IR receiver with a bunch of FPGA logic controlled - directly via the FX2. Turning this on tells the pvrusb2 driver - to virtualize the presence of the non-existant IR receiver chip and - implement the virtual receiver in terms of appropriate FX2 - commands. */ - unsigned int flag_has_hauppauge_custom_ir:1; + /* Indicate any specialized IR scheme that might need to be + supported by this driver. If not set, then it is assumed that + IR can work without help from the driver (which is frequently + the case). This is otherwise set to one of + PVR2_IR_SCHEME_xxxx. For "xxxx", the value "24XXX" indicates a + Hauppauge 24xxx class device which has an FPGA-hosted IR + receiver that can only be reached via FX2 command codes. In + that case the pvrusb2 driver will emulate the behavior of the + older 29xxx device's IR receiver (a "virtual" I2C chip) in terms + of those command codes. For the value "ZILOG", we're dealing + with an IR chip that must be taken out of reset via another FX2 + command code (which is the case for HVR-1950 devices). */ + unsigned int ir_scheme:2; /* These bits define which kinds of sources the device can handle. Note: Digital tuner presence is inferred by the diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.c b/drivers/media/video/pvrusb2/pvrusb2-dvb.c index 6ec4bf81fc7f..77b3c3385066 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-dvb.c +++ b/drivers/media/video/pvrusb2/pvrusb2-dvb.c @@ -20,6 +20,7 @@ #include <linux/kthread.h> #include <linux/freezer.h> +#include <linux/mm.h> #include "dvbdev.h" #include "pvrusb2-debug.h" #include "pvrusb2-hdw-internal.h" diff --git a/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h b/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h index b58369e7f30b..614755ea2ea3 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h +++ b/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h @@ -24,6 +24,8 @@ #define FX2CMD_MEM_WRITE_DWORD 0x01u #define FX2CMD_MEM_READ_DWORD 0x02u +#define FX2CMD_HCW_ZILOG_RESET 0x10u /* 1=reset 0=release */ + #define FX2CMD_MEM_READ_64BYTES 0x28u #define FX2CMD_REG_WRITE 0x04u diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index a5217a2cf4c0..f051c6aa7f1f 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -250,6 +250,7 @@ struct pvr2_fx2cmd_descdef { static const struct pvr2_fx2cmd_descdef pvr2_fx2cmd_desc[] = { {FX2CMD_MEM_WRITE_DWORD, "write encoder dword"}, {FX2CMD_MEM_READ_DWORD, "read encoder dword"}, + {FX2CMD_HCW_ZILOG_RESET, "zilog IR reset control"}, {FX2CMD_MEM_READ_64BYTES, "read encoder 64bytes"}, {FX2CMD_REG_WRITE, "write encoder register"}, {FX2CMD_REG_READ, "read encoder register"}, @@ -1711,6 +1712,14 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) if (!pvr2_hdw_dev_ok(hdw)) return; } + /* Take the IR chip out of reset, if appropriate */ + if (hdw->hdw_desc->ir_scheme == PVR2_IR_SCHEME_ZILOG) { + pvr2_issue_simple_cmd(hdw, + FX2CMD_HCW_ZILOG_RESET | + (1 << 8) | + ((0) << 16)); + } + // This step MUST happen after the earlier powerup step. pvr2_i2c_core_init(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 9d3c18b24744..e600576a6c4b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -979,7 +979,9 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) printk(KERN_INFO "%s: IR disabled\n",hdw->name); hdw->i2c_func[0x18] = i2c_black_hole; } else if (ir_mode[hdw->unit_number] == 1) { - if (hdw->hdw_desc->flag_has_hauppauge_custom_ir) { + if (hdw->hdw_desc->ir_scheme == PVR2_IR_SCHEME_24XXX) { + /* This comment is present PURELY to get + checkpatch.pl to STFU. Lovely, eh? */ hdw->i2c_func[0x18] = i2c_24xxx_ir; } } diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/drivers/media/video/pvrusb2/pvrusb2-ioread.c index 05a1376405e7..b4824782d858 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-ioread.c +++ b/drivers/media/video/pvrusb2/pvrusb2-ioread.c @@ -22,6 +22,7 @@ #include "pvrusb2-debug.h" #include <linux/errno.h> #include <linux/string.h> +#include <linux/mm.h> #include <linux/slab.h> #include <linux/mutex.h> #include <asm/uaccess.h> diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 0d72dc470fef..00306faeac01 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -30,6 +30,7 @@ #include <linux/videodev2.h> #include <media/v4l2-dev.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> struct pvr2_v4l2_dev; struct pvr2_v4l2_fh; @@ -1160,11 +1161,6 @@ static const struct file_operations vdev_fops = { static struct video_device vdev_template = { - .owner = THIS_MODULE, - .type = VID_TYPE_CAPTURE | VID_TYPE_TUNER, - .type2 = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE - | V4L2_CAP_TUNER | V4L2_CAP_AUDIO - | V4L2_CAP_READWRITE), .fops = &vdev_fops, }; diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 423fa7c2d0c9..9aee7cb6f79a 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -165,9 +165,7 @@ static const struct file_operations pwc_fops = { .llseek = no_llseek, }; static struct video_device pwc_template = { - .owner = THIS_MODULE, .name = "Philips Webcam", /* Filled in later */ - .type = VID_TYPE_CAPTURE, .release = video_device_release, .fops = &pwc_fops, .minor = -1, @@ -1048,19 +1046,20 @@ static int pwc_create_sysfs_files(struct video_device *vdev) struct pwc_device *pdev = video_get_drvdata(vdev); int rc; - rc = video_device_create_file(vdev, &dev_attr_button); + rc = device_create_file(&vdev->dev, &dev_attr_button); if (rc) goto err; if (pdev->features & FEATURE_MOTOR_PANTILT) { - rc = video_device_create_file(vdev, &dev_attr_pan_tilt); + rc = device_create_file(&vdev->dev, &dev_attr_pan_tilt); if (rc) goto err_button; } return 0; err_button: - video_device_remove_file(vdev, &dev_attr_button); + device_remove_file(&vdev->dev, &dev_attr_button); err: + PWC_ERROR("Could not create sysfs files.\n"); return rc; } @@ -1068,8 +1067,8 @@ static void pwc_remove_sysfs_files(struct video_device *vdev) { struct pwc_device *pdev = video_get_drvdata(vdev); if (pdev->features & FEATURE_MOTOR_PANTILT) - video_device_remove_file(vdev, &dev_attr_pan_tilt); - video_device_remove_file(vdev, &dev_attr_button); + device_remove_file(&vdev->dev, &dev_attr_pan_tilt); + device_remove_file(&vdev->dev, &dev_attr_button); } #ifdef CONFIG_USB_PWC_DEBUG @@ -1767,9 +1766,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id return -ENOMEM; } memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template)); - pdev->vdev->dev = &(udev->dev); + pdev->vdev->parent = &(udev->dev); strcpy(pdev->vdev->name, name); - pdev->vdev->owner = THIS_MODULE; video_set_drvdata(pdev->vdev, pdev); pdev->release = le16_to_cpu(udev->descriptor.bcdDevice); diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index 8e8e5b27e77e..74178754b39b 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -32,9 +32,11 @@ #include <linux/smp_lock.h> #include <linux/version.h> #include <linux/mutex.h> +#include <linux/mm.h> #include <asm/errno.h> #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include "pwc-uncompress.h" #include <media/pwc-ioctl.h> diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index 04eb2c3fabd8..b1d09d8e2b85 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -47,8 +47,10 @@ #include <linux/mutex.h> #include <linux/videodev2.h> #include <linux/version.h> +#include <linux/mm.h> #include <media/videobuf-vmalloc.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/vmalloc.h> #include <linux/usb.h> @@ -184,6 +186,7 @@ struct s2255_dmaqueue { #define S2255_FW_LOADED_DSPWAIT 1 #define S2255_FW_SUCCESS 2 #define S2255_FW_FAILED 3 +#define S2255_FW_DISCONNECTING 4 struct s2255_fw { int fw_loaded; @@ -263,7 +266,6 @@ struct s2255_buffer { struct s2255_fh { struct s2255_dev *dev; - unsigned int resources; const struct s2255_fmt *fmt; unsigned int width; unsigned int height; @@ -273,14 +275,9 @@ struct s2255_fh { /* mode below is the desired mode. mode in s2255_dev is the current mode that was last set */ struct s2255_mode mode; + int resources[MAX_CHANNELS]; }; -/* - * TODO: fixme S2255_MAX_USERS. Do not limit open driver handles. - * Limit V4L to one stream at a time. - */ -#define S2255_MAX_USERS 1 - #define CUR_USB_FWVER 774 /* current cypress EEPROM firmware version */ #define S2255_MAJOR_VERSION 1 #define S2255_MINOR_VERSION 13 @@ -476,10 +473,9 @@ static void s2255_timer(unsigned long user_data) dprintk(100, "s2255 timer\n"); if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) { printk(KERN_ERR "s2255: can't submit urb\n"); - if (data->fw) { - release_firmware(data->fw); - data->fw = NULL; - } + atomic_set(&data->fw_state, S2255_FW_FAILED); + /* wake up anything waiting for the firmware */ + wake_up(&data->wait_fw); return; } } @@ -509,13 +505,18 @@ static void s2255_fwchunk_complete(struct urb *urb) struct usb_device *udev = urb->dev; int len; dprintk(100, "udev %p urb %p", udev, urb); - /* TODO: fixme. reflect change in status */ if (urb->status) { dev_err(&udev->dev, "URB failed with status %d", urb->status); + atomic_set(&data->fw_state, S2255_FW_FAILED); + /* wake up anything waiting for the firmware */ + wake_up(&data->wait_fw); return; } if (data->fw_urb == NULL) { - dev_err(&udev->dev, "early disconncect\n"); + dev_err(&udev->dev, "s2255 disconnected\n"); + atomic_set(&data->fw_state, S2255_FW_FAILED); + /* wake up anything waiting for the firmware */ + wake_up(&data->wait_fw); return; } #define CHUNK_SIZE 512 @@ -789,7 +790,8 @@ static int res_get(struct s2255_dev *dev, struct s2255_fh *fh) } /* it's free, grab it */ dev->resources[fh->channel] = 1; - dprintk(1, "res: get\n"); + fh->resources[fh->channel] = 1; + dprintk(1, "s2255: res: get\n"); mutex_unlock(&dev->lock); return 1; } @@ -799,9 +801,18 @@ static int res_locked(struct s2255_dev *dev, struct s2255_fh *fh) return dev->resources[fh->channel]; } +static int res_check(struct s2255_fh *fh) +{ + return fh->resources[fh->channel]; +} + + static void res_free(struct s2255_dev *dev, struct s2255_fh *fh) { + mutex_lock(&dev->lock); dev->resources[fh->channel] = 0; + fh->resources[fh->channel] = 0; + mutex_unlock(&dev->lock); dprintk(1, "res: put\n"); } @@ -1232,7 +1243,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) } if (!res_get(dev, fh)) { - dev_err(&dev->udev->dev, "res get busy\n"); + dev_err(&dev->udev->dev, "s2255: stream busy\n"); return -EBUSY; } @@ -1288,8 +1299,10 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) } s2255_stop_acquire(dev, fh->channel); res = videobuf_streamoff(&fh->vb_vidq); + if (res < 0) + return res; res_free(dev, fh); - return res; + return 0; } static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) @@ -1462,12 +1475,7 @@ static int s2255_open(struct inode *inode, struct file *file) mutex_lock(&dev->open_lock); dev->users[cur_channel]++; - if (dev->users[cur_channel] > S2255_MAX_USERS) { - dev->users[cur_channel]--; - mutex_unlock(&dev->open_lock); - printk(KERN_INFO "s2255drv: too many open handles!\n"); - return -EBUSY; - } + dprintk(4, "s2255: open_handles %d\n", dev->users[cur_channel]); if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_FAILED) { err("2255 firmware load failed. retrying.\n"); @@ -1478,7 +1486,8 @@ static int s2255_open(struct inode *inode, struct file *file) msecs_to_jiffies(S2255_LOAD_TIMEOUT)); if (atomic_read(&dev->fw_data->fw_state) != S2255_FW_SUCCESS) { - printk(KERN_INFO "2255 FW load failed after 2 tries\n"); + printk(KERN_INFO "2255 FW load failed.\n"); + dev->users[cur_channel]--; mutex_unlock(&dev->open_lock); return -EFAULT; } @@ -1494,6 +1503,7 @@ static int s2255_open(struct inode *inode, struct file *file) != S2255_FW_SUCCESS) { printk(KERN_INFO "2255 firmware not loaded" "try again\n"); + dev->users[cur_channel]--; mutex_unlock(&dev->open_lock); return -EBUSY; } @@ -1502,6 +1512,7 @@ static int s2255_open(struct inode *inode, struct file *file) /* allocate + initialize per filehandle data */ fh = kzalloc(sizeof(*fh), GFP_KERNEL); if (NULL == fh) { + dev->users[cur_channel]--; mutex_unlock(&dev->open_lock); return -ENOMEM; } @@ -1561,44 +1572,48 @@ static void s2255_destroy(struct kref *kref) printk(KERN_ERR "s2255drv: kref problem\n"); return; } + + /* + * Wake up any firmware load waiting (only done in .open, + * which holds the open_lock mutex) + */ + atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING); + wake_up(&dev->fw_data->wait_fw); + /* prevent s2255_disconnect from racing s2255_open */ mutex_lock(&dev->open_lock); s2255_exit_v4l(dev); - /* device unregistered so no longer possible to open. open_mutex - can be unlocked */ + /* + * device unregistered so no longer possible to open. open_mutex + * can be unlocked and timers deleted afterwards. + */ mutex_unlock(&dev->open_lock); /* board shutdown stops the read pipe if it is running */ s2255_board_shutdown(dev); /* make sure firmware still not trying to load */ + del_timer(&dev->timer); /* only started in .probe and .open */ + if (dev->fw_data->fw_urb) { dprintk(2, "kill fw_urb\n"); usb_kill_urb(dev->fw_data->fw_urb); usb_free_urb(dev->fw_data->fw_urb); dev->fw_data->fw_urb = NULL; } + /* - * TODO: fixme(above, below): potentially leaving timers alive. - * do not ignore timeout below if - * it occurs. + * delete the dsp_wait timer, which sets the firmware + * state on completion. This is done before fw_data + * is freed below. */ - /* make sure we aren't waiting for the DSP */ - if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_LOADED_DSPWAIT) { - /* if we are, wait for the wakeup for fw_success or timeout */ - wait_event_timeout(dev->fw_data->wait_fw, - (atomic_read(&dev->fw_data->fw_state) - == S2255_FW_SUCCESS), - msecs_to_jiffies(S2255_LOAD_TIMEOUT)); - } + del_timer(&dev->fw_data->dsp_wait); /* only started in .open */ - if (dev->fw_data) { - if (dev->fw_data->fw) - release_firmware(dev->fw_data->fw); - kfree(dev->fw_data->pfw_data); - kfree(dev->fw_data); - } + if (dev->fw_data->fw) + release_firmware(dev->fw_data->fw); + kfree(dev->fw_data->pfw_data); + kfree(dev->fw_data); usb_put_dev(dev->udev); dprintk(1, "%s", __func__); @@ -1615,17 +1630,23 @@ static int s2255_close(struct inode *inode, struct file *file) mutex_lock(&dev->open_lock); - if (dev->b_acquire[fh->channel]) - s2255_stop_acquire(dev, fh->channel); - res_free(dev, fh); + /* turn off stream */ + if (res_check(fh)) { + if (dev->b_acquire[fh->channel]) + s2255_stop_acquire(dev, fh->channel); + videobuf_streamoff(&fh->vb_vidq); + res_free(dev, fh); + } + videobuf_mmap_free(&fh->vb_vidq); - kfree(fh); dev->users[fh->channel]--; + mutex_unlock(&dev->open_lock); kref_put(&dev->kref, s2255_destroy); dprintk(1, "s2255: close called (minor=%d, users=%d)\n", minor, dev->users[fh->channel]); + kfree(fh); return 0; } @@ -1658,12 +1679,7 @@ static const struct file_operations s2255_fops_v4l = { .llseek = no_llseek, }; -static struct video_device template = { - .name = "s2255v", - .type = VID_TYPE_CAPTURE, - .fops = &s2255_fops_v4l, - .minor = -1, - .release = video_device_release, +static const struct v4l2_ioctl_ops s2255_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, @@ -1685,6 +1701,14 @@ static struct video_device template = { #ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidioc_cgmbuf, #endif +}; + +static struct video_device template = { + .name = "s2255v", + .fops = &s2255_fops_v4l, + .ioctl_ops = &s2255_ioctl_ops, + .minor = -1, + .release = video_device_release, .tvnorms = S2255_NORMS, .current_norm = V4L2_STD_NTSC_M, }; @@ -1706,7 +1730,7 @@ static int s2255_probe_v4l(struct s2255_dev *dev) /* register 4 video devices */ dev->vdev[i] = video_device_alloc(); memcpy(dev->vdev[i], &template, sizeof(struct video_device)); - dev->vdev[i]->dev = &dev->interface->dev; + dev->vdev[i]->parent = &dev->interface->dev; if (video_nr == -1) ret = video_register_device(dev->vdev[i], VFL_TYPE_GRABBER, diff --git a/drivers/media/video/saa5246a.c b/drivers/media/video/saa5246a.c index 03e772130b55..6ee63e69b36c 100644 --- a/drivers/media/video/saa5246a.c +++ b/drivers/media/video/saa5246a.c @@ -46,6 +46,7 @@ #include <linux/videotext.h> #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/mutex.h> #include "saa5246a.h" @@ -829,9 +830,7 @@ static const struct file_operations saa_fops = { static struct video_device saa_template = { - .owner = THIS_MODULE, .name = IF_NAME, - .type = VID_TYPE_TELETEXT, .fops = &saa_fops, .release = video_device_release, .minor = -1, diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c index fde99d9ee71f..0d639738d4e6 100644 --- a/drivers/media/video/saa5249.c +++ b/drivers/media/video/saa5249.c @@ -57,6 +57,7 @@ #include <linux/videotext.h> #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/mutex.h> @@ -710,9 +711,7 @@ static const struct file_operations saa_fops = { static struct video_device saa_template = { - .owner = THIS_MODULE, .name = IF_NAME, - .type = VID_TYPE_TELETEXT, /*| VID_TYPE_TUNER ?? */ .fops = &saa_fops, }; diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig index 83f076abce35..7021bbf5897b 100644 --- a/drivers/media/video/saa7134/Kconfig +++ b/drivers/media/video/saa7134/Kconfig @@ -27,9 +27,7 @@ config VIDEO_SAA7134_ALSA config VIDEO_SAA7134_DVB tristate "DVB/ATSC Support for saa7134 based TV cards" depends on VIDEO_SAA7134 && DVB_CORE - depends on HOTPLUG # due to FW_LOADER select VIDEOBUF_DVB - select FW_LOADER select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_TDA1004X if !DVB_FE_CUSTOMISE diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 6893f998d292..98364d171def 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -5853,9 +5853,6 @@ int saa7134_board_init2(struct saa7134_dev *dev) unsigned char buf; int board; - dev->tuner_type = saa7134_boards[dev->board].tuner_type; - dev->tuner_addr = saa7134_boards[dev->board].tuner_addr; - switch (dev->board) { case SAA7134_BOARD_BMK_MPEX_NOTUNER: case SAA7134_BOARD_BMK_MPEX_TUNER: diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index cfee84ee7a88..75d618415f4f 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -798,7 +798,7 @@ static struct video_device *vdev_init(struct saa7134_dev *dev, return NULL; *vfd = *template; vfd->minor = -1; - vfd->dev = &dev->pci->dev; + vfd->parent = &dev->pci->dev; vfd->release = video_device_release; vfd->debug = video_debug; snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", @@ -945,11 +945,12 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, dev->board = SAA7134_BOARD_UNKNOWN; } dev->autodetected = card[dev->nr] != dev->board; - dev->tuner_type = saa7134_boards[dev->board].tuner_type; + dev->tuner_type = saa7134_boards[dev->board].tuner_type; + dev->tuner_addr = saa7134_boards[dev->board].tuner_addr; dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf; if (UNSET != tuner[dev->nr]) dev->tuner_type = tuner[dev->nr]; - printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", + printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", dev->name,pci_dev->subsystem_vendor, pci_dev->subsystem_device,saa7134_boards[dev->board].name, dev->board, dev->autodetected ? @@ -1007,11 +1008,9 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, v4l2_prio_init(&dev->prio); /* register v4l devices */ - if (saa7134_no_overlay <= 0) { - saa7134_video_template.type |= VID_TYPE_OVERLAY; - } else { - printk("%s: Overlay support disabled.\n",dev->name); - } + if (saa7134_no_overlay > 0) + printk(KERN_INFO "%s: Overlay support disabled.\n", dev->name); + dev->video_dev = vdev_init(dev,&saa7134_video_template,"video"); err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER, video_nr[dev->nr]); @@ -1024,7 +1023,6 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, dev->name,dev->video_dev->minor & 0x1f); dev->vbi_dev = vdev_init(dev, &saa7134_video_template, "vbi"); - dev->vbi_dev->type = VID_TYPE_TUNER | VID_TYPE_TELETEXT; err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI, vbi_nr[dev->nr]); diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c index 2a5ab957542d..c0c5d7509c25 100644 --- a/drivers/media/video/saa7134/saa7134-empress.c +++ b/drivers/media/video/saa7134/saa7134-empress.c @@ -89,14 +89,14 @@ static int ts_open(struct inode *inode, struct file *file) err = -EBUSY; if (!mutex_trylock(&dev->empress_tsq.vb_lock)) goto done; - if (dev->empress_users) + if (atomic_read(&dev->empress_users)) goto done_up; /* Unmute audio */ saa_writeb(SAA7134_AUDIO_MUTE_CTRL, saa_readb(SAA7134_AUDIO_MUTE_CTRL) & ~(1 << 6)); - dev->empress_users++; + atomic_inc(&dev->empress_users); file->private_data = dev; err = 0; @@ -110,8 +110,6 @@ static int ts_release(struct inode *inode, struct file *file) { struct saa7134_dev *dev = file->private_data; - mutex_lock(&dev->empress_tsq.vb_lock); - videobuf_stop(&dev->empress_tsq); videobuf_mmap_free(&dev->empress_tsq); @@ -122,9 +120,7 @@ static int ts_release(struct inode *inode, struct file *file) saa_writeb(SAA7134_AUDIO_MUTE_CTRL, saa_readb(SAA7134_AUDIO_MUTE_CTRL) | (1 << 6)); - dev->empress_users--; - - mutex_unlock(&dev->empress_tsq.vb_lock); + atomic_dec(&dev->empress_users); return 0; } @@ -333,6 +329,22 @@ static int empress_g_ext_ctrls(struct file *file, void *priv, return saa7134_i2c_call_saa6752(dev, VIDIOC_G_EXT_CTRLS, ctrls); } +static int empress_g_ctrl(struct file *file, void *priv, + struct v4l2_control *c) +{ + struct saa7134_dev *dev = file->private_data; + + return saa7134_g_ctrl_internal(dev, NULL, c); +} + +static int empress_s_ctrl(struct file *file, void *priv, + struct v4l2_control *c) +{ + struct saa7134_dev *dev = file->private_data; + + return saa7134_s_ctrl_internal(dev, NULL, c); +} + static int empress_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c) { @@ -400,16 +412,7 @@ static const struct file_operations ts_fops = .llseek = no_llseek, }; -/* ----------------------------------------------------------- */ - -static struct video_device saa7134_empress_template = -{ - .name = "saa7134-empress", - .type = 0 /* FIXME */, - .type2 = 0 /* FIXME */, - .fops = &ts_fops, - .minor = -1, - +static const struct v4l2_ioctl_ops ts_ioctl_ops = { .vidioc_querycap = empress_querycap, .vidioc_enum_fmt_vid_cap = empress_enum_fmt_vid_cap, .vidioc_s_fmt_vid_cap = empress_s_fmt_vid_cap, @@ -428,8 +431,17 @@ static struct video_device saa7134_empress_template = .vidioc_queryctrl = empress_queryctrl, .vidioc_querymenu = empress_querymenu, - .vidioc_g_ctrl = saa7134_g_ctrl, - .vidioc_s_ctrl = saa7134_s_ctrl, + .vidioc_g_ctrl = empress_g_ctrl, + .vidioc_s_ctrl = empress_s_ctrl, +}; + +/* ----------------------------------------------------------- */ + +static struct video_device saa7134_empress_template = { + .name = "saa7134-empress", + .fops = &ts_fops, + .minor = -1, + .ioctl_ops = &ts_ioctl_ops, .tvnorms = SAA7134_NORMS, .current_norm = V4L2_STD_PAL, @@ -445,7 +457,7 @@ static void empress_signal_update(struct work_struct *work) ts_reset_encoder(dev); } else { dprintk("video signal acquired\n"); - if (dev->empress_users) + if (atomic_read(&dev->empress_users)) ts_init_encoder(dev); } } @@ -465,7 +477,7 @@ static int empress_init(struct saa7134_dev *dev) if (NULL == dev->empress_dev) return -ENOMEM; *(dev->empress_dev) = saa7134_empress_template; - dev->empress_dev->dev = &dev->pci->dev; + dev->empress_dev->parent = &dev->pci->dev; dev->empress_dev->release = video_device_release; snprintf(dev->empress_dev->name, sizeof(dev->empress_dev->name), "%s empress (%s)", dev->name, diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index 1a5137550e7a..68c268981861 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -1112,10 +1112,8 @@ static struct videobuf_queue_ops video_qops = { /* ------------------------------------------------------------------ */ -int saa7134_g_ctrl(struct file *file, void *priv, struct v4l2_control *c) +int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; const struct v4l2_queryctrl* ctrl; ctrl = ctrl_by_id(c->id); @@ -1160,20 +1158,31 @@ int saa7134_g_ctrl(struct file *file, void *priv, struct v4l2_control *c) } return 0; } -EXPORT_SYMBOL_GPL(saa7134_g_ctrl); +EXPORT_SYMBOL_GPL(saa7134_g_ctrl_internal); + +static int saa7134_g_ctrl(struct file *file, void *priv, struct v4l2_control *c) +{ + struct saa7134_fh *fh = priv; + + return saa7134_g_ctrl_internal(fh->dev, fh, c); +} -int saa7134_s_ctrl(struct file *file, void *f, struct v4l2_control *c) +int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c) { const struct v4l2_queryctrl* ctrl; - struct saa7134_fh *fh = f; - struct saa7134_dev *dev = fh->dev; unsigned long flags; int restart_overlay = 0; - int err = -EINVAL; + int err; - err = v4l2_prio_check(&dev->prio, &fh->prio); - if (0 != err) - return err; + /* When called from the empress code fh == NULL. + That needs to be fixed somehow, but for now this is + good enough. */ + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + err = -EINVAL; mutex_lock(&dev->lock); @@ -1274,7 +1283,14 @@ error: mutex_unlock(&dev->lock); return err; } -EXPORT_SYMBOL_GPL(saa7134_s_ctrl); +EXPORT_SYMBOL_GPL(saa7134_s_ctrl_internal); + +static int saa7134_s_ctrl(struct file *file, void *f, struct v4l2_control *c) +{ + struct saa7134_fh *fh = f; + + return saa7134_s_ctrl_internal(fh->dev, fh, c); +} /* ------------------------------------------------------------------ */ @@ -2353,26 +2369,7 @@ static const struct file_operations video_fops = .llseek = no_llseek, }; -static const struct file_operations radio_fops = -{ - .owner = THIS_MODULE, - .open = video_open, - .release = video_release, - .ioctl = video_ioctl2, - .compat_ioctl = v4l_compat_ioctl32, - .llseek = no_llseek, -}; - -/* ----------------------------------------------------------- */ -/* exported stuff */ - -struct video_device saa7134_video_template = -{ - .name = "saa7134-video", - .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER | - VID_TYPE_CLIPPING|VID_TYPE_SCALES, - .fops = &video_fops, - .minor = -1, +static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_querycap = saa7134_querycap, .vidioc_enum_fmt_vid_cap = saa7134_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = saa7134_g_fmt_vid_cap, @@ -2421,16 +2418,18 @@ struct video_device saa7134_video_template = .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, #endif - .tvnorms = SAA7134_NORMS, - .current_norm = V4L2_STD_PAL, }; -struct video_device saa7134_radio_template = -{ - .name = "saa7134-radio", - .type = VID_TYPE_TUNER, - .fops = &radio_fops, - .minor = -1, +static const struct file_operations radio_fops = { + .owner = THIS_MODULE, + .open = video_open, + .release = video_release, + .ioctl = video_ioctl2, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_querycap = radio_querycap, .vidioc_g_tuner = radio_g_tuner, .vidioc_enum_input = radio_enum_input, @@ -2447,6 +2446,25 @@ struct video_device saa7134_radio_template = .vidioc_s_frequency = saa7134_s_frequency, }; +/* ----------------------------------------------------------- */ +/* exported stuff */ + +struct video_device saa7134_video_template = { + .name = "saa7134-video", + .fops = &video_fops, + .ioctl_ops = &video_ioctl_ops, + .minor = -1, + .tvnorms = SAA7134_NORMS, + .current_norm = V4L2_STD_PAL, +}; + +struct video_device saa7134_radio_template = { + .name = "saa7134-radio", + .fops = &radio_fops, + .ioctl_ops = &radio_ioctl_ops, + .minor = -1, +}; + int saa7134_video_init1(struct saa7134_dev *dev) { /* sanitycheck insmod options */ diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 6927cbea8624..a0884f639f65 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -34,6 +34,7 @@ #include <asm/io.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <media/tuner.h> #include <media/ir-common.h> #include <media/ir-kbd-i2c.h> @@ -560,7 +561,7 @@ struct saa7134_dev { /* SAA7134_MPEG_EMPRESS only */ struct video_device *empress_dev; struct videobuf_queue empress_tsq; - unsigned int empress_users; + atomic_t empress_users; struct work_struct empress_workqueue; int empress_started; @@ -662,8 +663,8 @@ extern unsigned int video_debug; extern struct video_device saa7134_video_template; extern struct video_device saa7134_radio_template; -int saa7134_g_ctrl(struct file *file, void *priv, struct v4l2_control *c); -int saa7134_s_ctrl(struct file *file, void *f, struct v4l2_control *c); +int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c); +int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c); int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c); int saa7134_videoport_init(struct saa7134_dev *dev); diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c index 2220f9569941..af60ede5310d 100644 --- a/drivers/media/video/saa717x.c +++ b/drivers/media/video/saa717x.c @@ -35,7 +35,6 @@ #include <linux/kernel.h> #include <linux/sched.h> -#include <linux/videodev.h> #include <linux/videodev2.h> #include <linux/i2c.h> #include <media/v4l2-common.h> diff --git a/drivers/media/video/saa7196.h b/drivers/media/video/saa7196.h index cd4b6354a7b3..e69de29bb2d1 100644 --- a/drivers/media/video/saa7196.h +++ b/drivers/media/video/saa7196.h @@ -1,117 +0,0 @@ -/* - Definitions for the Philips SAA7196 digital video decoder, - scaler, and clock generator circuit (DESCpro), as used in - the PlanB video input of the Powermac 7x00/8x00 series. - - Copyright (C) 1998 Michel Lanners (mlan@cpu.lu) - - The register defines are shamelessly copied from the meteor - driver out of NetBSD (with permission), - and are copyrighted (c) 1995 Mark Tinguely and Jim Lowe - (Thanks !) - - Additional debugging and coding by Takashi Oe (toe@unlinfo.unl.edu) - - The default values used for PlanB are my mistakes. -*/ - -/* $Id: saa7196.h,v 1.5 1999/03/26 23:28:47 mlan Exp $ */ - -#ifndef _SAA7196_H_ -#define _SAA7196_H_ - -#define SAA7196_NUMREGS 0x31 /* Number of registers (used)*/ -#define NUM_SUPPORTED_NORM 3 /* Number of supported norms by PlanB */ - -/* Decoder part: */ -#define SAA7196_IDEL 0x00 /* Increment delay */ -#define SAA7196_HSB5 0x01 /* H-sync begin; 50 hz */ -#define SAA7196_HSS5 0x02 /* H-sync stop; 50 hz */ -#define SAA7196_HCB5 0x03 /* H-clamp begin; 50 hz */ -#define SAA7196_HCS5 0x04 /* H-clamp stop; 50 hz */ -#define SAA7196_HSP5 0x05 /* H-sync after PHI1; 50 hz */ -#define SAA7196_LUMC 0x06 /* Luminance control */ -#define SAA7196_HUEC 0x07 /* Hue control */ -#define SAA7196_CKTQ 0x08 /* Colour Killer Threshold QAM (PAL, NTSC) */ -#define SAA7196_CKTS 0x09 /* Colour Killer Threshold SECAM */ -#define SAA7196_PALS 0x0a /* PAL switch sensitivity */ -#define SAA7196_SECAMS 0x0b /* SECAM switch sensitivity */ -#define SAA7196_CGAINC 0x0c /* Chroma gain control */ -#define SAA7196_STDC 0x0d /* Standard/Mode control */ -#define SAA7196_IOCC 0x0e /* I/O and Clock Control */ -#define SAA7196_CTRL1 0x0f /* Control #1 */ -#define SAA7196_CTRL2 0x10 /* Control #2 */ -#define SAA7196_CGAINR 0x11 /* Chroma Gain Reference */ -#define SAA7196_CSAT 0x12 /* Chroma Saturation */ -#define SAA7196_CONT 0x13 /* Luminance Contrast */ -#define SAA7196_HSB6 0x14 /* H-sync begin; 60 hz */ -#define SAA7196_HSS6 0x15 /* H-sync stop; 60 hz */ -#define SAA7196_HCB6 0x16 /* H-clamp begin; 60 hz */ -#define SAA7196_HCS6 0x17 /* H-clamp stop; 60 hz */ -#define SAA7196_HSP6 0x18 /* H-sync after PHI1; 60 hz */ -#define SAA7196_BRIG 0x19 /* Luminance Brightness */ - -/* Scaler part: */ -#define SAA7196_FMTS 0x20 /* Formats and sequence */ -#define SAA7196_OUTPIX 0x21 /* Output data pixel/line */ -#define SAA7196_INPIX 0x22 /* Input data pixel/line */ -#define SAA7196_HWS 0x23 /* Horiz. window start */ -#define SAA7196_HFILT 0x24 /* Horiz. filter */ -#define SAA7196_OUTLINE 0x25 /* Output data lines/field */ -#define SAA7196_INLINE 0x26 /* Input data lines/field */ -#define SAA7196_VWS 0x27 /* Vertical window start */ -#define SAA7196_VYP 0x28 /* AFS/vertical Y processing */ -#define SAA7196_VBS 0x29 /* Vertical Bypass start */ -#define SAA7196_VBCNT 0x2a /* Vertical Bypass count */ -#define SAA7196_VBP 0x2b /* veritcal Bypass Polarity */ -#define SAA7196_VLOW 0x2c /* Colour-keying lower V limit */ -#define SAA7196_VHIGH 0x2d /* Colour-keying upper V limit */ -#define SAA7196_ULOW 0x2e /* Colour-keying lower U limit */ -#define SAA7196_UHIGH 0x2f /* Colour-keying upper U limit */ -#define SAA7196_DPATH 0x30 /* Data path setting */ - -/* Initialization default values: */ - -unsigned char saa_regs[NUM_SUPPORTED_NORM][SAA7196_NUMREGS] = { - -/* PAL, 768x576 (no scaling), composite video-in */ -/* Decoder: */ - { 0x50, 0x30, 0x00, 0xe8, 0xb6, 0xe5, 0x63, 0xff, - 0xfe, 0xf0, 0xfe, 0xe0, 0x20, 0x06, 0x3b, 0x98, - 0x00, 0x59, 0x41, 0x45, 0x34, 0x0a, 0xf4, 0xd2, - 0xe9, 0xa2, -/* Padding */ - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* Scaler: */ - 0x72, 0x80, 0x00, 0x03, 0x8d, 0x20, 0x20, 0x12, - 0xa5, 0x12, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, - 0x87 }, - -/* NTSC, 640x480? (no scaling), composite video-in */ -/* Decoder: */ - { 0x50, 0x30, 0x00, 0xe8, 0xb6, 0xe5, 0x50, 0x00, - 0xf8, 0xf0, 0xfe, 0xe0, 0x00, 0x06, 0x3b, 0x98, - 0x00, 0x2c, 0x3d, 0x40, 0x34, 0x0a, 0xf4, 0xd2, - 0xe9, 0x98, -/* Padding */ - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* Scaler: */ - 0x72, 0x80, 0x80, 0x03, 0x89, 0xf0, 0xf0, 0x0d, - 0xa0, 0x0d, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, - 0x87 }, - -/* SECAM, 768x576 (no scaling), composite video-in */ -/* Decoder: */ - { 0x50, 0x30, 0x00, 0xe8, 0xb6, 0xe5, 0x63, 0xff, - 0xfe, 0xf0, 0xfe, 0xe0, 0x20, 0x07, 0x3b, 0x98, - 0x00, 0x59, 0x41, 0x45, 0x34, 0x0a, 0xf4, 0xd2, - 0xe9, 0xa2, -/* Padding */ - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -/* Scaler: */ - 0x72, 0x80, 0x00, 0x03, 0x8d, 0x20, 0x20, 0x12, - 0xa5, 0x12, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, - 0x87 } - }; - -#endif /* _SAA7196_H_ */ diff --git a/drivers/media/video/se401.c b/drivers/media/video/se401.c index 1cd629380f71..f481277892da 100644 --- a/drivers/media/video/se401.c +++ b/drivers/media/video/se401.c @@ -1230,9 +1230,7 @@ static const struct file_operations se401_fops = { .llseek = no_llseek, }; static struct video_device se401_template = { - .owner = THIS_MODULE, .name = "se401 USB camera", - .type = VID_TYPE_CAPTURE, .fops = &se401_fops, }; diff --git a/drivers/media/video/se401.h b/drivers/media/video/se401.h index 835ef872e803..2ce685db5d8b 100644 --- a/drivers/media/video/se401.h +++ b/drivers/media/video/se401.h @@ -5,6 +5,7 @@ #include <asm/uaccess.h> #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/mutex.h> #define se401_DEBUG /* Turn on debug messages */ diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 012005e1a77b..f7ca3cb9340a 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -91,6 +91,7 @@ struct sh_mobile_ceu_dev { void __iomem *base; unsigned long video_limit; + /* lock used to protect videobuf */ spinlock_t lock; struct list_head capture; struct videobuf_buffer *active; diff --git a/drivers/media/video/sn9c102/sn9c102.h b/drivers/media/video/sn9c102/sn9c102.h index 0c8d87d8d18d..cbfc44433b99 100644 --- a/drivers/media/video/sn9c102/sn9c102.h +++ b/drivers/media/video/sn9c102/sn9c102.h @@ -25,6 +25,7 @@ #include <linux/usb.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/device.h> #include <linux/list.h> #include <linux/spinlock.h> diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index 7f9c7bcf3c85..23408764d0ef 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -1038,8 +1038,7 @@ static ssize_t sn9c102_show_reg(struct device* cd, if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(container_of(cd, struct video_device, - class_dev)); + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -1064,8 +1063,7 @@ sn9c102_store_reg(struct device* cd, struct device_attribute *attr, if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(container_of(cd, struct video_device, - class_dev)); + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -1098,8 +1096,7 @@ static ssize_t sn9c102_show_val(struct device* cd, if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(container_of(cd, struct video_device, - class_dev)); + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -1132,8 +1129,7 @@ sn9c102_store_val(struct device* cd, struct device_attribute *attr, if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(container_of(cd, struct video_device, - class_dev)); + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -1170,8 +1166,7 @@ static ssize_t sn9c102_show_i2c_reg(struct device* cd, if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(container_of(cd, struct video_device, - class_dev)); + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -1198,8 +1193,7 @@ sn9c102_store_i2c_reg(struct device* cd, struct device_attribute *attr, if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(container_of(cd, struct video_device, - class_dev)); + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -1232,8 +1226,7 @@ static ssize_t sn9c102_show_i2c_val(struct device* cd, if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(container_of(cd, struct video_device, - class_dev)); + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -1271,8 +1264,7 @@ sn9c102_store_i2c_val(struct device* cd, struct device_attribute *attr, if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(container_of(cd, struct video_device, - class_dev)); + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -1318,8 +1310,7 @@ sn9c102_store_green(struct device* cd, struct device_attribute *attr, if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(container_of(cd, struct video_device, - class_dev)); + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -1400,8 +1391,7 @@ static ssize_t sn9c102_show_frame_header(struct device* cd, struct sn9c102_device* cam; ssize_t count; - cam = video_get_drvdata(container_of(cd, struct video_device, - class_dev)); + cam = video_get_drvdata(container_of(cd, struct video_device, dev)); if (!cam) return -ENODEV; @@ -1428,49 +1418,49 @@ static DEVICE_ATTR(frame_header, S_IRUGO, sn9c102_show_frame_header, NULL); static int sn9c102_create_sysfs(struct sn9c102_device* cam) { - struct device *classdev = &(cam->v4ldev->class_dev); + struct device *dev = &(cam->v4ldev->dev); int err = 0; - if ((err = device_create_file(classdev, &dev_attr_reg))) + if ((err = device_create_file(dev, &dev_attr_reg))) goto err_out; - if ((err = device_create_file(classdev, &dev_attr_val))) + if ((err = device_create_file(dev, &dev_attr_val))) goto err_reg; - if ((err = device_create_file(classdev, &dev_attr_frame_header))) + if ((err = device_create_file(dev, &dev_attr_frame_header))) goto err_val; if (cam->sensor.sysfs_ops) { - if ((err = device_create_file(classdev, &dev_attr_i2c_reg))) + if ((err = device_create_file(dev, &dev_attr_i2c_reg))) goto err_frame_header; - if ((err = device_create_file(classdev, &dev_attr_i2c_val))) + if ((err = device_create_file(dev, &dev_attr_i2c_val))) goto err_i2c_reg; } if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) { - if ((err = device_create_file(classdev, &dev_attr_green))) + if ((err = device_create_file(dev, &dev_attr_green))) goto err_i2c_val; } else { - if ((err = device_create_file(classdev, &dev_attr_blue))) + if ((err = device_create_file(dev, &dev_attr_blue))) goto err_i2c_val; - if ((err = device_create_file(classdev, &dev_attr_red))) + if ((err = device_create_file(dev, &dev_attr_red))) goto err_blue; } return 0; err_blue: - device_remove_file(classdev, &dev_attr_blue); + device_remove_file(dev, &dev_attr_blue); err_i2c_val: if (cam->sensor.sysfs_ops) - device_remove_file(classdev, &dev_attr_i2c_val); + device_remove_file(dev, &dev_attr_i2c_val); err_i2c_reg: if (cam->sensor.sysfs_ops) - device_remove_file(classdev, &dev_attr_i2c_reg); + device_remove_file(dev, &dev_attr_i2c_reg); err_frame_header: - device_remove_file(classdev, &dev_attr_frame_header); + device_remove_file(dev, &dev_attr_frame_header); err_val: - device_remove_file(classdev, &dev_attr_val); + device_remove_file(dev, &dev_attr_val); err_reg: - device_remove_file(classdev, &dev_attr_reg); + device_remove_file(dev, &dev_attr_reg); err_out: return err; } @@ -3319,8 +3309,6 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) } strcpy(cam->v4ldev->name, "SN9C1xx PC Camera"); - cam->v4ldev->owner = THIS_MODULE; - cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; cam->v4ldev->fops = &sn9c102_fops; cam->v4ldev->minor = video_nr[dev_nr]; cam->v4ldev->release = video_device_release; diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index e39b98f1eca4..b6be5ee678b6 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -25,6 +25,7 @@ #include <linux/vmalloc.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <media/v4l2-dev.h> #include <media/videobuf-core.h> #include <media/soc_camera.h> @@ -193,7 +194,7 @@ static int soc_camera_open(struct inode *inode, struct file *file) mutex_lock(&video_lock); vdev = video_devdata(file); - icd = container_of(vdev->dev, struct soc_camera_device, dev); + icd = container_of(vdev->parent, struct soc_camera_device, dev); ici = to_soc_camera_host(icd->dev.parent); if (!try_module_get(icd->ops->owner)) { @@ -258,7 +259,7 @@ static int soc_camera_close(struct inode *inode, struct file *file) vfree(icf); - dev_dbg(vdev->dev, "camera device close\n"); + dev_dbg(vdev->parent, "camera device close\n"); return 0; } @@ -271,7 +272,7 @@ static ssize_t soc_camera_read(struct file *file, char __user *buf, struct video_device *vdev = icd->vdev; int err = -EINVAL; - dev_err(vdev->dev, "camera device read not implemented\n"); + dev_err(vdev->parent, "camera device read not implemented\n"); return err; } @@ -861,6 +862,35 @@ void soc_camera_device_unregister(struct soc_camera_device *icd) } EXPORT_SYMBOL(soc_camera_device_unregister); +static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { + .vidioc_querycap = soc_camera_querycap, + .vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = soc_camera_s_fmt_vid_cap, + .vidioc_enum_input = soc_camera_enum_input, + .vidioc_g_input = soc_camera_g_input, + .vidioc_s_input = soc_camera_s_input, + .vidioc_s_std = soc_camera_s_std, + .vidioc_reqbufs = soc_camera_reqbufs, + .vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap, + .vidioc_querybuf = soc_camera_querybuf, + .vidioc_qbuf = soc_camera_qbuf, + .vidioc_dqbuf = soc_camera_dqbuf, + .vidioc_streamon = soc_camera_streamon, + .vidioc_streamoff = soc_camera_streamoff, + .vidioc_queryctrl = soc_camera_queryctrl, + .vidioc_g_ctrl = soc_camera_g_ctrl, + .vidioc_s_ctrl = soc_camera_s_ctrl, + .vidioc_cropcap = soc_camera_cropcap, + .vidioc_g_crop = soc_camera_g_crop, + .vidioc_s_crop = soc_camera_s_crop, + .vidioc_g_chip_ident = soc_camera_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = soc_camera_g_register, + .vidioc_s_register = soc_camera_s_register, +#endif +}; + int soc_camera_video_start(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); @@ -877,45 +907,19 @@ int soc_camera_video_start(struct soc_camera_device *icd) strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name)); /* Maybe better &ici->dev */ - vdev->dev = &icd->dev; - vdev->type = VID_TYPE_CAPTURE; + vdev->parent = &icd->dev; vdev->current_norm = V4L2_STD_UNKNOWN; vdev->fops = &soc_camera_fops; + vdev->ioctl_ops = &soc_camera_ioctl_ops; vdev->release = video_device_release; vdev->minor = -1; vdev->tvnorms = V4L2_STD_UNKNOWN, - vdev->vidioc_querycap = soc_camera_querycap; - vdev->vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap; - vdev->vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap; - vdev->vidioc_s_fmt_vid_cap = soc_camera_s_fmt_vid_cap; - vdev->vidioc_enum_input = soc_camera_enum_input; - vdev->vidioc_g_input = soc_camera_g_input; - vdev->vidioc_s_input = soc_camera_s_input; - vdev->vidioc_s_std = soc_camera_s_std; - vdev->vidioc_reqbufs = soc_camera_reqbufs; - vdev->vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap; - vdev->vidioc_querybuf = soc_camera_querybuf; - vdev->vidioc_qbuf = soc_camera_qbuf; - vdev->vidioc_dqbuf = soc_camera_dqbuf; - vdev->vidioc_streamon = soc_camera_streamon; - vdev->vidioc_streamoff = soc_camera_streamoff; - vdev->vidioc_queryctrl = soc_camera_queryctrl; - vdev->vidioc_g_ctrl = soc_camera_g_ctrl; - vdev->vidioc_s_ctrl = soc_camera_s_ctrl; - vdev->vidioc_cropcap = soc_camera_cropcap; - vdev->vidioc_g_crop = soc_camera_g_crop; - vdev->vidioc_s_crop = soc_camera_s_crop; - vdev->vidioc_g_chip_ident = soc_camera_g_chip_ident; -#ifdef CONFIG_VIDEO_ADV_DEBUG - vdev->vidioc_g_register = soc_camera_g_register; - vdev->vidioc_s_register = soc_camera_s_register; -#endif icd->current_fmt = &icd->formats[0]; err = video_register_device(vdev, VFL_TYPE_GRABBER, vdev->minor); if (err < 0) { - dev_err(vdev->dev, "video_register_device failed\n"); + dev_err(vdev->parent, "video_register_device failed\n"); goto evidregd; } icd->vdev = vdev; diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c index f308c38d744f..ad36af30e099 100644 --- a/drivers/media/video/stk-webcam.c +++ b/drivers/media/video/stk-webcam.c @@ -34,6 +34,7 @@ #include <linux/vmalloc.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include "stk-webcam.h" @@ -340,17 +341,19 @@ static int stk_create_sysfs_files(struct video_device *vdev) { int ret; - ret = video_device_create_file(vdev, &dev_attr_brightness); - ret += video_device_create_file(vdev, &dev_attr_hflip); - ret += video_device_create_file(vdev, &dev_attr_vflip); + ret = device_create_file(&vdev->dev, &dev_attr_brightness); + ret += device_create_file(&vdev->dev, &dev_attr_hflip); + ret += device_create_file(&vdev->dev, &dev_attr_vflip); + if (ret) + STK_WARNING("Could not create sysfs files\n"); return ret; } static void stk_remove_sysfs_files(struct video_device *vdev) { - video_device_remove_file(vdev, &dev_attr_brightness); - video_device_remove_file(vdev, &dev_attr_hflip); - video_device_remove_file(vdev, &dev_attr_vflip); + device_remove_file(&vdev->dev, &dev_attr_brightness); + device_remove_file(&vdev->dev, &dev_attr_hflip); + device_remove_file(&vdev->dev, &dev_attr_vflip); } #else @@ -442,18 +445,19 @@ static void stk_isoc_handler(struct urb *urb) fb->v4lbuf.bytesused = 0; fill = fb->buffer; } else if (fb->v4lbuf.bytesused == dev->frame_size) { - list_move_tail(dev->sio_avail.next, - &dev->sio_full); - wake_up(&dev->wait_frame); - if (list_empty(&dev->sio_avail)) { - (void) (printk_ratelimit() && - STK_ERROR("No buffer available\n")); - goto resubmit; + if (list_is_singular(&dev->sio_avail)) { + /* Always reuse the last buffer */ + fb->v4lbuf.bytesused = 0; + fill = fb->buffer; + } else { + list_move_tail(dev->sio_avail.next, + &dev->sio_full); + wake_up(&dev->wait_frame); + fb = list_first_entry(&dev->sio_avail, + struct stk_sio_buffer, list); + fb->v4lbuf.bytesused = 0; + fill = fb->buffer; } - fb = list_first_entry(&dev->sio_avail, - struct stk_sio_buffer, list); - fb->v4lbuf.bytesused = 0; - fill = fb->buffer; } } else { framelen -= 4; @@ -1327,20 +1331,7 @@ static struct file_operations v4l_stk_fops = { .llseek = no_llseek }; -static void stk_v4l_dev_release(struct video_device *vd) -{ -} - -static struct video_device stk_v4l_data = { - .name = "stkwebcam", - .type = VFL_TYPE_GRABBER, - .type2 = VID_TYPE_CAPTURE, - .minor = -1, - .tvnorms = V4L2_STD_UNKNOWN, - .current_norm = V4L2_STD_UNKNOWN, - .fops = &v4l_stk_fops, - .release = stk_v4l_dev_release, - +static const struct v4l2_ioctl_ops v4l_stk_ioctl_ops = { .vidioc_querycap = stk_vidioc_querycap, .vidioc_enum_fmt_vid_cap = stk_vidioc_enum_fmt_vid_cap, .vidioc_try_fmt_vid_cap = stk_vidioc_try_fmt_vid_cap, @@ -1362,6 +1353,20 @@ static struct video_device stk_v4l_data = { .vidioc_g_parm = stk_vidioc_g_parm, }; +static void stk_v4l_dev_release(struct video_device *vd) +{ +} + +static struct video_device stk_v4l_data = { + .name = "stkwebcam", + .minor = -1, + .tvnorms = V4L2_STD_UNKNOWN, + .current_norm = V4L2_STD_UNKNOWN, + .fops = &v4l_stk_fops, + .ioctl_ops = &v4l_stk_ioctl_ops, + .release = stk_v4l_dev_release, +}; + static int stk_register_video_device(struct stk_camera *dev) { @@ -1369,7 +1374,7 @@ static int stk_register_video_device(struct stk_camera *dev) dev->vdev = stk_v4l_data; dev->vdev.debug = debug; - dev->vdev.dev = &dev->interface->dev; + dev->vdev.parent = &dev->interface->dev; dev->vdev.priv = dev; err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); if (err) diff --git a/drivers/media/video/stradis.c b/drivers/media/video/stradis.c index c109511f21ea..276bded06ab3 100644 --- a/drivers/media/video/stradis.c +++ b/drivers/media/video/stradis.c @@ -43,6 +43,7 @@ #include <linux/vmalloc.h> #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include "saa7146.h" #include "saa7146reg.h" @@ -1918,7 +1919,6 @@ static const struct file_operations saa_fops = { /* template for video_device-structure */ static struct video_device saa_template = { .name = "SAA7146A", - .type = VID_TYPE_CAPTURE | VID_TYPE_OVERLAY, .fops = &saa_fops, .minor = -1, }; diff --git a/drivers/media/video/stv680.c b/drivers/media/video/stv680.c index d7f130bedb5f..56dc3d6b5b29 100644 --- a/drivers/media/video/stv680.c +++ b/drivers/media/video/stv680.c @@ -66,6 +66,7 @@ #include <linux/errno.h> #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/usb.h> #include <linux/mutex.h> @@ -524,53 +525,54 @@ static int stv680_create_sysfs_files(struct video_device *vdev) { int rc; - rc = video_device_create_file(vdev, &dev_attr_model); + rc = device_create_file(&vdev->dev, &dev_attr_model); if (rc) goto err; - rc = video_device_create_file(vdev, &dev_attr_in_use); + rc = device_create_file(&vdev->dev, &dev_attr_in_use); if (rc) goto err_model; - rc = video_device_create_file(vdev, &dev_attr_streaming); + rc = device_create_file(&vdev->dev, &dev_attr_streaming); if (rc) goto err_inuse; - rc = video_device_create_file(vdev, &dev_attr_palette); + rc = device_create_file(&vdev->dev, &dev_attr_palette); if (rc) goto err_stream; - rc = video_device_create_file(vdev, &dev_attr_frames_total); + rc = device_create_file(&vdev->dev, &dev_attr_frames_total); if (rc) goto err_pal; - rc = video_device_create_file(vdev, &dev_attr_frames_read); + rc = device_create_file(&vdev->dev, &dev_attr_frames_read); if (rc) goto err_framtot; - rc = video_device_create_file(vdev, &dev_attr_packets_dropped); + rc = device_create_file(&vdev->dev, &dev_attr_packets_dropped); if (rc) goto err_framread; - rc = video_device_create_file(vdev, &dev_attr_decoding_errors); + rc = device_create_file(&vdev->dev, &dev_attr_decoding_errors); if (rc) goto err_dropped; return 0; err_dropped: - video_device_remove_file(vdev, &dev_attr_packets_dropped); + device_remove_file(&vdev->dev, &dev_attr_packets_dropped); err_framread: - video_device_remove_file(vdev, &dev_attr_frames_read); + device_remove_file(&vdev->dev, &dev_attr_frames_read); err_framtot: - video_device_remove_file(vdev, &dev_attr_frames_total); + device_remove_file(&vdev->dev, &dev_attr_frames_total); err_pal: - video_device_remove_file(vdev, &dev_attr_palette); + device_remove_file(&vdev->dev, &dev_attr_palette); err_stream: - video_device_remove_file(vdev, &dev_attr_streaming); + device_remove_file(&vdev->dev, &dev_attr_streaming); err_inuse: - video_device_remove_file(vdev, &dev_attr_in_use); + device_remove_file(&vdev->dev, &dev_attr_in_use); err_model: - video_device_remove_file(vdev, &dev_attr_model); + device_remove_file(&vdev->dev, &dev_attr_model); err: + PDEBUG(0, "STV(e): Could not create sysfs files"); return rc; } static void stv680_remove_sysfs_files(struct video_device *vdev) { - video_device_remove_file(vdev, &dev_attr_model); - video_device_remove_file(vdev, &dev_attr_in_use); - video_device_remove_file(vdev, &dev_attr_streaming); - video_device_remove_file(vdev, &dev_attr_palette); - video_device_remove_file(vdev, &dev_attr_frames_total); - video_device_remove_file(vdev, &dev_attr_frames_read); - video_device_remove_file(vdev, &dev_attr_packets_dropped); - video_device_remove_file(vdev, &dev_attr_decoding_errors); + device_remove_file(&vdev->dev, &dev_attr_model); + device_remove_file(&vdev->dev, &dev_attr_in_use); + device_remove_file(&vdev->dev, &dev_attr_streaming); + device_remove_file(&vdev->dev, &dev_attr_palette); + device_remove_file(&vdev->dev, &dev_attr_frames_total); + device_remove_file(&vdev->dev, &dev_attr_frames_read); + device_remove_file(&vdev->dev, &dev_attr_packets_dropped); + device_remove_file(&vdev->dev, &dev_attr_decoding_errors); } /******************************************************************** @@ -1400,9 +1402,7 @@ static const struct file_operations stv680_fops = { .llseek = no_llseek, }; static struct video_device stv680_template = { - .owner = THIS_MODULE, .name = "STV0680 USB camera", - .type = VID_TYPE_CAPTURE, .fops = &stv680_fops, .release = video_device_release, .minor = -1, @@ -1454,7 +1454,7 @@ static int stv680_probe (struct usb_interface *intf, const struct usb_device_id goto error; } memcpy(stv680->vdev, &stv680_template, sizeof(stv680_template)); - stv680->vdev->dev = &intf->dev; + stv680->vdev->parent = &intf->dev; video_set_drvdata(stv680->vdev, stv680); memcpy (stv680->vdev->name, stv680->camera_name, strlen (stv680->camera_name)); diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c index ae75c187da79..4963d4264880 100644 --- a/drivers/media/video/tda7432.c +++ b/drivers/media/video/tda7432.c @@ -44,10 +44,11 @@ #include <linux/delay.h> #include <linux/errno.h> #include <linux/slab.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <linux/i2c.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <media/i2c-addr.h> #ifndef VIDEO_AUDIO_BALANCE diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c index 7a8ce8fb46dc..792f0b079909 100644 --- a/drivers/media/video/tda9875.c +++ b/drivers/media/video/tda9875.c @@ -25,7 +25,7 @@ #include <linux/delay.h> #include <linux/errno.h> #include <linux/slab.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> #include <linux/i2c.h> #include <linux/init.h> diff --git a/drivers/media/video/tlv320aic23b.c b/drivers/media/video/tlv320aic23b.c index 9220378a5637..281065b9dd2d 100644 --- a/drivers/media/video/tlv320aic23b.c +++ b/drivers/media/video/tlv320aic23b.c @@ -29,7 +29,7 @@ #include <asm/uaccess.h> #include <linux/i2c.h> #include <linux/i2c-id.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> #include <media/v4l2-i2c-drv-legacy.h> diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 93d879dc510f..d806a3556eed 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -19,6 +19,7 @@ #include <media/tuner.h> #include <media/tuner-types.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <media/v4l2-i2c-drv-legacy.h> #include "mt20xx.h" #include "tda8290.h" diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index 9da0e1807ffb..bcc32fa92a81 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -34,13 +34,13 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/types.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <linux/i2c.h> #include <media/tuner.h> #include <media/tveeprom.h> #include <media/v4l2-common.h> -#include <media/audiochip.h> +#include <media/v4l2-chip-ident.h> MODULE_DESCRIPTION("i2c Hauppauge eeprom decoder driver"); MODULE_AUTHOR("John Klar"); @@ -261,70 +261,72 @@ hauppauge_tuner[] = { TUNER_ABSENT, "MaxLinear MXL5005_v2"}, { TUNER_PHILIPS_TDA8290, "Philips 18271_8295"}, /* 150-159 */ - { TUNER_ABSENT, "Xceive XC5000"}, + { TUNER_ABSENT, "Xceive XC5000"}, }; +/* Use V4L2_IDENT_AMBIGUOUS for those audio 'chips' that are + * internal to a video chip, i.e. not a separate audio chip. */ static struct HAUPPAUGE_AUDIOIC { - enum audiochip id; + u32 id; char *name; } audioIC[] = { /* 0-4 */ - {AUDIO_CHIP_NONE, "None"}, - {AUDIO_CHIP_TEA6300, "TEA6300"}, - {AUDIO_CHIP_TEA6300, "TEA6320"}, - {AUDIO_CHIP_TDA985X, "TDA9850"}, - {AUDIO_CHIP_MSP34XX, "MSP3400C"}, + { V4L2_IDENT_NONE, "None" }, + { V4L2_IDENT_UNKNOWN, "TEA6300" }, + { V4L2_IDENT_UNKNOWN, "TEA6320" }, + { V4L2_IDENT_UNKNOWN, "TDA9850" }, + { V4L2_IDENT_MSPX4XX, "MSP3400C" }, /* 5-9 */ - {AUDIO_CHIP_MSP34XX, "MSP3410D"}, - {AUDIO_CHIP_MSP34XX, "MSP3415"}, - {AUDIO_CHIP_MSP34XX, "MSP3430"}, - {AUDIO_CHIP_MSP34XX, "MSP3438"}, - {AUDIO_CHIP_UNKNOWN, "CS5331"}, + { V4L2_IDENT_MSPX4XX, "MSP3410D" }, + { V4L2_IDENT_MSPX4XX, "MSP3415" }, + { V4L2_IDENT_MSPX4XX, "MSP3430" }, + { V4L2_IDENT_MSPX4XX, "MSP3438" }, + { V4L2_IDENT_UNKNOWN, "CS5331" }, /* 10-14 */ - {AUDIO_CHIP_MSP34XX, "MSP3435"}, - {AUDIO_CHIP_MSP34XX, "MSP3440"}, - {AUDIO_CHIP_MSP34XX, "MSP3445"}, - {AUDIO_CHIP_MSP34XX, "MSP3411"}, - {AUDIO_CHIP_MSP34XX, "MSP3416"}, + { V4L2_IDENT_MSPX4XX, "MSP3435" }, + { V4L2_IDENT_MSPX4XX, "MSP3440" }, + { V4L2_IDENT_MSPX4XX, "MSP3445" }, + { V4L2_IDENT_MSPX4XX, "MSP3411" }, + { V4L2_IDENT_MSPX4XX, "MSP3416" }, /* 15-19 */ - {AUDIO_CHIP_MSP34XX, "MSP3425"}, - {AUDIO_CHIP_MSP34XX, "MSP3451"}, - {AUDIO_CHIP_MSP34XX, "MSP3418"}, - {AUDIO_CHIP_UNKNOWN, "Type 0x12"}, - {AUDIO_CHIP_UNKNOWN, "OKI7716"}, + { V4L2_IDENT_MSPX4XX, "MSP3425" }, + { V4L2_IDENT_MSPX4XX, "MSP3451" }, + { V4L2_IDENT_MSPX4XX, "MSP3418" }, + { V4L2_IDENT_UNKNOWN, "Type 0x12" }, + { V4L2_IDENT_UNKNOWN, "OKI7716" }, /* 20-24 */ - {AUDIO_CHIP_MSP34XX, "MSP4410"}, - {AUDIO_CHIP_MSP34XX, "MSP4420"}, - {AUDIO_CHIP_MSP34XX, "MSP4440"}, - {AUDIO_CHIP_MSP34XX, "MSP4450"}, - {AUDIO_CHIP_MSP34XX, "MSP4408"}, + { V4L2_IDENT_MSPX4XX, "MSP4410" }, + { V4L2_IDENT_MSPX4XX, "MSP4420" }, + { V4L2_IDENT_MSPX4XX, "MSP4440" }, + { V4L2_IDENT_MSPX4XX, "MSP4450" }, + { V4L2_IDENT_MSPX4XX, "MSP4408" }, /* 25-29 */ - {AUDIO_CHIP_MSP34XX, "MSP4418"}, - {AUDIO_CHIP_MSP34XX, "MSP4428"}, - {AUDIO_CHIP_MSP34XX, "MSP4448"}, - {AUDIO_CHIP_MSP34XX, "MSP4458"}, - {AUDIO_CHIP_MSP34XX, "Type 0x1d"}, + { V4L2_IDENT_MSPX4XX, "MSP4418" }, + { V4L2_IDENT_MSPX4XX, "MSP4428" }, + { V4L2_IDENT_MSPX4XX, "MSP4448" }, + { V4L2_IDENT_MSPX4XX, "MSP4458" }, + { V4L2_IDENT_MSPX4XX, "Type 0x1d" }, /* 30-34 */ - {AUDIO_CHIP_INTERNAL, "CX880"}, - {AUDIO_CHIP_INTERNAL, "CX881"}, - {AUDIO_CHIP_INTERNAL, "CX883"}, - {AUDIO_CHIP_INTERNAL, "CX882"}, - {AUDIO_CHIP_INTERNAL, "CX25840"}, + { V4L2_IDENT_AMBIGUOUS, "CX880" }, + { V4L2_IDENT_AMBIGUOUS, "CX881" }, + { V4L2_IDENT_AMBIGUOUS, "CX883" }, + { V4L2_IDENT_AMBIGUOUS, "CX882" }, + { V4L2_IDENT_AMBIGUOUS, "CX25840" }, /* 35-39 */ - {AUDIO_CHIP_INTERNAL, "CX25841"}, - {AUDIO_CHIP_INTERNAL, "CX25842"}, - {AUDIO_CHIP_INTERNAL, "CX25843"}, - {AUDIO_CHIP_INTERNAL, "CX23418"}, - {AUDIO_CHIP_INTERNAL, "CX23885"}, + { V4L2_IDENT_AMBIGUOUS, "CX25841" }, + { V4L2_IDENT_AMBIGUOUS, "CX25842" }, + { V4L2_IDENT_AMBIGUOUS, "CX25843" }, + { V4L2_IDENT_AMBIGUOUS, "CX23418" }, + { V4L2_IDENT_AMBIGUOUS, "CX23885" }, /* 40-44 */ - {AUDIO_CHIP_INTERNAL, "CX23888"}, - {AUDIO_CHIP_INTERNAL, "SAA7131"}, - {AUDIO_CHIP_INTERNAL, "CX23887"}, - {AUDIO_CHIP_INTERNAL, "SAA7164"}, - {AUDIO_CHIP_INTERNAL, "AU8522"}, + { V4L2_IDENT_AMBIGUOUS, "CX23888" }, + { V4L2_IDENT_AMBIGUOUS, "SAA7131" }, + { V4L2_IDENT_AMBIGUOUS, "CX23887" }, + { V4L2_IDENT_AMBIGUOUS, "SAA7164" }, + { V4L2_IDENT_AMBIGUOUS, "AU8522" }, }; /* This list is supplied by Hauppauge. Thanks! */ @@ -483,7 +485,7 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, tvee->has_radio = eeprom_data[i+len-1]; /* old style tag, don't know how to detect IR presence, mark as unknown. */ - tvee->has_ir = -1; + tvee->has_ir = 0; tvee->model = eeprom_data[i+8] + (eeprom_data[i+9] << 8); @@ -509,7 +511,7 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, if (audioic < ARRAY_SIZE(audioIC)) tvee->audio_processor = audioIC[audioic].id; else - tvee->audio_processor = AUDIO_CHIP_UNKNOWN; + tvee->audio_processor = V4L2_IDENT_UNKNOWN; break; /* case 0x03: tag 'EEInfo' */ @@ -542,7 +544,7 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, if (audioic < ARRAY_SIZE(audioIC)) tvee->audio_processor = audioIC[audioic].id; else - tvee->audio_processor = AUDIO_CHIP_UNKNOWN; + tvee->audio_processor = V4L2_IDENT_UNKNOWN; break; @@ -603,7 +605,7 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, case 0x0f: /* tag 'IRInfo' */ - tvee->has_ir = eeprom_data[i+1]; + tvee->has_ir = 1 | (eeprom_data[i+1] << 1); break; /* case 0x10: tag 'VBIInfo' */ @@ -690,7 +692,7 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, t_fmt_name2[6], t_fmt_name2[7], t_format2); if (audioic < 0) { tveeprom_info("audio processor is unknown (no idx)\n"); - tvee->audio_processor = AUDIO_CHIP_UNKNOWN; + tvee->audio_processor = V4L2_IDENT_UNKNOWN; } else { if (audioic < ARRAY_SIZE(audioIC)) tveeprom_info("audio processor is %s (idx %d)\n", @@ -703,14 +705,14 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, tveeprom_info("decoder processor is %s (idx %d)\n", STRM(decoderIC, tvee->decoder_processor), tvee->decoder_processor); - if (tvee->has_ir == -1) - tveeprom_info("has %sradio\n", - tvee->has_radio ? "" : "no "); - else + if (tvee->has_ir) tveeprom_info("has %sradio, has %sIR receiver, has %sIR transmitter\n", tvee->has_radio ? "" : "no ", - (tvee->has_ir & 1) ? "" : "no ", - (tvee->has_ir & 2) ? "" : "no "); + (tvee->has_ir & 2) ? "" : "no ", + (tvee->has_ir & 4) ? "" : "no "); + else + tveeprom_info("has %sradio\n", + tvee->has_radio ? "" : "no "); } EXPORT_SYMBOL(tveeprom_hauppauge_analog); diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index 6a3af1005f03..28af5ce5560d 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -6,7 +6,7 @@ */ #include <linux/i2c.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <linux/delay.h> #include <linux/video_decoder.h> #include <media/v4l2-common.h> diff --git a/drivers/media/video/usbvideo/usbvideo.c b/drivers/media/video/usbvideo/usbvideo.c index 4128ee20b64e..bf1bc2f69b02 100644 --- a/drivers/media/video/usbvideo/usbvideo.c +++ b/drivers/media/video/usbvideo/usbvideo.c @@ -952,8 +952,6 @@ static const struct file_operations usbvideo_fops = { .llseek = no_llseek, }; static const struct video_device usbvideo_template = { - .owner = THIS_MODULE, - .type = VID_TYPE_CAPTURE, .fops = &usbvideo_fops, }; @@ -1040,7 +1038,7 @@ int usbvideo_RegisterVideoDevice(struct uvd *uvd) err("%s: uvd->dev == NULL", __func__); return -EINVAL; } - uvd->vdev.dev = &uvd->dev->dev; + uvd->vdev.parent = &uvd->dev->dev; if (video_register_device(&uvd->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { err("%s: video_register_device failed", __func__); return -EPIPE; diff --git a/drivers/media/video/usbvideo/usbvideo.h b/drivers/media/video/usbvideo/usbvideo.h index 051775d4c726..c66985beb8c9 100644 --- a/drivers/media/video/usbvideo/usbvideo.h +++ b/drivers/media/video/usbvideo/usbvideo.h @@ -18,6 +18,7 @@ #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/usb.h> #include <linux/mutex.h> diff --git a/drivers/media/video/usbvideo/vicam.c b/drivers/media/video/usbvideo/vicam.c index 40d053e0d5bf..b7792451a299 100644 --- a/drivers/media/video/usbvideo/vicam.c +++ b/drivers/media/video/usbvideo/vicam.c @@ -41,6 +41,7 @@ #include <linux/videodev.h> #include <linux/usb.h> #include <linux/vmalloc.h> +#include <linux/mm.h> #include <linux/slab.h> #include <linux/mutex.h> #include <linux/firmware.h> @@ -791,9 +792,7 @@ static const struct file_operations vicam_fops = { }; static struct video_device vicam_template = { - .owner = THIS_MODULE, .name = "ViCam-based USB Camera", - .type = VID_TYPE_CAPTURE, .fops = &vicam_fops, .minor = -1, }; diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index abf685464b7c..c317ed7a8482 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -30,7 +30,6 @@ #include <linux/mm.h> #include <linux/utsname.h> #include <linux/highmem.h> -#include <linux/videodev.h> #include <linux/vmalloc.h> #include <linux/module.h> #include <linux/init.h> @@ -43,7 +42,6 @@ #include <media/saa7115.h> #include <media/v4l2-common.h> #include <media/tuner.h> -#include <media/audiochip.h> #include <linux/workqueue.h> diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index cd6c41d67899..b977116a0dd9 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -53,7 +53,6 @@ #include <linux/mm.h> #include <linux/utsname.h> #include <linux/highmem.h> -#include <linux/videodev.h> #include <linux/vmalloc.h> #include <linux/module.h> #include <linux/init.h> @@ -65,8 +64,8 @@ #include <media/saa7115.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <media/tuner.h> -#include <media/audiochip.h> #include <linux/workqueue.h> @@ -184,7 +183,7 @@ MODULE_ALIAS(DRIVER_ALIAS); static inline struct usb_usbvision *cd_to_usbvision(struct device *cd) { struct video_device *vdev = - container_of(cd, struct video_device, class_dev); + container_of(cd, struct video_device, dev); return video_get_drvdata(vdev); } @@ -199,7 +198,7 @@ static ssize_t show_model(struct device *cd, struct device_attribute *attr, char *buf) { struct video_device *vdev = - container_of(cd, struct video_device, class_dev); + container_of(cd, struct video_device, dev); struct usb_usbvision *usbvision = video_get_drvdata(vdev); return sprintf(buf, "%s\n", usbvision_device_data[usbvision->DevModel].ModelString); @@ -210,7 +209,7 @@ static ssize_t show_hue(struct device *cd, struct device_attribute *attr, char *buf) { struct video_device *vdev = - container_of(cd, struct video_device, class_dev); + container_of(cd, struct video_device, dev); struct usb_usbvision *usbvision = video_get_drvdata(vdev); struct v4l2_control ctrl; ctrl.id = V4L2_CID_HUE; @@ -225,7 +224,7 @@ static ssize_t show_contrast(struct device *cd, struct device_attribute *attr, char *buf) { struct video_device *vdev = - container_of(cd, struct video_device, class_dev); + container_of(cd, struct video_device, dev); struct usb_usbvision *usbvision = video_get_drvdata(vdev); struct v4l2_control ctrl; ctrl.id = V4L2_CID_CONTRAST; @@ -240,7 +239,7 @@ static ssize_t show_brightness(struct device *cd, struct device_attribute *attr, char *buf) { struct video_device *vdev = - container_of(cd, struct video_device, class_dev); + container_of(cd, struct video_device, dev); struct usb_usbvision *usbvision = video_get_drvdata(vdev); struct v4l2_control ctrl; ctrl.id = V4L2_CID_BRIGHTNESS; @@ -255,7 +254,7 @@ static ssize_t show_saturation(struct device *cd, struct device_attribute *attr, char *buf) { struct video_device *vdev = - container_of(cd, struct video_device, class_dev); + container_of(cd, struct video_device, dev); struct usb_usbvision *usbvision = video_get_drvdata(vdev); struct v4l2_control ctrl; ctrl.id = V4L2_CID_SATURATION; @@ -270,7 +269,7 @@ static ssize_t show_streaming(struct device *cd, struct device_attribute *attr, char *buf) { struct video_device *vdev = - container_of(cd, struct video_device, class_dev); + container_of(cd, struct video_device, dev); struct usb_usbvision *usbvision = video_get_drvdata(vdev); return sprintf(buf, "%s\n", YES_NO(usbvision->streaming==Stream_On?1:0)); @@ -281,7 +280,7 @@ static ssize_t show_compression(struct device *cd, struct device_attribute *attr, char *buf) { struct video_device *vdev = - container_of(cd, struct video_device, class_dev); + container_of(cd, struct video_device, dev); struct usb_usbvision *usbvision = video_get_drvdata(vdev); return sprintf(buf, "%s\n", YES_NO(usbvision->isocMode==ISOC_MODE_COMPRESS)); @@ -292,7 +291,7 @@ static ssize_t show_device_bridge(struct device *cd, struct device_attribute *attr, char *buf) { struct video_device *vdev = - container_of(cd, struct video_device, class_dev); + container_of(cd, struct video_device, dev); struct usb_usbvision *usbvision = video_get_drvdata(vdev); return sprintf(buf, "%d\n", usbvision->bridgeType); } @@ -304,40 +303,31 @@ static void usbvision_create_sysfs(struct video_device *vdev) if (!vdev) return; do { - res = device_create_file(&vdev->class_dev, - &dev_attr_version); + res = device_create_file(&vdev->dev, &dev_attr_version); if (res<0) break; - res = device_create_file(&vdev->class_dev, - &dev_attr_model); + res = device_create_file(&vdev->dev, &dev_attr_model); if (res<0) break; - res = device_create_file(&vdev->class_dev, - &dev_attr_hue); + res = device_create_file(&vdev->dev, &dev_attr_hue); if (res<0) break; - res = device_create_file(&vdev->class_dev, - &dev_attr_contrast); + res = device_create_file(&vdev->dev, &dev_attr_contrast); if (res<0) break; - res = device_create_file(&vdev->class_dev, - &dev_attr_brightness); + res = device_create_file(&vdev->dev, &dev_attr_brightness); if (res<0) break; - res = device_create_file(&vdev->class_dev, - &dev_attr_saturation); + res = device_create_file(&vdev->dev, &dev_attr_saturation); if (res<0) break; - res = device_create_file(&vdev->class_dev, - &dev_attr_streaming); + res = device_create_file(&vdev->dev, &dev_attr_streaming); if (res<0) break; - res = device_create_file(&vdev->class_dev, - &dev_attr_compression); + res = device_create_file(&vdev->dev, &dev_attr_compression); if (res<0) break; - res = device_create_file(&vdev->class_dev, - &dev_attr_bridge); + res = device_create_file(&vdev->dev, &dev_attr_bridge); if (res>=0) return; } while (0); @@ -348,24 +338,15 @@ static void usbvision_create_sysfs(struct video_device *vdev) static void usbvision_remove_sysfs(struct video_device *vdev) { if (vdev) { - device_remove_file(&vdev->class_dev, - &dev_attr_version); - device_remove_file(&vdev->class_dev, - &dev_attr_model); - device_remove_file(&vdev->class_dev, - &dev_attr_hue); - device_remove_file(&vdev->class_dev, - &dev_attr_contrast); - device_remove_file(&vdev->class_dev, - &dev_attr_brightness); - device_remove_file(&vdev->class_dev, - &dev_attr_saturation); - device_remove_file(&vdev->class_dev, - &dev_attr_streaming); - device_remove_file(&vdev->class_dev, - &dev_attr_compression); - device_remove_file(&vdev->class_dev, - &dev_attr_bridge); + device_remove_file(&vdev->dev, &dev_attr_version); + device_remove_file(&vdev->dev, &dev_attr_model); + device_remove_file(&vdev->dev, &dev_attr_hue); + device_remove_file(&vdev->dev, &dev_attr_contrast); + device_remove_file(&vdev->dev, &dev_attr_brightness); + device_remove_file(&vdev->dev, &dev_attr_saturation); + device_remove_file(&vdev->dev, &dev_attr_streaming); + device_remove_file(&vdev->dev, &dev_attr_compression); + device_remove_file(&vdev->dev, &dev_attr_bridge); } } @@ -1388,13 +1369,8 @@ static const struct file_operations usbvision_fops = { /* .poll = video_poll, */ .compat_ioctl = v4l_compat_ioctl32, }; -static struct video_device usbvision_video_template = { - .owner = THIS_MODULE, - .type = VID_TYPE_TUNER | VID_TYPE_CAPTURE, - .fops = &usbvision_fops, - .name = "usbvision-video", - .release = video_device_release, - .minor = -1, + +static const struct v4l2_ioctl_ops usbvision_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, @@ -1426,6 +1402,14 @@ static struct video_device usbvision_video_template = { .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, #endif +}; + +static struct video_device usbvision_video_template = { + .fops = &usbvision_fops, + .ioctl_ops = &usbvision_ioctl_ops, + .name = "usbvision-video", + .release = video_device_release, + .minor = -1, .tvnorms = USBVISION_NORMS, .current_norm = V4L2_STD_PAL }; @@ -1441,14 +1425,7 @@ static const struct file_operations usbvision_radio_fops = { .compat_ioctl = v4l_compat_ioctl32, }; -static struct video_device usbvision_radio_template= -{ - .owner = THIS_MODULE, - .type = VID_TYPE_TUNER, - .fops = &usbvision_radio_fops, - .name = "usbvision-radio", - .release = video_device_release, - .minor = -1, +static const struct v4l2_ioctl_ops usbvision_radio_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, @@ -1462,6 +1439,14 @@ static struct video_device usbvision_radio_template= .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, +}; + +static struct video_device usbvision_radio_template = { + .fops = &usbvision_radio_fops, + .name = "usbvision-radio", + .release = video_device_release, + .minor = -1, + .ioctl_ops = &usbvision_radio_ioctl_ops, .tvnorms = USBVISION_NORMS, .current_norm = V4L2_STD_PAL @@ -1479,8 +1464,6 @@ static const struct file_operations usbvision_vbi_fops = { static struct video_device usbvision_vbi_template= { - .owner = THIS_MODULE, - .type = VID_TYPE_TUNER, .fops = &usbvision_vbi_fops, .release = video_device_release, .name = "usbvision-vbi", @@ -1506,7 +1489,7 @@ static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision, } *vdev = *vdev_template; // vdev->minor = -1; - vdev->dev = &usb_dev->dev; + vdev->parent = &usb_dev->dev; snprintf(vdev->name, sizeof(vdev->name), "%s", name); video_set_drvdata(vdev, usbvision); return vdev; diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 3ae95512666f..626f4ad7e876 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -195,8 +195,8 @@ static struct uvc_menu_info power_line_frequency_controls[] = { }; static struct uvc_menu_info exposure_auto_controls[] = { - { 1, "Manual Mode" }, { 2, "Auto Mode" }, + { 1, "Manual Mode" }, { 4, "Shutter Priority Mode" }, { 8, "Aperture Priority Mode" }, }; @@ -592,6 +592,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, if (ctrl == NULL) return -EINVAL; + memset(v4l2_ctrl, 0, sizeof *v4l2_ctrl); v4l2_ctrl->id = mapping->id; v4l2_ctrl->type = mapping->v4l2_type; strncpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name); @@ -608,7 +609,8 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, v4l2_ctrl->default_value = uvc_get_le_value(data, mapping); } - if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { + switch (mapping->v4l2_type) { + case V4L2_CTRL_TYPE_MENU: v4l2_ctrl->minimum = 0; v4l2_ctrl->maximum = mapping->menu_count - 1; v4l2_ctrl->step = 1; @@ -622,6 +624,15 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, } return 0; + + case V4L2_CTRL_TYPE_BOOLEAN: + v4l2_ctrl->minimum = 0; + v4l2_ctrl->maximum = 1; + v4l2_ctrl->step = 1; + return 0; + + default: + break; } if (ctrl->info->flags & UVC_CONTROL_GET_MIN) { diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index f2b2983fe062..b3c4d75e8490 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -1458,9 +1458,7 @@ static int uvc_register_video(struct uvc_device *dev) * unregistered before the reference is released, so we don't need to * get another one. */ - vdev->dev = &dev->intf->dev; - vdev->type = 0; - vdev->type2 = 0; + vdev->parent = &dev->intf->dev; vdev->minor = -1; vdev->fops = &uvc_fops; vdev->release = video_device_release; diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c index 7388d0cee3d4..5646a6a32939 100644 --- a/drivers/media/video/uvc/uvc_queue.c +++ b/drivers/media/video/uvc/uvc_queue.c @@ -13,6 +13,7 @@ #include <linux/kernel.h> #include <linux/version.h> +#include <linux/mm.h> #include <linux/list.h> #include <linux/module.h> #include <linux/usb.h> diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index b5a11eb8f9fa..d7bd71be40a9 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -23,6 +23,7 @@ #include <asm/atomic.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include "uvcvideo.h" diff --git a/drivers/media/video/v4l1-compat.c b/drivers/media/video/v4l1-compat.c index a0f6c60279ec..79937d1031fc 100644 --- a/drivers/media/video/v4l1-compat.c +++ b/drivers/media/video/v4l1-compat.c @@ -30,6 +30,7 @@ #include <linux/slab.h> #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <asm/uaccess.h> #include <asm/system.h> diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index e9dd996fd5df..88ca13104417 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -64,7 +64,7 @@ #include <linux/kmod.h> #endif -#include <linux/videodev.h> +#include <linux/videodev2.h> MODULE_AUTHOR("Bill Dirks, Justin Schoeman, Gerd Knorr"); MODULE_DESCRIPTION("misc helper functions for v4l2 device drivers"); diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c new file mode 100644 index 000000000000..556615fe93de --- /dev/null +++ b/drivers/media/video/v4l2-dev.c @@ -0,0 +1,422 @@ +/* + * Video capture interface for Linux version 2 + * + * A generic video device interface for the LINUX operating system + * using a set of device structures/vectors for low level operations. + * + * 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. + * + * Authors: Alan Cox, <alan@redhat.com> (version 1) + * Mauro Carvalho Chehab <mchehab@infradead.org> (version 2) + * + * Fixes: 20000516 Claudio Matsuoka <claudio@conectiva.com> + * - Added procfs support + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/kmod.h> +#include <linux/slab.h> +#include <linux/smp_lock.h> +#include <asm/uaccess.h> +#include <asm/system.h> + +#include <media/v4l2-common.h> + +#define VIDEO_NUM_DEVICES 256 +#define VIDEO_NAME "video4linux" + +/* + * sysfs stuff + */ + +static ssize_t show_index(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vfd = container_of(cd, struct video_device, dev); + return sprintf(buf, "%i\n", vfd->index); +} + +static ssize_t show_name(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vfd = container_of(cd, struct video_device, dev); + return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name); +} + +static struct device_attribute video_device_attrs[] = { + __ATTR(name, S_IRUGO, show_name, NULL), + __ATTR(index, S_IRUGO, show_index, NULL), + __ATTR_NULL +}; + +struct video_device *video_device_alloc(void) +{ + struct video_device *vfd; + + vfd = kzalloc(sizeof(*vfd), GFP_KERNEL); + return vfd; +} +EXPORT_SYMBOL(video_device_alloc); + +void video_device_release(struct video_device *vfd) +{ + kfree(vfd); +} +EXPORT_SYMBOL(video_device_release); + +static void video_release(struct device *cd) +{ + struct video_device *vfd = container_of(cd, struct video_device, dev); + +#if 1 + /* needed until all drivers are fixed */ + if (!vfd->release) + return; +#endif + vfd->release(vfd); +} + +static struct class video_class = { + .name = VIDEO_NAME, + .dev_attrs = video_device_attrs, + .dev_release = video_release, +}; + +/* + * Active devices + */ + +static struct video_device *video_device[VIDEO_NUM_DEVICES]; +static DEFINE_MUTEX(videodev_lock); + +struct video_device *video_devdata(struct file *file) +{ + return video_device[iminor(file->f_path.dentry->d_inode)]; +} +EXPORT_SYMBOL(video_devdata); + +/* + * Open a video device - FIXME: Obsoleted + */ +static int video_open(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + int err = 0; + struct video_device *vfl; + const struct file_operations *old_fops; + + if (minor >= VIDEO_NUM_DEVICES) + return -ENODEV; + lock_kernel(); + mutex_lock(&videodev_lock); + vfl = video_device[minor]; + if (vfl == NULL) { + mutex_unlock(&videodev_lock); + request_module("char-major-%d-%d", VIDEO_MAJOR, minor); + mutex_lock(&videodev_lock); + vfl = video_device[minor]; + if (vfl == NULL) { + mutex_unlock(&videodev_lock); + unlock_kernel(); + return -ENODEV; + } + } + old_fops = file->f_op; + file->f_op = fops_get(vfl->fops); + if (file->f_op->open) + err = file->f_op->open(inode, file); + if (err) { + fops_put(file->f_op); + file->f_op = fops_get(old_fops); + } + fops_put(old_fops); + mutex_unlock(&videodev_lock); + unlock_kernel(); + return err; +} + +/* + * open/release helper functions -- handle exclusive opens + * Should be removed soon + */ +int video_exclusive_open(struct inode *inode, struct file *file) +{ + struct video_device *vfl = video_devdata(file); + int retval = 0; + + mutex_lock(&vfl->lock); + if (vfl->users) + retval = -EBUSY; + else + vfl->users++; + mutex_unlock(&vfl->lock); + return retval; +} +EXPORT_SYMBOL(video_exclusive_open); + +int video_exclusive_release(struct inode *inode, struct file *file) +{ + struct video_device *vfl = video_devdata(file); + + vfl->users--; + return 0; +} +EXPORT_SYMBOL(video_exclusive_release); + +/** + * get_index - assign stream number based on parent device + * @vdev: video_device to assign index number to, vdev->dev should be assigned + * @num: -1 if auto assign, requested number otherwise + * + * + * returns -ENFILE if num is already in use, a free index number if + * successful. + */ +static int get_index(struct video_device *vdev, int num) +{ + u32 used = 0; + const int max_index = sizeof(used) * 8 - 1; + int i; + + /* Currently a single v4l driver instance cannot create more than + 32 devices. + Increase to u64 or an array of u32 if more are needed. */ + if (num > max_index) { + printk(KERN_ERR "videodev: %s num is too large\n", __func__); + return -EINVAL; + } + + for (i = 0; i < VIDEO_NUM_DEVICES; i++) { + if (video_device[i] != NULL && + video_device[i] != vdev && + video_device[i]->parent == vdev->parent) { + used |= 1 << video_device[i]->index; + } + } + + if (num >= 0) { + if (used & (1 << num)) + return -ENFILE; + return num; + } + + i = ffz(used); + return i > max_index ? -ENFILE : i; +} + +static const struct file_operations video_fops; + +int video_register_device(struct video_device *vfd, int type, int nr) +{ + return video_register_device_index(vfd, type, nr, -1); +} +EXPORT_SYMBOL(video_register_device); + +/** + * video_register_device - register video4linux devices + * @vfd: video device structure we want to register + * @type: type of device to register + * @nr: which device number (0 == /dev/video0, 1 == /dev/video1, ... + * -1 == first free) + * + * The registration code assigns minor numbers based on the type + * requested. -ENFILE is returned in all the device slots for this + * category are full. If not then the minor field is set and the + * driver initialize function is called (if non %NULL). + * + * Zero is returned on success. + * + * Valid types are + * + * %VFL_TYPE_GRABBER - A frame grabber + * + * %VFL_TYPE_VTX - A teletext device + * + * %VFL_TYPE_VBI - Vertical blank data (undecoded) + * + * %VFL_TYPE_RADIO - A radio card + */ + +int video_register_device_index(struct video_device *vfd, int type, int nr, + int index) +{ + int i = 0; + int base; + int end; + int ret; + char *name_base; + + switch (type) { + case VFL_TYPE_GRABBER: + base = MINOR_VFL_TYPE_GRABBER_MIN; + end = MINOR_VFL_TYPE_GRABBER_MAX+1; + name_base = "video"; + break; + case VFL_TYPE_VTX: + base = MINOR_VFL_TYPE_VTX_MIN; + end = MINOR_VFL_TYPE_VTX_MAX+1; + name_base = "vtx"; + break; + case VFL_TYPE_VBI: + base = MINOR_VFL_TYPE_VBI_MIN; + end = MINOR_VFL_TYPE_VBI_MAX+1; + name_base = "vbi"; + break; + case VFL_TYPE_RADIO: + base = MINOR_VFL_TYPE_RADIO_MIN; + end = MINOR_VFL_TYPE_RADIO_MAX+1; + name_base = "radio"; + break; + default: + printk(KERN_ERR "%s called with unknown type: %d\n", + __func__, type); + return -1; + } + + /* pick a minor number */ + mutex_lock(&videodev_lock); + if (nr >= 0 && nr < end-base) { + /* use the one the driver asked for */ + i = base + nr; + if (NULL != video_device[i]) { + mutex_unlock(&videodev_lock); + return -ENFILE; + } + } else { + /* use first free */ + for (i = base; i < end; i++) + if (NULL == video_device[i]) + break; + if (i == end) { + mutex_unlock(&videodev_lock); + return -ENFILE; + } + } + video_device[i] = vfd; + vfd->vfl_type = type; + vfd->minor = i; + + ret = get_index(vfd, index); + vfd->index = ret; + + mutex_unlock(&videodev_lock); + + if (ret < 0) { + printk(KERN_ERR "%s: get_index failed\n", __func__); + goto fail_minor; + } + + mutex_init(&vfd->lock); + + /* sysfs class */ + memset(&vfd->dev, 0x00, sizeof(vfd->dev)); + vfd->dev.class = &video_class; + vfd->dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor); + if (vfd->parent) + vfd->dev.parent = vfd->parent; + sprintf(vfd->dev.bus_id, "%s%d", name_base, i - base); + ret = device_register(&vfd->dev); + if (ret < 0) { + printk(KERN_ERR "%s: device_register failed\n", __func__); + goto fail_minor; + } + +#if 1 + /* needed until all drivers are fixed */ + if (!vfd->release) + printk(KERN_WARNING "videodev: \"%s\" has no release callback. " + "Please fix your driver for proper sysfs support, see " + "http://lwn.net/Articles/36850/\n", vfd->name); +#endif + return 0; + +fail_minor: + mutex_lock(&videodev_lock); + video_device[vfd->minor] = NULL; + vfd->minor = -1; + mutex_unlock(&videodev_lock); + return ret; +} +EXPORT_SYMBOL(video_register_device_index); + +/** + * video_unregister_device - unregister a video4linux device + * @vfd: the device to unregister + * + * This unregisters the passed device and deassigns the minor + * number. Future open calls will be met with errors. + */ + +void video_unregister_device(struct video_device *vfd) +{ + mutex_lock(&videodev_lock); + if (video_device[vfd->minor] != vfd) + panic("videodev: bad unregister"); + + video_device[vfd->minor] = NULL; + device_unregister(&vfd->dev); + mutex_unlock(&videodev_lock); +} +EXPORT_SYMBOL(video_unregister_device); + +/* + * Video fs operations + */ +static const struct file_operations video_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = video_open, +}; + +/* + * Initialise video for linux + */ + +static int __init videodev_init(void) +{ + int ret; + + printk(KERN_INFO "Linux video capture interface: v2.00\n"); + if (register_chrdev(VIDEO_MAJOR, VIDEO_NAME, &video_fops)) { + printk(KERN_WARNING "video_dev: unable to get major %d\n", VIDEO_MAJOR); + return -EIO; + } + + ret = class_register(&video_class); + if (ret < 0) { + unregister_chrdev(VIDEO_MAJOR, VIDEO_NAME); + printk(KERN_WARNING "video_dev: class_register failed\n"); + return -EIO; + } + + return 0; +} + +static void __exit videodev_exit(void) +{ + class_unregister(&video_class); + unregister_chrdev(VIDEO_MAJOR, VIDEO_NAME); +} + +module_init(videodev_init) +module_exit(videodev_exit) + +MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>"); +MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2"); +MODULE_LICENSE("GPL"); + + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c new file mode 100644 index 000000000000..fdfe7739c96e --- /dev/null +++ b/drivers/media/video/v4l2-ioctl.c @@ -0,0 +1,1875 @@ +/* + * Video capture interface for Linux version 2 + * + * A generic framework to process V4L2 ioctl commands. + * + * 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. + * + * Authors: Alan Cox, <alan@redhat.com> (version 1) + * Mauro Carvalho Chehab <mchehab@infradead.org> (version 2) + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> + +#define __OLD_VIDIOC_ /* To allow fixing old calls */ +#include <linux/videodev2.h> + +#ifdef CONFIG_VIDEO_V4L1 +#include <linux/videodev.h> +#endif +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <linux/video_decoder.h> + +#define dbgarg(cmd, fmt, arg...) \ + do { \ + if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { \ + printk(KERN_DEBUG "%s: ", vfd->name); \ + v4l_printk_ioctl(cmd); \ + printk(" " fmt, ## arg); \ + } \ + } while (0) + +#define dbgarg2(fmt, arg...) \ + do { \ + if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) \ + printk(KERN_DEBUG "%s: " fmt, vfd->name, ## arg);\ + } while (0) + +struct std_descr { + v4l2_std_id std; + const char *descr; +}; + +static const struct std_descr standards[] = { + { V4L2_STD_NTSC, "NTSC" }, + { V4L2_STD_NTSC_M, "NTSC-M" }, + { V4L2_STD_NTSC_M_JP, "NTSC-M-JP" }, + { V4L2_STD_NTSC_M_KR, "NTSC-M-KR" }, + { V4L2_STD_NTSC_443, "NTSC-443" }, + { V4L2_STD_PAL, "PAL" }, + { V4L2_STD_PAL_BG, "PAL-BG" }, + { V4L2_STD_PAL_B, "PAL-B" }, + { V4L2_STD_PAL_B1, "PAL-B1" }, + { V4L2_STD_PAL_G, "PAL-G" }, + { V4L2_STD_PAL_H, "PAL-H" }, + { V4L2_STD_PAL_I, "PAL-I" }, + { V4L2_STD_PAL_DK, "PAL-DK" }, + { V4L2_STD_PAL_D, "PAL-D" }, + { V4L2_STD_PAL_D1, "PAL-D1" }, + { V4L2_STD_PAL_K, "PAL-K" }, + { V4L2_STD_PAL_M, "PAL-M" }, + { V4L2_STD_PAL_N, "PAL-N" }, + { V4L2_STD_PAL_Nc, "PAL-Nc" }, + { V4L2_STD_PAL_60, "PAL-60" }, + { V4L2_STD_SECAM, "SECAM" }, + { V4L2_STD_SECAM_B, "SECAM-B" }, + { V4L2_STD_SECAM_G, "SECAM-G" }, + { V4L2_STD_SECAM_H, "SECAM-H" }, + { V4L2_STD_SECAM_DK, "SECAM-DK" }, + { V4L2_STD_SECAM_D, "SECAM-D" }, + { V4L2_STD_SECAM_K, "SECAM-K" }, + { V4L2_STD_SECAM_K1, "SECAM-K1" }, + { V4L2_STD_SECAM_L, "SECAM-L" }, + { V4L2_STD_SECAM_LC, "SECAM-Lc" }, + { 0, "Unknown" } +}; + +/* video4linux standard ID conversion to standard name + */ +const char *v4l2_norm_to_name(v4l2_std_id id) +{ + u32 myid = id; + int i; + + /* HACK: ppc32 architecture doesn't have __ucmpdi2 function to handle + 64 bit comparations. So, on that architecture, with some gcc + variants, compilation fails. Currently, the max value is 30bit wide. + */ + BUG_ON(myid != id); + + for (i = 0; standards[i].std; i++) + if (myid == standards[i].std) + break; + return standards[i].descr; +} +EXPORT_SYMBOL(v4l2_norm_to_name); + +/* Fill in the fields of a v4l2_standard structure according to the + 'id' and 'transmission' parameters. Returns negative on error. */ +int v4l2_video_std_construct(struct v4l2_standard *vs, + int id, const char *name) +{ + u32 index = vs->index; + + memset(vs, 0, sizeof(struct v4l2_standard)); + vs->index = index; + vs->id = id; + if (id & V4L2_STD_525_60) { + vs->frameperiod.numerator = 1001; + vs->frameperiod.denominator = 30000; + vs->framelines = 525; + } else { + vs->frameperiod.numerator = 1; + vs->frameperiod.denominator = 25; + vs->framelines = 625; + } + strlcpy(vs->name, name, sizeof(vs->name)); + return 0; +} +EXPORT_SYMBOL(v4l2_video_std_construct); + +/* ----------------------------------------------------------------- */ +/* some arrays for pretty-printing debug messages of enum types */ + +const char *v4l2_field_names[] = { + [V4L2_FIELD_ANY] = "any", + [V4L2_FIELD_NONE] = "none", + [V4L2_FIELD_TOP] = "top", + [V4L2_FIELD_BOTTOM] = "bottom", + [V4L2_FIELD_INTERLACED] = "interlaced", + [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", +}; +EXPORT_SYMBOL(v4l2_field_names); + +const char *v4l2_type_names[] = { + [V4L2_BUF_TYPE_VIDEO_CAPTURE] = "vid-cap", + [V4L2_BUF_TYPE_VIDEO_OVERLAY] = "vid-overlay", + [V4L2_BUF_TYPE_VIDEO_OUTPUT] = "vid-out", + [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] = "sliced-vbi-out", + [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "vid-out-overlay", +}; +EXPORT_SYMBOL(v4l2_type_names); + +static const 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") + +/* ------------------------------------------------------------------ */ +/* debug help functions */ + +#ifdef CONFIG_VIDEO_V4L1_COMPAT +static const char *v4l1_ioctls[] = { + [_IOC_NR(VIDIOCGCAP)] = "VIDIOCGCAP", + [_IOC_NR(VIDIOCGCHAN)] = "VIDIOCGCHAN", + [_IOC_NR(VIDIOCSCHAN)] = "VIDIOCSCHAN", + [_IOC_NR(VIDIOCGTUNER)] = "VIDIOCGTUNER", + [_IOC_NR(VIDIOCSTUNER)] = "VIDIOCSTUNER", + [_IOC_NR(VIDIOCGPICT)] = "VIDIOCGPICT", + [_IOC_NR(VIDIOCSPICT)] = "VIDIOCSPICT", + [_IOC_NR(VIDIOCCAPTURE)] = "VIDIOCCAPTURE", + [_IOC_NR(VIDIOCGWIN)] = "VIDIOCGWIN", + [_IOC_NR(VIDIOCSWIN)] = "VIDIOCSWIN", + [_IOC_NR(VIDIOCGFBUF)] = "VIDIOCGFBUF", + [_IOC_NR(VIDIOCSFBUF)] = "VIDIOCSFBUF", + [_IOC_NR(VIDIOCKEY)] = "VIDIOCKEY", + [_IOC_NR(VIDIOCGFREQ)] = "VIDIOCGFREQ", + [_IOC_NR(VIDIOCSFREQ)] = "VIDIOCSFREQ", + [_IOC_NR(VIDIOCGAUDIO)] = "VIDIOCGAUDIO", + [_IOC_NR(VIDIOCSAUDIO)] = "VIDIOCSAUDIO", + [_IOC_NR(VIDIOCSYNC)] = "VIDIOCSYNC", + [_IOC_NR(VIDIOCMCAPTURE)] = "VIDIOCMCAPTURE", + [_IOC_NR(VIDIOCGMBUF)] = "VIDIOCGMBUF", + [_IOC_NR(VIDIOCGUNIT)] = "VIDIOCGUNIT", + [_IOC_NR(VIDIOCGCAPTURE)] = "VIDIOCGCAPTURE", + [_IOC_NR(VIDIOCSCAPTURE)] = "VIDIOCSCAPTURE", + [_IOC_NR(VIDIOCSPLAYMODE)] = "VIDIOCSPLAYMODE", + [_IOC_NR(VIDIOCSWRITEMODE)] = "VIDIOCSWRITEMODE", + [_IOC_NR(VIDIOCGPLAYINFO)] = "VIDIOCGPLAYINFO", + [_IOC_NR(VIDIOCSMICROCODE)] = "VIDIOCSMICROCODE", + [_IOC_NR(VIDIOCGVBIFMT)] = "VIDIOCGVBIFMT", + [_IOC_NR(VIDIOCSVBIFMT)] = "VIDIOCSVBIFMT" +}; +#define V4L1_IOCTLS ARRAY_SIZE(v4l1_ioctls) +#endif + +static const char *v4l2_ioctls[] = { + [_IOC_NR(VIDIOC_QUERYCAP)] = "VIDIOC_QUERYCAP", + [_IOC_NR(VIDIOC_RESERVED)] = "VIDIOC_RESERVED", + [_IOC_NR(VIDIOC_ENUM_FMT)] = "VIDIOC_ENUM_FMT", + [_IOC_NR(VIDIOC_G_FMT)] = "VIDIOC_G_FMT", + [_IOC_NR(VIDIOC_S_FMT)] = "VIDIOC_S_FMT", + [_IOC_NR(VIDIOC_REQBUFS)] = "VIDIOC_REQBUFS", + [_IOC_NR(VIDIOC_QUERYBUF)] = "VIDIOC_QUERYBUF", + [_IOC_NR(VIDIOC_G_FBUF)] = "VIDIOC_G_FBUF", + [_IOC_NR(VIDIOC_S_FBUF)] = "VIDIOC_S_FBUF", + [_IOC_NR(VIDIOC_OVERLAY)] = "VIDIOC_OVERLAY", + [_IOC_NR(VIDIOC_QBUF)] = "VIDIOC_QBUF", + [_IOC_NR(VIDIOC_DQBUF)] = "VIDIOC_DQBUF", + [_IOC_NR(VIDIOC_STREAMON)] = "VIDIOC_STREAMON", + [_IOC_NR(VIDIOC_STREAMOFF)] = "VIDIOC_STREAMOFF", + [_IOC_NR(VIDIOC_G_PARM)] = "VIDIOC_G_PARM", + [_IOC_NR(VIDIOC_S_PARM)] = "VIDIOC_S_PARM", + [_IOC_NR(VIDIOC_G_STD)] = "VIDIOC_G_STD", + [_IOC_NR(VIDIOC_S_STD)] = "VIDIOC_S_STD", + [_IOC_NR(VIDIOC_ENUMSTD)] = "VIDIOC_ENUMSTD", + [_IOC_NR(VIDIOC_ENUMINPUT)] = "VIDIOC_ENUMINPUT", + [_IOC_NR(VIDIOC_G_CTRL)] = "VIDIOC_G_CTRL", + [_IOC_NR(VIDIOC_S_CTRL)] = "VIDIOC_S_CTRL", + [_IOC_NR(VIDIOC_G_TUNER)] = "VIDIOC_G_TUNER", + [_IOC_NR(VIDIOC_S_TUNER)] = "VIDIOC_S_TUNER", + [_IOC_NR(VIDIOC_G_AUDIO)] = "VIDIOC_G_AUDIO", + [_IOC_NR(VIDIOC_S_AUDIO)] = "VIDIOC_S_AUDIO", + [_IOC_NR(VIDIOC_QUERYCTRL)] = "VIDIOC_QUERYCTRL", + [_IOC_NR(VIDIOC_QUERYMENU)] = "VIDIOC_QUERYMENU", + [_IOC_NR(VIDIOC_G_INPUT)] = "VIDIOC_G_INPUT", + [_IOC_NR(VIDIOC_S_INPUT)] = "VIDIOC_S_INPUT", + [_IOC_NR(VIDIOC_G_OUTPUT)] = "VIDIOC_G_OUTPUT", + [_IOC_NR(VIDIOC_S_OUTPUT)] = "VIDIOC_S_OUTPUT", + [_IOC_NR(VIDIOC_ENUMOUTPUT)] = "VIDIOC_ENUMOUTPUT", + [_IOC_NR(VIDIOC_G_AUDOUT)] = "VIDIOC_G_AUDOUT", + [_IOC_NR(VIDIOC_S_AUDOUT)] = "VIDIOC_S_AUDOUT", + [_IOC_NR(VIDIOC_G_MODULATOR)] = "VIDIOC_G_MODULATOR", + [_IOC_NR(VIDIOC_S_MODULATOR)] = "VIDIOC_S_MODULATOR", + [_IOC_NR(VIDIOC_G_FREQUENCY)] = "VIDIOC_G_FREQUENCY", + [_IOC_NR(VIDIOC_S_FREQUENCY)] = "VIDIOC_S_FREQUENCY", + [_IOC_NR(VIDIOC_CROPCAP)] = "VIDIOC_CROPCAP", + [_IOC_NR(VIDIOC_G_CROP)] = "VIDIOC_G_CROP", + [_IOC_NR(VIDIOC_S_CROP)] = "VIDIOC_S_CROP", + [_IOC_NR(VIDIOC_G_JPEGCOMP)] = "VIDIOC_G_JPEGCOMP", + [_IOC_NR(VIDIOC_S_JPEGCOMP)] = "VIDIOC_S_JPEGCOMP", + [_IOC_NR(VIDIOC_QUERYSTD)] = "VIDIOC_QUERYSTD", + [_IOC_NR(VIDIOC_TRY_FMT)] = "VIDIOC_TRY_FMT", + [_IOC_NR(VIDIOC_ENUMAUDIO)] = "VIDIOC_ENUMAUDIO", + [_IOC_NR(VIDIOC_ENUMAUDOUT)] = "VIDIOC_ENUMAUDOUT", + [_IOC_NR(VIDIOC_G_PRIORITY)] = "VIDIOC_G_PRIORITY", + [_IOC_NR(VIDIOC_S_PRIORITY)] = "VIDIOC_S_PRIORITY", + [_IOC_NR(VIDIOC_G_SLICED_VBI_CAP)] = "VIDIOC_G_SLICED_VBI_CAP", + [_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", +#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", + [_IOC_NR(VIDIOC_S_HW_FREQ_SEEK)] = "VIDIOC_S_HW_FREQ_SEEK", +#endif +}; +#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) + +static const char *v4l2_int_ioctls[] = { +#ifdef CONFIG_VIDEO_V4L1_COMPAT + [_IOC_NR(DECODER_GET_CAPABILITIES)] = "DECODER_GET_CAPABILITIES", + [_IOC_NR(DECODER_GET_STATUS)] = "DECODER_GET_STATUS", + [_IOC_NR(DECODER_SET_NORM)] = "DECODER_SET_NORM", + [_IOC_NR(DECODER_SET_INPUT)] = "DECODER_SET_INPUT", + [_IOC_NR(DECODER_SET_OUTPUT)] = "DECODER_SET_OUTPUT", + [_IOC_NR(DECODER_ENABLE_OUTPUT)] = "DECODER_ENABLE_OUTPUT", + [_IOC_NR(DECODER_SET_PICTURE)] = "DECODER_SET_PICTURE", + [_IOC_NR(DECODER_SET_GPIO)] = "DECODER_SET_GPIO", + [_IOC_NR(DECODER_INIT)] = "DECODER_INIT", + [_IOC_NR(DECODER_SET_VBI_BYPASS)] = "DECODER_SET_VBI_BYPASS", + [_IOC_NR(DECODER_DUMP)] = "DECODER_DUMP", +#endif + [_IOC_NR(AUDC_SET_RADIO)] = "AUDC_SET_RADIO", + + [_IOC_NR(TUNER_SET_TYPE_ADDR)] = "TUNER_SET_TYPE_ADDR", + [_IOC_NR(TUNER_SET_STANDBY)] = "TUNER_SET_STANDBY", + [_IOC_NR(TUNER_SET_CONFIG)] = "TUNER_SET_CONFIG", + + [_IOC_NR(VIDIOC_INT_S_TUNER_MODE)] = "VIDIOC_INT_S_TUNER_MODE", + [_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_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_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) + +/* Common ioctl debug function. This function can be used by + external ioctl messages as well as internal V4L ioctl */ +void v4l_printk_ioctl(unsigned int cmd) +{ + char *dir, *type; + + switch (_IOC_TYPE(cmd)) { + case 'd': + if (_IOC_NR(cmd) >= V4L2_INT_IOCTLS) { + type = "v4l2_int"; + break; + } + printk("%s", v4l2_int_ioctls[_IOC_NR(cmd)]); + return; +#ifdef CONFIG_VIDEO_V4L1_COMPAT + case 'v': + if (_IOC_NR(cmd) >= V4L1_IOCTLS) { + type = "v4l1"; + break; + } + printk("%s", v4l1_ioctls[_IOC_NR(cmd)]); + return; +#endif + case 'V': + if (_IOC_NR(cmd) >= V4L2_IOCTLS) { + type = "v4l2"; + break; + } + printk("%s", v4l2_ioctls[_IOC_NR(cmd)]); + return; + default: + type = "unknown"; + } + + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: dir = "--"; break; + case _IOC_READ: dir = "r-"; break; + case _IOC_WRITE: dir = "-w"; break; + case _IOC_READ | _IOC_WRITE: dir = "rw"; break; + default: dir = "*ERR*"; break; + } + printk("%s ioctl '%c', dir=%s, #%d (0x%08x)", + type, _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd); +} +EXPORT_SYMBOL(v4l_printk_ioctl); + +/* + * helper function -- handles userspace copying for ioctl arguments + */ + +#ifdef __OLD_VIDIOC_ +static unsigned int +video_fix_command(unsigned int cmd) +{ + switch (cmd) { + case VIDIOC_OVERLAY_OLD: + cmd = VIDIOC_OVERLAY; + break; + case VIDIOC_S_PARM_OLD: + cmd = VIDIOC_S_PARM; + break; + case VIDIOC_S_CTRL_OLD: + cmd = VIDIOC_S_CTRL; + break; + case VIDIOC_G_AUDIO_OLD: + cmd = VIDIOC_G_AUDIO; + break; + case VIDIOC_G_AUDOUT_OLD: + cmd = VIDIOC_G_AUDOUT; + break; + case VIDIOC_CROPCAP_OLD: + cmd = VIDIOC_CROPCAP; + break; + } + return cmd; +} +#endif + +/* + * Obsolete usercopy function - Should be removed soon + */ +int +video_usercopy(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg, + int (*func)(struct inode *inode, struct file *file, + unsigned int cmd, void *arg)) +{ + char sbuf[128]; + void *mbuf = NULL; + void *parg = NULL; + int err = -EINVAL; + int is_ext_ctrl; + size_t ctrls_size = 0; + void __user *user_ptr = NULL; + +#ifdef __OLD_VIDIOC_ + cmd = video_fix_command(cmd); +#endif + is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS || + cmd == VIDIOC_TRY_EXT_CTRLS); + + /* Copy arguments into temp kernel buffer */ + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: + parg = NULL; + break; + case _IOC_READ: + case _IOC_WRITE: + case (_IOC_WRITE | _IOC_READ): + if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { + parg = sbuf; + } else { + /* too big to allocate from stack */ + mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); + if (NULL == mbuf) + return -ENOMEM; + parg = mbuf; + } + + err = -EFAULT; + if (_IOC_DIR(cmd) & _IOC_WRITE) + if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) + goto out; + break; + } + if (is_ext_ctrl) { + struct v4l2_ext_controls *p = parg; + + /* In case of an error, tell the caller that it wasn't + a specific control that caused it. */ + p->error_idx = p->count; + user_ptr = (void __user *)p->controls; + if (p->count) { + ctrls_size = sizeof(struct v4l2_ext_control) * p->count; + /* Note: v4l2_ext_controls fits in sbuf[] so mbuf is still NULL. */ + mbuf = kmalloc(ctrls_size, GFP_KERNEL); + err = -ENOMEM; + if (NULL == mbuf) + goto out_ext_ctrl; + err = -EFAULT; + if (copy_from_user(mbuf, user_ptr, ctrls_size)) + goto out_ext_ctrl; + p->controls = mbuf; + } + } + + /* call driver */ + err = func(inode, file, cmd, parg); + if (err == -ENOIOCTLCMD) + err = -EINVAL; + if (is_ext_ctrl) { + struct v4l2_ext_controls *p = parg; + + p->controls = (void *)user_ptr; + if (p->count && err == 0 && copy_to_user(user_ptr, mbuf, ctrls_size)) + err = -EFAULT; + goto out_ext_ctrl; + } + if (err < 0) + goto out; + +out_ext_ctrl: + /* Copy results into user buffer */ + switch (_IOC_DIR(cmd)) { + case _IOC_READ: + case (_IOC_WRITE | _IOC_READ): + if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) + err = -EFAULT; + break; + } + +out: + kfree(mbuf); + return err; +} +EXPORT_SYMBOL(video_usercopy); + +static void dbgbuf(unsigned int cmd, struct video_device *vfd, + struct v4l2_buffer *p) +{ + struct v4l2_timecode *tc = &p->timecode; + + dbgarg(cmd, "%02ld:%02d:%02d.%08ld index=%d, type=%s, " + "bytesused=%d, flags=0x%08d, " + "field=%0d, sequence=%d, memory=%s, offset/userptr=0x%08lx, length=%d\n", + 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, p->length); + dbgarg2("timecode=%02d:%02d:%02d type=%d, " + "flags=0x%08d, frames=%d, userbits=0x%08x\n", + tc->hours, tc->minutes, tc->seconds, + tc->type, tc->flags, tc->frames, *(__u32 *)tc->userbits); +} + +static inline void dbgrect(struct video_device *vfd, char *s, + struct v4l2_rect *r) +{ + dbgarg2("%sRect start at %dx%d, size=%dx%d\n", s, r->left, r->top, + r->width, r->height); +}; + +static inline void v4l_print_pix_fmt(struct video_device *vfd, + struct v4l2_pix_format *fmt) +{ + dbgarg2("width=%d, height=%d, format=%c%c%c%c, field=%s, " + "bytesperline=%d sizeimage=%d, colorspace=%d\n", + fmt->width, fmt->height, + (fmt->pixelformat & 0xff), + (fmt->pixelformat >> 8) & 0xff, + (fmt->pixelformat >> 16) & 0xff, + (fmt->pixelformat >> 24) & 0xff, + prt_names(fmt->field, v4l2_field_names), + fmt->bytesperline, fmt->sizeimage, fmt->colorspace); +}; + +static inline void v4l_print_ext_ctrls(unsigned int cmd, + struct video_device *vfd, struct v4l2_ext_controls *c, int show_vals) +{ + __u32 i; + + if (!(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) + return; + dbgarg(cmd, ""); + printk(KERN_CONT "class=0x%x", c->ctrl_class); + for (i = 0; i < c->count; i++) { + if (show_vals) + printk(KERN_CONT " id/val=0x%x/0x%x", + c->controls[i].id, c->controls[i].value); + else + printk(KERN_CONT " id=0x%x", c->controls[i].id); + } + printk(KERN_CONT "\n"); +}; + +static inline int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv) +{ + __u32 i; + + /* zero the reserved fields */ + c->reserved[0] = c->reserved[1] = 0; + for (i = 0; i < c->count; i++) { + c->controls[i].reserved2[0] = 0; + c->controls[i].reserved2[1] = 0; + } + /* V4L2_CID_PRIVATE_BASE cannot be used as control class + when using extended controls. + Only when passed in through VIDIOC_G_CTRL and VIDIOC_S_CTRL + is it allowed for backwards compatibility. + */ + if (!allow_priv && c->ctrl_class == V4L2_CID_PRIVATE_BASE) + return 0; + /* Check that all controls are from the same control class. */ + for (i = 0; i < c->count; i++) { + if (V4L2_CTRL_ID2CLASS(c->controls[i].id) != c->ctrl_class) { + c->error_idx = i; + return 0; + } + } + return 1; +} + +static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type) +{ + if (ops == NULL) + return -EINVAL; + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (ops->vidioc_try_fmt_vid_cap) + return 0; + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (ops->vidioc_try_fmt_vid_overlay) + return 0; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (ops->vidioc_try_fmt_vid_out) + return 0; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (ops->vidioc_try_fmt_vid_out_overlay) + return 0; + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (ops->vidioc_try_fmt_vbi_cap) + return 0; + break; + case V4L2_BUF_TYPE_VBI_OUTPUT: + if (ops->vidioc_try_fmt_vbi_out) + return 0; + break; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + if (ops->vidioc_try_fmt_sliced_vbi_cap) + return 0; + break; + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + if (ops->vidioc_try_fmt_sliced_vbi_out) + return 0; + break; + case V4L2_BUF_TYPE_PRIVATE: + if (ops->vidioc_try_fmt_type_private) + return 0; + break; + } + return -EINVAL; +} + +static int __video_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *vfd = video_devdata(file); + const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; + void *fh = file->private_data; + int ret = -EINVAL; + + if ((vfd->debug & V4L2_DEBUG_IOCTL) && + !(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) { + v4l_print_ioctl(vfd->name, cmd); + printk(KERN_CONT "\n"); + } + + if (ops == NULL) { + printk(KERN_WARNING "videodev: \"%s\" has no ioctl_ops.\n", + vfd->name); + return -EINVAL; + } + +#ifdef CONFIG_VIDEO_V4L1_COMPAT + /*********************************************************** + Handles calls to the obsoleted V4L1 API + Due to the nature of VIDIOCGMBUF, each driver that supports + V4L1 should implement its own handler for this ioctl. + ***********************************************************/ + + /* --- streaming capture ------------------------------------- */ + if (cmd == VIDIOCGMBUF) { + struct video_mbuf *p = arg; + + memset(p, 0, sizeof(*p)); + + if (!ops->vidiocgmbuf) + return ret; + ret = ops->vidiocgmbuf(file, fh, p); + if (!ret) + dbgarg(cmd, "size=%d, frames=%d, offsets=0x%08lx\n", + p->size, p->frames, + (unsigned long)p->offsets); + return ret; + } + + /******************************************************** + All other V4L1 calls are handled by v4l1_compat module. + Those calls will be translated into V4L2 calls, and + __video_do_ioctl will be called again, with one or more + V4L2 ioctls. + ********************************************************/ + if (_IOC_TYPE(cmd) == 'v') + return v4l_compat_translate_ioctl(inode, file, cmd, arg, + __video_do_ioctl); +#endif + + switch (cmd) { + /* --- capabilities ------------------------------------------ */ + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = (struct v4l2_capability *)arg; + memset(cap, 0, sizeof(*cap)); + + if (!ops->vidioc_querycap) + break; + + ret = ops->vidioc_querycap(file, fh, cap); + if (!ret) + dbgarg(cmd, "driver=%s, card=%s, bus=%s, " + "version=0x%08x, " + "capabilities=0x%08x\n", + cap->driver, cap->card, cap->bus_info, + cap->version, + cap->capabilities); + break; + } + + /* --- priority ------------------------------------------ */ + case VIDIOC_G_PRIORITY: + { + enum v4l2_priority *p = arg; + + if (!ops->vidioc_g_priority) + break; + ret = ops->vidioc_g_priority(file, fh, p); + if (!ret) + dbgarg(cmd, "priority is %d\n", *p); + break; + } + case VIDIOC_S_PRIORITY: + { + enum v4l2_priority *p = arg; + + if (!ops->vidioc_s_priority) + break; + dbgarg(cmd, "setting priority to %d\n", *p); + ret = ops->vidioc_s_priority(file, fh, *p); + break; + } + + /* --- 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; + memset(f, 0, sizeof(*f)); + f->index = index; + f->type = type; + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (ops->vidioc_enum_fmt_vid_cap) + ret = ops->vidioc_enum_fmt_vid_cap(file, fh, f); + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (ops->vidioc_enum_fmt_vid_overlay) + ret = ops->vidioc_enum_fmt_vid_overlay(file, + fh, f); + break; +#if 1 + /* V4L2_BUF_TYPE_VBI_CAPTURE should not support VIDIOC_ENUM_FMT + * according to the spec. The bttv and saa7134 drivers support + * it though, so just warn that this is deprecated and will be + * removed in the near future. */ + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (ops->vidioc_enum_fmt_vbi_cap) { + printk(KERN_WARNING "vidioc_enum_fmt_vbi_cap will be removed in 2.6.28!\n"); + ret = ops->vidioc_enum_fmt_vbi_cap(file, fh, f); + } + break; +#endif + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (ops->vidioc_enum_fmt_vid_out) + ret = ops->vidioc_enum_fmt_vid_out(file, fh, f); + break; + case V4L2_BUF_TYPE_PRIVATE: + if (ops->vidioc_enum_fmt_type_private) + ret = ops->vidioc_enum_fmt_type_private(file, + fh, f); + break; + default: + break; + } + if (!ret) + dbgarg(cmd, "index=%d, type=%d, flags=%d, " + "pixelformat=%c%c%c%c, description='%s'\n", + f->index, f->type, f->flags, + (f->pixelformat & 0xff), + (f->pixelformat >> 8) & 0xff, + (f->pixelformat >> 16) & 0xff, + (f->pixelformat >> 24) & 0xff, + f->description); + break; + } + case VIDIOC_G_FMT: + { + struct v4l2_format *f = (struct v4l2_format *)arg; + + memset(f->fmt.raw_data, 0, sizeof(f->fmt.raw_data)); + + /* FIXME: Should be one dump per type */ + dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names)); + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (ops->vidioc_g_fmt_vid_cap) + ret = ops->vidioc_g_fmt_vid_cap(file, fh, f); + if (!ret) + v4l_print_pix_fmt(vfd, &f->fmt.pix); + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (ops->vidioc_g_fmt_vid_overlay) + ret = ops->vidioc_g_fmt_vid_overlay(file, + fh, f); + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (ops->vidioc_g_fmt_vid_out) + ret = ops->vidioc_g_fmt_vid_out(file, fh, f); + if (!ret) + v4l_print_pix_fmt(vfd, &f->fmt.pix); + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (ops->vidioc_g_fmt_vid_out_overlay) + ret = ops->vidioc_g_fmt_vid_out_overlay(file, + fh, f); + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (ops->vidioc_g_fmt_vbi_cap) + ret = ops->vidioc_g_fmt_vbi_cap(file, fh, f); + break; + case V4L2_BUF_TYPE_VBI_OUTPUT: + if (ops->vidioc_g_fmt_vbi_out) + ret = ops->vidioc_g_fmt_vbi_out(file, fh, f); + break; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + if (ops->vidioc_g_fmt_sliced_vbi_cap) + ret = ops->vidioc_g_fmt_sliced_vbi_cap(file, + fh, f); + break; + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + if (ops->vidioc_g_fmt_sliced_vbi_out) + ret = ops->vidioc_g_fmt_sliced_vbi_out(file, + fh, f); + break; + case V4L2_BUF_TYPE_PRIVATE: + if (ops->vidioc_g_fmt_type_private) + ret = ops->vidioc_g_fmt_type_private(file, + fh, f); + break; + } + + break; + } + case VIDIOC_S_FMT: + { + struct v4l2_format *f = (struct v4l2_format *)arg; + + /* FIXME: Should be one dump per type */ + dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names)); + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + v4l_print_pix_fmt(vfd, &f->fmt.pix); + if (ops->vidioc_s_fmt_vid_cap) + ret = ops->vidioc_s_fmt_vid_cap(file, fh, f); + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (ops->vidioc_s_fmt_vid_overlay) + ret = ops->vidioc_s_fmt_vid_overlay(file, + fh, f); + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + v4l_print_pix_fmt(vfd, &f->fmt.pix); + if (ops->vidioc_s_fmt_vid_out) + ret = ops->vidioc_s_fmt_vid_out(file, fh, f); + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (ops->vidioc_s_fmt_vid_out_overlay) + ret = ops->vidioc_s_fmt_vid_out_overlay(file, + fh, f); + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (ops->vidioc_s_fmt_vbi_cap) + ret = ops->vidioc_s_fmt_vbi_cap(file, fh, f); + break; + case V4L2_BUF_TYPE_VBI_OUTPUT: + if (ops->vidioc_s_fmt_vbi_out) + ret = ops->vidioc_s_fmt_vbi_out(file, fh, f); + break; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + if (ops->vidioc_s_fmt_sliced_vbi_cap) + ret = ops->vidioc_s_fmt_sliced_vbi_cap(file, + fh, f); + break; + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + if (ops->vidioc_s_fmt_sliced_vbi_out) + ret = ops->vidioc_s_fmt_sliced_vbi_out(file, + fh, f); + break; + case V4L2_BUF_TYPE_PRIVATE: + if (ops->vidioc_s_fmt_type_private) + ret = ops->vidioc_s_fmt_type_private(file, + fh, f); + break; + } + break; + } + case VIDIOC_TRY_FMT: + { + struct v4l2_format *f = (struct v4l2_format *)arg; + + /* FIXME: Should be one dump per type */ + dbgarg(cmd, "type=%s\n", prt_names(f->type, + v4l2_type_names)); + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (ops->vidioc_try_fmt_vid_cap) + ret = ops->vidioc_try_fmt_vid_cap(file, fh, f); + if (!ret) + v4l_print_pix_fmt(vfd, &f->fmt.pix); + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (ops->vidioc_try_fmt_vid_overlay) + ret = ops->vidioc_try_fmt_vid_overlay(file, + fh, f); + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (ops->vidioc_try_fmt_vid_out) + ret = ops->vidioc_try_fmt_vid_out(file, fh, f); + if (!ret) + v4l_print_pix_fmt(vfd, &f->fmt.pix); + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (ops->vidioc_try_fmt_vid_out_overlay) + ret = ops->vidioc_try_fmt_vid_out_overlay(file, + fh, f); + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (ops->vidioc_try_fmt_vbi_cap) + ret = ops->vidioc_try_fmt_vbi_cap(file, fh, f); + break; + case V4L2_BUF_TYPE_VBI_OUTPUT: + if (ops->vidioc_try_fmt_vbi_out) + ret = ops->vidioc_try_fmt_vbi_out(file, fh, f); + break; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + if (ops->vidioc_try_fmt_sliced_vbi_cap) + ret = ops->vidioc_try_fmt_sliced_vbi_cap(file, + fh, f); + break; + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + if (ops->vidioc_try_fmt_sliced_vbi_out) + ret = ops->vidioc_try_fmt_sliced_vbi_out(file, + fh, f); + break; + case V4L2_BUF_TYPE_PRIVATE: + if (ops->vidioc_try_fmt_type_private) + ret = ops->vidioc_try_fmt_type_private(file, + fh, f); + break; + } + + break; + } + /* FIXME: Those buf reqs could be handled here, + with some changes on videobuf to allow its header to be included at + videodev2.h or being merged at videodev2. + */ + case VIDIOC_REQBUFS: + { + struct v4l2_requestbuffers *p = arg; + + if (!ops->vidioc_reqbufs) + break; + ret = check_fmt(ops, p->type); + if (ret) + break; + + ret = ops->vidioc_reqbufs(file, fh, p); + dbgarg(cmd, "count=%d, type=%s, memory=%s\n", + p->count, + prt_names(p->type, v4l2_type_names), + prt_names(p->memory, v4l2_memory_names)); + break; + } + case VIDIOC_QUERYBUF: + { + struct v4l2_buffer *p = arg; + + if (!ops->vidioc_querybuf) + break; + ret = check_fmt(ops, p->type); + if (ret) + break; + + ret = ops->vidioc_querybuf(file, fh, p); + if (!ret) + dbgbuf(cmd, vfd, p); + break; + } + case VIDIOC_QBUF: + { + struct v4l2_buffer *p = arg; + + if (!ops->vidioc_qbuf) + break; + ret = check_fmt(ops, p->type); + if (ret) + break; + + ret = ops->vidioc_qbuf(file, fh, p); + if (!ret) + dbgbuf(cmd, vfd, p); + break; + } + case VIDIOC_DQBUF: + { + struct v4l2_buffer *p = arg; + + if (!ops->vidioc_dqbuf) + break; + ret = check_fmt(ops, p->type); + if (ret) + break; + + ret = ops->vidioc_dqbuf(file, fh, p); + if (!ret) + dbgbuf(cmd, vfd, p); + break; + } + case VIDIOC_OVERLAY: + { + int *i = arg; + + if (!ops->vidioc_overlay) + break; + dbgarg(cmd, "value=%d\n", *i); + ret = ops->vidioc_overlay(file, fh, *i); + break; + } + case VIDIOC_G_FBUF: + { + struct v4l2_framebuffer *p = arg; + + if (!ops->vidioc_g_fbuf) + break; + ret = ops->vidioc_g_fbuf(file, fh, arg); + if (!ret) { + dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n", + p->capability, p->flags, + (unsigned long)p->base); + v4l_print_pix_fmt(vfd, &p->fmt); + } + break; + } + case VIDIOC_S_FBUF: + { + struct v4l2_framebuffer *p = arg; + + if (!ops->vidioc_s_fbuf) + break; + dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n", + p->capability, p->flags, (unsigned long)p->base); + v4l_print_pix_fmt(vfd, &p->fmt); + ret = ops->vidioc_s_fbuf(file, fh, arg); + break; + } + case VIDIOC_STREAMON: + { + enum v4l2_buf_type i = *(int *)arg; + + if (!ops->vidioc_streamon) + break; + dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names)); + ret = ops->vidioc_streamon(file, fh, i); + break; + } + case VIDIOC_STREAMOFF: + { + enum v4l2_buf_type i = *(int *)arg; + + if (!ops->vidioc_streamoff) + break; + dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names)); + ret = ops->vidioc_streamoff(file, fh, i); + break; + } + /* ---------- tv norms ---------- */ + case VIDIOC_ENUMSTD: + { + struct v4l2_standard *p = arg; + v4l2_std_id id = vfd->tvnorms, curr_id = 0; + unsigned int index = p->index, i, j = 0; + const char *descr = ""; + + /* Return norm array in a canonical way */ + for (i = 0; i <= index && id; i++) { + /* last std value in the standards array is 0, so this + while always ends there since (id & 0) == 0. */ + while ((id & standards[j].std) != standards[j].std) + j++; + curr_id = standards[j].std; + descr = standards[j].descr; + j++; + if (curr_id == 0) + break; + if (curr_id != V4L2_STD_PAL && + curr_id != V4L2_STD_SECAM && + curr_id != V4L2_STD_NTSC) + id &= ~curr_id; + } + if (i <= index) + return -EINVAL; + + v4l2_video_std_construct(p, curr_id, descr); + p->index = index; + + dbgarg(cmd, "index=%d, id=0x%Lx, name=%s, fps=%d/%d, " + "framelines=%d\n", p->index, + (unsigned long long)p->id, p->name, + p->frameperiod.numerator, + p->frameperiod.denominator, + p->framelines); + + ret = 0; + break; + } + case VIDIOC_G_STD: + { + v4l2_std_id *id = arg; + + ret = 0; + /* Calls the specific handler */ + if (ops->vidioc_g_std) + ret = ops->vidioc_g_std(file, fh, id); + else + *id = vfd->current_norm; + + if (!ret) + dbgarg(cmd, "std=0x%08Lx\n", (long long unsigned)*id); + break; + } + case VIDIOC_S_STD: + { + v4l2_std_id *id = arg, norm; + + dbgarg(cmd, "std=%08Lx\n", (long long unsigned)*id); + + norm = (*id) & vfd->tvnorms; + if (vfd->tvnorms && !norm) /* Check if std is supported */ + break; + + /* Calls the specific handler */ + if (ops->vidioc_s_std) + ret = ops->vidioc_s_std(file, fh, &norm); + else + ret = -EINVAL; + + /* Updates standard information */ + if (ret >= 0) + vfd->current_norm = norm; + break; + } + case VIDIOC_QUERYSTD: + { + v4l2_std_id *p = arg; + + if (!ops->vidioc_querystd) + break; + ret = ops->vidioc_querystd(file, fh, arg); + if (!ret) + dbgarg(cmd, "detected std=%08Lx\n", + (unsigned long long)*p); + break; + } + /* ------ input switching ---------- */ + /* FIXME: Inputs can be handled inside videodev2 */ + case VIDIOC_ENUMINPUT: + { + struct v4l2_input *p = arg; + int i = p->index; + + if (!ops->vidioc_enum_input) + break; + memset(p, 0, sizeof(*p)); + p->index = i; + + ret = ops->vidioc_enum_input(file, fh, p); + if (!ret) + dbgarg(cmd, "index=%d, name=%s, type=%d, " + "audioset=%d, " + "tuner=%d, std=%08Lx, status=%d\n", + p->index, p->name, p->type, p->audioset, + p->tuner, + (unsigned long long)p->std, + p->status); + break; + } + case VIDIOC_G_INPUT: + { + unsigned int *i = arg; + + if (!ops->vidioc_g_input) + break; + ret = ops->vidioc_g_input(file, fh, i); + if (!ret) + dbgarg(cmd, "value=%d\n", *i); + break; + } + case VIDIOC_S_INPUT: + { + unsigned int *i = arg; + + if (!ops->vidioc_s_input) + break; + dbgarg(cmd, "value=%d\n", *i); + ret = ops->vidioc_s_input(file, fh, *i); + break; + } + + /* ------ output switching ---------- */ + case VIDIOC_ENUMOUTPUT: + { + struct v4l2_output *p = arg; + int i = p->index; + + if (!ops->vidioc_enum_output) + break; + memset(p, 0, sizeof(*p)); + p->index = i; + + ret = ops->vidioc_enum_output(file, fh, p); + if (!ret) + dbgarg(cmd, "index=%d, name=%s, type=%d, " + "audioset=0x%x, " + "modulator=%d, std=0x%08Lx\n", + p->index, p->name, p->type, p->audioset, + p->modulator, (unsigned long long)p->std); + break; + } + case VIDIOC_G_OUTPUT: + { + unsigned int *i = arg; + + if (!ops->vidioc_g_output) + break; + ret = ops->vidioc_g_output(file, fh, i); + if (!ret) + dbgarg(cmd, "value=%d\n", *i); + break; + } + case VIDIOC_S_OUTPUT: + { + unsigned int *i = arg; + + if (!ops->vidioc_s_output) + break; + dbgarg(cmd, "value=%d\n", *i); + ret = ops->vidioc_s_output(file, fh, *i); + break; + } + + /* --- controls ---------------------------------------------- */ + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *p = arg; + + if (!ops->vidioc_queryctrl) + break; + ret = ops->vidioc_queryctrl(file, fh, p); + if (!ret) + dbgarg(cmd, "id=0x%x, type=%d, name=%s, min/max=%d/%d, " + "step=%d, default=%d, flags=0x%08x\n", + p->id, p->type, p->name, + p->minimum, p->maximum, + p->step, p->default_value, p->flags); + else + dbgarg(cmd, "id=0x%x\n", p->id); + break; + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *p = arg; + + if (ops->vidioc_g_ctrl) + ret = ops->vidioc_g_ctrl(file, fh, p); + else if (ops->vidioc_g_ext_ctrls) { + struct v4l2_ext_controls ctrls; + struct v4l2_ext_control ctrl; + + ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id); + ctrls.count = 1; + ctrls.controls = &ctrl; + ctrl.id = p->id; + ctrl.value = p->value; + if (check_ext_ctrls(&ctrls, 1)) { + ret = ops->vidioc_g_ext_ctrls(file, fh, &ctrls); + if (ret == 0) + p->value = ctrl.value; + } + } else + break; + if (!ret) + dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value); + else + dbgarg(cmd, "id=0x%x\n", p->id); + break; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *p = arg; + struct v4l2_ext_controls ctrls; + struct v4l2_ext_control ctrl; + + if (!ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls) + break; + + dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value); + + if (ops->vidioc_s_ctrl) { + ret = ops->vidioc_s_ctrl(file, fh, p); + break; + } + if (!ops->vidioc_s_ext_ctrls) + break; + + ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id); + ctrls.count = 1; + ctrls.controls = &ctrl; + ctrl.id = p->id; + ctrl.value = p->value; + if (check_ext_ctrls(&ctrls, 1)) + ret = ops->vidioc_s_ext_ctrls(file, fh, &ctrls); + break; + } + case VIDIOC_G_EXT_CTRLS: + { + struct v4l2_ext_controls *p = arg; + + p->error_idx = p->count; + if (!ops->vidioc_g_ext_ctrls) + break; + if (check_ext_ctrls(p, 0)) + ret = ops->vidioc_g_ext_ctrls(file, fh, p); + v4l_print_ext_ctrls(cmd, vfd, p, !ret); + break; + } + case VIDIOC_S_EXT_CTRLS: + { + struct v4l2_ext_controls *p = arg; + + p->error_idx = p->count; + if (!ops->vidioc_s_ext_ctrls) + break; + v4l_print_ext_ctrls(cmd, vfd, p, 1); + if (check_ext_ctrls(p, 0)) + ret = ops->vidioc_s_ext_ctrls(file, fh, p); + break; + } + case VIDIOC_TRY_EXT_CTRLS: + { + struct v4l2_ext_controls *p = arg; + + p->error_idx = p->count; + if (!ops->vidioc_try_ext_ctrls) + break; + v4l_print_ext_ctrls(cmd, vfd, p, 1); + if (check_ext_ctrls(p, 0)) + ret = ops->vidioc_try_ext_ctrls(file, fh, p); + break; + } + case VIDIOC_QUERYMENU: + { + struct v4l2_querymenu *p = arg; + + if (!ops->vidioc_querymenu) + break; + ret = ops->vidioc_querymenu(file, fh, p); + if (!ret) + dbgarg(cmd, "id=0x%x, index=%d, name=%s\n", + p->id, p->index, p->name); + else + dbgarg(cmd, "id=0x%x, index=%d\n", + p->id, p->index); + break; + } + /* --- audio ---------------------------------------------- */ + case VIDIOC_ENUMAUDIO: + { + struct v4l2_audio *p = arg; + + if (!ops->vidioc_enumaudio) + break; + ret = ops->vidioc_enumaudio(file, fh, p); + if (!ret) + dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " + "mode=0x%x\n", p->index, p->name, + p->capability, p->mode); + else + dbgarg(cmd, "index=%d\n", p->index); + break; + } + case VIDIOC_G_AUDIO: + { + struct v4l2_audio *p = arg; + __u32 index = p->index; + + if (!ops->vidioc_g_audio) + break; + + memset(p, 0, sizeof(*p)); + p->index = index; + ret = ops->vidioc_g_audio(file, fh, p); + if (!ret) + dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " + "mode=0x%x\n", p->index, + p->name, p->capability, p->mode); + else + dbgarg(cmd, "index=%d\n", p->index); + break; + } + case VIDIOC_S_AUDIO: + { + struct v4l2_audio *p = arg; + + if (!ops->vidioc_s_audio) + break; + dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " + "mode=0x%x\n", p->index, p->name, + p->capability, p->mode); + ret = ops->vidioc_s_audio(file, fh, p); + break; + } + case VIDIOC_ENUMAUDOUT: + { + struct v4l2_audioout *p = arg; + + if (!ops->vidioc_enumaudout) + break; + dbgarg(cmd, "Enum for index=%d\n", p->index); + ret = ops->vidioc_enumaudout(file, fh, p); + if (!ret) + dbgarg2("index=%d, name=%s, capability=%d, " + "mode=%d\n", p->index, p->name, + p->capability, p->mode); + break; + } + case VIDIOC_G_AUDOUT: + { + struct v4l2_audioout *p = arg; + + if (!ops->vidioc_g_audout) + break; + dbgarg(cmd, "Enum for index=%d\n", p->index); + ret = ops->vidioc_g_audout(file, fh, p); + if (!ret) + dbgarg2("index=%d, name=%s, capability=%d, " + "mode=%d\n", p->index, p->name, + p->capability, p->mode); + break; + } + case VIDIOC_S_AUDOUT: + { + struct v4l2_audioout *p = arg; + + if (!ops->vidioc_s_audout) + break; + dbgarg(cmd, "index=%d, name=%s, capability=%d, " + "mode=%d\n", p->index, p->name, + p->capability, p->mode); + + ret = ops->vidioc_s_audout(file, fh, p); + break; + } + case VIDIOC_G_MODULATOR: + { + struct v4l2_modulator *p = arg; + + if (!ops->vidioc_g_modulator) + break; + ret = ops->vidioc_g_modulator(file, fh, p); + if (!ret) + dbgarg(cmd, "index=%d, name=%s, " + "capability=%d, rangelow=%d," + " rangehigh=%d, txsubchans=%d\n", + p->index, p->name, p->capability, + p->rangelow, p->rangehigh, + p->txsubchans); + break; + } + case VIDIOC_S_MODULATOR: + { + struct v4l2_modulator *p = arg; + + if (!ops->vidioc_s_modulator) + break; + dbgarg(cmd, "index=%d, name=%s, capability=%d, " + "rangelow=%d, rangehigh=%d, txsubchans=%d\n", + p->index, p->name, p->capability, p->rangelow, + p->rangehigh, p->txsubchans); + ret = ops->vidioc_s_modulator(file, fh, p); + break; + } + case VIDIOC_G_CROP: + { + struct v4l2_crop *p = arg; + + if (!ops->vidioc_g_crop) + break; + dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); + ret = ops->vidioc_g_crop(file, fh, p); + if (!ret) + dbgrect(vfd, "", &p->c); + break; + } + case VIDIOC_S_CROP: + { + struct v4l2_crop *p = arg; + + if (!ops->vidioc_s_crop) + break; + dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); + dbgrect(vfd, "", &p->c); + ret = ops->vidioc_s_crop(file, fh, p); + break; + } + case VIDIOC_CROPCAP: + { + struct v4l2_cropcap *p = arg; + + /*FIXME: Should also show v4l2_fract pixelaspect */ + if (!ops->vidioc_cropcap) + break; + dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); + ret = ops->vidioc_cropcap(file, fh, p); + if (!ret) { + dbgrect(vfd, "bounds ", &p->bounds); + dbgrect(vfd, "defrect ", &p->defrect); + } + break; + } + case VIDIOC_G_JPEGCOMP: + { + struct v4l2_jpegcompression *p = arg; + + if (!ops->vidioc_g_jpegcomp) + break; + ret = ops->vidioc_g_jpegcomp(file, fh, p); + if (!ret) + dbgarg(cmd, "quality=%d, APPn=%d, " + "APP_len=%d, COM_len=%d, " + "jpeg_markers=%d\n", + p->quality, p->APPn, p->APP_len, + p->COM_len, p->jpeg_markers); + break; + } + case VIDIOC_S_JPEGCOMP: + { + struct v4l2_jpegcompression *p = arg; + + if (!ops->vidioc_g_jpegcomp) + break; + dbgarg(cmd, "quality=%d, APPn=%d, APP_len=%d, " + "COM_len=%d, jpeg_markers=%d\n", + p->quality, p->APPn, p->APP_len, + p->COM_len, p->jpeg_markers); + ret = ops->vidioc_s_jpegcomp(file, fh, p); + break; + } + case VIDIOC_G_ENC_INDEX: + { + struct v4l2_enc_idx *p = arg; + + if (!ops->vidioc_g_enc_index) + break; + ret = ops->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 (!ops->vidioc_encoder_cmd) + break; + memset(&p->raw, 0, sizeof(p->raw)); + ret = ops->vidioc_encoder_cmd(file, fh, p); + if (!ret) + dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); + break; + } + case VIDIOC_TRY_ENCODER_CMD: + { + struct v4l2_encoder_cmd *p = arg; + + if (!ops->vidioc_try_encoder_cmd) + break; + memset(&p->raw, 0, sizeof(p->raw)); + ret = ops->vidioc_try_encoder_cmd(file, fh, p); + if (!ret) + dbgarg(cmd, "cmd=%d, flags=%x\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 (ops->vidioc_g_parm) { + ret = ops->vidioc_g_parm(file, fh, p); + } else { + struct v4l2_standard s; + + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + v4l2_video_std_construct(&s, vfd->current_norm, + v4l2_norm_to_name(vfd->current_norm)); + + p->parm.capture.timeperframe = s.frameperiod; + ret = 0; + } + + dbgarg(cmd, "type=%d\n", p->type); + break; + } + case VIDIOC_S_PARM: + { + struct v4l2_streamparm *p = arg; + + if (!ops->vidioc_s_parm) + break; + dbgarg(cmd, "type=%d\n", p->type); + ret = ops->vidioc_s_parm(file, fh, p); + break; + } + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *p = arg; + __u32 index = p->index; + + if (!ops->vidioc_g_tuner) + break; + + memset(p, 0, sizeof(*p)); + p->index = index; + + ret = ops->vidioc_g_tuner(file, fh, p); + if (!ret) + dbgarg(cmd, "index=%d, name=%s, type=%d, " + "capability=0x%x, rangelow=%d, " + "rangehigh=%d, signal=%d, afc=%d, " + "rxsubchans=0x%x, audmode=%d\n", + p->index, p->name, p->type, + p->capability, p->rangelow, + p->rangehigh, p->signal, p->afc, + p->rxsubchans, p->audmode); + break; + } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *p = arg; + + if (!ops->vidioc_s_tuner) + break; + dbgarg(cmd, "index=%d, name=%s, type=%d, " + "capability=0x%x, rangelow=%d, " + "rangehigh=%d, signal=%d, afc=%d, " + "rxsubchans=0x%x, audmode=%d\n", + p->index, p->name, p->type, + p->capability, p->rangelow, + p->rangehigh, p->signal, p->afc, + p->rxsubchans, p->audmode); + ret = ops->vidioc_s_tuner(file, fh, p); + break; + } + case VIDIOC_G_FREQUENCY: + { + struct v4l2_frequency *p = arg; + + if (!ops->vidioc_g_frequency) + break; + + memset(p->reserved, 0, sizeof(p->reserved)); + + ret = ops->vidioc_g_frequency(file, fh, p); + if (!ret) + dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n", + p->tuner, p->type, p->frequency); + break; + } + case VIDIOC_S_FREQUENCY: + { + struct v4l2_frequency *p = arg; + + if (!ops->vidioc_s_frequency) + break; + dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n", + p->tuner, p->type, p->frequency); + ret = ops->vidioc_s_frequency(file, fh, p); + break; + } + case VIDIOC_G_SLICED_VBI_CAP: + { + struct v4l2_sliced_vbi_cap *p = arg; + __u32 type = p->type; + + if (!ops->vidioc_g_sliced_vbi_cap) + break; + memset(p, 0, sizeof(*p)); + p->type = type; + dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); + ret = ops->vidioc_g_sliced_vbi_cap(file, fh, p); + if (!ret) + dbgarg2("service_set=%d\n", p->service_set); + break; + } + case VIDIOC_LOG_STATUS: + { + if (!ops->vidioc_log_status) + break; + ret = ops->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 (ops->vidioc_g_register) + ret = ops->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 (ops->vidioc_s_register) + ret = ops->vidioc_s_register(file, fh, p); + break; + } +#endif + case VIDIOC_G_CHIP_IDENT: + { + struct v4l2_chip_ident *p = arg; + + if (!ops->vidioc_g_chip_ident) + break; + ret = ops->vidioc_g_chip_ident(file, fh, p); + if (!ret) + dbgarg(cmd, "chip_ident=%u, revision=0x%x\n", p->ident, p->revision); + break; + } + case VIDIOC_S_HW_FREQ_SEEK: + { + struct v4l2_hw_freq_seek *p = arg; + + if (!ops->vidioc_s_hw_freq_seek) + break; + dbgarg(cmd, + "tuner=%d, type=%d, seek_upward=%d, wrap_around=%d\n", + p->tuner, p->type, p->seek_upward, p->wrap_around); + ret = ops->vidioc_s_hw_freq_seek(file, fh, p); + break; + } + default: + { + if (!ops->vidioc_default) + break; + ret = ops->vidioc_default(file, fh, cmd, arg); + break; + } + } /* switch */ + + if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { + if (ret < 0) { + v4l_print_ioctl(vfd->name, cmd); + printk(KERN_CONT " error %d\n", ret); + } + } + + return ret; +} + +int video_ioctl2(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + char sbuf[128]; + void *mbuf = NULL; + void *parg = NULL; + int err = -EINVAL; + int is_ext_ctrl; + size_t ctrls_size = 0; + void __user *user_ptr = NULL; + +#ifdef __OLD_VIDIOC_ + cmd = video_fix_command(cmd); +#endif + is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS || + cmd == VIDIOC_TRY_EXT_CTRLS); + + /* Copy arguments into temp kernel buffer */ + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: + parg = NULL; + break; + case _IOC_READ: + case _IOC_WRITE: + case (_IOC_WRITE | _IOC_READ): + if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { + parg = sbuf; + } else { + /* too big to allocate from stack */ + mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); + if (NULL == mbuf) + return -ENOMEM; + parg = mbuf; + } + + err = -EFAULT; + if (_IOC_DIR(cmd) & _IOC_WRITE) + if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) + goto out; + break; + } + + if (is_ext_ctrl) { + struct v4l2_ext_controls *p = parg; + + /* In case of an error, tell the caller that it wasn't + a specific control that caused it. */ + p->error_idx = p->count; + user_ptr = (void __user *)p->controls; + if (p->count) { + ctrls_size = sizeof(struct v4l2_ext_control) * p->count; + /* Note: v4l2_ext_controls fits in sbuf[] so mbuf is still NULL. */ + mbuf = kmalloc(ctrls_size, GFP_KERNEL); + err = -ENOMEM; + if (NULL == mbuf) + goto out_ext_ctrl; + err = -EFAULT; + if (copy_from_user(mbuf, user_ptr, ctrls_size)) + goto out_ext_ctrl; + p->controls = mbuf; + } + } + + /* Handles IOCTL */ + err = __video_do_ioctl(inode, file, cmd, parg); + if (err == -ENOIOCTLCMD) + err = -EINVAL; + if (is_ext_ctrl) { + struct v4l2_ext_controls *p = parg; + + p->controls = (void *)user_ptr; + if (p->count && err == 0 && copy_to_user(user_ptr, mbuf, ctrls_size)) + err = -EFAULT; + goto out_ext_ctrl; + } + if (err < 0) + goto out; + +out_ext_ctrl: + /* Copy results into user buffer */ + switch (_IOC_DIR(cmd)) { + case _IOC_READ: + case (_IOC_WRITE | _IOC_READ): + if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) + err = -EFAULT; + break; + } + +out: + kfree(mbuf); + return err; +} +EXPORT_SYMBOL(video_ioctl2); diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index 0a88c44ace00..b7b05842cf28 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -16,6 +16,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/mm.h> #include <linux/slab.h> #include <linux/interrupt.h> diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c index 03f20acb668c..31944b11e6ea 100644 --- a/drivers/media/video/videobuf-dma-contig.c +++ b/drivers/media/video/videobuf-dma-contig.c @@ -28,10 +28,10 @@ struct videobuf_dma_contig_memory { }; #define MAGIC_DC_MEM 0x0733ac61 -#define MAGIC_CHECK(is, should) \ - if (unlikely((is) != (should))) { \ - pr_err("magic mismatch: %x expected %x\n", is, should); \ - BUG(); \ +#define MAGIC_CHECK(is, should) \ + if (unlikely((is) != (should))) { \ + pr_err("magic mismatch: %x expected %x\n", (is), (should)); \ + BUG(); \ } static void diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c index a868b7ed75ff..be65a2fb3976 100644 --- a/drivers/media/video/videobuf-vmalloc.c +++ b/drivers/media/video/videobuf-vmalloc.c @@ -203,7 +203,7 @@ static int __videobuf_iolock (struct videobuf_queue* q, return 0; /* FIXME: to properly support USERPTR, remap should occur. - The code bellow won't work, since mem->vma = NULL + The code below won't work, since mem->vma = NULL */ /* Try to remap memory */ rc = remap_vmalloc_range(mem->vma, (void *)vb->baddr, 0); diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index 6616e6570557..e69de29bb2d1 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -1,2262 +0,0 @@ -/* - * Video capture interface for Linux version 2 - * - * A generic video device interface for the LINUX operating system - * using a set of device structures/vectors for low level operations. - * - * 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. - * - * Authors: Alan Cox, <alan@redhat.com> (version 1) - * Mauro Carvalho Chehab <mchehab@infradead.org> (version 2) - * - * Fixes: 20000516 Claudio Matsuoka <claudio@conectiva.com> - * - Added procfs support - */ - -#define dbgarg(cmd, fmt, arg...) \ - do { \ - if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { \ - printk(KERN_DEBUG "%s: ", vfd->name); \ - v4l_printk_ioctl(cmd); \ - printk(" " fmt, ## arg); \ - } \ - } while (0) - -#define dbgarg2(fmt, arg...) \ - do { \ - if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) \ - printk(KERN_DEBUG "%s: " fmt, vfd->name, ## arg);\ - } while (0) - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/kmod.h> -#include <linux/slab.h> -#include <linux/smp_lock.h> -#include <asm/uaccess.h> -#include <asm/system.h> - -#define __OLD_VIDIOC_ /* To allow fixing old calls*/ -#include <linux/videodev2.h> - -#ifdef CONFIG_VIDEO_V4L1 -#include <linux/videodev.h> -#endif -#include <media/v4l2-common.h> -#include <linux/video_decoder.h> - -#define VIDEO_NUM_DEVICES 256 -#define VIDEO_NAME "video4linux" - -struct std_descr { - v4l2_std_id std; - const char *descr; -}; - -static const struct std_descr standards[] = { - { V4L2_STD_NTSC, "NTSC" }, - { V4L2_STD_NTSC_M, "NTSC-M" }, - { V4L2_STD_NTSC_M_JP, "NTSC-M-JP" }, - { V4L2_STD_NTSC_M_KR, "NTSC-M-KR" }, - { V4L2_STD_NTSC_443, "NTSC-443" }, - { V4L2_STD_PAL, "PAL" }, - { V4L2_STD_PAL_BG, "PAL-BG" }, - { V4L2_STD_PAL_B, "PAL-B" }, - { V4L2_STD_PAL_B1, "PAL-B1" }, - { V4L2_STD_PAL_G, "PAL-G" }, - { V4L2_STD_PAL_H, "PAL-H" }, - { V4L2_STD_PAL_I, "PAL-I" }, - { V4L2_STD_PAL_DK, "PAL-DK" }, - { V4L2_STD_PAL_D, "PAL-D" }, - { V4L2_STD_PAL_D1, "PAL-D1" }, - { V4L2_STD_PAL_K, "PAL-K" }, - { V4L2_STD_PAL_M, "PAL-M" }, - { V4L2_STD_PAL_N, "PAL-N" }, - { V4L2_STD_PAL_Nc, "PAL-Nc" }, - { V4L2_STD_PAL_60, "PAL-60" }, - { V4L2_STD_SECAM, "SECAM" }, - { V4L2_STD_SECAM_B, "SECAM-B" }, - { V4L2_STD_SECAM_G, "SECAM-G" }, - { V4L2_STD_SECAM_H, "SECAM-H" }, - { V4L2_STD_SECAM_DK, "SECAM-DK" }, - { V4L2_STD_SECAM_D, "SECAM-D" }, - { V4L2_STD_SECAM_K, "SECAM-K" }, - { V4L2_STD_SECAM_K1, "SECAM-K1" }, - { V4L2_STD_SECAM_L, "SECAM-L" }, - { V4L2_STD_SECAM_LC, "SECAM-Lc" }, - { 0, "Unknown" } -}; - -/* video4linux standard ID conversion to standard name - */ -const char *v4l2_norm_to_name(v4l2_std_id id) -{ - u32 myid = id; - int i; - - /* HACK: ppc32 architecture doesn't have __ucmpdi2 function to handle - 64 bit comparations. So, on that architecture, with some gcc - variants, compilation fails. Currently, the max value is 30bit wide. - */ - BUG_ON(myid != id); - - for (i = 0; standards[i].std; i++) - if (myid == standards[i].std) - break; - return standards[i].descr; -} -EXPORT_SYMBOL(v4l2_norm_to_name); - -/* Fill in the fields of a v4l2_standard structure according to the - 'id' and 'transmission' parameters. Returns negative on error. */ -int v4l2_video_std_construct(struct v4l2_standard *vs, - int id, const char *name) -{ - u32 index = vs->index; - - memset(vs, 0, sizeof(struct v4l2_standard)); - vs->index = index; - vs->id = id; - if (id & V4L2_STD_525_60) { - vs->frameperiod.numerator = 1001; - vs->frameperiod.denominator = 30000; - vs->framelines = 525; - } else { - vs->frameperiod.numerator = 1; - vs->frameperiod.denominator = 25; - vs->framelines = 625; - } - strlcpy(vs->name, name, sizeof(vs->name)); - return 0; -} -EXPORT_SYMBOL(v4l2_video_std_construct); - -/* ----------------------------------------------------------------- */ -/* some arrays for pretty-printing debug messages of enum types */ - -const char *v4l2_field_names[] = { - [V4L2_FIELD_ANY] = "any", - [V4L2_FIELD_NONE] = "none", - [V4L2_FIELD_TOP] = "top", - [V4L2_FIELD_BOTTOM] = "bottom", - [V4L2_FIELD_INTERLACED] = "interlaced", - [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", -}; -EXPORT_SYMBOL(v4l2_field_names); - -const char *v4l2_type_names[] = { - [V4L2_BUF_TYPE_VIDEO_CAPTURE] = "vid-cap", - [V4L2_BUF_TYPE_VIDEO_OVERLAY] = "vid-overlay", - [V4L2_BUF_TYPE_VIDEO_OUTPUT] = "vid-out", - [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] = "sliced-vbi-out", - [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "vid-out-overlay", -}; -EXPORT_SYMBOL(v4l2_type_names); - -static const 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") - -/* ------------------------------------------------------------------ */ -/* debug help functions */ - -#ifdef CONFIG_VIDEO_V4L1_COMPAT -static const char *v4l1_ioctls[] = { - [_IOC_NR(VIDIOCGCAP)] = "VIDIOCGCAP", - [_IOC_NR(VIDIOCGCHAN)] = "VIDIOCGCHAN", - [_IOC_NR(VIDIOCSCHAN)] = "VIDIOCSCHAN", - [_IOC_NR(VIDIOCGTUNER)] = "VIDIOCGTUNER", - [_IOC_NR(VIDIOCSTUNER)] = "VIDIOCSTUNER", - [_IOC_NR(VIDIOCGPICT)] = "VIDIOCGPICT", - [_IOC_NR(VIDIOCSPICT)] = "VIDIOCSPICT", - [_IOC_NR(VIDIOCCAPTURE)] = "VIDIOCCAPTURE", - [_IOC_NR(VIDIOCGWIN)] = "VIDIOCGWIN", - [_IOC_NR(VIDIOCSWIN)] = "VIDIOCSWIN", - [_IOC_NR(VIDIOCGFBUF)] = "VIDIOCGFBUF", - [_IOC_NR(VIDIOCSFBUF)] = "VIDIOCSFBUF", - [_IOC_NR(VIDIOCKEY)] = "VIDIOCKEY", - [_IOC_NR(VIDIOCGFREQ)] = "VIDIOCGFREQ", - [_IOC_NR(VIDIOCSFREQ)] = "VIDIOCSFREQ", - [_IOC_NR(VIDIOCGAUDIO)] = "VIDIOCGAUDIO", - [_IOC_NR(VIDIOCSAUDIO)] = "VIDIOCSAUDIO", - [_IOC_NR(VIDIOCSYNC)] = "VIDIOCSYNC", - [_IOC_NR(VIDIOCMCAPTURE)] = "VIDIOCMCAPTURE", - [_IOC_NR(VIDIOCGMBUF)] = "VIDIOCGMBUF", - [_IOC_NR(VIDIOCGUNIT)] = "VIDIOCGUNIT", - [_IOC_NR(VIDIOCGCAPTURE)] = "VIDIOCGCAPTURE", - [_IOC_NR(VIDIOCSCAPTURE)] = "VIDIOCSCAPTURE", - [_IOC_NR(VIDIOCSPLAYMODE)] = "VIDIOCSPLAYMODE", - [_IOC_NR(VIDIOCSWRITEMODE)] = "VIDIOCSWRITEMODE", - [_IOC_NR(VIDIOCGPLAYINFO)] = "VIDIOCGPLAYINFO", - [_IOC_NR(VIDIOCSMICROCODE)] = "VIDIOCSMICROCODE", - [_IOC_NR(VIDIOCGVBIFMT)] = "VIDIOCGVBIFMT", - [_IOC_NR(VIDIOCSVBIFMT)] = "VIDIOCSVBIFMT" -}; -#define V4L1_IOCTLS ARRAY_SIZE(v4l1_ioctls) -#endif - -static const char *v4l2_ioctls[] = { - [_IOC_NR(VIDIOC_QUERYCAP)] = "VIDIOC_QUERYCAP", - [_IOC_NR(VIDIOC_RESERVED)] = "VIDIOC_RESERVED", - [_IOC_NR(VIDIOC_ENUM_FMT)] = "VIDIOC_ENUM_FMT", - [_IOC_NR(VIDIOC_G_FMT)] = "VIDIOC_G_FMT", - [_IOC_NR(VIDIOC_S_FMT)] = "VIDIOC_S_FMT", - [_IOC_NR(VIDIOC_REQBUFS)] = "VIDIOC_REQBUFS", - [_IOC_NR(VIDIOC_QUERYBUF)] = "VIDIOC_QUERYBUF", - [_IOC_NR(VIDIOC_G_FBUF)] = "VIDIOC_G_FBUF", - [_IOC_NR(VIDIOC_S_FBUF)] = "VIDIOC_S_FBUF", - [_IOC_NR(VIDIOC_OVERLAY)] = "VIDIOC_OVERLAY", - [_IOC_NR(VIDIOC_QBUF)] = "VIDIOC_QBUF", - [_IOC_NR(VIDIOC_DQBUF)] = "VIDIOC_DQBUF", - [_IOC_NR(VIDIOC_STREAMON)] = "VIDIOC_STREAMON", - [_IOC_NR(VIDIOC_STREAMOFF)] = "VIDIOC_STREAMOFF", - [_IOC_NR(VIDIOC_G_PARM)] = "VIDIOC_G_PARM", - [_IOC_NR(VIDIOC_S_PARM)] = "VIDIOC_S_PARM", - [_IOC_NR(VIDIOC_G_STD)] = "VIDIOC_G_STD", - [_IOC_NR(VIDIOC_S_STD)] = "VIDIOC_S_STD", - [_IOC_NR(VIDIOC_ENUMSTD)] = "VIDIOC_ENUMSTD", - [_IOC_NR(VIDIOC_ENUMINPUT)] = "VIDIOC_ENUMINPUT", - [_IOC_NR(VIDIOC_G_CTRL)] = "VIDIOC_G_CTRL", - [_IOC_NR(VIDIOC_S_CTRL)] = "VIDIOC_S_CTRL", - [_IOC_NR(VIDIOC_G_TUNER)] = "VIDIOC_G_TUNER", - [_IOC_NR(VIDIOC_S_TUNER)] = "VIDIOC_S_TUNER", - [_IOC_NR(VIDIOC_G_AUDIO)] = "VIDIOC_G_AUDIO", - [_IOC_NR(VIDIOC_S_AUDIO)] = "VIDIOC_S_AUDIO", - [_IOC_NR(VIDIOC_QUERYCTRL)] = "VIDIOC_QUERYCTRL", - [_IOC_NR(VIDIOC_QUERYMENU)] = "VIDIOC_QUERYMENU", - [_IOC_NR(VIDIOC_G_INPUT)] = "VIDIOC_G_INPUT", - [_IOC_NR(VIDIOC_S_INPUT)] = "VIDIOC_S_INPUT", - [_IOC_NR(VIDIOC_G_OUTPUT)] = "VIDIOC_G_OUTPUT", - [_IOC_NR(VIDIOC_S_OUTPUT)] = "VIDIOC_S_OUTPUT", - [_IOC_NR(VIDIOC_ENUMOUTPUT)] = "VIDIOC_ENUMOUTPUT", - [_IOC_NR(VIDIOC_G_AUDOUT)] = "VIDIOC_G_AUDOUT", - [_IOC_NR(VIDIOC_S_AUDOUT)] = "VIDIOC_S_AUDOUT", - [_IOC_NR(VIDIOC_G_MODULATOR)] = "VIDIOC_G_MODULATOR", - [_IOC_NR(VIDIOC_S_MODULATOR)] = "VIDIOC_S_MODULATOR", - [_IOC_NR(VIDIOC_G_FREQUENCY)] = "VIDIOC_G_FREQUENCY", - [_IOC_NR(VIDIOC_S_FREQUENCY)] = "VIDIOC_S_FREQUENCY", - [_IOC_NR(VIDIOC_CROPCAP)] = "VIDIOC_CROPCAP", - [_IOC_NR(VIDIOC_G_CROP)] = "VIDIOC_G_CROP", - [_IOC_NR(VIDIOC_S_CROP)] = "VIDIOC_S_CROP", - [_IOC_NR(VIDIOC_G_JPEGCOMP)] = "VIDIOC_G_JPEGCOMP", - [_IOC_NR(VIDIOC_S_JPEGCOMP)] = "VIDIOC_S_JPEGCOMP", - [_IOC_NR(VIDIOC_QUERYSTD)] = "VIDIOC_QUERYSTD", - [_IOC_NR(VIDIOC_TRY_FMT)] = "VIDIOC_TRY_FMT", - [_IOC_NR(VIDIOC_ENUMAUDIO)] = "VIDIOC_ENUMAUDIO", - [_IOC_NR(VIDIOC_ENUMAUDOUT)] = "VIDIOC_ENUMAUDOUT", - [_IOC_NR(VIDIOC_G_PRIORITY)] = "VIDIOC_G_PRIORITY", - [_IOC_NR(VIDIOC_S_PRIORITY)] = "VIDIOC_S_PRIORITY", - [_IOC_NR(VIDIOC_G_SLICED_VBI_CAP)] = "VIDIOC_G_SLICED_VBI_CAP", - [_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", -#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", - [_IOC_NR(VIDIOC_S_HW_FREQ_SEEK)] = "VIDIOC_S_HW_FREQ_SEEK", -#endif -}; -#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) - -static const char *v4l2_int_ioctls[] = { -#ifdef CONFIG_VIDEO_V4L1_COMPAT - [_IOC_NR(DECODER_GET_CAPABILITIES)] = "DECODER_GET_CAPABILITIES", - [_IOC_NR(DECODER_GET_STATUS)] = "DECODER_GET_STATUS", - [_IOC_NR(DECODER_SET_NORM)] = "DECODER_SET_NORM", - [_IOC_NR(DECODER_SET_INPUT)] = "DECODER_SET_INPUT", - [_IOC_NR(DECODER_SET_OUTPUT)] = "DECODER_SET_OUTPUT", - [_IOC_NR(DECODER_ENABLE_OUTPUT)] = "DECODER_ENABLE_OUTPUT", - [_IOC_NR(DECODER_SET_PICTURE)] = "DECODER_SET_PICTURE", - [_IOC_NR(DECODER_SET_GPIO)] = "DECODER_SET_GPIO", - [_IOC_NR(DECODER_INIT)] = "DECODER_INIT", - [_IOC_NR(DECODER_SET_VBI_BYPASS)] = "DECODER_SET_VBI_BYPASS", - [_IOC_NR(DECODER_DUMP)] = "DECODER_DUMP", -#endif - [_IOC_NR(AUDC_SET_RADIO)] = "AUDC_SET_RADIO", - - [_IOC_NR(TUNER_SET_TYPE_ADDR)] = "TUNER_SET_TYPE_ADDR", - [_IOC_NR(TUNER_SET_STANDBY)] = "TUNER_SET_STANDBY", - [_IOC_NR(TUNER_SET_CONFIG)] = "TUNER_SET_CONFIG", - - [_IOC_NR(VIDIOC_INT_S_TUNER_MODE)] = "VIDIOC_INT_S_TUNER_MODE", - [_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_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_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) - -/* Common ioctl debug function. This function can be used by - external ioctl messages as well as internal V4L ioctl */ -void v4l_printk_ioctl(unsigned int cmd) -{ - char *dir, *type; - - switch (_IOC_TYPE(cmd)) { - case 'd': - if (_IOC_NR(cmd) >= V4L2_INT_IOCTLS) { - type = "v4l2_int"; - break; - } - printk("%s", v4l2_int_ioctls[_IOC_NR(cmd)]); - return; -#ifdef CONFIG_VIDEO_V4L1_COMPAT - case 'v': - if (_IOC_NR(cmd) >= V4L1_IOCTLS) { - type = "v4l1"; - break; - } - printk("%s", v4l1_ioctls[_IOC_NR(cmd)]); - return; -#endif - case 'V': - if (_IOC_NR(cmd) >= V4L2_IOCTLS) { - type = "v4l2"; - break; - } - printk("%s", v4l2_ioctls[_IOC_NR(cmd)]); - return; - default: - type = "unknown"; - } - - switch (_IOC_DIR(cmd)) { - case _IOC_NONE: dir = "--"; break; - case _IOC_READ: dir = "r-"; break; - case _IOC_WRITE: dir = "-w"; break; - case _IOC_READ | _IOC_WRITE: dir = "rw"; break; - default: dir = "*ERR*"; break; - } - printk("%s ioctl '%c', dir=%s, #%d (0x%08x)", - type, _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd); -} -EXPORT_SYMBOL(v4l_printk_ioctl); - -/* - * sysfs stuff - */ - -static ssize_t show_index(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct video_device *vfd = container_of(cd, struct video_device, - class_dev); - return sprintf(buf, "%i\n", vfd->index); -} - -static ssize_t show_name(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct video_device *vfd = container_of(cd, struct video_device, - class_dev); - return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name); -} - -static struct device_attribute video_device_attrs[] = { - __ATTR(name, S_IRUGO, show_name, NULL), - __ATTR(index, S_IRUGO, show_index, NULL), - __ATTR_NULL -}; - -struct video_device *video_device_alloc(void) -{ - struct video_device *vfd; - - vfd = kzalloc(sizeof(*vfd),GFP_KERNEL); - return vfd; -} -EXPORT_SYMBOL(video_device_alloc); - -void video_device_release(struct video_device *vfd) -{ - kfree(vfd); -} -EXPORT_SYMBOL(video_device_release); - -static void video_release(struct device *cd) -{ - struct video_device *vfd = container_of(cd, struct video_device, - class_dev); - -#if 1 - /* needed until all drivers are fixed */ - if (!vfd->release) - return; -#endif - vfd->release(vfd); -} - -static struct class video_class = { - .name = VIDEO_NAME, - .dev_attrs = video_device_attrs, - .dev_release = video_release, -}; - -/* - * Active devices - */ - -static struct video_device *video_device[VIDEO_NUM_DEVICES]; -static DEFINE_MUTEX(videodev_lock); - -struct video_device* video_devdata(struct file *file) -{ - return video_device[iminor(file->f_path.dentry->d_inode)]; -} -EXPORT_SYMBOL(video_devdata); - -/* - * Open a video device - FIXME: Obsoleted - */ -static int video_open(struct inode *inode, struct file *file) -{ - unsigned int minor = iminor(inode); - int err = 0; - struct video_device *vfl; - const struct file_operations *old_fops; - - if(minor>=VIDEO_NUM_DEVICES) - return -ENODEV; - lock_kernel(); - mutex_lock(&videodev_lock); - vfl=video_device[minor]; - if(vfl==NULL) { - mutex_unlock(&videodev_lock); - request_module("char-major-%d-%d", VIDEO_MAJOR, minor); - mutex_lock(&videodev_lock); - vfl=video_device[minor]; - if (vfl==NULL) { - mutex_unlock(&videodev_lock); - unlock_kernel(); - return -ENODEV; - } - } - old_fops = file->f_op; - file->f_op = fops_get(vfl->fops); - if(file->f_op->open) - err = file->f_op->open(inode,file); - if (err) { - fops_put(file->f_op); - file->f_op = fops_get(old_fops); - } - fops_put(old_fops); - mutex_unlock(&videodev_lock); - unlock_kernel(); - return err; -} - -/* - * helper function -- handles userspace copying for ioctl arguments - */ - -#ifdef __OLD_VIDIOC_ -static unsigned int -video_fix_command(unsigned int cmd) -{ - switch (cmd) { - case VIDIOC_OVERLAY_OLD: - cmd = VIDIOC_OVERLAY; - break; - case VIDIOC_S_PARM_OLD: - cmd = VIDIOC_S_PARM; - break; - case VIDIOC_S_CTRL_OLD: - cmd = VIDIOC_S_CTRL; - break; - case VIDIOC_G_AUDIO_OLD: - cmd = VIDIOC_G_AUDIO; - break; - case VIDIOC_G_AUDOUT_OLD: - cmd = VIDIOC_G_AUDOUT; - break; - case VIDIOC_CROPCAP_OLD: - cmd = VIDIOC_CROPCAP; - break; - } - return cmd; -} -#endif - -/* - * Obsolete usercopy function - Should be removed soon - */ -int -video_usercopy(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg, - int (*func)(struct inode *inode, struct file *file, - unsigned int cmd, void *arg)) -{ - char sbuf[128]; - void *mbuf = NULL; - void *parg = NULL; - int err = -EINVAL; - int is_ext_ctrl; - size_t ctrls_size = 0; - void __user *user_ptr = NULL; - -#ifdef __OLD_VIDIOC_ - cmd = video_fix_command(cmd); -#endif - is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS || - cmd == VIDIOC_TRY_EXT_CTRLS); - - /* Copy arguments into temp kernel buffer */ - switch (_IOC_DIR(cmd)) { - case _IOC_NONE: - parg = NULL; - break; - case _IOC_READ: - case _IOC_WRITE: - case (_IOC_WRITE | _IOC_READ): - if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { - parg = sbuf; - } else { - /* too big to allocate from stack */ - mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL); - if (NULL == mbuf) - return -ENOMEM; - parg = mbuf; - } - - err = -EFAULT; - if (_IOC_DIR(cmd) & _IOC_WRITE) - if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) - goto out; - break; - } - if (is_ext_ctrl) { - struct v4l2_ext_controls *p = parg; - - /* In case of an error, tell the caller that it wasn't - a specific control that caused it. */ - p->error_idx = p->count; - user_ptr = (void __user *)p->controls; - if (p->count) { - ctrls_size = sizeof(struct v4l2_ext_control) * p->count; - /* Note: v4l2_ext_controls fits in sbuf[] so mbuf is still NULL. */ - mbuf = kmalloc(ctrls_size, GFP_KERNEL); - err = -ENOMEM; - if (NULL == mbuf) - goto out_ext_ctrl; - err = -EFAULT; - if (copy_from_user(mbuf, user_ptr, ctrls_size)) - goto out_ext_ctrl; - p->controls = mbuf; - } - } - - /* call driver */ - err = func(inode, file, cmd, parg); - if (err == -ENOIOCTLCMD) - err = -EINVAL; - if (is_ext_ctrl) { - struct v4l2_ext_controls *p = parg; - - p->controls = (void *)user_ptr; - if (p->count && err == 0 && copy_to_user(user_ptr, mbuf, ctrls_size)) - err = -EFAULT; - goto out_ext_ctrl; - } - if (err < 0) - goto out; - -out_ext_ctrl: - /* Copy results into user buffer */ - switch (_IOC_DIR(cmd)) - { - case _IOC_READ: - case (_IOC_WRITE | _IOC_READ): - if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) - err = -EFAULT; - break; - } - -out: - kfree(mbuf); - return err; -} -EXPORT_SYMBOL(video_usercopy); - -/* - * open/release helper functions -- handle exclusive opens - * Should be removed soon - */ -int video_exclusive_open(struct inode *inode, struct file *file) -{ - struct video_device *vfl = video_devdata(file); - int retval = 0; - - mutex_lock(&vfl->lock); - if (vfl->users) { - retval = -EBUSY; - } else { - vfl->users++; - } - mutex_unlock(&vfl->lock); - return retval; -} -EXPORT_SYMBOL(video_exclusive_open); - -int video_exclusive_release(struct inode *inode, struct file *file) -{ - struct video_device *vfl = video_devdata(file); - - vfl->users--; - return 0; -} -EXPORT_SYMBOL(video_exclusive_release); - -static void dbgbuf(unsigned int cmd, struct video_device *vfd, - struct v4l2_buffer *p) -{ - struct v4l2_timecode *tc=&p->timecode; - - dbgarg (cmd, "%02ld:%02d:%02d.%08ld index=%d, type=%s, " - "bytesused=%d, flags=0x%08d, " - "field=%0d, sequence=%d, memory=%s, offset/userptr=0x%08lx, length=%d\n", - (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, p->length); - dbgarg2("timecode=%02d:%02d:%02d type=%d, " - "flags=0x%08d, frames=%d, userbits=0x%08x\n", - tc->hours,tc->minutes,tc->seconds, - tc->type, tc->flags, tc->frames, *(__u32 *) tc->userbits); -} - -static inline void dbgrect(struct video_device *vfd, char *s, - struct v4l2_rect *r) -{ - dbgarg2("%sRect start at %dx%d, size=%dx%d\n", s, r->left, r->top, - r->width, r->height); -}; - -static inline void v4l_print_pix_fmt (struct video_device *vfd, - struct v4l2_pix_format *fmt) -{ - dbgarg2 ("width=%d, height=%d, format=%c%c%c%c, field=%s, " - "bytesperline=%d sizeimage=%d, colorspace=%d\n", - fmt->width,fmt->height, - (fmt->pixelformat & 0xff), - (fmt->pixelformat >> 8) & 0xff, - (fmt->pixelformat >> 16) & 0xff, - (fmt->pixelformat >> 24) & 0xff, - prt_names(fmt->field, v4l2_field_names), - fmt->bytesperline, fmt->sizeimage, fmt->colorspace); -}; - -static inline void v4l_print_ext_ctrls(unsigned int cmd, - struct video_device *vfd, struct v4l2_ext_controls *c, int show_vals) -{ - __u32 i; - - if (!(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) - return; - dbgarg(cmd, ""); - printk(KERN_CONT "class=0x%x", c->ctrl_class); - for (i = 0; i < c->count; i++) { - if (show_vals) - printk(KERN_CONT " id/val=0x%x/0x%x", - c->controls[i].id, c->controls[i].value); - else - printk(KERN_CONT " id=0x%x", c->controls[i].id); - } - printk(KERN_CONT "\n"); -}; - -static inline int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv) -{ - __u32 i; - - /* zero the reserved fields */ - c->reserved[0] = c->reserved[1] = 0; - for (i = 0; i < c->count; i++) { - c->controls[i].reserved2[0] = 0; - c->controls[i].reserved2[1] = 0; - } - /* V4L2_CID_PRIVATE_BASE cannot be used as control class - when using extended controls. - Only when passed in through VIDIOC_G_CTRL and VIDIOC_S_CTRL - is it allowed for backwards compatibility. - */ - if (!allow_priv && c->ctrl_class == V4L2_CID_PRIVATE_BASE) - return 0; - /* Check that all controls are from the same control class. */ - for (i = 0; i < c->count; i++) { - if (V4L2_CTRL_ID2CLASS(c->controls[i].id) != c->ctrl_class) { - c->error_idx = i; - return 0; - } - } - return 1; -} - -static int check_fmt (struct video_device *vfd, enum v4l2_buf_type type) -{ - switch (type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (vfd->vidioc_try_fmt_vid_cap) - return (0); - break; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (vfd->vidioc_try_fmt_vid_overlay) - return (0); - break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (vfd->vidioc_try_fmt_vid_out) - return (0); - break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - if (vfd->vidioc_try_fmt_vid_out_overlay) - return (0); - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (vfd->vidioc_try_fmt_vbi_cap) - return (0); - break; - case V4L2_BUF_TYPE_VBI_OUTPUT: - if (vfd->vidioc_try_fmt_vbi_out) - return (0); - break; - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - if (vfd->vidioc_try_fmt_sliced_vbi_cap) - return (0); - break; - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - if (vfd->vidioc_try_fmt_sliced_vbi_out) - return (0); - break; - case V4L2_BUF_TYPE_PRIVATE: - if (vfd->vidioc_try_fmt_type_private) - return (0); - break; - } - return (-EINVAL); -} - -static int __video_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) -{ - struct video_device *vfd = video_devdata(file); - void *fh = file->private_data; - int ret = -EINVAL; - - if ( (vfd->debug & V4L2_DEBUG_IOCTL) && - !(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) { - v4l_print_ioctl(vfd->name, cmd); - printk("\n"); - } - -#ifdef CONFIG_VIDEO_V4L1_COMPAT - /*********************************************************** - Handles calls to the obsoleted V4L1 API - Due to the nature of VIDIOCGMBUF, each driver that supports - V4L1 should implement its own handler for this ioctl. - ***********************************************************/ - - /* --- streaming capture ------------------------------------- */ - if (cmd == VIDIOCGMBUF) { - struct video_mbuf *p=arg; - - memset(p, 0, sizeof(*p)); - - if (!vfd->vidiocgmbuf) - return ret; - ret=vfd->vidiocgmbuf(file, fh, p); - if (!ret) - dbgarg (cmd, "size=%d, frames=%d, offsets=0x%08lx\n", - p->size, p->frames, - (unsigned long)p->offsets); - return ret; - } - - /******************************************************** - All other V4L1 calls are handled by v4l1_compat module. - Those calls will be translated into V4L2 calls, and - __video_do_ioctl will be called again, with one or more - V4L2 ioctls. - ********************************************************/ - if (_IOC_TYPE(cmd)=='v') - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - __video_do_ioctl); -#endif - - switch(cmd) { - /* --- capabilities ------------------------------------------ */ - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = (struct v4l2_capability*)arg; - memset(cap, 0, sizeof(*cap)); - - if (!vfd->vidioc_querycap) - break; - - ret=vfd->vidioc_querycap(file, fh, cap); - if (!ret) - dbgarg (cmd, "driver=%s, card=%s, bus=%s, " - "version=0x%08x, " - "capabilities=0x%08x\n", - cap->driver,cap->card,cap->bus_info, - cap->version, - cap->capabilities); - break; - } - - /* --- priority ------------------------------------------ */ - case VIDIOC_G_PRIORITY: - { - enum v4l2_priority *p=arg; - - if (!vfd->vidioc_g_priority) - break; - ret=vfd->vidioc_g_priority(file, fh, p); - if (!ret) - dbgarg(cmd, "priority is %d\n", *p); - break; - } - case VIDIOC_S_PRIORITY: - { - enum v4l2_priority *p=arg; - - if (!vfd->vidioc_s_priority) - break; - dbgarg(cmd, "setting priority to %d\n", *p); - ret=vfd->vidioc_s_priority(file, fh, *p); - break; - } - - /* --- 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; - memset(f,0,sizeof(*f)); - f->index = index; - f->type = type; - - switch (type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (vfd->vidioc_enum_fmt_vid_cap) - ret = vfd->vidioc_enum_fmt_vid_cap(file, fh, f); - break; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (vfd->vidioc_enum_fmt_vid_overlay) - ret = vfd->vidioc_enum_fmt_vid_overlay(file, - fh, f); - break; -#if 1 - /* V4L2_BUF_TYPE_VBI_CAPTURE should not support VIDIOC_ENUM_FMT - * according to the spec. The bttv and saa7134 drivers support - * it though, so just warn that this is deprecated and will be - * removed in the near future. */ - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (vfd->vidioc_enum_fmt_vbi_cap) { - printk(KERN_WARNING "vidioc_enum_fmt_vbi_cap will be removed in 2.6.28!\n"); - ret = vfd->vidioc_enum_fmt_vbi_cap(file, fh, f); - } - break; -#endif - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (vfd->vidioc_enum_fmt_vid_out) - ret = vfd->vidioc_enum_fmt_vid_out(file, fh, f); - break; - case V4L2_BUF_TYPE_PRIVATE: - if (vfd->vidioc_enum_fmt_type_private) - ret = vfd->vidioc_enum_fmt_type_private(file, - fh, f); - break; - default: - break; - } - if (!ret) - dbgarg (cmd, "index=%d, type=%d, flags=%d, " - "pixelformat=%c%c%c%c, description='%s'\n", - f->index, f->type, f->flags, - (f->pixelformat & 0xff), - (f->pixelformat >> 8) & 0xff, - (f->pixelformat >> 16) & 0xff, - (f->pixelformat >> 24) & 0xff, - f->description); - break; - } - case VIDIOC_G_FMT: - { - struct v4l2_format *f = (struct v4l2_format *)arg; - - memset(f->fmt.raw_data, 0, sizeof(f->fmt.raw_data)); - - /* FIXME: Should be one dump per type */ - dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names)); - - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (vfd->vidioc_g_fmt_vid_cap) - ret = vfd->vidioc_g_fmt_vid_cap(file, fh, f); - if (!ret) - v4l_print_pix_fmt(vfd, &f->fmt.pix); - break; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (vfd->vidioc_g_fmt_vid_overlay) - ret = vfd->vidioc_g_fmt_vid_overlay(file, - fh, f); - break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (vfd->vidioc_g_fmt_vid_out) - ret = vfd->vidioc_g_fmt_vid_out(file, fh, f); - if (!ret) - v4l_print_pix_fmt(vfd, &f->fmt.pix); - break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - if (vfd->vidioc_g_fmt_vid_out_overlay) - ret = vfd->vidioc_g_fmt_vid_out_overlay(file, - fh, f); - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (vfd->vidioc_g_fmt_vbi_cap) - ret = vfd->vidioc_g_fmt_vbi_cap(file, fh, f); - break; - case V4L2_BUF_TYPE_VBI_OUTPUT: - if (vfd->vidioc_g_fmt_vbi_out) - ret = vfd->vidioc_g_fmt_vbi_out(file, fh, f); - break; - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - if (vfd->vidioc_g_fmt_sliced_vbi_cap) - ret = vfd->vidioc_g_fmt_sliced_vbi_cap(file, - fh, f); - break; - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - if (vfd->vidioc_g_fmt_sliced_vbi_out) - ret = vfd->vidioc_g_fmt_sliced_vbi_out(file, - fh, f); - break; - case V4L2_BUF_TYPE_PRIVATE: - if (vfd->vidioc_g_fmt_type_private) - ret = vfd->vidioc_g_fmt_type_private(file, - fh, f); - break; - } - - break; - } - case VIDIOC_S_FMT: - { - struct v4l2_format *f = (struct v4l2_format *)arg; - - /* FIXME: Should be one dump per type */ - dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names)); - - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - v4l_print_pix_fmt(vfd, &f->fmt.pix); - if (vfd->vidioc_s_fmt_vid_cap) - ret = vfd->vidioc_s_fmt_vid_cap(file, fh, f); - break; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (vfd->vidioc_s_fmt_vid_overlay) - ret = vfd->vidioc_s_fmt_vid_overlay(file, - fh, f); - break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - v4l_print_pix_fmt(vfd, &f->fmt.pix); - if (vfd->vidioc_s_fmt_vid_out) - ret = vfd->vidioc_s_fmt_vid_out(file, fh, f); - break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - if (vfd->vidioc_s_fmt_vid_out_overlay) - ret = vfd->vidioc_s_fmt_vid_out_overlay(file, - fh, f); - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (vfd->vidioc_s_fmt_vbi_cap) - ret = vfd->vidioc_s_fmt_vbi_cap(file, fh, f); - break; - case V4L2_BUF_TYPE_VBI_OUTPUT: - if (vfd->vidioc_s_fmt_vbi_out) - ret = vfd->vidioc_s_fmt_vbi_out(file, fh, f); - break; - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - if (vfd->vidioc_s_fmt_sliced_vbi_cap) - ret = vfd->vidioc_s_fmt_sliced_vbi_cap(file, - fh, f); - break; - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - if (vfd->vidioc_s_fmt_sliced_vbi_out) - ret = vfd->vidioc_s_fmt_sliced_vbi_out(file, - fh, f); - break; - case V4L2_BUF_TYPE_PRIVATE: - if (vfd->vidioc_s_fmt_type_private) - ret = vfd->vidioc_s_fmt_type_private(file, - fh, f); - break; - } - break; - } - case VIDIOC_TRY_FMT: - { - struct v4l2_format *f = (struct v4l2_format *)arg; - - /* FIXME: Should be one dump per type */ - dbgarg (cmd, "type=%s\n", prt_names(f->type, - v4l2_type_names)); - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (vfd->vidioc_try_fmt_vid_cap) - ret = vfd->vidioc_try_fmt_vid_cap(file, fh, f); - if (!ret) - v4l_print_pix_fmt(vfd, &f->fmt.pix); - break; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (vfd->vidioc_try_fmt_vid_overlay) - ret = vfd->vidioc_try_fmt_vid_overlay(file, - fh, f); - break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (vfd->vidioc_try_fmt_vid_out) - ret = vfd->vidioc_try_fmt_vid_out(file, fh, f); - if (!ret) - v4l_print_pix_fmt(vfd, &f->fmt.pix); - break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - if (vfd->vidioc_try_fmt_vid_out_overlay) - ret = vfd->vidioc_try_fmt_vid_out_overlay(file, - fh, f); - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (vfd->vidioc_try_fmt_vbi_cap) - ret = vfd->vidioc_try_fmt_vbi_cap(file, fh, f); - break; - case V4L2_BUF_TYPE_VBI_OUTPUT: - if (vfd->vidioc_try_fmt_vbi_out) - ret = vfd->vidioc_try_fmt_vbi_out(file, fh, f); - break; - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - if (vfd->vidioc_try_fmt_sliced_vbi_cap) - ret = vfd->vidioc_try_fmt_sliced_vbi_cap(file, - fh, f); - break; - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - if (vfd->vidioc_try_fmt_sliced_vbi_out) - ret = vfd->vidioc_try_fmt_sliced_vbi_out(file, - fh, f); - break; - case V4L2_BUF_TYPE_PRIVATE: - if (vfd->vidioc_try_fmt_type_private) - ret = vfd->vidioc_try_fmt_type_private(file, - fh, f); - break; - } - - break; - } - /* FIXME: Those buf reqs could be handled here, - with some changes on videobuf to allow its header to be included at - videodev2.h or being merged at videodev2. - */ - case VIDIOC_REQBUFS: - { - struct v4l2_requestbuffers *p=arg; - - if (!vfd->vidioc_reqbufs) - break; - ret = check_fmt (vfd, p->type); - if (ret) - break; - - ret=vfd->vidioc_reqbufs(file, fh, p); - dbgarg (cmd, "count=%d, type=%s, memory=%s\n", - p->count, - prt_names(p->type, v4l2_type_names), - prt_names(p->memory, v4l2_memory_names)); - break; - } - case VIDIOC_QUERYBUF: - { - struct v4l2_buffer *p=arg; - - if (!vfd->vidioc_querybuf) - break; - ret = check_fmt (vfd, p->type); - if (ret) - break; - - ret=vfd->vidioc_querybuf(file, fh, p); - if (!ret) - dbgbuf(cmd,vfd,p); - break; - } - case VIDIOC_QBUF: - { - struct v4l2_buffer *p=arg; - - if (!vfd->vidioc_qbuf) - break; - ret = check_fmt (vfd, p->type); - if (ret) - break; - - ret=vfd->vidioc_qbuf(file, fh, p); - if (!ret) - dbgbuf(cmd,vfd,p); - break; - } - case VIDIOC_DQBUF: - { - struct v4l2_buffer *p=arg; - if (!vfd->vidioc_dqbuf) - break; - ret = check_fmt (vfd, p->type); - if (ret) - break; - - ret=vfd->vidioc_dqbuf(file, fh, p); - if (!ret) - dbgbuf(cmd,vfd,p); - break; - } - case VIDIOC_OVERLAY: - { - int *i = arg; - - if (!vfd->vidioc_overlay) - break; - dbgarg (cmd, "value=%d\n",*i); - ret=vfd->vidioc_overlay(file, fh, *i); - break; - } - case VIDIOC_G_FBUF: - { - struct v4l2_framebuffer *p = arg; - - if (!vfd->vidioc_g_fbuf) - break; - ret = vfd->vidioc_g_fbuf(file, fh, arg); - if (!ret) { - dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n", - p->capability, p->flags, - (unsigned long)p->base); - v4l_print_pix_fmt(vfd, &p->fmt); - } - break; - } - case VIDIOC_S_FBUF: - { - struct v4l2_framebuffer *p = arg; - - if (!vfd->vidioc_s_fbuf) - break; - dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n", - p->capability, p->flags, (unsigned long)p->base); - v4l_print_pix_fmt(vfd, &p->fmt); - ret = vfd->vidioc_s_fbuf(file, fh, arg); - break; - } - case VIDIOC_STREAMON: - { - enum v4l2_buf_type i = *(int *)arg; - if (!vfd->vidioc_streamon) - break; - dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names)); - ret=vfd->vidioc_streamon(file, fh,i); - break; - } - case VIDIOC_STREAMOFF: - { - enum v4l2_buf_type i = *(int *)arg; - - if (!vfd->vidioc_streamoff) - break; - dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names)); - ret=vfd->vidioc_streamoff(file, fh, i); - break; - } - /* ---------- tv norms ---------- */ - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *p = arg; - v4l2_std_id id = vfd->tvnorms, curr_id = 0; - unsigned int index = p->index, i, j = 0; - const char *descr = ""; - - /* Return norm array in a canonical way */ - for (i = 0; i <= index && id; i++) { - /* last std value in the standards array is 0, so this - while always ends there since (id & 0) == 0. */ - while ((id & standards[j].std) != standards[j].std) - j++; - curr_id = standards[j].std; - descr = standards[j].descr; - j++; - if (curr_id == 0) - break; - if (curr_id != V4L2_STD_PAL && - curr_id != V4L2_STD_SECAM && - curr_id != V4L2_STD_NTSC) - id &= ~curr_id; - } - if (i <= index) - return -EINVAL; - - v4l2_video_std_construct(p, curr_id, descr); - p->index = index; - - dbgarg(cmd, "index=%d, id=0x%Lx, name=%s, fps=%d/%d, " - "framelines=%d\n", p->index, - (unsigned long long)p->id, p->name, - p->frameperiod.numerator, - p->frameperiod.denominator, - p->framelines); - - ret = 0; - break; - } - case VIDIOC_G_STD: - { - v4l2_std_id *id = arg; - - ret = 0; - /* Calls the specific handler */ - if (vfd->vidioc_g_std) - ret = vfd->vidioc_g_std(file, fh, id); - else - *id = vfd->current_norm; - - if (!ret) - dbgarg(cmd, "std=0x%08Lx\n", (long long unsigned)*id); - break; - } - case VIDIOC_S_STD: - { - v4l2_std_id *id = arg,norm; - - dbgarg(cmd, "std=%08Lx\n", (long long unsigned)*id); - - norm = (*id) & vfd->tvnorms; - if ( vfd->tvnorms && !norm) /* Check if std is supported */ - break; - - /* Calls the specific handler */ - if (vfd->vidioc_s_std) - ret=vfd->vidioc_s_std(file, fh, &norm); - else - ret=-EINVAL; - - /* Updates standard information */ - if (ret>=0) - vfd->current_norm=norm; - - break; - } - case VIDIOC_QUERYSTD: - { - v4l2_std_id *p=arg; - - if (!vfd->vidioc_querystd) - break; - ret=vfd->vidioc_querystd(file, fh, arg); - if (!ret) - dbgarg (cmd, "detected std=%08Lx\n", - (unsigned long long)*p); - break; - } - /* ------ input switching ---------- */ - /* FIXME: Inputs can be handled inside videodev2 */ - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *p=arg; - int i=p->index; - - if (!vfd->vidioc_enum_input) - break; - memset(p, 0, sizeof(*p)); - p->index=i; - - ret=vfd->vidioc_enum_input(file, fh, p); - if (!ret) - dbgarg (cmd, "index=%d, name=%s, type=%d, " - "audioset=%d, " - "tuner=%d, std=%08Lx, status=%d\n", - p->index,p->name,p->type,p->audioset, - p->tuner, - (unsigned long long)p->std, - p->status); - break; - } - case VIDIOC_G_INPUT: - { - unsigned int *i = arg; - - if (!vfd->vidioc_g_input) - break; - ret=vfd->vidioc_g_input(file, fh, i); - if (!ret) - dbgarg (cmd, "value=%d\n",*i); - break; - } - case VIDIOC_S_INPUT: - { - unsigned int *i = arg; - - if (!vfd->vidioc_s_input) - break; - dbgarg (cmd, "value=%d\n",*i); - ret=vfd->vidioc_s_input(file, fh, *i); - break; - } - - /* ------ output switching ---------- */ - case VIDIOC_ENUMOUTPUT: - { - struct v4l2_output *p = arg; - int i = p->index; - - if (!vfd->vidioc_enum_output) - break; - memset(p, 0, sizeof(*p)); - p->index = i; - - ret = vfd->vidioc_enum_output(file, fh, p); - if (!ret) - dbgarg(cmd, "index=%d, name=%s, type=%d, " - "audioset=0x%x, " - "modulator=%d, std=0x%08Lx\n", - p->index, p->name, p->type, p->audioset, - p->modulator, (unsigned long long)p->std); - break; - } - case VIDIOC_G_OUTPUT: - { - unsigned int *i = arg; - - if (!vfd->vidioc_g_output) - break; - ret=vfd->vidioc_g_output(file, fh, i); - if (!ret) - dbgarg (cmd, "value=%d\n",*i); - break; - } - case VIDIOC_S_OUTPUT: - { - unsigned int *i = arg; - - if (!vfd->vidioc_s_output) - break; - dbgarg (cmd, "value=%d\n",*i); - ret=vfd->vidioc_s_output(file, fh, *i); - break; - } - - /* --- controls ---------------------------------------------- */ - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *p = arg; - - if (!vfd->vidioc_queryctrl) - break; - ret = vfd->vidioc_queryctrl(file, fh, p); - if (!ret) - dbgarg(cmd, "id=0x%x, type=%d, name=%s, min/max=%d/%d, " - "step=%d, default=%d, flags=0x%08x\n", - p->id, p->type, p->name, - p->minimum, p->maximum, - p->step, p->default_value, p->flags); - else - dbgarg(cmd, "id=0x%x\n", p->id); - break; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *p = arg; - - if (vfd->vidioc_g_ctrl) - ret = vfd->vidioc_g_ctrl(file, fh, p); - else if (vfd->vidioc_g_ext_ctrls) { - struct v4l2_ext_controls ctrls; - struct v4l2_ext_control ctrl; - - ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id); - ctrls.count = 1; - ctrls.controls = &ctrl; - ctrl.id = p->id; - ctrl.value = p->value; - if (check_ext_ctrls(&ctrls, 1)) { - ret = vfd->vidioc_g_ext_ctrls(file, fh, &ctrls); - if (ret == 0) - p->value = ctrl.value; - } - } else - break; - if (!ret) - dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value); - else - dbgarg(cmd, "id=0x%x\n", p->id); - break; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *p = arg; - struct v4l2_ext_controls ctrls; - struct v4l2_ext_control ctrl; - - if (!vfd->vidioc_s_ctrl && !vfd->vidioc_s_ext_ctrls) - break; - - dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value); - - if (vfd->vidioc_s_ctrl) { - ret = vfd->vidioc_s_ctrl(file, fh, p); - break; - } - if (!vfd->vidioc_s_ext_ctrls) - break; - - ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id); - ctrls.count = 1; - ctrls.controls = &ctrl; - ctrl.id = p->id; - ctrl.value = p->value; - if (check_ext_ctrls(&ctrls, 1)) - ret = vfd->vidioc_s_ext_ctrls(file, fh, &ctrls); - break; - } - case VIDIOC_G_EXT_CTRLS: - { - struct v4l2_ext_controls *p = arg; - - p->error_idx = p->count; - if (!vfd->vidioc_g_ext_ctrls) - break; - if (check_ext_ctrls(p, 0)) - ret = vfd->vidioc_g_ext_ctrls(file, fh, p); - v4l_print_ext_ctrls(cmd, vfd, p, !ret); - break; - } - case VIDIOC_S_EXT_CTRLS: - { - struct v4l2_ext_controls *p = arg; - - p->error_idx = p->count; - if (!vfd->vidioc_s_ext_ctrls) - break; - v4l_print_ext_ctrls(cmd, vfd, p, 1); - if (check_ext_ctrls(p, 0)) - ret = vfd->vidioc_s_ext_ctrls(file, fh, p); - break; - } - case VIDIOC_TRY_EXT_CTRLS: - { - struct v4l2_ext_controls *p = arg; - - p->error_idx = p->count; - if (!vfd->vidioc_try_ext_ctrls) - break; - v4l_print_ext_ctrls(cmd, vfd, p, 1); - if (check_ext_ctrls(p, 0)) - ret = vfd->vidioc_try_ext_ctrls(file, fh, p); - break; - } - case VIDIOC_QUERYMENU: - { - struct v4l2_querymenu *p = arg; - - if (!vfd->vidioc_querymenu) - break; - ret = vfd->vidioc_querymenu(file, fh, p); - if (!ret) - dbgarg(cmd, "id=0x%x, index=%d, name=%s\n", - p->id, p->index, p->name); - else - dbgarg(cmd, "id=0x%x, index=%d\n", - p->id, p->index); - break; - } - /* --- audio ---------------------------------------------- */ - case VIDIOC_ENUMAUDIO: - { - struct v4l2_audio *p = arg; - - if (!vfd->vidioc_enumaudio) - break; - ret = vfd->vidioc_enumaudio(file, fh, p); - if (!ret) - dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " - "mode=0x%x\n", p->index, p->name, - p->capability, p->mode); - else - dbgarg(cmd, "index=%d\n", p->index); - break; - } - case VIDIOC_G_AUDIO: - { - struct v4l2_audio *p = arg; - __u32 index = p->index; - - if (!vfd->vidioc_g_audio) - break; - - memset(p, 0, sizeof(*p)); - p->index = index; - ret = vfd->vidioc_g_audio(file, fh, p); - if (!ret) - dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " - "mode=0x%x\n", p->index, - p->name, p->capability, p->mode); - else - dbgarg(cmd, "index=%d\n", p->index); - break; - } - case VIDIOC_S_AUDIO: - { - struct v4l2_audio *p = arg; - - if (!vfd->vidioc_s_audio) - break; - dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " - "mode=0x%x\n", p->index, p->name, - p->capability, p->mode); - ret = vfd->vidioc_s_audio(file, fh, p); - break; - } - case VIDIOC_ENUMAUDOUT: - { - struct v4l2_audioout *p=arg; - - if (!vfd->vidioc_enumaudout) - break; - dbgarg(cmd, "Enum for index=%d\n", p->index); - ret=vfd->vidioc_enumaudout(file, fh, p); - if (!ret) - dbgarg2("index=%d, name=%s, capability=%d, " - "mode=%d\n", p->index, p->name, - p->capability,p->mode); - break; - } - case VIDIOC_G_AUDOUT: - { - struct v4l2_audioout *p=arg; - - if (!vfd->vidioc_g_audout) - break; - dbgarg(cmd, "Enum for index=%d\n", p->index); - ret=vfd->vidioc_g_audout(file, fh, p); - if (!ret) - dbgarg2("index=%d, name=%s, capability=%d, " - "mode=%d\n", p->index, p->name, - p->capability,p->mode); - break; - } - case VIDIOC_S_AUDOUT: - { - struct v4l2_audioout *p=arg; - - if (!vfd->vidioc_s_audout) - break; - dbgarg(cmd, "index=%d, name=%s, capability=%d, " - "mode=%d\n", p->index, p->name, - p->capability,p->mode); - - ret=vfd->vidioc_s_audout(file, fh, p); - break; - } - case VIDIOC_G_MODULATOR: - { - struct v4l2_modulator *p=arg; - if (!vfd->vidioc_g_modulator) - break; - ret=vfd->vidioc_g_modulator(file, fh, p); - if (!ret) - dbgarg(cmd, "index=%d, name=%s, " - "capability=%d, rangelow=%d," - " rangehigh=%d, txsubchans=%d\n", - p->index, p->name,p->capability, - p->rangelow, p->rangehigh, - p->txsubchans); - break; - } - case VIDIOC_S_MODULATOR: - { - struct v4l2_modulator *p=arg; - if (!vfd->vidioc_s_modulator) - break; - dbgarg(cmd, "index=%d, name=%s, capability=%d, " - "rangelow=%d, rangehigh=%d, txsubchans=%d\n", - p->index, p->name,p->capability,p->rangelow, - p->rangehigh,p->txsubchans); - ret=vfd->vidioc_s_modulator(file, fh, p); - break; - } - case VIDIOC_G_CROP: - { - struct v4l2_crop *p=arg; - if (!vfd->vidioc_g_crop) - break; - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); - ret=vfd->vidioc_g_crop(file, fh, p); - if (!ret) { - dbgrect(vfd, "", &p->c); - } - break; - } - case VIDIOC_S_CROP: - { - struct v4l2_crop *p=arg; - if (!vfd->vidioc_s_crop) - break; - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); - dbgrect(vfd, "", &p->c); - ret=vfd->vidioc_s_crop(file, fh, p); - break; - } - case VIDIOC_CROPCAP: - { - struct v4l2_cropcap *p = arg; - - /*FIXME: Should also show v4l2_fract pixelaspect */ - if (!vfd->vidioc_cropcap) - break; - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); - ret = vfd->vidioc_cropcap(file, fh, p); - if (!ret) { - dbgrect(vfd, "bounds ", &p->bounds); - dbgrect(vfd, "defrect ", &p->defrect); - } - break; - } - case VIDIOC_G_JPEGCOMP: - { - struct v4l2_jpegcompression *p=arg; - if (!vfd->vidioc_g_jpegcomp) - break; - ret=vfd->vidioc_g_jpegcomp(file, fh, p); - if (!ret) - dbgarg (cmd, "quality=%d, APPn=%d, " - "APP_len=%d, COM_len=%d, " - "jpeg_markers=%d\n", - p->quality,p->APPn,p->APP_len, - p->COM_len,p->jpeg_markers); - break; - } - case VIDIOC_S_JPEGCOMP: - { - struct v4l2_jpegcompression *p=arg; - if (!vfd->vidioc_g_jpegcomp) - break; - dbgarg (cmd, "quality=%d, APPn=%d, APP_len=%d, " - "COM_len=%d, jpeg_markers=%d\n", - p->quality,p->APPn,p->APP_len, - p->COM_len,p->jpeg_markers); - 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; - memset(&p->raw, 0, sizeof(p->raw)); - ret = vfd->vidioc_encoder_cmd(file, fh, p); - if (!ret) - dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); - break; - } - case VIDIOC_TRY_ENCODER_CMD: - { - struct v4l2_encoder_cmd *p = arg; - - if (!vfd->vidioc_try_encoder_cmd) - break; - memset(&p->raw, 0, sizeof(p->raw)); - ret = vfd->vidioc_try_encoder_cmd(file, fh, p); - if (!ret) - dbgarg(cmd, "cmd=%d, flags=%x\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 { - struct v4l2_standard s; - - if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - v4l2_video_std_construct(&s, vfd->current_norm, - v4l2_norm_to_name(vfd->current_norm)); - - p->parm.capture.timeperframe = s.frameperiod; - ret=0; - } - - dbgarg (cmd, "type=%d\n", p->type); - break; - } - case VIDIOC_S_PARM: - { - struct v4l2_streamparm *p=arg; - if (!vfd->vidioc_s_parm) - break; - dbgarg (cmd, "type=%d\n", p->type); - ret=vfd->vidioc_s_parm(file, fh, p); - break; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *p = arg; - __u32 index = p->index; - - if (!vfd->vidioc_g_tuner) - break; - - memset(p, 0, sizeof(*p)); - p->index = index; - - ret = vfd->vidioc_g_tuner(file, fh, p); - if (!ret) - dbgarg(cmd, "index=%d, name=%s, type=%d, " - "capability=0x%x, rangelow=%d, " - "rangehigh=%d, signal=%d, afc=%d, " - "rxsubchans=0x%x, audmode=%d\n", - p->index, p->name, p->type, - p->capability, p->rangelow, - p->rangehigh, p->signal, p->afc, - p->rxsubchans, p->audmode); - break; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *p = arg; - - if (!vfd->vidioc_s_tuner) - break; - dbgarg(cmd, "index=%d, name=%s, type=%d, " - "capability=0x%x, rangelow=%d, " - "rangehigh=%d, signal=%d, afc=%d, " - "rxsubchans=0x%x, audmode=%d\n", - p->index, p->name, p->type, - p->capability, p->rangelow, - p->rangehigh, p->signal, p->afc, - p->rxsubchans, p->audmode); - ret = vfd->vidioc_s_tuner(file, fh, p); - break; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *p = arg; - - if (!vfd->vidioc_g_frequency) - break; - - memset(p->reserved, 0, sizeof(p->reserved)); - - ret = vfd->vidioc_g_frequency(file, fh, p); - if (!ret) - dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n", - p->tuner, p->type, p->frequency); - break; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *p=arg; - if (!vfd->vidioc_s_frequency) - break; - dbgarg (cmd, "tuner=%d, type=%d, frequency=%d\n", - p->tuner,p->type,p->frequency); - ret=vfd->vidioc_s_frequency(file, fh, p); - break; - } - case VIDIOC_G_SLICED_VBI_CAP: - { - struct v4l2_sliced_vbi_cap *p = arg; - __u32 type = p->type; - - if (!vfd->vidioc_g_sliced_vbi_cap) - break; - memset(p, 0, sizeof(*p)); - p->type = type; - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); - ret = vfd->vidioc_g_sliced_vbi_cap(file, fh, p); - if (!ret) - dbgarg2("service_set=%d\n", p->service_set); - break; - } - case VIDIOC_LOG_STATUS: - { - if (!vfd->vidioc_log_status) - break; - 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; - } - default: - { - if (!vfd->vidioc_default) - break; - ret = vfd->vidioc_default(file, fh, cmd, arg); - break; - } - case VIDIOC_S_HW_FREQ_SEEK: - { - struct v4l2_hw_freq_seek *p = arg; - if (!vfd->vidioc_s_hw_freq_seek) - break; - dbgarg(cmd, - "tuner=%d, type=%d, seek_upward=%d, wrap_around=%d\n", - p->tuner, p->type, p->seek_upward, p->wrap_around); - ret = vfd->vidioc_s_hw_freq_seek(file, fh, p); - break; - } - } /* switch */ - - if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { - if (ret < 0) { - v4l_print_ioctl(vfd->name, cmd); - printk(KERN_CONT " error %d\n", ret); - } - } - - return ret; -} - -int video_ioctl2 (struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - char sbuf[128]; - void *mbuf = NULL; - void *parg = NULL; - int err = -EINVAL; - int is_ext_ctrl; - size_t ctrls_size = 0; - void __user *user_ptr = NULL; - -#ifdef __OLD_VIDIOC_ - cmd = video_fix_command(cmd); -#endif - is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS || - cmd == VIDIOC_TRY_EXT_CTRLS); - - /* Copy arguments into temp kernel buffer */ - switch (_IOC_DIR(cmd)) { - case _IOC_NONE: - parg = NULL; - break; - case _IOC_READ: - case _IOC_WRITE: - case (_IOC_WRITE | _IOC_READ): - if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { - parg = sbuf; - } else { - /* too big to allocate from stack */ - mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL); - if (NULL == mbuf) - return -ENOMEM; - parg = mbuf; - } - - err = -EFAULT; - if (_IOC_DIR(cmd) & _IOC_WRITE) - if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) - goto out; - break; - } - - if (is_ext_ctrl) { - struct v4l2_ext_controls *p = parg; - - /* In case of an error, tell the caller that it wasn't - a specific control that caused it. */ - p->error_idx = p->count; - user_ptr = (void __user *)p->controls; - if (p->count) { - ctrls_size = sizeof(struct v4l2_ext_control) * p->count; - /* Note: v4l2_ext_controls fits in sbuf[] so mbuf is still NULL. */ - mbuf = kmalloc(ctrls_size, GFP_KERNEL); - err = -ENOMEM; - if (NULL == mbuf) - goto out_ext_ctrl; - err = -EFAULT; - if (copy_from_user(mbuf, user_ptr, ctrls_size)) - goto out_ext_ctrl; - p->controls = mbuf; - } - } - - /* Handles IOCTL */ - err = __video_do_ioctl(inode, file, cmd, parg); - if (err == -ENOIOCTLCMD) - err = -EINVAL; - if (is_ext_ctrl) { - struct v4l2_ext_controls *p = parg; - - p->controls = (void *)user_ptr; - if (p->count && err == 0 && copy_to_user(user_ptr, mbuf, ctrls_size)) - err = -EFAULT; - goto out_ext_ctrl; - } - if (err < 0) - goto out; - -out_ext_ctrl: - /* Copy results into user buffer */ - switch (_IOC_DIR(cmd)) - { - case _IOC_READ: - case (_IOC_WRITE | _IOC_READ): - if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) - err = -EFAULT; - break; - } - -out: - kfree(mbuf); - return err; -} -EXPORT_SYMBOL(video_ioctl2); - -/** - * get_index - assign stream number based on parent device - * @vdev: video_device to assign index number to, vdev->dev should be assigned - * @num: -1 if auto assign, requested number otherwise - * - * - * returns -ENFILE if num is already in use, a free index number if - * successful. - */ -static int get_index(struct video_device *vdev, int num) -{ - u32 used = 0; - const int max_index = sizeof(used) * 8 - 1; - int i; - - /* Currently a single v4l driver instance cannot create more than - 32 devices. - Increase to u64 or an array of u32 if more are needed. */ - if (num > max_index) { - printk(KERN_ERR "videodev: %s num is too large\n", __func__); - return -EINVAL; - } - - for (i = 0; i < VIDEO_NUM_DEVICES; i++) { - if (video_device[i] != NULL && - video_device[i] != vdev && - video_device[i]->dev == vdev->dev) { - used |= 1 << video_device[i]->index; - } - } - - if (num >= 0) { - if (used & (1 << num)) - return -ENFILE; - return num; - } - - i = ffz(used); - return i > max_index ? -ENFILE : i; -} - -static const struct file_operations video_fops; - -int video_register_device(struct video_device *vfd, int type, int nr) -{ - return video_register_device_index(vfd, type, nr, -1); -} -EXPORT_SYMBOL(video_register_device); - -/** - * video_register_device - register video4linux devices - * @vfd: video device structure we want to register - * @type: type of device to register - * @nr: which device number (0 == /dev/video0, 1 == /dev/video1, ... - * -1 == first free) - * - * The registration code assigns minor numbers based on the type - * requested. -ENFILE is returned in all the device slots for this - * category are full. If not then the minor field is set and the - * driver initialize function is called (if non %NULL). - * - * Zero is returned on success. - * - * Valid types are - * - * %VFL_TYPE_GRABBER - A frame grabber - * - * %VFL_TYPE_VTX - A teletext device - * - * %VFL_TYPE_VBI - Vertical blank data (undecoded) - * - * %VFL_TYPE_RADIO - A radio card - */ - -int video_register_device_index(struct video_device *vfd, int type, int nr, - int index) -{ - int i=0; - int base; - int end; - int ret; - char *name_base; - - switch(type) - { - case VFL_TYPE_GRABBER: - base=MINOR_VFL_TYPE_GRABBER_MIN; - end=MINOR_VFL_TYPE_GRABBER_MAX+1; - name_base = "video"; - break; - case VFL_TYPE_VTX: - base=MINOR_VFL_TYPE_VTX_MIN; - end=MINOR_VFL_TYPE_VTX_MAX+1; - name_base = "vtx"; - break; - case VFL_TYPE_VBI: - base=MINOR_VFL_TYPE_VBI_MIN; - end=MINOR_VFL_TYPE_VBI_MAX+1; - name_base = "vbi"; - break; - case VFL_TYPE_RADIO: - base=MINOR_VFL_TYPE_RADIO_MIN; - end=MINOR_VFL_TYPE_RADIO_MAX+1; - name_base = "radio"; - break; - default: - printk(KERN_ERR "%s called with unknown type: %d\n", - __func__, type); - return -1; - } - - /* pick a minor number */ - mutex_lock(&videodev_lock); - if (nr >= 0 && nr < end-base) { - /* use the one the driver asked for */ - i = base+nr; - if (NULL != video_device[i]) { - mutex_unlock(&videodev_lock); - return -ENFILE; - } - } else { - /* use first free */ - for(i=base;i<end;i++) - if (NULL == video_device[i]) - break; - if (i == end) { - mutex_unlock(&videodev_lock); - return -ENFILE; - } - } - video_device[i]=vfd; - vfd->minor=i; - - ret = get_index(vfd, index); - vfd->index = ret; - - mutex_unlock(&videodev_lock); - - if (ret < 0) { - printk(KERN_ERR "%s: get_index failed\n", __func__); - goto fail_minor; - } - - mutex_init(&vfd->lock); - - /* sysfs class */ - memset(&vfd->class_dev, 0x00, sizeof(vfd->class_dev)); - vfd->class_dev.class = &video_class; - vfd->class_dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor); - if (vfd->dev) - vfd->class_dev.parent = vfd->dev; - sprintf(vfd->class_dev.bus_id, "%s%d", name_base, i - base); - ret = device_register(&vfd->class_dev); - if (ret < 0) { - printk(KERN_ERR "%s: device_register failed\n", __func__); - goto fail_minor; - } - -#if 1 - /* needed until all drivers are fixed */ - if (!vfd->release) - printk(KERN_WARNING "videodev: \"%s\" has no release callback. " - "Please fix your driver for proper sysfs support, see " - "http://lwn.net/Articles/36850/\n", vfd->name); -#endif - return 0; - -fail_minor: - mutex_lock(&videodev_lock); - video_device[vfd->minor] = NULL; - vfd->minor = -1; - mutex_unlock(&videodev_lock); - return ret; -} -EXPORT_SYMBOL(video_register_device_index); - -/** - * video_unregister_device - unregister a video4linux device - * @vfd: the device to unregister - * - * This unregisters the passed device and deassigns the minor - * number. Future open calls will be met with errors. - */ - -void video_unregister_device(struct video_device *vfd) -{ - mutex_lock(&videodev_lock); - if(video_device[vfd->minor]!=vfd) - panic("videodev: bad unregister"); - - video_device[vfd->minor]=NULL; - device_unregister(&vfd->class_dev); - mutex_unlock(&videodev_lock); -} -EXPORT_SYMBOL(video_unregister_device); - -/* - * Video fs operations - */ -static const struct file_operations video_fops= -{ - .owner = THIS_MODULE, - .llseek = no_llseek, - .open = video_open, -}; - -/* - * Initialise video for linux - */ - -static int __init videodev_init(void) -{ - int ret; - - printk(KERN_INFO "Linux video capture interface: v2.00\n"); - if (register_chrdev(VIDEO_MAJOR, VIDEO_NAME, &video_fops)) { - printk(KERN_WARNING "video_dev: unable to get major %d\n", VIDEO_MAJOR); - return -EIO; - } - - ret = class_register(&video_class); - if (ret < 0) { - unregister_chrdev(VIDEO_MAJOR, VIDEO_NAME); - printk(KERN_WARNING "video_dev: class_register failed\n"); - return -EIO; - } - - return 0; -} - -static void __exit videodev_exit(void) -{ - class_unregister(&video_class); - unregister_chrdev(VIDEO_MAJOR, VIDEO_NAME); -} - -module_init(videodev_init) -module_exit(videodev_exit) - -MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>"); -MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2"); -MODULE_LICENSE("GPL"); - - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c index 01ea99c9bc1a..3989b0eded28 100644 --- a/drivers/media/video/vino.c +++ b/drivers/media/video/vino.c @@ -38,7 +38,7 @@ #include <linux/i2c.h> #include <linux/i2c-algo-sgi.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> #include <linux/video_decoder.h> #include <linux/mutex.h> @@ -4385,8 +4385,6 @@ static const struct file_operations vino_fops = { static struct video_device v4l_device_template = { .name = "NOT SET", - /*.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE | */ - /* VID_TYPE_CLIPPING | VID_TYPE_SCALES, VID_TYPE_OVERLAY */ .fops = &vino_fops, .minor = -1, }; diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 059b01c11dc1..3518af071a2e 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -35,6 +35,7 @@ #include <linux/interrupt.h> #include <media/videobuf-vmalloc.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/kthread.h> #include <linux/highmem.h> #include <linux/freezer.h> @@ -1065,13 +1066,7 @@ static const struct file_operations vivi_fops = { .llseek = no_llseek, }; -static struct video_device vivi_template = { - .name = "vivi", - .type = VID_TYPE_CAPTURE, - .fops = &vivi_fops, - .minor = -1, - .release = video_device_release, - +static const struct v4l2_ioctl_ops vivi_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, @@ -1093,6 +1088,15 @@ static struct video_device vivi_template = { #ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidiocgmbuf, #endif +}; + +static struct video_device vivi_template = { + .name = "vivi", + .fops = &vivi_fops, + .ioctl_ops = &vivi_ioctl_ops, + .minor = -1, + .release = video_device_release, + .tvnorms = V4L2_STD_525_60, .current_norm = V4L2_STD_NTSC_M, }; diff --git a/drivers/media/video/vp27smpx.c b/drivers/media/video/vp27smpx.c index cbecb3cbbbaa..577956c5410b 100644 --- a/drivers/media/video/vp27smpx.c +++ b/drivers/media/video/vp27smpx.c @@ -27,7 +27,7 @@ #include <asm/uaccess.h> #include <linux/i2c.h> #include <linux/i2c-id.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c index 33f702698a56..9402f40095b4 100644 --- a/drivers/media/video/w9966.c +++ b/drivers/media/video/w9966.c @@ -57,8 +57,9 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/delay.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/parport.h> /*#define DEBUG*/ /* Undef me for production */ @@ -195,9 +196,7 @@ static const struct file_operations w9966_fops = { .llseek = no_llseek, }; static struct video_device w9966_template = { - .owner = THIS_MODULE, .name = W9966_DRIVERNAME, - .type = VID_TYPE_CAPTURE | VID_TYPE_SCALES, .fops = &w9966_fops, }; diff --git a/drivers/media/video/w9968cf.c b/drivers/media/video/w9968cf.c index 840522442d07..168baabe4659 100644 --- a/drivers/media/video/w9968cf.c +++ b/drivers/media/video/w9968cf.c @@ -42,6 +42,7 @@ #include <asm/page.h> #include <asm/uaccess.h> #include <linux/page-flags.h> +#include <media/v4l2-ioctl.h> #include "w9968cf.h" #include "w9968cf_decoder.h" @@ -3549,13 +3550,11 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) } strcpy(cam->v4ldev->name, symbolic(camlist, mod_id)); - cam->v4ldev->owner = THIS_MODULE; - cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; cam->v4ldev->fops = &w9968cf_fops; cam->v4ldev->minor = video_nr[dev_nr]; cam->v4ldev->release = video_device_release; video_set_drvdata(cam->v4ldev, cam); - cam->v4ldev->dev = &cam->dev; + cam->v4ldev->parent = &cam->dev; err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, video_nr[dev_nr]); diff --git a/drivers/media/video/w9968cf.h b/drivers/media/video/w9968cf.h index 3c95316bc030..30032e15e23c 100644 --- a/drivers/media/video/w9968cf.h +++ b/drivers/media/video/w9968cf.h @@ -21,7 +21,7 @@ #ifndef _W9968CF_H_ #define _W9968CF_H_ -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <linux/usb.h> #include <linux/i2c.h> #include <linux/device.h> diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c index 7be47a255853..95c79ad80487 100644 --- a/drivers/media/video/wm8739.c +++ b/drivers/media/video/wm8739.c @@ -27,7 +27,7 @@ #include <asm/uaccess.h> #include <linux/i2c.h> #include <linux/i2c-id.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c index c2ab70a04a74..48df661d4fc3 100644 --- a/drivers/media/video/wm8775.c +++ b/drivers/media/video/wm8775.c @@ -31,7 +31,7 @@ #include <asm/uaccess.h> #include <linux/i2c.h> #include <linux/i2c-id.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv-legacy.h> diff --git a/drivers/media/video/zc0301/zc0301.h b/drivers/media/video/zc0301/zc0301.h index 7bbab541a309..b1b5cceb4baa 100644 --- a/drivers/media/video/zc0301/zc0301.h +++ b/drivers/media/video/zc0301/zc0301.h @@ -25,6 +25,7 @@ #include <linux/usb.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <linux/device.h> #include <linux/list.h> #include <linux/spinlock.h> diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c index e5c4e9f5193f..550ce7bd5c87 100644 --- a/drivers/media/video/zc0301/zc0301_core.c +++ b/drivers/media/video/zc0301/zc0301_core.c @@ -1985,8 +1985,6 @@ zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) } strcpy(cam->v4ldev->name, "ZC0301[P] PC Camera"); - cam->v4ldev->owner = THIS_MODULE; - cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; cam->v4ldev->fops = &zc0301_fops; cam->v4ldev->minor = video_nr[dev_nr]; cam->v4ldev->release = video_device_release; diff --git a/drivers/media/video/zoran_card.c b/drivers/media/video/zoran_card.c index 0929edb2d4f1..d842a7cb99d2 100644 --- a/drivers/media/video/zoran_card.c +++ b/drivers/media/video/zoran_card.c @@ -161,7 +161,7 @@ static struct pci_device_id zr36067_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl); int zoran_num; /* number of Buzs in use */ -struct zoran zoran[BUZ_MAX]; +struct zoran *zoran[BUZ_MAX]; /* videocodec bus functions ZR36060 */ static u32 @@ -355,9 +355,15 @@ i2cid_to_modulename (u16 i2c_id) case I2C_DRIVERID_BT856: name = "bt856"; break; + case I2C_DRIVERID_BT866: + name = "bt866"; + break; case I2C_DRIVERID_VPX3220: name = "vpx3220"; break; + case I2C_DRIVERID_KS0127: + name = "ks0127"; + break; } return name; @@ -1164,7 +1170,7 @@ static void zoran_release (struct zoran *zr) { if (!zr->initialized) - return; + goto exit_free; /* unregister videocodec bus */ if (zr->codec) { struct videocodec_master *master = zr->codec->master_data; @@ -1192,6 +1198,8 @@ zoran_release (struct zoran *zr) iounmap(zr->zr36057_mem); pci_disable_device(zr->pci_dev); video_unregister_device(zr->video_dev); +exit_free: + kfree(zr); } void @@ -1269,8 +1277,14 @@ find_zr36057 (void) while (zoran_num < BUZ_MAX && (dev = pci_get_device(PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36057, dev)) != NULL) { card_num = card[zoran_num]; - zr = &zoran[zoran_num]; - memset(zr, 0, sizeof(struct zoran)); // Just in case if previous cycle failed + zr = kzalloc(sizeof(struct zoran), GFP_KERNEL); + if (!zr) { + dprintk(1, + KERN_ERR + "%s: find_zr36057() - kzalloc failed\n", + ZORAN_NAME); + continue; + } zr->pci_dev = dev; //zr->zr36057_mem = NULL; zr->id = zoran_num; @@ -1278,7 +1292,7 @@ find_zr36057 (void) spin_lock_init(&zr->spinlock); mutex_init(&zr->resource_lock); if (pci_enable_device(dev)) - continue; + goto zr_free_mem; zr->zr36057_adr = pci_resource_start(zr->pci_dev, 0); pci_read_config_byte(zr->pci_dev, PCI_CLASS_REVISION, &zr->revision); @@ -1294,7 +1308,7 @@ find_zr36057 (void) KERN_ERR "%s: find_zr36057() - no card specified, please use the card=X insmod option\n", ZR_DEVNAME(zr)); - continue; + goto zr_free_mem; } } else { int i; @@ -1333,7 +1347,7 @@ find_zr36057 (void) KERN_ERR "%s: find_zr36057() - unknown card\n", ZR_DEVNAME(zr)); - continue; + goto zr_free_mem; } } } @@ -1343,7 +1357,7 @@ find_zr36057 (void) KERN_ERR "%s: find_zr36057() - invalid cardnum %d\n", ZR_DEVNAME(zr), card_num); - continue; + goto zr_free_mem; } /* even though we make this a non pointer and thus @@ -1361,7 +1375,7 @@ find_zr36057 (void) KERN_ERR "%s: find_zr36057() - ioremap failed\n", ZR_DEVNAME(zr)); - continue; + goto zr_free_mem; } result = request_irq(zr->pci_dev->irq, @@ -1530,7 +1544,7 @@ find_zr36057 (void) } /* Success so keep the pci_dev referenced */ pci_dev_get(zr->pci_dev); - zoran_num++; + zoran[zoran_num++] = zr; continue; // Init errors @@ -1549,6 +1563,8 @@ find_zr36057 (void) free_irq(zr->pci_dev->irq, zr); zr_unmap: iounmap(zr->zr36057_mem); + zr_free_mem: + kfree(zr); continue; } if (dev) /* Clean up ref count on early exit */ @@ -1620,7 +1636,7 @@ init_dc10_cards (void) /* take care of Natoma chipset and a revision 1 zr36057 */ for (i = 0; i < zoran_num; i++) { - struct zoran *zr = &zoran[i]; + struct zoran *zr = zoran[i]; if ((pci_pci_problems & PCIPCI_NATOMA) && zr->revision <= 1) { zr->jpg_buffers.need_contiguous = 1; @@ -1632,7 +1648,7 @@ init_dc10_cards (void) if (zr36057_init(zr) < 0) { for (i = 0; i < zoran_num; i++) - zoran_release(&zoran[i]); + zoran_release(zoran[i]); return -EIO; } zoran_proc_init(zr); @@ -1647,7 +1663,7 @@ unload_dc10_cards (void) int i; for (i = 0; i < zoran_num; i++) - zoran_release(&zoran[i]); + zoran_release(zoran[i]); } module_init(init_dc10_cards); diff --git a/drivers/media/video/zoran_card.h b/drivers/media/video/zoran_card.h index 1b5c4171cf9c..e4dc9d29b404 100644 --- a/drivers/media/video/zoran_card.h +++ b/drivers/media/video/zoran_card.h @@ -41,7 +41,7 @@ extern int zr36067_debug; /* Anybody who uses more than four? */ #define BUZ_MAX 4 extern int zoran_num; -extern struct zoran zoran[BUZ_MAX]; +extern struct zoran *zoran[BUZ_MAX]; extern struct video_device zoran_template; diff --git a/drivers/media/video/zoran_driver.c b/drivers/media/video/zoran_driver.c index c0675921fe20..ec6f59674b10 100644 --- a/drivers/media/video/zoran_driver.c +++ b/drivers/media/video/zoran_driver.c @@ -71,6 +71,7 @@ #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include "videocodec.h" #include <asm/byteorder.h> @@ -1212,8 +1213,8 @@ zoran_open (struct inode *inode, /* find the device */ for (i = 0; i < zoran_num; i++) { - if (zoran[i].video_dev->minor == minor) { - zr = &zoran[i]; + if (zoran[i]->video_dev->minor == minor) { + zr = zoran[i]; break; } } @@ -4643,8 +4644,6 @@ static const struct file_operations zoran_fops = { struct video_device zoran_template __devinitdata = { .name = ZORAN_NAME, - .type = ZORAN_VID_TYPE, - .type2 = ZORAN_V4L2_VID_FLAGS, .fops = &zoran_fops, .release = &zoran_vdev_release, .minor = -1 diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index 485df2e36132..18d1c4ba79fb 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -35,6 +35,7 @@ #include <linux/proc_fs.h> #include <linux/highmem.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> /* Version Information */ @@ -761,14 +762,7 @@ static const struct file_operations zr364xx_fops = { .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, - +static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = { .vidioc_querycap = zr364xx_vidioc_querycap, .vidioc_enum_fmt_vid_cap = zr364xx_vidioc_enum_fmt_vid_cap, .vidioc_try_fmt_vid_cap = zr364xx_vidioc_try_fmt_vid_cap, @@ -784,6 +778,14 @@ static struct video_device zr364xx_template = { .vidioc_s_ctrl = zr364xx_vidioc_s_ctrl, }; +static struct video_device zr364xx_template = { + .name = DRIVER_DESC, + .fops = &zr364xx_fops, + .ioctl_ops = &zr364xx_ioctl_ops, + .release = video_device_release, + .minor = -1, +}; + /*******************/ diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c index 61b98c333cb0..a38005008a20 100644 --- a/drivers/memstick/core/memstick.c +++ b/drivers/memstick/core/memstick.c @@ -249,8 +249,11 @@ EXPORT_SYMBOL(memstick_next_req); */ void memstick_new_req(struct memstick_host *host) { - host->retries = cmd_retries; - host->request(host); + if (host->card) { + host->retries = cmd_retries; + INIT_COMPLETION(host->card->mrq_complete); + host->request(host); + } } EXPORT_SYMBOL(memstick_new_req); @@ -415,10 +418,14 @@ err_out: return NULL; } -static void memstick_power_on(struct memstick_host *host) +static int memstick_power_on(struct memstick_host *host) { - host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON); - host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL); + int rc = host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON); + + if (!rc) + rc = host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL); + + return rc; } static void memstick_check(struct work_struct *work) @@ -429,8 +436,11 @@ static void memstick_check(struct work_struct *work) dev_dbg(&host->dev, "memstick_check started\n"); mutex_lock(&host->lock); - if (!host->card) - memstick_power_on(host); + if (!host->card) { + if (memstick_power_on(host)) + goto out_power_off; + } else + host->card->stop(host->card); card = memstick_alloc_card(host); @@ -448,7 +458,8 @@ static void memstick_check(struct work_struct *work) || !(host->card->check(host->card))) { device_unregister(&host->card->dev); host->card = NULL; - } + } else + host->card->start(host->card); } if (!host->card) { @@ -461,6 +472,7 @@ static void memstick_check(struct work_struct *work) kfree(card); } +out_power_off: if (!host->card) host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF); @@ -573,11 +585,15 @@ EXPORT_SYMBOL(memstick_suspend_host); */ void memstick_resume_host(struct memstick_host *host) { + int rc = 0; + mutex_lock(&host->lock); if (host->card) - memstick_power_on(host); + rc = memstick_power_on(host); mutex_unlock(&host->lock); - memstick_detect_change(host); + + if (!rc) + memstick_detect_change(host); } EXPORT_SYMBOL(memstick_resume_host); diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c index 477d0fb6e588..44b1817f2f2f 100644 --- a/drivers/memstick/core/mspro_block.c +++ b/drivers/memstick/core/mspro_block.c @@ -136,9 +136,8 @@ struct mspro_block_data { unsigned int caps; struct gendisk *disk; struct request_queue *queue; + struct request *block_req; spinlock_t q_lock; - wait_queue_head_t q_wait; - struct task_struct *q_thread; unsigned short page_size; unsigned short cylinders; @@ -147,9 +146,10 @@ struct mspro_block_data { unsigned char system; unsigned char read_only:1, - active:1, + eject:1, has_request:1, - data_dir:1; + data_dir:1, + active:1; unsigned char transfer_cmd; int (*mrq_handler)(struct memstick_dev *card, @@ -160,12 +160,14 @@ struct mspro_block_data { struct scatterlist req_sg[MSPRO_BLOCK_MAX_SEGS]; unsigned int seg_count; unsigned int current_seg; - unsigned short current_page; + unsigned int current_page; }; static DEFINE_IDR(mspro_block_disk_idr); static DEFINE_MUTEX(mspro_block_disk_lock); +static int mspro_block_complete_req(struct memstick_dev *card, int error); + /*** Block device ***/ static int mspro_block_bd_open(struct inode *inode, struct file *filp) @@ -197,8 +199,10 @@ static int mspro_block_disk_release(struct gendisk *disk) mutex_lock(&mspro_block_disk_lock); - if (msb->usage_count) { - msb->usage_count--; + if (msb) { + if (msb->usage_count) + msb->usage_count--; + if (!msb->usage_count) { kfree(msb); disk->private_data = NULL; @@ -523,11 +527,13 @@ static int h_mspro_block_req_init(struct memstick_dev *card, static int h_mspro_block_default(struct memstick_dev *card, struct memstick_request **mrq) { - complete(&card->mrq_complete); - if (!(*mrq)->error) - return -EAGAIN; - else - return (*mrq)->error; + return mspro_block_complete_req(card, (*mrq)->error); +} + +static int h_mspro_block_default_bad(struct memstick_dev *card, + struct memstick_request **mrq) +{ + return -ENXIO; } static int h_mspro_block_get_ro(struct memstick_dev *card, @@ -535,44 +541,30 @@ static int h_mspro_block_get_ro(struct memstick_dev *card, { struct mspro_block_data *msb = memstick_get_drvdata(card); - if ((*mrq)->error) { - complete(&card->mrq_complete); - return (*mrq)->error; + if (!(*mrq)->error) { + if ((*mrq)->data[offsetof(struct ms_status_register, status0)] + & MEMSTICK_STATUS0_WP) + msb->read_only = 1; + else + msb->read_only = 0; } - if ((*mrq)->data[offsetof(struct ms_status_register, status0)] - & MEMSTICK_STATUS0_WP) - msb->read_only = 1; - else - msb->read_only = 0; - - complete(&card->mrq_complete); - return -EAGAIN; + return mspro_block_complete_req(card, (*mrq)->error); } static int h_mspro_block_wait_for_ced(struct memstick_dev *card, struct memstick_request **mrq) { - if ((*mrq)->error) { - complete(&card->mrq_complete); - return (*mrq)->error; - } - dev_dbg(&card->dev, "wait for ced: value %x\n", (*mrq)->data[0]); - if ((*mrq)->data[0] & (MEMSTICK_INT_CMDNAK | MEMSTICK_INT_ERR)) { - card->current_mrq.error = -EFAULT; - complete(&card->mrq_complete); - return card->current_mrq.error; + if (!(*mrq)->error) { + if ((*mrq)->data[0] & (MEMSTICK_INT_CMDNAK | MEMSTICK_INT_ERR)) + (*mrq)->error = -EFAULT; + else if (!((*mrq)->data[0] & MEMSTICK_INT_CED)) + return 0; } - if (!((*mrq)->data[0] & MEMSTICK_INT_CED)) - return 0; - else { - card->current_mrq.error = 0; - complete(&card->mrq_complete); - return -EAGAIN; - } + return mspro_block_complete_req(card, (*mrq)->error); } static int h_mspro_block_transfer_data(struct memstick_dev *card, @@ -583,10 +575,8 @@ static int h_mspro_block_transfer_data(struct memstick_dev *card, struct scatterlist t_sg = { 0 }; size_t t_offset; - if ((*mrq)->error) { - complete(&card->mrq_complete); - return (*mrq)->error; - } + if ((*mrq)->error) + return mspro_block_complete_req(card, (*mrq)->error); switch ((*mrq)->tpc) { case MS_TPC_WRITE_REG: @@ -617,8 +607,8 @@ has_int_reg: if (msb->current_seg == msb->seg_count) { if (t_val & MEMSTICK_INT_CED) { - complete(&card->mrq_complete); - return -EAGAIN; + return mspro_block_complete_req(card, + 0); } else { card->next_request = h_mspro_block_wait_for_ced; @@ -666,140 +656,184 @@ has_int_reg: /*** Data transfer ***/ -static void mspro_block_process_request(struct memstick_dev *card, - struct request *req) +static int mspro_block_issue_req(struct memstick_dev *card, int chunk) { struct mspro_block_data *msb = memstick_get_drvdata(card); - struct mspro_param_register param; - int rc, chunk, cnt; - unsigned short page_count; sector_t t_sec; - unsigned long flags; + unsigned int count; + struct mspro_param_register param; - do { - page_count = 0; +try_again: + while (chunk) { + msb->current_page = 0; msb->current_seg = 0; - msb->seg_count = blk_rq_map_sg(req->q, req, msb->req_sg); + msb->seg_count = blk_rq_map_sg(msb->block_req->q, + msb->block_req, + msb->req_sg); - if (msb->seg_count) { - msb->current_page = 0; - for (rc = 0; rc < msb->seg_count; rc++) - page_count += msb->req_sg[rc].length - / msb->page_size; - - t_sec = req->sector; - sector_div(t_sec, msb->page_size >> 9); - param.system = msb->system; - param.data_count = cpu_to_be16(page_count); - param.data_address = cpu_to_be32((uint32_t)t_sec); - param.tpc_param = 0; - - msb->data_dir = rq_data_dir(req); - msb->transfer_cmd = msb->data_dir == READ - ? MSPRO_CMD_READ_DATA - : MSPRO_CMD_WRITE_DATA; - - dev_dbg(&card->dev, "data transfer: cmd %x, " - "lba %x, count %x\n", msb->transfer_cmd, - be32_to_cpu(param.data_address), - page_count); - - card->next_request = h_mspro_block_req_init; - msb->mrq_handler = h_mspro_block_transfer_data; - memstick_init_req(&card->current_mrq, MS_TPC_WRITE_REG, - ¶m, sizeof(param)); - memstick_new_req(card->host); - wait_for_completion(&card->mrq_complete); - rc = card->current_mrq.error; + if (!msb->seg_count) { + chunk = __blk_end_request(msb->block_req, -ENOMEM, + blk_rq_cur_bytes(msb->block_req)); + continue; + } - if (rc || (card->current_mrq.tpc == MSPRO_CMD_STOP)) { - for (cnt = 0; cnt < msb->current_seg; cnt++) - page_count += msb->req_sg[cnt].length - / msb->page_size; - - if (msb->current_page) - page_count += msb->current_page - 1; - - if (page_count && (msb->data_dir == READ)) - rc = msb->page_size * page_count; - else - rc = -EIO; - } else - rc = msb->page_size * page_count; - } else - rc = -EFAULT; + t_sec = msb->block_req->sector << 9; + sector_div(t_sec, msb->page_size); - spin_lock_irqsave(&msb->q_lock, flags); - if (rc >= 0) - chunk = __blk_end_request(req, 0, rc); - else - chunk = __blk_end_request(req, rc, 0); + count = msb->block_req->nr_sectors << 9; + count /= msb->page_size; - dev_dbg(&card->dev, "end chunk %d, %d\n", rc, chunk); - spin_unlock_irqrestore(&msb->q_lock, flags); - } while (chunk); + param.system = msb->system; + param.data_count = cpu_to_be16(count); + param.data_address = cpu_to_be32((uint32_t)t_sec); + param.tpc_param = 0; + + msb->data_dir = rq_data_dir(msb->block_req); + msb->transfer_cmd = msb->data_dir == READ + ? MSPRO_CMD_READ_DATA + : MSPRO_CMD_WRITE_DATA; + + dev_dbg(&card->dev, "data transfer: cmd %x, " + "lba %x, count %x\n", msb->transfer_cmd, + be32_to_cpu(param.data_address), count); + + card->next_request = h_mspro_block_req_init; + msb->mrq_handler = h_mspro_block_transfer_data; + memstick_init_req(&card->current_mrq, MS_TPC_WRITE_REG, + ¶m, sizeof(param)); + memstick_new_req(card->host); + return 0; + } + + dev_dbg(&card->dev, "elv_next\n"); + msb->block_req = elv_next_request(msb->queue); + if (!msb->block_req) { + dev_dbg(&card->dev, "issue end\n"); + return -EAGAIN; + } + + dev_dbg(&card->dev, "trying again\n"); + chunk = 1; + goto try_again; } -static int mspro_block_has_request(struct mspro_block_data *msb) +static int mspro_block_complete_req(struct memstick_dev *card, int error) { - int rc = 0; + struct mspro_block_data *msb = memstick_get_drvdata(card); + int chunk, cnt; + unsigned int t_len = 0; unsigned long flags; spin_lock_irqsave(&msb->q_lock, flags); - if (kthread_should_stop() || msb->has_request) - rc = 1; + dev_dbg(&card->dev, "complete %d, %d\n", msb->has_request ? 1 : 0, + error); + + if (msb->has_request) { + /* Nothing to do - not really an error */ + if (error == -EAGAIN) + error = 0; + + if (error || (card->current_mrq.tpc == MSPRO_CMD_STOP)) { + if (msb->data_dir == READ) { + for (cnt = 0; cnt < msb->current_seg; cnt++) + t_len += msb->req_sg[cnt].length + / msb->page_size; + + if (msb->current_page) + t_len += msb->current_page - 1; + + t_len *= msb->page_size; + } + } else + t_len = msb->block_req->nr_sectors << 9; + + dev_dbg(&card->dev, "transferred %x (%d)\n", t_len, error); + + if (error && !t_len) + t_len = blk_rq_cur_bytes(msb->block_req); + + chunk = __blk_end_request(msb->block_req, error, t_len); + + error = mspro_block_issue_req(card, chunk); + + if (!error) + goto out; + else + msb->has_request = 0; + } else { + if (!error) + error = -EAGAIN; + } + + card->next_request = h_mspro_block_default_bad; + complete_all(&card->mrq_complete); +out: spin_unlock_irqrestore(&msb->q_lock, flags); - return rc; + return error; } -static int mspro_block_queue_thread(void *data) +static void mspro_block_stop(struct memstick_dev *card) { - struct memstick_dev *card = data; - struct memstick_host *host = card->host; struct mspro_block_data *msb = memstick_get_drvdata(card); - struct request *req; + int rc = 0; unsigned long flags; while (1) { - wait_event(msb->q_wait, mspro_block_has_request(msb)); - dev_dbg(&card->dev, "thread iter\n"); - spin_lock_irqsave(&msb->q_lock, flags); - req = elv_next_request(msb->queue); - dev_dbg(&card->dev, "next req %p\n", req); - if (!req) { - msb->has_request = 0; - if (kthread_should_stop()) { - spin_unlock_irqrestore(&msb->q_lock, flags); - break; - } - } else - msb->has_request = 1; + if (!msb->has_request) { + blk_stop_queue(msb->queue); + rc = 1; + } spin_unlock_irqrestore(&msb->q_lock, flags); - if (req) { - mutex_lock(&host->lock); - mspro_block_process_request(card, req); - mutex_unlock(&host->lock); - } + if (rc) + break; + + wait_for_completion(&card->mrq_complete); } - dev_dbg(&card->dev, "thread finished\n"); - return 0; } -static void mspro_block_request(struct request_queue *q) +static void mspro_block_start(struct memstick_dev *card) +{ + struct mspro_block_data *msb = memstick_get_drvdata(card); + unsigned long flags; + + spin_lock_irqsave(&msb->q_lock, flags); + blk_start_queue(msb->queue); + spin_unlock_irqrestore(&msb->q_lock, flags); +} + +static int mspro_block_prepare_req(struct request_queue *q, struct request *req) +{ + if (!blk_fs_request(req) && !blk_pc_request(req)) { + blk_dump_rq_flags(req, "MSPro unsupported request"); + return BLKPREP_KILL; + } + + req->cmd_flags |= REQ_DONTPREP; + + return BLKPREP_OK; +} + +static void mspro_block_submit_req(struct request_queue *q) { struct memstick_dev *card = q->queuedata; struct mspro_block_data *msb = memstick_get_drvdata(card); struct request *req = NULL; - if (msb->q_thread) { - msb->has_request = 1; - wake_up_all(&msb->q_wait); - } else { + if (msb->has_request) + return; + + if (msb->eject) { while ((req = elv_next_request(q)) != NULL) end_queued_request(req, -ENODEV); + + return; } + + msb->has_request = 1; + if (mspro_block_issue_req(card, 0)) + msb->has_request = 0; } /*** Initialization ***/ @@ -1169,16 +1203,14 @@ static int mspro_block_init_disk(struct memstick_dev *card) goto out_release_id; } - spin_lock_init(&msb->q_lock); - init_waitqueue_head(&msb->q_wait); - - msb->queue = blk_init_queue(mspro_block_request, &msb->q_lock); + msb->queue = blk_init_queue(mspro_block_submit_req, &msb->q_lock); if (!msb->queue) { rc = -ENOMEM; goto out_put_disk; } msb->queue->queuedata = card; + blk_queue_prep_rq(msb->queue, mspro_block_prepare_req); blk_queue_bounce_limit(msb->queue, limit); blk_queue_max_sectors(msb->queue, MSPRO_BLOCK_MAX_PAGES); @@ -1204,14 +1236,8 @@ static int mspro_block_init_disk(struct memstick_dev *card) capacity *= msb->page_size >> 9; set_capacity(msb->disk, capacity); dev_dbg(&card->dev, "capacity set %ld\n", capacity); - msb->q_thread = kthread_run(mspro_block_queue_thread, card, - DRIVER_NAME"d"); - if (IS_ERR(msb->q_thread)) - goto out_put_disk; - mutex_unlock(&host->lock); add_disk(msb->disk); - mutex_lock(&host->lock); msb->active = 1; return 0; @@ -1259,6 +1285,7 @@ static int mspro_block_probe(struct memstick_dev *card) return -ENOMEM; memstick_set_drvdata(card, msb); msb->card = card; + spin_lock_init(&msb->q_lock); rc = mspro_block_init_card(card); @@ -1272,6 +1299,8 @@ static int mspro_block_probe(struct memstick_dev *card) rc = mspro_block_init_disk(card); if (!rc) { card->check = mspro_block_check_card; + card->stop = mspro_block_stop; + card->start = mspro_block_start; return 0; } @@ -1286,26 +1315,17 @@ out_free: static void mspro_block_remove(struct memstick_dev *card) { struct mspro_block_data *msb = memstick_get_drvdata(card); - struct task_struct *q_thread = NULL; unsigned long flags; del_gendisk(msb->disk); dev_dbg(&card->dev, "mspro block remove\n"); spin_lock_irqsave(&msb->q_lock, flags); - q_thread = msb->q_thread; - msb->q_thread = NULL; - msb->active = 0; + msb->eject = 1; + blk_start_queue(msb->queue); spin_unlock_irqrestore(&msb->q_lock, flags); - if (q_thread) { - mutex_unlock(&card->host->lock); - kthread_stop(q_thread); - mutex_lock(&card->host->lock); - } - - dev_dbg(&card->dev, "queue thread stopped\n"); - blk_cleanup_queue(msb->queue); + msb->queue = NULL; sysfs_remove_group(&card->dev.kobj, &msb->attr_group); @@ -1322,19 +1342,13 @@ static void mspro_block_remove(struct memstick_dev *card) static int mspro_block_suspend(struct memstick_dev *card, pm_message_t state) { struct mspro_block_data *msb = memstick_get_drvdata(card); - struct task_struct *q_thread = NULL; unsigned long flags; spin_lock_irqsave(&msb->q_lock, flags); - q_thread = msb->q_thread; - msb->q_thread = NULL; - msb->active = 0; blk_stop_queue(msb->queue); + msb->active = 0; spin_unlock_irqrestore(&msb->q_lock, flags); - if (q_thread) - kthread_stop(q_thread); - return 0; } @@ -1373,14 +1387,7 @@ static int mspro_block_resume(struct memstick_dev *card) if (memcmp(s_attr->data, r_attr->data, s_attr->size)) break; - memstick_set_drvdata(card, msb); - msb->q_thread = kthread_run(mspro_block_queue_thread, - card, DRIVER_NAME"d"); - if (IS_ERR(msb->q_thread)) - msb->q_thread = NULL; - else - msb->active = 1; - + msb->active = 1; break; } } diff --git a/drivers/memstick/host/jmb38x_ms.c b/drivers/memstick/host/jmb38x_ms.c index a054668eda16..3485c63d20b0 100644 --- a/drivers/memstick/host/jmb38x_ms.c +++ b/drivers/memstick/host/jmb38x_ms.c @@ -50,8 +50,9 @@ struct jmb38x_ms_host { struct jmb38x_ms *chip; void __iomem *addr; spinlock_t lock; + struct tasklet_struct notify; int id; - char host_id[DEVICE_ID_SIZE]; + char host_id[32]; int irq; unsigned int block_pos; unsigned long timeout_jiffies; @@ -590,55 +591,97 @@ static void jmb38x_ms_abort(unsigned long data) spin_unlock_irqrestore(&host->lock, flags); } -static void jmb38x_ms_request(struct memstick_host *msh) +static void jmb38x_ms_req_tasklet(unsigned long data) { + struct memstick_host *msh = (struct memstick_host *)data; struct jmb38x_ms_host *host = memstick_priv(msh); unsigned long flags; int rc; spin_lock_irqsave(&host->lock, flags); - if (host->req) { - spin_unlock_irqrestore(&host->lock, flags); - BUG(); - return; + if (!host->req) { + do { + rc = memstick_next_req(msh, &host->req); + dev_dbg(&host->chip->pdev->dev, "tasklet req %d\n", rc); + } while (!rc && jmb38x_ms_issue_cmd(msh)); } - - do { - rc = memstick_next_req(msh, &host->req); - } while (!rc && jmb38x_ms_issue_cmd(msh)); spin_unlock_irqrestore(&host->lock, flags); } -static void jmb38x_ms_reset(struct jmb38x_ms_host *host) +static void jmb38x_ms_dummy_submit(struct memstick_host *msh) { - unsigned int host_ctl = readl(host->addr + HOST_CONTROL); + return; +} + +static void jmb38x_ms_submit_req(struct memstick_host *msh) +{ + struct jmb38x_ms_host *host = memstick_priv(msh); + + tasklet_schedule(&host->notify); +} + +static int jmb38x_ms_reset(struct jmb38x_ms_host *host) +{ + int cnt; + + writel(HOST_CONTROL_RESET_REQ | HOST_CONTROL_CLOCK_EN + | readl(host->addr + HOST_CONTROL), + host->addr + HOST_CONTROL); + mmiowb(); + + for (cnt = 0; cnt < 20; ++cnt) { + if (!(HOST_CONTROL_RESET_REQ + & readl(host->addr + HOST_CONTROL))) + goto reset_next; - writel(HOST_CONTROL_RESET_REQ, host->addr + HOST_CONTROL); + ndelay(20); + } + dev_dbg(&host->chip->pdev->dev, "reset_req timeout\n"); + return -EIO; + +reset_next: + writel(HOST_CONTROL_RESET | HOST_CONTROL_CLOCK_EN + | readl(host->addr + HOST_CONTROL), + host->addr + HOST_CONTROL); + mmiowb(); + + for (cnt = 0; cnt < 20; ++cnt) { + if (!(HOST_CONTROL_RESET + & readl(host->addr + HOST_CONTROL))) + goto reset_ok; - while (HOST_CONTROL_RESET_REQ - & (host_ctl = readl(host->addr + HOST_CONTROL))) { ndelay(20); - dev_dbg(&host->chip->pdev->dev, "reset %08x\n", host_ctl); } + dev_dbg(&host->chip->pdev->dev, "reset timeout\n"); + return -EIO; - writel(HOST_CONTROL_RESET, host->addr + HOST_CONTROL); +reset_ok: mmiowb(); writel(INT_STATUS_ALL, host->addr + INT_SIGNAL_ENABLE); writel(INT_STATUS_ALL, host->addr + INT_STATUS_ENABLE); + return 0; } -static void jmb38x_ms_set_param(struct memstick_host *msh, - enum memstick_param param, - int value) +static int jmb38x_ms_set_param(struct memstick_host *msh, + enum memstick_param param, + int value) { struct jmb38x_ms_host *host = memstick_priv(msh); unsigned int host_ctl = readl(host->addr + HOST_CONTROL); unsigned int clock_ctl = CLOCK_CONTROL_40MHZ, clock_delay = 0; + int rc = 0; switch (param) { case MEMSTICK_POWER: if (value == MEMSTICK_POWER_ON) { - jmb38x_ms_reset(host); + rc = jmb38x_ms_reset(host); + if (rc) + return rc; + + host_ctl = 7; + host_ctl |= HOST_CONTROL_POWER_EN + | HOST_CONTROL_CLOCK_EN; + writel(host_ctl, host->addr + HOST_CONTROL); writel(host->id ? PAD_PU_PD_ON_MS_SOCK1 : PAD_PU_PD_ON_MS_SOCK0, @@ -647,11 +690,7 @@ static void jmb38x_ms_set_param(struct memstick_host *msh, writel(PAD_OUTPUT_ENABLE_MS, host->addr + PAD_OUTPUT_ENABLE); - host_ctl = 7; - host_ctl |= HOST_CONTROL_POWER_EN - | HOST_CONTROL_CLOCK_EN; - writel(host_ctl, host->addr + HOST_CONTROL); - + msleep(10); dev_dbg(&host->chip->pdev->dev, "power on\n"); } else if (value == MEMSTICK_POWER_OFF) { host_ctl &= ~(HOST_CONTROL_POWER_EN @@ -660,7 +699,8 @@ static void jmb38x_ms_set_param(struct memstick_host *msh, writel(0, host->addr + PAD_OUTPUT_ENABLE); writel(PAD_PU_PD_OFF, host->addr + PAD_PU_PD); dev_dbg(&host->chip->pdev->dev, "power off\n"); - } + } else + return -EINVAL; break; case MEMSTICK_INTERFACE: host_ctl &= ~(3 << HOST_CONTROL_IF_SHIFT); @@ -686,12 +726,14 @@ static void jmb38x_ms_set_param(struct memstick_host *msh, host_ctl &= ~HOST_CONTROL_REI; clock_ctl = CLOCK_CONTROL_60MHZ; clock_delay = 0; - } + } else + return -EINVAL; writel(host_ctl, host->addr + HOST_CONTROL); writel(clock_ctl, host->addr + CLOCK_CONTROL); writel(clock_delay, host->addr + CLOCK_DELAY); break; }; + return 0; } #ifdef CONFIG_PM @@ -781,11 +823,13 @@ static struct memstick_host *jmb38x_ms_alloc_host(struct jmb38x_ms *jm, int cnt) spin_lock_init(&host->lock); host->id = cnt; - snprintf(host->host_id, DEVICE_ID_SIZE, DRIVER_NAME ":slot%d", + snprintf(host->host_id, sizeof(host->host_id), DRIVER_NAME ":slot%d", host->id); host->irq = jm->pdev->irq; host->timeout_jiffies = msecs_to_jiffies(1000); - msh->request = jmb38x_ms_request; + + tasklet_init(&host->notify, jmb38x_ms_req_tasklet, (unsigned long)msh); + msh->request = jmb38x_ms_submit_req; msh->set_param = jmb38x_ms_set_param; msh->caps = MEMSTICK_CAP_PAR4 | MEMSTICK_CAP_PAR8; @@ -897,6 +941,8 @@ static void jmb38x_ms_remove(struct pci_dev *dev) host = memstick_priv(jm->hosts[cnt]); + jm->hosts[cnt]->request = jmb38x_ms_dummy_submit; + tasklet_kill(&host->notify); writel(0, host->addr + INT_SIGNAL_ENABLE); writel(0, host->addr + INT_STATUS_ENABLE); mmiowb(); diff --git a/drivers/memstick/host/tifm_ms.c b/drivers/memstick/host/tifm_ms.c index 8577de4ebb0e..d32d6ad8f3fc 100644 --- a/drivers/memstick/host/tifm_ms.c +++ b/drivers/memstick/host/tifm_ms.c @@ -71,6 +71,7 @@ struct tifm_ms { struct tifm_dev *dev; struct timer_list timer; struct memstick_request *req; + struct tasklet_struct notify; unsigned int mode_mask; unsigned int block_pos; unsigned long timeout_jiffies; @@ -455,49 +456,51 @@ static void tifm_ms_card_event(struct tifm_dev *sock) return; } -static void tifm_ms_request(struct memstick_host *msh) +static void tifm_ms_req_tasklet(unsigned long data) { + struct memstick_host *msh = (struct memstick_host *)data; struct tifm_ms *host = memstick_priv(msh); struct tifm_dev *sock = host->dev; unsigned long flags; int rc; spin_lock_irqsave(&sock->lock, flags); - if (host->req) { - printk(KERN_ERR "%s : unfinished request detected\n", - sock->dev.bus_id); - spin_unlock_irqrestore(&sock->lock, flags); - tifm_eject(host->dev); - return; - } + if (!host->req) { + if (host->eject) { + do { + rc = memstick_next_req(msh, &host->req); + if (!rc) + host->req->error = -ETIME; + } while (!rc); + spin_unlock_irqrestore(&sock->lock, flags); + return; + } - if (host->eject) { do { rc = memstick_next_req(msh, &host->req); - if (!rc) - host->req->error = -ETIME; - } while (!rc); - spin_unlock_irqrestore(&sock->lock, flags); - return; + } while (!rc && tifm_ms_issue_cmd(host)); } - - do { - rc = memstick_next_req(msh, &host->req); - } while (!rc && tifm_ms_issue_cmd(host)); - spin_unlock_irqrestore(&sock->lock, flags); +} + +static void tifm_ms_dummy_submit(struct memstick_host *msh) +{ return; } -static void tifm_ms_set_param(struct memstick_host *msh, - enum memstick_param param, - int value) +static void tifm_ms_submit_req(struct memstick_host *msh) { struct tifm_ms *host = memstick_priv(msh); - struct tifm_dev *sock = host->dev; - unsigned long flags; - spin_lock_irqsave(&sock->lock, flags); + tasklet_schedule(&host->notify); +} + +static int tifm_ms_set_param(struct memstick_host *msh, + enum memstick_param param, + int value) +{ + struct tifm_ms *host = memstick_priv(msh); + struct tifm_dev *sock = host->dev; switch (param) { case MEMSTICK_POWER: @@ -512,7 +515,8 @@ static void tifm_ms_set_param(struct memstick_host *msh, writel(TIFM_MS_SYS_FCLR | TIFM_MS_SYS_INTCLR, sock->addr + SOCK_MS_SYSTEM); writel(0xffffffff, sock->addr + SOCK_MS_STATUS); - } + } else + return -EINVAL; break; case MEMSTICK_INTERFACE: if (value == MEMSTICK_SERIAL) { @@ -525,11 +529,12 @@ static void tifm_ms_set_param(struct memstick_host *msh, writel(TIFM_CTRL_FAST_CLK | readl(sock->addr + SOCK_CONTROL), sock->addr + SOCK_CONTROL); - } + } else + return -EINVAL; break; }; - spin_unlock_irqrestore(&sock->lock, flags); + return 0; } static void tifm_ms_abort(unsigned long data) @@ -570,8 +575,9 @@ static int tifm_ms_probe(struct tifm_dev *sock) host->timeout_jiffies = msecs_to_jiffies(1000); setup_timer(&host->timer, tifm_ms_abort, (unsigned long)host); + tasklet_init(&host->notify, tifm_ms_req_tasklet, (unsigned long)msh); - msh->request = tifm_ms_request; + msh->request = tifm_ms_submit_req; msh->set_param = tifm_ms_set_param; sock->card_event = tifm_ms_card_event; sock->data_event = tifm_ms_data_event; @@ -593,6 +599,8 @@ static void tifm_ms_remove(struct tifm_dev *sock) int rc = 0; unsigned long flags; + msh->request = tifm_ms_dummy_submit; + tasklet_kill(&host->notify); spin_lock_irqsave(&sock->lock, flags); host->eject = 1; if (host->req) { diff --git a/drivers/message/fusion/lsi/mpi_history.txt b/drivers/message/fusion/lsi/mpi_history.txt index 241592ab13ad..3f15fcfe4a2e 100644 --- a/drivers/message/fusion/lsi/mpi_history.txt +++ b/drivers/message/fusion/lsi/mpi_history.txt @@ -127,7 +127,7 @@ mpi_ioc.h * 08-08-01 01.02.01 Original release for v1.2 work. * New format for FWVersion and ProductId in * MSG_IOC_FACTS_REPLY and MPI_FW_HEADER. - * 08-31-01 01.02.02 Addded event MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE and + * 08-31-01 01.02.02 Added event MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE and * related structure and defines. * Added event MPI_EVENT_ON_BUS_TIMER_EXPIRED. * Added MPI_IOCINIT_FLAGS_DISCARD_FW_IMAGE. @@ -187,7 +187,7 @@ mpi_ioc.h * 10-11-06 01.05.12 Added MPI_IOCFACTS_EXCEPT_METADATA_UNSUPPORTED. * Added MaxInitiators field to PortFacts reply. * Added SAS Device Status Change ReasonCode for - * asynchronous notificaiton. + * asynchronous notification. * Added MPI_EVENT_SAS_EXPANDER_STATUS_CHANGE and event * data structure. * Added new ImageType values for FWDownload and FWUpload @@ -213,7 +213,7 @@ mpi_cnfg.h * Added _RESPONSE_ID_MASK definition to SCSI_PORT_1 * page and updated the page version. * Added Information field and _INFO_PARAMS_NEGOTIATED - * definitionto SCSI_DEVICE_0 page. + * definition to SCSI_DEVICE_0 page. * 06-22-00 01.00.03 Removed batch controls from LAN_0 page and updated the * page version. * Added BucketsRemaining to LAN_1 page, redefined the diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index 75e599b85b64..d6a0074b9dc3 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -273,12 +273,12 @@ mpt_fault_reset_work(struct work_struct *work) ioc_raw_state = mpt_GetIocState(ioc, 0); if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT) { printk(MYIOC_s_WARN_FMT "IOC is in FAULT state (%04xh)!!!\n", - ioc->name, ioc_raw_state & MPI_DOORBELL_DATA_MASK); + ioc->name, ioc_raw_state & MPI_DOORBELL_DATA_MASK); printk(MYIOC_s_WARN_FMT "Issuing HardReset from %s!!\n", - ioc->name, __FUNCTION__); + ioc->name, __func__); rc = mpt_HardResetHandler(ioc, CAN_SLEEP); printk(MYIOC_s_WARN_FMT "%s: HardReset: %s\n", ioc->name, - __FUNCTION__, (rc == 0) ? "success" : "failed"); + __func__, (rc == 0) ? "success" : "failed"); ioc_raw_state = mpt_GetIocState(ioc, 0); if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT) printk(MYIOC_s_WARN_FMT "IOC is in FAULT state after " @@ -356,7 +356,7 @@ mpt_turbo_reply(MPT_ADAPTER *ioc, u32 pa) if (!cb_idx || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS || MptCallbacks[cb_idx] == NULL) { printk(MYIOC_s_WARN_FMT "%s: Invalid cb_idx (%d)!\n", - __FUNCTION__, ioc->name, cb_idx); + __func__, ioc->name, cb_idx); goto out; } @@ -420,7 +420,7 @@ mpt_reply(MPT_ADAPTER *ioc, u32 pa) if (!cb_idx || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS || MptCallbacks[cb_idx] == NULL) { printk(MYIOC_s_WARN_FMT "%s: Invalid cb_idx (%d)!\n", - __FUNCTION__, ioc->name, cb_idx); + __func__, ioc->name, cb_idx); freeme = 0; goto out; } @@ -1670,7 +1670,8 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) INIT_DELAYED_WORK(&ioc->fault_reset_work, mpt_fault_reset_work); spin_lock_init(&ioc->fault_reset_work_lock); - snprintf(ioc->reset_work_q_name, KOBJ_NAME_LEN, "mpt_poll_%d", ioc->id); + snprintf(ioc->reset_work_q_name, sizeof(ioc->reset_work_q_name), + "mpt_poll_%d", ioc->id); ioc->reset_work_q = create_singlethread_workqueue(ioc->reset_work_q_name); if (!ioc->reset_work_q) { @@ -2433,7 +2434,7 @@ mpt_adapter_disable(MPT_ADAPTER *ioc) if (ioc->cached_fw != NULL) { ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: Pushing FW onto " - "adapter\n", __FUNCTION__, ioc->name)); + "adapter\n", __func__, ioc->name)); if ((ret = mpt_downloadboot(ioc, (MpiFwHeader_t *) ioc->cached_fw, CAN_SLEEP)) < 0) { printk(MYIOC_s_WARN_FMT @@ -3692,7 +3693,7 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag) if (ioc->pcidev->device == MPI_MANUFACTPAGE_DEVID_SAS1078) { drsprintk(ioc, printk(MYIOC_s_WARN_FMT "%s: Doorbell=%p; 1078 reset " - "address=%p\n", ioc->name, __FUNCTION__, + "address=%p\n", ioc->name, __func__, &ioc->chip->Doorbell, &ioc->chip->Reset_1078)); CHIPREG_WRITE32(&ioc->chip->Reset_1078, 0x07); if (sleepFlag == CAN_SLEEP) @@ -4741,12 +4742,12 @@ mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode) break; } - printk("%s: persist_opcode=%x\n",__FUNCTION__, persist_opcode); + printk("%s: persist_opcode=%x\n",__func__, persist_opcode); /* Get a MF for this command. */ if ((mf = mpt_get_msg_frame(mpt_base_index, ioc)) == NULL) { - printk("%s: no msg frames!\n",__FUNCTION__); + printk("%s: no msg frames!\n",__func__); return -1; } @@ -4770,13 +4771,13 @@ mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode) (SasIoUnitControlReply_t *)ioc->persist_reply_frame; if (le16_to_cpu(sasIoUnitCntrReply->IOCStatus) != MPI_IOCSTATUS_SUCCESS) { printk("%s: IOCStatus=0x%X IOCLogInfo=0x%X\n", - __FUNCTION__, + __func__, sasIoUnitCntrReply->IOCStatus, sasIoUnitCntrReply->IOCLogInfo); return -1; } - printk("%s: success\n",__FUNCTION__); + printk("%s: success\n",__func__); return 0; } @@ -5783,7 +5784,7 @@ SendEventAck(MPT_ADAPTER *ioc, EventNotificationReply_t *evnp) if ((pAck = (EventAck_t *) mpt_get_msg_frame(mpt_base_index, ioc)) == NULL) { dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, no msg frames!!\n", - ioc->name,__FUNCTION__)); + ioc->name,__func__)); return -1; } diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h index 6adab648dbb9..dff048cfa101 100644 --- a/drivers/message/fusion/mptbase.h +++ b/drivers/message/fusion/mptbase.h @@ -707,12 +707,12 @@ typedef struct _MPT_ADAPTER u8 fc_link_speed[2]; spinlock_t fc_rescan_work_lock; struct work_struct fc_rescan_work; - char fc_rescan_work_q_name[KOBJ_NAME_LEN]; + char fc_rescan_work_q_name[20]; struct workqueue_struct *fc_rescan_work_q; struct scsi_cmnd **ScsiLookup; spinlock_t scsi_lookup_lock; - char reset_work_q_name[KOBJ_NAME_LEN]; + char reset_work_q_name[20]; struct workqueue_struct *reset_work_q; struct delayed_work fault_reset_work; spinlock_t fault_reset_work_lock; diff --git a/drivers/message/fusion/mptctl.c b/drivers/message/fusion/mptctl.c index a5920423e2b2..f5233f3d9eff 100644 --- a/drivers/message/fusion/mptctl.c +++ b/drivers/message/fusion/mptctl.c @@ -505,7 +505,7 @@ mptctl_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply) event = le32_to_cpu(pEvReply->Event) & 0xFF; dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s() called\n", - ioc->name, __FUNCTION__)); + ioc->name, __func__)); if(async_queue == NULL) return 1; @@ -2482,7 +2482,7 @@ mptctl_hp_hostinfo(unsigned long arg, unsigned int data_size) */ if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) { dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, no msg frames!!\n", - ioc->name,__FUNCTION__)); + ioc->name,__func__)); goto out; } diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c index fc31ca6829d8..c3c24fdf9fb6 100644 --- a/drivers/message/fusion/mptfc.c +++ b/drivers/message/fusion/mptfc.c @@ -231,28 +231,28 @@ static int mptfc_abort(struct scsi_cmnd *SCpnt) { return - mptfc_block_error_handler(SCpnt, mptscsih_abort, __FUNCTION__); + mptfc_block_error_handler(SCpnt, mptscsih_abort, __func__); } static int mptfc_dev_reset(struct scsi_cmnd *SCpnt) { return - mptfc_block_error_handler(SCpnt, mptscsih_dev_reset, __FUNCTION__); + mptfc_block_error_handler(SCpnt, mptscsih_dev_reset, __func__); } static int mptfc_bus_reset(struct scsi_cmnd *SCpnt) { return - mptfc_block_error_handler(SCpnt, mptscsih_bus_reset, __FUNCTION__); + mptfc_block_error_handler(SCpnt, mptscsih_bus_reset, __func__); } static int mptfc_host_reset(struct scsi_cmnd *SCpnt) { return - mptfc_block_error_handler(SCpnt, mptscsih_host_reset, __FUNCTION__); + mptfc_block_error_handler(SCpnt, mptscsih_host_reset, __func__); } static void @@ -1326,8 +1326,8 @@ mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* initialize workqueue */ - snprintf(ioc->fc_rescan_work_q_name, KOBJ_NAME_LEN, "mptfc_wq_%d", - sh->host_no); + snprintf(ioc->fc_rescan_work_q_name, sizeof(ioc->fc_rescan_work_q_name), + "mptfc_wq_%d", sh->host_no); ioc->fc_rescan_work_q = create_singlethread_workqueue(ioc->fc_rescan_work_q_name); if (!ioc->fc_rescan_work_q) diff --git a/drivers/message/fusion/mptlan.c b/drivers/message/fusion/mptlan.c index d709d92b7b30..a1abf95cf751 100644 --- a/drivers/message/fusion/mptlan.c +++ b/drivers/message/fusion/mptlan.c @@ -610,7 +610,7 @@ mpt_lan_send_turbo(struct net_device *dev, u32 tmsg) dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, skb %p sent.\n", IOC_AND_NETDEV_NAMES_s_s(dev), - __FUNCTION__, sent)); + __func__, sent)); priv->SendCtl[ctx].skb = NULL; pci_unmap_single(mpt_dev->pcidev, priv->SendCtl[ctx].dma, @@ -676,7 +676,7 @@ mpt_lan_send_reply(struct net_device *dev, LANSendReply_t *pSendRep) dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, skb %p sent.\n", IOC_AND_NETDEV_NAMES_s_s(dev), - __FUNCTION__, sent)); + __func__, sent)); priv->SendCtl[ctx].skb = NULL; pci_unmap_single(mpt_dev->pcidev, priv->SendCtl[ctx].dma, @@ -715,7 +715,7 @@ mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev) u16 cur_naa = 0x1000; dioprintk((KERN_INFO MYNAM ": %s called, skb_addr = %p\n", - __FUNCTION__, skb)); + __func__, skb)); spin_lock_irqsave(&priv->txfidx_lock, flags); if (priv->mpt_txfidx_tail < 0) { @@ -723,7 +723,7 @@ mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev) spin_unlock_irqrestore(&priv->txfidx_lock, flags); printk (KERN_ERR "%s: no tx context available: %u\n", - __FUNCTION__, priv->mpt_txfidx_tail); + __func__, priv->mpt_txfidx_tail); return 1; } @@ -733,7 +733,7 @@ mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev) spin_unlock_irqrestore(&priv->txfidx_lock, flags); printk (KERN_ERR "%s: Unable to alloc request frame\n", - __FUNCTION__); + __func__); return 1; } @@ -1208,7 +1208,7 @@ mpt_lan_post_receive_buckets(struct mpt_lan_priv *priv) dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, Start_buckets = %u, buckets_out = %u\n", IOC_AND_NETDEV_NAMES_s_s(dev), - __FUNCTION__, buckets, curr)); + __func__, buckets, curr)); max = (mpt_dev->req_sz - MPT_LAN_RECEIVE_POST_REQUEST_SIZE) / (MPT_LAN_TRANSACTION32_SIZE + sizeof(SGESimple64_t)); @@ -1217,9 +1217,9 @@ mpt_lan_post_receive_buckets(struct mpt_lan_priv *priv) mf = mpt_get_msg_frame(LanCtx, mpt_dev); if (mf == NULL) { printk (KERN_ERR "%s: Unable to alloc request frame\n", - __FUNCTION__); + __func__); dioprintk((KERN_ERR "%s: %u buckets remaining\n", - __FUNCTION__, buckets)); + __func__, buckets)); goto out; } pRecvReq = (LANReceivePostRequest_t *) mf; @@ -1244,7 +1244,7 @@ mpt_lan_post_receive_buckets(struct mpt_lan_priv *priv) spin_lock_irqsave(&priv->rxfidx_lock, flags); if (priv->mpt_rxfidx_tail < 0) { printk (KERN_ERR "%s: Can't alloc context\n", - __FUNCTION__); + __func__); spin_unlock_irqrestore(&priv->rxfidx_lock, flags); break; @@ -1267,7 +1267,7 @@ mpt_lan_post_receive_buckets(struct mpt_lan_priv *priv) if (skb == NULL) { printk (KERN_WARNING MYNAM "/%s: Can't alloc skb\n", - __FUNCTION__); + __func__); priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx; spin_unlock_irqrestore(&priv->rxfidx_lock, flags); break; @@ -1305,7 +1305,7 @@ mpt_lan_post_receive_buckets(struct mpt_lan_priv *priv) if (pSimple == NULL) { /**/ printk (KERN_WARNING MYNAM "/%s: No buckets posted\n", -/**/ __FUNCTION__); +/**/ __func__); mpt_free_msg_frame(mpt_dev, mf); goto out; } @@ -1329,9 +1329,9 @@ mpt_lan_post_receive_buckets(struct mpt_lan_priv *priv) out: dioprintk((KERN_INFO MYNAM "/%s: End_buckets = %u, priv->buckets_out = %u\n", - __FUNCTION__, buckets, atomic_read(&priv->buckets_out))); + __func__, buckets, atomic_read(&priv->buckets_out))); dioprintk((KERN_INFO MYNAM "/%s: Posted %u buckets and received %u back\n", - __FUNCTION__, priv->total_posted, priv->total_received)); + __func__, priv->total_posted, priv->total_received)); clear_bit(0, &priv->post_buckets_active); } diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index b1147aa7afde..12b732512e57 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c @@ -300,7 +300,7 @@ mptsas_port_delete(MPT_ADAPTER *ioc, struct mptsas_portinfo_details * port_detai phy_info = port_info->phy_info; dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: [%p]: num_phys=%02d " - "bitmask=0x%016llX\n", ioc->name, __FUNCTION__, port_details, + "bitmask=0x%016llX\n", ioc->name, __func__, port_details, port_details->num_phys, (unsigned long long) port_details->phy_bitmask)); @@ -411,7 +411,7 @@ mptsas_setup_wide_ports(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info) */ dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: [%p]: deleting phy = %d\n", - ioc->name, __FUNCTION__, port_details, i)); + ioc->name, __func__, port_details, i)); port_details->num_phys--; port_details->phy_bitmask &= ~ (1 << phy_info->phy_id); memset(&phy_info->attached, 0, sizeof(struct mptsas_devinfo)); @@ -497,7 +497,7 @@ mptsas_setup_wide_ports(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info) continue; dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: [%p]: phy_id=%02d num_phys=%02d " - "bitmask=0x%016llX\n", ioc->name, __FUNCTION__, + "bitmask=0x%016llX\n", ioc->name, __func__, port_details, i, port_details->num_phys, (unsigned long long)port_details->phy_bitmask)); dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "\t\tport = %p rphy=%p\n", @@ -553,7 +553,7 @@ mptsas_target_reset(MPT_ADAPTER *ioc, u8 channel, u8 id) if ((mf = mpt_get_msg_frame(ioc->TaskCtx, ioc)) == NULL) { dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, no msg frames @%d!!\n", - ioc->name,__FUNCTION__, __LINE__)); + ioc->name,__func__, __LINE__)); return 0; } @@ -606,7 +606,7 @@ mptsas_target_reset_queue(MPT_ADAPTER *ioc, GFP_ATOMIC); if (!target_reset_list) { dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, failed to allocate mem @%d..!!\n", - ioc->name,__FUNCTION__, __LINE__)); + ioc->name,__func__, __LINE__)); return; } @@ -673,7 +673,7 @@ mptsas_dev_reset_complete(MPT_ADAPTER *ioc) ev = kzalloc(sizeof(*ev), GFP_ATOMIC); if (!ev) { dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, failed to allocate mem @%d..!!\n", - ioc->name,__FUNCTION__, __LINE__)); + ioc->name,__func__, __LINE__)); return; } @@ -1183,7 +1183,7 @@ static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset) reply = (SasIoUnitControlReply_t *)ioc->sas_mgmt.reply; if (reply->IOCStatus != MPI_IOCSTATUS_SUCCESS) { printk(MYIOC_s_INFO_FMT "%s: IOCStatus=0x%X IOCLogInfo=0x%X\n", - ioc->name, __FUNCTION__, reply->IOCStatus, reply->IOCLogInfo); + ioc->name, __func__, reply->IOCStatus, reply->IOCLogInfo); error = -ENXIO; goto out_unlock; } @@ -1270,14 +1270,14 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, if (!rsp) { printk(MYIOC_s_ERR_FMT "%s: the smp response space is missing\n", - ioc->name, __FUNCTION__); + ioc->name, __func__); return -EINVAL; } /* do we need to support multiple segments? */ if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) { printk(MYIOC_s_ERR_FMT "%s: multiple segments req %u %u, rsp %u %u\n", - ioc->name, __FUNCTION__, req->bio->bi_vcnt, req->data_len, + ioc->name, __func__, req->bio->bi_vcnt, req->data_len, rsp->bio->bi_vcnt, rsp->data_len); return -EINVAL; } @@ -1343,7 +1343,7 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done, 10 * HZ); if (!timeleft) { - printk(MYIOC_s_ERR_FMT "%s: smp timeout!\n", ioc->name, __FUNCTION__); + printk(MYIOC_s_ERR_FMT "%s: smp timeout!\n", ioc->name, __func__); /* On timeout reset the board */ mpt_HardResetHandler(ioc, CAN_SLEEP); ret = -ETIMEDOUT; @@ -1361,7 +1361,7 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, rsp->data_len -= smprep->ResponseDataLength; } else { printk(MYIOC_s_ERR_FMT "%s: smp passthru reply failed to be returned\n", - ioc->name, __FUNCTION__); + ioc->name, __func__); ret = -ENXIO; } unmap: @@ -2006,7 +2006,7 @@ static int mptsas_probe_one_phy(struct device *dev, if (error) { dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "%s: exit at line=%d\n", ioc->name, - __FUNCTION__, __LINE__)); + __func__, __LINE__)); goto out; } mptsas_set_port(ioc, phy_info, port); @@ -2076,7 +2076,7 @@ static int mptsas_probe_one_phy(struct device *dev, if (!rphy) { dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "%s: exit at line=%d\n", ioc->name, - __FUNCTION__, __LINE__)); + __func__, __LINE__)); goto out; } @@ -2085,7 +2085,7 @@ static int mptsas_probe_one_phy(struct device *dev, if (error) { dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "%s: exit at line=%d\n", ioc->name, - __FUNCTION__, __LINE__)); + __func__, __LINE__)); sas_rphy_free(rphy); goto out; } @@ -2613,7 +2613,7 @@ mptsas_hotplug_work(struct work_struct *work) (ev->channel << 8) + ev->id)) { dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "%s: exit at line=%d\n", ioc->name, - __FUNCTION__, __LINE__)); + __func__, __LINE__)); break; } phy_info = mptsas_find_phyinfo_by_sas_address( @@ -2633,20 +2633,20 @@ mptsas_hotplug_work(struct work_struct *work) if (!phy_info){ dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "%s: exit at line=%d\n", ioc->name, - __FUNCTION__, __LINE__)); + __func__, __LINE__)); break; } if (!phy_info->port_details) { dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "%s: exit at line=%d\n", ioc->name, - __FUNCTION__, __LINE__)); + __func__, __LINE__)); break; } rphy = mptsas_get_rphy(phy_info); if (!rphy) { dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "%s: exit at line=%d\n", ioc->name, - __FUNCTION__, __LINE__)); + __func__, __LINE__)); break; } @@ -2654,7 +2654,7 @@ mptsas_hotplug_work(struct work_struct *work) if (!port) { dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "%s: exit at line=%d\n", ioc->name, - __FUNCTION__, __LINE__)); + __func__, __LINE__)); break; } @@ -2665,7 +2665,7 @@ mptsas_hotplug_work(struct work_struct *work) if (!vtarget) { dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "%s: exit at line=%d\n", ioc->name, - __FUNCTION__, __LINE__)); + __func__, __LINE__)); break; } @@ -2720,7 +2720,7 @@ mptsas_hotplug_work(struct work_struct *work) (ev->channel << 8) + ev->id)) { dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "%s: exit at line=%d\n", ioc->name, - __FUNCTION__, __LINE__)); + __func__, __LINE__)); break; } @@ -2732,7 +2732,7 @@ mptsas_hotplug_work(struct work_struct *work) if (!phy_info || !phy_info->port_details) { dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "%s: exit at line=%d\n", ioc->name, - __FUNCTION__, __LINE__)); + __func__, __LINE__)); break; } @@ -2744,7 +2744,7 @@ mptsas_hotplug_work(struct work_struct *work) if (!vtarget) { dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "%s: exit at line=%d\n", ioc->name, - __FUNCTION__, __LINE__)); + __func__, __LINE__)); break; } /* @@ -2767,7 +2767,7 @@ mptsas_hotplug_work(struct work_struct *work) if (mptsas_get_rphy(phy_info)) { dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "%s: exit at line=%d\n", ioc->name, - __FUNCTION__, __LINE__)); + __func__, __LINE__)); if (ev->channel) printk("%d\n", __LINE__); break; } @@ -2776,7 +2776,7 @@ mptsas_hotplug_work(struct work_struct *work) if (!port) { dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "%s: exit at line=%d\n", ioc->name, - __FUNCTION__, __LINE__)); + __func__, __LINE__)); break; } memcpy(&phy_info->attached, &sas_device, @@ -2801,7 +2801,7 @@ mptsas_hotplug_work(struct work_struct *work) if (!rphy) { dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "%s: exit at line=%d\n", ioc->name, - __FUNCTION__, __LINE__)); + __func__, __LINE__)); break; /* non-fatal: an rphy can be added later */ } @@ -2809,7 +2809,7 @@ mptsas_hotplug_work(struct work_struct *work) if (sas_rphy_add(rphy)) { dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "%s: exit at line=%d\n", ioc->name, - __FUNCTION__, __LINE__)); + __func__, __LINE__)); sas_rphy_free(rphy); break; } diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c index d142b6b4b976..9f9354fd3516 100644 --- a/drivers/message/fusion/mptscsih.c +++ b/drivers/message/fusion/mptscsih.c @@ -461,7 +461,7 @@ mptscsih_issue_sep_command(MPT_ADAPTER *ioc, VirtTarget *vtarget, if ((mf = mpt_get_msg_frame(ioc->InternalCtx, ioc)) == NULL) { dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s: no msg frames!!\n", - ioc->name,__FUNCTION__)); + ioc->name,__func__)); return; } @@ -2187,7 +2187,7 @@ mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *m (ioc->debug_level & MPT_DEBUG_TM )) printk("%s: ha=%d [%d:%d:0] task_type=0x%02X " "iocstatus=0x%04X\n\tloginfo=0x%08X response_code=0x%02X " - "term_cmnds=%d\n", __FUNCTION__, ioc->id, pScsiTmReply->Bus, + "term_cmnds=%d\n", __func__, ioc->id, pScsiTmReply->Bus, pScsiTmReply->TargetID, pScsiTmReq->TaskType, le16_to_cpu(pScsiTmReply->IOCStatus), le32_to_cpu(pScsiTmReply->IOCLogInfo),pScsiTmReply->ResponseCode, diff --git a/drivers/message/i2o/device.c b/drivers/message/i2o/device.c index 489d7c5c4965..8774c670e668 100644 --- a/drivers/message/i2o/device.c +++ b/drivers/message/i2o/device.c @@ -243,29 +243,41 @@ static int i2o_device_add(struct i2o_controller *c, i2o_lct_entry *entry) /* create user entries for this device */ tmp = i2o_iop_find_device(i2o_dev->iop, i2o_dev->lct_data.user_tid); - if (tmp && (tmp != i2o_dev)) - sysfs_create_link(&i2o_dev->device.kobj, &tmp->device.kobj, - "user"); + if (tmp && (tmp != i2o_dev)) { + rc = sysfs_create_link(&i2o_dev->device.kobj, + &tmp->device.kobj, "user"); + if (rc) + goto unreg_dev; + } /* create user entries refering to this device */ list_for_each_entry(tmp, &c->devices, list) if ((tmp->lct_data.user_tid == i2o_dev->lct_data.tid) - && (tmp != i2o_dev)) - sysfs_create_link(&tmp->device.kobj, - &i2o_dev->device.kobj, "user"); + && (tmp != i2o_dev)) { + rc = sysfs_create_link(&tmp->device.kobj, + &i2o_dev->device.kobj, "user"); + if (rc) + goto rmlink1; + } /* create parent entries for this device */ tmp = i2o_iop_find_device(i2o_dev->iop, i2o_dev->lct_data.parent_tid); - if (tmp && (tmp != i2o_dev)) - sysfs_create_link(&i2o_dev->device.kobj, &tmp->device.kobj, - "parent"); + if (tmp && (tmp != i2o_dev)) { + rc = sysfs_create_link(&i2o_dev->device.kobj, + &tmp->device.kobj, "parent"); + if (rc) + goto rmlink1; + } /* create parent entries refering to this device */ list_for_each_entry(tmp, &c->devices, list) if ((tmp->lct_data.parent_tid == i2o_dev->lct_data.tid) - && (tmp != i2o_dev)) - sysfs_create_link(&tmp->device.kobj, - &i2o_dev->device.kobj, "parent"); + && (tmp != i2o_dev)) { + rc = sysfs_create_link(&tmp->device.kobj, + &i2o_dev->device.kobj, "parent"); + if (rc) + goto rmlink2; + } i2o_driver_notify_device_add_all(i2o_dev); @@ -273,6 +285,24 @@ static int i2o_device_add(struct i2o_controller *c, i2o_lct_entry *entry) return 0; +rmlink2: + /* If link creating failed halfway, we loop whole list to cleanup. + * And we don't care wrong removing of link, because sysfs_remove_link + * will take care of it. + */ + list_for_each_entry(tmp, &c->devices, list) { + if (tmp->lct_data.parent_tid == i2o_dev->lct_data.tid) + sysfs_remove_link(&tmp->device.kobj, "parent"); + } + sysfs_remove_link(&i2o_dev->device.kobj, "parent"); +rmlink1: + list_for_each_entry(tmp, &c->devices, list) + if (tmp->lct_data.user_tid == i2o_dev->lct_data.tid) + sysfs_remove_link(&tmp->device.kobj, "user"); + sysfs_remove_link(&i2o_dev->device.kobj, "user"); +unreg_dev: + list_del(&i2o_dev->list); + device_unregister(&i2o_dev->device); err: kfree(i2o_dev); return rc; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 260bade0a5ec..883e7ea31de2 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -5,6 +5,10 @@ menu "Multifunction device drivers" depends on HAS_IOMEM +config MFD_CORE + tristate + default n + config MFD_SM501 tristate "Support for Silicon Motion SM501" ---help--- @@ -15,6 +19,14 @@ config MFD_SM501 interface. The device may be connected by PCI or local bus with varying functions enabled. +config MFD_SM501_GPIO + bool "Export GPIO via GPIO layer" + depends on MFD_SM501 && HAVE_GPIO_LIB + ---help--- + This option uses the gpio library layer to export the 64 GPIO + lines on the SM501. The platform data is used to supply the + base number for the first GPIO line to register. + config MFD_ASIC3 bool "Support for Compaq ASIC3" depends on GENERIC_HARDIRQS && HAVE_GPIO_LIB && ARM @@ -24,7 +36,7 @@ config MFD_ASIC3 config HTC_EGPIO bool "HTC EGPIO support" - depends on GENERIC_HARDIRQS && HAVE_GPIO_LIB && ARM + depends on GENERIC_HARDIRQS && GPIOLIB && ARM help This driver supports the CPLD egpio chip present on several HTC phones. It provides basic support for input @@ -38,6 +50,13 @@ config HTC_PASIC3 HTC Magician devices, respectively. Actual functionality is handled by the leds-pasic3 and ds1wm drivers. +config MFD_TC6393XB + bool "Support Toshiba TC6393XB" + depends on GPIOLIB && ARM + select MFD_CORE + help + Support for Toshiba Mobile IO Controller TC6393XB + endmenu menu "Multimedia Capabilities Port drivers" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index eef4e26807df..33daa2f45dd8 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -8,6 +8,10 @@ obj-$(CONFIG_MFD_ASIC3) += asic3.o obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o +obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o + +obj-$(CONFIG_MFD_CORE) += mfd-core.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/asic3.c b/drivers/mfd/asic3.c index 3b870e7fb3e1..c6408a62d95e 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -256,28 +256,28 @@ static int asic3_gpio_irq_type(unsigned int irq, unsigned int type) bank + ASIC3_GPIO_TRIGGER_TYPE); asic->irq_bothedge[(irq - asic->irq_base) >> 4] &= ~bit; - if (type == IRQT_RISING) { + if (type == IRQ_TYPE_EDGE_RISING) { trigger |= bit; edge |= bit; - } else if (type == IRQT_FALLING) { + } else if (type == IRQ_TYPE_EDGE_FALLING) { trigger |= bit; edge &= ~bit; - } else if (type == IRQT_BOTHEDGE) { + } else if (type == IRQ_TYPE_EDGE_BOTH) { trigger |= bit; if (asic3_gpio_get(&asic->gpio, irq - asic->irq_base)) edge &= ~bit; else edge |= bit; asic->irq_bothedge[(irq - asic->irq_base) >> 4] |= bit; - } else if (type == IRQT_LOW) { + } else if (type == IRQ_TYPE_LEVEL_LOW) { trigger &= ~bit; level &= ~bit; - } else if (type == IRQT_HIGH) { + } else if (type == IRQ_TYPE_LEVEL_HIGH) { trigger &= ~bit; level |= bit; } else { /* - * if type == IRQT_NOEDGE, we should mask interrupts, but + * if type == IRQ_TYPE_NONE, we should mask interrupts, but * be careful to not unmask them if mask was also called. * Probably need internal state for mask. */ @@ -314,10 +314,12 @@ static int __init asic3_irq_probe(struct platform_device *pdev) unsigned long clksel = 0; unsigned int irq, irq_base; int map_size; + int ret; - asic->irq_nr = platform_get_irq(pdev, 0); - if (asic->irq_nr < 0) - return asic->irq_nr; + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + asic->irq_nr = ret; /* turn on clock to IRQ controller */ clksel |= CLOCK_SEL_CX; @@ -341,7 +343,7 @@ static int __init asic3_irq_probe(struct platform_device *pdev) ASIC3_INTMASK_GINTMASK); set_irq_chained_handler(asic->irq_nr, asic3_irq_demux); - set_irq_type(asic->irq_nr, IRQT_RISING); + set_irq_type(asic->irq_nr, IRQ_TYPE_EDGE_RISING); set_irq_data(asic->irq_nr, asic); return 0; diff --git a/drivers/mfd/htc-egpio.c b/drivers/mfd/htc-egpio.c index 8872cc077519..6be43172dc65 100644 --- a/drivers/mfd/htc-egpio.c +++ b/drivers/mfd/htc-egpio.c @@ -318,6 +318,8 @@ static int __init egpio_probe(struct platform_device *pdev) ei->chip[i].dev = &(pdev->dev); chip = &(ei->chip[i].chip); chip->label = "htc-egpio"; + chip->dev = &pdev->dev; + chip->owner = THIS_MODULE; chip->get = egpio_get; chip->set = egpio_set; chip->direction_input = egpio_direction_input; diff --git a/drivers/mfd/htc-pasic3.c b/drivers/mfd/htc-pasic3.c index 633cbba072f0..91b294dcc133 100644 --- a/drivers/mfd/htc-pasic3.c +++ b/drivers/mfd/htc-pasic3.c @@ -238,6 +238,8 @@ static int pasic3_remove(struct platform_device *pdev) return 0; } +MODULE_ALIAS("platform:pasic3"); + static struct platform_driver pasic3_driver = { .driver = { .name = "pasic3", diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 1eab7cffceaa..b5272b5ce3fa 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -242,6 +242,8 @@ static int mcp_sa11x0_resume(struct platform_device *dev) /* * The driver for the SA11x0 MCP port. */ +MODULE_ALIAS("platform:sa11x0-mcp"); + static struct platform_driver mcp_sa11x0_driver = { .probe = mcp_sa11x0_probe, .remove = mcp_sa11x0_remove, diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c new file mode 100644 index 000000000000..9c9c126ed334 --- /dev/null +++ b/drivers/mfd/mfd-core.c @@ -0,0 +1,112 @@ +/* + * drivers/mfd/mfd-core.c + * + * core MFD support + * Copyright (c) 2006 Ian Molton + * Copyright (c) 2007,2008 Dmitry Baryshkov + * + * 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/kernel.h> +#include <linux/platform_device.h> +#include <linux/mfd/core.h> + +static int mfd_add_device(struct device *parent, int id, + const struct mfd_cell *cell, + struct resource *mem_base, + int irq_base) +{ + struct resource res[cell->num_resources]; + struct platform_device *pdev; + int ret = -ENOMEM; + int r; + + pdev = platform_device_alloc(cell->name, id); + if (!pdev) + goto fail_alloc; + + pdev->dev.parent = parent; + + ret = platform_device_add_data(pdev, + cell->platform_data, cell->data_size); + if (ret) + goto fail_device; + + memset(res, 0, sizeof(res)); + for (r = 0; r < cell->num_resources; r++) { + res[r].name = cell->resources[r].name; + res[r].flags = cell->resources[r].flags; + + /* Find out base to use */ + if (cell->resources[r].flags & IORESOURCE_MEM) { + res[r].parent = mem_base; + res[r].start = mem_base->start + + cell->resources[r].start; + res[r].end = mem_base->start + + cell->resources[r].end; + } else if (cell->resources[r].flags & IORESOURCE_IRQ) { + res[r].start = irq_base + + cell->resources[r].start; + res[r].end = irq_base + + cell->resources[r].end; + } else { + res[r].parent = cell->resources[r].parent; + res[r].start = cell->resources[r].start; + res[r].end = cell->resources[r].end; + } + } + + platform_device_add_resources(pdev, res, cell->num_resources); + + ret = platform_device_add(pdev); + if (ret) + goto fail_device; + + return 0; + +/* platform_device_del(pdev); */ +fail_device: + platform_device_put(pdev); +fail_alloc: + return ret; +} + +int mfd_add_devices(struct device *parent, int id, + const struct mfd_cell *cells, int n_devs, + struct resource *mem_base, + int irq_base) +{ + int i; + int ret = 0; + + for (i = 0; i < n_devs; i++) { + ret = mfd_add_device(parent, id, cells + i, mem_base, irq_base); + if (ret) + break; + } + + if (ret) + mfd_remove_devices(parent); + + return ret; +} +EXPORT_SYMBOL(mfd_add_devices); + +static int mfd_remove_devices_fn(struct device *dev, void *unused) +{ + platform_device_unregister(to_platform_device(dev)); + return 0; +} + +void mfd_remove_devices(struct device *parent) +{ + device_for_each_child(parent, NULL, mfd_remove_devices_fn); +} +EXPORT_SYMBOL(mfd_remove_devices); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov"); diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index 2fe64734d8af..7aebad4c06ff 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -19,6 +19,7 @@ #include <linux/device.h> #include <linux/platform_device.h> #include <linux/pci.h> +#include <linux/i2c-gpio.h> #include <linux/sm501.h> #include <linux/sm501-regs.h> @@ -31,10 +32,37 @@ struct sm501_device { struct platform_device pdev; }; +struct sm501_gpio; + +#ifdef CONFIG_MFD_SM501_GPIO +#include <linux/gpio.h> + +struct sm501_gpio_chip { + struct gpio_chip gpio; + struct sm501_gpio *ourgpio; /* to get back to parent. */ + void __iomem *regbase; +}; + +struct sm501_gpio { + struct sm501_gpio_chip low; + struct sm501_gpio_chip high; + spinlock_t lock; + + unsigned int registered : 1; + void __iomem *regs; + struct resource *regs_res; +}; +#else +struct sm501_gpio { + /* no gpio support, empty definition for sm501_devdata. */ +}; +#endif + struct sm501_devdata { spinlock_t reg_lock; struct mutex clock_lock; struct list_head devices; + struct sm501_gpio gpio; struct device *dev; struct resource *io_res; @@ -42,6 +70,7 @@ struct sm501_devdata { struct resource *regs_claim; struct sm501_platdata *platdata; + unsigned int in_suspend; unsigned long pm_misc; @@ -52,6 +81,7 @@ struct sm501_devdata { unsigned int rev; }; + #define MHZ (1000 * 1000) #ifdef DEBUG @@ -276,58 +306,6 @@ unsigned long sm501_modify_reg(struct device *dev, 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 @@ -906,6 +884,313 @@ static int sm501_register_display(struct sm501_devdata *sm, return sm501_register_device(sm, pdev); } +#ifdef CONFIG_MFD_SM501_GPIO + +static inline struct sm501_gpio_chip *to_sm501_gpio(struct gpio_chip *gc) +{ + return container_of(gc, struct sm501_gpio_chip, gpio); +} + +static inline struct sm501_devdata *sm501_gpio_to_dev(struct sm501_gpio *gpio) +{ + return container_of(gpio, struct sm501_devdata, gpio); +} + +static int sm501_gpio_get(struct gpio_chip *chip, unsigned offset) + +{ + struct sm501_gpio_chip *smgpio = to_sm501_gpio(chip); + unsigned long result; + + result = readl(smgpio->regbase + SM501_GPIO_DATA_LOW); + result >>= offset; + + return result & 1UL; +} + +static void sm501_gpio_set(struct gpio_chip *chip, unsigned offset, int value) + +{ + struct sm501_gpio_chip *smchip = to_sm501_gpio(chip); + struct sm501_gpio *smgpio = smchip->ourgpio; + unsigned long bit = 1 << offset; + void __iomem *regs = smchip->regbase; + unsigned long save; + unsigned long val; + + dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n", + __func__, chip, offset); + + spin_lock_irqsave(&smgpio->lock, save); + + val = readl(regs + SM501_GPIO_DATA_LOW) & ~bit; + if (value) + val |= bit; + writel(val, regs); + + sm501_sync_regs(sm501_gpio_to_dev(smgpio)); + spin_unlock_irqrestore(&smgpio->lock, save); +} + +static int sm501_gpio_input(struct gpio_chip *chip, unsigned offset) +{ + struct sm501_gpio_chip *smchip = to_sm501_gpio(chip); + struct sm501_gpio *smgpio = smchip->ourgpio; + void __iomem *regs = smchip->regbase; + unsigned long bit = 1 << offset; + unsigned long save; + unsigned long ddr; + + dev_info(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n", + __func__, chip, offset); + + spin_lock_irqsave(&smgpio->lock, save); + + ddr = readl(regs + SM501_GPIO_DDR_LOW); + writel(ddr & ~bit, regs + SM501_GPIO_DDR_LOW); + + sm501_sync_regs(sm501_gpio_to_dev(smgpio)); + spin_unlock_irqrestore(&smgpio->lock, save); + + return 0; +} + +static int sm501_gpio_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct sm501_gpio_chip *smchip = to_sm501_gpio(chip); + struct sm501_gpio *smgpio = smchip->ourgpio; + unsigned long bit = 1 << offset; + void __iomem *regs = smchip->regbase; + unsigned long save; + unsigned long val; + unsigned long ddr; + + dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d,%d)\n", + __func__, chip, offset, value); + + spin_lock_irqsave(&smgpio->lock, save); + + val = readl(regs + SM501_GPIO_DATA_LOW); + if (value) + val |= bit; + else + val &= ~bit; + writel(val, regs); + + ddr = readl(regs + SM501_GPIO_DDR_LOW); + writel(ddr | bit, regs + SM501_GPIO_DDR_LOW); + + sm501_sync_regs(sm501_gpio_to_dev(smgpio)); + writel(val, regs + SM501_GPIO_DATA_LOW); + + sm501_sync_regs(sm501_gpio_to_dev(smgpio)); + spin_unlock_irqrestore(&smgpio->lock, save); + + return 0; +} + +static struct gpio_chip gpio_chip_template = { + .ngpio = 32, + .direction_input = sm501_gpio_input, + .direction_output = sm501_gpio_output, + .set = sm501_gpio_set, + .get = sm501_gpio_get, +}; + +static int __devinit sm501_gpio_register_chip(struct sm501_devdata *sm, + struct sm501_gpio *gpio, + struct sm501_gpio_chip *chip) +{ + struct sm501_platdata *pdata = sm->platdata; + struct gpio_chip *gchip = &chip->gpio; + int base = pdata->gpio_base; + + chip->gpio = gpio_chip_template; + + if (chip == &gpio->high) { + if (base > 0) + base += 32; + chip->regbase = gpio->regs + SM501_GPIO_DATA_HIGH; + gchip->label = "SM501-HIGH"; + } else { + chip->regbase = gpio->regs + SM501_GPIO_DATA_LOW; + gchip->label = "SM501-LOW"; + } + + gchip->base = base; + chip->ourgpio = gpio; + + return gpiochip_add(gchip); +} + +static int sm501_register_gpio(struct sm501_devdata *sm) +{ + struct sm501_gpio *gpio = &sm->gpio; + resource_size_t iobase = sm->io_res->start + SM501_GPIO; + int ret; + int tmp; + + dev_dbg(sm->dev, "registering gpio block %08llx\n", + (unsigned long long)iobase); + + spin_lock_init(&gpio->lock); + + gpio->regs_res = request_mem_region(iobase, 0x20, "sm501-gpio"); + if (gpio->regs_res == NULL) { + dev_err(sm->dev, "gpio: failed to request region\n"); + return -ENXIO; + } + + gpio->regs = ioremap(iobase, 0x20); + if (gpio->regs == NULL) { + dev_err(sm->dev, "gpio: failed to remap registers\n"); + ret = -ENXIO; + goto err_claimed; + } + + /* Register both our chips. */ + + ret = sm501_gpio_register_chip(sm, gpio, &gpio->low); + if (ret) { + dev_err(sm->dev, "failed to add low chip\n"); + goto err_mapped; + } + + ret = sm501_gpio_register_chip(sm, gpio, &gpio->high); + if (ret) { + dev_err(sm->dev, "failed to add high chip\n"); + goto err_low_chip; + } + + gpio->registered = 1; + + return 0; + + err_low_chip: + tmp = gpiochip_remove(&gpio->low.gpio); + if (tmp) { + dev_err(sm->dev, "cannot remove low chip, cannot tidy up\n"); + return ret; + } + + err_mapped: + iounmap(gpio->regs); + + err_claimed: + release_resource(gpio->regs_res); + kfree(gpio->regs_res); + + return ret; +} + +static void sm501_gpio_remove(struct sm501_devdata *sm) +{ + struct sm501_gpio *gpio = &sm->gpio; + int ret; + + if (!sm->gpio.registered) + return; + + ret = gpiochip_remove(&gpio->low.gpio); + if (ret) + dev_err(sm->dev, "cannot remove low chip, cannot tidy up\n"); + + ret = gpiochip_remove(&gpio->high.gpio); + if (ret) + dev_err(sm->dev, "cannot remove high chip, cannot tidy up\n"); + + iounmap(gpio->regs); + release_resource(gpio->regs_res); + kfree(gpio->regs_res); +} + +static inline int sm501_gpio_pin2nr(struct sm501_devdata *sm, unsigned int pin) +{ + struct sm501_gpio *gpio = &sm->gpio; + int base = (pin < 32) ? gpio->low.gpio.base : gpio->high.gpio.base; + + return (pin % 32) + base; +} + +static inline int sm501_gpio_isregistered(struct sm501_devdata *sm) +{ + return sm->gpio.registered; +} +#else +static inline int sm501_register_gpio(struct sm501_devdata *sm) +{ + return 0; +} + +static inline void sm501_gpio_remove(struct sm501_devdata *sm) +{ +} + +static inline int sm501_gpio_pin2nr(struct sm501_devdata *sm, unsigned int pin) +{ + return -1; +} + +static inline int sm501_gpio_isregistered(struct sm501_devdata *sm) +{ + return 0; +} +#endif + +static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm, + struct sm501_platdata_gpio_i2c *iic) +{ + struct i2c_gpio_platform_data *icd; + struct platform_device *pdev; + + pdev = sm501_create_subdev(sm, "i2c-gpio", 0, + sizeof(struct i2c_gpio_platform_data)); + if (!pdev) + return -ENOMEM; + + icd = pdev->dev.platform_data; + + /* We keep the pin_sda and pin_scl fields relative in case the + * same platform data is passed to >1 SM501. + */ + + icd->sda_pin = sm501_gpio_pin2nr(sm, iic->pin_sda); + icd->scl_pin = sm501_gpio_pin2nr(sm, iic->pin_scl); + icd->timeout = iic->timeout; + icd->udelay = iic->udelay; + + /* note, we can't use either of the pin numbers, as the i2c-gpio + * driver uses the platform.id field to generate the bus number + * to register with the i2c core; The i2c core doesn't have enough + * entries to deal with anything we currently use. + */ + + pdev->id = iic->bus_num; + + dev_info(sm->dev, "registering i2c-%d: sda=%d (%d), scl=%d (%d)\n", + iic->bus_num, + icd->sda_pin, iic->pin_sda, icd->scl_pin, iic->pin_scl); + + return sm501_register_device(sm, pdev); +} + +static int sm501_register_gpio_i2c(struct sm501_devdata *sm, + struct sm501_platdata *pdata) +{ + struct sm501_platdata_gpio_i2c *iic = pdata->gpio_i2c; + int index; + int ret; + + for (index = 0; index < pdata->gpio_i2c_nr; index++, iic++) { + ret = sm501_register_gpio_i2c_instance(sm, iic); + if (ret < 0) + return ret; + } + + return 0; +} + /* sm501_dbg_regs * * Debug attribute to attach to parent device to show core registers @@ -1013,6 +1298,7 @@ static unsigned int sm501_mem_local[] = { static int sm501_init_dev(struct sm501_devdata *sm) { struct sm501_initdata *idata; + struct sm501_platdata *pdata; resource_size_t mem_avail; unsigned long dramctrl; unsigned long devid; @@ -1051,7 +1337,9 @@ static int sm501_init_dev(struct sm501_devdata *sm) /* check to see if we have some device initialisation */ - idata = sm->platdata ? sm->platdata->init : NULL; + pdata = sm->platdata; + idata = pdata ? pdata->init : NULL; + if (idata) { sm501_init_regs(sm, idata); @@ -1059,6 +1347,15 @@ static int sm501_init_dev(struct sm501_devdata *sm) sm501_register_usbhost(sm, &mem_avail); if (idata->devices & (SM501_USE_UART0 | SM501_USE_UART1)) sm501_register_uart(sm, idata->devices); + if (idata->devices & SM501_USE_GPIO) + sm501_register_gpio(sm); + } + + if (pdata->gpio_i2c != NULL && pdata->gpio_i2c_nr > 0) { + if (!sm501_gpio_isregistered(sm)) + dev_err(sm->dev, "no gpio available for i2c gpio.\n"); + else + sm501_register_gpio_i2c(sm, pdata); } ret = sm501_check_clocks(sm); @@ -1138,8 +1435,31 @@ static int sm501_plat_probe(struct platform_device *dev) } #ifdef CONFIG_PM + /* power management support */ +static void sm501_set_power(struct sm501_devdata *sm, int on) +{ + struct sm501_platdata *pd = sm->platdata; + + if (pd == NULL) + return; + + if (pd->get_power) { + if (pd->get_power(sm->dev) == on) { + dev_dbg(sm->dev, "is already %d\n", on); + return; + } + } + + if (pd->set_power) { + dev_dbg(sm->dev, "setting power to %d\n", on); + + pd->set_power(sm->dev, on); + sm501_mdelay(sm, 10); + } +} + static int sm501_plat_suspend(struct platform_device *pdev, pm_message_t state) { struct sm501_devdata *sm = platform_get_drvdata(pdev); @@ -1148,6 +1468,12 @@ static int sm501_plat_suspend(struct platform_device *pdev, pm_message_t state) sm->pm_misc = readl(sm->regs + SM501_MISC_CONTROL); sm501_dump_regs(sm); + + if (sm->platdata) { + if (sm->platdata->flags & SM501_FLAG_SUSPEND_OFF) + sm501_set_power(sm, 0); + } + return 0; } @@ -1155,6 +1481,8 @@ static int sm501_plat_resume(struct platform_device *pdev) { struct sm501_devdata *sm = platform_get_drvdata(pdev); + sm501_set_power(sm, 1); + sm501_dump_regs(sm); sm501_dump_gate(sm); sm501_dump_clk(sm); @@ -1229,6 +1557,7 @@ static struct sm501_platdata_fb sm501_fb_pdata = { static struct sm501_platdata sm501_pci_platdata = { .init = &sm501_pci_initdata, .fb = &sm501_fb_pdata, + .gpio_base = -1, }; static int sm501_pci_probe(struct pci_dev *dev, @@ -1335,6 +1664,8 @@ static void sm501_dev_remove(struct sm501_devdata *sm) sm501_remove_sub(sm, smdev); device_remove_file(sm->dev, &dev_attr_dbg_regs); + + sm501_gpio_remove(sm); } static void sm501_pci_remove(struct pci_dev *dev) @@ -1378,6 +1709,8 @@ static struct pci_driver sm501_pci_drv = { .remove = sm501_pci_remove, }; +MODULE_ALIAS("platform:sm501"); + static struct platform_driver sm501_plat_drv = { .driver = { .name = "sm501", diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c new file mode 100644 index 000000000000..f4fd797c1590 --- /dev/null +++ b/drivers/mfd/tc6393xb.c @@ -0,0 +1,604 @@ +/* + * Toshiba TC6393XB SoC support + * + * Copyright(c) 2005-2006 Chris Humbert + * Copyright(c) 2005 Dirk Opfer + * Copyright(c) 2005 Ian Molton <spyro@f2s.com> + * Copyright(c) 2007 Dmitry Baryshkov + * + * Based on code written by Sharp/Lineo for 2.4 kernels + * Based on locomo.c + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/fb.h> +#include <linux/clk.h> +#include <linux/mfd/core.h> +#include <linux/mfd/tmio.h> +#include <linux/mfd/tc6393xb.h> +#include <linux/gpio.h> + +#define SCR_REVID 0x08 /* b Revision ID */ +#define SCR_ISR 0x50 /* b Interrupt Status */ +#define SCR_IMR 0x52 /* b Interrupt Mask */ +#define SCR_IRR 0x54 /* b Interrupt Routing */ +#define SCR_GPER 0x60 /* w GP Enable */ +#define SCR_GPI_SR(i) (0x64 + (i)) /* b3 GPI Status */ +#define SCR_GPI_IMR(i) (0x68 + (i)) /* b3 GPI INT Mask */ +#define SCR_GPI_EDER(i) (0x6c + (i)) /* b3 GPI Edge Detect Enable */ +#define SCR_GPI_LIR(i) (0x70 + (i)) /* b3 GPI Level Invert */ +#define SCR_GPO_DSR(i) (0x78 + (i)) /* b3 GPO Data Set */ +#define SCR_GPO_DOECR(i) (0x7c + (i)) /* b3 GPO Data OE Control */ +#define SCR_GP_IARCR(i) (0x80 + (i)) /* b3 GP Internal Active Register Control */ +#define SCR_GP_IARLCR(i) (0x84 + (i)) /* b3 GP INTERNAL Active Register Level Control */ +#define SCR_GPI_BCR(i) (0x88 + (i)) /* b3 GPI Buffer Control */ +#define SCR_GPA_IARCR 0x8c /* w GPa Internal Active Register Control */ +#define SCR_GPA_IARLCR 0x90 /* w GPa Internal Active Register Level Control */ +#define SCR_GPA_BCR 0x94 /* w GPa Buffer Control */ +#define SCR_CCR 0x98 /* w Clock Control */ +#define SCR_PLL2CR 0x9a /* w PLL2 Control */ +#define SCR_PLL1CR 0x9c /* l PLL1 Control */ +#define SCR_DIARCR 0xa0 /* b Device Internal Active Register Control */ +#define SCR_DBOCR 0xa1 /* b Device Buffer Off Control */ +#define SCR_FER 0xe0 /* b Function Enable */ +#define SCR_MCR 0xe4 /* w Mode Control */ +#define SCR_CONFIG 0xfc /* b Configuration Control */ +#define SCR_DEBUG 0xff /* b Debug */ + +#define SCR_CCR_CK32K BIT(0) +#define SCR_CCR_USBCK BIT(1) +#define SCR_CCR_UNK1 BIT(4) +#define SCR_CCR_MCLK_MASK (7 << 8) +#define SCR_CCR_MCLK_OFF (0 << 8) +#define SCR_CCR_MCLK_12 (1 << 8) +#define SCR_CCR_MCLK_24 (2 << 8) +#define SCR_CCR_MCLK_48 (3 << 8) +#define SCR_CCR_HCLK_MASK (3 << 12) +#define SCR_CCR_HCLK_24 (0 << 12) +#define SCR_CCR_HCLK_48 (1 << 12) + +#define SCR_FER_USBEN BIT(0) /* USB host enable */ +#define SCR_FER_LCDCVEN BIT(1) /* polysilicon TFT enable */ +#define SCR_FER_SLCDEN BIT(2) /* SLCD enable */ + +#define SCR_MCR_RDY_MASK (3 << 0) +#define SCR_MCR_RDY_OPENDRAIN (0 << 0) +#define SCR_MCR_RDY_TRISTATE (1 << 0) +#define SCR_MCR_RDY_PUSHPULL (2 << 0) +#define SCR_MCR_RDY_UNK BIT(2) +#define SCR_MCR_RDY_EN BIT(3) +#define SCR_MCR_INT_MASK (3 << 4) +#define SCR_MCR_INT_OPENDRAIN (0 << 4) +#define SCR_MCR_INT_TRISTATE (1 << 4) +#define SCR_MCR_INT_PUSHPULL (2 << 4) +#define SCR_MCR_INT_UNK BIT(6) +#define SCR_MCR_INT_EN BIT(7) +/* bits 8 - 16 are unknown */ + +#define TC_GPIO_BIT(i) (1 << (i & 0x7)) + +/*--------------------------------------------------------------------------*/ + +struct tc6393xb { + void __iomem *scr; + + struct gpio_chip gpio; + + struct clk *clk; /* 3,6 Mhz */ + + spinlock_t lock; /* protects RMW cycles */ + + struct { + u8 fer; + u16 ccr; + u8 gpi_bcr[3]; + u8 gpo_dsr[3]; + u8 gpo_doecr[3]; + } suspend_state; + + struct resource rscr; + struct resource *iomem; + int irq; + int irq_base; +}; + +enum { + TC6393XB_CELL_NAND, +}; + +/*--------------------------------------------------------------------------*/ + +static int tc6393xb_nand_enable(struct platform_device *nand) +{ + struct platform_device *dev = to_platform_device(nand->dev.parent); + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&tc6393xb->lock, flags); + + /* SMD buffer on */ + dev_dbg(&dev->dev, "SMD buffer on\n"); + iowrite8(0xff, tc6393xb->scr + SCR_GPI_BCR(1)); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); + + return 0; +} + +static struct resource __devinitdata tc6393xb_nand_resources[] = { + { + .name = TMIO_NAND_CONFIG, + .start = 0x0100, + .end = 0x01ff, + .flags = IORESOURCE_MEM, + }, + { + .name = TMIO_NAND_CONTROL, + .start = 0x1000, + .end = 0x1007, + .flags = IORESOURCE_MEM, + }, + { + .name = TMIO_NAND_IRQ, + .start = IRQ_TC6393_NAND, + .end = IRQ_TC6393_NAND, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell __devinitdata tc6393xb_cells[] = { + [TC6393XB_CELL_NAND] = { + .name = "tmio-nand", + .enable = tc6393xb_nand_enable, + .num_resources = ARRAY_SIZE(tc6393xb_nand_resources), + .resources = tc6393xb_nand_resources, + }, +}; + +/*--------------------------------------------------------------------------*/ + +static int tc6393xb_gpio_get(struct gpio_chip *chip, + unsigned offset) +{ + struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio); + + /* XXX: does dsr also represent inputs? */ + return ioread8(tc6393xb->scr + SCR_GPO_DSR(offset / 8)) + & TC_GPIO_BIT(offset); +} + +static void __tc6393xb_gpio_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio); + u8 dsr; + + dsr = ioread8(tc6393xb->scr + SCR_GPO_DSR(offset / 8)); + if (value) + dsr |= TC_GPIO_BIT(offset); + else + dsr &= ~TC_GPIO_BIT(offset); + + iowrite8(dsr, tc6393xb->scr + SCR_GPO_DSR(offset / 8)); +} + +static void tc6393xb_gpio_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio); + unsigned long flags; + + spin_lock_irqsave(&tc6393xb->lock, flags); + + __tc6393xb_gpio_set(chip, offset, value); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); +} + +static int tc6393xb_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio); + unsigned long flags; + u8 doecr; + + spin_lock_irqsave(&tc6393xb->lock, flags); + + doecr = ioread8(tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); + doecr &= ~TC_GPIO_BIT(offset); + iowrite8(doecr, tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); + + return 0; +} + +static int tc6393xb_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio); + unsigned long flags; + u8 doecr; + + spin_lock_irqsave(&tc6393xb->lock, flags); + + __tc6393xb_gpio_set(chip, offset, value); + + doecr = ioread8(tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); + doecr |= TC_GPIO_BIT(offset); + iowrite8(doecr, tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); + + return 0; +} + +static int tc6393xb_register_gpio(struct tc6393xb *tc6393xb, int gpio_base) +{ + tc6393xb->gpio.label = "tc6393xb"; + tc6393xb->gpio.base = gpio_base; + tc6393xb->gpio.ngpio = 16; + tc6393xb->gpio.set = tc6393xb_gpio_set; + tc6393xb->gpio.get = tc6393xb_gpio_get; + tc6393xb->gpio.direction_input = tc6393xb_gpio_direction_input; + tc6393xb->gpio.direction_output = tc6393xb_gpio_direction_output; + + return gpiochip_add(&tc6393xb->gpio); +} + +/*--------------------------------------------------------------------------*/ + +static void +tc6393xb_irq(unsigned int irq, struct irq_desc *desc) +{ + struct tc6393xb *tc6393xb = get_irq_data(irq); + unsigned int isr; + unsigned int i, irq_base; + + irq_base = tc6393xb->irq_base; + + while ((isr = ioread8(tc6393xb->scr + SCR_ISR) & + ~ioread8(tc6393xb->scr + SCR_IMR))) + for (i = 0; i < TC6393XB_NR_IRQS; i++) { + if (isr & (1 << i)) + generic_handle_irq(irq_base + i); + } +} + +static void tc6393xb_irq_ack(unsigned int irq) +{ +} + +static void tc6393xb_irq_mask(unsigned int irq) +{ + struct tc6393xb *tc6393xb = get_irq_chip_data(irq); + unsigned long flags; + u8 imr; + + spin_lock_irqsave(&tc6393xb->lock, flags); + imr = ioread8(tc6393xb->scr + SCR_IMR); + imr |= 1 << (irq - tc6393xb->irq_base); + iowrite8(imr, tc6393xb->scr + SCR_IMR); + spin_unlock_irqrestore(&tc6393xb->lock, flags); +} + +static void tc6393xb_irq_unmask(unsigned int irq) +{ + struct tc6393xb *tc6393xb = get_irq_chip_data(irq); + unsigned long flags; + u8 imr; + + spin_lock_irqsave(&tc6393xb->lock, flags); + imr = ioread8(tc6393xb->scr + SCR_IMR); + imr &= ~(1 << (irq - tc6393xb->irq_base)); + iowrite8(imr, tc6393xb->scr + SCR_IMR); + spin_unlock_irqrestore(&tc6393xb->lock, flags); +} + +static struct irq_chip tc6393xb_chip = { + .name = "tc6393xb", + .ack = tc6393xb_irq_ack, + .mask = tc6393xb_irq_mask, + .unmask = tc6393xb_irq_unmask, +}; + +static void tc6393xb_attach_irq(struct platform_device *dev) +{ + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + unsigned int irq, irq_base; + + irq_base = tc6393xb->irq_base; + + for (irq = irq_base; irq < irq_base + TC6393XB_NR_IRQS; irq++) { + set_irq_chip(irq, &tc6393xb_chip); + set_irq_chip_data(irq, tc6393xb); + set_irq_handler(irq, handle_edge_irq); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + } + + set_irq_type(tc6393xb->irq, IRQ_TYPE_EDGE_FALLING); + set_irq_data(tc6393xb->irq, tc6393xb); + set_irq_chained_handler(tc6393xb->irq, tc6393xb_irq); +} + +static void tc6393xb_detach_irq(struct platform_device *dev) +{ + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + unsigned int irq, irq_base; + + set_irq_chained_handler(tc6393xb->irq, NULL); + set_irq_data(tc6393xb->irq, NULL); + + irq_base = tc6393xb->irq_base; + + for (irq = irq_base; irq < irq_base + TC6393XB_NR_IRQS; irq++) { + set_irq_flags(irq, 0); + set_irq_chip(irq, NULL); + set_irq_chip_data(irq, NULL); + } +} + +/*--------------------------------------------------------------------------*/ + +static int tc6393xb_hw_init(struct platform_device *dev) +{ + struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + int i; + + iowrite8(tc6393xb->suspend_state.fer, tc6393xb->scr + SCR_FER); + iowrite16(tcpd->scr_pll2cr, tc6393xb->scr + SCR_PLL2CR); + iowrite16(tc6393xb->suspend_state.ccr, tc6393xb->scr + SCR_CCR); + iowrite16(SCR_MCR_RDY_OPENDRAIN | SCR_MCR_RDY_UNK | SCR_MCR_RDY_EN | + SCR_MCR_INT_OPENDRAIN | SCR_MCR_INT_UNK | SCR_MCR_INT_EN | + BIT(15), tc6393xb->scr + SCR_MCR); + iowrite16(tcpd->scr_gper, tc6393xb->scr + SCR_GPER); + iowrite8(0, tc6393xb->scr + SCR_IRR); + iowrite8(0xbf, tc6393xb->scr + SCR_IMR); + + for (i = 0; i < 3; i++) { + iowrite8(tc6393xb->suspend_state.gpo_dsr[i], + tc6393xb->scr + SCR_GPO_DSR(i)); + iowrite8(tc6393xb->suspend_state.gpo_doecr[i], + tc6393xb->scr + SCR_GPO_DOECR(i)); + iowrite8(tc6393xb->suspend_state.gpi_bcr[i], + tc6393xb->scr + SCR_GPI_BCR(i)); + } + + return 0; +} + +static int __devinit tc6393xb_probe(struct platform_device *dev) +{ + struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; + struct tc6393xb *tc6393xb; + struct resource *iomem; + struct resource *rscr; + int retval, temp; + int i; + + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!iomem) + return -EINVAL; + + tc6393xb = kzalloc(sizeof *tc6393xb, GFP_KERNEL); + if (!tc6393xb) { + retval = -ENOMEM; + goto err_kzalloc; + } + + spin_lock_init(&tc6393xb->lock); + + platform_set_drvdata(dev, tc6393xb); + tc6393xb->iomem = iomem; + tc6393xb->irq = platform_get_irq(dev, 0); + tc6393xb->irq_base = tcpd->irq_base; + + tc6393xb->clk = clk_get(&dev->dev, "GPIO27_CLK" /* "CK3P6MI" */); + if (IS_ERR(tc6393xb->clk)) { + retval = PTR_ERR(tc6393xb->clk); + goto err_clk_get; + } + + rscr = &tc6393xb->rscr; + rscr->name = "tc6393xb-core"; + rscr->start = iomem->start; + rscr->end = iomem->start + 0xff; + rscr->flags = IORESOURCE_MEM; + + retval = request_resource(iomem, rscr); + if (retval) + goto err_request_scr; + + tc6393xb->scr = ioremap(rscr->start, rscr->end - rscr->start + 1); + if (!tc6393xb->scr) { + retval = -ENOMEM; + goto err_ioremap; + } + + retval = clk_enable(tc6393xb->clk); + if (retval) + goto err_clk_enable; + + retval = tcpd->enable(dev); + if (retval) + goto err_enable; + + tc6393xb->suspend_state.fer = 0; + for (i = 0; i < 3; i++) { + tc6393xb->suspend_state.gpo_dsr[i] = + (tcpd->scr_gpo_dsr >> (8 * i)) & 0xff; + tc6393xb->suspend_state.gpo_doecr[i] = + (tcpd->scr_gpo_doecr >> (8 * i)) & 0xff; + } + /* + * It may be necessary to change this back to + * platform-dependant code + */ + tc6393xb->suspend_state.ccr = SCR_CCR_UNK1 | + SCR_CCR_HCLK_48; + + retval = tc6393xb_hw_init(dev); + if (retval) + goto err_hw_init; + + printk(KERN_INFO "Toshiba tc6393xb revision %d at 0x%08lx, irq %d\n", + ioread8(tc6393xb->scr + SCR_REVID), + (unsigned long) iomem->start, tc6393xb->irq); + + tc6393xb->gpio.base = -1; + + if (tcpd->gpio_base >= 0) { + retval = tc6393xb_register_gpio(tc6393xb, tcpd->gpio_base); + if (retval) + goto err_gpio_add; + } + + if (tc6393xb->irq) + tc6393xb_attach_irq(dev); + + tc6393xb_cells[TC6393XB_CELL_NAND].driver_data = tcpd->nand_data; + tc6393xb_cells[TC6393XB_CELL_NAND].platform_data = + &tc6393xb_cells[TC6393XB_CELL_NAND]; + tc6393xb_cells[TC6393XB_CELL_NAND].data_size = + sizeof(tc6393xb_cells[TC6393XB_CELL_NAND]); + + retval = mfd_add_devices(&dev->dev, dev->id, + tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells), + iomem, tcpd->irq_base); + + return 0; + + if (tc6393xb->irq) + tc6393xb_detach_irq(dev); + +err_gpio_add: + if (tc6393xb->gpio.base != -1) + temp = gpiochip_remove(&tc6393xb->gpio); +err_hw_init: + tcpd->disable(dev); +err_clk_enable: + clk_disable(tc6393xb->clk); +err_enable: + iounmap(tc6393xb->scr); +err_ioremap: + release_resource(&tc6393xb->rscr); +err_request_scr: + clk_put(tc6393xb->clk); +err_clk_get: + kfree(tc6393xb); +err_kzalloc: + return retval; +} + +static int __devexit tc6393xb_remove(struct platform_device *dev) +{ + struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + int ret; + + mfd_remove_devices(&dev->dev); + + if (tc6393xb->irq) + tc6393xb_detach_irq(dev); + + if (tc6393xb->gpio.base != -1) { + ret = gpiochip_remove(&tc6393xb->gpio); + if (ret) { + dev_err(&dev->dev, "Can't remove gpio chip: %d\n", ret); + return ret; + } + } + + ret = tcpd->disable(dev); + + clk_disable(tc6393xb->clk); + + iounmap(tc6393xb->scr); + + release_resource(&tc6393xb->rscr); + + platform_set_drvdata(dev, NULL); + + clk_put(tc6393xb->clk); + + kfree(tc6393xb); + + return ret; +} + +#ifdef CONFIG_PM +static int tc6393xb_suspend(struct platform_device *dev, pm_message_t state) +{ + struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + int i; + + + tc6393xb->suspend_state.ccr = ioread16(tc6393xb->scr + SCR_CCR); + tc6393xb->suspend_state.fer = ioread8(tc6393xb->scr + SCR_FER); + + for (i = 0; i < 3; i++) { + tc6393xb->suspend_state.gpo_dsr[i] = + ioread8(tc6393xb->scr + SCR_GPO_DSR(i)); + tc6393xb->suspend_state.gpo_doecr[i] = + ioread8(tc6393xb->scr + SCR_GPO_DOECR(i)); + tc6393xb->suspend_state.gpi_bcr[i] = + ioread8(tc6393xb->scr + SCR_GPI_BCR(i)); + } + + return tcpd->suspend(dev); +} + +static int tc6393xb_resume(struct platform_device *dev) +{ + struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; + int ret = tcpd->resume(dev); + + if (ret) + return ret; + + return tc6393xb_hw_init(dev); +} +#else +#define tc6393xb_suspend NULL +#define tc6393xb_resume NULL +#endif + +static struct platform_driver tc6393xb_driver = { + .probe = tc6393xb_probe, + .remove = __devexit_p(tc6393xb_remove), + .suspend = tc6393xb_suspend, + .resume = tc6393xb_resume, + + .driver = { + .name = "tc6393xb", + .owner = THIS_MODULE, + }, +}; + +static int __init tc6393xb_init(void) +{ + return platform_driver_register(&tc6393xb_driver); +} + +static void __exit tc6393xb_exit(void) +{ + platform_driver_unregister(&tc6393xb_driver); +} + +subsys_initcall(tc6393xb_init); +module_exit(tc6393xb_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov and Dirk Opfer"); +MODULE_DESCRIPTION("tc6393xb Toshiba Mobile IO Controller"); +MODULE_ALIAS("platform:tc6393xb"); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 1921b8dbb242..f5ade1904aad 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -77,11 +77,13 @@ config IBM_ASM for your IBM server. config PHANTOM - tristate "Sensable PHANToM" + tristate "Sensable PHANToM (PCI)" depends on PCI help Say Y here if you want to build a driver for Sensable PHANToM device. + This driver is only for PCI PHANToMs. + If you choose to build module, its name will be phantom. If unsure, say N here. @@ -212,6 +214,18 @@ config TC1100_WMI This is a driver for the WMI extensions (wireless and bluetooth power control) of the HP Compaq TC1100 tablet. +config HP_WMI + tristate "HP WMI extras" + depends on ACPI_WMI + depends on INPUT + depends on RFKILL + help + Say Y here if you want to support WMI-based hotkeys on HP laptops and + to read data from WMI such as docking or ambient light sensor state. + + To compile this driver as a module, choose M here: the module will + be called hp-wmi. + config MSI_LAPTOP tristate "MSI Laptop Extras" depends on X86 @@ -279,6 +293,8 @@ config THINKPAD_ACPI select INPUT select NEW_LEDS select LEDS_CLASS + select NET + select RFKILL ---help--- This is a driver for the IBM and Lenovo ThinkPad laptops. It adds support for Fn-Fx key combinations, Bluetooth control, video @@ -344,7 +360,7 @@ config THINKPAD_ACPI_VIDEO If you are not sure, say Y here. config THINKPAD_ACPI_HOTKEY_POLL - bool "Suport NVRAM polling for hot keys" + bool "Support NVRAM polling for hot keys" depends on THINKPAD_ACPI default y ---help--- @@ -420,4 +436,18 @@ config SGI_XP this feature will allow for direct communication between SSIs based on a network adapter and DMA messaging. +config HP_ILO + tristate "Channel interface driver for HP iLO/iLO2 processor" + depends on PCI + default n + help + The channel interface driver allows applications to communicate + with iLO/iLO2 management processors present on HP ProLiant + servers. Upon loading, the driver creates /dev/hpilo/dXccbN files, + which can be used to gather data from the management processor, + via read and write system calls. + + To compile this driver as a module, choose M here: the + module will be called hpilo. + endif # MISC_DEVICES diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index a6dac6a2e7e5..f5e273420c09 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_ACER_WMI) += acer-wmi.o obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o +obj-$(CONFIG_HP_WMI) += hp-wmi.o obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o obj-$(CONFIG_LKDTM) += lkdtm.o obj-$(CONFIG_TIFM_CORE) += tifm_core.o @@ -27,3 +28,4 @@ obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o obj-$(CONFIG_KGDB_TESTS) += kgdbts.o obj-$(CONFIG_SGI_XP) += sgi-xp/ +obj-$(CONFIG_HP_ILO) += hpilo.o diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index e171650766ce..bf5e4d065436 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -13,7 +13,6 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> -#include <linux/list.h> #include <linux/spinlock.h> #include <linux/atmel-ssc.h> diff --git a/drivers/misc/atmel_pwm.c b/drivers/misc/atmel_pwm.c index 5b5a14dab3d3..6aa5294dfec4 100644 --- a/drivers/misc/atmel_pwm.c +++ b/drivers/misc/atmel_pwm.c @@ -211,8 +211,7 @@ int pwm_clk_alloc(unsigned prescale, unsigned div) if ((mr & 0xffff) == 0) { mr |= val; ret = PWM_CPR_CLKA; - } - if ((mr & (0xffff << 16)) == 0) { + } else if ((mr & (0xffff << 16)) == 0) { mr |= val << 16; ret = PWM_CPR_CLKB; } diff --git a/drivers/misc/hp-wmi.c b/drivers/misc/hp-wmi.c new file mode 100644 index 000000000000..1dbcbcb323a2 --- /dev/null +++ b/drivers/misc/hp-wmi.c @@ -0,0 +1,494 @@ +/* + * HP WMI hotkeys + * + * Copyright (C) 2008 Red Hat <mjg@redhat.com> + * + * Portions based on wistron_btns.c: + * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> + * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org> + * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru> + * + * 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/input.h> +#include <acpi/acpi_drivers.h> +#include <linux/platform_device.h> +#include <linux/acpi.h> +#include <linux/rfkill.h> +#include <linux/string.h> + +MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>"); +MODULE_DESCRIPTION("HP laptop WMI hotkeys driver"); +MODULE_LICENSE("GPL"); + +MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C"); +MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); + +#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C" +#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4" + +#define HPWMI_DISPLAY_QUERY 0x1 +#define HPWMI_HDDTEMP_QUERY 0x2 +#define HPWMI_ALS_QUERY 0x3 +#define HPWMI_DOCK_QUERY 0x4 +#define HPWMI_WIRELESS_QUERY 0x5 + +static int __init hp_wmi_bios_setup(struct platform_device *device); +static int __exit hp_wmi_bios_remove(struct platform_device *device); + +struct bios_args { + u32 signature; + u32 command; + u32 commandtype; + u32 datasize; + u32 data; +}; + +struct bios_return { + u32 sigpass; + u32 return_code; + u32 value; +}; + +struct key_entry { + char type; /* See KE_* below */ + u8 code; + u16 keycode; +}; + +enum { KE_KEY, KE_SW, KE_END }; + +static struct key_entry hp_wmi_keymap[] = { + {KE_SW, 0x01, SW_DOCK}, + {KE_KEY, 0x02, KEY_BRIGHTNESSUP}, + {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN}, + {KE_KEY, 0x04, KEY_HELP}, + {KE_END, 0} +}; + +static struct input_dev *hp_wmi_input_dev; +static struct platform_device *hp_wmi_platform_dev; + +static struct rfkill *wifi_rfkill; +static struct rfkill *bluetooth_rfkill; +static struct rfkill *wwan_rfkill; + +static struct platform_driver hp_wmi_driver = { + .driver = { + .name = "hp-wmi", + .owner = THIS_MODULE, + }, + .probe = hp_wmi_bios_setup, + .remove = hp_wmi_bios_remove, +}; + +static int hp_wmi_perform_query(int query, int write, int value) +{ + struct bios_return bios_return; + acpi_status status; + union acpi_object *obj; + struct bios_args args = { + .signature = 0x55434553, + .command = write ? 0x2 : 0x1, + .commandtype = query, + .datasize = write ? 0x4 : 0, + .data = value, + }; + struct acpi_buffer input = { sizeof(struct bios_args), &args }; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + + status = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output); + + obj = output.pointer; + + if (!obj || obj->type != ACPI_TYPE_BUFFER) + return -EINVAL; + + bios_return = *((struct bios_return *)obj->buffer.pointer); + if (bios_return.return_code > 0) + return bios_return.return_code * -1; + else + return bios_return.value; +} + +static int hp_wmi_display_state(void) +{ + return hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, 0); +} + +static int hp_wmi_hddtemp_state(void) +{ + return hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, 0); +} + +static int hp_wmi_als_state(void) +{ + return hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, 0); +} + +static int hp_wmi_dock_state(void) +{ + return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0); +} + +static int hp_wmi_wifi_set(void *data, enum rfkill_state state) +{ + if (state) + return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x101); + else + return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x100); +} + +static int hp_wmi_bluetooth_set(void *data, enum rfkill_state state) +{ + if (state) + return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x202); + else + return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x200); +} + +static int hp_wmi_wwan_set(void *data, enum rfkill_state state) +{ + if (state) + return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x404); + else + return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x400); +} + +static int hp_wmi_wifi_state(void) +{ + int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); + + if (wireless & 0x100) + return 1; + else + return 0; +} + +static int hp_wmi_bluetooth_state(void) +{ + int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); + + if (wireless & 0x10000) + return 1; + else + return 0; +} + +static int hp_wmi_wwan_state(void) +{ + int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); + + if (wireless & 0x1000000) + return 1; + else + return 0; +} + +static ssize_t show_display(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_display_state(); + if (value < 0) + return -EINVAL; + return sprintf(buf, "%d\n", value); +} + +static ssize_t show_hddtemp(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_hddtemp_state(); + if (value < 0) + return -EINVAL; + return sprintf(buf, "%d\n", value); +} + +static ssize_t show_als(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_als_state(); + if (value < 0) + return -EINVAL; + return sprintf(buf, "%d\n", value); +} + +static ssize_t show_dock(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_dock_state(); + if (value < 0) + return -EINVAL; + return sprintf(buf, "%d\n", value); +} + +static ssize_t set_als(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 tmp = simple_strtoul(buf, NULL, 10); + hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, tmp); + return count; +} + +static DEVICE_ATTR(display, S_IRUGO, show_display, NULL); +static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL); +static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als); +static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); + +static struct key_entry *hp_wmi_get_entry_by_scancode(int code) +{ + struct key_entry *key; + + for (key = hp_wmi_keymap; key->type != KE_END; key++) + if (code == key->code) + return key; + + return NULL; +} + +static struct key_entry *hp_wmi_get_entry_by_keycode(int keycode) +{ + struct key_entry *key; + + for (key = hp_wmi_keymap; key->type != KE_END; key++) + if (key->type == KE_KEY && keycode == key->keycode) + return key; + + return NULL; +} + +static int hp_wmi_getkeycode(struct input_dev *dev, int scancode, int *keycode) +{ + struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode); + + if (key && key->type == KE_KEY) { + *keycode = key->keycode; + return 0; + } + + return -EINVAL; +} + +static int hp_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode) +{ + struct key_entry *key; + int old_keycode; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + key = hp_wmi_get_entry_by_scancode(scancode); + if (key && key->type == KE_KEY) { + old_keycode = key->keycode; + key->keycode = keycode; + set_bit(keycode, dev->keybit); + if (!hp_wmi_get_entry_by_keycode(old_keycode)) + clear_bit(old_keycode, dev->keybit); + return 0; + } + + return -EINVAL; +} + +void hp_wmi_notify(u32 value, void *context) +{ + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + static struct key_entry *key; + union acpi_object *obj; + + wmi_get_event_data(value, &response); + + obj = (union acpi_object *)response.pointer; + + if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == 8) { + int eventcode = *((u8 *) obj->buffer.pointer); + key = hp_wmi_get_entry_by_scancode(eventcode); + if (key) { + switch (key->type) { + case KE_KEY: + input_report_key(hp_wmi_input_dev, + key->keycode, 1); + input_sync(hp_wmi_input_dev); + input_report_key(hp_wmi_input_dev, + key->keycode, 0); + input_sync(hp_wmi_input_dev); + break; + case KE_SW: + input_report_switch(hp_wmi_input_dev, + key->keycode, + hp_wmi_dock_state()); + input_sync(hp_wmi_input_dev); + break; + } + } else if (eventcode == 0x5) { + if (wifi_rfkill) + wifi_rfkill->state = hp_wmi_wifi_state(); + if (bluetooth_rfkill) + bluetooth_rfkill->state = + hp_wmi_bluetooth_state(); + if (wwan_rfkill) + wwan_rfkill->state = hp_wmi_wwan_state(); + } else + printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n", + eventcode); + } else + printk(KERN_INFO "HP WMI: Unknown response received\n"); +} + +static int __init hp_wmi_input_setup(void) +{ + struct key_entry *key; + int err; + + hp_wmi_input_dev = input_allocate_device(); + + hp_wmi_input_dev->name = "HP WMI hotkeys"; + hp_wmi_input_dev->phys = "wmi/input0"; + hp_wmi_input_dev->id.bustype = BUS_HOST; + hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode; + hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode; + + for (key = hp_wmi_keymap; key->type != KE_END; key++) { + switch (key->type) { + case KE_KEY: + set_bit(EV_KEY, hp_wmi_input_dev->evbit); + set_bit(key->keycode, hp_wmi_input_dev->keybit); + break; + case KE_SW: + set_bit(EV_SW, hp_wmi_input_dev->evbit); + set_bit(key->keycode, hp_wmi_input_dev->swbit); + break; + } + } + + err = input_register_device(hp_wmi_input_dev); + + if (err) { + input_free_device(hp_wmi_input_dev); + return err; + } + + return 0; +} + +static void cleanup_sysfs(struct platform_device *device) +{ + device_remove_file(&device->dev, &dev_attr_display); + device_remove_file(&device->dev, &dev_attr_hddtemp); + device_remove_file(&device->dev, &dev_attr_als); + device_remove_file(&device->dev, &dev_attr_dock); +} + +static int __init hp_wmi_bios_setup(struct platform_device *device) +{ + int err; + + err = device_create_file(&device->dev, &dev_attr_display); + if (err) + goto add_sysfs_error; + err = device_create_file(&device->dev, &dev_attr_hddtemp); + if (err) + goto add_sysfs_error; + err = device_create_file(&device->dev, &dev_attr_als); + if (err) + goto add_sysfs_error; + err = device_create_file(&device->dev, &dev_attr_dock); + if (err) + goto add_sysfs_error; + + wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN); + wifi_rfkill->name = "hp-wifi"; + wifi_rfkill->state = hp_wmi_wifi_state(); + wifi_rfkill->toggle_radio = hp_wmi_wifi_set; + wifi_rfkill->user_claim_unsupported = 1; + + bluetooth_rfkill = rfkill_allocate(&device->dev, + RFKILL_TYPE_BLUETOOTH); + bluetooth_rfkill->name = "hp-bluetooth"; + bluetooth_rfkill->state = hp_wmi_bluetooth_state(); + bluetooth_rfkill->toggle_radio = hp_wmi_bluetooth_set; + bluetooth_rfkill->user_claim_unsupported = 1; + + wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WIMAX); + wwan_rfkill->name = "hp-wwan"; + wwan_rfkill->state = hp_wmi_wwan_state(); + wwan_rfkill->toggle_radio = hp_wmi_wwan_set; + wwan_rfkill->user_claim_unsupported = 1; + + rfkill_register(wifi_rfkill); + rfkill_register(bluetooth_rfkill); + rfkill_register(wwan_rfkill); + + return 0; +add_sysfs_error: + cleanup_sysfs(device); + return err; +} + +static int __exit hp_wmi_bios_remove(struct platform_device *device) +{ + cleanup_sysfs(device); + + rfkill_unregister(wifi_rfkill); + rfkill_unregister(bluetooth_rfkill); + rfkill_unregister(wwan_rfkill); + + return 0; +} + +static int __init hp_wmi_init(void) +{ + int err; + + if (wmi_has_guid(HPWMI_EVENT_GUID)) { + err = wmi_install_notify_handler(HPWMI_EVENT_GUID, + hp_wmi_notify, NULL); + if (!err) + hp_wmi_input_setup(); + } + + if (wmi_has_guid(HPWMI_BIOS_GUID)) { + err = platform_driver_register(&hp_wmi_driver); + if (err) + return 0; + hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1); + if (!hp_wmi_platform_dev) { + platform_driver_unregister(&hp_wmi_driver); + return 0; + } + platform_device_add(hp_wmi_platform_dev); + } + + return 0; +} + +static void __exit hp_wmi_exit(void) +{ + if (wmi_has_guid(HPWMI_EVENT_GUID)) { + wmi_remove_notify_handler(HPWMI_EVENT_GUID); + input_unregister_device(hp_wmi_input_dev); + } + if (hp_wmi_platform_dev) { + platform_device_del(hp_wmi_platform_dev); + platform_driver_unregister(&hp_wmi_driver); + } +} + +module_init(hp_wmi_init); +module_exit(hp_wmi_exit); diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c new file mode 100644 index 000000000000..05e298289238 --- /dev/null +++ b/drivers/misc/hpilo.c @@ -0,0 +1,768 @@ +/* + * Driver for HP iLO/iLO2 management processor. + * + * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. + * David Altobelli <david.altobelli@hp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/pci.h> +#include <linux/ioport.h> +#include <linux/device.h> +#include <linux/file.h> +#include <linux/cdev.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/uaccess.h> +#include <linux/io.h> +#include "hpilo.h" + +static struct class *ilo_class; +static unsigned int ilo_major; +static char ilo_hwdev[MAX_ILO_DEV]; + +static inline int get_entry_id(int entry) +{ + return (entry & ENTRY_MASK_DESCRIPTOR) >> ENTRY_BITPOS_DESCRIPTOR; +} + +static inline int get_entry_len(int entry) +{ + return ((entry & ENTRY_MASK_QWORDS) >> ENTRY_BITPOS_QWORDS) << 3; +} + +static inline int mk_entry(int id, int len) +{ + int qlen = len & 7 ? (len >> 3) + 1 : len >> 3; + return id << ENTRY_BITPOS_DESCRIPTOR | qlen << ENTRY_BITPOS_QWORDS; +} + +static inline int desc_mem_sz(int nr_entry) +{ + return nr_entry << L2_QENTRY_SZ; +} + +/* + * FIFO queues, shared with hardware. + * + * If a queue has empty slots, an entry is added to the queue tail, + * and that entry is marked as occupied. + * Entries can be dequeued from the head of the list, when the device + * has marked the entry as consumed. + * + * Returns true on successful queue/dequeue, false on failure. + */ +static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry) +{ + struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); + int ret = 0; + + spin_lock(&hw->fifo_lock); + if (!(fifo_q->fifobar[(fifo_q->tail + 1) & fifo_q->imask] + & ENTRY_MASK_O)) { + fifo_q->fifobar[fifo_q->tail & fifo_q->imask] |= + (entry & ENTRY_MASK_NOSTATE) | fifo_q->merge; + fifo_q->tail += 1; + ret = 1; + } + spin_unlock(&hw->fifo_lock); + + return ret; +} + +static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry) +{ + struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); + int ret = 0; + u64 c; + + spin_lock(&hw->fifo_lock); + c = fifo_q->fifobar[fifo_q->head & fifo_q->imask]; + if (c & ENTRY_MASK_C) { + if (entry) + *entry = c & ENTRY_MASK_NOSTATE; + + fifo_q->fifobar[fifo_q->head & fifo_q->imask] = + (c | ENTRY_MASK) + 1; + fifo_q->head += 1; + ret = 1; + } + spin_unlock(&hw->fifo_lock); + + return ret; +} + +static int ilo_pkt_enqueue(struct ilo_hwinfo *hw, struct ccb *ccb, + int dir, int id, int len) +{ + char *fifobar; + int entry; + + if (dir == SENDQ) + fifobar = ccb->ccb_u1.send_fifobar; + else + fifobar = ccb->ccb_u3.recv_fifobar; + + entry = mk_entry(id, len); + return fifo_enqueue(hw, fifobar, entry); +} + +static int ilo_pkt_dequeue(struct ilo_hwinfo *hw, struct ccb *ccb, + int dir, int *id, int *len, void **pkt) +{ + char *fifobar, *desc; + int entry = 0, pkt_id = 0; + int ret; + + if (dir == SENDQ) { + fifobar = ccb->ccb_u1.send_fifobar; + desc = ccb->ccb_u2.send_desc; + } else { + fifobar = ccb->ccb_u3.recv_fifobar; + desc = ccb->ccb_u4.recv_desc; + } + + ret = fifo_dequeue(hw, fifobar, &entry); + if (ret) { + pkt_id = get_entry_id(entry); + if (id) + *id = pkt_id; + if (len) + *len = get_entry_len(entry); + if (pkt) + *pkt = (void *)(desc + desc_mem_sz(pkt_id)); + } + + return ret; +} + +static inline void doorbell_set(struct ccb *ccb) +{ + iowrite8(1, ccb->ccb_u5.db_base); +} + +static inline void doorbell_clr(struct ccb *ccb) +{ + iowrite8(2, ccb->ccb_u5.db_base); +} +static inline int ctrl_set(int l2sz, int idxmask, int desclim) +{ + int active = 0, go = 1; + return l2sz << CTRL_BITPOS_L2SZ | + idxmask << CTRL_BITPOS_FIFOINDEXMASK | + desclim << CTRL_BITPOS_DESCLIMIT | + active << CTRL_BITPOS_A | + go << CTRL_BITPOS_G; +} +static void ctrl_setup(struct ccb *ccb, int nr_desc, int l2desc_sz) +{ + /* for simplicity, use the same parameters for send and recv ctrls */ + ccb->send_ctrl = ctrl_set(l2desc_sz, nr_desc-1, nr_desc-1); + ccb->recv_ctrl = ctrl_set(l2desc_sz, nr_desc-1, nr_desc-1); +} + +static inline int fifo_sz(int nr_entry) +{ + /* size of a fifo is determined by the number of entries it contains */ + return (nr_entry * sizeof(u64)) + FIFOHANDLESIZE; +} + +static void fifo_setup(void *base_addr, int nr_entry) +{ + struct fifo *fifo_q = base_addr; + int i; + + /* set up an empty fifo */ + fifo_q->head = 0; + fifo_q->tail = 0; + fifo_q->reset = 0; + fifo_q->nrents = nr_entry; + fifo_q->imask = nr_entry - 1; + fifo_q->merge = ENTRY_MASK_O; + + for (i = 0; i < nr_entry; i++) + fifo_q->fifobar[i] = 0; +} + +static void ilo_ccb_close(struct pci_dev *pdev, struct ccb_data *data) +{ + struct ccb *driver_ccb; + struct ccb __iomem *device_ccb; + int retries; + + driver_ccb = &data->driver_ccb; + device_ccb = data->mapped_ccb; + + /* complicated dance to tell the hw we are stopping */ + doorbell_clr(driver_ccb); + iowrite32(ioread32(&device_ccb->send_ctrl) & ~(1 << CTRL_BITPOS_G), + &device_ccb->send_ctrl); + iowrite32(ioread32(&device_ccb->recv_ctrl) & ~(1 << CTRL_BITPOS_G), + &device_ccb->recv_ctrl); + + /* give iLO some time to process stop request */ + for (retries = 1000; retries > 0; retries--) { + doorbell_set(driver_ccb); + udelay(1); + if (!(ioread32(&device_ccb->send_ctrl) & (1 << CTRL_BITPOS_A)) + && + !(ioread32(&device_ccb->recv_ctrl) & (1 << CTRL_BITPOS_A))) + break; + } + if (retries == 0) + dev_err(&pdev->dev, "Closing, but controller still active\n"); + + /* clear the hw ccb */ + memset_io(device_ccb, 0, sizeof(struct ccb)); + + /* free resources used to back send/recv queues */ + pci_free_consistent(pdev, data->dma_size, data->dma_va, data->dma_pa); +} + +static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) +{ + char *dma_va, *dma_pa; + int pkt_id, pkt_sz, i, error; + struct ccb *driver_ccb, *ilo_ccb; + struct pci_dev *pdev; + + driver_ccb = &data->driver_ccb; + ilo_ccb = &data->ilo_ccb; + pdev = hw->ilo_dev; + + data->dma_size = 2 * fifo_sz(NR_QENTRY) + + 2 * desc_mem_sz(NR_QENTRY) + + ILO_START_ALIGN + ILO_CACHE_SZ; + + error = -ENOMEM; + data->dma_va = pci_alloc_consistent(pdev, data->dma_size, + &data->dma_pa); + if (!data->dma_va) + goto out; + + dma_va = (char *)data->dma_va; + dma_pa = (char *)data->dma_pa; + + memset(dma_va, 0, data->dma_size); + + dma_va = (char *)roundup((unsigned long)dma_va, ILO_START_ALIGN); + dma_pa = (char *)roundup((unsigned long)dma_pa, ILO_START_ALIGN); + + /* + * Create two ccb's, one with virt addrs, one with phys addrs. + * Copy the phys addr ccb to device shared mem. + */ + ctrl_setup(driver_ccb, NR_QENTRY, L2_QENTRY_SZ); + ctrl_setup(ilo_ccb, NR_QENTRY, L2_QENTRY_SZ); + + fifo_setup(dma_va, NR_QENTRY); + driver_ccb->ccb_u1.send_fifobar = dma_va + FIFOHANDLESIZE; + ilo_ccb->ccb_u1.send_fifobar = dma_pa + FIFOHANDLESIZE; + dma_va += fifo_sz(NR_QENTRY); + dma_pa += fifo_sz(NR_QENTRY); + + dma_va = (char *)roundup((unsigned long)dma_va, ILO_CACHE_SZ); + dma_pa = (char *)roundup((unsigned long)dma_pa, ILO_CACHE_SZ); + + fifo_setup(dma_va, NR_QENTRY); + driver_ccb->ccb_u3.recv_fifobar = dma_va + FIFOHANDLESIZE; + ilo_ccb->ccb_u3.recv_fifobar = dma_pa + FIFOHANDLESIZE; + dma_va += fifo_sz(NR_QENTRY); + dma_pa += fifo_sz(NR_QENTRY); + + driver_ccb->ccb_u2.send_desc = dma_va; + ilo_ccb->ccb_u2.send_desc = dma_pa; + dma_pa += desc_mem_sz(NR_QENTRY); + dma_va += desc_mem_sz(NR_QENTRY); + + driver_ccb->ccb_u4.recv_desc = dma_va; + ilo_ccb->ccb_u4.recv_desc = dma_pa; + + driver_ccb->channel = slot; + ilo_ccb->channel = slot; + + driver_ccb->ccb_u5.db_base = hw->db_vaddr + (slot << L2_DB_SIZE); + ilo_ccb->ccb_u5.db_base = NULL; /* hw ccb's doorbell is not used */ + + /* copy the ccb with physical addrs to device memory */ + data->mapped_ccb = (struct ccb __iomem *) + (hw->ram_vaddr + (slot * ILOHW_CCB_SZ)); + memcpy_toio(data->mapped_ccb, ilo_ccb, sizeof(struct ccb)); + + /* put packets on the send and receive queues */ + pkt_sz = 0; + for (pkt_id = 0; pkt_id < NR_QENTRY; pkt_id++) { + ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, pkt_sz); + doorbell_set(driver_ccb); + } + + pkt_sz = desc_mem_sz(1); + for (pkt_id = 0; pkt_id < NR_QENTRY; pkt_id++) + ilo_pkt_enqueue(hw, driver_ccb, RECVQ, pkt_id, pkt_sz); + + doorbell_clr(driver_ccb); + + /* make sure iLO is really handling requests */ + for (i = 1000; i > 0; i--) { + if (ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, NULL, NULL)) + break; + udelay(1); + } + + if (i) { + ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, 0); + doorbell_set(driver_ccb); + } else { + dev_err(&pdev->dev, "Open could not dequeue a packet\n"); + error = -EBUSY; + goto free; + } + + return 0; +free: + pci_free_consistent(pdev, data->dma_size, data->dma_va, data->dma_pa); +out: + return error; +} + +static inline int is_channel_reset(struct ccb *ccb) +{ + /* check for this particular channel needing a reset */ + return FIFOBARTOHANDLE(ccb->ccb_u1.send_fifobar)->reset; +} + +static inline void set_channel_reset(struct ccb *ccb) +{ + /* set a flag indicating this channel needs a reset */ + FIFOBARTOHANDLE(ccb->ccb_u1.send_fifobar)->reset = 1; +} + +static inline int is_device_reset(struct ilo_hwinfo *hw) +{ + /* check for global reset condition */ + return ioread32(&hw->mmio_vaddr[DB_OUT]) & (1 << DB_RESET); +} + +static inline void clear_device(struct ilo_hwinfo *hw) +{ + /* clear the device (reset bits, pending channel entries) */ + iowrite32(-1, &hw->mmio_vaddr[DB_OUT]); +} + +static void ilo_locked_reset(struct ilo_hwinfo *hw) +{ + int slot; + + /* + * Mapped memory is zeroed on ilo reset, so set a per ccb flag + * to indicate that this ccb needs to be closed and reopened. + */ + for (slot = 0; slot < MAX_CCB; slot++) { + if (!hw->ccb_alloc[slot]) + continue; + set_channel_reset(&hw->ccb_alloc[slot]->driver_ccb); + } + + clear_device(hw); +} + +static void ilo_reset(struct ilo_hwinfo *hw) +{ + spin_lock(&hw->alloc_lock); + + /* reset might have been handled after lock was taken */ + if (is_device_reset(hw)) + ilo_locked_reset(hw); + + spin_unlock(&hw->alloc_lock); +} + +static ssize_t ilo_read(struct file *fp, char __user *buf, + size_t len, loff_t *off) +{ + int err, found, cnt, pkt_id, pkt_len; + struct ccb_data *data; + struct ccb *driver_ccb; + struct ilo_hwinfo *hw; + void *pkt; + + data = fp->private_data; + driver_ccb = &data->driver_ccb; + hw = data->ilo_hw; + + if (is_device_reset(hw) || is_channel_reset(driver_ccb)) { + /* + * If the device has been reset, applications + * need to close and reopen all ccbs. + */ + ilo_reset(hw); + return -ENODEV; + } + + /* + * This function is to be called when data is expected + * in the channel, and will return an error if no packet is found + * during the loop below. The sleep/retry logic is to allow + * applications to call read() immediately post write(), + * and give iLO some time to process the sent packet. + */ + cnt = 20; + do { + /* look for a received packet */ + found = ilo_pkt_dequeue(hw, driver_ccb, RECVQ, &pkt_id, + &pkt_len, &pkt); + if (found) + break; + cnt--; + msleep(100); + } while (!found && cnt); + + if (!found) + return -EAGAIN; + + /* only copy the length of the received packet */ + if (pkt_len < len) + len = pkt_len; + + err = copy_to_user(buf, pkt, len); + + /* return the received packet to the queue */ + ilo_pkt_enqueue(hw, driver_ccb, RECVQ, pkt_id, desc_mem_sz(1)); + + return err ? -EFAULT : len; +} + +static ssize_t ilo_write(struct file *fp, const char __user *buf, + size_t len, loff_t *off) +{ + int err, pkt_id, pkt_len; + struct ccb_data *data; + struct ccb *driver_ccb; + struct ilo_hwinfo *hw; + void *pkt; + + data = fp->private_data; + driver_ccb = &data->driver_ccb; + hw = data->ilo_hw; + + if (is_device_reset(hw) || is_channel_reset(driver_ccb)) { + /* + * If the device has been reset, applications + * need to close and reopen all ccbs. + */ + ilo_reset(hw); + return -ENODEV; + } + + /* get a packet to send the user command */ + if (!ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, &pkt_len, &pkt)) + return -EBUSY; + + /* limit the length to the length of the packet */ + if (pkt_len < len) + len = pkt_len; + + /* on failure, set the len to 0 to return empty packet to the device */ + err = copy_from_user(pkt, buf, len); + if (err) + len = 0; + + /* send the packet */ + ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, len); + doorbell_set(driver_ccb); + + return err ? -EFAULT : len; +} + +static int ilo_close(struct inode *ip, struct file *fp) +{ + int slot; + struct ccb_data *data; + struct ilo_hwinfo *hw; + + slot = iminor(ip) % MAX_CCB; + hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev); + + spin_lock(&hw->alloc_lock); + + if (is_device_reset(hw)) + ilo_locked_reset(hw); + + if (hw->ccb_alloc[slot]->ccb_cnt == 1) { + + data = fp->private_data; + + ilo_ccb_close(hw->ilo_dev, data); + + kfree(data); + hw->ccb_alloc[slot] = NULL; + } else + hw->ccb_alloc[slot]->ccb_cnt--; + + spin_unlock(&hw->alloc_lock); + + return 0; +} + +static int ilo_open(struct inode *ip, struct file *fp) +{ + int slot, error; + struct ccb_data *data; + struct ilo_hwinfo *hw; + + slot = iminor(ip) % MAX_CCB; + hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev); + + /* new ccb allocation */ + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + spin_lock(&hw->alloc_lock); + + if (is_device_reset(hw)) + ilo_locked_reset(hw); + + /* each fd private_data holds sw/hw view of ccb */ + if (hw->ccb_alloc[slot] == NULL) { + /* create a channel control block for this minor */ + error = ilo_ccb_open(hw, data, slot); + if (!error) { + hw->ccb_alloc[slot] = data; + hw->ccb_alloc[slot]->ccb_cnt = 1; + hw->ccb_alloc[slot]->ccb_excl = fp->f_flags & O_EXCL; + hw->ccb_alloc[slot]->ilo_hw = hw; + } else + kfree(data); + } else { + kfree(data); + if (fp->f_flags & O_EXCL || hw->ccb_alloc[slot]->ccb_excl) { + /* + * The channel exists, and either this open + * or a previous open of this channel wants + * exclusive access. + */ + error = -EBUSY; + } else { + hw->ccb_alloc[slot]->ccb_cnt++; + error = 0; + } + } + spin_unlock(&hw->alloc_lock); + + if (!error) + fp->private_data = hw->ccb_alloc[slot]; + + return error; +} + +static const struct file_operations ilo_fops = { + .owner = THIS_MODULE, + .read = ilo_read, + .write = ilo_write, + .open = ilo_open, + .release = ilo_close, +}; + +static void ilo_unmap_device(struct pci_dev *pdev, struct ilo_hwinfo *hw) +{ + pci_iounmap(pdev, hw->db_vaddr); + pci_iounmap(pdev, hw->ram_vaddr); + pci_iounmap(pdev, hw->mmio_vaddr); +} + +static int __devinit ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw) +{ + int error = -ENOMEM; + + /* map the memory mapped i/o registers */ + hw->mmio_vaddr = pci_iomap(pdev, 1, 0); + if (hw->mmio_vaddr == NULL) { + dev_err(&pdev->dev, "Error mapping mmio\n"); + goto out; + } + + /* map the adapter shared memory region */ + hw->ram_vaddr = pci_iomap(pdev, 2, MAX_CCB * ILOHW_CCB_SZ); + if (hw->ram_vaddr == NULL) { + dev_err(&pdev->dev, "Error mapping shared mem\n"); + goto mmio_free; + } + + /* map the doorbell aperture */ + hw->db_vaddr = pci_iomap(pdev, 3, MAX_CCB * ONE_DB_SIZE); + if (hw->db_vaddr == NULL) { + dev_err(&pdev->dev, "Error mapping doorbell\n"); + goto ram_free; + } + + return 0; +ram_free: + pci_iounmap(pdev, hw->ram_vaddr); +mmio_free: + pci_iounmap(pdev, hw->mmio_vaddr); +out: + return error; +} + +static void ilo_remove(struct pci_dev *pdev) +{ + int i, minor; + struct ilo_hwinfo *ilo_hw = pci_get_drvdata(pdev); + + clear_device(ilo_hw); + + minor = MINOR(ilo_hw->cdev.dev); + for (i = minor; i < minor + MAX_CCB; i++) + device_destroy(ilo_class, MKDEV(ilo_major, i)); + + cdev_del(&ilo_hw->cdev); + ilo_unmap_device(pdev, ilo_hw); + pci_release_regions(pdev); + pci_disable_device(pdev); + kfree(ilo_hw); + ilo_hwdev[(minor / MAX_CCB)] = 0; +} + +static int __devinit ilo_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int devnum, minor, start, error; + struct ilo_hwinfo *ilo_hw; + + /* find a free range for device files */ + for (devnum = 0; devnum < MAX_ILO_DEV; devnum++) { + if (ilo_hwdev[devnum] == 0) { + ilo_hwdev[devnum] = 1; + break; + } + } + + if (devnum == MAX_ILO_DEV) { + dev_err(&pdev->dev, "Error finding free device\n"); + return -ENODEV; + } + + /* track global allocations for this device */ + error = -ENOMEM; + ilo_hw = kzalloc(sizeof(*ilo_hw), GFP_KERNEL); + if (!ilo_hw) + goto out; + + ilo_hw->ilo_dev = pdev; + spin_lock_init(&ilo_hw->alloc_lock); + spin_lock_init(&ilo_hw->fifo_lock); + + error = pci_enable_device(pdev); + if (error) + goto free; + + pci_set_master(pdev); + + error = pci_request_regions(pdev, ILO_NAME); + if (error) + goto disable; + + error = ilo_map_device(pdev, ilo_hw); + if (error) + goto free_regions; + + pci_set_drvdata(pdev, ilo_hw); + clear_device(ilo_hw); + + cdev_init(&ilo_hw->cdev, &ilo_fops); + ilo_hw->cdev.owner = THIS_MODULE; + start = devnum * MAX_CCB; + error = cdev_add(&ilo_hw->cdev, MKDEV(ilo_major, start), MAX_CCB); + if (error) { + dev_err(&pdev->dev, "Could not add cdev\n"); + goto unmap; + } + + for (minor = 0 ; minor < MAX_CCB; minor++) { + struct device *dev; + dev = device_create(ilo_class, &pdev->dev, + MKDEV(ilo_major, minor), NULL, + "hpilo!d%dccb%d", devnum, minor); + if (IS_ERR(dev)) + dev_err(&pdev->dev, "Could not create files\n"); + } + + return 0; +unmap: + ilo_unmap_device(pdev, ilo_hw); +free_regions: + pci_release_regions(pdev); +disable: + pci_disable_device(pdev); +free: + kfree(ilo_hw); +out: + ilo_hwdev[devnum] = 0; + return error; +} + +static struct pci_device_id ilo_devices[] = { + { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB204) }, + { } +}; +MODULE_DEVICE_TABLE(pci, ilo_devices); + +static struct pci_driver ilo_driver = { + .name = ILO_NAME, + .id_table = ilo_devices, + .probe = ilo_probe, + .remove = __devexit_p(ilo_remove), +}; + +static int __init ilo_init(void) +{ + int error; + dev_t dev; + + ilo_class = class_create(THIS_MODULE, "iLO"); + if (IS_ERR(ilo_class)) { + error = PTR_ERR(ilo_class); + goto out; + } + + error = alloc_chrdev_region(&dev, 0, MAX_OPEN, ILO_NAME); + if (error) + goto class_destroy; + + ilo_major = MAJOR(dev); + + error = pci_register_driver(&ilo_driver); + if (error) + goto chr_remove; + + return 0; +chr_remove: + unregister_chrdev_region(dev, MAX_OPEN); +class_destroy: + class_destroy(ilo_class); +out: + return error; +} + +static void __exit ilo_exit(void) +{ + pci_unregister_driver(&ilo_driver); + unregister_chrdev_region(MKDEV(ilo_major, 0), MAX_OPEN); + class_destroy(ilo_class); +} + +MODULE_VERSION("0.05"); +MODULE_ALIAS(ILO_NAME); +MODULE_DESCRIPTION(ILO_NAME); +MODULE_AUTHOR("David Altobelli <david.altobelli@hp.com>"); +MODULE_LICENSE("GPL v2"); + +module_init(ilo_init); +module_exit(ilo_exit); diff --git a/drivers/misc/hpilo.h b/drivers/misc/hpilo.h new file mode 100644 index 000000000000..a281207696c1 --- /dev/null +++ b/drivers/misc/hpilo.h @@ -0,0 +1,189 @@ +/* + * linux/drivers/char/hpilo.h + * + * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. + * David Altobelli <david.altobelli@hp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __HPILO_H +#define __HPILO_H + +#define ILO_NAME "hpilo" + +/* max number of open channel control blocks per device, hw limited to 32 */ +#define MAX_CCB 8 +/* max number of supported devices */ +#define MAX_ILO_DEV 1 +/* max number of files */ +#define MAX_OPEN (MAX_CCB * MAX_ILO_DEV) + +/* + * Per device, used to track global memory allocations. + */ +struct ilo_hwinfo { + /* mmio registers on device */ + char __iomem *mmio_vaddr; + + /* doorbell registers on device */ + char __iomem *db_vaddr; + + /* shared memory on device used for channel control blocks */ + char __iomem *ram_vaddr; + + /* files corresponding to this device */ + struct ccb_data *ccb_alloc[MAX_CCB]; + + struct pci_dev *ilo_dev; + + spinlock_t alloc_lock; + spinlock_t fifo_lock; + + struct cdev cdev; +}; + +/* offset from mmio_vaddr */ +#define DB_OUT 0xD4 +/* DB_OUT reset bit */ +#define DB_RESET 26 + +/* + * Channel control block. Used to manage hardware queues. + * The format must match hw's version. The hw ccb is 128 bytes, + * but the context area shouldn't be touched by the driver. + */ +#define ILOSW_CCB_SZ 64 +#define ILOHW_CCB_SZ 128 +struct ccb { + union { + char *send_fifobar; + u64 padding1; + } ccb_u1; + union { + char *send_desc; + u64 padding2; + } ccb_u2; + u64 send_ctrl; + + union { + char *recv_fifobar; + u64 padding3; + } ccb_u3; + union { + char *recv_desc; + u64 padding4; + } ccb_u4; + u64 recv_ctrl; + + union { + char __iomem *db_base; + u64 padding5; + } ccb_u5; + + u64 channel; + + /* unused context area (64 bytes) */ +}; + +/* ccb queue parameters */ +#define SENDQ 1 +#define RECVQ 2 +#define NR_QENTRY 4 +#define L2_QENTRY_SZ 12 + +/* ccb ctrl bitfields */ +#define CTRL_BITPOS_L2SZ 0 +#define CTRL_BITPOS_FIFOINDEXMASK 4 +#define CTRL_BITPOS_DESCLIMIT 18 +#define CTRL_BITPOS_A 30 +#define CTRL_BITPOS_G 31 + +/* ccb doorbell macros */ +#define L2_DB_SIZE 14 +#define ONE_DB_SIZE (1 << L2_DB_SIZE) + +/* + * Per fd structure used to track the ccb allocated to that dev file. + */ +struct ccb_data { + /* software version of ccb, using virtual addrs */ + struct ccb driver_ccb; + + /* hardware version of ccb, using physical addrs */ + struct ccb ilo_ccb; + + /* hardware ccb is written to this shared mapped device memory */ + struct ccb __iomem *mapped_ccb; + + /* dma'able memory used for send/recv queues */ + void *dma_va; + dma_addr_t dma_pa; + size_t dma_size; + + /* pointer to hardware device info */ + struct ilo_hwinfo *ilo_hw; + + /* usage count, to allow for shared ccb's */ + int ccb_cnt; + + /* open wanted exclusive access to this ccb */ + int ccb_excl; +}; + +/* + * FIFO queue structure, shared with hw. + */ +#define ILO_START_ALIGN 4096 +#define ILO_CACHE_SZ 128 +struct fifo { + u64 nrents; /* user requested number of fifo entries */ + u64 imask; /* mask to extract valid fifo index */ + u64 merge; /* O/C bits to merge in during enqueue operation */ + u64 reset; /* set to non-zero when the target device resets */ + u8 pad_0[ILO_CACHE_SZ - (sizeof(u64) * 4)]; + + u64 head; + u8 pad_1[ILO_CACHE_SZ - (sizeof(u64))]; + + u64 tail; + u8 pad_2[ILO_CACHE_SZ - (sizeof(u64))]; + + u64 fifobar[1]; +}; + +/* convert between struct fifo, and the fifobar, which is saved in the ccb */ +#define FIFOHANDLESIZE (sizeof(struct fifo) - sizeof(u64)) +#define FIFOBARTOHANDLE(_fifo) \ + ((struct fifo *)(((char *)(_fifo)) - FIFOHANDLESIZE)) + +/* the number of qwords to consume from the entry descriptor */ +#define ENTRY_BITPOS_QWORDS 0 +/* descriptor index number (within a specified queue) */ +#define ENTRY_BITPOS_DESCRIPTOR 10 +/* state bit, fifo entry consumed by consumer */ +#define ENTRY_BITPOS_C 22 +/* state bit, fifo entry is occupied */ +#define ENTRY_BITPOS_O 23 + +#define ENTRY_BITS_QWORDS 10 +#define ENTRY_BITS_DESCRIPTOR 12 +#define ENTRY_BITS_C 1 +#define ENTRY_BITS_O 1 +#define ENTRY_BITS_TOTAL \ + (ENTRY_BITS_C + ENTRY_BITS_O + \ + ENTRY_BITS_QWORDS + ENTRY_BITS_DESCRIPTOR) + +/* extract various entry fields */ +#define ENTRY_MASK ((1 << ENTRY_BITS_TOTAL) - 1) +#define ENTRY_MASK_C (((1 << ENTRY_BITS_C) - 1) << ENTRY_BITPOS_C) +#define ENTRY_MASK_O (((1 << ENTRY_BITS_O) - 1) << ENTRY_BITPOS_O) +#define ENTRY_MASK_QWORDS \ + (((1 << ENTRY_BITS_QWORDS) - 1) << ENTRY_BITPOS_QWORDS) +#define ENTRY_MASK_DESCRIPTOR \ + (((1 << ENTRY_BITS_DESCRIPTOR) - 1) << ENTRY_BITPOS_DESCRIPTOR) + +#define ENTRY_MASK_NOSTATE (ENTRY_MASK >> (ENTRY_BITS_C + ENTRY_BITS_O)) + +#endif /* __HPILO_H */ diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index 186162470090..daf585689ce3 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c @@ -399,8 +399,9 @@ static int __devinit phantom_probe(struct pci_dev *pdev, goto err_irq; } - if (IS_ERR(device_create(phantom_class, &pdev->dev, MKDEV(phantom_major, - minor), "phantom%u", minor))) + if (IS_ERR(device_create_drvdata(phantom_class, &pdev->dev, + MKDEV(phantom_major, minor), + NULL, "phantom%u", minor))) dev_err(&pdev->dev, "can't create device\n"); pci_set_drvdata(pdev, pht); @@ -562,6 +563,6 @@ module_init(phantom_init); module_exit(phantom_exit); MODULE_AUTHOR("Jiri Slaby <jirislaby@gmail.com>"); -MODULE_DESCRIPTION("Sensable Phantom driver"); +MODULE_DESCRIPTION("Sensable Phantom driver (PCI devices)"); MODULE_LICENSE("GPL"); MODULE_VERSION(PHANTOM_VERSION); diff --git a/drivers/misc/sgi-xp/xpc_main.c b/drivers/misc/sgi-xp/xpc_main.c index 08256ed0d9a6..c3b4227f48a5 100644 --- a/drivers/misc/sgi-xp/xpc_main.c +++ b/drivers/misc/sgi-xp/xpc_main.c @@ -232,7 +232,7 @@ xpc_hb_checker(void *ignore) /* this thread was marked active by xpc_hb_init() */ - set_cpus_allowed(current, cpumask_of_cpu(XPC_HB_CHECK_CPU)); + set_cpus_allowed_ptr(current, &cpumask_of_cpu(XPC_HB_CHECK_CPU)); /* set our heartbeating to other partitions into motion */ xpc_hb_check_timeout = jiffies + (xpc_hb_check_interval * HZ); diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index b5969298f3d3..d3eb7903c346 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -21,7 +21,7 @@ * 02110-1301, USA. */ -#define TPACPI_VERSION "0.20" +#define TPACPI_VERSION "0.21" #define TPACPI_SYSFS_VERSION 0x020200 /* @@ -68,6 +68,7 @@ #include <linux/hwmon-sysfs.h> #include <linux/input.h> #include <linux/leds.h> +#include <linux/rfkill.h> #include <asm/uaccess.h> #include <linux/dmi.h> @@ -144,6 +145,12 @@ enum { #define TPACPI_MAX_ACPI_ARGS 3 +/* rfkill switches */ +enum { + TPACPI_RFK_BLUETOOTH_SW_ID = 0, + TPACPI_RFK_WWAN_SW_ID, +}; + /* Debugging */ #define TPACPI_LOG TPACPI_FILE ": " #define TPACPI_ERR KERN_ERR TPACPI_LOG @@ -905,6 +912,43 @@ static int __init tpacpi_check_std_acpi_brightness_support(void) return 0; } +static int __init tpacpi_new_rfkill(const unsigned int id, + struct rfkill **rfk, + const enum rfkill_type rfktype, + const char *name, + int (*toggle_radio)(void *, enum rfkill_state), + int (*get_state)(void *, enum rfkill_state *)) +{ + int res; + enum rfkill_state initial_state; + + *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype); + if (!*rfk) { + printk(TPACPI_ERR + "failed to allocate memory for rfkill class\n"); + return -ENOMEM; + } + + (*rfk)->name = name; + (*rfk)->get_state = get_state; + (*rfk)->toggle_radio = toggle_radio; + + if (!get_state(NULL, &initial_state)) + (*rfk)->state = initial_state; + + res = rfkill_register(*rfk); + if (res < 0) { + printk(TPACPI_ERR + "failed to register %s rfkill switch: %d\n", + name, res); + rfkill_free(*rfk); + *rfk = NULL; + return res; + } + + return 0; +} + /************************************************************************* * thinkpad-acpi driver attributes */ @@ -1285,21 +1329,6 @@ static int hotkey_status_set(int status) return 0; } -static void tpacpi_input_send_radiosw(void) -{ - int wlsw; - - if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { - mutex_lock(&tpacpi_inputdev_send_mutex); - - input_report_switch(tpacpi_inputdev, - SW_RFKILL_ALL, !!wlsw); - input_sync(tpacpi_inputdev); - - mutex_unlock(&tpacpi_inputdev_send_mutex); - } -} - static void tpacpi_input_send_tabletsw(void) { int state; @@ -1921,6 +1950,30 @@ static struct attribute *hotkey_mask_attributes[] __initdata = { &dev_attr_hotkey_wakeup_hotunplug_complete.attr, }; +static void bluetooth_update_rfk(void); +static void wan_update_rfk(void); +static void tpacpi_send_radiosw_update(void) +{ + int wlsw; + + /* Sync these BEFORE sending any rfkill events */ + if (tp_features.bluetooth) + bluetooth_update_rfk(); + if (tp_features.wan) + wan_update_rfk(); + + if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { + mutex_lock(&tpacpi_inputdev_send_mutex); + + input_report_switch(tpacpi_inputdev, + SW_RFKILL_ALL, !!wlsw); + input_sync(tpacpi_inputdev); + + mutex_unlock(&tpacpi_inputdev_send_mutex); + } + hotkey_radio_sw_notify_change(); +} + static void hotkey_exit(void) { #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL @@ -2167,9 +2220,10 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) printk(TPACPI_INFO "radio switch found; radios are %s\n", enabled(status, 0)); + } + if (tp_features.hotkey_wlsw) res = add_to_attr_set(hotkey_dev_attributes, &dev_attr_hotkey_radio_sw.attr); - } /* For X41t, X60t, X61t Tablets... */ if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { @@ -2287,7 +2341,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) tpacpi_inputdev->close = &hotkey_inputdev_close; hotkey_poll_setup_safe(1); - tpacpi_input_send_radiosw(); + tpacpi_send_radiosw_update(); tpacpi_input_send_tabletsw(); return 0; @@ -2419,8 +2473,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) case 7: /* 0x7000-0x7FFF: misc */ if (tp_features.hotkey_wlsw && hkey == 0x7000) { - tpacpi_input_send_radiosw(); - hotkey_radio_sw_notify_change(); + tpacpi_send_radiosw_update(); send_acpi_ev = 0; break; } @@ -2463,8 +2516,7 @@ static void hotkey_resume(void) printk(TPACPI_ERR "error while trying to read hot key mask " "from firmware\n"); - tpacpi_input_send_radiosw(); - hotkey_radio_sw_notify_change(); + tpacpi_send_radiosw_update(); hotkey_tablet_mode_notify_change(); hotkey_wakeup_reason_notify_change(); hotkey_wakeup_hotunplug_complete_notify_change(); @@ -2581,8 +2633,66 @@ enum { TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */ }; -static int bluetooth_get_radiosw(void); -static int bluetooth_set_radiosw(int radio_on); +static struct rfkill *tpacpi_bluetooth_rfkill; + +static int bluetooth_get_radiosw(void) +{ + int status; + + if (!tp_features.bluetooth) + return -ENODEV; + + /* WLSW overrides bluetooth in firmware/hardware, reflect that */ + if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) + return RFKILL_STATE_HARD_BLOCKED; + + if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) + return -EIO; + + return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ? + RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; +} + +static void bluetooth_update_rfk(void) +{ + int status; + + if (!tpacpi_bluetooth_rfkill) + return; + + status = bluetooth_get_radiosw(); + if (status < 0) + return; + rfkill_force_state(tpacpi_bluetooth_rfkill, status); +} + +static int bluetooth_set_radiosw(int radio_on, int update_rfk) +{ + int status; + + if (!tp_features.bluetooth) + return -ENODEV; + + /* WLSW overrides bluetooth in firmware/hardware, but there is no + * reason to risk weird behaviour. */ + if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status + && radio_on) + return -EPERM; + + 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; + + if (update_rfk) + bluetooth_update_rfk(); + + return 0; +} /* sysfs bluetooth enable ---------------------------------------------- */ static ssize_t bluetooth_enable_show(struct device *dev, @@ -2595,7 +2705,8 @@ static ssize_t bluetooth_enable_show(struct device *dev, if (status < 0) return status; - return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); + return snprintf(buf, PAGE_SIZE, "%d\n", + (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); } static ssize_t bluetooth_enable_store(struct device *dev, @@ -2608,7 +2719,7 @@ static ssize_t bluetooth_enable_store(struct device *dev, if (parse_strtoul(buf, 1, &t)) return -EINVAL; - res = bluetooth_set_radiosw(t); + res = bluetooth_set_radiosw(t, 1); return (res) ? res : count; } @@ -2628,6 +2739,31 @@ static const struct attribute_group bluetooth_attr_group = { .attrs = bluetooth_attributes, }; +static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state) +{ + int bts = bluetooth_get_radiosw(); + + if (bts < 0) + return bts; + + *state = bts; + return 0; +} + +static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state) +{ + return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); +} + +static void bluetooth_exit(void) +{ + if (tpacpi_bluetooth_rfkill) + rfkill_unregister(tpacpi_bluetooth_rfkill); + + sysfs_remove_group(&tpacpi_pdev->dev.kobj, + &bluetooth_attr_group); +} + static int __init bluetooth_init(struct ibm_init_struct *iibm) { int res; @@ -2646,57 +2782,32 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) 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; - } + if (tp_features.bluetooth && + !(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"); } - 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; + return 1; - if (!tp_features.bluetooth) - return -ENODEV; + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &bluetooth_attr_group); + if (res) + return res; - 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; + res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID, + &tpacpi_bluetooth_rfkill, + RFKILL_TYPE_BLUETOOTH, + "tpacpi_bluetooth_sw", + tpacpi_bluetooth_rfk_set, + tpacpi_bluetooth_rfk_get); + if (res) { + bluetooth_exit(); + return res; + } return 0; } @@ -2711,7 +2822,8 @@ static int bluetooth_read(char *p) len += sprintf(p + len, "status:\t\tnot supported\n"); else { len += sprintf(p + len, "status:\t\t%s\n", - (status)? "enabled" : "disabled"); + (status == RFKILL_STATE_UNBLOCKED) ? + "enabled" : "disabled"); len += sprintf(p + len, "commands:\tenable, disable\n"); } @@ -2727,9 +2839,9 @@ static int bluetooth_write(char *buf) while ((cmd = next_cmd(&buf))) { if (strlencmp(cmd, "enable") == 0) { - bluetooth_set_radiosw(1); + bluetooth_set_radiosw(1, 1); } else if (strlencmp(cmd, "disable") == 0) { - bluetooth_set_radiosw(0); + bluetooth_set_radiosw(0, 1); } else return -EINVAL; } @@ -2755,8 +2867,66 @@ enum { TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */ }; -static int wan_get_radiosw(void); -static int wan_set_radiosw(int radio_on); +static struct rfkill *tpacpi_wan_rfkill; + +static int wan_get_radiosw(void) +{ + int status; + + if (!tp_features.wan) + return -ENODEV; + + /* WLSW overrides WWAN in firmware/hardware, reflect that */ + if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) + return RFKILL_STATE_HARD_BLOCKED; + + if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) + return -EIO; + + return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ? + RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; +} + +static void wan_update_rfk(void) +{ + int status; + + if (!tpacpi_wan_rfkill) + return; + + status = wan_get_radiosw(); + if (status < 0) + return; + rfkill_force_state(tpacpi_wan_rfkill, status); +} + +static int wan_set_radiosw(int radio_on, int update_rfk) +{ + int status; + + if (!tp_features.wan) + return -ENODEV; + + /* WLSW overrides bluetooth in firmware/hardware, but there is no + * reason to risk weird behaviour. */ + if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status + && radio_on) + return -EPERM; + + 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; + + if (update_rfk) + wan_update_rfk(); + + return 0; +} /* sysfs wan enable ---------------------------------------------------- */ static ssize_t wan_enable_show(struct device *dev, @@ -2769,7 +2939,8 @@ static ssize_t wan_enable_show(struct device *dev, if (status < 0) return status; - return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); + return snprintf(buf, PAGE_SIZE, "%d\n", + (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); } static ssize_t wan_enable_store(struct device *dev, @@ -2782,7 +2953,7 @@ static ssize_t wan_enable_store(struct device *dev, if (parse_strtoul(buf, 1, &t)) return -EINVAL; - res = wan_set_radiosw(t); + res = wan_set_radiosw(t, 1); return (res) ? res : count; } @@ -2802,6 +2973,31 @@ static const struct attribute_group wan_attr_group = { .attrs = wan_attributes, }; +static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state) +{ + int wans = wan_get_radiosw(); + + if (wans < 0) + return wans; + + *state = wans; + return 0; +} + +static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state) +{ + return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); +} + +static void wan_exit(void) +{ + if (tpacpi_wan_rfkill) + rfkill_unregister(tpacpi_wan_rfkill); + + sysfs_remove_group(&tpacpi_pdev->dev.kobj, + &wan_attr_group); +} + static int __init wan_init(struct ibm_init_struct *iibm) { int res; @@ -2818,57 +3014,32 @@ static int __init wan_init(struct ibm_init_struct *iibm) 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; - } + if (tp_features.wan && + !(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"); } - 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; + return 1; - if (!tp_features.wan) - return -ENODEV; + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &wan_attr_group); + if (res) + return res; - 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; + res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID, + &tpacpi_wan_rfkill, + RFKILL_TYPE_WWAN, + "tpacpi_wwan_sw", + tpacpi_wan_rfk_set, + tpacpi_wan_rfk_get); + if (res) { + wan_exit(); + return res; + } return 0; } @@ -2883,7 +3054,8 @@ static int wan_read(char *p) len += sprintf(p + len, "status:\t\tnot supported\n"); else { len += sprintf(p + len, "status:\t\t%s\n", - (status)? "enabled" : "disabled"); + (status == RFKILL_STATE_UNBLOCKED) ? + "enabled" : "disabled"); len += sprintf(p + len, "commands:\tenable, disable\n"); } @@ -2899,9 +3071,9 @@ static int wan_write(char *buf) while ((cmd = next_cmd(&buf))) { if (strlencmp(cmd, "enable") == 0) { - wan_set_radiosw(1); + wan_set_radiosw(1, 1); } else if (strlencmp(cmd, "disable") == 0) { - wan_set_radiosw(0); + wan_set_radiosw(0, 1); } else return -EINVAL; } @@ -6168,13 +6340,18 @@ err_out: /* Probing */ -static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) +/* returns 0 - probe ok, or < 0 - probe error. + * Probe ok doesn't mean thinkpad found. + * On error, kfree() cleanup on tp->* is not performed, caller must do it */ +static int __must_check __init get_thinkpad_model_data( + struct thinkpad_id_data *tp) { const struct dmi_device *dev = NULL; char ec_fw_string[18]; + char const *s; if (!tp) - return; + return -EINVAL; memset(tp, 0, sizeof(*tp)); @@ -6183,12 +6360,14 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) else if (dmi_name_in_vendors("LENOVO")) tp->vendor = PCI_VENDOR_ID_LENOVO; else - return; + return 0; - tp->bios_version_str = kstrdup(dmi_get_system_info(DMI_BIOS_VERSION), - GFP_KERNEL); + s = dmi_get_system_info(DMI_BIOS_VERSION); + tp->bios_version_str = kstrdup(s, GFP_KERNEL); + if (s && !tp->bios_version_str) + return -ENOMEM; if (!tp->bios_version_str) - return; + return 0; tp->bios_model = tp->bios_version_str[0] | (tp->bios_version_str[1] << 8); @@ -6207,21 +6386,27 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL); + if (!tp->ec_version_str) + return -ENOMEM; tp->ec_model = ec_fw_string[0] | (ec_fw_string[1] << 8); break; } } - tp->model_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_VERSION), - GFP_KERNEL); - if (tp->model_str && strnicmp(tp->model_str, "ThinkPad", 8) != 0) { - kfree(tp->model_str); - tp->model_str = NULL; + s = dmi_get_system_info(DMI_PRODUCT_VERSION); + if (s && !strnicmp(s, "ThinkPad", 8)) { + tp->model_str = kstrdup(s, GFP_KERNEL); + if (!tp->model_str) + return -ENOMEM; } - tp->nummodel_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_NAME), - GFP_KERNEL); + s = dmi_get_system_info(DMI_PRODUCT_NAME); + tp->nummodel_str = kstrdup(s, GFP_KERNEL); + if (s && !tp->nummodel_str) + return -ENOMEM; + + return 0; } static int __init probe_for_thinkpad(void) @@ -6484,7 +6669,13 @@ static int __init thinkpad_acpi_module_init(void) /* Driver-level probe */ - get_thinkpad_model_data(&thinkpad_id); + ret = get_thinkpad_model_data(&thinkpad_id); + if (ret) { + printk(TPACPI_ERR + "unable to get DMI data: %d\n", ret); + thinkpad_acpi_module_exit(); + return ret; + } ret = probe_for_thinkpad(); if (ret) { thinkpad_acpi_module_exit(); diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index d6b9b486417c..a067fe436301 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -21,13 +21,17 @@ #define RESULT_UNSUP_HOST 2 #define RESULT_UNSUP_CARD 3 -#define BUFFER_SIZE (PAGE_SIZE * 4) +#define BUFFER_ORDER 2 +#define BUFFER_SIZE (PAGE_SIZE << BUFFER_ORDER) struct mmc_test_card { struct mmc_card *card; u8 scratch[BUFFER_SIZE]; u8 *buffer; +#ifdef CONFIG_HIGHMEM + struct page *highmem; +#endif }; /*******************************************************************/ @@ -384,14 +388,16 @@ static int mmc_test_transfer(struct mmc_test_card *test, int ret, i; unsigned long flags; + BUG_ON(blocks * blksz > BUFFER_SIZE); + if (write) { for (i = 0;i < blocks * blksz;i++) test->scratch[i] = i; } else { - memset(test->scratch, 0, BUFFER_SIZE); + memset(test->scratch, 0, blocks * blksz); } local_irq_save(flags); - sg_copy_from_buffer(sg, sg_len, test->scratch, BUFFER_SIZE); + sg_copy_from_buffer(sg, sg_len, test->scratch, blocks * blksz); local_irq_restore(flags); ret = mmc_test_set_blksize(test, blksz); @@ -438,7 +444,7 @@ static int mmc_test_transfer(struct mmc_test_card *test, } } else { local_irq_save(flags); - sg_copy_to_buffer(sg, sg_len, test->scratch, BUFFER_SIZE); + sg_copy_to_buffer(sg, sg_len, test->scratch, blocks * blksz); local_irq_restore(flags); for (i = 0;i < blocks * blksz;i++) { if (test->scratch[i] != (u8)i) @@ -799,6 +805,157 @@ static int mmc_test_multi_xfersize_read(struct mmc_test_card *test) return 0; } +static int mmc_test_bigsg_write(struct mmc_test_card *test) +{ + int ret; + unsigned int size; + struct scatterlist sg; + + if (test->card->host->max_blk_count == 1) + return RESULT_UNSUP_HOST; + + size = PAGE_SIZE * 2; + size = min(size, test->card->host->max_req_size); + size = min(size, test->card->host->max_seg_size); + size = min(size, test->card->host->max_blk_count * 512); + + memset(test->buffer, 0, BUFFER_SIZE); + + if (size < 1024) + return RESULT_UNSUP_HOST; + + sg_init_table(&sg, 1); + sg_init_one(&sg, test->buffer, BUFFER_SIZE); + + ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1); + if (ret) + return ret; + + return 0; +} + +static int mmc_test_bigsg_read(struct mmc_test_card *test) +{ + int ret, i; + unsigned int size; + struct scatterlist sg; + + if (test->card->host->max_blk_count == 1) + return RESULT_UNSUP_HOST; + + size = PAGE_SIZE * 2; + size = min(size, test->card->host->max_req_size); + size = min(size, test->card->host->max_seg_size); + size = min(size, test->card->host->max_blk_count * 512); + + if (size < 1024) + return RESULT_UNSUP_HOST; + + memset(test->buffer, 0xCD, BUFFER_SIZE); + + sg_init_table(&sg, 1); + sg_init_one(&sg, test->buffer, BUFFER_SIZE); + ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0); + if (ret) + return ret; + + /* mmc_test_transfer() doesn't check for read overflows */ + for (i = size;i < BUFFER_SIZE;i++) { + if (test->buffer[i] != 0xCD) + return RESULT_FAIL; + } + + return 0; +} + +#ifdef CONFIG_HIGHMEM + +static int mmc_test_write_high(struct mmc_test_card *test) +{ + int ret; + struct scatterlist sg; + + sg_init_table(&sg, 1); + sg_set_page(&sg, test->highmem, 512, 0); + + ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1); + if (ret) + return ret; + + return 0; +} + +static int mmc_test_read_high(struct mmc_test_card *test) +{ + int ret; + struct scatterlist sg; + + sg_init_table(&sg, 1); + sg_set_page(&sg, test->highmem, 512, 0); + + ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0); + if (ret) + return ret; + + return 0; +} + +static int mmc_test_multi_write_high(struct mmc_test_card *test) +{ + int ret; + unsigned int size; + struct scatterlist sg; + + if (test->card->host->max_blk_count == 1) + return RESULT_UNSUP_HOST; + + size = PAGE_SIZE * 2; + size = min(size, test->card->host->max_req_size); + size = min(size, test->card->host->max_seg_size); + size = min(size, test->card->host->max_blk_count * 512); + + if (size < 1024) + return RESULT_UNSUP_HOST; + + sg_init_table(&sg, 1); + sg_set_page(&sg, test->highmem, size, 0); + + ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1); + if (ret) + return ret; + + return 0; +} + +static int mmc_test_multi_read_high(struct mmc_test_card *test) +{ + int ret; + unsigned int size; + struct scatterlist sg; + + if (test->card->host->max_blk_count == 1) + return RESULT_UNSUP_HOST; + + size = PAGE_SIZE * 2; + size = min(size, test->card->host->max_req_size); + size = min(size, test->card->host->max_seg_size); + size = min(size, test->card->host->max_blk_count * 512); + + if (size < 1024) + return RESULT_UNSUP_HOST; + + sg_init_table(&sg, 1); + sg_set_page(&sg, test->highmem, size, 0); + + ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0); + if (ret) + return ret; + + return 0; +} + +#endif /* CONFIG_HIGHMEM */ + static const struct mmc_test_case mmc_test_cases[] = { { .name = "Basic write (no data verification)", @@ -913,6 +1070,53 @@ static const struct mmc_test_case mmc_test_cases[] = { .name = "Correct xfer_size at read (midway failure)", .run = mmc_test_multi_xfersize_read, }, + + { + .name = "Over-sized SG list write", + .prepare = mmc_test_prepare_write, + .run = mmc_test_bigsg_write, + .cleanup = mmc_test_cleanup, + }, + + { + .name = "Over-sized SG list read", + .prepare = mmc_test_prepare_read, + .run = mmc_test_bigsg_read, + .cleanup = mmc_test_cleanup, + }, + +#ifdef CONFIG_HIGHMEM + + { + .name = "Highmem write", + .prepare = mmc_test_prepare_write, + .run = mmc_test_write_high, + .cleanup = mmc_test_cleanup, + }, + + { + .name = "Highmem read", + .prepare = mmc_test_prepare_read, + .run = mmc_test_read_high, + .cleanup = mmc_test_cleanup, + }, + + { + .name = "Multi-block highmem write", + .prepare = mmc_test_prepare_write, + .run = mmc_test_multi_write_high, + .cleanup = mmc_test_cleanup, + }, + + { + .name = "Multi-block highmem read", + .prepare = mmc_test_prepare_read, + .run = mmc_test_multi_read_high, + .cleanup = mmc_test_cleanup, + }, + +#endif /* CONFIG_HIGHMEM */ + }; static struct mutex mmc_test_lock; @@ -1014,12 +1218,23 @@ static ssize_t mmc_test_store(struct device *dev, test->card = card; test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL); +#ifdef CONFIG_HIGHMEM + test->highmem = alloc_pages(GFP_KERNEL | __GFP_HIGHMEM, BUFFER_ORDER); +#endif + +#ifdef CONFIG_HIGHMEM + if (test->buffer && test->highmem) { +#else if (test->buffer) { +#endif mutex_lock(&mmc_test_lock); mmc_test_run(test, testcase); mutex_unlock(&mmc_test_lock); } +#ifdef CONFIG_HIGHMEM + __free_pages(test->highmem, BUFFER_ORDER); +#endif kfree(test->buffer); kfree(test); @@ -1041,6 +1256,8 @@ static int mmc_test_probe(struct mmc_card *card) if (ret) return ret; + dev_info(&card->dev, "Card claimed for testing.\n"); + return 0; } diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index 7731ddefdc1b..3dee97e7d165 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -148,7 +148,7 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock printk(KERN_WARNING "%s: unable to allocate " "bounce buffer\n", mmc_card_name(card)); } else { - blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_HIGH); + blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_ANY); blk_queue_max_sectors(mq->queue, bouncesz / 512); blk_queue_max_phys_segments(mq->queue, bouncesz / 512); blk_queue_max_hw_segments(mq->queue, bouncesz / 512); @@ -290,55 +290,15 @@ void mmc_queue_resume(struct mmc_queue *mq) } } -static void copy_sg(struct scatterlist *dst, unsigned int dst_len, - struct scatterlist *src, unsigned int src_len) -{ - unsigned int chunk; - char *dst_buf, *src_buf; - unsigned int dst_size, src_size; - - dst_buf = NULL; - src_buf = NULL; - dst_size = 0; - src_size = 0; - - while (src_len) { - BUG_ON(dst_len == 0); - - if (dst_size == 0) { - dst_buf = sg_virt(dst); - dst_size = dst->length; - } - - if (src_size == 0) { - src_buf = sg_virt(src); - src_size = src->length; - } - - chunk = min(dst_size, src_size); - - memcpy(dst_buf, src_buf, chunk); - - dst_buf += chunk; - src_buf += chunk; - dst_size -= chunk; - src_size -= chunk; - - if (dst_size == 0) { - dst++; - dst_len--; - } - - if (src_size == 0) { - src++; - src_len--; - } - } -} - +/* + * Prepare the sg list(s) to be handed of to the host driver + */ unsigned int mmc_queue_map_sg(struct mmc_queue *mq) { unsigned int sg_len; + size_t buflen; + struct scatterlist *sg; + int i; if (!mq->bounce_buf) return blk_rq_map_sg(mq->queue, mq->req, mq->sg); @@ -349,47 +309,52 @@ unsigned int mmc_queue_map_sg(struct mmc_queue *mq) mq->bounce_sg_len = sg_len; - /* - * Shortcut in the event we only get a single entry. - */ - if (sg_len == 1) { - memcpy(mq->sg, mq->bounce_sg, sizeof(struct scatterlist)); - return 1; - } + buflen = 0; + for_each_sg(mq->bounce_sg, sg, sg_len, i) + buflen += sg->length; - sg_init_one(mq->sg, mq->bounce_buf, 0); - - while (sg_len) { - mq->sg[0].length += mq->bounce_sg[sg_len - 1].length; - sg_len--; - } + sg_init_one(mq->sg, mq->bounce_buf, buflen); return 1; } +/* + * If writing, bounce the data to the buffer before the request + * is sent to the host driver + */ void mmc_queue_bounce_pre(struct mmc_queue *mq) { + unsigned long flags; + if (!mq->bounce_buf) return; - if (mq->bounce_sg_len == 1) - return; if (rq_data_dir(mq->req) != WRITE) return; - copy_sg(mq->sg, 1, mq->bounce_sg, mq->bounce_sg_len); + local_irq_save(flags); + sg_copy_to_buffer(mq->bounce_sg, mq->bounce_sg_len, + mq->bounce_buf, mq->sg[0].length); + local_irq_restore(flags); } +/* + * If reading, bounce the data from the buffer after the request + * has been handled by the host driver + */ void mmc_queue_bounce_post(struct mmc_queue *mq) { + unsigned long flags; + if (!mq->bounce_buf) return; - if (mq->bounce_sg_len == 1) - return; if (rq_data_dir(mq->req) != READ) return; - copy_sg(mq->bounce_sg, mq->bounce_sg_len, mq->sg, 1); + local_irq_save(flags); + sg_copy_from_buffer(mq->bounce_sg, mq->bounce_sg_len, + mq->bounce_buf, mq->sg[0].length); + local_irq_restore(flags); } diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 19a1a254a0c5..889e5f898f6f 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -12,3 +12,4 @@ mmc_core-y := core.o bus.o host.o \ sdio.o sdio_ops.o sdio_bus.o \ sdio_cis.o sdio_io.o sdio_irq.o +mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index fd95b18e988b..0d9b2d6f9ebf 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -252,6 +252,10 @@ int mmc_add_card(struct mmc_card *card) if (ret) return ret; +#ifdef CONFIG_DEBUG_FS + mmc_add_card_debugfs(card); +#endif + mmc_card_set_present(card); return 0; @@ -263,6 +267,10 @@ int mmc_add_card(struct mmc_card *card) */ void mmc_remove_card(struct mmc_card *card) { +#ifdef CONFIG_DEBUG_FS + mmc_remove_card_debugfs(card); +#endif + if (mmc_card_present(card)) { if (mmc_host_is_spi(card->host)) { printk(KERN_INFO "%s: SPI card removed\n", diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index cdb332b7dedc..c819effa1032 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -52,5 +52,12 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr); extern int use_spi_crc; +/* Debugfs information for hosts and cards */ +void mmc_add_host_debugfs(struct mmc_host *host); +void mmc_remove_host_debugfs(struct mmc_host *host); + +void mmc_add_card_debugfs(struct mmc_card *card); +void mmc_remove_card_debugfs(struct mmc_card *card); + #endif diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c new file mode 100644 index 000000000000..1237bb4c722b --- /dev/null +++ b/drivers/mmc/core/debugfs.c @@ -0,0 +1,225 @@ +/* + * Debugfs support for hosts and cards + * + * Copyright (C) 2008 Atmel Corporation + * + * 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/debugfs.h> +#include <linux/fs.h> +#include <linux/seq_file.h> +#include <linux/stat.h> + +#include <linux/mmc/card.h> +#include <linux/mmc/host.h> + +#include "core.h" +#include "mmc_ops.h" + +/* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */ +static int mmc_ios_show(struct seq_file *s, void *data) +{ + static const char *vdd_str[] = { + [8] = "2.0", + [9] = "2.1", + [10] = "2.2", + [11] = "2.3", + [12] = "2.4", + [13] = "2.5", + [14] = "2.6", + [15] = "2.7", + [16] = "2.8", + [17] = "2.9", + [18] = "3.0", + [19] = "3.1", + [20] = "3.2", + [21] = "3.3", + [22] = "3.4", + [23] = "3.5", + [24] = "3.6", + }; + struct mmc_host *host = s->private; + struct mmc_ios *ios = &host->ios; + const char *str; + + seq_printf(s, "clock:\t\t%u Hz\n", ios->clock); + seq_printf(s, "vdd:\t\t%u ", ios->vdd); + if ((1 << ios->vdd) & MMC_VDD_165_195) + seq_printf(s, "(1.65 - 1.95 V)\n"); + else if (ios->vdd < (ARRAY_SIZE(vdd_str) - 1) + && vdd_str[ios->vdd] && vdd_str[ios->vdd + 1]) + seq_printf(s, "(%s ~ %s V)\n", vdd_str[ios->vdd], + vdd_str[ios->vdd + 1]); + else + seq_printf(s, "(invalid)\n"); + + switch (ios->bus_mode) { + case MMC_BUSMODE_OPENDRAIN: + str = "open drain"; + break; + case MMC_BUSMODE_PUSHPULL: + str = "push-pull"; + break; + default: + str = "invalid"; + break; + } + seq_printf(s, "bus mode:\t%u (%s)\n", ios->bus_mode, str); + + switch (ios->chip_select) { + case MMC_CS_DONTCARE: + str = "don't care"; + break; + case MMC_CS_HIGH: + str = "active high"; + break; + case MMC_CS_LOW: + str = "active low"; + break; + default: + str = "invalid"; + break; + } + seq_printf(s, "chip select:\t%u (%s)\n", ios->chip_select, str); + + switch (ios->power_mode) { + case MMC_POWER_OFF: + str = "off"; + break; + case MMC_POWER_UP: + str = "up"; + break; + case MMC_POWER_ON: + str = "on"; + break; + default: + str = "invalid"; + break; + } + seq_printf(s, "power mode:\t%u (%s)\n", ios->power_mode, str); + seq_printf(s, "bus width:\t%u (%u bits)\n", + ios->bus_width, 1 << ios->bus_width); + + switch (ios->timing) { + case MMC_TIMING_LEGACY: + str = "legacy"; + break; + case MMC_TIMING_MMC_HS: + str = "mmc high-speed"; + break; + case MMC_TIMING_SD_HS: + str = "sd high-speed"; + break; + default: + str = "invalid"; + break; + } + seq_printf(s, "timing spec:\t%u (%s)\n", ios->timing, str); + + return 0; +} + +static int mmc_ios_open(struct inode *inode, struct file *file) +{ + return single_open(file, mmc_ios_show, inode->i_private); +} + +static const struct file_operations mmc_ios_fops = { + .open = mmc_ios_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void mmc_add_host_debugfs(struct mmc_host *host) +{ + struct dentry *root; + + root = debugfs_create_dir(mmc_hostname(host), NULL); + if (IS_ERR(root)) + /* Don't complain -- debugfs just isn't enabled */ + return; + if (!root) + /* Complain -- debugfs is enabled, but it failed to + * create the directory. */ + goto err_root; + + host->debugfs_root = root; + + if (!debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops)) + goto err_ios; + + return; + +err_ios: + debugfs_remove_recursive(root); + host->debugfs_root = NULL; +err_root: + dev_err(&host->class_dev, "failed to initialize debugfs\n"); +} + +void mmc_remove_host_debugfs(struct mmc_host *host) +{ + debugfs_remove_recursive(host->debugfs_root); +} + +static int mmc_dbg_card_status_get(void *data, u64 *val) +{ + struct mmc_card *card = data; + u32 status; + int ret; + + mmc_claim_host(card->host); + + ret = mmc_send_status(data, &status); + if (!ret) + *val = status; + + mmc_release_host(card->host); + + return ret; +} +DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get, + NULL, "%08llx\n"); + +void mmc_add_card_debugfs(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + struct dentry *root; + + if (!host->debugfs_root) + return; + + root = debugfs_create_dir(mmc_card_id(card), host->debugfs_root); + if (IS_ERR(root)) + /* Don't complain -- debugfs just isn't enabled */ + return; + if (!root) + /* Complain -- debugfs is enabled, but it failed to + * create the directory. */ + goto err; + + card->debugfs_root = root; + + if (!debugfs_create_x32("state", S_IRUSR, root, &card->state)) + goto err; + + if (mmc_card_mmc(card) || mmc_card_sd(card)) + if (!debugfs_create_file("status", S_IRUSR, root, card, + &mmc_dbg_card_status_fops)) + goto err; + + return; + +err: + debugfs_remove_recursive(root); + card->debugfs_root = NULL; + dev_err(&card->dev, "failed to initialize debugfs\n"); +} + +void mmc_remove_card_debugfs(struct mmc_card *card) +{ + debugfs_remove_recursive(card->debugfs_root); +} diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 1d795c5379b5..6da80fd4d974 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -127,6 +127,10 @@ int mmc_add_host(struct mmc_host *host) if (err) return err; +#ifdef CONFIG_DEBUG_FS + mmc_add_host_debugfs(host); +#endif + mmc_start_host(host); return 0; @@ -146,6 +150,10 @@ void mmc_remove_host(struct mmc_host *host) { mmc_stop_host(host); +#ifdef CONFIG_DEBUG_FS + mmc_remove_host_debugfs(host); +#endif + device_del(&host->class_dev); led_trigger_unregister_simple(host->led); diff --git a/drivers/mmc/host/atmel-mci-regs.h b/drivers/mmc/host/atmel-mci-regs.h index a9a5657706c6..26bd80e65031 100644 --- a/drivers/mmc/host/atmel-mci-regs.h +++ b/drivers/mmc/host/atmel-mci-regs.h @@ -82,6 +82,8 @@ # define MCI_OVRE ( 1 << 30) /* RX Overrun Error */ # define MCI_UNRE ( 1 << 31) /* TX Underrun Error */ +#define MCI_REGS_SIZE 0x100 + /* Register access macros */ #define mci_readl(port,reg) \ __raw_readl((port)->regs + MCI_##reg) diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index cce873c5a149..992b4beb757c 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -9,13 +9,18 @@ */ #include <linux/blkdev.h> #include <linux/clk.h> +#include <linux/debugfs.h> #include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/scatterlist.h> +#include <linux/seq_file.h> +#include <linux/stat.h> #include <linux/mmc/host.h> @@ -24,7 +29,6 @@ #include <asm/unaligned.h> #include <asm/arch/board.h> -#include <asm/arch/gpio.h> #include "atmel-mci-regs.h" @@ -88,6 +92,188 @@ struct atmel_mci { #define atmci_clear_pending(host, event) \ clear_bit(event, &host->pending_events) +/* + * The debugfs stuff below is mostly optimized away when + * CONFIG_DEBUG_FS is not set. + */ +static int atmci_req_show(struct seq_file *s, void *v) +{ + struct atmel_mci *host = s->private; + struct mmc_request *mrq = host->mrq; + struct mmc_command *cmd; + struct mmc_command *stop; + struct mmc_data *data; + + /* Make sure we get a consistent snapshot */ + spin_lock_irq(&host->mmc->lock); + + if (mrq) { + cmd = mrq->cmd; + data = mrq->data; + stop = mrq->stop; + + if (cmd) + seq_printf(s, + "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n", + cmd->opcode, cmd->arg, cmd->flags, + cmd->resp[0], cmd->resp[1], cmd->resp[2], + cmd->resp[2], cmd->error); + if (data) + seq_printf(s, "DATA %u / %u * %u flg %x err %d\n", + data->bytes_xfered, data->blocks, + data->blksz, data->flags, data->error); + if (stop) + seq_printf(s, + "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n", + stop->opcode, stop->arg, stop->flags, + stop->resp[0], stop->resp[1], stop->resp[2], + stop->resp[2], stop->error); + } + + spin_unlock_irq(&host->mmc->lock); + + return 0; +} + +static int atmci_req_open(struct inode *inode, struct file *file) +{ + return single_open(file, atmci_req_show, inode->i_private); +} + +static const struct file_operations atmci_req_fops = { + .owner = THIS_MODULE, + .open = atmci_req_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void atmci_show_status_reg(struct seq_file *s, + const char *regname, u32 value) +{ + static const char *sr_bit[] = { + [0] = "CMDRDY", + [1] = "RXRDY", + [2] = "TXRDY", + [3] = "BLKE", + [4] = "DTIP", + [5] = "NOTBUSY", + [8] = "SDIOIRQA", + [9] = "SDIOIRQB", + [16] = "RINDE", + [17] = "RDIRE", + [18] = "RCRCE", + [19] = "RENDE", + [20] = "RTOE", + [21] = "DCRCE", + [22] = "DTOE", + [30] = "OVRE", + [31] = "UNRE", + }; + unsigned int i; + + seq_printf(s, "%s:\t0x%08x", regname, value); + for (i = 0; i < ARRAY_SIZE(sr_bit); i++) { + if (value & (1 << i)) { + if (sr_bit[i]) + seq_printf(s, " %s", sr_bit[i]); + else + seq_puts(s, " UNKNOWN"); + } + } + seq_putc(s, '\n'); +} + +static int atmci_regs_show(struct seq_file *s, void *v) +{ + struct atmel_mci *host = s->private; + u32 *buf; + + buf = kmalloc(MCI_REGS_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* Grab a more or less consistent snapshot */ + spin_lock_irq(&host->mmc->lock); + memcpy_fromio(buf, host->regs, MCI_REGS_SIZE); + spin_unlock_irq(&host->mmc->lock); + + seq_printf(s, "MR:\t0x%08x%s%s CLKDIV=%u\n", + buf[MCI_MR / 4], + buf[MCI_MR / 4] & MCI_MR_RDPROOF ? " RDPROOF" : "", + buf[MCI_MR / 4] & MCI_MR_WRPROOF ? " WRPROOF" : "", + buf[MCI_MR / 4] & 0xff); + seq_printf(s, "DTOR:\t0x%08x\n", buf[MCI_DTOR / 4]); + seq_printf(s, "SDCR:\t0x%08x\n", buf[MCI_SDCR / 4]); + seq_printf(s, "ARGR:\t0x%08x\n", buf[MCI_ARGR / 4]); + seq_printf(s, "BLKR:\t0x%08x BCNT=%u BLKLEN=%u\n", + buf[MCI_BLKR / 4], + buf[MCI_BLKR / 4] & 0xffff, + (buf[MCI_BLKR / 4] >> 16) & 0xffff); + + /* Don't read RSPR and RDR; it will consume the data there */ + + atmci_show_status_reg(s, "SR", buf[MCI_SR / 4]); + atmci_show_status_reg(s, "IMR", buf[MCI_IMR / 4]); + + return 0; +} + +static int atmci_regs_open(struct inode *inode, struct file *file) +{ + return single_open(file, atmci_regs_show, inode->i_private); +} + +static const struct file_operations atmci_regs_fops = { + .owner = THIS_MODULE, + .open = atmci_regs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void atmci_init_debugfs(struct atmel_mci *host) +{ + struct mmc_host *mmc; + struct dentry *root; + struct dentry *node; + struct resource *res; + + mmc = host->mmc; + root = mmc->debugfs_root; + if (!root) + return; + + node = debugfs_create_file("regs", S_IRUSR, root, host, + &atmci_regs_fops); + if (IS_ERR(node)) + return; + if (!node) + goto err; + + res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0); + node->d_inode->i_size = res->end - res->start + 1; + + node = debugfs_create_file("req", S_IRUSR, root, host, &atmci_req_fops); + if (!node) + goto err; + + node = debugfs_create_x32("pending_events", S_IRUSR, root, + (u32 *)&host->pending_events); + if (!node) + goto err; + + node = debugfs_create_x32("completed_events", S_IRUSR, root, + (u32 *)&host->completed_events); + if (!node) + goto err; + + return; + +err: + dev_err(&host->pdev->dev, + "failed to initialize debugfs for controller\n"); +} static void atmci_enable(struct atmel_mci *host) { @@ -388,7 +574,7 @@ static int atmci_get_ro(struct mmc_host *mmc) int read_only = 0; struct atmel_mci *host = mmc_priv(mmc); - if (host->wp_pin >= 0) { + if (gpio_is_valid(host->wp_pin)) { read_only = gpio_get_value(host->wp_pin); dev_dbg(&mmc->class_dev, "card is %s\n", read_only ? "read-only" : "read-write"); @@ -450,7 +636,7 @@ static void atmci_detect_change(unsigned long data) * been freed. */ smp_rmb(); - if (host->detect_pin < 0) + if (!gpio_is_valid(host->detect_pin)) return; enable_irq(gpio_to_irq(host->detect_pin)); @@ -865,7 +1051,7 @@ static int __init atmci_probe(struct platform_device *pdev) /* Assume card is present if we don't have a detect pin */ host->present = 1; - if (host->detect_pin >= 0) { + if (gpio_is_valid(host->detect_pin)) { if (gpio_request(host->detect_pin, "mmc_detect")) { dev_dbg(&mmc->class_dev, "no detect pin available\n"); host->detect_pin = -1; @@ -873,7 +1059,7 @@ static int __init atmci_probe(struct platform_device *pdev) host->present = !gpio_get_value(host->detect_pin); } } - if (host->wp_pin >= 0) { + if (gpio_is_valid(host->wp_pin)) { if (gpio_request(host->wp_pin, "mmc_wp")) { dev_dbg(&mmc->class_dev, "no WP pin available\n"); host->wp_pin = -1; @@ -884,7 +1070,7 @@ static int __init atmci_probe(struct platform_device *pdev) mmc_add_host(mmc); - if (host->detect_pin >= 0) { + if (gpio_is_valid(host->detect_pin)) { setup_timer(&host->detect_timer, atmci_detect_change, (unsigned long)host); @@ -905,6 +1091,8 @@ static int __init atmci_probe(struct platform_device *pdev) "Atmel MCI controller at 0x%08lx irq %d\n", host->mapbase, irq); + atmci_init_debugfs(host); + return 0; err_request_irq: @@ -923,7 +1111,9 @@ static int __exit atmci_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); if (host) { - if (host->detect_pin >= 0) { + /* Debugfs stuff is cleaned up by mmc core */ + + if (gpio_is_valid(host->detect_pin)) { int pin = host->detect_pin; /* Make sure the timer doesn't enable the interrupt */ @@ -943,7 +1133,7 @@ static int __exit atmci_remove(struct platform_device *pdev) mci_readl(host, SR); clk_disable(host->mck); - if (host->wp_pin >= 0) + if (gpio_is_valid(host->wp_pin)) gpio_free(host->wp_pin); free_irq(platform_get_irq(pdev, 0), host->mmc); diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index 3f15eb204895..99b20917cc0f 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -1043,7 +1043,7 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev) goto out6; } - platform_set_drvdata(pdev, mmc); + platform_set_drvdata(pdev, host); printk(KERN_INFO DRIVER_NAME ": MMC Controller %d set up at %8.8X" " (mode=%s)\n", pdev->id, host->iobase, @@ -1087,13 +1087,10 @@ out0: static int __devexit au1xmmc_remove(struct platform_device *pdev) { - struct mmc_host *mmc = platform_get_drvdata(pdev); - struct au1xmmc_host *host; - - if (mmc) { - host = mmc_priv(mmc); + struct au1xmmc_host *host = platform_get_drvdata(pdev); - mmc_remove_host(mmc); + if (host) { + mmc_remove_host(host->mmc); #ifdef CONFIG_LEDS_CLASS if (host->platdata && host->platdata->led) @@ -1101,8 +1098,8 @@ static int __devexit au1xmmc_remove(struct platform_device *pdev) #endif if (host->platdata && host->platdata->cd_setup && - !(mmc->caps & MMC_CAP_NEEDS_POLL)) - host->platdata->cd_setup(mmc, 0); + !(host->mmc->caps & MMC_CAP_NEEDS_POLL)) + host->platdata->cd_setup(host->mmc, 0); au_writel(0, HOST_ENABLE(host)); au_writel(0, HOST_CONFIG(host)); @@ -1122,16 +1119,49 @@ static int __devexit au1xmmc_remove(struct platform_device *pdev) release_resource(host->ioarea); kfree(host->ioarea); - mmc_free_host(mmc); + mmc_free_host(host->mmc); + platform_set_drvdata(pdev, NULL); } return 0; } +#ifdef CONFIG_PM +static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct au1xmmc_host *host = platform_get_drvdata(pdev); + int ret; + + ret = mmc_suspend_host(host->mmc, state); + if (ret) + return ret; + + au_writel(0, HOST_CONFIG2(host)); + au_writel(0, HOST_CONFIG(host)); + au_writel(0xffffffff, HOST_STATUS(host)); + au_writel(0, HOST_ENABLE(host)); + au_sync(); + + return 0; +} + +static int au1xmmc_resume(struct platform_device *pdev) +{ + struct au1xmmc_host *host = platform_get_drvdata(pdev); + + au1xmmc_reset_controller(host); + + return mmc_resume_host(host->mmc); +} +#else +#define au1xmmc_suspend NULL +#define au1xmmc_resume NULL +#endif + static struct platform_driver au1xmmc_driver = { .probe = au1xmmc_probe, .remove = au1xmmc_remove, - .suspend = NULL, - .resume = NULL, + .suspend = au1xmmc_suspend, + .resume = au1xmmc_resume, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, diff --git a/drivers/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c index 5e880c0f1349..f61406da65d2 100644 --- a/drivers/mmc/host/imxmmc.c +++ b/drivers/mmc/host/imxmmc.c @@ -26,12 +26,6 @@ * */ -#ifdef CONFIG_MMC_DEBUG -#define DEBUG -#else -#undef DEBUG -#endif - #include <linux/module.h> #include <linux/init.h> #include <linux/ioport.h> @@ -907,31 +901,12 @@ static const struct mmc_host_ops imxmci_ops = { .get_ro = imxmci_get_ro, }; -static struct resource *platform_device_resource(struct platform_device *dev, unsigned int mask, int nr) -{ - int i; - - for (i = 0; i < dev->num_resources; i++) - if (dev->resource[i].flags == mask && nr-- == 0) - return &dev->resource[i]; - return NULL; -} - -static int platform_device_irq(struct platform_device *dev, int nr) -{ - int i; - - for (i = 0; i < dev->num_resources; i++) - if (dev->resource[i].flags == IORESOURCE_IRQ && nr-- == 0) - return dev->resource[i].start; - return NO_IRQ; -} - static void imxmci_check_status(unsigned long data) { struct imxmci_host *host = (struct imxmci_host *)data; - if( host->pdata->card_present(mmc_dev(host->mmc)) != host->present ) { + if (host->pdata && host->pdata->card_present && + host->pdata->card_present(mmc_dev(host->mmc)) != host->present) { host->present ^= 1; dev_info(mmc_dev(host->mmc), "card %s\n", host->present ? "inserted" : "removed"); @@ -962,13 +937,12 @@ static int imxmci_probe(struct platform_device *pdev) printk(KERN_INFO "i.MX mmc driver\n"); - r = platform_device_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_device_irq(pdev, 0); - if (!r || irq == NO_IRQ) + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!r || irq < 0) return -ENXIO; - r = request_mem_region(r->start, 0x100, "IMXMCI"); - if (!r) + if (!request_mem_region(r->start, 0x100, pdev->name)) return -EBUSY; mmc = mmc_alloc_host(sizeof(struct imxmci_host), &pdev->dev); @@ -995,6 +969,8 @@ static int imxmci_probe(struct platform_device *pdev) host->mmc = mmc; host->dma_allocated = 0; host->pdata = pdev->dev.platform_data; + if (!host->pdata) + dev_warn(&pdev->dev, "No platform data provided!\n"); spin_lock_init(&host->lock); host->res = r; @@ -1047,7 +1023,11 @@ static int imxmci_probe(struct platform_device *pdev) if (ret) goto out; - host->present = host->pdata->card_present(mmc_dev(mmc)); + if (host->pdata && host->pdata->card_present) + host->present = host->pdata->card_present(mmc_dev(mmc)); + else /* if there is no way to detect assume that card is present */ + host->present = 1; + init_timer(&host->timer); host->timer.data = (unsigned long)host; host->timer.function = imxmci_check_status; @@ -1073,7 +1053,7 @@ out: } if (mmc) mmc_free_host(mmc); - release_resource(r); + release_mem_region(r->start, 0x100); return ret; } @@ -1102,7 +1082,7 @@ static int imxmci_remove(struct platform_device *pdev) clk_disable(host->clk); clk_put(host->clk); - release_resource(host->res); + release_mem_region(host->res->start, 0x100); mmc_free_host(mmc); } diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 41cc63360e43..7503b81374e0 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1076,6 +1076,7 @@ static void mmc_spi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) */ if (canpower && ios->power_mode == MMC_POWER_OFF) { int mres; + u8 nullbyte = 0; host->spi->mode &= ~(SPI_CPOL|SPI_CPHA); mres = spi_setup(host->spi); @@ -1083,7 +1084,7 @@ static void mmc_spi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) dev_dbg(&host->spi->dev, "switch to SPI mode 0 failed\n"); - if (spi_w8r8(host->spi, 0x00) < 0) + if (spi_write(host->spi, &nullbyte, 1) < 0) dev_dbg(&host->spi->dev, "put spi signals to low failed\n"); diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index d39f59738866..a8e18fe53077 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -177,7 +177,7 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) if (dalgn) DALGN |= (1 << host->dma); else - DALGN &= (1 << host->dma); + DALGN &= ~(1 << host->dma); DDADR(host->dma) = host->sg_dma; DCSR(host->dma) = DCSR_RUN; } diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 6a1e4994b724..be550c26da68 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -1331,21 +1331,30 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440) return ret; } +static void s3cmci_shutdown(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct s3cmci_host *host = mmc_priv(mmc); + + if (host->irq_cd >= 0) + free_irq(host->irq_cd, host); + + mmc_remove_host(mmc); + clk_disable(host->clk); +} + static int __devexit s3cmci_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); struct s3cmci_host *host = mmc_priv(mmc); - mmc_remove_host(mmc); + s3cmci_shutdown(pdev); - clk_disable(host->clk); clk_put(host->clk); tasklet_disable(&host->pio_tasklet); s3c2410_dma_free(S3CMCI_DMA, &s3cmci_dma_client); - if (host->irq_cd >= 0) - free_irq(host->irq_cd, host); free_irq(host->irq, host); iounmap(host->base); @@ -1355,17 +1364,17 @@ static int __devexit s3cmci_remove(struct platform_device *pdev) return 0; } -static int __devinit s3cmci_probe_2410(struct platform_device *dev) +static int __devinit s3cmci_2410_probe(struct platform_device *dev) { return s3cmci_probe(dev, 0); } -static int __devinit s3cmci_probe_2412(struct platform_device *dev) +static int __devinit s3cmci_2412_probe(struct platform_device *dev) { return s3cmci_probe(dev, 1); } -static int __devinit s3cmci_probe_2440(struct platform_device *dev) +static int __devinit s3cmci_2440_probe(struct platform_device *dev) { return s3cmci_probe(dev, 1); } @@ -1392,29 +1401,32 @@ static int s3cmci_resume(struct platform_device *dev) #endif /* CONFIG_PM */ -static struct platform_driver s3cmci_driver_2410 = { +static struct platform_driver s3cmci_2410_driver = { .driver.name = "s3c2410-sdi", .driver.owner = THIS_MODULE, - .probe = s3cmci_probe_2410, + .probe = s3cmci_2410_probe, .remove = __devexit_p(s3cmci_remove), + .shutdown = s3cmci_shutdown, .suspend = s3cmci_suspend, .resume = s3cmci_resume, }; -static struct platform_driver s3cmci_driver_2412 = { +static struct platform_driver s3cmci_2412_driver = { .driver.name = "s3c2412-sdi", .driver.owner = THIS_MODULE, - .probe = s3cmci_probe_2412, + .probe = s3cmci_2412_probe, .remove = __devexit_p(s3cmci_remove), + .shutdown = s3cmci_shutdown, .suspend = s3cmci_suspend, .resume = s3cmci_resume, }; -static struct platform_driver s3cmci_driver_2440 = { +static struct platform_driver s3cmci_2440_driver = { .driver.name = "s3c2440-sdi", .driver.owner = THIS_MODULE, - .probe = s3cmci_probe_2440, + .probe = s3cmci_2440_probe, .remove = __devexit_p(s3cmci_remove), + .shutdown = s3cmci_shutdown, .suspend = s3cmci_suspend, .resume = s3cmci_resume, }; @@ -1422,17 +1434,17 @@ static struct platform_driver s3cmci_driver_2440 = { static int __init s3cmci_init(void) { - platform_driver_register(&s3cmci_driver_2410); - platform_driver_register(&s3cmci_driver_2412); - platform_driver_register(&s3cmci_driver_2440); + platform_driver_register(&s3cmci_2410_driver); + platform_driver_register(&s3cmci_2412_driver); + platform_driver_register(&s3cmci_2440_driver); return 0; } static void __exit s3cmci_exit(void) { - platform_driver_unregister(&s3cmci_driver_2410); - platform_driver_unregister(&s3cmci_driver_2412); - platform_driver_unregister(&s3cmci_driver_2440); + platform_driver_unregister(&s3cmci_2410_driver); + platform_driver_unregister(&s3cmci_2412_driver); + platform_driver_unregister(&s3cmci_2440_driver); } module_init(s3cmci_init); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 17701c3da733..5f95e10229b5 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -173,119 +173,95 @@ static void sdhci_led_control(struct led_classdev *led, * * \*****************************************************************************/ -static inline char* sdhci_sg_to_buffer(struct sdhci_host* host) -{ - return sg_virt(host->cur_sg); -} - -static inline int sdhci_next_sg(struct sdhci_host* host) -{ - /* - * Skip to next SG entry. - */ - host->cur_sg++; - host->num_sg--; - - /* - * Any entries left? - */ - if (host->num_sg > 0) { - host->offset = 0; - host->remain = host->cur_sg->length; - } - - return host->num_sg; -} - static void sdhci_read_block_pio(struct sdhci_host *host) { - int blksize, chunk_remain; - u32 data; - char *buffer; - int size; + unsigned long flags; + size_t blksize, len, chunk; + u32 scratch; + u8 *buf; DBG("PIO reading\n"); blksize = host->data->blksz; - chunk_remain = 0; - data = 0; + chunk = 0; - buffer = sdhci_sg_to_buffer(host) + host->offset; + local_irq_save(flags); while (blksize) { - if (chunk_remain == 0) { - data = readl(host->ioaddr + SDHCI_BUFFER); - chunk_remain = min(blksize, 4); - } + if (!sg_miter_next(&host->sg_miter)) + BUG(); - size = min(host->remain, chunk_remain); + len = min(host->sg_miter.length, blksize); - chunk_remain -= size; - blksize -= size; - host->offset += size; - host->remain -= size; + blksize -= len; + host->sg_miter.consumed = len; - while (size) { - *buffer = data & 0xFF; - buffer++; - data >>= 8; - size--; - } + buf = host->sg_miter.addr; - if (host->remain == 0) { - if (sdhci_next_sg(host) == 0) { - BUG_ON(blksize != 0); - return; + while (len) { + if (chunk == 0) { + scratch = readl(host->ioaddr + SDHCI_BUFFER); + chunk = 4; } - buffer = sdhci_sg_to_buffer(host); + + *buf = scratch & 0xFF; + + buf++; + scratch >>= 8; + chunk--; + len--; } } + + sg_miter_stop(&host->sg_miter); + + local_irq_restore(flags); } static void sdhci_write_block_pio(struct sdhci_host *host) { - int blksize, chunk_remain; - u32 data; - char *buffer; - int bytes, size; + unsigned long flags; + size_t blksize, len, chunk; + u32 scratch; + u8 *buf; DBG("PIO writing\n"); blksize = host->data->blksz; - chunk_remain = 4; - data = 0; + chunk = 0; + scratch = 0; - bytes = 0; - buffer = sdhci_sg_to_buffer(host) + host->offset; + local_irq_save(flags); while (blksize) { - size = min(host->remain, chunk_remain); - - chunk_remain -= size; - blksize -= size; - host->offset += size; - host->remain -= size; - - while (size) { - data >>= 8; - data |= (u32)*buffer << 24; - buffer++; - size--; - } + if (!sg_miter_next(&host->sg_miter)) + BUG(); - if (chunk_remain == 0) { - writel(data, host->ioaddr + SDHCI_BUFFER); - chunk_remain = min(blksize, 4); - } + len = min(host->sg_miter.length, blksize); + + blksize -= len; + host->sg_miter.consumed = len; + + buf = host->sg_miter.addr; - if (host->remain == 0) { - if (sdhci_next_sg(host) == 0) { - BUG_ON(blksize != 0); - return; + while (len) { + scratch |= (u32)*buf << (chunk * 8); + + buf++; + chunk++; + len--; + + if ((chunk == 4) || ((len == 0) && (blksize == 0))) { + writel(scratch, host->ioaddr + SDHCI_BUFFER); + chunk = 0; + scratch = 0; } - buffer = sdhci_sg_to_buffer(host); } } + + sg_miter_stop(&host->sg_miter); + + local_irq_restore(flags); } static void sdhci_transfer_pio(struct sdhci_host *host) @@ -294,7 +270,7 @@ static void sdhci_transfer_pio(struct sdhci_host *host) BUG_ON(!host->data); - if (host->num_sg == 0) + if (host->blocks == 0) return; if (host->data->flags & MMC_DATA_READ) @@ -308,7 +284,8 @@ static void sdhci_transfer_pio(struct sdhci_host *host) else sdhci_write_block_pio(host); - if (host->num_sg == 0) + host->blocks--; + if (host->blocks == 0) break; } @@ -360,7 +337,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, host->align_addr = dma_map_single(mmc_dev(host->mmc), host->align_buffer, 128 * 4, direction); - if (dma_mapping_error(host->align_addr)) + if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr)) goto fail; BUG_ON(host->align_addr & 0x3); @@ -389,6 +366,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, if (offset) { if (data->flags & MMC_DATA_WRITE) { buffer = sdhci_kmap_atomic(sg, &flags); + WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3)); memcpy(align, buffer, offset); sdhci_kunmap_atomic(buffer, &flags); } @@ -461,7 +439,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, host->adma_addr = dma_map_single(mmc_dev(host->mmc), host->adma_desc, (128 * 2 + 1) * 4, DMA_TO_DEVICE); - if (dma_mapping_error(host->align_addr)) + if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr)) goto unmap_entries; BUG_ON(host->adma_addr & 0x3); @@ -510,6 +488,7 @@ static void sdhci_adma_table_post(struct sdhci_host *host, size = 4 - (sg_dma_address(sg) & 0x3); buffer = sdhci_kmap_atomic(sg, &flags); + WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3)); memcpy(buffer, align, size); sdhci_kunmap_atomic(buffer, &flags); @@ -687,7 +666,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) WARN_ON(1); host->flags &= ~SDHCI_USE_DMA; } else { - WARN_ON(count != 1); + WARN_ON(sg_cnt != 1); writel(sg_dma_address(data->sg), host->ioaddr + SDHCI_DMA_ADDRESS); } @@ -711,11 +690,9 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) } if (!(host->flags & SDHCI_REQ_USE_DMA)) { - host->cur_sg = data->sg; - host->num_sg = data->sg_len; - - host->offset = 0; - host->remain = host->cur_sg->length; + sg_miter_start(&host->sg_miter, + data->sg, data->sg_len, SG_MITER_ATOMIC); + host->blocks = data->blocks; } /* We do not handle DMA boundaries, so set it to max (512 KiB) */ @@ -1581,9 +1558,15 @@ int sdhci_add_host(struct sdhci_host *host) } } - /* XXX: Hack to get MMC layer to avoid highmem */ - if (!(host->flags & SDHCI_USE_DMA)) - mmc_dev(host->mmc)->dma_mask = NULL; + /* + * If we use DMA, then it's up to the caller to set the DMA + * mask, but PIO does not need the hw shim so we set a new + * mask here in that case. + */ + if (!(host->flags & SDHCI_USE_DMA)) { + host->dma_mask = DMA_BIT_MASK(64); + mmc_dev(host->mmc)->dma_mask = &host->dma_mask; + } host->max_clk = (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 5bb355281765..e354faee5df0 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -9,6 +9,8 @@ * your option) any later version. */ +#include <linux/scatterlist.h> + /* * Controller registers */ @@ -212,6 +214,7 @@ struct sdhci_host { /* Internal data */ struct mmc_host *mmc; /* MMC structure */ + u64 dma_mask; /* custom DMA mask */ #ifdef CONFIG_LEDS_CLASS struct led_classdev led; /* LED control */ @@ -238,10 +241,8 @@ struct sdhci_host { struct mmc_data *data; /* Current data request */ unsigned int data_early:1; /* Data finished before cmd */ - struct scatterlist *cur_sg; /* We're working on this */ - int num_sg; /* Entries left */ - int offset; /* Offset into current sg */ - int remain; /* Bytes left in current */ + struct sg_mapping_iter sg_miter; /* SG state for PIO */ + unsigned int blocks; /* remaining PIO blocks */ int sg_count; /* Mapped sg entries */ diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index eed06d068fd1..14f11f8b9e5f 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -1,5 +1,3 @@ -# $Id: Kconfig,v 1.11 2005/11/07 11:14:19 gleixner Exp $ - menuconfig MTD tristate "Memory Technology Device (MTD) support" depends on HAS_IOMEM diff --git a/drivers/mtd/afs.c b/drivers/mtd/afs.c index 52d51eb91c16..d072ca5be689 100644 --- a/drivers/mtd/afs.c +++ b/drivers/mtd/afs.c @@ -21,8 +21,6 @@ This is access code for flashes using ARM's flash partitioning standards. - $Id: afs.c,v 1.15 2005/11/07 11:14:19 gleixner Exp $ - ======================================================================*/ #include <linux/module.h> diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index fcd1aeccdf93..5f1b472137a0 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -4,8 +4,6 @@ * * (C) 2000 Red Hat. GPL'd * - * $Id: cfi_cmdset_0001.c,v 1.186 2005/11/23 22:07:52 nico Exp $ - * * * 10/10/2000 Nicolas Pitre <nico@cam.org> * - completely revamped method functions so they are aware and @@ -50,6 +48,8 @@ #define I82802AC 0x00ac #define MANUFACTURER_ST 0x0020 #define M50LPW080 0x002F +#define M50FLW080A 0x0080 +#define M50FLW080B 0x0081 #define AT49BV640D 0x02de static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); @@ -204,7 +204,7 @@ static void fixup_intel_strataflash(struct mtd_info *mtd, void* param) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - struct cfi_pri_amdstd *extp = cfi->cmdset_priv; + struct cfi_pri_intelext *extp = cfi->cmdset_priv; printk(KERN_WARNING "cfi_cmdset_0001: Suspend " "erase on write disabled.\n"); @@ -301,6 +301,8 @@ static struct cfi_fixup jedec_fixup_table[] = { { MANUFACTURER_INTEL, I82802AB, fixup_use_fwh_lock, NULL, }, { MANUFACTURER_INTEL, I82802AC, fixup_use_fwh_lock, NULL, }, { MANUFACTURER_ST, M50LPW080, fixup_use_fwh_lock, NULL, }, + { MANUFACTURER_ST, M50FLW080A, fixup_use_fwh_lock, NULL, }, + { MANUFACTURER_ST, M50FLW080B, fixup_use_fwh_lock, NULL, }, { 0, 0, NULL, NULL } }; static struct cfi_fixup fixup_table[] = { @@ -1147,7 +1149,7 @@ static int inval_cache_and_wait_for_operation( struct cfi_private *cfi = map->fldrv_priv; map_word status, status_OK = CMD(0x80); int chip_state = chip->state; - unsigned int timeo, sleep_time; + unsigned int timeo, sleep_time, reset_timeo; spin_unlock(chip->mutex); if (inval_len) @@ -1158,6 +1160,7 @@ static int inval_cache_and_wait_for_operation( timeo = chip_op_time * 8; if (!timeo) timeo = 500000; + reset_timeo = timeo; sleep_time = chip_op_time / 2; for (;;) { @@ -1199,6 +1202,12 @@ static int inval_cache_and_wait_for_operation( remove_wait_queue(&chip->wq, &wait); spin_lock(chip->mutex); } + if (chip->erase_suspended || chip->write_suspended) { + /* Suspend has occured while sleep: reset timeout */ + timeo = reset_timeo; + chip->erase_suspended = 0; + chip->write_suspended = 0; + } } /* Done and happy. */ diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index f7fcc6389533..a972cc6be436 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -16,9 +16,6 @@ * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com * * This code is GPL - * - * $Id: cfi_cmdset_0002.c,v 1.122 2005/11/07 11:14:22 gleixner Exp $ - * */ #include <linux/module.h> diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index 1b720cc571f3..d4714dd9f7ab 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -4,8 +4,6 @@ * * (C) 2000 Red Hat. GPL'd * - * $Id: cfi_cmdset_0020.c,v 1.22 2005/11/07 11:14:22 gleixner Exp $ - * * 10/10/2000 Nicolas Pitre <nico@cam.org> * - completely revamped method functions so they are aware and * independent of the flash geometry (buswidth, interleave, etc.) diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index a4463a91ce31..c418e92e1d92 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -1,7 +1,6 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: cfi_probe.c,v 1.86 2005/11/29 14:48:31 gleixner Exp $ */ #include <linux/module.h> diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index 72e0022a47bf..0ee457018016 100644 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -6,9 +6,6 @@ * Copyright (C) 2003 STMicroelectronics Limited * * This code is covered by the GPL. - * - * $Id: cfi_util.c,v 1.10 2005/11/07 11:14:23 gleixner Exp $ - * */ #include <linux/module.h> diff --git a/drivers/mtd/chips/chipreg.c b/drivers/mtd/chips/chipreg.c index 2174c97549f0..c85760968227 100644 --- a/drivers/mtd/chips/chipreg.c +++ b/drivers/mtd/chips/chipreg.c @@ -1,6 +1,4 @@ /* - * $Id: chipreg.c,v 1.17 2004/11/16 18:29:00 dwmw2 Exp $ - * * Registration for chip drivers * */ diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index d338b8c92780..f061885b2812 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -2,7 +2,6 @@ * Routines common to all CFI-type probes. * (C) 2001-2003 Red Hat, Inc. * GPL'd - * $Id: gen_probe.c,v 1.24 2005/11/07 11:14:23 gleixner Exp $ */ #include <linux/kernel.h> @@ -71,8 +70,8 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi interleave and device type, etc. */ if (!genprobe_new_chip(map, cp, &cfi)) { /* The probe didn't like it */ - printk(KERN_DEBUG "%s: Found no %s device at location zero\n", - cp->name, map->name); + pr_debug("%s: Found no %s device at location zero\n", + cp->name, map->name); return NULL; } diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index aa07575eb288..dbba5abf0db8 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -1,7 +1,6 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: jedec_probe.c,v 1.66 2005/11/07 11:14:23 gleixner Exp $ See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5) for the standard this probe goes back to. @@ -26,6 +25,7 @@ /* Manufacturers */ #define MANUFACTURER_AMD 0x0001 #define MANUFACTURER_ATMEL 0x001f +#define MANUFACTURER_EON 0x001c #define MANUFACTURER_FUJITSU 0x0004 #define MANUFACTURER_HYUNDAI 0x00AD #define MANUFACTURER_INTEL 0x0089 @@ -37,6 +37,7 @@ #define MANUFACTURER_ST 0x0020 #define MANUFACTURER_TOSHIBA 0x0098 #define MANUFACTURER_WINBOND 0x00da +#define CONTINUATION_CODE 0x007f /* AMD */ @@ -58,6 +59,8 @@ #define AM29LV040B 0x004F #define AM29F032B 0x0041 #define AM29F002T 0x00B0 +#define AM29SL800DB 0x226B +#define AM29SL800DT 0x22EA /* Atmel */ #define AT49BV512 0x0003 @@ -67,6 +70,10 @@ #define AT49BV32X 0x00C8 #define AT49BV32XT 0x00C9 +/* Eon */ +#define EN29SL800BB 0x226B +#define EN29SL800BT 0x22EA + /* Fujitsu */ #define MBM29F040C 0x00A4 #define MBM29F800BA 0x2258 @@ -141,6 +148,8 @@ #define M50FW080 0x002D #define M50FW016 0x002E #define M50LPW080 0x002F +#define M50FLW080A 0x0080 +#define M50FLW080B 0x0081 /* SST */ #define SST29EE020 0x0010 @@ -191,6 +200,7 @@ enum uaddr { MTD_UADDR_0x0555_0x0AAA, MTD_UADDR_0x5555_0x2AAA, MTD_UADDR_0x0AAA_0x0555, + MTD_UADDR_0xAAAA_0x5555, MTD_UADDR_DONT_CARE, /* Requires an arbitrary address */ MTD_UADDR_UNNECESSARY, /* Does not require any address */ }; @@ -238,6 +248,11 @@ static const struct unlock_addr unlock_addrs[] = { .addr2 = 0x0555 }, + [MTD_UADDR_0xAAAA_0x5555] = { + .addr1 = 0xaaaa, + .addr2 = 0x5555 + }, + [MTD_UADDR_DONT_CARE] = { .addr1 = 0x0000, /* Doesn't matter which address */ .addr2 = 0x0000 /* is used - must be last entry */ @@ -522,6 +537,36 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1), } }, { + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29SL800DT, + .name = "AMD AM29SL800DT", + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, + .regions = { + ERASEINFO(0x10000,15), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1), + } + }, { + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29SL800DB, + .name = "AMD AM29SL800DB", + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, + .regions = { + ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,15), + } + }, { .mfr_id = MANUFACTURER_ATMEL, .dev_id = AT49BV512, .name = "Atmel AT49BV512", @@ -599,6 +644,36 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000,8) } }, { + .mfr_id = MANUFACTURER_EON, + .dev_id = EN29SL800BT, + .name = "Eon EN29SL800BT", + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, + .regions = { + ERASEINFO(0x10000,15), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1), + } + }, { + .mfr_id = MANUFACTURER_EON, + .dev_id = EN29SL800BB, + .name = "Eon EN29SL800BB", + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, + .regions = { + ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,15), + } + }, { .mfr_id = MANUFACTURER_FUJITSU, .dev_id = MBM29F040C, .name = "Fujitsu MBM29F040C", @@ -1392,8 +1467,8 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_SST, /* should be CFI */ .dev_id = SST39LF160, .name = "SST 39LF160", - .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, - .uaddr = MTD_UADDR_0x5555_0x2AAA, /* ???? */ + .devtypes = CFI_DEVICETYPE_X16, + .uaddr = MTD_UADDR_0xAAAA_0x5555, .dev_size = SIZE_2MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 2, @@ -1405,8 +1480,8 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_SST, /* should be CFI */ .dev_id = SST39VF1601, .name = "SST 39VF1601", - .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, - .uaddr = MTD_UADDR_0x5555_0x2AAA, /* ???? */ + .devtypes = CFI_DEVICETYPE_X16, + .uaddr = MTD_UADDR_0xAAAA_0x5555, .dev_size = SIZE_2MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 2, @@ -1590,6 +1665,36 @@ static const struct amd_flash_info jedec_table[] = { .nr_regions = 1, .regions = { ERASEINFO(0x10000,16), + }, + }, { + .mfr_id = MANUFACTURER_ST, + .dev_id = M50FLW080A, + .name = "ST M50FLW080A", + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_INTEL_EXT, + .nr_regions = 4, + .regions = { + ERASEINFO(0x1000,16), + ERASEINFO(0x10000,13), + ERASEINFO(0x1000,16), + ERASEINFO(0x1000,16), + } + }, { + .mfr_id = MANUFACTURER_ST, + .dev_id = M50FLW080B, + .name = "ST M50FLW080B", + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_INTEL_EXT, + .nr_regions = 4, + .regions = { + ERASEINFO(0x1000,16), + ERASEINFO(0x1000,16), + ERASEINFO(0x10000,13), + ERASEINFO(0x1000,16), } }, { .mfr_id = MANUFACTURER_TOSHIBA, @@ -1696,9 +1801,21 @@ static inline u32 jedec_read_mfr(struct map_info *map, uint32_t base, { map_word result; unsigned long mask; - u32 ofs = cfi_build_cmd_addr(0, cfi_interleave(cfi), cfi->device_type); - mask = (1 << (cfi->device_type * 8)) -1; - result = map_read(map, base + ofs); + int bank = 0; + + /* According to JEDEC "Standard Manufacturer's Identification Code" + * (http://www.jedec.org/download/search/jep106W.pdf) + * several first banks can contain 0x7f instead of actual ID + */ + do { + uint32_t ofs = cfi_build_cmd_addr(0 + (bank << 8), + cfi_interleave(cfi), + cfi->device_type); + mask = (1 << (cfi->device_type * 8)) - 1; + result = map_read(map, base + ofs); + bank++; + } while ((result.x[0] & mask) == CONTINUATION_CODE); + return result.x[0] & mask; } diff --git a/drivers/mtd/chips/map_absent.c b/drivers/mtd/chips/map_absent.c index fc478c0f93f5..494d30d0631a 100644 --- a/drivers/mtd/chips/map_absent.c +++ b/drivers/mtd/chips/map_absent.c @@ -1,7 +1,6 @@ /* * Common code to handle absent "placeholder" devices * Copyright 2001 Resilience Corporation <ebrower@resilience.com> - * $Id: map_absent.c,v 1.6 2005/11/07 11:14:23 gleixner Exp $ * * This map driver is used to allocate "placeholder" MTD * devices on systems that have socketed/removable media. diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c index 5cb6d5263661..072dd8abf33a 100644 --- a/drivers/mtd/chips/map_ram.c +++ b/drivers/mtd/chips/map_ram.c @@ -1,7 +1,6 @@ /* * Common code to handle map devices which are simple RAM * (C) 2000 Red Hat. GPL'd. - * $Id: map_ram.c,v 1.22 2005/01/05 18:05:12 dwmw2 Exp $ */ #include <linux/module.h> diff --git a/drivers/mtd/chips/map_rom.c b/drivers/mtd/chips/map_rom.c index cb27f855074c..821d0ed6bae3 100644 --- a/drivers/mtd/chips/map_rom.c +++ b/drivers/mtd/chips/map_rom.c @@ -1,7 +1,6 @@ /* * Common code to handle map devices which are simple ROM * (C) 2000 Red Hat. GPL'd. - * $Id: map_rom.c,v 1.23 2005/01/05 18:05:12 dwmw2 Exp $ */ #include <linux/module.h> diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index e472a0e9de9d..71bc07f149b7 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -1,6 +1,4 @@ /* - * $Id: cmdlinepart.c,v 1.19 2005/11/07 11:14:19 gleixner Exp $ - * * Read flash partition table from command line * * Copyright 2002 SYSGO Real-Time Solutions GmbH @@ -308,7 +306,7 @@ static int parse_cmdline_partitions(struct mtd_info *master, unsigned long offset; int i; struct cmdline_mtd_partition *part; - char *mtd_id = master->name; + const char *mtd_id = master->name; /* parse command line */ if (!cmdline_parsed) diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 35ed1103dbb2..9c613f06623c 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -1,5 +1,4 @@ # drivers/mtd/maps/Kconfig -# $Id: Kconfig,v 1.18 2005/11/07 11:14:24 gleixner Exp $ menu "Self-contained MTD device drivers" depends on MTD!=n diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 0f788d5c4bf8..0993d5cf3923 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -1,7 +1,6 @@ # # linux/drivers/devices/Makefile # -# $Id: Makefile.common,v 1.7 2004/12/22 17:51:15 joern Exp $ obj-$(CONFIG_MTD_DOC2000) += doc2000.o obj-$(CONFIG_MTD_DOC2001) += doc2001.o diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 519d942e7940..91fbba767635 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -1,6 +1,4 @@ /* - * $Id: block2mtd.c,v 1.30 2005/11/29 14:48:32 gleixner Exp $ - * * block2mtd.c - create an mtd from a block device * * Copyright (C) 2001,2002 Simon Evans <spse@secret.org.uk> @@ -20,9 +18,6 @@ #include <linux/mutex.h> #include <linux/mount.h> -#define VERSION "$Revision: 1.30 $" - - #define ERROR(fmt, args...) printk(KERN_ERR "block2mtd: " fmt "\n" , ## args) #define INFO(fmt, args...) printk(KERN_INFO "block2mtd: " fmt "\n" , ## args) @@ -241,6 +236,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) { struct block_device *bdev; struct block2mtd_dev *dev; + char *name; if (!devname) return NULL; @@ -279,12 +275,13 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) /* Setup the MTD structure */ /* make the name contain the block device in */ - dev->mtd.name = kmalloc(sizeof("block2mtd: ") + strlen(devname), + name = kmalloc(sizeof("block2mtd: ") + strlen(devname) + 1, GFP_KERNEL); - if (!dev->mtd.name) + if (!name) goto devinit_err; - sprintf(dev->mtd.name, "block2mtd: %s", devname); + sprintf(name, "block2mtd: %s", devname); + dev->mtd.name = name; dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; dev->mtd.erasesize = erase_size; @@ -451,7 +448,6 @@ MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=<dev>[,<erasesize>]\""); static int __init block2mtd_init(void) { int ret = 0; - INFO("version " VERSION); #ifndef MODULE if (strlen(block2mtd_paramline)) diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index 846989f292e3..50de839c77a9 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -3,8 +3,6 @@ * Linux driver for Disk-On-Chip 2000 and Millennium * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> - * - * $Id: doc2000.c,v 1.67 2005/11/07 11:14:24 gleixner Exp $ */ #include <linux/kernel.h> diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c index 6413efc045e0..e32c568c1145 100644 --- a/drivers/mtd/devices/doc2001.c +++ b/drivers/mtd/devices/doc2001.c @@ -3,8 +3,6 @@ * Linux driver for Disk-On-Chip Millennium * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> - * - * $Id: doc2001.c,v 1.49 2005/11/07 11:14:24 gleixner Exp $ */ #include <linux/kernel.h> diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c index 83be3461658f..d853f891b586 100644 --- a/drivers/mtd/devices/doc2001plus.c +++ b/drivers/mtd/devices/doc2001plus.c @@ -6,8 +6,6 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> * - * $Id: doc2001plus.c,v 1.14 2005/11/07 11:14:24 gleixner Exp $ - * * Released under GPL */ diff --git a/drivers/mtd/devices/docecc.c b/drivers/mtd/devices/docecc.c index fd8a8daba3a8..874e51b110a2 100644 --- a/drivers/mtd/devices/docecc.c +++ b/drivers/mtd/devices/docecc.c @@ -7,8 +7,6 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: docecc.c,v 1.7 2005/11/07 11:14:25 gleixner Exp $ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/drivers/mtd/devices/docprobe.c b/drivers/mtd/devices/docprobe.c index d8cc94ec4e50..6e62922942b1 100644 --- a/drivers/mtd/devices/docprobe.c +++ b/drivers/mtd/devices/docprobe.c @@ -4,9 +4,6 @@ /* (C) 1999 Machine Vision Holdings, Inc. */ /* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> */ -/* $Id: docprobe.c,v 1.46 2005/11/07 11:14:25 gleixner Exp $ */ - - /* DOC_PASSIVE_PROBE: In order to ensure that the BIOS checksum is correct at boot time, and @@ -79,8 +76,6 @@ static unsigned long __initdata doc_locations[] = { 0xe0000, 0xe2000, 0xe4000, 0xe6000, 0xe8000, 0xea000, 0xec000, 0xee000, #endif /* CONFIG_MTD_DOCPROBE_HIGH */ -#elif defined(__PPC__) - 0xe4000000, #else #warning Unknown architecture for DiskOnChip. No default probe locations defined #endif diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index 1d324e5c412d..f4bda4cee495 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c @@ -2,8 +2,6 @@ /* * MTD driver for the 28F160F3 Flash Memory (non-CFI) on LART. * - * $Id: lart.c,v 1.9 2005/11/07 11:14:25 gleixner Exp $ - * * Author: Abraham vd Merwe <abraham@2d3d.co.za> * * Copyright (c) 2001, 2d3D, Inc. diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index b402269301f6..b35c3333e210 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -33,6 +33,7 @@ /* Flash opcodes. */ #define OPCODE_WREN 0x06 /* Write enable */ #define OPCODE_RDSR 0x05 /* Read status register */ +#define OPCODE_WRSR 0x01 /* Write status register 1 byte */ #define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ #define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ #define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ @@ -112,6 +113,17 @@ static int read_sr(struct m25p *flash) return val; } +/* + * Write status register 1 byte + * Returns negative if error occurred. + */ +static int write_sr(struct m25p *flash, u8 val) +{ + flash->command[0] = OPCODE_WRSR; + flash->command[1] = val; + + return spi_write(flash->spi, flash->command, 2); +} /* * Set write enable latch with Write Enable command. @@ -589,6 +601,16 @@ static int __devinit m25p_probe(struct spi_device *spi) mutex_init(&flash->lock); dev_set_drvdata(&spi->dev, flash); + /* + * Atmel serial flash tend to power up + * with the software protection bits set + */ + + if (info->jedec_id >> 16 == 0x1f) { + write_enable(flash); + write_sr(flash, 0); + } + if (data && data->name) flash->mtd.name = data->name; else diff --git a/drivers/mtd/devices/ms02-nv.c b/drivers/mtd/devices/ms02-nv.c index 9cff119a2024..6a9a24a80a6d 100644 --- a/drivers/mtd/devices/ms02-nv.c +++ b/drivers/mtd/devices/ms02-nv.c @@ -5,8 +5,6 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. - * - * $Id: ms02-nv.c,v 1.11 2005/11/14 13:41:47 macro Exp $ */ #include <linux/init.h> diff --git a/drivers/mtd/devices/ms02-nv.h b/drivers/mtd/devices/ms02-nv.h index 8a6eef7cfee3..04deafd3a771 100644 --- a/drivers/mtd/devices/ms02-nv.h +++ b/drivers/mtd/devices/ms02-nv.h @@ -9,8 +9,6 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. - * - * $Id: ms02-nv.h,v 1.3 2003/08/19 09:25:36 dwmw2 Exp $ */ #include <linux/ioport.h> diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index b35e4813a3a5..54e36bfc2c3b 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -82,7 +82,7 @@ struct dataflash { - u8 command[4]; + uint8_t command[4]; char name[24]; unsigned partitioned:1; @@ -150,7 +150,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) struct spi_transfer x = { .tx_dma = 0, }; struct spi_message msg; unsigned blocksize = priv->page_size << 3; - u8 *command; + uint8_t *command; DEBUG(MTD_DEBUG_LEVEL2, "%s: erase addr=0x%x len 0x%x\n", spi->dev.bus_id, @@ -182,8 +182,8 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) pageaddr = pageaddr << priv->page_offset; command[0] = do_block ? OP_ERASE_BLOCK : OP_ERASE_PAGE; - command[1] = (u8)(pageaddr >> 16); - command[2] = (u8)(pageaddr >> 8); + command[1] = (uint8_t)(pageaddr >> 16); + command[2] = (uint8_t)(pageaddr >> 8); command[3] = 0; DEBUG(MTD_DEBUG_LEVEL3, "ERASE %s: (%x) %x %x %x [%i]\n", @@ -234,7 +234,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len, struct spi_transfer x[2] = { { .tx_dma = 0, }, }; struct spi_message msg; unsigned int addr; - u8 *command; + uint8_t *command; int status; DEBUG(MTD_DEBUG_LEVEL2, "%s: read 0x%x..0x%x\n", @@ -274,9 +274,9 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len, * fewer "don't care" bytes. Both buffers stay unchanged. */ command[0] = OP_READ_CONTINUOUS; - command[1] = (u8)(addr >> 16); - command[2] = (u8)(addr >> 8); - command[3] = (u8)(addr >> 0); + command[1] = (uint8_t)(addr >> 16); + command[2] = (uint8_t)(addr >> 8); + command[3] = (uint8_t)(addr >> 0); /* plus 4 "don't care" bytes */ status = spi_sync(priv->spi, &msg); @@ -311,7 +311,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, size_t remaining = len; u_char *writebuf = (u_char *) buf; int status = -EINVAL; - u8 *command; + uint8_t *command; DEBUG(MTD_DEBUG_LEVEL2, "%s: write 0x%x..0x%x\n", spi->dev.bus_id, (unsigned)to, (unsigned)(to + len)); @@ -487,7 +487,9 @@ add_dataflash(struct spi_device *spi, char *name, device->write = dataflash_write; device->priv = priv; - dev_info(&spi->dev, "%s (%d KBytes)\n", name, device->size/1024); + dev_info(&spi->dev, "%s (%d KBytes) pagesize %d bytes, " + "erasesize %d bytes\n", name, device->size/1024, + pagesize, pagesize * 8); /* 8 pages = 1 block */ dev_set_drvdata(&spi->dev, priv); if (mtd_has_partitions()) { @@ -521,7 +523,7 @@ add_dataflash(struct spi_device *spi, char *name, * * Device Density ID code #Pages PageSize Offset * AT45DB011B 1Mbit (128K) xx0011xx (0x0c) 512 264 9 - * AT45DB021B 2Mbit (256K) xx0101xx (0x14) 1025 264 9 + * AT45DB021B 2Mbit (256K) xx0101xx (0x14) 1024 264 9 * AT45DB041B 4Mbit (512K) xx0111xx (0x1c) 2048 264 9 * AT45DB081B 8Mbit (1M) xx1001xx (0x24) 4096 264 9 * AT45DB0161B 16Mbit (2M) xx1011xx (0x2c) 4096 528 10 @@ -529,9 +531,114 @@ add_dataflash(struct spi_device *spi, char *name, * AT45DB0642 64Mbit (8M) xx111xxx (0x3c) 8192 1056 11 * AT45DB1282 128Mbit (16M) xx0100xx (0x10) 16384 1056 11 */ + +struct flash_info { + char *name; + + /* JEDEC id zero means "no ID" (most older chips); otherwise it has + * a high byte of zero plus three data bytes: the manufacturer id, + * then a two byte device id. + */ + uint32_t jedec_id; + + /* The size listed here is what works with OPCODE_SE, which isn't + * necessarily called a "sector" by the vendor. + */ + unsigned nr_pages; + uint16_t pagesize; + uint16_t pageoffset; + + uint16_t flags; +#define SUP_POW2PS 0x02 +#define IS_POW2PS 0x01 +}; + +static struct flash_info __devinitdata dataflash_data [] = { + + { "at45db011d", 0x1f2200, 512, 264, 9, SUP_POW2PS}, + { "at45db011d", 0x1f2200, 512, 256, 8, SUP_POW2PS | IS_POW2PS}, + + { "at45db021d", 0x1f2300, 1024, 264, 9, SUP_POW2PS}, + { "at45db021d", 0x1f2300, 1024, 256, 8, SUP_POW2PS | IS_POW2PS}, + + { "at45db041d", 0x1f2400, 2048, 264, 9, SUP_POW2PS}, + { "at45db041d", 0x1f2400, 2048, 256, 8, SUP_POW2PS | IS_POW2PS}, + + { "at45db081d", 0x1f2500, 4096, 264, 9, SUP_POW2PS}, + { "at45db081d", 0x1f2500, 4096, 256, 8, SUP_POW2PS | IS_POW2PS}, + + { "at45db161d", 0x1f2600, 4096, 528, 10, SUP_POW2PS}, + { "at45db161d", 0x1f2600, 4096, 512, 9, SUP_POW2PS | IS_POW2PS}, + + { "at45db321c", 0x1f2700, 8192, 528, 10, }, + + { "at45db321d", 0x1f2701, 8192, 528, 10, SUP_POW2PS}, + { "at45db321d", 0x1f2701, 8192, 512, 9, SUP_POW2PS | IS_POW2PS}, + + { "at45db641d", 0x1f2800, 8192, 1056, 11, SUP_POW2PS}, + { "at45db641d", 0x1f2800, 8192, 1024, 10, SUP_POW2PS | IS_POW2PS}, +}; + +static struct flash_info *__devinit jedec_probe(struct spi_device *spi) +{ + int tmp; + uint8_t code = OP_READ_ID; + uint8_t id[3]; + uint32_t jedec; + struct flash_info *info; + int status; + + + /* JEDEC also defines an optional "extended device information" + * string for after vendor-specific data, after the three bytes + * we use here. Supporting some chips might require using it. + */ + tmp = spi_write_then_read(spi, &code, 1, id, 3); + if (tmp < 0) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n", + spi->dev.bus_id, tmp); + return NULL; + } + jedec = id[0]; + jedec = jedec << 8; + jedec |= id[1]; + jedec = jedec << 8; + jedec |= id[2]; + + for (tmp = 0, info = dataflash_data; + tmp < ARRAY_SIZE(dataflash_data); + tmp++, info++) { + if (info->jedec_id == jedec) { + if (info->flags & SUP_POW2PS) { + status = dataflash_status(spi); + if (status & 0x1) + /* return power of 2 pagesize */ + return ++info; + else + return info; + } + } + } + return NULL; +} + static int __devinit dataflash_probe(struct spi_device *spi) { int status; + struct flash_info *info; + + /* + * Try to detect dataflash by JEDEC ID. + * If it succeeds we know we have either a C or D part. + * D will support power of 2 pagesize option. + */ + + info = jedec_probe(spi); + + if (info != NULL) + return add_dataflash(spi, info->name, info->nr_pages, + info->pagesize, info->pageoffset); + status = dataflash_status(spi); if (status <= 0 || status == 0xff) { @@ -551,16 +658,16 @@ static int __devinit dataflash_probe(struct spi_device *spi) status = add_dataflash(spi, "AT45DB011B", 512, 264, 9); break; case 0x14: /* 0 1 0 1 x x */ - status = add_dataflash(spi, "AT45DB021B", 1025, 264, 9); + status = add_dataflash(spi, "AT45DB021B", 1024, 264, 9); break; case 0x1c: /* 0 1 1 1 x x */ - status = add_dataflash(spi, "AT45DB041x", 2048, 264, 9); + status = add_dataflash(spi, "AT45DB041B", 2048, 264, 9); break; case 0x24: /* 1 0 0 1 x x */ status = add_dataflash(spi, "AT45DB081B", 4096, 264, 9); break; case 0x2c: /* 1 0 1 1 x x */ - status = add_dataflash(spi, "AT45DB161x", 4096, 528, 10); + status = add_dataflash(spi, "AT45DB161B", 4096, 528, 10); break; case 0x34: /* 1 1 0 1 x x */ status = add_dataflash(spi, "AT45DB321x", 8192, 528, 10); diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index 0399be178620..3aaca88847d3 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -1,6 +1,5 @@ /* * mtdram - a test mtd device - * $Id: mtdram.c,v 1.37 2005/04/21 03:42:11 joern Exp $ * Author: Alexander Larsson <alex@cendio.se> * * Copyright (c) 1999 Alexander Larsson <alex@cendio.se> diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index c7987b1c5e01..088fbb7595b5 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -1,6 +1,4 @@ /** - * $Id: phram.c,v 1.16 2005/11/07 11:14:25 gleixner Exp $ - * * Copyright (c) ???? Jochen Schäuble <psionic@psionic.de> * Copyright (c) 2003-2004 Joern Engel <joern@wh.fh-wedel.de> * diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index bc9981749064..d38bca64bb15 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -1,6 +1,4 @@ /* - * $Id: pmc551.c,v 1.32 2005/11/07 11:14:25 gleixner Exp $ - * * PMC551 PCI Mezzanine Ram Device * * Author: diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index cb86db746f28..a425d09f35a0 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -1,7 +1,5 @@ /*====================================================================== - $Id: slram.c,v 1.36 2005/11/07 11:14:25 gleixner Exp $ - This driver provides a method to access memory not used by the kernel itself (i.e. if the kernel commandline mem=xxx is used). To actually use slram at least mtdblock or mtdchar is required (for block or diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index 5c29872184e6..f34f20c78911 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -1,5 +1,4 @@ /* This version ported to the Linux-MTD system by dwmw2@infradead.org - * $Id: ftl.c,v 1.59 2005/11/29 14:48:31 gleixner Exp $ * * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br> * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups @@ -1078,8 +1077,6 @@ static struct mtd_blktrans_ops ftl_tr = { static int init_ftl(void) { - DEBUG(0, "$Id: ftl.c,v 1.59 2005/11/29 14:48:31 gleixner Exp $\n"); - return register_mtd_blktrans(&ftl_tr); } diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index b0e396504e67..c4f9d3378b24 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -7,8 +7,6 @@ * (c) 1999 Machine Vision Holdings, Inc. * Author: David Woodhouse <dwmw2@infradead.org> * - * $Id: inftlcore.c,v 1.19 2005/11/07 11:14:20 gleixner Exp $ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -953,9 +951,6 @@ static struct mtd_blktrans_ops inftl_tr = { static int __init init_inftl(void) { - printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.19 $, " - "inftlmount.c %s\n", inftlmountrev); - return register_mtd_blktrans(&inftl_tr); } diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index c551d2f0779c..9113628ed1ef 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -8,8 +8,6 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: inftlmount.c,v 1.18 2005/11/07 11:14:20 gleixner Exp $ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -39,8 +37,6 @@ #include <linux/mtd/inftl.h> #include <linux/mtd/compatmac.h> -char inftlmountrev[]="$Revision: 1.18 $"; - /* * find_boot_record: Find the INFTL Media Header and its Spare copy which * contains the various device information of the INFTL partition and diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index d2fbc2964523..df8e00bba07b 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -1,5 +1,4 @@ # drivers/mtd/maps/Kconfig -# $Id: Kconfig,v 1.61 2005/11/07 11:14:26 gleixner Exp $ menu "Mapping drivers for chip access" depends on MTD!=n @@ -510,6 +509,17 @@ config MTD_PCMCIA_ANONYMOUS If unsure, say N. +config MTD_BFIN_ASYNC + tristate "Blackfin BF533-STAMP Flash Chip Support" + depends on BFIN533_STAMP && MTD_CFI + select MTD_PARTITIONS + default y + help + Map driver which allows for simultaneous utilization of + ethernet and CFI parallel flash. + + If compiled as a module, it will be called bfin-async-flash. + config MTD_UCLINUX tristate "Generic uClinux RAM/ROM filesystem support" depends on MTD_PARTITIONS && !MMU @@ -539,24 +549,6 @@ config MTD_DMV182 help Map driver for Dy-4 SVME/DMV-182 board. -config MTD_BAST - tristate "Map driver for Simtec BAST (EB2410ITX) or Thorcom VR1000" - depends on ARCH_BAST || MACH_VR1000 - select MTD_PARTITIONS - select MTD_MAP_BANK_WIDTH_16 - select MTD_JEDECPROBE - help - Map driver for NOR flash on the Simtec BAST (EB2410ITX), or the - Thorcom VR1000 - - Note, this driver *cannot* over-ride the WP link on the - board, or currently detect the state of the link. - -config MTD_BAST_MAXSIZE - int "Maximum size for BAST flash area (MiB)" - depends on MTD_BAST - default "4" - config MTD_SHARP_SL tristate "ROM mapped on Sharp SL Series" depends on ARCH_PXA diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index c6ce8673dab2..6cda6df973e5 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -1,7 +1,6 @@ # # linux/drivers/maps/Makefile # -# $Id: Makefile.common,v 1.34 2005/11/07 11:14:26 gleixner Exp $ ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y) obj-$(CONFIG_MTD) += map_funcs.o @@ -10,7 +9,6 @@ endif # Chip mappings obj-$(CONFIG_MTD_CDB89712) += cdb89712.o obj-$(CONFIG_MTD_ARM_INTEGRATOR)+= integrator-flash.o -obj-$(CONFIG_MTD_BAST) += bast-flash.o obj-$(CONFIG_MTD_CFI_FLAGADM) += cfi_flagadm.o obj-$(CONFIG_MTD_DC21285) += dc21285.o obj-$(CONFIG_MTD_DILNETPC) += dilnetpc.o @@ -66,3 +64,4 @@ obj-$(CONFIG_MTD_SHARP_SL) += sharpsl-flash.o obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o +obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index 728aed6ad722..948b86f35ef4 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c @@ -2,7 +2,6 @@ * amd76xrom.c * * Normal mappings of chips in physical memory - * $Id: amd76xrom.c,v 1.21 2005/11/07 11:14:26 gleixner Exp $ */ #include <linux/module.h> diff --git a/drivers/mtd/maps/autcpu12-nvram.c b/drivers/mtd/maps/autcpu12-nvram.c index 7ed3424dd959..cf32267263df 100644 --- a/drivers/mtd/maps/autcpu12-nvram.c +++ b/drivers/mtd/maps/autcpu12-nvram.c @@ -2,8 +2,6 @@ * NV-RAM memory access on autcpu12 * (C) 2002 Thomas Gleixner (gleixner@autronix.de) * - * $Id: autcpu12-nvram.c,v 1.9 2005/11/07 11:14:26 gleixner Exp $ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/drivers/mtd/maps/bast-flash.c b/drivers/mtd/maps/bast-flash.c deleted file mode 100644 index 1f492062f8ca..000000000000 --- a/drivers/mtd/maps/bast-flash.c +++ /dev/null @@ -1,226 +0,0 @@ -/* linux/drivers/mtd/maps/bast-flash.c - * - * Copyright (c) 2004-2005 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> - * - * Simtec Bast (EB2410ITX) NOR MTD Mapping driver - * - * Changelog: - * 20-Sep-2004 BJD Initial version - * 17-Jan-2005 BJD Add whole device if no partitions found - * - * $Id: bast-flash.c,v 1.5 2005/11/07 11:14:26 gleixner Exp $ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 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/device.h> -#include <linux/slab.h> -#include <linux/platform_device.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> -#include <linux/mtd/partitions.h> - -#include <asm/io.h> -#include <asm/mach/flash.h> - -#include <asm/arch/map.h> -#include <asm/arch/bast-map.h> -#include <asm/arch/bast-cpld.h> - -#ifdef CONFIG_MTD_BAST_MAXSIZE -#define AREA_MAXSIZE (CONFIG_MTD_BAST_MAXSIZE * SZ_1M) -#else -#define AREA_MAXSIZE (32 * SZ_1M) -#endif - -#define PFX "bast-flash: " - -struct bast_flash_info { - struct mtd_info *mtd; - struct map_info map; - struct mtd_partition *partitions; - struct resource *area; -}; - -static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; - -static void bast_flash_setrw(int to) -{ - unsigned int val; - unsigned long flags; - - local_irq_save(flags); - val = __raw_readb(BAST_VA_CTRL3); - - if (to) - val |= BAST_CPLD_CTRL3_ROMWEN; - else - val &= ~BAST_CPLD_CTRL3_ROMWEN; - - pr_debug("new cpld ctrl3=%02x\n", val); - - __raw_writeb(val, BAST_VA_CTRL3); - local_irq_restore(flags); -} - -static int bast_flash_remove(struct platform_device *pdev) -{ - struct bast_flash_info *info = platform_get_drvdata(pdev); - - platform_set_drvdata(pdev, NULL); - - if (info == NULL) - return 0; - - if (info->map.virt != NULL) - iounmap(info->map.virt); - - if (info->mtd) { - del_mtd_partitions(info->mtd); - map_destroy(info->mtd); - } - - kfree(info->partitions); - - if (info->area) { - release_resource(info->area); - kfree(info->area); - } - - kfree(info); - - return 0; -} - -static int bast_flash_probe(struct platform_device *pdev) -{ - struct bast_flash_info *info; - struct resource *res; - int err = 0; - - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (info == NULL) { - printk(KERN_ERR PFX "no memory for flash info\n"); - err = -ENOMEM; - goto exit_error; - } - - memzero(info, sizeof(*info)); - platform_set_drvdata(pdev, info); - - res = pdev->resource; /* assume that the flash has one resource */ - - info->map.phys = res->start; - info->map.size = res->end - res->start + 1; - info->map.name = pdev->dev.bus_id; - info->map.bankwidth = 2; - - if (info->map.size > AREA_MAXSIZE) - info->map.size = AREA_MAXSIZE; - - pr_debug("%s: area %08lx, size %ld\n", __func__, - info->map.phys, info->map.size); - - info->area = request_mem_region(res->start, info->map.size, - pdev->name); - if (info->area == NULL) { - printk(KERN_ERR PFX "cannot reserve flash memory region\n"); - err = -ENOENT; - goto exit_error; - } - - info->map.virt = ioremap(res->start, info->map.size); - pr_debug("%s: virt at %08x\n", __func__, (int)info->map.virt); - - if (info->map.virt == 0) { - printk(KERN_ERR PFX "failed to ioremap() region\n"); - err = -EIO; - goto exit_error; - } - - simple_map_init(&info->map); - - /* enable the write to the flash area */ - - bast_flash_setrw(1); - - /* probe for the device(s) */ - - info->mtd = do_map_probe("jedec_probe", &info->map); - if (info->mtd == NULL) - info->mtd = do_map_probe("cfi_probe", &info->map); - - if (info->mtd == NULL) { - printk(KERN_ERR PFX "map_probe() failed\n"); - err = -ENXIO; - goto exit_error; - } - - /* mark ourselves as the owner */ - info->mtd->owner = THIS_MODULE; - - err = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0); - if (err > 0) { - err = add_mtd_partitions(info->mtd, info->partitions, err); - if (err) - printk(KERN_ERR PFX "cannot add/parse partitions\n"); - } else { - err = add_mtd_device(info->mtd); - } - - if (err == 0) - return 0; - - /* fall through to exit error */ - - exit_error: - bast_flash_remove(pdev); - return err; -} - -static struct platform_driver bast_flash_driver = { - .probe = bast_flash_probe, - .remove = bast_flash_remove, - .driver = { - .name = "bast-nor", - .owner = THIS_MODULE, - }, -}; - -static int __init bast_flash_init(void) -{ - printk("BAST NOR-Flash Driver, (c) 2004 Simtec Electronics\n"); - return platform_driver_register(&bast_flash_driver); -} - -static void __exit bast_flash_exit(void) -{ - platform_driver_unregister(&bast_flash_driver); -} - -module_init(bast_flash_init); -module_exit(bast_flash_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); -MODULE_DESCRIPTION("BAST MTD Map driver"); -MODULE_ALIAS("platform:bast-nor"); diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c new file mode 100644 index 000000000000..6fec86aaed7e --- /dev/null +++ b/drivers/mtd/maps/bfin-async-flash.c @@ -0,0 +1,219 @@ +/* + * drivers/mtd/maps/bfin-async-flash.c + * + * Handle the case where flash memory and ethernet mac/phy are + * mapped onto the same async bank. The BF533-STAMP does this + * for example. All board-specific configuration goes in your + * board resources file. + * + * Copyright 2000 Nicolas Pitre <nico@cam.org> + * Copyright 2005-2008 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> +#include <linux/platform_device.h> +#include <linux/types.h> + +#include <asm/blackfin.h> +#include <linux/gpio.h> +#include <linux/io.h> +#include <asm/unaligned.h> + +#define pr_devinit(fmt, args...) ({ static const __devinitconst char __fmt[] = fmt; printk(__fmt, ## args); }) + +#define DRIVER_NAME "bfin-async-flash" + +struct async_state { + struct mtd_info *mtd; + struct map_info map; + int enet_flash_pin; + uint32_t flash_ambctl0, flash_ambctl1; + uint32_t save_ambctl0, save_ambctl1; + unsigned long irq_flags; +}; + +static void switch_to_flash(struct async_state *state) +{ + local_irq_save(state->irq_flags); + + gpio_set_value(state->enet_flash_pin, 0); + + state->save_ambctl0 = bfin_read_EBIU_AMBCTL0(); + state->save_ambctl1 = bfin_read_EBIU_AMBCTL1(); + bfin_write_EBIU_AMBCTL0(state->flash_ambctl0); + bfin_write_EBIU_AMBCTL1(state->flash_ambctl1); + SSYNC(); +} + +static void switch_back(struct async_state *state) +{ + bfin_write_EBIU_AMBCTL0(state->save_ambctl0); + bfin_write_EBIU_AMBCTL1(state->save_ambctl1); + SSYNC(); + + gpio_set_value(state->enet_flash_pin, 1); + + local_irq_restore(state->irq_flags); +} + +static map_word bfin_read(struct map_info *map, unsigned long ofs) +{ + struct async_state *state = (struct async_state *)map->map_priv_1; + uint16_t word; + map_word test; + + switch_to_flash(state); + + word = readw(map->virt + ofs); + + switch_back(state); + + test.x[0] = word; + return test; +} + +static void bfin_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + struct async_state *state = (struct async_state *)map->map_priv_1; + + switch_to_flash(state); + + memcpy(to, map->virt + from, len); + + switch_back(state); +} + +static void bfin_write(struct map_info *map, map_word d1, unsigned long ofs) +{ + struct async_state *state = (struct async_state *)map->map_priv_1; + uint16_t d; + + d = d1.x[0]; + + switch_to_flash(state); + + writew(d, map->virt + ofs); + SSYNC(); + + switch_back(state); +} + +static void bfin_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + struct async_state *state = (struct async_state *)map->map_priv_1; + + switch_to_flash(state); + + memcpy(map->virt + to, from, len); + SSYNC(); + + switch_back(state); +} + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; +#endif + +static int __devinit bfin_flash_probe(struct platform_device *pdev) +{ + int ret; + struct physmap_flash_data *pdata = pdev->dev.platform_data; + struct resource *memory = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *flash_ambctl = platform_get_resource(pdev, IORESOURCE_MEM, 1); + struct async_state *state; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + state->map.name = DRIVER_NAME; + state->map.read = bfin_read; + state->map.copy_from = bfin_copy_from; + state->map.write = bfin_write; + state->map.copy_to = bfin_copy_to; + state->map.bankwidth = pdata->width; + state->map.size = memory->end - memory->start + 1; + state->map.virt = (void __iomem *)memory->start; + state->map.phys = memory->start; + state->map.map_priv_1 = (unsigned long)state; + state->enet_flash_pin = platform_get_irq(pdev, 0); + state->flash_ambctl0 = flash_ambctl->start; + state->flash_ambctl1 = flash_ambctl->end; + + if (gpio_request(state->enet_flash_pin, DRIVER_NAME)) { + pr_devinit(KERN_ERR DRIVER_NAME ": Failed to request gpio %d\n", state->enet_flash_pin); + return -EBUSY; + } + gpio_direction_output(state->enet_flash_pin, 1); + + pr_devinit(KERN_NOTICE DRIVER_NAME ": probing %d-bit flash bus\n", state->map.bankwidth * 8); + state->mtd = do_map_probe(memory->name, &state->map); + if (!state->mtd) + return -ENXIO; + +#ifdef CONFIG_MTD_PARTITIONS + ret = parse_mtd_partitions(state->mtd, part_probe_types, &pdata->parts, 0); + if (ret > 0) { + pr_devinit(KERN_NOTICE DRIVER_NAME ": Using commandline partition definition\n"); + add_mtd_partitions(state->mtd, pdata->parts, ret); + + } else if (pdata->nr_parts) { + pr_devinit(KERN_NOTICE DRIVER_NAME ": Using board partition definition\n"); + add_mtd_partitions(state->mtd, pdata->parts, pdata->nr_parts); + + } else +#endif + { + pr_devinit(KERN_NOTICE DRIVER_NAME ": no partition info available, registering whole flash at once\n"); + add_mtd_device(state->mtd); + } + + platform_set_drvdata(pdev, state); + + return 0; +} + +static int __devexit bfin_flash_remove(struct platform_device *pdev) +{ + struct async_state *state = platform_get_drvdata(pdev); + gpio_free(state->enet_flash_pin); +#ifdef CONFIG_MTD_PARTITIONS + del_mtd_partitions(state->mtd); +#endif + map_destroy(state->mtd); + kfree(state); + return 0; +} + +static struct platform_driver bfin_flash_driver = { + .probe = bfin_flash_probe, + .remove = __devexit_p(bfin_flash_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init bfin_flash_init(void) +{ + return platform_driver_register(&bfin_flash_driver); +} +module_init(bfin_flash_init); + +static void __exit bfin_flash_exit(void) +{ + platform_driver_unregister(&bfin_flash_driver); +} +module_exit(bfin_flash_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MTD map driver for Blackfins with flash/ethernet on same async bank"); diff --git a/drivers/mtd/maps/cdb89712.c b/drivers/mtd/maps/cdb89712.c index 9f17bb6c5a9d..cb507da0a87d 100644 --- a/drivers/mtd/maps/cdb89712.c +++ b/drivers/mtd/maps/cdb89712.c @@ -1,7 +1,6 @@ /* * Flash on Cirrus CDB89712 * - * $Id: cdb89712.c,v 1.11 2005/11/07 11:14:26 gleixner Exp $ */ #include <linux/module.h> diff --git a/drivers/mtd/maps/ceiva.c b/drivers/mtd/maps/ceiva.c index 629e6e2641a8..6464d487eb1a 100644 --- a/drivers/mtd/maps/ceiva.c +++ b/drivers/mtd/maps/ceiva.c @@ -11,7 +11,6 @@ * * (C) 2000 Nicolas Pitre <nico@cam.org> * - * $Id: ceiva.c,v 1.11 2004/09/16 23:27:12 gleixner Exp $ */ #include <linux/module.h> diff --git a/drivers/mtd/maps/cfi_flagadm.c b/drivers/mtd/maps/cfi_flagadm.c index 65e5ee552010..0ecc3f6d735b 100644 --- a/drivers/mtd/maps/cfi_flagadm.c +++ b/drivers/mtd/maps/cfi_flagadm.c @@ -1,8 +1,6 @@ /* * Copyright © 2001 Flaga hf. Medical Devices, Kári DavÃðsson <kd@flaga.is> * - * $Id: cfi_flagadm.c,v 1.15 2005/11/07 11:14:26 gleixner Exp $ - * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your diff --git a/drivers/mtd/maps/dbox2-flash.c b/drivers/mtd/maps/dbox2-flash.c index 92a9c7fac993..e115667bf1d0 100644 --- a/drivers/mtd/maps/dbox2-flash.c +++ b/drivers/mtd/maps/dbox2-flash.c @@ -1,6 +1,4 @@ /* - * $Id: dbox2-flash.c,v 1.14 2005/11/07 11:14:26 gleixner Exp $ - * * D-Box 2 flash driver */ diff --git a/drivers/mtd/maps/dc21285.c b/drivers/mtd/maps/dc21285.c index b32bb9347d71..3aa018c092f8 100644 --- a/drivers/mtd/maps/dc21285.c +++ b/drivers/mtd/maps/dc21285.c @@ -4,8 +4,6 @@ * (C) 2000 Nicolas Pitre <nico@cam.org> * * This code is GPL - * - * $Id: dc21285.c,v 1.24 2005/11/07 11:14:26 gleixner Exp $ */ #include <linux/module.h> #include <linux/types.h> diff --git a/drivers/mtd/maps/dilnetpc.c b/drivers/mtd/maps/dilnetpc.c index 1c3b34ad7325..0713e3a5a22c 100644 --- a/drivers/mtd/maps/dilnetpc.c +++ b/drivers/mtd/maps/dilnetpc.c @@ -14,8 +14,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: dilnetpc.c,v 1.20 2005/11/07 11:14:26 gleixner Exp $ - * * The DIL/Net PC is a tiny embedded PC board made by SSV Embedded Systems * featuring the AMD Elan SC410 processor. There are two variants of this * board: DNP/1486 and ADNP/1486. The DNP version has 2 megs of flash diff --git a/drivers/mtd/maps/dmv182.c b/drivers/mtd/maps/dmv182.c index e0558b0b2fe6..d171674eb2ed 100644 --- a/drivers/mtd/maps/dmv182.c +++ b/drivers/mtd/maps/dmv182.c @@ -4,8 +4,6 @@ * * Flash map driver for the Dy4 SVME182 board * - * $Id: dmv182.c,v 1.6 2005/11/07 11:14:26 gleixner Exp $ - * * Copyright 2003-2004, TimeSys Corporation * * Based on the SVME181 flash map, by Tom Nelson, Dot4, Inc. for TimeSys Corp. diff --git a/drivers/mtd/maps/ebony.c b/drivers/mtd/maps/ebony.c index 1488bb92f26f..d92b7c70d3ed 100644 --- a/drivers/mtd/maps/ebony.c +++ b/drivers/mtd/maps/ebony.c @@ -1,6 +1,4 @@ /* - * $Id: ebony.c,v 1.16 2005/11/07 11:14:26 gleixner Exp $ - * * Mapping for Ebony user flash * * Matt Porter <mporter@kernel.crashing.org> diff --git a/drivers/mtd/maps/edb7312.c b/drivers/mtd/maps/edb7312.c index 1c5b97c89685..9433738c1664 100644 --- a/drivers/mtd/maps/edb7312.c +++ b/drivers/mtd/maps/edb7312.c @@ -1,6 +1,4 @@ /* - * $Id: edb7312.c,v 1.14 2005/11/07 11:14:27 gleixner Exp $ - * * Handle mapping of the NOR flash on Cogent EDB7312 boards * * Copyright 2002 SYSGO Real-Time Solutions GmbH diff --git a/drivers/mtd/maps/fortunet.c b/drivers/mtd/maps/fortunet.c index 7c50c271651c..a8e3fde4cbd5 100644 --- a/drivers/mtd/maps/fortunet.c +++ b/drivers/mtd/maps/fortunet.c @@ -1,6 +1,5 @@ /* fortunet.c memory map * - * $Id: fortunet.c,v 1.11 2005/11/07 11:14:27 gleixner Exp $ */ #include <linux/module.h> diff --git a/drivers/mtd/maps/h720x-flash.c b/drivers/mtd/maps/h720x-flash.c index 6dde3182d64a..ef8915474462 100644 --- a/drivers/mtd/maps/h720x-flash.c +++ b/drivers/mtd/maps/h720x-flash.c @@ -2,8 +2,6 @@ * Flash memory access on Hynix GMS30C7201/HMS30C7202 based * evaluation boards * - * $Id: h720x-flash.c,v 1.12 2005/11/07 11:14:27 gleixner Exp $ - * * (C) 2002 Jungjun Kim <jungjun.kim@hynix.com> * 2003 Thomas Gleixner <tglx@linutronix.de> */ diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c index 2c884c49e84a..aeb6c916e23f 100644 --- a/drivers/mtd/maps/ichxrom.c +++ b/drivers/mtd/maps/ichxrom.c @@ -2,7 +2,6 @@ * ichxrom.c * * Normal mappings of chips in physical memory - * $Id: ichxrom.c,v 1.19 2005/11/07 11:14:27 gleixner Exp $ */ #include <linux/module.h> diff --git a/drivers/mtd/maps/impa7.c b/drivers/mtd/maps/impa7.c index a0b4dc7155dc..2682ab51a367 100644 --- a/drivers/mtd/maps/impa7.c +++ b/drivers/mtd/maps/impa7.c @@ -1,6 +1,4 @@ /* - * $Id: impa7.c,v 1.14 2005/11/07 11:14:27 gleixner Exp $ - * * Handle mapping of the NOR flash on implementa A7 boards * * Copyright 2002 SYSGO Real-Time Solutions GmbH diff --git a/drivers/mtd/maps/integrator-flash.c b/drivers/mtd/maps/integrator-flash.c index 325c8880c437..ee361aaadb1e 100644 --- a/drivers/mtd/maps/integrator-flash.c +++ b/drivers/mtd/maps/integrator-flash.c @@ -22,8 +22,6 @@ This is access code for flashes using ARM's flash partitioning standards. - $Id: integrator-flash.c,v 1.20 2005/11/07 11:14:27 gleixner Exp $ - ======================================================================*/ #include <linux/module.h> diff --git a/drivers/mtd/maps/ipaq-flash.c b/drivers/mtd/maps/ipaq-flash.c index f27c132794c3..a806119797e0 100644 --- a/drivers/mtd/maps/ipaq-flash.c +++ b/drivers/mtd/maps/ipaq-flash.c @@ -4,8 +4,6 @@ * (C) 2000 Nicolas Pitre <nico@cam.org> * (C) 2002 Hewlett-Packard Company <jamey.hicks@hp.com> * (C) 2003 Christian Pellegrin <chri@ascensit.com>, <chri@infis.univ.ts.it>: concatenation of multiple flashes - * - * $Id: ipaq-flash.c,v 1.5 2005/11/07 11:14:27 gleixner Exp $ */ #include <linux/module.h> diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c index c8396b8574c4..c2264792a20b 100644 --- a/drivers/mtd/maps/ixp2000.c +++ b/drivers/mtd/maps/ixp2000.c @@ -1,6 +1,4 @@ /* - * $Id: ixp2000.c,v 1.9 2005/11/07 11:14:27 gleixner Exp $ - * * drivers/mtd/maps/ixp2000.c * * Mapping for the Intel XScale IXP2000 based systems diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index 01f19a4714b5..9c7a5fbd4e51 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -1,6 +1,4 @@ /* - * $Id: ixp4xx.c,v 1.13 2005/11/16 16:23:21 dvrabel Exp $ - * * drivers/mtd/maps/ixp4xx.c * * MTD Map file for IXP4XX based systems. Please do not make per-board diff --git a/drivers/mtd/maps/l440gx.c b/drivers/mtd/maps/l440gx.c index 67620adf4811..9e054503c4cf 100644 --- a/drivers/mtd/maps/l440gx.c +++ b/drivers/mtd/maps/l440gx.c @@ -1,6 +1,4 @@ /* - * $Id: l440gx.c,v 1.18 2005/11/07 11:14:27 gleixner Exp $ - * * BIOS Flash chip on Intel 440GX board. * * Bugs this currently does not work under linuxBIOS. diff --git a/drivers/mtd/maps/map_funcs.c b/drivers/mtd/maps/map_funcs.c index 9105e6ca0aa6..3f268370eeca 100644 --- a/drivers/mtd/maps/map_funcs.c +++ b/drivers/mtd/maps/map_funcs.c @@ -1,6 +1,4 @@ /* - * $Id: map_funcs.c,v 1.10 2005/06/06 23:04:36 tpoynor Exp $ - * * Out-of-line map I/O functions for simple maps when CONFIG_COMPLEX_MAPPINGS * is enabled. */ diff --git a/drivers/mtd/maps/mbx860.c b/drivers/mtd/maps/mbx860.c index 06b118727846..706f67394b07 100644 --- a/drivers/mtd/maps/mbx860.c +++ b/drivers/mtd/maps/mbx860.c @@ -1,6 +1,4 @@ /* - * $Id: mbx860.c,v 1.9 2005/11/07 11:14:27 gleixner Exp $ - * * Handle mapping of the flash on MBX860 boards * * Author: Anton Todorov diff --git a/drivers/mtd/maps/netsc520.c b/drivers/mtd/maps/netsc520.c index 95dcab2146ad..c0cb319b2b70 100644 --- a/drivers/mtd/maps/netsc520.c +++ b/drivers/mtd/maps/netsc520.c @@ -3,8 +3,6 @@ * Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com) * based on sc520cdp.c by Sysgo Real-Time Solutions GmbH * - * $Id: netsc520.c,v 1.14 2005/11/07 11:14:27 gleixner Exp $ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index 0c9b305a72e0..965e6c6d6ab0 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -5,8 +5,6 @@ * * (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com) * (C) Copyright 2001-2002, SnapGear (www.snapgear.com) - * - * $Id: nettel.c,v 1.12 2005/11/29 14:30:00 gleixner Exp $ */ /****************************************************************************/ diff --git a/drivers/mtd/maps/octagon-5066.c b/drivers/mtd/maps/octagon-5066.c index a6642db3d325..43e04c1d22a9 100644 --- a/drivers/mtd/maps/octagon-5066.c +++ b/drivers/mtd/maps/octagon-5066.c @@ -1,4 +1,3 @@ -// $Id: octagon-5066.c,v 1.28 2005/11/07 11:14:27 gleixner Exp $ /* ###################################################################### Octagon 5066 MTD Driver. diff --git a/drivers/mtd/maps/omap-toto-flash.c b/drivers/mtd/maps/omap-toto-flash.c index e6e391efbeb6..0a60ebbc2175 100644 --- a/drivers/mtd/maps/omap-toto-flash.c +++ b/drivers/mtd/maps/omap-toto-flash.c @@ -4,8 +4,6 @@ * jzhang@ti.com (C) 2003 Texas Instruments. * * (C) 2002 MontVista Software, Inc. - * - * $Id: omap-toto-flash.c,v 1.5 2005/11/07 11:14:27 gleixner Exp $ */ #include <linux/module.h> diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c index d2ab1bae9c34..5c6a25c90380 100644 --- a/drivers/mtd/maps/pci.c +++ b/drivers/mtd/maps/pci.c @@ -7,8 +7,6 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * $Id: pci.c,v 1.14 2005/11/17 08:20:27 dwmw2 Exp $ - * * Generic PCI memory map driver. We support the following boards: * - Intel IQ80310 ATU. * - Intel EBSA285 (blank rom programming mode). Tested working 27/09/2001 diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index 0cc31675aeb9..90924fb00481 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -1,6 +1,4 @@ /* - * $Id: pcmciamtd.c,v 1.55 2005/11/07 11:14:28 gleixner Exp $ - * * pcmciamtd.c - MTD driver for PCMCIA flash memory cards * * Author: Simon Evans <spse@secret.org.uk> @@ -48,7 +46,6 @@ static const int debug = 0; #define DRIVER_DESC "PCMCIA Flash memory card driver" -#define DRIVER_VERSION "$Revision: 1.55 $" /* Size of the PCMCIA address space: 26 bits = 64 MB */ #define MAX_PCMCIA_ADDR 0x4000000 @@ -785,7 +782,7 @@ static struct pcmcia_driver pcmciamtd_driver = { static int __init init_pcmciamtd(void) { - info(DRIVER_DESC " " DRIVER_VERSION); + info(DRIVER_DESC); if(bankwidth && bankwidth != 1 && bankwidth != 2) { info("bad bankwidth (%d), using default", bankwidth); diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 183255fcfdcb..42d844f8f6bf 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -1,6 +1,4 @@ /* - * $Id: physmap.c,v 1.39 2005/11/29 14:49:36 gleixner Exp $ - * * Normal mappings of chips in physical memory * * Copyright (C) 2003 MontaVista Software Inc. @@ -203,7 +201,19 @@ static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state int i; for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++) - ret |= info->mtd[i]->suspend(info->mtd[i]); + if (info->mtd[i]->suspend) { + ret = info->mtd[i]->suspend(info->mtd[i]); + if (ret) + goto fail; + } + + return 0; +fail: + for (--i; i >= 0; --i) + if (info->mtd[i]->suspend) { + BUG_ON(!info->mtd[i]->resume); + info->mtd[i]->resume(info->mtd[i]); + } return ret; } @@ -214,7 +224,8 @@ static int physmap_flash_resume(struct platform_device *dev) int i; for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++) - info->mtd[i]->resume(info->mtd[i]); + if (info->mtd[i]->resume) + info->mtd[i]->resume(info->mtd[i]); return 0; } @@ -225,8 +236,9 @@ static void physmap_flash_shutdown(struct platform_device *dev) int i; for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++) - if (info->mtd[i]->suspend(info->mtd[i]) == 0) - info->mtd[i]->resume(info->mtd[i]); + if (info->mtd[i]->suspend && info->mtd[i]->resume) + if (info->mtd[i]->suspend(info->mtd[i]) == 0) + info->mtd[i]->resume(info->mtd[i]); } #else #define physmap_flash_suspend NULL diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 3eb2643b2328..e7dd9c8a965e 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -6,8 +6,6 @@ * * Generic platfrom device based RAM map * - * $Id: plat-ram.c,v 1.7 2005/11/07 11:14:28 gleixner Exp $ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/drivers/mtd/maps/redwood.c b/drivers/mtd/maps/redwood.c index 4d858b3d5f82..de002eb1a7fe 100644 --- a/drivers/mtd/maps/redwood.c +++ b/drivers/mtd/maps/redwood.c @@ -1,6 +1,4 @@ /* - * $Id: redwood.c,v 1.11 2005/11/07 11:14:28 gleixner Exp $ - * * drivers/mtd/maps/redwood.c * * FLASH map for the IBM Redwood 4/5/6 boards. diff --git a/drivers/mtd/maps/rpxlite.c b/drivers/mtd/maps/rpxlite.c index 809a0c8e7aaf..14d90edb4430 100644 --- a/drivers/mtd/maps/rpxlite.c +++ b/drivers/mtd/maps/rpxlite.c @@ -1,6 +1,4 @@ /* - * $Id: rpxlite.c,v 1.22 2004/11/04 13:24:15 gleixner Exp $ - * * Handle mapping of the flash on the RPX Lite and CLLF boards */ diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index c7d5a52a2d55..e177a43dfff0 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -2,8 +2,6 @@ * Flash memory access on SA11x0 based devices * * (C) 2000 Nicolas Pitre <nico@cam.org> - * - * $Id: sa1100-flash.c,v 1.51 2005/11/07 11:14:28 gleixner Exp $ */ #include <linux/module.h> #include <linux/types.h> diff --git a/drivers/mtd/maps/sbc8240.c b/drivers/mtd/maps/sbc8240.c index b8c1331b7a04..6e1e99cd2b59 100644 --- a/drivers/mtd/maps/sbc8240.c +++ b/drivers/mtd/maps/sbc8240.c @@ -4,9 +4,6 @@ * Carolyn Smith, Tektronix, Inc. * * This code is GPLed - * - * $Id: sbc8240.c,v 1.5 2005/11/07 11:14:28 gleixner Exp $ - * */ /* diff --git a/drivers/mtd/maps/sbc_gxx.c b/drivers/mtd/maps/sbc_gxx.c index 7cc4041d096d..1b1c0b7e11ef 100644 --- a/drivers/mtd/maps/sbc_gxx.c +++ b/drivers/mtd/maps/sbc_gxx.c @@ -17,8 +17,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - $Id: sbc_gxx.c,v 1.35 2005/11/07 11:14:28 gleixner Exp $ - The SBC-MediaGX / SBC-GXx has up to 16 MiB of Intel StrataFlash (28F320/28F640) in x8 mode. diff --git a/drivers/mtd/maps/sc520cdp.c b/drivers/mtd/maps/sc520cdp.c index 4045e372b90d..85c1e56309ec 100644 --- a/drivers/mtd/maps/sc520cdp.c +++ b/drivers/mtd/maps/sc520cdp.c @@ -16,8 +16,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: sc520cdp.c,v 1.23 2005/11/17 08:20:27 dwmw2 Exp $ - * * * The SC520CDP is an evaluation board for the Elan SC520 processor available * from AMD. It has two banks of 32-bit Flash ROM, each 8 Megabytes in size, diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c index 0fc5584324e3..21169e6d646c 100644 --- a/drivers/mtd/maps/scb2_flash.c +++ b/drivers/mtd/maps/scb2_flash.c @@ -1,6 +1,5 @@ /* * MTD map driver for BIOS Flash on Intel SCB2 boards - * $Id: scb2_flash.c,v 1.12 2005/03/18 14:04:35 gleixner Exp $ * Copyright (C) 2002 Sun Microsystems, Inc. * Tim Hockin <thockin@sun.com> * diff --git a/drivers/mtd/maps/scx200_docflash.c b/drivers/mtd/maps/scx200_docflash.c index 5e2bce22f37c..b5391ebb736e 100644 --- a/drivers/mtd/maps/scx200_docflash.c +++ b/drivers/mtd/maps/scx200_docflash.c @@ -2,8 +2,6 @@ Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> - $Id: scx200_docflash.c,v 1.12 2005/11/07 11:14:28 gleixner Exp $ - National Semiconductor SCx200 flash mapped with DOCCS */ diff --git a/drivers/mtd/maps/sharpsl-flash.c b/drivers/mtd/maps/sharpsl-flash.c index 917dc778f24e..026eab028189 100644 --- a/drivers/mtd/maps/sharpsl-flash.c +++ b/drivers/mtd/maps/sharpsl-flash.c @@ -4,8 +4,6 @@ * Copyright (C) 2001 Lineo Japan, Inc. * Copyright (C) 2002 SHARP * - * $Id: sharpsl-flash.c,v 1.7 2005/11/07 11:14:28 gleixner Exp $ - * * based on rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp * Handle mapping of the flash on the RPX Lite and CLLF boards * diff --git a/drivers/mtd/maps/solutionengine.c b/drivers/mtd/maps/solutionengine.c index d76ceef453ce..0eb41d9c6786 100644 --- a/drivers/mtd/maps/solutionengine.c +++ b/drivers/mtd/maps/solutionengine.c @@ -1,6 +1,4 @@ /* - * $Id: solutionengine.c,v 1.15 2005/11/07 11:14:28 gleixner Exp $ - * * Flash and EPROM on Hitachi Solution Engine and similar boards. * * (C) 2001 Red Hat, Inc. diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c index 001af7f7ddda..0d7c88396c88 100644 --- a/drivers/mtd/maps/sun_uflash.c +++ b/drivers/mtd/maps/sun_uflash.c @@ -1,4 +1,4 @@ -/* $Id: sun_uflash.c,v 1.13 2005/11/07 11:14:28 gleixner Exp $ +/* * * sun_uflash - Driver implementation for user-programmable flash * present on many Sun Microsystems SME boardsets. diff --git a/drivers/mtd/maps/tqm8xxl.c b/drivers/mtd/maps/tqm8xxl.c index 521734057314..a5d3d8531faa 100644 --- a/drivers/mtd/maps/tqm8xxl.c +++ b/drivers/mtd/maps/tqm8xxl.c @@ -2,8 +2,6 @@ * Handle mapping of the flash memory access routines * on TQM8xxL based devices. * - * $Id: tqm8xxl.c,v 1.15 2005/11/07 11:14:28 gleixner Exp $ - * * based on rpxlite.c * * Copyright(C) 2001 Kirk Lee <kirk@hpc.ee.ntu.edu.tw> diff --git a/drivers/mtd/maps/ts5500_flash.c b/drivers/mtd/maps/ts5500_flash.c index b47270e850bc..e2147bf11c88 100644 --- a/drivers/mtd/maps/ts5500_flash.c +++ b/drivers/mtd/maps/ts5500_flash.c @@ -22,8 +22,6 @@ * - Drive A and B use the resident flash disk (RFD) flash translation layer. * - If you have created your own jffs file system and the bios overwrites * it during boot, try disabling Drive A: and B: in the boot order. - * - * $Id: ts5500_flash.c,v 1.5 2005/11/07 11:14:28 gleixner Exp $ */ #include <linux/init.h> diff --git a/drivers/mtd/maps/tsunami_flash.c b/drivers/mtd/maps/tsunami_flash.c index 0f915ac3102e..77a8bfc02577 100644 --- a/drivers/mtd/maps/tsunami_flash.c +++ b/drivers/mtd/maps/tsunami_flash.c @@ -2,7 +2,6 @@ * tsunami_flash.c * * flash chip on alpha ds10... - * $Id: tsunami_flash.c,v 1.10 2005/11/07 11:14:29 gleixner Exp $ */ #include <asm/io.h> #include <asm/core_tsunami.h> diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index c42f4b83f686..0dc645f8152f 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c @@ -4,8 +4,6 @@ * uclinux.c -- generic memory mapped MTD driver for uclinux * * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) - * - * $Id: uclinux.c,v 1.12 2005/11/07 11:14:29 gleixner Exp $ */ /****************************************************************************/ @@ -15,6 +13,7 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/fs.h> +#include <linux/mm.h> #include <linux/major.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> diff --git a/drivers/mtd/maps/vmax301.c b/drivers/mtd/maps/vmax301.c index b3e487395435..5a0c9a353b0f 100644 --- a/drivers/mtd/maps/vmax301.c +++ b/drivers/mtd/maps/vmax301.c @@ -1,4 +1,3 @@ -// $Id: vmax301.c,v 1.32 2005/11/07 11:14:29 gleixner Exp $ /* ###################################################################### Tempustech VMAX SBC301 MTD Driver. diff --git a/drivers/mtd/maps/walnut.c b/drivers/mtd/maps/walnut.c index ca932122fb64..e243476c8171 100644 --- a/drivers/mtd/maps/walnut.c +++ b/drivers/mtd/maps/walnut.c @@ -1,6 +1,4 @@ /* - * $Id: walnut.c,v 1.3 2005/11/07 11:14:29 gleixner Exp $ - * * Mapping for Walnut flash * (used ebony.c as a "framework") * diff --git a/drivers/mtd/maps/wr_sbc82xx_flash.c b/drivers/mtd/maps/wr_sbc82xx_flash.c index ac5b8105b6ef..413b0cf9bbd2 100644 --- a/drivers/mtd/maps/wr_sbc82xx_flash.c +++ b/drivers/mtd/maps/wr_sbc82xx_flash.c @@ -1,6 +1,4 @@ /* - * $Id: wr_sbc82xx_flash.c,v 1.8 2005/11/07 11:14:29 gleixner Exp $ - * * Map for flash chips on Wind River PowerQUICC II SBC82xx board. * * Copyright (C) 2004 Red Hat, Inc. diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 839eed8430a2..9ff007c4962c 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -1,6 +1,4 @@ /* - * $Id: mtd_blkdevs.c,v 1.27 2005/11/07 11:14:20 gleixner Exp $ - * * (C) 2003 David Woodhouse <dwmw2@infradead.org> * * Interface to Linux 2.5 block layer for MTD 'translation layers'. @@ -212,7 +210,7 @@ static struct block_device_operations mtd_blktrans_ops = { int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) { struct mtd_blktrans_ops *tr = new->tr; - struct list_head *this; + struct mtd_blktrans_dev *d; int last_devnum = -1; struct gendisk *gd; @@ -221,8 +219,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) BUG(); } - list_for_each(this, &tr->devs) { - struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev, list); + list_for_each_entry(d, &tr->devs, list) { if (new->devnum == -1) { /* Use first free number */ if (d->devnum != last_devnum+1) { @@ -309,33 +306,24 @@ int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) static void blktrans_notify_remove(struct mtd_info *mtd) { - struct list_head *this, *this2, *next; - - list_for_each(this, &blktrans_majors) { - struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); - - list_for_each_safe(this2, next, &tr->devs) { - struct mtd_blktrans_dev *dev = list_entry(this2, struct mtd_blktrans_dev, list); + struct mtd_blktrans_ops *tr; + struct mtd_blktrans_dev *dev, *next; + list_for_each_entry(tr, &blktrans_majors, list) + list_for_each_entry_safe(dev, next, &tr->devs, list) if (dev->mtd == mtd) tr->remove_dev(dev); - } - } } static void blktrans_notify_add(struct mtd_info *mtd) { - struct list_head *this; + struct mtd_blktrans_ops *tr; if (mtd->type == MTD_ABSENT) return; - list_for_each(this, &blktrans_majors) { - struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); - + list_for_each_entry(tr, &blktrans_majors, list) tr->add_mtd(tr, mtd); - } - } static struct mtd_notifier blktrans_notifier = { @@ -406,7 +394,7 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) { - struct list_head *this, *next; + struct mtd_blktrans_dev *dev, *next; mutex_lock(&mtd_table_mutex); @@ -416,10 +404,8 @@ int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) /* Remove it from the list of active majors */ list_del(&tr->list); - list_for_each_safe(this, next, &tr->devs) { - struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list); + list_for_each_entry_safe(dev, next, &tr->devs, list) tr->remove_dev(dev); - } blk_cleanup_queue(tr->blkcore_priv->rq); unregister_blkdev(tr->major, tr->name); diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 952da30b1745..208c6faa0358 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -1,8 +1,6 @@ /* * Direct MTD block device access * - * $Id: mtdblock.c,v 1.68 2005/11/07 11:14:20 gleixner Exp $ - * * (C) 2000-2003 Nicolas Pitre <nico@cam.org> * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> */ diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c index f79dbb49b1a2..852165f8b1c3 100644 --- a/drivers/mtd/mtdblock_ro.c +++ b/drivers/mtd/mtdblock_ro.c @@ -1,6 +1,4 @@ /* - * $Id: mtdblock_ro.c,v 1.19 2004/11/16 18:28:59 dwmw2 Exp $ - * * (C) 2003 David Woodhouse <dwmw2@infradead.org> * * Simple read-only (writable only for RAM) mtdblock driver diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 129d429cd2da..d2f331876e4c 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -1,6 +1,4 @@ /* - * $Id: mtdchar.c,v 1.76 2005/11/07 11:14:20 gleixner Exp $ - * * Character-device access to raw MTD devices. * */ @@ -28,10 +26,13 @@ static void mtd_notify_add(struct mtd_info* mtd) if (!mtd) return; - device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2), "mtd%d", mtd->index); + device_create_drvdata(mtd_class, NULL, + MKDEV(MTD_CHAR_MAJOR, mtd->index*2), + NULL, "mtd%d", mtd->index); - device_create(mtd_class, NULL, - MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), "mtd%dro", mtd->index); + device_create_drvdata(mtd_class, NULL, + MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), + NULL, "mtd%dro", mtd->index); } static void mtd_notify_remove(struct mtd_info* mtd) @@ -491,6 +492,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, { struct mtd_oob_buf buf; struct mtd_oob_ops ops; + struct mtd_oob_buf __user *user_buf = argp; uint32_t retlen; if(!(file->f_mode & 2)) @@ -534,8 +536,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, if (ops.oobretlen > 0xFFFFFFFFU) ret = -EOVERFLOW; retlen = ops.oobretlen; - if (copy_to_user(&((struct mtd_oob_buf *)argp)->length, - &retlen, sizeof(buf.length))) + if (copy_to_user(&user_buf->length, &retlen, sizeof(buf.length))) ret = -EFAULT; kfree(ops.oobbuf); @@ -589,29 +590,29 @@ static int mtd_ioctl(struct inode *inode, struct file *file, case MEMLOCK: { - struct erase_info_user info; + struct erase_info_user einfo; - if (copy_from_user(&info, argp, sizeof(info))) + if (copy_from_user(&einfo, argp, sizeof(einfo))) return -EFAULT; if (!mtd->lock) ret = -EOPNOTSUPP; else - ret = mtd->lock(mtd, info.start, info.length); + ret = mtd->lock(mtd, einfo.start, einfo.length); break; } case MEMUNLOCK: { - struct erase_info_user info; + struct erase_info_user einfo; - if (copy_from_user(&info, argp, sizeof(info))) + if (copy_from_user(&einfo, argp, sizeof(einfo))) return -EFAULT; if (!mtd->unlock) ret = -EOPNOTSUPP; else - ret = mtd->unlock(mtd, info.start, info.length); + ret = mtd->unlock(mtd, einfo.start, einfo.length); break; } @@ -711,15 +712,15 @@ static int mtd_ioctl(struct inode *inode, struct file *file, case OTPLOCK: { - struct otp_info info; + struct otp_info oinfo; if (mfi->mode != MTD_MODE_OTP_USER) return -EINVAL; - if (copy_from_user(&info, argp, sizeof(info))) + if (copy_from_user(&oinfo, argp, sizeof(oinfo))) return -EFAULT; if (!mtd->lock_user_prot_reg) return -EOPNOTSUPP; - ret = mtd->lock_user_prot_reg(mtd, info.start, info.length); + ret = mtd->lock_user_prot_reg(mtd, oinfo.start, oinfo.length); break; } #endif diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index d563dcd4b264..2972a5edb73d 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -6,8 +6,6 @@ * NAND support by Christian Gan <cgan@iders.ca> * * This code is GPL - * - * $Id: mtdconcat.c,v 1.11 2005/11/07 11:14:20 gleixner Exp $ */ #include <linux/kernel.h> diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index f7e7890e5bc6..a9d246949820 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1,6 +1,4 @@ /* - * $Id: mtdcore.c,v 1.47 2005/11/07 11:14:20 gleixner Exp $ - * * Core registration and callback routines for MTD * drivers and users. * @@ -53,7 +51,7 @@ int add_mtd_device(struct mtd_info *mtd) for (i=0; i < MAX_MTD_DEVICES; i++) if (!mtd_table[i]) { - struct list_head *this; + struct mtd_notifier *not; mtd_table[i] = mtd; mtd->index = i; @@ -72,10 +70,8 @@ int add_mtd_device(struct mtd_info *mtd) DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name); /* No need to get a refcount on the module containing the notifier, since we hold the mtd_table_mutex */ - list_for_each(this, &mtd_notifiers) { - struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list); + list_for_each_entry(not, &mtd_notifiers, list) not->add(mtd); - } mutex_unlock(&mtd_table_mutex); /* We _know_ we aren't being removed, because @@ -113,14 +109,12 @@ int del_mtd_device (struct mtd_info *mtd) mtd->index, mtd->name, mtd->usecount); ret = -EBUSY; } else { - struct list_head *this; + struct mtd_notifier *not; /* No need to get a refcount on the module containing the notifier, since we hold the mtd_table_mutex */ - list_for_each(this, &mtd_notifiers) { - struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list); + list_for_each_entry(not, &mtd_notifiers, list) not->remove(mtd); - } mtd_table[mtd->index] = NULL; diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 07c701169344..edb90b58a9b1 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -5,8 +5,6 @@ * * This code is GPL * - * $Id: mtdpart.c,v 1.55 2005/11/07 11:14:20 gleixner Exp $ - * * 02-21-2002 Thomas Gleixner <gleixner@autronix.de> * added support for read_oob, write_oob */ @@ -46,8 +44,8 @@ struct mtd_part { * to the _real_ device. */ -static int part_read (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int part_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) { struct mtd_part *part = PART(mtd); int res; @@ -56,7 +54,7 @@ static int part_read (struct mtd_info *mtd, loff_t from, size_t len, len = 0; else if (from + len > mtd->size) len = mtd->size - from; - res = part->master->read (part->master, from + part->offset, + res = part->master->read(part->master, from + part->offset, len, retlen, buf); if (unlikely(res)) { if (res == -EUCLEAN) @@ -67,8 +65,8 @@ static int part_read (struct mtd_info *mtd, loff_t from, size_t len, return res; } -static int part_point (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, void **virt, resource_size_t *phys) +static int part_point(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, void **virt, resource_size_t *phys) { struct mtd_part *part = PART(mtd); if (from >= mtd->size) @@ -87,7 +85,7 @@ static void part_unpoint(struct mtd_info *mtd, loff_t from, size_t len) } static int part_read_oob(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops) + struct mtd_oob_ops *ops) { struct mtd_part *part = PART(mtd); int res; @@ -107,38 +105,38 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from, return res; } -static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, u_char *buf) { struct mtd_part *part = PART(mtd); - return part->master->read_user_prot_reg (part->master, from, + return part->master->read_user_prot_reg(part->master, from, len, retlen, buf); } -static int part_get_user_prot_info (struct mtd_info *mtd, - struct otp_info *buf, size_t len) +static int part_get_user_prot_info(struct mtd_info *mtd, + struct otp_info *buf, size_t len) { struct mtd_part *part = PART(mtd); - return part->master->get_user_prot_info (part->master, buf, len); + return part->master->get_user_prot_info(part->master, buf, len); } -static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, u_char *buf) { struct mtd_part *part = PART(mtd); - return part->master->read_fact_prot_reg (part->master, from, + return part->master->read_fact_prot_reg(part->master, from, len, retlen, buf); } -static int part_get_fact_prot_info (struct mtd_info *mtd, - struct otp_info *buf, size_t len) +static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf, + size_t len) { struct mtd_part *part = PART(mtd); - return part->master->get_fact_prot_info (part->master, buf, len); + return part->master->get_fact_prot_info(part->master, buf, len); } -static int part_write (struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) +static int part_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) { struct mtd_part *part = PART(mtd); if (!(mtd->flags & MTD_WRITEABLE)) @@ -147,12 +145,12 @@ static int part_write (struct mtd_info *mtd, loff_t to, size_t len, len = 0; else if (to + len > mtd->size) len = mtd->size - to; - return part->master->write (part->master, to + part->offset, + return part->master->write(part->master, to + part->offset, len, retlen, buf); } -static int part_panic_write (struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) +static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) { struct mtd_part *part = PART(mtd); if (!(mtd->flags & MTD_WRITEABLE)) @@ -161,12 +159,12 @@ static int part_panic_write (struct mtd_info *mtd, loff_t to, size_t len, len = 0; else if (to + len > mtd->size) len = mtd->size - to; - return part->master->panic_write (part->master, to + part->offset, + return part->master->panic_write(part->master, to + part->offset, len, retlen, buf); } static int part_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) + struct mtd_oob_ops *ops) { struct mtd_part *part = PART(mtd); @@ -180,31 +178,32 @@ static int part_write_oob(struct mtd_info *mtd, loff_t to, return part->master->write_oob(part->master, to + part->offset, ops); } -static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, u_char *buf) { struct mtd_part *part = PART(mtd); - return part->master->write_user_prot_reg (part->master, from, + return part->master->write_user_prot_reg(part->master, from, len, retlen, buf); } -static int part_lock_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len) +static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len) { struct mtd_part *part = PART(mtd); - return part->master->lock_user_prot_reg (part->master, from, len); + return part->master->lock_user_prot_reg(part->master, from, len); } -static int part_writev (struct mtd_info *mtd, const struct kvec *vecs, - unsigned long count, loff_t to, size_t *retlen) +static int part_writev(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen) { struct mtd_part *part = PART(mtd); if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; - return part->master->writev (part->master, vecs, count, + return part->master->writev(part->master, vecs, count, to + part->offset, retlen); } -static int part_erase (struct mtd_info *mtd, struct erase_info *instr) +static int part_erase(struct mtd_info *mtd, struct erase_info *instr) { struct mtd_part *part = PART(mtd); int ret; @@ -236,7 +235,7 @@ void mtd_erase_callback(struct erase_info *instr) } EXPORT_SYMBOL_GPL(mtd_erase_callback); -static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len) +static int part_lock(struct mtd_info *mtd, loff_t ofs, size_t len) { struct mtd_part *part = PART(mtd); if ((len + ofs) > mtd->size) @@ -244,7 +243,7 @@ static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len) return part->master->lock(part->master, ofs + part->offset, len); } -static int part_unlock (struct mtd_info *mtd, loff_t ofs, size_t len) +static int part_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) { struct mtd_part *part = PART(mtd); if ((len + ofs) > mtd->size) @@ -270,7 +269,7 @@ static void part_resume(struct mtd_info *mtd) part->master->resume(part->master); } -static int part_block_isbad (struct mtd_info *mtd, loff_t ofs) +static int part_block_isbad(struct mtd_info *mtd, loff_t ofs) { struct mtd_part *part = PART(mtd); if (ofs >= mtd->size) @@ -279,7 +278,7 @@ static int part_block_isbad (struct mtd_info *mtd, loff_t ofs) return part->master->block_isbad(part->master, ofs); } -static int part_block_markbad (struct mtd_info *mtd, loff_t ofs) +static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct mtd_part *part = PART(mtd); int res; @@ -302,229 +301,237 @@ static int part_block_markbad (struct mtd_info *mtd, loff_t ofs) int del_mtd_partitions(struct mtd_info *master) { - struct list_head *node; - struct mtd_part *slave; + struct mtd_part *slave, *next; - for (node = mtd_partitions.next; - node != &mtd_partitions; - node = node->next) { - slave = list_entry(node, struct mtd_part, list); + list_for_each_entry_safe(slave, next, &mtd_partitions, list) if (slave->master == master) { - struct list_head *prev = node->prev; - __list_del(prev, node->next); - if(slave->registered) + list_del(&slave->list); + if (slave->registered) del_mtd_device(&slave->mtd); kfree(slave); - node = prev; } - } return 0; } +EXPORT_SYMBOL(del_mtd_partitions); -/* - * This function, given a master MTD object and a partition table, creates - * and registers slave MTD objects which are bound to the master according to - * the partition definitions. - * (Q: should we register the master MTD object as well?) - */ - -int add_mtd_partitions(struct mtd_info *master, - const struct mtd_partition *parts, - int nbparts) +static struct mtd_part *add_one_partition(struct mtd_info *master, + const struct mtd_partition *part, int partno, + u_int32_t cur_offset) { struct mtd_part *slave; - u_int32_t cur_offset = 0; - int i; - - printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); - - for (i = 0; i < nbparts; i++) { - /* allocate the partition structure */ - slave = kzalloc (sizeof(*slave), GFP_KERNEL); - if (!slave) { - printk ("memory allocation error while creating partitions for \"%s\"\n", - master->name); - del_mtd_partitions(master); - return -ENOMEM; - } - list_add(&slave->list, &mtd_partitions); + /* allocate the partition structure */ + slave = kzalloc(sizeof(*slave), GFP_KERNEL); + if (!slave) { + printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n", + master->name); + del_mtd_partitions(master); + return NULL; + } + list_add(&slave->list, &mtd_partitions); - /* set up the MTD object for this partition */ - slave->mtd.type = master->type; - slave->mtd.flags = master->flags & ~parts[i].mask_flags; - slave->mtd.size = parts[i].size; - slave->mtd.writesize = master->writesize; - slave->mtd.oobsize = master->oobsize; - slave->mtd.oobavail = master->oobavail; - slave->mtd.subpage_sft = master->subpage_sft; + /* set up the MTD object for this partition */ + slave->mtd.type = master->type; + slave->mtd.flags = master->flags & ~part->mask_flags; + slave->mtd.size = part->size; + slave->mtd.writesize = master->writesize; + slave->mtd.oobsize = master->oobsize; + slave->mtd.oobavail = master->oobavail; + slave->mtd.subpage_sft = master->subpage_sft; - slave->mtd.name = parts[i].name; - slave->mtd.owner = master->owner; + slave->mtd.name = part->name; + slave->mtd.owner = master->owner; - slave->mtd.read = part_read; - slave->mtd.write = part_write; + slave->mtd.read = part_read; + slave->mtd.write = part_write; - if (master->panic_write) - slave->mtd.panic_write = part_panic_write; + if (master->panic_write) + slave->mtd.panic_write = part_panic_write; - if(master->point && master->unpoint){ - slave->mtd.point = part_point; - slave->mtd.unpoint = part_unpoint; - } + if (master->point && master->unpoint) { + slave->mtd.point = part_point; + slave->mtd.unpoint = part_unpoint; + } - if (master->read_oob) - slave->mtd.read_oob = part_read_oob; - if (master->write_oob) - slave->mtd.write_oob = part_write_oob; - if(master->read_user_prot_reg) - slave->mtd.read_user_prot_reg = part_read_user_prot_reg; - if(master->read_fact_prot_reg) - slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; - if(master->write_user_prot_reg) - slave->mtd.write_user_prot_reg = part_write_user_prot_reg; - if(master->lock_user_prot_reg) - slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; - if(master->get_user_prot_info) - slave->mtd.get_user_prot_info = part_get_user_prot_info; - if(master->get_fact_prot_info) - slave->mtd.get_fact_prot_info = part_get_fact_prot_info; - if (master->sync) - slave->mtd.sync = part_sync; - if (!i && master->suspend && master->resume) { - slave->mtd.suspend = part_suspend; - slave->mtd.resume = part_resume; + if (master->read_oob) + slave->mtd.read_oob = part_read_oob; + if (master->write_oob) + slave->mtd.write_oob = part_write_oob; + if (master->read_user_prot_reg) + slave->mtd.read_user_prot_reg = part_read_user_prot_reg; + if (master->read_fact_prot_reg) + slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; + if (master->write_user_prot_reg) + slave->mtd.write_user_prot_reg = part_write_user_prot_reg; + if (master->lock_user_prot_reg) + slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; + if (master->get_user_prot_info) + slave->mtd.get_user_prot_info = part_get_user_prot_info; + if (master->get_fact_prot_info) + slave->mtd.get_fact_prot_info = part_get_fact_prot_info; + if (master->sync) + slave->mtd.sync = part_sync; + if (!partno && master->suspend && master->resume) { + slave->mtd.suspend = part_suspend; + slave->mtd.resume = part_resume; + } + if (master->writev) + slave->mtd.writev = part_writev; + if (master->lock) + slave->mtd.lock = part_lock; + if (master->unlock) + slave->mtd.unlock = part_unlock; + if (master->block_isbad) + slave->mtd.block_isbad = part_block_isbad; + if (master->block_markbad) + slave->mtd.block_markbad = part_block_markbad; + slave->mtd.erase = part_erase; + slave->master = master; + slave->offset = part->offset; + slave->index = partno; + + if (slave->offset == MTDPART_OFS_APPEND) + slave->offset = cur_offset; + if (slave->offset == MTDPART_OFS_NXTBLK) { + slave->offset = cur_offset; + if ((cur_offset % master->erasesize) != 0) { + /* Round up to next erasesize */ + slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize; + printk(KERN_NOTICE "Moving partition %d: " + "0x%08x -> 0x%08x\n", partno, + cur_offset, slave->offset); } - if (master->writev) - slave->mtd.writev = part_writev; - if (master->lock) - slave->mtd.lock = part_lock; - if (master->unlock) - slave->mtd.unlock = part_unlock; - if (master->block_isbad) - slave->mtd.block_isbad = part_block_isbad; - if (master->block_markbad) - slave->mtd.block_markbad = part_block_markbad; - slave->mtd.erase = part_erase; - slave->master = master; - slave->offset = parts[i].offset; - slave->index = i; - - if (slave->offset == MTDPART_OFS_APPEND) - slave->offset = cur_offset; - if (slave->offset == MTDPART_OFS_NXTBLK) { - slave->offset = cur_offset; - if ((cur_offset % master->erasesize) != 0) { - /* Round up to next erasesize */ - slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize; - printk(KERN_NOTICE "Moving partition %d: " - "0x%08x -> 0x%08x\n", i, - cur_offset, slave->offset); + } + if (slave->mtd.size == MTDPART_SIZ_FULL) + slave->mtd.size = master->size - slave->offset; + + printk(KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, + slave->offset + slave->mtd.size, slave->mtd.name); + + /* let's do some sanity checks */ + if (slave->offset >= master->size) { + /* let's register it anyway to preserve ordering */ + slave->offset = 0; + slave->mtd.size = 0; + printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n", + part->name); + goto out_register; + } + if (slave->offset + slave->mtd.size > master->size) { + slave->mtd.size = master->size - slave->offset; + printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n", + part->name, master->name, slave->mtd.size); + } + if (master->numeraseregions > 1) { + /* Deal with variable erase size stuff */ + int i, max = master->numeraseregions; + u32 end = slave->offset + slave->mtd.size; + struct mtd_erase_region_info *regions = master->eraseregions; + + /* Find the first erase regions which is part of this + * partition. */ + for (i = 0; i < max && regions[i].offset <= slave->offset; i++) + ; + /* The loop searched for the region _behind_ the first one */ + i--; + + /* Pick biggest erasesize */ + for (; i < max && regions[i].offset < end; i++) { + if (slave->mtd.erasesize < regions[i].erasesize) { + slave->mtd.erasesize = regions[i].erasesize; } } - if (slave->mtd.size == MTDPART_SIZ_FULL) - slave->mtd.size = master->size - slave->offset; - cur_offset = slave->offset + slave->mtd.size; + BUG_ON(slave->mtd.erasesize == 0); + } else { + /* Single erase size */ + slave->mtd.erasesize = master->erasesize; + } - printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, - slave->offset + slave->mtd.size, slave->mtd.name); + if ((slave->mtd.flags & MTD_WRITEABLE) && + (slave->offset % slave->mtd.erasesize)) { + /* Doesn't start on a boundary of major erase size */ + /* FIXME: Let it be writable if it is on a boundary of + * _minor_ erase size though */ + slave->mtd.flags &= ~MTD_WRITEABLE; + printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", + part->name); + } + if ((slave->mtd.flags & MTD_WRITEABLE) && + (slave->mtd.size % slave->mtd.erasesize)) { + slave->mtd.flags &= ~MTD_WRITEABLE; + printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", + part->name); + } - /* let's do some sanity checks */ - if (slave->offset >= master->size) { - /* let's register it anyway to preserve ordering */ - slave->offset = 0; - slave->mtd.size = 0; - printk ("mtd: partition \"%s\" is out of reach -- disabled\n", - parts[i].name); - } - if (slave->offset + slave->mtd.size > master->size) { - slave->mtd.size = master->size - slave->offset; - printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n", - parts[i].name, master->name, slave->mtd.size); - } - if (master->numeraseregions>1) { - /* Deal with variable erase size stuff */ - int i; - struct mtd_erase_region_info *regions = master->eraseregions; - - /* Find the first erase regions which is part of this partition. */ - for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++) - ; - - for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) { - if (slave->mtd.erasesize < regions[i].erasesize) { - slave->mtd.erasesize = regions[i].erasesize; - } - } - } else { - /* Single erase size */ - slave->mtd.erasesize = master->erasesize; - } + slave->mtd.ecclayout = master->ecclayout; + if (master->block_isbad) { + uint32_t offs = 0; - if ((slave->mtd.flags & MTD_WRITEABLE) && - (slave->offset % slave->mtd.erasesize)) { - /* Doesn't start on a boundary of major erase size */ - /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */ - slave->mtd.flags &= ~MTD_WRITEABLE; - printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", - parts[i].name); - } - if ((slave->mtd.flags & MTD_WRITEABLE) && - (slave->mtd.size % slave->mtd.erasesize)) { - slave->mtd.flags &= ~MTD_WRITEABLE; - printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", - parts[i].name); + while (offs < slave->mtd.size) { + if (master->block_isbad(master, + offs + slave->offset)) + slave->mtd.ecc_stats.badblocks++; + offs += slave->mtd.erasesize; } + } - slave->mtd.ecclayout = master->ecclayout; - if (master->block_isbad) { - uint32_t offs = 0; +out_register: + if (part->mtdp) { + /* store the object pointer (caller may or may not register it*/ + *part->mtdp = &slave->mtd; + slave->registered = 0; + } else { + /* register our partition */ + add_mtd_device(&slave->mtd); + slave->registered = 1; + } + return slave; +} - while(offs < slave->mtd.size) { - if (master->block_isbad(master, - offs + slave->offset)) - slave->mtd.ecc_stats.badblocks++; - offs += slave->mtd.erasesize; - } - } +/* + * This function, given a master MTD object and a partition table, creates + * and registers slave MTD objects which are bound to the master according to + * the partition definitions. + * (Q: should we register the master MTD object as well?) + */ - if(parts[i].mtdp) - { /* store the object pointer (caller may or may not register it */ - *parts[i].mtdp = &slave->mtd; - slave->registered = 0; - } - else - { - /* register our partition */ - add_mtd_device(&slave->mtd); - slave->registered = 1; - } +int add_mtd_partitions(struct mtd_info *master, + const struct mtd_partition *parts, + int nbparts) +{ + struct mtd_part *slave; + u_int32_t cur_offset = 0; + int i; + + printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); + + for (i = 0; i < nbparts; i++) { + slave = add_one_partition(master, parts + i, i, cur_offset); + if (!slave) + return -ENOMEM; + cur_offset = slave->offset + slave->mtd.size; } return 0; } - EXPORT_SYMBOL(add_mtd_partitions); -EXPORT_SYMBOL(del_mtd_partitions); static DEFINE_SPINLOCK(part_parser_lock); static LIST_HEAD(part_parsers); static struct mtd_part_parser *get_partition_parser(const char *name) { - struct list_head *this; - void *ret = NULL; - spin_lock(&part_parser_lock); + struct mtd_part_parser *p, *ret = NULL; - list_for_each(this, &part_parsers) { - struct mtd_part_parser *p = list_entry(this, struct mtd_part_parser, list); + spin_lock(&part_parser_lock); + list_for_each_entry(p, &part_parsers, list) if (!strcmp(p->name, name) && try_module_get(p->owner)) { ret = p; break; } - } + spin_unlock(&part_parser_lock); return ret; @@ -538,6 +545,7 @@ int register_mtd_parser(struct mtd_part_parser *p) return 0; } +EXPORT_SYMBOL_GPL(register_mtd_parser); int deregister_mtd_parser(struct mtd_part_parser *p) { @@ -546,6 +554,7 @@ int deregister_mtd_parser(struct mtd_part_parser *p) spin_unlock(&part_parser_lock); return 0; } +EXPORT_SYMBOL_GPL(deregister_mtd_parser); int parse_mtd_partitions(struct mtd_info *master, const char **types, struct mtd_partition **pparts, unsigned long origin) @@ -573,7 +582,4 @@ int parse_mtd_partitions(struct mtd_info *master, const char **types, } return ret; } - EXPORT_SYMBOL_GPL(parse_mtd_partitions); -EXPORT_SYMBOL_GPL(register_mtd_parser); -EXPORT_SYMBOL_GPL(deregister_mtd_parser); diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 5076faf9ca66..71406e517857 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,5 +1,4 @@ # drivers/mtd/nand/Kconfig -# $Id: Kconfig,v 1.35 2005/11/07 11:14:30 gleixner Exp $ menuconfig MTD_NAND tristate "NAND Device Support" @@ -272,22 +271,23 @@ config MTD_NAND_CS553X If you say "m", the module will be called "cs553x_nand.ko". -config MTD_NAND_AT91 - bool "Support for NAND Flash / SmartMedia on AT91" - depends on ARCH_AT91 +config MTD_NAND_ATMEL + tristate "Support for NAND Flash / SmartMedia on AT91 and AVR32" + depends on ARCH_AT91 || AVR32 help Enables support for NAND Flash / Smart Media Card interface - on Atmel AT91 processors. + on Atmel AT91 and AVR32 processors. choice - prompt "ECC management for NAND Flash / SmartMedia on AT91" - depends on MTD_NAND_AT91 + prompt "ECC management for NAND Flash / SmartMedia on AT91 / AVR32" + depends on MTD_NAND_ATMEL -config MTD_NAND_AT91_ECC_HW +config MTD_NAND_ATMEL_ECC_HW bool "Hardware ECC" - depends on ARCH_AT91SAM9263 || ARCH_AT91SAM9260 + depends on ARCH_AT91SAM9263 || ARCH_AT91SAM9260 || AVR32 help - Uses hardware ECC provided by the at91sam9260/at91sam9263 chip - instead of software ECC. + Use hardware ECC instead of software ECC when the chip + supports it. + The hardware ECC controller is capable of single bit error correction and 2-bit random detection per page. @@ -297,16 +297,16 @@ config MTD_NAND_AT91_ECC_HW If unsure, say Y -config MTD_NAND_AT91_ECC_SOFT +config MTD_NAND_ATMEL_ECC_SOFT bool "Software ECC" help - Uses software ECC. + Use software ECC. NB : hardware and software ECC schemes are incompatible. If you switch from one to another, you'll have to erase your mtd partition. -config MTD_NAND_AT91_ECC_NONE +config MTD_NAND_ATMEL_ECC_NONE bool "No ECC (testing only, DANGEROUS)" depends on DEBUG_KERNEL help diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index a6e74a46992a..d772581de573 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -1,7 +1,6 @@ # # linux/drivers/nand/Makefile # -# $Id: Makefile.common,v 1.15 2004/11/26 12:28:22 dedekind Exp $ obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o @@ -24,7 +23,7 @@ obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o 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_ATMEL) += atmel_nand.o obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o diff --git a/drivers/mtd/nand/at91_nand.c b/drivers/mtd/nand/atmel_nand.c index 0adb287027a2..99aec46e2145 100644 --- a/drivers/mtd/nand/at91_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/at91_nand.c - * * Copyright (C) 2003 Rick Bronson * * Derived from drivers/mtd/nand/autcpu12.c @@ -31,20 +29,19 @@ #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> -#include <asm/io.h> -#include <asm/sizes.h> +#include <linux/gpio.h> +#include <linux/io.h> -#include <asm/hardware.h> #include <asm/arch/board.h> -#include <asm/arch/gpio.h> +#include <asm/arch/cpu.h> -#ifdef CONFIG_MTD_NAND_AT91_ECC_HW +#ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW #define hard_ecc 1 #else #define hard_ecc 0 #endif -#ifdef CONFIG_MTD_NAND_AT91_ECC_NONE +#ifdef CONFIG_MTD_NAND_ATMEL_ECC_NONE #define no_ecc 1 #else #define no_ecc 0 @@ -52,18 +49,18 @@ /* Register access macros */ #define ecc_readl(add, reg) \ - __raw_readl(add + AT91_ECC_##reg) + __raw_readl(add + ATMEL_ECC_##reg) #define ecc_writel(add, reg, value) \ - __raw_writel((value), add + AT91_ECC_##reg) + __raw_writel((value), add + ATMEL_ECC_##reg) -#include <asm/arch/at91_ecc.h> /* AT91SAM9260/3 ECC registers */ +#include "atmel_nand_ecc.h" /* Hardware ECC registers */ /* oob layout for large page size * bad block info is on bytes 0 and 1 * the bytes have to be consecutives to avoid * several NAND_CMD_RNDOUT during read */ -static struct nand_ecclayout at91_oobinfo_large = { +static struct nand_ecclayout atmel_oobinfo_large = { .eccbytes = 4, .eccpos = {60, 61, 62, 63}, .oobfree = { @@ -76,7 +73,7 @@ static struct nand_ecclayout at91_oobinfo_large = { * the bytes have to be consecutives to avoid * several NAND_CMD_RNDOUT during read */ -static struct nand_ecclayout at91_oobinfo_small = { +static struct nand_ecclayout atmel_oobinfo_small = { .eccbytes = 4, .eccpos = {0, 1, 2, 3}, .oobfree = { @@ -84,11 +81,11 @@ static struct nand_ecclayout at91_oobinfo_small = { }, }; -struct at91_nand_host { +struct atmel_nand_host { struct nand_chip nand_chip; struct mtd_info mtd; void __iomem *io_base; - struct at91_nand_data *board; + struct atmel_nand_data *board; struct device *dev; void __iomem *ecc; }; @@ -96,34 +93,34 @@ struct at91_nand_host { /* * Enable NAND. */ -static void at91_nand_enable(struct at91_nand_host *host) +static void atmel_nand_enable(struct atmel_nand_host *host) { if (host->board->enable_pin) - at91_set_gpio_value(host->board->enable_pin, 0); + gpio_set_value(host->board->enable_pin, 0); } /* * Disable NAND. */ -static void at91_nand_disable(struct at91_nand_host *host) +static void atmel_nand_disable(struct atmel_nand_host *host) { if (host->board->enable_pin) - at91_set_gpio_value(host->board->enable_pin, 1); + gpio_set_value(host->board->enable_pin, 1); } /* * Hardware specific access to control-lines */ -static void at91_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) { struct nand_chip *nand_chip = mtd->priv; - struct at91_nand_host *host = nand_chip->priv; + struct atmel_nand_host *host = nand_chip->priv; if (ctrl & NAND_CTRL_CHANGE) { if (ctrl & NAND_NCE) - at91_nand_enable(host); + atmel_nand_enable(host); else - at91_nand_disable(host); + atmel_nand_disable(host); } if (cmd == NAND_CMD_NONE) return; @@ -137,18 +134,49 @@ static void at91_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) /* * Read the Device Ready pin. */ -static int at91_nand_device_ready(struct mtd_info *mtd) +static int atmel_nand_device_ready(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd->priv; - struct at91_nand_host *host = nand_chip->priv; + struct atmel_nand_host *host = nand_chip->priv; - return at91_get_gpio_value(host->board->rdy_pin); + return gpio_get_value(host->board->rdy_pin); +} + +/* + * Minimal-overhead PIO for data access. + */ +static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + + __raw_readsb(nand_chip->IO_ADDR_R, buf, len); +} + +static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + + __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2); +} + +static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + + __raw_writesb(nand_chip->IO_ADDR_W, buf, len); +} + +static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + + __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2); } /* * write oob for small pages */ -static int at91_nand_write_oob_512(struct mtd_info *mtd, +static int atmel_nand_write_oob_512(struct mtd_info *mtd, struct nand_chip *chip, int page) { int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; @@ -176,7 +204,7 @@ static int at91_nand_write_oob_512(struct mtd_info *mtd, /* * read oob for small pages */ -static int at91_nand_read_oob_512(struct mtd_info *mtd, +static int atmel_nand_read_oob_512(struct mtd_info *mtd, struct nand_chip *chip, int page, int sndcmd) { if (sndcmd) { @@ -196,11 +224,11 @@ static int at91_nand_read_oob_512(struct mtd_info *mtd, * dat: raw data (unused) * ecc_code: buffer for ECC */ -static int at91_nand_calculate(struct mtd_info *mtd, +static int atmel_nand_calculate(struct mtd_info *mtd, const u_char *dat, unsigned char *ecc_code) { struct nand_chip *nand_chip = mtd->priv; - struct at91_nand_host *host = nand_chip->priv; + struct atmel_nand_host *host = nand_chip->priv; uint32_t *eccpos = nand_chip->ecc.layout->eccpos; unsigned int ecc_value; @@ -211,7 +239,7 @@ static int at91_nand_calculate(struct mtd_info *mtd, ecc_code[eccpos[1]] = (ecc_value >> 8) & 0xFF; /* get the last 2 ECC bytes */ - ecc_value = ecc_readl(host->ecc, NPR) & AT91_ECC_NPARITY; + ecc_value = ecc_readl(host->ecc, NPR) & ATMEL_ECC_NPARITY; ecc_code[eccpos[2]] = ecc_value & 0xFF; ecc_code[eccpos[3]] = (ecc_value >> 8) & 0xFF; @@ -226,7 +254,7 @@ static int at91_nand_calculate(struct mtd_info *mtd, * chip: nand chip info structure * buf: buffer to store read data */ -static int at91_nand_read_page(struct mtd_info *mtd, +static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf) { int eccsize = chip->ecc.size; @@ -237,6 +265,19 @@ static int at91_nand_read_page(struct mtd_info *mtd, uint8_t *ecc_pos; int stat; + /* + * Errata: ALE is incorrectly wired up to the ECC controller + * on the AP7000, so it will include the address cycles in the + * ECC calculation. + * + * Workaround: Reset the parity registers before reading the + * actual data. + */ + if (cpu_is_at32ap7000()) { + struct atmel_nand_host *host = chip->priv; + ecc_writel(host->ecc, CR, ATMEL_ECC_RST); + } + /* read the page */ chip->read_buf(mtd, p, eccsize); @@ -285,11 +326,11 @@ static int at91_nand_read_page(struct mtd_info *mtd, * * Detect and correct a 1 bit error for a page */ -static int at91_nand_correct(struct mtd_info *mtd, u_char *dat, +static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *isnull) { struct nand_chip *nand_chip = mtd->priv; - struct at91_nand_host *host = nand_chip->priv; + struct atmel_nand_host *host = nand_chip->priv; unsigned int ecc_status; unsigned int ecc_word, ecc_bit; @@ -297,43 +338,43 @@ static int at91_nand_correct(struct mtd_info *mtd, u_char *dat, ecc_status = ecc_readl(host->ecc, SR); /* if there's no error */ - if (likely(!(ecc_status & AT91_ECC_RECERR))) + if (likely(!(ecc_status & ATMEL_ECC_RECERR))) return 0; /* get error bit offset (4 bits) */ - ecc_bit = ecc_readl(host->ecc, PR) & AT91_ECC_BITADDR; + ecc_bit = ecc_readl(host->ecc, PR) & ATMEL_ECC_BITADDR; /* get word address (12 bits) */ - ecc_word = ecc_readl(host->ecc, PR) & AT91_ECC_WORDADDR; + ecc_word = ecc_readl(host->ecc, PR) & ATMEL_ECC_WORDADDR; ecc_word >>= 4; /* if there are multiple errors */ - if (ecc_status & AT91_ECC_MULERR) { + if (ecc_status & ATMEL_ECC_MULERR) { /* check if it is a freshly erased block * (filled with 0xff) */ - if ((ecc_bit == AT91_ECC_BITADDR) - && (ecc_word == (AT91_ECC_WORDADDR >> 4))) { + if ((ecc_bit == ATMEL_ECC_BITADDR) + && (ecc_word == (ATMEL_ECC_WORDADDR >> 4))) { /* the block has just been erased, return OK */ return 0; } /* it doesn't seems to be a freshly * erased block. * We can't correct so many errors */ - dev_dbg(host->dev, "at91_nand : multiple errors detected." + dev_dbg(host->dev, "atmel_nand : multiple errors detected." " Unable to correct.\n"); return -EIO; } /* if there's a single bit error : we can correct it */ - if (ecc_status & AT91_ECC_ECCERR) { + if (ecc_status & ATMEL_ECC_ECCERR) { /* there's nothing much to do here. * the bit error is on the ECC itself. */ - dev_dbg(host->dev, "at91_nand : one bit error on ECC code." + dev_dbg(host->dev, "atmel_nand : one bit error on ECC code." " Nothing to correct\n"); return 0; } - dev_dbg(host->dev, "at91_nand : one bit error on data." + dev_dbg(host->dev, "atmel_nand : one bit error on data." " (word offset in the page :" " 0x%x bit offset : 0x%x)\n", ecc_word, ecc_bit); @@ -345,14 +386,21 @@ static int at91_nand_correct(struct mtd_info *mtd, u_char *dat, /* 8 bits words */ dat[ecc_word] ^= (1 << ecc_bit); } - dev_dbg(host->dev, "at91_nand : error corrected\n"); + dev_dbg(host->dev, "atmel_nand : error corrected\n"); return 1; } /* - * Enable HW ECC : unsused + * Enable HW ECC : unused on most chips */ -static void at91_nand_hwctl(struct mtd_info *mtd, int mode) { ; } +static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) +{ + if (cpu_is_at32ap7000()) { + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + ecc_writel(host->ecc, CR, ATMEL_ECC_RST); + } +} #ifdef CONFIG_MTD_PARTITIONS static const char *part_probes[] = { "cmdlinepart", NULL }; @@ -361,9 +409,9 @@ static const char *part_probes[] = { "cmdlinepart", NULL }; /* * Probe for the NAND device. */ -static int __init at91_nand_probe(struct platform_device *pdev) +static int __init atmel_nand_probe(struct platform_device *pdev) { - struct at91_nand_host *host; + struct atmel_nand_host *host; struct mtd_info *mtd; struct nand_chip *nand_chip; struct resource *regs; @@ -375,24 +423,24 @@ static int __init at91_nand_probe(struct platform_device *pdev) int num_partitions = 0; #endif - /* Allocate memory for the device structure (and zero it) */ - host = kzalloc(sizeof(struct at91_nand_host), GFP_KERNEL); - if (!host) { - printk(KERN_ERR "at91_nand: failed to allocate device structure.\n"); - return -ENOMEM; - } - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) { - printk(KERN_ERR "at91_nand: can't get I/O resource mem\n"); + printk(KERN_ERR "atmel_nand: can't get I/O resource mem\n"); return -ENXIO; } + /* Allocate memory for the device structure (and zero it) */ + host = kzalloc(sizeof(struct atmel_nand_host), GFP_KERNEL); + if (!host) { + printk(KERN_ERR "atmel_nand: failed to allocate device structure.\n"); + return -ENOMEM; + } + host->io_base = ioremap(mem->start, mem->end - mem->start + 1); if (host->io_base == NULL) { - printk(KERN_ERR "at91_nand: ioremap failed\n"); - kfree(host); - return -EIO; + printk(KERN_ERR "atmel_nand: ioremap failed\n"); + res = -EIO; + goto err_nand_ioremap; } mtd = &host->mtd; @@ -407,14 +455,14 @@ static int __init at91_nand_probe(struct platform_device *pdev) /* Set address of NAND IO lines */ nand_chip->IO_ADDR_R = host->io_base; nand_chip->IO_ADDR_W = host->io_base; - nand_chip->cmd_ctrl = at91_nand_cmd_ctrl; + nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl; if (host->board->rdy_pin) - nand_chip->dev_ready = at91_nand_device_ready; + nand_chip->dev_ready = atmel_nand_device_ready; regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!regs && hard_ecc) { - printk(KERN_ERR "at91_nand: can't get I/O resource " + printk(KERN_ERR "atmel_nand: can't get I/O resource " "regs\nFalling back on software ECC\n"); } @@ -424,15 +472,15 @@ static int __init at91_nand_probe(struct platform_device *pdev) if (hard_ecc && regs) { host->ecc = ioremap(regs->start, regs->end - regs->start + 1); if (host->ecc == NULL) { - printk(KERN_ERR "at91_nand: ioremap failed\n"); + printk(KERN_ERR "atmel_nand: ioremap failed\n"); res = -EIO; goto err_ecc_ioremap; } nand_chip->ecc.mode = NAND_ECC_HW_SYNDROME; - nand_chip->ecc.calculate = at91_nand_calculate; - nand_chip->ecc.correct = at91_nand_correct; - nand_chip->ecc.hwctl = at91_nand_hwctl; - nand_chip->ecc.read_page = at91_nand_read_page; + nand_chip->ecc.calculate = atmel_nand_calculate; + nand_chip->ecc.correct = atmel_nand_correct; + nand_chip->ecc.hwctl = atmel_nand_hwctl; + nand_chip->ecc.read_page = atmel_nand_read_page; nand_chip->ecc.bytes = 4; nand_chip->ecc.prepad = 0; nand_chip->ecc.postpad = 0; @@ -440,24 +488,30 @@ static int __init at91_nand_probe(struct platform_device *pdev) nand_chip->chip_delay = 20; /* 20us command delay time */ - if (host->board->bus_width_16) /* 16-bit bus width */ + if (host->board->bus_width_16) { /* 16-bit bus width */ nand_chip->options |= NAND_BUSWIDTH_16; + nand_chip->read_buf = atmel_read_buf16; + nand_chip->write_buf = atmel_write_buf16; + } else { + nand_chip->read_buf = atmel_read_buf; + nand_chip->write_buf = atmel_write_buf; + } platform_set_drvdata(pdev, host); - at91_nand_enable(host); + atmel_nand_enable(host); if (host->board->det_pin) { - if (at91_get_gpio_value(host->board->det_pin)) { - printk ("No SmartMedia card inserted.\n"); + if (gpio_get_value(host->board->det_pin)) { + printk("No SmartMedia card inserted.\n"); res = ENXIO; - goto out; + goto err_no_card; } } /* first scan to find the device and get the page size */ if (nand_scan_ident(mtd, 1)) { res = -ENXIO; - goto out; + goto err_scan_ident; } if (nand_chip->ecc.mode == NAND_ECC_HW_SYNDROME) { @@ -467,22 +521,22 @@ static int __init at91_nand_probe(struct platform_device *pdev) /* set ECC page size and oob layout */ switch (mtd->writesize) { case 512: - nand_chip->ecc.layout = &at91_oobinfo_small; - nand_chip->ecc.read_oob = at91_nand_read_oob_512; - nand_chip->ecc.write_oob = at91_nand_write_oob_512; - ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_528); + nand_chip->ecc.layout = &atmel_oobinfo_small; + nand_chip->ecc.read_oob = atmel_nand_read_oob_512; + nand_chip->ecc.write_oob = atmel_nand_write_oob_512; + ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_528); break; case 1024: - nand_chip->ecc.layout = &at91_oobinfo_large; - ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_1056); + nand_chip->ecc.layout = &atmel_oobinfo_large; + ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_1056); break; case 2048: - nand_chip->ecc.layout = &at91_oobinfo_large; - ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_2112); + nand_chip->ecc.layout = &atmel_oobinfo_large; + ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_2112); break; case 4096: - nand_chip->ecc.layout = &at91_oobinfo_large; - ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_4224); + nand_chip->ecc.layout = &atmel_oobinfo_large; + ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_4224); break; default: /* page size not handled by HW ECC */ @@ -502,12 +556,12 @@ static int __init at91_nand_probe(struct platform_device *pdev) /* second phase scan */ if (nand_scan_tail(mtd)) { res = -ENXIO; - goto out; + goto err_scan_tail; } #ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_CMDLINE_PARTS - mtd->name = "at91_nand"; + mtd->name = "atmel_nand"; num_partitions = parse_mtd_partitions(mtd, part_probes, &partitions, 0); #endif @@ -516,9 +570,9 @@ static int __init at91_nand_probe(struct platform_device *pdev) &num_partitions); if ((!partitions) || (num_partitions == 0)) { - printk(KERN_ERR "at91_nand: No parititions defined, or unsupported device.\n"); + printk(KERN_ERR "atmel_nand: No parititions defined, or unsupported device.\n"); res = ENXIO; - goto release; + goto err_no_partitions; } res = add_mtd_partitions(mtd, partitions, num_partitions); @@ -530,17 +584,19 @@ static int __init at91_nand_probe(struct platform_device *pdev) return res; #ifdef CONFIG_MTD_PARTITIONS -release: +err_no_partitions: #endif nand_release(mtd); - -out: - iounmap(host->ecc); - -err_ecc_ioremap: - at91_nand_disable(host); +err_scan_tail: +err_scan_ident: +err_no_card: + atmel_nand_disable(host); platform_set_drvdata(pdev, NULL); + if (host->ecc) + iounmap(host->ecc); +err_ecc_ioremap: iounmap(host->io_base); +err_nand_ioremap: kfree(host); return res; } @@ -548,47 +604,47 @@ err_ecc_ioremap: /* * Remove a NAND device. */ -static int __devexit at91_nand_remove(struct platform_device *pdev) +static int __exit atmel_nand_remove(struct platform_device *pdev) { - struct at91_nand_host *host = platform_get_drvdata(pdev); + struct atmel_nand_host *host = platform_get_drvdata(pdev); struct mtd_info *mtd = &host->mtd; nand_release(mtd); - at91_nand_disable(host); + atmel_nand_disable(host); + if (host->ecc) + iounmap(host->ecc); iounmap(host->io_base); - iounmap(host->ecc); kfree(host); return 0; } -static struct platform_driver at91_nand_driver = { - .probe = at91_nand_probe, - .remove = at91_nand_remove, +static struct platform_driver atmel_nand_driver = { + .remove = __exit_p(atmel_nand_remove), .driver = { - .name = "at91_nand", + .name = "atmel_nand", .owner = THIS_MODULE, }, }; -static int __init at91_nand_init(void) +static int __init atmel_nand_init(void) { - return platform_driver_register(&at91_nand_driver); + return platform_driver_probe(&atmel_nand_driver, atmel_nand_probe); } -static void __exit at91_nand_exit(void) +static void __exit atmel_nand_exit(void) { - platform_driver_unregister(&at91_nand_driver); + platform_driver_unregister(&atmel_nand_driver); } -module_init(at91_nand_init); -module_exit(at91_nand_exit); +module_init(atmel_nand_init); +module_exit(atmel_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Rick Bronson"); -MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91RM9200 / AT91SAM9"); -MODULE_ALIAS("platform:at91_nand"); +MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91 / AVR32"); +MODULE_ALIAS("platform:atmel_nand"); diff --git a/drivers/mtd/nand/atmel_nand_ecc.h b/drivers/mtd/nand/atmel_nand_ecc.h new file mode 100644 index 000000000000..1ee7f993db1c --- /dev/null +++ b/drivers/mtd/nand/atmel_nand_ecc.h @@ -0,0 +1,36 @@ +/* + * Error Corrected Code Controller (ECC) - System peripherals regsters. + * Based on AT91SAM9260 datasheet revision B. + * + * 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 ATMEL_NAND_ECC_H +#define ATMEL_NAND_ECC_H + +#define ATMEL_ECC_CR 0x00 /* Control register */ +#define ATMEL_ECC_RST (1 << 0) /* Reset parity */ + +#define ATMEL_ECC_MR 0x04 /* Mode register */ +#define ATMEL_ECC_PAGESIZE (3 << 0) /* Page Size */ +#define ATMEL_ECC_PAGESIZE_528 (0) +#define ATMEL_ECC_PAGESIZE_1056 (1) +#define ATMEL_ECC_PAGESIZE_2112 (2) +#define ATMEL_ECC_PAGESIZE_4224 (3) + +#define ATMEL_ECC_SR 0x08 /* Status register */ +#define ATMEL_ECC_RECERR (1 << 0) /* Recoverable Error */ +#define ATMEL_ECC_ECCERR (1 << 1) /* ECC Single Bit Error */ +#define ATMEL_ECC_MULERR (1 << 2) /* Multiple Errors */ + +#define ATMEL_ECC_PR 0x0c /* Parity register */ +#define ATMEL_ECC_BITADDR (0xf << 0) /* Bit Error Address */ +#define ATMEL_ECC_WORDADDR (0xfff << 4) /* Word Error Address */ + +#define ATMEL_ECC_NPR 0x10 /* NParity register */ +#define ATMEL_ECC_NPARITY (0xffff << 0) /* NParity */ + +#endif diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 09e421a96893..761946ea45b1 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -3,8 +3,6 @@ * * Copyright (C) 2004 Embedded Edge, LLC * - * $Id: au1550nd.c,v 1.13 2005/11/07 11:14:30 gleixner Exp $ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. @@ -604,8 +602,6 @@ module_init(au1xxx_nand_init); */ static void __exit au1550_cleanup(void) { - struct nand_chip *this = (struct nand_chip *)&au1550_mtd[1]; - /* Release resources, unregister device */ nand_release(au1550_mtd); diff --git a/drivers/mtd/nand/autcpu12.c b/drivers/mtd/nand/autcpu12.c index dd38011ee0b7..553dd7e9b41c 100644 --- a/drivers/mtd/nand/autcpu12.c +++ b/drivers/mtd/nand/autcpu12.c @@ -6,8 +6,6 @@ * Derived from drivers/mtd/spia.c * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * - * $Id: autcpu12.c,v 1.23 2005/11/07 11:14:30 gleixner Exp $ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index da6ceaa80ba1..95345d051579 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -626,10 +626,12 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, { struct mtd_info *mtd; struct cafe_priv *cafe; - struct mtd_partition *parts; uint32_t ctrl; - int nr_parts; int err = 0; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts; + int nr_parts; +#endif /* Very old versions shared the same PCI ident for all three functions on the chip. Verify the class too... */ diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c index cb663ef245d5..fc8529bedfdf 100644 --- a/drivers/mtd/nand/cmx270_nand.c +++ b/drivers/mtd/nand/cmx270_nand.c @@ -20,9 +20,11 @@ #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> +#include <linux/gpio.h> #include <asm/io.h> #include <asm/irq.h> +#include <asm/mach-types.h> #include <asm/arch/hardware.h> #include <asm/arch/pxa-regs.h> @@ -30,20 +32,6 @@ #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; @@ -103,14 +91,14 @@ static int cmx270_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) static inline void nand_cs_on(void) { - GPCR(GPIO_NAND_CS) = GPIO_bit(GPIO_NAND_CS); + gpio_set_value(GPIO_NAND_CS, 0); } static void nand_cs_off(void) { - DRAIN_WB(); + dsb(); - GPSR(GPIO_NAND_CS) = GPIO_bit(GPIO_NAND_CS); + gpio_set_value(GPIO_NAND_CS, 1); } /* @@ -122,7 +110,7 @@ static void cmx270_hwcontrol(struct mtd_info *mtd, int dat, struct nand_chip* this = mtd->priv; unsigned int nandaddr = (unsigned int)this->IO_ADDR_W; - DRAIN_WB(); + dsb(); if (ctrl & NAND_CTRL_CHANGE) { if ( ctrl & NAND_ALE ) @@ -139,12 +127,12 @@ static void cmx270_hwcontrol(struct mtd_info *mtd, int dat, nand_cs_off(); } - DRAIN_WB(); + dsb(); this->IO_ADDR_W = (void __iomem*)nandaddr; if (dat != NAND_CMD_NONE) writel((dat << 16), this->IO_ADDR_W); - DRAIN_WB(); + dsb(); } /* @@ -152,9 +140,9 @@ static void cmx270_hwcontrol(struct mtd_info *mtd, int dat, */ static int cmx270_device_ready(struct mtd_info *mtd) { - DRAIN_WB(); + dsb(); - return (GPLR(GPIO_NAND_RB) & GPIO_bit(GPIO_NAND_RB)); + return (gpio_get_value(GPIO_NAND_RB)); } /* @@ -168,20 +156,40 @@ static int cmx270_init(void) int mtd_parts_nb = 0; int ret; + if (!machine_is_armcore()) + return -ENODEV; + + ret = gpio_request(GPIO_NAND_CS, "NAND CS"); + if (ret) { + pr_warning("CM-X270: failed to request NAND CS gpio\n"); + return ret; + } + + gpio_direction_output(GPIO_NAND_CS, 1); + + ret = gpio_request(GPIO_NAND_RB, "NAND R/B"); + if (ret) { + pr_warning("CM-X270: failed to request NAND R/B gpio\n"); + goto err_gpio_request; + } + + gpio_direction_input(GPIO_NAND_RB); + /* 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; + pr_debug("Unable to allocate CM-X270 NAND MTD device structure.\n"); + ret = -ENOMEM; + goto err_kzalloc; } cmx270_nand_io = ioremap(PXA_CS1_PHYS, 12); if (!cmx270_nand_io) { - printk("Unable to ioremap NAND device\n"); + pr_debug("Unable to ioremap NAND device\n"); ret = -EINVAL; - goto err1; + goto err_ioremap; } /* Get pointer to private data */ @@ -209,9 +217,9 @@ static int cmx270_init(void) /* Scan to find existence of the device */ if (nand_scan (cmx270_nand_mtd, 1)) { - printk(KERN_NOTICE "No NAND device\n"); + pr_notice("No NAND device\n"); ret = -ENXIO; - goto err2; + goto err_scan; } #ifdef CONFIG_MTD_CMDLINE_PARTS @@ -229,18 +237,22 @@ static int cmx270_init(void) } /* Register the partitions */ - printk(KERN_NOTICE "Using %s partition definition\n", part_type); + pr_notice("Using %s partition definition\n", part_type); ret = add_mtd_partitions(cmx270_nand_mtd, mtd_parts, mtd_parts_nb); if (ret) - goto err2; + goto err_scan; /* Return happy */ return 0; -err2: +err_scan: iounmap(cmx270_nand_io); -err1: +err_ioremap: kfree(cmx270_nand_mtd); +err_kzalloc: + gpio_free(GPIO_NAND_RB); +err_gpio_request: + gpio_free(GPIO_NAND_CS); return ret; @@ -255,6 +267,9 @@ static void cmx270_cleanup(void) /* Release resources, unregister device */ nand_release(cmx270_nand_mtd); + gpio_free(GPIO_NAND_RB); + gpio_free(GPIO_NAND_CS); + iounmap(cmx270_nand_io); /* Free the MTD device structure */ diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 0e72153b3297..765d4f0f7c86 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -15,8 +15,6 @@ * converted to the generic Reed-Solomon library by Thomas Gleixner <tglx@linutronix.de> * * Interface to generic NAND code for M-Systems DiskOnChip devices - * - * $Id: diskonchip.c,v 1.55 2005/11/07 11:14:30 gleixner Exp $ */ #include <linux/kernel.h> @@ -54,8 +52,6 @@ static unsigned long __initdata doc_locations[] = { 0xe0000, 0xe2000, 0xe4000, 0xe6000, 0xe8000, 0xea000, 0xec000, 0xee000, #endif /* CONFIG_MTD_DOCPROBE_HIGH */ -#elif defined(__PPC__) - 0xe4000000, #else #warning Unknown architecture for DiskOnChip. No default probe locations defined #endif diff --git a/drivers/mtd/nand/edb7312.c b/drivers/mtd/nand/edb7312.c index ba67bbec20d3..387e4352903e 100644 --- a/drivers/mtd/nand/edb7312.c +++ b/drivers/mtd/nand/edb7312.c @@ -6,8 +6,6 @@ * Derived from drivers/mtd/nand/autcpu12.c * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * - * $Id: edb7312.c,v 1.12 2005/11/07 11:14:30 gleixner Exp $ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. diff --git a/drivers/mtd/nand/excite_nandflash.c b/drivers/mtd/nand/excite_nandflash.c index bed87290decc..ced14b5294d5 100644 --- a/drivers/mtd/nand/excite_nandflash.c +++ b/drivers/mtd/nand/excite_nandflash.c @@ -209,7 +209,7 @@ static int __init excite_nand_probe(struct device *dev) 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]); + ARRAY_SIZE(partition_info)); } else { iounmap(drvdata->regs); kfree(drvdata); diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 4b69aacdf5ca..9dff51351f4f 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -89,7 +89,6 @@ static struct nand_ecclayout fsl_elbc_oob_sp_eccm0 = { .eccbytes = 3, .eccpos = {6, 7, 8}, .oobfree = { {0, 5}, {9, 7} }, - .oobavail = 12, }; /* Small Page FLASH with FMR[ECCM] = 1 */ @@ -97,7 +96,6 @@ static struct nand_ecclayout fsl_elbc_oob_sp_eccm1 = { .eccbytes = 3, .eccpos = {8, 9, 10}, .oobfree = { {0, 5}, {6, 2}, {11, 5} }, - .oobavail = 12, }; /* Large Page FLASH with FMR[ECCM] = 0 */ @@ -105,7 +103,6 @@ static struct nand_ecclayout fsl_elbc_oob_lp_eccm0 = { .eccbytes = 12, .eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56}, .oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} }, - .oobavail = 48, }; /* Large Page FLASH with FMR[ECCM] = 1 */ @@ -113,7 +110,48 @@ static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = { .eccbytes = 12, .eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58}, .oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} }, - .oobavail = 48, +}; + +/* + * fsl_elbc_oob_lp_eccm* specify that LP NAND's OOB free area starts at offset + * 1, so we have to adjust bad block pattern. This pattern should be used for + * x8 chips only. So far hardware does not support x16 chips anyway. + */ +static u8 scan_ff_pattern[] = { 0xff, }; + +static struct nand_bbt_descr largepage_memorybased = { + .options = 0, + .offs = 0, + .len = 1, + .pattern = scan_ff_pattern, +}; + +/* + * ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt, + * interfere with ECC positions, that's why we implement our own descriptors. + * OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0. + */ +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 11, + .len = 4, + .veroffs = 15, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 11, + .len = 4, + .veroffs = 15, + .maxblocks = 4, + .pattern = mirror_pattern, }; /*=================================*/ @@ -687,8 +725,7 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) chip->ecc.layout = (priv->fmr & FMR_ECCM) ? &fsl_elbc_oob_lp_eccm1 : &fsl_elbc_oob_lp_eccm0; - mtd->ecclayout = chip->ecc.layout; - mtd->oobavail = chip->ecc.layout->oobavail; + chip->badblock_pattern = &largepage_memorybased; } } else { dev_err(ctrl->dev, @@ -752,8 +789,12 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) chip->cmdfunc = fsl_elbc_cmdfunc; chip->waitfunc = fsl_elbc_wait; + chip->bbt_td = &bbt_main_descr; + chip->bbt_md = &bbt_mirror_descr; + /* set up nand options */ - chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR; + chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR | + NAND_USE_FLASH_BBT; chip->controller = &ctrl->controller; chip->priv = priv; @@ -795,8 +836,8 @@ static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv) return 0; } -static int fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, - struct device_node *node) +static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, + struct device_node *node) { struct fsl_lbc_regs __iomem *lbc = ctrl->regs; struct fsl_elbc_mtd *priv; @@ -917,7 +958,7 @@ static int __devinit fsl_elbc_ctrl_init(struct fsl_elbc_ctrl *ctrl) return 0; } -static int __devexit fsl_elbc_ctrl_remove(struct of_device *ofdev) +static int fsl_elbc_ctrl_remove(struct of_device *ofdev) { struct fsl_elbc_ctrl *ctrl = dev_get_drvdata(&ofdev->dev); int i; @@ -1041,7 +1082,7 @@ static struct of_platform_driver fsl_elbc_ctrl_driver = { }, .match_table = fsl_elbc_match, .probe = fsl_elbc_ctrl_probe, - .remove = __devexit_p(fsl_elbc_ctrl_remove), + .remove = fsl_elbc_ctrl_remove, }; static int __init fsl_elbc_init(void) diff --git a/drivers/mtd/nand/h1910.c b/drivers/mtd/nand/h1910.c index 2d585d2d090c..9e59de501c2e 100644 --- a/drivers/mtd/nand/h1910.c +++ b/drivers/mtd/nand/h1910.c @@ -7,8 +7,6 @@ * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * - * $Id: h1910.c,v 1.6 2005/11/07 11:14:30 gleixner Exp $ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index ba1bdf787323..d1129bae6c27 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -798,6 +798,87 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, } /** + * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @dataofs offset of requested data within the page + * @readlen data length + * @buf: buffer to store read data + */ +static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) +{ + int start_step, end_step, num_steps; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *p; + int data_col_addr, i, gaps = 0; + int datafrag_len, eccfrag_len, aligned_len, aligned_pos; + int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; + + /* Column address wihin the page aligned to ECC size (256bytes). */ + start_step = data_offs / chip->ecc.size; + end_step = (data_offs + readlen - 1) / chip->ecc.size; + num_steps = end_step - start_step + 1; + + /* Data size aligned to ECC ecc.size*/ + datafrag_len = num_steps * chip->ecc.size; + eccfrag_len = num_steps * chip->ecc.bytes; + + data_col_addr = start_step * chip->ecc.size; + /* If we read not a page aligned data */ + if (data_col_addr != 0) + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1); + + p = bufpoi + data_col_addr; + chip->read_buf(mtd, p, datafrag_len); + + /* Calculate ECC */ + for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) + chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]); + + /* The performance is faster if to position offsets + according to ecc.pos. Let make sure here that + there are no gaps in ecc positions */ + for (i = 0; i < eccfrag_len - 1; i++) { + if (eccpos[i + start_step * chip->ecc.bytes] + 1 != + eccpos[i + start_step * chip->ecc.bytes + 1]) { + gaps = 1; + break; + } + } + if (gaps) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + } else { + /* send the command to read the particular ecc bytes */ + /* take care about buswidth alignment in read_buf */ + aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1); + aligned_len = eccfrag_len; + if (eccpos[start_step * chip->ecc.bytes] & (busw - 1)) + aligned_len++; + if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1)) + aligned_len++; + + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1); + chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); + } + + for (i = 0; i < eccfrag_len; i++) + chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]]; + + p = bufpoi + data_col_addr; + for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) { + int stat; + + stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); + if (stat == -1) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/** * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function * @mtd: mtd info structure * @chip: nand chip info structure @@ -994,6 +1075,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, /* Now read the page into the buffer */ if (unlikely(ops->mode == MTD_OOB_RAW)) ret = chip->ecc.read_page_raw(mtd, chip, bufpoi); + else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) + ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); else ret = chip->ecc.read_page(mtd, chip, bufpoi); if (ret < 0) @@ -1001,7 +1084,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, /* Transfer not aligned data */ if (!aligned) { - chip->pagebuf = realpage; + if (!NAND_SUBPAGE_READ(chip) && !oob) + chip->pagebuf = realpage; memcpy(buf, chip->buffers->databuf + col, bytes); } @@ -2521,6 +2605,7 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.calculate = nand_calculate_ecc; chip->ecc.correct = nand_correct_data; chip->ecc.read_page = nand_read_page_swecc; + chip->ecc.read_subpage = nand_read_subpage; chip->ecc.write_page = nand_write_page_swecc; chip->ecc.read_oob = nand_read_oob_std; chip->ecc.write_oob = nand_write_oob_std; diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 5e121ceaa598..0b1c48595f12 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -6,8 +6,6 @@ * * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) * - * $Id: nand_bbt.c,v 1.36 2005/11/07 11:14:30 gleixner Exp $ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index 9003a135e050..918a806a8471 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -9,8 +9,6 @@ * * Copyright (C) 2006 Thomas Gleixner <tglx@linutronix.de> * - * $Id: nand_ecc.c,v 1.15 2005/11/07 11:14:30 gleixner Exp $ - * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 or (at your option) any diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index a3e3ab0185d5..69ee2c90eb0b 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -3,8 +3,6 @@ * * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) * - * $Id: nand_ids.c,v 1.16 2005/11/07 11:14:31 gleixner Exp $ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index bb885d1fcab5..ecd70e2504f6 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -21,8 +21,6 @@ * 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 - * - * $Id: nandsim.c,v 1.8 2005/03/19 15:33:56 dedekind Exp $ */ #include <linux/init.h> @@ -39,6 +37,7 @@ #include <linux/delay.h> #include <linux/list.h> #include <linux/random.h> +#include <asm/div64.h> /* Default simulator parameters values */ #if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ @@ -298,11 +297,11 @@ struct nandsim { /* NAND flash "geometry" */ struct nandsin_geometry { - uint32_t totsz; /* total flash size, bytes */ + uint64_t totsz; /* total flash size, bytes */ uint32_t secsz; /* flash sector (erase block) size, bytes */ uint pgsz; /* NAND flash page size, bytes */ uint oobsz; /* page OOB area size, bytes */ - uint32_t totszoob; /* total flash size including OOB, bytes */ + uint64_t totszoob; /* total flash size including OOB, bytes */ uint pgszoob; /* page size including OOB , bytes*/ uint secszoob; /* sector size including OOB, bytes */ uint pgnum; /* total number of pages */ @@ -459,6 +458,12 @@ static char *get_partition_name(int i) return kstrdup(buf, GFP_KERNEL); } +static u_int64_t divide(u_int64_t n, u_int32_t d) +{ + do_div(n, d); + return n; +} + /* * Initialize the nandsim structure. * @@ -469,8 +474,8 @@ 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, ret = 0; - u_int32_t remains; - u_int32_t next_offset; + u_int64_t remains; + u_int64_t next_offset; if (NS_IS_INITIALIZED(ns)) { NS_ERR("init_nandsim: nandsim is already initialized\n"); @@ -487,8 +492,8 @@ static int init_nandsim(struct mtd_info *mtd) ns->geom.oobsz = mtd->oobsize; ns->geom.secsz = mtd->erasesize; ns->geom.pgszoob = ns->geom.pgsz + ns->geom.oobsz; - ns->geom.pgnum = ns->geom.totsz / ns->geom.pgsz; - ns->geom.totszoob = ns->geom.totsz + ns->geom.pgnum * ns->geom.oobsz; + ns->geom.pgnum = divide(ns->geom.totsz, ns->geom.pgsz); + ns->geom.totszoob = ns->geom.totsz + (uint64_t)ns->geom.pgnum * ns->geom.oobsz; ns->geom.secshift = ffs(ns->geom.secsz) - 1; ns->geom.pgshift = chip->page_shift; ns->geom.oobshift = ffs(ns->geom.oobsz) - 1; @@ -511,7 +516,7 @@ static int init_nandsim(struct mtd_info *mtd) } if (ns->options & OPT_SMALLPAGE) { - if (ns->geom.totsz < (32 << 20)) { + if (ns->geom.totsz <= (32 << 20)) { ns->geom.pgaddrbytes = 3; ns->geom.secaddrbytes = 2; } else { @@ -537,15 +542,16 @@ static int init_nandsim(struct mtd_info *mtd) 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) { + u_int64_t part_sz = (u_int64_t)parts[i] * ns->geom.secsz; + + if (!part_sz || part_sz > remains) { 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; + ns->partitions[i].size = part_sz; next_offset += ns->partitions[i].size; remains -= ns->partitions[i].size; } @@ -573,7 +579,7 @@ static int init_nandsim(struct mtd_info *mtd) if (ns->busw == 16) NS_WARN("16-bit flashes support wasn't tested\n"); - printk("flash size: %u MiB\n", ns->geom.totsz >> 20); + printk("flash size: %llu MiB\n", ns->geom.totsz >> 20); printk("page size: %u bytes\n", ns->geom.pgsz); printk("OOB area size: %u bytes\n", ns->geom.oobsz); printk("sector size: %u KiB\n", ns->geom.secsz >> 10); @@ -583,7 +589,7 @@ static int init_nandsim(struct mtd_info *mtd) printk("bits in sector size: %u\n", ns->geom.secshift); printk("bits in page size: %u\n", ns->geom.pgshift); printk("bits in OOB size: %u\n", ns->geom.oobshift); - printk("flash size with OOB: %u KiB\n", ns->geom.totszoob >> 10); + printk("flash size with OOB: %llu KiB\n", ns->geom.totszoob >> 10); printk("page address bytes: %u\n", ns->geom.pgaddrbytes); printk("sector address bytes: %u\n", ns->geom.secaddrbytes); printk("options: %#x\n", ns->options); @@ -825,7 +831,7 @@ static int setup_wear_reporting(struct mtd_info *mtd) if (!rptwear) return 0; - wear_eb_count = mtd->size / mtd->erasesize; + wear_eb_count = divide(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"); @@ -2013,7 +2019,7 @@ static int __init ns_init_module(void) } if (overridesize) { - u_int32_t new_size = nsmtd->erasesize << overridesize; + u_int64_t new_size = (u_int64_t)nsmtd->erasesize << overridesize; if (new_size >> overridesize != nsmtd->erasesize) { NS_ERR("overridesize is too big\n"); goto err_exit; @@ -2021,7 +2027,8 @@ static int __init ns_init_module(void) /* 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; + chip->chip_shift = ffs(nsmtd->erasesize) + overridesize - 1; + chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; } if ((retval = setup_wear_reporting(nsmtd)) != 0) diff --git a/drivers/mtd/nand/ppchameleonevb.c b/drivers/mtd/nand/ppchameleonevb.c index 082073acf20f..cc8658431851 100644 --- a/drivers/mtd/nand/ppchameleonevb.c +++ b/drivers/mtd/nand/ppchameleonevb.c @@ -6,8 +6,6 @@ * Derived from drivers/mtd/nand/edb7312.c * * - * $Id: ppchameleonevb.c,v 1.7 2005/11/07 11:14:31 gleixner Exp $ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index 26f88215bc47..a033c4cd8e16 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -6,8 +6,6 @@ * Derived from drivers/mtd/nand/spia.c * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * - * $Id: rtc_from4.c,v 1.10 2005/11/07 11:14:31 gleixner Exp $ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index b34a460ab679..556139ed1fdf 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -1,26 +1,10 @@ /* linux/drivers/mtd/nand/s3c2410.c * - * Copyright (c) 2004,2005 Simtec Electronics - * http://www.simtec.co.uk/products/SWLINUX/ + * Copyright © 2004-2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ * Ben Dooks <ben@simtec.co.uk> * - * Samsung S3C2410/S3C240 NAND driver - * - * Changelog: - * 21-Sep-2004 BJD Initial version - * 23-Sep-2004 BJD Multiple device support - * 28-Sep-2004 BJD Fixed ECC placement for Hardware mode - * 12-Oct-2004 BJD Fixed errors in use of platform data - * 18-Feb-2005 BJD Fix sparse errors - * 14-Mar-2005 BJD Applied tglx's code reduction patch - * 02-May-2005 BJD Fixed s3c2440 support - * 02-May-2005 BJD Reduced hwcontrol decode - * 20-Jun-2005 BJD Updated s3c2440 support, fixed timing bug - * 08-Jul-2005 BJD Fix OOPS when no platform data supplied - * 20-Oct-2005 BJD Fix timing calculation bug - * 14-Jan-2006 BJD Allow clock to be stopped when idle - * - * $Id: s3c2410.c,v 1.23 2006/04/01 18:06:29 bjd Exp $ + * Samsung S3C2410/S3C2440/S3C2412 NAND 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 @@ -52,6 +36,7 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/clk.h> +#include <linux/cpufreq.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> @@ -120,8 +105,13 @@ struct s3c2410_nand_info { int sel_bit; int mtd_count; unsigned long save_sel; + unsigned long clk_rate; enum s3c_cpu_type cpu_type; + +#ifdef CONFIG_CPU_FREQ + struct notifier_block freq_transition; +#endif }; /* conversion functions */ @@ -179,17 +169,18 @@ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) /* controller setup */ -static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, - struct platform_device *pdev) +static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) { - struct s3c2410_platform_nand *plat = to_nand_plat(pdev); - unsigned long clkrate = clk_get_rate(info->clk); + struct s3c2410_platform_nand *plat = info->platform; int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4; int tacls, twrph0, twrph1; - unsigned long cfg = 0; + unsigned long clkrate = clk_get_rate(info->clk); + unsigned long set, cfg, mask; + unsigned long flags; /* calculate the timing information for the controller */ + info->clk_rate = clkrate; clkrate /= 1000; /* turn clock into kHz for ease of use */ if (plat != NULL) { @@ -211,28 +202,69 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n", tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate)); + switch (info->cpu_type) { + case TYPE_S3C2410: + mask = (S3C2410_NFCONF_TACLS(3) | + S3C2410_NFCONF_TWRPH0(7) | + S3C2410_NFCONF_TWRPH1(7)); + set = S3C2410_NFCONF_EN; + set |= S3C2410_NFCONF_TACLS(tacls - 1); + set |= S3C2410_NFCONF_TWRPH0(twrph0 - 1); + set |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); + break; + + case TYPE_S3C2440: + case TYPE_S3C2412: + mask = (S3C2410_NFCONF_TACLS(tacls_max - 1) | + S3C2410_NFCONF_TWRPH0(7) | + S3C2410_NFCONF_TWRPH1(7)); + + set = S3C2440_NFCONF_TACLS(tacls - 1); + set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); + set |= S3C2440_NFCONF_TWRPH1(twrph1 - 1); + break; + + default: + /* keep compiler happy */ + mask = 0; + set = 0; + BUG(); + } + + dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); + + local_irq_save(flags); + + cfg = readl(info->regs + S3C2410_NFCONF); + cfg &= ~mask; + cfg |= set; + writel(cfg, info->regs + S3C2410_NFCONF); + + local_irq_restore(flags); + + return 0; +} + +static int s3c2410_nand_inithw(struct s3c2410_nand_info *info) +{ + int ret; + + ret = s3c2410_nand_setrate(info); + if (ret < 0) + return ret; + switch (info->cpu_type) { case TYPE_S3C2410: - cfg = S3C2410_NFCONF_EN; - cfg |= S3C2410_NFCONF_TACLS(tacls - 1); - cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1); - cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); + default: break; case TYPE_S3C2440: case TYPE_S3C2412: - cfg = S3C2440_NFCONF_TACLS(tacls - 1); - cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); - cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1); - /* enable the controller and de-assert nFCE */ writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT); } - dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); - - writel(cfg, info->regs + S3C2410_NFCONF); return 0; } @@ -513,6 +545,52 @@ static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int writesl(info->regs + S3C2440_NFDATA, buf, len / 4); } +/* cpufreq driver support */ + +#ifdef CONFIG_CPU_FREQ + +static int s3c2410_nand_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct s3c2410_nand_info *info; + unsigned long newclk; + + info = container_of(nb, struct s3c2410_nand_info, freq_transition); + newclk = clk_get_rate(info->clk); + + if ((val == CPUFREQ_POSTCHANGE && newclk < info->clk_rate) || + (val == CPUFREQ_PRECHANGE && newclk > info->clk_rate)) { + s3c2410_nand_setrate(info); + } + + return 0; +} + +static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info) +{ + info->freq_transition.notifier_call = s3c2410_nand_cpufreq_transition; + + return cpufreq_register_notifier(&info->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) +{ + cpufreq_unregister_notifier(&info->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +#else +static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info) +{ + return 0; +} + +static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) +{ +} +#endif + /* device management functions */ static int s3c2410_nand_remove(struct platform_device *pdev) @@ -524,9 +602,10 @@ static int s3c2410_nand_remove(struct platform_device *pdev) if (info == NULL) return 0; - /* first thing we need to do is release all our mtds - * and their partitions, then go through freeing the - * resources used + s3c2410_nand_cpufreq_deregister(info); + + /* Release all our mtds and their partitions, then go through + * freeing the resources used */ if (info->mtds != NULL) { @@ -691,7 +770,8 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, { struct nand_chip *chip = &nmtd->chip; - printk("%s: chip %p: %d\n", __func__, chip, chip->page_shift); + dev_dbg(info->device, "chip %p => page shift %d\n", + chip, chip->page_shift); if (hardware_ecc) { /* change the behaviour depending on wether we are using @@ -784,7 +864,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, /* initialise the hardware */ - err = s3c2410_nand_inithw(info, pdev); + err = s3c2410_nand_inithw(info); if (err != 0) goto exit_error; @@ -827,6 +907,12 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, sets++; } + err = s3c2410_nand_cpufreq_register(info); + if (err < 0) { + dev_err(&pdev->dev, "failed to init cpufreq support\n"); + goto exit_error; + } + if (allow_clk_stop(info)) { dev_info(&pdev->dev, "clock idle support enabled\n"); clk_disable(info->clk); @@ -874,7 +960,7 @@ static int s3c24xx_nand_resume(struct platform_device *dev) if (info) { clk_enable(info->clk); - s3c2410_nand_inithw(info, dev); + s3c2410_nand_inithw(info); /* Restore the state of the nFCE line. */ diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 033f8800b1e6..6dba2fb66ae5 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -3,8 +3,6 @@ * * Copyright (C) 2004 Richard Purdie * - * $Id: sharpsl.c,v 1.7 2005/11/07 11:14:31 gleixner Exp $ - * * Based on Sharp's NAND driver sharp_sl.c * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/mtd/nand/spia.c b/drivers/mtd/nand/spia.c index 1f6d429b1583..0cc6d0acb8fe 100644 --- a/drivers/mtd/nand/spia.c +++ b/drivers/mtd/nand/spia.c @@ -8,8 +8,6 @@ * to controllines (due to change in nand.c) * page_cache added * - * $Id: spia.c,v 1.25 2005/11/07 11:14:31 gleixner Exp $ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. diff --git a/drivers/mtd/nand/toto.c b/drivers/mtd/nand/toto.c index f9e2d4a0ab8c..bbf492e6830d 100644 --- a/drivers/mtd/nand/toto.c +++ b/drivers/mtd/nand/toto.c @@ -14,8 +14,6 @@ * Overview: * This is a device driver for the NAND flash device found on the * TI fido board. It supports 32MiB and 64MiB cards - * - * $Id: toto.c,v 1.5 2005/11/07 11:14:31 gleixner Exp $ */ #include <linux/slab.h> diff --git a/drivers/mtd/nand/ts7250.c b/drivers/mtd/nand/ts7250.c index f40081069ab2..807a72752eeb 100644 --- a/drivers/mtd/nand/ts7250.c +++ b/drivers/mtd/nand/ts7250.c @@ -9,8 +9,6 @@ * Derived from drivers/mtd/nand/autcpu12.c * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * - * $Id: ts7250.c,v 1.4 2004/12/30 22:02:07 joff Exp $ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index 0c9ce19ea27a..320b929abe79 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -1,7 +1,6 @@ /* Linux driver for NAND Flash Translation Layer */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse <dwmw2@infradead.org> */ -/* $Id: nftlcore.c,v 1.98 2005/11/07 11:14:21 gleixner Exp $ */ /* The contents of this file are distributed under the GNU General @@ -803,12 +802,8 @@ static struct mtd_blktrans_ops nftl_tr = { .owner = THIS_MODULE, }; -extern char nftlmountrev[]; - static int __init init_nftl(void) { - printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.98 $, nftlmount.c %s\n", nftlmountrev); - return register_mtd_blktrans(&nftl_tr); } diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index 345e6eff89ce..ccc4f209fbb5 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -4,8 +4,6 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: nftlmount.c,v 1.41 2005/11/07 11:14:21 gleixner Exp $ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -31,8 +29,6 @@ #define SECTORSIZE 512 -char nftlmountrev[]="$Revision: 1.41 $"; - /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the * various device information of the NFTL partition and Bad Unit Table. Update * the ReplUnitTable[] table accroding to the Bad Unit Table. ReplUnitTable[] diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 5d7965f7e9ce..926cf3a4135d 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -325,28 +325,11 @@ 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) { - printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", ctrl); - if (ctrl & ONENAND_CTRL_LOCK) - printk(KERN_ERR "onenand_wait: it's locked error.\n"); - if (state == FL_READING) { - /* - * A power loss while writing can result in a page - * becoming unreadable. When the device is mounted - * again, reading that page gives controller errors. - * Upper level software like JFFS2 treat -EIO as fatal, - * refusing to mount at all. That means it is necessary - * to treat the error as an ECC error to allow recovery. - * Note that typically in this case, the eraseblock can - * still be erased and rewritten i.e. it has not become - * a bad block. - */ - mtd->ecc_stats.failed++; - return -EBADMSG; - } - return -EIO; - } - + /* + * In the Spec. it checks the controller status first + * However if you get the correct information in case of + * power off recovery (POR) test, it should read ECC status first + */ if (interrupt & ONENAND_INT_READ) { int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); if (ecc) { @@ -364,6 +347,15 @@ static int onenand_wait(struct mtd_info *mtd, int state) return -EIO; } + /* If there's controller error, it's a real error */ + if (ctrl & ONENAND_CTRL_ERROR) { + printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", + ctrl); + if (ctrl & ONENAND_CTRL_LOCK) + printk(KERN_ERR "onenand_wait: it's locked error.\n"); + return -EIO; + } + return 0; } @@ -1135,22 +1127,26 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state) interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); - /* Initial bad block case: 0x2400 or 0x0400 */ - if (ctrl & ONENAND_CTRL_ERROR) { - printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl); - return ONENAND_BBT_READ_ERROR; - } - if (interrupt & ONENAND_INT_READ) { int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); - if (ecc & ONENAND_ECC_2BIT_ALL) + if (ecc & ONENAND_ECC_2BIT_ALL) { + printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x" + ", controller error 0x%04x\n", ecc, ctrl); 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; } + /* Initial bad block case: 0x2400 or 0x0400 */ + if (ctrl & ONENAND_CTRL_ERROR) { + printk(KERN_DEBUG "onenand_bbt_wait: " + "controller error = 0x%04x\n", ctrl); + return ONENAND_BBT_READ_ERROR; + } + return 0; } diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index c5030f94f04e..2d600a1bf2aa 100644 --- a/drivers/mtd/redboot.c +++ b/drivers/mtd/redboot.c @@ -1,6 +1,4 @@ /* - * $Id: redboot.c,v 1.21 2006/03/30 18:34:37 bjd Exp $ - * * Parse RedBoot-style Flash Image System (FIS) tables and * produce a Linux partition array to match. */ diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index c84e45465499..e538c0a72abb 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -3,8 +3,6 @@ * * Copyright (C) 2005 Sean Young <sean@mess.org> * - * $Id: rfd_ftl.c,v 1.8 2006/01/15 12:51:44 sean Exp $ - * * This type of flash translation layer (FTL) is used by the Embedded BIOS * by General Software. It is known as the Resident Flash Disk (RFD), see: * diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 961416ac0616..c7630a228310 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -51,14 +51,13 @@ * @name: MTD device name or number string * @vid_hdr_offs: VID header offset */ -struct mtd_dev_param -{ +struct mtd_dev_param { char name[MTD_PARAM_LEN_MAX]; int vid_hdr_offs; }; /* Numbers of elements set in the @mtd_dev_param array */ -static int mtd_devs = 0; +static int mtd_devs; /* MTD devices specification parameters */ static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES]; @@ -160,8 +159,7 @@ void ubi_put_device(struct ubi_device *ubi) } /** - * ubi_get_by_major - get UBI device description object by character device - * major number. + * ubi_get_by_major - get UBI device by character device major number. * @major: major number * * This function is similar to 'ubi_get_device()', but it searches the device @@ -355,15 +353,34 @@ static void kill_volumes(struct ubi_device *ubi) } /** + * free_user_volumes - free all user volumes. + * @ubi: UBI device description object + * + * Normally the volumes are freed at the release function of the volume device + * objects. However, on error paths the volumes have to be freed before the + * device objects have been initialized. + */ +static void free_user_volumes(struct ubi_device *ubi) +{ + int i; + + for (i = 0; i < ubi->vtbl_slots; i++) + if (ubi->volumes[i]) { + kfree(ubi->volumes[i]->eba_tbl); + kfree(ubi->volumes[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. + * case of failure. Note, this function destroys all volumes if it failes. */ static int uif_init(struct ubi_device *ubi) { - int i, err; + int i, err, do_free = 0; dev_t dev; sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num); @@ -384,7 +401,7 @@ static int uif_init(struct ubi_device *ubi) ubi_assert(MINOR(dev) == 0); cdev_init(&ubi->cdev, &ubi_cdev_operations); - dbg_msg("%s major is %u", ubi->ubi_name, MAJOR(dev)); + dbg_gen("%s major is %u", ubi->ubi_name, MAJOR(dev)); ubi->cdev.owner = THIS_MODULE; err = cdev_add(&ubi->cdev, dev, 1); @@ -410,10 +427,13 @@ static int uif_init(struct ubi_device *ubi) out_volumes: kill_volumes(ubi); + do_free = 0; out_sysfs: ubi_sysfs_close(ubi); cdev_del(&ubi->cdev); out_unreg: + if (do_free) + free_user_volumes(ubi); unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1); ubi_err("cannot initialize UBI %s, error %d", ubi->ubi_name, err); return err; @@ -422,6 +442,10 @@ out_unreg: /** * uif_close - close user interfaces for an UBI device. * @ubi: UBI device description object + * + * Note, since this function un-registers UBI volume device objects (@vol->dev), + * the memory allocated voe the volumes is freed as well (in the release + * function). */ static void uif_close(struct ubi_device *ubi) { @@ -432,6 +456,21 @@ static void uif_close(struct ubi_device *ubi) } /** + * free_internal_volumes - free internal volumes. + * @ubi: UBI device description object + */ +static void free_internal_volumes(struct ubi_device *ubi) +{ + int i; + + for (i = ubi->vtbl_slots; + i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) { + kfree(ubi->volumes[i]->eba_tbl); + kfree(ubi->volumes[i]); + } +} + +/** * attach_by_scanning - attach an MTD device using scanning method. * @ubi: UBI device descriptor * @@ -475,6 +514,7 @@ static int attach_by_scanning(struct ubi_device *ubi) out_wl: ubi_wl_close(ubi); out_vtbl: + free_internal_volumes(ubi); vfree(ubi->vtbl); out_si: ubi_scan_destroy_si(si); @@ -482,7 +522,7 @@ out_si: } /** - * io_init - initialize I/O unit for a given UBI device. + * io_init - initialize I/O sub-system for a given UBI device. * @ubi: UBI device description object * * If @ubi->vid_hdr_offset or @ubi->leb_start is zero, default offsets are @@ -530,7 +570,11 @@ static int io_init(struct ubi_device *ubi) 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 */ + /* + * Make sure minimal I/O unit is power of 2. Note, there is no + * fundamental reason for this assumption. It is just an optimization + * which allows us to avoid costly division operations. + */ if (!is_power_of_2(ubi->min_io_size)) { ubi_err("min. I/O unit (%d) is not power of 2", ubi->min_io_size); @@ -581,7 +625,7 @@ static int io_init(struct ubi_device *ubi) 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->leb_start & (ubi->min_io_size - 1)) { ubi_err("bad VID header (%d) or data offsets (%d)", ubi->vid_hdr_offset, ubi->leb_start); return -EINVAL; @@ -646,7 +690,7 @@ static int autoresize(struct ubi_device *ubi, int vol_id) /* * Clear the auto-resize flag in the volume in-memory copy of the - * volume table, and 'ubi_resize_volume()' will propogate this change + * volume table, and 'ubi_resize_volume()' will propagate this change * to the flash. */ ubi->vtbl[vol_id].flags &= ~UBI_VTBL_AUTORESIZE_FLG; @@ -655,7 +699,7 @@ static int autoresize(struct ubi_device *ubi, int vol_id) struct ubi_vtbl_record vtbl_rec; /* - * No avalilable PEBs to re-size the volume, clear the flag on + * No available PEBs to re-size the volume, clear the flag on * flash and exit. */ memcpy(&vtbl_rec, &ubi->vtbl[vol_id], @@ -682,13 +726,13 @@ static int autoresize(struct ubi_device *ubi, int vol_id) /** * ubi_attach_mtd_dev - attach an MTD device. - * @mtd_dev: MTD device description object + * @mtd: MTD device description object * @ubi_num: number to assign to the new UBI device * @vid_hdr_offset: VID header offset * * This function attaches MTD device @mtd_dev to UBI and assign @ubi_num number * to the newly created UBI device, unless @ubi_num is %UBI_DEV_NUM_AUTO, in - * which case this function finds a vacant device nubert and assings it + * which case this function finds a vacant device number and assigns it * automatically. Returns the new UBI device number in case of success and a * negative error code in case of failure. * @@ -698,7 +742,7 @@ static int autoresize(struct ubi_device *ubi, int vol_id) int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) { struct ubi_device *ubi; - int i, err; + int i, err, do_free = 1; /* * Check if we already have the same MTD device attached. @@ -735,7 +779,8 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) if (!ubi_devices[ubi_num]) break; if (ubi_num == UBI_MAX_DEVICES) { - dbg_err("only %d UBI devices may be created", UBI_MAX_DEVICES); + dbg_err("only %d UBI devices may be created", + UBI_MAX_DEVICES); return -ENFILE; } } else { @@ -760,6 +805,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) mutex_init(&ubi->buf_mutex); mutex_init(&ubi->ckvol_mutex); + mutex_init(&ubi->mult_mutex); mutex_init(&ubi->volumes_mutex); spin_lock_init(&ubi->volumes_lock); @@ -798,7 +844,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) err = uif_init(ubi); if (err) - goto out_detach; + goto out_nofree; ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name); if (IS_ERR(ubi->bgt_thread)) { @@ -824,20 +870,22 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) 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) { + if (!DBG_DISABLE_BGT) ubi->thread_enabled = 1; - wake_up_process(ubi->bgt_thread); - } + wake_up_process(ubi->bgt_thread); ubi_devices[ubi_num] = ubi; return ubi_num; out_uif: uif_close(ubi); +out_nofree: + do_free = 0; out_detach: - ubi_eba_close(ubi); ubi_wl_close(ubi); + if (do_free) + free_user_volumes(ubi); + free_internal_volumes(ubi); vfree(ubi->vtbl); out_free: vfree(ubi->peb_buf1); @@ -899,8 +947,8 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) kthread_stop(ubi->bgt_thread); uif_close(ubi); - ubi_eba_close(ubi); ubi_wl_close(ubi); + free_internal_volumes(ubi); vfree(ubi->vtbl); put_mtd_device(ubi->mtd); vfree(ubi->peb_buf1); @@ -1044,8 +1092,7 @@ static void __exit ubi_exit(void) module_exit(ubi_exit); /** - * bytes_str_to_int - convert a string representing number of bytes to an - * integer. + * bytes_str_to_int - convert a number of bytes string into an integer. * @str: the string to convert * * This function returns positive resulting integer in case of success and a diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 89193ba9451e..03c759b4eeb5 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -39,9 +39,9 @@ #include <linux/stat.h> #include <linux/ioctl.h> #include <linux/capability.h> +#include <linux/uaccess.h> #include <linux/smp_lock.h> #include <mtd/ubi-user.h> -#include <asm/uaccess.h> #include <asm/div64.h> #include "ubi.h" @@ -116,7 +116,7 @@ static int vol_cdev_open(struct inode *inode, struct file *file) else mode = UBI_READONLY; - dbg_msg("open volume %d, mode %d", vol_id, mode); + dbg_gen("open volume %d, mode %d", vol_id, mode); desc = ubi_open_volume(ubi_num, vol_id, mode); unlock_kernel(); @@ -132,7 +132,7 @@ 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); + dbg_gen("release volume %d, mode %d", vol->vol_id, desc->mode); if (vol->updating) { ubi_warn("update of volume %d not finished, volume is damaged", @@ -141,7 +141,7 @@ static int vol_cdev_release(struct inode *inode, struct file *file) vol->updating = 0; vfree(vol->upd_buf); } else if (vol->changing_leb) { - dbg_msg("only %lld of %lld bytes received for atomic LEB change" + dbg_gen("only %lld of %lld bytes received for atomic LEB change" " for volume %d:%d, cancel", vol->upd_received, vol->upd_bytes, vol->ubi->ubi_num, vol->vol_id); vol->changing_leb = 0; @@ -183,7 +183,7 @@ static loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin) return -EINVAL; } - dbg_msg("seek volume %d, offset %lld, origin %d, new offset %lld", + dbg_gen("seek volume %d, offset %lld, origin %d, new offset %lld", vol->vol_id, offset, origin, new_offset); file->f_pos = new_offset; @@ -201,7 +201,7 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, void *tbuf; uint64_t tmp; - dbg_msg("read %zd bytes from offset %lld of volume %d", + dbg_gen("read %zd bytes from offset %lld of volume %d", count, *offp, vol->vol_id); if (vol->updating) { @@ -216,7 +216,7 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, return 0; if (vol->corrupted) - dbg_msg("read from corrupted volume %d", vol->vol_id); + dbg_gen("read from corrupted volume %d", vol->vol_id); if (*offp + count > vol->used_bytes) count_save = count = vol->used_bytes - *offp; @@ -285,7 +285,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, char *tbuf; uint64_t tmp; - dbg_msg("requested: write %zd bytes to offset %lld of volume %u", + dbg_gen("requested: write %zd bytes to offset %lld of volume %u", count, *offp, vol->vol_id); if (vol->vol_type == UBI_STATIC_VOLUME) @@ -295,7 +295,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, off = do_div(tmp, vol->usable_leb_size); lnum = tmp; - if (off % ubi->min_io_size) { + if (off & (ubi->min_io_size - 1)) { dbg_err("unaligned position"); return -EINVAL; } @@ -304,7 +304,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, 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) { + if (count & (ubi->min_io_size - 1)) { dbg_err("unaligned write length"); return -EINVAL; } @@ -352,7 +352,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, } #else -#define vol_cdev_direct_write(file, buf, count, offp) -EPERM +#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, @@ -437,7 +437,8 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, break; } - rsvd_bytes = vol->reserved_pebs * (ubi->leb_size-vol->data_pad); + rsvd_bytes = (long long)vol->reserved_pebs * + ubi->leb_size-vol->data_pad; if (bytes < 0 || bytes > rsvd_bytes) { err = -EINVAL; break; @@ -513,7 +514,7 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, break; } - dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); + dbg_gen("erase LEB %d:%d", vol->vol_id, lnum); err = ubi_eba_unmap_leb(ubi, vol, lnum); if (err) break; @@ -564,7 +565,7 @@ static int verify_mkvol_req(const struct ubi_device *ubi, if (req->alignment > ubi->leb_size) goto bad; - n = req->alignment % ubi->min_io_size; + n = req->alignment & (ubi->min_io_size - 1); if (req->alignment != 1 && n) goto bad; @@ -573,6 +574,10 @@ static int verify_mkvol_req(const struct ubi_device *ubi, goto bad; } + n = strnlen(req->name, req->name_len + 1); + if (n != req->name_len) + goto bad; + return 0; bad: @@ -600,6 +605,166 @@ static int verify_rsvol_req(const struct ubi_device *ubi, return 0; } +/** + * rename_volumes - rename UBI volumes. + * @ubi: UBI device description object + * @req: volumes re-name request + * + * This is a helper function for the volume re-name IOCTL which validates the + * the request, opens the volume and calls corresponding volumes management + * function. Returns zero in case of success and a negative error code in case + * of failure. + */ +static int rename_volumes(struct ubi_device *ubi, + struct ubi_rnvol_req *req) +{ + int i, n, err; + struct list_head rename_list; + struct ubi_rename_entry *re, *re1; + + if (req->count < 0 || req->count > UBI_MAX_RNVOL) + return -EINVAL; + + if (req->count == 0) + return 0; + + /* Validate volume IDs and names in the request */ + for (i = 0; i < req->count; i++) { + if (req->ents[i].vol_id < 0 || + req->ents[i].vol_id >= ubi->vtbl_slots) + return -EINVAL; + if (req->ents[i].name_len < 0) + return -EINVAL; + if (req->ents[i].name_len > UBI_VOL_NAME_MAX) + return -ENAMETOOLONG; + req->ents[i].name[req->ents[i].name_len] = '\0'; + n = strlen(req->ents[i].name); + if (n != req->ents[i].name_len) + err = -EINVAL; + } + + /* Make sure volume IDs and names are unique */ + for (i = 0; i < req->count - 1; i++) { + for (n = i + 1; n < req->count; n++) { + if (req->ents[i].vol_id == req->ents[n].vol_id) { + dbg_err("duplicated volume id %d", + req->ents[i].vol_id); + return -EINVAL; + } + if (!strcmp(req->ents[i].name, req->ents[n].name)) { + dbg_err("duplicated volume name \"%s\"", + req->ents[i].name); + return -EINVAL; + } + } + } + + /* Create the re-name list */ + INIT_LIST_HEAD(&rename_list); + for (i = 0; i < req->count; i++) { + int vol_id = req->ents[i].vol_id; + int name_len = req->ents[i].name_len; + const char *name = req->ents[i].name; + + re = kzalloc(sizeof(struct ubi_rename_entry), GFP_KERNEL); + if (!re) { + err = -ENOMEM; + goto out_free; + } + + re->desc = ubi_open_volume(ubi->ubi_num, vol_id, UBI_EXCLUSIVE); + if (IS_ERR(re->desc)) { + err = PTR_ERR(re->desc); + dbg_err("cannot open volume %d, error %d", vol_id, err); + kfree(re); + goto out_free; + } + + /* Skip this re-naming if the name does not really change */ + if (re->desc->vol->name_len == name_len && + !memcmp(re->desc->vol->name, name, name_len)) { + ubi_close_volume(re->desc); + kfree(re); + continue; + } + + re->new_name_len = name_len; + memcpy(re->new_name, name, name_len); + list_add_tail(&re->list, &rename_list); + dbg_msg("will rename volume %d from \"%s\" to \"%s\"", + vol_id, re->desc->vol->name, name); + } + + if (list_empty(&rename_list)) + return 0; + + /* Find out the volumes which have to be removed */ + list_for_each_entry(re, &rename_list, list) { + struct ubi_volume_desc *desc; + int no_remove_needed = 0; + + /* + * Volume @re->vol_id is going to be re-named to + * @re->new_name, while its current name is @name. If a volume + * with name @re->new_name currently exists, it has to be + * removed, unless it is also re-named in the request (@req). + */ + list_for_each_entry(re1, &rename_list, list) { + if (re->new_name_len == re1->desc->vol->name_len && + !memcmp(re->new_name, re1->desc->vol->name, + re1->desc->vol->name_len)) { + no_remove_needed = 1; + break; + } + } + + if (no_remove_needed) + continue; + + /* + * It seems we need to remove volume with name @re->new_name, + * if it exists. + */ + desc = ubi_open_volume_nm(ubi->ubi_num, re->new_name, UBI_EXCLUSIVE); + if (IS_ERR(desc)) { + err = PTR_ERR(desc); + if (err == -ENODEV) + /* Re-naming into a non-existing volume name */ + continue; + + /* The volume exists but busy, or an error occurred */ + dbg_err("cannot open volume \"%s\", error %d", + re->new_name, err); + goto out_free; + } + + re = kzalloc(sizeof(struct ubi_rename_entry), GFP_KERNEL); + if (!re) { + err = -ENOMEM; + ubi_close_volume(desc); + goto out_free; + } + + re->remove = 1; + re->desc = desc; + list_add(&re->list, &rename_list); + dbg_msg("will remove volume %d, name \"%s\"", + re->desc->vol->vol_id, re->desc->vol->name); + } + + mutex_lock(&ubi->volumes_mutex); + err = ubi_rename_volumes(ubi, &rename_list); + mutex_unlock(&ubi->volumes_mutex); + +out_free: + list_for_each_entry_safe(re, re1, &rename_list, list) { + ubi_close_volume(re->desc); + list_del(&re->list); + kfree(re); + } + return err; +} + static int ubi_cdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { @@ -621,19 +786,18 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, { struct ubi_mkvol_req req; - dbg_msg("create volume"); + dbg_gen("create volume"); err = copy_from_user(&req, argp, sizeof(struct ubi_mkvol_req)); if (err) { err = -EFAULT; break; } + req.name[req.name_len] = '\0'; err = verify_mkvol_req(ubi, &req); if (err) break; - req.name[req.name_len] = '\0'; - mutex_lock(&ubi->volumes_mutex); err = ubi_create_volume(ubi, &req); mutex_unlock(&ubi->volumes_mutex); @@ -652,7 +816,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, { int vol_id; - dbg_msg("remove volume"); + dbg_gen("remove volume"); err = get_user(vol_id, (__user int32_t *)argp); if (err) { err = -EFAULT; @@ -666,7 +830,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, } mutex_lock(&ubi->volumes_mutex); - err = ubi_remove_volume(desc); + err = ubi_remove_volume(desc, 0); mutex_unlock(&ubi->volumes_mutex); /* @@ -685,7 +849,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, uint64_t tmp; struct ubi_rsvol_req req; - dbg_msg("re-size volume"); + dbg_gen("re-size volume"); err = copy_from_user(&req, argp, sizeof(struct ubi_rsvol_req)); if (err) { err = -EFAULT; @@ -713,6 +877,32 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, break; } + /* Re-name volumes command */ + case UBI_IOCRNVOL: + { + struct ubi_rnvol_req *req; + + dbg_msg("re-name volumes"); + req = kmalloc(sizeof(struct ubi_rnvol_req), GFP_KERNEL); + if (!req) { + err = -ENOMEM; + break; + }; + + err = copy_from_user(req, argp, sizeof(struct ubi_rnvol_req)); + if (err) { + err = -EFAULT; + kfree(req); + break; + } + + mutex_lock(&ubi->mult_mutex); + err = rename_volumes(ubi, req); + mutex_unlock(&ubi->mult_mutex); + kfree(req); + break; + } + default: err = -ENOTTY; break; @@ -738,7 +928,7 @@ static int ctrl_cdev_ioctl(struct inode *inode, struct file *file, struct ubi_attach_req req; struct mtd_info *mtd; - dbg_msg("attach MTD device"); + dbg_gen("attach MTD device"); err = copy_from_user(&req, argp, sizeof(struct ubi_attach_req)); if (err) { err = -EFAULT; @@ -778,7 +968,7 @@ static int ctrl_cdev_ioctl(struct inode *inode, struct file *file, { int ubi_num; - dbg_msg("dettach MTD device"); + dbg_gen("dettach MTD device"); err = get_user(ubi_num, (__user int32_t *)argp); if (err) { err = -EFAULT; diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c index 56956ec2845f..c0ed60e8ade9 100644 --- a/drivers/mtd/ubi/debug.c +++ b/drivers/mtd/ubi/debug.c @@ -24,7 +24,7 @@ * changes. */ -#ifdef CONFIG_MTD_UBI_DEBUG_MSG +#ifdef CONFIG_MTD_UBI_DEBUG #include "ubi.h" @@ -34,14 +34,19 @@ */ void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr) { - dbg_msg("erase counter header dump:"); - dbg_msg("magic %#08x", be32_to_cpu(ec_hdr->magic)); - dbg_msg("version %d", (int)ec_hdr->version); - dbg_msg("ec %llu", (long long)be64_to_cpu(ec_hdr->ec)); - dbg_msg("vid_hdr_offset %d", be32_to_cpu(ec_hdr->vid_hdr_offset)); - dbg_msg("data_offset %d", be32_to_cpu(ec_hdr->data_offset)); - dbg_msg("hdr_crc %#08x", be32_to_cpu(ec_hdr->hdr_crc)); - dbg_msg("erase counter header hexdump:"); + printk(KERN_DEBUG "Erase counter header dump:\n"); + printk(KERN_DEBUG "\tmagic %#08x\n", + be32_to_cpu(ec_hdr->magic)); + printk(KERN_DEBUG "\tversion %d\n", (int)ec_hdr->version); + printk(KERN_DEBUG "\tec %llu\n", + (long long)be64_to_cpu(ec_hdr->ec)); + printk(KERN_DEBUG "\tvid_hdr_offset %d\n", + be32_to_cpu(ec_hdr->vid_hdr_offset)); + printk(KERN_DEBUG "\tdata_offset %d\n", + be32_to_cpu(ec_hdr->data_offset)); + printk(KERN_DEBUG "\thdr_crc %#08x\n", + be32_to_cpu(ec_hdr->hdr_crc)); + printk(KERN_DEBUG "erase counter header hexdump:\n"); print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, ec_hdr, UBI_EC_HDR_SIZE, 1); } @@ -52,22 +57,23 @@ 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) { - dbg_msg("volume identifier header dump:"); - dbg_msg("magic %08x", be32_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", be32_to_cpu(vid_hdr->vol_id)); - dbg_msg("lnum %d", be32_to_cpu(vid_hdr->lnum)); - dbg_msg("leb_ver %u", be32_to_cpu(vid_hdr->leb_ver)); - dbg_msg("data_size %d", be32_to_cpu(vid_hdr->data_size)); - dbg_msg("used_ebs %d", be32_to_cpu(vid_hdr->used_ebs)); - dbg_msg("data_pad %d", be32_to_cpu(vid_hdr->data_pad)); - dbg_msg("sqnum %llu", + printk(KERN_DEBUG "Volume identifier header dump:\n"); + printk(KERN_DEBUG "\tmagic %08x\n", be32_to_cpu(vid_hdr->magic)); + printk(KERN_DEBUG "\tversion %d\n", (int)vid_hdr->version); + printk(KERN_DEBUG "\tvol_type %d\n", (int)vid_hdr->vol_type); + printk(KERN_DEBUG "\tcopy_flag %d\n", (int)vid_hdr->copy_flag); + printk(KERN_DEBUG "\tcompat %d\n", (int)vid_hdr->compat); + printk(KERN_DEBUG "\tvol_id %d\n", be32_to_cpu(vid_hdr->vol_id)); + printk(KERN_DEBUG "\tlnum %d\n", be32_to_cpu(vid_hdr->lnum)); + printk(KERN_DEBUG "\tdata_size %d\n", be32_to_cpu(vid_hdr->data_size)); + printk(KERN_DEBUG "\tused_ebs %d\n", be32_to_cpu(vid_hdr->used_ebs)); + printk(KERN_DEBUG "\tdata_pad %d\n", be32_to_cpu(vid_hdr->data_pad)); + printk(KERN_DEBUG "\tsqnum %llu\n", (unsigned long long)be64_to_cpu(vid_hdr->sqnum)); - dbg_msg("hdr_crc %08x", be32_to_cpu(vid_hdr->hdr_crc)); - dbg_msg("volume identifier header hexdump:"); + printk(KERN_DEBUG "\thdr_crc %08x\n", be32_to_cpu(vid_hdr->hdr_crc)); + printk(KERN_DEBUG "Volume identifier header hexdump:\n"); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, + vid_hdr, UBI_VID_HDR_SIZE, 1); } /** @@ -76,27 +82,27 @@ void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr) */ 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); + printk(KERN_DEBUG "Volume information dump:\n"); + printk(KERN_DEBUG "\tvol_id %d\n", vol->vol_id); + printk(KERN_DEBUG "\treserved_pebs %d\n", vol->reserved_pebs); + printk(KERN_DEBUG "\talignment %d\n", vol->alignment); + printk(KERN_DEBUG "\tdata_pad %d\n", vol->data_pad); + printk(KERN_DEBUG "\tvol_type %d\n", vol->vol_type); + printk(KERN_DEBUG "\tname_len %d\n", vol->name_len); + printk(KERN_DEBUG "\tusable_leb_size %d\n", vol->usable_leb_size); + printk(KERN_DEBUG "\tused_ebs %d\n", vol->used_ebs); + printk(KERN_DEBUG "\tused_bytes %lld\n", vol->used_bytes); + printk(KERN_DEBUG "\tlast_eb_bytes %d\n", vol->last_eb_bytes); + printk(KERN_DEBUG "\tcorrupted %d\n", vol->corrupted); + printk(KERN_DEBUG "\tupd_marker %d\n", 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); + printk(KERN_DEBUG "\tname %s\n", 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]); + printk(KERN_DEBUG "\t1st 5 characters of name: %c%c%c%c%c\n", + vol->name[0], vol->name[1], vol->name[2], + vol->name[3], vol->name[4]); } } @@ -109,28 +115,29 @@ void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx) { int name_len = be16_to_cpu(r->name_len); - dbg_msg("volume table record %d dump:", idx); - dbg_msg("reserved_pebs %d", be32_to_cpu(r->reserved_pebs)); - dbg_msg("alignment %d", be32_to_cpu(r->alignment)); - dbg_msg("data_pad %d", be32_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); + printk(KERN_DEBUG "Volume table record %d dump:\n", idx); + printk(KERN_DEBUG "\treserved_pebs %d\n", + be32_to_cpu(r->reserved_pebs)); + printk(KERN_DEBUG "\talignment %d\n", be32_to_cpu(r->alignment)); + printk(KERN_DEBUG "\tdata_pad %d\n", be32_to_cpu(r->data_pad)); + printk(KERN_DEBUG "\tvol_type %d\n", (int)r->vol_type); + printk(KERN_DEBUG "\tupd_marker %d\n", (int)r->upd_marker); + printk(KERN_DEBUG "\tname_len %d\n", name_len); if (r->name[0] == '\0') { - dbg_msg("name NULL"); + printk(KERN_DEBUG "\tname NULL\n"); return; } if (name_len <= UBI_VOL_NAME_MAX && strnlen(&r->name[0], name_len + 1) == name_len) { - dbg_msg("name %s", &r->name[0]); + printk(KERN_DEBUG "\tname %s\n", &r->name[0]); } else { - dbg_msg("1st 5 characters of the name: %c%c%c%c%c", + printk(KERN_DEBUG "\t1st 5 characters of name: %c%c%c%c%c\n", r->name[0], r->name[1], r->name[2], r->name[3], r->name[4]); } - dbg_msg("crc %#08x", be32_to_cpu(r->crc)); + printk(KERN_DEBUG "\tcrc %#08x\n", be32_to_cpu(r->crc)); } /** @@ -139,15 +146,15 @@ 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) { - 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); + printk(KERN_DEBUG "Volume scanning information dump:\n"); + printk(KERN_DEBUG "\tvol_id %d\n", sv->vol_id); + printk(KERN_DEBUG "\thighest_lnum %d\n", sv->highest_lnum); + printk(KERN_DEBUG "\tleb_count %d\n", sv->leb_count); + printk(KERN_DEBUG "\tcompat %d\n", sv->compat); + printk(KERN_DEBUG "\tvol_type %d\n", sv->vol_type); + printk(KERN_DEBUG "\tused_ebs %d\n", sv->used_ebs); + printk(KERN_DEBUG "\tlast_data_size %d\n", sv->last_data_size); + printk(KERN_DEBUG "\tdata_pad %d\n", sv->data_pad); } /** @@ -157,14 +164,13 @@ void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv) */ 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); + printk(KERN_DEBUG "eraseblock scanning information dump:\n"); + printk(KERN_DEBUG "\tec %d\n", seb->ec); + printk(KERN_DEBUG "\tpnum %d\n", 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); + printk(KERN_DEBUG "\tlnum %d\n", seb->lnum); + printk(KERN_DEBUG "\tscrub %d\n", seb->scrub); + printk(KERN_DEBUG "\tsqnum %llu\n", seb->sqnum); } } @@ -176,16 +182,16 @@ 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); + printk(KERN_DEBUG "Volume creation request dump:\n"); + printk(KERN_DEBUG "\tvol_id %d\n", req->vol_id); + printk(KERN_DEBUG "\talignment %d\n", req->alignment); + printk(KERN_DEBUG "\tbytes %lld\n", (long long)req->bytes); + printk(KERN_DEBUG "\tvol_type %d\n", req->vol_type); + printk(KERN_DEBUG "\tname_len %d\n", req->name_len); memcpy(nm, req->name, 16); nm[16] = 0; - dbg_msg("the 1st 16 characters of the name: %s", nm); + printk(KERN_DEBUG "\t1st 16 characters of name: %s\n", nm); } -#endif /* CONFIG_MTD_UBI_DEBUG_MSG */ +#endif /* CONFIG_MTD_UBI_DEBUG */ diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h index 8ea99d8c9e1f..78e914d23ece 100644 --- a/drivers/mtd/ubi/debug.h +++ b/drivers/mtd/ubi/debug.h @@ -24,21 +24,16 @@ #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 +#define ubi_assert(expr) do { \ + if (unlikely(!(expr))) { \ + printk(KERN_CRIT "UBI assert failed in %s at %u (pid %d)\n", \ + __func__, __LINE__, current->pid); \ + ubi_dbg_dump_stack(); \ + } \ +} while (0) -#ifdef CONFIG_MTD_UBI_DEBUG_MSG -/* Generic debugging message */ #define dbg_msg(fmt, ...) \ printk(KERN_DEBUG "UBI DBG (pid %d): %s: " fmt "\n", \ current->pid, __func__, ##__VA_ARGS__) @@ -61,36 +56,29 @@ 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); +#ifdef CONFIG_MTD_UBI_DEBUG_MSG +/* General debugging messages */ +#define dbg_gen(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) #else - -#define dbg_msg(fmt, ...) ({}) -#define ubi_dbg_dump_stack() ({}) -#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) ({}) - -#endif /* CONFIG_MTD_UBI_DEBUG_MSG */ +#define dbg_gen(fmt, ...) ({}) +#endif #ifdef CONFIG_MTD_UBI_DEBUG_MSG_EBA -/* Messages from the eraseblock association unit */ +/* Messages from the eraseblock association sub-system */ #define dbg_eba(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) #else #define dbg_eba(fmt, ...) ({}) #endif #ifdef CONFIG_MTD_UBI_DEBUG_MSG_WL -/* Messages from the wear-leveling unit */ +/* Messages from the wear-leveling sub-system */ #define dbg_wl(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) #else #define dbg_wl(fmt, ...) ({}) #endif #ifdef CONFIG_MTD_UBI_DEBUG_MSG_IO -/* Messages from the input/output unit */ +/* Messages from the input/output sub-system */ #define dbg_io(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) #else #define dbg_io(fmt, ...) ({}) @@ -105,6 +93,12 @@ void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req); #define UBI_IO_DEBUG 0 #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_EMULATE_BITFLIPS /** * ubi_dbg_is_bitflip - if it is time to emulate a bit-flip. @@ -149,4 +143,30 @@ static inline int ubi_dbg_is_erase_failure(void) #define ubi_dbg_is_erase_failure() 0 #endif +#else + +#define ubi_assert(expr) ({}) +#define dbg_err(fmt, ...) ({}) +#define dbg_msg(fmt, ...) ({}) +#define dbg_gen(fmt, ...) ({}) +#define dbg_eba(fmt, ...) ({}) +#define dbg_wl(fmt, ...) ({}) +#define dbg_io(fmt, ...) ({}) +#define dbg_bld(fmt, ...) ({}) +#define ubi_dbg_dump_stack() ({}) +#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_IO_DEBUG 0 +#define DBG_DISABLE_BGT 0 +#define ubi_dbg_is_bitflip() 0 +#define ubi_dbg_is_write_failure() 0 +#define ubi_dbg_is_erase_failure() 0 + +#endif /* !CONFIG_MTD_UBI_DEBUG */ #endif /* !__UBI_DEBUG_H__ */ diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 7ce91ca742b1..e04bcf1dff87 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -19,20 +19,20 @@ */ /* - * The UBI Eraseblock Association (EBA) unit. + * The UBI Eraseblock Association (EBA) sub-system. * - * This unit is responsible for I/O to/from logical eraseblock. + * This sub-system 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 ubi_ltree_entry objects. They are indexed by - * (@vol_id, @lnum) pairs. + * The EBA sub-system 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 ubi_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 @@ -189,9 +189,7 @@ static struct ubi_ltree_entry *ltree_add_entry(struct ubi_device *ubi, le->users += 1; spin_unlock(&ubi->ltree_lock); - if (le_free) - kfree(le_free); - + kfree(le_free); return le; } @@ -223,22 +221,18 @@ static int leb_read_lock(struct ubi_device *ubi, int vol_id, int lnum) */ static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum) { - int free = 0; struct ubi_ltree_entry *le; spin_lock(&ubi->ltree_lock); le = ltree_lookup(ubi, vol_id, lnum); le->users -= 1; ubi_assert(le->users >= 0); + up_read(&le->mutex); if (le->users == 0) { rb_erase(&le->rb, &ubi->ltree); - free = 1; + kfree(le); } spin_unlock(&ubi->ltree_lock); - - up_read(&le->mutex); - if (free) - kfree(le); } /** @@ -274,7 +268,6 @@ static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum) */ static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum) { - int free; struct ubi_ltree_entry *le; le = ltree_add_entry(ubi, vol_id, lnum); @@ -289,12 +282,9 @@ static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum) 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); - if (free) kfree(le); + } + spin_unlock(&ubi->ltree_lock); return 1; } @@ -307,23 +297,18 @@ static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum) */ static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum) { - int free; struct ubi_ltree_entry *le; spin_lock(&ubi->ltree_lock); le = ltree_lookup(ubi, vol_id, lnum); le->users -= 1; ubi_assert(le->users >= 0); + up_write(&le->mutex); 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) kfree(le); + } + spin_unlock(&ubi->ltree_lock); } /** @@ -516,9 +501,8 @@ static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum, struct ubi_vid_hdr *vid_hdr; vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); - if (!vid_hdr) { + if (!vid_hdr) return -ENOMEM; - } mutex_lock(&ubi->buf_mutex); @@ -752,7 +736,7 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, /* 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); + ubi_assert(!(len & (ubi->min_io_size - 1))); vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); if (!vid_hdr) @@ -919,7 +903,7 @@ retry: } if (vol->eba_tbl[lnum] >= 0) { - err = ubi_wl_put_peb(ubi, vol->eba_tbl[lnum], 1); + err = ubi_wl_put_peb(ubi, vol->eba_tbl[lnum], 0); if (err) goto out_leb_unlock; } @@ -1141,7 +1125,7 @@ out_unlock_leb: } /** - * ubi_eba_init_scan - initialize the EBA unit using scanning information. + * ubi_eba_init_scan - initialize the EBA sub-system using scanning information. * @ubi: UBI device description object * @si: scanning information * @@ -1156,7 +1140,7 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) struct ubi_scan_leb *seb; struct rb_node *rb; - dbg_eba("initialize EBA unit"); + dbg_eba("initialize EBA sub-system"); spin_lock_init(&ubi->ltree_lock); mutex_init(&ubi->alc_mutex); @@ -1222,7 +1206,7 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) ubi->rsvd_pebs += ubi->beb_rsvd_pebs; } - dbg_eba("EBA unit is initialized"); + dbg_eba("EBA sub-system is initialized"); return 0; out_free: @@ -1233,20 +1217,3 @@ out_free: } 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); - } -} diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c index e909b390069a..605812bb0b1a 100644 --- a/drivers/mtd/ubi/gluebi.c +++ b/drivers/mtd/ubi/gluebi.c @@ -111,7 +111,7 @@ static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, struct ubi_device *ubi; uint64_t tmp = from; - dbg_msg("read %zd bytes from offset %lld", len, from); + dbg_gen("read %zd bytes from offset %lld", len, from); if (len < 0 || from < 0 || from + len > mtd->size) return -EINVAL; @@ -162,7 +162,7 @@ static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, struct ubi_device *ubi; uint64_t tmp = to; - dbg_msg("write %zd bytes to offset %lld", len, to); + dbg_gen("write %zd bytes to offset %lld", len, to); if (len < 0 || to < 0 || len + to > mtd->size) return -EINVAL; @@ -215,7 +215,7 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) struct ubi_volume *vol; struct ubi_device *ubi; - dbg_msg("erase %u bytes at offset %u", instr->len, instr->addr); + dbg_gen("erase %u bytes at offset %u", instr->len, instr->addr); if (instr->addr < 0 || instr->addr > mtd->size - mtd->erasesize) return -EINVAL; @@ -249,8 +249,8 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) if (err) goto out_err; - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); return 0; out_err: @@ -299,12 +299,12 @@ int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol) mtd->size = vol->used_bytes; if (add_mtd_device(mtd)) { - ubi_err("cannot not add MTD device\n"); + ubi_err("cannot not add MTD device"); kfree(mtd->name); return -ENFILE; } - dbg_msg("added mtd%d (\"%s\"), size %u, EB size %u", + dbg_gen("added mtd%d (\"%s\"), size %u, EB size %u", mtd->index, mtd->name, mtd->size, mtd->erasesize); return 0; } @@ -322,7 +322,7 @@ int ubi_destroy_gluebi(struct ubi_volume *vol) int err; struct mtd_info *mtd = &vol->gluebi_mtd; - dbg_msg("remove mtd%d", mtd->index); + dbg_gen("remove mtd%d", mtd->index); err = del_mtd_device(mtd); if (err) return err; diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 4ac11df7b048..2fb64be44f1b 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -20,15 +20,15 @@ */ /* - * UBI input/output unit. + * UBI input/output sub-system. * - * 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. + * This sub-system 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. + * from the flash media in order to be more secure and robust. So this + * sub-system validates every single header it reads from the flash media. * * Some words about how the eraseblock headers are stored. * @@ -79,11 +79,11 @@ * 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. + * The I/O sub-system 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> @@ -156,15 +156,19 @@ retry: /* * -EUCLEAN is reported if there was a bit-flip which * was corrected, so this is harmless. + * + * We do not report about it here unless debugging is + * enabled. A corresponding message will be printed + * later, when it is has been scrubbed. */ - ubi_msg("fixable bit-flip detected at PEB %d", pnum); + dbg_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", + 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; @@ -187,7 +191,7 @@ retry: ubi_assert(len == read); if (ubi_dbg_is_bitflip()) { - dbg_msg("bit-flip (emulated)"); + dbg_gen("bit-flip (emulated)"); err = UBI_IO_BITFLIPS; } } @@ -391,6 +395,7 @@ static int torture_peb(struct ubi_device *ubi, int pnum) { int err, i, patt_count; + ubi_msg("run torture test for PEB %d", pnum); patt_count = ARRAY_SIZE(patterns); ubi_assert(patt_count > 0); @@ -434,6 +439,7 @@ static int torture_peb(struct ubi_device *ubi, int pnum) } err = patt_count; + ubi_msg("PEB %d passed torture test, do not mark it a bad", pnum); out: mutex_unlock(&ubi->buf_mutex); @@ -699,8 +705,8 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum, if (hdr_crc != crc) { if (verbose) { - ubi_warn("bad EC header CRC at PEB %d, calculated %#08x," - " read %#08x", pnum, crc, hdr_crc); + 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; @@ -1095,8 +1101,7 @@ fail: } /** - * paranoid_check_peb_ec_hdr - check that the erase counter header of a - * physical eraseblock is in-place and is all right. + * paranoid_check_peb_ec_hdr - check erase counter header. * @ubi: UBI device description object * @pnum: the physical eraseblock number to check * @@ -1174,8 +1179,7 @@ fail: } /** - * paranoid_check_peb_vid_hdr - check that the volume identifier header of a - * physical eraseblock is in-place and is all right. + * paranoid_check_peb_vid_hdr - check volume identifier header. * @ubi: UBI device description object * @pnum: the physical eraseblock number to check * @@ -1256,7 +1260,7 @@ static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset, fail: ubi_err("paranoid check failed for PEB %d", pnum); - dbg_msg("hex dump of the %d-%d region", offset, offset + len); + ubi_msg("hex dump of the %d-%d region", offset, offset + len); print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, ubi->dbg_peb_buf, len, 1); err = 1; diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index a70d58823f8d..5d9bcf109c13 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -106,7 +106,7 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) struct ubi_device *ubi; struct ubi_volume *vol; - dbg_msg("open device %d volume %d, mode %d", ubi_num, vol_id, mode); + dbg_gen("open device %d volume %d, mode %d", ubi_num, vol_id, mode); if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) return ERR_PTR(-EINVAL); @@ -215,7 +215,7 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, struct ubi_device *ubi; struct ubi_volume_desc *ret; - dbg_msg("open volume %s, mode %d", name, mode); + dbg_gen("open volume %s, mode %d", name, mode); if (!name) return ERR_PTR(-EINVAL); @@ -266,7 +266,7 @@ void ubi_close_volume(struct ubi_volume_desc *desc) struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; - dbg_msg("close volume %d, mode %d", vol->vol_id, desc->mode); + dbg_gen("close volume %d, mode %d", vol->vol_id, desc->mode); spin_lock(&ubi->volumes_lock); switch (desc->mode) { @@ -323,7 +323,7 @@ int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, 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); + dbg_gen("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 || @@ -388,7 +388,7 @@ int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf, 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); + dbg_gen("write %d bytes to LEB %d:%d:%d", len, vol_id, lnum, offset); if (vol_id < 0 || vol_id >= ubi->vtbl_slots) return -EINVAL; @@ -397,8 +397,8 @@ int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf, 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) + offset + len > vol->usable_leb_size || + offset & (ubi->min_io_size - 1) || len & (ubi->min_io_size - 1)) return -EINVAL; if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM && @@ -438,7 +438,7 @@ int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, 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); + dbg_gen("atomically write %d bytes to LEB %d:%d", len, vol_id, lnum); if (vol_id < 0 || vol_id >= ubi->vtbl_slots) return -EINVAL; @@ -447,7 +447,7 @@ int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, return -EROFS; if (lnum < 0 || lnum >= vol->reserved_pebs || len < 0 || - len > vol->usable_leb_size || len % ubi->min_io_size) + len > vol->usable_leb_size || len & (ubi->min_io_size - 1)) return -EINVAL; if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM && @@ -482,7 +482,7 @@ int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum) struct ubi_device *ubi = vol->ubi; int err; - dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); + dbg_gen("erase LEB %d:%d", vol->vol_id, lnum); if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) return -EROFS; @@ -542,7 +542,7 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum) struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; - dbg_msg("unmap LEB %d:%d", vol->vol_id, lnum); + dbg_gen("unmap LEB %d:%d", vol->vol_id, lnum); if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) return -EROFS; @@ -579,7 +579,7 @@ int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype) struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; - dbg_msg("unmap LEB %d:%d", vol->vol_id, lnum); + dbg_gen("unmap LEB %d:%d", vol->vol_id, lnum); if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) return -EROFS; @@ -621,7 +621,7 @@ 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); + dbg_gen("test LEB %d:%d", vol->vol_id, lnum); if (lnum < 0 || lnum >= vol->reserved_pebs) return -EINVAL; @@ -632,3 +632,27 @@ int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum) return vol->eba_tbl[lnum] >= 0; } EXPORT_SYMBOL_GPL(ubi_is_mapped); + +/** + * ubi_sync - synchronize UBI device buffers. + * @ubi_num: UBI device to synchronize + * + * The underlying MTD device may cache data in hardware or in software. This + * function ensures the caches are flushed. Returns zero in case of success and + * a negative error code in case of failure. + */ +int ubi_sync(int ubi_num) +{ + struct ubi_device *ubi; + + ubi = ubi_get_device(ubi_num); + if (!ubi) + return -ENODEV; + + if (ubi->mtd->sync) + ubi->mtd->sync(ubi->mtd); + + ubi_put_device(ubi); + return 0; +} +EXPORT_SYMBOL_GPL(ubi_sync); diff --git a/drivers/mtd/ubi/misc.c b/drivers/mtd/ubi/misc.c index 93e052812012..22ad31402945 100644 --- a/drivers/mtd/ubi/misc.c +++ b/drivers/mtd/ubi/misc.c @@ -37,7 +37,7 @@ int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, { int i; - ubi_assert(length % ubi->min_io_size == 0); + ubi_assert(!(length & (ubi->min_io_size - 1))); for (i = length - 1; i >= 0; i--) if (((const uint8_t *)buf)[i] != 0xFF) diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index 96d410e106ab..967bb4406df9 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -19,9 +19,9 @@ */ /* - * UBI scanning unit. + * UBI scanning sub-system. * - * This unit is responsible for scanning the flash media, checking UBI + * This sub-system is responsible for scanning the flash media, checking UBI * headers and providing complete information about the UBI flash image. * * The scanning information is represented by a &struct ubi_scan_info' object. @@ -93,8 +93,7 @@ static int add_to_list(struct ubi_scan_info *si, int pnum, int ec, } /** - * validate_vid_hdr - check that volume identifier header is correct and - * consistent. + * validate_vid_hdr - check volume identifier header. * @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 @@ -103,7 +102,7 @@ static int add_to_list(struct ubi_scan_info *si, int pnum, int ec, * 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 + * Most of the checks are done in the I/O sub-system. Here we check that the * information in the VID header is consistent to the information in other VID * headers of the same volume. */ @@ -247,45 +246,21 @@ static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb, struct ubi_vid_hdr *vh = NULL; unsigned long long sqnum2 = be64_to_cpu(vid_hdr->sqnum); - if (seb->sqnum == 0 && sqnum2 == 0) { - long long abs, v1 = seb->leb_ver, v2 = be32_to_cpu(vid_hdr->leb_ver); - + if (sqnum2 == seb->sqnum) { /* - * 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. + * This must be a really ancient UBI image which has been + * created before sequence numbers support has been added. At + * that times we used 32-bit LEB versions stored in logical + * eraseblocks. That was before UBI got into mainline. We do not + * support these images anymore. Well, those images will work + * still work, but only if no unclean reboots happened. */ - dbg_bld("using old crappy leb_ver stuff"); - - if (v1 == v2) { - ubi_err("PEB %d and PEB %d have the same version %lld", - seb->pnum, pnum, v1); - return -EINVAL; - } - - abs = v1 - v2; - if (abs < 0) - abs = -abs; + ubi_err("unsupported on-flash UBI format\n"); + return -EINVAL; + } - 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; + /* 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 @@ -293,7 +268,7 @@ static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb, * 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. + * Note: this may be optimized so that we wouldn't read twice. */ if (second_is_newer) { @@ -379,8 +354,7 @@ out_free_vidh: } /** - * ubi_scan_add_used - add information about a physical eraseblock to the - * scanning information. + * ubi_scan_add_used - add physical eraseblock to the scanning information. * @ubi: UBI device description object * @si: scanning information * @pnum: the physical eraseblock number @@ -400,7 +374,6 @@ int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si, 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; @@ -409,10 +382,9 @@ int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si, vol_id = be32_to_cpu(vid_hdr->vol_id); lnum = be32_to_cpu(vid_hdr->lnum); sqnum = be64_to_cpu(vid_hdr->sqnum); - leb_ver = be32_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); + dbg_bld("PEB %d, LEB %d:%d, EC %d, sqnum %llu, bitflips %d", + pnum, vol_id, lnum, ec, sqnum, bitflips); sv = add_volume(si, vol_id, pnum, vid_hdr); if (IS_ERR(sv) < 0) @@ -445,25 +417,20 @@ int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si, */ 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; - } + "EC %d", seb->pnum, seb->sqnum, seb->ec); /* * 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. + * However, if the sequence number is zero, we assume it must + * be an ancient UBI image from the era when UBI did not have + * sequence numbers. We still can attach these images, unless + * there is a need to distinguish between old and new + * eraseblocks, in which case we'll refuse the image in + * 'compare_lebs()'. In other words, we attach old clean + * images, but refuse attaching old images with duplicated + * logical eraseblocks because there was an unclean reboot. */ if (seb->sqnum == sqnum && sqnum != 0) { ubi_err("two LEBs with same sequence number %llu", @@ -503,7 +470,6 @@ int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si, 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 = @@ -540,7 +506,6 @@ int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si, seb->lnum = lnum; seb->sqnum = sqnum; seb->scrub = bitflips; - seb->leb_ver = leb_ver; if (sv->highest_lnum <= lnum) { sv->highest_lnum = lnum; @@ -554,8 +519,7 @@ int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si, } /** - * ubi_scan_find_sv - find information about a particular volume in the - * scanning information. + * ubi_scan_find_sv - find volume in the scanning information. * @si: scanning information * @vol_id: the requested volume ID * @@ -584,8 +548,7 @@ struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si, } /** - * ubi_scan_find_seb - find information about a particular logical - * eraseblock in the volume scanning information. + * ubi_scan_find_seb - find LEB in the volume scanning information. * @sv: a pointer to the volume scanning information * @lnum: the requested logical eraseblock * @@ -645,9 +608,9 @@ void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv) * * 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. + * initialization stages, when the EBA sub-system 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(struct ubi_device *ubi, const struct ubi_scan_info *si, int pnum, int ec) @@ -687,9 +650,10 @@ out_free: * @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. + * called on the UBI initialization stages when the wear-leveling sub-system 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. @@ -742,8 +706,7 @@ struct ubi_scan_leb *ubi_scan_get_free_peb(struct ubi_device *ubi, } /** - * process_eb - read UBI headers, check them and add corresponding data - * to the scanning information. + * process_eb - read, check UBI headers, and add them to scanning information. * @ubi: UBI device description object * @si: scanning information * @pnum: the physical eraseblock number @@ -751,7 +714,8 @@ struct ubi_scan_leb *ubi_scan_get_free_peb(struct ubi_device *ubi, * This function returns a zero if the physical eraseblock was successfully * 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) +static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, + int pnum) { long long uninitialized_var(ec); int err, bitflips = 0, vol_id, ec_corr = 0; @@ -764,8 +728,9 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum 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. + * FIXME: this is actually duty of the I/O sub-system to + * initialize this, but MTD does not provide enough + * information. */ si->bad_peb_count += 1; return 0; @@ -930,7 +895,7 @@ struct ubi_scan_info *ubi_scan(struct ubi_device *ubi) for (pnum = 0; pnum < ubi->peb_count; pnum++) { cond_resched(); - dbg_msg("process PEB %d", pnum); + dbg_gen("process PEB %d", pnum); err = process_eb(ubi, si, pnum); if (err < 0) goto out_vidh; @@ -1079,8 +1044,7 @@ void ubi_scan_destroy_si(struct ubi_scan_info *si) #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID /** - * paranoid_check_si - check if the scanning information is correct and - * consistent. + * paranoid_check_si - check the scanning information. * @ubi: UBI device description object * @si: scanning information * @@ -1265,11 +1229,6 @@ static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si) ubi_err("bad data_pad %d", sv->data_pad); goto bad_vid_hdr; } - - if (seb->leb_ver != be32_to_cpu(vidh->leb_ver)) { - ubi_err("bad leb_ver %u", seb->leb_ver); - goto bad_vid_hdr; - } } if (!last_seb) @@ -1299,8 +1258,7 @@ static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si) if (err < 0) { kfree(buf); return err; - } - else if (err) + } else if (err) buf[pnum] = 1; } diff --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h index 966b9b682a42..61df208e2f20 100644 --- a/drivers/mtd/ubi/scan.h +++ b/drivers/mtd/ubi/scan.h @@ -34,7 +34,6 @@ * @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. @@ -49,7 +48,6 @@ struct ubi_scan_leb { struct rb_node rb; struct list_head list; } u; - uint32_t leb_ver; }; /** @@ -59,16 +57,16 @@ struct ubi_scan_leb { * @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) + * 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) + * 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) + * 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) + * volume (&struct ubi_scan_leb objects) * * One object of this type is allocated for each volume during scanning. */ @@ -92,8 +90,8 @@ struct ubi_scan_volume { * @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., + * those belonging to "preserve"-compatible internal volumes) * @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 @@ -106,8 +104,8 @@ struct ubi_scan_volume { * @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. + * UBI sub-systems to build final UBI data structures, further error-recovery + * and so on. */ struct ubi_scan_info { struct rb_root volumes; @@ -132,8 +130,7 @@ struct ubi_device; struct ubi_vid_hdr; /* - * ubi_scan_move_to_list - move a physical eraseblock from the volume tree to a - * list. + * ubi_scan_move_to_list - move a PEB from the volume tree to a list. * * @sv: volume scanning information * @seb: scanning eraseblock infprmation diff --git a/drivers/mtd/ubi/ubi-media.h b/drivers/mtd/ubi/ubi-media.h index c3185d9fd048..2ad940409053 100644 --- a/drivers/mtd/ubi/ubi-media.h +++ b/drivers/mtd/ubi/ubi-media.h @@ -98,10 +98,11 @@ enum { * Compatibility constants used by internal volumes. * * @UBI_COMPAT_DELETE: delete this internal volume before anything is written - * to the flash + * to the flash * @UBI_COMPAT_RO: attach this device in read-only mode * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its - * physical eraseblocks, don't allow the wear-leveling unit to move them + * physical eraseblocks, don't allow the wear-leveling + * sub-system to move them * @UBI_COMPAT_REJECT: reject this UBI image */ enum { @@ -123,7 +124,7 @@ enum { * struct ubi_ec_hdr - UBI erase counter header. * @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC) * @version: version of UBI implementation which is supposed to accept this - * UBI image + * UBI image * @padding1: reserved for future, zeroes * @ec: the erase counter * @vid_hdr_offset: where the VID header starts @@ -159,24 +160,23 @@ struct ubi_ec_hdr { * struct ubi_vid_hdr - on-flash UBI volume identifier header. * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC) * @version: UBI implementation version which is supposed to accept this UBI - * image (%UBI_VERSION) + * image (%UBI_VERSION) * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) * @copy_flag: if this logical eraseblock was copied from another physical - * eraseblock (for wear-leveling reasons) + * eraseblock (for wear-leveling reasons) * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE, - * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) + * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) * @vol_id: ID of this volume * @lnum: logical eraseblock number - * @leb_ver: version of this logical eraseblock (IMPORTANT: obsolete, to be - * removed, kept only for not breaking older UBI users) + * @padding1: reserved for future, zeroes * @data_size: how many bytes of data this logical eraseblock contains * @used_ebs: total number of used logical eraseblocks in this volume * @data_pad: how many bytes at the end of this physical eraseblock are not - * used + * used * @data_crc: CRC checksum of the data stored in this logical eraseblock - * @padding1: reserved for future, zeroes - * @sqnum: sequence number * @padding2: reserved for future, zeroes + * @sqnum: sequence number + * @padding3: reserved for future, zeroes * @hdr_crc: volume identifier header CRC checksum * * The @sqnum is the value of the global sequence counter at the time when this @@ -224,10 +224,6 @@ struct ubi_ec_hdr { * checksum is correct, this physical eraseblock is selected (P1). Otherwise * the older one (P) is selected. * - * Note, there is an obsolete @leb_ver field which was used instead of @sqnum - * in the past. But it is not used anymore and we keep it in order to be able - * to deal with old UBI images. It will be removed at some point. - * * There are 2 sorts of volumes in UBI: user volumes and internal volumes. * Internal volumes are not seen from outside and are used for various internal * UBI purposes. In this implementation there is only one internal volume - the @@ -248,9 +244,9 @@ struct ubi_ec_hdr { * The @data_crc field contains the CRC checksum of the contents of the logical * eraseblock if this is a static volume. In case of dynamic volumes, it does * not contain the CRC checksum as a rule. The only exception is when the - * data of the physical eraseblock was moved by the wear-leveling unit, then - * the wear-leveling unit calculates the data CRC and stores it in the - * @data_crc field. And of course, the @copy_flag is %in this case. + * data of the physical eraseblock was moved by the wear-leveling sub-system, + * then the wear-leveling sub-system calculates the data CRC and stores it in + * the @data_crc field. And of course, the @copy_flag is %in this case. * * The @data_size field is used only for static volumes because UBI has to know * how many bytes of data are stored in this eraseblock. For dynamic volumes, @@ -277,14 +273,14 @@ struct ubi_vid_hdr { __u8 compat; __be32 vol_id; __be32 lnum; - __be32 leb_ver; /* obsolete, to be removed, don't use */ + __u8 padding1[4]; __be32 data_size; __be32 used_ebs; __be32 data_pad; __be32 data_crc; - __u8 padding1[4]; + __u8 padding2[4]; __be64 sqnum; - __u8 padding2[12]; + __u8 padding3[12]; __be32 hdr_crc; } __attribute__ ((packed)); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 67dcbd11c15c..1c3fa18c26a7 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -74,15 +74,15 @@ #define UBI_IO_RETRIES 3 /* - * Error codes returned by the I/O unit. + * Error codes returned by the I/O sub-system. * * UBI_IO_PEB_EMPTY: the physical eraseblock is empty, i.e. it contains only - * 0xFF bytes + * %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 + * 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) + * CRC) * UBI_IO_BITFLIPS: bit-flips were detected and corrected */ enum { @@ -99,9 +99,9 @@ enum { * @ec: erase counter * @pnum: physical eraseblock number * - * This data structure is used in the WL unit. Each physical eraseblock has a - * corresponding &struct wl_entry object which may be kept in different - * RB-trees. See WL unit for details. + * This data structure is used in the WL sub-system. Each physical eraseblock + * has a corresponding &struct wl_entry object which may be kept in different + * RB-trees. See WL sub-system for details. */ struct ubi_wl_entry { struct rb_node rb; @@ -118,10 +118,10 @@ struct ubi_wl_entry { * @mutex: read/write mutex to implement read/write access serialization to * the (@vol_id, @lnum) logical eraseblock * - * This data structure is used in the EBA unit to implement per-LEB locking. - * When a logical eraseblock is being locked - corresponding + * This data structure is used in the EBA sub-system to implement per-LEB + * locking. When a logical eraseblock is being locked - corresponding * &struct ubi_ltree_entry object is inserted to the lock tree (@ubi->ltree). - * See EBA unit for details. + * See EBA sub-system for details. */ struct ubi_ltree_entry { struct rb_node rb; @@ -131,6 +131,27 @@ struct ubi_ltree_entry { struct rw_semaphore mutex; }; +/** + * struct ubi_rename_entry - volume re-name description data structure. + * @new_name_len: new volume name length + * @new_name: new volume name + * @remove: if not zero, this volume should be removed, not re-named + * @desc: descriptor of the volume + * @list: links re-name entries into a list + * + * This data structure is utilized in the multiple volume re-name code. Namely, + * UBI first creates a list of &struct ubi_rename_entry objects from the + * &struct ubi_rnvol_req request object, and then utilizes this list to do all + * the job. + */ +struct ubi_rename_entry { + int new_name_len; + char new_name[UBI_VOL_NAME_MAX + 1]; + int remove; + struct ubi_volume_desc *desc; + struct list_head list; +}; + struct ubi_volume_desc; /** @@ -206,7 +227,7 @@ struct ubi_volume { int alignment; int data_pad; int name_len; - char name[UBI_VOL_NAME_MAX+1]; + char name[UBI_VOL_NAME_MAX + 1]; int upd_ebs; int ch_lnum; @@ -225,7 +246,7 @@ struct ubi_volume { #ifdef CONFIG_MTD_UBI_GLUEBI /* * Gluebi-related stuff may be compiled out. - * TODO: this should not be built into UBI but should be a separate + * Note: this should not be built into UBI but should be a separate * ubimtd driver which works on top of UBI and emulates MTD devices. */ struct ubi_volume_desc *gluebi_desc; @@ -235,8 +256,7 @@ struct ubi_volume { }; /** - * struct ubi_volume_desc - descriptor of the UBI volume returned when it is - * opened. + * struct ubi_volume_desc - UBI volume descriptor returned when it is opened. * @vol: reference to the corresponding volume description object * @mode: open mode (%UBI_READONLY, %UBI_READWRITE, or %UBI_EXCLUSIVE) */ @@ -273,7 +293,7 @@ struct ubi_wl_entry; * @vtbl_size: size of the volume table in bytes * @vtbl: in-RAM volume table copy * @volumes_mutex: protects on-flash volume table and serializes volume - * changes, like creation, deletion, update, resize + * changes, like creation, deletion, update, re-size and re-name * * @max_ec: current highest erase counter value * @mean_ec: current mean erase counter value @@ -293,6 +313,7 @@ struct ubi_wl_entry; * @move_to, @move_to_put @erase_pending, @wl_scheduled, and @works * fields * @move_mutex: serializes eraseblock moves + * @work_sem: sycnhronizes the WL worker with use tasks * @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 @@ -316,11 +337,11 @@ struct ubi_wl_entry; * @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 + * 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) + * 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 @@ -331,6 +352,8 @@ struct ubi_wl_entry; * @peb_buf1: a buffer of PEB size used for different purposes * @peb_buf2: another buffer of PEB size used for different purposes * @buf_mutex: proptects @peb_buf1 and @peb_buf2 + * @ckvol_mutex: serializes static volume checking when opening + * @mult_mutex: serializes operations on multiple volumes, like re-nameing * @dbg_peb_buf: buffer of PEB size used for debugging * @dbg_buf_mutex: proptects @dbg_peb_buf */ @@ -356,16 +379,16 @@ struct ubi_device { struct mutex volumes_mutex; int max_ec; - /* TODO: mean_ec is not updated run-time, fix */ + /* Note, mean_ec is not updated run-time - should be fixed */ int mean_ec; - /* EBA unit's stuff */ + /* EBA sub-system's stuff */ unsigned long long global_sqnum; spinlock_t ltree_lock; struct rb_root ltree; struct mutex alc_mutex; - /* Wear-leveling unit's stuff */ + /* Wear-leveling sub-system's stuff */ struct rb_root used; struct rb_root free; struct rb_root scrub; @@ -388,7 +411,7 @@ struct ubi_device { int thread_enabled; char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2]; - /* I/O unit's stuff */ + /* I/O sub-system's stuff */ long long flash_size; int peb_count; int peb_size; @@ -411,6 +434,7 @@ struct ubi_device { void *peb_buf2; struct mutex buf_mutex; struct mutex ckvol_mutex; + struct mutex mult_mutex; #ifdef CONFIG_MTD_UBI_DEBUG void *dbg_peb_buf; struct mutex dbg_buf_mutex; @@ -427,12 +451,15 @@ extern struct mutex ubi_devices_mutex; /* vtbl.c */ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, struct ubi_vtbl_record *vtbl_rec); +int ubi_vtbl_rename_volumes(struct ubi_device *ubi, + struct list_head *rename_list); 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_remove_volume(struct ubi_volume_desc *desc, int no_vtbl); int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs); +int ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list); int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol); void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol); @@ -447,7 +474,8 @@ int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, 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_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); @@ -477,7 +505,6 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, 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); diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c index ddaa1a56cc69..8b89cc18ff0b 100644 --- a/drivers/mtd/ubi/upd.c +++ b/drivers/mtd/ubi/upd.c @@ -39,7 +39,7 @@ */ #include <linux/err.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <asm/div64.h> #include "ubi.h" @@ -56,11 +56,11 @@ static int set_update_marker(struct ubi_device *ubi, struct ubi_volume *vol) int err; struct ubi_vtbl_record vtbl_rec; - dbg_msg("set update marker for volume %d", vol->vol_id); + dbg_gen("set update marker for volume %d", vol->vol_id); if (vol->upd_marker) { ubi_assert(ubi->vtbl[vol->vol_id].upd_marker); - dbg_msg("already set"); + dbg_gen("already set"); return 0; } @@ -92,7 +92,7 @@ static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol, uint64_t tmp; struct ubi_vtbl_record vtbl_rec; - dbg_msg("clear update marker for volume %d", vol->vol_id); + dbg_gen("clear update marker for volume %d", vol->vol_id); memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id], sizeof(struct ubi_vtbl_record)); @@ -133,7 +133,7 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, int i, err; uint64_t tmp; - dbg_msg("start update of volume %d, %llu bytes", vol->vol_id, bytes); + dbg_gen("start update of volume %d, %llu bytes", vol->vol_id, bytes); ubi_assert(!vol->updating && !vol->changing_leb); vol->updating = 1; @@ -183,7 +183,7 @@ int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, { ubi_assert(!vol->updating && !vol->changing_leb); - dbg_msg("start changing LEB %d:%d, %u bytes", + dbg_gen("start changing LEB %d:%d, %u bytes", vol->vol_id, req->lnum, req->bytes); if (req->bytes == 0) return ubi_eba_atomic_leb_change(ubi, vol, req->lnum, NULL, 0, @@ -237,16 +237,17 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, int err; if (vol->vol_type == UBI_DYNAMIC_VOLUME) { - len = ALIGN(len, ubi->min_io_size); - memset(buf + len, 0xFF, len - len); + int l = ALIGN(len, ubi->min_io_size); - len = ubi_calc_data_len(ubi, buf, len); + memset(buf + len, 0xFF, l - len); + len = ubi_calc_data_len(ubi, buf, l); if (len == 0) { - dbg_msg("all %d bytes contain 0xFF - skip", len); + dbg_gen("all %d bytes contain 0xFF - skip", len); return 0; } - err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len, UBI_UNKNOWN); + err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len, + UBI_UNKNOWN); } else { /* * When writing static volume, and this is the last logical @@ -267,6 +268,7 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, /** * ubi_more_update_data - write more update data. + * @ubi: UBI device description object * @vol: volume description object * @buf: write data (user-space memory buffer) * @count: how much bytes to write @@ -283,7 +285,7 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, uint64_t tmp; int lnum, offs, err = 0, len, to_write = count; - dbg_msg("write %d of %lld bytes, %lld already passed", + dbg_gen("write %d of %lld bytes, %lld already passed", count, vol->upd_bytes, vol->upd_received); if (ubi->ro_mode) @@ -384,6 +386,7 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, /** * ubi_more_leb_change_data - accept more data for atomic LEB change. + * @ubi: UBI device description object * @vol: volume description object * @buf: write data (user-space memory buffer) * @count: how much bytes to write @@ -400,7 +403,7 @@ int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, { int err; - dbg_msg("write %d of %lld bytes, %lld already passed", + dbg_gen("write %d of %lld bytes, %lld already passed", count, vol->upd_bytes, vol->upd_received); if (ubi->ro_mode) @@ -418,7 +421,8 @@ int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, if (vol->upd_received == vol->upd_bytes) { int len = ALIGN((int)vol->upd_bytes, ubi->min_io_size); - memset(vol->upd_buf + vol->upd_bytes, 0xFF, len - vol->upd_bytes); + memset(vol->upd_buf + vol->upd_bytes, 0xFF, + len - vol->upd_bytes); len = ubi_calc_data_len(ubi, vol->upd_buf, len); err = ubi_eba_atomic_leb_change(ubi, vol, vol->ch_lnum, vol->upd_buf, len, UBI_UNKNOWN); diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 5be58d85c639..3531ca9a1e24 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -28,9 +28,9 @@ #include "ubi.h" #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID -static void paranoid_check_volumes(struct ubi_device *ubi); +static int paranoid_check_volumes(struct ubi_device *ubi); #else -#define paranoid_check_volumes(ubi) +#define paranoid_check_volumes(ubi) 0 #endif static ssize_t vol_attribute_show(struct device *dev, @@ -127,6 +127,7 @@ static void vol_release(struct device *dev) { struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); + kfree(vol->eba_tbl); kfree(vol); } @@ -201,7 +202,7 @@ static void volume_sysfs_close(struct ubi_volume *vol) */ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) { - int i, err, vol_id = req->vol_id, dont_free = 0; + int i, err, vol_id = req->vol_id, do_free = 1; struct ubi_volume *vol; struct ubi_vtbl_record vtbl_rec; uint64_t bytes; @@ -217,7 +218,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) spin_lock(&ubi->volumes_lock); if (vol_id == UBI_VOL_NUM_AUTO) { /* Find unused volume ID */ - dbg_msg("search for vacant volume ID"); + dbg_gen("search for vacant volume ID"); for (i = 0; i < ubi->vtbl_slots; i++) if (!ubi->volumes[i]) { vol_id = i; @@ -232,7 +233,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) req->vol_id = vol_id; } - dbg_msg("volume ID %d, %llu bytes, type %d, name %s", + dbg_gen("volume ID %d, %llu bytes, type %d, name %s", vol_id, (unsigned long long)req->bytes, (int)req->vol_type, req->name); @@ -252,7 +253,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) goto out_unlock; } - /* Calculate how many eraseblocks are requested */ + /* 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)) @@ -274,7 +275,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) 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); + memcpy(vol->name, req->name, vol->name_len); vol->ubi = ubi; /* @@ -349,7 +350,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) 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); + memcpy(vtbl_rec.name, vol->name, vol->name_len); err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); if (err) @@ -360,19 +361,19 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) ubi->vol_count += 1; spin_unlock(&ubi->volumes_lock); - paranoid_check_volumes(ubi); - return 0; + err = paranoid_check_volumes(ubi); + return err; out_sysfs: /* - * We have registered our device, we should not free the volume* + * We have registered our device, we should not free the volume * description object in this function in case of an error - it is * freed by the release function. * * Get device reference to prevent the release function from being * called just after sysfs has been closed. */ - dont_free = 1; + do_free = 0; get_device(&vol->dev); volume_sysfs_close(vol); out_gluebi: @@ -382,17 +383,18 @@ out_gluebi: out_cdev: cdev_del(&vol->cdev); out_mapping: - kfree(vol->eba_tbl); + if (do_free) + 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); - if (dont_free) - put_device(&vol->dev); - else + if (do_free) kfree(vol); + else + put_device(&vol->dev); ubi_err("cannot create volume %d, error %d", vol_id, err); return err; } @@ -400,19 +402,20 @@ out_unlock: /** * ubi_remove_volume - remove volume. * @desc: volume descriptor + * @no_vtbl: do not change volume table if not zero * * 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. The caller has to have the @ubi->volumes_mutex * locked. */ -int ubi_remove_volume(struct ubi_volume_desc *desc) +int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl) { 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); + dbg_gen("remove UBI volume %d", vol_id); ubi_assert(desc->mode == UBI_EXCLUSIVE); ubi_assert(vol == ubi->volumes[vol_id]); @@ -435,9 +438,11 @@ int ubi_remove_volume(struct ubi_volume_desc *desc) if (err) goto out_err; - err = ubi_change_vtbl_record(ubi, vol_id, NULL); - if (err) - goto out_err; + if (!no_vtbl) { + err = ubi_change_vtbl_record(ubi, vol_id, NULL); + if (err) + goto out_err; + } for (i = 0; i < vol->reserved_pebs; i++) { err = ubi_eba_unmap_leb(ubi, vol, i); @@ -445,8 +450,6 @@ int ubi_remove_volume(struct ubi_volume_desc *desc) goto out_err; } - kfree(vol->eba_tbl); - vol->eba_tbl = NULL; cdev_del(&vol->cdev); volume_sysfs_close(vol); @@ -465,8 +468,9 @@ int ubi_remove_volume(struct ubi_volume_desc *desc) ubi->vol_count -= 1; spin_unlock(&ubi->volumes_lock); - paranoid_check_volumes(ubi); - return 0; + if (!no_vtbl) + err = paranoid_check_volumes(ubi); + return err; out_err: ubi_err("cannot remove volume %d, error %d", vol_id, err); @@ -497,7 +501,7 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) if (ubi->ro_mode) return -EROFS; - dbg_msg("re-size volume %d to from %d to %d PEBs", + dbg_gen("re-size volume %d to from %d to %d PEBs", vol_id, vol->reserved_pebs, reserved_pebs); if (vol->vol_type == UBI_STATIC_VOLUME && @@ -586,8 +590,8 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) (long long)vol->used_ebs * vol->usable_leb_size; } - paranoid_check_volumes(ubi); - return 0; + err = paranoid_check_volumes(ubi); + return err; out_acc: if (pebs > 0) { @@ -602,6 +606,44 @@ out_free: } /** + * ubi_rename_volumes - re-name UBI volumes. + * @ubi: UBI device description object + * @rename_list: list of &struct ubi_rename_entry objects + * + * This function re-names or removes volumes specified in the re-name list. + * Returns zero in case of success and a negative error code in case of + * failure. + */ +int ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list) +{ + int err; + struct ubi_rename_entry *re; + + err = ubi_vtbl_rename_volumes(ubi, rename_list); + if (err) + return err; + + list_for_each_entry(re, rename_list, list) { + if (re->remove) { + err = ubi_remove_volume(re->desc, 1); + if (err) + break; + } else { + struct ubi_volume *vol = re->desc->vol; + + spin_lock(&ubi->volumes_lock); + vol->name_len = re->new_name_len; + memcpy(vol->name, re->new_name, re->new_name_len + 1); + spin_unlock(&ubi->volumes_lock); + } + } + + if (!err) + err = paranoid_check_volumes(ubi); + return err; +} + +/** * ubi_add_volume - add volume. * @ubi: UBI device description object * @vol: volume description object @@ -615,8 +657,7 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) int err, vol_id = vol->vol_id; dev_t dev; - dbg_msg("add volume %d", vol_id); - ubi_dbg_dump_vol_info(vol); + dbg_gen("add volume %d", vol_id); /* Register character device for the volume */ cdev_init(&vol->cdev, &ubi_vol_cdev_operations); @@ -650,8 +691,8 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) return err; } - paranoid_check_volumes(ubi); - return 0; + err = paranoid_check_volumes(ubi); + return err; out_gluebi: err = ubi_destroy_gluebi(vol); @@ -672,7 +713,7 @@ void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol) { int err; - dbg_msg("free volume %d", vol->vol_id); + dbg_gen("free volume %d", vol->vol_id); ubi->volumes[vol->vol_id] = NULL; err = ubi_destroy_gluebi(vol); @@ -686,8 +727,10 @@ void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol) * paranoid_check_volume - check volume information. * @ubi: UBI device description object * @vol_id: volume ID + * + * Returns zero if volume is all right and a a negative error code if not. */ -static void paranoid_check_volume(struct ubi_device *ubi, int vol_id) +static int paranoid_check_volume(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; @@ -705,16 +748,7 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id) goto fail; } spin_unlock(&ubi->volumes_lock); - return; - } - - if (vol->exclusive) { - /* - * The volume may be being created at the moment, do not check - * it (e.g., it may be in the middle of ubi_create_volume(). - */ - spin_unlock(&ubi->volumes_lock); - return; + return 0; } if (vol->reserved_pebs < 0 || vol->alignment < 0 || vol->data_pad < 0 || @@ -727,7 +761,7 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id) goto fail; } - n = vol->alignment % ubi->min_io_size; + n = vol->alignment & (ubi->min_io_size - 1); if (vol->alignment != 1 && n) { ubi_err("alignment is not multiple of min I/O unit"); goto fail; @@ -824,31 +858,39 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id) 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)) { + name_len != vol->name_len || strncmp(name, vol->name, name_len)) { ubi_err("volume info is different"); goto fail; } spin_unlock(&ubi->volumes_lock); - return; + return 0; fail: ubi_err("paranoid check failed for volume %d", vol_id); - ubi_dbg_dump_vol_info(vol); + if (vol) + ubi_dbg_dump_vol_info(vol); ubi_dbg_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id); spin_unlock(&ubi->volumes_lock); - BUG(); + return -EINVAL; } /** * paranoid_check_volumes - check information about all volumes. * @ubi: UBI device description object + * + * Returns zero if volumes are all right and a a negative error code if not. */ -static void paranoid_check_volumes(struct ubi_device *ubi) +static int paranoid_check_volumes(struct ubi_device *ubi) { - int i; + int i, err = 0; - for (i = 0; i < ubi->vtbl_slots; i++) - paranoid_check_volume(ubi, i); + for (i = 0; i < ubi->vtbl_slots; i++) { + err = paranoid_check_volume(ubi, i); + if (err) + break; + } + + return err; } #endif diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c index af36b12be278..217d0e111b2a 100644 --- a/drivers/mtd/ubi/vtbl.c +++ b/drivers/mtd/ubi/vtbl.c @@ -115,8 +115,58 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, } /** - * vtbl_check - check if volume table is not corrupted and contains sensible - * data. + * ubi_vtbl_rename_volumes - rename UBI volumes in the volume table. + * @ubi: UBI device description object + * @rename_list: list of &struct ubi_rename_entry objects + * + * This function re-names multiple volumes specified in @req in the volume + * table. Returns zero in case of success and a negative error code in case of + * failure. + */ +int ubi_vtbl_rename_volumes(struct ubi_device *ubi, + struct list_head *rename_list) +{ + int i, err; + struct ubi_rename_entry *re; + struct ubi_volume *layout_vol; + + list_for_each_entry(re, rename_list, list) { + uint32_t crc; + struct ubi_volume *vol = re->desc->vol; + struct ubi_vtbl_record *vtbl_rec = &ubi->vtbl[vol->vol_id]; + + if (re->remove) { + memcpy(vtbl_rec, &empty_vtbl_record, + sizeof(struct ubi_vtbl_record)); + continue; + } + + vtbl_rec->name_len = cpu_to_be16(re->new_name_len); + memcpy(vtbl_rec->name, re->new_name, re->new_name_len); + memset(vtbl_rec->name + re->new_name_len, 0, + UBI_VOL_NAME_MAX + 1 - re->new_name_len); + crc = crc32(UBI_CRC32_INIT, vtbl_rec, + UBI_VTBL_RECORD_SIZE_CRC); + vtbl_rec->crc = cpu_to_be32(crc); + } + + layout_vol = ubi->volumes[vol_id2idx(ubi, UBI_LAYOUT_VOLUME_ID)]; + for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) { + err = ubi_eba_unmap_leb(ubi, layout_vol, i); + if (err) + return err; + + err = ubi_eba_write_leb(ubi, layout_vol, i, ubi->vtbl, 0, + ubi->vtbl_size, UBI_LONGTERM); + if (err) + return err; + } + + return 0; +} + +/** + * vtbl_check - check if volume table is not corrupted and sensible. * @ubi: UBI device description object * @vtbl: volume table * @@ -127,7 +177,7 @@ 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; + int upd_marker, err; uint32_t crc; const char *name; @@ -153,7 +203,7 @@ static int vtbl_check(const struct ubi_device *ubi, if (reserved_pebs == 0) { if (memcmp(&vtbl[i], &empty_vtbl_record, UBI_VTBL_RECORD_SIZE)) { - dbg_err("bad empty record"); + err = 2; goto bad; } continue; @@ -161,56 +211,57 @@ static int vtbl_check(const struct ubi_device *ubi, if (reserved_pebs < 0 || alignment < 0 || data_pad < 0 || name_len < 0) { - dbg_err("negative values"); + err = 3; goto bad; } if (alignment > ubi->leb_size || alignment == 0) { - dbg_err("bad alignment"); + err = 4; goto bad; } - n = alignment % ubi->min_io_size; + n = alignment & (ubi->min_io_size - 1); if (alignment != 1 && n) { - dbg_err("alignment is not multiple of min I/O unit"); + err = 5; goto bad; } n = ubi->leb_size % alignment; if (data_pad != n) { dbg_err("bad data_pad, has to be %d", n); + err = 6; goto bad; } if (vol_type != UBI_VID_DYNAMIC && vol_type != UBI_VID_STATIC) { - dbg_err("bad vol_type"); + err = 7; goto bad; } if (upd_marker != 0 && upd_marker != 1) { - dbg_err("bad upd_marker"); + err = 8; goto bad; } if (reserved_pebs > ubi->good_peb_count) { dbg_err("too large reserved_pebs, good PEBs %d", ubi->good_peb_count); + err = 9; goto bad; } if (name_len > UBI_VOL_NAME_MAX) { - dbg_err("too long volume name, max %d", - UBI_VOL_NAME_MAX); + err = 10; goto bad; } if (name[0] == '\0') { - dbg_err("NULL volume name"); + err = 11; goto bad; } if (name_len != strnlen(name, name_len + 1)) { - dbg_err("bad name_len"); + err = 12; goto bad; } } @@ -235,7 +286,7 @@ static int vtbl_check(const struct ubi_device *ubi, return 0; bad: - ubi_err("volume table check failed, record %d", i); + ubi_err("volume table check failed: record %d, error %d", i, err); ubi_dbg_dump_vtbl_record(&vtbl[i], i); return -EINVAL; } @@ -287,7 +338,6 @@ retry: vid_hdr->data_pad = cpu_to_be32(0); vid_hdr->lnum = cpu_to_be32(copy); vid_hdr->sqnum = cpu_to_be64(++si->max_sqnum); - vid_hdr->leb_ver = cpu_to_be32(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); @@ -370,7 +420,7 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi, * to LEB 0. */ - dbg_msg("check layout volume"); + dbg_gen("check layout volume"); /* Read both LEB 0 and LEB 1 into memory */ ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) { @@ -384,7 +434,16 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi, 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 */ + /* + * Scrub the PEB later. Note, -EBADMSG indicates an + * uncorrectable ECC error, but we have our own CRC and + * the data will be checked later. If the data is OK, + * the PEB will be scrubbed (because we set + * seb->scrub). If the data is not OK, the contents of + * the PEB will be recovered from the second copy, and + * seb->scrub will be cleared in + * 'ubi_scan_add_used()'. + */ seb->scrub = 1; else if (err) goto out_free; @@ -400,7 +459,8 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi, if (!leb_corrupted[0]) { /* LEB 0 is OK */ if (leb[1]) - leb_corrupted[1] = memcmp(leb[0], leb[1], ubi->vtbl_size); + 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]); @@ -620,30 +680,32 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, static int check_sv(const struct ubi_volume *vol, const struct ubi_scan_volume *sv) { + int err; + if (sv->highest_lnum >= vol->reserved_pebs) { - dbg_err("bad highest_lnum"); + err = 1; goto bad; } if (sv->leb_count > vol->reserved_pebs) { - dbg_err("bad leb_count"); + err = 2; goto bad; } if (sv->vol_type != vol->vol_type) { - dbg_err("bad vol_type"); + err = 3; goto bad; } if (sv->used_ebs > vol->reserved_pebs) { - dbg_err("bad used_ebs"); + err = 4; goto bad; } if (sv->data_pad != vol->data_pad) { - dbg_err("bad data_pad"); + err = 5; goto bad; } return 0; bad: - ubi_err("bad scanning information"); + ubi_err("bad scanning information, error %d", err); ubi_dbg_dump_sv(sv); ubi_dbg_dump_vol_info(vol); return -EINVAL; @@ -672,14 +734,13 @@ static int check_scanning_info(const struct ubi_device *ubi, return -EINVAL; } - if (si->highest_vol_id >= ubi->vtbl_slots + UBI_INT_VOL_COUNT&& + 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(); @@ -717,8 +778,7 @@ static int check_scanning_info(const struct ubi_device *ubi, } /** - * ubi_read_volume_table - read volume table. - * information. + * ubi_read_volume_table - read the volume table. * @ubi: UBI device description object * @si: scanning information * @@ -797,11 +857,10 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si) out_free: vfree(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; - } + for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) { + kfree(ubi->volumes[i]); + ubi->volumes[i] = NULL; + } return err; } diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index a471a491f0ab..05d70937b543 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -19,22 +19,22 @@ */ /* - * UBI wear-leveling unit. + * UBI wear-leveling sub-system. * - * 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. + * This sub-system 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 sub-system'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. + * header. The rest of the physical eraseblock contains only %0xFF bytes. * - * When physical eraseblocks are returned to the WL unit by means of the + * When physical eraseblocks are returned to the WL sub-system 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. + * which is also managed by the WL sub-system. * * The wear-leveling is ensured by means of moving the contents of used * physical eraseblocks with low erase counter to free physical eraseblocks @@ -43,34 +43,36 @@ * 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. + * the WL sub-system 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. + * If the WL sub-system 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. + * This sub-system 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, + * As it was said, for the UBI sub-system 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. + * re-work this sub-system 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 + * At the moment this sub-system 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. + * room for future re-works of the WL sub-system. * - * FIXME: looks too complex, should be simplified (later). + * Note: the stuff with protection trees looks too complex and is difficult to + * understand. Should be fixed. */ #include <linux/slab.h> @@ -92,20 +94,21 @@ /* * 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. + * exceeded, the WL sub-system 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 + * When a physical eraseblock is moved, the WL sub-system 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 of the free physical eraseblock to pick. Namely, the WL sub-system + * 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) @@ -123,11 +126,11 @@ * @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). + * When the WL sub-system 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 @@ -175,7 +178,6 @@ struct ubi_wl_prot_entry { * @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 * @@ -473,52 +475,47 @@ 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); + 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(); + 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(); } /* @@ -582,7 +579,8 @@ found: * 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) +static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, + int torture) { int err; struct ubi_ec_hdr *ec_hdr; @@ -634,8 +632,7 @@ out_free: } /** - * check_protection_over - check if it is time to stop protecting some - * physical eraseblocks. + * check_protection_over - check if it is time to stop protecting some PEBs. * @ubi: UBI device description object * * This function is called after each erase operation, when the absolute erase @@ -871,6 +868,10 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, } ubi_free_vid_hdr(ubi, vid_hdr); + if (scrubbing && !protect) + ubi_msg("scrubbed PEB %d, data moved to PEB %d", + e1->pnum, e2->pnum); + spin_lock(&ubi->wl_lock); if (protect) prot_tree_add(ubi, e1, pe, protect); @@ -1054,8 +1055,8 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, spin_unlock(&ubi->wl_lock); /* - * One more erase operation has happened, take care about protected - * physical eraseblocks. + * One more erase operation has happened, take care about + * protected physical eraseblocks. */ check_protection_over(ubi); @@ -1136,7 +1137,7 @@ out_ro: } /** - * ubi_wl_put_peb - return a physical eraseblock to the wear-leveling unit. + * ubi_wl_put_peb - return a PEB to the wear-leveling sub-system. * @ubi: UBI device description object * @pnum: physical eraseblock to return * @torture: if this physical eraseblock has to be tortured @@ -1175,11 +1176,11 @@ retry: /* * 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 in 'ubi_eba_copy_leb()' but - * the WL unit has not put the PEB to the "used" tree yet, but - * it is about to do this. So we just set a flag which will - * tell the WL worker that the PEB is not needed anymore and - * should be scheduled for erasure. + * sub-system already re-mapped the LEB in 'ubi_eba_copy_leb()' + * but the WL sub-system has not put the PEB to the "used" tree + * yet, but it is about to do this. So we just set a flag which + * will tell the WL worker that the PEB is not needed anymore + * and should be scheduled for erasure. */ dbg_wl("PEB %d is the target of data moving", pnum); ubi_assert(!ubi->move_to_put); @@ -1229,7 +1230,7 @@ int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum) { struct ubi_wl_entry *e; - ubi_msg("schedule PEB %d for scrubbing", pnum); + dbg_msg("schedule PEB %d for scrubbing", pnum); retry: spin_lock(&ubi->wl_lock); @@ -1368,7 +1369,7 @@ int ubi_thread(void *u) int err; if (kthread_should_stop()) - goto out; + break; if (try_to_freeze()) continue; @@ -1403,7 +1404,6 @@ int ubi_thread(void *u) cond_resched(); } -out: dbg_wl("background thread \"%s\" is killed", ubi->bgt_name); return 0; } @@ -1426,8 +1426,7 @@ static void cancel_pending(struct ubi_device *ubi) } /** - * ubi_wl_init_scan - initialize the wear-leveling unit using scanning - * information. + * ubi_wl_init_scan - initialize the WL sub-system using scanning information. * @ubi: UBI device description object * @si: scanning information * @@ -1584,13 +1583,12 @@ static void protection_trees_destroy(struct ubi_device *ubi) } /** - * ubi_wl_close - close the wear-leveling unit. + * ubi_wl_close - close the wear-leveling sub-system. * @ubi: UBI device description object */ void ubi_wl_close(struct ubi_device *ubi) { - dbg_wl("close the UBI wear-leveling unit"); - + dbg_wl("close the WL sub-system"); cancel_pending(ubi); protection_trees_destroy(ubi); tree_destroy(&ubi->used); @@ -1602,8 +1600,7 @@ void ubi_wl_close(struct ubi_device *ubi) #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID /** - * paranoid_check_ec - make sure that the erase counter of a physical eraseblock - * is correct. + * paranoid_check_ec - make sure that the erase counter of a PEB is correct. * @ubi: UBI device description object * @pnum: the physical eraseblock number to check * @ec: the erase counter to check @@ -1644,13 +1641,12 @@ out_free: } /** - * paranoid_check_in_wl_tree - make sure that a wear-leveling entry is present - * in a WL RB-tree. + * paranoid_check_in_wl_tree - check that wear-leveling entry is in 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. + * 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) diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index aabad8ce7458..8db4e6b89482 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -1010,7 +1010,7 @@ static int __devinit vortex_probe1(struct device *gendev, static int printed_version; int retval, print_info; struct vortex_chip_info * const vci = &vortex_info_tbl[chip_idx]; - char *print_name = "3c59x"; + const char *print_name = "3c59x"; struct pci_dev *pdev = NULL; struct eisa_device *edev = NULL; DECLARE_MAC_BUF(mac); diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c index 75317a14ad1c..8a5b0d293f75 100644 --- a/drivers/net/8139too.c +++ b/drivers/net/8139too.c @@ -98,7 +98,6 @@ #include <linux/compiler.h> #include <linux/pci.h> #include <linux/init.h> -#include <linux/ioport.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/rtnetlink.h> @@ -120,11 +119,6 @@ NETIF_MSG_LINK) -/* enable PIO instead of MMIO, if CONFIG_8139TOO_PIO is selected */ -#ifdef CONFIG_8139TOO_PIO -#define USE_IO_OPS 1 -#endif - /* define to 1, 2 or 3 to enable copious debugging info */ #define RTL8139_DEBUG 0 @@ -156,6 +150,13 @@ static int media[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +/* Whether to use MMIO or PIO. Default to MMIO. */ +#ifdef CONFIG_8139TOO_PIO +static int use_io = 1; +#else +static int use_io = 0; +#endif + /* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). The RTL chips use a 64 element hash table based on the Ethernet CRC. */ static int multicast_filter_limit = 32; @@ -614,6 +615,8 @@ MODULE_DESCRIPTION ("RealTek RTL-8139 Fast Ethernet driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); +module_param(use_io, int, 0); +MODULE_PARM_DESC(use_io, "Force use of I/O access mode. 0=MMIO 1=PIO"); module_param(multicast_filter_limit, int, 0); module_param_array(media, int, NULL, 0); module_param_array(full_duplex, int, NULL, 0); @@ -709,13 +712,8 @@ static void __rtl8139_cleanup_dev (struct net_device *dev) assert (tp->pci_dev != NULL); pdev = tp->pci_dev; -#ifdef USE_IO_OPS - if (tp->mmio_addr) - ioport_unmap (tp->mmio_addr); -#else if (tp->mmio_addr) pci_iounmap (pdev, tp->mmio_addr); -#endif /* USE_IO_OPS */ /* it's ok to call this even if we have no regions to free */ pci_release_regions (pdev); @@ -790,32 +788,33 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, DPRINTK("PIO region size == 0x%02X\n", pio_len); DPRINTK("MMIO region size == 0x%02lX\n", mmio_len); -#ifdef USE_IO_OPS - /* make sure PCI base addr 0 is PIO */ - if (!(pio_flags & IORESOURCE_IO)) { - dev_err(&pdev->dev, "region #0 not a PIO resource, aborting\n"); - rc = -ENODEV; - goto err_out; - } - /* check for weird/broken PCI region reporting */ - if (pio_len < RTL_MIN_IO_SIZE) { - dev_err(&pdev->dev, "Invalid PCI I/O region size(s), aborting\n"); - rc = -ENODEV; - goto err_out; - } -#else - /* 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; - } - if (mmio_len < RTL_MIN_IO_SIZE) { - dev_err(&pdev->dev, "Invalid PCI mem region size(s), aborting\n"); - rc = -ENODEV; - goto err_out; +retry: + if (use_io) { + /* make sure PCI base addr 0 is PIO */ + if (!(pio_flags & IORESOURCE_IO)) { + dev_err(&pdev->dev, "region #0 not a PIO resource, aborting\n"); + rc = -ENODEV; + goto err_out; + } + /* check for weird/broken PCI region reporting */ + if (pio_len < RTL_MIN_IO_SIZE) { + dev_err(&pdev->dev, "Invalid PCI I/O region size(s), aborting\n"); + rc = -ENODEV; + goto err_out; + } + } else { + /* 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; + } + if (mmio_len < RTL_MIN_IO_SIZE) { + dev_err(&pdev->dev, "Invalid PCI mem region size(s), aborting\n"); + rc = -ENODEV; + goto err_out; + } } -#endif rc = pci_request_regions (pdev, DRV_NAME); if (rc) @@ -825,28 +824,28 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, /* enable PCI bus-mastering */ pci_set_master (pdev); -#ifdef USE_IO_OPS - ioaddr = ioport_map(pio_start, pio_len); - if (!ioaddr) { - dev_err(&pdev->dev, "cannot map PIO, aborting\n"); - rc = -EIO; - goto err_out; - } - dev->base_addr = pio_start; - tp->mmio_addr = ioaddr; - tp->regs_len = pio_len; -#else - /* ioremap MMIO region */ - ioaddr = pci_iomap(pdev, 1, 0); - if (ioaddr == NULL) { - dev_err(&pdev->dev, "cannot remap MMIO, aborting\n"); - rc = -EIO; - goto err_out; + if (use_io) { + ioaddr = pci_iomap(pdev, 0, 0); + if (!ioaddr) { + dev_err(&pdev->dev, "cannot map PIO, aborting\n"); + rc = -EIO; + goto err_out; + } + dev->base_addr = pio_start; + tp->regs_len = pio_len; + } else { + /* ioremap MMIO region */ + ioaddr = pci_iomap(pdev, 1, 0); + if (ioaddr == NULL) { + dev_err(&pdev->dev, "cannot remap MMIO, trying PIO\n"); + pci_release_regions(pdev); + use_io = 1; + goto retry; + } + dev->base_addr = (long) ioaddr; + tp->regs_len = mmio_len; } - dev->base_addr = (long) ioaddr; tp->mmio_addr = ioaddr; - tp->regs_len = mmio_len; -#endif /* USE_IO_OPS */ /* Bring old chips out of low-power mode. */ RTL_W8 (HltClk, 'R'); @@ -952,6 +951,14 @@ static int __devinit rtl8139_init_one (struct pci_dev *pdev, "Use the \"8139cp\" driver for improved performance and stability.\n"); } + if (pdev->vendor == PCI_VENDOR_ID_REALTEK && + pdev->device == PCI_DEVICE_ID_REALTEK_8139 && + pdev->subsystem_vendor == PCI_VENDOR_ID_ATHEROS && + pdev->subsystem_device == PCI_DEVICE_ID_REALTEK_8139) { + printk(KERN_INFO "8139too: OQO Model 2 detected. Forcing PIO\n"); + use_io = 1; + } + i = rtl8139_init_board (pdev, &dev); if (i < 0) return i; @@ -2381,20 +2388,24 @@ static void rtl8139_set_msglevel(struct net_device *dev, u32 datum) np->msg_enable = datum; } -/* TODO: we are too slack to do reg dumping for pio, for now */ -#ifdef CONFIG_8139TOO_PIO -#define rtl8139_get_regs_len NULL -#define rtl8139_get_regs NULL -#else static int rtl8139_get_regs_len(struct net_device *dev) { - struct rtl8139_private *np = netdev_priv(dev); + struct rtl8139_private *np; + /* TODO: we are too slack to do reg dumping for pio, for now */ + if (use_io) + return 0; + np = netdev_priv(dev); return np->regs_len; } static void rtl8139_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf) { - struct rtl8139_private *np = netdev_priv(dev); + struct rtl8139_private *np; + + /* TODO: we are too slack to do reg dumping for pio, for now */ + if (use_io) + return; + np = netdev_priv(dev); regs->version = RTL_REGS_VER; @@ -2402,7 +2413,6 @@ static void rtl8139_get_regs(struct net_device *dev, struct ethtool_regs *regs, memcpy_fromio(regbuf, np->mmio_addr, regs->len); spin_unlock_irq(&np->lock); } -#endif /* CONFIG_8139TOO_MMIO */ static int rtl8139_get_sset_count(struct net_device *dev, int sset) { diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 3e5e64c33e18..fa533c27052a 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1926,20 +1926,6 @@ config E1000 To compile this driver as a module, choose M here. The module will be called e1000. -config E1000_NAPI - bool "Use Rx Polling (NAPI)" - depends on E1000 - 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 - still somewhat experimental and thus not yet enabled by default. - - If your estimated Rx load is 10kpps or more, or if the card will be - deployed on potentially unfriendly networks (e.g. in a firewall), - then say Y here. - - If in doubt, say N. - config E1000_DISABLE_PACKET_SPLIT bool "Disable Packet Split for PCI express adapters" depends on E1000 @@ -2304,6 +2290,17 @@ config ATL1 To compile this driver as a module, choose M here. The module will be called atl1. +config ATL1E + tristate "Atheros L1E Gigabit Ethernet support (EXPERIMENTAL)" + depends on PCI && EXPERIMENTAL + select CRC32 + select MII + help + This driver supports the Atheros L1E gigabit ethernet adapter. + + To compile this driver as a module, choose M here. The module + will be called atl1e. + endif # NETDEV_1000 # diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 4b17a9ab7861..7629c9017215 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_EHEA) += ehea/ obj-$(CONFIG_CAN) += can/ obj-$(CONFIG_BONDING) += bonding/ obj-$(CONFIG_ATL1) += atlx/ +obj-$(CONFIG_ATL1E) += atl1e/ obj-$(CONFIG_GIANFAR) += gianfar_driver.o obj-$(CONFIG_TEHUTI) += tehuti.o diff --git a/drivers/net/arm/at91_ether.c b/drivers/net/arm/at91_ether.c index 1e39e78f1778..ffae266e2d7f 100644 --- a/drivers/net/arm/at91_ether.c +++ b/drivers/net/arm/at91_ether.c @@ -677,7 +677,7 @@ static void at91ether_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo { strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, dev->dev.parent->bus_id, sizeof(info->bus_info)); + strlcpy(info->bus_info, dev_name(dev->dev.parent), sizeof(info->bus_info)); } static const struct ethtool_ops at91ether_ethtool_ops = { @@ -820,7 +820,7 @@ static int at91ether_tx(struct sk_buff *skb, struct net_device *dev) lp->skb = skb; lp->skb_length = skb->len; lp->skb_physaddr = dma_map_single(NULL, skb->data, skb->len, DMA_TO_DEVICE); - lp->stats.tx_bytes += skb->len; + dev->stats.tx_bytes += skb->len; /* Set address of the data in the Transmit Address register */ at91_emac_write(AT91_EMAC_TAR, lp->skb_physaddr); @@ -843,34 +843,33 @@ static int at91ether_tx(struct sk_buff *skb, struct net_device *dev) */ static struct net_device_stats *at91ether_stats(struct net_device *dev) { - struct at91_private *lp = netdev_priv(dev); int ale, lenerr, seqe, lcol, ecol; if (netif_running(dev)) { - lp->stats.rx_packets += at91_emac_read(AT91_EMAC_OK); /* Good frames received */ + dev->stats.rx_packets += at91_emac_read(AT91_EMAC_OK); /* Good frames received */ ale = at91_emac_read(AT91_EMAC_ALE); - lp->stats.rx_frame_errors += ale; /* Alignment errors */ + dev->stats.rx_frame_errors += ale; /* Alignment errors */ lenerr = at91_emac_read(AT91_EMAC_ELR) + at91_emac_read(AT91_EMAC_USF); - lp->stats.rx_length_errors += lenerr; /* Excessive Length or Undersize Frame error */ + dev->stats.rx_length_errors += lenerr; /* Excessive Length or Undersize Frame error */ seqe = at91_emac_read(AT91_EMAC_SEQE); - lp->stats.rx_crc_errors += seqe; /* CRC error */ - lp->stats.rx_fifo_errors += at91_emac_read(AT91_EMAC_DRFC); /* Receive buffer not available */ - lp->stats.rx_errors += (ale + lenerr + seqe + dev->stats.rx_crc_errors += seqe; /* CRC error */ + dev->stats.rx_fifo_errors += at91_emac_read(AT91_EMAC_DRFC); /* Receive buffer not available */ + dev->stats.rx_errors += (ale + lenerr + seqe + at91_emac_read(AT91_EMAC_CDE) + at91_emac_read(AT91_EMAC_RJB)); - lp->stats.tx_packets += at91_emac_read(AT91_EMAC_FRA); /* Frames successfully transmitted */ - lp->stats.tx_fifo_errors += at91_emac_read(AT91_EMAC_TUE); /* Transmit FIFO underruns */ - lp->stats.tx_carrier_errors += at91_emac_read(AT91_EMAC_CSE); /* Carrier Sense errors */ - lp->stats.tx_heartbeat_errors += at91_emac_read(AT91_EMAC_SQEE);/* Heartbeat error */ + dev->stats.tx_packets += at91_emac_read(AT91_EMAC_FRA); /* Frames successfully transmitted */ + dev->stats.tx_fifo_errors += at91_emac_read(AT91_EMAC_TUE); /* Transmit FIFO underruns */ + dev->stats.tx_carrier_errors += at91_emac_read(AT91_EMAC_CSE); /* Carrier Sense errors */ + dev->stats.tx_heartbeat_errors += at91_emac_read(AT91_EMAC_SQEE);/* Heartbeat error */ lcol = at91_emac_read(AT91_EMAC_LCOL); ecol = at91_emac_read(AT91_EMAC_ECOL); - lp->stats.tx_window_errors += lcol; /* Late collisions */ - lp->stats.tx_aborted_errors += ecol; /* 16 collisions */ + dev->stats.tx_window_errors += lcol; /* Late collisions */ + dev->stats.tx_aborted_errors += ecol; /* 16 collisions */ - lp->stats.collisions += (at91_emac_read(AT91_EMAC_SCOL) + at91_emac_read(AT91_EMAC_MCOL) + lcol + ecol); + dev->stats.collisions += (at91_emac_read(AT91_EMAC_SCOL) + at91_emac_read(AT91_EMAC_MCOL) + lcol + ecol); } - return &lp->stats; + return &dev->stats; } /* @@ -896,16 +895,16 @@ static void at91ether_rx(struct net_device *dev) skb->protocol = eth_type_trans(skb, dev); dev->last_rx = jiffies; - lp->stats.rx_bytes += pktlen; + dev->stats.rx_bytes += pktlen; netif_rx(skb); } else { - lp->stats.rx_dropped += 1; + dev->stats.rx_dropped += 1; printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name); } if (dlist->descriptors[lp->rxBuffIndex].size & EMAC_MULTICAST) - lp->stats.multicast++; + dev->stats.multicast++; dlist->descriptors[lp->rxBuffIndex].addr &= ~EMAC_DESC_DONE; /* reset ownership bit */ if (lp->rxBuffIndex == MAX_RX_DESCR-1) /* wrap after last buffer */ @@ -934,7 +933,7 @@ static irqreturn_t at91ether_interrupt(int irq, void *dev_id) if (intstatus & AT91_EMAC_TCOM) { /* Transmit complete */ /* The TCOM bit is set even if the transmission failed. */ if (intstatus & (AT91_EMAC_TUND | AT91_EMAC_RTRY)) - lp->stats.tx_errors += 1; + dev->stats.tx_errors += 1; if (lp->skb) { dev_kfree_skb_irq(lp->skb); diff --git a/drivers/net/arm/at91_ether.h b/drivers/net/arm/at91_ether.h index a38fd2d053a6..353f4dab62be 100644 --- a/drivers/net/arm/at91_ether.h +++ b/drivers/net/arm/at91_ether.h @@ -84,7 +84,6 @@ struct recv_desc_bufs struct at91_private { - struct net_device_stats stats; struct mii_if_info mii; /* ethtool support */ struct at91_eth_data board_data; /* board-specific configuration */ struct clk *ether_clk; /* clock */ diff --git a/drivers/net/arm/ep93xx_eth.c b/drivers/net/arm/ep93xx_eth.c index ecd8fc6146e9..18d3eeb7eab2 100644 --- a/drivers/net/arm/ep93xx_eth.c +++ b/drivers/net/arm/ep93xx_eth.c @@ -482,7 +482,7 @@ static int ep93xx_alloc_buffers(struct ep93xx_priv *ep) goto err; d = dma_map_single(NULL, page, PAGE_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(d)) { + if (dma_mapping_error(NULL, d)) { free_page((unsigned long)page); goto err; } @@ -505,7 +505,7 @@ static int ep93xx_alloc_buffers(struct ep93xx_priv *ep) goto err; d = dma_map_single(NULL, page, PAGE_SIZE, DMA_TO_DEVICE); - if (dma_mapping_error(d)) { + if (dma_mapping_error(NULL, d)) { free_page((unsigned long)page); goto err; } @@ -848,7 +848,7 @@ static int ep93xx_eth_probe(struct platform_device *pdev) ep->res = request_mem_region(pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start + 1, - pdev->dev.bus_id); + dev_name(&pdev->dev)); if (ep->res == NULL) { dev_err(&pdev->dev, "Could not reserve memory region\n"); err = -ENOMEM; diff --git a/drivers/net/arm/etherh.c b/drivers/net/arm/etherh.c index e9d15eccad08..5c5f1e470d3c 100644 --- a/drivers/net/arm/etherh.c +++ b/drivers/net/arm/etherh.c @@ -535,7 +535,7 @@ static int __init etherh_addr(char *addr, struct expansion_card *ec) if (!ecard_readchunk(&cd, ec, 0xf5, 0)) { printk(KERN_ERR "%s: unable to read podule description string\n", - ec->dev.bus_id); + dev_name(&ec->dev)); goto no_addr; } @@ -554,7 +554,7 @@ static int __init etherh_addr(char *addr, struct expansion_card *ec) } printk(KERN_ERR "%s: unable to parse MAC address: %s\n", - ec->dev.bus_id, cd.d.string); + dev_name(&ec->dev), cd.d.string); no_addr: return -ENODEV; @@ -585,7 +585,7 @@ static void etherh_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *i { strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, dev->dev.parent->bus_id, + strlcpy(info->bus_info, dev_name(dev->dev.parent), sizeof(info->bus_info)); } diff --git a/drivers/net/atl1e/Makefile b/drivers/net/atl1e/Makefile new file mode 100644 index 000000000000..bc11be824e76 --- /dev/null +++ b/drivers/net/atl1e/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_ATL1E) += atl1e.o +atl1e-objs += atl1e_main.o atl1e_hw.o atl1e_ethtool.o atl1e_param.o diff --git a/drivers/net/atl1e/atl1e.h b/drivers/net/atl1e/atl1e.h new file mode 100644 index 000000000000..b645fa0f3f64 --- /dev/null +++ b/drivers/net/atl1e/atl1e.h @@ -0,0 +1,503 @@ +/* + * Copyright(c) 2007 Atheros Corporation. All rights reserved. + * Copyright(c) 2007 xiong huang <xiong.huang@atheros.com> + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * 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 _ATL1E_H_ +#define _ATL1E_H_ + +#include <linux/version.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <linux/udp.h> +#include <linux/mii.h> +#include <linux/io.h> +#include <linux/vmalloc.h> +#include <linux/pagemap.h> +#include <linux/tcp.h> +#include <linux/mii.h> +#include <linux/ethtool.h> +#include <linux/if_vlan.h> +#include <linux/workqueue.h> +#include <net/checksum.h> +#include <net/ip6_checksum.h> + +#include "atl1e_hw.h" + +#define PCI_REG_COMMAND 0x04 /* PCI Command Register */ +#define CMD_IO_SPACE 0x0001 +#define CMD_MEMORY_SPACE 0x0002 +#define CMD_BUS_MASTER 0x0004 + +#define BAR_0 0 +#define BAR_1 1 +#define BAR_5 5 + +/* Wake Up Filter Control */ +#define AT_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */ +#define AT_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */ +#define AT_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */ +#define AT_WUFC_MC 0x00000008 /* Multicast Wakeup Enable */ +#define AT_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */ + +#define SPEED_0 0xffff +#define HALF_DUPLEX 1 +#define FULL_DUPLEX 2 + +/* Error Codes */ +#define AT_ERR_EEPROM 1 +#define AT_ERR_PHY 2 +#define AT_ERR_CONFIG 3 +#define AT_ERR_PARAM 4 +#define AT_ERR_MAC_TYPE 5 +#define AT_ERR_PHY_TYPE 6 +#define AT_ERR_PHY_SPEED 7 +#define AT_ERR_PHY_RES 8 +#define AT_ERR_TIMEOUT 9 + +#define MAX_JUMBO_FRAME_SIZE 0x2000 + +#define AT_VLAN_TAG_TO_TPD_TAG(_vlan, _tpd) \ + _tpd = (((_vlan) << (4)) | (((_vlan) >> 13) & 7) |\ + (((_vlan) >> 9) & 8)) + +#define AT_TPD_TAG_TO_VLAN_TAG(_tpd, _vlan) \ + _vlan = (((_tpd) >> 8) | (((_tpd) & 0x77) << 9) |\ + (((_tdp) & 0x88) << 5)) + +#define AT_MAX_RECEIVE_QUEUE 4 +#define AT_PAGE_NUM_PER_QUEUE 2 + +#define AT_DMA_HI_ADDR_MASK 0xffffffff00000000ULL +#define AT_DMA_LO_ADDR_MASK 0x00000000ffffffffULL + +#define AT_TX_WATCHDOG (5 * HZ) +#define AT_MAX_INT_WORK 10 +#define AT_TWSI_EEPROM_TIMEOUT 100 +#define AT_HW_MAX_IDLE_DELAY 10 +#define AT_SUSPEND_LINK_TIMEOUT 28 + +#define AT_REGS_LEN 75 +#define AT_EEPROM_LEN 512 +#define AT_ADV_MASK (ADVERTISE_10_HALF |\ + ADVERTISE_10_FULL |\ + ADVERTISE_100_HALF |\ + ADVERTISE_100_FULL |\ + ADVERTISE_1000_FULL) + +/* tpd word 2 */ +#define TPD_BUFLEN_MASK 0x3FFF +#define TPD_BUFLEN_SHIFT 0 +#define TPD_DMAINT_MASK 0x0001 +#define TPD_DMAINT_SHIFT 14 +#define TPD_PKTNT_MASK 0x0001 +#define TPD_PKTINT_SHIFT 15 +#define TPD_VLANTAG_MASK 0xFFFF +#define TPD_VLAN_SHIFT 16 + +/* tpd word 3 bits 0:4 */ +#define TPD_EOP_MASK 0x0001 +#define TPD_EOP_SHIFT 0 +#define TPD_IP_VERSION_MASK 0x0001 +#define TPD_IP_VERSION_SHIFT 1 /* 0 : IPV4, 1 : IPV6 */ +#define TPD_INS_VL_TAG_MASK 0x0001 +#define TPD_INS_VL_TAG_SHIFT 2 +#define TPD_CC_SEGMENT_EN_MASK 0x0001 +#define TPD_CC_SEGMENT_EN_SHIFT 3 +#define TPD_SEGMENT_EN_MASK 0x0001 +#define TPD_SEGMENT_EN_SHIFT 4 + +/* tdp word 3 bits 5:7 if ip version is 0 */ +#define TPD_IP_CSUM_MASK 0x0001 +#define TPD_IP_CSUM_SHIFT 5 +#define TPD_TCP_CSUM_MASK 0x0001 +#define TPD_TCP_CSUM_SHIFT 6 +#define TPD_UDP_CSUM_MASK 0x0001 +#define TPD_UDP_CSUM_SHIFT 7 + +/* tdp word 3 bits 5:7 if ip version is 1 */ +#define TPD_V6_IPHLLO_MASK 0x0007 +#define TPD_V6_IPHLLO_SHIFT 7 + +/* tpd word 3 bits 8:9 bit */ +#define TPD_VL_TAGGED_MASK 0x0001 +#define TPD_VL_TAGGED_SHIFT 8 +#define TPD_ETHTYPE_MASK 0x0001 +#define TPD_ETHTYPE_SHIFT 9 + +/* tdp word 3 bits 10:13 if ip version is 0 */ +#define TDP_V4_IPHL_MASK 0x000F +#define TPD_V4_IPHL_SHIFT 10 + +/* tdp word 3 bits 10:13 if ip version is 1 */ +#define TPD_V6_IPHLHI_MASK 0x000F +#define TPD_V6_IPHLHI_SHIFT 10 + +/* tpd word 3 bit 14:31 if segment enabled */ +#define TPD_TCPHDRLEN_MASK 0x000F +#define TPD_TCPHDRLEN_SHIFT 14 +#define TPD_HDRFLAG_MASK 0x0001 +#define TPD_HDRFLAG_SHIFT 18 +#define TPD_MSS_MASK 0x1FFF +#define TPD_MSS_SHIFT 19 + +/* tdp word 3 bit 16:31 if custom csum enabled */ +#define TPD_PLOADOFFSET_MASK 0x00FF +#define TPD_PLOADOFFSET_SHIFT 16 +#define TPD_CCSUMOFFSET_MASK 0x00FF +#define TPD_CCSUMOFFSET_SHIFT 24 + +struct atl1e_tpd_desc { + __le64 buffer_addr; + __le32 word2; + __le32 word3; +}; + +/* how about 0x2000 */ +#define MAX_TX_BUF_LEN 0x2000 +#define MAX_TX_BUF_SHIFT 13 +/*#define MAX_TX_BUF_LEN 0x3000 */ + +/* rrs word 1 bit 0:31 */ +#define RRS_RX_CSUM_MASK 0xFFFF +#define RRS_RX_CSUM_SHIFT 0 +#define RRS_PKT_SIZE_MASK 0x3FFF +#define RRS_PKT_SIZE_SHIFT 16 +#define RRS_CPU_NUM_MASK 0x0003 +#define RRS_CPU_NUM_SHIFT 30 + +#define RRS_IS_RSS_IPV4 0x0001 +#define RRS_IS_RSS_IPV4_TCP 0x0002 +#define RRS_IS_RSS_IPV6 0x0004 +#define RRS_IS_RSS_IPV6_TCP 0x0008 +#define RRS_IS_IPV6 0x0010 +#define RRS_IS_IP_FRAG 0x0020 +#define RRS_IS_IP_DF 0x0040 +#define RRS_IS_802_3 0x0080 +#define RRS_IS_VLAN_TAG 0x0100 +#define RRS_IS_ERR_FRAME 0x0200 +#define RRS_IS_IPV4 0x0400 +#define RRS_IS_UDP 0x0800 +#define RRS_IS_TCP 0x1000 +#define RRS_IS_BCAST 0x2000 +#define RRS_IS_MCAST 0x4000 +#define RRS_IS_PAUSE 0x8000 + +#define RRS_ERR_BAD_CRC 0x0001 +#define RRS_ERR_CODE 0x0002 +#define RRS_ERR_DRIBBLE 0x0004 +#define RRS_ERR_RUNT 0x0008 +#define RRS_ERR_RX_OVERFLOW 0x0010 +#define RRS_ERR_TRUNC 0x0020 +#define RRS_ERR_IP_CSUM 0x0040 +#define RRS_ERR_L4_CSUM 0x0080 +#define RRS_ERR_LENGTH 0x0100 +#define RRS_ERR_DES_ADDR 0x0200 + +struct atl1e_recv_ret_status { + u16 seq_num; + u16 hash_lo; + __le32 word1; + u16 pkt_flag; + u16 err_flag; + u16 hash_hi; + u16 vtag; +}; + +enum atl1e_dma_req_block { + atl1e_dma_req_128 = 0, + atl1e_dma_req_256 = 1, + atl1e_dma_req_512 = 2, + atl1e_dma_req_1024 = 3, + atl1e_dma_req_2048 = 4, + atl1e_dma_req_4096 = 5 +}; + +enum atl1e_rrs_type { + atl1e_rrs_disable = 0, + atl1e_rrs_ipv4 = 1, + atl1e_rrs_ipv4_tcp = 2, + atl1e_rrs_ipv6 = 4, + atl1e_rrs_ipv6_tcp = 8 +}; + +enum atl1e_nic_type { + athr_l1e = 0, + athr_l2e_revA = 1, + athr_l2e_revB = 2 +}; + +struct atl1e_hw_stats { + /* rx */ + unsigned long rx_ok; /* The number of good packet received. */ + unsigned long rx_bcast; /* The number of good broadcast packet received. */ + unsigned long rx_mcast; /* The number of good multicast packet received. */ + unsigned long rx_pause; /* The number of Pause packet received. */ + unsigned long rx_ctrl; /* The number of Control packet received other than Pause frame. */ + unsigned long rx_fcs_err; /* The number of packets with bad FCS. */ + unsigned long rx_len_err; /* The number of packets with mismatch of length field and actual size. */ + unsigned long rx_byte_cnt; /* The number of bytes of good packet received. FCS is NOT included. */ + unsigned long rx_runt; /* The number of packets received that are less than 64 byte long and with good FCS. */ + unsigned long rx_frag; /* The number of packets received that are less than 64 byte long and with bad FCS. */ + unsigned long rx_sz_64; /* The number of good and bad packets received that are 64 byte long. */ + unsigned long rx_sz_65_127; /* The number of good and bad packets received that are between 65 and 127-byte long. */ + unsigned long rx_sz_128_255; /* The number of good and bad packets received that are between 128 and 255-byte long. */ + unsigned long rx_sz_256_511; /* The number of good and bad packets received that are between 256 and 511-byte long. */ + unsigned long rx_sz_512_1023; /* The number of good and bad packets received that are between 512 and 1023-byte long. */ + unsigned long rx_sz_1024_1518; /* The number of good and bad packets received that are between 1024 and 1518-byte long. */ + unsigned long rx_sz_1519_max; /* The number of good and bad packets received that are between 1519-byte and MTU. */ + unsigned long rx_sz_ov; /* The number of good and bad packets received that are more than MTU size truncated by Selene. */ + unsigned long rx_rxf_ov; /* The number of frame dropped due to occurrence of RX FIFO overflow. */ + unsigned long rx_rrd_ov; /* The number of frame dropped due to occurrence of RRD overflow. */ + unsigned long rx_align_err; /* Alignment Error */ + unsigned long rx_bcast_byte_cnt; /* The byte count of broadcast packet received, excluding FCS. */ + unsigned long rx_mcast_byte_cnt; /* The byte count of multicast packet received, excluding FCS. */ + unsigned long rx_err_addr; /* The number of packets dropped due to address filtering. */ + + /* tx */ + unsigned long tx_ok; /* The number of good packet transmitted. */ + unsigned long tx_bcast; /* The number of good broadcast packet transmitted. */ + unsigned long tx_mcast; /* The number of good multicast packet transmitted. */ + unsigned long tx_pause; /* The number of Pause packet transmitted. */ + unsigned long tx_exc_defer; /* The number of packets transmitted with excessive deferral. */ + unsigned long tx_ctrl; /* The number of packets transmitted is a control frame, excluding Pause frame. */ + unsigned long tx_defer; /* The number of packets transmitted that is deferred. */ + unsigned long tx_byte_cnt; /* The number of bytes of data transmitted. FCS is NOT included. */ + unsigned long tx_sz_64; /* The number of good and bad packets transmitted that are 64 byte long. */ + unsigned long tx_sz_65_127; /* The number of good and bad packets transmitted that are between 65 and 127-byte long. */ + unsigned long tx_sz_128_255; /* The number of good and bad packets transmitted that are between 128 and 255-byte long. */ + unsigned long tx_sz_256_511; /* The number of good and bad packets transmitted that are between 256 and 511-byte long. */ + unsigned long tx_sz_512_1023; /* The number of good and bad packets transmitted that are between 512 and 1023-byte long. */ + unsigned long tx_sz_1024_1518; /* The number of good and bad packets transmitted that are between 1024 and 1518-byte long. */ + unsigned long tx_sz_1519_max; /* The number of good and bad packets transmitted that are between 1519-byte and MTU. */ + unsigned long tx_1_col; /* The number of packets subsequently transmitted successfully with a single prior collision. */ + unsigned long tx_2_col; /* The number of packets subsequently transmitted successfully with multiple prior collisions. */ + unsigned long tx_late_col; /* The number of packets transmitted with late collisions. */ + unsigned long tx_abort_col; /* The number of transmit packets aborted due to excessive collisions. */ + unsigned long tx_underrun; /* The number of transmit packets aborted due to transmit FIFO underrun, or TRD FIFO underrun */ + unsigned long tx_rd_eop; /* The number of times that read beyond the EOP into the next frame area when TRD was not written timely */ + unsigned long tx_len_err; /* The number of transmit packets with length field does NOT match the actual frame size. */ + unsigned long tx_trunc; /* The number of transmit packets truncated due to size exceeding MTU. */ + unsigned long tx_bcast_byte; /* The byte count of broadcast packet transmitted, excluding FCS. */ + unsigned long tx_mcast_byte; /* The byte count of multicast packet transmitted, excluding FCS. */ +}; + +struct atl1e_hw { + u8 __iomem *hw_addr; /* inner register address */ + resource_size_t mem_rang; + struct atl1e_adapter *adapter; + enum atl1e_nic_type nic_type; + u16 device_id; + u16 vendor_id; + u16 subsystem_id; + u16 subsystem_vendor_id; + u8 revision_id; + u16 pci_cmd_word; + u8 mac_addr[ETH_ALEN]; + u8 perm_mac_addr[ETH_ALEN]; + u8 preamble_len; + u16 max_frame_size; + u16 rx_jumbo_th; + u16 tx_jumbo_th; + + u16 media_type; +#define MEDIA_TYPE_AUTO_SENSOR 0 +#define MEDIA_TYPE_100M_FULL 1 +#define MEDIA_TYPE_100M_HALF 2 +#define MEDIA_TYPE_10M_FULL 3 +#define MEDIA_TYPE_10M_HALF 4 + + u16 autoneg_advertised; +#define ADVERTISE_10_HALF 0x0001 +#define ADVERTISE_10_FULL 0x0002 +#define ADVERTISE_100_HALF 0x0004 +#define ADVERTISE_100_FULL 0x0008 +#define ADVERTISE_1000_HALF 0x0010 /* Not used, just FYI */ +#define ADVERTISE_1000_FULL 0x0020 + u16 mii_autoneg_adv_reg; + u16 mii_1000t_ctrl_reg; + + u16 imt; /* Interrupt Moderator timer ( 2us resolution) */ + u16 ict; /* Interrupt Clear timer (2us resolution) */ + u32 smb_timer; + u16 rrd_thresh; /* Threshold of number of RRD produced to trigger + interrupt request */ + u16 tpd_thresh; + u16 rx_count_down; /* 2us resolution */ + u16 tx_count_down; + + u8 tpd_burst; /* Number of TPD to prefetch in cache-aligned burst. */ + enum atl1e_rrs_type rrs_type; + u32 base_cpu; + u32 indirect_tab; + + enum atl1e_dma_req_block dmar_block; + enum atl1e_dma_req_block dmaw_block; + u8 dmaw_dly_cnt; + u8 dmar_dly_cnt; + + bool phy_configured; + bool re_autoneg; + bool emi_ca; +}; + +/* + * wrapper around a pointer to a socket buffer, + * so a DMA handle can be stored along with the buffer + */ +struct atl1e_tx_buffer { + struct sk_buff *skb; + u16 length; + dma_addr_t dma; +}; + +struct atl1e_rx_page { + dma_addr_t dma; /* receive rage DMA address */ + u8 *addr; /* receive rage virtual address */ + dma_addr_t write_offset_dma; /* the DMA address which contain the + receive data offset in the page */ + u32 *write_offset_addr; /* the virtaul address which contain + the receive data offset in the page */ + u32 read_offset; /* the offset where we have read */ +}; + +struct atl1e_rx_page_desc { + struct atl1e_rx_page rx_page[AT_PAGE_NUM_PER_QUEUE]; + u8 rx_using; + u16 rx_nxseq; +}; + +/* transmit packet descriptor (tpd) ring */ +struct atl1e_tx_ring { + struct atl1e_tpd_desc *desc; /* descriptor ring virtual address */ + dma_addr_t dma; /* descriptor ring physical address */ + u16 count; /* the count of transmit rings */ + rwlock_t tx_lock; + u16 next_to_use; + atomic_t next_to_clean; + struct atl1e_tx_buffer *tx_buffer; + dma_addr_t cmb_dma; + u32 *cmb; +}; + +/* receive packet descriptor ring */ +struct atl1e_rx_ring { + void *desc; + dma_addr_t dma; + int size; + u32 page_size; /* bytes length of rxf page */ + u32 real_page_size; /* real_page_size = page_size + jumbo + aliagn */ + struct atl1e_rx_page_desc rx_page_desc[AT_MAX_RECEIVE_QUEUE]; +}; + +/* board specific private data structure */ +struct atl1e_adapter { + struct net_device *netdev; + struct pci_dev *pdev; + struct vlan_group *vlgrp; + struct napi_struct napi; + struct mii_if_info mii; /* MII interface info */ + struct atl1e_hw hw; + struct atl1e_hw_stats hw_stats; + struct net_device_stats net_stats; + + bool have_msi; + u32 wol; + u16 link_speed; + u16 link_duplex; + + spinlock_t mdio_lock; + spinlock_t tx_lock; + atomic_t irq_sem; + + struct work_struct reset_task; + struct work_struct link_chg_task; + struct timer_list watchdog_timer; + struct timer_list phy_config_timer; + + /* All Descriptor memory */ + dma_addr_t ring_dma; + void *ring_vir_addr; + int ring_size; + + struct atl1e_tx_ring tx_ring; + struct atl1e_rx_ring rx_ring; + int num_rx_queues; + unsigned long flags; +#define __AT_TESTING 0x0001 +#define __AT_RESETTING 0x0002 +#define __AT_DOWN 0x0003 + + u32 bd_number; /* board number;*/ + u32 pci_state[16]; + u32 *config_space; +}; + +#define AT_WRITE_REG(a, reg, value) ( \ + writel((value), ((a)->hw_addr + reg))) + +#define AT_WRITE_FLUSH(a) (\ + readl((a)->hw_addr)) + +#define AT_READ_REG(a, reg) ( \ + readl((a)->hw_addr + reg)) + +#define AT_WRITE_REGB(a, reg, value) (\ + writeb((value), ((a)->hw_addr + reg))) + +#define AT_READ_REGB(a, reg) (\ + readb((a)->hw_addr + reg)) + +#define AT_WRITE_REGW(a, reg, value) (\ + writew((value), ((a)->hw_addr + reg))) + +#define AT_READ_REGW(a, reg) (\ + readw((a)->hw_addr + reg)) + +#define AT_WRITE_REG_ARRAY(a, reg, offset, value) ( \ + writel((value), (((a)->hw_addr + reg) + ((offset) << 2)))) + +#define AT_READ_REG_ARRAY(a, reg, offset) ( \ + readl(((a)->hw_addr + reg) + ((offset) << 2))) + +extern char atl1e_driver_name[]; +extern char atl1e_driver_version[]; + +extern void atl1e_check_options(struct atl1e_adapter *adapter); +extern int atl1e_up(struct atl1e_adapter *adapter); +extern void atl1e_down(struct atl1e_adapter *adapter); +extern void atl1e_reinit_locked(struct atl1e_adapter *adapter); +extern s32 atl1e_reset_hw(struct atl1e_hw *hw); +extern void atl1e_set_ethtool_ops(struct net_device *netdev); +#endif /* _ATL1_E_H_ */ diff --git a/drivers/net/atl1e/atl1e_ethtool.c b/drivers/net/atl1e/atl1e_ethtool.c new file mode 100644 index 000000000000..cdc3b85b10b9 --- /dev/null +++ b/drivers/net/atl1e/atl1e_ethtool.c @@ -0,0 +1,405 @@ +/* + * Copyright(c) 2007 Atheros Corporation. All rights reserved. + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * 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/netdevice.h> +#include <linux/ethtool.h> + +#include "atl1e.h" + +static int atl1e_get_settings(struct net_device *netdev, + struct ethtool_cmd *ecmd) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + struct atl1e_hw *hw = &adapter->hw; + + ecmd->supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_TP); + if (hw->nic_type == athr_l1e) + ecmd->supported |= SUPPORTED_1000baseT_Full; + + ecmd->advertising = ADVERTISED_TP; + + ecmd->advertising |= ADVERTISED_Autoneg; + ecmd->advertising |= hw->autoneg_advertised; + + ecmd->port = PORT_TP; + ecmd->phy_address = 0; + ecmd->transceiver = XCVR_INTERNAL; + + if (adapter->link_speed != SPEED_0) { + ecmd->speed = adapter->link_speed; + if (adapter->link_duplex == FULL_DUPLEX) + ecmd->duplex = DUPLEX_FULL; + else + ecmd->duplex = DUPLEX_HALF; + } else { + ecmd->speed = -1; + ecmd->duplex = -1; + } + + ecmd->autoneg = AUTONEG_ENABLE; + return 0; +} + +static int atl1e_set_settings(struct net_device *netdev, + struct ethtool_cmd *ecmd) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + struct atl1e_hw *hw = &adapter->hw; + + while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) + msleep(1); + + if (ecmd->autoneg == AUTONEG_ENABLE) { + u16 adv4, adv9; + + if ((ecmd->advertising&ADVERTISE_1000_FULL)) { + if (hw->nic_type == athr_l1e) { + hw->autoneg_advertised = + ecmd->advertising & AT_ADV_MASK; + } else { + clear_bit(__AT_RESETTING, &adapter->flags); + return -EINVAL; + } + } else if (ecmd->advertising&ADVERTISE_1000_HALF) { + clear_bit(__AT_RESETTING, &adapter->flags); + return -EINVAL; + } else { + hw->autoneg_advertised = + ecmd->advertising & AT_ADV_MASK; + } + ecmd->advertising = hw->autoneg_advertised | + ADVERTISED_TP | ADVERTISED_Autoneg; + + adv4 = hw->mii_autoneg_adv_reg & ~MII_AR_SPEED_MASK; + adv9 = hw->mii_1000t_ctrl_reg & ~MII_AT001_CR_1000T_SPEED_MASK; + if (hw->autoneg_advertised & ADVERTISE_10_HALF) + adv4 |= MII_AR_10T_HD_CAPS; + if (hw->autoneg_advertised & ADVERTISE_10_FULL) + adv4 |= MII_AR_10T_FD_CAPS; + if (hw->autoneg_advertised & ADVERTISE_100_HALF) + adv4 |= MII_AR_100TX_HD_CAPS; + if (hw->autoneg_advertised & ADVERTISE_100_FULL) + adv4 |= MII_AR_100TX_FD_CAPS; + if (hw->autoneg_advertised & ADVERTISE_1000_FULL) + adv9 |= MII_AT001_CR_1000T_FD_CAPS; + + if (adv4 != hw->mii_autoneg_adv_reg || + adv9 != hw->mii_1000t_ctrl_reg) { + hw->mii_autoneg_adv_reg = adv4; + hw->mii_1000t_ctrl_reg = adv9; + hw->re_autoneg = true; + } + + } else { + clear_bit(__AT_RESETTING, &adapter->flags); + return -EINVAL; + } + + /* reset the link */ + + if (netif_running(adapter->netdev)) { + atl1e_down(adapter); + atl1e_up(adapter); + } else + atl1e_reset_hw(&adapter->hw); + + clear_bit(__AT_RESETTING, &adapter->flags); + return 0; +} + +static u32 atl1e_get_tx_csum(struct net_device *netdev) +{ + return (netdev->features & NETIF_F_HW_CSUM) != 0; +} + +static u32 atl1e_get_msglevel(struct net_device *netdev) +{ +#ifdef DBG + return 1; +#else + return 0; +#endif +} + +static void atl1e_set_msglevel(struct net_device *netdev, u32 data) +{ +} + +static int atl1e_get_regs_len(struct net_device *netdev) +{ + return AT_REGS_LEN * sizeof(u32); +} + +static void atl1e_get_regs(struct net_device *netdev, + struct ethtool_regs *regs, void *p) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + struct atl1e_hw *hw = &adapter->hw; + u32 *regs_buff = p; + u16 phy_data; + + memset(p, 0, AT_REGS_LEN * sizeof(u32)); + + regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id; + + regs_buff[0] = AT_READ_REG(hw, REG_VPD_CAP); + regs_buff[1] = AT_READ_REG(hw, REG_SPI_FLASH_CTRL); + regs_buff[2] = AT_READ_REG(hw, REG_SPI_FLASH_CONFIG); + regs_buff[3] = AT_READ_REG(hw, REG_TWSI_CTRL); + regs_buff[4] = AT_READ_REG(hw, REG_PCIE_DEV_MISC_CTRL); + regs_buff[5] = AT_READ_REG(hw, REG_MASTER_CTRL); + regs_buff[6] = AT_READ_REG(hw, REG_MANUAL_TIMER_INIT); + regs_buff[7] = AT_READ_REG(hw, REG_IRQ_MODU_TIMER_INIT); + regs_buff[8] = AT_READ_REG(hw, REG_GPHY_CTRL); + regs_buff[9] = AT_READ_REG(hw, REG_CMBDISDMA_TIMER); + regs_buff[10] = AT_READ_REG(hw, REG_IDLE_STATUS); + regs_buff[11] = AT_READ_REG(hw, REG_MDIO_CTRL); + regs_buff[12] = AT_READ_REG(hw, REG_SERDES_LOCK); + regs_buff[13] = AT_READ_REG(hw, REG_MAC_CTRL); + regs_buff[14] = AT_READ_REG(hw, REG_MAC_IPG_IFG); + regs_buff[15] = AT_READ_REG(hw, REG_MAC_STA_ADDR); + regs_buff[16] = AT_READ_REG(hw, REG_MAC_STA_ADDR+4); + regs_buff[17] = AT_READ_REG(hw, REG_RX_HASH_TABLE); + regs_buff[18] = AT_READ_REG(hw, REG_RX_HASH_TABLE+4); + regs_buff[19] = AT_READ_REG(hw, REG_MAC_HALF_DUPLX_CTRL); + regs_buff[20] = AT_READ_REG(hw, REG_MTU); + regs_buff[21] = AT_READ_REG(hw, REG_WOL_CTRL); + regs_buff[22] = AT_READ_REG(hw, REG_SRAM_TRD_ADDR); + regs_buff[23] = AT_READ_REG(hw, REG_SRAM_TRD_LEN); + regs_buff[24] = AT_READ_REG(hw, REG_SRAM_RXF_ADDR); + regs_buff[25] = AT_READ_REG(hw, REG_SRAM_RXF_LEN); + regs_buff[26] = AT_READ_REG(hw, REG_SRAM_TXF_ADDR); + regs_buff[27] = AT_READ_REG(hw, REG_SRAM_TXF_LEN); + regs_buff[28] = AT_READ_REG(hw, REG_SRAM_TCPH_ADDR); + regs_buff[29] = AT_READ_REG(hw, REG_SRAM_PKTH_ADDR); + + atl1e_read_phy_reg(hw, MII_BMCR, &phy_data); + regs_buff[73] = (u32)phy_data; + atl1e_read_phy_reg(hw, MII_BMSR, &phy_data); + regs_buff[74] = (u32)phy_data; +} + +static int atl1e_get_eeprom_len(struct net_device *netdev) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + + if (!atl1e_check_eeprom_exist(&adapter->hw)) + return AT_EEPROM_LEN; + else + return 0; +} + +static int atl1e_get_eeprom(struct net_device *netdev, + struct ethtool_eeprom *eeprom, u8 *bytes) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + struct atl1e_hw *hw = &adapter->hw; + u32 *eeprom_buff; + int first_dword, last_dword; + int ret_val = 0; + int i; + + if (eeprom->len == 0) + return -EINVAL; + + if (atl1e_check_eeprom_exist(hw)) /* not exist */ + return -EINVAL; + + eeprom->magic = hw->vendor_id | (hw->device_id << 16); + + first_dword = eeprom->offset >> 2; + last_dword = (eeprom->offset + eeprom->len - 1) >> 2; + + eeprom_buff = kmalloc(sizeof(u32) * + (last_dword - first_dword + 1), GFP_KERNEL); + if (eeprom_buff == NULL) + return -ENOMEM; + + for (i = first_dword; i < last_dword; i++) { + if (!atl1e_read_eeprom(hw, i * 4, &(eeprom_buff[i-first_dword]))) { + kfree(eeprom_buff); + return -EIO; + } + } + + memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 3), + eeprom->len); + kfree(eeprom_buff); + + return ret_val; +} + +static int atl1e_set_eeprom(struct net_device *netdev, + struct ethtool_eeprom *eeprom, u8 *bytes) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + struct atl1e_hw *hw = &adapter->hw; + u32 *eeprom_buff; + u32 *ptr; + int first_dword, last_dword; + int ret_val = 0; + int i; + + if (eeprom->len == 0) + return -EOPNOTSUPP; + + if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16))) + return -EINVAL; + + first_dword = eeprom->offset >> 2; + last_dword = (eeprom->offset + eeprom->len - 1) >> 2; + eeprom_buff = kmalloc(AT_EEPROM_LEN, GFP_KERNEL); + if (eeprom_buff == NULL) + return -ENOMEM; + + ptr = (u32 *)eeprom_buff; + + if (eeprom->offset & 3) { + /* need read/modify/write of first changed EEPROM word */ + /* only the second byte of the word is being modified */ + if (!atl1e_read_eeprom(hw, first_dword * 4, &(eeprom_buff[0]))) { + ret_val = -EIO; + goto out; + } + ptr++; + } + if (((eeprom->offset + eeprom->len) & 3)) { + /* need read/modify/write of last changed EEPROM word */ + /* only the first byte of the word is being modified */ + + if (!atl1e_read_eeprom(hw, last_dword * 4, + &(eeprom_buff[last_dword - first_dword]))) { + ret_val = -EIO; + goto out; + } + } + + /* Device's eeprom is always little-endian, word addressable */ + memcpy(ptr, bytes, eeprom->len); + + for (i = 0; i < last_dword - first_dword + 1; i++) { + if (!atl1e_write_eeprom(hw, ((first_dword + i) * 4), + eeprom_buff[i])) { + ret_val = -EIO; + goto out; + } + } +out: + kfree(eeprom_buff); + return ret_val; +} + +static void atl1e_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + + strncpy(drvinfo->driver, atl1e_driver_name, 32); + strncpy(drvinfo->version, atl1e_driver_version, 32); + strncpy(drvinfo->fw_version, "L1e", 32); + strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); + drvinfo->n_stats = 0; + drvinfo->testinfo_len = 0; + drvinfo->regdump_len = atl1e_get_regs_len(netdev); + drvinfo->eedump_len = atl1e_get_eeprom_len(netdev); +} + +static void atl1e_get_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + + wol->supported = WAKE_MAGIC | WAKE_PHY; + wol->wolopts = 0; + + if (adapter->wol & AT_WUFC_EX) + wol->wolopts |= WAKE_UCAST; + if (adapter->wol & AT_WUFC_MC) + wol->wolopts |= WAKE_MCAST; + if (adapter->wol & AT_WUFC_BC) + wol->wolopts |= WAKE_BCAST; + if (adapter->wol & AT_WUFC_MAG) + wol->wolopts |= WAKE_MAGIC; + if (adapter->wol & AT_WUFC_LNKC) + wol->wolopts |= WAKE_PHY; + + return; +} + +static int atl1e_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + + if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE | + WAKE_MCAST | WAKE_BCAST | WAKE_MCAST)) + return -EOPNOTSUPP; + /* these settings will always override what we currently have */ + adapter->wol = 0; + + if (wol->wolopts & WAKE_MAGIC) + adapter->wol |= AT_WUFC_MAG; + if (wol->wolopts & WAKE_PHY) + adapter->wol |= AT_WUFC_LNKC; + + return 0; +} + +static int atl1e_nway_reset(struct net_device *netdev) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + if (netif_running(netdev)) + atl1e_reinit_locked(adapter); + return 0; +} + +static struct ethtool_ops atl1e_ethtool_ops = { + .get_settings = atl1e_get_settings, + .set_settings = atl1e_set_settings, + .get_drvinfo = atl1e_get_drvinfo, + .get_regs_len = atl1e_get_regs_len, + .get_regs = atl1e_get_regs, + .get_wol = atl1e_get_wol, + .set_wol = atl1e_set_wol, + .get_msglevel = atl1e_get_msglevel, + .set_msglevel = atl1e_set_msglevel, + .nway_reset = atl1e_nway_reset, + .get_link = ethtool_op_get_link, + .get_eeprom_len = atl1e_get_eeprom_len, + .get_eeprom = atl1e_get_eeprom, + .set_eeprom = atl1e_set_eeprom, + .get_tx_csum = atl1e_get_tx_csum, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, +#ifdef NETIF_F_TSO + .get_tso = ethtool_op_get_tso, +#endif +}; + +void atl1e_set_ethtool_ops(struct net_device *netdev) +{ + SET_ETHTOOL_OPS(netdev, &atl1e_ethtool_ops); +} diff --git a/drivers/net/atl1e/atl1e_hw.c b/drivers/net/atl1e/atl1e_hw.c new file mode 100644 index 000000000000..949e75358bf0 --- /dev/null +++ b/drivers/net/atl1e/atl1e_hw.c @@ -0,0 +1,664 @@ +/* + * Copyright(c) 2007 Atheros Corporation. All rights reserved. + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * 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/pci.h> +#include <linux/delay.h> +#include <linux/mii.h> +#include <linux/crc32.h> + +#include "atl1e.h" + +/* + * check_eeprom_exist + * return 0 if eeprom exist + */ +int atl1e_check_eeprom_exist(struct atl1e_hw *hw) +{ + u32 value; + + value = AT_READ_REG(hw, REG_SPI_FLASH_CTRL); + if (value & SPI_FLASH_CTRL_EN_VPD) { + value &= ~SPI_FLASH_CTRL_EN_VPD; + AT_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value); + } + value = AT_READ_REGW(hw, REG_PCIE_CAP_LIST); + return ((value & 0xFF00) == 0x6C00) ? 0 : 1; +} + +void atl1e_hw_set_mac_addr(struct atl1e_hw *hw) +{ + u32 value; + /* + * 00-0B-6A-F6-00-DC + * 0: 6AF600DC 1: 000B + * low dword + */ + value = (((u32)hw->mac_addr[2]) << 24) | + (((u32)hw->mac_addr[3]) << 16) | + (((u32)hw->mac_addr[4]) << 8) | + (((u32)hw->mac_addr[5])) ; + AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value); + /* hight dword */ + value = (((u32)hw->mac_addr[0]) << 8) | + (((u32)hw->mac_addr[1])) ; + AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value); +} + +/* + * atl1e_get_permanent_address + * return 0 if get valid mac address, + */ +static int atl1e_get_permanent_address(struct atl1e_hw *hw) +{ + u32 addr[2]; + u32 i; + u32 twsi_ctrl_data; + u8 eth_addr[ETH_ALEN]; + + if (is_valid_ether_addr(hw->perm_mac_addr)) + return 0; + + /* init */ + addr[0] = addr[1] = 0; + + if (!atl1e_check_eeprom_exist(hw)) { + /* eeprom exist */ + twsi_ctrl_data = AT_READ_REG(hw, REG_TWSI_CTRL); + twsi_ctrl_data |= TWSI_CTRL_SW_LDSTART; + AT_WRITE_REG(hw, REG_TWSI_CTRL, twsi_ctrl_data); + for (i = 0; i < AT_TWSI_EEPROM_TIMEOUT; i++) { + msleep(10); + twsi_ctrl_data = AT_READ_REG(hw, REG_TWSI_CTRL); + if ((twsi_ctrl_data & TWSI_CTRL_SW_LDSTART) == 0) + break; + } + if (i >= AT_TWSI_EEPROM_TIMEOUT) + return AT_ERR_TIMEOUT; + } + + /* maybe MAC-address is from BIOS */ + addr[0] = AT_READ_REG(hw, REG_MAC_STA_ADDR); + addr[1] = AT_READ_REG(hw, 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 AT_ERR_EEPROM; +} + +bool atl1e_write_eeprom(struct atl1e_hw *hw, u32 offset, u32 value) +{ + return true; +} + +bool atl1e_read_eeprom(struct atl1e_hw *hw, u32 offset, u32 *p_value) +{ + int i; + u32 control; + + if (offset & 3) + return false; /* address do not align */ + + AT_WRITE_REG(hw, REG_VPD_DATA, 0); + control = (offset & VPD_CAP_VPD_ADDR_MASK) << VPD_CAP_VPD_ADDR_SHIFT; + AT_WRITE_REG(hw, REG_VPD_CAP, control); + + for (i = 0; i < 10; i++) { + msleep(2); + control = AT_READ_REG(hw, REG_VPD_CAP); + if (control & VPD_CAP_VPD_FLAG) + break; + } + if (control & VPD_CAP_VPD_FLAG) { + *p_value = AT_READ_REG(hw, REG_VPD_DATA); + return true; + } + return false; /* timeout */ +} + +void atl1e_force_ps(struct atl1e_hw *hw) +{ + AT_WRITE_REGW(hw, REG_GPHY_CTRL, + GPHY_CTRL_PW_WOL_DIS | GPHY_CTRL_EXT_RESET); +} + +/* + * Reads the adapter's MAC address from the EEPROM + * + * hw - Struct containing variables accessed by shared code + */ +int atl1e_read_mac_addr(struct atl1e_hw *hw) +{ + int err = 0; + + err = atl1e_get_permanent_address(hw); + if (err) + return AT_ERR_EEPROM; + memcpy(hw->mac_addr, hw->perm_mac_addr, sizeof(hw->perm_mac_addr)); + return 0; +} + +/* + * atl1e_hash_mc_addr + * purpose + * set hash value for a multicast address + * hash calcu processing : + * 1. calcu 32bit CRC for multicast address + * 2. reverse crc with MSB to LSB + */ +u32 atl1e_hash_mc_addr(struct atl1e_hw *hw, u8 *mc_addr) +{ + u32 crc32; + u32 value = 0; + int i; + + crc32 = ether_crc_le(6, mc_addr); + crc32 = ~crc32; + for (i = 0; i < 32; i++) + value |= (((crc32 >> i) & 1) << (31 - i)); + + return value; +} + +/* + * Sets the bit in the multicast table corresponding to the hash value. + * hw - Struct containing variables accessed by shared code + * hash_value - Multicast address hash value + */ +void atl1e_hash_set(struct atl1e_hw *hw, u32 hash_value) +{ + u32 hash_bit, hash_reg; + u32 mta; + + /* + * The HASH Table is a register array of 2 32-bit registers. + * It is treated like an array of 64 bits. We want to set + * bit BitArray[hash_value]. So we figure out what register + * the bit is in, read it, OR in the new bit, then write + * back the new value. The register is determined by the + * upper 7 bits of the hash value and the bit within that + * register are determined by the lower 5 bits of the value. + */ + hash_reg = (hash_value >> 31) & 0x1; + hash_bit = (hash_value >> 26) & 0x1F; + + mta = AT_READ_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg); + + mta |= (1 << hash_bit); + + AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta); +} +/* + * Reads the value from a PHY register + * hw - Struct containing variables accessed by shared code + * reg_addr - address of the PHY register to read + */ +int atl1e_read_phy_reg(struct atl1e_hw *hw, u16 reg_addr, u16 *phy_data) +{ + u32 val; + int i; + + val = ((u32)(reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT | + MDIO_START | MDIO_SUP_PREAMBLE | MDIO_RW | + MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; + + AT_WRITE_REG(hw, REG_MDIO_CTRL, val); + + wmb(); + + for (i = 0; i < MDIO_WAIT_TIMES; i++) { + udelay(2); + val = AT_READ_REG(hw, REG_MDIO_CTRL); + if (!(val & (MDIO_START | MDIO_BUSY))) + break; + wmb(); + } + if (!(val & (MDIO_START | MDIO_BUSY))) { + *phy_data = (u16)val; + return 0; + } + + return AT_ERR_PHY; +} + +/* + * Writes a value to a PHY register + * hw - Struct containing variables accessed by shared code + * reg_addr - address of the PHY register to write + * data - data to write to the PHY + */ +int atl1e_write_phy_reg(struct atl1e_hw *hw, u32 reg_addr, u16 phy_data) +{ + int i; + u32 val; + + val = ((u32)(phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT | + (reg_addr&MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT | + MDIO_SUP_PREAMBLE | + MDIO_START | + MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; + + AT_WRITE_REG(hw, REG_MDIO_CTRL, val); + wmb(); + + for (i = 0; i < MDIO_WAIT_TIMES; i++) { + udelay(2); + val = AT_READ_REG(hw, REG_MDIO_CTRL); + if (!(val & (MDIO_START | MDIO_BUSY))) + break; + wmb(); + } + + if (!(val & (MDIO_START | MDIO_BUSY))) + return 0; + + return AT_ERR_PHY; +} + +/* + * atl1e_init_pcie - init PCIE module + */ +static void atl1e_init_pcie(struct atl1e_hw *hw) +{ + u32 value; + /* comment 2lines below to save more power when sususpend + value = LTSSM_TEST_MODE_DEF; + AT_WRITE_REG(hw, REG_LTSSM_TEST_MODE, value); + */ + + /* pcie flow control mode change */ + value = AT_READ_REG(hw, 0x1008); + value |= 0x8000; + AT_WRITE_REG(hw, 0x1008, value); +} +/* + * Configures PHY autoneg and flow control advertisement settings + * + * hw - Struct containing variables accessed by shared code + */ +static int atl1e_phy_setup_autoneg_adv(struct atl1e_hw *hw) +{ + s32 ret_val; + u16 mii_autoneg_adv_reg; + u16 mii_1000t_ctrl_reg; + + if (0 != hw->mii_autoneg_adv_reg) + return 0; + /* Read the MII Auto-Neg Advertisement Register (Address 4/9). */ + mii_autoneg_adv_reg = MII_AR_DEFAULT_CAP_MASK; + mii_1000t_ctrl_reg = MII_AT001_CR_1000T_DEFAULT_CAP_MASK; + + /* + * Need to parse autoneg_advertised and set up + * the appropriate PHY registers. First we will parse for + * autoneg_advertised software override. Since we can advertise + * a plethora of combinations, we need to check each bit + * individually. + */ + + /* + * First we clear all the 10/100 mb speed bits in the Auto-Neg + * Advertisement Register (Address 4) and the 1000 mb speed bits in + * the 1000Base-T control Register (Address 9). + */ + mii_autoneg_adv_reg &= ~MII_AR_SPEED_MASK; + mii_1000t_ctrl_reg &= ~MII_AT001_CR_1000T_SPEED_MASK; + + /* + * Need to parse MediaType and setup the + * appropriate PHY registers. + */ + switch (hw->media_type) { + case MEDIA_TYPE_AUTO_SENSOR: + mii_autoneg_adv_reg |= (MII_AR_10T_HD_CAPS | + MII_AR_10T_FD_CAPS | + MII_AR_100TX_HD_CAPS | + MII_AR_100TX_FD_CAPS); + hw->autoneg_advertised = ADVERTISE_10_HALF | + ADVERTISE_10_FULL | + ADVERTISE_100_HALF | + ADVERTISE_100_FULL; + if (hw->nic_type == athr_l1e) { + mii_1000t_ctrl_reg |= + MII_AT001_CR_1000T_FD_CAPS; + hw->autoneg_advertised |= ADVERTISE_1000_FULL; + } + break; + + case MEDIA_TYPE_100M_FULL: + mii_autoneg_adv_reg |= MII_AR_100TX_FD_CAPS; + hw->autoneg_advertised = ADVERTISE_100_FULL; + break; + + case MEDIA_TYPE_100M_HALF: + mii_autoneg_adv_reg |= MII_AR_100TX_HD_CAPS; + hw->autoneg_advertised = ADVERTISE_100_HALF; + break; + + case MEDIA_TYPE_10M_FULL: + mii_autoneg_adv_reg |= MII_AR_10T_FD_CAPS; + hw->autoneg_advertised = ADVERTISE_10_FULL; + break; + + default: + mii_autoneg_adv_reg |= MII_AR_10T_HD_CAPS; + hw->autoneg_advertised = ADVERTISE_10_HALF; + break; + } + + /* flow control fixed to enable all */ + mii_autoneg_adv_reg |= (MII_AR_ASM_DIR | MII_AR_PAUSE); + + hw->mii_autoneg_adv_reg = mii_autoneg_adv_reg; + hw->mii_1000t_ctrl_reg = mii_1000t_ctrl_reg; + + ret_val = atl1e_write_phy_reg(hw, MII_ADVERTISE, mii_autoneg_adv_reg); + if (ret_val) + return ret_val; + + if (hw->nic_type == athr_l1e || hw->nic_type == athr_l2e_revA) { + ret_val = atl1e_write_phy_reg(hw, MII_AT001_CR, + mii_1000t_ctrl_reg); + if (ret_val) + return ret_val; + } + + return 0; +} + + +/* + * Resets the PHY and make all config validate + * + * hw - Struct containing variables accessed by shared code + * + * Sets bit 15 and 12 of the MII control regiser (for F001 bug) + */ +int atl1e_phy_commit(struct atl1e_hw *hw) +{ + struct atl1e_adapter *adapter = (struct atl1e_adapter *)hw->adapter; + struct pci_dev *pdev = adapter->pdev; + int ret_val; + u16 phy_data; + + phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG; + + ret_val = atl1e_write_phy_reg(hw, MII_BMCR, phy_data); + if (ret_val) { + u32 val; + int i; + /************************************** + * pcie serdes link may be down ! + **************************************/ + for (i = 0; i < 25; i++) { + msleep(1); + val = AT_READ_REG(hw, REG_MDIO_CTRL); + if (!(val & (MDIO_START | MDIO_BUSY))) + break; + } + + if (0 != (val & (MDIO_START | MDIO_BUSY))) { + dev_err(&pdev->dev, + "pcie linkdown at least for 25ms\n"); + return ret_val; + } + + dev_err(&pdev->dev, "pcie linkup after %d ms\n", i); + } + return 0; +} + +int atl1e_phy_init(struct atl1e_hw *hw) +{ + struct atl1e_adapter *adapter = (struct atl1e_adapter *)hw->adapter; + struct pci_dev *pdev = adapter->pdev; + s32 ret_val; + u16 phy_val; + + if (hw->phy_configured) { + if (hw->re_autoneg) { + hw->re_autoneg = false; + return atl1e_restart_autoneg(hw); + } + return 0; + } + + /* RESET GPHY Core */ + AT_WRITE_REGW(hw, REG_GPHY_CTRL, GPHY_CTRL_DEFAULT); + msleep(2); + AT_WRITE_REGW(hw, REG_GPHY_CTRL, GPHY_CTRL_DEFAULT | + GPHY_CTRL_EXT_RESET); + msleep(2); + + /* patches */ + /* p1. eable hibernation mode */ + ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0xB); + if (ret_val) + return ret_val; + ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0xBC00); + if (ret_val) + return ret_val; + /* p2. set Class A/B for all modes */ + ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0); + if (ret_val) + return ret_val; + phy_val = 0x02ef; + /* remove Class AB */ + /* phy_val = hw->emi_ca ? 0x02ef : 0x02df; */ + ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, phy_val); + if (ret_val) + return ret_val; + /* p3. 10B ??? */ + ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0x12); + if (ret_val) + return ret_val; + ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0x4C04); + if (ret_val) + return ret_val; + /* p4. 1000T power */ + ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0x4); + if (ret_val) + return ret_val; + ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0x8BBB); + if (ret_val) + return ret_val; + + ret_val = atl1e_write_phy_reg(hw, MII_DBG_ADDR, 0x5); + if (ret_val) + return ret_val; + ret_val = atl1e_write_phy_reg(hw, MII_DBG_DATA, 0x2C46); + if (ret_val) + return ret_val; + + msleep(1); + + /*Enable PHY LinkChange Interrupt */ + ret_val = atl1e_write_phy_reg(hw, MII_INT_CTRL, 0xC00); + if (ret_val) { + dev_err(&pdev->dev, "Error enable PHY linkChange Interrupt\n"); + return ret_val; + } + /* setup AutoNeg parameters */ + ret_val = atl1e_phy_setup_autoneg_adv(hw); + if (ret_val) { + dev_err(&pdev->dev, "Error Setting up Auto-Negotiation\n"); + return ret_val; + } + /* SW.Reset & En-Auto-Neg to restart Auto-Neg*/ + dev_dbg(&pdev->dev, "Restarting Auto-Neg"); + ret_val = atl1e_phy_commit(hw); + if (ret_val) { + dev_err(&pdev->dev, "Error Resetting the phy"); + return ret_val; + } + + hw->phy_configured = true; + + return 0; +} + +/* + * Reset the transmit and receive units; mask and clear all interrupts. + * hw - Struct containing variables accessed by shared code + * return : 0 or idle status (if error) + */ +int atl1e_reset_hw(struct atl1e_hw *hw) +{ + struct atl1e_adapter *adapter = (struct atl1e_adapter *)hw->adapter; + struct pci_dev *pdev = adapter->pdev; + + u32 idle_status_data = 0; + u16 pci_cfg_cmd_word = 0; + int timeout = 0; + + /* Workaround for PCI problem when BIOS sets MMRBC incorrectly. */ + pci_read_config_word(pdev, PCI_REG_COMMAND, &pci_cfg_cmd_word); + if ((pci_cfg_cmd_word & (CMD_IO_SPACE | + CMD_MEMORY_SPACE | CMD_BUS_MASTER)) + != (CMD_IO_SPACE | CMD_MEMORY_SPACE | CMD_BUS_MASTER)) { + pci_cfg_cmd_word |= (CMD_IO_SPACE | + CMD_MEMORY_SPACE | CMD_BUS_MASTER); + pci_write_config_word(pdev, PCI_REG_COMMAND, pci_cfg_cmd_word); + } + + /* + * Issue Soft Reset to the MAC. This will reset the chip's + * transmit, receive, DMA. It will not effect + * the current PCI configuration. The global reset bit is self- + * clearing, and should clear within a microsecond. + */ + AT_WRITE_REG(hw, REG_MASTER_CTRL, + MASTER_CTRL_LED_MODE | MASTER_CTRL_SOFT_RST); + wmb(); + msleep(1); + + /* Wait at least 10ms for All module to be Idle */ + for (timeout = 0; timeout < AT_HW_MAX_IDLE_DELAY; timeout++) { + idle_status_data = AT_READ_REG(hw, REG_IDLE_STATUS); + if (idle_status_data == 0) + break; + msleep(1); + cpu_relax(); + } + + if (timeout >= AT_HW_MAX_IDLE_DELAY) { + dev_err(&pdev->dev, + "MAC state machine cann't be idle since" + " disabled for 10ms second\n"); + return AT_ERR_TIMEOUT; + } + + return 0; +} + + +/* + * Performs basic configuration of the adapter. + * + * hw - Struct containing variables accessed by shared code + * Assumes that the controller has previously been reset and is in a + * post-reset uninitialized state. Initializes multicast table, + * and Calls routines to setup link + * Leaves the transmit and receive units disabled and uninitialized. + */ +int atl1e_init_hw(struct atl1e_hw *hw) +{ + s32 ret_val = 0; + + atl1e_init_pcie(hw); + + /* Zero out the Multicast HASH table */ + /* clear the old settings from the multicast hash table */ + AT_WRITE_REG(hw, REG_RX_HASH_TABLE, 0); + AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0); + + ret_val = atl1e_phy_init(hw); + + return ret_val; +} + +/* + * Detects the current speed and duplex settings of the hardware. + * + * hw - Struct containing variables accessed by shared code + * speed - Speed of the connection + * duplex - Duplex setting of the connection + */ +int atl1e_get_speed_and_duplex(struct atl1e_hw *hw, u16 *speed, u16 *duplex) +{ + int err; + u16 phy_data; + + /* Read PHY Specific Status Register (17) */ + err = atl1e_read_phy_reg(hw, MII_AT001_PSSR, &phy_data); + if (err) + return err; + + if (!(phy_data & MII_AT001_PSSR_SPD_DPLX_RESOLVED)) + return AT_ERR_PHY_RES; + + switch (phy_data & MII_AT001_PSSR_SPEED) { + case MII_AT001_PSSR_1000MBS: + *speed = SPEED_1000; + break; + case MII_AT001_PSSR_100MBS: + *speed = SPEED_100; + break; + case MII_AT001_PSSR_10MBS: + *speed = SPEED_10; + break; + default: + return AT_ERR_PHY_SPEED; + break; + } + + if (phy_data & MII_AT001_PSSR_DPLX) + *duplex = FULL_DUPLEX; + else + *duplex = HALF_DUPLEX; + + return 0; +} + +int atl1e_restart_autoneg(struct atl1e_hw *hw) +{ + int err = 0; + + err = atl1e_write_phy_reg(hw, MII_ADVERTISE, hw->mii_autoneg_adv_reg); + if (err) + return err; + + if (hw->nic_type == athr_l1e || hw->nic_type == athr_l2e_revA) { + err = atl1e_write_phy_reg(hw, MII_AT001_CR, + hw->mii_1000t_ctrl_reg); + if (err) + return err; + } + + err = atl1e_write_phy_reg(hw, MII_BMCR, + MII_CR_RESET | MII_CR_AUTO_NEG_EN | + MII_CR_RESTART_AUTO_NEG); + return err; +} + diff --git a/drivers/net/atl1e/atl1e_hw.h b/drivers/net/atl1e/atl1e_hw.h new file mode 100644 index 000000000000..5ea2f4d86cfa --- /dev/null +++ b/drivers/net/atl1e/atl1e_hw.h @@ -0,0 +1,793 @@ +/* + * Copyright(c) 2007 Atheros Corporation. All rights reserved. + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * 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 _ATHL1E_HW_H_ +#define _ATHL1E_HW_H_ + +#include <linux/types.h> +#include <linux/mii.h> + +struct atl1e_adapter; +struct atl1e_hw; + +/* function prototype */ +s32 atl1e_reset_hw(struct atl1e_hw *hw); +s32 atl1e_read_mac_addr(struct atl1e_hw *hw); +s32 atl1e_init_hw(struct atl1e_hw *hw); +s32 atl1e_phy_commit(struct atl1e_hw *hw); +s32 atl1e_get_speed_and_duplex(struct atl1e_hw *hw, u16 *speed, u16 *duplex); +u32 atl1e_auto_get_fc(struct atl1e_adapter *adapter, u16 duplex); +u32 atl1e_hash_mc_addr(struct atl1e_hw *hw, u8 *mc_addr); +void atl1e_hash_set(struct atl1e_hw *hw, u32 hash_value); +s32 atl1e_read_phy_reg(struct atl1e_hw *hw, u16 reg_addr, u16 *phy_data); +s32 atl1e_write_phy_reg(struct atl1e_hw *hw, u32 reg_addr, u16 phy_data); +s32 atl1e_validate_mdi_setting(struct atl1e_hw *hw); +void atl1e_hw_set_mac_addr(struct atl1e_hw *hw); +bool atl1e_read_eeprom(struct atl1e_hw *hw, u32 offset, u32 *p_value); +bool atl1e_write_eeprom(struct atl1e_hw *hw, u32 offset, u32 value); +s32 atl1e_phy_enter_power_saving(struct atl1e_hw *hw); +s32 atl1e_phy_leave_power_saving(struct atl1e_hw *hw); +s32 atl1e_phy_init(struct atl1e_hw *hw); +int atl1e_check_eeprom_exist(struct atl1e_hw *hw); +void atl1e_force_ps(struct atl1e_hw *hw); +s32 atl1e_restart_autoneg(struct atl1e_hw *hw); + +/* register definition */ +#define REG_PM_CTRLSTAT 0x44 + +#define REG_PCIE_CAP_LIST 0x58 + +#define REG_DEVICE_CAP 0x5C +#define DEVICE_CAP_MAX_PAYLOAD_MASK 0x7 +#define DEVICE_CAP_MAX_PAYLOAD_SHIFT 0 + +#define REG_DEVICE_CTRL 0x60 +#define DEVICE_CTRL_MAX_PAYLOAD_MASK 0x7 +#define DEVICE_CTRL_MAX_PAYLOAD_SHIFT 5 +#define DEVICE_CTRL_MAX_RREQ_SZ_MASK 0x7 +#define DEVICE_CTRL_MAX_RREQ_SZ_SHIFT 12 + +#define REG_VPD_CAP 0x6C +#define VPD_CAP_ID_MASK 0xff +#define VPD_CAP_ID_SHIFT 0 +#define VPD_CAP_NEXT_PTR_MASK 0xFF +#define VPD_CAP_NEXT_PTR_SHIFT 8 +#define VPD_CAP_VPD_ADDR_MASK 0x7FFF +#define VPD_CAP_VPD_ADDR_SHIFT 16 +#define VPD_CAP_VPD_FLAG 0x80000000 + +#define REG_VPD_DATA 0x70 + +#define REG_SPI_FLASH_CTRL 0x200 +#define SPI_FLASH_CTRL_STS_NON_RDY 0x1 +#define SPI_FLASH_CTRL_STS_WEN 0x2 +#define SPI_FLASH_CTRL_STS_WPEN 0x80 +#define SPI_FLASH_CTRL_DEV_STS_MASK 0xFF +#define SPI_FLASH_CTRL_DEV_STS_SHIFT 0 +#define SPI_FLASH_CTRL_INS_MASK 0x7 +#define SPI_FLASH_CTRL_INS_SHIFT 8 +#define SPI_FLASH_CTRL_START 0x800 +#define SPI_FLASH_CTRL_EN_VPD 0x2000 +#define SPI_FLASH_CTRL_LDSTART 0x8000 +#define SPI_FLASH_CTRL_CS_HI_MASK 0x3 +#define SPI_FLASH_CTRL_CS_HI_SHIFT 16 +#define SPI_FLASH_CTRL_CS_HOLD_MASK 0x3 +#define SPI_FLASH_CTRL_CS_HOLD_SHIFT 18 +#define SPI_FLASH_CTRL_CLK_LO_MASK 0x3 +#define SPI_FLASH_CTRL_CLK_LO_SHIFT 20 +#define SPI_FLASH_CTRL_CLK_HI_MASK 0x3 +#define SPI_FLASH_CTRL_CLK_HI_SHIFT 22 +#define SPI_FLASH_CTRL_CS_SETUP_MASK 0x3 +#define SPI_FLASH_CTRL_CS_SETUP_SHIFT 24 +#define SPI_FLASH_CTRL_EROM_PGSZ_MASK 0x3 +#define SPI_FLASH_CTRL_EROM_PGSZ_SHIFT 26 +#define SPI_FLASH_CTRL_WAIT_READY 0x10000000 + +#define REG_SPI_ADDR 0x204 + +#define REG_SPI_DATA 0x208 + +#define REG_SPI_FLASH_CONFIG 0x20C +#define SPI_FLASH_CONFIG_LD_ADDR_MASK 0xFFFFFF +#define SPI_FLASH_CONFIG_LD_ADDR_SHIFT 0 +#define SPI_FLASH_CONFIG_VPD_ADDR_MASK 0x3 +#define SPI_FLASH_CONFIG_VPD_ADDR_SHIFT 24 +#define SPI_FLASH_CONFIG_LD_EXIST 0x4000000 + + +#define REG_SPI_FLASH_OP_PROGRAM 0x210 +#define REG_SPI_FLASH_OP_SC_ERASE 0x211 +#define REG_SPI_FLASH_OP_CHIP_ERASE 0x212 +#define REG_SPI_FLASH_OP_RDID 0x213 +#define REG_SPI_FLASH_OP_WREN 0x214 +#define REG_SPI_FLASH_OP_RDSR 0x215 +#define REG_SPI_FLASH_OP_WRSR 0x216 +#define REG_SPI_FLASH_OP_READ 0x217 + +#define REG_TWSI_CTRL 0x218 +#define TWSI_CTRL_LD_OFFSET_MASK 0xFF +#define TWSI_CTRL_LD_OFFSET_SHIFT 0 +#define TWSI_CTRL_LD_SLV_ADDR_MASK 0x7 +#define TWSI_CTRL_LD_SLV_ADDR_SHIFT 8 +#define TWSI_CTRL_SW_LDSTART 0x800 +#define TWSI_CTRL_HW_LDSTART 0x1000 +#define TWSI_CTRL_SMB_SLV_ADDR_MASK 0x0x7F +#define TWSI_CTRL_SMB_SLV_ADDR_SHIFT 15 +#define TWSI_CTRL_LD_EXIST 0x400000 +#define TWSI_CTRL_READ_FREQ_SEL_MASK 0x3 +#define TWSI_CTRL_READ_FREQ_SEL_SHIFT 23 +#define TWSI_CTRL_FREQ_SEL_100K 0 +#define TWSI_CTRL_FREQ_SEL_200K 1 +#define TWSI_CTRL_FREQ_SEL_300K 2 +#define TWSI_CTRL_FREQ_SEL_400K 3 +#define TWSI_CTRL_SMB_SLV_ADDR +#define TWSI_CTRL_WRITE_FREQ_SEL_MASK 0x3 +#define TWSI_CTRL_WRITE_FREQ_SEL_SHIFT 24 + + +#define REG_PCIE_DEV_MISC_CTRL 0x21C +#define PCIE_DEV_MISC_CTRL_EXT_PIPE 0x2 +#define PCIE_DEV_MISC_CTRL_RETRY_BUFDIS 0x1 +#define PCIE_DEV_MISC_CTRL_SPIROM_EXIST 0x4 +#define PCIE_DEV_MISC_CTRL_SERDES_ENDIAN 0x8 +#define PCIE_DEV_MISC_CTRL_SERDES_SEL_DIN 0x10 + +#define REG_PCIE_PHYMISC 0x1000 +#define PCIE_PHYMISC_FORCE_RCV_DET 0x4 + +#define REG_LTSSM_TEST_MODE 0x12FC +#define LTSSM_TEST_MODE_DEF 0xE000 + +/* Selene Master Control Register */ +#define REG_MASTER_CTRL 0x1400 +#define MASTER_CTRL_SOFT_RST 0x1 +#define MASTER_CTRL_MTIMER_EN 0x2 +#define MASTER_CTRL_ITIMER_EN 0x4 +#define MASTER_CTRL_MANUAL_INT 0x8 +#define MASTER_CTRL_ITIMER2_EN 0x20 +#define MASTER_CTRL_INT_RDCLR 0x40 +#define MASTER_CTRL_LED_MODE 0x200 +#define MASTER_CTRL_REV_NUM_SHIFT 16 +#define MASTER_CTRL_REV_NUM_MASK 0xff +#define MASTER_CTRL_DEV_ID_SHIFT 24 +#define MASTER_CTRL_DEV_ID_MASK 0xff + +/* Timer Initial Value Register */ +#define REG_MANUAL_TIMER_INIT 0x1404 + + +/* IRQ ModeratorTimer Initial Value Register */ +#define REG_IRQ_MODU_TIMER_INIT 0x1408 /* w */ +#define REG_IRQ_MODU_TIMER2_INIT 0x140A /* w */ + + +#define REG_GPHY_CTRL 0x140C +#define GPHY_CTRL_EXT_RESET 1 +#define GPHY_CTRL_PIPE_MOD 2 +#define GPHY_CTRL_TEST_MODE_MASK 3 +#define GPHY_CTRL_TEST_MODE_SHIFT 2 +#define GPHY_CTRL_BERT_START 0x10 +#define GPHY_CTRL_GATE_25M_EN 0x20 +#define GPHY_CTRL_LPW_EXIT 0x40 +#define GPHY_CTRL_PHY_IDDQ 0x80 +#define GPHY_CTRL_PHY_IDDQ_DIS 0x100 +#define GPHY_CTRL_PCLK_SEL_DIS 0x200 +#define GPHY_CTRL_HIB_EN 0x400 +#define GPHY_CTRL_HIB_PULSE 0x800 +#define GPHY_CTRL_SEL_ANA_RST 0x1000 +#define GPHY_CTRL_PHY_PLL_ON 0x2000 +#define GPHY_CTRL_PWDOWN_HW 0x4000 +#define GPHY_CTRL_DEFAULT (\ + GPHY_CTRL_PHY_PLL_ON |\ + GPHY_CTRL_SEL_ANA_RST |\ + GPHY_CTRL_HIB_PULSE |\ + GPHY_CTRL_HIB_EN) + +#define GPHY_CTRL_PW_WOL_DIS (\ + GPHY_CTRL_PHY_PLL_ON |\ + GPHY_CTRL_SEL_ANA_RST |\ + GPHY_CTRL_HIB_PULSE |\ + GPHY_CTRL_HIB_EN |\ + GPHY_CTRL_PWDOWN_HW |\ + GPHY_CTRL_PCLK_SEL_DIS |\ + GPHY_CTRL_PHY_IDDQ) + +/* IRQ Anti-Lost Timer Initial Value Register */ +#define REG_CMBDISDMA_TIMER 0x140E + + +/* Block IDLE Status Register */ +#define REG_IDLE_STATUS 0x1410 +#define IDLE_STATUS_RXMAC 1 /* 1: RXMAC state machine is in non-IDLE state. 0: RXMAC is idling */ +#define IDLE_STATUS_TXMAC 2 /* 1: TXMAC state machine is in non-IDLE state. 0: TXMAC is idling */ +#define IDLE_STATUS_RXQ 4 /* 1: RXQ state machine is in non-IDLE state. 0: RXQ is idling */ +#define IDLE_STATUS_TXQ 8 /* 1: TXQ state machine is in non-IDLE state. 0: TXQ is idling */ +#define IDLE_STATUS_DMAR 0x10 /* 1: DMAR state machine is in non-IDLE state. 0: DMAR is idling */ +#define IDLE_STATUS_DMAW 0x20 /* 1: DMAW state machine is in non-IDLE state. 0: DMAW is idling */ +#define IDLE_STATUS_SMB 0x40 /* 1: SMB state machine is in non-IDLE state. 0: SMB is idling */ +#define IDLE_STATUS_CMB 0x80 /* 1: CMB state machine is in non-IDLE state. 0: CMB is idling */ + +/* MDIO Control Register */ +#define REG_MDIO_CTRL 0x1414 +#define MDIO_DATA_MASK 0xffff /* On MDIO write, the 16-bit control data to write to PHY MII management register */ +#define MDIO_DATA_SHIFT 0 /* On MDIO read, the 16-bit status data that was read from the PHY MII management register*/ +#define MDIO_REG_ADDR_MASK 0x1f /* MDIO register address */ +#define MDIO_REG_ADDR_SHIFT 16 +#define MDIO_RW 0x200000 /* 1: read, 0: write */ +#define MDIO_SUP_PREAMBLE 0x400000 /* Suppress preamble */ +#define MDIO_START 0x800000 /* Write 1 to initiate the MDIO master. And this bit is self cleared after one cycle*/ +#define MDIO_CLK_SEL_SHIFT 24 +#define MDIO_CLK_25_4 0 +#define MDIO_CLK_25_6 2 +#define MDIO_CLK_25_8 3 +#define MDIO_CLK_25_10 4 +#define MDIO_CLK_25_14 5 +#define MDIO_CLK_25_20 6 +#define MDIO_CLK_25_28 7 +#define MDIO_BUSY 0x8000000 +#define MDIO_AP_EN 0x10000000 +#define MDIO_WAIT_TIMES 10 + +/* MII PHY Status Register */ +#define REG_PHY_STATUS 0x1418 +#define PHY_STATUS_100M 0x20000 +#define PHY_STATUS_EMI_CA 0x40000 + +/* BIST Control and Status Register0 (for the Packet Memory) */ +#define REG_BIST0_CTRL 0x141c +#define BIST0_NOW 0x1 /* 1: To trigger BIST0 logic. This bit stays high during the */ +/* BIST process and reset to zero when BIST is done */ +#define BIST0_SRAM_FAIL 0x2 /* 1: The SRAM failure is un-repairable because it has address */ +/* decoder failure or more than 1 cell stuck-to-x failure */ +#define BIST0_FUSE_FLAG 0x4 /* 1: Indicating one cell has been fixed */ + +/* BIST Control and Status Register1(for the retry buffer of PCI Express) */ +#define REG_BIST1_CTRL 0x1420 +#define BIST1_NOW 0x1 /* 1: To trigger BIST0 logic. This bit stays high during the */ +/* BIST process and reset to zero when BIST is done */ +#define BIST1_SRAM_FAIL 0x2 /* 1: The SRAM failure is un-repairable because it has address */ +/* decoder failure or more than 1 cell stuck-to-x failure.*/ +#define BIST1_FUSE_FLAG 0x4 + +/* SerDes Lock Detect Control and Status Register */ +#define REG_SERDES_LOCK 0x1424 +#define SERDES_LOCK_DETECT 1 /* 1: SerDes lock detected . This signal comes from Analog SerDes */ +#define SERDES_LOCK_DETECT_EN 2 /* 1: Enable SerDes Lock detect function */ + +/* MAC Control Register */ +#define REG_MAC_CTRL 0x1480 +#define MAC_CTRL_TX_EN 1 /* 1: Transmit Enable */ +#define MAC_CTRL_RX_EN 2 /* 1: Receive Enable */ +#define MAC_CTRL_TX_FLOW 4 /* 1: Transmit Flow Control Enable */ +#define MAC_CTRL_RX_FLOW 8 /* 1: Receive Flow Control Enable */ +#define MAC_CTRL_LOOPBACK 0x10 /* 1: Loop back at G/MII Interface */ +#define MAC_CTRL_DUPLX 0x20 /* 1: Full-duplex mode 0: Half-duplex mode */ +#define MAC_CTRL_ADD_CRC 0x40 /* 1: Instruct MAC to attach CRC on all egress Ethernet frames */ +#define MAC_CTRL_PAD 0x80 /* 1: Instruct MAC to pad short frames to 60-bytes, and then attach CRC. This bit has higher priority over CRC_EN */ +#define MAC_CTRL_LENCHK 0x100 /* 1: Instruct MAC to check if length field matches the real packet length */ +#define MAC_CTRL_HUGE_EN 0x200 /* 1: receive Jumbo frame enable */ +#define MAC_CTRL_PRMLEN_SHIFT 10 /* Preamble length */ +#define MAC_CTRL_PRMLEN_MASK 0xf +#define MAC_CTRL_RMV_VLAN 0x4000 /* 1: to remove VLAN Tag automatically from all receive packets */ +#define MAC_CTRL_PROMIS_EN 0x8000 /* 1: Promiscuous Mode Enable */ +#define MAC_CTRL_TX_PAUSE 0x10000 /* 1: transmit test pause */ +#define MAC_CTRL_SCNT 0x20000 /* 1: shortcut slot time counter */ +#define MAC_CTRL_SRST_TX 0x40000 /* 1: synchronized reset Transmit MAC module */ +#define MAC_CTRL_TX_SIMURST 0x80000 /* 1: transmit simulation reset */ +#define MAC_CTRL_SPEED_SHIFT 20 /* 10: gigabit 01:10M/100M */ +#define MAC_CTRL_SPEED_MASK 0x300000 +#define MAC_CTRL_SPEED_1000 2 +#define MAC_CTRL_SPEED_10_100 1 +#define MAC_CTRL_DBG_TX_BKPRESURE 0x400000 /* 1: transmit maximum backoff (half-duplex test bit) */ +#define MAC_CTRL_TX_HUGE 0x800000 /* 1: transmit huge enable */ +#define MAC_CTRL_RX_CHKSUM_EN 0x1000000 /* 1: RX checksum enable */ +#define MAC_CTRL_MC_ALL_EN 0x2000000 /* 1: upload all multicast frame without error to system */ +#define MAC_CTRL_BC_EN 0x4000000 /* 1: upload all broadcast frame without error to system */ +#define MAC_CTRL_DBG 0x8000000 /* 1: upload all received frame to system (Debug Mode) */ + +/* MAC IPG/IFG Control Register */ +#define REG_MAC_IPG_IFG 0x1484 +#define MAC_IPG_IFG_IPGT_SHIFT 0 /* Desired back to back inter-packet gap. The default is 96-bit time */ +#define MAC_IPG_IFG_IPGT_MASK 0x7f +#define MAC_IPG_IFG_MIFG_SHIFT 8 /* Minimum number of IFG to enforce in between RX frames */ +#define MAC_IPG_IFG_MIFG_MASK 0xff /* Frame gap below such IFP is dropped */ +#define MAC_IPG_IFG_IPGR1_SHIFT 16 /* 64bit Carrier-Sense window */ +#define MAC_IPG_IFG_IPGR1_MASK 0x7f +#define MAC_IPG_IFG_IPGR2_SHIFT 24 /* 96-bit IPG window */ +#define MAC_IPG_IFG_IPGR2_MASK 0x7f + +/* MAC STATION ADDRESS */ +#define REG_MAC_STA_ADDR 0x1488 + +/* Hash table for multicast address */ +#define REG_RX_HASH_TABLE 0x1490 + + +/* MAC Half-Duplex Control Register */ +#define REG_MAC_HALF_DUPLX_CTRL 0x1498 +#define MAC_HALF_DUPLX_CTRL_LCOL_SHIFT 0 /* Collision Window */ +#define MAC_HALF_DUPLX_CTRL_LCOL_MASK 0x3ff +#define MAC_HALF_DUPLX_CTRL_RETRY_SHIFT 12 /* Retransmission maximum, afterwards the packet will be discarded */ +#define MAC_HALF_DUPLX_CTRL_RETRY_MASK 0xf +#define MAC_HALF_DUPLX_CTRL_EXC_DEF_EN 0x10000 /* 1: Allow the transmission of a packet which has been excessively deferred */ +#define MAC_HALF_DUPLX_CTRL_NO_BACK_C 0x20000 /* 1: No back-off on collision, immediately start the retransmission */ +#define MAC_HALF_DUPLX_CTRL_NO_BACK_P 0x40000 /* 1: No back-off on backpressure, immediately start the transmission after back pressure */ +#define MAC_HALF_DUPLX_CTRL_ABEBE 0x80000 /* 1: Alternative Binary Exponential Back-off Enabled */ +#define MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT 20 /* Maximum binary exponential number */ +#define MAC_HALF_DUPLX_CTRL_ABEBT_MASK 0xf +#define MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT 24 /* IPG to start JAM for collision based flow control in half-duplex */ +#define MAC_HALF_DUPLX_CTRL_JAMIPG_MASK 0xf /* mode. In unit of 8-bit time */ + +/* Maximum Frame Length Control Register */ +#define REG_MTU 0x149c + +/* Wake-On-Lan control register */ +#define REG_WOL_CTRL 0x14a0 +#define WOL_PATTERN_EN 0x00000001 +#define WOL_PATTERN_PME_EN 0x00000002 +#define WOL_MAGIC_EN 0x00000004 +#define WOL_MAGIC_PME_EN 0x00000008 +#define WOL_LINK_CHG_EN 0x00000010 +#define WOL_LINK_CHG_PME_EN 0x00000020 +#define WOL_PATTERN_ST 0x00000100 +#define WOL_MAGIC_ST 0x00000200 +#define WOL_LINKCHG_ST 0x00000400 +#define WOL_CLK_SWITCH_EN 0x00008000 +#define WOL_PT0_EN 0x00010000 +#define WOL_PT1_EN 0x00020000 +#define WOL_PT2_EN 0x00040000 +#define WOL_PT3_EN 0x00080000 +#define WOL_PT4_EN 0x00100000 +#define WOL_PT5_EN 0x00200000 +#define WOL_PT6_EN 0x00400000 +/* WOL Length ( 2 DWORD ) */ +#define REG_WOL_PATTERN_LEN 0x14a4 +#define WOL_PT_LEN_MASK 0x7f +#define WOL_PT0_LEN_SHIFT 0 +#define WOL_PT1_LEN_SHIFT 8 +#define WOL_PT2_LEN_SHIFT 16 +#define WOL_PT3_LEN_SHIFT 24 +#define WOL_PT4_LEN_SHIFT 0 +#define WOL_PT5_LEN_SHIFT 8 +#define WOL_PT6_LEN_SHIFT 16 + +/* Internal SRAM Partition Register */ +#define REG_SRAM_TRD_ADDR 0x1518 +#define REG_SRAM_TRD_LEN 0x151C +#define REG_SRAM_RXF_ADDR 0x1520 +#define REG_SRAM_RXF_LEN 0x1524 +#define REG_SRAM_TXF_ADDR 0x1528 +#define REG_SRAM_TXF_LEN 0x152C +#define REG_SRAM_TCPH_ADDR 0x1530 +#define REG_SRAM_PKTH_ADDR 0x1532 + +/* Load Ptr Register */ +#define REG_LOAD_PTR 0x1534 /* Software sets this bit after the initialization of the head and tail */ + +/* + * addresses of all descriptors, as well as the following descriptor + * control register, which triggers each function block to load the head + * pointer to prepare for the operation. This bit is then self-cleared + * after one cycle. + */ + +/* Descriptor Control register */ +#define REG_RXF3_BASE_ADDR_HI 0x153C +#define REG_DESC_BASE_ADDR_HI 0x1540 +#define REG_RXF0_BASE_ADDR_HI 0x1540 /* share with DESC BASE ADDR HI */ +#define REG_HOST_RXF0_PAGE0_LO 0x1544 +#define REG_HOST_RXF0_PAGE1_LO 0x1548 +#define REG_TPD_BASE_ADDR_LO 0x154C +#define REG_RXF1_BASE_ADDR_HI 0x1550 +#define REG_RXF2_BASE_ADDR_HI 0x1554 +#define REG_HOST_RXFPAGE_SIZE 0x1558 +#define REG_TPD_RING_SIZE 0x155C +/* RSS about */ +#define REG_RSS_KEY0 0x14B0 +#define REG_RSS_KEY1 0x14B4 +#define REG_RSS_KEY2 0x14B8 +#define REG_RSS_KEY3 0x14BC +#define REG_RSS_KEY4 0x14C0 +#define REG_RSS_KEY5 0x14C4 +#define REG_RSS_KEY6 0x14C8 +#define REG_RSS_KEY7 0x14CC +#define REG_RSS_KEY8 0x14D0 +#define REG_RSS_KEY9 0x14D4 +#define REG_IDT_TABLE4 0x14E0 +#define REG_IDT_TABLE5 0x14E4 +#define REG_IDT_TABLE6 0x14E8 +#define REG_IDT_TABLE7 0x14EC +#define REG_IDT_TABLE0 0x1560 +#define REG_IDT_TABLE1 0x1564 +#define REG_IDT_TABLE2 0x1568 +#define REG_IDT_TABLE3 0x156C +#define REG_IDT_TABLE REG_IDT_TABLE0 +#define REG_RSS_HASH_VALUE 0x1570 +#define REG_RSS_HASH_FLAG 0x1574 +#define REG_BASE_CPU_NUMBER 0x157C + + +/* TXQ Control Register */ +#define REG_TXQ_CTRL 0x1580 +#define TXQ_CTRL_NUM_TPD_BURST_MASK 0xF +#define TXQ_CTRL_NUM_TPD_BURST_SHIFT 0 +#define TXQ_CTRL_EN 0x20 /* 1: Enable TXQ */ +#define TXQ_CTRL_ENH_MODE 0x40 /* Performance enhancement mode, in which up to two back-to-back DMA read commands might be dispatched. */ +#define TXQ_CTRL_TXF_BURST_NUM_SHIFT 16 /* Number of data byte to read in a cache-aligned burst. Each SRAM entry is 8-byte in length. */ +#define TXQ_CTRL_TXF_BURST_NUM_MASK 0xffff + +/* Jumbo packet Threshold for task offload */ +#define REG_TX_EARLY_TH 0x1584 /* Jumbo frame threshold in QWORD unit. Packet greater than */ +/* JUMBO_TASK_OFFLOAD_THRESHOLD will not be task offloaded. */ +#define TX_TX_EARLY_TH_MASK 0x7ff +#define TX_TX_EARLY_TH_SHIFT 0 + + +/* RXQ Control Register */ +#define REG_RXQ_CTRL 0x15A0 +#define RXQ_CTRL_PBA_ALIGN_32 0 /* rx-packet alignment */ +#define RXQ_CTRL_PBA_ALIGN_64 1 +#define RXQ_CTRL_PBA_ALIGN_128 2 +#define RXQ_CTRL_PBA_ALIGN_256 3 +#define RXQ_CTRL_Q1_EN 0x10 +#define RXQ_CTRL_Q2_EN 0x20 +#define RXQ_CTRL_Q3_EN 0x40 +#define RXQ_CTRL_IPV6_XSUM_VERIFY_EN 0x80 +#define RXQ_CTRL_HASH_TLEN_SHIFT 8 +#define RXQ_CTRL_HASH_TLEN_MASK 0xFF +#define RXQ_CTRL_HASH_TYPE_IPV4 0x10000 +#define RXQ_CTRL_HASH_TYPE_IPV4_TCP 0x20000 +#define RXQ_CTRL_HASH_TYPE_IPV6 0x40000 +#define RXQ_CTRL_HASH_TYPE_IPV6_TCP 0x80000 +#define RXQ_CTRL_RSS_MODE_DISABLE 0 +#define RXQ_CTRL_RSS_MODE_SQSINT 0x4000000 +#define RXQ_CTRL_RSS_MODE_MQUESINT 0x8000000 +#define RXQ_CTRL_RSS_MODE_MQUEMINT 0xC000000 +#define RXQ_CTRL_NIP_QUEUE_SEL_TBL 0x10000000 +#define RXQ_CTRL_HASH_ENABLE 0x20000000 +#define RXQ_CTRL_CUT_THRU_EN 0x40000000 +#define RXQ_CTRL_EN 0x80000000 + +/* Rx jumbo packet threshold and rrd retirement timer */ +#define REG_RXQ_JMBOSZ_RRDTIM 0x15A4 +/* + * Jumbo packet threshold for non-VLAN packet, in QWORD (64-bit) unit. + * When the packet length greater than or equal to this value, RXQ + * shall start cut-through forwarding of the received packet. + */ +#define RXQ_JMBOSZ_TH_MASK 0x7ff +#define RXQ_JMBOSZ_TH_SHIFT 0 /* RRD retirement timer. Decrement by 1 after every 512ns passes*/ +#define RXQ_JMBO_LKAH_MASK 0xf +#define RXQ_JMBO_LKAH_SHIFT 11 + +/* RXF flow control register */ +#define REG_RXQ_RXF_PAUSE_THRESH 0x15A8 +#define RXQ_RXF_PAUSE_TH_HI_SHIFT 0 +#define RXQ_RXF_PAUSE_TH_HI_MASK 0xfff +#define RXQ_RXF_PAUSE_TH_LO_SHIFT 16 +#define RXQ_RXF_PAUSE_TH_LO_MASK 0xfff + + +/* DMA Engine Control Register */ +#define REG_DMA_CTRL 0x15C0 +#define DMA_CTRL_DMAR_IN_ORDER 0x1 +#define DMA_CTRL_DMAR_ENH_ORDER 0x2 +#define DMA_CTRL_DMAR_OUT_ORDER 0x4 +#define DMA_CTRL_RCB_VALUE 0x8 +#define DMA_CTRL_DMAR_BURST_LEN_SHIFT 4 +#define DMA_CTRL_DMAR_BURST_LEN_MASK 7 +#define DMA_CTRL_DMAW_BURST_LEN_SHIFT 7 +#define DMA_CTRL_DMAW_BURST_LEN_MASK 7 +#define DMA_CTRL_DMAR_REQ_PRI 0x400 +#define DMA_CTRL_DMAR_DLY_CNT_MASK 0x1F +#define DMA_CTRL_DMAR_DLY_CNT_SHIFT 11 +#define DMA_CTRL_DMAW_DLY_CNT_MASK 0xF +#define DMA_CTRL_DMAW_DLY_CNT_SHIFT 16 +#define DMA_CTRL_TXCMB_EN 0x100000 +#define DMA_CTRL_RXCMB_EN 0x200000 + + +/* CMB/SMB Control Register */ +#define REG_SMB_STAT_TIMER 0x15C4 +#define REG_TRIG_RRD_THRESH 0x15CA +#define REG_TRIG_TPD_THRESH 0x15C8 +#define REG_TRIG_TXTIMER 0x15CC +#define REG_TRIG_RXTIMER 0x15CE + +/* HOST RXF Page 1,2,3 address */ +#define REG_HOST_RXF1_PAGE0_LO 0x15D0 +#define REG_HOST_RXF1_PAGE1_LO 0x15D4 +#define REG_HOST_RXF2_PAGE0_LO 0x15D8 +#define REG_HOST_RXF2_PAGE1_LO 0x15DC +#define REG_HOST_RXF3_PAGE0_LO 0x15E0 +#define REG_HOST_RXF3_PAGE1_LO 0x15E4 + +/* Mail box */ +#define REG_MB_RXF1_RADDR 0x15B4 +#define REG_MB_RXF2_RADDR 0x15B8 +#define REG_MB_RXF3_RADDR 0x15BC +#define REG_MB_TPD_PROD_IDX 0x15F0 + +/* RXF-Page 0-3 PageNo & Valid bit */ +#define REG_HOST_RXF0_PAGE0_VLD 0x15F4 +#define HOST_RXF_VALID 1 +#define HOST_RXF_PAGENO_SHIFT 1 +#define HOST_RXF_PAGENO_MASK 0x7F +#define REG_HOST_RXF0_PAGE1_VLD 0x15F5 +#define REG_HOST_RXF1_PAGE0_VLD 0x15F6 +#define REG_HOST_RXF1_PAGE1_VLD 0x15F7 +#define REG_HOST_RXF2_PAGE0_VLD 0x15F8 +#define REG_HOST_RXF2_PAGE1_VLD 0x15F9 +#define REG_HOST_RXF3_PAGE0_VLD 0x15FA +#define REG_HOST_RXF3_PAGE1_VLD 0x15FB + +/* Interrupt Status Register */ +#define REG_ISR 0x1600 +#define ISR_SMB 1 +#define ISR_TIMER 2 /* Interrupt when Timer is counted down to zero */ +/* + * Software manual interrupt, for debug. Set when SW_MAN_INT_EN is set + * in Table 51 Selene Master Control Register (Offset 0x1400). + */ +#define ISR_MANUAL 4 +#define ISR_HW_RXF_OV 8 /* RXF overflow interrupt */ +#define ISR_HOST_RXF0_OV 0x10 +#define ISR_HOST_RXF1_OV 0x20 +#define ISR_HOST_RXF2_OV 0x40 +#define ISR_HOST_RXF3_OV 0x80 +#define ISR_TXF_UN 0x100 +#define ISR_RX0_PAGE_FULL 0x200 +#define ISR_DMAR_TO_RST 0x400 +#define ISR_DMAW_TO_RST 0x800 +#define ISR_GPHY 0x1000 +#define ISR_TX_CREDIT 0x2000 +#define ISR_GPHY_LPW 0x4000 /* GPHY low power state interrupt */ +#define ISR_RX_PKT 0x10000 /* One packet received, triggered by RFD */ +#define ISR_TX_PKT 0x20000 /* One packet transmitted, triggered by TPD */ +#define ISR_TX_DMA 0x40000 +#define ISR_RX_PKT_1 0x80000 +#define ISR_RX_PKT_2 0x100000 +#define ISR_RX_PKT_3 0x200000 +#define ISR_MAC_RX 0x400000 +#define ISR_MAC_TX 0x800000 +#define ISR_UR_DETECTED 0x1000000 +#define ISR_FERR_DETECTED 0x2000000 +#define ISR_NFERR_DETECTED 0x4000000 +#define ISR_CERR_DETECTED 0x8000000 +#define ISR_PHY_LINKDOWN 0x10000000 +#define ISR_DIS_INT 0x80000000 + + +/* Interrupt Mask Register */ +#define REG_IMR 0x1604 + + +#define IMR_NORMAL_MASK (\ + ISR_SMB |\ + ISR_TXF_UN |\ + ISR_HW_RXF_OV |\ + ISR_HOST_RXF0_OV|\ + ISR_MANUAL |\ + ISR_GPHY |\ + ISR_GPHY_LPW |\ + ISR_DMAR_TO_RST |\ + ISR_DMAW_TO_RST |\ + ISR_PHY_LINKDOWN|\ + ISR_RX_PKT |\ + ISR_TX_PKT) + +#define ISR_TX_EVENT (ISR_TXF_UN | ISR_TX_PKT) +#define ISR_RX_EVENT (ISR_HOST_RXF0_OV | ISR_HW_RXF_OV | ISR_RX_PKT) + +#define REG_MAC_RX_STATUS_BIN 0x1700 +#define REG_MAC_RX_STATUS_END 0x175c +#define REG_MAC_TX_STATUS_BIN 0x1760 +#define REG_MAC_TX_STATUS_END 0x17c0 + +/* Hardware Offset Register */ +#define REG_HOST_RXF0_PAGEOFF 0x1800 +#define REG_TPD_CONS_IDX 0x1804 +#define REG_HOST_RXF1_PAGEOFF 0x1808 +#define REG_HOST_RXF2_PAGEOFF 0x180C +#define REG_HOST_RXF3_PAGEOFF 0x1810 + +/* RXF-Page 0-3 Offset DMA Address */ +#define REG_HOST_RXF0_MB0_LO 0x1820 +#define REG_HOST_RXF0_MB1_LO 0x1824 +#define REG_HOST_RXF1_MB0_LO 0x1828 +#define REG_HOST_RXF1_MB1_LO 0x182C +#define REG_HOST_RXF2_MB0_LO 0x1830 +#define REG_HOST_RXF2_MB1_LO 0x1834 +#define REG_HOST_RXF3_MB0_LO 0x1838 +#define REG_HOST_RXF3_MB1_LO 0x183C + +/* Tpd CMB DMA Address */ +#define REG_HOST_TX_CMB_LO 0x1840 +#define REG_HOST_SMB_ADDR_LO 0x1844 + +/* DEBUG ADDR */ +#define REG_DEBUG_DATA0 0x1900 +#define REG_DEBUG_DATA1 0x1904 + +/***************************** MII definition ***************************************/ +/* PHY Common Register */ +#define MII_BMCR 0x00 +#define MII_BMSR 0x01 +#define MII_PHYSID1 0x02 +#define MII_PHYSID2 0x03 +#define MII_ADVERTISE 0x04 +#define MII_LPA 0x05 +#define MII_EXPANSION 0x06 +#define MII_AT001_CR 0x09 +#define MII_AT001_SR 0x0A +#define MII_AT001_ESR 0x0F +#define MII_AT001_PSCR 0x10 +#define MII_AT001_PSSR 0x11 +#define MII_INT_CTRL 0x12 +#define MII_INT_STATUS 0x13 +#define MII_SMARTSPEED 0x14 +#define MII_RERRCOUNTER 0x15 +#define MII_SREVISION 0x16 +#define MII_RESV1 0x17 +#define MII_LBRERROR 0x18 +#define MII_PHYADDR 0x19 +#define MII_RESV2 0x1a +#define MII_TPISTATUS 0x1b +#define MII_NCONFIG 0x1c + +#define MII_DBG_ADDR 0x1D +#define MII_DBG_DATA 0x1E + + +/* PHY Control Register */ +#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ +#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ +#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ +#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ +#define MII_CR_POWER_DOWN 0x0800 /* Power down */ +#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ +#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ +#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ +#define MII_CR_SPEED_MASK 0x2040 +#define MII_CR_SPEED_1000 0x0040 +#define MII_CR_SPEED_100 0x2000 +#define MII_CR_SPEED_10 0x0000 + + +/* PHY Status Register */ +#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +/* Link partner ability register. */ +#define MII_LPA_SLCT 0x001f /* Same as advertise selector */ +#define MII_LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */ +#define MII_LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */ +#define MII_LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */ +#define MII_LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */ +#define MII_LPA_100BASE4 0x0200 /* 100BASE-T4 */ +#define MII_LPA_PAUSE 0x0400 /* PAUSE */ +#define MII_LPA_ASYPAUSE 0x0800 /* Asymmetrical PAUSE */ +#define MII_LPA_RFAULT 0x2000 /* Link partner faulted */ +#define MII_LPA_LPACK 0x4000 /* Link partner acked us */ +#define MII_LPA_NPAGE 0x8000 /* Next page bit */ + +/* Autoneg Advertisement Register */ +#define MII_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ +#define MII_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ +#define MII_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ +#define MII_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ +#define MII_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ +#define MII_AR_100T4_CAPS 0x0200 /* 100T4 Capable */ +#define MII_AR_PAUSE 0x0400 /* Pause operation desired */ +#define MII_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ +#define MII_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */ +#define MII_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */ +#define MII_AR_SPEED_MASK 0x01E0 +#define MII_AR_DEFAULT_CAP_MASK 0x0DE0 + +/* 1000BASE-T Control Register */ +#define MII_AT001_CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ +#define MII_AT001_CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ +#define MII_AT001_CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port */ +/* 0=DTE device */ +#define MII_AT001_CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master */ +/* 0=Configure PHY as Slave */ +#define MII_AT001_CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value */ +/* 0=Automatic Master/Slave config */ +#define MII_AT001_CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ +#define MII_AT001_CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ +#define MII_AT001_CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ +#define MII_AT001_CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ +#define MII_AT001_CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ +#define MII_AT001_CR_1000T_SPEED_MASK 0x0300 +#define MII_AT001_CR_1000T_DEFAULT_CAP_MASK 0x0300 + +/* 1000BASE-T Status Register */ +#define MII_AT001_SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */ +#define MII_AT001_SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */ +#define MII_AT001_SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ +#define MII_AT001_SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */ +#define MII_AT001_SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master, 0=Slave */ +#define MII_AT001_SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */ +#define MII_AT001_SR_1000T_REMOTE_RX_STATUS_SHIFT 12 +#define MII_AT001_SR_1000T_LOCAL_RX_STATUS_SHIFT 13 + +/* Extended Status Register */ +#define MII_AT001_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */ +#define MII_AT001_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */ +#define MII_AT001_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */ +#define MII_AT001_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */ + +/* AT001 PHY Specific Control Register */ +#define MII_AT001_PSCR_JABBER_DISABLE 0x0001 /* 1=Jabber Function disabled */ +#define MII_AT001_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reversal enabled */ +#define MII_AT001_PSCR_SQE_TEST 0x0004 /* 1=SQE Test enabled */ +#define MII_AT001_PSCR_MAC_POWERDOWN 0x0008 +#define MII_AT001_PSCR_CLK125_DISABLE 0x0010 /* 1=CLK125 low, + * 0=CLK125 toggling + */ +#define MII_AT001_PSCR_MDI_MANUAL_MODE 0x0000 /* MDI Crossover Mode bits 6:5 */ +/* Manual MDI configuration */ +#define MII_AT001_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */ +#define MII_AT001_PSCR_AUTO_X_1000T 0x0040 /* 1000BASE-T: Auto crossover, + * 100BASE-TX/10BASE-T: + * MDI Mode + */ +#define MII_AT001_PSCR_AUTO_X_MODE 0x0060 /* Auto crossover enabled + * all speeds. + */ +#define MII_AT001_PSCR_10BT_EXT_DIST_ENABLE 0x0080 +/* 1=Enable Extended 10BASE-T distance + * (Lower 10BASE-T RX Threshold) + * 0=Normal 10BASE-T RX Threshold */ +#define MII_AT001_PSCR_MII_5BIT_ENABLE 0x0100 +/* 1=5-Bit interface in 100BASE-TX + * 0=MII interface in 100BASE-TX */ +#define MII_AT001_PSCR_SCRAMBLER_DISABLE 0x0200 /* 1=Scrambler disable */ +#define MII_AT001_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force link good */ +#define MII_AT001_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Transmit */ +#define MII_AT001_PSCR_POLARITY_REVERSAL_SHIFT 1 +#define MII_AT001_PSCR_AUTO_X_MODE_SHIFT 5 +#define MII_AT001_PSCR_10BT_EXT_DIST_ENABLE_SHIFT 7 +/* AT001 PHY Specific Status Register */ +#define MII_AT001_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */ +#define MII_AT001_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */ +#define MII_AT001_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */ +#define MII_AT001_PSSR_10MBS 0x0000 /* 00=10Mbs */ +#define MII_AT001_PSSR_100MBS 0x4000 /* 01=100Mbs */ +#define MII_AT001_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ + +#endif /*_ATHL1E_HW_H_*/ diff --git a/drivers/net/atl1e/atl1e_main.c b/drivers/net/atl1e/atl1e_main.c new file mode 100644 index 000000000000..35264c244cfd --- /dev/null +++ b/drivers/net/atl1e/atl1e_main.c @@ -0,0 +1,2599 @@ +/* + * Copyright(c) 2007 Atheros Corporation. All rights reserved. + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * 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 "atl1e.h" + +#define DRV_VERSION "1.0.0.7-NAPI" + +char atl1e_driver_name[] = "ATL1E"; +char atl1e_driver_version[] = DRV_VERSION; +#define PCI_DEVICE_ID_ATTANSIC_L1E 0x1026 +/* + * atl1e_pci_tbl - PCI Device ID Table + * + * Wildcard entries (PCI_ANY_ID) should come last + * Last entry must be all 0s + * + * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, + * Class, Class Mask, private data (not used) } + */ +static struct pci_device_id atl1e_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L1E)}, + /* required last entry */ + { 0 } +}; +MODULE_DEVICE_TABLE(pci, atl1e_pci_tbl); + +MODULE_AUTHOR("Atheros Corporation, <xiong.huang@atheros.com>, Jie Yang <jie.yang@atheros.com>"); +MODULE_DESCRIPTION("Atheros 1000M Ethernet Network Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +static inline void atl1e_setup_mac_ctrl(struct atl1e_adapter *adapter); + +static const u16 +atl1e_rx_page_vld_regs[AT_MAX_RECEIVE_QUEUE][AT_PAGE_NUM_PER_QUEUE] = +{ + {REG_HOST_RXF0_PAGE0_VLD, REG_HOST_RXF0_PAGE1_VLD}, + {REG_HOST_RXF1_PAGE0_VLD, REG_HOST_RXF1_PAGE1_VLD}, + {REG_HOST_RXF2_PAGE0_VLD, REG_HOST_RXF2_PAGE1_VLD}, + {REG_HOST_RXF3_PAGE0_VLD, REG_HOST_RXF3_PAGE1_VLD} +}; + +static const u16 atl1e_rx_page_hi_addr_regs[AT_MAX_RECEIVE_QUEUE] = +{ + REG_RXF0_BASE_ADDR_HI, + REG_RXF1_BASE_ADDR_HI, + REG_RXF2_BASE_ADDR_HI, + REG_RXF3_BASE_ADDR_HI +}; + +static const u16 +atl1e_rx_page_lo_addr_regs[AT_MAX_RECEIVE_QUEUE][AT_PAGE_NUM_PER_QUEUE] = +{ + {REG_HOST_RXF0_PAGE0_LO, REG_HOST_RXF0_PAGE1_LO}, + {REG_HOST_RXF1_PAGE0_LO, REG_HOST_RXF1_PAGE1_LO}, + {REG_HOST_RXF2_PAGE0_LO, REG_HOST_RXF2_PAGE1_LO}, + {REG_HOST_RXF3_PAGE0_LO, REG_HOST_RXF3_PAGE1_LO} +}; + +static const u16 +atl1e_rx_page_write_offset_regs[AT_MAX_RECEIVE_QUEUE][AT_PAGE_NUM_PER_QUEUE] = +{ + {REG_HOST_RXF0_MB0_LO, REG_HOST_RXF0_MB1_LO}, + {REG_HOST_RXF1_MB0_LO, REG_HOST_RXF1_MB1_LO}, + {REG_HOST_RXF2_MB0_LO, REG_HOST_RXF2_MB1_LO}, + {REG_HOST_RXF3_MB0_LO, REG_HOST_RXF3_MB1_LO} +}; + +static const u16 atl1e_pay_load_size[] = { + 128, 256, 512, 1024, 2048, 4096, +}; + +/* + * atl1e_irq_enable - Enable default interrupt generation settings + * @adapter: board private structure + */ +static inline void atl1e_irq_enable(struct atl1e_adapter *adapter) +{ + if (likely(atomic_dec_and_test(&adapter->irq_sem))) { + AT_WRITE_REG(&adapter->hw, REG_ISR, 0); + AT_WRITE_REG(&adapter->hw, REG_IMR, IMR_NORMAL_MASK); + AT_WRITE_FLUSH(&adapter->hw); + } +} + +/* + * atl1e_irq_disable - Mask off interrupt generation on the NIC + * @adapter: board private structure + */ +static inline void atl1e_irq_disable(struct atl1e_adapter *adapter) +{ + atomic_inc(&adapter->irq_sem); + AT_WRITE_REG(&adapter->hw, REG_IMR, 0); + AT_WRITE_FLUSH(&adapter->hw); + synchronize_irq(adapter->pdev->irq); +} + +/* + * atl1e_irq_reset - reset interrupt confiure on the NIC + * @adapter: board private structure + */ +static inline void atl1e_irq_reset(struct atl1e_adapter *adapter) +{ + atomic_set(&adapter->irq_sem, 0); + AT_WRITE_REG(&adapter->hw, REG_ISR, 0); + AT_WRITE_REG(&adapter->hw, REG_IMR, 0); + AT_WRITE_FLUSH(&adapter->hw); +} + +/* + * atl1e_phy_config - Timer Call-back + * @data: pointer to netdev cast into an unsigned long + */ +static void atl1e_phy_config(unsigned long data) +{ + struct atl1e_adapter *adapter = (struct atl1e_adapter *) data; + struct atl1e_hw *hw = &adapter->hw; + unsigned long flags; + + spin_lock_irqsave(&adapter->mdio_lock, flags); + atl1e_restart_autoneg(hw); + spin_unlock_irqrestore(&adapter->mdio_lock, flags); +} + +void atl1e_reinit_locked(struct atl1e_adapter *adapter) +{ + + WARN_ON(in_interrupt()); + while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) + msleep(1); + atl1e_down(adapter); + atl1e_up(adapter); + clear_bit(__AT_RESETTING, &adapter->flags); +} + +static void atl1e_reset_task(struct work_struct *work) +{ + struct atl1e_adapter *adapter; + adapter = container_of(work, struct atl1e_adapter, reset_task); + + atl1e_reinit_locked(adapter); +} + +static int atl1e_check_link(struct atl1e_adapter *adapter) +{ + struct atl1e_hw *hw = &adapter->hw; + struct net_device *netdev = adapter->netdev; + struct pci_dev *pdev = adapter->pdev; + int err = 0; + u16 speed, duplex, phy_data; + + /* MII_BMSR must read twise */ + atl1e_read_phy_reg(hw, MII_BMSR, &phy_data); + atl1e_read_phy_reg(hw, MII_BMSR, &phy_data); + if ((phy_data & BMSR_LSTATUS) == 0) { + /* link down */ + if (netif_carrier_ok(netdev)) { /* old link state: Up */ + u32 value; + /* disable rx */ + value = AT_READ_REG(hw, REG_MAC_CTRL); + value &= ~MAC_CTRL_RX_EN; + AT_WRITE_REG(hw, REG_MAC_CTRL, value); + adapter->link_speed = SPEED_0; + netif_carrier_off(netdev); + netif_stop_queue(netdev); + } + } else { + /* Link Up */ + err = atl1e_get_speed_and_duplex(hw, &speed, &duplex); + if (unlikely(err)) + return err; + + /* link result is our setting */ + if (adapter->link_speed != speed || + adapter->link_duplex != duplex) { + adapter->link_speed = speed; + adapter->link_duplex = duplex; + atl1e_setup_mac_ctrl(adapter); + dev_info(&pdev->dev, + "%s: %s NIC Link is Up<%d Mbps %s>\n", + atl1e_driver_name, netdev->name, + adapter->link_speed, + adapter->link_duplex == FULL_DUPLEX ? + "Full Duplex" : "Half Duplex"); + } + + if (!netif_carrier_ok(netdev)) { + /* Link down -> Up */ + netif_carrier_on(netdev); + netif_wake_queue(netdev); + } + } + return 0; +} + +/* + * atl1e_link_chg_task - deal with link change event Out of interrupt context + * @netdev: network interface device structure + */ +static void atl1e_link_chg_task(struct work_struct *work) +{ + struct atl1e_adapter *adapter; + unsigned long flags; + + adapter = container_of(work, struct atl1e_adapter, link_chg_task); + spin_lock_irqsave(&adapter->mdio_lock, flags); + atl1e_check_link(adapter); + spin_unlock_irqrestore(&adapter->mdio_lock, flags); +} + +static void atl1e_link_chg_event(struct atl1e_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + struct pci_dev *pdev = adapter->pdev; + u16 phy_data = 0; + u16 link_up = 0; + + spin_lock(&adapter->mdio_lock); + atl1e_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); + atl1e_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); + spin_unlock(&adapter->mdio_lock); + link_up = phy_data & BMSR_LSTATUS; + /* notify upper layer link down ASAP */ + if (!link_up) { + if (netif_carrier_ok(netdev)) { + /* old link state: Up */ + dev_info(&pdev->dev, "%s: %s NIC Link is Down\n", + atl1e_driver_name, netdev->name); + adapter->link_speed = SPEED_0; + netif_stop_queue(netdev); + } + } + schedule_work(&adapter->link_chg_task); +} + +static void atl1e_del_timer(struct atl1e_adapter *adapter) +{ + del_timer_sync(&adapter->phy_config_timer); +} + +static void atl1e_cancel_work(struct atl1e_adapter *adapter) +{ + cancel_work_sync(&adapter->reset_task); + cancel_work_sync(&adapter->link_chg_task); +} + +/* + * atl1e_tx_timeout - Respond to a Tx Hang + * @netdev: network interface device structure + */ +static void atl1e_tx_timeout(struct net_device *netdev) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + + /* Do the reset outside of interrupt context */ + schedule_work(&adapter->reset_task); +} + +/* + * atl1e_set_multi - Multicast and Promiscuous mode set + * @netdev: network interface device structure + * + * The set_multi entry point is called whenever the multicast address + * list or the network interface flags are updated. This routine is + * responsible for configuring the hardware for proper multicast, + * promiscuous mode, and all-multi behavior. + */ +static void atl1e_set_multi(struct net_device *netdev) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + struct atl1e_hw *hw = &adapter->hw; + struct dev_mc_list *mc_ptr; + u32 mac_ctrl_data = 0; + u32 hash_value; + + /* Check for Promiscuous and All Multicast modes */ + mac_ctrl_data = AT_READ_REG(hw, REG_MAC_CTRL); + + if (netdev->flags & IFF_PROMISC) { + mac_ctrl_data |= MAC_CTRL_PROMIS_EN; + } else if (netdev->flags & IFF_ALLMULTI) { + mac_ctrl_data |= MAC_CTRL_MC_ALL_EN; + mac_ctrl_data &= ~MAC_CTRL_PROMIS_EN; + } else { + mac_ctrl_data &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN); + } + + AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data); + + /* clear the old settings from the multicast hash table */ + AT_WRITE_REG(hw, REG_RX_HASH_TABLE, 0); + AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, 1, 0); + + /* comoute mc addresses' hash value ,and put it into hash table */ + for (mc_ptr = netdev->mc_list; mc_ptr; mc_ptr = mc_ptr->next) { + hash_value = atl1e_hash_mc_addr(hw, mc_ptr->dmi_addr); + atl1e_hash_set(hw, hash_value); + } +} + +static void atl1e_vlan_rx_register(struct net_device *netdev, + struct vlan_group *grp) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + struct pci_dev *pdev = adapter->pdev; + u32 mac_ctrl_data = 0; + + dev_dbg(&pdev->dev, "atl1e_vlan_rx_register\n"); + + atl1e_irq_disable(adapter); + + adapter->vlgrp = grp; + mac_ctrl_data = AT_READ_REG(&adapter->hw, REG_MAC_CTRL); + + if (grp) { + /* enable VLAN tag insert/strip */ + mac_ctrl_data |= MAC_CTRL_RMV_VLAN; + } else { + /* disable VLAN tag insert/strip */ + mac_ctrl_data &= ~MAC_CTRL_RMV_VLAN; + } + + AT_WRITE_REG(&adapter->hw, REG_MAC_CTRL, mac_ctrl_data); + atl1e_irq_enable(adapter); +} + +static void atl1e_restore_vlan(struct atl1e_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + + dev_dbg(&pdev->dev, "atl1e_restore_vlan !"); + atl1e_vlan_rx_register(adapter->netdev, adapter->vlgrp); +} +/* + * atl1e_set_mac - Change the Ethernet Address of the NIC + * @netdev: network interface device structure + * @p: pointer to an address structure + * + * Returns 0 on success, negative on failure + */ +static int atl1e_set_mac_addr(struct net_device *netdev, void *p) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + if (netif_running(netdev)) + return -EBUSY; + + memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); + memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len); + + atl1e_hw_set_mac_addr(&adapter->hw); + + return 0; +} + +/* + * atl1e_change_mtu - Change the Maximum Transfer Unit + * @netdev: network interface device structure + * @new_mtu: new value for maximum frame size + * + * Returns 0 on success, negative on failure + */ +static int atl1e_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + int old_mtu = netdev->mtu; + int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; + + if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) || + (max_frame > MAX_JUMBO_FRAME_SIZE)) { + dev_warn(&adapter->pdev->dev, "invalid MTU setting\n"); + return -EINVAL; + } + /* set MTU */ + if (old_mtu != new_mtu && netif_running(netdev)) { + while (test_and_set_bit(__AT_RESETTING, &adapter->flags)) + msleep(1); + netdev->mtu = new_mtu; + adapter->hw.max_frame_size = new_mtu; + adapter->hw.rx_jumbo_th = (max_frame + 7) >> 3; + atl1e_down(adapter); + atl1e_up(adapter); + clear_bit(__AT_RESETTING, &adapter->flags); + } + return 0; +} + +/* + * caller should hold mdio_lock + */ +static int atl1e_mdio_read(struct net_device *netdev, int phy_id, int reg_num) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + u16 result; + + atl1e_read_phy_reg(&adapter->hw, reg_num & MDIO_REG_ADDR_MASK, &result); + return result; +} + +static void atl1e_mdio_write(struct net_device *netdev, int phy_id, + int reg_num, int val) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + + atl1e_write_phy_reg(&adapter->hw, reg_num & MDIO_REG_ADDR_MASK, val); +} + +/* + * atl1e_mii_ioctl - + * @netdev: + * @ifreq: + * @cmd: + */ +static int atl1e_mii_ioctl(struct net_device *netdev, + struct ifreq *ifr, int cmd) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + struct pci_dev *pdev = adapter->pdev; + struct mii_ioctl_data *data = if_mii(ifr); + unsigned long flags; + int retval = 0; + + if (!netif_running(netdev)) + return -EINVAL; + + spin_lock_irqsave(&adapter->mdio_lock, flags); + switch (cmd) { + case SIOCGMIIPHY: + data->phy_id = 0; + break; + + case SIOCGMIIREG: + if (!capable(CAP_NET_ADMIN)) { + retval = -EPERM; + goto out; + } + if (atl1e_read_phy_reg(&adapter->hw, data->reg_num & 0x1F, + &data->val_out)) { + retval = -EIO; + goto out; + } + break; + + case SIOCSMIIREG: + if (!capable(CAP_NET_ADMIN)) { + retval = -EPERM; + goto out; + } + if (data->reg_num & ~(0x1F)) { + retval = -EFAULT; + goto out; + } + + dev_dbg(&pdev->dev, "<atl1e_mii_ioctl> write %x %x", + data->reg_num, data->val_in); + if (atl1e_write_phy_reg(&adapter->hw, + data->reg_num, data->val_in)) { + retval = -EIO; + goto out; + } + break; + + default: + retval = -EOPNOTSUPP; + break; + } +out: + spin_unlock_irqrestore(&adapter->mdio_lock, flags); + return retval; + +} + +/* + * atl1e_ioctl - + * @netdev: + * @ifreq: + * @cmd: + */ +static int atl1e_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + switch (cmd) { + case SIOCGMIIPHY: + case SIOCGMIIREG: + case SIOCSMIIREG: + return atl1e_mii_ioctl(netdev, ifr, cmd); + default: + return -EOPNOTSUPP; + } +} + +static void atl1e_setup_pcicmd(struct pci_dev *pdev) +{ + u16 cmd; + + pci_read_config_word(pdev, PCI_COMMAND, &cmd); + cmd &= ~(PCI_COMMAND_INTX_DISABLE | PCI_COMMAND_IO); + cmd |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + pci_write_config_word(pdev, PCI_COMMAND, cmd); + + /* + * some motherboards BIOS(PXE/EFI) driver may set PME + * while they transfer control to OS (Windows/Linux) + * so we should clear this bit before NIC work normally + */ + pci_write_config_dword(pdev, REG_PM_CTRLSTAT, 0); + msleep(1); +} + +/* + * atl1e_alloc_queues - Allocate memory for all rings + * @adapter: board private structure to initialize + * + */ +static int __devinit atl1e_alloc_queues(struct atl1e_adapter *adapter) +{ + return 0; +} + +/* + * atl1e_sw_init - Initialize general software structures (struct atl1e_adapter) + * @adapter: board private structure to initialize + * + * atl1e_sw_init initializes the Adapter private data structure. + * Fields are initialized based on PCI device information and + * OS network device settings (MTU size). + */ +static int __devinit atl1e_sw_init(struct atl1e_adapter *adapter) +{ + struct atl1e_hw *hw = &adapter->hw; + struct pci_dev *pdev = adapter->pdev; + u32 phy_status_data = 0; + + adapter->wol = 0; + adapter->link_speed = SPEED_0; /* hardware init */ + adapter->link_duplex = FULL_DUPLEX; + adapter->num_rx_queues = 1; + + /* PCI config space info */ + hw->vendor_id = pdev->vendor; + hw->device_id = pdev->device; + hw->subsystem_vendor_id = pdev->subsystem_vendor; + hw->subsystem_id = pdev->subsystem_device; + + pci_read_config_byte(pdev, PCI_REVISION_ID, &hw->revision_id); + pci_read_config_word(pdev, PCI_COMMAND, &hw->pci_cmd_word); + + phy_status_data = AT_READ_REG(hw, REG_PHY_STATUS); + /* nic type */ + if (hw->revision_id >= 0xF0) { + hw->nic_type = athr_l2e_revB; + } else { + if (phy_status_data & PHY_STATUS_100M) + hw->nic_type = athr_l1e; + else + hw->nic_type = athr_l2e_revA; + } + + phy_status_data = AT_READ_REG(hw, REG_PHY_STATUS); + + if (phy_status_data & PHY_STATUS_EMI_CA) + hw->emi_ca = true; + else + hw->emi_ca = false; + + hw->phy_configured = false; + hw->preamble_len = 7; + hw->max_frame_size = adapter->netdev->mtu; + hw->rx_jumbo_th = (hw->max_frame_size + ETH_HLEN + + VLAN_HLEN + ETH_FCS_LEN + 7) >> 3; + + hw->rrs_type = atl1e_rrs_disable; + hw->indirect_tab = 0; + hw->base_cpu = 0; + + /* need confirm */ + + hw->ict = 50000; /* 100ms */ + hw->smb_timer = 200000; /* 200ms */ + hw->tpd_burst = 5; + hw->rrd_thresh = 1; + hw->tpd_thresh = adapter->tx_ring.count / 2; + hw->rx_count_down = 4; /* 2us resolution */ + hw->tx_count_down = hw->imt * 4 / 3; + hw->dmar_block = atl1e_dma_req_1024; + hw->dmaw_block = atl1e_dma_req_1024; + hw->dmar_dly_cnt = 15; + hw->dmaw_dly_cnt = 4; + + if (atl1e_alloc_queues(adapter)) { + dev_err(&pdev->dev, "Unable to allocate memory for queues\n"); + return -ENOMEM; + } + + atomic_set(&adapter->irq_sem, 1); + spin_lock_init(&adapter->mdio_lock); + spin_lock_init(&adapter->tx_lock); + + set_bit(__AT_DOWN, &adapter->flags); + + return 0; +} + +/* + * atl1e_clean_tx_ring - Free Tx-skb + * @adapter: board private structure + */ +static void atl1e_clean_tx_ring(struct atl1e_adapter *adapter) +{ + struct atl1e_tx_ring *tx_ring = (struct atl1e_tx_ring *) + &adapter->tx_ring; + struct atl1e_tx_buffer *tx_buffer = NULL; + struct pci_dev *pdev = adapter->pdev; + u16 index, ring_count; + + if (tx_ring->desc == NULL || tx_ring->tx_buffer == NULL) + return; + + ring_count = tx_ring->count; + /* first unmmap dma */ + for (index = 0; index < ring_count; index++) { + tx_buffer = &tx_ring->tx_buffer[index]; + if (tx_buffer->dma) { + pci_unmap_page(pdev, tx_buffer->dma, + tx_buffer->length, PCI_DMA_TODEVICE); + tx_buffer->dma = 0; + } + } + /* second free skb */ + for (index = 0; index < ring_count; index++) { + tx_buffer = &tx_ring->tx_buffer[index]; + if (tx_buffer->skb) { + dev_kfree_skb_any(tx_buffer->skb); + tx_buffer->skb = NULL; + } + } + /* Zero out Tx-buffers */ + memset(tx_ring->desc, 0, sizeof(struct atl1e_tpd_desc) * + ring_count); + memset(tx_ring->tx_buffer, 0, sizeof(struct atl1e_tx_buffer) * + ring_count); +} + +/* + * atl1e_clean_rx_ring - Free rx-reservation skbs + * @adapter: board private structure + */ +static void atl1e_clean_rx_ring(struct atl1e_adapter *adapter) +{ + struct atl1e_rx_ring *rx_ring = + (struct atl1e_rx_ring *)&adapter->rx_ring; + struct atl1e_rx_page_desc *rx_page_desc = rx_ring->rx_page_desc; + u16 i, j; + + + if (adapter->ring_vir_addr == NULL) + return; + /* Zero out the descriptor ring */ + for (i = 0; i < adapter->num_rx_queues; i++) { + for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) { + if (rx_page_desc[i].rx_page[j].addr != NULL) { + memset(rx_page_desc[i].rx_page[j].addr, 0, + rx_ring->real_page_size); + } + } + } +} + +static void atl1e_cal_ring_size(struct atl1e_adapter *adapter, u32 *ring_size) +{ + *ring_size = ((u32)(adapter->tx_ring.count * + sizeof(struct atl1e_tpd_desc) + 7 + /* tx ring, qword align */ + + adapter->rx_ring.real_page_size * AT_PAGE_NUM_PER_QUEUE * + adapter->num_rx_queues + 31 + /* rx ring, 32 bytes align */ + + (1 + AT_PAGE_NUM_PER_QUEUE * adapter->num_rx_queues) * + sizeof(u32) + 3)); + /* tx, rx cmd, dword align */ +} + +static void atl1e_init_ring_resources(struct atl1e_adapter *adapter) +{ + struct atl1e_tx_ring *tx_ring = NULL; + struct atl1e_rx_ring *rx_ring = NULL; + + tx_ring = &adapter->tx_ring; + rx_ring = &adapter->rx_ring; + + rx_ring->real_page_size = adapter->rx_ring.page_size + + adapter->hw.max_frame_size + + ETH_HLEN + VLAN_HLEN + + ETH_FCS_LEN; + rx_ring->real_page_size = roundup(rx_ring->real_page_size, 32); + atl1e_cal_ring_size(adapter, &adapter->ring_size); + + adapter->ring_vir_addr = NULL; + adapter->rx_ring.desc = NULL; + rwlock_init(&adapter->tx_ring.tx_lock); + + return; +} + +/* + * Read / Write Ptr Initialize: + */ +static void atl1e_init_ring_ptrs(struct atl1e_adapter *adapter) +{ + struct atl1e_tx_ring *tx_ring = NULL; + struct atl1e_rx_ring *rx_ring = NULL; + struct atl1e_rx_page_desc *rx_page_desc = NULL; + int i, j; + + tx_ring = &adapter->tx_ring; + rx_ring = &adapter->rx_ring; + rx_page_desc = rx_ring->rx_page_desc; + + tx_ring->next_to_use = 0; + atomic_set(&tx_ring->next_to_clean, 0); + + for (i = 0; i < adapter->num_rx_queues; i++) { + rx_page_desc[i].rx_using = 0; + rx_page_desc[i].rx_nxseq = 0; + for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) { + *rx_page_desc[i].rx_page[j].write_offset_addr = 0; + rx_page_desc[i].rx_page[j].read_offset = 0; + } + } +} + +/* + * atl1e_free_ring_resources - Free Tx / RX descriptor Resources + * @adapter: board private structure + * + * Free all transmit software resources + */ +static void atl1e_free_ring_resources(struct atl1e_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + + atl1e_clean_tx_ring(adapter); + atl1e_clean_rx_ring(adapter); + + if (adapter->ring_vir_addr) { + pci_free_consistent(pdev, adapter->ring_size, + adapter->ring_vir_addr, adapter->ring_dma); + adapter->ring_vir_addr = NULL; + } + + if (adapter->tx_ring.tx_buffer) { + kfree(adapter->tx_ring.tx_buffer); + adapter->tx_ring.tx_buffer = NULL; + } +} + +/* + * atl1e_setup_mem_resources - allocate Tx / RX descriptor resources + * @adapter: board private structure + * + * Return 0 on success, negative on failure + */ +static int atl1e_setup_ring_resources(struct atl1e_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + struct atl1e_tx_ring *tx_ring; + struct atl1e_rx_ring *rx_ring; + struct atl1e_rx_page_desc *rx_page_desc; + int size, i, j; + u32 offset = 0; + int err = 0; + + if (adapter->ring_vir_addr != NULL) + return 0; /* alloced already */ + + tx_ring = &adapter->tx_ring; + rx_ring = &adapter->rx_ring; + + /* real ring DMA buffer */ + + size = adapter->ring_size; + adapter->ring_vir_addr = pci_alloc_consistent(pdev, + adapter->ring_size, &adapter->ring_dma); + + if (adapter->ring_vir_addr == NULL) { + dev_err(&pdev->dev, "pci_alloc_consistent failed, " + "size = D%d", size); + return -ENOMEM; + } + + memset(adapter->ring_vir_addr, 0, adapter->ring_size); + + rx_page_desc = rx_ring->rx_page_desc; + + /* Init TPD Ring */ + tx_ring->dma = roundup(adapter->ring_dma, 8); + offset = tx_ring->dma - adapter->ring_dma; + tx_ring->desc = (struct atl1e_tpd_desc *) + (adapter->ring_vir_addr + offset); + size = sizeof(struct atl1e_tx_buffer) * (tx_ring->count); + tx_ring->tx_buffer = kzalloc(size, GFP_KERNEL); + if (tx_ring->tx_buffer == NULL) { + dev_err(&pdev->dev, "kzalloc failed , size = D%d", size); + err = -ENOMEM; + goto failed; + } + + /* Init RXF-Pages */ + offset += (sizeof(struct atl1e_tpd_desc) * tx_ring->count); + offset = roundup(offset, 32); + + for (i = 0; i < adapter->num_rx_queues; i++) { + for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) { + rx_page_desc[i].rx_page[j].dma = + adapter->ring_dma + offset; + rx_page_desc[i].rx_page[j].addr = + adapter->ring_vir_addr + offset; + offset += rx_ring->real_page_size; + } + } + + /* Init CMB dma address */ + tx_ring->cmb_dma = adapter->ring_dma + offset; + tx_ring->cmb = (u32 *)(adapter->ring_vir_addr + offset); + offset += sizeof(u32); + + for (i = 0; i < adapter->num_rx_queues; i++) { + for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) { + rx_page_desc[i].rx_page[j].write_offset_dma = + adapter->ring_dma + offset; + rx_page_desc[i].rx_page[j].write_offset_addr = + adapter->ring_vir_addr + offset; + offset += sizeof(u32); + } + } + + if (unlikely(offset > adapter->ring_size)) { + dev_err(&pdev->dev, "offset(%d) > ring size(%d) !!\n", + offset, adapter->ring_size); + err = -1; + goto failed; + } + + return 0; +failed: + if (adapter->ring_vir_addr != NULL) { + pci_free_consistent(pdev, adapter->ring_size, + adapter->ring_vir_addr, adapter->ring_dma); + adapter->ring_vir_addr = NULL; + } + return err; +} + +static inline void atl1e_configure_des_ring(const struct atl1e_adapter *adapter) +{ + + struct atl1e_hw *hw = (struct atl1e_hw *)&adapter->hw; + struct atl1e_rx_ring *rx_ring = + (struct atl1e_rx_ring *)&adapter->rx_ring; + struct atl1e_tx_ring *tx_ring = + (struct atl1e_tx_ring *)&adapter->tx_ring; + struct atl1e_rx_page_desc *rx_page_desc = NULL; + int i, j; + + AT_WRITE_REG(hw, REG_DESC_BASE_ADDR_HI, + (u32)((adapter->ring_dma & AT_DMA_HI_ADDR_MASK) >> 32)); + AT_WRITE_REG(hw, REG_TPD_BASE_ADDR_LO, + (u32)((tx_ring->dma) & AT_DMA_LO_ADDR_MASK)); + AT_WRITE_REG(hw, REG_TPD_RING_SIZE, (u16)(tx_ring->count)); + AT_WRITE_REG(hw, REG_HOST_TX_CMB_LO, + (u32)((tx_ring->cmb_dma) & AT_DMA_LO_ADDR_MASK)); + + rx_page_desc = rx_ring->rx_page_desc; + /* RXF Page Physical address / Page Length */ + for (i = 0; i < AT_MAX_RECEIVE_QUEUE; i++) { + AT_WRITE_REG(hw, atl1e_rx_page_hi_addr_regs[i], + (u32)((adapter->ring_dma & + AT_DMA_HI_ADDR_MASK) >> 32)); + for (j = 0; j < AT_PAGE_NUM_PER_QUEUE; j++) { + u32 page_phy_addr; + u32 offset_phy_addr; + + page_phy_addr = rx_page_desc[i].rx_page[j].dma; + offset_phy_addr = + rx_page_desc[i].rx_page[j].write_offset_dma; + + AT_WRITE_REG(hw, atl1e_rx_page_lo_addr_regs[i][j], + page_phy_addr & AT_DMA_LO_ADDR_MASK); + AT_WRITE_REG(hw, atl1e_rx_page_write_offset_regs[i][j], + offset_phy_addr & AT_DMA_LO_ADDR_MASK); + AT_WRITE_REGB(hw, atl1e_rx_page_vld_regs[i][j], 1); + } + } + /* Page Length */ + AT_WRITE_REG(hw, REG_HOST_RXFPAGE_SIZE, rx_ring->page_size); + /* Load all of base address above */ + AT_WRITE_REG(hw, REG_LOAD_PTR, 1); + + return; +} + +static inline void atl1e_configure_tx(struct atl1e_adapter *adapter) +{ + struct atl1e_hw *hw = (struct atl1e_hw *)&adapter->hw; + u32 dev_ctrl_data = 0; + u32 max_pay_load = 0; + u32 jumbo_thresh = 0; + u32 extra_size = 0; /* Jumbo frame threshold in QWORD unit */ + + /* configure TXQ param */ + if (hw->nic_type != athr_l2e_revB) { + extra_size = ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN; + if (hw->max_frame_size <= 1500) { + jumbo_thresh = hw->max_frame_size + extra_size; + } else if (hw->max_frame_size < 6*1024) { + jumbo_thresh = + (hw->max_frame_size + extra_size) * 2 / 3; + } else { + jumbo_thresh = (hw->max_frame_size + extra_size) / 2; + } + AT_WRITE_REG(hw, REG_TX_EARLY_TH, (jumbo_thresh + 7) >> 3); + } + + dev_ctrl_data = AT_READ_REG(hw, REG_DEVICE_CTRL); + + max_pay_load = ((dev_ctrl_data >> DEVICE_CTRL_MAX_PAYLOAD_SHIFT)) & + DEVICE_CTRL_MAX_PAYLOAD_MASK; + + hw->dmaw_block = min(max_pay_load, hw->dmaw_block); + + max_pay_load = ((dev_ctrl_data >> DEVICE_CTRL_MAX_RREQ_SZ_SHIFT)) & + DEVICE_CTRL_MAX_RREQ_SZ_MASK; + hw->dmar_block = min(max_pay_load, hw->dmar_block); + + if (hw->nic_type != athr_l2e_revB) + AT_WRITE_REGW(hw, REG_TXQ_CTRL + 2, + atl1e_pay_load_size[hw->dmar_block]); + /* enable TXQ */ + AT_WRITE_REGW(hw, REG_TXQ_CTRL, + (((u16)hw->tpd_burst & TXQ_CTRL_NUM_TPD_BURST_MASK) + << TXQ_CTRL_NUM_TPD_BURST_SHIFT) + | TXQ_CTRL_ENH_MODE | TXQ_CTRL_EN); + return; +} + +static inline void atl1e_configure_rx(struct atl1e_adapter *adapter) +{ + struct atl1e_hw *hw = (struct atl1e_hw *)&adapter->hw; + u32 rxf_len = 0; + u32 rxf_low = 0; + u32 rxf_high = 0; + u32 rxf_thresh_data = 0; + u32 rxq_ctrl_data = 0; + + if (hw->nic_type != athr_l2e_revB) { + AT_WRITE_REGW(hw, REG_RXQ_JMBOSZ_RRDTIM, + (u16)((hw->rx_jumbo_th & RXQ_JMBOSZ_TH_MASK) << + RXQ_JMBOSZ_TH_SHIFT | + (1 & RXQ_JMBO_LKAH_MASK) << + RXQ_JMBO_LKAH_SHIFT)); + + rxf_len = AT_READ_REG(hw, REG_SRAM_RXF_LEN); + rxf_high = rxf_len * 4 / 5; + rxf_low = rxf_len / 5; + rxf_thresh_data = ((rxf_high & RXQ_RXF_PAUSE_TH_HI_MASK) + << RXQ_RXF_PAUSE_TH_HI_SHIFT) | + ((rxf_low & RXQ_RXF_PAUSE_TH_LO_MASK) + << RXQ_RXF_PAUSE_TH_LO_SHIFT); + + AT_WRITE_REG(hw, REG_RXQ_RXF_PAUSE_THRESH, rxf_thresh_data); + } + + /* RRS */ + AT_WRITE_REG(hw, REG_IDT_TABLE, hw->indirect_tab); + AT_WRITE_REG(hw, REG_BASE_CPU_NUMBER, hw->base_cpu); + + if (hw->rrs_type & atl1e_rrs_ipv4) + rxq_ctrl_data |= RXQ_CTRL_HASH_TYPE_IPV4; + + if (hw->rrs_type & atl1e_rrs_ipv4_tcp) + rxq_ctrl_data |= RXQ_CTRL_HASH_TYPE_IPV4_TCP; + + if (hw->rrs_type & atl1e_rrs_ipv6) + rxq_ctrl_data |= RXQ_CTRL_HASH_TYPE_IPV6; + + if (hw->rrs_type & atl1e_rrs_ipv6_tcp) + rxq_ctrl_data |= RXQ_CTRL_HASH_TYPE_IPV6_TCP; + + if (hw->rrs_type != atl1e_rrs_disable) + rxq_ctrl_data |= + (RXQ_CTRL_HASH_ENABLE | RXQ_CTRL_RSS_MODE_MQUESINT); + + rxq_ctrl_data |= RXQ_CTRL_IPV6_XSUM_VERIFY_EN | RXQ_CTRL_PBA_ALIGN_32 | + RXQ_CTRL_CUT_THRU_EN | RXQ_CTRL_EN; + + AT_WRITE_REG(hw, REG_RXQ_CTRL, rxq_ctrl_data); + return; +} + +static inline void atl1e_configure_dma(struct atl1e_adapter *adapter) +{ + struct atl1e_hw *hw = &adapter->hw; + u32 dma_ctrl_data = 0; + + dma_ctrl_data = DMA_CTRL_RXCMB_EN; + dma_ctrl_data |= (((u32)hw->dmar_block) & DMA_CTRL_DMAR_BURST_LEN_MASK) + << DMA_CTRL_DMAR_BURST_LEN_SHIFT; + dma_ctrl_data |= (((u32)hw->dmaw_block) & DMA_CTRL_DMAW_BURST_LEN_MASK) + << DMA_CTRL_DMAW_BURST_LEN_SHIFT; + dma_ctrl_data |= DMA_CTRL_DMAR_REQ_PRI | DMA_CTRL_DMAR_OUT_ORDER; + dma_ctrl_data |= (((u32)hw->dmar_dly_cnt) & DMA_CTRL_DMAR_DLY_CNT_MASK) + << DMA_CTRL_DMAR_DLY_CNT_SHIFT; + dma_ctrl_data |= (((u32)hw->dmaw_dly_cnt) & DMA_CTRL_DMAW_DLY_CNT_MASK) + << DMA_CTRL_DMAW_DLY_CNT_SHIFT; + + AT_WRITE_REG(hw, REG_DMA_CTRL, dma_ctrl_data); + return; +} + +static inline void atl1e_setup_mac_ctrl(struct atl1e_adapter *adapter) +{ + u32 value; + struct atl1e_hw *hw = &adapter->hw; + struct net_device *netdev = adapter->netdev; + + /* Config MAC CTRL Register */ + value = MAC_CTRL_TX_EN | + MAC_CTRL_RX_EN ; + + if (FULL_DUPLEX == adapter->link_duplex) + value |= MAC_CTRL_DUPLX; + + value |= ((u32)((SPEED_1000 == adapter->link_speed) ? + MAC_CTRL_SPEED_1000 : MAC_CTRL_SPEED_10_100) << + MAC_CTRL_SPEED_SHIFT); + value |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW); + + value |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD); + value |= (((u32)adapter->hw.preamble_len & + MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT); + + if (adapter->vlgrp) + value |= MAC_CTRL_RMV_VLAN; + + value |= MAC_CTRL_BC_EN; + if (netdev->flags & IFF_PROMISC) + value |= MAC_CTRL_PROMIS_EN; + if (netdev->flags & IFF_ALLMULTI) + value |= MAC_CTRL_MC_ALL_EN; + + AT_WRITE_REG(hw, REG_MAC_CTRL, value); +} + +/* + * atl1e_configure - Configure Transmit&Receive Unit after Reset + * @adapter: board private structure + * + * Configure the Tx /Rx unit of the MAC after a reset. + */ +static int atl1e_configure(struct atl1e_adapter *adapter) +{ + struct atl1e_hw *hw = &adapter->hw; + struct pci_dev *pdev = adapter->pdev; + + u32 intr_status_data = 0; + + /* clear interrupt status */ + AT_WRITE_REG(hw, REG_ISR, ~0); + + /* 1. set MAC Address */ + atl1e_hw_set_mac_addr(hw); + + /* 2. Init the Multicast HASH table done by set_muti */ + + /* 3. Clear any WOL status */ + AT_WRITE_REG(hw, REG_WOL_CTRL, 0); + + /* 4. Descripter Ring BaseMem/Length/Read ptr/Write ptr + * TPD Ring/SMB/RXF0 Page CMBs, they use the same + * High 32bits memory */ + atl1e_configure_des_ring(adapter); + + /* 5. set Interrupt Moderator Timer */ + AT_WRITE_REGW(hw, REG_IRQ_MODU_TIMER_INIT, hw->imt); + AT_WRITE_REGW(hw, REG_IRQ_MODU_TIMER2_INIT, hw->imt); + AT_WRITE_REG(hw, REG_MASTER_CTRL, MASTER_CTRL_LED_MODE | + MASTER_CTRL_ITIMER_EN | MASTER_CTRL_ITIMER2_EN); + + /* 6. rx/tx threshold to trig interrupt */ + AT_WRITE_REGW(hw, REG_TRIG_RRD_THRESH, hw->rrd_thresh); + AT_WRITE_REGW(hw, REG_TRIG_TPD_THRESH, hw->tpd_thresh); + AT_WRITE_REGW(hw, REG_TRIG_RXTIMER, hw->rx_count_down); + AT_WRITE_REGW(hw, REG_TRIG_TXTIMER, hw->tx_count_down); + + /* 7. set Interrupt Clear Timer */ + AT_WRITE_REGW(hw, REG_CMBDISDMA_TIMER, hw->ict); + + /* 8. set MTU */ + AT_WRITE_REG(hw, REG_MTU, hw->max_frame_size + ETH_HLEN + + VLAN_HLEN + ETH_FCS_LEN); + + /* 9. config TXQ early tx threshold */ + atl1e_configure_tx(adapter); + + /* 10. config RXQ */ + atl1e_configure_rx(adapter); + + /* 11. config DMA Engine */ + atl1e_configure_dma(adapter); + + /* 12. smb timer to trig interrupt */ + AT_WRITE_REG(hw, REG_SMB_STAT_TIMER, hw->smb_timer); + + intr_status_data = AT_READ_REG(hw, REG_ISR); + if (unlikely((intr_status_data & ISR_PHY_LINKDOWN) != 0)) { + dev_err(&pdev->dev, "atl1e_configure failed," + "PCIE phy link down\n"); + return -1; + } + + AT_WRITE_REG(hw, REG_ISR, 0x7fffffff); + return 0; +} + +/* + * atl1e_get_stats - Get System Network Statistics + * @netdev: network interface device structure + * + * Returns the address of the device statistics structure. + * The statistics are actually updated from the timer callback. + */ +static struct net_device_stats *atl1e_get_stats(struct net_device *netdev) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + struct atl1e_hw_stats *hw_stats = &adapter->hw_stats; + struct net_device_stats *net_stats = &adapter->net_stats; + + net_stats->rx_packets = hw_stats->rx_ok; + net_stats->tx_packets = hw_stats->tx_ok; + net_stats->rx_bytes = hw_stats->rx_byte_cnt; + net_stats->tx_bytes = hw_stats->tx_byte_cnt; + net_stats->multicast = hw_stats->rx_mcast; + net_stats->collisions = hw_stats->tx_1_col + + hw_stats->tx_2_col * 2 + + hw_stats->tx_late_col + hw_stats->tx_abort_col; + + net_stats->rx_errors = hw_stats->rx_frag + hw_stats->rx_fcs_err + + hw_stats->rx_len_err + hw_stats->rx_sz_ov + + hw_stats->rx_rrd_ov + hw_stats->rx_align_err; + net_stats->rx_fifo_errors = hw_stats->rx_rxf_ov; + net_stats->rx_length_errors = hw_stats->rx_len_err; + net_stats->rx_crc_errors = hw_stats->rx_fcs_err; + net_stats->rx_frame_errors = hw_stats->rx_align_err; + net_stats->rx_over_errors = hw_stats->rx_rrd_ov + hw_stats->rx_rxf_ov; + + net_stats->rx_missed_errors = hw_stats->rx_rrd_ov + hw_stats->rx_rxf_ov; + + net_stats->tx_errors = hw_stats->tx_late_col + hw_stats->tx_abort_col + + hw_stats->tx_underrun + hw_stats->tx_trunc; + net_stats->tx_fifo_errors = hw_stats->tx_underrun; + net_stats->tx_aborted_errors = hw_stats->tx_abort_col; + net_stats->tx_window_errors = hw_stats->tx_late_col; + + return &adapter->net_stats; +} + +static void atl1e_update_hw_stats(struct atl1e_adapter *adapter) +{ + u16 hw_reg_addr = 0; + unsigned long *stats_item = NULL; + + /* update rx status */ + hw_reg_addr = REG_MAC_RX_STATUS_BIN; + stats_item = &adapter->hw_stats.rx_ok; + while (hw_reg_addr <= REG_MAC_RX_STATUS_END) { + *stats_item += AT_READ_REG(&adapter->hw, hw_reg_addr); + stats_item++; + hw_reg_addr += 4; + } + /* update tx status */ + hw_reg_addr = REG_MAC_TX_STATUS_BIN; + stats_item = &adapter->hw_stats.tx_ok; + while (hw_reg_addr <= REG_MAC_TX_STATUS_END) { + *stats_item += AT_READ_REG(&adapter->hw, hw_reg_addr); + stats_item++; + hw_reg_addr += 4; + } +} + +static inline void atl1e_clear_phy_int(struct atl1e_adapter *adapter) +{ + u16 phy_data; + + spin_lock(&adapter->mdio_lock); + atl1e_read_phy_reg(&adapter->hw, MII_INT_STATUS, &phy_data); + spin_unlock(&adapter->mdio_lock); +} + +static bool atl1e_clean_tx_irq(struct atl1e_adapter *adapter) +{ + struct atl1e_tx_ring *tx_ring = (struct atl1e_tx_ring *) + &adapter->tx_ring; + struct atl1e_tx_buffer *tx_buffer = NULL; + u16 hw_next_to_clean = AT_READ_REGW(&adapter->hw, REG_TPD_CONS_IDX); + u16 next_to_clean = atomic_read(&tx_ring->next_to_clean); + + while (next_to_clean != hw_next_to_clean) { + tx_buffer = &tx_ring->tx_buffer[next_to_clean]; + if (tx_buffer->dma) { + pci_unmap_page(adapter->pdev, tx_buffer->dma, + tx_buffer->length, PCI_DMA_TODEVICE); + tx_buffer->dma = 0; + } + + if (tx_buffer->skb) { + dev_kfree_skb_irq(tx_buffer->skb); + tx_buffer->skb = NULL; + } + + if (++next_to_clean == tx_ring->count) + next_to_clean = 0; + } + + atomic_set(&tx_ring->next_to_clean, next_to_clean); + + if (netif_queue_stopped(adapter->netdev) && + netif_carrier_ok(adapter->netdev)) { + netif_wake_queue(adapter->netdev); + } + + return true; +} + +/* + * atl1e_intr - Interrupt Handler + * @irq: interrupt number + * @data: pointer to a network interface device structure + * @pt_regs: CPU registers structure + */ +static irqreturn_t atl1e_intr(int irq, void *data) +{ + struct net_device *netdev = data; + struct atl1e_adapter *adapter = netdev_priv(netdev); + struct pci_dev *pdev = adapter->pdev; + struct atl1e_hw *hw = &adapter->hw; + int max_ints = AT_MAX_INT_WORK; + int handled = IRQ_NONE; + u32 status; + + do { + status = AT_READ_REG(hw, REG_ISR); + if ((status & IMR_NORMAL_MASK) == 0 || + (status & ISR_DIS_INT) != 0) { + if (max_ints != AT_MAX_INT_WORK) + handled = IRQ_HANDLED; + break; + } + /* link event */ + if (status & ISR_GPHY) + atl1e_clear_phy_int(adapter); + /* Ack ISR */ + AT_WRITE_REG(hw, REG_ISR, status | ISR_DIS_INT); + + handled = IRQ_HANDLED; + /* check if PCIE PHY Link down */ + if (status & ISR_PHY_LINKDOWN) { + dev_err(&pdev->dev, + "pcie phy linkdown %x\n", status); + if (netif_running(adapter->netdev)) { + /* reset MAC */ + atl1e_irq_reset(adapter); + schedule_work(&adapter->reset_task); + break; + } + } + + /* check if DMA read/write error */ + if (status & (ISR_DMAR_TO_RST | ISR_DMAW_TO_RST)) { + dev_err(&pdev->dev, + "PCIE DMA RW error (status = 0x%x)\n", + status); + atl1e_irq_reset(adapter); + schedule_work(&adapter->reset_task); + break; + } + + if (status & ISR_SMB) + atl1e_update_hw_stats(adapter); + + /* link event */ + if (status & (ISR_GPHY | ISR_MANUAL)) { + adapter->net_stats.tx_carrier_errors++; + atl1e_link_chg_event(adapter); + break; + } + + /* transmit event */ + if (status & ISR_TX_EVENT) + atl1e_clean_tx_irq(adapter); + + if (status & ISR_RX_EVENT) { + /* + * disable rx interrupts, without + * the synchronize_irq bit + */ + AT_WRITE_REG(hw, REG_IMR, + IMR_NORMAL_MASK & ~ISR_RX_EVENT); + AT_WRITE_FLUSH(hw); + if (likely(netif_rx_schedule_prep(netdev, + &adapter->napi))) + __netif_rx_schedule(netdev, &adapter->napi); + } + } while (--max_ints > 0); + /* re-enable Interrupt*/ + AT_WRITE_REG(&adapter->hw, REG_ISR, 0); + + return handled; +} + +static inline void atl1e_rx_checksum(struct atl1e_adapter *adapter, + struct sk_buff *skb, struct atl1e_recv_ret_status *prrs) +{ + u8 *packet = (u8 *)(prrs + 1); + struct iphdr *iph; + u16 head_len = ETH_HLEN; + u16 pkt_flags; + u16 err_flags; + + skb->ip_summed = CHECKSUM_NONE; + pkt_flags = prrs->pkt_flag; + err_flags = prrs->err_flag; + if (((pkt_flags & RRS_IS_IPV4) || (pkt_flags & RRS_IS_IPV6)) && + ((pkt_flags & RRS_IS_TCP) || (pkt_flags & RRS_IS_UDP))) { + if (pkt_flags & RRS_IS_IPV4) { + if (pkt_flags & RRS_IS_802_3) + head_len += 8; + iph = (struct iphdr *) (packet + head_len); + if (iph->frag_off != 0 && !(pkt_flags & RRS_IS_IP_DF)) + goto hw_xsum; + } + if (!(err_flags & (RRS_ERR_IP_CSUM | RRS_ERR_L4_CSUM))) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + return; + } + } + +hw_xsum : + return; +} + +static struct atl1e_rx_page *atl1e_get_rx_page(struct atl1e_adapter *adapter, + u8 que) +{ + struct atl1e_rx_page_desc *rx_page_desc = + (struct atl1e_rx_page_desc *) adapter->rx_ring.rx_page_desc; + u8 rx_using = rx_page_desc[que].rx_using; + + return (struct atl1e_rx_page *)&(rx_page_desc[que].rx_page[rx_using]); +} + +static void atl1e_clean_rx_irq(struct atl1e_adapter *adapter, u8 que, + int *work_done, int work_to_do) +{ + struct pci_dev *pdev = adapter->pdev; + struct net_device *netdev = adapter->netdev; + struct atl1e_rx_ring *rx_ring = (struct atl1e_rx_ring *) + &adapter->rx_ring; + struct atl1e_rx_page_desc *rx_page_desc = + (struct atl1e_rx_page_desc *) rx_ring->rx_page_desc; + struct sk_buff *skb = NULL; + struct atl1e_rx_page *rx_page = atl1e_get_rx_page(adapter, que); + u32 packet_size, write_offset; + struct atl1e_recv_ret_status *prrs; + + write_offset = *(rx_page->write_offset_addr); + if (likely(rx_page->read_offset < write_offset)) { + do { + if (*work_done >= work_to_do) + break; + (*work_done)++; + /* get new packet's rrs */ + prrs = (struct atl1e_recv_ret_status *) (rx_page->addr + + rx_page->read_offset); + /* check sequence number */ + if (prrs->seq_num != rx_page_desc[que].rx_nxseq) { + dev_err(&pdev->dev, + "rx sequence number" + " error (rx=%d) (expect=%d)\n", + prrs->seq_num, + rx_page_desc[que].rx_nxseq); + rx_page_desc[que].rx_nxseq++; + /* just for debug use */ + AT_WRITE_REG(&adapter->hw, REG_DEBUG_DATA0, + (((u32)prrs->seq_num) << 16) | + rx_page_desc[que].rx_nxseq); + goto fatal_err; + } + rx_page_desc[que].rx_nxseq++; + + /* error packet */ + if (prrs->pkt_flag & RRS_IS_ERR_FRAME) { + if (prrs->err_flag & (RRS_ERR_BAD_CRC | + RRS_ERR_DRIBBLE | RRS_ERR_CODE | + RRS_ERR_TRUNC)) { + /* hardware error, discard this packet*/ + dev_err(&pdev->dev, + "rx packet desc error %x\n", + *((u32 *)prrs + 1)); + goto skip_pkt; + } + } + + packet_size = ((prrs->word1 >> RRS_PKT_SIZE_SHIFT) & + RRS_PKT_SIZE_MASK) - 4; /* CRC */ + skb = netdev_alloc_skb(netdev, + packet_size + NET_IP_ALIGN); + if (skb == NULL) { + dev_warn(&pdev->dev, "%s: Memory squeeze," + "deferring packet.\n", netdev->name); + goto skip_pkt; + } + skb_reserve(skb, NET_IP_ALIGN); + skb->dev = netdev; + memcpy(skb->data, (u8 *)(prrs + 1), packet_size); + skb_put(skb, packet_size); + skb->protocol = eth_type_trans(skb, netdev); + atl1e_rx_checksum(adapter, skb, prrs); + + if (unlikely(adapter->vlgrp && + (prrs->pkt_flag & RRS_IS_VLAN_TAG))) { + u16 vlan_tag = (prrs->vtag >> 4) | + ((prrs->vtag & 7) << 13) | + ((prrs->vtag & 8) << 9); + dev_dbg(&pdev->dev, + "RXD VLAN TAG<RRD>=0x%04x\n", + prrs->vtag); + vlan_hwaccel_receive_skb(skb, adapter->vlgrp, + vlan_tag); + } else { + netif_receive_skb(skb); + } + + netdev->last_rx = jiffies; +skip_pkt: + /* skip current packet whether it's ok or not. */ + rx_page->read_offset += + (((u32)((prrs->word1 >> RRS_PKT_SIZE_SHIFT) & + RRS_PKT_SIZE_MASK) + + sizeof(struct atl1e_recv_ret_status) + 31) & + 0xFFFFFFE0); + + if (rx_page->read_offset >= rx_ring->page_size) { + /* mark this page clean */ + u16 reg_addr; + u8 rx_using; + + rx_page->read_offset = + *(rx_page->write_offset_addr) = 0; + rx_using = rx_page_desc[que].rx_using; + reg_addr = + atl1e_rx_page_vld_regs[que][rx_using]; + AT_WRITE_REGB(&adapter->hw, reg_addr, 1); + rx_page_desc[que].rx_using ^= 1; + rx_page = atl1e_get_rx_page(adapter, que); + } + write_offset = *(rx_page->write_offset_addr); + } while (rx_page->read_offset < write_offset); + } + + return; + +fatal_err: + if (!test_bit(__AT_DOWN, &adapter->flags)) + schedule_work(&adapter->reset_task); +} + +/* + * atl1e_clean - NAPI Rx polling callback + * @adapter: board private structure + */ +static int atl1e_clean(struct napi_struct *napi, int budget) +{ + struct atl1e_adapter *adapter = + container_of(napi, struct atl1e_adapter, napi); + struct net_device *netdev = adapter->netdev; + struct pci_dev *pdev = adapter->pdev; + u32 imr_data; + int work_done = 0; + + /* Keep link state information with original netdev */ + if (!netif_carrier_ok(adapter->netdev)) + goto quit_polling; + + atl1e_clean_rx_irq(adapter, 0, &work_done, budget); + + /* If no Tx and not enough Rx work done, exit the polling mode */ + if (work_done < budget) { +quit_polling: + netif_rx_complete(netdev, napi); + imr_data = AT_READ_REG(&adapter->hw, REG_IMR); + AT_WRITE_REG(&adapter->hw, REG_IMR, imr_data | ISR_RX_EVENT); + /* test debug */ + if (test_bit(__AT_DOWN, &adapter->flags)) { + atomic_dec(&adapter->irq_sem); + dev_err(&pdev->dev, + "atl1e_clean is called when AT_DOWN\n"); + } + /* reenable RX intr */ + /*atl1e_irq_enable(adapter); */ + + } + return work_done; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER + +/* + * Polling 'interrupt' - used by things like netconsole to send skbs + * without having to re-enable interrupts. It's not called while + * the interrupt routine is executing. + */ +static void atl1e_netpoll(struct net_device *netdev) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + + disable_irq(adapter->pdev->irq); + atl1e_intr(adapter->pdev->irq, netdev); + enable_irq(adapter->pdev->irq); +} +#endif + +static inline u16 atl1e_tpd_avail(struct atl1e_adapter *adapter) +{ + struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; + u16 next_to_use = 0; + u16 next_to_clean = 0; + + next_to_clean = atomic_read(&tx_ring->next_to_clean); + next_to_use = tx_ring->next_to_use; + + return (u16)(next_to_clean > next_to_use) ? + (next_to_clean - next_to_use - 1) : + (tx_ring->count + next_to_clean - next_to_use - 1); +} + +/* + * get next usable tpd + * Note: should call atl1e_tdp_avail to make sure + * there is enough tpd to use + */ +static struct atl1e_tpd_desc *atl1e_get_tpd(struct atl1e_adapter *adapter) +{ + struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; + u16 next_to_use = 0; + + next_to_use = tx_ring->next_to_use; + if (++tx_ring->next_to_use == tx_ring->count) + tx_ring->next_to_use = 0; + + memset(&tx_ring->desc[next_to_use], 0, sizeof(struct atl1e_tpd_desc)); + return (struct atl1e_tpd_desc *)&tx_ring->desc[next_to_use]; +} + +static struct atl1e_tx_buffer * +atl1e_get_tx_buffer(struct atl1e_adapter *adapter, struct atl1e_tpd_desc *tpd) +{ + struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; + + return &tx_ring->tx_buffer[tpd - tx_ring->desc]; +} + +/* Calculate the transmit packet descript needed*/ +static u16 atl1e_cal_tdp_req(const struct sk_buff *skb) +{ + int i = 0; + u16 tpd_req = 1; + u16 fg_size = 0; + u16 proto_hdr_len = 0; + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + fg_size = skb_shinfo(skb)->frags[i].size; + tpd_req += ((fg_size + MAX_TX_BUF_LEN - 1) >> MAX_TX_BUF_SHIFT); + } + + if (skb_is_gso(skb)) { + if (skb->protocol == ntohs(ETH_P_IP) || + (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6)) { + proto_hdr_len = skb_transport_offset(skb) + + tcp_hdrlen(skb); + if (proto_hdr_len < skb_headlen(skb)) { + tpd_req += ((skb_headlen(skb) - proto_hdr_len + + MAX_TX_BUF_LEN - 1) >> + MAX_TX_BUF_SHIFT); + } + } + + } + return tpd_req; +} + +static int atl1e_tso_csum(struct atl1e_adapter *adapter, + struct sk_buff *skb, struct atl1e_tpd_desc *tpd) +{ + struct pci_dev *pdev = adapter->pdev; + u8 hdr_len; + u32 real_len; + unsigned short offload_type; + int err; + + if (skb_is_gso(skb)) { + if (skb_header_cloned(skb)) { + err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); + if (unlikely(err)) + return -1; + } + offload_type = skb_shinfo(skb)->gso_type; + + if (offload_type & SKB_GSO_TCPV4) { + real_len = (((unsigned char *)ip_hdr(skb) - skb->data) + + ntohs(ip_hdr(skb)->tot_len)); + + if (real_len < skb->len) + pskb_trim(skb, real_len); + + hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb)); + if (unlikely(skb->len == hdr_len)) { + /* only xsum need */ + dev_warn(&pdev->dev, + "IPV4 tso with zero data??\n"); + goto check_sum; + } else { + ip_hdr(skb)->check = 0; + ip_hdr(skb)->tot_len = 0; + tcp_hdr(skb)->check = ~csum_tcpudp_magic( + ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, + 0, IPPROTO_TCP, 0); + tpd->word3 |= (ip_hdr(skb)->ihl & + TDP_V4_IPHL_MASK) << + TPD_V4_IPHL_SHIFT; + tpd->word3 |= ((tcp_hdrlen(skb) >> 2) & + TPD_TCPHDRLEN_MASK) << + TPD_TCPHDRLEN_SHIFT; + tpd->word3 |= ((skb_shinfo(skb)->gso_size) & + TPD_MSS_MASK) << TPD_MSS_SHIFT; + tpd->word3 |= 1 << TPD_SEGMENT_EN_SHIFT; + } + return 0; + } + + if (offload_type & SKB_GSO_TCPV6) { + real_len = (((unsigned char *)ipv6_hdr(skb) - skb->data) + + ntohs(ipv6_hdr(skb)->payload_len)); + if (real_len < skb->len) + pskb_trim(skb, real_len); + + /* check payload == 0 byte ? */ + hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb)); + if (unlikely(skb->len == hdr_len)) { + /* only xsum need */ + dev_warn(&pdev->dev, + "IPV6 tso with zero data??\n"); + goto check_sum; + } else { + tcp_hdr(skb)->check = ~csum_ipv6_magic( + &ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, + 0, IPPROTO_TCP, 0); + tpd->word3 |= 1 << TPD_IP_VERSION_SHIFT; + hdr_len >>= 1; + tpd->word3 |= (hdr_len & TPD_V6_IPHLLO_MASK) << + TPD_V6_IPHLLO_SHIFT; + tpd->word3 |= ((hdr_len >> 3) & + TPD_V6_IPHLHI_MASK) << + TPD_V6_IPHLHI_SHIFT; + tpd->word3 |= (tcp_hdrlen(skb) >> 2 & + TPD_TCPHDRLEN_MASK) << + TPD_TCPHDRLEN_SHIFT; + tpd->word3 |= ((skb_shinfo(skb)->gso_size) & + TPD_MSS_MASK) << TPD_MSS_SHIFT; + tpd->word3 |= 1 << TPD_SEGMENT_EN_SHIFT; + } + } + return 0; + } + +check_sum: + if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { + u8 css, cso; + + cso = skb_transport_offset(skb); + if (unlikely(cso & 0x1)) { + dev_err(&adapter->pdev->dev, + "pay load offset should not ant event number\n"); + return -1; + } else { + css = cso + skb->csum_offset; + tpd->word3 |= (cso & TPD_PLOADOFFSET_MASK) << + TPD_PLOADOFFSET_SHIFT; + tpd->word3 |= (css & TPD_CCSUMOFFSET_MASK) << + TPD_CCSUMOFFSET_SHIFT; + tpd->word3 |= 1 << TPD_CC_SEGMENT_EN_SHIFT; + } + } + + return 0; +} + +static void atl1e_tx_map(struct atl1e_adapter *adapter, + struct sk_buff *skb, struct atl1e_tpd_desc *tpd) +{ + struct atl1e_tpd_desc *use_tpd = NULL; + struct atl1e_tx_buffer *tx_buffer = NULL; + u16 buf_len = skb->len - skb->data_len; + u16 map_len = 0; + u16 mapped_len = 0; + u16 hdr_len = 0; + u16 nr_frags; + u16 f; + int segment; + + nr_frags = skb_shinfo(skb)->nr_frags; + segment = (tpd->word3 >> TPD_SEGMENT_EN_SHIFT) & TPD_SEGMENT_EN_MASK; + if (segment) { + /* TSO */ + map_len = hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + use_tpd = tpd; + + tx_buffer = atl1e_get_tx_buffer(adapter, use_tpd); + tx_buffer->length = map_len; + tx_buffer->dma = pci_map_single(adapter->pdev, + skb->data, hdr_len, PCI_DMA_TODEVICE); + mapped_len += map_len; + use_tpd->buffer_addr = cpu_to_le64(tx_buffer->dma); + use_tpd->word2 = (use_tpd->word2 & (~TPD_BUFLEN_MASK)) | + ((cpu_to_le32(tx_buffer->length) & + TPD_BUFLEN_MASK) << TPD_BUFLEN_SHIFT); + } + + while (mapped_len < buf_len) { + /* mapped_len == 0, means we should use the first tpd, + which is given by caller */ + if (mapped_len == 0) { + use_tpd = tpd; + } else { + use_tpd = atl1e_get_tpd(adapter); + memcpy(use_tpd, tpd, sizeof(struct atl1e_tpd_desc)); + } + tx_buffer = atl1e_get_tx_buffer(adapter, use_tpd); + tx_buffer->skb = NULL; + + tx_buffer->length = map_len = + ((buf_len - mapped_len) >= MAX_TX_BUF_LEN) ? + MAX_TX_BUF_LEN : (buf_len - mapped_len); + tx_buffer->dma = + pci_map_single(adapter->pdev, skb->data + mapped_len, + map_len, PCI_DMA_TODEVICE); + mapped_len += map_len; + use_tpd->buffer_addr = cpu_to_le64(tx_buffer->dma); + use_tpd->word2 = (use_tpd->word2 & (~TPD_BUFLEN_MASK)) | + ((cpu_to_le32(tx_buffer->length) & + TPD_BUFLEN_MASK) << TPD_BUFLEN_SHIFT); + } + + for (f = 0; f < nr_frags; f++) { + struct skb_frag_struct *frag; + u16 i; + u16 seg_num; + + frag = &skb_shinfo(skb)->frags[f]; + buf_len = frag->size; + + seg_num = (buf_len + MAX_TX_BUF_LEN - 1) / MAX_TX_BUF_LEN; + for (i = 0; i < seg_num; i++) { + use_tpd = atl1e_get_tpd(adapter); + memcpy(use_tpd, tpd, sizeof(struct atl1e_tpd_desc)); + + tx_buffer = atl1e_get_tx_buffer(adapter, use_tpd); + if (tx_buffer->skb) + BUG(); + + tx_buffer->skb = NULL; + tx_buffer->length = + (buf_len > MAX_TX_BUF_LEN) ? + MAX_TX_BUF_LEN : buf_len; + buf_len -= tx_buffer->length; + + tx_buffer->dma = + pci_map_page(adapter->pdev, frag->page, + frag->page_offset + + (i * MAX_TX_BUF_LEN), + tx_buffer->length, + PCI_DMA_TODEVICE); + use_tpd->buffer_addr = cpu_to_le64(tx_buffer->dma); + use_tpd->word2 = (use_tpd->word2 & (~TPD_BUFLEN_MASK)) | + ((cpu_to_le32(tx_buffer->length) & + TPD_BUFLEN_MASK) << TPD_BUFLEN_SHIFT); + } + } + + if ((tpd->word3 >> TPD_SEGMENT_EN_SHIFT) & TPD_SEGMENT_EN_MASK) + /* note this one is a tcp header */ + tpd->word3 |= 1 << TPD_HDRFLAG_SHIFT; + /* The last tpd */ + + use_tpd->word3 |= 1 << TPD_EOP_SHIFT; + /* The last buffer info contain the skb address, + so it will be free after unmap */ + tx_buffer->skb = skb; +} + +static void atl1e_tx_queue(struct atl1e_adapter *adapter, u16 count, + struct atl1e_tpd_desc *tpd) +{ + struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; + /* Force memory writes to complete before letting h/w + * know there are new descriptors to fetch. (Only + * applicable for weak-ordered memory model archs, + * such as IA-64). */ + wmb(); + AT_WRITE_REG(&adapter->hw, REG_MB_TPD_PROD_IDX, tx_ring->next_to_use); +} + +static int atl1e_xmit_frame(struct sk_buff *skb, struct net_device *netdev) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + unsigned long flags; + u16 tpd_req = 1; + struct atl1e_tpd_desc *tpd; + + if (test_bit(__AT_DOWN, &adapter->flags)) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + if (unlikely(skb->len <= 0)) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + tpd_req = atl1e_cal_tdp_req(skb); + if (!spin_trylock_irqsave(&adapter->tx_lock, flags)) + return NETDEV_TX_LOCKED; + + if (atl1e_tpd_avail(adapter) < tpd_req) { + /* no enough descriptor, just stop queue */ + netif_stop_queue(netdev); + spin_unlock_irqrestore(&adapter->tx_lock, flags); + return NETDEV_TX_BUSY; + } + + tpd = atl1e_get_tpd(adapter); + + if (unlikely(adapter->vlgrp && vlan_tx_tag_present(skb))) { + u16 vlan_tag = vlan_tx_tag_get(skb); + u16 atl1e_vlan_tag; + + tpd->word3 |= 1 << TPD_INS_VL_TAG_SHIFT; + AT_VLAN_TAG_TO_TPD_TAG(vlan_tag, atl1e_vlan_tag); + tpd->word2 |= (atl1e_vlan_tag & TPD_VLANTAG_MASK) << + TPD_VLAN_SHIFT; + } + + if (skb->protocol == ntohs(ETH_P_8021Q)) + tpd->word3 |= 1 << TPD_VL_TAGGED_SHIFT; + + if (skb_network_offset(skb) != ETH_HLEN) + tpd->word3 |= 1 << TPD_ETHTYPE_SHIFT; /* 802.3 frame */ + + /* do TSO and check sum */ + if (atl1e_tso_csum(adapter, skb, tpd) != 0) { + spin_unlock_irqrestore(&adapter->tx_lock, flags); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + atl1e_tx_map(adapter, skb, tpd); + atl1e_tx_queue(adapter, tpd_req, tpd); + + netdev->trans_start = jiffies; + spin_unlock_irqrestore(&adapter->tx_lock, flags); + return NETDEV_TX_OK; +} + +static void atl1e_free_irq(struct atl1e_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + free_irq(adapter->pdev->irq, netdev); + + if (adapter->have_msi) + pci_disable_msi(adapter->pdev); +} + +static int atl1e_request_irq(struct atl1e_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + struct net_device *netdev = adapter->netdev; + int flags = 0; + int err = 0; + + adapter->have_msi = true; + err = pci_enable_msi(adapter->pdev); + if (err) { + dev_dbg(&pdev->dev, + "Unable to allocate MSI interrupt Error: %d\n", err); + adapter->have_msi = false; + } else + netdev->irq = pdev->irq; + + + if (!adapter->have_msi) + flags |= IRQF_SHARED; + err = request_irq(adapter->pdev->irq, &atl1e_intr, flags, + netdev->name, netdev); + if (err) { + dev_dbg(&pdev->dev, + "Unable to allocate interrupt Error: %d\n", err); + if (adapter->have_msi) + pci_disable_msi(adapter->pdev); + return err; + } + dev_dbg(&pdev->dev, "atl1e_request_irq OK\n"); + return err; +} + +int atl1e_up(struct atl1e_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + int err = 0; + u32 val; + + /* hardware has been reset, we need to reload some things */ + err = atl1e_init_hw(&adapter->hw); + if (err) { + err = -EIO; + return err; + } + atl1e_init_ring_ptrs(adapter); + atl1e_set_multi(netdev); + atl1e_restore_vlan(adapter); + + if (atl1e_configure(adapter)) { + err = -EIO; + goto err_up; + } + + clear_bit(__AT_DOWN, &adapter->flags); + napi_enable(&adapter->napi); + atl1e_irq_enable(adapter); + val = AT_READ_REG(&adapter->hw, REG_MASTER_CTRL); + AT_WRITE_REG(&adapter->hw, REG_MASTER_CTRL, + val | MASTER_CTRL_MANUAL_INT); + +err_up: + return err; +} + +void atl1e_down(struct atl1e_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + /* signal that we're down so the interrupt handler does not + * reschedule our watchdog timer */ + set_bit(__AT_DOWN, &adapter->flags); + +#ifdef NETIF_F_LLTX + netif_stop_queue(netdev); +#else + netif_tx_disable(netdev); +#endif + + /* reset MAC to disable all RX/TX */ + atl1e_reset_hw(&adapter->hw); + msleep(1); + + napi_disable(&adapter->napi); + atl1e_del_timer(adapter); + atl1e_irq_disable(adapter); + + netif_carrier_off(netdev); + adapter->link_speed = SPEED_0; + adapter->link_duplex = -1; + atl1e_clean_tx_ring(adapter); + atl1e_clean_rx_ring(adapter); +} + +/* + * atl1e_open - Called when a network interface is made active + * @netdev: network interface device structure + * + * Returns 0 on success, negative value on failure + * + * The open entry point is called when a network interface is made + * active by the system (IFF_UP). At this point all resources needed + * for transmit and receive operations are allocated, the interrupt + * handler is registered with the OS, the watchdog timer is started, + * and the stack is notified that the interface is ready. + */ +static int atl1e_open(struct net_device *netdev) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + int err; + + /* disallow open during test */ + if (test_bit(__AT_TESTING, &adapter->flags)) + return -EBUSY; + + /* allocate rx/tx dma buffer & descriptors */ + atl1e_init_ring_resources(adapter); + err = atl1e_setup_ring_resources(adapter); + if (unlikely(err)) + return err; + + err = atl1e_request_irq(adapter); + if (unlikely(err)) + goto err_req_irq; + + err = atl1e_up(adapter); + if (unlikely(err)) + goto err_up; + + return 0; + +err_up: + atl1e_free_irq(adapter); +err_req_irq: + atl1e_free_ring_resources(adapter); + atl1e_reset_hw(&adapter->hw); + + return err; +} + +/* + * atl1e_close - Disables a network interface + * @netdev: network interface device structure + * + * Returns 0, this is not allowed to fail + * + * The close entry point is called when an interface is de-activated + * by the OS. The hardware is still under the drivers control, but + * needs to be disabled. A global MAC reset is issued to stop the + * hardware, and all transmit and receive resources are freed. + */ +static int atl1e_close(struct net_device *netdev) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + + WARN_ON(test_bit(__AT_RESETTING, &adapter->flags)); + atl1e_down(adapter); + atl1e_free_irq(adapter); + atl1e_free_ring_resources(adapter); + + return 0; +} + +static int atl1e_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1e_adapter *adapter = netdev_priv(netdev); + struct atl1e_hw *hw = &adapter->hw; + u32 ctrl = 0; + u32 mac_ctrl_data = 0; + u32 wol_ctrl_data = 0; + u16 mii_advertise_data = 0; + u16 mii_bmsr_data = 0; + u16 mii_intr_status_data = 0; + u32 wufc = adapter->wol; + u32 i; +#ifdef CONFIG_PM + int retval = 0; +#endif + + if (netif_running(netdev)) { + WARN_ON(test_bit(__AT_RESETTING, &adapter->flags)); + atl1e_down(adapter); + } + netif_device_detach(netdev); + +#ifdef CONFIG_PM + retval = pci_save_state(pdev); + if (retval) + return retval; +#endif + + if (wufc) { + /* get link status */ + atl1e_read_phy_reg(hw, MII_BMSR, (u16 *)&mii_bmsr_data); + atl1e_read_phy_reg(hw, MII_BMSR, (u16 *)&mii_bmsr_data); + + mii_advertise_data = MII_AR_10T_HD_CAPS; + + if ((atl1e_write_phy_reg(hw, MII_AT001_CR, 0) != 0) || + (atl1e_write_phy_reg(hw, + MII_ADVERTISE, mii_advertise_data) != 0) || + (atl1e_phy_commit(hw)) != 0) { + dev_dbg(&pdev->dev, "set phy register failed\n"); + goto wol_dis; + } + + hw->phy_configured = false; /* re-init PHY when resume */ + + /* turn on magic packet wol */ + if (wufc & AT_WUFC_MAG) + wol_ctrl_data |= WOL_MAGIC_EN | WOL_MAGIC_PME_EN; + + if (wufc & AT_WUFC_LNKC) { + /* if orignal link status is link, just wait for retrive link */ + if (mii_bmsr_data & BMSR_LSTATUS) { + for (i = 0; i < AT_SUSPEND_LINK_TIMEOUT; i++) { + msleep(100); + atl1e_read_phy_reg(hw, MII_BMSR, + (u16 *)&mii_bmsr_data); + if (mii_bmsr_data & BMSR_LSTATUS) + break; + } + + if ((mii_bmsr_data & BMSR_LSTATUS) == 0) + dev_dbg(&pdev->dev, + "%s: Link may change" + "when suspend\n", + atl1e_driver_name); + } + wol_ctrl_data |= WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN; + /* only link up can wake up */ + if (atl1e_write_phy_reg(hw, MII_INT_CTRL, 0x400) != 0) { + dev_dbg(&pdev->dev, "%s: read write phy " + "register failed.\n", + atl1e_driver_name); + goto wol_dis; + } + } + /* clear phy interrupt */ + atl1e_read_phy_reg(hw, MII_INT_STATUS, &mii_intr_status_data); + /* Config MAC Ctrl register */ + mac_ctrl_data = MAC_CTRL_RX_EN; + /* set to 10/100M halt duplex */ + mac_ctrl_data |= MAC_CTRL_SPEED_10_100 << MAC_CTRL_SPEED_SHIFT; + mac_ctrl_data |= (((u32)adapter->hw.preamble_len & + MAC_CTRL_PRMLEN_MASK) << + MAC_CTRL_PRMLEN_SHIFT); + + if (adapter->vlgrp) + mac_ctrl_data |= MAC_CTRL_RMV_VLAN; + + /* magic packet maybe Broadcast&multicast&Unicast frame */ + if (wufc & AT_WUFC_MAG) + mac_ctrl_data |= MAC_CTRL_BC_EN; + + dev_dbg(&pdev->dev, + "%s: suspend MAC=0x%x\n", + atl1e_driver_name, mac_ctrl_data); + + AT_WRITE_REG(hw, REG_WOL_CTRL, wol_ctrl_data); + AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data); + /* pcie patch */ + ctrl = AT_READ_REG(hw, REG_PCIE_PHYMISC); + ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; + AT_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); + pci_enable_wake(pdev, pci_choose_state(pdev, state), 1); + goto suspend_exit; + } +wol_dis: + + /* WOL disabled */ + AT_WRITE_REG(hw, REG_WOL_CTRL, 0); + + /* pcie patch */ + ctrl = AT_READ_REG(hw, REG_PCIE_PHYMISC); + ctrl |= PCIE_PHYMISC_FORCE_RCV_DET; + AT_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl); + + atl1e_force_ps(hw); + hw->phy_configured = false; /* re-init PHY when resume */ + + pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); + +suspend_exit: + + if (netif_running(netdev)) + atl1e_free_irq(adapter); + + pci_disable_device(pdev); + + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +#ifdef CONFIG_PM +static int atl1e_resume(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1e_adapter *adapter = netdev_priv(netdev); + u32 err; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + err = pci_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "ATL1e: Cannot enable PCI" + " device from suspend\n"); + return err; + } + + pci_set_master(pdev); + + AT_READ_REG(&adapter->hw, REG_WOL_CTRL); /* clear WOL status */ + + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); + + AT_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0); + + if (netif_running(netdev)) + err = atl1e_request_irq(adapter); + if (err) + return err; + + atl1e_reset_hw(&adapter->hw); + + if (netif_running(netdev)) + atl1e_up(adapter); + + netif_device_attach(netdev); + + return 0; +} +#endif + +static void atl1e_shutdown(struct pci_dev *pdev) +{ + atl1e_suspend(pdev, PMSG_SUSPEND); +} + +static int atl1e_init_netdev(struct net_device *netdev, struct pci_dev *pdev) +{ + SET_NETDEV_DEV(netdev, &pdev->dev); + pci_set_drvdata(pdev, netdev); + + netdev->irq = pdev->irq; + netdev->open = &atl1e_open; + netdev->stop = &atl1e_close; + netdev->hard_start_xmit = &atl1e_xmit_frame; + netdev->get_stats = &atl1e_get_stats; + netdev->set_multicast_list = &atl1e_set_multi; + netdev->set_mac_address = &atl1e_set_mac_addr; + netdev->change_mtu = &atl1e_change_mtu; + netdev->do_ioctl = &atl1e_ioctl; + netdev->tx_timeout = &atl1e_tx_timeout; + netdev->watchdog_timeo = AT_TX_WATCHDOG; + netdev->vlan_rx_register = atl1e_vlan_rx_register; +#ifdef CONFIG_NET_POLL_CONTROLLER + netdev->poll_controller = atl1e_netpoll; +#endif + atl1e_set_ethtool_ops(netdev); + + netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM | + NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; + netdev->features |= NETIF_F_LLTX; + netdev->features |= NETIF_F_TSO; + netdev->features |= NETIF_F_TSO6; + + return 0; +} + +/* + * atl1e_probe - Device Initialization Routine + * @pdev: PCI device information struct + * @ent: entry in atl1e_pci_tbl + * + * Returns 0 on success, negative on failure + * + * atl1e_probe initializes an adapter identified by a pci_dev structure. + * The OS initialization, configuring of the adapter private structure, + * and a hardware reset occur. + */ +static int __devinit atl1e_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *netdev; + struct atl1e_adapter *adapter = NULL; + static int cards_found; + + int err = 0; + + err = pci_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "cannot enable PCI device\n"); + return err; + } + + /* + * The atl1e chip can DMA to 64-bit addresses, but it uses a single + * shared register for the high 32 bits, so only a single, aligned, + * 4 GB physical address range can be used at a time. + * + * Supporting 64-bit DMA on this hardware is more trouble than it's + * worth. It is far easier to limit to 32-bit DMA than update + * various kernel subsystems to support the mechanics required by a + * fixed-high-32-bit system. + */ + if ((pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0) || + (pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK) != 0)) { + dev_err(&pdev->dev, "No usable DMA configuration,aborting\n"); + goto err_dma; + } + + err = pci_request_regions(pdev, atl1e_driver_name); + if (err) { + dev_err(&pdev->dev, "cannot obtain PCI resources\n"); + goto err_pci_reg; + } + + pci_set_master(pdev); + + netdev = alloc_etherdev(sizeof(struct atl1e_adapter)); + if (netdev == NULL) { + err = -ENOMEM; + dev_err(&pdev->dev, "etherdev alloc failed\n"); + goto err_alloc_etherdev; + } + + err = atl1e_init_netdev(netdev, pdev); + if (err) { + dev_err(&pdev->dev, "init netdevice failed\n"); + goto err_init_netdev; + } + adapter = netdev_priv(netdev); + adapter->bd_number = cards_found; + adapter->netdev = netdev; + adapter->pdev = pdev; + adapter->hw.adapter = adapter; + adapter->hw.hw_addr = pci_iomap(pdev, BAR_0, 0); + if (!adapter->hw.hw_addr) { + err = -EIO; + dev_err(&pdev->dev, "cannot map device registers\n"); + goto err_ioremap; + } + netdev->base_addr = (unsigned long)adapter->hw.hw_addr; + + /* init mii data */ + adapter->mii.dev = netdev; + adapter->mii.mdio_read = atl1e_mdio_read; + adapter->mii.mdio_write = atl1e_mdio_write; + adapter->mii.phy_id_mask = 0x1f; + adapter->mii.reg_num_mask = MDIO_REG_ADDR_MASK; + + netif_napi_add(netdev, &adapter->napi, atl1e_clean, 64); + + init_timer(&adapter->phy_config_timer); + adapter->phy_config_timer.function = &atl1e_phy_config; + adapter->phy_config_timer.data = (unsigned long) adapter; + + /* get user settings */ + atl1e_check_options(adapter); + /* + * Mark all PCI regions associated with PCI device + * pdev as being reserved by owner atl1e_driver_name + * Enables bus-mastering on the device and calls + * pcibios_set_master to do the needed arch specific settings + */ + atl1e_setup_pcicmd(pdev); + /* setup the private structure */ + err = atl1e_sw_init(adapter); + if (err) { + dev_err(&pdev->dev, "net device private data init failed\n"); + goto err_sw_init; + } + + /* Init GPHY as early as possible due to power saving issue */ + spin_lock(&adapter->mdio_lock); + atl1e_phy_init(&adapter->hw); + spin_unlock(&adapter->mdio_lock); + /* reset the controller to + * put the device in a known good starting state */ + err = atl1e_reset_hw(&adapter->hw); + if (err) { + err = -EIO; + goto err_reset; + } + + if (atl1e_read_mac_addr(&adapter->hw) != 0) { + err = -EIO; + dev_err(&pdev->dev, "get mac address failed\n"); + goto err_eeprom; + } + + memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len); + memcpy(netdev->perm_addr, adapter->hw.mac_addr, netdev->addr_len); + dev_dbg(&pdev->dev, "mac address : %02x-%02x-%02x-%02x-%02x-%02x\n", + adapter->hw.mac_addr[0], adapter->hw.mac_addr[1], + adapter->hw.mac_addr[2], adapter->hw.mac_addr[3], + adapter->hw.mac_addr[4], adapter->hw.mac_addr[5]); + + INIT_WORK(&adapter->reset_task, atl1e_reset_task); + INIT_WORK(&adapter->link_chg_task, atl1e_link_chg_task); + err = register_netdev(netdev); + if (err) { + dev_err(&pdev->dev, "register netdevice failed\n"); + goto err_register; + } + + /* assume we have no link for now */ + netif_stop_queue(netdev); + netif_carrier_off(netdev); + + cards_found++; + + return 0; + +err_reset: +err_register: +err_sw_init: +err_eeprom: + iounmap(adapter->hw.hw_addr); +err_init_netdev: +err_ioremap: + free_netdev(netdev); +err_alloc_etherdev: + pci_release_regions(pdev); +err_pci_reg: +err_dma: + pci_disable_device(pdev); + return err; +} + +/* + * atl1e_remove - Device Removal Routine + * @pdev: PCI device information struct + * + * atl1e_remove is called by the PCI subsystem to alert the driver + * that it should release a PCI device. The could be caused by a + * Hot-Plug event, or because the driver is going to be removed from + * memory. + */ +static void __devexit atl1e_remove(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1e_adapter *adapter = netdev_priv(netdev); + + /* + * flush_scheduled work may reschedule our watchdog task, so + * explicitly disable watchdog tasks from being rescheduled + */ + set_bit(__AT_DOWN, &adapter->flags); + + atl1e_del_timer(adapter); + atl1e_cancel_work(adapter); + + unregister_netdev(netdev); + atl1e_free_ring_resources(adapter); + atl1e_force_ps(&adapter->hw); + iounmap(adapter->hw.hw_addr); + pci_release_regions(pdev); + free_netdev(netdev); + pci_disable_device(pdev); +} + +/* + * atl1e_io_error_detected - called when PCI error is detected + * @pdev: Pointer to PCI device + * @state: The current pci connection state + * + * This function is called after a PCI bus error affecting + * this device has been detected. + */ +static pci_ers_result_t +atl1e_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1e_adapter *adapter = netdev->priv; + + netif_device_detach(netdev); + + if (netif_running(netdev)) + atl1e_down(adapter); + + pci_disable_device(pdev); + + /* Request a slot slot reset. */ + return PCI_ERS_RESULT_NEED_RESET; +} + +/* + * atl1e_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. Implementation + * resembles the first-half of the e1000_resume routine. + */ +static pci_ers_result_t atl1e_io_slot_reset(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1e_adapter *adapter = netdev->priv; + + if (pci_enable_device(pdev)) { + dev_err(&pdev->dev, + "ATL1e: Cannot re-enable PCI device after reset.\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + pci_set_master(pdev); + + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); + + atl1e_reset_hw(&adapter->hw); + + return PCI_ERS_RESULT_RECOVERED; +} + +/* + * atl1e_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. Implementation resembles the + * second-half of the atl1e_resume routine. + */ +static void atl1e_io_resume(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1e_adapter *adapter = netdev->priv; + + if (netif_running(netdev)) { + if (atl1e_up(adapter)) { + dev_err(&pdev->dev, + "ATL1e: can't bring device back up after reset\n"); + return; + } + } + + netif_device_attach(netdev); +} + +static struct pci_error_handlers atl1e_err_handler = { + .error_detected = atl1e_io_error_detected, + .slot_reset = atl1e_io_slot_reset, + .resume = atl1e_io_resume, +}; + +static struct pci_driver atl1e_driver = { + .name = atl1e_driver_name, + .id_table = atl1e_pci_tbl, + .probe = atl1e_probe, + .remove = __devexit_p(atl1e_remove), + /* Power Managment Hooks */ +#ifdef CONFIG_PM + .suspend = atl1e_suspend, + .resume = atl1e_resume, +#endif + .shutdown = atl1e_shutdown, + .err_handler = &atl1e_err_handler +}; + +/* + * atl1e_init_module - Driver Registration Routine + * + * atl1e_init_module is the first routine called when the driver is + * loaded. All it does is register with the PCI subsystem. + */ +static int __init atl1e_init_module(void) +{ + return pci_register_driver(&atl1e_driver); +} + +/* + * atl1e_exit_module - Driver Exit Cleanup Routine + * + * atl1e_exit_module is called just before the driver is removed + * from memory. + */ +static void __exit atl1e_exit_module(void) +{ + pci_unregister_driver(&atl1e_driver); +} + +module_init(atl1e_init_module); +module_exit(atl1e_exit_module); diff --git a/drivers/net/atl1e/atl1e_param.c b/drivers/net/atl1e/atl1e_param.c new file mode 100644 index 000000000000..f72abb34b0cd --- /dev/null +++ b/drivers/net/atl1e/atl1e_param.c @@ -0,0 +1,263 @@ +/* + * Copyright(c) 2007 Atheros Corporation. All rights reserved. + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * 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/netdevice.h> + +#include "atl1e.h" + +/* This is the only thing that needs to be changed to adjust the + * maximum number of ports that the driver can manage. + */ + +#define ATL1E_MAX_NIC 32 + +#define OPTION_UNSET -1 +#define OPTION_DISABLED 0 +#define OPTION_ENABLED 1 + +/* All parameters are treated the same, as an integer array of values. + * This macro just reduces the need to repeat the same declaration code + * over and over (plus this helps to avoid typo bugs). + */ +#define ATL1E_PARAM_INIT { [0 ... ATL1E_MAX_NIC] = OPTION_UNSET } + +#define ATL1E_PARAM(x, desc) \ + static int __devinitdata x[ATL1E_MAX_NIC + 1] = ATL1E_PARAM_INIT; \ + static int num_##x; \ + module_param_array_named(x, x, int, &num_##x, 0); \ + MODULE_PARM_DESC(x, desc); + +/* Transmit Memory count + * + * Valid Range: 64-2048 + * + * Default Value: 128 + */ +#define ATL1E_MIN_TX_DESC_CNT 32 +#define ATL1E_MAX_TX_DESC_CNT 1020 +#define ATL1E_DEFAULT_TX_DESC_CNT 128 +ATL1E_PARAM(tx_desc_cnt, "Transmit description count"); + +/* Receive Memory Block Count + * + * Valid Range: 16-512 + * + * Default Value: 128 + */ +#define ATL1E_MIN_RX_MEM_SIZE 8 /* 8KB */ +#define ATL1E_MAX_RX_MEM_SIZE 1024 /* 1MB */ +#define ATL1E_DEFAULT_RX_MEM_SIZE 256 /* 128KB */ +ATL1E_PARAM(rx_mem_size, "memory size of rx buffer(KB)"); + +/* User Specified MediaType Override + * + * Valid Range: 0-5 + * - 0 - auto-negotiate at all supported speeds + * - 1 - only link at 100Mbps Full Duplex + * - 2 - only link at 100Mbps Half Duplex + * - 3 - only link at 10Mbps Full Duplex + * - 4 - only link at 10Mbps Half Duplex + * Default Value: 0 + */ + +ATL1E_PARAM(media_type, "MediaType Select"); + +/* Interrupt Moderate Timer in units of 2 us + * + * Valid Range: 10-65535 + * + * Default Value: 45000(90ms) + */ +#define INT_MOD_DEFAULT_CNT 100 /* 200us */ +#define INT_MOD_MAX_CNT 65000 +#define INT_MOD_MIN_CNT 50 +ATL1E_PARAM(int_mod_timer, "Interrupt Moderator Timer"); + +#define AUTONEG_ADV_DEFAULT 0x2F +#define AUTONEG_ADV_MASK 0x2F +#define FLOW_CONTROL_DEFAULT FLOW_CONTROL_FULL + +#define FLASH_VENDOR_DEFAULT 0 +#define FLASH_VENDOR_MIN 0 +#define FLASH_VENDOR_MAX 2 + +struct atl1e_option { + enum { enable_option, range_option, list_option } type; + char *name; + char *err; + int def; + union { + struct { /* range_option info */ + int min; + int max; + } r; + struct { /* list_option info */ + int nr; + struct atl1e_opt_list { int i; char *str; } *p; + } l; + } arg; +}; + +static int __devinit atl1e_validate_option(int *value, struct atl1e_option *opt, struct pci_dev *pdev) +{ + if (*value == OPTION_UNSET) { + *value = opt->def; + return 0; + } + + switch (opt->type) { + case enable_option: + switch (*value) { + case OPTION_ENABLED: + dev_info(&pdev->dev, "%s Enabled\n", opt->name); + return 0; + case OPTION_DISABLED: + dev_info(&pdev->dev, "%s Disabled\n", opt->name); + return 0; + } + break; + case range_option: + if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) { + dev_info(&pdev->dev, "%s set to %i\n", opt->name, *value); + return 0; + } + break; + case list_option:{ + int i; + struct atl1e_opt_list *ent; + + for (i = 0; i < opt->arg.l.nr; i++) { + ent = &opt->arg.l.p[i]; + if (*value == ent->i) { + if (ent->str[0] != '\0') + dev_info(&pdev->dev, "%s\n", + ent->str); + return 0; + } + } + break; + } + default: + BUG(); + } + + dev_info(&pdev->dev, "Invalid %s specified (%i) %s\n", + opt->name, *value, opt->err); + *value = opt->def; + return -1; +} + +/* + * atl1e_check_options - Range Checking for Command Line Parameters + * @adapter: board private structure + * + * This routine checks all command line parameters for valid user + * input. If an invalid value is given, or if no user specified + * value exists, a default value is used. The final value is stored + * in a variable in the adapter structure. + */ +void __devinit atl1e_check_options(struct atl1e_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + int bd = adapter->bd_number; + if (bd >= ATL1E_MAX_NIC) { + dev_notice(&pdev->dev, "no configuration for board #%i\n", bd); + dev_notice(&pdev->dev, "Using defaults for all values\n"); + } + + { /* Transmit Ring Size */ + struct atl1e_option opt = { + .type = range_option, + .name = "Transmit Ddescription Count", + .err = "using default of " + __MODULE_STRING(ATL1E_DEFAULT_TX_DESC_CNT), + .def = ATL1E_DEFAULT_TX_DESC_CNT, + .arg = { .r = { .min = ATL1E_MIN_TX_DESC_CNT, + .max = ATL1E_MAX_TX_DESC_CNT} } + }; + int val; + if (num_tx_desc_cnt > bd) { + val = tx_desc_cnt[bd]; + atl1e_validate_option(&val, &opt, pdev); + adapter->tx_ring.count = (u16) val & 0xFFFC; + } else + adapter->tx_ring.count = (u16)opt.def; + } + + { /* Receive Memory Block Count */ + struct atl1e_option opt = { + .type = range_option, + .name = "Memory size of rx buffer(KB)", + .err = "using default of " + __MODULE_STRING(ATL1E_DEFAULT_RX_MEM_SIZE), + .def = ATL1E_DEFAULT_RX_MEM_SIZE, + .arg = { .r = { .min = ATL1E_MIN_RX_MEM_SIZE, + .max = ATL1E_MAX_RX_MEM_SIZE} } + }; + int val; + if (num_rx_mem_size > bd) { + val = rx_mem_size[bd]; + atl1e_validate_option(&val, &opt, pdev); + adapter->rx_ring.page_size = (u32)val * 1024; + } else { + adapter->rx_ring.page_size = (u32)opt.def * 1024; + } + } + + { /* Interrupt Moderate Timer */ + struct atl1e_option opt = { + .type = range_option, + .name = "Interrupt Moderate Timer", + .err = "using default of " + __MODULE_STRING(INT_MOD_DEFAULT_CNT), + .def = INT_MOD_DEFAULT_CNT, + .arg = { .r = { .min = INT_MOD_MIN_CNT, + .max = INT_MOD_MAX_CNT} } + } ; + int val; + if (num_int_mod_timer > bd) { + val = int_mod_timer[bd]; + atl1e_validate_option(&val, &opt, pdev); + adapter->hw.imt = (u16) val; + } else + adapter->hw.imt = (u16)(opt.def); + } + + { /* MediaType */ + struct atl1e_option opt = { + .type = range_option, + .name = "Speed/Duplex Selection", + .err = "using default of " + __MODULE_STRING(MEDIA_TYPE_AUTO_SENSOR), + .def = MEDIA_TYPE_AUTO_SENSOR, + .arg = { .r = { .min = MEDIA_TYPE_AUTO_SENSOR, + .max = MEDIA_TYPE_10M_HALF} } + } ; + int val; + if (num_media_type > bd) { + val = media_type[bd]; + atl1e_validate_option(&val, &opt, pdev); + adapter->hw.media_type = (u16) val; + } else + adapter->hw.media_type = (u16)(opt.def); + + } +} diff --git a/drivers/net/atlx/atl1.c b/drivers/net/atlx/atl1.c index 3e22e7817bc3..f12e3d12474b 100644 --- a/drivers/net/atlx/atl1.c +++ b/drivers/net/atlx/atl1.c @@ -1308,7 +1308,6 @@ static u32 atl1_check_link(struct atl1_adapter *adapter) dev_info(&adapter->pdev->dev, "link is down\n"); adapter->link_speed = SPEED_0; netif_carrier_off(netdev); - netif_stop_queue(netdev); } return 0; } @@ -1358,7 +1357,6 @@ static u32 atl1_check_link(struct atl1_adapter *adapter) if (!netif_carrier_ok(netdev)) { /* Link down -> Up */ netif_carrier_on(netdev); - netif_wake_queue(netdev); } return 0; } @@ -2627,6 +2625,7 @@ static s32 atl1_up(struct atl1_adapter *adapter) mod_timer(&adapter->watchdog_timer, jiffies); atlx_irq_enable(adapter); atl1_check_link(adapter); + netif_start_queue(netdev); return 0; err_up: diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c index 3ab61e40e86a..cb8be490e5ae 100644 --- a/drivers/net/au1000_eth.c +++ b/drivers/net/au1000_eth.c @@ -911,9 +911,8 @@ au1000_adjust_link(struct net_device *dev) if(phydev->link != aup->old_link) { // link state changed - if (phydev->link) // link went up - netif_tx_schedule_all(dev); - else { // link went down + if (!phydev->link) { + /* link went down */ aup->old_speed = 0; aup->old_duplex = -1; } diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c index a6a3da89f590..a8ec60e1ed75 100644 --- a/drivers/net/bfin_mac.c +++ b/drivers/net/bfin_mac.c @@ -357,7 +357,6 @@ static void bfin_mac_adjust_link(struct net_device *dev) if (!lp->old_link) { new_state = 1; lp->old_link = 1; - netif_tx_schedule_all(dev); } } else if (lp->old_link) { new_state = 1; diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 0263bef9cc6d..af251a5df844 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -814,7 +814,7 @@ static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fastpath *fp, } /* release skb */ - BUG_TRAP(skb); + WARN_ON(!skb); dev_kfree_skb(skb); tx_buf->first_bd = 0; tx_buf->skb = NULL; @@ -837,9 +837,9 @@ static inline u16 bnx2x_tx_avail(struct bnx2x_fastpath *fp) used = SUB_S16(prod, cons) + (s16)NUM_TX_RINGS; #ifdef BNX2X_STOP_ON_ERROR - BUG_TRAP(used >= 0); - BUG_TRAP(used <= fp->bp->tx_ring_size); - BUG_TRAP((fp->bp->tx_ring_size - used) <= MAX_TX_AVAIL); + WARN_ON(used < 0); + WARN_ON(used > fp->bp->tx_ring_size); + WARN_ON((fp->bp->tx_ring_size - used) > MAX_TX_AVAIL); #endif return (s16)(fp->bp->tx_ring_size) - used; @@ -1020,7 +1020,7 @@ static inline int bnx2x_alloc_rx_sge(struct bnx2x *bp, mapping = pci_map_page(bp->pdev, page, 0, BCM_PAGE_SIZE*PAGES_PER_SGE, PCI_DMA_FROMDEVICE); - if (unlikely(dma_mapping_error(mapping))) { + if (unlikely(dma_mapping_error(&bp->pdev->dev, mapping))) { __free_pages(page, PAGES_PER_SGE_SHIFT); return -ENOMEM; } @@ -1048,7 +1048,7 @@ static inline int bnx2x_alloc_rx_skb(struct bnx2x *bp, mapping = pci_map_single(bp->pdev, skb->data, bp->rx_buf_use_size, PCI_DMA_FROMDEVICE); - if (unlikely(dma_mapping_error(mapping))) { + if (unlikely(dma_mapping_error(&bp->pdev->dev, mapping))) { dev_kfree_skb(skb); return -ENOMEM; } @@ -4374,7 +4374,7 @@ static void bnx2x_init_rx_rings(struct bnx2x *bp) } ring_prod = NEXT_RX_IDX(ring_prod); cqe_ring_prod = NEXT_RCQ_IDX(cqe_ring_prod); - BUG_TRAP(ring_prod > i); + WARN_ON(ring_prod <= i); } fp->rx_bd_prod = ring_prod; diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 9737c06045d6..a641eeaa2a2f 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -5041,6 +5041,7 @@ static int bond_check_params(struct bond_params *params) } static struct lock_class_key bonding_netdev_xmit_lock_key; +static struct lock_class_key bonding_netdev_addr_lock_key; static void bond_set_lockdep_class_one(struct net_device *dev, struct netdev_queue *txq, @@ -5052,6 +5053,8 @@ static void bond_set_lockdep_class_one(struct net_device *dev, static void bond_set_lockdep_class(struct net_device *dev) { + lockdep_set_class(&dev->addr_list_lock, + &bonding_netdev_addr_lock_key); netdev_for_each_tx_queue(dev, bond_set_lockdep_class_one, NULL); } diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index 83768df27806..f1936d51b458 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -576,6 +576,18 @@ static void cas_spare_recover(struct cas *cp, const gfp_t flags) list_for_each_safe(elem, tmp, &list) { cas_page_t *page = list_entry(elem, cas_page_t, list); + /* + * With the lockless pagecache, cassini buffering scheme gets + * slightly less accurate: we might find that a page has an + * elevated reference count here, due to a speculative ref, + * and skip it as in-use. Ideally we would be able to reclaim + * it. However this would be such a rare case, it doesn't + * matter too much as we should pick it up the next time round. + * + * Importantly, if we find that the page has a refcount of 1 + * here (our refcount), then we know it is definitely not inuse + * so we can reuse it. + */ if (page_count(page->buffer) > 1) continue; diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index fbd4280c102c..a7800e559090 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -945,10 +945,8 @@ static void cpmac_adjust_link(struct net_device *dev) if (!priv->oldlink) { new_state = 1; priv->oldlink = 1; - netif_tx_schedule_all(dev); } } else if (priv->oldlink) { - netif_tx_stop_all_queues(dev); new_state = 1; priv->oldlink = 0; priv->oldspeed = 0; diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c index a96331c875e6..1b0861d73ab7 100644 --- a/drivers/net/cxgb3/sge.c +++ b/drivers/net/cxgb3/sge.c @@ -386,7 +386,7 @@ static inline int add_one_rx_buf(void *va, unsigned int len, dma_addr_t mapping; mapping = pci_map_single(pdev, va, len, PCI_DMA_FROMDEVICE); - if (unlikely(pci_dma_mapping_error(mapping))) + if (unlikely(pci_dma_mapping_error(pdev, mapping))) return -ENOMEM; pci_unmap_addr_set(sd, dma_addr, mapping); diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c index 952e10d686ec..0b0f1c407a7e 100644 --- a/drivers/net/dm9000.c +++ b/drivers/net/dm9000.c @@ -888,19 +888,22 @@ dm9000_rx(struct net_device *dev) dev_dbg(db->dev, "RST: RX Len:%x\n", RxLen); } - if (rxhdr.RxStatus & 0xbf) { + /* rxhdr.RxStatus is identical to RSR register. */ + if (rxhdr.RxStatus & (RSR_FOE | RSR_CE | RSR_AE | + RSR_PLE | RSR_RWTO | + RSR_LCS | RSR_RF)) { GoodPacket = false; - if (rxhdr.RxStatus & 0x01) { + if (rxhdr.RxStatus & RSR_FOE) { if (netif_msg_rx_err(db)) dev_dbg(db->dev, "fifo error\n"); dev->stats.rx_fifo_errors++; } - if (rxhdr.RxStatus & 0x02) { + if (rxhdr.RxStatus & RSR_CE) { if (netif_msg_rx_err(db)) dev_dbg(db->dev, "crc error\n"); dev->stats.rx_crc_errors++; } - if (rxhdr.RxStatus & 0x80) { + if (rxhdr.RxStatus & RSR_RF) { if (netif_msg_rx_err(db)) dev_dbg(db->dev, "length error\n"); dev->stats.rx_length_errors++; @@ -1067,7 +1070,7 @@ dm9000_phy_read(struct net_device *dev, int phy_reg_unused, int reg) /* Fill the phyxcer register into REG_0C */ iow(db, DM9000_EPAR, DM9000_PHY | reg); - iow(db, DM9000_EPCR, 0xc); /* Issue phyxcer read command */ + iow(db, DM9000_EPCR, EPCR_ERPRR | EPCR_EPOS); /* Issue phyxcer read command */ writeb(reg_save, db->io_addr); spin_unlock_irqrestore(&db->lock,flags); @@ -1118,7 +1121,7 @@ dm9000_phy_write(struct net_device *dev, iow(db, DM9000_EPDRL, value); iow(db, DM9000_EPDRH, value >> 8); - iow(db, DM9000_EPCR, 0xa); /* Issue phyxcer write command */ + iow(db, DM9000_EPCR, EPCR_EPOS | EPCR_ERPRW); /* Issue phyxcer write command */ writeb(reg_save, db->io_addr); spin_unlock_irqrestore(&db->lock, flags); diff --git a/drivers/net/e100.c b/drivers/net/e100.c index 1037b1332312..19d32a227be1 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -1790,7 +1790,7 @@ static int e100_rx_alloc_skb(struct nic *nic, struct rx *rx) rx->dma_addr = pci_map_single(nic->pdev, rx->skb->data, RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(rx->dma_addr)) { + if (pci_dma_mapping_error(nic->pdev, rx->dma_addr)) { dev_kfree_skb_any(rx->skb); rx->skb = NULL; rx->dma_addr = 0; diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h index 31feae1ea390..19e317eaf5bc 100644 --- a/drivers/net/e1000/e1000.h +++ b/drivers/net/e1000/e1000.h @@ -90,10 +90,13 @@ struct e1000_adapter; #define E1000_ERR(args...) printk(KERN_ERR "e1000: " args) #define PFX "e1000: " -#define DPRINTK(nlevel, klevel, fmt, args...) \ - (void)((NETIF_MSG_##nlevel & adapter->msg_enable) && \ - printk(KERN_##klevel PFX "%s: %s: " fmt, adapter->netdev->name, \ - __FUNCTION__ , ## args)) + +#define DPRINTK(nlevel, klevel, fmt, args...) \ +do { \ + if (NETIF_MSG_##nlevel & adapter->msg_enable) \ + printk(KERN_##klevel PFX "%s: %s: " fmt, \ + adapter->netdev->name, __func__, ##args); \ +} while (0) #define E1000_MAX_INTR 10 @@ -151,9 +154,9 @@ struct e1000_adapter; #define E1000_MASTER_SLAVE e1000_ms_hw_default #endif -#define E1000_MNG_VLAN_NONE -1 +#define E1000_MNG_VLAN_NONE (-1) /* Number of packet split data buffers (not including the header buffer) */ -#define PS_PAGE_BUFFERS MAX_PS_BUFFERS-1 +#define PS_PAGE_BUFFERS (MAX_PS_BUFFERS - 1) /* wrapper around a pointer to a socket buffer, * so a DMA handle can be stored along with the buffer */ @@ -165,9 +168,13 @@ struct e1000_buffer { u16 next_to_watch; }; +struct e1000_ps_page { + struct page *ps_page[PS_PAGE_BUFFERS]; +}; -struct e1000_ps_page { struct page *ps_page[PS_PAGE_BUFFERS]; }; -struct e1000_ps_page_dma { u64 ps_page_dma[PS_PAGE_BUFFERS]; }; +struct e1000_ps_page_dma { + u64 ps_page_dma[PS_PAGE_BUFFERS]; +}; struct e1000_tx_ring { /* pointer to the descriptor ring memory */ @@ -217,13 +224,13 @@ struct e1000_rx_ring { u16 rdt; }; -#define E1000_DESC_UNUSED(R) \ - ((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->count) + \ - (R)->next_to_clean - (R)->next_to_use - 1) +#define E1000_DESC_UNUSED(R) \ + ((((R)->next_to_clean > (R)->next_to_use) \ + ? 0 : (R)->count) + (R)->next_to_clean - (R)->next_to_use - 1) -#define E1000_RX_DESC_PS(R, i) \ +#define E1000_RX_DESC_PS(R, i) \ (&(((union e1000_rx_desc_packet_split *)((R).desc))[i])) -#define E1000_RX_DESC_EXT(R, i) \ +#define E1000_RX_DESC_EXT(R, i) \ (&(((union e1000_rx_desc_extended *)((R).desc))[i])) #define E1000_GET_DESC(R, i, type) (&(((struct type *)((R).desc))[i])) #define E1000_RX_DESC(R, i) E1000_GET_DESC(R, i, e1000_rx_desc) @@ -246,9 +253,7 @@ struct e1000_adapter { u16 link_speed; u16 link_duplex; spinlock_t stats_lock; -#ifdef CONFIG_E1000_NAPI spinlock_t tx_queue_lock; -#endif unsigned int total_tx_bytes; unsigned int total_tx_packets; unsigned int total_rx_bytes; @@ -286,22 +291,16 @@ struct e1000_adapter { bool detect_tx_hung; /* RX */ -#ifdef CONFIG_E1000_NAPI - bool (*clean_rx) (struct e1000_adapter *adapter, - struct e1000_rx_ring *rx_ring, - int *work_done, int work_to_do); -#else - bool (*clean_rx) (struct e1000_adapter *adapter, - struct e1000_rx_ring *rx_ring); -#endif - void (*alloc_rx_buf) (struct e1000_adapter *adapter, - struct e1000_rx_ring *rx_ring, - int cleaned_count); + bool (*clean_rx)(struct e1000_adapter *adapter, + struct e1000_rx_ring *rx_ring, + int *work_done, int work_to_do); + void (*alloc_rx_buf)(struct e1000_adapter *adapter, + struct e1000_rx_ring *rx_ring, + int cleaned_count); struct e1000_rx_ring *rx_ring; /* One per active queue */ -#ifdef CONFIG_E1000_NAPI struct napi_struct napi; struct net_device *polling_netdev; /* One per active queue */ -#endif + int num_tx_queues; int num_rx_queues; @@ -317,7 +316,6 @@ struct e1000_adapter { u64 gorcl_old; u16 rx_ps_bsize0; - /* OS defined structs */ struct net_device *netdev; struct pci_dev *pdev; @@ -342,6 +340,10 @@ struct e1000_adapter { bool quad_port_a; unsigned long flags; u32 eeprom_wol; + + /* for ioport free */ + int bars; + int need_ioport; }; enum e1000_state_t { @@ -353,9 +355,18 @@ enum e1000_state_t { extern char e1000_driver_name[]; extern const char e1000_driver_version[]; +extern int e1000_up(struct e1000_adapter *adapter); +extern void e1000_down(struct e1000_adapter *adapter); +extern void e1000_reinit_locked(struct e1000_adapter *adapter); +extern void e1000_reset(struct e1000_adapter *adapter); +extern int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx); +extern int e1000_setup_all_rx_resources(struct e1000_adapter *adapter); +extern int e1000_setup_all_tx_resources(struct e1000_adapter *adapter); +extern void e1000_free_all_rx_resources(struct e1000_adapter *adapter); +extern void e1000_free_all_tx_resources(struct e1000_adapter *adapter); +extern void e1000_update_stats(struct e1000_adapter *adapter); extern void e1000_power_up_phy(struct e1000_adapter *); extern void e1000_set_ethtool_ops(struct net_device *netdev); extern void e1000_check_options(struct e1000_adapter *adapter); - #endif /* _E1000_H_ */ diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c index a3f6a9c72ec8..6a3893acfe04 100644 --- a/drivers/net/e1000/e1000_ethtool.c +++ b/drivers/net/e1000/e1000_ethtool.c @@ -29,21 +29,8 @@ /* ethtool support for e1000 */ #include "e1000.h" - #include <asm/uaccess.h> -extern int e1000_up(struct e1000_adapter *adapter); -extern void e1000_down(struct e1000_adapter *adapter); -extern void e1000_reinit_locked(struct e1000_adapter *adapter); -extern void e1000_reset(struct e1000_adapter *adapter); -extern int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx); -extern int e1000_setup_all_rx_resources(struct e1000_adapter *adapter); -extern int e1000_setup_all_tx_resources(struct e1000_adapter *adapter); -extern void e1000_free_all_rx_resources(struct e1000_adapter *adapter); -extern void e1000_free_all_tx_resources(struct e1000_adapter *adapter); -extern void e1000_update_stats(struct e1000_adapter *adapter); - - struct e1000_stats { char stat_string[ETH_GSTRING_LEN]; int sizeof_stat; @@ -112,8 +99,8 @@ static const char e1000_gstrings_test[][ETH_GSTRING_LEN] = { }; #define E1000_TEST_LEN ARRAY_SIZE(e1000_gstrings_test) -static int -e1000_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +static int e1000_get_settings(struct net_device *netdev, + struct ethtool_cmd *ecmd) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; @@ -162,7 +149,7 @@ e1000_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) ecmd->transceiver = XCVR_EXTERNAL; } - if (E1000_READ_REG(&adapter->hw, STATUS) & E1000_STATUS_LU) { + if (er32(STATUS) & E1000_STATUS_LU) { e1000_get_speed_and_duplex(hw, &adapter->link_speed, &adapter->link_duplex); @@ -185,8 +172,8 @@ e1000_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) return 0; } -static int -e1000_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +static int e1000_set_settings(struct net_device *netdev, + struct ethtool_cmd *ecmd) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; @@ -231,9 +218,8 @@ e1000_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) return 0; } -static void -e1000_get_pauseparam(struct net_device *netdev, - struct ethtool_pauseparam *pause) +static void e1000_get_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; @@ -251,9 +237,8 @@ e1000_get_pauseparam(struct net_device *netdev, } } -static int -e1000_set_pauseparam(struct net_device *netdev, - struct ethtool_pauseparam *pause) +static int e1000_set_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; @@ -289,15 +274,13 @@ e1000_set_pauseparam(struct net_device *netdev, return retval; } -static u32 -e1000_get_rx_csum(struct net_device *netdev) +static u32 e1000_get_rx_csum(struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); return adapter->rx_csum; } -static int -e1000_set_rx_csum(struct net_device *netdev, u32 data) +static int e1000_set_rx_csum(struct net_device *netdev, u32 data) { struct e1000_adapter *adapter = netdev_priv(netdev); adapter->rx_csum = data; @@ -309,18 +292,17 @@ e1000_set_rx_csum(struct net_device *netdev, u32 data) return 0; } -static u32 -e1000_get_tx_csum(struct net_device *netdev) +static u32 e1000_get_tx_csum(struct net_device *netdev) { return (netdev->features & NETIF_F_HW_CSUM) != 0; } -static int -e1000_set_tx_csum(struct net_device *netdev, u32 data) +static int e1000_set_tx_csum(struct net_device *netdev, u32 data) { struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; - if (adapter->hw.mac_type < e1000_82543) { + if (hw->mac_type < e1000_82543) { if (!data) return -EINVAL; return 0; @@ -334,12 +316,13 @@ e1000_set_tx_csum(struct net_device *netdev, u32 data) return 0; } -static int -e1000_set_tso(struct net_device *netdev, u32 data) +static int e1000_set_tso(struct net_device *netdev, u32 data) { struct e1000_adapter *adapter = netdev_priv(netdev); - if ((adapter->hw.mac_type < e1000_82544) || - (adapter->hw.mac_type == e1000_82547)) + struct e1000_hw *hw = &adapter->hw; + + if ((hw->mac_type < e1000_82544) || + (hw->mac_type == e1000_82547)) return data ? -EINVAL : 0; if (data) @@ -357,30 +340,26 @@ e1000_set_tso(struct net_device *netdev, u32 data) return 0; } -static u32 -e1000_get_msglevel(struct net_device *netdev) +static u32 e1000_get_msglevel(struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); return adapter->msg_enable; } -static void -e1000_set_msglevel(struct net_device *netdev, u32 data) +static void e1000_set_msglevel(struct net_device *netdev, u32 data) { struct e1000_adapter *adapter = netdev_priv(netdev); adapter->msg_enable = data; } -static int -e1000_get_regs_len(struct net_device *netdev) +static int e1000_get_regs_len(struct net_device *netdev) { #define E1000_REGS_LEN 32 return E1000_REGS_LEN * sizeof(u32); } -static void -e1000_get_regs(struct net_device *netdev, - struct ethtool_regs *regs, void *p) +static void e1000_get_regs(struct net_device *netdev, struct ethtool_regs *regs, + void *p) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; @@ -391,22 +370,22 @@ e1000_get_regs(struct net_device *netdev, regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id; - regs_buff[0] = E1000_READ_REG(hw, CTRL); - regs_buff[1] = E1000_READ_REG(hw, STATUS); + regs_buff[0] = er32(CTRL); + regs_buff[1] = er32(STATUS); - regs_buff[2] = E1000_READ_REG(hw, RCTL); - regs_buff[3] = E1000_READ_REG(hw, RDLEN); - regs_buff[4] = E1000_READ_REG(hw, RDH); - regs_buff[5] = E1000_READ_REG(hw, RDT); - regs_buff[6] = E1000_READ_REG(hw, RDTR); + regs_buff[2] = er32(RCTL); + regs_buff[3] = er32(RDLEN); + regs_buff[4] = er32(RDH); + regs_buff[5] = er32(RDT); + regs_buff[6] = er32(RDTR); - regs_buff[7] = E1000_READ_REG(hw, TCTL); - regs_buff[8] = E1000_READ_REG(hw, TDLEN); - regs_buff[9] = E1000_READ_REG(hw, TDH); - regs_buff[10] = E1000_READ_REG(hw, TDT); - regs_buff[11] = E1000_READ_REG(hw, TIDV); + regs_buff[7] = er32(TCTL); + regs_buff[8] = er32(TDLEN); + regs_buff[9] = er32(TDH); + regs_buff[10] = er32(TDT); + regs_buff[11] = er32(TIDV); - regs_buff[12] = adapter->hw.phy_type; /* PHY type (IGP=1, M88=0) */ + regs_buff[12] = hw->phy_type; /* PHY type (IGP=1, M88=0) */ if (hw->phy_type == e1000_phy_igp) { e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT, IGP01E1000_PHY_AGC_A); @@ -464,20 +443,20 @@ e1000_get_regs(struct net_device *netdev, if (hw->mac_type >= e1000_82540 && hw->mac_type < e1000_82571 && hw->media_type == e1000_media_type_copper) { - regs_buff[26] = E1000_READ_REG(hw, MANC); + regs_buff[26] = er32(MANC); } } -static int -e1000_get_eeprom_len(struct net_device *netdev) +static int e1000_get_eeprom_len(struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); - return adapter->hw.eeprom.word_size * 2; + struct e1000_hw *hw = &adapter->hw; + + return hw->eeprom.word_size * 2; } -static int -e1000_get_eeprom(struct net_device *netdev, - struct ethtool_eeprom *eeprom, u8 *bytes) +static int e1000_get_eeprom(struct net_device *netdev, + struct ethtool_eeprom *eeprom, u8 *bytes) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; @@ -504,10 +483,12 @@ e1000_get_eeprom(struct net_device *netdev, last_word - first_word + 1, eeprom_buff); else { - for (i = 0; i < last_word - first_word + 1; i++) - if ((ret_val = e1000_read_eeprom(hw, first_word + i, 1, - &eeprom_buff[i]))) + for (i = 0; i < last_word - first_word + 1; i++) { + ret_val = e1000_read_eeprom(hw, first_word + i, 1, + &eeprom_buff[i]); + if (ret_val) break; + } } /* Device's eeprom is always little-endian, word addressable */ @@ -521,9 +502,8 @@ e1000_get_eeprom(struct net_device *netdev, return ret_val; } -static int -e1000_set_eeprom(struct net_device *netdev, - struct ethtool_eeprom *eeprom, u8 *bytes) +static int e1000_set_eeprom(struct net_device *netdev, + struct ethtool_eeprom *eeprom, u8 *bytes) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; @@ -584,11 +564,11 @@ e1000_set_eeprom(struct net_device *netdev, return ret_val; } -static void -e1000_get_drvinfo(struct net_device *netdev, - struct ethtool_drvinfo *drvinfo) +static void e1000_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) { struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; char firmware_version[32]; u16 eeprom_data; @@ -597,8 +577,8 @@ e1000_get_drvinfo(struct net_device *netdev, /* EEPROM image version # is reported as firmware version # for * 8257{1|2|3} controllers */ - e1000_read_eeprom(&adapter->hw, 5, 1, &eeprom_data); - switch (adapter->hw.mac_type) { + e1000_read_eeprom(hw, 5, 1, &eeprom_data); + switch (hw->mac_type) { case e1000_82571: case e1000_82572: case e1000_82573: @@ -619,12 +599,12 @@ e1000_get_drvinfo(struct net_device *netdev, drvinfo->eedump_len = e1000_get_eeprom_len(netdev); } -static void -e1000_get_ringparam(struct net_device *netdev, - struct ethtool_ringparam *ring) +static void e1000_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) { struct e1000_adapter *adapter = netdev_priv(netdev); - e1000_mac_type mac_type = adapter->hw.mac_type; + struct e1000_hw *hw = &adapter->hw; + e1000_mac_type mac_type = hw->mac_type; struct e1000_tx_ring *txdr = adapter->tx_ring; struct e1000_rx_ring *rxdr = adapter->rx_ring; @@ -640,12 +620,12 @@ e1000_get_ringparam(struct net_device *netdev, ring->rx_jumbo_pending = 0; } -static int -e1000_set_ringparam(struct net_device *netdev, - struct ethtool_ringparam *ring) +static int e1000_set_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) { struct e1000_adapter *adapter = netdev_priv(netdev); - e1000_mac_type mac_type = adapter->hw.mac_type; + struct e1000_hw *hw = &adapter->hw; + e1000_mac_type mac_type = hw->mac_type; struct e1000_tx_ring *txdr, *tx_old; struct e1000_rx_ring *rxdr, *rx_old; int i, err; @@ -691,9 +671,11 @@ e1000_set_ringparam(struct net_device *netdev, if (netif_running(adapter->netdev)) { /* Try to get new resources before deleting old */ - if ((err = e1000_setup_all_rx_resources(adapter))) + err = e1000_setup_all_rx_resources(adapter); + if (err) goto err_setup_rx; - if ((err = e1000_setup_all_tx_resources(adapter))) + err = e1000_setup_all_tx_resources(adapter); + if (err) goto err_setup_tx; /* save the new, restore the old in order to free it, @@ -707,7 +689,8 @@ e1000_set_ringparam(struct net_device *netdev, kfree(rx_old); adapter->rx_ring = rxdr; adapter->tx_ring = txdr; - if ((err = e1000_up(adapter))) + err = e1000_up(adapter); + if (err) goto err_setup; } @@ -728,12 +711,13 @@ err_setup: return err; } -static bool reg_pattern_test(struct e1000_adapter *adapter, u64 *data, - int reg, u32 mask, u32 write) +static bool reg_pattern_test(struct e1000_adapter *adapter, u64 *data, int reg, + u32 mask, u32 write) { + struct e1000_hw *hw = &adapter->hw; static const u32 test[] = {0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF}; - u8 __iomem *address = adapter->hw.hw_addr + reg; + u8 __iomem *address = hw->hw_addr + reg; u32 read; int i; @@ -751,10 +735,11 @@ static bool reg_pattern_test(struct e1000_adapter *adapter, u64 *data, return false; } -static bool reg_set_and_check(struct e1000_adapter *adapter, u64 *data, - int reg, u32 mask, u32 write) +static bool reg_set_and_check(struct e1000_adapter *adapter, u64 *data, int reg, + u32 mask, u32 write) { - u8 __iomem *address = adapter->hw.hw_addr + reg; + struct e1000_hw *hw = &adapter->hw; + u8 __iomem *address = hw->hw_addr + reg; u32 read; writel(write & mask, address); @@ -772,7 +757,7 @@ static bool reg_set_and_check(struct e1000_adapter *adapter, u64 *data, #define REG_PATTERN_TEST(reg, mask, write) \ do { \ if (reg_pattern_test(adapter, data, \ - (adapter->hw.mac_type >= e1000_82543) \ + (hw->mac_type >= e1000_82543) \ ? E1000_##reg : E1000_82542_##reg, \ mask, write)) \ return 1; \ @@ -781,22 +766,22 @@ static bool reg_set_and_check(struct e1000_adapter *adapter, u64 *data, #define REG_SET_AND_CHECK(reg, mask, write) \ do { \ if (reg_set_and_check(adapter, data, \ - (adapter->hw.mac_type >= e1000_82543) \ + (hw->mac_type >= e1000_82543) \ ? E1000_##reg : E1000_82542_##reg, \ mask, write)) \ return 1; \ } while (0) -static int -e1000_reg_test(struct e1000_adapter *adapter, u64 *data) +static int e1000_reg_test(struct e1000_adapter *adapter, u64 *data) { u32 value, before, after; u32 i, toggle; + struct e1000_hw *hw = &adapter->hw; /* The status register is Read Only, so a write should fail. * Some bits that get toggled are ignored. */ - switch (adapter->hw.mac_type) { + switch (hw->mac_type) { /* there are several bits on newer hardware that are r/w */ case e1000_82571: case e1000_82572: @@ -812,10 +797,10 @@ e1000_reg_test(struct e1000_adapter *adapter, u64 *data) break; } - before = E1000_READ_REG(&adapter->hw, STATUS); - value = (E1000_READ_REG(&adapter->hw, STATUS) & toggle); - E1000_WRITE_REG(&adapter->hw, STATUS, toggle); - after = E1000_READ_REG(&adapter->hw, STATUS) & toggle; + before = er32(STATUS); + value = (er32(STATUS) & toggle); + ew32(STATUS, toggle); + after = er32(STATUS) & toggle; if (value != after) { DPRINTK(DRV, ERR, "failed STATUS register test got: " "0x%08X expected: 0x%08X\n", after, value); @@ -823,9 +808,9 @@ e1000_reg_test(struct e1000_adapter *adapter, u64 *data) return 1; } /* restore previous status */ - E1000_WRITE_REG(&adapter->hw, STATUS, before); + ew32(STATUS, before); - if (adapter->hw.mac_type != e1000_ich8lan) { + if (hw->mac_type != e1000_ich8lan) { REG_PATTERN_TEST(FCAL, 0xFFFFFFFF, 0xFFFFFFFF); REG_PATTERN_TEST(FCAH, 0x0000FFFF, 0xFFFFFFFF); REG_PATTERN_TEST(FCT, 0x0000FFFF, 0xFFFFFFFF); @@ -845,20 +830,20 @@ e1000_reg_test(struct e1000_adapter *adapter, u64 *data) REG_SET_AND_CHECK(RCTL, 0xFFFFFFFF, 0x00000000); - before = (adapter->hw.mac_type == e1000_ich8lan ? + before = (hw->mac_type == e1000_ich8lan ? 0x06C3B33E : 0x06DFB3FE); REG_SET_AND_CHECK(RCTL, before, 0x003FFFFB); REG_SET_AND_CHECK(TCTL, 0xFFFFFFFF, 0x00000000); - if (adapter->hw.mac_type >= e1000_82543) { + if (hw->mac_type >= e1000_82543) { REG_SET_AND_CHECK(RCTL, before, 0xFFFFFFFF); REG_PATTERN_TEST(RDBAL, 0xFFFFFFF0, 0xFFFFFFFF); - if (adapter->hw.mac_type != e1000_ich8lan) + if (hw->mac_type != e1000_ich8lan) REG_PATTERN_TEST(TXCW, 0xC000FFFF, 0x0000FFFF); REG_PATTERN_TEST(TDBAL, 0xFFFFFFF0, 0xFFFFFFFF); REG_PATTERN_TEST(TIDV, 0x0000FFFF, 0x0000FFFF); - value = (adapter->hw.mac_type == e1000_ich8lan ? + value = (hw->mac_type == e1000_ich8lan ? E1000_RAR_ENTRIES_ICH8LAN : E1000_RAR_ENTRIES); for (i = 0; i < value; i++) { REG_PATTERN_TEST(RA + (((i << 1) + 1) << 2), 0x8003FFFF, @@ -874,7 +859,7 @@ e1000_reg_test(struct e1000_adapter *adapter, u64 *data) } - value = (adapter->hw.mac_type == e1000_ich8lan ? + value = (hw->mac_type == e1000_ich8lan ? E1000_MC_TBL_SIZE_ICH8LAN : E1000_MC_TBL_SIZE); for (i = 0; i < value; i++) REG_PATTERN_TEST(MTA + (i << 2), 0xFFFFFFFF, 0xFFFFFFFF); @@ -883,9 +868,9 @@ e1000_reg_test(struct e1000_adapter *adapter, u64 *data) return 0; } -static int -e1000_eeprom_test(struct e1000_adapter *adapter, u64 *data) +static int e1000_eeprom_test(struct e1000_adapter *adapter, u64 *data) { + struct e1000_hw *hw = &adapter->hw; u16 temp; u16 checksum = 0; u16 i; @@ -893,7 +878,7 @@ e1000_eeprom_test(struct e1000_adapter *adapter, u64 *data) *data = 0; /* Read and add up the contents of the EEPROM */ for (i = 0; i < (EEPROM_CHECKSUM_REG + 1); i++) { - if ((e1000_read_eeprom(&adapter->hw, i, 1, &temp)) < 0) { + if ((e1000_read_eeprom(hw, i, 1, &temp)) < 0) { *data = 1; break; } @@ -901,30 +886,30 @@ e1000_eeprom_test(struct e1000_adapter *adapter, u64 *data) } /* If Checksum is not Correct return error else test passed */ - if ((checksum != (u16) EEPROM_SUM) && !(*data)) + if ((checksum != (u16)EEPROM_SUM) && !(*data)) *data = 2; return *data; } -static irqreturn_t -e1000_test_intr(int irq, void *data) +static irqreturn_t e1000_test_intr(int irq, void *data) { - struct net_device *netdev = (struct net_device *) data; + struct net_device *netdev = (struct net_device *)data; struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; - adapter->test_icr |= E1000_READ_REG(&adapter->hw, ICR); + adapter->test_icr |= er32(ICR); return IRQ_HANDLED; } -static int -e1000_intr_test(struct e1000_adapter *adapter, u64 *data) +static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data) { struct net_device *netdev = adapter->netdev; u32 mask, i = 0; bool shared_int = true; u32 irq = adapter->pdev->irq; + struct e1000_hw *hw = &adapter->hw; *data = 0; @@ -942,13 +927,13 @@ e1000_intr_test(struct e1000_adapter *adapter, u64 *data) (shared_int ? "shared" : "unshared")); /* Disable all the interrupts */ - E1000_WRITE_REG(&adapter->hw, IMC, 0xFFFFFFFF); + ew32(IMC, 0xFFFFFFFF); msleep(10); /* Test each interrupt */ for (; i < 10; i++) { - if (adapter->hw.mac_type == e1000_ich8lan && i == 8) + if (hw->mac_type == e1000_ich8lan && i == 8) continue; /* Interrupt to test */ @@ -962,8 +947,8 @@ e1000_intr_test(struct e1000_adapter *adapter, u64 *data) * test failed. */ adapter->test_icr = 0; - E1000_WRITE_REG(&adapter->hw, IMC, mask); - E1000_WRITE_REG(&adapter->hw, ICS, mask); + ew32(IMC, mask); + ew32(ICS, mask); msleep(10); if (adapter->test_icr & mask) { @@ -979,8 +964,8 @@ e1000_intr_test(struct e1000_adapter *adapter, u64 *data) * test failed. */ adapter->test_icr = 0; - E1000_WRITE_REG(&adapter->hw, IMS, mask); - E1000_WRITE_REG(&adapter->hw, ICS, mask); + ew32(IMS, mask); + ew32(ICS, mask); msleep(10); if (!(adapter->test_icr & mask)) { @@ -996,8 +981,8 @@ e1000_intr_test(struct e1000_adapter *adapter, u64 *data) * test failed. */ adapter->test_icr = 0; - E1000_WRITE_REG(&adapter->hw, IMC, ~mask & 0x00007FFF); - E1000_WRITE_REG(&adapter->hw, ICS, ~mask & 0x00007FFF); + ew32(IMC, ~mask & 0x00007FFF); + ew32(ICS, ~mask & 0x00007FFF); msleep(10); if (adapter->test_icr) { @@ -1008,7 +993,7 @@ e1000_intr_test(struct e1000_adapter *adapter, u64 *data) } /* Disable all the interrupts */ - E1000_WRITE_REG(&adapter->hw, IMC, 0xFFFFFFFF); + ew32(IMC, 0xFFFFFFFF); msleep(10); /* Unhook test interrupt handler */ @@ -1017,8 +1002,7 @@ e1000_intr_test(struct e1000_adapter *adapter, u64 *data) return *data; } -static void -e1000_free_desc_rings(struct e1000_adapter *adapter) +static void e1000_free_desc_rings(struct e1000_adapter *adapter) { struct e1000_tx_ring *txdr = &adapter->test_tx_ring; struct e1000_rx_ring *rxdr = &adapter->test_rx_ring; @@ -1064,9 +1048,9 @@ e1000_free_desc_rings(struct e1000_adapter *adapter) return; } -static int -e1000_setup_desc_rings(struct e1000_adapter *adapter) +static int e1000_setup_desc_rings(struct e1000_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; struct e1000_tx_ring *txdr = &adapter->test_tx_ring; struct e1000_rx_ring *rxdr = &adapter->test_rx_ring; struct pci_dev *pdev = adapter->pdev; @@ -1078,41 +1062,39 @@ e1000_setup_desc_rings(struct e1000_adapter *adapter) if (!txdr->count) txdr->count = E1000_DEFAULT_TXD; - if (!(txdr->buffer_info = kcalloc(txdr->count, - sizeof(struct e1000_buffer), - GFP_KERNEL))) { + txdr->buffer_info = kcalloc(txdr->count, sizeof(struct e1000_buffer), + GFP_KERNEL); + if (!txdr->buffer_info) { ret_val = 1; goto err_nomem; } txdr->size = txdr->count * sizeof(struct e1000_tx_desc); txdr->size = ALIGN(txdr->size, 4096); - if (!(txdr->desc = pci_alloc_consistent(pdev, txdr->size, - &txdr->dma))) { + txdr->desc = pci_alloc_consistent(pdev, txdr->size, &txdr->dma); + if (!txdr->desc) { ret_val = 2; goto err_nomem; } memset(txdr->desc, 0, txdr->size); txdr->next_to_use = txdr->next_to_clean = 0; - E1000_WRITE_REG(&adapter->hw, TDBAL, - ((u64) txdr->dma & 0x00000000FFFFFFFF)); - E1000_WRITE_REG(&adapter->hw, TDBAH, ((u64) txdr->dma >> 32)); - E1000_WRITE_REG(&adapter->hw, TDLEN, - txdr->count * sizeof(struct e1000_tx_desc)); - E1000_WRITE_REG(&adapter->hw, TDH, 0); - E1000_WRITE_REG(&adapter->hw, TDT, 0); - E1000_WRITE_REG(&adapter->hw, TCTL, - E1000_TCTL_PSP | E1000_TCTL_EN | - E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT | - E1000_FDX_COLLISION_DISTANCE << E1000_COLD_SHIFT); + ew32(TDBAL, ((u64)txdr->dma & 0x00000000FFFFFFFF)); + ew32(TDBAH, ((u64)txdr->dma >> 32)); + ew32(TDLEN, txdr->count * sizeof(struct e1000_tx_desc)); + ew32(TDH, 0); + ew32(TDT, 0); + ew32(TCTL, E1000_TCTL_PSP | E1000_TCTL_EN | + E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT | + E1000_FDX_COLLISION_DISTANCE << E1000_COLD_SHIFT); for (i = 0; i < txdr->count; i++) { struct e1000_tx_desc *tx_desc = E1000_TX_DESC(*txdr, i); struct sk_buff *skb; unsigned int size = 1024; - if (!(skb = alloc_skb(size, GFP_KERNEL))) { + skb = alloc_skb(size, GFP_KERNEL); + if (!skb) { ret_val = 3; goto err_nomem; } @@ -1135,40 +1117,40 @@ e1000_setup_desc_rings(struct e1000_adapter *adapter) if (!rxdr->count) rxdr->count = E1000_DEFAULT_RXD; - if (!(rxdr->buffer_info = kcalloc(rxdr->count, - sizeof(struct e1000_buffer), - GFP_KERNEL))) { + rxdr->buffer_info = kcalloc(rxdr->count, sizeof(struct e1000_buffer), + GFP_KERNEL); + if (!rxdr->buffer_info) { ret_val = 4; goto err_nomem; } rxdr->size = rxdr->count * sizeof(struct e1000_rx_desc); - if (!(rxdr->desc = pci_alloc_consistent(pdev, rxdr->size, &rxdr->dma))) { + rxdr->desc = pci_alloc_consistent(pdev, rxdr->size, &rxdr->dma); + if (!rxdr->desc) { ret_val = 5; goto err_nomem; } memset(rxdr->desc, 0, rxdr->size); rxdr->next_to_use = rxdr->next_to_clean = 0; - rctl = E1000_READ_REG(&adapter->hw, RCTL); - E1000_WRITE_REG(&adapter->hw, RCTL, rctl & ~E1000_RCTL_EN); - E1000_WRITE_REG(&adapter->hw, RDBAL, - ((u64) rxdr->dma & 0xFFFFFFFF)); - E1000_WRITE_REG(&adapter->hw, RDBAH, ((u64) rxdr->dma >> 32)); - E1000_WRITE_REG(&adapter->hw, RDLEN, rxdr->size); - E1000_WRITE_REG(&adapter->hw, RDH, 0); - E1000_WRITE_REG(&adapter->hw, RDT, 0); + rctl = er32(RCTL); + ew32(RCTL, rctl & ~E1000_RCTL_EN); + ew32(RDBAL, ((u64)rxdr->dma & 0xFFFFFFFF)); + ew32(RDBAH, ((u64)rxdr->dma >> 32)); + ew32(RDLEN, rxdr->size); + ew32(RDH, 0); + ew32(RDT, 0); rctl = E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_SZ_2048 | E1000_RCTL_LBM_NO | E1000_RCTL_RDMTS_HALF | - (adapter->hw.mc_filter_type << E1000_RCTL_MO_SHIFT); - E1000_WRITE_REG(&adapter->hw, RCTL, rctl); + (hw->mc_filter_type << E1000_RCTL_MO_SHIFT); + ew32(RCTL, rctl); for (i = 0; i < rxdr->count; i++) { struct e1000_rx_desc *rx_desc = E1000_RX_DESC(*rxdr, i); struct sk_buff *skb; - if (!(skb = alloc_skb(E1000_RXBUFFER_2048 + NET_IP_ALIGN, - GFP_KERNEL))) { + skb = alloc_skb(E1000_RXBUFFER_2048 + NET_IP_ALIGN, GFP_KERNEL); + if (!skb) { ret_val = 6; goto err_nomem; } @@ -1189,73 +1171,74 @@ err_nomem: return ret_val; } -static void -e1000_phy_disable_receiver(struct e1000_adapter *adapter) +static void e1000_phy_disable_receiver(struct e1000_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; + /* Write out to PHY registers 29 and 30 to disable the Receiver. */ - e1000_write_phy_reg(&adapter->hw, 29, 0x001F); - e1000_write_phy_reg(&adapter->hw, 30, 0x8FFC); - e1000_write_phy_reg(&adapter->hw, 29, 0x001A); - e1000_write_phy_reg(&adapter->hw, 30, 0x8FF0); + e1000_write_phy_reg(hw, 29, 0x001F); + e1000_write_phy_reg(hw, 30, 0x8FFC); + e1000_write_phy_reg(hw, 29, 0x001A); + e1000_write_phy_reg(hw, 30, 0x8FF0); } -static void -e1000_phy_reset_clk_and_crs(struct e1000_adapter *adapter) +static void e1000_phy_reset_clk_and_crs(struct e1000_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; u16 phy_reg; /* Because we reset the PHY above, we need to re-force TX_CLK in the * Extended PHY Specific Control Register to 25MHz clock. This * value defaults back to a 2.5MHz clock when the PHY is reset. */ - e1000_read_phy_reg(&adapter->hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_reg); + e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_reg); phy_reg |= M88E1000_EPSCR_TX_CLK_25; - e1000_write_phy_reg(&adapter->hw, + e1000_write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_reg); /* In addition, because of the s/w reset above, we need to enable * CRS on TX. This must be set for both full and half duplex * operation. */ - e1000_read_phy_reg(&adapter->hw, M88E1000_PHY_SPEC_CTRL, &phy_reg); + e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_reg); phy_reg |= M88E1000_PSCR_ASSERT_CRS_ON_TX; - e1000_write_phy_reg(&adapter->hw, + e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_reg); } -static int -e1000_nonintegrated_phy_loopback(struct e1000_adapter *adapter) +static int e1000_nonintegrated_phy_loopback(struct e1000_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; u32 ctrl_reg; u16 phy_reg; /* Setup the Device Control Register for PHY loopback test. */ - ctrl_reg = E1000_READ_REG(&adapter->hw, CTRL); + ctrl_reg = er32(CTRL); ctrl_reg |= (E1000_CTRL_ILOS | /* Invert Loss-Of-Signal */ E1000_CTRL_FRCSPD | /* Set the Force Speed Bit */ E1000_CTRL_FRCDPX | /* Set the Force Duplex Bit */ E1000_CTRL_SPD_1000 | /* Force Speed to 1000 */ E1000_CTRL_FD); /* Force Duplex to FULL */ - E1000_WRITE_REG(&adapter->hw, CTRL, ctrl_reg); + ew32(CTRL, ctrl_reg); /* Read the PHY Specific Control Register (0x10) */ - e1000_read_phy_reg(&adapter->hw, M88E1000_PHY_SPEC_CTRL, &phy_reg); + e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_reg); /* Clear Auto-Crossover bits in PHY Specific Control Register * (bits 6:5). */ phy_reg &= ~M88E1000_PSCR_AUTO_X_MODE; - e1000_write_phy_reg(&adapter->hw, M88E1000_PHY_SPEC_CTRL, phy_reg); + e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_reg); /* Perform software reset on the PHY */ - e1000_phy_reset(&adapter->hw); + e1000_phy_reset(hw); /* Have to setup TX_CLK and TX_CRS after software reset */ e1000_phy_reset_clk_and_crs(adapter); - e1000_write_phy_reg(&adapter->hw, PHY_CTRL, 0x8100); + e1000_write_phy_reg(hw, PHY_CTRL, 0x8100); /* Wait for reset to complete. */ udelay(500); @@ -1267,55 +1250,55 @@ e1000_nonintegrated_phy_loopback(struct e1000_adapter *adapter) e1000_phy_disable_receiver(adapter); /* Set the loopback bit in the PHY control register. */ - e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &phy_reg); + e1000_read_phy_reg(hw, PHY_CTRL, &phy_reg); phy_reg |= MII_CR_LOOPBACK; - e1000_write_phy_reg(&adapter->hw, PHY_CTRL, phy_reg); + e1000_write_phy_reg(hw, PHY_CTRL, phy_reg); /* Setup TX_CLK and TX_CRS one more time. */ e1000_phy_reset_clk_and_crs(adapter); /* Check Phy Configuration */ - e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &phy_reg); + e1000_read_phy_reg(hw, PHY_CTRL, &phy_reg); if (phy_reg != 0x4100) return 9; - e1000_read_phy_reg(&adapter->hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_reg); + e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_reg); if (phy_reg != 0x0070) return 10; - e1000_read_phy_reg(&adapter->hw, 29, &phy_reg); + e1000_read_phy_reg(hw, 29, &phy_reg); if (phy_reg != 0x001A) return 11; return 0; } -static int -e1000_integrated_phy_loopback(struct e1000_adapter *adapter) +static int e1000_integrated_phy_loopback(struct e1000_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; u32 ctrl_reg = 0; u32 stat_reg = 0; - adapter->hw.autoneg = false; + hw->autoneg = false; - if (adapter->hw.phy_type == e1000_phy_m88) { + if (hw->phy_type == e1000_phy_m88) { /* Auto-MDI/MDIX Off */ - e1000_write_phy_reg(&adapter->hw, + e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, 0x0808); /* reset to update Auto-MDI/MDIX */ - e1000_write_phy_reg(&adapter->hw, PHY_CTRL, 0x9140); + e1000_write_phy_reg(hw, PHY_CTRL, 0x9140); /* autoneg off */ - e1000_write_phy_reg(&adapter->hw, PHY_CTRL, 0x8140); - } else if (adapter->hw.phy_type == e1000_phy_gg82563) - e1000_write_phy_reg(&adapter->hw, + e1000_write_phy_reg(hw, PHY_CTRL, 0x8140); + } else if (hw->phy_type == e1000_phy_gg82563) + e1000_write_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, 0x1CC); - ctrl_reg = E1000_READ_REG(&adapter->hw, CTRL); + ctrl_reg = er32(CTRL); - if (adapter->hw.phy_type == e1000_phy_ife) { + if (hw->phy_type == e1000_phy_ife) { /* force 100, set loopback */ - e1000_write_phy_reg(&adapter->hw, PHY_CTRL, 0x6100); + e1000_write_phy_reg(hw, PHY_CTRL, 0x6100); /* Now set up the MAC to the same speed/duplex as the PHY. */ ctrl_reg &= ~E1000_CTRL_SPD_SEL; /* Clear the speed sel bits */ @@ -1325,10 +1308,10 @@ e1000_integrated_phy_loopback(struct e1000_adapter *adapter) E1000_CTRL_FD); /* Force Duplex to FULL */ } else { /* force 1000, set loopback */ - e1000_write_phy_reg(&adapter->hw, PHY_CTRL, 0x4140); + e1000_write_phy_reg(hw, PHY_CTRL, 0x4140); /* Now set up the MAC to the same speed/duplex as the PHY. */ - ctrl_reg = E1000_READ_REG(&adapter->hw, CTRL); + ctrl_reg = er32(CTRL); ctrl_reg &= ~E1000_CTRL_SPD_SEL; /* Clear the speed sel bits */ ctrl_reg |= (E1000_CTRL_FRCSPD | /* Set the Force Speed Bit */ E1000_CTRL_FRCDPX | /* Set the Force Duplex Bit */ @@ -1336,23 +1319,23 @@ e1000_integrated_phy_loopback(struct e1000_adapter *adapter) E1000_CTRL_FD); /* Force Duplex to FULL */ } - if (adapter->hw.media_type == e1000_media_type_copper && - adapter->hw.phy_type == e1000_phy_m88) + if (hw->media_type == e1000_media_type_copper && + hw->phy_type == e1000_phy_m88) ctrl_reg |= E1000_CTRL_ILOS; /* Invert Loss of Signal */ else { /* Set the ILOS bit on the fiber Nic is half * duplex link is detected. */ - stat_reg = E1000_READ_REG(&adapter->hw, STATUS); + stat_reg = er32(STATUS); if ((stat_reg & E1000_STATUS_FD) == 0) ctrl_reg |= (E1000_CTRL_ILOS | E1000_CTRL_SLU); } - E1000_WRITE_REG(&adapter->hw, CTRL, ctrl_reg); + ew32(CTRL, ctrl_reg); /* Disable the receiver on the PHY so when a cable is plugged in, the * PHY does not begin to autoneg when a cable is reconnected to the NIC. */ - if (adapter->hw.phy_type == e1000_phy_m88) + if (hw->phy_type == e1000_phy_m88) e1000_phy_disable_receiver(adapter); udelay(500); @@ -1360,15 +1343,15 @@ e1000_integrated_phy_loopback(struct e1000_adapter *adapter) return 0; } -static int -e1000_set_phy_loopback(struct e1000_adapter *adapter) +static int e1000_set_phy_loopback(struct e1000_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; u16 phy_reg = 0; u16 count = 0; - switch (adapter->hw.mac_type) { + switch (hw->mac_type) { case e1000_82543: - if (adapter->hw.media_type == e1000_media_type_copper) { + if (hw->media_type == e1000_media_type_copper) { /* Attempt to setup Loopback mode on Non-integrated PHY. * Some PHY registers get corrupted at random, so * attempt this 10 times. @@ -1402,9 +1385,9 @@ e1000_set_phy_loopback(struct e1000_adapter *adapter) /* Default PHY loopback work is to read the MII * control register and assert bit 14 (loopback mode). */ - e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &phy_reg); + e1000_read_phy_reg(hw, PHY_CTRL, &phy_reg); phy_reg |= MII_CR_LOOPBACK; - e1000_write_phy_reg(&adapter->hw, PHY_CTRL, phy_reg); + e1000_write_phy_reg(hw, PHY_CTRL, phy_reg); return 0; break; } @@ -1412,8 +1395,7 @@ e1000_set_phy_loopback(struct e1000_adapter *adapter) return 8; } -static int -e1000_setup_loopback_test(struct e1000_adapter *adapter) +static int e1000_setup_loopback_test(struct e1000_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; u32 rctl; @@ -1431,14 +1413,14 @@ e1000_setup_loopback_test(struct e1000_adapter *adapter) case e1000_82572: #define E1000_SERDES_LB_ON 0x410 e1000_set_phy_loopback(adapter); - E1000_WRITE_REG(hw, SCTL, E1000_SERDES_LB_ON); + ew32(SCTL, E1000_SERDES_LB_ON); msleep(10); return 0; break; default: - rctl = E1000_READ_REG(hw, RCTL); + rctl = er32(RCTL); rctl |= E1000_RCTL_LBM_TCVR; - E1000_WRITE_REG(hw, RCTL, rctl); + ew32(RCTL, rctl); return 0; } } else if (hw->media_type == e1000_media_type_copper) @@ -1447,16 +1429,15 @@ e1000_setup_loopback_test(struct e1000_adapter *adapter) return 7; } -static void -e1000_loopback_cleanup(struct e1000_adapter *adapter) +static void e1000_loopback_cleanup(struct e1000_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; u32 rctl; u16 phy_reg; - rctl = E1000_READ_REG(hw, RCTL); + rctl = er32(RCTL); rctl &= ~(E1000_RCTL_LBM_TCVR | E1000_RCTL_LBM_MAC); - E1000_WRITE_REG(hw, RCTL, rctl); + ew32(RCTL, rctl); switch (hw->mac_type) { case e1000_82571: @@ -1464,7 +1445,7 @@ e1000_loopback_cleanup(struct e1000_adapter *adapter) if (hw->media_type == e1000_media_type_fiber || hw->media_type == e1000_media_type_internal_serdes) { #define E1000_SERDES_LB_OFF 0x400 - E1000_WRITE_REG(hw, SCTL, E1000_SERDES_LB_OFF); + ew32(SCTL, E1000_SERDES_LB_OFF); msleep(10); break; } @@ -1489,8 +1470,8 @@ e1000_loopback_cleanup(struct e1000_adapter *adapter) } } -static void -e1000_create_lbtest_frame(struct sk_buff *skb, unsigned int frame_size) +static void e1000_create_lbtest_frame(struct sk_buff *skb, + unsigned int frame_size) { memset(skb->data, 0xFF, frame_size); frame_size &= ~1; @@ -1499,8 +1480,8 @@ e1000_create_lbtest_frame(struct sk_buff *skb, unsigned int frame_size) memset(&skb->data[frame_size / 2 + 12], 0xAF, 1); } -static int -e1000_check_lbtest_frame(struct sk_buff *skb, unsigned int frame_size) +static int e1000_check_lbtest_frame(struct sk_buff *skb, + unsigned int frame_size) { frame_size &= ~1; if (*(skb->data + 3) == 0xFF) { @@ -1512,16 +1493,16 @@ e1000_check_lbtest_frame(struct sk_buff *skb, unsigned int frame_size) return 13; } -static int -e1000_run_loopback_test(struct e1000_adapter *adapter) +static int e1000_run_loopback_test(struct e1000_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; struct e1000_tx_ring *txdr = &adapter->test_tx_ring; struct e1000_rx_ring *rxdr = &adapter->test_rx_ring; struct pci_dev *pdev = adapter->pdev; int i, j, k, l, lc, good_cnt, ret_val=0; unsigned long time; - E1000_WRITE_REG(&adapter->hw, RDT, rxdr->count - 1); + ew32(RDT, rxdr->count - 1); /* Calculate the loop count based on the largest descriptor ring * The idea is to wrap the largest ring a number of times using 64 @@ -1544,7 +1525,7 @@ e1000_run_loopback_test(struct e1000_adapter *adapter) PCI_DMA_TODEVICE); if (unlikely(++k == txdr->count)) k = 0; } - E1000_WRITE_REG(&adapter->hw, TDT, k); + ew32(TDT, k); msleep(200); time = jiffies; /* set the start time for the receive */ good_cnt = 0; @@ -1577,21 +1558,24 @@ e1000_run_loopback_test(struct e1000_adapter *adapter) return ret_val; } -static int -e1000_loopback_test(struct e1000_adapter *adapter, u64 *data) +static int e1000_loopback_test(struct e1000_adapter *adapter, u64 *data) { + struct e1000_hw *hw = &adapter->hw; + /* PHY loopback cannot be performed if SoL/IDER * sessions are active */ - if (e1000_check_phy_reset_block(&adapter->hw)) { + if (e1000_check_phy_reset_block(hw)) { DPRINTK(DRV, ERR, "Cannot do PHY loopback test " "when SoL/IDER is active.\n"); *data = 0; goto out; } - if ((*data = e1000_setup_desc_rings(adapter))) + *data = e1000_setup_desc_rings(adapter); + if (*data) goto out; - if ((*data = e1000_setup_loopback_test(adapter))) + *data = e1000_setup_loopback_test(adapter); + if (*data) goto err_loopback; *data = e1000_run_loopback_test(adapter); e1000_loopback_cleanup(adapter); @@ -1602,38 +1586,37 @@ out: return *data; } -static int -e1000_link_test(struct e1000_adapter *adapter, u64 *data) +static int e1000_link_test(struct e1000_adapter *adapter, u64 *data) { + struct e1000_hw *hw = &adapter->hw; *data = 0; - if (adapter->hw.media_type == e1000_media_type_internal_serdes) { + if (hw->media_type == e1000_media_type_internal_serdes) { int i = 0; - adapter->hw.serdes_link_down = true; + hw->serdes_link_down = true; /* On some blade server designs, link establishment * could take as long as 2-3 minutes */ do { - e1000_check_for_link(&adapter->hw); - if (!adapter->hw.serdes_link_down) + e1000_check_for_link(hw); + if (!hw->serdes_link_down) return *data; msleep(20); } while (i++ < 3750); *data = 1; } else { - e1000_check_for_link(&adapter->hw); - if (adapter->hw.autoneg) /* if auto_neg is set wait for it */ + e1000_check_for_link(hw); + if (hw->autoneg) /* if auto_neg is set wait for it */ msleep(4000); - if (!(E1000_READ_REG(&adapter->hw, STATUS) & E1000_STATUS_LU)) { + if (!(er32(STATUS) & E1000_STATUS_LU)) { *data = 1; } } return *data; } -static int -e1000_get_sset_count(struct net_device *netdev, int sset) +static int e1000_get_sset_count(struct net_device *netdev, int sset) { switch (sset) { case ETH_SS_TEST: @@ -1645,11 +1628,11 @@ e1000_get_sset_count(struct net_device *netdev, int sset) } } -static void -e1000_diag_test(struct net_device *netdev, - struct ethtool_test *eth_test, u64 *data) +static void e1000_diag_test(struct net_device *netdev, + struct ethtool_test *eth_test, u64 *data) { struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; bool if_running = netif_running(netdev); set_bit(__E1000_TESTING, &adapter->flags); @@ -1657,9 +1640,9 @@ e1000_diag_test(struct net_device *netdev, /* Offline tests */ /* save speed, duplex, autoneg settings */ - u16 autoneg_advertised = adapter->hw.autoneg_advertised; - u8 forced_speed_duplex = adapter->hw.forced_speed_duplex; - u8 autoneg = adapter->hw.autoneg; + u16 autoneg_advertised = hw->autoneg_advertised; + u8 forced_speed_duplex = hw->forced_speed_duplex; + u8 autoneg = hw->autoneg; DPRINTK(HW, INFO, "offline testing starting\n"); @@ -1692,9 +1675,9 @@ e1000_diag_test(struct net_device *netdev, eth_test->flags |= ETH_TEST_FL_FAILED; /* restore speed, duplex, autoneg settings */ - adapter->hw.autoneg_advertised = autoneg_advertised; - adapter->hw.forced_speed_duplex = forced_speed_duplex; - adapter->hw.autoneg = autoneg; + hw->autoneg_advertised = autoneg_advertised; + hw->forced_speed_duplex = forced_speed_duplex; + hw->autoneg = autoneg; e1000_reset(adapter); clear_bit(__E1000_TESTING, &adapter->flags); @@ -1717,7 +1700,8 @@ e1000_diag_test(struct net_device *netdev, msleep_interruptible(4 * 1000); } -static int e1000_wol_exclusion(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol) +static int e1000_wol_exclusion(struct e1000_adapter *adapter, + struct ethtool_wolinfo *wol) { struct e1000_hw *hw = &adapter->hw; int retval = 1; /* fail by default */ @@ -1742,7 +1726,7 @@ static int e1000_wol_exclusion(struct e1000_adapter *adapter, struct ethtool_wol case E1000_DEV_ID_82571EB_SERDES: case E1000_DEV_ID_82571EB_COPPER: /* Wake events not supported on port B */ - if (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1) { + if (er32(STATUS) & E1000_STATUS_FUNC_1) { wol->supported = 0; break; } @@ -1766,7 +1750,7 @@ static int e1000_wol_exclusion(struct e1000_adapter *adapter, struct ethtool_wol /* dual port cards only support WoL on port A from now on * unless it was enabled in the eeprom for port B * so exclude FUNC_1 ports from having WoL enabled */ - if (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1 && + if (er32(STATUS) & E1000_STATUS_FUNC_1 && !adapter->eeprom_wol) { wol->supported = 0; break; @@ -1778,10 +1762,11 @@ static int e1000_wol_exclusion(struct e1000_adapter *adapter, struct ethtool_wol return retval; } -static void -e1000_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) +static void e1000_get_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) { struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; wol->supported = WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC; @@ -1793,7 +1778,7 @@ e1000_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) return; /* apply any specific unsupported masks here */ - switch (adapter->hw.device_id) { + switch (hw->device_id) { case E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3: /* KSP3 does not suppport UCAST wake-ups */ wol->supported &= ~WAKE_UCAST; @@ -1818,8 +1803,7 @@ e1000_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) return; } -static int -e1000_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) +static int e1000_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; @@ -1863,61 +1847,60 @@ e1000_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) /* bit defines for adapter->led_status */ #define E1000_LED_ON 0 -static void -e1000_led_blink_callback(unsigned long data) +static void e1000_led_blink_callback(unsigned long data) { struct e1000_adapter *adapter = (struct e1000_adapter *) data; + struct e1000_hw *hw = &adapter->hw; if (test_and_change_bit(E1000_LED_ON, &adapter->led_status)) - e1000_led_off(&adapter->hw); + e1000_led_off(hw); else - e1000_led_on(&adapter->hw); + e1000_led_on(hw); mod_timer(&adapter->blink_timer, jiffies + E1000_ID_INTERVAL); } -static int -e1000_phys_id(struct net_device *netdev, u32 data) +static int e1000_phys_id(struct net_device *netdev, u32 data) { struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; if (!data) data = INT_MAX; - if (adapter->hw.mac_type < e1000_82571) { + if (hw->mac_type < e1000_82571) { if (!adapter->blink_timer.function) { init_timer(&adapter->blink_timer); adapter->blink_timer.function = e1000_led_blink_callback; - adapter->blink_timer.data = (unsigned long) adapter; + adapter->blink_timer.data = (unsigned long)adapter; } - e1000_setup_led(&adapter->hw); + e1000_setup_led(hw); mod_timer(&adapter->blink_timer, jiffies); msleep_interruptible(data * 1000); del_timer_sync(&adapter->blink_timer); - } else if (adapter->hw.phy_type == e1000_phy_ife) { + } else if (hw->phy_type == e1000_phy_ife) { if (!adapter->blink_timer.function) { init_timer(&adapter->blink_timer); adapter->blink_timer.function = e1000_led_blink_callback; - adapter->blink_timer.data = (unsigned long) adapter; + adapter->blink_timer.data = (unsigned long)adapter; } mod_timer(&adapter->blink_timer, jiffies); msleep_interruptible(data * 1000); del_timer_sync(&adapter->blink_timer); e1000_write_phy_reg(&(adapter->hw), IFE_PHY_SPECIAL_CONTROL_LED, 0); } else { - e1000_blink_led_start(&adapter->hw); + e1000_blink_led_start(hw); msleep_interruptible(data * 1000); } - e1000_led_off(&adapter->hw); + e1000_led_off(hw); clear_bit(E1000_LED_ON, &adapter->led_status); - e1000_cleanup_led(&adapter->hw); + e1000_cleanup_led(hw); return 0; } -static int -e1000_nway_reset(struct net_device *netdev) +static int e1000_nway_reset(struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); if (netif_running(netdev)) @@ -1925,9 +1908,8 @@ e1000_nway_reset(struct net_device *netdev) return 0; } -static void -e1000_get_ethtool_stats(struct net_device *netdev, - struct ethtool_stats *stats, u64 *data) +static void e1000_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data) { struct e1000_adapter *adapter = netdev_priv(netdev); int i; @@ -1941,8 +1923,8 @@ e1000_get_ethtool_stats(struct net_device *netdev, /* BUG_ON(i != E1000_STATS_LEN); */ } -static void -e1000_get_strings(struct net_device *netdev, u32 stringset, u8 *data) +static void e1000_get_strings(struct net_device *netdev, u32 stringset, + u8 *data) { u8 *p = data; int i; diff --git a/drivers/net/e1000/e1000_hw.c b/drivers/net/e1000/e1000_hw.c index 9a4b6cbddf2c..9d6edf3e73f9 100644 --- a/drivers/net/e1000/e1000_hw.c +++ b/drivers/net/e1000/e1000_hw.c @@ -42,48 +42,65 @@ static void e1000_release_software_semaphore(struct e1000_hw *hw); static u8 e1000_arc_subsystem_valid(struct e1000_hw *hw); static s32 e1000_check_downshift(struct e1000_hw *hw); -static s32 e1000_check_polarity(struct e1000_hw *hw, e1000_rev_polarity *polarity); +static s32 e1000_check_polarity(struct e1000_hw *hw, + e1000_rev_polarity *polarity); static void e1000_clear_hw_cntrs(struct e1000_hw *hw); static void e1000_clear_vfta(struct e1000_hw *hw); static s32 e1000_commit_shadow_ram(struct e1000_hw *hw); static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, - bool link_up); + bool link_up); static s32 e1000_config_fc_after_link_up(struct e1000_hw *hw); static s32 e1000_detect_gig_phy(struct e1000_hw *hw); static s32 e1000_erase_ich8_4k_segment(struct e1000_hw *hw, u32 bank); static s32 e1000_get_auto_rd_done(struct e1000_hw *hw); -static s32 e1000_get_cable_length(struct e1000_hw *hw, u16 *min_length, u16 *max_length); +static s32 e1000_get_cable_length(struct e1000_hw *hw, u16 *min_length, + u16 *max_length); static s32 e1000_get_hw_eeprom_semaphore(struct e1000_hw *hw); static s32 e1000_get_phy_cfg_done(struct e1000_hw *hw); static s32 e1000_get_software_flag(struct e1000_hw *hw); static s32 e1000_ich8_cycle_init(struct e1000_hw *hw); static s32 e1000_ich8_flash_cycle(struct e1000_hw *hw, u32 timeout); static s32 e1000_id_led_init(struct e1000_hw *hw); -static s32 e1000_init_lcd_from_nvm_config_region(struct e1000_hw *hw, u32 cnf_base_addr, u32 cnf_size); +static s32 e1000_init_lcd_from_nvm_config_region(struct e1000_hw *hw, + u32 cnf_base_addr, + u32 cnf_size); static s32 e1000_init_lcd_from_nvm(struct e1000_hw *hw); static void e1000_init_rx_addrs(struct e1000_hw *hw); static void e1000_initialize_hardware_bits(struct e1000_hw *hw); static bool e1000_is_onboard_nvm_eeprom(struct e1000_hw *hw); static s32 e1000_kumeran_lock_loss_workaround(struct e1000_hw *hw); static s32 e1000_mng_enable_host_if(struct e1000_hw *hw); -static s32 e1000_mng_host_if_write(struct e1000_hw *hw, u8 *buffer, u16 length, u16 offset, u8 *sum); -static s32 e1000_mng_write_cmd_header(struct e1000_hw* hw, struct e1000_host_mng_command_header* hdr); +static s32 e1000_mng_host_if_write(struct e1000_hw *hw, u8 *buffer, u16 length, + u16 offset, u8 *sum); +static s32 e1000_mng_write_cmd_header(struct e1000_hw* hw, + struct e1000_host_mng_command_header + *hdr); static s32 e1000_mng_write_commit(struct e1000_hw *hw); -static s32 e1000_phy_ife_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info); -static s32 e1000_phy_igp_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info); -static s32 e1000_read_eeprom_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); -static s32 e1000_write_eeprom_eewr(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); +static s32 e1000_phy_ife_get_info(struct e1000_hw *hw, + struct e1000_phy_info *phy_info); +static s32 e1000_phy_igp_get_info(struct e1000_hw *hw, + struct e1000_phy_info *phy_info); +static s32 e1000_read_eeprom_eerd(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data); +static s32 e1000_write_eeprom_eewr(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data); static s32 e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int eerd); -static s32 e1000_phy_m88_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info); +static s32 e1000_phy_m88_get_info(struct e1000_hw *hw, + struct e1000_phy_info *phy_info); static void e1000_put_hw_eeprom_semaphore(struct e1000_hw *hw); static s32 e1000_read_ich8_byte(struct e1000_hw *hw, u32 index, u8 *data); -static s32 e1000_verify_write_ich8_byte(struct e1000_hw *hw, u32 index, u8 byte); +static s32 e1000_verify_write_ich8_byte(struct e1000_hw *hw, u32 index, + u8 byte); static s32 e1000_write_ich8_byte(struct e1000_hw *hw, u32 index, u8 byte); static s32 e1000_read_ich8_word(struct e1000_hw *hw, u32 index, u16 *data); -static s32 e1000_read_ich8_data(struct e1000_hw *hw, u32 index, u32 size, u16 *data); -static s32 e1000_write_ich8_data(struct e1000_hw *hw, u32 index, u32 size, u16 data); -static s32 e1000_read_eeprom_ich8(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); -static s32 e1000_write_eeprom_ich8(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); +static s32 e1000_read_ich8_data(struct e1000_hw *hw, u32 index, u32 size, + u16 *data); +static s32 e1000_write_ich8_data(struct e1000_hw *hw, u32 index, u32 size, + u16 data); +static s32 e1000_read_eeprom_ich8(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data); +static s32 e1000_write_eeprom_ich8(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data); static void e1000_release_software_flag(struct e1000_hw *hw); static s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active); static s32 e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active); @@ -101,23 +118,21 @@ static s32 e1000_config_mac_to_phy(struct e1000_hw *hw); static void e1000_raise_mdi_clk(struct e1000_hw *hw, u32 *ctrl); static void e1000_lower_mdi_clk(struct e1000_hw *hw, u32 *ctrl); static void e1000_shift_out_mdi_bits(struct e1000_hw *hw, u32 data, - u16 count); + u16 count); static u16 e1000_shift_in_mdi_bits(struct e1000_hw *hw); static s32 e1000_phy_reset_dsp(struct e1000_hw *hw); static s32 e1000_write_eeprom_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); -static s32 e1000_write_eeprom_microwire(struct e1000_hw *hw, - u16 offset, u16 words, - u16 *data); +static s32 e1000_write_eeprom_microwire(struct e1000_hw *hw, u16 offset, + u16 words, u16 *data); static s32 e1000_spi_eeprom_ready(struct e1000_hw *hw); static void e1000_raise_ee_clk(struct e1000_hw *hw, u32 *eecd); static void e1000_lower_ee_clk(struct e1000_hw *hw, u32 *eecd); -static void e1000_shift_out_ee_bits(struct e1000_hw *hw, u16 data, - u16 count); +static void e1000_shift_out_ee_bits(struct e1000_hw *hw, u16 data, u16 count); static s32 e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, - u16 phy_data); + u16 phy_data); static s32 e1000_read_phy_reg_ex(struct e1000_hw *hw,u32 reg_addr, - u16 *phy_data); + u16 *phy_data); static u16 e1000_shift_in_ee_bits(struct e1000_hw *hw, u16 count); static s32 e1000_acquire_eeprom(struct e1000_hw *hw); static void e1000_release_eeprom(struct e1000_hw *hw); @@ -127,8 +142,7 @@ static s32 e1000_polarity_reversal_workaround(struct e1000_hw *hw); static s32 e1000_set_phy_mode(struct e1000_hw *hw); static s32 e1000_host_if_read_cookie(struct e1000_hw *hw, u8 *buffer); static u8 e1000_calculate_mng_checksum(char *buffer, u32 length); -static s32 e1000_configure_kmrn_for_10_100(struct e1000_hw *hw, - u16 duplex); +static s32 e1000_configure_kmrn_for_10_100(struct e1000_hw *hw, u16 duplex); static s32 e1000_configure_kmrn_for_1000(struct e1000_hw *hw); /* IGP cable length table */ @@ -159,8 +173,7 @@ u16 e1000_igp_2_cable_length_table[IGP02E1000_AGC_LENGTH_TABLE_SIZE] = * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -static s32 -e1000_set_phy_type(struct e1000_hw *hw) +static s32 e1000_set_phy_type(struct e1000_hw *hw) { DEBUGFUNC("e1000_set_phy_type"); @@ -210,8 +223,7 @@ e1000_set_phy_type(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -static void -e1000_phy_init_script(struct e1000_hw *hw) +static void e1000_phy_init_script(struct e1000_hw *hw) { u32 ret_val; u16 phy_saved_data; @@ -306,8 +318,7 @@ e1000_phy_init_script(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -s32 -e1000_set_mac_type(struct e1000_hw *hw) +s32 e1000_set_mac_type(struct e1000_hw *hw) { DEBUGFUNC("e1000_set_mac_type"); @@ -474,8 +485,7 @@ e1000_set_mac_type(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code * **************************************************************************/ -void -e1000_set_media_type(struct e1000_hw *hw) +void e1000_set_media_type(struct e1000_hw *hw) { u32 status; @@ -510,7 +520,7 @@ e1000_set_media_type(struct e1000_hw *hw) hw->media_type = e1000_media_type_copper; break; default: - status = E1000_READ_REG(hw, STATUS); + status = er32(STATUS); if (status & E1000_STATUS_TBIMODE) { hw->media_type = e1000_media_type_fiber; /* tbi_compatibility not valid on fiber */ @@ -528,8 +538,7 @@ e1000_set_media_type(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -s32 -e1000_reset_hw(struct e1000_hw *hw) +s32 e1000_reset_hw(struct e1000_hw *hw) { u32 ctrl; u32 ctrl_ext; @@ -559,15 +568,15 @@ e1000_reset_hw(struct e1000_hw *hw) /* Clear interrupt mask to stop board from generating interrupts */ DEBUGOUT("Masking off all interrupts\n"); - E1000_WRITE_REG(hw, IMC, 0xffffffff); + ew32(IMC, 0xffffffff); /* Disable the Transmit and Receive units. Then delay to allow * any pending transactions to complete before we hit the MAC with * the global reset. */ - E1000_WRITE_REG(hw, RCTL, 0); - E1000_WRITE_REG(hw, TCTL, E1000_TCTL_PSP); - E1000_WRITE_FLUSH(hw); + ew32(RCTL, 0); + ew32(TCTL, E1000_TCTL_PSP); + E1000_WRITE_FLUSH(); /* The tbi_compatibility_on Flag must be cleared when Rctl is cleared. */ hw->tbi_compatibility_on = false; @@ -577,11 +586,11 @@ e1000_reset_hw(struct e1000_hw *hw) */ msleep(10); - ctrl = E1000_READ_REG(hw, CTRL); + ctrl = er32(CTRL); /* Must reset the PHY before resetting the MAC */ if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { - E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_PHY_RST)); + ew32(CTRL, (ctrl | E1000_CTRL_PHY_RST)); msleep(5); } @@ -590,12 +599,12 @@ e1000_reset_hw(struct e1000_hw *hw) if (hw->mac_type == e1000_82573) { timeout = 10; - extcnf_ctrl = E1000_READ_REG(hw, EXTCNF_CTRL); + extcnf_ctrl = er32(EXTCNF_CTRL); extcnf_ctrl |= E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP; do { - E1000_WRITE_REG(hw, EXTCNF_CTRL, extcnf_ctrl); - extcnf_ctrl = E1000_READ_REG(hw, EXTCNF_CTRL); + ew32(EXTCNF_CTRL, extcnf_ctrl); + extcnf_ctrl = er32(EXTCNF_CTRL); if (extcnf_ctrl & E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP) break; @@ -610,9 +619,9 @@ e1000_reset_hw(struct e1000_hw *hw) /* Workaround for ICH8 bit corruption issue in FIFO memory */ if (hw->mac_type == e1000_ich8lan) { /* Set Tx and Rx buffer allocation to 8k apiece. */ - E1000_WRITE_REG(hw, PBA, E1000_PBA_8K); + ew32(PBA, E1000_PBA_8K); /* Set Packet Buffer Size to 16k. */ - E1000_WRITE_REG(hw, PBS, E1000_PBS_16K); + ew32(PBS, E1000_PBS_16K); } /* Issue a global reset to the MAC. This will reset the chip's @@ -636,7 +645,7 @@ e1000_reset_hw(struct e1000_hw *hw) case e1000_82545_rev_3: case e1000_82546_rev_3: /* Reset is performed on a shadow of the control register */ - E1000_WRITE_REG(hw, CTRL_DUP, (ctrl | E1000_CTRL_RST)); + ew32(CTRL_DUP, (ctrl | E1000_CTRL_RST)); break; case e1000_ich8lan: if (!hw->phy_reset_disable && @@ -649,11 +658,11 @@ e1000_reset_hw(struct e1000_hw *hw) } e1000_get_software_flag(hw); - E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_RST)); + ew32(CTRL, (ctrl | E1000_CTRL_RST)); msleep(5); break; default: - E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_RST)); + ew32(CTRL, (ctrl | E1000_CTRL_RST)); break; } @@ -668,10 +677,10 @@ e1000_reset_hw(struct e1000_hw *hw) case e1000_82544: /* Wait for reset to complete */ udelay(10); - ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext = er32(CTRL_EXT); ctrl_ext |= E1000_CTRL_EXT_EE_RST; - E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); - E1000_WRITE_FLUSH(hw); + ew32(CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(); /* Wait for EEPROM reload */ msleep(2); break; @@ -685,10 +694,10 @@ e1000_reset_hw(struct e1000_hw *hw) case e1000_82573: if (!e1000_is_onboard_nvm_eeprom(hw)) { udelay(10); - ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext = er32(CTRL_EXT); ctrl_ext |= E1000_CTRL_EXT_EE_RST; - E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); - E1000_WRITE_FLUSH(hw); + ew32(CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(); } /* fall through */ default: @@ -701,27 +710,27 @@ e1000_reset_hw(struct e1000_hw *hw) /* Disable HW ARPs on ASF enabled adapters */ if (hw->mac_type >= e1000_82540 && hw->mac_type <= e1000_82547_rev_2) { - manc = E1000_READ_REG(hw, MANC); + manc = er32(MANC); manc &= ~(E1000_MANC_ARP_EN); - E1000_WRITE_REG(hw, MANC, manc); + ew32(MANC, manc); } if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { e1000_phy_init_script(hw); /* Configure activity LED after PHY reset */ - led_ctrl = E1000_READ_REG(hw, LEDCTL); + led_ctrl = er32(LEDCTL); led_ctrl &= IGP_ACTIVITY_LED_MASK; led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); - E1000_WRITE_REG(hw, LEDCTL, led_ctrl); + ew32(LEDCTL, led_ctrl); } /* Clear interrupt mask to stop board from generating interrupts */ DEBUGOUT("Masking off all interrupts\n"); - E1000_WRITE_REG(hw, IMC, 0xffffffff); + ew32(IMC, 0xffffffff); /* Clear any pending interrupt events. */ - icr = E1000_READ_REG(hw, ICR); + icr = er32(ICR); /* If MWI was previously enabled, reenable it. */ if (hw->mac_type == e1000_82542_rev2_0) { @@ -730,9 +739,9 @@ e1000_reset_hw(struct e1000_hw *hw) } if (hw->mac_type == e1000_ich8lan) { - u32 kab = E1000_READ_REG(hw, KABGTXD); + u32 kab = er32(KABGTXD); kab |= E1000_KABGTXD_BGSQLBIAS; - E1000_WRITE_REG(hw, KABGTXD, kab); + ew32(KABGTXD, kab); } return E1000_SUCCESS; @@ -747,8 +756,7 @@ e1000_reset_hw(struct e1000_hw *hw) * This function contains hardware limitation workarounds for PCI-E adapters * *****************************************************************************/ -static void -e1000_initialize_hardware_bits(struct e1000_hw *hw) +static void e1000_initialize_hardware_bits(struct e1000_hw *hw) { if ((hw->mac_type >= e1000_82571) && (!hw->initialize_hw_bits_disable)) { /* Settings common to all PCI-express silicon */ @@ -758,22 +766,22 @@ e1000_initialize_hardware_bits(struct e1000_hw *hw) u32 reg_txdctl, reg_txdctl1; /* link autonegotiation/sync workarounds */ - reg_tarc0 = E1000_READ_REG(hw, TARC0); + reg_tarc0 = er32(TARC0); reg_tarc0 &= ~((1 << 30)|(1 << 29)|(1 << 28)|(1 << 27)); /* Enable not-done TX descriptor counting */ - reg_txdctl = E1000_READ_REG(hw, TXDCTL); + reg_txdctl = er32(TXDCTL); reg_txdctl |= E1000_TXDCTL_COUNT_DESC; - E1000_WRITE_REG(hw, TXDCTL, reg_txdctl); - reg_txdctl1 = E1000_READ_REG(hw, TXDCTL1); + ew32(TXDCTL, reg_txdctl); + reg_txdctl1 = er32(TXDCTL1); reg_txdctl1 |= E1000_TXDCTL_COUNT_DESC; - E1000_WRITE_REG(hw, TXDCTL1, reg_txdctl1); + ew32(TXDCTL1, reg_txdctl1); switch (hw->mac_type) { case e1000_82571: case e1000_82572: /* Clear PHY TX compatible mode bits */ - reg_tarc1 = E1000_READ_REG(hw, TARC1); + reg_tarc1 = er32(TARC1); reg_tarc1 &= ~((1 << 30)|(1 << 29)); /* link autonegotiation/sync workarounds */ @@ -783,25 +791,25 @@ e1000_initialize_hardware_bits(struct e1000_hw *hw) reg_tarc1 |= ((1 << 26)|(1 << 25)|(1 << 24)); /* Multiple read bit is reversed polarity */ - reg_tctl = E1000_READ_REG(hw, TCTL); + reg_tctl = er32(TCTL); if (reg_tctl & E1000_TCTL_MULR) reg_tarc1 &= ~(1 << 28); else reg_tarc1 |= (1 << 28); - E1000_WRITE_REG(hw, TARC1, reg_tarc1); + ew32(TARC1, reg_tarc1); break; case e1000_82573: - reg_ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + reg_ctrl_ext = er32(CTRL_EXT); reg_ctrl_ext &= ~(1 << 23); reg_ctrl_ext |= (1 << 22); /* TX byte count fix */ - reg_ctrl = E1000_READ_REG(hw, CTRL); + reg_ctrl = er32(CTRL); reg_ctrl &= ~(1 << 29); - E1000_WRITE_REG(hw, CTRL_EXT, reg_ctrl_ext); - E1000_WRITE_REG(hw, CTRL, reg_ctrl); + ew32(CTRL_EXT, reg_ctrl_ext); + ew32(CTRL, reg_ctrl); break; case e1000_80003es2lan: /* improve small packet performace for fiber/serdes */ @@ -811,14 +819,14 @@ e1000_initialize_hardware_bits(struct e1000_hw *hw) } /* Multiple read bit is reversed polarity */ - reg_tctl = E1000_READ_REG(hw, TCTL); - reg_tarc1 = E1000_READ_REG(hw, TARC1); + reg_tctl = er32(TCTL); + reg_tarc1 = er32(TARC1); if (reg_tctl & E1000_TCTL_MULR) reg_tarc1 &= ~(1 << 28); else reg_tarc1 |= (1 << 28); - E1000_WRITE_REG(hw, TARC1, reg_tarc1); + ew32(TARC1, reg_tarc1); break; case e1000_ich8lan: /* Reduce concurrent DMA requests to 3 from 4 */ @@ -827,16 +835,16 @@ e1000_initialize_hardware_bits(struct e1000_hw *hw) (hw->device_id != E1000_DEV_ID_ICH8_IGP_M))) reg_tarc0 |= ((1 << 29)|(1 << 28)); - reg_ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + reg_ctrl_ext = er32(CTRL_EXT); reg_ctrl_ext |= (1 << 22); - E1000_WRITE_REG(hw, CTRL_EXT, reg_ctrl_ext); + ew32(CTRL_EXT, reg_ctrl_ext); /* workaround TX hang with TSO=on */ reg_tarc0 |= ((1 << 27)|(1 << 26)|(1 << 24)|(1 << 23)); /* Multiple read bit is reversed polarity */ - reg_tctl = E1000_READ_REG(hw, TCTL); - reg_tarc1 = E1000_READ_REG(hw, TARC1); + reg_tctl = er32(TCTL); + reg_tarc1 = er32(TARC1); if (reg_tctl & E1000_TCTL_MULR) reg_tarc1 &= ~(1 << 28); else @@ -845,13 +853,13 @@ e1000_initialize_hardware_bits(struct e1000_hw *hw) /* workaround TX hang with TSO=on */ reg_tarc1 |= ((1 << 30)|(1 << 26)|(1 << 24)); - E1000_WRITE_REG(hw, TARC1, reg_tarc1); + ew32(TARC1, reg_tarc1); break; default: break; } - E1000_WRITE_REG(hw, TARC0, reg_tarc0); + ew32(TARC0, reg_tarc0); } } @@ -866,8 +874,7 @@ e1000_initialize_hardware_bits(struct e1000_hw *hw) * configuration and flow control settings. Clears all on-chip counters. Leaves * the transmit and receive units disabled and uninitialized. *****************************************************************************/ -s32 -e1000_init_hw(struct e1000_hw *hw) +s32 e1000_init_hw(struct e1000_hw *hw) { u32 ctrl; u32 i; @@ -883,9 +890,9 @@ e1000_init_hw(struct e1000_hw *hw) ((hw->revision_id < 3) || ((hw->device_id != E1000_DEV_ID_ICH8_IGP_M_AMT) && (hw->device_id != E1000_DEV_ID_ICH8_IGP_M)))) { - reg_data = E1000_READ_REG(hw, STATUS); + reg_data = er32(STATUS); reg_data &= ~0x80000000; - E1000_WRITE_REG(hw, STATUS, reg_data); + ew32(STATUS, reg_data); } /* Initialize Identification LED */ @@ -906,7 +913,7 @@ e1000_init_hw(struct e1000_hw *hw) /* VET hardcoded to standard value and VFTA removed in ICH8 LAN */ if (hw->mac_type != e1000_ich8lan) { if (hw->mac_type < e1000_82545_rev_3) - E1000_WRITE_REG(hw, VET, 0); + ew32(VET, 0); e1000_clear_vfta(hw); } @@ -914,8 +921,8 @@ e1000_init_hw(struct e1000_hw *hw) if (hw->mac_type == e1000_82542_rev2_0) { DEBUGOUT("Disabling MWI on 82542 rev 2.0\n"); e1000_pci_clear_mwi(hw); - E1000_WRITE_REG(hw, RCTL, E1000_RCTL_RST); - E1000_WRITE_FLUSH(hw); + ew32(RCTL, E1000_RCTL_RST); + E1000_WRITE_FLUSH(); msleep(5); } @@ -926,8 +933,8 @@ e1000_init_hw(struct e1000_hw *hw) /* For 82542 (rev 2.0), take the receiver out of reset and enable MWI */ if (hw->mac_type == e1000_82542_rev2_0) { - E1000_WRITE_REG(hw, RCTL, 0); - E1000_WRITE_FLUSH(hw); + ew32(RCTL, 0); + E1000_WRITE_FLUSH(); msleep(1); if (hw->pci_cmd_word & PCI_COMMAND_INVALIDATE) e1000_pci_set_mwi(hw); @@ -942,7 +949,7 @@ e1000_init_hw(struct e1000_hw *hw) E1000_WRITE_REG_ARRAY(hw, MTA, i, 0); /* use write flush to prevent Memory Write Block (MWB) from * occuring when accessing our register space */ - E1000_WRITE_FLUSH(hw); + E1000_WRITE_FLUSH(); } /* Set the PCI priority bit correctly in the CTRL register. This @@ -951,8 +958,8 @@ e1000_init_hw(struct e1000_hw *hw) * 82542 and 82543 silicon. */ if (hw->dma_fairness && hw->mac_type <= e1000_82543) { - ctrl = E1000_READ_REG(hw, CTRL); - E1000_WRITE_REG(hw, CTRL, ctrl | E1000_CTRL_PRIOR); + ctrl = er32(CTRL); + ew32(CTRL, ctrl | E1000_CTRL_PRIOR); } switch (hw->mac_type) { @@ -975,9 +982,9 @@ e1000_init_hw(struct e1000_hw *hw) /* Set the transmit descriptor write-back policy */ if (hw->mac_type > e1000_82544) { - ctrl = E1000_READ_REG(hw, TXDCTL); + ctrl = er32(TXDCTL); ctrl = (ctrl & ~E1000_TXDCTL_WTHRESH) | E1000_TXDCTL_FULL_TX_DESC_WB; - E1000_WRITE_REG(hw, TXDCTL, ctrl); + ew32(TXDCTL, ctrl); } if (hw->mac_type == e1000_82573) { @@ -989,21 +996,21 @@ e1000_init_hw(struct e1000_hw *hw) break; case e1000_80003es2lan: /* Enable retransmit on late collisions */ - reg_data = E1000_READ_REG(hw, TCTL); + reg_data = er32(TCTL); reg_data |= E1000_TCTL_RTLC; - E1000_WRITE_REG(hw, TCTL, reg_data); + ew32(TCTL, reg_data); /* Configure Gigabit Carry Extend Padding */ - reg_data = E1000_READ_REG(hw, TCTL_EXT); + reg_data = er32(TCTL_EXT); reg_data &= ~E1000_TCTL_EXT_GCEX_MASK; reg_data |= DEFAULT_80003ES2LAN_TCTL_EXT_GCEX; - E1000_WRITE_REG(hw, TCTL_EXT, reg_data); + ew32(TCTL_EXT, reg_data); /* Configure Transmit Inter-Packet Gap */ - reg_data = E1000_READ_REG(hw, TIPG); + reg_data = er32(TIPG); reg_data &= ~E1000_TIPG_IPGT_MASK; reg_data |= DEFAULT_80003ES2LAN_TIPG_IPGT_1000; - E1000_WRITE_REG(hw, TIPG, reg_data); + ew32(TIPG, reg_data); reg_data = E1000_READ_REG_ARRAY(hw, FFLT, 0x0001); reg_data &= ~0x00100000; @@ -1012,17 +1019,17 @@ e1000_init_hw(struct e1000_hw *hw) case e1000_82571: case e1000_82572: case e1000_ich8lan: - ctrl = E1000_READ_REG(hw, TXDCTL1); + ctrl = er32(TXDCTL1); ctrl = (ctrl & ~E1000_TXDCTL_WTHRESH) | E1000_TXDCTL_FULL_TX_DESC_WB; - E1000_WRITE_REG(hw, TXDCTL1, ctrl); + ew32(TXDCTL1, ctrl); break; } if (hw->mac_type == e1000_82573) { - u32 gcr = E1000_READ_REG(hw, GCR); + u32 gcr = er32(GCR); gcr |= E1000_GCR_L1_ACT_WITHOUT_L0S_RX; - E1000_WRITE_REG(hw, GCR, gcr); + ew32(GCR, gcr); } /* Clear all of the statistics registers (clear on read). It is @@ -1039,11 +1046,11 @@ e1000_init_hw(struct e1000_hw *hw) if (hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER || hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3) { - ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext = er32(CTRL_EXT); /* Relaxed ordering must be disabled to avoid a parity * error crash in a PCI slot. */ ctrl_ext |= E1000_CTRL_EXT_RO_DIS; - E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + ew32(CTRL_EXT, ctrl_ext); } return ret_val; @@ -1054,8 +1061,7 @@ e1000_init_hw(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code. *****************************************************************************/ -static s32 -e1000_adjust_serdes_amplitude(struct e1000_hw *hw) +static s32 e1000_adjust_serdes_amplitude(struct e1000_hw *hw) { u16 eeprom_data; s32 ret_val; @@ -1100,8 +1106,7 @@ e1000_adjust_serdes_amplitude(struct e1000_hw *hw) * established. Assumes the hardware has previously been reset and the * transmitter and receiver are not enabled. *****************************************************************************/ -s32 -e1000_setup_link(struct e1000_hw *hw) +s32 e1000_setup_link(struct e1000_hw *hw) { u32 ctrl_ext; s32 ret_val; @@ -1176,7 +1181,7 @@ e1000_setup_link(struct e1000_hw *hw) } ctrl_ext = ((eeprom_data & EEPROM_WORD0F_SWPDIO_EXT) << SWDPIO__EXT_SHIFT); - E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + ew32(CTRL_EXT, ctrl_ext); } /* Call the necessary subroutine to configure the link. */ @@ -1193,12 +1198,12 @@ e1000_setup_link(struct e1000_hw *hw) /* FCAL/H and FCT are hardcoded to standard values in e1000_ich8lan. */ if (hw->mac_type != e1000_ich8lan) { - E1000_WRITE_REG(hw, FCT, FLOW_CONTROL_TYPE); - E1000_WRITE_REG(hw, FCAH, FLOW_CONTROL_ADDRESS_HIGH); - E1000_WRITE_REG(hw, FCAL, FLOW_CONTROL_ADDRESS_LOW); + ew32(FCT, FLOW_CONTROL_TYPE); + ew32(FCAH, FLOW_CONTROL_ADDRESS_HIGH); + ew32(FCAL, FLOW_CONTROL_ADDRESS_LOW); } - E1000_WRITE_REG(hw, FCTTV, hw->fc_pause_time); + ew32(FCTTV, hw->fc_pause_time); /* Set the flow control receive threshold registers. Normally, * these registers will be set to a default threshold that may be @@ -1207,18 +1212,18 @@ e1000_setup_link(struct e1000_hw *hw) * registers will be set to 0. */ if (!(hw->fc & E1000_FC_TX_PAUSE)) { - E1000_WRITE_REG(hw, FCRTL, 0); - E1000_WRITE_REG(hw, FCRTH, 0); + ew32(FCRTL, 0); + ew32(FCRTH, 0); } else { /* We need to set up the Receive Threshold high and low water marks * as well as (optionally) enabling the transmission of XON frames. */ if (hw->fc_send_xon) { - E1000_WRITE_REG(hw, FCRTL, (hw->fc_low_water | E1000_FCRTL_XONE)); - E1000_WRITE_REG(hw, FCRTH, hw->fc_high_water); + ew32(FCRTL, (hw->fc_low_water | E1000_FCRTL_XONE)); + ew32(FCRTH, hw->fc_high_water); } else { - E1000_WRITE_REG(hw, FCRTL, hw->fc_low_water); - E1000_WRITE_REG(hw, FCRTH, hw->fc_high_water); + ew32(FCRTL, hw->fc_low_water); + ew32(FCRTH, hw->fc_high_water); } } return ret_val; @@ -1233,8 +1238,7 @@ e1000_setup_link(struct e1000_hw *hw) * link. Assumes the hardware has been previously reset and the transmitter * and receiver are not enabled. *****************************************************************************/ -static s32 -e1000_setup_fiber_serdes_link(struct e1000_hw *hw) +static s32 e1000_setup_fiber_serdes_link(struct e1000_hw *hw) { u32 ctrl; u32 status; @@ -1251,7 +1255,7 @@ e1000_setup_fiber_serdes_link(struct e1000_hw *hw) * loopback mode is disabled during initialization. */ if (hw->mac_type == e1000_82571 || hw->mac_type == e1000_82572) - E1000_WRITE_REG(hw, SCTL, E1000_DISABLE_SERDES_LOOPBACK); + ew32(SCTL, E1000_DISABLE_SERDES_LOOPBACK); /* On adapters with a MAC newer than 82544, SWDP 1 will be * set when the optics detect a signal. On older adapters, it will be @@ -1259,7 +1263,7 @@ e1000_setup_fiber_serdes_link(struct e1000_hw *hw) * If we're on serdes media, adjust the output amplitude to value * set in the EEPROM. */ - ctrl = E1000_READ_REG(hw, CTRL); + ctrl = er32(CTRL); if (hw->media_type == e1000_media_type_fiber) signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0; @@ -1330,9 +1334,9 @@ e1000_setup_fiber_serdes_link(struct e1000_hw *hw) */ DEBUGOUT("Auto-negotiation enabled\n"); - E1000_WRITE_REG(hw, TXCW, txcw); - E1000_WRITE_REG(hw, CTRL, ctrl); - E1000_WRITE_FLUSH(hw); + ew32(TXCW, txcw); + ew32(CTRL, ctrl); + E1000_WRITE_FLUSH(); hw->txcw = txcw; msleep(1); @@ -1344,11 +1348,11 @@ e1000_setup_fiber_serdes_link(struct e1000_hw *hw) * For internal serdes, we just assume a signal is present, then poll. */ if (hw->media_type == e1000_media_type_internal_serdes || - (E1000_READ_REG(hw, CTRL) & E1000_CTRL_SWDPIN1) == signal) { + (er32(CTRL) & E1000_CTRL_SWDPIN1) == signal) { DEBUGOUT("Looking for Link\n"); for (i = 0; i < (LINK_UP_TIMEOUT / 10); i++) { msleep(10); - status = E1000_READ_REG(hw, STATUS); + status = er32(STATUS); if (status & E1000_STATUS_LU) break; } if (i == (LINK_UP_TIMEOUT / 10)) { @@ -1380,8 +1384,7 @@ e1000_setup_fiber_serdes_link(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code ******************************************************************************/ -static s32 -e1000_copper_link_preconfig(struct e1000_hw *hw) +static s32 e1000_copper_link_preconfig(struct e1000_hw *hw) { u32 ctrl; s32 ret_val; @@ -1389,7 +1392,7 @@ e1000_copper_link_preconfig(struct e1000_hw *hw) DEBUGFUNC("e1000_copper_link_preconfig"); - ctrl = E1000_READ_REG(hw, CTRL); + ctrl = er32(CTRL); /* With 82543, we need to force speed and duplex on the MAC equal to what * the PHY speed and duplex configuration is. In addition, we need to * perform a hardware reset on the PHY to take it out of reset. @@ -1397,10 +1400,10 @@ e1000_copper_link_preconfig(struct e1000_hw *hw) if (hw->mac_type > e1000_82543) { ctrl |= E1000_CTRL_SLU; ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); - E1000_WRITE_REG(hw, CTRL, ctrl); + ew32(CTRL, ctrl); } else { ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX | E1000_CTRL_SLU); - E1000_WRITE_REG(hw, CTRL, ctrl); + ew32(CTRL, ctrl); ret_val = e1000_phy_hw_reset(hw); if (ret_val) return ret_val; @@ -1440,8 +1443,7 @@ e1000_copper_link_preconfig(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code *********************************************************************/ -static s32 -e1000_copper_link_igp_setup(struct e1000_hw *hw) +static s32 e1000_copper_link_igp_setup(struct e1000_hw *hw) { u32 led_ctrl; s32 ret_val; @@ -1462,10 +1464,10 @@ e1000_copper_link_igp_setup(struct e1000_hw *hw) msleep(15); if (hw->mac_type != e1000_ich8lan) { /* Configure activity LED after PHY reset */ - led_ctrl = E1000_READ_REG(hw, LEDCTL); + led_ctrl = er32(LEDCTL); led_ctrl &= IGP_ACTIVITY_LED_MASK; led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); - E1000_WRITE_REG(hw, LEDCTL, led_ctrl); + ew32(LEDCTL, led_ctrl); } /* The NVM settings will configure LPLU in D3 for IGP2 and IGP3 PHYs */ @@ -1587,8 +1589,7 @@ e1000_copper_link_igp_setup(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code *********************************************************************/ -static s32 -e1000_copper_link_ggp_setup(struct e1000_hw *hw) +static s32 e1000_copper_link_ggp_setup(struct e1000_hw *hw) { s32 ret_val; u16 phy_data; @@ -1679,9 +1680,9 @@ e1000_copper_link_ggp_setup(struct e1000_hw *hw) if (ret_val) return ret_val; - reg_data = E1000_READ_REG(hw, CTRL_EXT); + reg_data = er32(CTRL_EXT); reg_data &= ~(E1000_CTRL_EXT_LINK_MODE_MASK); - E1000_WRITE_REG(hw, CTRL_EXT, reg_data); + ew32(CTRL_EXT, reg_data); ret_val = e1000_read_phy_reg(hw, GG82563_PHY_PWR_MGMT_CTRL, &phy_data); @@ -1735,8 +1736,7 @@ e1000_copper_link_ggp_setup(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code *********************************************************************/ -static s32 -e1000_copper_link_mgp_setup(struct e1000_hw *hw) +static s32 e1000_copper_link_mgp_setup(struct e1000_hw *hw) { s32 ret_val; u16 phy_data; @@ -1839,8 +1839,7 @@ e1000_copper_link_mgp_setup(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code *********************************************************************/ -static s32 -e1000_copper_link_autoneg(struct e1000_hw *hw) +static s32 e1000_copper_link_autoneg(struct e1000_hw *hw) { s32 ret_val; u16 phy_data; @@ -1910,8 +1909,7 @@ e1000_copper_link_autoneg(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code ******************************************************************************/ -static s32 -e1000_copper_link_postconfig(struct e1000_hw *hw) +static s32 e1000_copper_link_postconfig(struct e1000_hw *hw) { s32 ret_val; DEBUGFUNC("e1000_copper_link_postconfig"); @@ -1948,8 +1946,7 @@ e1000_copper_link_postconfig(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code ******************************************************************************/ -static s32 -e1000_setup_copper_link(struct e1000_hw *hw) +static s32 e1000_setup_copper_link(struct e1000_hw *hw) { s32 ret_val; u16 i; @@ -2062,8 +2059,7 @@ e1000_setup_copper_link(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code ******************************************************************************/ -static s32 -e1000_configure_kmrn_for_10_100(struct e1000_hw *hw, u16 duplex) +static s32 e1000_configure_kmrn_for_10_100(struct e1000_hw *hw, u16 duplex) { s32 ret_val = E1000_SUCCESS; u32 tipg; @@ -2078,10 +2074,10 @@ e1000_configure_kmrn_for_10_100(struct e1000_hw *hw, u16 duplex) return ret_val; /* Configure Transmit Inter-Packet Gap */ - tipg = E1000_READ_REG(hw, TIPG); + tipg = er32(TIPG); tipg &= ~E1000_TIPG_IPGT_MASK; tipg |= DEFAULT_80003ES2LAN_TIPG_IPGT_10_100; - E1000_WRITE_REG(hw, TIPG, tipg); + ew32(TIPG, tipg); ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data); @@ -2098,8 +2094,7 @@ e1000_configure_kmrn_for_10_100(struct e1000_hw *hw, u16 duplex) return ret_val; } -static s32 -e1000_configure_kmrn_for_1000(struct e1000_hw *hw) +static s32 e1000_configure_kmrn_for_1000(struct e1000_hw *hw) { s32 ret_val = E1000_SUCCESS; u16 reg_data; @@ -2114,10 +2109,10 @@ e1000_configure_kmrn_for_1000(struct e1000_hw *hw) return ret_val; /* Configure Transmit Inter-Packet Gap */ - tipg = E1000_READ_REG(hw, TIPG); + tipg = er32(TIPG); tipg &= ~E1000_TIPG_IPGT_MASK; tipg |= DEFAULT_80003ES2LAN_TIPG_IPGT_1000; - E1000_WRITE_REG(hw, TIPG, tipg); + ew32(TIPG, tipg); ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data); @@ -2135,8 +2130,7 @@ e1000_configure_kmrn_for_1000(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code ******************************************************************************/ -s32 -e1000_phy_setup_autoneg(struct e1000_hw *hw) +s32 e1000_phy_setup_autoneg(struct e1000_hw *hw) { s32 ret_val; u16 mii_autoneg_adv_reg; @@ -2284,8 +2278,7 @@ e1000_phy_setup_autoneg(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code ******************************************************************************/ -static s32 -e1000_phy_force_speed_duplex(struct e1000_hw *hw) +static s32 e1000_phy_force_speed_duplex(struct e1000_hw *hw) { u32 ctrl; s32 ret_val; @@ -2302,7 +2295,7 @@ e1000_phy_force_speed_duplex(struct e1000_hw *hw) DEBUGOUT1("hw->fc = %d\n", hw->fc); /* Read the Device Control Register. */ - ctrl = E1000_READ_REG(hw, CTRL); + ctrl = er32(CTRL); /* Set the bits to Force Speed and Duplex in the Device Ctrl Reg. */ ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); @@ -2357,7 +2350,7 @@ e1000_phy_force_speed_duplex(struct e1000_hw *hw) e1000_config_collision_dist(hw); /* Write the configured values back to the Device Control Reg. */ - E1000_WRITE_REG(hw, CTRL, ctrl); + ew32(CTRL, ctrl); if ((hw->phy_type == e1000_phy_m88) || (hw->phy_type == e1000_phy_gg82563)) { @@ -2535,8 +2528,7 @@ e1000_phy_force_speed_duplex(struct e1000_hw *hw) * Link should have been established previously. Reads the speed and duplex * information from the Device Status register. ******************************************************************************/ -void -e1000_config_collision_dist(struct e1000_hw *hw) +void e1000_config_collision_dist(struct e1000_hw *hw) { u32 tctl, coll_dist; @@ -2547,13 +2539,13 @@ e1000_config_collision_dist(struct e1000_hw *hw) else coll_dist = E1000_COLLISION_DISTANCE; - tctl = E1000_READ_REG(hw, TCTL); + tctl = er32(TCTL); tctl &= ~E1000_TCTL_COLD; tctl |= coll_dist << E1000_COLD_SHIFT; - E1000_WRITE_REG(hw, TCTL, tctl); - E1000_WRITE_FLUSH(hw); + ew32(TCTL, tctl); + E1000_WRITE_FLUSH(); } /****************************************************************************** @@ -2565,8 +2557,7 @@ e1000_config_collision_dist(struct e1000_hw *hw) * The contents of the PHY register containing the needed information need to * be passed in. ******************************************************************************/ -static s32 -e1000_config_mac_to_phy(struct e1000_hw *hw) +static s32 e1000_config_mac_to_phy(struct e1000_hw *hw) { u32 ctrl; s32 ret_val; @@ -2582,7 +2573,7 @@ e1000_config_mac_to_phy(struct e1000_hw *hw) /* Read the Device Control Register and set the bits to Force Speed * and Duplex. */ - ctrl = E1000_READ_REG(hw, CTRL); + ctrl = er32(CTRL); ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); ctrl &= ~(E1000_CTRL_SPD_SEL | E1000_CTRL_ILOS); @@ -2609,7 +2600,7 @@ e1000_config_mac_to_phy(struct e1000_hw *hw) ctrl |= E1000_CTRL_SPD_100; /* Write the configured values back to the Device Control Reg. */ - E1000_WRITE_REG(hw, CTRL, ctrl); + ew32(CTRL, ctrl); return E1000_SUCCESS; } @@ -2624,15 +2615,14 @@ e1000_config_mac_to_phy(struct e1000_hw *hw) * by the PHY rather than the MAC. Software must also configure these * bits when link is forced on a fiber connection. *****************************************************************************/ -s32 -e1000_force_mac_fc(struct e1000_hw *hw) +s32 e1000_force_mac_fc(struct e1000_hw *hw) { u32 ctrl; DEBUGFUNC("e1000_force_mac_fc"); /* Get the current configuration of the Device Control Register */ - ctrl = E1000_READ_REG(hw, CTRL); + ctrl = er32(CTRL); /* Because we didn't get link via the internal auto-negotiation * mechanism (we either forced link or we got link via PHY @@ -2676,7 +2666,7 @@ e1000_force_mac_fc(struct e1000_hw *hw) if (hw->mac_type == e1000_82542_rev2_0) ctrl &= (~E1000_CTRL_TFCE); - E1000_WRITE_REG(hw, CTRL, ctrl); + ew32(CTRL, ctrl); return E1000_SUCCESS; } @@ -2691,8 +2681,7 @@ e1000_force_mac_fc(struct e1000_hw *hw) * based on the flow control negotiated by the PHY. In TBI mode, the TFCE * and RFCE bits will be automaticaly set to the negotiated flow control mode. *****************************************************************************/ -static s32 -e1000_config_fc_after_link_up(struct e1000_hw *hw) +static s32 e1000_config_fc_after_link_up(struct e1000_hw *hw) { s32 ret_val; u16 mii_status_reg; @@ -2896,8 +2885,7 @@ e1000_config_fc_after_link_up(struct e1000_hw *hw) * * Called by any function that needs to check the link status of the adapter. *****************************************************************************/ -s32 -e1000_check_for_link(struct e1000_hw *hw) +s32 e1000_check_for_link(struct e1000_hw *hw) { u32 rxcw = 0; u32 ctrl; @@ -2910,8 +2898,8 @@ e1000_check_for_link(struct e1000_hw *hw) DEBUGFUNC("e1000_check_for_link"); - ctrl = E1000_READ_REG(hw, CTRL); - status = E1000_READ_REG(hw, STATUS); + ctrl = er32(CTRL); + status = er32(STATUS); /* On adapters with a MAC newer than 82544, SW Defineable pin 1 will be * set when the optics detect a signal. On older adapters, it will be @@ -2919,7 +2907,7 @@ e1000_check_for_link(struct e1000_hw *hw) */ if ((hw->media_type == e1000_media_type_fiber) || (hw->media_type == e1000_media_type_internal_serdes)) { - rxcw = E1000_READ_REG(hw, RXCW); + rxcw = er32(RXCW); if (hw->media_type == e1000_media_type_fiber) { signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0; @@ -2965,11 +2953,11 @@ e1000_check_for_link(struct e1000_hw *hw) (!hw->autoneg) && (hw->forced_speed_duplex == e1000_10_full || hw->forced_speed_duplex == e1000_10_half)) { - E1000_WRITE_REG(hw, IMC, 0xffffffff); + ew32(IMC, 0xffffffff); ret_val = e1000_polarity_reversal_workaround(hw); - icr = E1000_READ_REG(hw, ICR); - E1000_WRITE_REG(hw, ICS, (icr & ~E1000_ICS_LSC)); - E1000_WRITE_REG(hw, IMS, IMS_ENABLE_MASK); + icr = er32(ICR); + ew32(ICS, (icr & ~E1000_ICS_LSC)); + ew32(IMS, IMS_ENABLE_MASK); } } else { @@ -3034,9 +3022,9 @@ e1000_check_for_link(struct e1000_hw *hw) */ if (hw->tbi_compatibility_on) { /* If we previously were in the mode, turn it off. */ - rctl = E1000_READ_REG(hw, RCTL); + rctl = er32(RCTL); rctl &= ~E1000_RCTL_SBP; - E1000_WRITE_REG(hw, RCTL, rctl); + ew32(RCTL, rctl); hw->tbi_compatibility_on = false; } } else { @@ -3047,9 +3035,9 @@ e1000_check_for_link(struct e1000_hw *hw) */ if (!hw->tbi_compatibility_on) { hw->tbi_compatibility_on = true; - rctl = E1000_READ_REG(hw, RCTL); + rctl = er32(RCTL); rctl |= E1000_RCTL_SBP; - E1000_WRITE_REG(hw, RCTL, rctl); + ew32(RCTL, rctl); } } } @@ -3073,12 +3061,12 @@ e1000_check_for_link(struct e1000_hw *hw) DEBUGOUT("NOT RXing /C/, disable AutoNeg and force link.\n"); /* Disable auto-negotiation in the TXCW register */ - E1000_WRITE_REG(hw, TXCW, (hw->txcw & ~E1000_TXCW_ANE)); + ew32(TXCW, (hw->txcw & ~E1000_TXCW_ANE)); /* Force link-up and also force full-duplex. */ - ctrl = E1000_READ_REG(hw, CTRL); + ctrl = er32(CTRL); ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD); - E1000_WRITE_REG(hw, CTRL, ctrl); + ew32(CTRL, ctrl); /* Configure Flow Control after forcing link up. */ ret_val = e1000_config_fc_after_link_up(hw); @@ -3096,8 +3084,8 @@ e1000_check_for_link(struct e1000_hw *hw) (hw->media_type == e1000_media_type_internal_serdes)) && (ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { DEBUGOUT("RXing /C/, enable AutoNeg and stop forcing link.\n"); - E1000_WRITE_REG(hw, TXCW, hw->txcw); - E1000_WRITE_REG(hw, CTRL, (ctrl & ~E1000_CTRL_SLU)); + ew32(TXCW, hw->txcw); + ew32(CTRL, (ctrl & ~E1000_CTRL_SLU)); hw->serdes_link_down = false; } @@ -3105,10 +3093,10 @@ e1000_check_for_link(struct e1000_hw *hw) * based on MAC synchronization for internal serdes media type. */ else if ((hw->media_type == e1000_media_type_internal_serdes) && - !(E1000_TXCW_ANE & E1000_READ_REG(hw, TXCW))) { + !(E1000_TXCW_ANE & er32(TXCW))) { /* SYNCH bit and IV bit are sticky. */ udelay(10); - if (E1000_RXCW_SYNCH & E1000_READ_REG(hw, RXCW)) { + if (E1000_RXCW_SYNCH & er32(RXCW)) { if (!(rxcw & E1000_RXCW_IV)) { hw->serdes_link_down = false; DEBUGOUT("SERDES: Link is up.\n"); @@ -3119,8 +3107,8 @@ e1000_check_for_link(struct e1000_hw *hw) } } if ((hw->media_type == e1000_media_type_internal_serdes) && - (E1000_TXCW_ANE & E1000_READ_REG(hw, TXCW))) { - hw->serdes_link_down = !(E1000_STATUS_LU & E1000_READ_REG(hw, STATUS)); + (E1000_TXCW_ANE & er32(TXCW))) { + hw->serdes_link_down = !(E1000_STATUS_LU & er32(STATUS)); } return E1000_SUCCESS; } @@ -3132,10 +3120,7 @@ e1000_check_for_link(struct e1000_hw *hw) * speed - Speed of the connection * duplex - Duplex setting of the connection *****************************************************************************/ -s32 -e1000_get_speed_and_duplex(struct e1000_hw *hw, - u16 *speed, - u16 *duplex) +s32 e1000_get_speed_and_duplex(struct e1000_hw *hw, u16 *speed, u16 *duplex) { u32 status; s32 ret_val; @@ -3144,7 +3129,7 @@ e1000_get_speed_and_duplex(struct e1000_hw *hw, DEBUGFUNC("e1000_get_speed_and_duplex"); if (hw->mac_type >= e1000_82543) { - status = E1000_READ_REG(hw, STATUS); + status = er32(STATUS); if (status & E1000_STATUS_SPEED_1000) { *speed = SPEED_1000; DEBUGOUT("1000 Mbs, "); @@ -3214,8 +3199,7 @@ e1000_get_speed_and_duplex(struct e1000_hw *hw, * * hw - Struct containing variables accessed by shared code ******************************************************************************/ -static s32 -e1000_wait_autoneg(struct e1000_hw *hw) +static s32 e1000_wait_autoneg(struct e1000_hw *hw) { s32 ret_val; u16 i; @@ -3249,15 +3233,13 @@ e1000_wait_autoneg(struct e1000_hw *hw) * hw - Struct containing variables accessed by shared code * ctrl - Device control register's current value ******************************************************************************/ -static void -e1000_raise_mdi_clk(struct e1000_hw *hw, - u32 *ctrl) +static void e1000_raise_mdi_clk(struct e1000_hw *hw, u32 *ctrl) { /* Raise the clock input to the Management Data Clock (by setting the MDC * bit), and then delay 10 microseconds. */ - E1000_WRITE_REG(hw, CTRL, (*ctrl | E1000_CTRL_MDC)); - E1000_WRITE_FLUSH(hw); + ew32(CTRL, (*ctrl | E1000_CTRL_MDC)); + E1000_WRITE_FLUSH(); udelay(10); } @@ -3267,15 +3249,13 @@ e1000_raise_mdi_clk(struct e1000_hw *hw, * hw - Struct containing variables accessed by shared code * ctrl - Device control register's current value ******************************************************************************/ -static void -e1000_lower_mdi_clk(struct e1000_hw *hw, - u32 *ctrl) +static void e1000_lower_mdi_clk(struct e1000_hw *hw, u32 *ctrl) { /* Lower the clock input to the Management Data Clock (by clearing the MDC * bit), and then delay 10 microseconds. */ - E1000_WRITE_REG(hw, CTRL, (*ctrl & ~E1000_CTRL_MDC)); - E1000_WRITE_FLUSH(hw); + ew32(CTRL, (*ctrl & ~E1000_CTRL_MDC)); + E1000_WRITE_FLUSH(); udelay(10); } @@ -3288,10 +3268,7 @@ e1000_lower_mdi_clk(struct e1000_hw *hw, * * Bits are shifted out in MSB to LSB order. ******************************************************************************/ -static void -e1000_shift_out_mdi_bits(struct e1000_hw *hw, - u32 data, - u16 count) +static void e1000_shift_out_mdi_bits(struct e1000_hw *hw, u32 data, u16 count) { u32 ctrl; u32 mask; @@ -3303,7 +3280,7 @@ e1000_shift_out_mdi_bits(struct e1000_hw *hw, mask = 0x01; mask <<= (count - 1); - ctrl = E1000_READ_REG(hw, CTRL); + ctrl = er32(CTRL); /* Set MDIO_DIR and MDC_DIR direction bits to be used as output pins. */ ctrl |= (E1000_CTRL_MDIO_DIR | E1000_CTRL_MDC_DIR); @@ -3319,8 +3296,8 @@ e1000_shift_out_mdi_bits(struct e1000_hw *hw, else ctrl &= ~E1000_CTRL_MDIO; - E1000_WRITE_REG(hw, CTRL, ctrl); - E1000_WRITE_FLUSH(hw); + ew32(CTRL, ctrl); + E1000_WRITE_FLUSH(); udelay(10); @@ -3338,8 +3315,7 @@ e1000_shift_out_mdi_bits(struct e1000_hw *hw, * * Bits are shifted in in MSB to LSB order. ******************************************************************************/ -static u16 -e1000_shift_in_mdi_bits(struct e1000_hw *hw) +static u16 e1000_shift_in_mdi_bits(struct e1000_hw *hw) { u32 ctrl; u16 data = 0; @@ -3352,14 +3328,14 @@ e1000_shift_in_mdi_bits(struct e1000_hw *hw) * by raising the input to the Management Data Clock (setting the MDC bit), * and then reading the value of the MDIO bit. */ - ctrl = E1000_READ_REG(hw, CTRL); + ctrl = er32(CTRL); /* Clear MDIO_DIR (SWDPIO1) to indicate this bit is to be used as input. */ ctrl &= ~E1000_CTRL_MDIO_DIR; ctrl &= ~E1000_CTRL_MDIO; - E1000_WRITE_REG(hw, CTRL, ctrl); - E1000_WRITE_FLUSH(hw); + ew32(CTRL, ctrl); + E1000_WRITE_FLUSH(); /* Raise and Lower the clock before reading in the data. This accounts for * the turnaround bits. The first clock occurred when we clocked out the @@ -3371,7 +3347,7 @@ e1000_shift_in_mdi_bits(struct e1000_hw *hw) for (data = 0, i = 0; i < 16; i++) { data = data << 1; e1000_raise_mdi_clk(hw, &ctrl); - ctrl = E1000_READ_REG(hw, CTRL); + ctrl = er32(CTRL); /* Check to see if we shifted in a "1". */ if (ctrl & E1000_CTRL_MDIO) data |= 1; @@ -3384,8 +3360,7 @@ e1000_shift_in_mdi_bits(struct e1000_hw *hw) return data; } -static s32 -e1000_swfw_sync_acquire(struct e1000_hw *hw, u16 mask) +static s32 e1000_swfw_sync_acquire(struct e1000_hw *hw, u16 mask) { u32 swfw_sync = 0; u32 swmask = mask; @@ -3404,7 +3379,7 @@ e1000_swfw_sync_acquire(struct e1000_hw *hw, u16 mask) if (e1000_get_hw_eeprom_semaphore(hw)) return -E1000_ERR_SWFW_SYNC; - swfw_sync = E1000_READ_REG(hw, SW_FW_SYNC); + swfw_sync = er32(SW_FW_SYNC); if (!(swfw_sync & (fwmask | swmask))) { break; } @@ -3422,14 +3397,13 @@ e1000_swfw_sync_acquire(struct e1000_hw *hw, u16 mask) } swfw_sync |= swmask; - E1000_WRITE_REG(hw, SW_FW_SYNC, swfw_sync); + ew32(SW_FW_SYNC, swfw_sync); e1000_put_hw_eeprom_semaphore(hw); return E1000_SUCCESS; } -static void -e1000_swfw_sync_release(struct e1000_hw *hw, u16 mask) +static void e1000_swfw_sync_release(struct e1000_hw *hw, u16 mask) { u32 swfw_sync; u32 swmask = mask; @@ -3451,9 +3425,9 @@ e1000_swfw_sync_release(struct e1000_hw *hw, u16 mask) while (e1000_get_hw_eeprom_semaphore(hw) != E1000_SUCCESS); /* empty */ - swfw_sync = E1000_READ_REG(hw, SW_FW_SYNC); + swfw_sync = er32(SW_FW_SYNC); swfw_sync &= ~swmask; - E1000_WRITE_REG(hw, SW_FW_SYNC, swfw_sync); + ew32(SW_FW_SYNC, swfw_sync); e1000_put_hw_eeprom_semaphore(hw); } @@ -3464,10 +3438,7 @@ e1000_swfw_sync_release(struct e1000_hw *hw, u16 mask) * hw - Struct containing variables accessed by shared code * reg_addr - address of the PHY register to read ******************************************************************************/ -s32 -e1000_read_phy_reg(struct e1000_hw *hw, - u32 reg_addr, - u16 *phy_data) +s32 e1000_read_phy_reg(struct e1000_hw *hw, u32 reg_addr, u16 *phy_data) { u32 ret_val; u16 swfw; @@ -3475,7 +3446,7 @@ e1000_read_phy_reg(struct e1000_hw *hw, DEBUGFUNC("e1000_read_phy_reg"); if ((hw->mac_type == e1000_80003es2lan) && - (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) { + (er32(STATUS) & E1000_STATUS_FUNC_1)) { swfw = E1000_SWFW_PHY1_SM; } else { swfw = E1000_SWFW_PHY0_SM; @@ -3523,9 +3494,8 @@ e1000_read_phy_reg(struct e1000_hw *hw, return ret_val; } -static s32 -e1000_read_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, - u16 *phy_data) +static s32 e1000_read_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, + u16 *phy_data) { u32 i; u32 mdic = 0; @@ -3547,12 +3517,12 @@ e1000_read_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, (phy_addr << E1000_MDIC_PHY_SHIFT) | (E1000_MDIC_OP_READ)); - E1000_WRITE_REG(hw, MDIC, mdic); + ew32(MDIC, mdic); /* Poll the ready bit to see if the MDI read completed */ for (i = 0; i < 64; i++) { udelay(50); - mdic = E1000_READ_REG(hw, MDIC); + mdic = er32(MDIC); if (mdic & E1000_MDIC_READY) break; } if (!(mdic & E1000_MDIC_READY)) { @@ -3563,7 +3533,7 @@ e1000_read_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, DEBUGOUT("MDI Error\n"); return -E1000_ERR_PHY; } - *phy_data = (u16) mdic; + *phy_data = (u16)mdic; } else { /* We must first send a preamble through the MDIO pin to signal the * beginning of an MII instruction. This is done by sending 32 @@ -3603,9 +3573,7 @@ e1000_read_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, * reg_addr - address of the PHY register to write * data - data to write to the PHY ******************************************************************************/ -s32 -e1000_write_phy_reg(struct e1000_hw *hw, u32 reg_addr, - u16 phy_data) +s32 e1000_write_phy_reg(struct e1000_hw *hw, u32 reg_addr, u16 phy_data) { u32 ret_val; u16 swfw; @@ -3613,7 +3581,7 @@ e1000_write_phy_reg(struct e1000_hw *hw, u32 reg_addr, DEBUGFUNC("e1000_write_phy_reg"); if ((hw->mac_type == e1000_80003es2lan) && - (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) { + (er32(STATUS) & E1000_STATUS_FUNC_1)) { swfw = E1000_SWFW_PHY1_SM; } else { swfw = E1000_SWFW_PHY0_SM; @@ -3661,9 +3629,8 @@ e1000_write_phy_reg(struct e1000_hw *hw, u32 reg_addr, return ret_val; } -static s32 -e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, - u16 phy_data) +static s32 e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, + u16 phy_data) { u32 i; u32 mdic = 0; @@ -3681,17 +3648,17 @@ e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, * for the PHY register in the MDI Control register. The MAC will take * care of interfacing with the PHY to send the desired data. */ - mdic = (((u32) phy_data) | + mdic = (((u32)phy_data) | (reg_addr << E1000_MDIC_REG_SHIFT) | (phy_addr << E1000_MDIC_PHY_SHIFT) | (E1000_MDIC_OP_WRITE)); - E1000_WRITE_REG(hw, MDIC, mdic); + ew32(MDIC, mdic); /* Poll the ready bit to see if the MDI read completed */ for (i = 0; i < 641; i++) { udelay(5); - mdic = E1000_READ_REG(hw, MDIC); + mdic = er32(MDIC); if (mdic & E1000_MDIC_READY) break; } if (!(mdic & E1000_MDIC_READY)) { @@ -3715,7 +3682,7 @@ e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, mdic = ((PHY_TURNAROUND) | (reg_addr << 2) | (phy_addr << 7) | (PHY_OP_WRITE << 12) | (PHY_SOF << 14)); mdic <<= 16; - mdic |= (u32) phy_data; + mdic |= (u32)phy_data; e1000_shift_out_mdi_bits(hw, mdic, 32); } @@ -3723,17 +3690,14 @@ e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, return E1000_SUCCESS; } -static s32 -e1000_read_kmrn_reg(struct e1000_hw *hw, - u32 reg_addr, - u16 *data) +static s32 e1000_read_kmrn_reg(struct e1000_hw *hw, u32 reg_addr, u16 *data) { u32 reg_val; u16 swfw; DEBUGFUNC("e1000_read_kmrn_reg"); if ((hw->mac_type == e1000_80003es2lan) && - (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) { + (er32(STATUS) & E1000_STATUS_FUNC_1)) { swfw = E1000_SWFW_PHY1_SM; } else { swfw = E1000_SWFW_PHY0_SM; @@ -3745,28 +3709,25 @@ e1000_read_kmrn_reg(struct e1000_hw *hw, reg_val = ((reg_addr << E1000_KUMCTRLSTA_OFFSET_SHIFT) & E1000_KUMCTRLSTA_OFFSET) | E1000_KUMCTRLSTA_REN; - E1000_WRITE_REG(hw, KUMCTRLSTA, reg_val); + ew32(KUMCTRLSTA, reg_val); udelay(2); /* Read the data returned */ - reg_val = E1000_READ_REG(hw, KUMCTRLSTA); + reg_val = er32(KUMCTRLSTA); *data = (u16)reg_val; e1000_swfw_sync_release(hw, swfw); return E1000_SUCCESS; } -static s32 -e1000_write_kmrn_reg(struct e1000_hw *hw, - u32 reg_addr, - u16 data) +static s32 e1000_write_kmrn_reg(struct e1000_hw *hw, u32 reg_addr, u16 data) { u32 reg_val; u16 swfw; DEBUGFUNC("e1000_write_kmrn_reg"); if ((hw->mac_type == e1000_80003es2lan) && - (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) { + (er32(STATUS) & E1000_STATUS_FUNC_1)) { swfw = E1000_SWFW_PHY1_SM; } else { swfw = E1000_SWFW_PHY0_SM; @@ -3776,7 +3737,7 @@ e1000_write_kmrn_reg(struct e1000_hw *hw, reg_val = ((reg_addr << E1000_KUMCTRLSTA_OFFSET_SHIFT) & E1000_KUMCTRLSTA_OFFSET) | data; - E1000_WRITE_REG(hw, KUMCTRLSTA, reg_val); + ew32(KUMCTRLSTA, reg_val); udelay(2); e1000_swfw_sync_release(hw, swfw); @@ -3788,8 +3749,7 @@ e1000_write_kmrn_reg(struct e1000_hw *hw, * * hw - Struct containing variables accessed by shared code ******************************************************************************/ -s32 -e1000_phy_hw_reset(struct e1000_hw *hw) +s32 e1000_phy_hw_reset(struct e1000_hw *hw) { u32 ctrl, ctrl_ext; u32 led_ctrl; @@ -3808,7 +3768,7 @@ e1000_phy_hw_reset(struct e1000_hw *hw) if (hw->mac_type > e1000_82543) { if ((hw->mac_type == e1000_80003es2lan) && - (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) { + (er32(STATUS) & E1000_STATUS_FUNC_1)) { swfw = E1000_SWFW_PHY1_SM; } else { swfw = E1000_SWFW_PHY0_SM; @@ -3823,17 +3783,17 @@ e1000_phy_hw_reset(struct e1000_hw *hw) * and deassert. For e1000_82571 hardware and later, we instead delay * for 50us between and 10ms after the deassertion. */ - ctrl = E1000_READ_REG(hw, CTRL); - E1000_WRITE_REG(hw, CTRL, ctrl | E1000_CTRL_PHY_RST); - E1000_WRITE_FLUSH(hw); + ctrl = er32(CTRL); + ew32(CTRL, ctrl | E1000_CTRL_PHY_RST); + E1000_WRITE_FLUSH(); if (hw->mac_type < e1000_82571) msleep(10); else udelay(100); - E1000_WRITE_REG(hw, CTRL, ctrl); - E1000_WRITE_FLUSH(hw); + ew32(CTRL, ctrl); + E1000_WRITE_FLUSH(); if (hw->mac_type >= e1000_82571) mdelay(10); @@ -3843,24 +3803,24 @@ e1000_phy_hw_reset(struct e1000_hw *hw) /* Read the Extended Device Control Register, assert the PHY_RESET_DIR * bit to put the PHY into reset. Then, take it out of reset. */ - ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext = er32(CTRL_EXT); ctrl_ext |= E1000_CTRL_EXT_SDP4_DIR; ctrl_ext &= ~E1000_CTRL_EXT_SDP4_DATA; - E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); - E1000_WRITE_FLUSH(hw); + ew32(CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(); msleep(10); ctrl_ext |= E1000_CTRL_EXT_SDP4_DATA; - E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); - E1000_WRITE_FLUSH(hw); + ew32(CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(); } udelay(150); if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { /* Configure activity LED after PHY reset */ - led_ctrl = E1000_READ_REG(hw, LEDCTL); + led_ctrl = er32(LEDCTL); led_ctrl &= IGP_ACTIVITY_LED_MASK; led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); - E1000_WRITE_REG(hw, LEDCTL, led_ctrl); + ew32(LEDCTL, led_ctrl); } /* Wait for FW to finish PHY configuration. */ @@ -3882,8 +3842,7 @@ e1000_phy_hw_reset(struct e1000_hw *hw) * * Sets bit 15 of the MII Control register ******************************************************************************/ -s32 -e1000_phy_reset(struct e1000_hw *hw) +s32 e1000_phy_reset(struct e1000_hw *hw) { s32 ret_val; u16 phy_data; @@ -3934,8 +3893,7 @@ e1000_phy_reset(struct e1000_hw *hw) * * hw - struct containing variables accessed by shared code ******************************************************************************/ -void -e1000_phy_powerdown_workaround(struct e1000_hw *hw) +void e1000_phy_powerdown_workaround(struct e1000_hw *hw) { s32 reg; u16 phy_data; @@ -3948,8 +3906,8 @@ e1000_phy_powerdown_workaround(struct e1000_hw *hw) do { /* Disable link */ - reg = E1000_READ_REG(hw, PHY_CTRL); - E1000_WRITE_REG(hw, PHY_CTRL, reg | E1000_PHY_CTRL_GBE_DISABLE | + reg = er32(PHY_CTRL); + ew32(PHY_CTRL, reg | E1000_PHY_CTRL_GBE_DISABLE | E1000_PHY_CTRL_NOND0A_GBE_DISABLE); /* Write VR power-down enable - bits 9:8 should be 10b */ @@ -3964,8 +3922,8 @@ e1000_phy_powerdown_workaround(struct e1000_hw *hw) break; /* Issue PHY reset and repeat at most one more time */ - reg = E1000_READ_REG(hw, CTRL); - E1000_WRITE_REG(hw, CTRL, reg | E1000_CTRL_PHY_RST); + reg = er32(CTRL); + ew32(CTRL, reg | E1000_CTRL_PHY_RST); retry++; } while (retry); @@ -3987,8 +3945,7 @@ e1000_phy_powerdown_workaround(struct e1000_hw *hw) * * hw - struct containing variables accessed by shared code ******************************************************************************/ -static s32 -e1000_kumeran_lock_loss_workaround(struct e1000_hw *hw) +static s32 e1000_kumeran_lock_loss_workaround(struct e1000_hw *hw) { s32 ret_val; s32 reg; @@ -4024,8 +3981,8 @@ e1000_kumeran_lock_loss_workaround(struct e1000_hw *hw) mdelay(5); } /* Disable GigE link negotiation */ - reg = E1000_READ_REG(hw, PHY_CTRL); - E1000_WRITE_REG(hw, PHY_CTRL, reg | E1000_PHY_CTRL_GBE_DISABLE | + reg = er32(PHY_CTRL); + ew32(PHY_CTRL, reg | E1000_PHY_CTRL_GBE_DISABLE | E1000_PHY_CTRL_NOND0A_GBE_DISABLE); /* unable to acquire PCS lock */ @@ -4040,8 +3997,7 @@ e1000_kumeran_lock_loss_workaround(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code ******************************************************************************/ -static s32 -e1000_detect_gig_phy(struct e1000_hw *hw) +static s32 e1000_detect_gig_phy(struct e1000_hw *hw) { s32 phy_init_status, ret_val; u16 phy_id_high, phy_id_low; @@ -4076,14 +4032,14 @@ e1000_detect_gig_phy(struct e1000_hw *hw) if (ret_val) return ret_val; - hw->phy_id = (u32) (phy_id_high << 16); + hw->phy_id = (u32)(phy_id_high << 16); udelay(20); ret_val = e1000_read_phy_reg(hw, PHY_ID2, &phy_id_low); if (ret_val) return ret_val; - hw->phy_id |= (u32) (phy_id_low & PHY_REVISION_MASK); - hw->phy_revision = (u32) phy_id_low & ~PHY_REVISION_MASK; + hw->phy_id |= (u32)(phy_id_low & PHY_REVISION_MASK); + hw->phy_revision = (u32)phy_id_low & ~PHY_REVISION_MASK; switch (hw->mac_type) { case e1000_82543: @@ -4136,8 +4092,7 @@ e1000_detect_gig_phy(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code ******************************************************************************/ -static s32 -e1000_phy_reset_dsp(struct e1000_hw *hw) +static s32 e1000_phy_reset_dsp(struct e1000_hw *hw) { s32 ret_val; DEBUGFUNC("e1000_phy_reset_dsp"); @@ -4163,9 +4118,8 @@ e1000_phy_reset_dsp(struct e1000_hw *hw) * hw - Struct containing variables accessed by shared code * phy_info - PHY information structure ******************************************************************************/ -static s32 -e1000_phy_igp_get_info(struct e1000_hw *hw, - struct e1000_phy_info *phy_info) +static s32 e1000_phy_igp_get_info(struct e1000_hw *hw, + struct e1000_phy_info *phy_info) { s32 ret_val; u16 phy_data, min_length, max_length, average; @@ -4240,9 +4194,8 @@ e1000_phy_igp_get_info(struct e1000_hw *hw, * hw - Struct containing variables accessed by shared code * phy_info - PHY information structure ******************************************************************************/ -static s32 -e1000_phy_ife_get_info(struct e1000_hw *hw, - struct e1000_phy_info *phy_info) +static s32 e1000_phy_ife_get_info(struct e1000_hw *hw, + struct e1000_phy_info *phy_info) { s32 ret_val; u16 phy_data; @@ -4290,9 +4243,8 @@ e1000_phy_ife_get_info(struct e1000_hw *hw, * hw - Struct containing variables accessed by shared code * phy_info - PHY information structure ******************************************************************************/ -static s32 -e1000_phy_m88_get_info(struct e1000_hw *hw, - struct e1000_phy_info *phy_info) +static s32 e1000_phy_m88_get_info(struct e1000_hw *hw, + struct e1000_phy_info *phy_info) { s32 ret_val; u16 phy_data; @@ -4369,9 +4321,7 @@ e1000_phy_m88_get_info(struct e1000_hw *hw, * hw - Struct containing variables accessed by shared code * phy_info - PHY information structure ******************************************************************************/ -s32 -e1000_phy_get_info(struct e1000_hw *hw, - struct e1000_phy_info *phy_info) +s32 e1000_phy_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info) { s32 ret_val; u16 phy_data; @@ -4415,8 +4365,7 @@ e1000_phy_get_info(struct e1000_hw *hw, return e1000_phy_m88_get_info(hw, phy_info); } -s32 -e1000_validate_mdi_setting(struct e1000_hw *hw) +s32 e1000_validate_mdi_setting(struct e1000_hw *hw) { DEBUGFUNC("e1000_validate_mdi_settings"); @@ -4436,11 +4385,10 @@ e1000_validate_mdi_setting(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -s32 -e1000_init_eeprom_params(struct e1000_hw *hw) +s32 e1000_init_eeprom_params(struct e1000_hw *hw) { struct e1000_eeprom_info *eeprom = &hw->eeprom; - u32 eecd = E1000_READ_REG(hw, EECD); + u32 eecd = er32(EECD); s32 ret_val = E1000_SUCCESS; u16 eeprom_size; @@ -4542,7 +4490,7 @@ e1000_init_eeprom_params(struct e1000_hw *hw) /* Ensure that the Autonomous FLASH update bit is cleared due to * Flash update issue on parts which use a FLASH for NVM. */ eecd &= ~E1000_EECD_AUPDEN; - E1000_WRITE_REG(hw, EECD, eecd); + ew32(EECD, eecd); } break; case e1000_80003es2lan: @@ -4626,16 +4574,14 @@ e1000_init_eeprom_params(struct e1000_hw *hw) * hw - Struct containing variables accessed by shared code * eecd - EECD's current value *****************************************************************************/ -static void -e1000_raise_ee_clk(struct e1000_hw *hw, - u32 *eecd) +static void e1000_raise_ee_clk(struct e1000_hw *hw, u32 *eecd) { /* Raise the clock input to the EEPROM (by setting the SK bit), and then * wait <delay> microseconds. */ *eecd = *eecd | E1000_EECD_SK; - E1000_WRITE_REG(hw, EECD, *eecd); - E1000_WRITE_FLUSH(hw); + ew32(EECD, *eecd); + E1000_WRITE_FLUSH(); udelay(hw->eeprom.delay_usec); } @@ -4645,16 +4591,14 @@ e1000_raise_ee_clk(struct e1000_hw *hw, * hw - Struct containing variables accessed by shared code * eecd - EECD's current value *****************************************************************************/ -static void -e1000_lower_ee_clk(struct e1000_hw *hw, - u32 *eecd) +static void e1000_lower_ee_clk(struct e1000_hw *hw, u32 *eecd) { /* Lower the clock input to the EEPROM (by clearing the SK bit), and then * wait 50 microseconds. */ *eecd = *eecd & ~E1000_EECD_SK; - E1000_WRITE_REG(hw, EECD, *eecd); - E1000_WRITE_FLUSH(hw); + ew32(EECD, *eecd); + E1000_WRITE_FLUSH(); udelay(hw->eeprom.delay_usec); } @@ -4665,10 +4609,7 @@ e1000_lower_ee_clk(struct e1000_hw *hw, * data - data to send to the EEPROM * count - number of bits to shift out *****************************************************************************/ -static void -e1000_shift_out_ee_bits(struct e1000_hw *hw, - u16 data, - u16 count) +static void e1000_shift_out_ee_bits(struct e1000_hw *hw, u16 data, u16 count) { struct e1000_eeprom_info *eeprom = &hw->eeprom; u32 eecd; @@ -4679,7 +4620,7 @@ e1000_shift_out_ee_bits(struct e1000_hw *hw, * In order to do this, "data" must be broken down into bits. */ mask = 0x01 << (count - 1); - eecd = E1000_READ_REG(hw, EECD); + eecd = er32(EECD); if (eeprom->type == e1000_eeprom_microwire) { eecd &= ~E1000_EECD_DO; } else if (eeprom->type == e1000_eeprom_spi) { @@ -4696,8 +4637,8 @@ e1000_shift_out_ee_bits(struct e1000_hw *hw, if (data & mask) eecd |= E1000_EECD_DI; - E1000_WRITE_REG(hw, EECD, eecd); - E1000_WRITE_FLUSH(hw); + ew32(EECD, eecd); + E1000_WRITE_FLUSH(); udelay(eeprom->delay_usec); @@ -4710,7 +4651,7 @@ e1000_shift_out_ee_bits(struct e1000_hw *hw, /* We leave the "DI" bit set to "0" when we leave this routine. */ eecd &= ~E1000_EECD_DI; - E1000_WRITE_REG(hw, EECD, eecd); + ew32(EECD, eecd); } /****************************************************************************** @@ -4718,9 +4659,7 @@ e1000_shift_out_ee_bits(struct e1000_hw *hw, * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -static u16 -e1000_shift_in_ee_bits(struct e1000_hw *hw, - u16 count) +static u16 e1000_shift_in_ee_bits(struct e1000_hw *hw, u16 count) { u32 eecd; u32 i; @@ -4733,7 +4672,7 @@ e1000_shift_in_ee_bits(struct e1000_hw *hw, * always be clear. */ - eecd = E1000_READ_REG(hw, EECD); + eecd = er32(EECD); eecd &= ~(E1000_EECD_DO | E1000_EECD_DI); data = 0; @@ -4742,7 +4681,7 @@ e1000_shift_in_ee_bits(struct e1000_hw *hw, data = data << 1; e1000_raise_ee_clk(hw, &eecd); - eecd = E1000_READ_REG(hw, EECD); + eecd = er32(EECD); eecd &= ~(E1000_EECD_DI); if (eecd & E1000_EECD_DO) @@ -4762,8 +4701,7 @@ e1000_shift_in_ee_bits(struct e1000_hw *hw, * Lowers EEPROM clock. Clears input pin. Sets the chip select pin. This * function should be called before issuing a command to the EEPROM. *****************************************************************************/ -static s32 -e1000_acquire_eeprom(struct e1000_hw *hw) +static s32 e1000_acquire_eeprom(struct e1000_hw *hw) { struct e1000_eeprom_info *eeprom = &hw->eeprom; u32 eecd, i=0; @@ -4772,23 +4710,23 @@ e1000_acquire_eeprom(struct e1000_hw *hw) if (e1000_swfw_sync_acquire(hw, E1000_SWFW_EEP_SM)) return -E1000_ERR_SWFW_SYNC; - eecd = E1000_READ_REG(hw, EECD); + eecd = er32(EECD); if (hw->mac_type != e1000_82573) { /* Request EEPROM Access */ if (hw->mac_type > e1000_82544) { eecd |= E1000_EECD_REQ; - E1000_WRITE_REG(hw, EECD, eecd); - eecd = E1000_READ_REG(hw, EECD); + ew32(EECD, eecd); + eecd = er32(EECD); while ((!(eecd & E1000_EECD_GNT)) && (i < E1000_EEPROM_GRANT_ATTEMPTS)) { i++; udelay(5); - eecd = E1000_READ_REG(hw, EECD); + eecd = er32(EECD); } if (!(eecd & E1000_EECD_GNT)) { eecd &= ~E1000_EECD_REQ; - E1000_WRITE_REG(hw, EECD, eecd); + ew32(EECD, eecd); DEBUGOUT("Could not acquire EEPROM grant\n"); e1000_swfw_sync_release(hw, E1000_SWFW_EEP_SM); return -E1000_ERR_EEPROM; @@ -4801,15 +4739,15 @@ e1000_acquire_eeprom(struct e1000_hw *hw) if (eeprom->type == e1000_eeprom_microwire) { /* Clear SK and DI */ eecd &= ~(E1000_EECD_DI | E1000_EECD_SK); - E1000_WRITE_REG(hw, EECD, eecd); + ew32(EECD, eecd); /* Set CS */ eecd |= E1000_EECD_CS; - E1000_WRITE_REG(hw, EECD, eecd); + ew32(EECD, eecd); } else if (eeprom->type == e1000_eeprom_spi) { /* Clear SK and CS */ eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); - E1000_WRITE_REG(hw, EECD, eecd); + ew32(EECD, eecd); udelay(1); } @@ -4821,46 +4759,45 @@ e1000_acquire_eeprom(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -static void -e1000_standby_eeprom(struct e1000_hw *hw) +static void e1000_standby_eeprom(struct e1000_hw *hw) { struct e1000_eeprom_info *eeprom = &hw->eeprom; u32 eecd; - eecd = E1000_READ_REG(hw, EECD); + eecd = er32(EECD); if (eeprom->type == e1000_eeprom_microwire) { eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); - E1000_WRITE_REG(hw, EECD, eecd); - E1000_WRITE_FLUSH(hw); + ew32(EECD, eecd); + E1000_WRITE_FLUSH(); udelay(eeprom->delay_usec); /* Clock high */ eecd |= E1000_EECD_SK; - E1000_WRITE_REG(hw, EECD, eecd); - E1000_WRITE_FLUSH(hw); + ew32(EECD, eecd); + E1000_WRITE_FLUSH(); udelay(eeprom->delay_usec); /* Select EEPROM */ eecd |= E1000_EECD_CS; - E1000_WRITE_REG(hw, EECD, eecd); - E1000_WRITE_FLUSH(hw); + ew32(EECD, eecd); + E1000_WRITE_FLUSH(); udelay(eeprom->delay_usec); /* Clock low */ eecd &= ~E1000_EECD_SK; - E1000_WRITE_REG(hw, EECD, eecd); - E1000_WRITE_FLUSH(hw); + ew32(EECD, eecd); + E1000_WRITE_FLUSH(); udelay(eeprom->delay_usec); } else if (eeprom->type == e1000_eeprom_spi) { /* Toggle CS to flush commands */ eecd |= E1000_EECD_CS; - E1000_WRITE_REG(hw, EECD, eecd); - E1000_WRITE_FLUSH(hw); + ew32(EECD, eecd); + E1000_WRITE_FLUSH(); udelay(eeprom->delay_usec); eecd &= ~E1000_EECD_CS; - E1000_WRITE_REG(hw, EECD, eecd); - E1000_WRITE_FLUSH(hw); + ew32(EECD, eecd); + E1000_WRITE_FLUSH(); udelay(eeprom->delay_usec); } } @@ -4870,20 +4807,19 @@ e1000_standby_eeprom(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -static void -e1000_release_eeprom(struct e1000_hw *hw) +static void e1000_release_eeprom(struct e1000_hw *hw) { u32 eecd; DEBUGFUNC("e1000_release_eeprom"); - eecd = E1000_READ_REG(hw, EECD); + eecd = er32(EECD); if (hw->eeprom.type == e1000_eeprom_spi) { eecd |= E1000_EECD_CS; /* Pull CS high */ eecd &= ~E1000_EECD_SK; /* Lower SCK */ - E1000_WRITE_REG(hw, EECD, eecd); + ew32(EECD, eecd); udelay(hw->eeprom.delay_usec); } else if (hw->eeprom.type == e1000_eeprom_microwire) { @@ -4892,25 +4828,25 @@ e1000_release_eeprom(struct e1000_hw *hw) /* CS on Microwire is active-high */ eecd &= ~(E1000_EECD_CS | E1000_EECD_DI); - E1000_WRITE_REG(hw, EECD, eecd); + ew32(EECD, eecd); /* Rising edge of clock */ eecd |= E1000_EECD_SK; - E1000_WRITE_REG(hw, EECD, eecd); - E1000_WRITE_FLUSH(hw); + ew32(EECD, eecd); + E1000_WRITE_FLUSH(); udelay(hw->eeprom.delay_usec); /* Falling edge of clock */ eecd &= ~E1000_EECD_SK; - E1000_WRITE_REG(hw, EECD, eecd); - E1000_WRITE_FLUSH(hw); + ew32(EECD, eecd); + E1000_WRITE_FLUSH(); udelay(hw->eeprom.delay_usec); } /* Stop requesting EEPROM access */ if (hw->mac_type > e1000_82544) { eecd &= ~E1000_EECD_REQ; - E1000_WRITE_REG(hw, EECD, eecd); + ew32(EECD, eecd); } e1000_swfw_sync_release(hw, E1000_SWFW_EEP_SM); @@ -4921,8 +4857,7 @@ e1000_release_eeprom(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -static s32 -e1000_spi_eeprom_ready(struct e1000_hw *hw) +static s32 e1000_spi_eeprom_ready(struct e1000_hw *hw) { u16 retry_count = 0; u8 spi_stat_reg; @@ -4967,11 +4902,7 @@ e1000_spi_eeprom_ready(struct e1000_hw *hw) * data - word read from the EEPROM * words - number of words to read *****************************************************************************/ -s32 -e1000_read_eeprom(struct e1000_hw *hw, - u16 offset, - u16 words, - u16 *data) +s32 e1000_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) { struct e1000_eeprom_info *eeprom = &hw->eeprom; u32 i = 0; @@ -5068,11 +4999,8 @@ e1000_read_eeprom(struct e1000_hw *hw, * data - word read from the EEPROM * words - number of words to read *****************************************************************************/ -static s32 -e1000_read_eeprom_eerd(struct e1000_hw *hw, - u16 offset, - u16 words, - u16 *data) +static s32 e1000_read_eeprom_eerd(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data) { u32 i, eerd = 0; s32 error = 0; @@ -5081,13 +5009,13 @@ e1000_read_eeprom_eerd(struct e1000_hw *hw, eerd = ((offset+i) << E1000_EEPROM_RW_ADDR_SHIFT) + E1000_EEPROM_RW_REG_START; - E1000_WRITE_REG(hw, EERD, eerd); + ew32(EERD, eerd); error = e1000_poll_eerd_eewr_done(hw, E1000_EEPROM_POLL_READ); if (error) { break; } - data[i] = (E1000_READ_REG(hw, EERD) >> E1000_EEPROM_RW_REG_DATA); + data[i] = (er32(EERD) >> E1000_EEPROM_RW_REG_DATA); } @@ -5102,11 +5030,8 @@ e1000_read_eeprom_eerd(struct e1000_hw *hw, * data - word read from the EEPROM * words - number of words to read *****************************************************************************/ -static s32 -e1000_write_eeprom_eewr(struct e1000_hw *hw, - u16 offset, - u16 words, - u16 *data) +static s32 e1000_write_eeprom_eewr(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data) { u32 register_value = 0; u32 i = 0; @@ -5125,7 +5050,7 @@ e1000_write_eeprom_eewr(struct e1000_hw *hw, break; } - E1000_WRITE_REG(hw, EEWR, register_value); + ew32(EEWR, register_value); error = e1000_poll_eerd_eewr_done(hw, E1000_EEPROM_POLL_WRITE); @@ -5143,8 +5068,7 @@ e1000_write_eeprom_eewr(struct e1000_hw *hw, * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -static s32 -e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int eerd) +static s32 e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int eerd) { u32 attempts = 100000; u32 i, reg = 0; @@ -5152,9 +5076,9 @@ e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int eerd) for (i = 0; i < attempts; i++) { if (eerd == E1000_EEPROM_POLL_READ) - reg = E1000_READ_REG(hw, EERD); + reg = er32(EERD); else - reg = E1000_READ_REG(hw, EEWR); + reg = er32(EEWR); if (reg & E1000_EEPROM_RW_REG_DONE) { done = E1000_SUCCESS; @@ -5171,8 +5095,7 @@ e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int eerd) * * hw - Struct containing variables accessed by shared code ****************************************************************************/ -static bool -e1000_is_onboard_nvm_eeprom(struct e1000_hw *hw) +static bool e1000_is_onboard_nvm_eeprom(struct e1000_hw *hw) { u32 eecd = 0; @@ -5182,7 +5105,7 @@ e1000_is_onboard_nvm_eeprom(struct e1000_hw *hw) return false; if (hw->mac_type == e1000_82573) { - eecd = E1000_READ_REG(hw, EECD); + eecd = er32(EECD); /* Isolate bits 15 & 16 */ eecd = ((eecd >> 15) & 0x03); @@ -5204,8 +5127,7 @@ e1000_is_onboard_nvm_eeprom(struct e1000_hw *hw) * If the the sum of the 64 16 bit words is 0xBABA, the EEPROM's checksum is * valid. *****************************************************************************/ -s32 -e1000_validate_eeprom_checksum(struct e1000_hw *hw) +s32 e1000_validate_eeprom_checksum(struct e1000_hw *hw) { u16 checksum = 0; u16 i, eeprom_data; @@ -5252,7 +5174,7 @@ e1000_validate_eeprom_checksum(struct e1000_hw *hw) checksum += eeprom_data; } - if (checksum == (u16) EEPROM_SUM) + if (checksum == (u16)EEPROM_SUM) return E1000_SUCCESS; else { DEBUGOUT("EEPROM Checksum Invalid\n"); @@ -5268,8 +5190,7 @@ e1000_validate_eeprom_checksum(struct e1000_hw *hw) * Sums the first 63 16 bit words of the EEPROM. Subtracts the sum from 0xBABA. * Writes the difference to word offset 63 of the EEPROM. *****************************************************************************/ -s32 -e1000_update_eeprom_checksum(struct e1000_hw *hw) +s32 e1000_update_eeprom_checksum(struct e1000_hw *hw) { u32 ctrl_ext; u16 checksum = 0; @@ -5284,7 +5205,7 @@ e1000_update_eeprom_checksum(struct e1000_hw *hw) } checksum += eeprom_data; } - checksum = (u16) EEPROM_SUM - checksum; + checksum = (u16)EEPROM_SUM - checksum; if (e1000_write_eeprom(hw, EEPROM_CHECKSUM_REG, 1, &checksum) < 0) { DEBUGOUT("EEPROM Write Error\n"); return -E1000_ERR_EEPROM; @@ -5294,9 +5215,9 @@ e1000_update_eeprom_checksum(struct e1000_hw *hw) e1000_commit_shadow_ram(hw); /* Reload the EEPROM, or else modifications will not appear * until after next adapter reset. */ - ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext = er32(CTRL_EXT); ctrl_ext |= E1000_CTRL_EXT_EE_RST; - E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + ew32(CTRL_EXT, ctrl_ext); msleep(10); } return E1000_SUCCESS; @@ -5313,11 +5234,7 @@ e1000_update_eeprom_checksum(struct e1000_hw *hw) * If e1000_update_eeprom_checksum is not called after this function, the * EEPROM will most likely contain an invalid checksum. *****************************************************************************/ -s32 -e1000_write_eeprom(struct e1000_hw *hw, - u16 offset, - u16 words, - u16 *data) +s32 e1000_write_eeprom(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) { struct e1000_eeprom_info *eeprom = &hw->eeprom; s32 status = 0; @@ -5370,11 +5287,8 @@ e1000_write_eeprom(struct e1000_hw *hw, * data - pointer to array of 8 bit words to be written to the EEPROM * *****************************************************************************/ -static s32 -e1000_write_eeprom_spi(struct e1000_hw *hw, - u16 offset, - u16 words, - u16 *data) +static s32 e1000_write_eeprom_spi(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data) { struct e1000_eeprom_info *eeprom = &hw->eeprom; u16 widx = 0; @@ -5436,11 +5350,8 @@ e1000_write_eeprom_spi(struct e1000_hw *hw, * data - pointer to array of 16 bit words to be written to the EEPROM * *****************************************************************************/ -static s32 -e1000_write_eeprom_microwire(struct e1000_hw *hw, - u16 offset, - u16 words, - u16 *data) +static s32 e1000_write_eeprom_microwire(struct e1000_hw *hw, u16 offset, + u16 words, u16 *data) { struct e1000_eeprom_info *eeprom = &hw->eeprom; u32 eecd; @@ -5484,7 +5395,7 @@ e1000_write_eeprom_microwire(struct e1000_hw *hw, * If DO does not go high in 10 milliseconds, then error out. */ for (i = 0; i < 200; i++) { - eecd = E1000_READ_REG(hw, EECD); + eecd = er32(EECD); if (eecd & E1000_EECD_DO) break; udelay(50); } @@ -5523,8 +5434,7 @@ e1000_write_eeprom_microwire(struct e1000_hw *hw, * data - word read from the EEPROM * words - number of words to read *****************************************************************************/ -static s32 -e1000_commit_shadow_ram(struct e1000_hw *hw) +static s32 e1000_commit_shadow_ram(struct e1000_hw *hw) { u32 attempts = 100000; u32 eecd = 0; @@ -5539,9 +5449,9 @@ e1000_commit_shadow_ram(struct e1000_hw *hw) if (hw->mac_type == e1000_82573) { /* The flop register will be used to determine if flash type is STM */ - flop = E1000_READ_REG(hw, FLOP); + flop = er32(FLOP); for (i=0; i < attempts; i++) { - eecd = E1000_READ_REG(hw, EECD); + eecd = er32(EECD); if ((eecd & E1000_EECD_FLUPD) == 0) { break; } @@ -5554,14 +5464,14 @@ e1000_commit_shadow_ram(struct e1000_hw *hw) /* If STM opcode located in bits 15:8 of flop, reset firmware */ if ((flop & 0xFF00) == E1000_STM_OPCODE) { - E1000_WRITE_REG(hw, HICR, E1000_HICR_FW_RESET); + ew32(HICR, E1000_HICR_FW_RESET); } /* Perform the flash update */ - E1000_WRITE_REG(hw, EECD, eecd | E1000_EECD_FLUPD); + ew32(EECD, eecd | E1000_EECD_FLUPD); for (i=0; i < attempts; i++) { - eecd = E1000_READ_REG(hw, EECD); + eecd = er32(EECD); if ((eecd & E1000_EECD_FLUPD) == 0) { break; } @@ -5577,7 +5487,7 @@ e1000_commit_shadow_ram(struct e1000_hw *hw) /* We're writing to the opposite bank so if we're on bank 1, * write to bank 0 etc. We also need to erase the segment that * is going to be written */ - if (!(E1000_READ_REG(hw, EECD) & E1000_EECD_SEC1VAL)) { + if (!(er32(EECD) & E1000_EECD_SEC1VAL)) { new_bank_offset = hw->flash_bank_size * 2; old_bank_offset = 0; e1000_erase_ich8_4k_segment(hw, 1); @@ -5687,8 +5597,7 @@ e1000_commit_shadow_ram(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -s32 -e1000_read_mac_addr(struct e1000_hw * hw) +s32 e1000_read_mac_addr(struct e1000_hw *hw) { u16 offset; u16 eeprom_data, i; @@ -5701,8 +5610,8 @@ e1000_read_mac_addr(struct e1000_hw * hw) DEBUGOUT("EEPROM Read Error\n"); return -E1000_ERR_EEPROM; } - hw->perm_mac_addr[i] = (u8) (eeprom_data & 0x00FF); - hw->perm_mac_addr[i+1] = (u8) (eeprom_data >> 8); + hw->perm_mac_addr[i] = (u8)(eeprom_data & 0x00FF); + hw->perm_mac_addr[i+1] = (u8)(eeprom_data >> 8); } switch (hw->mac_type) { @@ -5712,7 +5621,7 @@ e1000_read_mac_addr(struct e1000_hw * hw) case e1000_82546_rev_3: case e1000_82571: case e1000_80003es2lan: - if (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1) + if (er32(STATUS) & E1000_STATUS_FUNC_1) hw->perm_mac_addr[5] ^= 0x01; break; } @@ -5731,8 +5640,7 @@ e1000_read_mac_addr(struct e1000_hw * hw) * of the receive addresss registers. Clears the multicast table. Assumes * the receiver is in reset when the routine is called. *****************************************************************************/ -static void -e1000_init_rx_addrs(struct e1000_hw *hw) +static void e1000_init_rx_addrs(struct e1000_hw *hw) { u32 i; u32 rar_num; @@ -5758,9 +5666,9 @@ e1000_init_rx_addrs(struct e1000_hw *hw) DEBUGOUT("Clearing RAR[1-15]\n"); for (i = 1; i < rar_num; i++) { E1000_WRITE_REG_ARRAY(hw, RA, (i << 1), 0); - E1000_WRITE_FLUSH(hw); + E1000_WRITE_FLUSH(); E1000_WRITE_REG_ARRAY(hw, RA, ((i << 1) + 1), 0); - E1000_WRITE_FLUSH(hw); + E1000_WRITE_FLUSH(); } } @@ -5770,9 +5678,7 @@ e1000_init_rx_addrs(struct e1000_hw *hw) * hw - Struct containing variables accessed by shared code * mc_addr - the multicast address to hash *****************************************************************************/ -u32 -e1000_hash_mc_addr(struct e1000_hw *hw, - u8 *mc_addr) +u32 e1000_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr) { u32 hash_value = 0; @@ -5787,37 +5693,37 @@ e1000_hash_mc_addr(struct e1000_hw *hw, case 0: if (hw->mac_type == e1000_ich8lan) { /* [47:38] i.e. 0x158 for above example address */ - hash_value = ((mc_addr[4] >> 6) | (((u16) mc_addr[5]) << 2)); + hash_value = ((mc_addr[4] >> 6) | (((u16)mc_addr[5]) << 2)); } else { /* [47:36] i.e. 0x563 for above example address */ - hash_value = ((mc_addr[4] >> 4) | (((u16) mc_addr[5]) << 4)); + hash_value = ((mc_addr[4] >> 4) | (((u16)mc_addr[5]) << 4)); } break; case 1: if (hw->mac_type == e1000_ich8lan) { /* [46:37] i.e. 0x2B1 for above example address */ - hash_value = ((mc_addr[4] >> 5) | (((u16) mc_addr[5]) << 3)); + hash_value = ((mc_addr[4] >> 5) | (((u16)mc_addr[5]) << 3)); } else { /* [46:35] i.e. 0xAC6 for above example address */ - hash_value = ((mc_addr[4] >> 3) | (((u16) mc_addr[5]) << 5)); + hash_value = ((mc_addr[4] >> 3) | (((u16)mc_addr[5]) << 5)); } break; case 2: if (hw->mac_type == e1000_ich8lan) { /*[45:36] i.e. 0x163 for above example address */ - hash_value = ((mc_addr[4] >> 4) | (((u16) mc_addr[5]) << 4)); + hash_value = ((mc_addr[4] >> 4) | (((u16)mc_addr[5]) << 4)); } else { /* [45:34] i.e. 0x5D8 for above example address */ - hash_value = ((mc_addr[4] >> 2) | (((u16) mc_addr[5]) << 6)); + hash_value = ((mc_addr[4] >> 2) | (((u16)mc_addr[5]) << 6)); } break; case 3: if (hw->mac_type == e1000_ich8lan) { /* [43:34] i.e. 0x18D for above example address */ - hash_value = ((mc_addr[4] >> 2) | (((u16) mc_addr[5]) << 6)); + hash_value = ((mc_addr[4] >> 2) | (((u16)mc_addr[5]) << 6)); } else { /* [43:32] i.e. 0x634 for above example address */ - hash_value = ((mc_addr[4]) | (((u16) mc_addr[5]) << 8)); + hash_value = ((mc_addr[4]) | (((u16)mc_addr[5]) << 8)); } break; } @@ -5835,9 +5741,7 @@ e1000_hash_mc_addr(struct e1000_hw *hw, * hw - Struct containing variables accessed by shared code * hash_value - Multicast address hash value *****************************************************************************/ -void -e1000_mta_set(struct e1000_hw *hw, - u32 hash_value) +void e1000_mta_set(struct e1000_hw *hw, u32 hash_value) { u32 hash_bit, hash_reg; u32 mta; @@ -5868,12 +5772,12 @@ e1000_mta_set(struct e1000_hw *hw, if ((hw->mac_type == e1000_82544) && ((hash_reg & 0x1) == 1)) { temp = E1000_READ_REG_ARRAY(hw, MTA, (hash_reg - 1)); E1000_WRITE_REG_ARRAY(hw, MTA, hash_reg, mta); - E1000_WRITE_FLUSH(hw); + E1000_WRITE_FLUSH(); E1000_WRITE_REG_ARRAY(hw, MTA, (hash_reg - 1), temp); - E1000_WRITE_FLUSH(hw); + E1000_WRITE_FLUSH(); } else { E1000_WRITE_REG_ARRAY(hw, MTA, hash_reg, mta); - E1000_WRITE_FLUSH(hw); + E1000_WRITE_FLUSH(); } } @@ -5884,20 +5788,16 @@ e1000_mta_set(struct e1000_hw *hw, * addr - Address to put into receive address register * index - Receive address register to write *****************************************************************************/ -void -e1000_rar_set(struct e1000_hw *hw, - u8 *addr, - u32 index) +void e1000_rar_set(struct e1000_hw *hw, u8 *addr, u32 index) { u32 rar_low, rar_high; /* HW expects these in little endian so we reverse the byte order * from network order (big endian) to little endian */ - rar_low = ((u32) addr[0] | - ((u32) addr[1] << 8) | - ((u32) addr[2] << 16) | ((u32) addr[3] << 24)); - rar_high = ((u32) addr[4] | ((u32) addr[5] << 8)); + rar_low = ((u32)addr[0] | ((u32)addr[1] << 8) | + ((u32)addr[2] << 16) | ((u32)addr[3] << 24)); + rar_high = ((u32)addr[4] | ((u32)addr[5] << 8)); /* Disable Rx and flush all Rx frames before enabling RSS to avoid Rx * unit hang. @@ -5930,9 +5830,9 @@ e1000_rar_set(struct e1000_hw *hw, } E1000_WRITE_REG_ARRAY(hw, RA, (index << 1), rar_low); - E1000_WRITE_FLUSH(hw); + E1000_WRITE_FLUSH(); E1000_WRITE_REG_ARRAY(hw, RA, ((index << 1) + 1), rar_high); - E1000_WRITE_FLUSH(hw); + E1000_WRITE_FLUSH(); } /****************************************************************************** @@ -5942,10 +5842,7 @@ e1000_rar_set(struct e1000_hw *hw, * offset - Offset in VLAN filer table to write * value - Value to write into VLAN filter table *****************************************************************************/ -void -e1000_write_vfta(struct e1000_hw *hw, - u32 offset, - u32 value) +void e1000_write_vfta(struct e1000_hw *hw, u32 offset, u32 value) { u32 temp; @@ -5955,12 +5852,12 @@ e1000_write_vfta(struct e1000_hw *hw, if ((hw->mac_type == e1000_82544) && ((offset & 0x1) == 1)) { temp = E1000_READ_REG_ARRAY(hw, VFTA, (offset - 1)); E1000_WRITE_REG_ARRAY(hw, VFTA, offset, value); - E1000_WRITE_FLUSH(hw); + E1000_WRITE_FLUSH(); E1000_WRITE_REG_ARRAY(hw, VFTA, (offset - 1), temp); - E1000_WRITE_FLUSH(hw); + E1000_WRITE_FLUSH(); } else { E1000_WRITE_REG_ARRAY(hw, VFTA, offset, value); - E1000_WRITE_FLUSH(hw); + E1000_WRITE_FLUSH(); } } @@ -5969,8 +5866,7 @@ e1000_write_vfta(struct e1000_hw *hw, * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -static void -e1000_clear_vfta(struct e1000_hw *hw) +static void e1000_clear_vfta(struct e1000_hw *hw) { u32 offset; u32 vfta_value = 0; @@ -5999,12 +5895,11 @@ e1000_clear_vfta(struct e1000_hw *hw) * manageability unit */ vfta_value = (offset == vfta_offset) ? vfta_bit_in_reg : 0; E1000_WRITE_REG_ARRAY(hw, VFTA, offset, vfta_value); - E1000_WRITE_FLUSH(hw); + E1000_WRITE_FLUSH(); } } -static s32 -e1000_id_led_init(struct e1000_hw * hw) +static s32 e1000_id_led_init(struct e1000_hw *hw) { u32 ledctl; const u32 ledctl_mask = 0x000000FF; @@ -6020,7 +5915,7 @@ e1000_id_led_init(struct e1000_hw * hw) return E1000_SUCCESS; } - ledctl = E1000_READ_REG(hw, LEDCTL); + ledctl = er32(LEDCTL); hw->ledctl_default = ledctl; hw->ledctl_mode1 = hw->ledctl_default; hw->ledctl_mode2 = hw->ledctl_default; @@ -6086,8 +5981,7 @@ e1000_id_led_init(struct e1000_hw * hw) * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -s32 -e1000_setup_led(struct e1000_hw *hw) +s32 e1000_setup_led(struct e1000_hw *hw) { u32 ledctl; s32 ret_val = E1000_SUCCESS; @@ -6118,7 +6012,7 @@ e1000_setup_led(struct e1000_hw *hw) /* Fall Through */ default: if (hw->media_type == e1000_media_type_fiber) { - ledctl = E1000_READ_REG(hw, LEDCTL); + ledctl = er32(LEDCTL); /* Save current LEDCTL settings */ hw->ledctl_default = ledctl; /* Turn off LED0 */ @@ -6127,9 +6021,9 @@ e1000_setup_led(struct e1000_hw *hw) E1000_LEDCTL_LED0_MODE_MASK); ledctl |= (E1000_LEDCTL_MODE_LED_OFF << E1000_LEDCTL_LED0_MODE_SHIFT); - E1000_WRITE_REG(hw, LEDCTL, ledctl); + ew32(LEDCTL, ledctl); } else if (hw->media_type == e1000_media_type_copper) - E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode1); + ew32(LEDCTL, hw->ledctl_mode1); break; } @@ -6145,8 +6039,7 @@ e1000_setup_led(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -s32 -e1000_blink_led_start(struct e1000_hw *hw) +s32 e1000_blink_led_start(struct e1000_hw *hw) { s16 i; u32 ledctl_blink = 0; @@ -6170,7 +6063,7 @@ e1000_blink_led_start(struct e1000_hw *hw) ledctl_blink |= (E1000_LEDCTL_LED0_BLINK << (i * 8)); } - E1000_WRITE_REG(hw, LEDCTL, ledctl_blink); + ew32(LEDCTL, ledctl_blink); return E1000_SUCCESS; } @@ -6180,8 +6073,7 @@ e1000_blink_led_start(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -s32 -e1000_cleanup_led(struct e1000_hw *hw) +s32 e1000_cleanup_led(struct e1000_hw *hw) { s32 ret_val = E1000_SUCCESS; @@ -6210,7 +6102,7 @@ e1000_cleanup_led(struct e1000_hw *hw) break; } /* Restore LEDCTL settings */ - E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_default); + ew32(LEDCTL, hw->ledctl_default); break; } @@ -6222,10 +6114,9 @@ e1000_cleanup_led(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -s32 -e1000_led_on(struct e1000_hw *hw) +s32 e1000_led_on(struct e1000_hw *hw) { - u32 ctrl = E1000_READ_REG(hw, CTRL); + u32 ctrl = er32(CTRL); DEBUGFUNC("e1000_led_on"); @@ -6257,13 +6148,13 @@ e1000_led_on(struct e1000_hw *hw) e1000_write_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED, (IFE_PSCL_PROBE_MODE | IFE_PSCL_PROBE_LEDS_ON)); } else if (hw->media_type == e1000_media_type_copper) { - E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode2); + ew32(LEDCTL, hw->ledctl_mode2); return E1000_SUCCESS; } break; } - E1000_WRITE_REG(hw, CTRL, ctrl); + ew32(CTRL, ctrl); return E1000_SUCCESS; } @@ -6273,10 +6164,9 @@ e1000_led_on(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -s32 -e1000_led_off(struct e1000_hw *hw) +s32 e1000_led_off(struct e1000_hw *hw) { - u32 ctrl = E1000_READ_REG(hw, CTRL); + u32 ctrl = er32(CTRL); DEBUGFUNC("e1000_led_off"); @@ -6308,13 +6198,13 @@ e1000_led_off(struct e1000_hw *hw) e1000_write_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED, (IFE_PSCL_PROBE_MODE | IFE_PSCL_PROBE_LEDS_OFF)); } else if (hw->media_type == e1000_media_type_copper) { - E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode1); + ew32(LEDCTL, hw->ledctl_mode1); return E1000_SUCCESS; } break; } - E1000_WRITE_REG(hw, CTRL, ctrl); + ew32(CTRL, ctrl); return E1000_SUCCESS; } @@ -6324,98 +6214,97 @@ e1000_led_off(struct e1000_hw *hw) * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -static void -e1000_clear_hw_cntrs(struct e1000_hw *hw) +static void e1000_clear_hw_cntrs(struct e1000_hw *hw) { volatile u32 temp; - temp = E1000_READ_REG(hw, CRCERRS); - temp = E1000_READ_REG(hw, SYMERRS); - temp = E1000_READ_REG(hw, MPC); - temp = E1000_READ_REG(hw, SCC); - temp = E1000_READ_REG(hw, ECOL); - temp = E1000_READ_REG(hw, MCC); - temp = E1000_READ_REG(hw, LATECOL); - temp = E1000_READ_REG(hw, COLC); - temp = E1000_READ_REG(hw, DC); - temp = E1000_READ_REG(hw, SEC); - temp = E1000_READ_REG(hw, RLEC); - temp = E1000_READ_REG(hw, XONRXC); - temp = E1000_READ_REG(hw, XONTXC); - temp = E1000_READ_REG(hw, XOFFRXC); - temp = E1000_READ_REG(hw, XOFFTXC); - temp = E1000_READ_REG(hw, FCRUC); + temp = er32(CRCERRS); + temp = er32(SYMERRS); + temp = er32(MPC); + temp = er32(SCC); + temp = er32(ECOL); + temp = er32(MCC); + temp = er32(LATECOL); + temp = er32(COLC); + temp = er32(DC); + temp = er32(SEC); + temp = er32(RLEC); + temp = er32(XONRXC); + temp = er32(XONTXC); + temp = er32(XOFFRXC); + temp = er32(XOFFTXC); + temp = er32(FCRUC); if (hw->mac_type != e1000_ich8lan) { - temp = E1000_READ_REG(hw, PRC64); - temp = E1000_READ_REG(hw, PRC127); - temp = E1000_READ_REG(hw, PRC255); - temp = E1000_READ_REG(hw, PRC511); - temp = E1000_READ_REG(hw, PRC1023); - temp = E1000_READ_REG(hw, PRC1522); - } - - temp = E1000_READ_REG(hw, GPRC); - temp = E1000_READ_REG(hw, BPRC); - temp = E1000_READ_REG(hw, MPRC); - temp = E1000_READ_REG(hw, GPTC); - temp = E1000_READ_REG(hw, GORCL); - temp = E1000_READ_REG(hw, GORCH); - temp = E1000_READ_REG(hw, GOTCL); - temp = E1000_READ_REG(hw, GOTCH); - temp = E1000_READ_REG(hw, RNBC); - temp = E1000_READ_REG(hw, RUC); - temp = E1000_READ_REG(hw, RFC); - temp = E1000_READ_REG(hw, ROC); - temp = E1000_READ_REG(hw, RJC); - temp = E1000_READ_REG(hw, TORL); - temp = E1000_READ_REG(hw, TORH); - temp = E1000_READ_REG(hw, TOTL); - temp = E1000_READ_REG(hw, TOTH); - temp = E1000_READ_REG(hw, TPR); - temp = E1000_READ_REG(hw, TPT); + temp = er32(PRC64); + temp = er32(PRC127); + temp = er32(PRC255); + temp = er32(PRC511); + temp = er32(PRC1023); + temp = er32(PRC1522); + } + + temp = er32(GPRC); + temp = er32(BPRC); + temp = er32(MPRC); + temp = er32(GPTC); + temp = er32(GORCL); + temp = er32(GORCH); + temp = er32(GOTCL); + temp = er32(GOTCH); + temp = er32(RNBC); + temp = er32(RUC); + temp = er32(RFC); + temp = er32(ROC); + temp = er32(RJC); + temp = er32(TORL); + temp = er32(TORH); + temp = er32(TOTL); + temp = er32(TOTH); + temp = er32(TPR); + temp = er32(TPT); if (hw->mac_type != e1000_ich8lan) { - temp = E1000_READ_REG(hw, PTC64); - temp = E1000_READ_REG(hw, PTC127); - temp = E1000_READ_REG(hw, PTC255); - temp = E1000_READ_REG(hw, PTC511); - temp = E1000_READ_REG(hw, PTC1023); - temp = E1000_READ_REG(hw, PTC1522); + temp = er32(PTC64); + temp = er32(PTC127); + temp = er32(PTC255); + temp = er32(PTC511); + temp = er32(PTC1023); + temp = er32(PTC1522); } - temp = E1000_READ_REG(hw, MPTC); - temp = E1000_READ_REG(hw, BPTC); + temp = er32(MPTC); + temp = er32(BPTC); if (hw->mac_type < e1000_82543) return; - temp = E1000_READ_REG(hw, ALGNERRC); - temp = E1000_READ_REG(hw, RXERRC); - temp = E1000_READ_REG(hw, TNCRS); - temp = E1000_READ_REG(hw, CEXTERR); - temp = E1000_READ_REG(hw, TSCTC); - temp = E1000_READ_REG(hw, TSCTFC); + temp = er32(ALGNERRC); + temp = er32(RXERRC); + temp = er32(TNCRS); + temp = er32(CEXTERR); + temp = er32(TSCTC); + temp = er32(TSCTFC); if (hw->mac_type <= e1000_82544) return; - temp = E1000_READ_REG(hw, MGTPRC); - temp = E1000_READ_REG(hw, MGTPDC); - temp = E1000_READ_REG(hw, MGTPTC); + temp = er32(MGTPRC); + temp = er32(MGTPDC); + temp = er32(MGTPTC); if (hw->mac_type <= e1000_82547_rev_2) return; - temp = E1000_READ_REG(hw, IAC); - temp = E1000_READ_REG(hw, ICRXOC); + temp = er32(IAC); + temp = er32(ICRXOC); if (hw->mac_type == e1000_ich8lan) return; - temp = E1000_READ_REG(hw, ICRXPTC); - temp = E1000_READ_REG(hw, ICRXATC); - temp = E1000_READ_REG(hw, ICTXPTC); - temp = E1000_READ_REG(hw, ICTXATC); - temp = E1000_READ_REG(hw, ICTXQEC); - temp = E1000_READ_REG(hw, ICTXQMTC); - temp = E1000_READ_REG(hw, ICRXDMTC); + temp = er32(ICRXPTC); + temp = er32(ICRXATC); + temp = er32(ICTXPTC); + temp = er32(ICTXATC); + temp = er32(ICTXQEC); + temp = er32(ICTXQMTC); + temp = er32(ICRXDMTC); } /****************************************************************************** @@ -6428,8 +6317,7 @@ e1000_clear_hw_cntrs(struct e1000_hw *hw) * current_ifs_val, ifs_min_val, ifs_max_val, ifs_step_size, and ifs_ratio * before calling this function. *****************************************************************************/ -void -e1000_reset_adaptive(struct e1000_hw *hw) +void e1000_reset_adaptive(struct e1000_hw *hw) { DEBUGFUNC("e1000_reset_adaptive"); @@ -6442,7 +6330,7 @@ e1000_reset_adaptive(struct e1000_hw *hw) hw->ifs_ratio = IFS_RATIO; } hw->in_ifs_mode = false; - E1000_WRITE_REG(hw, AIT, 0); + ew32(AIT, 0); } else { DEBUGOUT("Not in Adaptive IFS mode!\n"); } @@ -6456,8 +6344,7 @@ e1000_reset_adaptive(struct e1000_hw *hw) * tx_packets - Number of transmits since last callback * total_collisions - Number of collisions since last callback *****************************************************************************/ -void -e1000_update_adaptive(struct e1000_hw *hw) +void e1000_update_adaptive(struct e1000_hw *hw) { DEBUGFUNC("e1000_update_adaptive"); @@ -6470,14 +6357,14 @@ e1000_update_adaptive(struct e1000_hw *hw) hw->current_ifs_val = hw->ifs_min_val; else hw->current_ifs_val += hw->ifs_step_size; - E1000_WRITE_REG(hw, AIT, hw->current_ifs_val); + ew32(AIT, hw->current_ifs_val); } } } else { if (hw->in_ifs_mode && (hw->tx_packet_delta <= MIN_NUM_XMITS)) { hw->current_ifs_val = 0; hw->in_ifs_mode = false; - E1000_WRITE_REG(hw, AIT, 0); + ew32(AIT, 0); } } } else { @@ -6492,11 +6379,8 @@ e1000_update_adaptive(struct e1000_hw *hw) * frame_len - The length of the frame in question * mac_addr - The Ethernet destination address of the frame in question *****************************************************************************/ -void -e1000_tbi_adjust_stats(struct e1000_hw *hw, - struct e1000_hw_stats *stats, - u32 frame_len, - u8 *mac_addr) +void e1000_tbi_adjust_stats(struct e1000_hw *hw, struct e1000_hw_stats *stats, + u32 frame_len, u8 *mac_addr) { u64 carry_bit; @@ -6527,7 +6411,7 @@ e1000_tbi_adjust_stats(struct e1000_hw *hw, * since the test for a multicast frame will test positive on * a broadcast frame. */ - if ((mac_addr[0] == (u8) 0xff) && (mac_addr[1] == (u8) 0xff)) + if ((mac_addr[0] == (u8)0xff) && (mac_addr[1] == (u8)0xff)) /* Broadcast packet */ stats->bprc++; else if (*mac_addr & 0x01) @@ -6570,8 +6454,7 @@ e1000_tbi_adjust_stats(struct e1000_hw *hw, * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -void -e1000_get_bus_info(struct e1000_hw *hw) +void e1000_get_bus_info(struct e1000_hw *hw) { s32 ret_val; u16 pci_ex_link_status; @@ -6605,7 +6488,7 @@ e1000_get_bus_info(struct e1000_hw *hw) hw->bus_width = e1000_bus_width_pciex_1; break; default: - status = E1000_READ_REG(hw, STATUS); + status = er32(STATUS); hw->bus_type = (status & E1000_STATUS_PCIX_MODE) ? e1000_bus_type_pcix : e1000_bus_type_pci; @@ -6645,10 +6528,7 @@ e1000_get_bus_info(struct e1000_hw *hw) * offset - offset to write to * value - value to write *****************************************************************************/ -static void -e1000_write_reg_io(struct e1000_hw *hw, - u32 offset, - u32 value) +static void e1000_write_reg_io(struct e1000_hw *hw, u32 offset, u32 value) { unsigned long io_addr = hw->io_base; unsigned long io_data = hw->io_base + 4; @@ -6672,10 +6552,8 @@ e1000_write_reg_io(struct e1000_hw *hw, * register to the minimum and maximum range. * For IGP phy's, the function calculates the range by the AGC registers. *****************************************************************************/ -static s32 -e1000_get_cable_length(struct e1000_hw *hw, - u16 *min_length, - u16 *max_length) +static s32 e1000_get_cable_length(struct e1000_hw *hw, u16 *min_length, + u16 *max_length) { s32 ret_val; u16 agc_value = 0; @@ -6863,9 +6741,8 @@ e1000_get_cable_length(struct e1000_hw *hw, * return 0. If the link speed is 1000 Mbps the polarity status is in the * IGP01E1000_PHY_PCS_INIT_REG. *****************************************************************************/ -static s32 -e1000_check_polarity(struct e1000_hw *hw, - e1000_rev_polarity *polarity) +static s32 e1000_check_polarity(struct e1000_hw *hw, + e1000_rev_polarity *polarity) { s32 ret_val; u16 phy_data; @@ -6939,8 +6816,7 @@ e1000_check_polarity(struct e1000_hw *hw, * Link Health register. In IGP this bit is latched high, so the driver must * read it immediately after link is established. *****************************************************************************/ -static s32 -e1000_check_downshift(struct e1000_hw *hw) +static s32 e1000_check_downshift(struct e1000_hw *hw) { s32 ret_val; u16 phy_data; @@ -6985,9 +6861,7 @@ e1000_check_downshift(struct e1000_hw *hw) * ****************************************************************************/ -static s32 -e1000_config_dsp_after_link_change(struct e1000_hw *hw, - bool link_up) +static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up) { s32 ret_val; u16 phy_data, phy_saved_data, speed, duplex, i; @@ -7173,8 +7047,7 @@ e1000_config_dsp_after_link_change(struct e1000_hw *hw, * * hw - Struct containing variables accessed by shared code ****************************************************************************/ -static s32 -e1000_set_phy_mode(struct e1000_hw *hw) +static s32 e1000_set_phy_mode(struct e1000_hw *hw) { s32 ret_val; u16 eeprom_data; @@ -7218,9 +7091,7 @@ e1000_set_phy_mode(struct e1000_hw *hw) * ****************************************************************************/ -static s32 -e1000_set_d3_lplu_state(struct e1000_hw *hw, - bool active) +static s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active) { u32 phy_ctrl = 0; s32 ret_val; @@ -7242,7 +7113,7 @@ e1000_set_d3_lplu_state(struct e1000_hw *hw, /* MAC writes into PHY register based on the state transition * and start auto-negotiation. SW driver can overwrite the settings * in CSR PHY power control E1000_PHY_CTRL register. */ - phy_ctrl = E1000_READ_REG(hw, PHY_CTRL); + phy_ctrl = er32(PHY_CTRL); } else { ret_val = e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, &phy_data); if (ret_val) @@ -7259,7 +7130,7 @@ e1000_set_d3_lplu_state(struct e1000_hw *hw, } else { if (hw->mac_type == e1000_ich8lan) { phy_ctrl &= ~E1000_PHY_CTRL_NOND0A_LPLU; - E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + ew32(PHY_CTRL, phy_ctrl); } else { phy_data &= ~IGP02E1000_PM_D3_LPLU; ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, @@ -7310,7 +7181,7 @@ e1000_set_d3_lplu_state(struct e1000_hw *hw, } else { if (hw->mac_type == e1000_ich8lan) { phy_ctrl |= E1000_PHY_CTRL_NOND0A_LPLU; - E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + ew32(PHY_CTRL, phy_ctrl); } else { phy_data |= IGP02E1000_PM_D3_LPLU; ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, @@ -7348,9 +7219,7 @@ e1000_set_d3_lplu_state(struct e1000_hw *hw, * ****************************************************************************/ -static s32 -e1000_set_d0_lplu_state(struct e1000_hw *hw, - bool active) +static s32 e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active) { u32 phy_ctrl = 0; s32 ret_val; @@ -7361,7 +7230,7 @@ e1000_set_d0_lplu_state(struct e1000_hw *hw, return E1000_SUCCESS; if (hw->mac_type == e1000_ich8lan) { - phy_ctrl = E1000_READ_REG(hw, PHY_CTRL); + phy_ctrl = er32(PHY_CTRL); } else { ret_val = e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, &phy_data); if (ret_val) @@ -7371,7 +7240,7 @@ e1000_set_d0_lplu_state(struct e1000_hw *hw, if (!active) { if (hw->mac_type == e1000_ich8lan) { phy_ctrl &= ~E1000_PHY_CTRL_D0A_LPLU; - E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + ew32(PHY_CTRL, phy_ctrl); } else { phy_data &= ~IGP02E1000_PM_D0_LPLU; ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, phy_data); @@ -7412,7 +7281,7 @@ e1000_set_d0_lplu_state(struct e1000_hw *hw, if (hw->mac_type == e1000_ich8lan) { phy_ctrl |= E1000_PHY_CTRL_D0A_LPLU; - E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + ew32(PHY_CTRL, phy_ctrl); } else { phy_data |= IGP02E1000_PM_D0_LPLU; ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, phy_data); @@ -7439,8 +7308,7 @@ e1000_set_d0_lplu_state(struct e1000_hw *hw, * * hw - Struct containing variables accessed by shared code *****************************************************************************/ -static s32 -e1000_set_vco_speed(struct e1000_hw *hw) +static s32 e1000_set_vco_speed(struct e1000_hw *hw) { s32 ret_val; u16 default_page = 0; @@ -7503,8 +7371,7 @@ e1000_set_vco_speed(struct e1000_hw *hw) * * returns: - E1000_SUCCESS . ****************************************************************************/ -static s32 -e1000_host_if_read_cookie(struct e1000_hw * hw, u8 *buffer) +static s32 e1000_host_if_read_cookie(struct e1000_hw *hw, u8 *buffer) { u8 i; u32 offset = E1000_MNG_DHCP_COOKIE_OFFSET; @@ -7514,7 +7381,7 @@ e1000_host_if_read_cookie(struct e1000_hw * hw, u8 *buffer) offset = (offset >> 2); for (i = 0; i < length; i++) { - *((u32 *) buffer + i) = + *((u32 *)buffer + i) = E1000_READ_REG_ARRAY_DWORD(hw, HOST_IF, offset + i); } return E1000_SUCCESS; @@ -7530,21 +7397,20 @@ e1000_host_if_read_cookie(struct e1000_hw * hw, u8 *buffer) * timeout * - E1000_SUCCESS for success. ****************************************************************************/ -static s32 -e1000_mng_enable_host_if(struct e1000_hw * hw) +static s32 e1000_mng_enable_host_if(struct e1000_hw *hw) { u32 hicr; u8 i; /* Check that the host interface is enabled. */ - hicr = E1000_READ_REG(hw, HICR); + hicr = er32(HICR); if ((hicr & E1000_HICR_EN) == 0) { DEBUGOUT("E1000_HOST_EN bit disabled.\n"); return -E1000_ERR_HOST_INTERFACE_COMMAND; } /* check the previous command is completed */ for (i = 0; i < E1000_MNG_DHCP_COMMAND_TIMEOUT; i++) { - hicr = E1000_READ_REG(hw, HICR); + hicr = er32(HICR); if (!(hicr & E1000_HICR_C)) break; mdelay(1); @@ -7564,9 +7430,8 @@ e1000_mng_enable_host_if(struct e1000_hw * hw) * * returns - E1000_SUCCESS for success. ****************************************************************************/ -static s32 -e1000_mng_host_if_write(struct e1000_hw * hw, u8 *buffer, - u16 length, u16 offset, u8 *sum) +static s32 e1000_mng_host_if_write(struct e1000_hw *hw, u8 *buffer, u16 length, + u16 offset, u8 *sum) { u8 *tmp; u8 *bufptr = buffer; @@ -7632,9 +7497,8 @@ e1000_mng_host_if_write(struct e1000_hw * hw, u8 *buffer, * * returns - E1000_SUCCESS for success. ****************************************************************************/ -static s32 -e1000_mng_write_cmd_header(struct e1000_hw * hw, - struct e1000_host_mng_command_header * hdr) +static s32 e1000_mng_write_cmd_header(struct e1000_hw *hw, + struct e1000_host_mng_command_header *hdr) { u16 i; u8 sum; @@ -7648,7 +7512,7 @@ e1000_mng_write_cmd_header(struct e1000_hw * hw, sum = hdr->checksum; hdr->checksum = 0; - buffer = (u8 *) hdr; + buffer = (u8 *)hdr; i = length; while (i--) sum += buffer[i]; @@ -7658,8 +7522,8 @@ e1000_mng_write_cmd_header(struct e1000_hw * hw, length >>= 2; /* The device driver writes the relevant command block into the ram area. */ for (i = 0; i < length; i++) { - E1000_WRITE_REG_ARRAY_DWORD(hw, HOST_IF, i, *((u32 *) hdr + i)); - E1000_WRITE_FLUSH(hw); + E1000_WRITE_REG_ARRAY_DWORD(hw, HOST_IF, i, *((u32 *)hdr + i)); + E1000_WRITE_FLUSH(); } return E1000_SUCCESS; @@ -7672,14 +7536,13 @@ e1000_mng_write_cmd_header(struct e1000_hw * hw, * * returns - E1000_SUCCESS for success. ****************************************************************************/ -static s32 -e1000_mng_write_commit(struct e1000_hw * hw) +static s32 e1000_mng_write_commit(struct e1000_hw *hw) { u32 hicr; - hicr = E1000_READ_REG(hw, HICR); + hicr = er32(HICR); /* Setting this bit tells the ARC that a new command is pending. */ - E1000_WRITE_REG(hw, HICR, hicr | E1000_HICR_C); + ew32(HICR, hicr | E1000_HICR_C); return E1000_SUCCESS; } @@ -7690,12 +7553,11 @@ e1000_mng_write_commit(struct e1000_hw * hw) * * returns - true when the mode is IAMT or false. ****************************************************************************/ -bool -e1000_check_mng_mode(struct e1000_hw *hw) +bool e1000_check_mng_mode(struct e1000_hw *hw) { u32 fwsm; - fwsm = E1000_READ_REG(hw, FWSM); + fwsm = er32(FWSM); if (hw->mac_type == e1000_ich8lan) { if ((fwsm & E1000_FWSM_MODE_MASK) == @@ -7712,9 +7574,7 @@ e1000_check_mng_mode(struct e1000_hw *hw) /***************************************************************************** * This function writes the dhcp info . ****************************************************************************/ -s32 -e1000_mng_write_dhcp_info(struct e1000_hw * hw, u8 *buffer, - u16 length) +s32 e1000_mng_write_dhcp_info(struct e1000_hw *hw, u8 *buffer, u16 length) { s32 ret_val; struct e1000_host_mng_command_header hdr; @@ -7744,8 +7604,7 @@ e1000_mng_write_dhcp_info(struct e1000_hw * hw, u8 *buffer, * * returns - checksum of buffer contents. ****************************************************************************/ -static u8 -e1000_calculate_mng_checksum(char *buffer, u32 length) +static u8 e1000_calculate_mng_checksum(char *buffer, u32 length) { u8 sum = 0; u32 i; @@ -7756,7 +7615,7 @@ e1000_calculate_mng_checksum(char *buffer, u32 length) for (i=0; i < length; i++) sum += buffer[i]; - return (u8) (0 - sum); + return (u8)(0 - sum); } /***************************************************************************** @@ -7764,8 +7623,7 @@ e1000_calculate_mng_checksum(char *buffer, u32 length) * * returns - true for packet filtering or false. ****************************************************************************/ -bool -e1000_enable_tx_pkt_filtering(struct e1000_hw *hw) +bool e1000_enable_tx_pkt_filtering(struct e1000_hw *hw) { /* called in init as well as watchdog timer functions */ @@ -7806,21 +7664,20 @@ e1000_enable_tx_pkt_filtering(struct e1000_hw *hw) * returns: - true/false * *****************************************************************************/ -u32 -e1000_enable_mng_pass_thru(struct e1000_hw *hw) +u32 e1000_enable_mng_pass_thru(struct e1000_hw *hw) { u32 manc; u32 fwsm, factps; if (hw->asf_firmware_present) { - manc = E1000_READ_REG(hw, MANC); + manc = er32(MANC); if (!(manc & E1000_MANC_RCV_TCO_EN) || !(manc & E1000_MANC_EN_MAC_ADDR_FILTER)) return false; if (e1000_arc_subsystem_valid(hw)) { - fwsm = E1000_READ_REG(hw, FWSM); - factps = E1000_READ_REG(hw, FACTPS); + fwsm = er32(FWSM); + factps = er32(FACTPS); if ((((fwsm & E1000_FWSM_MODE_MASK) >> E1000_FWSM_MODE_SHIFT) == e1000_mng_mode_pt) && !(factps & E1000_FACTPS_MNGCG)) @@ -7832,8 +7689,7 @@ e1000_enable_mng_pass_thru(struct e1000_hw *hw) return false; } -static s32 -e1000_polarity_reversal_workaround(struct e1000_hw *hw) +static s32 e1000_polarity_reversal_workaround(struct e1000_hw *hw) { s32 ret_val; u16 mii_status_reg; @@ -7926,8 +7782,7 @@ e1000_polarity_reversal_workaround(struct e1000_hw *hw) * returns: - none. * ***************************************************************************/ -static void -e1000_set_pci_express_master_disable(struct e1000_hw *hw) +static void e1000_set_pci_express_master_disable(struct e1000_hw *hw) { u32 ctrl; @@ -7936,9 +7791,9 @@ e1000_set_pci_express_master_disable(struct e1000_hw *hw) if (hw->bus_type != e1000_bus_type_pci_express) return; - ctrl = E1000_READ_REG(hw, CTRL); + ctrl = er32(CTRL); ctrl |= E1000_CTRL_GIO_MASTER_DISABLE; - E1000_WRITE_REG(hw, CTRL, ctrl); + ew32(CTRL, ctrl); } /******************************************************************************* @@ -7952,8 +7807,7 @@ e1000_set_pci_express_master_disable(struct e1000_hw *hw) * E1000_SUCCESS master requests disabled. * ******************************************************************************/ -s32 -e1000_disable_pciex_master(struct e1000_hw *hw) +s32 e1000_disable_pciex_master(struct e1000_hw *hw) { s32 timeout = MASTER_DISABLE_TIMEOUT; /* 80ms */ @@ -7965,7 +7819,7 @@ e1000_disable_pciex_master(struct e1000_hw *hw) e1000_set_pci_express_master_disable(hw); while (timeout) { - if (!(E1000_READ_REG(hw, STATUS) & E1000_STATUS_GIO_MASTER_ENABLE)) + if (!(er32(STATUS) & E1000_STATUS_GIO_MASTER_ENABLE)) break; else udelay(100); @@ -7990,8 +7844,7 @@ e1000_disable_pciex_master(struct e1000_hw *hw) * E1000_SUCCESS at any other case. * ******************************************************************************/ -static s32 -e1000_get_auto_rd_done(struct e1000_hw *hw) +static s32 e1000_get_auto_rd_done(struct e1000_hw *hw) { s32 timeout = AUTO_READ_DONE_TIMEOUT; @@ -8007,7 +7860,7 @@ e1000_get_auto_rd_done(struct e1000_hw *hw) case e1000_80003es2lan: case e1000_ich8lan: while (timeout) { - if (E1000_READ_REG(hw, EECD) & E1000_EECD_AUTO_RD) + if (er32(EECD) & E1000_EECD_AUTO_RD) break; else msleep(1); timeout--; @@ -8038,8 +7891,7 @@ e1000_get_auto_rd_done(struct e1000_hw *hw) * E1000_SUCCESS at any other case. * ***************************************************************************/ -static s32 -e1000_get_phy_cfg_done(struct e1000_hw *hw) +static s32 e1000_get_phy_cfg_done(struct e1000_hw *hw) { s32 timeout = PHY_CFG_TIMEOUT; u32 cfg_mask = E1000_EEPROM_CFG_DONE; @@ -8052,13 +7904,13 @@ e1000_get_phy_cfg_done(struct e1000_hw *hw) break; case e1000_80003es2lan: /* Separate *_CFG_DONE_* bit for each port */ - if (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1) + if (er32(STATUS) & E1000_STATUS_FUNC_1) cfg_mask = E1000_EEPROM_CFG_DONE_PORT_1; /* Fall Through */ case e1000_82571: case e1000_82572: while (timeout) { - if (E1000_READ_REG(hw, EEMNGCTL) & cfg_mask) + if (er32(EEMNGCTL) & cfg_mask) break; else msleep(1); @@ -8085,8 +7937,7 @@ e1000_get_phy_cfg_done(struct e1000_hw *hw) * E1000_SUCCESS at any other case. * ***************************************************************************/ -static s32 -e1000_get_hw_eeprom_semaphore(struct e1000_hw *hw) +static s32 e1000_get_hw_eeprom_semaphore(struct e1000_hw *hw) { s32 timeout; u32 swsm; @@ -8105,11 +7956,11 @@ e1000_get_hw_eeprom_semaphore(struct e1000_hw *hw) /* Get the FW semaphore. */ timeout = hw->eeprom.word_size + 1; while (timeout) { - swsm = E1000_READ_REG(hw, SWSM); + swsm = er32(SWSM); swsm |= E1000_SWSM_SWESMBI; - E1000_WRITE_REG(hw, SWSM, swsm); + ew32(SWSM, swsm); /* if we managed to set the bit we got the semaphore. */ - swsm = E1000_READ_REG(hw, SWSM); + swsm = er32(SWSM); if (swsm & E1000_SWSM_SWESMBI) break; @@ -8135,8 +7986,7 @@ e1000_get_hw_eeprom_semaphore(struct e1000_hw *hw) * returns: - None. * ***************************************************************************/ -static void -e1000_put_hw_eeprom_semaphore(struct e1000_hw *hw) +static void e1000_put_hw_eeprom_semaphore(struct e1000_hw *hw) { u32 swsm; @@ -8145,13 +7995,13 @@ e1000_put_hw_eeprom_semaphore(struct e1000_hw *hw) if (!hw->eeprom_semaphore_present) return; - swsm = E1000_READ_REG(hw, SWSM); + swsm = er32(SWSM); if (hw->mac_type == e1000_80003es2lan) { /* Release both semaphores. */ swsm &= ~(E1000_SWSM_SMBI | E1000_SWSM_SWESMBI); } else swsm &= ~(E1000_SWSM_SWESMBI); - E1000_WRITE_REG(hw, SWSM, swsm); + ew32(SWSM, swsm); } /*************************************************************************** @@ -8164,8 +8014,7 @@ e1000_put_hw_eeprom_semaphore(struct e1000_hw *hw) * E1000_SUCCESS at any other case. * ***************************************************************************/ -static s32 -e1000_get_software_semaphore(struct e1000_hw *hw) +static s32 e1000_get_software_semaphore(struct e1000_hw *hw) { s32 timeout = hw->eeprom.word_size + 1; u32 swsm; @@ -8177,7 +8026,7 @@ e1000_get_software_semaphore(struct e1000_hw *hw) } while (timeout) { - swsm = E1000_READ_REG(hw, SWSM); + swsm = er32(SWSM); /* If SMBI bit cleared, it is now set and we hold the semaphore */ if (!(swsm & E1000_SWSM_SMBI)) break; @@ -8200,8 +8049,7 @@ e1000_get_software_semaphore(struct e1000_hw *hw) * hw: Struct containing variables accessed by shared code * ***************************************************************************/ -static void -e1000_release_software_semaphore(struct e1000_hw *hw) +static void e1000_release_software_semaphore(struct e1000_hw *hw) { u32 swsm; @@ -8211,10 +8059,10 @@ e1000_release_software_semaphore(struct e1000_hw *hw) return; } - swsm = E1000_READ_REG(hw, SWSM); + swsm = er32(SWSM); /* Release the SW semaphores.*/ swsm &= ~E1000_SWSM_SMBI; - E1000_WRITE_REG(hw, SWSM, swsm); + ew32(SWSM, swsm); } /****************************************************************************** @@ -8228,26 +8076,24 @@ e1000_release_software_semaphore(struct e1000_hw *hw) * E1000_SUCCESS * *****************************************************************************/ -s32 -e1000_check_phy_reset_block(struct e1000_hw *hw) +s32 e1000_check_phy_reset_block(struct e1000_hw *hw) { u32 manc = 0; u32 fwsm = 0; if (hw->mac_type == e1000_ich8lan) { - fwsm = E1000_READ_REG(hw, FWSM); + fwsm = er32(FWSM); return (fwsm & E1000_FWSM_RSPCIPHY) ? E1000_SUCCESS : E1000_BLK_PHY_RESET; } if (hw->mac_type > e1000_82547_rev_2) - manc = E1000_READ_REG(hw, MANC); + manc = er32(MANC); return (manc & E1000_MANC_BLK_PHY_RST_ON_IDE) ? E1000_BLK_PHY_RESET : E1000_SUCCESS; } -static u8 -e1000_arc_subsystem_valid(struct e1000_hw *hw) +static u8 e1000_arc_subsystem_valid(struct e1000_hw *hw) { u32 fwsm; @@ -8261,7 +8107,7 @@ e1000_arc_subsystem_valid(struct e1000_hw *hw) case e1000_82572: case e1000_82573: case e1000_80003es2lan: - fwsm = E1000_READ_REG(hw, FWSM); + fwsm = er32(FWSM); if ((fwsm & E1000_FWSM_MODE_MASK) != 0) return true; break; @@ -8283,8 +8129,7 @@ e1000_arc_subsystem_valid(struct e1000_hw *hw) * returns: E1000_SUCCESS * *****************************************************************************/ -static s32 -e1000_set_pci_ex_no_snoop(struct e1000_hw *hw, u32 no_snoop) +static s32 e1000_set_pci_ex_no_snoop(struct e1000_hw *hw, u32 no_snoop) { u32 gcr_reg = 0; @@ -8297,19 +8142,19 @@ e1000_set_pci_ex_no_snoop(struct e1000_hw *hw, u32 no_snoop) return E1000_SUCCESS; if (no_snoop) { - gcr_reg = E1000_READ_REG(hw, GCR); + gcr_reg = er32(GCR); gcr_reg &= ~(PCI_EX_NO_SNOOP_ALL); gcr_reg |= no_snoop; - E1000_WRITE_REG(hw, GCR, gcr_reg); + ew32(GCR, gcr_reg); } if (hw->mac_type == e1000_ich8lan) { u32 ctrl_ext; - E1000_WRITE_REG(hw, GCR, PCI_EX_82566_SNOOP_ALL); + ew32(GCR, PCI_EX_82566_SNOOP_ALL); - ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext = er32(CTRL_EXT); ctrl_ext |= E1000_CTRL_EXT_RO_DIS; - E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + ew32(CTRL_EXT, ctrl_ext); } return E1000_SUCCESS; @@ -8324,8 +8169,7 @@ e1000_set_pci_ex_no_snoop(struct e1000_hw *hw, u32 no_snoop) * hw: Struct containing variables accessed by shared code * ***************************************************************************/ -static s32 -e1000_get_software_flag(struct e1000_hw *hw) +static s32 e1000_get_software_flag(struct e1000_hw *hw) { s32 timeout = PHY_CFG_TIMEOUT; u32 extcnf_ctrl; @@ -8334,11 +8178,11 @@ e1000_get_software_flag(struct e1000_hw *hw) if (hw->mac_type == e1000_ich8lan) { while (timeout) { - extcnf_ctrl = E1000_READ_REG(hw, EXTCNF_CTRL); + extcnf_ctrl = er32(EXTCNF_CTRL); extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG; - E1000_WRITE_REG(hw, EXTCNF_CTRL, extcnf_ctrl); + ew32(EXTCNF_CTRL, extcnf_ctrl); - extcnf_ctrl = E1000_READ_REG(hw, EXTCNF_CTRL); + extcnf_ctrl = er32(EXTCNF_CTRL); if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG) break; mdelay(1); @@ -8363,17 +8207,16 @@ e1000_get_software_flag(struct e1000_hw *hw) * hw: Struct containing variables accessed by shared code * ***************************************************************************/ -static void -e1000_release_software_flag(struct e1000_hw *hw) +static void e1000_release_software_flag(struct e1000_hw *hw) { u32 extcnf_ctrl; DEBUGFUNC("e1000_release_software_flag"); if (hw->mac_type == e1000_ich8lan) { - extcnf_ctrl= E1000_READ_REG(hw, EXTCNF_CTRL); + extcnf_ctrl= er32(EXTCNF_CTRL); extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG; - E1000_WRITE_REG(hw, EXTCNF_CTRL, extcnf_ctrl); + ew32(EXTCNF_CTRL, extcnf_ctrl); } return; @@ -8388,9 +8231,8 @@ e1000_release_software_flag(struct e1000_hw *hw) * data - word read from the EEPROM * words - number of words to read *****************************************************************************/ -static s32 -e1000_read_eeprom_ich8(struct e1000_hw *hw, u16 offset, u16 words, - u16 *data) +static s32 e1000_read_eeprom_ich8(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data) { s32 error = E1000_SUCCESS; u32 flash_bank = 0; @@ -8405,7 +8247,7 @@ e1000_read_eeprom_ich8(struct e1000_hw *hw, u16 offset, u16 words, * to be updated with each read. */ /* Value of bit 22 corresponds to the flash bank we're on. */ - flash_bank = (E1000_READ_REG(hw, EECD) & E1000_EECD_SEC1VAL) ? 1 : 0; + flash_bank = (er32(EECD) & E1000_EECD_SEC1VAL) ? 1 : 0; /* Adjust offset appropriately if we're on bank 1 - adjust for word size */ bank_offset = flash_bank * (hw->flash_bank_size * 2); @@ -8444,9 +8286,8 @@ e1000_read_eeprom_ich8(struct e1000_hw *hw, u16 offset, u16 words, * words - number of words to write * data - words to write to the EEPROM *****************************************************************************/ -static s32 -e1000_write_eeprom_ich8(struct e1000_hw *hw, u16 offset, u16 words, - u16 *data) +static s32 e1000_write_eeprom_ich8(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data) { u32 i = 0; s32 error = E1000_SUCCESS; @@ -8491,8 +8332,7 @@ e1000_write_eeprom_ich8(struct e1000_hw *hw, u16 offset, u16 words, * * hw - The pointer to the hw structure ****************************************************************************/ -static s32 -e1000_ich8_cycle_init(struct e1000_hw *hw) +static s32 e1000_ich8_cycle_init(struct e1000_hw *hw) { union ich8_hws_flash_status hsfsts; s32 error = E1000_ERR_EEPROM; @@ -8558,8 +8398,7 @@ e1000_ich8_cycle_init(struct e1000_hw *hw) * * hw - The pointer to the hw structure ****************************************************************************/ -static s32 -e1000_ich8_flash_cycle(struct e1000_hw *hw, u32 timeout) +static s32 e1000_ich8_flash_cycle(struct e1000_hw *hw, u32 timeout) { union ich8_hws_flash_ctrl hsflctl; union ich8_hws_flash_status hsfsts; @@ -8593,9 +8432,8 @@ e1000_ich8_flash_cycle(struct e1000_hw *hw, u32 timeout) * size - Size of data to read, 1=byte 2=word * data - Pointer to the word to store the value read. *****************************************************************************/ -static s32 -e1000_read_ich8_data(struct e1000_hw *hw, u32 index, - u32 size, u16* data) +static s32 e1000_read_ich8_data(struct e1000_hw *hw, u32 index, u32 size, + u16 *data) { union ich8_hws_flash_status hsfsts; union ich8_hws_flash_ctrl hsflctl; @@ -8672,9 +8510,8 @@ e1000_read_ich8_data(struct e1000_hw *hw, u32 index, * size - Size of data to read, 1=byte 2=word * data - The byte(s) to write to the NVM. *****************************************************************************/ -static s32 -e1000_write_ich8_data(struct e1000_hw *hw, u32 index, u32 size, - u16 data) +static s32 e1000_write_ich8_data(struct e1000_hw *hw, u32 index, u32 size, + u16 data) { union ich8_hws_flash_status hsfsts; union ich8_hws_flash_ctrl hsflctl; @@ -8747,8 +8584,7 @@ e1000_write_ich8_data(struct e1000_hw *hw, u32 index, u32 size, * index - The index of the byte to read. * data - Pointer to a byte to store the value read. *****************************************************************************/ -static s32 -e1000_read_ich8_byte(struct e1000_hw *hw, u32 index, u8* data) +static s32 e1000_read_ich8_byte(struct e1000_hw *hw, u32 index, u8 *data) { s32 status = E1000_SUCCESS; u16 word = 0; @@ -8770,8 +8606,7 @@ e1000_read_ich8_byte(struct e1000_hw *hw, u32 index, u8* data) * index - The index of the byte to write. * byte - The byte to write to the NVM. *****************************************************************************/ -static s32 -e1000_verify_write_ich8_byte(struct e1000_hw *hw, u32 index, u8 byte) +static s32 e1000_verify_write_ich8_byte(struct e1000_hw *hw, u32 index, u8 byte) { s32 error = E1000_SUCCESS; s32 program_retries = 0; @@ -8803,8 +8638,7 @@ e1000_verify_write_ich8_byte(struct e1000_hw *hw, u32 index, u8 byte) * index - The index of the byte to read. * data - The byte to write to the NVM. *****************************************************************************/ -static s32 -e1000_write_ich8_byte(struct e1000_hw *hw, u32 index, u8 data) +static s32 e1000_write_ich8_byte(struct e1000_hw *hw, u32 index, u8 data) { s32 status = E1000_SUCCESS; u16 word = (u16)data; @@ -8821,8 +8655,7 @@ e1000_write_ich8_byte(struct e1000_hw *hw, u32 index, u8 data) * index - The starting byte index of the word to read. * data - Pointer to a word to store the value read. *****************************************************************************/ -static s32 -e1000_read_ich8_word(struct e1000_hw *hw, u32 index, u16 *data) +static s32 e1000_read_ich8_word(struct e1000_hw *hw, u32 index, u16 *data) { s32 status = E1000_SUCCESS; status = e1000_read_ich8_data(hw, index, 2, data); @@ -8840,8 +8673,7 @@ e1000_read_ich8_word(struct e1000_hw *hw, u32 index, u16 *data) * amount of NVM used in each bank is a *minimum* of 4 KBytes, but in fact the * bank size may be 4, 8 or 64 KBytes *****************************************************************************/ -static s32 -e1000_erase_ich8_4k_segment(struct e1000_hw *hw, u32 bank) +static s32 e1000_erase_ich8_4k_segment(struct e1000_hw *hw, u32 bank) { union ich8_hws_flash_status hsfsts; union ich8_hws_flash_ctrl hsflctl; @@ -8930,9 +8762,9 @@ e1000_erase_ich8_4k_segment(struct e1000_hw *hw, u32 bank) return error; } -static s32 -e1000_init_lcd_from_nvm_config_region(struct e1000_hw *hw, - u32 cnf_base_addr, u32 cnf_size) +static s32 e1000_init_lcd_from_nvm_config_region(struct e1000_hw *hw, + u32 cnf_base_addr, + u32 cnf_size) { u32 ret_val = E1000_SUCCESS; u16 word_addr, reg_data, reg_addr; @@ -8972,8 +8804,7 @@ e1000_init_lcd_from_nvm_config_region(struct e1000_hw *hw, * * hw: Struct containing variables accessed by shared code *****************************************************************************/ -static s32 -e1000_init_lcd_from_nvm(struct e1000_hw *hw) +static s32 e1000_init_lcd_from_nvm(struct e1000_hw *hw) { u32 reg_data, cnf_base_addr, cnf_size, ret_val, loop; @@ -8981,32 +8812,32 @@ e1000_init_lcd_from_nvm(struct e1000_hw *hw) return E1000_SUCCESS; /* Check if SW needs configure the PHY */ - reg_data = E1000_READ_REG(hw, FEXTNVM); + reg_data = er32(FEXTNVM); if (!(reg_data & FEXTNVM_SW_CONFIG)) return E1000_SUCCESS; /* Wait for basic configuration completes before proceeding*/ loop = 0; do { - reg_data = E1000_READ_REG(hw, STATUS) & E1000_STATUS_LAN_INIT_DONE; + reg_data = er32(STATUS) & E1000_STATUS_LAN_INIT_DONE; udelay(100); loop++; } while ((!reg_data) && (loop < 50)); /* Clear the Init Done bit for the next init event */ - reg_data = E1000_READ_REG(hw, STATUS); + reg_data = er32(STATUS); reg_data &= ~E1000_STATUS_LAN_INIT_DONE; - E1000_WRITE_REG(hw, STATUS, reg_data); + ew32(STATUS, reg_data); /* Make sure HW does not configure LCD from PHY extended configuration before SW configuration */ - reg_data = E1000_READ_REG(hw, EXTCNF_CTRL); + reg_data = er32(EXTCNF_CTRL); if ((reg_data & E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE) == 0x0000) { - reg_data = E1000_READ_REG(hw, EXTCNF_SIZE); + reg_data = er32(EXTCNF_SIZE); cnf_size = reg_data & E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH; cnf_size >>= 16; if (cnf_size) { - reg_data = E1000_READ_REG(hw, EXTCNF_CTRL); + reg_data = er32(EXTCNF_CTRL); cnf_base_addr = reg_data & E1000_EXTCNF_CTRL_EXT_CNF_POINTER; /* cnf_base_addr is in DWORD */ cnf_base_addr >>= 16; diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index cf12b05cd011..ad6da7b67e55 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -31,12 +31,7 @@ char e1000_driver_name[] = "e1000"; static char e1000_driver_string[] = "Intel(R) PRO/1000 Network Driver"; -#ifndef CONFIG_E1000_NAPI -#define DRIVERNAPI -#else -#define DRIVERNAPI "-NAPI" -#endif -#define DRV_VERSION "7.3.20-k2"DRIVERNAPI +#define DRV_VERSION "7.3.20-k3-NAPI" const char e1000_driver_version[] = DRV_VERSION; static const char e1000_copyright[] = "Copyright (c) 1999-2006 Intel Corporation."; @@ -138,7 +133,6 @@ static irqreturn_t e1000_intr(int irq, void *data); static irqreturn_t e1000_intr_msi(int irq, void *data); static bool e1000_clean_tx_irq(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring); -#ifdef CONFIG_E1000_NAPI static int e1000_clean(struct napi_struct *napi, int budget); static bool e1000_clean_rx_irq(struct e1000_adapter *adapter, struct e1000_rx_ring *rx_ring, @@ -146,12 +140,6 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter, static bool e1000_clean_rx_irq_ps(struct e1000_adapter *adapter, struct e1000_rx_ring *rx_ring, int *work_done, int work_to_do); -#else -static bool e1000_clean_rx_irq(struct e1000_adapter *adapter, - struct e1000_rx_ring *rx_ring); -static bool e1000_clean_rx_irq_ps(struct e1000_adapter *adapter, - struct e1000_rx_ring *rx_ring); -#endif static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter, struct e1000_rx_ring *rx_ring, int cleaned_count); @@ -232,8 +220,7 @@ MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); * loaded. All it does is register with the PCI subsystem. **/ -static int __init -e1000_init_module(void) +static int __init e1000_init_module(void) { int ret; printk(KERN_INFO "%s - version %s\n", @@ -261,8 +248,7 @@ module_init(e1000_init_module); * from memory. **/ -static void __exit -e1000_exit_module(void) +static void __exit e1000_exit_module(void) { pci_unregister_driver(&e1000_driver); } @@ -271,12 +257,13 @@ module_exit(e1000_exit_module); static int e1000_request_irq(struct e1000_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; irq_handler_t handler = e1000_intr; int irq_flags = IRQF_SHARED; int err; - if (adapter->hw.mac_type >= e1000_82571) { + if (hw->mac_type >= e1000_82571) { adapter->have_msi = !pci_enable_msi(adapter->pdev); if (adapter->have_msi) { handler = e1000_intr_msi; @@ -311,11 +298,12 @@ static void e1000_free_irq(struct e1000_adapter *adapter) * @adapter: board private structure **/ -static void -e1000_irq_disable(struct e1000_adapter *adapter) +static void e1000_irq_disable(struct e1000_adapter *adapter) { - E1000_WRITE_REG(&adapter->hw, IMC, ~0); - E1000_WRITE_FLUSH(&adapter->hw); + struct e1000_hw *hw = &adapter->hw; + + ew32(IMC, ~0); + E1000_WRITE_FLUSH(); synchronize_irq(adapter->pdev->irq); } @@ -324,22 +312,23 @@ e1000_irq_disable(struct e1000_adapter *adapter) * @adapter: board private structure **/ -static void -e1000_irq_enable(struct e1000_adapter *adapter) +static void e1000_irq_enable(struct e1000_adapter *adapter) { - E1000_WRITE_REG(&adapter->hw, IMS, IMS_ENABLE_MASK); - E1000_WRITE_FLUSH(&adapter->hw); + struct e1000_hw *hw = &adapter->hw; + + ew32(IMS, IMS_ENABLE_MASK); + E1000_WRITE_FLUSH(); } -static void -e1000_update_mng_vlan(struct e1000_adapter *adapter) +static void e1000_update_mng_vlan(struct e1000_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; - u16 vid = adapter->hw.mng_cookie.vlan_id; + u16 vid = hw->mng_cookie.vlan_id; u16 old_vid = adapter->mng_vlan_id; if (adapter->vlgrp) { if (!vlan_group_get_device(adapter->vlgrp, vid)) { - if (adapter->hw.mng_cookie.status & + if (hw->mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT) { e1000_vlan_rx_add_vid(netdev, vid); adapter->mng_vlan_id = vid; @@ -366,26 +355,24 @@ e1000_update_mng_vlan(struct e1000_adapter *adapter) * **/ -static void -e1000_release_hw_control(struct e1000_adapter *adapter) +static void e1000_release_hw_control(struct e1000_adapter *adapter) { u32 ctrl_ext; u32 swsm; + struct e1000_hw *hw = &adapter->hw; /* Let firmware taken over control of h/w */ - switch (adapter->hw.mac_type) { + switch (hw->mac_type) { case e1000_82573: - swsm = E1000_READ_REG(&adapter->hw, SWSM); - E1000_WRITE_REG(&adapter->hw, SWSM, - swsm & ~E1000_SWSM_DRV_LOAD); + swsm = er32(SWSM); + ew32(SWSM, swsm & ~E1000_SWSM_DRV_LOAD); break; case e1000_82571: case e1000_82572: case e1000_80003es2lan: case e1000_ich8lan: - ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT); - E1000_WRITE_REG(&adapter->hw, CTRL_EXT, - ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD); + ctrl_ext = er32(CTRL_EXT); + ew32(CTRL_EXT, ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD); break; default: break; @@ -403,37 +390,36 @@ e1000_release_hw_control(struct e1000_adapter *adapter) * **/ -static void -e1000_get_hw_control(struct e1000_adapter *adapter) +static void e1000_get_hw_control(struct e1000_adapter *adapter) { u32 ctrl_ext; u32 swsm; + struct e1000_hw *hw = &adapter->hw; /* Let firmware know the driver has taken over */ - switch (adapter->hw.mac_type) { + switch (hw->mac_type) { case e1000_82573: - swsm = E1000_READ_REG(&adapter->hw, SWSM); - E1000_WRITE_REG(&adapter->hw, SWSM, - swsm | E1000_SWSM_DRV_LOAD); + swsm = er32(SWSM); + ew32(SWSM, swsm | E1000_SWSM_DRV_LOAD); break; case e1000_82571: case e1000_82572: case e1000_80003es2lan: case e1000_ich8lan: - ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT); - E1000_WRITE_REG(&adapter->hw, CTRL_EXT, - ctrl_ext | E1000_CTRL_EXT_DRV_LOAD); + ctrl_ext = er32(CTRL_EXT); + ew32(CTRL_EXT, ctrl_ext | E1000_CTRL_EXT_DRV_LOAD); break; default: break; } } -static void -e1000_init_manageability(struct e1000_adapter *adapter) +static void e1000_init_manageability(struct e1000_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; + if (adapter->en_mng_pt) { - u32 manc = E1000_READ_REG(&adapter->hw, MANC); + u32 manc = er32(MANC); /* disable hardware interception of ARP */ manc &= ~(E1000_MANC_ARP_EN); @@ -441,37 +427,38 @@ e1000_init_manageability(struct e1000_adapter *adapter) /* enable receiving management packets to the host */ /* this will probably generate destination unreachable messages * from the host OS, but the packets will be handled on SMBUS */ - if (adapter->hw.has_manc2h) { - u32 manc2h = E1000_READ_REG(&adapter->hw, MANC2H); + if (hw->has_manc2h) { + u32 manc2h = er32(MANC2H); manc |= E1000_MANC_EN_MNG2HOST; #define E1000_MNG2HOST_PORT_623 (1 << 5) #define E1000_MNG2HOST_PORT_664 (1 << 6) manc2h |= E1000_MNG2HOST_PORT_623; manc2h |= E1000_MNG2HOST_PORT_664; - E1000_WRITE_REG(&adapter->hw, MANC2H, manc2h); + ew32(MANC2H, manc2h); } - E1000_WRITE_REG(&adapter->hw, MANC, manc); + ew32(MANC, manc); } } -static void -e1000_release_manageability(struct e1000_adapter *adapter) +static void e1000_release_manageability(struct e1000_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; + if (adapter->en_mng_pt) { - u32 manc = E1000_READ_REG(&adapter->hw, MANC); + u32 manc = er32(MANC); /* re-enable hardware interception of ARP */ manc |= E1000_MANC_ARP_EN; - if (adapter->hw.has_manc2h) + if (hw->has_manc2h) manc &= ~E1000_MANC_EN_MNG2HOST; /* don't explicitly have to mess with MANC2H since * MANC has an enable disable that gates MANC2H */ - E1000_WRITE_REG(&adapter->hw, MANC, manc); + ew32(MANC, manc); } } @@ -506,18 +493,19 @@ static void e1000_configure(struct e1000_adapter *adapter) int e1000_up(struct e1000_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; + /* hardware has been reset, we need to reload some things */ e1000_configure(adapter); clear_bit(__E1000_DOWN, &adapter->flags); -#ifdef CONFIG_E1000_NAPI napi_enable(&adapter->napi); -#endif + e1000_irq_enable(adapter); /* fire a link change interrupt to start the watchdog */ - E1000_WRITE_REG(&adapter->hw, ICS, E1000_ICS_LSC); + ew32(ICS, E1000_ICS_LSC); return 0; } @@ -533,30 +521,33 @@ int e1000_up(struct e1000_adapter *adapter) void e1000_power_up_phy(struct e1000_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; u16 mii_reg = 0; /* Just clear the power down bit to wake the phy back up */ - if (adapter->hw.media_type == e1000_media_type_copper) { + if (hw->media_type == e1000_media_type_copper) { /* according to the manual, the phy will retain its * settings across a power-down/up cycle */ - e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &mii_reg); + e1000_read_phy_reg(hw, PHY_CTRL, &mii_reg); mii_reg &= ~MII_CR_POWER_DOWN; - e1000_write_phy_reg(&adapter->hw, PHY_CTRL, mii_reg); + e1000_write_phy_reg(hw, PHY_CTRL, mii_reg); } } static void e1000_power_down_phy(struct e1000_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; + /* Power down the PHY so no link is implied when interface is down * * The PHY cannot be powered down if any of the following is true * * (a) WoL is enabled * (b) AMT is active * (c) SoL/IDER session is active */ - if (!adapter->wol && adapter->hw.mac_type >= e1000_82540 && - adapter->hw.media_type == e1000_media_type_copper) { + if (!adapter->wol && hw->mac_type >= e1000_82540 && + hw->media_type == e1000_media_type_copper) { u16 mii_reg = 0; - switch (adapter->hw.mac_type) { + switch (hw->mac_type) { case e1000_82540: case e1000_82545: case e1000_82545_rev_3: @@ -566,8 +557,7 @@ static void e1000_power_down_phy(struct e1000_adapter *adapter) case e1000_82541_rev_2: case e1000_82547: case e1000_82547_rev_2: - if (E1000_READ_REG(&adapter->hw, MANC) & - E1000_MANC_SMBUS_EN) + if (er32(MANC) & E1000_MANC_SMBUS_EN) goto out; break; case e1000_82571: @@ -575,24 +565,23 @@ static void e1000_power_down_phy(struct e1000_adapter *adapter) case e1000_82573: case e1000_80003es2lan: case e1000_ich8lan: - if (e1000_check_mng_mode(&adapter->hw) || - e1000_check_phy_reset_block(&adapter->hw)) + if (e1000_check_mng_mode(hw) || + e1000_check_phy_reset_block(hw)) goto out; break; default: goto out; } - e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &mii_reg); + e1000_read_phy_reg(hw, PHY_CTRL, &mii_reg); mii_reg |= MII_CR_POWER_DOWN; - e1000_write_phy_reg(&adapter->hw, PHY_CTRL, mii_reg); + e1000_write_phy_reg(hw, PHY_CTRL, mii_reg); mdelay(1); } out: return; } -void -e1000_down(struct e1000_adapter *adapter) +void e1000_down(struct e1000_adapter *adapter) { struct net_device *netdev = adapter->netdev; @@ -600,9 +589,8 @@ e1000_down(struct e1000_adapter *adapter) * reschedule our watchdog timer */ set_bit(__E1000_DOWN, &adapter->flags); -#ifdef CONFIG_E1000_NAPI napi_disable(&adapter->napi); -#endif + e1000_irq_disable(adapter); del_timer_sync(&adapter->tx_fifo_stall_timer); @@ -620,8 +608,7 @@ e1000_down(struct e1000_adapter *adapter) e1000_clean_all_rx_rings(adapter); } -void -e1000_reinit_locked(struct e1000_adapter *adapter) +void e1000_reinit_locked(struct e1000_adapter *adapter) { WARN_ON(in_interrupt()); while (test_and_set_bit(__E1000_RESETTING, &adapter->flags)) @@ -631,9 +618,9 @@ e1000_reinit_locked(struct e1000_adapter *adapter) clear_bit(__E1000_RESETTING, &adapter->flags); } -void -e1000_reset(struct e1000_adapter *adapter) +void e1000_reset(struct e1000_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; u32 pba = 0, tx_space, min_tx_space, min_rx_space; u16 fc_high_water_mark = E1000_FC_HIGH_DIFF; bool legacy_pba_adjust = false; @@ -642,7 +629,7 @@ e1000_reset(struct e1000_adapter *adapter) * To take effect CTRL.RST is required. */ - switch (adapter->hw.mac_type) { + switch (hw->mac_type) { case e1000_82542_rev2_0: case e1000_82542_rev2_1: case e1000_82543: @@ -683,16 +670,16 @@ e1000_reset(struct e1000_adapter *adapter) if (adapter->netdev->mtu > E1000_RXBUFFER_8192) pba -= 8; /* allocate more FIFO for Tx */ - if (adapter->hw.mac_type == e1000_82547) { + if (hw->mac_type == e1000_82547) { adapter->tx_fifo_head = 0; adapter->tx_head_addr = pba << E1000_TX_HEAD_ADDR_SHIFT; adapter->tx_fifo_size = (E1000_PBA_40K - pba) << E1000_PBA_BYTES_SHIFT; atomic_set(&adapter->tx_fifo_stall, 0); } - } else if (adapter->hw.max_frame_size > MAXIMUM_ETHERNET_FRAME_SIZE) { + } else if (hw->max_frame_size > MAXIMUM_ETHERNET_FRAME_SIZE) { /* adjust PBA for jumbo frames */ - E1000_WRITE_REG(&adapter->hw, PBA, pba); + ew32(PBA, pba); /* To maintain wire speed transmits, the Tx FIFO should be * large enough to accomodate two full transmit packets, @@ -700,7 +687,7 @@ e1000_reset(struct e1000_adapter *adapter) * the Rx FIFO should be large enough to accomodate at least * one full receive packet and is similarly rounded up and * expressed in KB. */ - pba = E1000_READ_REG(&adapter->hw, PBA); + pba = er32(PBA); /* upper 16 bits has Tx packet buffer allocation size in KB */ tx_space = pba >> 16; /* lower 16 bits has Rx packet buffer allocation size in KB */ @@ -723,7 +710,7 @@ e1000_reset(struct e1000_adapter *adapter) pba = pba - (min_tx_space - tx_space); /* PCI/PCIx hardware has PBA alignment constraints */ - switch (adapter->hw.mac_type) { + switch (hw->mac_type) { case e1000_82545 ... e1000_82546_rev_3: pba &= ~(E1000_PBA_8K - 1); break; @@ -734,7 +721,7 @@ e1000_reset(struct e1000_adapter *adapter) /* if short on rx space, rx wins and must trump tx * adjustment or use Early Receive if available */ if (pba < min_rx_space) { - switch (adapter->hw.mac_type) { + switch (hw->mac_type) { case e1000_82573: /* ERT enabled in e1000_configure_rx */ break; @@ -746,7 +733,7 @@ e1000_reset(struct e1000_adapter *adapter) } } - E1000_WRITE_REG(&adapter->hw, PBA, pba); + ew32(PBA, pba); /* flow control settings */ /* Set the FC high water mark to 90% of the FIFO size. @@ -759,54 +746,54 @@ e1000_reset(struct e1000_adapter *adapter) if (pba < E1000_PBA_16K) fc_high_water_mark = (pba * 1024) - 1600; - adapter->hw.fc_high_water = fc_high_water_mark; - adapter->hw.fc_low_water = fc_high_water_mark - 8; - if (adapter->hw.mac_type == e1000_80003es2lan) - adapter->hw.fc_pause_time = 0xFFFF; + hw->fc_high_water = fc_high_water_mark; + hw->fc_low_water = fc_high_water_mark - 8; + if (hw->mac_type == e1000_80003es2lan) + hw->fc_pause_time = 0xFFFF; else - adapter->hw.fc_pause_time = E1000_FC_PAUSE_TIME; - adapter->hw.fc_send_xon = 1; - adapter->hw.fc = adapter->hw.original_fc; + hw->fc_pause_time = E1000_FC_PAUSE_TIME; + hw->fc_send_xon = 1; + hw->fc = hw->original_fc; /* Allow time for pending master requests to run */ - e1000_reset_hw(&adapter->hw); - if (adapter->hw.mac_type >= e1000_82544) - E1000_WRITE_REG(&adapter->hw, WUC, 0); + e1000_reset_hw(hw); + if (hw->mac_type >= e1000_82544) + ew32(WUC, 0); - if (e1000_init_hw(&adapter->hw)) + if (e1000_init_hw(hw)) DPRINTK(PROBE, ERR, "Hardware Error\n"); e1000_update_mng_vlan(adapter); /* if (adapter->hwflags & HWFLAGS_PHY_PWR_BIT) { */ - if (adapter->hw.mac_type >= e1000_82544 && - adapter->hw.mac_type <= e1000_82547_rev_2 && - adapter->hw.autoneg == 1 && - adapter->hw.autoneg_advertised == ADVERTISE_1000_FULL) { - u32 ctrl = E1000_READ_REG(&adapter->hw, CTRL); + if (hw->mac_type >= e1000_82544 && + hw->mac_type <= e1000_82547_rev_2 && + hw->autoneg == 1 && + hw->autoneg_advertised == ADVERTISE_1000_FULL) { + u32 ctrl = er32(CTRL); /* clear phy power management bit if we are in gig only mode, * which if enabled will attempt negotiation to 100Mb, which * can cause a loss of link at power off or driver unload */ ctrl &= ~E1000_CTRL_SWDPIN3; - E1000_WRITE_REG(&adapter->hw, CTRL, ctrl); + ew32(CTRL, ctrl); } /* Enable h/w to recognize an 802.1Q VLAN Ethernet packet */ - E1000_WRITE_REG(&adapter->hw, VET, ETHERNET_IEEE_VLAN_TYPE); + ew32(VET, ETHERNET_IEEE_VLAN_TYPE); - e1000_reset_adaptive(&adapter->hw); - e1000_phy_get_info(&adapter->hw, &adapter->phy_info); + e1000_reset_adaptive(hw); + e1000_phy_get_info(hw, &adapter->phy_info); if (!adapter->smart_power_down && - (adapter->hw.mac_type == e1000_82571 || - adapter->hw.mac_type == e1000_82572)) { + (hw->mac_type == e1000_82571 || + hw->mac_type == e1000_82572)) { u16 phy_data = 0; /* speed up time to link by disabling smart power down, ignore * the return value of this function because there is nothing * different we would do if it failed */ - e1000_read_phy_reg(&adapter->hw, IGP02E1000_PHY_POWER_MGMT, + e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, &phy_data); phy_data &= ~IGP02E1000_PM_SPD; - e1000_write_phy_reg(&adapter->hw, IGP02E1000_PHY_POWER_MGMT, + e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, phy_data); } @@ -865,13 +852,49 @@ static void e1000_dump_eeprom(struct e1000_adapter *adapter) printk(KERN_ERR "to enable this network device.\n"); printk(KERN_ERR "Please inspect the EEPROM dump and report the issue " "to your hardware vendor\n"); - printk(KERN_ERR "or Intel Customer Support: linux-nics@intel.com\n"); + printk(KERN_ERR "or Intel Customer Support.\n"); printk(KERN_ERR "/*********************/\n"); kfree(data); } /** + * e1000_is_need_ioport - determine if an adapter needs ioport resources or not + * @pdev: PCI device information struct + * + * Return true if an adapter needs ioport resources + **/ +static int e1000_is_need_ioport(struct pci_dev *pdev) +{ + switch (pdev->device) { + case E1000_DEV_ID_82540EM: + case E1000_DEV_ID_82540EM_LOM: + case E1000_DEV_ID_82540EP: + case E1000_DEV_ID_82540EP_LOM: + case E1000_DEV_ID_82540EP_LP: + case E1000_DEV_ID_82541EI: + case E1000_DEV_ID_82541EI_MOBILE: + case E1000_DEV_ID_82541ER: + case E1000_DEV_ID_82541ER_LOM: + case E1000_DEV_ID_82541GI: + case E1000_DEV_ID_82541GI_LF: + case E1000_DEV_ID_82541GI_MOBILE: + case E1000_DEV_ID_82544EI_COPPER: + case E1000_DEV_ID_82544EI_FIBER: + case E1000_DEV_ID_82544GC_COPPER: + case E1000_DEV_ID_82544GC_LOM: + case E1000_DEV_ID_82545EM_COPPER: + case E1000_DEV_ID_82545EM_FIBER: + case E1000_DEV_ID_82546EB_COPPER: + case E1000_DEV_ID_82546EB_FIBER: + case E1000_DEV_ID_82546EB_QUAD_COPPER: + return true; + default: + return false; + } +} + +/** * e1000_probe - Device Initialization Routine * @pdev: PCI device information struct * @ent: entry in e1000_pci_tbl @@ -882,37 +905,51 @@ static void e1000_dump_eeprom(struct e1000_adapter *adapter) * The OS initialization, configuring of the adapter private structure, * and a hardware reset occur. **/ - -static int __devinit -e1000_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int __devinit e1000_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) { struct net_device *netdev; struct e1000_adapter *adapter; + struct e1000_hw *hw; static int cards_found = 0; static int global_quad_port_a = 0; /* global ksp3 port a indication */ int i, err, pci_using_dac; u16 eeprom_data = 0; u16 eeprom_apme_mask = E1000_EEPROM_APME; + int bars, need_ioport; DECLARE_MAC_BUF(mac); - if ((err = pci_enable_device(pdev))) + /* do not allocate ioport bars when not needed */ + need_ioport = e1000_is_need_ioport(pdev); + if (need_ioport) { + bars = pci_select_bars(pdev, IORESOURCE_MEM | IORESOURCE_IO); + err = pci_enable_device(pdev); + } else { + bars = pci_select_bars(pdev, IORESOURCE_MEM); + err = pci_enable_device(pdev); + } + if (err) return err; - if (!(err = pci_set_dma_mask(pdev, DMA_64BIT_MASK)) && - !(err = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK))) { + if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK) && + !pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK)) { pci_using_dac = 1; } else { - if ((err = pci_set_dma_mask(pdev, DMA_32BIT_MASK)) && - (err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK))) { - E1000_ERR("No usable DMA configuration, aborting\n"); - goto err_dma; + err = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (err) { + err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + if (err) { + E1000_ERR("No usable DMA configuration, " + "aborting\n"); + goto err_dma; + } } pci_using_dac = 0; } - if ((err = pci_request_regions(pdev, e1000_driver_name))) + err = pci_request_selected_regions(pdev, bars, e1000_driver_name); + if (err) goto err_pci_reg; pci_set_master(pdev); @@ -928,21 +965,27 @@ e1000_probe(struct pci_dev *pdev, adapter = netdev_priv(netdev); adapter->netdev = netdev; adapter->pdev = pdev; - adapter->hw.back = adapter; adapter->msg_enable = (1 << debug) - 1; + adapter->bars = bars; + adapter->need_ioport = need_ioport; + + hw = &adapter->hw; + hw->back = adapter; err = -EIO; - adapter->hw.hw_addr = ioremap(pci_resource_start(pdev, BAR_0), - pci_resource_len(pdev, BAR_0)); - if (!adapter->hw.hw_addr) + hw->hw_addr = ioremap(pci_resource_start(pdev, BAR_0), + pci_resource_len(pdev, BAR_0)); + if (!hw->hw_addr) goto err_ioremap; - for (i = BAR_1; i <= BAR_5; i++) { - if (pci_resource_len(pdev, i) == 0) - continue; - if (pci_resource_flags(pdev, i) & IORESOURCE_IO) { - adapter->hw.io_base = pci_resource_start(pdev, i); - break; + if (adapter->need_ioport) { + for (i = BAR_1; i <= BAR_5; i++) { + if (pci_resource_len(pdev, i) == 0) + continue; + if (pci_resource_flags(pdev, i) & IORESOURCE_IO) { + hw->io_base = pci_resource_start(pdev, i); + break; + } } } @@ -957,9 +1000,7 @@ e1000_probe(struct pci_dev *pdev, e1000_set_ethtool_ops(netdev); netdev->tx_timeout = &e1000_tx_timeout; netdev->watchdog_timeo = 5 * HZ; -#ifdef CONFIG_E1000_NAPI netif_napi_add(netdev, &adapter->napi, e1000_clean, 64); -#endif netdev->vlan_rx_register = e1000_vlan_rx_register; netdev->vlan_rx_add_vid = e1000_vlan_rx_add_vid; netdev->vlan_rx_kill_vid = e1000_vlan_rx_kill_vid; @@ -972,49 +1013,50 @@ e1000_probe(struct pci_dev *pdev, /* setup the private structure */ - if ((err = e1000_sw_init(adapter))) + err = e1000_sw_init(adapter); + if (err) goto err_sw_init; err = -EIO; /* Flash BAR mapping must happen after e1000_sw_init * because it depends on mac_type */ - if ((adapter->hw.mac_type == e1000_ich8lan) && + if ((hw->mac_type == e1000_ich8lan) && (pci_resource_flags(pdev, 1) & IORESOURCE_MEM)) { - adapter->hw.flash_address = + hw->flash_address = ioremap(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1)); - if (!adapter->hw.flash_address) + if (!hw->flash_address) goto err_flashmap; } - if (e1000_check_phy_reset_block(&adapter->hw)) + if (e1000_check_phy_reset_block(hw)) DPRINTK(PROBE, INFO, "PHY reset is blocked due to SOL/IDER session.\n"); - if (adapter->hw.mac_type >= e1000_82543) { + if (hw->mac_type >= e1000_82543) { netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER; - if (adapter->hw.mac_type == e1000_ich8lan) + if (hw->mac_type == e1000_ich8lan) netdev->features &= ~NETIF_F_HW_VLAN_FILTER; } - if ((adapter->hw.mac_type >= e1000_82544) && - (adapter->hw.mac_type != e1000_82547)) + if ((hw->mac_type >= e1000_82544) && + (hw->mac_type != e1000_82547)) netdev->features |= NETIF_F_TSO; - if (adapter->hw.mac_type > e1000_82547_rev_2) + if (hw->mac_type > e1000_82547_rev_2) netdev->features |= NETIF_F_TSO6; if (pci_using_dac) netdev->features |= NETIF_F_HIGHDMA; netdev->features |= NETIF_F_LLTX; - adapter->en_mng_pt = e1000_enable_mng_pass_thru(&adapter->hw); + adapter->en_mng_pt = e1000_enable_mng_pass_thru(hw); /* initialize eeprom parameters */ - if (e1000_init_eeprom_params(&adapter->hw)) { + if (e1000_init_eeprom_params(hw)) { E1000_ERR("EEPROM initialization failed\n"); goto err_eeprom; } @@ -1022,10 +1064,10 @@ e1000_probe(struct pci_dev *pdev, /* before reading the EEPROM, reset the controller to * put the device in a known good starting state */ - e1000_reset_hw(&adapter->hw); + e1000_reset_hw(hw); /* make sure the EEPROM is good */ - if (e1000_validate_eeprom_checksum(&adapter->hw) < 0) { + if (e1000_validate_eeprom_checksum(hw) < 0) { DPRINTK(PROBE, ERR, "The EEPROM Checksum Is Not Valid\n"); e1000_dump_eeprom(adapter); /* @@ -1036,24 +1078,24 @@ e1000_probe(struct pci_dev *pdev, * interface after manually setting a hw addr using * `ip set address` */ - memset(adapter->hw.mac_addr, 0, netdev->addr_len); + memset(hw->mac_addr, 0, netdev->addr_len); } else { /* copy the MAC address out of the EEPROM */ - if (e1000_read_mac_addr(&adapter->hw)) + if (e1000_read_mac_addr(hw)) DPRINTK(PROBE, ERR, "EEPROM Read Error\n"); } /* don't block initalization here due to bad MAC address */ - memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len); - memcpy(netdev->perm_addr, adapter->hw.mac_addr, netdev->addr_len); + memcpy(netdev->dev_addr, hw->mac_addr, netdev->addr_len); + memcpy(netdev->perm_addr, hw->mac_addr, netdev->addr_len); if (!is_valid_ether_addr(netdev->perm_addr)) DPRINTK(PROBE, ERR, "Invalid MAC Address\n"); - e1000_get_bus_info(&adapter->hw); + e1000_get_bus_info(hw); init_timer(&adapter->tx_fifo_stall_timer); adapter->tx_fifo_stall_timer.function = &e1000_82547_tx_fifo_stall; - adapter->tx_fifo_stall_timer.data = (unsigned long) adapter; + adapter->tx_fifo_stall_timer.data = (unsigned long)adapter; init_timer(&adapter->watchdog_timer); adapter->watchdog_timer.function = &e1000_watchdog; @@ -1061,7 +1103,7 @@ e1000_probe(struct pci_dev *pdev, init_timer(&adapter->phy_info_timer); adapter->phy_info_timer.function = &e1000_update_phy_info; - adapter->phy_info_timer.data = (unsigned long) adapter; + adapter->phy_info_timer.data = (unsigned long)adapter; INIT_WORK(&adapter->reset_task, e1000_reset_task); @@ -1072,18 +1114,18 @@ e1000_probe(struct pci_dev *pdev, * enable the ACPI Magic Packet filter */ - switch (adapter->hw.mac_type) { + switch (hw->mac_type) { case e1000_82542_rev2_0: case e1000_82542_rev2_1: case e1000_82543: break; case e1000_82544: - e1000_read_eeprom(&adapter->hw, + e1000_read_eeprom(hw, EEPROM_INIT_CONTROL2_REG, 1, &eeprom_data); eeprom_apme_mask = E1000_EEPROM_82544_APM; break; case e1000_ich8lan: - e1000_read_eeprom(&adapter->hw, + e1000_read_eeprom(hw, EEPROM_INIT_CONTROL1_REG, 1, &eeprom_data); eeprom_apme_mask = E1000_EEPROM_ICH8_APME; break; @@ -1091,14 +1133,14 @@ e1000_probe(struct pci_dev *pdev, case e1000_82546_rev_3: case e1000_82571: case e1000_80003es2lan: - if (E1000_READ_REG(&adapter->hw, STATUS) & E1000_STATUS_FUNC_1){ - e1000_read_eeprom(&adapter->hw, + if (er32(STATUS) & E1000_STATUS_FUNC_1){ + e1000_read_eeprom(hw, EEPROM_INIT_CONTROL3_PORT_B, 1, &eeprom_data); break; } /* Fall Through */ default: - e1000_read_eeprom(&adapter->hw, + e1000_read_eeprom(hw, EEPROM_INIT_CONTROL3_PORT_A, 1, &eeprom_data); break; } @@ -1117,7 +1159,7 @@ e1000_probe(struct pci_dev *pdev, case E1000_DEV_ID_82571EB_FIBER: /* Wake events only supported on port A for dual fiber * regardless of eeprom setting */ - if (E1000_READ_REG(&adapter->hw, STATUS) & E1000_STATUS_FUNC_1) + if (er32(STATUS) & E1000_STATUS_FUNC_1) adapter->eeprom_wol = 0; break; case E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3: @@ -1140,8 +1182,6 @@ e1000_probe(struct pci_dev *pdev, adapter->wol = adapter->eeprom_wol; /* print bus type/speed/width info */ - { - struct e1000_hw *hw = &adapter->hw; DPRINTK(PROBE, INFO, "(PCI%s:%s:%s) ", ((hw->bus_type == e1000_bus_type_pcix) ? "-X" : (hw->bus_type == e1000_bus_type_pci_express ? " Express":"")), @@ -1154,11 +1194,10 @@ e1000_probe(struct pci_dev *pdev, (hw->bus_width == e1000_bus_width_pciex_4) ? "Width x4" : (hw->bus_width == e1000_bus_width_pciex_1) ? "Width x1" : "32-bit")); - } printk("%s\n", print_mac(mac, netdev->dev_addr)); - if (adapter->hw.bus_type == e1000_bus_type_pci_express) { + if (hw->bus_type == e1000_bus_type_pci_express) { DPRINTK(PROBE, WARNING, "This device (id %04x:%04x) will no " "longer be supported by this driver in the future.\n", pdev->vendor, pdev->device); @@ -1173,8 +1212,8 @@ e1000_probe(struct pci_dev *pdev, * DRV_LOAD until the interface is up. For all other cases, * let the f/w know that the h/w is now under the control * of the driver. */ - if (adapter->hw.mac_type != e1000_82573 || - !e1000_check_mng_mode(&adapter->hw)) + if (hw->mac_type != e1000_82573 || + !e1000_check_mng_mode(hw)) e1000_get_hw_control(adapter); /* tell the stack to leave us alone until e1000_open() is called */ @@ -1182,7 +1221,8 @@ e1000_probe(struct pci_dev *pdev, netif_stop_queue(netdev); strcpy(netdev->name, "eth%d"); - if ((err = register_netdev(netdev))) + err = register_netdev(netdev); + if (err) goto err_register; DPRINTK(PROBE, INFO, "Intel(R) PRO/1000 Network Connection\n"); @@ -1193,28 +1233,24 @@ e1000_probe(struct pci_dev *pdev, err_register: e1000_release_hw_control(adapter); err_eeprom: - if (!e1000_check_phy_reset_block(&adapter->hw)) - e1000_phy_hw_reset(&adapter->hw); + if (!e1000_check_phy_reset_block(hw)) + e1000_phy_hw_reset(hw); - if (adapter->hw.flash_address) - iounmap(adapter->hw.flash_address); + if (hw->flash_address) + iounmap(hw->flash_address); err_flashmap: -#ifdef CONFIG_E1000_NAPI for (i = 0; i < adapter->num_rx_queues; i++) dev_put(&adapter->polling_netdev[i]); -#endif kfree(adapter->tx_ring); kfree(adapter->rx_ring); -#ifdef CONFIG_E1000_NAPI kfree(adapter->polling_netdev); -#endif err_sw_init: - iounmap(adapter->hw.hw_addr); + iounmap(hw->hw_addr); err_ioremap: free_netdev(netdev); err_alloc_etherdev: - pci_release_regions(pdev); + pci_release_selected_regions(pdev, bars); err_pci_reg: err_dma: pci_disable_device(pdev); @@ -1231,14 +1267,12 @@ err_dma: * memory. **/ -static void __devexit -e1000_remove(struct pci_dev *pdev) +static void __devexit e1000_remove(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct e1000_adapter *adapter = netdev_priv(netdev); -#ifdef CONFIG_E1000_NAPI + struct e1000_hw *hw = &adapter->hw; int i; -#endif cancel_work_sync(&adapter->reset_task); @@ -1248,26 +1282,22 @@ e1000_remove(struct pci_dev *pdev) * would have already happened in close and is redundant. */ e1000_release_hw_control(adapter); -#ifdef CONFIG_E1000_NAPI for (i = 0; i < adapter->num_rx_queues; i++) dev_put(&adapter->polling_netdev[i]); -#endif unregister_netdev(netdev); - if (!e1000_check_phy_reset_block(&adapter->hw)) - e1000_phy_hw_reset(&adapter->hw); + if (!e1000_check_phy_reset_block(hw)) + e1000_phy_hw_reset(hw); kfree(adapter->tx_ring); kfree(adapter->rx_ring); -#ifdef CONFIG_E1000_NAPI kfree(adapter->polling_netdev); -#endif - iounmap(adapter->hw.hw_addr); - if (adapter->hw.flash_address) - iounmap(adapter->hw.flash_address); - pci_release_regions(pdev); + iounmap(hw->hw_addr); + if (hw->flash_address) + iounmap(hw->flash_address); + pci_release_selected_regions(pdev, adapter->bars); free_netdev(netdev); @@ -1283,15 +1313,12 @@ e1000_remove(struct pci_dev *pdev) * OS network device settings (MTU size). **/ -static int __devinit -e1000_sw_init(struct e1000_adapter *adapter) +static int __devinit e1000_sw_init(struct e1000_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; struct pci_dev *pdev = adapter->pdev; -#ifdef CONFIG_E1000_NAPI int i; -#endif /* PCI config space info */ @@ -1349,14 +1376,12 @@ e1000_sw_init(struct e1000_adapter *adapter) return -ENOMEM; } -#ifdef CONFIG_E1000_NAPI for (i = 0; i < adapter->num_rx_queues; i++) { adapter->polling_netdev[i].priv = adapter; dev_hold(&adapter->polling_netdev[i]); set_bit(__LINK_STATE_START, &adapter->polling_netdev[i].state); } spin_lock_init(&adapter->tx_queue_lock); -#endif /* Explicitly disable IRQ since the NIC can be in any state. */ e1000_irq_disable(adapter); @@ -1377,8 +1402,7 @@ e1000_sw_init(struct e1000_adapter *adapter) * intended for Multiqueue, but should work fine with a single queue. **/ -static int __devinit -e1000_alloc_queues(struct e1000_adapter *adapter) +static int __devinit e1000_alloc_queues(struct e1000_adapter *adapter) { adapter->tx_ring = kcalloc(adapter->num_tx_queues, sizeof(struct e1000_tx_ring), GFP_KERNEL); @@ -1392,7 +1416,6 @@ e1000_alloc_queues(struct e1000_adapter *adapter) return -ENOMEM; } -#ifdef CONFIG_E1000_NAPI adapter->polling_netdev = kcalloc(adapter->num_rx_queues, sizeof(struct net_device), GFP_KERNEL); @@ -1401,7 +1424,6 @@ e1000_alloc_queues(struct e1000_adapter *adapter) kfree(adapter->rx_ring); return -ENOMEM; } -#endif return E1000_SUCCESS; } @@ -1419,10 +1441,10 @@ e1000_alloc_queues(struct e1000_adapter *adapter) * and the stack is notified that the interface is ready. **/ -static int -e1000_open(struct net_device *netdev) +static int e1000_open(struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; int err; /* disallow open during test */ @@ -1442,15 +1464,15 @@ e1000_open(struct net_device *netdev) e1000_power_up_phy(adapter); adapter->mng_vlan_id = E1000_MNG_VLAN_NONE; - if ((adapter->hw.mng_cookie.status & + if ((hw->mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT)) { e1000_update_mng_vlan(adapter); } /* If AMT is enabled, let the firmware know that the network * interface is now open */ - if (adapter->hw.mac_type == e1000_82573 && - e1000_check_mng_mode(&adapter->hw)) + if (hw->mac_type == e1000_82573 && + e1000_check_mng_mode(hw)) e1000_get_hw_control(adapter); /* before we allocate an interrupt, we must be ready to handle it. @@ -1466,16 +1488,14 @@ e1000_open(struct net_device *netdev) /* From here on the code is the same as e1000_up() */ clear_bit(__E1000_DOWN, &adapter->flags); -#ifdef CONFIG_E1000_NAPI napi_enable(&adapter->napi); -#endif e1000_irq_enable(adapter); netif_start_queue(netdev); /* fire a link status change interrupt to start the watchdog */ - E1000_WRITE_REG(&adapter->hw, ICS, E1000_ICS_LSC); + ew32(ICS, E1000_ICS_LSC); return E1000_SUCCESS; @@ -1503,10 +1523,10 @@ err_setup_tx: * hardware, and all transmit and receive resources are freed. **/ -static int -e1000_close(struct net_device *netdev) +static int e1000_close(struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; WARN_ON(test_bit(__E1000_RESETTING, &adapter->flags)); e1000_down(adapter); @@ -1518,7 +1538,7 @@ e1000_close(struct net_device *netdev) /* kill manageability vlan ID if supported, but not if a vlan with * the same ID is registered on the host OS (let 8021q kill it) */ - if ((adapter->hw.mng_cookie.status & + if ((hw->mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT) && !(adapter->vlgrp && vlan_group_get_device(adapter->vlgrp, adapter->mng_vlan_id))) { @@ -1527,8 +1547,8 @@ e1000_close(struct net_device *netdev) /* If AMT is enabled, let the firmware know that the network * interface is now closed */ - if (adapter->hw.mac_type == e1000_82573 && - e1000_check_mng_mode(&adapter->hw)) + if (hw->mac_type == e1000_82573 && + e1000_check_mng_mode(hw)) e1000_release_hw_control(adapter); return 0; @@ -1540,17 +1560,17 @@ e1000_close(struct net_device *netdev) * @start: address of beginning of memory * @len: length of memory **/ -static bool -e1000_check_64k_bound(struct e1000_adapter *adapter, - void *start, unsigned long len) +static bool e1000_check_64k_bound(struct e1000_adapter *adapter, void *start, + unsigned long len) { - unsigned long begin = (unsigned long) start; + struct e1000_hw *hw = &adapter->hw; + unsigned long begin = (unsigned long)start; unsigned long end = begin + len; /* First rev 82545 and 82546 need to not allow any memory * write location to cross 64k boundary due to errata 23 */ - if (adapter->hw.mac_type == e1000_82545 || - adapter->hw.mac_type == e1000_82546) { + if (hw->mac_type == e1000_82545 || + hw->mac_type == e1000_82546) { return ((begin ^ (end - 1)) >> 16) != 0 ? false : true; } @@ -1565,9 +1585,8 @@ e1000_check_64k_bound(struct e1000_adapter *adapter, * Return 0 on success, negative on failure **/ -static int -e1000_setup_tx_resources(struct e1000_adapter *adapter, - struct e1000_tx_ring *txdr) +static int e1000_setup_tx_resources(struct e1000_adapter *adapter, + struct e1000_tx_ring *txdr) { struct pci_dev *pdev = adapter->pdev; int size; @@ -1641,8 +1660,7 @@ setup_tx_desc_die: * Return 0 on success, negative on failure **/ -int -e1000_setup_all_tx_resources(struct e1000_adapter *adapter) +int e1000_setup_all_tx_resources(struct e1000_adapter *adapter) { int i, err = 0; @@ -1668,8 +1686,7 @@ e1000_setup_all_tx_resources(struct e1000_adapter *adapter) * Configure the Tx unit of the MAC after a reset. **/ -static void -e1000_configure_tx(struct e1000_adapter *adapter) +static void e1000_configure_tx(struct e1000_adapter *adapter) { u64 tdba; struct e1000_hw *hw = &adapter->hw; @@ -1684,18 +1701,18 @@ e1000_configure_tx(struct e1000_adapter *adapter) tdba = adapter->tx_ring[0].dma; tdlen = adapter->tx_ring[0].count * sizeof(struct e1000_tx_desc); - E1000_WRITE_REG(hw, TDLEN, tdlen); - E1000_WRITE_REG(hw, TDBAH, (tdba >> 32)); - E1000_WRITE_REG(hw, TDBAL, (tdba & 0x00000000ffffffffULL)); - E1000_WRITE_REG(hw, TDT, 0); - E1000_WRITE_REG(hw, TDH, 0); + ew32(TDLEN, tdlen); + ew32(TDBAH, (tdba >> 32)); + ew32(TDBAL, (tdba & 0x00000000ffffffffULL)); + ew32(TDT, 0); + ew32(TDH, 0); adapter->tx_ring[0].tdh = ((hw->mac_type >= e1000_82543) ? E1000_TDH : E1000_82542_TDH); adapter->tx_ring[0].tdt = ((hw->mac_type >= e1000_82543) ? E1000_TDT : E1000_82542_TDT); break; } /* Set the default values for the Tx Inter Packet Gap timer */ - if (adapter->hw.mac_type <= e1000_82547_rev_2 && + if (hw->mac_type <= e1000_82547_rev_2 && (hw->media_type == e1000_media_type_fiber || hw->media_type == e1000_media_type_internal_serdes)) tipg = DEFAULT_82543_TIPG_IPGT_FIBER; @@ -1720,34 +1737,34 @@ e1000_configure_tx(struct e1000_adapter *adapter) } tipg |= ipgr1 << E1000_TIPG_IPGR1_SHIFT; tipg |= ipgr2 << E1000_TIPG_IPGR2_SHIFT; - E1000_WRITE_REG(hw, TIPG, tipg); + ew32(TIPG, tipg); /* Set the Tx Interrupt Delay register */ - E1000_WRITE_REG(hw, TIDV, adapter->tx_int_delay); + ew32(TIDV, adapter->tx_int_delay); if (hw->mac_type >= e1000_82540) - E1000_WRITE_REG(hw, TADV, adapter->tx_abs_int_delay); + ew32(TADV, adapter->tx_abs_int_delay); /* Program the Transmit Control Register */ - tctl = E1000_READ_REG(hw, TCTL); + tctl = er32(TCTL); tctl &= ~E1000_TCTL_CT; tctl |= E1000_TCTL_PSP | E1000_TCTL_RTLC | (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT); if (hw->mac_type == e1000_82571 || hw->mac_type == e1000_82572) { - tarc = E1000_READ_REG(hw, TARC0); + tarc = er32(TARC0); /* set the speed mode bit, we'll clear it if we're not at * gigabit link later */ tarc |= (1 << 21); - E1000_WRITE_REG(hw, TARC0, tarc); + ew32(TARC0, tarc); } else if (hw->mac_type == e1000_80003es2lan) { - tarc = E1000_READ_REG(hw, TARC0); + tarc = er32(TARC0); tarc |= 1; - E1000_WRITE_REG(hw, TARC0, tarc); - tarc = E1000_READ_REG(hw, TARC1); + ew32(TARC0, tarc); + tarc = er32(TARC1); tarc |= 1; - E1000_WRITE_REG(hw, TARC1, tarc); + ew32(TARC1, tarc); } e1000_config_collision_dist(hw); @@ -1770,7 +1787,7 @@ e1000_configure_tx(struct e1000_adapter *adapter) hw->bus_type == e1000_bus_type_pcix) adapter->pcix_82544 = 1; - E1000_WRITE_REG(hw, TCTL, tctl); + ew32(TCTL, tctl); } @@ -1782,10 +1799,10 @@ e1000_configure_tx(struct e1000_adapter *adapter) * Returns 0 on success, negative on failure **/ -static int -e1000_setup_rx_resources(struct e1000_adapter *adapter, - struct e1000_rx_ring *rxdr) +static int e1000_setup_rx_resources(struct e1000_adapter *adapter, + struct e1000_rx_ring *rxdr) { + struct e1000_hw *hw = &adapter->hw; struct pci_dev *pdev = adapter->pdev; int size, desc_len; @@ -1818,7 +1835,7 @@ e1000_setup_rx_resources(struct e1000_adapter *adapter, return -ENOMEM; } - if (adapter->hw.mac_type <= e1000_82547_rev_2) + if (hw->mac_type <= e1000_82547_rev_2) desc_len = sizeof(struct e1000_rx_desc); else desc_len = sizeof(union e1000_rx_desc_packet_split); @@ -1887,8 +1904,7 @@ setup_rx_desc_die: * Return 0 on success, negative on failure **/ -int -e1000_setup_all_rx_resources(struct e1000_adapter *adapter) +int e1000_setup_all_rx_resources(struct e1000_adapter *adapter) { int i, err = 0; @@ -1913,24 +1929,24 @@ e1000_setup_all_rx_resources(struct e1000_adapter *adapter) **/ #define PAGE_USE_COUNT(S) (((S) >> PAGE_SHIFT) + \ (((S) & (PAGE_SIZE - 1)) ? 1 : 0)) -static void -e1000_setup_rctl(struct e1000_adapter *adapter) +static void e1000_setup_rctl(struct e1000_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; u32 rctl, rfctl; u32 psrctl = 0; #ifndef CONFIG_E1000_DISABLE_PACKET_SPLIT u32 pages = 0; #endif - rctl = E1000_READ_REG(&adapter->hw, RCTL); + rctl = er32(RCTL); rctl &= ~(3 << E1000_RCTL_MO_SHIFT); rctl |= E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_LBM_NO | E1000_RCTL_RDMTS_HALF | - (adapter->hw.mc_filter_type << E1000_RCTL_MO_SHIFT); + (hw->mc_filter_type << E1000_RCTL_MO_SHIFT); - if (adapter->hw.tbi_compatibility_on == 1) + if (hw->tbi_compatibility_on == 1) rctl |= E1000_RCTL_SBP; else rctl &= ~E1000_RCTL_SBP; @@ -1983,7 +1999,7 @@ e1000_setup_rctl(struct e1000_adapter *adapter) /* allocations using alloc_page take too long for regular MTU * so only enable packet split for jumbo frames */ pages = PAGE_USE_COUNT(adapter->netdev->mtu); - if ((adapter->hw.mac_type >= e1000_82571) && (pages <= 3) && + if ((hw->mac_type >= e1000_82571) && (pages <= 3) && PAGE_SIZE <= 16384 && (rctl & E1000_RCTL_LPE)) adapter->rx_ps_pages = pages; else @@ -1991,14 +2007,14 @@ e1000_setup_rctl(struct e1000_adapter *adapter) #endif if (adapter->rx_ps_pages) { /* Configure extra packet-split registers */ - rfctl = E1000_READ_REG(&adapter->hw, RFCTL); + rfctl = er32(RFCTL); rfctl |= E1000_RFCTL_EXTEN; /* disable packet split support for IPv6 extension headers, * because some malformed IPv6 headers can hang the RX */ rfctl |= (E1000_RFCTL_IPV6_EX_DIS | E1000_RFCTL_NEW_IPV6_EXT_DIS); - E1000_WRITE_REG(&adapter->hw, RFCTL, rfctl); + ew32(RFCTL, rfctl); rctl |= E1000_RCTL_DTYP_PS; @@ -2018,10 +2034,10 @@ e1000_setup_rctl(struct e1000_adapter *adapter) break; } - E1000_WRITE_REG(&adapter->hw, PSRCTL, psrctl); + ew32(PSRCTL, psrctl); } - E1000_WRITE_REG(&adapter->hw, RCTL, rctl); + ew32(RCTL, rctl); } /** @@ -2031,8 +2047,7 @@ e1000_setup_rctl(struct e1000_adapter *adapter) * Configure the Rx unit of the MAC after a reset. **/ -static void -e1000_configure_rx(struct e1000_adapter *adapter) +static void e1000_configure_rx(struct e1000_adapter *adapter) { u64 rdba; struct e1000_hw *hw = &adapter->hw; @@ -2052,30 +2067,27 @@ e1000_configure_rx(struct e1000_adapter *adapter) } /* disable receives while setting up the descriptors */ - rctl = E1000_READ_REG(hw, RCTL); - E1000_WRITE_REG(hw, RCTL, rctl & ~E1000_RCTL_EN); + rctl = er32(RCTL); + ew32(RCTL, rctl & ~E1000_RCTL_EN); /* set the Receive Delay Timer Register */ - E1000_WRITE_REG(hw, RDTR, adapter->rx_int_delay); + ew32(RDTR, adapter->rx_int_delay); if (hw->mac_type >= e1000_82540) { - E1000_WRITE_REG(hw, RADV, adapter->rx_abs_int_delay); + ew32(RADV, adapter->rx_abs_int_delay); if (adapter->itr_setting != 0) - E1000_WRITE_REG(hw, ITR, - 1000000000 / (adapter->itr * 256)); + ew32(ITR, 1000000000 / (adapter->itr * 256)); } if (hw->mac_type >= e1000_82571) { - ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext = er32(CTRL_EXT); /* Reset delay timers after every interrupt */ ctrl_ext |= E1000_CTRL_EXT_INT_TIMER_CLR; -#ifdef CONFIG_E1000_NAPI /* Auto-Mask interrupts upon ICR access */ ctrl_ext |= E1000_CTRL_EXT_IAME; - E1000_WRITE_REG(hw, IAM, 0xffffffff); -#endif - E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); - E1000_WRITE_FLUSH(hw); + ew32(IAM, 0xffffffff); + ew32(CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(); } /* Setup the HW Rx Head and Tail Descriptor Pointers and @@ -2084,11 +2096,11 @@ e1000_configure_rx(struct e1000_adapter *adapter) case 1: default: rdba = adapter->rx_ring[0].dma; - E1000_WRITE_REG(hw, RDLEN, rdlen); - E1000_WRITE_REG(hw, RDBAH, (rdba >> 32)); - E1000_WRITE_REG(hw, RDBAL, (rdba & 0x00000000ffffffffULL)); - E1000_WRITE_REG(hw, RDT, 0); - E1000_WRITE_REG(hw, RDH, 0); + ew32(RDLEN, rdlen); + ew32(RDBAH, (rdba >> 32)); + ew32(RDBAL, (rdba & 0x00000000ffffffffULL)); + ew32(RDT, 0); + ew32(RDH, 0); adapter->rx_ring[0].rdh = ((hw->mac_type >= e1000_82543) ? E1000_RDH : E1000_82542_RDH); adapter->rx_ring[0].rdt = ((hw->mac_type >= e1000_82543) ? E1000_RDT : E1000_82542_RDT); break; @@ -2096,7 +2108,7 @@ e1000_configure_rx(struct e1000_adapter *adapter) /* Enable 82543 Receive Checksum Offload for TCP and UDP */ if (hw->mac_type >= e1000_82543) { - rxcsum = E1000_READ_REG(hw, RXCSUM); + rxcsum = er32(RXCSUM); if (adapter->rx_csum) { rxcsum |= E1000_RXCSUM_TUOFL; @@ -2110,17 +2122,17 @@ e1000_configure_rx(struct e1000_adapter *adapter) rxcsum &= ~E1000_RXCSUM_TUOFL; /* don't need to clear IPPCSE as it defaults to 0 */ } - E1000_WRITE_REG(hw, RXCSUM, rxcsum); + ew32(RXCSUM, rxcsum); } /* enable early receives on 82573, only takes effect if using > 2048 * byte total frame size. for example only for jumbo frames */ #define E1000_ERT_2048 0x100 if (hw->mac_type == e1000_82573) - E1000_WRITE_REG(hw, ERT, E1000_ERT_2048); + ew32(ERT, E1000_ERT_2048); /* Enable Receives */ - E1000_WRITE_REG(hw, RCTL, rctl); + ew32(RCTL, rctl); } /** @@ -2131,9 +2143,8 @@ e1000_configure_rx(struct e1000_adapter *adapter) * Free all transmit software resources **/ -static void -e1000_free_tx_resources(struct e1000_adapter *adapter, - struct e1000_tx_ring *tx_ring) +static void e1000_free_tx_resources(struct e1000_adapter *adapter, + struct e1000_tx_ring *tx_ring) { struct pci_dev *pdev = adapter->pdev; @@ -2154,8 +2165,7 @@ e1000_free_tx_resources(struct e1000_adapter *adapter, * Free all transmit software resources **/ -void -e1000_free_all_tx_resources(struct e1000_adapter *adapter) +void e1000_free_all_tx_resources(struct e1000_adapter *adapter) { int i; @@ -2163,9 +2173,8 @@ e1000_free_all_tx_resources(struct e1000_adapter *adapter) e1000_free_tx_resources(adapter, &adapter->tx_ring[i]); } -static void -e1000_unmap_and_free_tx_resource(struct e1000_adapter *adapter, - struct e1000_buffer *buffer_info) +static void e1000_unmap_and_free_tx_resource(struct e1000_adapter *adapter, + struct e1000_buffer *buffer_info) { if (buffer_info->dma) { pci_unmap_page(adapter->pdev, @@ -2187,10 +2196,10 @@ e1000_unmap_and_free_tx_resource(struct e1000_adapter *adapter, * @tx_ring: ring to be cleaned **/ -static void -e1000_clean_tx_ring(struct e1000_adapter *adapter, - struct e1000_tx_ring *tx_ring) +static void e1000_clean_tx_ring(struct e1000_adapter *adapter, + struct e1000_tx_ring *tx_ring) { + struct e1000_hw *hw = &adapter->hw; struct e1000_buffer *buffer_info; unsigned long size; unsigned int i; @@ -2213,8 +2222,8 @@ e1000_clean_tx_ring(struct e1000_adapter *adapter, tx_ring->next_to_clean = 0; tx_ring->last_tx_tso = 0; - writel(0, adapter->hw.hw_addr + tx_ring->tdh); - writel(0, adapter->hw.hw_addr + tx_ring->tdt); + writel(0, hw->hw_addr + tx_ring->tdh); + writel(0, hw->hw_addr + tx_ring->tdt); } /** @@ -2222,8 +2231,7 @@ e1000_clean_tx_ring(struct e1000_adapter *adapter, * @adapter: board private structure **/ -static void -e1000_clean_all_tx_rings(struct e1000_adapter *adapter) +static void e1000_clean_all_tx_rings(struct e1000_adapter *adapter) { int i; @@ -2239,9 +2247,8 @@ e1000_clean_all_tx_rings(struct e1000_adapter *adapter) * Free all receive software resources **/ -static void -e1000_free_rx_resources(struct e1000_adapter *adapter, - struct e1000_rx_ring *rx_ring) +static void e1000_free_rx_resources(struct e1000_adapter *adapter, + struct e1000_rx_ring *rx_ring) { struct pci_dev *pdev = adapter->pdev; @@ -2266,8 +2273,7 @@ e1000_free_rx_resources(struct e1000_adapter *adapter, * Free all receive software resources **/ -void -e1000_free_all_rx_resources(struct e1000_adapter *adapter) +void e1000_free_all_rx_resources(struct e1000_adapter *adapter) { int i; @@ -2281,10 +2287,10 @@ e1000_free_all_rx_resources(struct e1000_adapter *adapter) * @rx_ring: ring to free buffers from **/ -static void -e1000_clean_rx_ring(struct e1000_adapter *adapter, - struct e1000_rx_ring *rx_ring) +static void e1000_clean_rx_ring(struct e1000_adapter *adapter, + struct e1000_rx_ring *rx_ring) { + struct e1000_hw *hw = &adapter->hw; struct e1000_buffer *buffer_info; struct e1000_ps_page *ps_page; struct e1000_ps_page_dma *ps_page_dma; @@ -2331,8 +2337,8 @@ e1000_clean_rx_ring(struct e1000_adapter *adapter, rx_ring->next_to_clean = 0; rx_ring->next_to_use = 0; - writel(0, adapter->hw.hw_addr + rx_ring->rdh); - writel(0, adapter->hw.hw_addr + rx_ring->rdt); + writel(0, hw->hw_addr + rx_ring->rdh); + writel(0, hw->hw_addr + rx_ring->rdt); } /** @@ -2340,8 +2346,7 @@ e1000_clean_rx_ring(struct e1000_adapter *adapter, * @adapter: board private structure **/ -static void -e1000_clean_all_rx_rings(struct e1000_adapter *adapter) +static void e1000_clean_all_rx_rings(struct e1000_adapter *adapter) { int i; @@ -2352,38 +2357,38 @@ e1000_clean_all_rx_rings(struct e1000_adapter *adapter) /* The 82542 2.0 (revision 2) needs to have the receive unit in reset * and memory write and invalidate disabled for certain operations */ -static void -e1000_enter_82542_rst(struct e1000_adapter *adapter) +static void e1000_enter_82542_rst(struct e1000_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; u32 rctl; - e1000_pci_clear_mwi(&adapter->hw); + e1000_pci_clear_mwi(hw); - rctl = E1000_READ_REG(&adapter->hw, RCTL); + rctl = er32(RCTL); rctl |= E1000_RCTL_RST; - E1000_WRITE_REG(&adapter->hw, RCTL, rctl); - E1000_WRITE_FLUSH(&adapter->hw); + ew32(RCTL, rctl); + E1000_WRITE_FLUSH(); mdelay(5); if (netif_running(netdev)) e1000_clean_all_rx_rings(adapter); } -static void -e1000_leave_82542_rst(struct e1000_adapter *adapter) +static void e1000_leave_82542_rst(struct e1000_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; u32 rctl; - rctl = E1000_READ_REG(&adapter->hw, RCTL); + rctl = er32(RCTL); rctl &= ~E1000_RCTL_RST; - E1000_WRITE_REG(&adapter->hw, RCTL, rctl); - E1000_WRITE_FLUSH(&adapter->hw); + ew32(RCTL, rctl); + E1000_WRITE_FLUSH(); mdelay(5); - if (adapter->hw.pci_cmd_word & PCI_COMMAND_INVALIDATE) - e1000_pci_set_mwi(&adapter->hw); + if (hw->pci_cmd_word & PCI_COMMAND_INVALIDATE) + e1000_pci_set_mwi(hw); if (netif_running(netdev)) { /* No need to loop, because 82542 supports only 1 queue */ @@ -2401,10 +2406,10 @@ e1000_leave_82542_rst(struct e1000_adapter *adapter) * Returns 0 on success, negative on failure **/ -static int -e1000_set_mac(struct net_device *netdev, void *p) +static int e1000_set_mac(struct net_device *netdev, void *p) { struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; struct sockaddr *addr = p; if (!is_valid_ether_addr(addr->sa_data)) @@ -2412,19 +2417,19 @@ e1000_set_mac(struct net_device *netdev, void *p) /* 82542 2.0 needs to be in reset to write receive address registers */ - if (adapter->hw.mac_type == e1000_82542_rev2_0) + if (hw->mac_type == e1000_82542_rev2_0) e1000_enter_82542_rst(adapter); memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); - memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len); + memcpy(hw->mac_addr, addr->sa_data, netdev->addr_len); - e1000_rar_set(&adapter->hw, adapter->hw.mac_addr, 0); + e1000_rar_set(hw, hw->mac_addr, 0); /* With 82571 controllers, LAA may be overwritten (with the default) * due to controller reset from the other port. */ - if (adapter->hw.mac_type == e1000_82571) { + if (hw->mac_type == e1000_82571) { /* activate the work around */ - adapter->hw.laa_is_present = 1; + hw->laa_is_present = 1; /* Hold a copy of the LAA in RAR[14] This is done so that * between the time RAR[0] gets clobbered and the time it @@ -2432,11 +2437,11 @@ e1000_set_mac(struct net_device *netdev, void *p) * of the RARs and no incoming packets directed to this port * are dropped. Eventaully the LAA will be in RAR[0] and * RAR[14] */ - e1000_rar_set(&adapter->hw, adapter->hw.mac_addr, + e1000_rar_set(hw, hw->mac_addr, E1000_RAR_ENTRIES - 1); } - if (adapter->hw.mac_type == e1000_82542_rev2_0) + if (hw->mac_type == e1000_82542_rev2_0) e1000_leave_82542_rst(adapter); return 0; @@ -2452,8 +2457,7 @@ e1000_set_mac(struct net_device *netdev, void *p) * promiscuous mode, and all-multi behavior. **/ -static void -e1000_set_rx_mode(struct net_device *netdev) +static void e1000_set_rx_mode(struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; @@ -2466,16 +2470,16 @@ e1000_set_rx_mode(struct net_device *netdev) E1000_NUM_MTA_REGISTERS_ICH8LAN : E1000_NUM_MTA_REGISTERS; - if (adapter->hw.mac_type == e1000_ich8lan) + if (hw->mac_type == e1000_ich8lan) rar_entries = E1000_RAR_ENTRIES_ICH8LAN; /* reserve RAR[14] for LAA over-write work-around */ - if (adapter->hw.mac_type == e1000_82571) + if (hw->mac_type == e1000_82571) rar_entries--; /* Check for Promiscuous and All Multicast modes */ - rctl = E1000_READ_REG(hw, RCTL); + rctl = er32(RCTL); if (netdev->flags & IFF_PROMISC) { rctl |= (E1000_RCTL_UPE | E1000_RCTL_MPE); @@ -2498,7 +2502,7 @@ e1000_set_rx_mode(struct net_device *netdev) uc_ptr = netdev->uc_list; } - E1000_WRITE_REG(hw, RCTL, rctl); + ew32(RCTL, rctl); /* 82542 2.0 needs to be in reset to write receive address registers */ @@ -2524,9 +2528,9 @@ e1000_set_rx_mode(struct net_device *netdev) mc_ptr = mc_ptr->next; } else { E1000_WRITE_REG_ARRAY(hw, RA, i << 1, 0); - E1000_WRITE_FLUSH(hw); + E1000_WRITE_FLUSH(); E1000_WRITE_REG_ARRAY(hw, RA, (i << 1) + 1, 0); - E1000_WRITE_FLUSH(hw); + E1000_WRITE_FLUSH(); } } WARN_ON(uc_ptr != NULL); @@ -2535,7 +2539,7 @@ e1000_set_rx_mode(struct net_device *netdev) for (i = 0; i < mta_reg_count; i++) { E1000_WRITE_REG_ARRAY(hw, MTA, i, 0); - E1000_WRITE_FLUSH(hw); + E1000_WRITE_FLUSH(); } /* load any remaining addresses into the hash table */ @@ -2552,11 +2556,11 @@ e1000_set_rx_mode(struct net_device *netdev) /* Need to wait a few seconds after link up to get diagnostic information from * the phy */ -static void -e1000_update_phy_info(unsigned long data) +static void e1000_update_phy_info(unsigned long data) { - struct e1000_adapter *adapter = (struct e1000_adapter *) data; - e1000_phy_get_info(&adapter->hw, &adapter->phy_info); + struct e1000_adapter *adapter = (struct e1000_adapter *)data; + struct e1000_hw *hw = &adapter->hw; + e1000_phy_get_info(hw, &adapter->phy_info); } /** @@ -2564,33 +2568,25 @@ e1000_update_phy_info(unsigned long data) * @data: pointer to adapter cast into an unsigned long **/ -static void -e1000_82547_tx_fifo_stall(unsigned long data) +static void e1000_82547_tx_fifo_stall(unsigned long data) { - struct e1000_adapter *adapter = (struct e1000_adapter *) data; + struct e1000_adapter *adapter = (struct e1000_adapter *)data; + struct e1000_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; u32 tctl; if (atomic_read(&adapter->tx_fifo_stall)) { - if ((E1000_READ_REG(&adapter->hw, TDT) == - E1000_READ_REG(&adapter->hw, TDH)) && - (E1000_READ_REG(&adapter->hw, TDFT) == - E1000_READ_REG(&adapter->hw, TDFH)) && - (E1000_READ_REG(&adapter->hw, TDFTS) == - E1000_READ_REG(&adapter->hw, TDFHS))) { - tctl = E1000_READ_REG(&adapter->hw, TCTL); - E1000_WRITE_REG(&adapter->hw, TCTL, - tctl & ~E1000_TCTL_EN); - E1000_WRITE_REG(&adapter->hw, TDFT, - adapter->tx_head_addr); - E1000_WRITE_REG(&adapter->hw, TDFH, - adapter->tx_head_addr); - E1000_WRITE_REG(&adapter->hw, TDFTS, - adapter->tx_head_addr); - E1000_WRITE_REG(&adapter->hw, TDFHS, - adapter->tx_head_addr); - E1000_WRITE_REG(&adapter->hw, TCTL, tctl); - E1000_WRITE_FLUSH(&adapter->hw); + if ((er32(TDT) == er32(TDH)) && + (er32(TDFT) == er32(TDFH)) && + (er32(TDFTS) == er32(TDFHS))) { + tctl = er32(TCTL); + ew32(TCTL, tctl & ~E1000_TCTL_EN); + ew32(TDFT, adapter->tx_head_addr); + ew32(TDFH, adapter->tx_head_addr); + ew32(TDFTS, adapter->tx_head_addr); + ew32(TDFHS, adapter->tx_head_addr); + ew32(TCTL, tctl); + E1000_WRITE_FLUSH(); adapter->tx_fifo_head = 0; atomic_set(&adapter->tx_fifo_stall, 0); @@ -2605,45 +2601,45 @@ e1000_82547_tx_fifo_stall(unsigned long data) * e1000_watchdog - Timer Call-back * @data: pointer to adapter cast into an unsigned long **/ -static void -e1000_watchdog(unsigned long data) +static void e1000_watchdog(unsigned long data) { - struct e1000_adapter *adapter = (struct e1000_adapter *) data; + struct e1000_adapter *adapter = (struct e1000_adapter *)data; + struct e1000_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; struct e1000_tx_ring *txdr = adapter->tx_ring; u32 link, tctl; s32 ret_val; - ret_val = e1000_check_for_link(&adapter->hw); + ret_val = e1000_check_for_link(hw); if ((ret_val == E1000_ERR_PHY) && - (adapter->hw.phy_type == e1000_phy_igp_3) && - (E1000_READ_REG(&adapter->hw, CTRL) & E1000_PHY_CTRL_GBE_DISABLE)) { + (hw->phy_type == e1000_phy_igp_3) && + (er32(CTRL) & E1000_PHY_CTRL_GBE_DISABLE)) { /* See e1000_kumeran_lock_loss_workaround() */ DPRINTK(LINK, INFO, "Gigabit has been disabled, downgrading speed\n"); } - if (adapter->hw.mac_type == e1000_82573) { - e1000_enable_tx_pkt_filtering(&adapter->hw); - if (adapter->mng_vlan_id != adapter->hw.mng_cookie.vlan_id) + if (hw->mac_type == e1000_82573) { + e1000_enable_tx_pkt_filtering(hw); + if (adapter->mng_vlan_id != hw->mng_cookie.vlan_id) e1000_update_mng_vlan(adapter); } - if ((adapter->hw.media_type == e1000_media_type_internal_serdes) && - !(E1000_READ_REG(&adapter->hw, TXCW) & E1000_TXCW_ANE)) - link = !adapter->hw.serdes_link_down; + if ((hw->media_type == e1000_media_type_internal_serdes) && + !(er32(TXCW) & E1000_TXCW_ANE)) + link = !hw->serdes_link_down; else - link = E1000_READ_REG(&adapter->hw, STATUS) & E1000_STATUS_LU; + link = er32(STATUS) & E1000_STATUS_LU; if (link) { if (!netif_carrier_ok(netdev)) { u32 ctrl; bool txb2b = true; - e1000_get_speed_and_duplex(&adapter->hw, + e1000_get_speed_and_duplex(hw, &adapter->link_speed, &adapter->link_duplex); - ctrl = E1000_READ_REG(&adapter->hw, CTRL); + ctrl = er32(CTRL); DPRINTK(LINK, INFO, "NIC Link is Up %d Mbps %s, " "Flow Control: %s\n", adapter->link_speed, @@ -2671,19 +2667,19 @@ e1000_watchdog(unsigned long data) break; } - if ((adapter->hw.mac_type == e1000_82571 || - adapter->hw.mac_type == e1000_82572) && + if ((hw->mac_type == e1000_82571 || + hw->mac_type == e1000_82572) && !txb2b) { u32 tarc0; - tarc0 = E1000_READ_REG(&adapter->hw, TARC0); + tarc0 = er32(TARC0); tarc0 &= ~(1 << 21); - E1000_WRITE_REG(&adapter->hw, TARC0, tarc0); + ew32(TARC0, tarc0); } /* disable TSO for pcie and 10/100 speeds, to avoid * some hardware issues */ if (!adapter->tso_force && - adapter->hw.bus_type == e1000_bus_type_pci_express){ + hw->bus_type == e1000_bus_type_pci_express){ switch (adapter->link_speed) { case SPEED_10: case SPEED_100: @@ -2704,9 +2700,9 @@ e1000_watchdog(unsigned long data) /* enable transmits in the hardware, need to do this * after setting TARC0 */ - tctl = E1000_READ_REG(&adapter->hw, TCTL); + tctl = er32(TCTL); tctl |= E1000_TCTL_EN; - E1000_WRITE_REG(&adapter->hw, TCTL, tctl); + ew32(TCTL, tctl); netif_carrier_on(netdev); netif_wake_queue(netdev); @@ -2714,10 +2710,9 @@ e1000_watchdog(unsigned long data) adapter->smartspeed = 0; } else { /* make sure the receive unit is started */ - if (adapter->hw.rx_needs_kicking) { - struct e1000_hw *hw = &adapter->hw; - u32 rctl = E1000_READ_REG(hw, RCTL); - E1000_WRITE_REG(hw, RCTL, rctl | E1000_RCTL_EN); + if (hw->rx_needs_kicking) { + u32 rctl = er32(RCTL); + ew32(RCTL, rctl | E1000_RCTL_EN); } } } else { @@ -2734,7 +2729,7 @@ e1000_watchdog(unsigned long data) * disable receives in the ISR and * reset device here in the watchdog */ - if (adapter->hw.mac_type == e1000_80003es2lan) + if (hw->mac_type == e1000_80003es2lan) /* reset device */ schedule_work(&adapter->reset_task); } @@ -2744,9 +2739,9 @@ e1000_watchdog(unsigned long data) e1000_update_stats(adapter); - adapter->hw.tx_packet_delta = adapter->stats.tpt - adapter->tpt_old; + hw->tx_packet_delta = adapter->stats.tpt - adapter->tpt_old; adapter->tpt_old = adapter->stats.tpt; - adapter->hw.collision_delta = adapter->stats.colc - adapter->colc_old; + hw->collision_delta = adapter->stats.colc - adapter->colc_old; adapter->colc_old = adapter->stats.colc; adapter->gorcl = adapter->stats.gorcl - adapter->gorcl_old; @@ -2754,7 +2749,7 @@ e1000_watchdog(unsigned long data) adapter->gotcl = adapter->stats.gotcl - adapter->gotcl_old; adapter->gotcl_old = adapter->stats.gotcl; - e1000_update_adaptive(&adapter->hw); + e1000_update_adaptive(hw); if (!netif_carrier_ok(netdev)) { if (E1000_DESC_UNUSED(txdr) + 1 < txdr->count) { @@ -2768,15 +2763,15 @@ e1000_watchdog(unsigned long data) } /* Cause software interrupt to ensure rx ring is cleaned */ - E1000_WRITE_REG(&adapter->hw, ICS, E1000_ICS_RXDMT0); + ew32(ICS, E1000_ICS_RXDMT0); /* Force detection of hung controller every watchdog period */ adapter->detect_tx_hung = true; /* With 82571 controllers, LAA may be overwritten due to controller * reset from the other port. Set the appropriate LAA in RAR[0] */ - if (adapter->hw.mac_type == e1000_82571 && adapter->hw.laa_is_present) - e1000_rar_set(&adapter->hw, adapter->hw.mac_addr, 0); + if (hw->mac_type == e1000_82571 && hw->laa_is_present) + e1000_rar_set(hw, hw->mac_addr, 0); /* Reset the timer */ mod_timer(&adapter->watchdog_timer, round_jiffies(jiffies + 2 * HZ)); @@ -2806,9 +2801,7 @@ enum latency_range { * @bytes: the number of bytes during this measurement interval **/ static unsigned int e1000_update_itr(struct e1000_adapter *adapter, - u16 itr_setting, - int packets, - int bytes) + u16 itr_setting, int packets, int bytes) { unsigned int retval = itr_setting; struct e1000_hw *hw = &adapter->hw; @@ -2913,7 +2906,7 @@ set_itr_now: min(adapter->itr + (new_itr >> 2), new_itr) : new_itr; adapter->itr = new_itr; - E1000_WRITE_REG(hw, ITR, 1000000000 / (new_itr * 256)); + ew32(ITR, 1000000000 / (new_itr * 256)); } return; @@ -2926,9 +2919,8 @@ set_itr_now: #define E1000_TX_FLAGS_VLAN_MASK 0xffff0000 #define E1000_TX_FLAGS_VLAN_SHIFT 16 -static int -e1000_tso(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring, - struct sk_buff *skb) +static int e1000_tso(struct e1000_adapter *adapter, + struct e1000_tx_ring *tx_ring, struct sk_buff *skb) { struct e1000_context_desc *context_desc; struct e1000_buffer *buffer_info; @@ -2999,9 +2991,8 @@ e1000_tso(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring, return false; } -static bool -e1000_tx_csum(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring, - struct sk_buff *skb) +static bool e1000_tx_csum(struct e1000_adapter *adapter, + struct e1000_tx_ring *tx_ring, struct sk_buff *skb) { struct e1000_context_desc *context_desc; struct e1000_buffer *buffer_info; @@ -3038,11 +3029,13 @@ e1000_tx_csum(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring, #define E1000_MAX_TXD_PWR 12 #define E1000_MAX_DATA_PER_TXD (1<<E1000_MAX_TXD_PWR) -static int -e1000_tx_map(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring, - struct sk_buff *skb, unsigned int first, unsigned int max_per_txd, - unsigned int nr_frags, unsigned int mss) +static int e1000_tx_map(struct e1000_adapter *adapter, + struct e1000_tx_ring *tx_ring, + struct sk_buff *skb, unsigned int first, + unsigned int max_per_txd, unsigned int nr_frags, + unsigned int mss) { + struct e1000_hw *hw = &adapter->hw; struct e1000_buffer *buffer_info; unsigned int len = skb->len; unsigned int offset = 0, size, count = 0, i; @@ -3073,7 +3066,7 @@ e1000_tx_map(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring, * The fix is to make sure that the first descriptor of a * packet is smaller than 2048 - 16 - 16 (or 2016) bytes */ - if (unlikely((adapter->hw.bus_type == e1000_bus_type_pcix) && + if (unlikely((hw->bus_type == e1000_bus_type_pcix) && (size > 2015) && count == 0)) size = 2015; @@ -3145,10 +3138,11 @@ e1000_tx_map(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring, return count; } -static void -e1000_tx_queue(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring, - int tx_flags, int count) +static void e1000_tx_queue(struct e1000_adapter *adapter, + struct e1000_tx_ring *tx_ring, int tx_flags, + int count) { + struct e1000_hw *hw = &adapter->hw; struct e1000_tx_desc *tx_desc = NULL; struct e1000_buffer *buffer_info; u32 txd_upper = 0, txd_lower = E1000_TXD_CMD_IFCS; @@ -3194,7 +3188,7 @@ e1000_tx_queue(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring, wmb(); tx_ring->next_to_use = i; - writel(i, adapter->hw.hw_addr + tx_ring->tdt); + writel(i, hw->hw_addr + tx_ring->tdt); /* we need this if more than one processor can write to our tail * at a time, it syncronizes IO on IA64/Altix systems */ mmiowb(); @@ -3212,8 +3206,8 @@ e1000_tx_queue(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring, #define E1000_FIFO_HDR 0x10 #define E1000_82547_PAD_LEN 0x3E0 -static int -e1000_82547_fifo_workaround(struct e1000_adapter *adapter, struct sk_buff *skb) +static int e1000_82547_fifo_workaround(struct e1000_adapter *adapter, + struct sk_buff *skb) { u32 fifo_space = adapter->tx_fifo_size - adapter->tx_fifo_head; u32 skb_fifo_len = skb->len + E1000_FIFO_HDR; @@ -3239,19 +3233,19 @@ no_fifo_stall_required: } #define MINIMUM_DHCP_PACKET_SIZE 282 -static int -e1000_transfer_dhcp_info(struct e1000_adapter *adapter, struct sk_buff *skb) +static int e1000_transfer_dhcp_info(struct e1000_adapter *adapter, + struct sk_buff *skb) { struct e1000_hw *hw = &adapter->hw; u16 length, offset; if (vlan_tx_tag_present(skb)) { - if (!((vlan_tx_tag_get(skb) == adapter->hw.mng_cookie.vlan_id) && - ( adapter->hw.mng_cookie.status & + if (!((vlan_tx_tag_get(skb) == hw->mng_cookie.vlan_id) && + ( hw->mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT)) ) return 0; } if (skb->len > MINIMUM_DHCP_PACKET_SIZE) { - struct ethhdr *eth = (struct ethhdr *) skb->data; + struct ethhdr *eth = (struct ethhdr *)skb->data; if ((htons(ETH_P_IP) == eth->h_proto)) { const struct iphdr *ip = (struct iphdr *)((u8 *)skb->data+14); @@ -3304,10 +3298,10 @@ static int e1000_maybe_stop_tx(struct net_device *netdev, } #define TXD_USE_COUNT(S, X) (((S) >> (X)) + 1 ) -static int -e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) +static int e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; struct e1000_tx_ring *tx_ring; unsigned int first, max_per_txd = E1000_MAX_DATA_PER_TXD; unsigned int max_txd_pwr = E1000_MAX_TXD_PWR; @@ -3333,7 +3327,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) /* 82571 and newer doesn't need the workaround that limited descriptor * length to 4kB */ - if (adapter->hw.mac_type >= e1000_82571) + if (hw->mac_type >= e1000_82571) max_per_txd = 8192; mss = skb_shinfo(skb)->gso_size; @@ -3353,7 +3347,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) * frags into skb->data */ hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); if (skb->data_len && hdr_len == len) { - switch (adapter->hw.mac_type) { + switch (hw->mac_type) { unsigned int pull_size; case e1000_82544: /* Make sure we have room to chop off 4 bytes, @@ -3402,7 +3396,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) /* work-around for errata 10 and it applies to all controllers * in PCI-X mode, so add one more descriptor to the count */ - if (unlikely((adapter->hw.bus_type == e1000_bus_type_pcix) && + if (unlikely((hw->bus_type == e1000_bus_type_pcix) && (len > 2015))) count++; @@ -3414,8 +3408,8 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) count += nr_frags; - if (adapter->hw.tx_pkt_filtering && - (adapter->hw.mac_type == e1000_82573)) + if (hw->tx_pkt_filtering && + (hw->mac_type == e1000_82573)) e1000_transfer_dhcp_info(adapter, skb); if (!spin_trylock_irqsave(&tx_ring->tx_lock, flags)) @@ -3429,7 +3423,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) return NETDEV_TX_BUSY; } - if (unlikely(adapter->hw.mac_type == e1000_82547)) { + if (unlikely(hw->mac_type == e1000_82547)) { if (unlikely(e1000_82547_fifo_workaround(adapter, skb))) { netif_stop_queue(netdev); mod_timer(&adapter->tx_fifo_stall_timer, jiffies + 1); @@ -3482,8 +3476,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) * @netdev: network interface device structure **/ -static void -e1000_tx_timeout(struct net_device *netdev) +static void e1000_tx_timeout(struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); @@ -3492,8 +3485,7 @@ e1000_tx_timeout(struct net_device *netdev) schedule_work(&adapter->reset_task); } -static void -e1000_reset_task(struct work_struct *work) +static void e1000_reset_task(struct work_struct *work) { struct e1000_adapter *adapter = container_of(work, struct e1000_adapter, reset_task); @@ -3509,8 +3501,7 @@ e1000_reset_task(struct work_struct *work) * The statistics are actually updated from the timer callback. **/ -static struct net_device_stats * -e1000_get_stats(struct net_device *netdev) +static struct net_device_stats *e1000_get_stats(struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); @@ -3526,10 +3517,10 @@ e1000_get_stats(struct net_device *netdev) * Returns 0 on success, negative on failure **/ -static int -e1000_change_mtu(struct net_device *netdev, int new_mtu) +static int e1000_change_mtu(struct net_device *netdev, int new_mtu) { struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; int max_frame = new_mtu + ENET_HEADER_SIZE + ETHERNET_FCS_SIZE; u16 eeprom_data = 0; @@ -3540,7 +3531,7 @@ e1000_change_mtu(struct net_device *netdev, int new_mtu) } /* Adapter-specific max frame size limits. */ - switch (adapter->hw.mac_type) { + switch (hw->mac_type) { case e1000_undefined ... e1000_82542_rev2_1: case e1000_ich8lan: if (max_frame > MAXIMUM_ETHERNET_FRAME_SIZE) { @@ -3552,9 +3543,9 @@ e1000_change_mtu(struct net_device *netdev, int new_mtu) /* Jumbo Frames not supported if: * - this is not an 82573L device * - ASPM is enabled in any way (0x1A bits 3:2) */ - e1000_read_eeprom(&adapter->hw, EEPROM_INIT_3GIO_3, 1, + e1000_read_eeprom(hw, EEPROM_INIT_3GIO_3, 1, &eeprom_data); - if ((adapter->hw.device_id != E1000_DEV_ID_82573L) || + if ((hw->device_id != E1000_DEV_ID_82573L) || (eeprom_data & EEPROM_WORD1A_ASPM_MASK)) { if (max_frame > MAXIMUM_ETHERNET_FRAME_SIZE) { DPRINTK(PROBE, ERR, @@ -3601,13 +3592,13 @@ e1000_change_mtu(struct net_device *netdev, int new_mtu) adapter->rx_buffer_len = E1000_RXBUFFER_16384; /* adjust allocation if LPE protects us, and we aren't using SBP */ - if (!adapter->hw.tbi_compatibility_on && + if (!hw->tbi_compatibility_on && ((max_frame == MAXIMUM_ETHERNET_FRAME_SIZE) || (max_frame == MAXIMUM_ETHERNET_VLAN_SIZE))) adapter->rx_buffer_len = MAXIMUM_ETHERNET_VLAN_SIZE; netdev->mtu = new_mtu; - adapter->hw.max_frame_size = max_frame; + hw->max_frame_size = max_frame; if (netif_running(netdev)) e1000_reinit_locked(adapter); @@ -3620,8 +3611,7 @@ e1000_change_mtu(struct net_device *netdev, int new_mtu) * @adapter: board private structure **/ -void -e1000_update_stats(struct e1000_adapter *adapter) +void e1000_update_stats(struct e1000_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; struct pci_dev *pdev = adapter->pdev; @@ -3646,89 +3636,89 @@ e1000_update_stats(struct e1000_adapter *adapter) * be written while holding adapter->stats_lock */ - adapter->stats.crcerrs += E1000_READ_REG(hw, CRCERRS); - adapter->stats.gprc += E1000_READ_REG(hw, GPRC); - adapter->stats.gorcl += E1000_READ_REG(hw, GORCL); - adapter->stats.gorch += E1000_READ_REG(hw, GORCH); - adapter->stats.bprc += E1000_READ_REG(hw, BPRC); - adapter->stats.mprc += E1000_READ_REG(hw, MPRC); - adapter->stats.roc += E1000_READ_REG(hw, ROC); - - if (adapter->hw.mac_type != e1000_ich8lan) { - adapter->stats.prc64 += E1000_READ_REG(hw, PRC64); - adapter->stats.prc127 += E1000_READ_REG(hw, PRC127); - adapter->stats.prc255 += E1000_READ_REG(hw, PRC255); - adapter->stats.prc511 += E1000_READ_REG(hw, PRC511); - adapter->stats.prc1023 += E1000_READ_REG(hw, PRC1023); - adapter->stats.prc1522 += E1000_READ_REG(hw, PRC1522); - } - - adapter->stats.symerrs += E1000_READ_REG(hw, SYMERRS); - adapter->stats.mpc += E1000_READ_REG(hw, MPC); - adapter->stats.scc += E1000_READ_REG(hw, SCC); - adapter->stats.ecol += E1000_READ_REG(hw, ECOL); - adapter->stats.mcc += E1000_READ_REG(hw, MCC); - adapter->stats.latecol += E1000_READ_REG(hw, LATECOL); - adapter->stats.dc += E1000_READ_REG(hw, DC); - adapter->stats.sec += E1000_READ_REG(hw, SEC); - adapter->stats.rlec += E1000_READ_REG(hw, RLEC); - adapter->stats.xonrxc += E1000_READ_REG(hw, XONRXC); - adapter->stats.xontxc += E1000_READ_REG(hw, XONTXC); - adapter->stats.xoffrxc += E1000_READ_REG(hw, XOFFRXC); - adapter->stats.xofftxc += E1000_READ_REG(hw, XOFFTXC); - adapter->stats.fcruc += E1000_READ_REG(hw, FCRUC); - adapter->stats.gptc += E1000_READ_REG(hw, GPTC); - adapter->stats.gotcl += E1000_READ_REG(hw, GOTCL); - adapter->stats.gotch += E1000_READ_REG(hw, GOTCH); - adapter->stats.rnbc += E1000_READ_REG(hw, RNBC); - adapter->stats.ruc += E1000_READ_REG(hw, RUC); - adapter->stats.rfc += E1000_READ_REG(hw, RFC); - adapter->stats.rjc += E1000_READ_REG(hw, RJC); - adapter->stats.torl += E1000_READ_REG(hw, TORL); - adapter->stats.torh += E1000_READ_REG(hw, TORH); - adapter->stats.totl += E1000_READ_REG(hw, TOTL); - adapter->stats.toth += E1000_READ_REG(hw, TOTH); - adapter->stats.tpr += E1000_READ_REG(hw, TPR); - - if (adapter->hw.mac_type != e1000_ich8lan) { - adapter->stats.ptc64 += E1000_READ_REG(hw, PTC64); - adapter->stats.ptc127 += E1000_READ_REG(hw, PTC127); - adapter->stats.ptc255 += E1000_READ_REG(hw, PTC255); - adapter->stats.ptc511 += E1000_READ_REG(hw, PTC511); - adapter->stats.ptc1023 += E1000_READ_REG(hw, PTC1023); - adapter->stats.ptc1522 += E1000_READ_REG(hw, PTC1522); - } - - adapter->stats.mptc += E1000_READ_REG(hw, MPTC); - adapter->stats.bptc += E1000_READ_REG(hw, BPTC); + adapter->stats.crcerrs += er32(CRCERRS); + adapter->stats.gprc += er32(GPRC); + adapter->stats.gorcl += er32(GORCL); + adapter->stats.gorch += er32(GORCH); + adapter->stats.bprc += er32(BPRC); + adapter->stats.mprc += er32(MPRC); + adapter->stats.roc += er32(ROC); + + if (hw->mac_type != e1000_ich8lan) { + adapter->stats.prc64 += er32(PRC64); + adapter->stats.prc127 += er32(PRC127); + adapter->stats.prc255 += er32(PRC255); + adapter->stats.prc511 += er32(PRC511); + adapter->stats.prc1023 += er32(PRC1023); + adapter->stats.prc1522 += er32(PRC1522); + } + + adapter->stats.symerrs += er32(SYMERRS); + adapter->stats.mpc += er32(MPC); + adapter->stats.scc += er32(SCC); + adapter->stats.ecol += er32(ECOL); + adapter->stats.mcc += er32(MCC); + adapter->stats.latecol += er32(LATECOL); + adapter->stats.dc += er32(DC); + adapter->stats.sec += er32(SEC); + adapter->stats.rlec += er32(RLEC); + adapter->stats.xonrxc += er32(XONRXC); + adapter->stats.xontxc += er32(XONTXC); + adapter->stats.xoffrxc += er32(XOFFRXC); + adapter->stats.xofftxc += er32(XOFFTXC); + adapter->stats.fcruc += er32(FCRUC); + adapter->stats.gptc += er32(GPTC); + adapter->stats.gotcl += er32(GOTCL); + adapter->stats.gotch += er32(GOTCH); + adapter->stats.rnbc += er32(RNBC); + adapter->stats.ruc += er32(RUC); + adapter->stats.rfc += er32(RFC); + adapter->stats.rjc += er32(RJC); + adapter->stats.torl += er32(TORL); + adapter->stats.torh += er32(TORH); + adapter->stats.totl += er32(TOTL); + adapter->stats.toth += er32(TOTH); + adapter->stats.tpr += er32(TPR); + + if (hw->mac_type != e1000_ich8lan) { + adapter->stats.ptc64 += er32(PTC64); + adapter->stats.ptc127 += er32(PTC127); + adapter->stats.ptc255 += er32(PTC255); + adapter->stats.ptc511 += er32(PTC511); + adapter->stats.ptc1023 += er32(PTC1023); + adapter->stats.ptc1522 += er32(PTC1522); + } + + adapter->stats.mptc += er32(MPTC); + adapter->stats.bptc += er32(BPTC); /* used for adaptive IFS */ - hw->tx_packet_delta = E1000_READ_REG(hw, TPT); + hw->tx_packet_delta = er32(TPT); adapter->stats.tpt += hw->tx_packet_delta; - hw->collision_delta = E1000_READ_REG(hw, COLC); + hw->collision_delta = er32(COLC); adapter->stats.colc += hw->collision_delta; if (hw->mac_type >= e1000_82543) { - adapter->stats.algnerrc += E1000_READ_REG(hw, ALGNERRC); - adapter->stats.rxerrc += E1000_READ_REG(hw, RXERRC); - adapter->stats.tncrs += E1000_READ_REG(hw, TNCRS); - adapter->stats.cexterr += E1000_READ_REG(hw, CEXTERR); - adapter->stats.tsctc += E1000_READ_REG(hw, TSCTC); - adapter->stats.tsctfc += E1000_READ_REG(hw, TSCTFC); + adapter->stats.algnerrc += er32(ALGNERRC); + adapter->stats.rxerrc += er32(RXERRC); + adapter->stats.tncrs += er32(TNCRS); + adapter->stats.cexterr += er32(CEXTERR); + adapter->stats.tsctc += er32(TSCTC); + adapter->stats.tsctfc += er32(TSCTFC); } if (hw->mac_type > e1000_82547_rev_2) { - adapter->stats.iac += E1000_READ_REG(hw, IAC); - adapter->stats.icrxoc += E1000_READ_REG(hw, ICRXOC); - - if (adapter->hw.mac_type != e1000_ich8lan) { - adapter->stats.icrxptc += E1000_READ_REG(hw, ICRXPTC); - adapter->stats.icrxatc += E1000_READ_REG(hw, ICRXATC); - adapter->stats.ictxptc += E1000_READ_REG(hw, ICTXPTC); - adapter->stats.ictxatc += E1000_READ_REG(hw, ICTXATC); - adapter->stats.ictxqec += E1000_READ_REG(hw, ICTXQEC); - adapter->stats.ictxqmtc += E1000_READ_REG(hw, ICTXQMTC); - adapter->stats.icrxdmtc += E1000_READ_REG(hw, ICRXDMTC); + adapter->stats.iac += er32(IAC); + adapter->stats.icrxoc += er32(ICRXOC); + + if (hw->mac_type != e1000_ich8lan) { + adapter->stats.icrxptc += er32(ICRXPTC); + adapter->stats.icrxatc += er32(ICRXATC); + adapter->stats.ictxptc += er32(ICTXPTC); + adapter->stats.ictxatc += er32(ICTXATC); + adapter->stats.ictxqec += er32(ICTXQEC); + adapter->stats.ictxqmtc += er32(ICTXQMTC); + adapter->stats.icrxdmtc += er32(ICRXDMTC); } } @@ -3756,7 +3746,7 @@ e1000_update_stats(struct e1000_adapter *adapter) adapter->net_stats.tx_aborted_errors = adapter->stats.ecol; adapter->net_stats.tx_window_errors = adapter->stats.latecol; adapter->net_stats.tx_carrier_errors = adapter->stats.tncrs; - if (adapter->hw.bad_tx_carr_stats_fd && + if (hw->bad_tx_carr_stats_fd && adapter->link_duplex == FULL_DUPLEX) { adapter->net_stats.tx_carrier_errors = 0; adapter->stats.tncrs = 0; @@ -3779,10 +3769,10 @@ e1000_update_stats(struct e1000_adapter *adapter) } /* Management Stats */ - if (adapter->hw.has_smbus) { - adapter->stats.mgptc += E1000_READ_REG(hw, MGTPTC); - adapter->stats.mgprc += E1000_READ_REG(hw, MGTPRC); - adapter->stats.mgpdc += E1000_READ_REG(hw, MGTPDC); + if (hw->has_smbus) { + adapter->stats.mgptc += er32(MGTPTC); + adapter->stats.mgprc += er32(MGTPRC); + adapter->stats.mgpdc += er32(MGTPDC); } spin_unlock_irqrestore(&adapter->stats_lock, flags); @@ -3794,16 +3784,12 @@ e1000_update_stats(struct e1000_adapter *adapter) * @data: pointer to a network interface device structure **/ -static irqreturn_t -e1000_intr_msi(int irq, void *data) +static irqreturn_t e1000_intr_msi(int irq, void *data) { struct net_device *netdev = data; struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; -#ifndef CONFIG_E1000_NAPI - int i; -#endif - u32 icr = E1000_READ_REG(hw, ICR); + u32 icr = er32(ICR); /* in NAPI mode read ICR disables interrupts using IAM */ @@ -3813,17 +3799,16 @@ e1000_intr_msi(int irq, void *data) * link down event; disable receives here in the ISR and reset * adapter in watchdog */ if (netif_carrier_ok(netdev) && - (adapter->hw.mac_type == e1000_80003es2lan)) { + (hw->mac_type == e1000_80003es2lan)) { /* disable receives */ - u32 rctl = E1000_READ_REG(hw, RCTL); - E1000_WRITE_REG(hw, RCTL, rctl & ~E1000_RCTL_EN); + u32 rctl = er32(RCTL); + ew32(RCTL, rctl & ~E1000_RCTL_EN); } /* guard against interrupt when we're going down */ if (!test_bit(__E1000_DOWN, &adapter->flags)) mod_timer(&adapter->watchdog_timer, jiffies + 1); } -#ifdef CONFIG_E1000_NAPI if (likely(netif_rx_schedule_prep(netdev, &adapter->napi))) { adapter->total_tx_bytes = 0; adapter->total_tx_packets = 0; @@ -3832,20 +3817,6 @@ e1000_intr_msi(int irq, void *data) __netif_rx_schedule(netdev, &adapter->napi); } else e1000_irq_enable(adapter); -#else - adapter->total_tx_bytes = 0; - adapter->total_rx_bytes = 0; - adapter->total_tx_packets = 0; - adapter->total_rx_packets = 0; - - 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))) - break; - - if (likely(adapter->itr_setting & 3)) - e1000_set_itr(adapter); -#endif return IRQ_HANDLED; } @@ -3856,20 +3827,16 @@ e1000_intr_msi(int irq, void *data) * @data: pointer to a network interface device structure **/ -static irqreturn_t -e1000_intr(int irq, void *data) +static irqreturn_t e1000_intr(int irq, void *data) { struct net_device *netdev = data; struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - u32 rctl, icr = E1000_READ_REG(hw, ICR); -#ifndef CONFIG_E1000_NAPI - int i; -#endif + u32 rctl, icr = er32(ICR); + if (unlikely(!icr)) return IRQ_NONE; /* Not our interrupt */ -#ifdef CONFIG_E1000_NAPI /* IMS will not auto-mask if INT_ASSERTED is not set, and if it is * not set, then the adapter didn't send an interrupt */ if (unlikely(hw->mac_type >= e1000_82571 && @@ -3878,7 +3845,6 @@ e1000_intr(int irq, void *data) /* Interrupt Auto-Mask...upon reading ICR, interrupts are masked. No * need for the IMC write */ -#endif if (unlikely(icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC))) { hw->get_link_status = 1; @@ -3888,21 +3854,20 @@ e1000_intr(int irq, void *data) * reset adapter in watchdog */ if (netif_carrier_ok(netdev) && - (adapter->hw.mac_type == e1000_80003es2lan)) { + (hw->mac_type == e1000_80003es2lan)) { /* disable receives */ - rctl = E1000_READ_REG(hw, RCTL); - E1000_WRITE_REG(hw, RCTL, rctl & ~E1000_RCTL_EN); + rctl = er32(RCTL); + ew32(RCTL, rctl & ~E1000_RCTL_EN); } /* guard against interrupt when we're going down */ if (!test_bit(__E1000_DOWN, &adapter->flags)) mod_timer(&adapter->watchdog_timer, jiffies + 1); } -#ifdef CONFIG_E1000_NAPI if (unlikely(hw->mac_type < e1000_82571)) { /* disable interrupts, without the synchronize_irq bit */ - E1000_WRITE_REG(hw, IMC, ~0); - E1000_WRITE_FLUSH(hw); + ew32(IMC, ~0); + E1000_WRITE_FLUSH(); } if (likely(netif_rx_schedule_prep(netdev, &adapter->napi))) { adapter->total_tx_bytes = 0; @@ -3914,48 +3879,15 @@ e1000_intr(int irq, void *data) /* this really should not happen! if it does it is basically a * bug, but not a hard error, so enable ints and continue */ e1000_irq_enable(adapter); -#else - /* Writing IMC and IMS is needed for 82547. - * Due to Hub Link bus being occupied, an interrupt - * de-assertion message is not able to be sent. - * When an interrupt assertion message is generated later, - * two messages are re-ordered and sent out. - * That causes APIC to think 82547 is in de-assertion - * state, while 82547 is in assertion state, resulting - * in dead lock. Writing IMC forces 82547 into - * de-assertion state. - */ - if (hw->mac_type == e1000_82547 || hw->mac_type == e1000_82547_rev_2) - E1000_WRITE_REG(hw, IMC, ~0); - - adapter->total_tx_bytes = 0; - adapter->total_rx_bytes = 0; - adapter->total_tx_packets = 0; - adapter->total_rx_packets = 0; - 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))) - break; - - if (likely(adapter->itr_setting & 3)) - e1000_set_itr(adapter); - - if (hw->mac_type == e1000_82547 || hw->mac_type == e1000_82547_rev_2) - e1000_irq_enable(adapter); - -#endif return IRQ_HANDLED; } -#ifdef CONFIG_E1000_NAPI /** * e1000_clean - NAPI Rx polling callback * @adapter: board private structure **/ - -static int -e1000_clean(struct napi_struct *napi, int budget) +static int e1000_clean(struct napi_struct *napi, int budget) { struct e1000_adapter *adapter = container_of(napi, struct e1000_adapter, napi); struct net_device *poll_dev = adapter->netdev; @@ -3991,23 +3923,19 @@ e1000_clean(struct napi_struct *napi, int budget) return work_done; } -#endif /** * e1000_clean_tx_irq - Reclaim resources after transmit completes * @adapter: board private structure **/ - -static bool -e1000_clean_tx_irq(struct e1000_adapter *adapter, - struct e1000_tx_ring *tx_ring) +static bool e1000_clean_tx_irq(struct e1000_adapter *adapter, + struct e1000_tx_ring *tx_ring) { + struct e1000_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; struct e1000_tx_desc *tx_desc, *eop_desc; struct e1000_buffer *buffer_info; unsigned int i, eop; -#ifdef CONFIG_E1000_NAPI unsigned int count = 0; -#endif bool cleaned = false; unsigned int total_tx_bytes=0, total_tx_packets=0; @@ -4039,11 +3967,10 @@ e1000_clean_tx_irq(struct e1000_adapter *adapter, eop = tx_ring->buffer_info[i].next_to_watch; eop_desc = E1000_TX_DESC(*tx_ring, eop); -#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) break; -#endif + if (count++ == E1000_TX_WEIGHT) + break; } tx_ring->next_to_clean = i; @@ -4068,8 +3995,7 @@ e1000_clean_tx_irq(struct e1000_adapter *adapter, if (tx_ring->buffer_info[eop].dma && time_after(jiffies, tx_ring->buffer_info[eop].time_stamp + (adapter->tx_timeout_factor * HZ)) - && !(E1000_READ_REG(&adapter->hw, STATUS) & - E1000_STATUS_TXOFF)) { + && !(er32(STATUS) & E1000_STATUS_TXOFF)) { /* detected Tx unit hang */ DPRINTK(DRV, ERR, "Detected Tx Unit Hang\n" @@ -4085,8 +4011,8 @@ e1000_clean_tx_irq(struct e1000_adapter *adapter, " next_to_watch.status <%x>\n", (unsigned long)((tx_ring - adapter->tx_ring) / sizeof(struct e1000_tx_ring)), - readl(adapter->hw.hw_addr + tx_ring->tdh), - readl(adapter->hw.hw_addr + tx_ring->tdt), + readl(hw->hw_addr + tx_ring->tdh), + readl(hw->hw_addr + tx_ring->tdt), tx_ring->next_to_use, tx_ring->next_to_clean, tx_ring->buffer_info[eop].time_stamp, @@ -4111,17 +4037,16 @@ e1000_clean_tx_irq(struct e1000_adapter *adapter, * @sk_buff: socket buffer with received data **/ -static void -e1000_rx_checksum(struct e1000_adapter *adapter, - u32 status_err, u32 csum, - struct sk_buff *skb) +static void e1000_rx_checksum(struct e1000_adapter *adapter, u32 status_err, + u32 csum, struct sk_buff *skb) { + struct e1000_hw *hw = &adapter->hw; u16 status = (u16)status_err; u8 errors = (u8)(status_err >> 24); skb->ip_summed = CHECKSUM_NONE; /* 82543 or newer only */ - if (unlikely(adapter->hw.mac_type < e1000_82543)) return; + if (unlikely(hw->mac_type < e1000_82543)) return; /* Ignore Checksum bit is set */ if (unlikely(status & E1000_RXD_STAT_IXSM)) return; /* TCP/UDP checksum error bit is set */ @@ -4131,7 +4056,7 @@ e1000_rx_checksum(struct e1000_adapter *adapter, return; } /* TCP/UDP Checksum has not been calculated */ - if (adapter->hw.mac_type <= e1000_82547_rev_2) { + if (hw->mac_type <= e1000_82547_rev_2) { if (!(status & E1000_RXD_STAT_TCPCS)) return; } else { @@ -4142,7 +4067,7 @@ e1000_rx_checksum(struct e1000_adapter *adapter, if (likely(status & E1000_RXD_STAT_TCPCS)) { /* TCP checksum is good */ skb->ip_summed = CHECKSUM_UNNECESSARY; - } else if (adapter->hw.mac_type > e1000_82547_rev_2) { + } else if (hw->mac_type > e1000_82547_rev_2) { /* IP fragment with UDP payload */ /* Hardware complements the payload checksum, so we undo it * and then put the value in host order for further stack use. @@ -4158,17 +4083,11 @@ e1000_rx_checksum(struct e1000_adapter *adapter, * e1000_clean_rx_irq - Send received data up the network stack; legacy * @adapter: board private structure **/ - -static bool -#ifdef CONFIG_E1000_NAPI -e1000_clean_rx_irq(struct e1000_adapter *adapter, - struct e1000_rx_ring *rx_ring, - int *work_done, int work_to_do) -#else -e1000_clean_rx_irq(struct e1000_adapter *adapter, - struct e1000_rx_ring *rx_ring) -#endif +static bool e1000_clean_rx_irq(struct e1000_adapter *adapter, + struct e1000_rx_ring *rx_ring, + int *work_done, int work_to_do) { + struct e1000_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; struct pci_dev *pdev = adapter->pdev; struct e1000_rx_desc *rx_desc, *next_rxd; @@ -4189,11 +4108,10 @@ e1000_clean_rx_irq(struct e1000_adapter *adapter, struct sk_buff *skb; u8 status; -#ifdef CONFIG_E1000_NAPI if (*work_done >= work_to_do) break; (*work_done)++; -#endif + status = rx_desc->status; skb = buffer_info->skb; buffer_info->skb = NULL; @@ -4226,11 +4144,10 @@ e1000_clean_rx_irq(struct e1000_adapter *adapter, if (unlikely(rx_desc->errors & E1000_RXD_ERR_FRAME_ERR_MASK)) { last_byte = *(skb->data + length - 1); - if (TBI_ACCEPT(&adapter->hw, status, - rx_desc->errors, length, last_byte)) { + if (TBI_ACCEPT(hw, status, rx_desc->errors, length, + last_byte)) { spin_lock_irqsave(&adapter->stats_lock, flags); - e1000_tbi_adjust_stats(&adapter->hw, - &adapter->stats, + e1000_tbi_adjust_stats(hw, &adapter->stats, length, skb->data); spin_unlock_irqrestore(&adapter->stats_lock, flags); @@ -4280,7 +4197,7 @@ e1000_clean_rx_irq(struct e1000_adapter *adapter, le16_to_cpu(rx_desc->csum), skb); skb->protocol = eth_type_trans(skb, netdev); -#ifdef CONFIG_E1000_NAPI + if (unlikely(adapter->vlgrp && (status & E1000_RXD_STAT_VP))) { vlan_hwaccel_receive_skb(skb, adapter->vlgrp, @@ -4288,15 +4205,7 @@ e1000_clean_rx_irq(struct e1000_adapter *adapter, } else { netif_receive_skb(skb); } -#else /* CONFIG_E1000_NAPI */ - if (unlikely(adapter->vlgrp && - (status & E1000_RXD_STAT_VP))) { - vlan_hwaccel_rx(skb, adapter->vlgrp, - le16_to_cpu(rx_desc->special)); - } else { - netif_rx(skb); - } -#endif /* CONFIG_E1000_NAPI */ + netdev->last_rx = jiffies; next_desc: @@ -4330,15 +4239,9 @@ next_desc: * @adapter: board private structure **/ -static bool -#ifdef CONFIG_E1000_NAPI -e1000_clean_rx_irq_ps(struct e1000_adapter *adapter, - struct e1000_rx_ring *rx_ring, - int *work_done, int work_to_do) -#else -e1000_clean_rx_irq_ps(struct e1000_adapter *adapter, - struct e1000_rx_ring *rx_ring) -#endif +static bool e1000_clean_rx_irq_ps(struct e1000_adapter *adapter, + struct e1000_rx_ring *rx_ring, + int *work_done, int work_to_do) { union e1000_rx_desc_packet_split *rx_desc, *next_rxd; struct net_device *netdev = adapter->netdev; @@ -4361,11 +4264,11 @@ e1000_clean_rx_irq_ps(struct e1000_adapter *adapter, while (staterr & E1000_RXD_STAT_DD) { ps_page = &rx_ring->ps_page[i]; ps_page_dma = &rx_ring->ps_page_dma[i]; -#ifdef CONFIG_E1000_NAPI + if (unlikely(*work_done >= work_to_do)) break; (*work_done)++; -#endif + skb = buffer_info->skb; /* in the packet split case this is header only */ @@ -4438,7 +4341,8 @@ e1000_clean_rx_irq_ps(struct e1000_adapter *adapter, } for (j = 0; j < adapter->rx_ps_pages; j++) { - if (!(length= le16_to_cpu(rx_desc->wb.upper.length[j]))) + length = le16_to_cpu(rx_desc->wb.upper.length[j]); + if (!length) break; pci_unmap_page(pdev, ps_page_dma->ps_page_dma[j], PAGE_SIZE, PCI_DMA_FROMDEVICE); @@ -4466,21 +4370,14 @@ copydone: if (likely(rx_desc->wb.upper.header_status & cpu_to_le16(E1000_RXDPS_HDRSTAT_HDRSP))) adapter->rx_hdr_split++; -#ifdef CONFIG_E1000_NAPI + if (unlikely(adapter->vlgrp && (staterr & E1000_RXD_STAT_VP))) { vlan_hwaccel_receive_skb(skb, adapter->vlgrp, le16_to_cpu(rx_desc->wb.middle.vlan)); } else { netif_receive_skb(skb); } -#else /* CONFIG_E1000_NAPI */ - if (unlikely(adapter->vlgrp && (staterr & E1000_RXD_STAT_VP))) { - vlan_hwaccel_rx(skb, adapter->vlgrp, - le16_to_cpu(rx_desc->wb.middle.vlan)); - } else { - netif_rx(skb); - } -#endif /* CONFIG_E1000_NAPI */ + netdev->last_rx = jiffies; next_desc: @@ -4517,11 +4414,11 @@ next_desc: * @adapter: address of board private structure **/ -static void -e1000_alloc_rx_buffers(struct e1000_adapter *adapter, - struct e1000_rx_ring *rx_ring, - int cleaned_count) +static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter, + struct e1000_rx_ring *rx_ring, + int cleaned_count) { + struct e1000_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; struct pci_dev *pdev = adapter->pdev; struct e1000_rx_desc *rx_desc; @@ -4619,7 +4516,7 @@ map_skb: * applicable for weak-ordered memory model archs, * such as IA-64). */ wmb(); - writel(i, adapter->hw.hw_addr + rx_ring->rdt); + writel(i, hw->hw_addr + rx_ring->rdt); } } @@ -4628,11 +4525,11 @@ map_skb: * @adapter: address of board private structure **/ -static void -e1000_alloc_rx_buffers_ps(struct e1000_adapter *adapter, - struct e1000_rx_ring *rx_ring, - int cleaned_count) +static void e1000_alloc_rx_buffers_ps(struct e1000_adapter *adapter, + struct e1000_rx_ring *rx_ring, + int cleaned_count) { + struct e1000_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; struct pci_dev *pdev = adapter->pdev; union e1000_rx_desc_packet_split *rx_desc; @@ -4717,7 +4614,7 @@ no_buffers: * descriptors are 32 bytes...so we increment tail * twice as much. */ - writel(i<<1, adapter->hw.hw_addr + rx_ring->rdt); + writel(i<<1, hw->hw_addr + rx_ring->rdt); } } @@ -4726,49 +4623,49 @@ no_buffers: * @adapter: **/ -static void -e1000_smartspeed(struct e1000_adapter *adapter) +static void e1000_smartspeed(struct e1000_adapter *adapter) { + struct e1000_hw *hw = &adapter->hw; u16 phy_status; u16 phy_ctrl; - if ((adapter->hw.phy_type != e1000_phy_igp) || !adapter->hw.autoneg || - !(adapter->hw.autoneg_advertised & ADVERTISE_1000_FULL)) + if ((hw->phy_type != e1000_phy_igp) || !hw->autoneg || + !(hw->autoneg_advertised & ADVERTISE_1000_FULL)) return; if (adapter->smartspeed == 0) { /* If Master/Slave config fault is asserted twice, * we assume back-to-back */ - e1000_read_phy_reg(&adapter->hw, PHY_1000T_STATUS, &phy_status); + e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_status); if (!(phy_status & SR_1000T_MS_CONFIG_FAULT)) return; - e1000_read_phy_reg(&adapter->hw, PHY_1000T_STATUS, &phy_status); + e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_status); if (!(phy_status & SR_1000T_MS_CONFIG_FAULT)) return; - e1000_read_phy_reg(&adapter->hw, PHY_1000T_CTRL, &phy_ctrl); + e1000_read_phy_reg(hw, PHY_1000T_CTRL, &phy_ctrl); if (phy_ctrl & CR_1000T_MS_ENABLE) { phy_ctrl &= ~CR_1000T_MS_ENABLE; - e1000_write_phy_reg(&adapter->hw, PHY_1000T_CTRL, + e1000_write_phy_reg(hw, PHY_1000T_CTRL, phy_ctrl); adapter->smartspeed++; - if (!e1000_phy_setup_autoneg(&adapter->hw) && - !e1000_read_phy_reg(&adapter->hw, PHY_CTRL, + if (!e1000_phy_setup_autoneg(hw) && + !e1000_read_phy_reg(hw, PHY_CTRL, &phy_ctrl)) { phy_ctrl |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); - e1000_write_phy_reg(&adapter->hw, PHY_CTRL, + e1000_write_phy_reg(hw, PHY_CTRL, phy_ctrl); } } return; } else if (adapter->smartspeed == E1000_SMARTSPEED_DOWNSHIFT) { /* If still no link, perhaps using 2/3 pair cable */ - e1000_read_phy_reg(&adapter->hw, PHY_1000T_CTRL, &phy_ctrl); + e1000_read_phy_reg(hw, PHY_1000T_CTRL, &phy_ctrl); phy_ctrl |= CR_1000T_MS_ENABLE; - e1000_write_phy_reg(&adapter->hw, PHY_1000T_CTRL, phy_ctrl); - if (!e1000_phy_setup_autoneg(&adapter->hw) && - !e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &phy_ctrl)) { + e1000_write_phy_reg(hw, PHY_1000T_CTRL, phy_ctrl); + if (!e1000_phy_setup_autoneg(hw) && + !e1000_read_phy_reg(hw, PHY_CTRL, &phy_ctrl)) { phy_ctrl |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); - e1000_write_phy_reg(&adapter->hw, PHY_CTRL, phy_ctrl); + e1000_write_phy_reg(hw, PHY_CTRL, phy_ctrl); } } /* Restart process after E1000_SMARTSPEED_MAX iterations */ @@ -4783,8 +4680,7 @@ e1000_smartspeed(struct e1000_adapter *adapter) * @cmd: **/ -static int -e1000_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +static int e1000_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { switch (cmd) { case SIOCGMIIPHY: @@ -4803,28 +4699,29 @@ e1000_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) * @cmd: **/ -static int -e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, + int cmd) { struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; struct mii_ioctl_data *data = if_mii(ifr); int retval; u16 mii_reg; u16 spddplx; unsigned long flags; - if (adapter->hw.media_type != e1000_media_type_copper) + if (hw->media_type != e1000_media_type_copper) return -EOPNOTSUPP; switch (cmd) { case SIOCGMIIPHY: - data->phy_id = adapter->hw.phy_addr; + data->phy_id = hw->phy_addr; break; case SIOCGMIIREG: if (!capable(CAP_NET_ADMIN)) return -EPERM; spin_lock_irqsave(&adapter->stats_lock, flags); - if (e1000_read_phy_reg(&adapter->hw, data->reg_num & 0x1F, + if (e1000_read_phy_reg(hw, data->reg_num & 0x1F, &data->val_out)) { spin_unlock_irqrestore(&adapter->stats_lock, flags); return -EIO; @@ -4838,20 +4735,20 @@ e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) return -EFAULT; mii_reg = data->val_in; spin_lock_irqsave(&adapter->stats_lock, flags); - if (e1000_write_phy_reg(&adapter->hw, data->reg_num, + if (e1000_write_phy_reg(hw, data->reg_num, mii_reg)) { spin_unlock_irqrestore(&adapter->stats_lock, flags); return -EIO; } spin_unlock_irqrestore(&adapter->stats_lock, flags); - if (adapter->hw.media_type == e1000_media_type_copper) { + if (hw->media_type == e1000_media_type_copper) { switch (data->reg_num) { case PHY_CTRL: if (mii_reg & MII_CR_POWER_DOWN) break; if (mii_reg & MII_CR_AUTO_NEG_EN) { - adapter->hw.autoneg = 1; - adapter->hw.autoneg_advertised = 0x2F; + hw->autoneg = 1; + hw->autoneg_advertised = 0x2F; } else { if (mii_reg & 0x40) spddplx = SPEED_1000; @@ -4874,7 +4771,7 @@ e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) break; case M88E1000_PHY_SPEC_CTRL: case M88E1000_EXT_PHY_SPEC_CTRL: - if (e1000_phy_reset(&adapter->hw)) + if (e1000_phy_reset(hw)) return -EIO; break; } @@ -4897,8 +4794,7 @@ e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) return E1000_SUCCESS; } -void -e1000_pci_set_mwi(struct e1000_hw *hw) +void e1000_pci_set_mwi(struct e1000_hw *hw) { struct e1000_adapter *adapter = hw->back; int ret_val = pci_set_mwi(adapter->pdev); @@ -4907,30 +4803,26 @@ e1000_pci_set_mwi(struct e1000_hw *hw) DPRINTK(PROBE, ERR, "Error in setting MWI\n"); } -void -e1000_pci_clear_mwi(struct e1000_hw *hw) +void e1000_pci_clear_mwi(struct e1000_hw *hw) { struct e1000_adapter *adapter = hw->back; pci_clear_mwi(adapter->pdev); } -int -e1000_pcix_get_mmrbc(struct e1000_hw *hw) +int e1000_pcix_get_mmrbc(struct e1000_hw *hw) { struct e1000_adapter *adapter = hw->back; return pcix_get_mmrbc(adapter->pdev); } -void -e1000_pcix_set_mmrbc(struct e1000_hw *hw, int mmrbc) +void e1000_pcix_set_mmrbc(struct e1000_hw *hw, int mmrbc) { struct e1000_adapter *adapter = hw->back; pcix_set_mmrbc(adapter->pdev, mmrbc); } -s32 -e1000_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value) +s32 e1000_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value) { struct e1000_adapter *adapter = hw->back; u16 cap_offset; @@ -4944,16 +4836,16 @@ e1000_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value) return E1000_SUCCESS; } -void -e1000_io_write(struct e1000_hw *hw, unsigned long port, u32 value) +void e1000_io_write(struct e1000_hw *hw, unsigned long port, u32 value) { outl(value, port); } -static void -e1000_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp) +static void e1000_vlan_rx_register(struct net_device *netdev, + struct vlan_group *grp) { struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; u32 ctrl, rctl; if (!test_bit(__E1000_DOWN, &adapter->flags)) @@ -4962,22 +4854,22 @@ e1000_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp) if (grp) { /* enable VLAN tag insert/strip */ - ctrl = E1000_READ_REG(&adapter->hw, CTRL); + ctrl = er32(CTRL); ctrl |= E1000_CTRL_VME; - E1000_WRITE_REG(&adapter->hw, CTRL, ctrl); + ew32(CTRL, ctrl); if (adapter->hw.mac_type != e1000_ich8lan) { /* enable VLAN receive filtering */ - rctl = E1000_READ_REG(&adapter->hw, RCTL); + rctl = er32(RCTL); rctl &= ~E1000_RCTL_CFIEN; - E1000_WRITE_REG(&adapter->hw, RCTL, rctl); + ew32(RCTL, rctl); e1000_update_mng_vlan(adapter); } } else { /* disable VLAN tag insert/strip */ - ctrl = E1000_READ_REG(&adapter->hw, CTRL); + ctrl = er32(CTRL); ctrl &= ~E1000_CTRL_VME; - E1000_WRITE_REG(&adapter->hw, CTRL, ctrl); + ew32(CTRL, ctrl); if (adapter->hw.mac_type != e1000_ich8lan) { if (adapter->mng_vlan_id != @@ -4993,27 +4885,27 @@ e1000_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp) e1000_irq_enable(adapter); } -static void -e1000_vlan_rx_add_vid(struct net_device *netdev, u16 vid) +static void e1000_vlan_rx_add_vid(struct net_device *netdev, u16 vid) { struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; u32 vfta, index; - if ((adapter->hw.mng_cookie.status & + if ((hw->mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT) && (vid == adapter->mng_vlan_id)) return; /* add VID to filter table */ index = (vid >> 5) & 0x7F; - vfta = E1000_READ_REG_ARRAY(&adapter->hw, VFTA, index); + vfta = E1000_READ_REG_ARRAY(hw, VFTA, index); vfta |= (1 << (vid & 0x1F)); - e1000_write_vfta(&adapter->hw, index, vfta); + e1000_write_vfta(hw, index, vfta); } -static void -e1000_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) +static void e1000_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) { struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; u32 vfta, index; if (!test_bit(__E1000_DOWN, &adapter->flags)) @@ -5022,7 +4914,7 @@ e1000_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) if (!test_bit(__E1000_DOWN, &adapter->flags)) e1000_irq_enable(adapter); - if ((adapter->hw.mng_cookie.status & + if ((hw->mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT) && (vid == adapter->mng_vlan_id)) { /* release control to f/w */ @@ -5032,13 +4924,12 @@ e1000_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) /* remove VID from filter table */ index = (vid >> 5) & 0x7F; - vfta = E1000_READ_REG_ARRAY(&adapter->hw, VFTA, index); + vfta = E1000_READ_REG_ARRAY(hw, VFTA, index); vfta &= ~(1 << (vid & 0x1F)); - e1000_write_vfta(&adapter->hw, index, vfta); + e1000_write_vfta(hw, index, vfta); } -static void -e1000_restore_vlan(struct e1000_adapter *adapter) +static void e1000_restore_vlan(struct e1000_adapter *adapter) { e1000_vlan_rx_register(adapter->netdev, adapter->vlgrp); @@ -5052,13 +4943,14 @@ e1000_restore_vlan(struct e1000_adapter *adapter) } } -int -e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx) +int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx) { - adapter->hw.autoneg = 0; + struct e1000_hw *hw = &adapter->hw; + + hw->autoneg = 0; /* Fiber NICs only allow 1000 gbps Full duplex */ - if ((adapter->hw.media_type == e1000_media_type_fiber) && + if ((hw->media_type == e1000_media_type_fiber) && spddplx != (SPEED_1000 + DUPLEX_FULL)) { DPRINTK(PROBE, ERR, "Unsupported Speed/Duplex configuration\n"); return -EINVAL; @@ -5066,20 +4958,20 @@ e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx) switch (spddplx) { case SPEED_10 + DUPLEX_HALF: - adapter->hw.forced_speed_duplex = e1000_10_half; + hw->forced_speed_duplex = e1000_10_half; break; case SPEED_10 + DUPLEX_FULL: - adapter->hw.forced_speed_duplex = e1000_10_full; + hw->forced_speed_duplex = e1000_10_full; break; case SPEED_100 + DUPLEX_HALF: - adapter->hw.forced_speed_duplex = e1000_100_half; + hw->forced_speed_duplex = e1000_100_half; break; case SPEED_100 + DUPLEX_FULL: - adapter->hw.forced_speed_duplex = e1000_100_full; + hw->forced_speed_duplex = e1000_100_full; break; case SPEED_1000 + DUPLEX_FULL: - adapter->hw.autoneg = 1; - adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL; + hw->autoneg = 1; + hw->autoneg_advertised = ADVERTISE_1000_FULL; break; case SPEED_1000 + DUPLEX_HALF: /* not supported */ default: @@ -5089,11 +4981,11 @@ e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx) return 0; } -static int -e1000_suspend(struct pci_dev *pdev, pm_message_t state) +static int e1000_suspend(struct pci_dev *pdev, pm_message_t state) { struct net_device *netdev = pci_get_drvdata(pdev); struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; u32 ctrl, ctrl_ext, rctl, status; u32 wufc = adapter->wol; #ifdef CONFIG_PM @@ -5113,7 +5005,7 @@ e1000_suspend(struct pci_dev *pdev, pm_message_t state) return retval; #endif - status = E1000_READ_REG(&adapter->hw, STATUS); + status = er32(STATUS); if (status & E1000_STATUS_LU) wufc &= ~E1000_WUFC_LNKC; @@ -5123,40 +5015,40 @@ e1000_suspend(struct pci_dev *pdev, pm_message_t state) /* turn on all-multi mode if wake on multicast is enabled */ if (wufc & E1000_WUFC_MC) { - rctl = E1000_READ_REG(&adapter->hw, RCTL); + rctl = er32(RCTL); rctl |= E1000_RCTL_MPE; - E1000_WRITE_REG(&adapter->hw, RCTL, rctl); + ew32(RCTL, rctl); } - if (adapter->hw.mac_type >= e1000_82540) { - ctrl = E1000_READ_REG(&adapter->hw, CTRL); + if (hw->mac_type >= e1000_82540) { + ctrl = er32(CTRL); /* advertise wake from D3Cold */ #define E1000_CTRL_ADVD3WUC 0x00100000 /* phy power management enable */ #define E1000_CTRL_EN_PHY_PWR_MGMT 0x00200000 ctrl |= E1000_CTRL_ADVD3WUC | E1000_CTRL_EN_PHY_PWR_MGMT; - E1000_WRITE_REG(&adapter->hw, CTRL, ctrl); + ew32(CTRL, ctrl); } - if (adapter->hw.media_type == e1000_media_type_fiber || - adapter->hw.media_type == e1000_media_type_internal_serdes) { + if (hw->media_type == e1000_media_type_fiber || + hw->media_type == e1000_media_type_internal_serdes) { /* keep the laser running in D3 */ - ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT); + ctrl_ext = er32(CTRL_EXT); ctrl_ext |= E1000_CTRL_EXT_SDP7_DATA; - E1000_WRITE_REG(&adapter->hw, CTRL_EXT, ctrl_ext); + ew32(CTRL_EXT, ctrl_ext); } /* Allow time for pending master requests to run */ - e1000_disable_pciex_master(&adapter->hw); + e1000_disable_pciex_master(hw); - E1000_WRITE_REG(&adapter->hw, WUC, E1000_WUC_PME_EN); - E1000_WRITE_REG(&adapter->hw, WUFC, wufc); + ew32(WUC, E1000_WUC_PME_EN); + ew32(WUFC, wufc); pci_enable_wake(pdev, PCI_D3hot, 1); pci_enable_wake(pdev, PCI_D3cold, 1); } else { - E1000_WRITE_REG(&adapter->hw, WUC, 0); - E1000_WRITE_REG(&adapter->hw, WUFC, 0); + ew32(WUC, 0); + ew32(WUFC, 0); pci_enable_wake(pdev, PCI_D3hot, 0); pci_enable_wake(pdev, PCI_D3cold, 0); } @@ -5169,8 +5061,8 @@ e1000_suspend(struct pci_dev *pdev, pm_message_t state) pci_enable_wake(pdev, PCI_D3cold, 1); } - if (adapter->hw.phy_type == e1000_phy_igp_3) - e1000_phy_powerdown_workaround(&adapter->hw); + if (hw->phy_type == e1000_phy_igp_3) + e1000_phy_powerdown_workaround(hw); if (netif_running(netdev)) e1000_free_irq(adapter); @@ -5187,16 +5079,21 @@ e1000_suspend(struct pci_dev *pdev, pm_message_t state) } #ifdef CONFIG_PM -static int -e1000_resume(struct pci_dev *pdev) +static int e1000_resume(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; u32 err; pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); - if ((err = pci_enable_device(pdev))) { + + if (adapter->need_ioport) + err = pci_enable_device(pdev); + else + err = pci_enable_device_mem(pdev); + if (err) { printk(KERN_ERR "e1000: Cannot enable PCI device from suspend\n"); return err; } @@ -5205,12 +5102,15 @@ e1000_resume(struct pci_dev *pdev) pci_enable_wake(pdev, PCI_D3hot, 0); pci_enable_wake(pdev, PCI_D3cold, 0); - if (netif_running(netdev) && (err = e1000_request_irq(adapter))) - return err; + if (netif_running(netdev)) { + err = e1000_request_irq(adapter); + if (err) + return err; + } e1000_power_up_phy(adapter); e1000_reset(adapter); - E1000_WRITE_REG(&adapter->hw, WUS, ~0); + ew32(WUS, ~0); e1000_init_manageability(adapter); @@ -5223,8 +5123,8 @@ e1000_resume(struct pci_dev *pdev) * DRV_LOAD until the interface is up. For all other cases, * let the f/w know that the h/w is now under the control * of the driver. */ - if (adapter->hw.mac_type != e1000_82573 || - !e1000_check_mng_mode(&adapter->hw)) + if (hw->mac_type != e1000_82573 || + !e1000_check_mng_mode(hw)) e1000_get_hw_control(adapter); return 0; @@ -5242,16 +5142,12 @@ static void e1000_shutdown(struct pci_dev *pdev) * without having to re-enable interrupts. It's not called while * the interrupt routine is executing. */ -static void -e1000_netpoll(struct net_device *netdev) +static void e1000_netpoll(struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); disable_irq(adapter->pdev->irq); e1000_intr(adapter->pdev->irq, netdev); -#ifndef CONFIG_E1000_NAPI - adapter->clean_rx(adapter, adapter->rx_ring); -#endif enable_irq(adapter->pdev->irq); } #endif @@ -5264,7 +5160,8 @@ e1000_netpoll(struct net_device *netdev) * This function is called after a PCI bus error affecting * this device has been detected. */ -static pci_ers_result_t e1000_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state) +static pci_ers_result_t e1000_io_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) { struct net_device *netdev = pci_get_drvdata(pdev); struct e1000_adapter *adapter = netdev->priv; @@ -5290,8 +5187,14 @@ static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct e1000_adapter *adapter = netdev->priv; + struct e1000_hw *hw = &adapter->hw; + int err; - if (pci_enable_device(pdev)) { + if (adapter->need_ioport) + err = pci_enable_device(pdev); + else + err = pci_enable_device_mem(pdev); + if (err) { printk(KERN_ERR "e1000: Cannot re-enable PCI device after reset.\n"); return PCI_ERS_RESULT_DISCONNECT; } @@ -5301,7 +5204,7 @@ static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev) pci_enable_wake(pdev, PCI_D3cold, 0); e1000_reset(adapter); - E1000_WRITE_REG(&adapter->hw, WUS, ~0); + ew32(WUS, ~0); return PCI_ERS_RESULT_RECOVERED; } @@ -5318,6 +5221,7 @@ static void e1000_io_resume(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct e1000_adapter *adapter = netdev->priv; + struct e1000_hw *hw = &adapter->hw; e1000_init_manageability(adapter); @@ -5334,8 +5238,8 @@ static void e1000_io_resume(struct pci_dev *pdev) * DRV_LOAD until the interface is up. For all other cases, * let the f/w know that the h/w is now under the control * of the driver. */ - if (adapter->hw.mac_type != e1000_82573 || - !e1000_check_mng_mode(&adapter->hw)) + if (hw->mac_type != e1000_82573 || + !e1000_check_mng_mode(hw)) e1000_get_hw_control(adapter); } diff --git a/drivers/net/e1000/e1000_osdep.h b/drivers/net/e1000/e1000_osdep.h index 365626d3177e..d9298522f5ae 100644 --- a/drivers/net/e1000/e1000_osdep.h +++ b/drivers/net/e1000/e1000_osdep.h @@ -55,13 +55,13 @@ #define DEBUGOUT7 DEBUGOUT3 -#define E1000_WRITE_REG(a, reg, value) ( \ - writel((value), ((a)->hw_addr + \ - (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg)))) +#define er32(reg) \ + (readl(hw->hw_addr + ((hw->mac_type >= e1000_82543) \ + ? E1000_##reg : E1000_82542_##reg))) -#define E1000_READ_REG(a, reg) ( \ - readl((a)->hw_addr + \ - (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg))) +#define ew32(reg, value) \ + (writel((value), (hw->hw_addr + ((hw->mac_type >= e1000_82543) \ + ? E1000_##reg : E1000_82542_##reg)))) #define E1000_WRITE_REG_ARRAY(a, reg, offset, value) ( \ writel((value), ((a)->hw_addr + \ @@ -96,7 +96,7 @@ (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg) + \ (offset))) -#define E1000_WRITE_FLUSH(a) E1000_READ_REG(a, STATUS) +#define E1000_WRITE_FLUSH() er32(STATUS) #define E1000_WRITE_ICH_FLASH_REG(a, reg, value) ( \ writel((value), ((a)->flash_address + reg))) diff --git a/drivers/net/e1000/e1000_param.c b/drivers/net/e1000/e1000_param.c index e6565ce686bc..b9f90a5d3d4d 100644 --- a/drivers/net/e1000/e1000_param.c +++ b/drivers/net/e1000/e1000_param.c @@ -213,10 +213,9 @@ struct e1000_option { } arg; }; -static int __devinit -e1000_validate_option(unsigned int *value, - const struct e1000_option *opt, - struct e1000_adapter *adapter) +static int __devinit e1000_validate_option(unsigned int *value, + const struct e1000_option *opt, + struct e1000_adapter *adapter) { if (*value == OPTION_UNSET) { *value = opt->def; @@ -278,8 +277,7 @@ static void e1000_check_copper_options(struct e1000_adapter *adapter); * in a variable in the adapter structure. **/ -void __devinit -e1000_check_options(struct e1000_adapter *adapter) +void __devinit e1000_check_options(struct e1000_adapter *adapter) { int bd = adapter->bd_number; if (bd >= E1000_MAX_NIC) { @@ -551,8 +549,7 @@ e1000_check_options(struct e1000_adapter *adapter) * Handles speed and duplex options on fiber adapters **/ -static void __devinit -e1000_check_fiber_options(struct e1000_adapter *adapter) +static void __devinit e1000_check_fiber_options(struct e1000_adapter *adapter) { int bd = adapter->bd_number; if (num_Speed > bd) { @@ -579,8 +576,7 @@ e1000_check_fiber_options(struct e1000_adapter *adapter) * Handles speed and duplex options on copper adapters **/ -static void __devinit -e1000_check_copper_options(struct e1000_adapter *adapter) +static void __devinit e1000_check_copper_options(struct e1000_adapter *adapter) { unsigned int speed, dplx, an; int bd = adapter->bd_number; diff --git a/drivers/net/e1000e/ethtool.c b/drivers/net/e1000e/ethtool.c index a14561f40db0..9350564065e7 100644 --- a/drivers/net/e1000e/ethtool.c +++ b/drivers/net/e1000e/ethtool.c @@ -1090,7 +1090,7 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter) tx_ring->buffer_info[i].dma = pci_map_single(pdev, skb->data, skb->len, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(tx_ring->buffer_info[i].dma)) { + if (pci_dma_mapping_error(pdev, tx_ring->buffer_info[i].dma)) { ret_val = 4; goto err_nomem; } @@ -1153,7 +1153,7 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter) rx_ring->buffer_info[i].dma = pci_map_single(pdev, skb->data, 2048, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rx_ring->buffer_info[i].dma)) { + if (pci_dma_mapping_error(pdev, rx_ring->buffer_info[i].dma)) { ret_val = 8; goto err_nomem; } diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index 869544b8c05c..d13677899767 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -195,7 +195,7 @@ map_skb: buffer_info->dma = pci_map_single(pdev, skb->data, adapter->rx_buffer_len, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(buffer_info->dma)) { + if (pci_dma_mapping_error(pdev, buffer_info->dma)) { dev_err(&pdev->dev, "RX DMA map failed\n"); adapter->rx_dma_failed++; break; @@ -265,7 +265,7 @@ static void e1000_alloc_rx_buffers_ps(struct e1000_adapter *adapter, ps_page->page, 0, PAGE_SIZE, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(ps_page->dma)) { + if (pci_dma_mapping_error(pdev, ps_page->dma)) { dev_err(&adapter->pdev->dev, "RX DMA page map failed\n"); adapter->rx_dma_failed++; @@ -300,7 +300,7 @@ static void e1000_alloc_rx_buffers_ps(struct e1000_adapter *adapter, buffer_info->dma = pci_map_single(pdev, skb->data, adapter->rx_ps_bsize0, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(buffer_info->dma)) { + if (pci_dma_mapping_error(pdev, buffer_info->dma)) { dev_err(&pdev->dev, "RX DMA map failed\n"); adapter->rx_dma_failed++; /* cleanup skb */ @@ -3344,7 +3344,7 @@ static int e1000_tx_map(struct e1000_adapter *adapter, skb->data + offset, size, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(buffer_info->dma)) { + if (pci_dma_mapping_error(adapter->pdev, buffer_info->dma)) { dev_err(&adapter->pdev->dev, "TX DMA map failed\n"); adapter->tx_dma_failed++; return -1; @@ -3382,7 +3382,8 @@ static int e1000_tx_map(struct e1000_adapter *adapter, offset, size, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(buffer_info->dma)) { + if (pci_dma_mapping_error(adapter->pdev, + buffer_info->dma)) { dev_err(&adapter->pdev->dev, "TX DMA page map failed\n"); adapter->tx_dma_failed++; @@ -4067,8 +4068,6 @@ static void e1000_netpoll(struct net_device *netdev) disable_irq(adapter->pdev->irq); e1000_intr(adapter->pdev->irq, netdev); - e1000_clean_tx_irq(adapter); - enable_irq(adapter->pdev->irq); } #endif diff --git a/drivers/net/fec.c b/drivers/net/fec.c index 32a4f17d35fc..ecd5c71a7a8a 100644 --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -2,12 +2,6 @@ * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx. * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) * - * This version of the driver is specific to the FADS implementation, - * since the board contains control registers external to the processor - * for the control of the LevelOne LXT970 transceiver. The MPC860T manual - * describes connections using the internal parallel port I/O, which - * is basically all of Port D. - * * Right now, I am very wasteful with the buffers. I allocate memory * pages and then divide them into 2K frame buffers. This way I know I * have buffers large enough to hold one frame within one buffer descriptor. @@ -49,17 +43,9 @@ #include <asm/pgtable.h> #include <asm/cacheflush.h> -#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || \ - defined(CONFIG_M5272) || defined(CONFIG_M528x) || \ - defined(CONFIG_M520x) || defined(CONFIG_M532x) #include <asm/coldfire.h> #include <asm/mcfsim.h> #include "fec.h" -#else -#include <asm/8xx_immap.h> -#include <asm/mpc8xx.h> -#include "commproc.h" -#endif #if defined(CONFIG_FEC2) #define FEC_MAX_PORTS 2 @@ -67,7 +53,7 @@ #define FEC_MAX_PORTS 1 #endif -#if defined(CONFIG_FADS) || defined(CONFIG_RPXCLASSIC) || defined(CONFIG_M5272) +#if defined(CONFIG_M5272) #define HAVE_mii_link_interrupt #endif @@ -1235,14 +1221,9 @@ static phy_info_t const * const phy_info[] = { /* ------------------------------------------------------------------------- */ #ifdef HAVE_mii_link_interrupt -#ifdef CONFIG_RPXCLASSIC -static void -mii_link_interrupt(void *dev_id); -#else static irqreturn_t mii_link_interrupt(int irq, void * dev_id); #endif -#endif #if defined(CONFIG_M5272) /* @@ -1795,24 +1776,6 @@ static void __inline__ fec_request_intrs(struct net_device *dev) if (request_8xxirq(FEC_INTERRUPT, fec_enet_interrupt, 0, "fec", dev) != 0) panic("Could not allocate FEC IRQ!"); - -#ifdef CONFIG_RPXCLASSIC - /* Make Port C, bit 15 an input that causes interrupts. - */ - immap->im_ioport.iop_pcpar &= ~0x0001; - immap->im_ioport.iop_pcdir &= ~0x0001; - immap->im_ioport.iop_pcso &= ~0x0001; - immap->im_ioport.iop_pcint |= 0x0001; - cpm_install_handler(CPMVEC_PIO_PC15, mii_link_interrupt, dev); - - /* Make LEDS reflect Link status. - */ - *((uint *) RPX_CSR_ADDR) &= ~BCSR2_FETHLEDMODE; -#endif -#ifdef CONFIG_FADS - if (request_8xxirq(SIU_IRQ2, mii_link_interrupt, 0, "mii", dev) != 0) - panic("Could not allocate MII IRQ!"); -#endif } static void __inline__ fec_get_mac(struct net_device *dev) @@ -1821,16 +1784,6 @@ static void __inline__ fec_get_mac(struct net_device *dev) bd = (bd_t *)__res; memcpy(dev->dev_addr, bd->bi_enetaddr, ETH_ALEN); - -#ifdef CONFIG_RPXCLASSIC - /* The Embedded Planet boards have only one MAC address in - * the EEPROM, but can have two Ethernet ports. For the - * FEC port, we create another address by setting one of - * the address bits above something that would have (up to - * now) been allocated. - */ - dev->dev_adrd[3] |= 0x80; -#endif } static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep) @@ -2109,13 +2062,8 @@ mii_discover_phy(uint mii_reg, struct net_device *dev) /* This interrupt occurs when the PHY detects a link change. */ #ifdef HAVE_mii_link_interrupt -#ifdef CONFIG_RPXCLASSIC -static void -mii_link_interrupt(void *dev_id) -#else static irqreturn_t mii_link_interrupt(int irq, void * dev_id) -#endif { struct net_device *dev = dev_id; struct fec_enet_private *fep = netdev_priv(dev); diff --git a/drivers/net/fec_mpc52xx.c b/drivers/net/fec_mpc52xx.c index ae9ecb7df22b..4e4f68304e82 100644 --- a/drivers/net/fec_mpc52xx.c +++ b/drivers/net/fec_mpc52xx.c @@ -197,9 +197,6 @@ static void mpc52xx_fec_adjust_link(struct net_device *dev) if (priv->link == PHY_DOWN) { new_state = 1; priv->link = phydev->link; - netif_tx_schedule_all(dev); - netif_carrier_on(dev); - netif_start_queue(dev); } } else if (priv->link) { @@ -207,8 +204,6 @@ static void mpc52xx_fec_adjust_link(struct net_device *dev) priv->link = PHY_DOWN; priv->speed = 0; priv->duplex = -1; - netif_stop_queue(dev); - netif_carrier_off(dev); } if (new_state && netif_msg_link(priv)) diff --git a/drivers/net/fs_enet/Makefile b/drivers/net/fs_enet/Makefile index 1ffbe0756a0c..d4a305ee3455 100644 --- a/drivers/net/fs_enet/Makefile +++ b/drivers/net/fs_enet/Makefile @@ -8,12 +8,7 @@ fs_enet-$(CONFIG_FS_ENET_HAS_SCC) += mac-scc.o fs_enet-$(CONFIG_FS_ENET_HAS_FEC) += mac-fec.o fs_enet-$(CONFIG_FS_ENET_HAS_FCC) += mac-fcc.o -ifeq ($(CONFIG_PPC_CPM_NEW_BINDING),y) obj-$(CONFIG_FS_ENET_MDIO_FEC) += mii-fec.o obj-$(CONFIG_FS_ENET_MDIO_FCC) += mii-bitbang.o -else -fs_enet-$(CONFIG_FS_ENET_MDIO_FEC) += mii-fec.o -fs_enet-$(CONFIG_FS_ENET_MDIO_FCC) += mii-bitbang.o -endif fs_enet-objs := fs_enet-main.o $(fs_enet-m) diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c index 445763e5648e..9a51ec8293cc 100644 --- a/drivers/net/fs_enet/fs_enet-main.c +++ b/drivers/net/fs_enet/fs_enet-main.c @@ -36,26 +36,18 @@ #include <linux/fs.h> #include <linux/platform_device.h> #include <linux/phy.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> #include <linux/vmalloc.h> #include <asm/pgtable.h> #include <asm/irq.h> #include <asm/uaccess.h> -#ifdef CONFIG_PPC_CPM_NEW_BINDING -#include <linux/of_gpio.h> -#include <linux/of_platform.h> -#endif - #include "fs_enet.h" /*************************************************/ -#ifndef CONFIG_PPC_CPM_NEW_BINDING -static char version[] __devinitdata = - DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")" "\n"; -#endif - MODULE_AUTHOR("Pantelis Antoniou <panto@intracom.gr>"); MODULE_DESCRIPTION("Freescale Ethernet Driver"); MODULE_LICENSE("GPL"); @@ -738,9 +730,6 @@ static void generic_adjust_link(struct net_device *dev) if (!fep->oldlink) { new_state = 1; fep->oldlink = 1; - netif_tx_schedule_all(dev); - netif_carrier_on(dev); - netif_start_queue(dev); } if (new_state) @@ -750,8 +739,6 @@ static void generic_adjust_link(struct net_device *dev) fep->oldlink = 0; fep->oldspeed = 0; fep->oldduplex = -1; - netif_carrier_off(dev); - netif_stop_queue(dev); } if (new_state && netif_msg_link(fep)) @@ -826,6 +813,8 @@ static int fs_enet_open(struct net_device *dev) } phy_start(fep->phydev); + netif_start_queue(dev); + return 0; } @@ -958,190 +947,6 @@ static int fs_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) extern int fs_mii_connect(struct net_device *dev); extern void fs_mii_disconnect(struct net_device *dev); -#ifndef CONFIG_PPC_CPM_NEW_BINDING -static struct net_device *fs_init_instance(struct device *dev, - struct fs_platform_info *fpi) -{ - struct net_device *ndev = NULL; - struct fs_enet_private *fep = NULL; - int privsize, i, r, err = 0, registered = 0; - - fpi->fs_no = fs_get_id(fpi); - /* guard */ - if ((unsigned int)fpi->fs_no >= FS_MAX_INDEX) - return ERR_PTR(-EINVAL); - - privsize = sizeof(*fep) + (sizeof(struct sk_buff **) * - (fpi->rx_ring + fpi->tx_ring)); - - ndev = alloc_etherdev(privsize); - if (!ndev) { - err = -ENOMEM; - goto err; - } - - fep = netdev_priv(ndev); - - fep->dev = dev; - dev_set_drvdata(dev, ndev); - fep->fpi = fpi; - if (fpi->init_ioports) - fpi->init_ioports((struct fs_platform_info *)fpi); - -#ifdef CONFIG_FS_ENET_HAS_FEC - if (fs_get_fec_index(fpi->fs_no) >= 0) - fep->ops = &fs_fec_ops; -#endif - -#ifdef CONFIG_FS_ENET_HAS_SCC - if (fs_get_scc_index(fpi->fs_no) >=0) - fep->ops = &fs_scc_ops; -#endif - -#ifdef CONFIG_FS_ENET_HAS_FCC - if (fs_get_fcc_index(fpi->fs_no) >= 0) - fep->ops = &fs_fcc_ops; -#endif - - if (fep->ops == NULL) { - printk(KERN_ERR DRV_MODULE_NAME - ": %s No matching ops found (%d).\n", - ndev->name, fpi->fs_no); - err = -EINVAL; - goto err; - } - - r = (*fep->ops->setup_data)(ndev); - if (r != 0) { - printk(KERN_ERR DRV_MODULE_NAME - ": %s setup_data failed\n", - ndev->name); - err = r; - goto err; - } - - /* point rx_skbuff, tx_skbuff */ - fep->rx_skbuff = (struct sk_buff **)&fep[1]; - fep->tx_skbuff = fep->rx_skbuff + fpi->rx_ring; - - /* init locks */ - spin_lock_init(&fep->lock); - spin_lock_init(&fep->tx_lock); - - /* - * Set the Ethernet address. - */ - for (i = 0; i < 6; i++) - ndev->dev_addr[i] = fpi->macaddr[i]; - - r = (*fep->ops->allocate_bd)(ndev); - - if (fep->ring_base == NULL) { - printk(KERN_ERR DRV_MODULE_NAME - ": %s buffer descriptor alloc failed (%d).\n", ndev->name, r); - err = r; - goto err; - } - - /* - * Set receive and transmit descriptor base. - */ - fep->rx_bd_base = fep->ring_base; - fep->tx_bd_base = fep->rx_bd_base + fpi->rx_ring; - - /* initialize ring size variables */ - fep->tx_ring = fpi->tx_ring; - fep->rx_ring = fpi->rx_ring; - - /* - * The FEC Ethernet specific entries in the device structure. - */ - ndev->open = fs_enet_open; - ndev->hard_start_xmit = fs_enet_start_xmit; - ndev->tx_timeout = fs_timeout; - ndev->watchdog_timeo = 2 * HZ; - ndev->stop = fs_enet_close; - ndev->get_stats = fs_enet_get_stats; - ndev->set_multicast_list = fs_set_multicast_list; - -#ifdef CONFIG_NET_POLL_CONTROLLER - ndev->poll_controller = fs_enet_netpoll; -#endif - - netif_napi_add(ndev, &fep->napi, - fs_enet_rx_napi, fpi->napi_weight); - - ndev->ethtool_ops = &fs_ethtool_ops; - ndev->do_ioctl = fs_ioctl; - - init_timer(&fep->phy_timer_list); - - netif_carrier_off(ndev); - - err = register_netdev(ndev); - if (err != 0) { - printk(KERN_ERR DRV_MODULE_NAME - ": %s register_netdev failed.\n", ndev->name); - goto err; - } - registered = 1; - - - return ndev; - -err: - if (ndev != NULL) { - if (registered) - unregister_netdev(ndev); - - if (fep && fep->ops) { - (*fep->ops->free_bd)(ndev); - (*fep->ops->cleanup_data)(ndev); - } - - free_netdev(ndev); - } - - dev_set_drvdata(dev, NULL); - - return ERR_PTR(err); -} - -static int fs_cleanup_instance(struct net_device *ndev) -{ - struct fs_enet_private *fep; - const struct fs_platform_info *fpi; - struct device *dev; - - if (ndev == NULL) - return -EINVAL; - - fep = netdev_priv(ndev); - if (fep == NULL) - return -EINVAL; - - fpi = fep->fpi; - - unregister_netdev(ndev); - - dma_free_coherent(fep->dev, (fpi->tx_ring + fpi->rx_ring) * sizeof(cbd_t), - (void __force *)fep->ring_base, fep->ring_mem_addr); - - /* reset it */ - (*fep->ops->cleanup_data)(ndev); - - dev = fep->dev; - if (dev != NULL) { - dev_set_drvdata(dev, NULL); - fep->dev = NULL; - } - - free_netdev(ndev); - - return 0; -} -#endif - /**************************************************************************************/ /* handy pointer to the immap */ @@ -1168,7 +973,6 @@ static void cleanup_immap(void) /**************************************************************************************/ -#ifdef CONFIG_PPC_CPM_NEW_BINDING static int __devinit find_phy(struct device_node *np, struct fs_platform_info *fpi) { @@ -1408,121 +1212,6 @@ static void __exit fs_cleanup(void) of_unregister_platform_driver(&fs_enet_driver); cleanup_immap(); } -#else -static int __devinit fs_enet_probe(struct device *dev) -{ - struct net_device *ndev; - - /* no fixup - no device */ - if (dev->platform_data == NULL) { - printk(KERN_INFO "fs_enet: " - "probe called with no platform data; " - "remove unused devices\n"); - return -ENODEV; - } - - ndev = fs_init_instance(dev, dev->platform_data); - if (IS_ERR(ndev)) - return PTR_ERR(ndev); - return 0; -} - -static int fs_enet_remove(struct device *dev) -{ - return fs_cleanup_instance(dev_get_drvdata(dev)); -} - -static struct device_driver fs_enet_fec_driver = { - .name = "fsl-cpm-fec", - .bus = &platform_bus_type, - .probe = fs_enet_probe, - .remove = fs_enet_remove, -#ifdef CONFIG_PM -/* .suspend = fs_enet_suspend, TODO */ -/* .resume = fs_enet_resume, TODO */ -#endif -}; - -static struct device_driver fs_enet_scc_driver = { - .name = "fsl-cpm-scc", - .bus = &platform_bus_type, - .probe = fs_enet_probe, - .remove = fs_enet_remove, -#ifdef CONFIG_PM -/* .suspend = fs_enet_suspend, TODO */ -/* .resume = fs_enet_resume, TODO */ -#endif -}; - -static struct device_driver fs_enet_fcc_driver = { - .name = "fsl-cpm-fcc", - .bus = &platform_bus_type, - .probe = fs_enet_probe, - .remove = fs_enet_remove, -#ifdef CONFIG_PM -/* .suspend = fs_enet_suspend, TODO */ -/* .resume = fs_enet_resume, TODO */ -#endif -}; - -static int __init fs_init(void) -{ - int r; - - printk(KERN_INFO - "%s", version); - - r = setup_immap(); - if (r != 0) - return r; - -#ifdef CONFIG_FS_ENET_HAS_FCC - /* let's insert mii stuff */ - r = fs_enet_mdio_bb_init(); - - if (r != 0) { - printk(KERN_ERR DRV_MODULE_NAME - "BB PHY init failed.\n"); - return r; - } - r = driver_register(&fs_enet_fcc_driver); - if (r != 0) - goto err; -#endif - -#ifdef CONFIG_FS_ENET_HAS_FEC - r = fs_enet_mdio_fec_init(); - if (r != 0) { - printk(KERN_ERR DRV_MODULE_NAME - "FEC PHY init failed.\n"); - return r; - } - - r = driver_register(&fs_enet_fec_driver); - if (r != 0) - goto err; -#endif - -#ifdef CONFIG_FS_ENET_HAS_SCC - r = driver_register(&fs_enet_scc_driver); - if (r != 0) - goto err; -#endif - - return 0; -err: - cleanup_immap(); - return r; -} - -static void __exit fs_cleanup(void) -{ - driver_unregister(&fs_enet_fec_driver); - driver_unregister(&fs_enet_fcc_driver); - driver_unregister(&fs_enet_scc_driver); - cleanup_immap(); -} -#endif #ifdef CONFIG_NET_POLL_CONTROLLER static void fs_enet_netpoll(struct net_device *dev) diff --git a/drivers/net/fs_enet/fs_enet.h b/drivers/net/fs_enet/fs_enet.h index e05389c49bbb..db46d2e72329 100644 --- a/drivers/net/fs_enet/fs_enet.h +++ b/drivers/net/fs_enet/fs_enet.h @@ -138,10 +138,6 @@ struct fs_enet_private { }; /***************************************************************************/ -#ifndef CONFIG_PPC_CPM_NEW_BINDING -int fs_enet_mdio_bb_init(void); -int fs_enet_mdio_fec_init(void); -#endif void fs_init_bds(struct net_device *dev); void fs_cleanup_bds(struct net_device *dev); diff --git a/drivers/net/fs_enet/mac-fcc.c b/drivers/net/fs_enet/mac-fcc.c index 8268b3535b30..0a97fc2d97ec 100644 --- a/drivers/net/fs_enet/mac-fcc.c +++ b/drivers/net/fs_enet/mac-fcc.c @@ -33,6 +33,7 @@ #include <linux/fs.h> #include <linux/platform_device.h> #include <linux/phy.h> +#include <linux/of_device.h> #include <asm/immap_cpm2.h> #include <asm/mpc8260.h> @@ -42,10 +43,6 @@ #include <asm/irq.h> #include <asm/uaccess.h> -#ifdef CONFIG_PPC_CPM_NEW_BINDING -#include <asm/of_device.h> -#endif - #include "fs_enet.h" /*************************************************/ @@ -87,7 +84,6 @@ static inline int fcc_cr_cmd(struct fs_enet_private *fep, u32 op) static int do_pd_setup(struct fs_enet_private *fep) { -#ifdef CONFIG_PPC_CPM_NEW_BINDING struct of_device *ofdev = to_of_device(fep->dev); struct fs_platform_info *fpi = fep->fpi; int ret = -EINVAL; @@ -125,44 +121,6 @@ out_fccp: iounmap(fep->fcc.fccp); out: return ret; -#else - struct platform_device *pdev = to_platform_device(fep->dev); - struct resource *r; - - /* Fill out IRQ field */ - fep->interrupt = platform_get_irq(pdev, 0); - if (fep->interrupt < 0) - return -EINVAL; - - /* Attach the memory for the FCC Parameter RAM */ - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fcc_pram"); - fep->fcc.ep = ioremap(r->start, r->end - r->start + 1); - if (fep->fcc.ep == NULL) - return -EINVAL; - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fcc_regs"); - fep->fcc.fccp = ioremap(r->start, r->end - r->start + 1); - if (fep->fcc.fccp == NULL) - return -EINVAL; - - if (fep->fpi->fcc_regs_c) { - fep->fcc.fcccp = (void __iomem *)fep->fpi->fcc_regs_c; - } else { - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "fcc_regs_c"); - fep->fcc.fcccp = ioremap(r->start, - r->end - r->start + 1); - } - - if (fep->fcc.fcccp == NULL) - return -EINVAL; - - fep->fcc.mem = (void __iomem *)fep->fpi->mem_offset; - if (fep->fcc.mem == NULL) - return -EINVAL; - - return 0; -#endif } #define FCC_NAPI_RX_EVENT_MSK (FCC_ENET_RXF | FCC_ENET_RXB) @@ -173,17 +131,6 @@ out: static int setup_data(struct net_device *dev) { struct fs_enet_private *fep = netdev_priv(dev); -#ifndef CONFIG_PPC_CPM_NEW_BINDING - struct fs_platform_info *fpi = fep->fpi; - - fpi->cp_command = (fpi->cp_page << 26) | - (fpi->cp_block << 21) | - (12 << 6); - - fep->fcc.idx = fs_get_fcc_index(fpi->fs_no); - if ((unsigned int)fep->fcc.idx >= 3) /* max 3 FCCs */ - return -EINVAL; -#endif if (do_pd_setup(fep) != 0) return -EINVAL; @@ -304,9 +251,6 @@ static void restart(struct net_device *dev) fcc_enet_t __iomem *ep = fep->fcc.ep; dma_addr_t rx_bd_base_phys, tx_bd_base_phys; u16 paddrh, paddrm, paddrl; -#ifndef CONFIG_PPC_CPM_NEW_BINDING - u16 mem_addr; -#endif const unsigned char *mac; int i; @@ -338,19 +282,10 @@ static void restart(struct net_device *dev) * this area. */ -#ifdef CONFIG_PPC_CPM_NEW_BINDING W16(ep, fen_genfcc.fcc_riptr, fpi->dpram_offset); W16(ep, fen_genfcc.fcc_tiptr, fpi->dpram_offset + 32); W16(ep, fen_padptr, fpi->dpram_offset + 64); -#else - mem_addr = (u32) fep->fcc.mem; /* de-fixup dpram offset */ - - W16(ep, fen_genfcc.fcc_riptr, (mem_addr & 0xffff)); - W16(ep, fen_genfcc.fcc_tiptr, ((mem_addr + 32) & 0xffff)); - - W16(ep, fen_padptr, mem_addr + 64); -#endif /* fill with special symbol... */ memset_io(fep->fcc.mem + fpi->dpram_offset + 64, 0x88, 32); diff --git a/drivers/net/fs_enet/mac-fec.c b/drivers/net/fs_enet/mac-fec.c index 8a311d1e435b..0a7d1c5c6524 100644 --- a/drivers/net/fs_enet/mac-fec.c +++ b/drivers/net/fs_enet/mac-fec.c @@ -32,6 +32,7 @@ #include <linux/bitops.h> #include <linux/fs.h> #include <linux/platform_device.h> +#include <linux/of_device.h> #include <asm/irq.h> #include <asm/uaccess.h> @@ -43,10 +44,6 @@ #include <asm/cpm1.h> #endif -#ifdef CONFIG_PPC_CPM_NEW_BINDING -#include <asm/of_device.h> -#endif - #include "fs_enet.h" #include "fec.h" @@ -99,7 +96,6 @@ static int whack_reset(fec_t __iomem *fecp) static int do_pd_setup(struct fs_enet_private *fep) { -#ifdef CONFIG_PPC_CPM_NEW_BINDING struct of_device *ofdev = to_of_device(fep->dev); fep->interrupt = of_irq_to_resource(ofdev->node, 0, NULL); @@ -111,23 +107,6 @@ static int do_pd_setup(struct fs_enet_private *fep) return -EINVAL; return 0; -#else - struct platform_device *pdev = to_platform_device(fep->dev); - struct resource *r; - - /* Fill out IRQ field */ - fep->interrupt = platform_get_irq_byname(pdev,"interrupt"); - if (fep->interrupt < 0) - return -EINVAL; - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); - fep->fec.fecp = ioremap(r->start, r->end - r->start + 1); - - if(fep->fec.fecp == NULL) - return -EINVAL; - - return 0; -#endif } #define FEC_NAPI_RX_EVENT_MSK (FEC_ENET_RXF | FEC_ENET_RXB) diff --git a/drivers/net/fs_enet/mac-scc.c b/drivers/net/fs_enet/mac-scc.c index e3557eca7b6d..029b3c7ef29c 100644 --- a/drivers/net/fs_enet/mac-scc.c +++ b/drivers/net/fs_enet/mac-scc.c @@ -32,6 +32,7 @@ #include <linux/bitops.h> #include <linux/fs.h> #include <linux/platform_device.h> +#include <linux/of_platform.h> #include <asm/irq.h> #include <asm/uaccess.h> @@ -43,10 +44,6 @@ #include <asm/cpm1.h> #endif -#ifdef CONFIG_PPC_CPM_NEW_BINDING -#include <linux/of_platform.h> -#endif - #include "fs_enet.h" /*************************************************/ @@ -99,7 +96,6 @@ static inline int scc_cr_cmd(struct fs_enet_private *fep, u32 op) static int do_pd_setup(struct fs_enet_private *fep) { -#ifdef CONFIG_PPC_CPM_NEW_BINDING struct of_device *ofdev = to_of_device(fep->dev); fep->interrupt = of_irq_to_resource(ofdev->node, 0, NULL); @@ -115,27 +111,6 @@ static int do_pd_setup(struct fs_enet_private *fep) iounmap(fep->scc.sccp); return -EINVAL; } -#else - struct platform_device *pdev = to_platform_device(fep->dev); - struct resource *r; - - /* Fill out IRQ field */ - fep->interrupt = platform_get_irq_byname(pdev, "interrupt"); - if (fep->interrupt < 0) - return -EINVAL; - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); - fep->scc.sccp = ioremap(r->start, r->end - r->start + 1); - - if (fep->scc.sccp == NULL) - return -EINVAL; - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pram"); - fep->scc.ep = ioremap(r->start, r->end - r->start + 1); - - if (fep->scc.ep == NULL) - return -EINVAL; -#endif return 0; } @@ -149,16 +124,6 @@ static int setup_data(struct net_device *dev) { struct fs_enet_private *fep = netdev_priv(dev); -#ifndef CONFIG_PPC_CPM_NEW_BINDING - struct fs_platform_info *fpi = fep->fpi; - - fep->scc.idx = fs_get_scc_index(fpi->fs_no); - if ((unsigned int)fep->fcc.idx >= 4) /* max 4 SCCs */ - return -EINVAL; - - fpi->cp_command = fep->fcc.idx << 6; -#endif - do_pd_setup(fep); fep->scc.hthi = 0; diff --git a/drivers/net/fs_enet/mii-bitbang.c b/drivers/net/fs_enet/mii-bitbang.c index 1620030cd33c..be4b72f4f49a 100644 --- a/drivers/net/fs_enet/mii-bitbang.c +++ b/drivers/net/fs_enet/mii-bitbang.c @@ -22,10 +22,7 @@ #include <linux/mii.h> #include <linux/platform_device.h> #include <linux/mdio-bitbang.h> - -#ifdef CONFIG_PPC_CPM_NEW_BINDING #include <linux/of_platform.h> -#endif #include "fs_enet.h" @@ -110,7 +107,6 @@ static struct mdiobb_ops bb_ops = { .get_mdio_data = mdio_read, }; -#ifdef CONFIG_PPC_CPM_NEW_BINDING static int __devinit fs_mii_bitbang_init(struct mii_bus *bus, struct device_node *np) { @@ -271,106 +267,3 @@ static void fs_enet_mdio_bb_exit(void) module_init(fs_enet_mdio_bb_init); module_exit(fs_enet_mdio_bb_exit); -#else -static int __devinit fs_mii_bitbang_init(struct bb_info *bitbang, - struct fs_mii_bb_platform_info *fmpi) -{ - bitbang->dir = (u32 __iomem *)fmpi->mdio_dir.offset; - bitbang->dat = (u32 __iomem *)fmpi->mdio_dat.offset; - bitbang->mdio_msk = 1U << (31 - fmpi->mdio_dat.bit); - bitbang->mdc_msk = 1U << (31 - fmpi->mdc_dat.bit); - - return 0; -} - -static int __devinit fs_enet_mdio_probe(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct fs_mii_bb_platform_info *pdata; - struct mii_bus *new_bus; - struct bb_info *bitbang; - int err = 0; - - if (NULL == dev) - return -EINVAL; - - bitbang = kzalloc(sizeof(struct bb_info), GFP_KERNEL); - - if (NULL == bitbang) - return -ENOMEM; - - bitbang->ctrl.ops = &bb_ops; - - new_bus = alloc_mdio_bitbang(&bitbang->ctrl); - - if (NULL == new_bus) - return -ENOMEM; - - new_bus->name = "BB MII Bus", - snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id); - - new_bus->phy_mask = ~0x9; - pdata = (struct fs_mii_bb_platform_info *)pdev->dev.platform_data; - - if (NULL == pdata) { - printk(KERN_ERR "gfar mdio %d: Missing platform data!\n", pdev->id); - return -ENODEV; - } - - /*set up workspace*/ - fs_mii_bitbang_init(bitbang, pdata); - - new_bus->priv = bitbang; - - new_bus->irq = pdata->irq; - - new_bus->dev = dev; - dev_set_drvdata(dev, new_bus); - - 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: - free_mdio_bitbang(new_bus); - kfree(bitbang); - - return err; -} - -static int fs_enet_mdio_remove(struct device *dev) -{ - struct mii_bus *bus = dev_get_drvdata(dev); - - mdiobus_unregister(bus); - - dev_set_drvdata(dev, NULL); - - free_mdio_bitbang(bus); - - return 0; -} - -static struct device_driver fs_enet_bb_mdio_driver = { - .name = "fsl-bb-mdio", - .bus = &platform_bus_type, - .probe = fs_enet_mdio_probe, - .remove = fs_enet_mdio_remove, -}; - -int fs_enet_mdio_bb_init(void) -{ - return driver_register(&fs_enet_bb_mdio_driver); -} - -void fs_enet_mdio_bb_exit(void) -{ - driver_unregister(&fs_enet_bb_mdio_driver); -} -#endif diff --git a/drivers/net/fs_enet/mii-fec.c b/drivers/net/fs_enet/mii-fec.c index 8f6a43b0e0ff..695f74cc4398 100644 --- a/drivers/net/fs_enet/mii-fec.c +++ b/drivers/net/fs_enet/mii-fec.c @@ -31,15 +31,12 @@ #include <linux/ethtool.h> #include <linux/bitops.h> #include <linux/platform_device.h> +#include <linux/of_platform.h> #include <asm/pgtable.h> #include <asm/irq.h> #include <asm/uaccess.h> -#ifdef CONFIG_PPC_CPM_NEW_BINDING -#include <linux/of_platform.h> -#endif - #include "fs_enet.h" #include "fec.h" @@ -51,52 +48,6 @@ #define FEC_MII_LOOPS 10000 -#ifndef CONFIG_PPC_CPM_NEW_BINDING -static int match_has_phy (struct device *dev, void* data) -{ - struct platform_device* pdev = container_of(dev, struct platform_device, dev); - struct fs_platform_info* fpi; - if(strcmp(pdev->name, (char*)data)) - { - return 0; - } - - fpi = pdev->dev.platform_data; - if((fpi)&&(fpi->has_phy)) - return 1; - return 0; -} - -static int fs_mii_fec_init(struct fec_info* fec, struct fs_mii_fec_platform_info *fmpi) -{ - struct resource *r; - fec_t __iomem *fecp; - char* name = "fsl-cpm-fec"; - - /* we need fec in order to be useful */ - struct platform_device *fec_pdev = - container_of(bus_find_device(&platform_bus_type, NULL, name, match_has_phy), - struct platform_device, dev); - - if(fec_pdev == NULL) { - printk(KERN_ERR"Unable to find PHY for %s", name); - return -ENODEV; - } - - r = platform_get_resource_byname(fec_pdev, IORESOURCE_MEM, "regs"); - - fec->fecp = fecp = ioremap(r->start,sizeof(fec_t)); - fec->mii_speed = fmpi->mii_speed; - - setbits32(&fecp->fec_r_cntrl, FEC_RCNTRL_MII_MODE); /* MII enable */ - setbits32(&fecp->fec_ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN); - out_be32(&fecp->fec_ievent, FEC_ENET_MII); - out_be32(&fecp->fec_mii_speed, fec->mii_speed); - - return 0; -} -#endif - static int fs_enet_fec_mii_read(struct mii_bus *bus , int phy_id, int location) { struct fec_info* fec = bus->priv; @@ -151,7 +102,6 @@ static int fs_enet_fec_mii_reset(struct mii_bus *bus) return 0; } -#ifdef CONFIG_PPC_CPM_NEW_BINDING static void __devinit add_phy(struct mii_bus *bus, struct device_node *np) { const u32 *data; @@ -286,95 +236,3 @@ static void fs_enet_mdio_fec_exit(void) module_init(fs_enet_mdio_fec_init); module_exit(fs_enet_mdio_fec_exit); -#else -static int __devinit fs_enet_fec_mdio_probe(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct fs_mii_fec_platform_info *pdata; - struct mii_bus *new_bus; - struct fec_info *fec; - int err = 0; - if (NULL == dev) - return -EINVAL; - new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL); - - if (NULL == new_bus) - return -ENOMEM; - - fec = kzalloc(sizeof(struct fec_info), GFP_KERNEL); - - if (NULL == fec) - return -ENOMEM; - - new_bus->name = "FEC MII Bus", - new_bus->read = &fs_enet_fec_mii_read, - new_bus->write = &fs_enet_fec_mii_write, - new_bus->reset = &fs_enet_fec_mii_reset, - snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id); - - pdata = (struct fs_mii_fec_platform_info *)pdev->dev.platform_data; - - if (NULL == pdata) { - printk(KERN_ERR "fs_enet FEC mdio %d: Missing platform data!\n", pdev->id); - return -ENODEV; - } - - /*set up workspace*/ - - fs_mii_fec_init(fec, pdata); - new_bus->priv = fec; - - new_bus->irq = pdata->irq; - - new_bus->dev = dev; - dev_set_drvdata(dev, new_bus); - - 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: - kfree(new_bus); - - return err; -} - - -static int fs_enet_fec_mdio_remove(struct device *dev) -{ - struct mii_bus *bus = dev_get_drvdata(dev); - - mdiobus_unregister(bus); - - dev_set_drvdata(dev, NULL); - kfree(bus->priv); - - bus->priv = NULL; - kfree(bus); - - return 0; -} - -static struct device_driver fs_enet_fec_mdio_driver = { - .name = "fsl-cpm-fec-mdio", - .bus = &platform_bus_type, - .probe = fs_enet_fec_mdio_probe, - .remove = fs_enet_fec_mdio_remove, -}; - -int fs_enet_mdio_fec_init(void) -{ - return driver_register(&fs_enet_fec_mdio_driver); -} - -void fs_enet_mdio_fec_exit(void) -{ - driver_unregister(&fs_enet_fec_mdio_driver); -} -#endif diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 45a63172852f..b8394cf134e8 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -134,6 +134,9 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int l static void gfar_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp); void gfar_halt(struct net_device *dev); +#ifdef CONFIG_PM +static void gfar_halt_nodisable(struct net_device *dev); +#endif void gfar_start(struct net_device *dev); static void gfar_clear_exact_match(struct net_device *dev); static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr); @@ -207,6 +210,7 @@ static int gfar_probe(struct platform_device *pdev) spin_lock_init(&priv->txlock); spin_lock_init(&priv->rxlock); + spin_lock_init(&priv->bflock); platform_set_drvdata(pdev, dev); @@ -378,6 +382,103 @@ static int gfar_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int gfar_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct gfar_private *priv = netdev_priv(dev); + unsigned long flags; + u32 tempval; + + int magic_packet = priv->wol_en && + (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + + netif_device_detach(dev); + + if (netif_running(dev)) { + spin_lock_irqsave(&priv->txlock, flags); + spin_lock(&priv->rxlock); + + gfar_halt_nodisable(dev); + + /* Disable Tx, and Rx if wake-on-LAN is disabled. */ + tempval = gfar_read(&priv->regs->maccfg1); + + tempval &= ~MACCFG1_TX_EN; + + if (!magic_packet) + tempval &= ~MACCFG1_RX_EN; + + gfar_write(&priv->regs->maccfg1, tempval); + + spin_unlock(&priv->rxlock); + spin_unlock_irqrestore(&priv->txlock, flags); + +#ifdef CONFIG_GFAR_NAPI + napi_disable(&priv->napi); +#endif + + if (magic_packet) { + /* Enable interrupt on Magic Packet */ + gfar_write(&priv->regs->imask, IMASK_MAG); + + /* Enable Magic Packet mode */ + tempval = gfar_read(&priv->regs->maccfg2); + tempval |= MACCFG2_MPEN; + gfar_write(&priv->regs->maccfg2, tempval); + } else { + phy_stop(priv->phydev); + } + } + + return 0; +} + +static int gfar_resume(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct gfar_private *priv = netdev_priv(dev); + unsigned long flags; + u32 tempval; + int magic_packet = priv->wol_en && + (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + + if (!netif_running(dev)) { + netif_device_attach(dev); + return 0; + } + + if (!magic_packet && priv->phydev) + phy_start(priv->phydev); + + /* Disable Magic Packet mode, in case something + * else woke us up. + */ + + spin_lock_irqsave(&priv->txlock, flags); + spin_lock(&priv->rxlock); + + tempval = gfar_read(&priv->regs->maccfg2); + tempval &= ~MACCFG2_MPEN; + gfar_write(&priv->regs->maccfg2, tempval); + + gfar_start(dev); + + spin_unlock(&priv->rxlock); + spin_unlock_irqrestore(&priv->txlock, flags); + + netif_device_attach(dev); + +#ifdef CONFIG_GFAR_NAPI + napi_enable(&priv->napi); +#endif + + return 0; +} +#else +#define gfar_suspend NULL +#define gfar_resume NULL +#endif /* Reads the controller's registers to determine what interface * connects it to the PHY. @@ -534,8 +635,9 @@ static void init_registers(struct net_device *dev) } +#ifdef CONFIG_PM /* Halt the receive and transmit queues */ -void gfar_halt(struct net_device *dev) +static void gfar_halt_nodisable(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); struct gfar __iomem *regs = priv->regs; @@ -558,6 +660,15 @@ void gfar_halt(struct net_device *dev) (IEVENT_GRSC | IEVENT_GTSC))) cpu_relax(); } +} +#endif + +/* Halt the receive and transmit queues */ +void gfar_halt(struct net_device *dev) +{ + struct gfar_private *priv = netdev_priv(dev); + struct gfar __iomem *regs = priv->regs; + u32 tempval; /* Disable Rx and Tx */ tempval = gfar_read(®s->maccfg1); @@ -1725,7 +1836,6 @@ static void adjust_link(struct net_device *dev) if (!priv->oldlink) { new_state = 1; priv->oldlink = 1; - netif_tx_schedule_all(dev); } } else if (priv->oldlink) { new_state = 1; @@ -1910,7 +2020,12 @@ static irqreturn_t gfar_error(int irq, void *dev_id) u32 events = gfar_read(&priv->regs->ievent); /* Clear IEVENT */ - gfar_write(&priv->regs->ievent, IEVENT_ERR_MASK); + gfar_write(&priv->regs->ievent, events & IEVENT_ERR_MASK); + + /* Magic Packet is not an error. */ + if ((priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) && + (events & IEVENT_MAG)) + events &= ~IEVENT_MAG; /* Hmm... */ if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv)) @@ -1978,6 +2093,8 @@ MODULE_ALIAS("platform:fsl-gianfar"); static struct platform_driver gfar_driver = { .probe = gfar_probe, .remove = gfar_remove, + .suspend = gfar_suspend, + .resume = gfar_resume, .driver = { .name = "fsl-gianfar", .owner = THIS_MODULE, diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index bead71cb2b16..d59df98bd636 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h @@ -157,6 +157,7 @@ extern const char gfar_driver_version[]; #define MACCFG2_GMII 0x00000200 #define MACCFG2_HUGEFRAME 0x00000020 #define MACCFG2_LENGTHCHECK 0x00000010 +#define MACCFG2_MPEN 0x00000008 #define ECNTRL_INIT_SETTINGS 0x00001000 #define ECNTRL_TBI_MODE 0x00000020 @@ -229,6 +230,7 @@ extern const char gfar_driver_version[]; #define IEVENT_CRL 0x00020000 #define IEVENT_XFUN 0x00010000 #define IEVENT_RXB0 0x00008000 +#define IEVENT_MAG 0x00000800 #define IEVENT_GRSC 0x00000100 #define IEVENT_RXF0 0x00000080 #define IEVENT_FIR 0x00000008 @@ -241,7 +243,8 @@ extern const char gfar_driver_version[]; #define IEVENT_ERR_MASK \ (IEVENT_RXC | IEVENT_BSY | IEVENT_EBERR | IEVENT_MSRO | \ IEVENT_BABT | IEVENT_TXC | IEVENT_TXE | IEVENT_LC \ - | IEVENT_CRL | IEVENT_XFUN | IEVENT_DPE | IEVENT_PERR) + | IEVENT_CRL | IEVENT_XFUN | IEVENT_DPE | IEVENT_PERR \ + | IEVENT_MAG) #define IMASK_INIT_CLEAR 0x00000000 #define IMASK_BABR 0x80000000 @@ -259,6 +262,7 @@ extern const char gfar_driver_version[]; #define IMASK_CRL 0x00020000 #define IMASK_XFUN 0x00010000 #define IMASK_RXB0 0x00008000 +#define IMASK_MAG 0x00000800 #define IMASK_GTSC 0x00000100 #define IMASK_RXFEN0 0x00000080 #define IMASK_FIR 0x00000008 @@ -726,10 +730,14 @@ struct gfar_private { unsigned int fifo_starve; unsigned int fifo_starve_off; + /* Bitfield update lock */ + spinlock_t bflock; + unsigned char vlan_enable:1, rx_csum_enable:1, extended_hash:1, - bd_stash_en:1; + bd_stash_en:1, + wol_en:1; /* Wake-on-LAN enabled */ unsigned short padding; unsigned int interruptTransmit; diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c index 6007147cc1e9..fb7d3ccc0fdc 100644 --- a/drivers/net/gianfar_ethtool.c +++ b/drivers/net/gianfar_ethtool.c @@ -479,14 +479,13 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva static int gfar_set_rx_csum(struct net_device *dev, uint32_t data) { struct gfar_private *priv = netdev_priv(dev); + unsigned long flags; int err = 0; if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) return -EOPNOTSUPP; if (dev->flags & IFF_UP) { - unsigned long flags; - /* Halt TX and RX, and process the frames which * have already been received */ spin_lock_irqsave(&priv->txlock, flags); @@ -502,7 +501,9 @@ static int gfar_set_rx_csum(struct net_device *dev, uint32_t data) stop_gfar(dev); } + spin_lock_irqsave(&priv->bflock, flags); priv->rx_csum_enable = data; + spin_unlock_irqrestore(&priv->bflock, flags); if (dev->flags & IFF_UP) err = startup_gfar(dev); @@ -564,6 +565,38 @@ static void gfar_set_msglevel(struct net_device *dev, uint32_t data) priv->msg_enable = data; } +#ifdef CONFIG_PM +static void gfar_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct gfar_private *priv = netdev_priv(dev); + + if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) { + wol->supported = WAKE_MAGIC; + wol->wolopts = priv->wol_en ? WAKE_MAGIC : 0; + } else { + wol->supported = wol->wolopts = 0; + } +} + +static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct gfar_private *priv = netdev_priv(dev); + unsigned long flags; + + if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) && + wol->wolopts != 0) + return -EINVAL; + + if (wol->wolopts & ~WAKE_MAGIC) + return -EINVAL; + + spin_lock_irqsave(&priv->bflock, flags); + priv->wol_en = wol->wolopts & WAKE_MAGIC ? 1 : 0; + spin_unlock_irqrestore(&priv->bflock, flags); + + return 0; +} +#endif const struct ethtool_ops gfar_ethtool_ops = { .get_settings = gfar_gsettings, @@ -585,4 +618,8 @@ const struct ethtool_ops gfar_ethtool_ops = { .set_tx_csum = gfar_set_tx_csum, .get_msglevel = gfar_get_msglevel, .set_msglevel = gfar_set_msglevel, +#ifdef CONFIG_PM + .get_wol = gfar_get_wol, + .set_wol = gfar_set_wol, +#endif }; diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index b6500b2aacf2..58f4b1d7bf1f 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -123,6 +123,7 @@ static LIST_HEAD(bpq_devices); * off into a separate class since they always nest. */ static struct lock_class_key bpq_netdev_xmit_lock_key; +static struct lock_class_key bpq_netdev_addr_lock_key; static void bpq_set_lockdep_class_one(struct net_device *dev, struct netdev_queue *txq, @@ -133,6 +134,7 @@ static void bpq_set_lockdep_class_one(struct net_device *dev, static void bpq_set_lockdep_class(struct net_device *dev) { + lockdep_set_class(&dev->addr_list_lock, &bpq_netdev_addr_lock_key); netdev_for_each_tx_queue(dev, bpq_set_lockdep_class_one, NULL); } diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c index ae9629fa6882..c258a0586e61 100644 --- a/drivers/net/hamradio/hdlcdrv.c +++ b/drivers/net/hamradio/hdlcdrv.c @@ -88,6 +88,7 @@ static inline void append_crc_ccitt(unsigned char *buffer, int len) { unsigned int crc = crc_ccitt(0xffff, buffer, len) ^ 0xffff; + buffer += len; *buffer++ = crc; *buffer++ = crc >> 8; } diff --git a/drivers/net/hp-plus.c b/drivers/net/hp-plus.c index c2c4f49d7578..8239939554bc 100644 --- a/drivers/net/hp-plus.c +++ b/drivers/net/hp-plus.c @@ -262,7 +262,7 @@ static int __init hpp_probe1(struct net_device *dev, int ioaddr) } outw(Perf_Page, ioaddr + HP_PAGING); - NS8390_init(dev, 0); + NS8390p_init(dev, 0); /* Leave the 8390 and HP chip reset. */ outw(inw(ioaddr + HPP_OPTION) & ~EnableIRQ, ioaddr + HPP_OPTION); diff --git a/drivers/net/hp.c b/drivers/net/hp.c index 8281209ededf..0a8c64930ad3 100644 --- a/drivers/net/hp.c +++ b/drivers/net/hp.c @@ -389,7 +389,7 @@ static void __init hp_init_card(struct net_device *dev) { int irq = dev->irq; - NS8390_init(dev, 0); + NS8390p_init(dev, 0); outb_p(irqmap[irq&0x0f] | HP_RUN, dev->base_addr - NIC_OFFSET + HP_CONFIGURE); return; diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c index 00527805e4f1..a03fe1fb61ca 100644 --- a/drivers/net/ibmveth.c +++ b/drivers/net/ibmveth.c @@ -33,6 +33,7 @@ */ #include <linux/module.h> +#include <linux/moduleparam.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/ioport.h> @@ -52,7 +53,9 @@ #include <asm/hvcall.h> #include <asm/atomic.h> #include <asm/vio.h> +#include <asm/iommu.h> #include <asm/uaccess.h> +#include <asm/firmware.h> #include <linux/seq_file.h> #include "ibmveth.h" @@ -94,8 +97,10 @@ 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 void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter); +static unsigned long ibmveth_get_desired_dma(struct vio_dev *vdev); static struct kobj_type ktype_veth_pool; + #ifdef CONFIG_PROC_FS #define IBMVETH_PROC_DIR "ibmveth" static struct proc_dir_entry *ibmveth_proc_dir; @@ -226,16 +231,16 @@ static void ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter, struc u32 i; u32 count = pool->size - atomic_read(&pool->available); u32 buffers_added = 0; + struct sk_buff *skb; + unsigned int free_index, index; + u64 correlator; + unsigned long lpar_rc; + dma_addr_t dma_addr; mb(); for(i = 0; i < count; ++i) { - struct sk_buff *skb; - unsigned int free_index, index; - u64 correlator; union ibmveth_buf_desc desc; - unsigned long lpar_rc; - dma_addr_t dma_addr; skb = alloc_skb(pool->buff_size, GFP_ATOMIC); @@ -255,6 +260,9 @@ static void ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter, struc dma_addr = dma_map_single(&adapter->vdev->dev, skb->data, pool->buff_size, DMA_FROM_DEVICE); + if (dma_mapping_error(&adapter->vdev->dev, dma_addr)) + goto failure; + pool->free_map[free_index] = IBM_VETH_INVALID_MAP; pool->dma_addr[index] = dma_addr; pool->skbuff[index] = skb; @@ -267,20 +275,9 @@ static void ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter, struc lpar_rc = h_add_logical_lan_buffer(adapter->vdev->unit_address, desc.desc); - if(lpar_rc != H_SUCCESS) { - pool->free_map[free_index] = index; - pool->skbuff[index] = NULL; - if (pool->consumer_index == 0) - pool->consumer_index = pool->size - 1; - else - pool->consumer_index--; - dma_unmap_single(&adapter->vdev->dev, - pool->dma_addr[index], pool->buff_size, - DMA_FROM_DEVICE); - dev_kfree_skb_any(skb); - adapter->replenish_add_buff_failure++; - break; - } else { + if (lpar_rc != H_SUCCESS) + goto failure; + else { buffers_added++; adapter->replenish_add_buff_success++; } @@ -288,6 +285,24 @@ static void ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter, struc mb(); atomic_add(buffers_added, &(pool->available)); + return; + +failure: + pool->free_map[free_index] = index; + pool->skbuff[index] = NULL; + if (pool->consumer_index == 0) + pool->consumer_index = pool->size - 1; + else + pool->consumer_index--; + if (!dma_mapping_error(&adapter->vdev->dev, dma_addr)) + dma_unmap_single(&adapter->vdev->dev, + pool->dma_addr[index], pool->buff_size, + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + adapter->replenish_add_buff_failure++; + + mb(); + atomic_add(buffers_added, &(pool->available)); } /* replenish routine */ @@ -297,7 +312,7 @@ static void ibmveth_replenish_task(struct ibmveth_adapter *adapter) adapter->replenish_task_cycles++; - for(i = 0; i < IbmVethNumBufferPools; i++) + for (i = (IbmVethNumBufferPools - 1); i >= 0; i--) if(adapter->rx_buff_pool[i].active) ibmveth_replenish_buffer_pool(adapter, &adapter->rx_buff_pool[i]); @@ -433,11 +448,11 @@ static void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter) static void ibmveth_cleanup(struct ibmveth_adapter *adapter) { int i; + struct device *dev = &adapter->vdev->dev; if(adapter->buffer_list_addr != NULL) { - if(!dma_mapping_error(adapter->buffer_list_dma)) { - dma_unmap_single(&adapter->vdev->dev, - adapter->buffer_list_dma, 4096, + if (!dma_mapping_error(dev, adapter->buffer_list_dma)) { + dma_unmap_single(dev, adapter->buffer_list_dma, 4096, DMA_BIDIRECTIONAL); adapter->buffer_list_dma = DMA_ERROR_CODE; } @@ -446,9 +461,8 @@ static void ibmveth_cleanup(struct ibmveth_adapter *adapter) } if(adapter->filter_list_addr != NULL) { - if(!dma_mapping_error(adapter->filter_list_dma)) { - dma_unmap_single(&adapter->vdev->dev, - adapter->filter_list_dma, 4096, + if (!dma_mapping_error(dev, adapter->filter_list_dma)) { + dma_unmap_single(dev, adapter->filter_list_dma, 4096, DMA_BIDIRECTIONAL); adapter->filter_list_dma = DMA_ERROR_CODE; } @@ -457,8 +471,8 @@ static void ibmveth_cleanup(struct ibmveth_adapter *adapter) } if(adapter->rx_queue.queue_addr != NULL) { - if(!dma_mapping_error(adapter->rx_queue.queue_dma)) { - dma_unmap_single(&adapter->vdev->dev, + if (!dma_mapping_error(dev, adapter->rx_queue.queue_dma)) { + dma_unmap_single(dev, adapter->rx_queue.queue_dma, adapter->rx_queue.queue_len, DMA_BIDIRECTIONAL); @@ -472,6 +486,18 @@ static void ibmveth_cleanup(struct ibmveth_adapter *adapter) if (adapter->rx_buff_pool[i].active) ibmveth_free_buffer_pool(adapter, &adapter->rx_buff_pool[i]); + + if (adapter->bounce_buffer != NULL) { + if (!dma_mapping_error(dev, adapter->bounce_buffer_dma)) { + dma_unmap_single(&adapter->vdev->dev, + adapter->bounce_buffer_dma, + adapter->netdev->mtu + IBMVETH_BUFF_OH, + DMA_BIDIRECTIONAL); + adapter->bounce_buffer_dma = DMA_ERROR_CODE; + } + kfree(adapter->bounce_buffer); + adapter->bounce_buffer = NULL; + } } static int ibmveth_register_logical_lan(struct ibmveth_adapter *adapter, @@ -508,6 +534,7 @@ static int ibmveth_open(struct net_device *netdev) int rc; union ibmveth_buf_desc rxq_desc; int i; + struct device *dev; ibmveth_debug_printk("open starting\n"); @@ -536,17 +563,19 @@ static int ibmveth_open(struct net_device *netdev) return -ENOMEM; } - adapter->buffer_list_dma = dma_map_single(&adapter->vdev->dev, + dev = &adapter->vdev->dev; + + adapter->buffer_list_dma = dma_map_single(dev, adapter->buffer_list_addr, 4096, DMA_BIDIRECTIONAL); - adapter->filter_list_dma = dma_map_single(&adapter->vdev->dev, + adapter->filter_list_dma = dma_map_single(dev, adapter->filter_list_addr, 4096, DMA_BIDIRECTIONAL); - adapter->rx_queue.queue_dma = dma_map_single(&adapter->vdev->dev, + adapter->rx_queue.queue_dma = dma_map_single(dev, adapter->rx_queue.queue_addr, adapter->rx_queue.queue_len, DMA_BIDIRECTIONAL); - if((dma_mapping_error(adapter->buffer_list_dma) ) || - (dma_mapping_error(adapter->filter_list_dma)) || - (dma_mapping_error(adapter->rx_queue.queue_dma))) { + if ((dma_mapping_error(dev, adapter->buffer_list_dma)) || + (dma_mapping_error(dev, adapter->filter_list_dma)) || + (dma_mapping_error(dev, adapter->rx_queue.queue_dma))) { ibmveth_error_printk("unable to map filter or buffer list pages\n"); ibmveth_cleanup(adapter); napi_disable(&adapter->napi); @@ -607,6 +636,24 @@ static int ibmveth_open(struct net_device *netdev) return rc; } + adapter->bounce_buffer = + kmalloc(netdev->mtu + IBMVETH_BUFF_OH, GFP_KERNEL); + if (!adapter->bounce_buffer) { + ibmveth_error_printk("unable to allocate bounce buffer\n"); + ibmveth_cleanup(adapter); + napi_disable(&adapter->napi); + return -ENOMEM; + } + adapter->bounce_buffer_dma = + dma_map_single(&adapter->vdev->dev, adapter->bounce_buffer, + netdev->mtu + IBMVETH_BUFF_OH, DMA_BIDIRECTIONAL); + if (dma_mapping_error(dev, adapter->bounce_buffer_dma)) { + ibmveth_error_printk("unable to map bounce buffer\n"); + ibmveth_cleanup(adapter); + napi_disable(&adapter->napi); + return -ENOMEM; + } + ibmveth_debug_printk("initial replenish cycle\n"); ibmveth_interrupt(netdev->irq, netdev); @@ -853,10 +900,12 @@ static int ibmveth_start_xmit(struct sk_buff *skb, struct net_device *netdev) unsigned int tx_packets = 0; unsigned int tx_send_failed = 0; unsigned int tx_map_failed = 0; + int used_bounce = 0; + unsigned long data_dma_addr; desc.fields.flags_len = IBMVETH_BUF_VALID | skb->len; - desc.fields.address = dma_map_single(&adapter->vdev->dev, skb->data, - skb->len, DMA_TO_DEVICE); + data_dma_addr = dma_map_single(&adapter->vdev->dev, skb->data, + skb->len, DMA_TO_DEVICE); if (skb->ip_summed == CHECKSUM_PARTIAL && ip_hdr(skb)->protocol != IPPROTO_TCP && skb_checksum_help(skb)) { @@ -875,12 +924,16 @@ static int ibmveth_start_xmit(struct sk_buff *skb, struct net_device *netdev) buf[1] = 0; } - if (dma_mapping_error(desc.fields.address)) { - ibmveth_error_printk("tx: unable to map xmit buffer\n"); + if (dma_mapping_error(&adapter->vdev->dev, data_dma_addr)) { + if (!firmware_has_feature(FW_FEATURE_CMO)) + ibmveth_error_printk("tx: unable to map xmit buffer\n"); + skb_copy_from_linear_data(skb, adapter->bounce_buffer, + skb->len); + desc.fields.address = adapter->bounce_buffer_dma; tx_map_failed++; - tx_dropped++; - goto out; - } + used_bounce = 1; + } else + desc.fields.address = data_dma_addr; /* send the frame. Arbitrarily set retrycount to 1024 */ correlator = 0; @@ -904,8 +957,9 @@ static int ibmveth_start_xmit(struct sk_buff *skb, struct net_device *netdev) netdev->trans_start = jiffies; } - dma_unmap_single(&adapter->vdev->dev, desc.fields.address, - skb->len, DMA_TO_DEVICE); + if (!used_bounce) + dma_unmap_single(&adapter->vdev->dev, data_dma_addr, + skb->len, DMA_TO_DEVICE); out: spin_lock_irqsave(&adapter->stats_lock, flags); netdev->stats.tx_dropped += tx_dropped; @@ -1053,9 +1107,9 @@ static void ibmveth_set_multicast_list(struct net_device *netdev) static int ibmveth_change_mtu(struct net_device *dev, int new_mtu) { struct ibmveth_adapter *adapter = dev->priv; + struct vio_dev *viodev = adapter->vdev; int new_mtu_oh = new_mtu + IBMVETH_BUFF_OH; - int reinit = 0; - int i, rc; + int i; if (new_mtu < IBMVETH_MAX_MTU) return -EINVAL; @@ -1067,23 +1121,34 @@ static int ibmveth_change_mtu(struct net_device *dev, int new_mtu) if (i == IbmVethNumBufferPools) return -EINVAL; + /* Deactivate all the buffer pools so that the next loop can activate + only the buffer pools necessary to hold the new MTU */ + for (i = 0; i < IbmVethNumBufferPools; i++) + if (adapter->rx_buff_pool[i].active) { + ibmveth_free_buffer_pool(adapter, + &adapter->rx_buff_pool[i]); + adapter->rx_buff_pool[i].active = 0; + } + /* Look for an active buffer pool that can hold the new MTU */ for(i = 0; i<IbmVethNumBufferPools; i++) { - if (!adapter->rx_buff_pool[i].active) { - adapter->rx_buff_pool[i].active = 1; - reinit = 1; - } + adapter->rx_buff_pool[i].active = 1; if (new_mtu_oh < adapter->rx_buff_pool[i].buff_size) { - if (reinit && netif_running(adapter->netdev)) { + if (netif_running(adapter->netdev)) { adapter->pool_config = 1; ibmveth_close(adapter->netdev); adapter->pool_config = 0; dev->mtu = new_mtu; - if ((rc = ibmveth_open(adapter->netdev))) - return rc; - } else - dev->mtu = new_mtu; + vio_cmo_set_dev_desired(viodev, + ibmveth_get_desired_dma + (viodev)); + return ibmveth_open(adapter->netdev); + } + dev->mtu = new_mtu; + vio_cmo_set_dev_desired(viodev, + ibmveth_get_desired_dma + (viodev)); return 0; } } @@ -1098,6 +1163,46 @@ static void ibmveth_poll_controller(struct net_device *dev) } #endif +/** + * ibmveth_get_desired_dma - Calculate IO memory desired by the driver + * + * @vdev: struct vio_dev for the device whose desired IO mem is to be returned + * + * Return value: + * Number of bytes of IO data the driver will need to perform well. + */ +static unsigned long ibmveth_get_desired_dma(struct vio_dev *vdev) +{ + struct net_device *netdev = dev_get_drvdata(&vdev->dev); + struct ibmveth_adapter *adapter; + unsigned long ret; + int i; + int rxqentries = 1; + + /* netdev inits at probe time along with the structures we need below*/ + if (netdev == NULL) + return IOMMU_PAGE_ALIGN(IBMVETH_IO_ENTITLEMENT_DEFAULT); + + adapter = netdev_priv(netdev); + + ret = IBMVETH_BUFF_LIST_SIZE + IBMVETH_FILT_LIST_SIZE; + ret += IOMMU_PAGE_ALIGN(netdev->mtu); + + for (i = 0; i < IbmVethNumBufferPools; i++) { + /* add the size of the active receive buffers */ + if (adapter->rx_buff_pool[i].active) + ret += + adapter->rx_buff_pool[i].size * + IOMMU_PAGE_ALIGN(adapter->rx_buff_pool[i]. + buff_size); + rxqentries += adapter->rx_buff_pool[i].size; + } + /* add the size of the receive queue entries */ + ret += IOMMU_PAGE_ALIGN(rxqentries * sizeof(struct ibmveth_rx_q_entry)); + + return ret; +} + static int __devinit ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id) { int rc, i; @@ -1242,6 +1347,8 @@ static int __devexit ibmveth_remove(struct vio_dev *dev) ibmveth_proc_unregister_adapter(adapter); free_netdev(netdev); + dev_set_drvdata(&dev->dev, NULL); + return 0; } @@ -1402,14 +1509,15 @@ const char * buf, size_t count) return -EPERM; } - pool->active = 0; if (netif_running(netdev)) { adapter->pool_config = 1; ibmveth_close(netdev); + pool->active = 0; adapter->pool_config = 0; if ((rc = ibmveth_open(netdev))) return rc; } + pool->active = 0; } } else if (attr == &veth_num_attr) { if (value <= 0 || value > IBMVETH_MAX_POOL_COUNT) @@ -1485,6 +1593,7 @@ static struct vio_driver ibmveth_driver = { .id_table = ibmveth_device_table, .probe = ibmveth_probe, .remove = ibmveth_remove, + .get_desired_dma = ibmveth_get_desired_dma, .driver = { .name = ibmveth_driver_name, .owner = THIS_MODULE, diff --git a/drivers/net/ibmveth.h b/drivers/net/ibmveth.h index 41f61cd18852..d28186948752 100644 --- a/drivers/net/ibmveth.h +++ b/drivers/net/ibmveth.h @@ -93,9 +93,12 @@ static inline long h_illan_attributes(unsigned long unit_address, plpar_hcall_norets(H_CHANGE_LOGICAL_LAN_MAC, ua, mac) #define IbmVethNumBufferPools 5 +#define IBMVETH_IO_ENTITLEMENT_DEFAULT 4243456 /* MTU of 1500 needs 4.2Mb */ #define IBMVETH_BUFF_OH 22 /* Overhead: 14 ethernet header + 8 opaque handle */ #define IBMVETH_MAX_MTU 68 #define IBMVETH_MAX_POOL_COUNT 4096 +#define IBMVETH_BUFF_LIST_SIZE 4096 +#define IBMVETH_FILT_LIST_SIZE 4096 #define IBMVETH_MAX_BUF_SIZE (1024 * 128) static int pool_size[] = { 512, 1024 * 2, 1024 * 16, 1024 * 32, 1024 * 64 }; @@ -143,6 +146,8 @@ struct ibmveth_adapter { struct ibmveth_rx_q rx_queue; int pool_config; int rx_csum; + void *bounce_buffer; + dma_addr_t bounce_buffer_dma; /* adapter specific stats */ u64 replenish_task_cycles; diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index 1b7cb29fe68e..b602c4dd0d14 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -385,7 +385,7 @@ static void igb_configure_msix(struct igb_adapter *adapter) for (i = 0; i < adapter->num_rx_queues; i++) { struct igb_ring *rx_ring = &adapter->rx_ring[i]; - rx_ring->buddy = 0; + rx_ring->buddy = NULL; igb_assign_vector(adapter, i, IGB_N0_QUEUE, vector++); adapter->eims_enable_mask |= rx_ring->eims_value; if (rx_ring->itr_val) diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index b8d0639c1cdf..c46864d626b2 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -1128,7 +1128,7 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp, msg->data.addr[0] = dma_map_single(port->dev, skb->data, skb->len, DMA_TO_DEVICE); - if (dma_mapping_error(msg->data.addr[0])) + if (dma_mapping_error(port->dev, msg->data.addr[0])) goto recycle_and_drop; msg->dev = port->dev; @@ -1226,7 +1226,7 @@ static void veth_recycle_msg(struct veth_lpar_connection *cnx, dma_address = msg->data.addr[0]; dma_length = msg->data.len[0]; - if (!dma_mapping_error(dma_address)) + if (!dma_mapping_error(msg->dev, dma_address)) dma_unmap_single(msg->dev, dma_address, dma_length, DMA_TO_DEVICE); diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index be7b723c924f..e5f3da8468cc 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -70,8 +70,6 @@ static struct pci_device_id ixgbe_pci_tbl[] = { board_82598 }, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AF_SINGLE_PORT), board_82598 }, - {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AT_DUAL_PORT), - board_82598 }, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_CX4), board_82598 }, diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 0496d16f9de5..daba82bbcb56 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -164,9 +164,7 @@ static void macb_handle_link_change(struct net_device *dev) } if (phydev->link != bp->link) { - if (phydev->link) - netif_tx_schedule_all(dev); - else { + if (!phydev->link) { bp->speed = 0; bp->duplex = -1; } diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index efbc15567dd3..42394505bb50 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -276,6 +276,7 @@ static int macvlan_change_mtu(struct net_device *dev, int new_mtu) * separate class since they always nest. */ static struct lock_class_key macvlan_netdev_xmit_lock_key; +static struct lock_class_key macvlan_netdev_addr_lock_key; #define MACVLAN_FEATURES \ (NETIF_F_SG | NETIF_F_ALL_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \ @@ -295,6 +296,8 @@ static void macvlan_set_lockdep_class_one(struct net_device *dev, static void macvlan_set_lockdep_class(struct net_device *dev) { + lockdep_set_class(&dev->addr_list_lock, + &macvlan_netdev_addr_lock_key); netdev_for_each_tx_queue(dev, macvlan_set_lockdep_class_one, NULL); } diff --git a/drivers/net/meth.c b/drivers/net/meth.c index 0b32648a2136..4cb364e67dc6 100644 --- a/drivers/net/meth.c +++ b/drivers/net/meth.c @@ -287,7 +287,7 @@ int meth_reset(struct net_device *dev) /* Initial mode: 10 | Half-duplex | Accept normal packets */ priv->mac_ctrl = METH_ACCEPT_MCAST | METH_DEFAULT_IPG; - if (dev->flags | IFF_PROMISC) + if (dev->flags & IFF_PROMISC) priv->mac_ctrl |= METH_PROMISC; mace->eth.mac_ctrl = priv->mac_ctrl; diff --git a/drivers/net/mlx4/alloc.c b/drivers/net/mlx4/alloc.c index f9d6b4dca180..096bca54bcf7 100644 --- a/drivers/net/mlx4/alloc.c +++ b/drivers/net/mlx4/alloc.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2007, 2008 Mellanox Technologies. 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/mlx4/catas.c b/drivers/net/mlx4/catas.c index aa9528779044..f094ee00c416 100644 --- a/drivers/net/mlx4/catas.c +++ b/drivers/net/mlx4/catas.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2007, 2008 Mellanox Technologies. 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/mlx4/cmd.c b/drivers/net/mlx4/cmd.c index 70dff94a8bc6..2845a0560b84 100644 --- a/drivers/net/mlx4/cmd.c +++ b/drivers/net/mlx4/cmd.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. - * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved. * Copyright (c) 2005, 2006, 2007 Cisco Systems, Inc. All rights reserved. * * This software is available to you under a choice of one of two @@ -67,6 +67,8 @@ enum { CMD_STAT_BAD_INDEX = 0x0a, /* FW image corrupted: */ CMD_STAT_BAD_NVMEM = 0x0b, + /* Error in ICM mapping (e.g. not enough auxiliary ICM pages to execute command): */ + CMD_STAT_ICM_ERROR = 0x0c, /* Attempt to modify a QP/EE which is not in the presumed state: */ CMD_STAT_BAD_QP_STATE = 0x10, /* Bad segment parameters (Address/Size): */ @@ -119,6 +121,7 @@ static int mlx4_status_to_errno(u8 status) [CMD_STAT_BAD_RES_STATE] = -EBADF, [CMD_STAT_BAD_INDEX] = -EBADF, [CMD_STAT_BAD_NVMEM] = -EFAULT, + [CMD_STAT_ICM_ERROR] = -ENFILE, [CMD_STAT_BAD_QP_STATE] = -EINVAL, [CMD_STAT_BAD_SEG_PARAM] = -EFAULT, [CMD_STAT_REG_BOUND] = -EBUSY, diff --git a/drivers/net/mlx4/cq.c b/drivers/net/mlx4/cq.c index 95e87a2f8896..9bb50e3f8974 100644 --- a/drivers/net/mlx4/cq.c +++ b/drivers/net/mlx4/cq.c @@ -2,7 +2,7 @@ * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * Copyright (c) 2005, 2006, 2007 Cisco Systems, Inc. All rights reserved. - * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved. * Copyright (c) 2004 Voltaire, Inc. All rights reserved. * * This software is available to you under a choice of one of two diff --git a/drivers/net/mlx4/eq.c b/drivers/net/mlx4/eq.c index e141a1513f07..8a8b56135a58 100644 --- a/drivers/net/mlx4/eq.c +++ b/drivers/net/mlx4/eq.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved. * Copyright (c) 2005, 2006, 2007 Cisco Systems, Inc. All rights reserved. * * This software is available to you under a choice of one of two @@ -33,6 +33,7 @@ #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/mm.h> #include <linux/dma-mapping.h> #include <linux/mlx4/cmd.h> @@ -525,7 +526,7 @@ int mlx4_map_eq_icm(struct mlx4_dev *dev, u64 icm_virt) return -ENOMEM; priv->eq_table.icm_dma = pci_map_page(dev->pdev, priv->eq_table.icm_page, 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(priv->eq_table.icm_dma)) { + if (pci_dma_mapping_error(dev->pdev, priv->eq_table.icm_dma)) { __free_page(priv->eq_table.icm_page); return -ENOMEM; } diff --git a/drivers/net/mlx4/fw.c b/drivers/net/mlx4/fw.c index 2b5006b9be67..7e32955da982 100644 --- a/drivers/net/mlx4/fw.c +++ b/drivers/net/mlx4/fw.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. - * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved. * Copyright (c) 2005, 2006, 2007 Cisco Systems, Inc. All rights reserved. * * This software is available to you under a choice of one of two @@ -46,6 +46,10 @@ enum { extern void __buggy_use_of_MLX4_GET(void); extern void __buggy_use_of_MLX4_PUT(void); +static int enable_qos; +module_param(enable_qos, bool, 0444); +MODULE_PARM_DESC(enable_qos, "Enable Quality of Service support in the HCA (default: off)"); + #define MLX4_GET(dest, source, offset) \ do { \ void *__p = (char *) (source) + (offset); \ @@ -198,7 +202,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) #define QUERY_DEV_CAP_C_MPT_ENTRY_SZ_OFFSET 0x8e #define QUERY_DEV_CAP_MTT_ENTRY_SZ_OFFSET 0x90 #define QUERY_DEV_CAP_D_MPT_ENTRY_SZ_OFFSET 0x92 -#define QUERY_DEV_CAP_BMME_FLAGS_OFFSET 0x97 +#define QUERY_DEV_CAP_BMME_FLAGS_OFFSET 0x94 #define QUERY_DEV_CAP_RSVD_LKEY_OFFSET 0x98 #define QUERY_DEV_CAP_MAX_ICM_SZ_OFFSET 0xa0 @@ -373,12 +377,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) } } - if (dev_cap->bmme_flags & 1) - mlx4_dbg(dev, "Base MM extensions: yes " - "(flags %d, rsvd L_Key %08x)\n", - dev_cap->bmme_flags, dev_cap->reserved_lkey); - else - mlx4_dbg(dev, "Base MM extensions: no\n"); + mlx4_dbg(dev, "Base MM extensions: flags %08x, rsvd L_Key %08x\n", + dev_cap->bmme_flags, dev_cap->reserved_lkey); /* * Each UAR has 4 EQ doorbells; so if a UAR is reserved, then @@ -737,6 +737,10 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param) if (dev->caps.flags & MLX4_DEV_CAP_FLAG_IPOIB_CSUM) *(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1 << 3); + /* Enable QoS support if module parameter set */ + if (enable_qos) + *(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1 << 2); + /* QPC/EEC/CQC/EQC/RDMARC attributes */ MLX4_PUT(inbox, param->qpc_base, INIT_HCA_QPC_BASE_OFFSET); diff --git a/drivers/net/mlx4/fw.h b/drivers/net/mlx4/fw.h index a0e046c149b7..decbb5c2ad41 100644 --- a/drivers/net/mlx4/fw.h +++ b/drivers/net/mlx4/fw.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. - * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved. * Copyright (c) 2006, 2007 Cisco Systems. All rights reserved. * * This software is available to you under a choice of one of two @@ -98,7 +98,7 @@ struct mlx4_dev_cap { int cmpt_entry_sz; int mtt_entry_sz; int resize_srq; - u8 bmme_flags; + u32 bmme_flags; u32 reserved_lkey; u64 max_icm_sz; int max_gso_sz; diff --git a/drivers/net/mlx4/icm.c b/drivers/net/mlx4/icm.c index 2a5bef6388fe..baf4bf66062c 100644 --- a/drivers/net/mlx4/icm.c +++ b/drivers/net/mlx4/icm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved. * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved. * * This software is available to you under a choice of one of two diff --git a/drivers/net/mlx4/icm.h b/drivers/net/mlx4/icm.h index 6c44edf35847..ab56a2f89b65 100644 --- a/drivers/net/mlx4/icm.h +++ b/drivers/net/mlx4/icm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved. * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved. * * This software is available to you under a choice of one of two diff --git a/drivers/net/mlx4/intf.c b/drivers/net/mlx4/intf.c index 4a6c4d526f1b..0e7eb1038f9f 100644 --- a/drivers/net/mlx4/intf.c +++ b/drivers/net/mlx4/intf.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2007, 2008 Mellanox Technologies. 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/mlx4/main.c b/drivers/net/mlx4/main.c index d3736013fe9b..1252a919de2e 100644 --- a/drivers/net/mlx4/main.c +++ b/drivers/net/mlx4/main.c @@ -1,7 +1,7 @@ /* * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. - * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved. * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved. * * This software is available to you under a choice of one of two @@ -158,6 +158,8 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev->caps.max_msg_sz = dev_cap->max_msg_sz; dev->caps.page_size_cap = ~(u32) (dev_cap->min_page_sz - 1); dev->caps.flags = dev_cap->flags; + dev->caps.bmme_flags = dev_cap->bmme_flags; + dev->caps.reserved_lkey = dev_cap->reserved_lkey; dev->caps.stat_rate_support = dev_cap->stat_rate_support; dev->caps.max_gso_sz = dev_cap->max_gso_sz; diff --git a/drivers/net/mlx4/mcg.c b/drivers/net/mlx4/mcg.c index b4b57870ddfd..c83f88ce0736 100644 --- a/drivers/net/mlx4/mcg.c +++ b/drivers/net/mlx4/mcg.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2007, 2008 Mellanox Technologies. 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/mlx4/mlx4.h b/drivers/net/mlx4/mlx4.h index a4023c2dd050..5337e3ac3e78 100644 --- a/drivers/net/mlx4/mlx4.h +++ b/drivers/net/mlx4/mlx4.h @@ -2,7 +2,7 @@ * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * Copyright (c) 2005, 2006, 2007 Cisco Systems. All rights reserved. - * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved. * Copyright (c) 2004 Voltaire, Inc. All rights reserved. * * This software is available to you under a choice of one of two @@ -118,6 +118,7 @@ struct mlx4_bitmap { struct mlx4_buddy { unsigned long **bits; + unsigned int *num_free; int max_order; spinlock_t lock; }; diff --git a/drivers/net/mlx4/mr.c b/drivers/net/mlx4/mr.c index 03a9abcce524..62071d9c4a55 100644 --- a/drivers/net/mlx4/mr.c +++ b/drivers/net/mlx4/mr.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2004 Topspin Communications. All rights reserved. - * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved. * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved. * * This software is available to you under a choice of one of two @@ -47,7 +47,7 @@ struct mlx4_mpt_entry { __be32 flags; __be32 qpn; __be32 key; - __be32 pd; + __be32 pd_flags; __be64 start; __be64 length; __be32 lkey; @@ -61,11 +61,15 @@ struct mlx4_mpt_entry { } __attribute__((packed)); #define MLX4_MPT_FLAG_SW_OWNS (0xfUL << 28) +#define MLX4_MPT_FLAG_FREE (0x3UL << 28) #define MLX4_MPT_FLAG_MIO (1 << 17) #define MLX4_MPT_FLAG_BIND_ENABLE (1 << 15) #define MLX4_MPT_FLAG_PHYSICAL (1 << 9) #define MLX4_MPT_FLAG_REGION (1 << 8) +#define MLX4_MPT_PD_FLAG_FAST_REG (1 << 26) +#define MLX4_MPT_PD_FLAG_EN_INV (3 << 24) + #define MLX4_MTT_FLAG_PRESENT 1 #define MLX4_MPT_STATUS_SW 0xF0 @@ -79,23 +83,26 @@ static u32 mlx4_buddy_alloc(struct mlx4_buddy *buddy, int order) spin_lock(&buddy->lock); - for (o = order; o <= buddy->max_order; ++o) { - m = 1 << (buddy->max_order - o); - seg = find_first_bit(buddy->bits[o], m); - if (seg < m) - goto found; - } + for (o = order; o <= buddy->max_order; ++o) + if (buddy->num_free[o]) { + m = 1 << (buddy->max_order - o); + seg = find_first_bit(buddy->bits[o], m); + if (seg < m) + goto found; + } spin_unlock(&buddy->lock); return -1; found: clear_bit(seg, buddy->bits[o]); + --buddy->num_free[o]; while (o > order) { --o; seg <<= 1; set_bit(seg ^ 1, buddy->bits[o]); + ++buddy->num_free[o]; } spin_unlock(&buddy->lock); @@ -113,11 +120,13 @@ static void mlx4_buddy_free(struct mlx4_buddy *buddy, u32 seg, int order) while (test_bit(seg ^ 1, buddy->bits[order])) { clear_bit(seg ^ 1, buddy->bits[order]); + --buddy->num_free[order]; seg >>= 1; ++order; } set_bit(seg, buddy->bits[order]); + ++buddy->num_free[order]; spin_unlock(&buddy->lock); } @@ -131,7 +140,9 @@ static int mlx4_buddy_init(struct mlx4_buddy *buddy, int max_order) buddy->bits = kzalloc((buddy->max_order + 1) * sizeof (long *), GFP_KERNEL); - if (!buddy->bits) + buddy->num_free = kzalloc((buddy->max_order + 1) * sizeof (int *), + GFP_KERNEL); + if (!buddy->bits || !buddy->num_free) goto err_out; for (i = 0; i <= buddy->max_order; ++i) { @@ -143,6 +154,7 @@ static int mlx4_buddy_init(struct mlx4_buddy *buddy, int max_order) } set_bit(0, buddy->bits[buddy->max_order]); + buddy->num_free[buddy->max_order] = 1; return 0; @@ -150,9 +162,10 @@ err_out_free: for (i = 0; i <= buddy->max_order; ++i) kfree(buddy->bits[i]); +err_out: kfree(buddy->bits); + kfree(buddy->num_free); -err_out: return -ENOMEM; } @@ -164,6 +177,7 @@ static void mlx4_buddy_cleanup(struct mlx4_buddy *buddy) kfree(buddy->bits[i]); kfree(buddy->bits); + kfree(buddy->num_free); } static u32 mlx4_alloc_mtt_range(struct mlx4_dev *dev, int order) @@ -314,21 +328,30 @@ int mlx4_mr_enable(struct mlx4_dev *dev, struct mlx4_mr *mr) memset(mpt_entry, 0, sizeof *mpt_entry); - mpt_entry->flags = cpu_to_be32(MLX4_MPT_FLAG_SW_OWNS | - MLX4_MPT_FLAG_MIO | + mpt_entry->flags = cpu_to_be32(MLX4_MPT_FLAG_MIO | MLX4_MPT_FLAG_REGION | mr->access); mpt_entry->key = cpu_to_be32(key_to_hw_index(mr->key)); - mpt_entry->pd = cpu_to_be32(mr->pd); + mpt_entry->pd_flags = cpu_to_be32(mr->pd | MLX4_MPT_PD_FLAG_EN_INV); mpt_entry->start = cpu_to_be64(mr->iova); mpt_entry->length = cpu_to_be64(mr->size); mpt_entry->entity_size = cpu_to_be32(mr->mtt.page_shift); + if (mr->mtt.order < 0) { mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_PHYSICAL); mpt_entry->mtt_seg = 0; - } else + } else { mpt_entry->mtt_seg = cpu_to_be64(mlx4_mtt_addr(dev, &mr->mtt)); + } + + if (mr->mtt.order >= 0 && mr->mtt.page_shift == 0) { + /* fast register MR in free state */ + mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_FREE); + mpt_entry->pd_flags |= cpu_to_be32(MLX4_MPT_PD_FLAG_FAST_REG); + } else { + mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_SW_OWNS); + } err = mlx4_SW2HW_MPT(dev, mailbox, key_to_hw_index(mr->key) & (dev->caps.num_mpts - 1)); diff --git a/drivers/net/mlx4/pd.c b/drivers/net/mlx4/pd.c index 3a93c5f0f7ab..aa616892d09c 100644 --- a/drivers/net/mlx4/pd.c +++ b/drivers/net/mlx4/pd.c @@ -91,6 +91,13 @@ EXPORT_SYMBOL_GPL(mlx4_uar_free); int mlx4_init_uar_table(struct mlx4_dev *dev) { + if (dev->caps.num_uars <= 128) { + mlx4_err(dev, "Only %d UAR pages (need more than 128)\n", + dev->caps.num_uars); + mlx4_err(dev, "Increase firmware log2_uar_bar_megabytes?\n"); + return -ENODEV; + } + return mlx4_bitmap_init(&mlx4_priv(dev)->uar_table.bitmap, dev->caps.num_uars, dev->caps.num_uars - 1, max(128, dev->caps.reserved_uars)); diff --git a/drivers/net/mlx4/qp.c b/drivers/net/mlx4/qp.c index ee5484c44a18..c49a86044bf7 100644 --- a/drivers/net/mlx4/qp.c +++ b/drivers/net/mlx4/qp.c @@ -1,7 +1,7 @@ /* * Copyright (c) 2004 Topspin Communications. All rights reserved. * Copyright (c) 2005, 2006, 2007 Cisco Systems, Inc. All rights reserved. - * Copyright (c) 2005 Mellanox Technologies. All rights reserved. + * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved. * Copyright (c) 2004 Voltaire, Inc. All rights reserved. * * This software is available to you under a choice of one of two diff --git a/drivers/net/mlx4/reset.c b/drivers/net/mlx4/reset.c index e199715fabd0..3951b884c0fb 100644 --- a/drivers/net/mlx4/reset.c +++ b/drivers/net/mlx4/reset.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2007, 2008 Mellanox Technologies. 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/mlx4/srq.c b/drivers/net/mlx4/srq.c index d23f46d692ef..533eb6db24b3 100644 --- a/drivers/net/mlx4/srq.c +++ b/drivers/net/mlx4/srq.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2007, 2008 Mellanox Technologies. 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/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 83a877f3a553..8a97a0066a88 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -2112,7 +2112,7 @@ static void mv643xx_eth_netpoll(struct net_device *dev) mv643xx_eth_irq(dev->irq, dev); - wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_CAUSE_EXT); + wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT); } #endif diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index b3981ed972bf..3ab0e5289f7a 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -125,7 +125,6 @@ struct myri10ge_cmd { struct myri10ge_rx_buf { struct mcp_kreq_ether_recv __iomem *lanai; /* lanai ptr for recv ring */ - u8 __iomem *wc_fifo; /* w/c rx dma addr fifo address */ struct mcp_kreq_ether_recv *shadow; /* host shadow of recv ring */ struct myri10ge_rx_buffer_state *info; struct page *page; @@ -140,7 +139,6 @@ struct myri10ge_rx_buf { struct myri10ge_tx_buf { struct mcp_kreq_ether_send __iomem *lanai; /* lanai ptr for sendq */ - u8 __iomem *wc_fifo; /* w/c send fifo address */ struct mcp_kreq_ether_send *req_list; /* host shadow of sendq */ char *req_bytes; struct myri10ge_tx_buffer_state *info; @@ -332,10 +330,6 @@ MODULE_PARM_DESC(myri10ge_fill_thresh, "Number of empty rx slots allowed"); static int myri10ge_reset_recover = 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"); - static int myri10ge_max_slices = 1; module_param(myri10ge_max_slices, int, S_IRUGO); MODULE_PARM_DESC(myri10ge_max_slices, "Max tx/rx queues"); @@ -1218,14 +1212,8 @@ myri10ge_alloc_rx_pages(struct myri10ge_priv *mgp, struct myri10ge_rx_buf *rx, /* copy 8 descriptors to the firmware at a time */ if ((idx & 7) == 7) { - if (rx->wc_fifo == NULL) - myri10ge_submit_8rx(&rx->lanai[idx - 7], - &rx->shadow[idx - 7]); - else { - mb(); - myri10ge_pio_copy(rx->wc_fifo, - &rx->shadow[idx - 7], 64); - } + myri10ge_submit_8rx(&rx->lanai[idx - 7], + &rx->shadow[idx - 7]); } } } @@ -2229,18 +2217,6 @@ static int myri10ge_get_txrx(struct myri10ge_priv *mgp, int slice) ss->rx_big.lanai = (struct mcp_kreq_ether_recv __iomem *) (mgp->sram + cmd.data0); - if (myri10ge_wcfifo && mgp->wc_enabled) { - ss->tx.wc_fifo = (u8 __iomem *) - mgp->sram + MXGEFW_ETH_SEND_4 + 64 * slice; - ss->rx_small.wc_fifo = (u8 __iomem *) - mgp->sram + MXGEFW_ETH_RECV_SMALL + 64 * slice; - ss->rx_big.wc_fifo = (u8 __iomem *) - mgp->sram + MXGEFW_ETH_RECV_BIG + 64 * slice; - } else { - ss->tx.wc_fifo = NULL; - ss->rx_small.wc_fifo = NULL; - ss->rx_big.wc_fifo = NULL; - } return status; } @@ -2573,27 +2549,6 @@ myri10ge_submit_req(struct myri10ge_tx_buf *tx, struct mcp_kreq_ether_send *src, mb(); } -static inline void -myri10ge_submit_req_wc(struct myri10ge_tx_buf *tx, - struct mcp_kreq_ether_send *src, int cnt) -{ - tx->req += cnt; - mb(); - while (cnt >= 4) { - myri10ge_pio_copy(tx->wc_fifo, src, 64); - mb(); - src += 4; - cnt -= 4; - } - if (cnt > 0) { - /* pad it to 64 bytes. The src is 64 bytes bigger than it - * needs to be so that we don't overrun it */ - myri10ge_pio_copy(tx->wc_fifo + MXGEFW_ETH_SEND_OFFSET(cnt), - src, 64); - mb(); - } -} - /* * Transmit a packet. We need to split the packet so that a single * segment does not cross myri10ge->tx_boundary, so this makes segment @@ -2830,10 +2785,7 @@ again: MXGEFW_FLAGS_FIRST))); idx = ((count - 1) + tx->req) & tx->mask; tx->info[idx].last = 1; - if (tx->wc_fifo == NULL) - myri10ge_submit_req(tx, tx->req_list, count); - else - myri10ge_submit_req_wc(tx, tx->req_list, count); + myri10ge_submit_req(tx, tx->req_list, count); tx->pkt_start++; if ((avail - count) < MXGEFW_MAX_SEND_DESC) { tx->stop_queue++; @@ -3768,14 +3720,14 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (mgp->sram_size > mgp->board_span) { dev_err(&pdev->dev, "board span %ld bytes too small\n", mgp->board_span); - goto abort_with_wc; + goto abort_with_mtrr; } - mgp->sram = ioremap(mgp->iomem_base, mgp->board_span); + mgp->sram = ioremap_wc(mgp->iomem_base, mgp->board_span); if (mgp->sram == NULL) { dev_err(&pdev->dev, "ioremap failed for %ld bytes at 0x%lx\n", mgp->board_span, mgp->iomem_base); status = -ENXIO; - goto abort_with_wc; + goto abort_with_mtrr; } memcpy_fromio(mgp->eeprom_strings, mgp->sram + mgp->sram_size - MYRI10GE_EEPROM_STRINGS_SIZE, @@ -3876,7 +3828,7 @@ abort_with_firmware: abort_with_ioremap: iounmap(mgp->sram); -abort_with_wc: +abort_with_mtrr: #ifdef CONFIG_MTRR if (mgp->mtrr >= 0) mtrr_del(mgp->mtrr, mgp->iomem_base, mgp->board_span); diff --git a/drivers/net/ne.c b/drivers/net/ne.c index 14126973bd12..2fec6122c7fa 100644 --- a/drivers/net/ne.c +++ b/drivers/net/ne.c @@ -355,7 +355,7 @@ static int __init ne_probe1(struct net_device *dev, unsigned long ioaddr) } /* Read the 16 bytes of station address PROM. - We must first initialize registers, similar to NS8390_init(eifdev, 0). + We must first initialize registers, similar to NS8390p_init(eifdev, 0). We can't reliably read the SAPROM address without this. (I learned the hard way!). */ { diff --git a/drivers/net/ne2.c b/drivers/net/ne2.c index 8f7256346922..332df75a9ab6 100644 --- a/drivers/net/ne2.c +++ b/drivers/net/ne2.c @@ -404,7 +404,7 @@ static int __init ne2_probe1(struct net_device *dev, int slot) /* Read the 16 bytes of station address PROM. We must first initialize registers, similar to - NS8390_init(eifdev, 0). + NS8390p_init(eifdev, 0). We can't reliably read the SAPROM address without this. (I learned the hard way!). */ { diff --git a/drivers/net/netxen/Makefile b/drivers/net/netxen/Makefile index a07cdc6f7384..8e7c4c910d2a 100644 --- a/drivers/net/netxen/Makefile +++ b/drivers/net/netxen/Makefile @@ -32,4 +32,4 @@ obj-$(CONFIG_NETXEN_NIC) := netxen_nic.o netxen_nic-y := netxen_nic_hw.o netxen_nic_main.o netxen_nic_init.o \ - netxen_nic_isr.o netxen_nic_ethtool.o netxen_nic_niu.o + netxen_nic_ethtool.o netxen_nic_niu.o netxen_nic_ctx.o diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h index da4c4fb97064..8e736614407d 100644 --- a/drivers/net/netxen/netxen_nic.h +++ b/drivers/net/netxen/netxen_nic.h @@ -54,6 +54,7 @@ #include <linux/mm.h> #include <linux/mman.h> +#include <linux/vmalloc.h> #include <asm/system.h> #include <asm/io.h> @@ -63,10 +64,12 @@ #include "netxen_nic_hw.h" -#define _NETXEN_NIC_LINUX_MAJOR 3 -#define _NETXEN_NIC_LINUX_MINOR 4 -#define _NETXEN_NIC_LINUX_SUBVERSION 18 -#define NETXEN_NIC_LINUX_VERSIONID "3.4.18" +#define _NETXEN_NIC_LINUX_MAJOR 4 +#define _NETXEN_NIC_LINUX_MINOR 0 +#define _NETXEN_NIC_LINUX_SUBVERSION 0 +#define NETXEN_NIC_LINUX_VERSIONID "4.0.0" + +#define NETXEN_VERSION_CODE(a, b, c) (((a) << 16) + ((b) << 8) + (c)) #define NETXEN_NUM_FLASH_SECTORS (64) #define NETXEN_FLASH_SECTOR_SIZE (64 * 1024) @@ -84,7 +87,7 @@ #define TX_RINGSIZE \ (sizeof(struct netxen_cmd_buffer) * adapter->max_tx_desc_count) #define RCV_BUFFSIZE \ - (sizeof(struct netxen_rx_buffer) * rcv_desc->max_rx_desc_count) + (sizeof(struct netxen_rx_buffer) * rds_ring->max_rx_desc_count) #define find_diff_among(a,b,range) ((a)<(b)?((b)-(a)):((b)+(range)-(a))) #define NETXEN_NETDEV_STATUS 0x1 @@ -111,6 +114,13 @@ #define NX_P2_C0 0x24 #define NX_P2_C1 0x25 +#define NX_P3_A0 0x30 +#define NX_P3_A2 0x30 +#define NX_P3_B0 0x40 +#define NX_P3_B1 0x41 + +#define NX_IS_REVISION_P2(REVISION) (REVISION <= NX_P2_C1) +#define NX_IS_REVISION_P3(REVISION) (REVISION >= NX_P3_A0) #define FIRST_PAGE_GROUP_START 0 #define FIRST_PAGE_GROUP_END 0x100000 @@ -125,6 +135,16 @@ #define SECOND_PAGE_GROUP_SIZE SECOND_PAGE_GROUP_END - SECOND_PAGE_GROUP_START #define THIRD_PAGE_GROUP_SIZE THIRD_PAGE_GROUP_END - THIRD_PAGE_GROUP_START +#define P2_MAX_MTU (8000) +#define P3_MAX_MTU (9600) +#define NX_ETHERMTU 1500 +#define NX_MAX_ETHERHDR 32 /* This contains some padding */ + +#define NX_RX_NORMAL_BUF_MAX_LEN (NX_MAX_ETHERHDR + NX_ETHERMTU) +#define NX_P2_RX_JUMBO_BUF_MAX_LEN (NX_MAX_ETHERHDR + P2_MAX_MTU) +#define NX_P3_RX_JUMBO_BUF_MAX_LEN (NX_MAX_ETHERHDR + P3_MAX_MTU) +#define NX_CT_DEFAULT_RX_BUF_LEN 2048 + #define MAX_RX_BUFFER_LENGTH 1760 #define MAX_RX_JUMBO_BUFFER_LENGTH 8062 #define MAX_RX_LRO_BUFFER_LENGTH ((48*1024)-512) @@ -132,7 +152,6 @@ #define RX_JUMBO_DMA_MAP_LEN \ (MAX_RX_JUMBO_BUFFER_LENGTH - 2) #define RX_LRO_DMA_MAP_LEN (MAX_RX_LRO_BUFFER_LENGTH - 2) -#define NETXEN_ROM_ROUNDUP 0x80000000ULL /* * Maximum number of ring contexts @@ -140,16 +159,16 @@ #define MAX_RING_CTX 1 /* Opcodes to be used with the commands */ -enum { - TX_ETHER_PKT = 0x01, -/* The following opcodes are for IP checksum */ - TX_TCP_PKT, - TX_UDP_PKT, - TX_IP_PKT, - TX_TCP_LSO, - TX_IPSEC, - TX_IPSEC_CMD -}; +#define TX_ETHER_PKT 0x01 +#define TX_TCP_PKT 0x02 +#define TX_UDP_PKT 0x03 +#define TX_IP_PKT 0x04 +#define TX_TCP_LSO 0x05 +#define TX_TCP_LSO6 0x06 +#define TX_IPSEC 0x07 +#define TX_IPSEC_CMD 0x0a +#define TX_TCPV6_PKT 0x0b +#define TX_UDPV6_PKT 0x0c /* The following opcodes are for internal consumption. */ #define NETXEN_CONTROL_OP 0x10 @@ -191,6 +210,7 @@ enum { #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_RCV_DESCRIPTORS_10G 8192 #define MAX_JUMBO_RCV_DESCRIPTORS 1024 #define MAX_LRO_RCV_DESCRIPTORS 64 #define MAX_RCVSTATUS_DESCRIPTORS MAX_RCV_DESCRIPTORS @@ -219,8 +239,6 @@ enum { #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 @@ -289,7 +307,7 @@ 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)) + ((cmd_desc)->port_ctxid |= ((var) << 4 & 0xF0)) #define netxen_set_cmd_desc_flags(cmd_desc, val) \ (cmd_desc)->flags_opcode = ((cmd_desc)->flags_opcode & \ @@ -377,8 +395,8 @@ struct rcv_desc { }; /* opcode field in status_desc */ -#define RCV_NIC_PKT (0xA) -#define STATUS_NIC_PKT ((RCV_NIC_PKT) << 12) +#define NETXEN_NIC_RXPKT_DESC 0x04 +#define NETXEN_OLD_RXPKT_DESC 0x3f /* for status field in status_desc */ #define STATUS_NEED_CKSUM (1) @@ -410,6 +428,8 @@ struct rcv_desc { (((sts_data) >> 28) & 0xFFFF) #define netxen_get_sts_prot(sts_data) \ (((sts_data) >> 44) & 0x0F) +#define netxen_get_sts_pkt_offset(sts_data) \ + (((sts_data) >> 48) & 0x1F) #define netxen_get_sts_opcode(sts_data) \ (((sts_data) >> 58) & 0x03F) @@ -424,17 +444,30 @@ struct rcv_desc { struct status_desc { /* Bit pattern: 0-3 port, 4-7 status, 8-11 type, 12-27 total_length - 28-43 reference_handle, 44-47 protocol, 48-52 unused + 28-43 reference_handle, 44-47 protocol, 48-52 pkt_offset 53-55 desc_cnt, 56-57 owner, 58-63 opcode */ __le64 status_desc_data; - __le32 hash_value; - u8 hash_type; - u8 msg_type; - u8 unused; - /* Bit pattern: 0-6 lro_count indicates frag sequence, - 7 last_frag indicates last frag */ - u8 lro; + union { + struct { + __le32 hash_value; + u8 hash_type; + u8 msg_type; + u8 unused; + union { + /* Bit pattern: 0-6 lro_count indicates frag + * sequence, 7 last_frag indicates last frag + */ + u8 lro; + + /* chained buffers */ + u8 nr_frags; + }; + }; + struct { + __le16 frag_handles[4]; + }; + }; } __attribute__ ((aligned(16))); enum { @@ -464,7 +497,20 @@ typedef enum { NETXEN_BRDTYPE_P2_SB31_10G_IMEZ = 0x000d, NETXEN_BRDTYPE_P2_SB31_10G_HMEZ = 0x000e, - NETXEN_BRDTYPE_P2_SB31_10G_CX4 = 0x000f + NETXEN_BRDTYPE_P2_SB31_10G_CX4 = 0x000f, + + NETXEN_BRDTYPE_P3_REF_QG = 0x0021, + NETXEN_BRDTYPE_P3_HMEZ = 0x0022, + NETXEN_BRDTYPE_P3_10G_CX4_LP = 0x0023, + NETXEN_BRDTYPE_P3_4_GB = 0x0024, + NETXEN_BRDTYPE_P3_IMEZ = 0x0025, + NETXEN_BRDTYPE_P3_10G_SFP_PLUS = 0x0026, + NETXEN_BRDTYPE_P3_10000_BASE_T = 0x0027, + NETXEN_BRDTYPE_P3_XG_LOM = 0x0028, + NETXEN_BRDTYPE_P3_4_GB_MM = 0x0029, + NETXEN_BRDTYPE_P3_10G_CX4 = 0x0031, + NETXEN_BRDTYPE_P3_10G_XFP = 0x0032 + } netxen_brdtype_t; typedef enum { @@ -747,6 +793,7 @@ struct netxen_cmd_buffer { /* In rx_buffer, we do not need multiple fragments as is a single buffer */ struct netxen_rx_buffer { + struct list_head list; struct sk_buff *skb; u64 dma; u16 ref_handle; @@ -765,7 +812,6 @@ struct netxen_rx_buffer { * contains interrupt info as well shared hardware info. */ struct netxen_hardware_context { - struct pci_dev *pdev; void __iomem *pci_base0; void __iomem *pci_base1; void __iomem *pci_base2; @@ -773,15 +819,20 @@ struct netxen_hardware_context { unsigned long first_page_group_start; void __iomem *db_base; unsigned long db_len; + unsigned long pci_len0; + + u8 cut_through; + int qdr_sn_window; + int ddr_mn_window; + unsigned long mn_win_crb; + unsigned long ms_win_crb; u8 revision_id; u16 board_type; struct netxen_board_info boardcfg; - u32 xg_linkup; - u32 qg_linksup; + u32 linkup; /* Address of cmd ring in Phantom */ struct cmd_desc_type0 *cmd_desc_head; - struct pci_dev *cmd_desc_pdev; dma_addr_t cmd_desc_phys_addr; struct netxen_adapter *adapter; int pci_func; @@ -813,17 +864,17 @@ struct netxen_adapter_stats { * Rcv Descriptor Context. One such per Rcv Descriptor. There may * be one Rcv Descriptor for normal packets, one for jumbo and may be others. */ -struct netxen_rcv_desc_ctx { +struct nx_host_rds_ring { u32 flags; u32 producer; - u32 rcv_pending; /* Num of bufs posted in phantom */ dma_addr_t phys_addr; - struct pci_dev *phys_pdev; + u32 crb_rcv_producer; /* reg offset */ struct rcv_desc *desc_head; /* address of rx ring in Phantom */ u32 max_rx_desc_count; u32 dma_size; u32 skb_size; struct netxen_rx_buffer *rx_buf_arr; /* rx buffers for receive */ + struct list_head free_list; int begin_alloc; }; @@ -834,17 +885,319 @@ struct netxen_rcv_desc_ctx { * present elsewhere. */ struct netxen_recv_context { - struct netxen_rcv_desc_ctx rcv_desc[NUM_RCV_DESC_RINGS]; - u32 status_rx_producer; + u32 state; + u16 context_id; + u16 virt_port; + + struct nx_host_rds_ring rds_rings[NUM_RCV_DESC_RINGS]; u32 status_rx_consumer; + u32 crb_sts_consumer; /* reg offset */ dma_addr_t rcv_status_desc_phys_addr; - struct pci_dev *rcv_status_desc_pdev; struct status_desc *rcv_status_desc_head; }; -#define NETXEN_NIC_MSI_ENABLED 0x02 -#define NETXEN_DMA_MASK 0xfffffffe -#define NETXEN_DB_MAPSIZE_BYTES 0x1000 +/* New HW context creation */ + +#define NX_OS_CRB_RETRY_COUNT 4000 +#define NX_CDRP_SIGNATURE_MAKE(pcifn, version) \ + (((pcifn) & 0xff) | (((version) & 0xff) << 8) | (0xcafe << 16)) + +#define NX_CDRP_CLEAR 0x00000000 +#define NX_CDRP_CMD_BIT 0x80000000 + +/* + * All responses must have the NX_CDRP_CMD_BIT cleared + * in the crb NX_CDRP_CRB_OFFSET. + */ +#define NX_CDRP_FORM_RSP(rsp) (rsp) +#define NX_CDRP_IS_RSP(rsp) (((rsp) & NX_CDRP_CMD_BIT) == 0) + +#define NX_CDRP_RSP_OK 0x00000001 +#define NX_CDRP_RSP_FAIL 0x00000002 +#define NX_CDRP_RSP_TIMEOUT 0x00000003 + +/* + * All commands must have the NX_CDRP_CMD_BIT set in + * the crb NX_CDRP_CRB_OFFSET. + */ +#define NX_CDRP_FORM_CMD(cmd) (NX_CDRP_CMD_BIT | (cmd)) +#define NX_CDRP_IS_CMD(cmd) (((cmd) & NX_CDRP_CMD_BIT) != 0) + +#define NX_CDRP_CMD_SUBMIT_CAPABILITIES 0x00000001 +#define NX_CDRP_CMD_READ_MAX_RDS_PER_CTX 0x00000002 +#define NX_CDRP_CMD_READ_MAX_SDS_PER_CTX 0x00000003 +#define NX_CDRP_CMD_READ_MAX_RULES_PER_CTX 0x00000004 +#define NX_CDRP_CMD_READ_MAX_RX_CTX 0x00000005 +#define NX_CDRP_CMD_READ_MAX_TX_CTX 0x00000006 +#define NX_CDRP_CMD_CREATE_RX_CTX 0x00000007 +#define NX_CDRP_CMD_DESTROY_RX_CTX 0x00000008 +#define NX_CDRP_CMD_CREATE_TX_CTX 0x00000009 +#define NX_CDRP_CMD_DESTROY_TX_CTX 0x0000000a +#define NX_CDRP_CMD_SETUP_STATISTICS 0x0000000e +#define NX_CDRP_CMD_GET_STATISTICS 0x0000000f +#define NX_CDRP_CMD_DELETE_STATISTICS 0x00000010 +#define NX_CDRP_CMD_SET_MTU 0x00000012 +#define NX_CDRP_CMD_MAX 0x00000013 + +#define NX_RCODE_SUCCESS 0 +#define NX_RCODE_NO_HOST_MEM 1 +#define NX_RCODE_NO_HOST_RESOURCE 2 +#define NX_RCODE_NO_CARD_CRB 3 +#define NX_RCODE_NO_CARD_MEM 4 +#define NX_RCODE_NO_CARD_RESOURCE 5 +#define NX_RCODE_INVALID_ARGS 6 +#define NX_RCODE_INVALID_ACTION 7 +#define NX_RCODE_INVALID_STATE 8 +#define NX_RCODE_NOT_SUPPORTED 9 +#define NX_RCODE_NOT_PERMITTED 10 +#define NX_RCODE_NOT_READY 11 +#define NX_RCODE_DOES_NOT_EXIST 12 +#define NX_RCODE_ALREADY_EXISTS 13 +#define NX_RCODE_BAD_SIGNATURE 14 +#define NX_RCODE_CMD_NOT_IMPL 15 +#define NX_RCODE_CMD_INVALID 16 +#define NX_RCODE_TIMEOUT 17 +#define NX_RCODE_CMD_FAILED 18 +#define NX_RCODE_MAX_EXCEEDED 19 +#define NX_RCODE_MAX 20 + +#define NX_DESTROY_CTX_RESET 0 +#define NX_DESTROY_CTX_D3_RESET 1 +#define NX_DESTROY_CTX_MAX 2 + +/* + * Capabilities + */ +#define NX_CAP_BIT(class, bit) (1 << bit) +#define NX_CAP0_LEGACY_CONTEXT NX_CAP_BIT(0, 0) +#define NX_CAP0_MULTI_CONTEXT NX_CAP_BIT(0, 1) +#define NX_CAP0_LEGACY_MN NX_CAP_BIT(0, 2) +#define NX_CAP0_LEGACY_MS NX_CAP_BIT(0, 3) +#define NX_CAP0_CUT_THROUGH NX_CAP_BIT(0, 4) +#define NX_CAP0_LRO NX_CAP_BIT(0, 5) +#define NX_CAP0_LSO NX_CAP_BIT(0, 6) +#define NX_CAP0_JUMBO_CONTIGUOUS NX_CAP_BIT(0, 7) +#define NX_CAP0_LRO_CONTIGUOUS NX_CAP_BIT(0, 8) + +/* + * Context state + */ +#define NX_HOST_CTX_STATE_FREED 0 +#define NX_HOST_CTX_STATE_ALLOCATED 1 +#define NX_HOST_CTX_STATE_ACTIVE 2 +#define NX_HOST_CTX_STATE_DISABLED 3 +#define NX_HOST_CTX_STATE_QUIESCED 4 +#define NX_HOST_CTX_STATE_MAX 5 + +/* + * Rx context + */ + +typedef struct { + u64 host_phys_addr; /* Ring base addr */ + u32 ring_size; /* Ring entries */ + u16 msi_index; + u16 rsvd; /* Padding */ +} nx_hostrq_sds_ring_t; + +typedef struct { + u64 host_phys_addr; /* Ring base addr */ + u64 buff_size; /* Packet buffer size */ + u32 ring_size; /* Ring entries */ + u32 ring_kind; /* Class of ring */ +} nx_hostrq_rds_ring_t; + +typedef struct { + u64 host_rsp_dma_addr; /* Response dma'd here */ + u32 capabilities[4]; /* Flag bit vector */ + u32 host_int_crb_mode; /* Interrupt crb usage */ + u32 host_rds_crb_mode; /* RDS crb usage */ + /* These ring offsets are relative to data[0] below */ + u32 rds_ring_offset; /* Offset to RDS config */ + u32 sds_ring_offset; /* Offset to SDS config */ + u16 num_rds_rings; /* Count of RDS rings */ + u16 num_sds_rings; /* Count of SDS rings */ + u16 rsvd1; /* Padding */ + u16 rsvd2; /* Padding */ + u8 reserved[128]; /* reserve space for future expansion*/ + /* MUST BE 64-bit aligned. + The following is packed: + - N hostrq_rds_rings + - N hostrq_sds_rings */ + char data[0]; +} nx_hostrq_rx_ctx_t; + +typedef struct { + u32 host_producer_crb; /* Crb to use */ + u32 rsvd1; /* Padding */ +} nx_cardrsp_rds_ring_t; + +typedef struct { + u32 host_consumer_crb; /* Crb to use */ + u32 interrupt_crb; /* Crb to use */ +} nx_cardrsp_sds_ring_t; + +typedef struct { + /* These ring offsets are relative to data[0] below */ + u32 rds_ring_offset; /* Offset to RDS config */ + u32 sds_ring_offset; /* Offset to SDS config */ + u32 host_ctx_state; /* Starting State */ + u32 num_fn_per_port; /* How many PCI fn share the port */ + u16 num_rds_rings; /* Count of RDS rings */ + u16 num_sds_rings; /* Count of SDS rings */ + u16 context_id; /* Handle for context */ + u8 phys_port; /* Physical id of port */ + u8 virt_port; /* Virtual/Logical id of port */ + u8 reserved[128]; /* save space for future expansion */ + /* MUST BE 64-bit aligned. + The following is packed: + - N cardrsp_rds_rings + - N cardrs_sds_rings */ + char data[0]; +} nx_cardrsp_rx_ctx_t; + +#define SIZEOF_HOSTRQ_RX(HOSTRQ_RX, rds_rings, sds_rings) \ + (sizeof(HOSTRQ_RX) + \ + (rds_rings)*(sizeof(nx_hostrq_rds_ring_t)) + \ + (sds_rings)*(sizeof(nx_hostrq_sds_ring_t))) + +#define SIZEOF_CARDRSP_RX(CARDRSP_RX, rds_rings, sds_rings) \ + (sizeof(CARDRSP_RX) + \ + (rds_rings)*(sizeof(nx_cardrsp_rds_ring_t)) + \ + (sds_rings)*(sizeof(nx_cardrsp_sds_ring_t))) + +/* + * Tx context + */ + +typedef struct { + u64 host_phys_addr; /* Ring base addr */ + u32 ring_size; /* Ring entries */ + u32 rsvd; /* Padding */ +} nx_hostrq_cds_ring_t; + +typedef struct { + u64 host_rsp_dma_addr; /* Response dma'd here */ + u64 cmd_cons_dma_addr; /* */ + u64 dummy_dma_addr; /* */ + u32 capabilities[4]; /* Flag bit vector */ + u32 host_int_crb_mode; /* Interrupt crb usage */ + u32 rsvd1; /* Padding */ + u16 rsvd2; /* Padding */ + u16 interrupt_ctl; + u16 msi_index; + u16 rsvd3; /* Padding */ + nx_hostrq_cds_ring_t cds_ring; /* Desc of cds ring */ + u8 reserved[128]; /* future expansion */ +} nx_hostrq_tx_ctx_t; + +typedef struct { + u32 host_producer_crb; /* Crb to use */ + u32 interrupt_crb; /* Crb to use */ +} nx_cardrsp_cds_ring_t; + +typedef struct { + u32 host_ctx_state; /* Starting state */ + u16 context_id; /* Handle for context */ + u8 phys_port; /* Physical id of port */ + u8 virt_port; /* Virtual/Logical id of port */ + nx_cardrsp_cds_ring_t cds_ring; /* Card cds settings */ + u8 reserved[128]; /* future expansion */ +} nx_cardrsp_tx_ctx_t; + +#define SIZEOF_HOSTRQ_TX(HOSTRQ_TX) (sizeof(HOSTRQ_TX)) +#define SIZEOF_CARDRSP_TX(CARDRSP_TX) (sizeof(CARDRSP_TX)) + +/* CRB */ + +#define NX_HOST_RDS_CRB_MODE_UNIQUE 0 +#define NX_HOST_RDS_CRB_MODE_SHARED 1 +#define NX_HOST_RDS_CRB_MODE_CUSTOM 2 +#define NX_HOST_RDS_CRB_MODE_MAX 3 + +#define NX_HOST_INT_CRB_MODE_UNIQUE 0 +#define NX_HOST_INT_CRB_MODE_SHARED 1 +#define NX_HOST_INT_CRB_MODE_NORX 2 +#define NX_HOST_INT_CRB_MODE_NOTX 3 +#define NX_HOST_INT_CRB_MODE_NORXTX 4 + + +/* MAC */ + +#define MC_COUNT_P2 16 +#define MC_COUNT_P3 38 + +#define NETXEN_MAC_NOOP 0 +#define NETXEN_MAC_ADD 1 +#define NETXEN_MAC_DEL 2 + +typedef struct nx_mac_list_s { + struct nx_mac_list_s *next; + uint8_t mac_addr[MAX_ADDR_LEN]; +} nx_mac_list_t; + +/* + * Interrupt coalescing defaults. The defaults are for 1500 MTU. It is + * adjusted based on configured MTU. + */ +#define NETXEN_DEFAULT_INTR_COALESCE_RX_TIME_US 3 +#define NETXEN_DEFAULT_INTR_COALESCE_RX_PACKETS 256 +#define NETXEN_DEFAULT_INTR_COALESCE_TX_PACKETS 64 +#define NETXEN_DEFAULT_INTR_COALESCE_TX_TIME_US 4 + +#define NETXEN_NIC_INTR_DEFAULT 0x04 + +typedef union { + struct { + uint16_t rx_packets; + uint16_t rx_time_us; + uint16_t tx_packets; + uint16_t tx_time_us; + } data; + uint64_t word; +} nx_nic_intr_coalesce_data_t; + +typedef struct { + uint16_t stats_time_us; + uint16_t rate_sample_time; + uint16_t flags; + uint16_t rsvd_1; + uint32_t low_threshold; + uint32_t high_threshold; + nx_nic_intr_coalesce_data_t normal; + nx_nic_intr_coalesce_data_t low; + nx_nic_intr_coalesce_data_t high; + nx_nic_intr_coalesce_data_t irq; +} nx_nic_intr_coalesce_t; + +typedef struct { + u64 qhdr; + u64 req_hdr; + u64 words[6]; +} nx_nic_req_t; + +typedef struct { + u8 op; + u8 tag; + u8 mac_addr[6]; +} nx_mac_req_t; + +#define MAX_PENDING_DESC_BLOCK_SIZE 64 + +#define NETXEN_NIC_MSI_ENABLED 0x02 +#define NETXEN_NIC_MSIX_ENABLED 0x04 +#define NETXEN_IS_MSI_FAMILY(adapter) \ + ((adapter)->flags & (NETXEN_NIC_MSI_ENABLED | NETXEN_NIC_MSIX_ENABLED)) + +#define MSIX_ENTRIES_PER_ADAPTER 8 +#define NETXEN_MSIX_TBL_SPACE 8192 +#define NETXEN_PCI_REG_MSIX_TBL 0x44 + +#define NETXEN_DB_MAPSIZE_BYTES 0x1000 + +#define NETXEN_NETDEV_WEIGHT 120 +#define NETXEN_ADAPTER_UP_MAGIC 777 +#define NETXEN_NIC_PEG_TUNE 0 struct netxen_dummy_dma { void *addr; @@ -854,46 +1207,65 @@ struct netxen_dummy_dma { struct netxen_adapter { struct netxen_hardware_context ahw; - struct netxen_adapter *master; struct net_device *netdev; struct pci_dev *pdev; + int pci_using_dac; struct napi_struct napi; struct net_device_stats net_stats; - unsigned char mac_addr[ETH_ALEN]; int mtu; int portnum; u8 physical_port; + u16 tx_context_id; + + uint8_t mc_enabled; + uint8_t max_mc_count; + nx_mac_list_t *mac_list; + + struct netxen_legacy_intr_set legacy_intr; + u32 crb_intr_mask; struct work_struct watchdog_task; struct timer_list watchdog_timer; struct work_struct tx_timeout_task; u32 curr_window; + u32 crb_win; + rwlock_t adapter_lock; + + uint64_t dma_mask; u32 cmd_producer; __le32 *cmd_consumer; u32 last_cmd_consumer; + u32 crb_addr_cmd_producer; + u32 crb_addr_cmd_consumer; u32 max_tx_desc_count; u32 max_rx_desc_count; u32 max_jumbo_rx_desc_count; u32 max_lro_rx_desc_count; + int max_rds_rings; + u32 flags; u32 irq; int driver_mismatch; u32 temp; + u32 fw_major; + + u8 msix_supported; + u8 max_possible_rss_rings; + struct msix_entry msix_entries[MSIX_ENTRIES_PER_ADAPTER]; + struct netxen_adapter_stats stats; - u16 portno; u16 link_speed; u16 link_duplex; u16 state; u16 link_autoneg; int rx_csum; int status; - spinlock_t stats_lock; struct netxen_cmd_buffer *cmd_buf_arr; /* Command buffers for xmit */ @@ -905,25 +1277,33 @@ struct netxen_adapter { int is_up; struct netxen_dummy_dma dummy_dma; + nx_nic_intr_coalesce_t coal; /* Context interface shared between card and host */ struct netxen_ring_ctx *ctx_desc; - struct pci_dev *ctx_desc_pdev; dma_addr_t ctx_desc_phys_addr; int intr_scheme; int msi_mode; 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_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 (*hw_read_wx)(struct netxen_adapter *, ulong, void *, int); + int (*hw_write_wx)(struct netxen_adapter *, ulong, void *, int); + int (*pci_mem_read)(struct netxen_adapter *, u64, void *, int); + int (*pci_mem_write)(struct netxen_adapter *, u64, void *, int); + int (*pci_write_immediate)(struct netxen_adapter *, u64, u32); + u32 (*pci_read_immediate)(struct netxen_adapter *, u64); + void (*pci_write_normalize)(struct netxen_adapter *, u64, u32); + u32 (*pci_read_normalize)(struct netxen_adapter *, u64); + unsigned long (*pci_set_window)(struct netxen_adapter *, + unsigned long long); }; /* netxen_adapter structure */ /* @@ -988,8 +1368,6 @@ 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); -void netxen_nic_xgbe_handle_phy_intr(struct netxen_adapter *adapter); -void netxen_nic_gbe_handle_phy_intr(struct netxen_adapter *adapter); int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long reg, __u32 * readval); int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter, @@ -998,27 +1376,61 @@ int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter, /* Functions available from netxen_nic_hw.c */ 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); int netxen_nic_reg_read(struct netxen_adapter *adapter, u64 off); void netxen_nic_write_w0(struct netxen_adapter *adapter, u32 index, u32 value); -void netxen_nic_read_w0(struct netxen_adapter *adapter, u32 index, u32 * value); +void netxen_nic_read_w0(struct netxen_adapter *adapter, u32 index, u32 *value); +void netxen_nic_write_w1(struct netxen_adapter *adapter, u32 index, u32 value); +void netxen_nic_read_w1(struct netxen_adapter *adapter, u32 index, u32 *value); int netxen_nic_get_board_info(struct netxen_adapter *adapter); -int netxen_nic_hw_read_wx(struct netxen_adapter *adapter, u64 off, void *data, - int len); -int netxen_nic_hw_write_wx(struct netxen_adapter *adapter, u64 off, void *data, - int len); + +int netxen_nic_hw_read_wx_128M(struct netxen_adapter *adapter, + ulong off, void *data, int len); +int netxen_nic_hw_write_wx_128M(struct netxen_adapter *adapter, + ulong off, void *data, int len); +int netxen_nic_pci_mem_read_128M(struct netxen_adapter *adapter, + u64 off, void *data, int size); +int netxen_nic_pci_mem_write_128M(struct netxen_adapter *adapter, + u64 off, void *data, int size); +int netxen_nic_pci_write_immediate_128M(struct netxen_adapter *adapter, + u64 off, u32 data); +u32 netxen_nic_pci_read_immediate_128M(struct netxen_adapter *adapter, u64 off); +void netxen_nic_pci_write_normalize_128M(struct netxen_adapter *adapter, + u64 off, u32 data); +u32 netxen_nic_pci_read_normalize_128M(struct netxen_adapter *adapter, u64 off); +unsigned long netxen_nic_pci_set_window_128M(struct netxen_adapter *adapter, + unsigned long long addr); +void netxen_nic_pci_change_crbwindow_128M(struct netxen_adapter *adapter, + u32 wndw); + +int netxen_nic_hw_read_wx_2M(struct netxen_adapter *adapter, + ulong off, void *data, int len); +int netxen_nic_hw_write_wx_2M(struct netxen_adapter *adapter, + ulong off, void *data, int len); +int netxen_nic_pci_mem_read_2M(struct netxen_adapter *adapter, + u64 off, void *data, int size); +int netxen_nic_pci_mem_write_2M(struct netxen_adapter *adapter, + u64 off, void *data, int size); void netxen_crb_writelit_adapter(struct netxen_adapter *adapter, unsigned long off, int data); +int netxen_nic_pci_write_immediate_2M(struct netxen_adapter *adapter, + u64 off, u32 data); +u32 netxen_nic_pci_read_immediate_2M(struct netxen_adapter *adapter, u64 off); +void netxen_nic_pci_write_normalize_2M(struct netxen_adapter *adapter, + u64 off, u32 data); +u32 netxen_nic_pci_read_normalize_2M(struct netxen_adapter *adapter, u64 off); +unsigned long netxen_nic_pci_set_window_2M(struct netxen_adapter *adapter, + unsigned long long addr); /* Functions from netxen_nic_init.c */ void netxen_free_adapter_offload(struct netxen_adapter *adapter); int netxen_initialize_adapter_offload(struct netxen_adapter *adapter); int netxen_phantom_init(struct netxen_adapter *adapter, int pegtune_val); +int netxen_receive_peg_ready(struct netxen_adapter *adapter); int netxen_load_firmware(struct netxen_adapter *adapter); int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose); + int netxen_rom_fast_read(struct netxen_adapter *adapter, int addr, int *valp); int netxen_rom_fast_read_words(struct netxen_adapter *adapter, int addr, u8 *bytes, size_t size); @@ -1032,33 +1444,43 @@ void netxen_halt_pegs(struct netxen_adapter *adapter); int netxen_rom_se(struct netxen_adapter *adapter, int addr); -/* Functions from netxen_nic_isr.c */ -void netxen_initialize_adapter_sw(struct netxen_adapter *adapter); -void *netxen_alloc(struct pci_dev *pdev, size_t sz, dma_addr_t * ptr, - struct pci_dev **used_dev); +int netxen_alloc_sw_resources(struct netxen_adapter *adapter); +void netxen_free_sw_resources(struct netxen_adapter *adapter); + +int netxen_alloc_hw_resources(struct netxen_adapter *adapter); +void netxen_free_hw_resources(struct netxen_adapter *adapter); + +void netxen_release_rx_buffers(struct netxen_adapter *adapter); +void netxen_release_tx_buffers(struct netxen_adapter *adapter); + void netxen_initialize_adapter_ops(struct netxen_adapter *adapter); int netxen_init_firmware(struct netxen_adapter *adapter); -void netxen_free_hw_resources(struct netxen_adapter *adapter); void netxen_tso_check(struct netxen_adapter *adapter, struct cmd_desc_type0 *desc, struct sk_buff *skb); -int netxen_nic_hw_resources(struct netxen_adapter *adapter); void netxen_nic_clear_stats(struct netxen_adapter *adapter); void netxen_watchdog_task(struct work_struct *work); void netxen_post_rx_buffers(struct netxen_adapter *adapter, u32 ctx, u32 ringid); int netxen_process_cmd_ring(struct netxen_adapter *adapter); u32 netxen_process_rcv_ring(struct netxen_adapter *adapter, int ctx, int max); -void netxen_nic_set_multi(struct net_device *netdev); +void netxen_p2_nic_set_multi(struct net_device *netdev); +void netxen_p3_nic_set_multi(struct net_device *netdev); +int netxen_config_intr_coalesce(struct netxen_adapter *adapter); + +u32 nx_fw_cmd_set_mtu(struct netxen_adapter *adapter, u32 mtu); int netxen_nic_change_mtu(struct net_device *netdev, int new_mtu); + int netxen_nic_set_mac(struct net_device *netdev, void *p); struct net_device_stats *netxen_nic_get_stats(struct net_device *netdev); +void netxen_nic_update_cmd_producer(struct netxen_adapter *adapter, + uint32_t crb_producer); /* * NetXen Board information */ -#define NETXEN_MAX_SHORT_NAME 16 +#define NETXEN_MAX_SHORT_NAME 32 struct netxen_brdinfo { netxen_brdtype_t brdtype; /* type of board */ long ports; /* max no of physical ports */ @@ -1072,6 +1494,17 @@ static const struct netxen_brdinfo netxen_boards[] = { {NETXEN_BRDTYPE_P2_SB31_10G, 1, "XGb XFP"}, {NETXEN_BRDTYPE_P2_SB35_4G, 4, "Quad Gb"}, {NETXEN_BRDTYPE_P2_SB31_2G, 2, "Dual Gb"}, + {NETXEN_BRDTYPE_P3_REF_QG, 4, "Reference Quad Gig "}, + {NETXEN_BRDTYPE_P3_HMEZ, 2, "Dual XGb HMEZ"}, + {NETXEN_BRDTYPE_P3_10G_CX4_LP, 2, "Dual XGb CX4 LP"}, + {NETXEN_BRDTYPE_P3_4_GB, 4, "Quad Gig LP"}, + {NETXEN_BRDTYPE_P3_IMEZ, 2, "Dual XGb IMEZ"}, + {NETXEN_BRDTYPE_P3_10G_SFP_PLUS, 2, "Dual XGb SFP+ LP"}, + {NETXEN_BRDTYPE_P3_10000_BASE_T, 1, "XGB 10G BaseT LP"}, + {NETXEN_BRDTYPE_P3_XG_LOM, 2, "Dual XGb LOM"}, + {NETXEN_BRDTYPE_P3_4_GB_MM, 4, "Quad GB - March Madness"}, + {NETXEN_BRDTYPE_P3_10G_CX4, 2, "Reference Dual CX4 Option"}, + {NETXEN_BRDTYPE_P3_10G_XFP, 1, "Reference Single XFP Option"} }; #define NUM_SUPPORTED_BOARDS ARRAY_SIZE(netxen_boards) @@ -1097,7 +1530,7 @@ dma_watchdog_shutdown_request(struct netxen_adapter *adapter) u32 ctrl; /* check if already inactive */ - if (netxen_nic_hw_read_wx(adapter, + if (adapter->hw_read_wx(adapter, NETXEN_CAM_RAM(NETXEN_CAM_RAM_DMA_WATCHDOG_CTRL), &ctrl, 4)) printk(KERN_ERR "failed to read dma watchdog status\n"); @@ -1117,7 +1550,7 @@ dma_watchdog_shutdown_poll_result(struct netxen_adapter *adapter) { u32 ctrl; - if (netxen_nic_hw_read_wx(adapter, + if (adapter->hw_read_wx(adapter, NETXEN_CAM_RAM(NETXEN_CAM_RAM_DMA_WATCHDOG_CTRL), &ctrl, 4)) printk(KERN_ERR "failed to read dma watchdog status\n"); @@ -1129,7 +1562,7 @@ dma_watchdog_wakeup(struct netxen_adapter *adapter) { u32 ctrl; - if (netxen_nic_hw_read_wx(adapter, + if (adapter->hw_read_wx(adapter, NETXEN_CAM_RAM(NETXEN_CAM_RAM_DMA_WATCHDOG_CTRL), &ctrl, 4)) printk(KERN_ERR "failed to read dma watchdog status\n"); diff --git a/drivers/net/netxen/netxen_nic_ctx.c b/drivers/net/netxen/netxen_nic_ctx.c new file mode 100644 index 000000000000..64babc59e699 --- /dev/null +++ b/drivers/net/netxen/netxen_nic_ctx.c @@ -0,0 +1,710 @@ +/* + * Copyright (C) 2003 - 2008 NetXen, Inc. + * All rights reserved. + * + * 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. + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE. + * + * Contact Information: + * info@netxen.com + * NetXen, + * 3965 Freedom Circle, Fourth floor, + * Santa Clara, CA 95054 + * + */ + +#include "netxen_nic_hw.h" +#include "netxen_nic.h" +#include "netxen_nic_phan_reg.h" + +#define NXHAL_VERSION 1 + +static int +netxen_api_lock(struct netxen_adapter *adapter) +{ + u32 done = 0, timeout = 0; + + for (;;) { + /* Acquire PCIE HW semaphore5 */ + netxen_nic_read_w0(adapter, + NETXEN_PCIE_REG(PCIE_SEM5_LOCK), &done); + + if (done == 1) + break; + + if (++timeout >= NX_OS_CRB_RETRY_COUNT) { + printk(KERN_ERR "%s: lock timeout.\n", __func__); + return -1; + } + + msleep(1); + } + +#if 0 + netxen_nic_write_w1(adapter, + NETXEN_API_LOCK_ID, NX_OS_API_LOCK_DRIVER); +#endif + return 0; +} + +static int +netxen_api_unlock(struct netxen_adapter *adapter) +{ + u32 val; + + /* Release PCIE HW semaphore5 */ + netxen_nic_read_w0(adapter, + NETXEN_PCIE_REG(PCIE_SEM5_UNLOCK), &val); + return 0; +} + +static u32 +netxen_poll_rsp(struct netxen_adapter *adapter) +{ + u32 raw_rsp, rsp = NX_CDRP_RSP_OK; + int timeout = 0; + + do { + /* give atleast 1ms for firmware to respond */ + msleep(1); + + if (++timeout > NX_OS_CRB_RETRY_COUNT) + return NX_CDRP_RSP_TIMEOUT; + + netxen_nic_read_w1(adapter, NX_CDRP_CRB_OFFSET, + &raw_rsp); + + rsp = le32_to_cpu(raw_rsp); + } while (!NX_CDRP_IS_RSP(rsp)); + + return rsp; +} + +static u32 +netxen_issue_cmd(struct netxen_adapter *adapter, + u32 pci_fn, u32 version, u32 arg1, u32 arg2, u32 arg3, u32 cmd) +{ + u32 rsp; + u32 signature = 0; + u32 rcode = NX_RCODE_SUCCESS; + + signature = NX_CDRP_SIGNATURE_MAKE(pci_fn, version); + + /* Acquire semaphore before accessing CRB */ + if (netxen_api_lock(adapter)) + return NX_RCODE_TIMEOUT; + + netxen_nic_write_w1(adapter, NX_SIGN_CRB_OFFSET, + cpu_to_le32(signature)); + + netxen_nic_write_w1(adapter, NX_ARG1_CRB_OFFSET, + cpu_to_le32(arg1)); + + netxen_nic_write_w1(adapter, NX_ARG2_CRB_OFFSET, + cpu_to_le32(arg2)); + + netxen_nic_write_w1(adapter, NX_ARG3_CRB_OFFSET, + cpu_to_le32(arg3)); + + netxen_nic_write_w1(adapter, NX_CDRP_CRB_OFFSET, + cpu_to_le32(NX_CDRP_FORM_CMD(cmd))); + + rsp = netxen_poll_rsp(adapter); + + if (rsp == NX_CDRP_RSP_TIMEOUT) { + printk(KERN_ERR "%s: card response timeout.\n", + netxen_nic_driver_name); + + rcode = NX_RCODE_TIMEOUT; + } else if (rsp == NX_CDRP_RSP_FAIL) { + netxen_nic_read_w1(adapter, NX_ARG1_CRB_OFFSET, &rcode); + rcode = le32_to_cpu(rcode); + + printk(KERN_ERR "%s: failed card response code:0x%x\n", + netxen_nic_driver_name, rcode); + } + + /* Release semaphore */ + netxen_api_unlock(adapter); + + return rcode; +} + +u32 +nx_fw_cmd_set_mtu(struct netxen_adapter *adapter, u32 mtu) +{ + u32 rcode = NX_RCODE_SUCCESS; + struct netxen_recv_context *recv_ctx = &adapter->recv_ctx[0]; + + if (recv_ctx->state == NX_HOST_CTX_STATE_ACTIVE) + rcode = netxen_issue_cmd(adapter, + adapter->ahw.pci_func, + NXHAL_VERSION, + recv_ctx->context_id, + mtu, + 0, + NX_CDRP_CMD_SET_MTU); + + return rcode; +} + +static int +nx_fw_cmd_create_rx_ctx(struct netxen_adapter *adapter) +{ + void *addr; + nx_hostrq_rx_ctx_t *prq; + nx_cardrsp_rx_ctx_t *prsp; + nx_hostrq_rds_ring_t *prq_rds; + nx_hostrq_sds_ring_t *prq_sds; + nx_cardrsp_rds_ring_t *prsp_rds; + nx_cardrsp_sds_ring_t *prsp_sds; + struct nx_host_rds_ring *rds_ring; + + dma_addr_t hostrq_phys_addr, cardrsp_phys_addr; + u64 phys_addr; + + int i, nrds_rings, nsds_rings; + size_t rq_size, rsp_size; + u32 cap, reg; + + int err; + + struct netxen_recv_context *recv_ctx = &adapter->recv_ctx[0]; + + /* only one sds ring for now */ + nrds_rings = adapter->max_rds_rings; + nsds_rings = 1; + + rq_size = + SIZEOF_HOSTRQ_RX(nx_hostrq_rx_ctx_t, nrds_rings, nsds_rings); + rsp_size = + SIZEOF_CARDRSP_RX(nx_cardrsp_rx_ctx_t, nrds_rings, nsds_rings); + + addr = pci_alloc_consistent(adapter->pdev, + rq_size, &hostrq_phys_addr); + if (addr == NULL) + return -ENOMEM; + prq = (nx_hostrq_rx_ctx_t *)addr; + + addr = pci_alloc_consistent(adapter->pdev, + rsp_size, &cardrsp_phys_addr); + if (addr == NULL) { + err = -ENOMEM; + goto out_free_rq; + } + prsp = (nx_cardrsp_rx_ctx_t *)addr; + + prq->host_rsp_dma_addr = cpu_to_le64(cardrsp_phys_addr); + + cap = (NX_CAP0_LEGACY_CONTEXT | NX_CAP0_LEGACY_MN); + cap |= (NX_CAP0_JUMBO_CONTIGUOUS | NX_CAP0_LRO_CONTIGUOUS); + + prq->capabilities[0] = cpu_to_le32(cap); + prq->host_int_crb_mode = + cpu_to_le32(NX_HOST_INT_CRB_MODE_SHARED); + prq->host_rds_crb_mode = + cpu_to_le32(NX_HOST_RDS_CRB_MODE_UNIQUE); + + prq->num_rds_rings = cpu_to_le16(nrds_rings); + prq->num_sds_rings = cpu_to_le16(nsds_rings); + prq->rds_ring_offset = 0; + prq->sds_ring_offset = prq->rds_ring_offset + + (sizeof(nx_hostrq_rds_ring_t) * nrds_rings); + + prq_rds = (nx_hostrq_rds_ring_t *)(prq->data + prq->rds_ring_offset); + + for (i = 0; i < nrds_rings; i++) { + + rds_ring = &recv_ctx->rds_rings[i]; + + prq_rds[i].host_phys_addr = cpu_to_le64(rds_ring->phys_addr); + prq_rds[i].ring_size = cpu_to_le32(rds_ring->max_rx_desc_count); + prq_rds[i].ring_kind = cpu_to_le32(i); + prq_rds[i].buff_size = cpu_to_le64(rds_ring->dma_size); + } + + prq_sds = (nx_hostrq_sds_ring_t *)(prq->data + prq->sds_ring_offset); + + prq_sds[0].host_phys_addr = + cpu_to_le64(recv_ctx->rcv_status_desc_phys_addr); + prq_sds[0].ring_size = cpu_to_le32(adapter->max_rx_desc_count); + /* only one msix vector for now */ + prq_sds[0].msi_index = cpu_to_le32(0); + + /* now byteswap offsets */ + prq->rds_ring_offset = cpu_to_le32(prq->rds_ring_offset); + prq->sds_ring_offset = cpu_to_le32(prq->sds_ring_offset); + + phys_addr = hostrq_phys_addr; + err = netxen_issue_cmd(adapter, + adapter->ahw.pci_func, + NXHAL_VERSION, + (u32)(phys_addr >> 32), + (u32)(phys_addr & 0xffffffff), + rq_size, + NX_CDRP_CMD_CREATE_RX_CTX); + if (err) { + printk(KERN_WARNING + "Failed to create rx ctx in firmware%d\n", err); + goto out_free_rsp; + } + + + prsp_rds = ((nx_cardrsp_rds_ring_t *) + &prsp->data[prsp->rds_ring_offset]); + + for (i = 0; i < le32_to_cpu(prsp->num_rds_rings); i++) { + rds_ring = &recv_ctx->rds_rings[i]; + + reg = le32_to_cpu(prsp_rds[i].host_producer_crb); + rds_ring->crb_rcv_producer = NETXEN_NIC_REG(reg - 0x200); + } + + prsp_sds = ((nx_cardrsp_sds_ring_t *) + &prsp->data[prsp->sds_ring_offset]); + reg = le32_to_cpu(prsp_sds[0].host_consumer_crb); + recv_ctx->crb_sts_consumer = NETXEN_NIC_REG(reg - 0x200); + + reg = le32_to_cpu(prsp_sds[0].interrupt_crb); + adapter->crb_intr_mask = NETXEN_NIC_REG(reg - 0x200); + + recv_ctx->state = le32_to_cpu(prsp->host_ctx_state); + recv_ctx->context_id = le16_to_cpu(prsp->context_id); + recv_ctx->virt_port = le16_to_cpu(prsp->virt_port); + +out_free_rsp: + pci_free_consistent(adapter->pdev, rsp_size, prsp, cardrsp_phys_addr); +out_free_rq: + pci_free_consistent(adapter->pdev, rq_size, prq, hostrq_phys_addr); + return err; +} + +static void +nx_fw_cmd_destroy_rx_ctx(struct netxen_adapter *adapter) +{ + struct netxen_recv_context *recv_ctx = &adapter->recv_ctx[0]; + + if (netxen_issue_cmd(adapter, + adapter->ahw.pci_func, + NXHAL_VERSION, + recv_ctx->context_id, + NX_DESTROY_CTX_RESET, + 0, + NX_CDRP_CMD_DESTROY_RX_CTX)) { + + printk(KERN_WARNING + "%s: Failed to destroy rx ctx in firmware\n", + netxen_nic_driver_name); + } +} + +static int +nx_fw_cmd_create_tx_ctx(struct netxen_adapter *adapter) +{ + nx_hostrq_tx_ctx_t *prq; + nx_hostrq_cds_ring_t *prq_cds; + nx_cardrsp_tx_ctx_t *prsp; + void *rq_addr, *rsp_addr; + size_t rq_size, rsp_size; + u32 temp; + int err = 0; + u64 offset, phys_addr; + dma_addr_t rq_phys_addr, rsp_phys_addr; + + rq_size = SIZEOF_HOSTRQ_TX(nx_hostrq_tx_ctx_t); + rq_addr = pci_alloc_consistent(adapter->pdev, + rq_size, &rq_phys_addr); + if (!rq_addr) + return -ENOMEM; + + rsp_size = SIZEOF_CARDRSP_TX(nx_cardrsp_tx_ctx_t); + rsp_addr = pci_alloc_consistent(adapter->pdev, + rsp_size, &rsp_phys_addr); + if (!rsp_addr) { + err = -ENOMEM; + goto out_free_rq; + } + + memset(rq_addr, 0, rq_size); + prq = (nx_hostrq_tx_ctx_t *)rq_addr; + + memset(rsp_addr, 0, rsp_size); + prsp = (nx_cardrsp_tx_ctx_t *)rsp_addr; + + prq->host_rsp_dma_addr = cpu_to_le64(rsp_phys_addr); + + temp = (NX_CAP0_LEGACY_CONTEXT | NX_CAP0_LEGACY_MN | NX_CAP0_LSO); + prq->capabilities[0] = cpu_to_le32(temp); + + prq->host_int_crb_mode = + cpu_to_le32(NX_HOST_INT_CRB_MODE_SHARED); + + prq->interrupt_ctl = 0; + prq->msi_index = 0; + + prq->dummy_dma_addr = cpu_to_le64(adapter->dummy_dma.phys_addr); + + offset = adapter->ctx_desc_phys_addr+sizeof(struct netxen_ring_ctx); + prq->cmd_cons_dma_addr = cpu_to_le64(offset); + + prq_cds = &prq->cds_ring; + + prq_cds->host_phys_addr = + cpu_to_le64(adapter->ahw.cmd_desc_phys_addr); + + prq_cds->ring_size = cpu_to_le32(adapter->max_tx_desc_count); + + phys_addr = rq_phys_addr; + err = netxen_issue_cmd(adapter, + adapter->ahw.pci_func, + NXHAL_VERSION, + (u32)(phys_addr >> 32), + ((u32)phys_addr & 0xffffffff), + rq_size, + NX_CDRP_CMD_CREATE_TX_CTX); + + if (err == NX_RCODE_SUCCESS) { + temp = le32_to_cpu(prsp->cds_ring.host_producer_crb); + adapter->crb_addr_cmd_producer = + NETXEN_NIC_REG(temp - 0x200); +#if 0 + adapter->tx_state = + le32_to_cpu(prsp->host_ctx_state); +#endif + adapter->tx_context_id = + le16_to_cpu(prsp->context_id); + } else { + printk(KERN_WARNING + "Failed to create tx ctx in firmware%d\n", err); + err = -EIO; + } + + pci_free_consistent(adapter->pdev, rsp_size, rsp_addr, rsp_phys_addr); + +out_free_rq: + pci_free_consistent(adapter->pdev, rq_size, rq_addr, rq_phys_addr); + + return err; +} + +static void +nx_fw_cmd_destroy_tx_ctx(struct netxen_adapter *adapter) +{ + if (netxen_issue_cmd(adapter, + adapter->ahw.pci_func, + NXHAL_VERSION, + adapter->tx_context_id, + NX_DESTROY_CTX_RESET, + 0, + NX_CDRP_CMD_DESTROY_TX_CTX)) { + + printk(KERN_WARNING + "%s: Failed to destroy tx ctx in firmware\n", + netxen_nic_driver_name); + } +} + +static 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)} +}; + +#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]) + +#define lower32(x) ((u32)((x) & 0xffffffff)) +#define upper32(x) ((u32)(((u64)(x) >> 32) & 0xffffffff)) + +static struct netxen_recv_crb recv_crb_registers[] = { + /* Instance 0 */ + { + /* crb_rcv_producer: */ + { + NETXEN_NIC_REG(0x100), + /* Jumbo frames */ + NETXEN_NIC_REG(0x110), + /* LRO */ + NETXEN_NIC_REG(0x120) + }, + /* crb_sts_consumer: */ + NETXEN_NIC_REG(0x138), + }, + /* Instance 1 */ + { + /* crb_rcv_producer: */ + { + NETXEN_NIC_REG(0x144), + /* Jumbo frames */ + NETXEN_NIC_REG(0x154), + /* LRO */ + NETXEN_NIC_REG(0x164) + }, + /* crb_sts_consumer: */ + NETXEN_NIC_REG(0x17c), + }, + /* Instance 2 */ + { + /* crb_rcv_producer: */ + { + NETXEN_NIC_REG(0x1d8), + /* Jumbo frames */ + NETXEN_NIC_REG(0x1f8), + /* LRO */ + NETXEN_NIC_REG(0x208) + }, + /* crb_sts_consumer: */ + NETXEN_NIC_REG(0x220), + }, + /* Instance 3 */ + { + /* crb_rcv_producer: */ + { + NETXEN_NIC_REG(0x22c), + /* Jumbo frames */ + NETXEN_NIC_REG(0x23c), + /* LRO */ + NETXEN_NIC_REG(0x24c) + }, + /* crb_sts_consumer: */ + NETXEN_NIC_REG(0x264), + }, +}; + +static int +netxen_init_old_ctx(struct netxen_adapter *adapter) +{ + struct netxen_recv_context *recv_ctx; + struct nx_host_rds_ring *rds_ring; + int ctx, ring; + int func_id = adapter->portnum; + + adapter->ctx_desc->cmd_ring_addr = + cpu_to_le64(adapter->ahw.cmd_desc_phys_addr); + adapter->ctx_desc->cmd_ring_size = + cpu_to_le32(adapter->max_tx_desc_count); + + for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) { + recv_ctx = &adapter->recv_ctx[ctx]; + + for (ring = 0; ring < adapter->max_rds_rings; ring++) { + rds_ring = &recv_ctx->rds_rings[ring]; + + adapter->ctx_desc->rcv_ctx[ring].rcv_ring_addr = + cpu_to_le64(rds_ring->phys_addr); + adapter->ctx_desc->rcv_ctx[ring].rcv_ring_size = + cpu_to_le32(rds_ring->max_rx_desc_count); + } + adapter->ctx_desc->sts_ring_addr = + cpu_to_le64(recv_ctx->rcv_status_desc_phys_addr); + adapter->ctx_desc->sts_ring_size = + cpu_to_le32(adapter->max_rx_desc_count); + } + + adapter->pci_write_normalize(adapter, CRB_CTX_ADDR_REG_LO(func_id), + lower32(adapter->ctx_desc_phys_addr)); + adapter->pci_write_normalize(adapter, CRB_CTX_ADDR_REG_HI(func_id), + upper32(adapter->ctx_desc_phys_addr)); + adapter->pci_write_normalize(adapter, CRB_CTX_SIGNATURE_REG(func_id), + NETXEN_CTX_SIGNATURE | func_id); + return 0; +} + +static uint32_t sw_int_mask[4] = { + CRB_SW_INT_MASK_0, CRB_SW_INT_MASK_1, + CRB_SW_INT_MASK_2, CRB_SW_INT_MASK_3 +}; + +int netxen_alloc_hw_resources(struct netxen_adapter *adapter) +{ + struct netxen_hardware_context *hw = &adapter->ahw; + u32 state = 0; + void *addr; + int err = 0; + int ctx, ring; + struct netxen_recv_context *recv_ctx; + struct nx_host_rds_ring *rds_ring; + + err = netxen_receive_peg_ready(adapter); + if (err) { + printk(KERN_ERR "Rcv Peg initialization not complete:%x.\n", + state); + return err; + } + + addr = pci_alloc_consistent(adapter->pdev, + sizeof(struct netxen_ring_ctx) + sizeof(uint32_t), + &adapter->ctx_desc_phys_addr); + + if (addr == NULL) { + DPRINTK(ERR, "failed to allocate hw context\n"); + return -ENOMEM; + } + 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 = + (__le32 *)(((char *)addr) + sizeof(struct netxen_ring_ctx)); + + /* cmd desc ring */ + addr = pci_alloc_consistent(adapter->pdev, + sizeof(struct cmd_desc_type0) * + adapter->max_tx_desc_count, + &hw->cmd_desc_phys_addr); + + if (addr == NULL) { + printk(KERN_ERR "%s failed to allocate tx desc ring\n", + netxen_nic_driver_name); + return -ENOMEM; + } + + hw->cmd_desc_head = (struct cmd_desc_type0 *)addr; + + for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) { + recv_ctx = &adapter->recv_ctx[ctx]; + + for (ring = 0; ring < adapter->max_rds_rings; ring++) { + /* rx desc ring */ + rds_ring = &recv_ctx->rds_rings[ring]; + addr = pci_alloc_consistent(adapter->pdev, + RCV_DESC_RINGSIZE, + &rds_ring->phys_addr); + if (addr == NULL) { + printk(KERN_ERR "%s failed to allocate rx " + "desc ring[%d]\n", + netxen_nic_driver_name, ring); + err = -ENOMEM; + goto err_out_free; + } + rds_ring->desc_head = (struct rcv_desc *)addr; + + if (adapter->fw_major < 4) + rds_ring->crb_rcv_producer = + recv_crb_registers[adapter->portnum]. + crb_rcv_producer[ring]; + } + + /* status desc ring */ + addr = pci_alloc_consistent(adapter->pdev, + STATUS_DESC_RINGSIZE, + &recv_ctx->rcv_status_desc_phys_addr); + if (addr == NULL) { + printk(KERN_ERR "%s failed to allocate sts desc ring\n", + netxen_nic_driver_name); + err = -ENOMEM; + goto err_out_free; + } + recv_ctx->rcv_status_desc_head = (struct status_desc *)addr; + + if (adapter->fw_major < 4) + recv_ctx->crb_sts_consumer = + recv_crb_registers[adapter->portnum]. + crb_sts_consumer; + } + + if (adapter->fw_major >= 4) { + adapter->intr_scheme = INTR_SCHEME_PERPORT; + adapter->msi_mode = MSI_MODE_MULTIFUNC; + + err = nx_fw_cmd_create_rx_ctx(adapter); + if (err) + goto err_out_free; + err = nx_fw_cmd_create_tx_ctx(adapter); + if (err) + goto err_out_free; + } else { + + adapter->intr_scheme = adapter->pci_read_normalize(adapter, + CRB_NIC_CAPABILITIES_FW); + adapter->msi_mode = adapter->pci_read_normalize(adapter, + CRB_NIC_MSI_MODE_FW); + adapter->crb_intr_mask = sw_int_mask[adapter->portnum]; + + err = netxen_init_old_ctx(adapter); + if (err) { + netxen_free_hw_resources(adapter); + return err; + } + + } + + return 0; + +err_out_free: + netxen_free_hw_resources(adapter); + return err; +} + +void netxen_free_hw_resources(struct netxen_adapter *adapter) +{ + struct netxen_recv_context *recv_ctx; + struct nx_host_rds_ring *rds_ring; + int ctx, ring; + + if (adapter->fw_major >= 4) { + nx_fw_cmd_destroy_tx_ctx(adapter); + nx_fw_cmd_destroy_rx_ctx(adapter); + } + + if (adapter->ctx_desc != NULL) { + pci_free_consistent(adapter->pdev, + sizeof(struct netxen_ring_ctx) + + sizeof(uint32_t), + adapter->ctx_desc, + adapter->ctx_desc_phys_addr); + adapter->ctx_desc = NULL; + } + + if (adapter->ahw.cmd_desc_head != NULL) { + pci_free_consistent(adapter->pdev, + sizeof(struct cmd_desc_type0) * + adapter->max_tx_desc_count, + adapter->ahw.cmd_desc_head, + adapter->ahw.cmd_desc_phys_addr); + adapter->ahw.cmd_desc_head = NULL; + } + + for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) { + recv_ctx = &adapter->recv_ctx[ctx]; + for (ring = 0; ring < adapter->max_rds_rings; ring++) { + rds_ring = &recv_ctx->rds_rings[ring]; + + if (rds_ring->desc_head != NULL) { + pci_free_consistent(adapter->pdev, + RCV_DESC_RINGSIZE, + rds_ring->desc_head, + rds_ring->phys_addr); + rds_ring->desc_head = NULL; + } + } + + if (recv_ctx->rcv_status_desc_head != NULL) { + pci_free_consistent(adapter->pdev, + STATUS_DESC_RINGSIZE, + recv_ctx->rcv_status_desc_head, + recv_ctx->rcv_status_desc_phys_addr); + recv_ctx->rcv_status_desc_head = NULL; + } + } +} + diff --git a/drivers/net/netxen/netxen_nic_ethtool.c b/drivers/net/netxen/netxen_nic_ethtool.c index 723487bf200c..48ee06b6f4e9 100644 --- a/drivers/net/netxen/netxen_nic_ethtool.c +++ b/drivers/net/netxen/netxen_nic_ethtool.c @@ -93,17 +93,21 @@ static void netxen_nic_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) { struct netxen_adapter *adapter = netdev_priv(dev); + unsigned long flags; u32 fw_major = 0; u32 fw_minor = 0; u32 fw_build = 0; strncpy(drvinfo->driver, netxen_nic_driver_name, 32); strncpy(drvinfo->version, NETXEN_NIC_LINUX_VERSIONID, 32); - fw_major = readl(NETXEN_CRB_NORMALIZE(adapter, - NETXEN_FW_VERSION_MAJOR)); - fw_minor = readl(NETXEN_CRB_NORMALIZE(adapter, - NETXEN_FW_VERSION_MINOR)); - fw_build = readl(NETXEN_CRB_NORMALIZE(adapter, NETXEN_FW_VERSION_SUB)); + write_lock_irqsave(&adapter->adapter_lock, flags); + fw_major = adapter->pci_read_normalize(adapter, + NETXEN_FW_VERSION_MAJOR); + fw_minor = adapter->pci_read_normalize(adapter, + NETXEN_FW_VERSION_MINOR); + fw_build = adapter->pci_read_normalize(adapter, + NETXEN_FW_VERSION_SUB); + write_unlock_irqrestore(&adapter->adapter_lock, flags); sprintf(drvinfo->fw_version, "%d.%d.%d", fw_major, fw_minor, fw_build); strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); @@ -159,9 +163,16 @@ netxen_nic_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) switch ((netxen_brdtype_t) boardinfo->board_type) { case NETXEN_BRDTYPE_P2_SB35_4G: case NETXEN_BRDTYPE_P2_SB31_2G: + case NETXEN_BRDTYPE_P3_REF_QG: + case NETXEN_BRDTYPE_P3_4_GB: + case NETXEN_BRDTYPE_P3_4_GB_MM: + case NETXEN_BRDTYPE_P3_10000_BASE_T: + ecmd->supported |= SUPPORTED_Autoneg; ecmd->advertising |= ADVERTISED_Autoneg; case NETXEN_BRDTYPE_P2_SB31_10G_CX4: + case NETXEN_BRDTYPE_P3_10G_CX4: + case NETXEN_BRDTYPE_P3_10G_CX4_LP: ecmd->supported |= SUPPORTED_TP; ecmd->advertising |= ADVERTISED_TP; ecmd->port = PORT_TP; @@ -171,12 +182,17 @@ netxen_nic_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) break; case NETXEN_BRDTYPE_P2_SB31_10G_HMEZ: case NETXEN_BRDTYPE_P2_SB31_10G_IMEZ: + case NETXEN_BRDTYPE_P3_IMEZ: + case NETXEN_BRDTYPE_P3_XG_LOM: + case NETXEN_BRDTYPE_P3_HMEZ: ecmd->supported |= SUPPORTED_MII; ecmd->advertising |= ADVERTISED_MII; ecmd->port = PORT_FIBRE; ecmd->autoneg = AUTONEG_DISABLE; break; case NETXEN_BRDTYPE_P2_SB31_10G: + case NETXEN_BRDTYPE_P3_10G_SFP_PLUS: + case NETXEN_BRDTYPE_P3_10G_XFP: ecmd->supported |= SUPPORTED_FIBRE; ecmd->advertising |= ADVERTISED_FIBRE; ecmd->port = PORT_FIBRE; @@ -349,19 +365,18 @@ netxen_nic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) { 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) | (adapter->pdev)->device; /* which mode */ - NETXEN_NIC_LOCKED_READ_REG(NETXEN_NIU_MODE, ®s_buff[0]); + adapter->hw_read_wx(adapter, NETXEN_NIU_MODE, ®s_buff[0], 4); mode = regs_buff[0]; /* Common registers to all the modes */ - NETXEN_NIC_LOCKED_READ_REG(NETXEN_NIU_STRAP_VALUE_SAVE_HIGHER, - ®s_buff[2]); + adapter->hw_read_wx(adapter, + NETXEN_NIU_STRAP_VALUE_SAVE_HIGHER, ®s_buff[2], 4); /* GB/XGB Mode */ mode = (mode / 2) - 1; window = 0; @@ -372,9 +387,9 @@ netxen_nic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) window = adapter->physical_port * NETXEN_NIC_PORT_WINDOW; - NETXEN_NIC_LOCKED_READ_REG(niu_registers[mode]. - reg[i - 3] + window, - ®s_buff[i]); + adapter->hw_read_wx(adapter, + niu_registers[mode].reg[i - 3] + window, + ®s_buff[i], 4); } } @@ -398,7 +413,7 @@ static u32 netxen_nic_test_link(struct net_device *dev) return !val; } } else if (adapter->ahw.board_type == NETXEN_NIC_XGBE) { - val = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_XG_STATE)); + val = adapter->pci_read_normalize(adapter, CRB_XG_STATE); return (val == XG_LINK_UP) ? 0 : 1; } return -EIO; @@ -427,6 +442,7 @@ netxen_nic_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, return 0; } +#if 0 static int netxen_nic_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 * bytes) @@ -447,7 +463,6 @@ 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", @@ -497,6 +512,7 @@ netxen_nic_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, return netxen_rom_fast_write_words(adapter, offset, bytes, eeprom->len); } +#endif /* 0 */ static void netxen_nic_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ring) @@ -508,9 +524,9 @@ netxen_nic_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ring) ring->rx_jumbo_pending = 0; for (i = 0; i < MAX_RCV_CTX; ++i) { ring->rx_pending += adapter->recv_ctx[i]. - rcv_desc[RCV_DESC_NORMAL_CTXID].max_rx_desc_count; + rds_rings[RCV_DESC_NORMAL_CTXID].max_rx_desc_count; ring->rx_jumbo_pending += adapter->recv_ctx[i]. - rcv_desc[RCV_DESC_JUMBO_CTXID].max_rx_desc_count; + rds_rings[RCV_DESC_JUMBO_CTXID].max_rx_desc_count; } ring->tx_pending = adapter->max_tx_desc_count; @@ -655,7 +671,7 @@ static int netxen_nic_reg_test(struct net_device *dev) data_written = (u32)0xa5a5a5a5; netxen_nic_reg_write(adapter, CRB_SCRATCHPAD_TEST, data_written); - data_read = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_SCRATCHPAD_TEST)); + data_read = adapter->pci_read_normalize(adapter, CRB_SCRATCHPAD_TEST); if (data_written != data_read) return 1; @@ -736,6 +752,117 @@ static int netxen_nic_set_rx_csum(struct net_device *dev, u32 data) return 0; } +static u32 netxen_nic_get_tso(struct net_device *dev) +{ + struct netxen_adapter *adapter = netdev_priv(dev); + + if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) + return (dev->features & (NETIF_F_TSO | NETIF_F_TSO6)) != 0; + + return (dev->features & NETIF_F_TSO) != 0; +} + +static int netxen_nic_set_tso(struct net_device *dev, u32 data) +{ + if (data) { + struct netxen_adapter *adapter = netdev_priv(dev); + + dev->features |= NETIF_F_TSO; + if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) + dev->features |= NETIF_F_TSO6; + } else + dev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6); + + return 0; +} + +/* + * Set the coalescing parameters. Currently only normal is supported. + * If rx_coalesce_usecs == 0 or rx_max_coalesced_frames == 0 then set the + * firmware coalescing to default. + */ +static int netxen_set_intr_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ethcoal) +{ + struct netxen_adapter *adapter = netdev_priv(netdev); + + if (!NX_IS_REVISION_P3(adapter->ahw.revision_id)) + return -EINVAL; + + if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC) + return -EINVAL; + + /* + * Return Error if unsupported values or + * unsupported parameters are set. + */ + if (ethcoal->rx_coalesce_usecs > 0xffff || + ethcoal->rx_max_coalesced_frames > 0xffff || + ethcoal->tx_coalesce_usecs > 0xffff || + ethcoal->tx_max_coalesced_frames > 0xffff || + ethcoal->rx_coalesce_usecs_irq || + ethcoal->rx_max_coalesced_frames_irq || + ethcoal->tx_coalesce_usecs_irq || + ethcoal->tx_max_coalesced_frames_irq || + ethcoal->stats_block_coalesce_usecs || + ethcoal->use_adaptive_rx_coalesce || + ethcoal->use_adaptive_tx_coalesce || + ethcoal->pkt_rate_low || + ethcoal->rx_coalesce_usecs_low || + ethcoal->rx_max_coalesced_frames_low || + ethcoal->tx_coalesce_usecs_low || + ethcoal->tx_max_coalesced_frames_low || + ethcoal->pkt_rate_high || + ethcoal->rx_coalesce_usecs_high || + ethcoal->rx_max_coalesced_frames_high || + ethcoal->tx_coalesce_usecs_high || + ethcoal->tx_max_coalesced_frames_high) + return -EINVAL; + + if (!ethcoal->rx_coalesce_usecs || + !ethcoal->rx_max_coalesced_frames) { + adapter->coal.flags = NETXEN_NIC_INTR_DEFAULT; + adapter->coal.normal.data.rx_time_us = + NETXEN_DEFAULT_INTR_COALESCE_RX_TIME_US; + adapter->coal.normal.data.rx_packets = + NETXEN_DEFAULT_INTR_COALESCE_RX_PACKETS; + } else { + adapter->coal.flags = 0; + adapter->coal.normal.data.rx_time_us = + ethcoal->rx_coalesce_usecs; + adapter->coal.normal.data.rx_packets = + ethcoal->rx_max_coalesced_frames; + } + adapter->coal.normal.data.tx_time_us = ethcoal->tx_coalesce_usecs; + adapter->coal.normal.data.tx_packets = + ethcoal->tx_max_coalesced_frames; + + netxen_config_intr_coalesce(adapter); + + return 0; +} + +static int netxen_get_intr_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ethcoal) +{ + struct netxen_adapter *adapter = netdev_priv(netdev); + + if (!NX_IS_REVISION_P3(adapter->ahw.revision_id)) + return -EINVAL; + + if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC) + return -EINVAL; + + ethcoal->rx_coalesce_usecs = adapter->coal.normal.data.rx_time_us; + ethcoal->tx_coalesce_usecs = adapter->coal.normal.data.tx_time_us; + ethcoal->rx_max_coalesced_frames = + adapter->coal.normal.data.rx_packets; + ethcoal->tx_max_coalesced_frames = + adapter->coal.normal.data.tx_packets; + + return 0; +} + struct ethtool_ops netxen_nic_ethtool_ops = { .get_settings = netxen_nic_get_settings, .set_settings = netxen_nic_set_settings, @@ -745,17 +872,22 @@ struct ethtool_ops netxen_nic_ethtool_ops = { .get_link = ethtool_op_get_link, .get_eeprom_len = netxen_nic_get_eeprom_len, .get_eeprom = netxen_nic_get_eeprom, +#if 0 .set_eeprom = netxen_nic_set_eeprom, +#endif .get_ringparam = netxen_nic_get_ringparam, .get_pauseparam = netxen_nic_get_pauseparam, .set_pauseparam = netxen_nic_set_pauseparam, .set_tx_csum = ethtool_op_set_tx_csum, .set_sg = ethtool_op_set_sg, - .set_tso = ethtool_op_set_tso, + .get_tso = netxen_nic_get_tso, + .set_tso = netxen_nic_set_tso, .self_test = netxen_nic_diag_test, .get_strings = netxen_nic_get_strings, .get_ethtool_stats = netxen_nic_get_ethtool_stats, .get_sset_count = netxen_get_sset_count, .get_rx_csum = netxen_nic_get_rx_csum, .set_rx_csum = netxen_nic_set_rx_csum, + .get_coalesce = netxen_get_intr_coalesce, + .set_coalesce = netxen_set_intr_coalesce, }; diff --git a/drivers/net/netxen/netxen_nic_hdr.h b/drivers/net/netxen/netxen_nic_hdr.h index 24d027e29c45..3ce13e451aac 100644 --- a/drivers/net/netxen/netxen_nic_hdr.h +++ b/drivers/net/netxen/netxen_nic_hdr.h @@ -126,7 +126,8 @@ enum { NETXEN_HW_PEGR0_CRB_AGT_ADR, NETXEN_HW_PEGR1_CRB_AGT_ADR, NETXEN_HW_PEGR2_CRB_AGT_ADR, - NETXEN_HW_PEGR3_CRB_AGT_ADR + NETXEN_HW_PEGR3_CRB_AGT_ADR, + NETXEN_HW_PEGN4_CRB_AGT_ADR }; /* Hub 5 */ @@ -316,6 +317,8 @@ enum { ((NETXEN_HW_H4_CH_HUB_ADR << 7) | NETXEN_HW_PEGN2_CRB_AGT_ADR) #define NETXEN_HW_CRB_HUB_AGT_ADR_PGN3 \ ((NETXEN_HW_H4_CH_HUB_ADR << 7) | NETXEN_HW_PEGN3_CRB_AGT_ADR) +#define NETXEN_HW_CRB_HUB_AGT_ADR_PGN4 \ + ((NETXEN_HW_H4_CH_HUB_ADR << 7) | NETXEN_HW_PEGN4_CRB_AGT_ADR) #define NETXEN_HW_CRB_HUB_AGT_ADR_PGNC \ ((NETXEN_HW_H4_CH_HUB_ADR << 7) | NETXEN_HW_PEGNC_CRB_AGT_ADR) #define NETXEN_HW_CRB_HUB_AGT_ADR_PGR0 \ @@ -435,6 +438,7 @@ enum { #define NETXEN_CRB_ROMUSB \ NETXEN_PCI_CRB_WINDOW(NETXEN_HW_PX_MAP_CRB_ROMUSB) #define NETXEN_CRB_I2Q NETXEN_PCI_CRB_WINDOW(NETXEN_HW_PX_MAP_CRB_I2Q) +#define NETXEN_CRB_SMB NETXEN_PCI_CRB_WINDOW(NETXEN_HW_PX_MAP_CRB_SMB) #define NETXEN_CRB_MAX NETXEN_PCI_CRB_WINDOW(64) #define NETXEN_CRB_PCIX_HOST NETXEN_PCI_CRB_WINDOW(NETXEN_HW_PX_MAP_CRB_PH) @@ -446,6 +450,7 @@ enum { #define NETXEN_CRB_PEG_NET_D NETXEN_PCI_CRB_WINDOW(NETXEN_HW_PX_MAP_CRB_PGND) #define NETXEN_CRB_PEG_NET_I NETXEN_PCI_CRB_WINDOW(NETXEN_HW_PX_MAP_CRB_PGNI) #define NETXEN_CRB_DDR_NET NETXEN_PCI_CRB_WINDOW(NETXEN_HW_PX_MAP_CRB_MN) +#define NETXEN_CRB_QDR_NET NETXEN_PCI_CRB_WINDOW(NETXEN_HW_PX_MAP_CRB_SN) #define NETXEN_CRB_PCIX_MD NETXEN_PCI_CRB_WINDOW(NETXEN_HW_PX_MAP_CRB_PS) #define NETXEN_CRB_PCIE NETXEN_CRB_PCIX_MD @@ -461,11 +466,20 @@ enum { #define ISR_INT_TARGET_MASK_F2 (NETXEN_PCIX_PS_REG(PCIX_TARGET_MASK_F2)) #define ISR_INT_TARGET_STATUS_F3 (NETXEN_PCIX_PS_REG(PCIX_TARGET_STATUS_F3)) #define ISR_INT_TARGET_MASK_F3 (NETXEN_PCIX_PS_REG(PCIX_TARGET_MASK_F3)) +#define ISR_INT_TARGET_STATUS_F4 (NETXEN_PCIX_PS_REG(PCIX_TARGET_STATUS_F4)) +#define ISR_INT_TARGET_MASK_F4 (NETXEN_PCIX_PS_REG(PCIX_TARGET_MASK_F4)) +#define ISR_INT_TARGET_STATUS_F5 (NETXEN_PCIX_PS_REG(PCIX_TARGET_STATUS_F5)) +#define ISR_INT_TARGET_MASK_F5 (NETXEN_PCIX_PS_REG(PCIX_TARGET_MASK_F5)) +#define ISR_INT_TARGET_STATUS_F6 (NETXEN_PCIX_PS_REG(PCIX_TARGET_STATUS_F6)) +#define ISR_INT_TARGET_MASK_F6 (NETXEN_PCIX_PS_REG(PCIX_TARGET_MASK_F6)) +#define ISR_INT_TARGET_STATUS_F7 (NETXEN_PCIX_PS_REG(PCIX_TARGET_STATUS_F7)) +#define ISR_INT_TARGET_MASK_F7 (NETXEN_PCIX_PS_REG(PCIX_TARGET_MASK_F7)) #define NETXEN_PCI_MAPSIZE 128 #define NETXEN_PCI_DDR_NET (0x00000000UL) #define NETXEN_PCI_QDR_NET (0x04000000UL) #define NETXEN_PCI_DIRECT_CRB (0x04400000UL) +#define NETXEN_PCI_CAMQM (0x04800000UL) #define NETXEN_PCI_CAMQM_MAX (0x04ffffffUL) #define NETXEN_PCI_OCM0 (0x05000000UL) #define NETXEN_PCI_OCM0_MAX (0x050fffffUL) @@ -474,6 +488,13 @@ enum { #define NETXEN_PCI_CRBSPACE (0x06000000UL) #define NETXEN_PCI_128MB_SIZE (0x08000000UL) #define NETXEN_PCI_32MB_SIZE (0x02000000UL) +#define NETXEN_PCI_2MB_SIZE (0x00200000UL) + +#define NETXEN_PCI_MN_2M (0) +#define NETXEN_PCI_MS_2M (0x80000) +#define NETXEN_PCI_OCM0_2M (0x000c0000UL) +#define NETXEN_PCI_CAMQM_2M_BASE (0x000ff800UL) +#define NETXEN_PCI_CAMQM_2M_END (0x04800800UL) #define NETXEN_CRB_CAM NETXEN_PCI_CRB_WINDOW(NETXEN_HW_PX_MAP_CRB_CAM) @@ -484,7 +505,14 @@ enum { #define NETXEN_ADDR_OCM1 (0x0000000200400000ULL) #define NETXEN_ADDR_OCM1_MAX (0x00000002004fffffULL) #define NETXEN_ADDR_QDR_NET (0x0000000300000000ULL) -#define NETXEN_ADDR_QDR_NET_MAX (0x00000003003fffffULL) +#define NETXEN_ADDR_QDR_NET_MAX_P2 (0x00000003003fffffULL) +#define NETXEN_ADDR_QDR_NET_MAX_P3 (0x0000000303ffffffULL) + +/* + * Register offsets for MN + */ +#define NETXEN_MIU_CONTROL (0x000) +#define NETXEN_MIU_MN_CONTROL (NETXEN_CRB_DDR_NET+NETXEN_MIU_CONTROL) /* 200ms delay in each loop */ #define NETXEN_NIU_PHY_WAITLEN 200000 @@ -550,6 +578,9 @@ enum { #define NETXEN_MULTICAST_ADDR_HI_2 (NETXEN_CRB_NIU + 0x1018) #define NETXEN_MULTICAST_ADDR_HI_3 (NETXEN_CRB_NIU + 0x101c) +#define NETXEN_UNICAST_ADDR_BASE (NETXEN_CRB_NIU + 0x1080) +#define NETXEN_MULTICAST_ADDR_BASE (NETXEN_CRB_NIU + 0x1100) + #define NETXEN_NIU_GB_MAC_CONFIG_0(I) \ (NETXEN_CRB_NIU + 0x30000 + (I)*0x10000) #define NETXEN_NIU_GB_MAC_CONFIG_1(I) \ @@ -630,16 +661,76 @@ enum { #define NETXEN_NIU_XG1_CONTROL_CHAR_CNT (NETXEN_CRB_NIU + 0x80054) #define NETXEN_NIU_XG1_PAUSE_FRAME_CNT (NETXEN_CRB_NIU + 0x80058) +/* P3 802.3ap */ +#define NETXEN_NIU_AP_MAC_CONFIG_0(I) (NETXEN_CRB_NIU+0xa0000+(I)*0x10000) +#define NETXEN_NIU_AP_MAC_CONFIG_1(I) (NETXEN_CRB_NIU+0xa0004+(I)*0x10000) +#define NETXEN_NIU_AP_MAC_IPG_IFG(I) (NETXEN_CRB_NIU+0xa0008+(I)*0x10000) +#define NETXEN_NIU_AP_HALF_DUPLEX_CTRL(I) (NETXEN_CRB_NIU+0xa000c+(I)*0x10000) +#define NETXEN_NIU_AP_MAX_FRAME_SIZE(I) (NETXEN_CRB_NIU+0xa0010+(I)*0x10000) +#define NETXEN_NIU_AP_TEST_REG(I) (NETXEN_CRB_NIU+0xa001c+(I)*0x10000) +#define NETXEN_NIU_AP_MII_MGMT_CONFIG(I) (NETXEN_CRB_NIU+0xa0020+(I)*0x10000) +#define NETXEN_NIU_AP_MII_MGMT_COMMAND(I) (NETXEN_CRB_NIU+0xa0024+(I)*0x10000) +#define NETXEN_NIU_AP_MII_MGMT_ADDR(I) (NETXEN_CRB_NIU+0xa0028+(I)*0x10000) +#define NETXEN_NIU_AP_MII_MGMT_CTRL(I) (NETXEN_CRB_NIU+0xa002c+(I)*0x10000) +#define NETXEN_NIU_AP_MII_MGMT_STATUS(I) (NETXEN_CRB_NIU+0xa0030+(I)*0x10000) +#define NETXEN_NIU_AP_MII_MGMT_INDICATE(I) (NETXEN_CRB_NIU+0xa0034+(I)*0x10000) +#define NETXEN_NIU_AP_INTERFACE_CTRL(I) (NETXEN_CRB_NIU+0xa0038+(I)*0x10000) +#define NETXEN_NIU_AP_INTERFACE_STATUS(I) (NETXEN_CRB_NIU+0xa003c+(I)*0x10000) +#define NETXEN_NIU_AP_STATION_ADDR_0(I) (NETXEN_CRB_NIU+0xa0040+(I)*0x10000) +#define NETXEN_NIU_AP_STATION_ADDR_1(I) (NETXEN_CRB_NIU+0xa0044+(I)*0x10000) + +/* + * Register offsets for MN + */ +#define MIU_CONTROL (0x000) +#define MIU_TEST_AGT_CTRL (0x090) +#define MIU_TEST_AGT_ADDR_LO (0x094) +#define MIU_TEST_AGT_ADDR_HI (0x098) +#define MIU_TEST_AGT_WRDATA_LO (0x0a0) +#define MIU_TEST_AGT_WRDATA_HI (0x0a4) +#define MIU_TEST_AGT_WRDATA(i) (0x0a0+(4*(i))) +#define MIU_TEST_AGT_RDDATA_LO (0x0a8) +#define MIU_TEST_AGT_RDDATA_HI (0x0ac) +#define MIU_TEST_AGT_RDDATA(i) (0x0a8+(4*(i))) +#define MIU_TEST_AGT_ADDR_MASK 0xfffffff8 +#define MIU_TEST_AGT_UPPER_ADDR(off) (0) + +/* MIU_TEST_AGT_CTRL flags. work for SIU as well */ +#define MIU_TA_CTL_START 1 +#define MIU_TA_CTL_ENABLE 2 +#define MIU_TA_CTL_WRITE 4 +#define MIU_TA_CTL_BUSY 8 + +#define SIU_TEST_AGT_CTRL (0x060) +#define SIU_TEST_AGT_ADDR_LO (0x064) +#define SIU_TEST_AGT_ADDR_HI (0x078) +#define SIU_TEST_AGT_WRDATA_LO (0x068) +#define SIU_TEST_AGT_WRDATA_HI (0x06c) +#define SIU_TEST_AGT_WRDATA(i) (0x068+(4*(i))) +#define SIU_TEST_AGT_RDDATA_LO (0x070) +#define SIU_TEST_AGT_RDDATA_HI (0x074) +#define SIU_TEST_AGT_RDDATA(i) (0x070+(4*(i))) + +#define SIU_TEST_AGT_ADDR_MASK 0x3ffff8 +#define SIU_TEST_AGT_UPPER_ADDR(off) ((off)>>22) + /* XG Link status */ #define XG_LINK_UP 0x10 #define XG_LINK_DOWN 0x20 +#define XG_LINK_UP_P3 0x01 +#define XG_LINK_DOWN_P3 0x02 +#define XG_LINK_STATE_P3_MASK 0xf +#define XG_LINK_STATE_P3(pcifn,val) \ + (((val) >> ((pcifn) * 4)) & XG_LINK_STATE_P3_MASK) + #define NETXEN_CAM_RAM_BASE (NETXEN_CRB_CAM + 0x02000) #define NETXEN_CAM_RAM(reg) (NETXEN_CAM_RAM_BASE + (reg)) #define NETXEN_FW_VERSION_MAJOR (NETXEN_CAM_RAM(0x150)) #define NETXEN_FW_VERSION_MINOR (NETXEN_CAM_RAM(0x154)) #define NETXEN_FW_VERSION_SUB (NETXEN_CAM_RAM(0x158)) #define NETXEN_ROM_LOCK_ID (NETXEN_CAM_RAM(0x100)) +#define NETXEN_CRB_WIN_LOCK_ID (NETXEN_CAM_RAM(0x124)) #define NETXEN_PHY_LOCK_ID (NETXEN_CAM_RAM(0x120)) @@ -654,30 +745,71 @@ enum { #define PCIX_INT_VECTOR (0x10100) #define PCIX_INT_MASK (0x10104) -#define PCIX_MN_WINDOW_F0 (0x10200) -#define PCIX_MN_WINDOW(_f) (PCIX_MN_WINDOW_F0 + (0x20 * (_f))) -#define PCIX_MS_WINDOW (0x10204) -#define PCIX_SN_WINDOW_F0 (0x10208) -#define PCIX_SN_WINDOW(_f) (PCIX_SN_WINDOW_F0 + (0x20 * (_f))) #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_CRB_WINDOW_F4 (0x102ac) +#define PCIX_CRB_WINDOW_F5 (0x102bc) +#define PCIX_CRB_WINDOW_F6 (0x102cc) +#define PCIX_CRB_WINDOW_F7 (0x102dc) +#define PCIE_CRB_WINDOW_REG(func) (((func) < 4) ? \ + (PCIX_CRB_WINDOW_F0 + (0x20 * (func))) :\ + (PCIX_CRB_WINDOW_F4 + (0x10 * ((func)-4)))) + +#define PCIX_MN_WINDOW (0x10200) +#define PCIX_MN_WINDOW_F0 (0x10200) +#define PCIX_MN_WINDOW_F1 (0x10220) +#define PCIX_MN_WINDOW_F2 (0x10240) +#define PCIX_MN_WINDOW_F3 (0x10260) +#define PCIX_MN_WINDOW_F4 (0x102a0) +#define PCIX_MN_WINDOW_F5 (0x102b0) +#define PCIX_MN_WINDOW_F6 (0x102c0) +#define PCIX_MN_WINDOW_F7 (0x102d0) +#define PCIE_MN_WINDOW_REG(func) (((func) < 4) ? \ + (PCIX_MN_WINDOW_F0 + (0x20 * (func))) :\ + (PCIX_MN_WINDOW_F4 + (0x10 * ((func)-4)))) + +#define PCIX_SN_WINDOW (0x10208) +#define PCIX_SN_WINDOW_F0 (0x10208) +#define PCIX_SN_WINDOW_F1 (0x10228) +#define PCIX_SN_WINDOW_F2 (0x10248) +#define PCIX_SN_WINDOW_F3 (0x10268) +#define PCIX_SN_WINDOW_F4 (0x102a8) +#define PCIX_SN_WINDOW_F5 (0x102b8) +#define PCIX_SN_WINDOW_F6 (0x102c8) +#define PCIX_SN_WINDOW_F7 (0x102d8) +#define PCIE_SN_WINDOW_REG(func) (((func) < 4) ? \ + (PCIX_SN_WINDOW_F0 + (0x20 * (func))) :\ + (PCIX_SN_WINDOW_F4 + (0x10 * ((func)-4)))) #define PCIX_TARGET_STATUS (0x10118) +#define PCIX_TARGET_STATUS_F1 (0x10160) +#define PCIX_TARGET_STATUS_F2 (0x10164) +#define PCIX_TARGET_STATUS_F3 (0x10168) +#define PCIX_TARGET_STATUS_F4 (0x10360) +#define PCIX_TARGET_STATUS_F5 (0x10364) +#define PCIX_TARGET_STATUS_F6 (0x10368) +#define PCIX_TARGET_STATUS_F7 (0x1036c) + #define PCIX_TARGET_MASK (0x10128) -#define PCIX_TARGET_STATUS_F1 (0x10160) -#define PCIX_TARGET_MASK_F1 (0x10170) -#define PCIX_TARGET_STATUS_F2 (0x10164) -#define PCIX_TARGET_MASK_F2 (0x10174) -#define PCIX_TARGET_STATUS_F3 (0x10168) -#define PCIX_TARGET_MASK_F3 (0x10178) +#define PCIX_TARGET_MASK_F1 (0x10170) +#define PCIX_TARGET_MASK_F2 (0x10174) +#define PCIX_TARGET_MASK_F3 (0x10178) +#define PCIX_TARGET_MASK_F4 (0x10370) +#define PCIX_TARGET_MASK_F5 (0x10374) +#define PCIX_TARGET_MASK_F6 (0x10378) +#define PCIX_TARGET_MASK_F7 (0x1037c) #define PCIX_MSI_F0 (0x13000) #define PCIX_MSI_F1 (0x13004) #define PCIX_MSI_F2 (0x13008) #define PCIX_MSI_F3 (0x1300c) +#define PCIX_MSI_F4 (0x13010) +#define PCIX_MSI_F5 (0x13014) +#define PCIX_MSI_F6 (0x13018) +#define PCIX_MSI_F7 (0x1301c) #define PCIX_MSI_F(i) (0x13000+((i)*4)) #define PCIX_PS_MEM_SPACE (0x90000) @@ -695,11 +827,102 @@ enum { #define PCIE_SEM2_UNLOCK (0x1c014) /* Flash unlock */ #define PCIE_SEM3_LOCK (0x1c018) /* Phy lock */ #define PCIE_SEM3_UNLOCK (0x1c01c) /* Phy unlock */ - +#define PCIE_SEM5_LOCK (0x1c028) /* API lock */ +#define PCIE_SEM5_UNLOCK (0x1c02c) /* API unlock */ +#define PCIE_SEM6_LOCK (0x1c030) /* sw lock */ +#define PCIE_SEM6_UNLOCK (0x1c034) /* sw unlock */ +#define PCIE_SEM7_LOCK (0x1c038) /* crb win lock */ +#define PCIE_SEM7_UNLOCK (0x1c03c) /* crbwin unlock*/ + +#define PCIE_SETUP_FUNCTION (0x12040) +#define PCIE_SETUP_FUNCTION2 (0x12048) #define PCIE_TGT_SPLIT_CHICKEN (0x12080) +#define PCIE_CHICKEN3 (0x120c8) #define PCIE_MAX_MASTER_SPLIT (0x14048) +#define NETXEN_PORT_MODE_NONE 0 +#define NETXEN_PORT_MODE_XG 1 +#define NETXEN_PORT_MODE_GB 2 +#define NETXEN_PORT_MODE_802_3_AP 3 +#define NETXEN_PORT_MODE_AUTO_NEG 4 +#define NETXEN_PORT_MODE_AUTO_NEG_1G 5 +#define NETXEN_PORT_MODE_AUTO_NEG_XG 6 +#define NETXEN_PORT_MODE_ADDR (NETXEN_CAM_RAM(0x24)) +#define NETXEN_WOL_PORT_MODE (NETXEN_CAM_RAM(0x198)) + #define NETXEN_CAM_RAM_DMA_WATCHDOG_CTRL (0x14) +#define ISR_MSI_INT_TRIGGER(FUNC) (NETXEN_PCIX_PS_REG(PCIX_MSI_F(FUNC))) + +/* + * PCI Interrupt Vector Values. + */ +#define PCIX_INT_VECTOR_BIT_F0 0x0080 +#define PCIX_INT_VECTOR_BIT_F1 0x0100 +#define PCIX_INT_VECTOR_BIT_F2 0x0200 +#define PCIX_INT_VECTOR_BIT_F3 0x0400 +#define PCIX_INT_VECTOR_BIT_F4 0x0800 +#define PCIX_INT_VECTOR_BIT_F5 0x1000 +#define PCIX_INT_VECTOR_BIT_F6 0x2000 +#define PCIX_INT_VECTOR_BIT_F7 0x4000 + +struct netxen_legacy_intr_set { + uint32_t int_vec_bit; + uint32_t tgt_status_reg; + uint32_t tgt_mask_reg; + uint32_t pci_int_reg; +}; + +#define NX_LEGACY_INTR_CONFIG \ +{ \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F0, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(0) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F1, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F1, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F1, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(1) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F2, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F2, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F2, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(2) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F3, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F3, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F3, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(3) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F4, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F4, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F4, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(4) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F5, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F5, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F5, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(5) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F6, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F6, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F6, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(6) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F7, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F7, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F7, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(7) }, \ +} + #endif /* __NETXEN_NIC_HDR_H_ */ diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c index c43d06b8de9b..96a3bc6426e2 100644 --- a/drivers/net/netxen/netxen_nic_hw.c +++ b/drivers/net/netxen/netxen_nic_hw.c @@ -38,242 +38,262 @@ #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), - }, +#define MASK(n) ((1ULL<<(n))-1) +#define MN_WIN(addr) (((addr & 0x1fc0000) >> 1) | ((addr >> 25) & 0x3ff)) +#define OCM_WIN(addr) (((addr & 0x1ff0000) >> 1) | ((addr >> 25) & 0x3ff)) +#define MS_WIN(addr) (addr & 0x0ffc0000) + +#define GET_MEM_OFFS_2M(addr) (addr & MASK(18)) + +#define CRB_BLK(off) ((off >> 20) & 0x3f) +#define CRB_SUBBLK(off) ((off >> 16) & 0xf) +#define CRB_WINDOW_2M (0x130060) +#define CRB_HI(off) ((crb_hub_agt[CRB_BLK(off)] << 20) | ((off) & 0xf0000)) +#define CRB_INDIRECT_2M (0x1e0000UL) + +#define CRB_WIN_LOCK_TIMEOUT 100000000 +static crb_128M_2M_block_map_t crb_128M_2M_map[64] = { + {{{0, 0, 0, 0} } }, /* 0: PCI */ + {{{1, 0x0100000, 0x0102000, 0x120000}, /* 1: PCIE */ + {1, 0x0110000, 0x0120000, 0x130000}, + {1, 0x0120000, 0x0122000, 0x124000}, + {1, 0x0130000, 0x0132000, 0x126000}, + {1, 0x0140000, 0x0142000, 0x128000}, + {1, 0x0150000, 0x0152000, 0x12a000}, + {1, 0x0160000, 0x0170000, 0x110000}, + {1, 0x0170000, 0x0172000, 0x12e000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {1, 0x01e0000, 0x01e0800, 0x122000}, + {0, 0x0000000, 0x0000000, 0x000000} } }, + {{{1, 0x0200000, 0x0210000, 0x180000} } },/* 2: MN */ + {{{0, 0, 0, 0} } }, /* 3: */ + {{{1, 0x0400000, 0x0401000, 0x169000} } },/* 4: P2NR1 */ + {{{1, 0x0500000, 0x0510000, 0x140000} } },/* 5: SRE */ + {{{1, 0x0600000, 0x0610000, 0x1c0000} } },/* 6: NIU */ + {{{1, 0x0700000, 0x0704000, 0x1b8000} } },/* 7: QM */ + {{{1, 0x0800000, 0x0802000, 0x170000}, /* 8: SQM0 */ + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {1, 0x08f0000, 0x08f2000, 0x172000} } }, + {{{1, 0x0900000, 0x0902000, 0x174000}, /* 9: SQM1*/ + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {1, 0x09f0000, 0x09f2000, 0x176000} } }, + {{{0, 0x0a00000, 0x0a02000, 0x178000}, /* 10: SQM2*/ + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {1, 0x0af0000, 0x0af2000, 0x17a000} } }, + {{{0, 0x0b00000, 0x0b02000, 0x17c000}, /* 11: SQM3*/ + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {1, 0x0bf0000, 0x0bf2000, 0x17e000} } }, + {{{1, 0x0c00000, 0x0c04000, 0x1d4000} } },/* 12: I2Q */ + {{{1, 0x0d00000, 0x0d04000, 0x1a4000} } },/* 13: TMR */ + {{{1, 0x0e00000, 0x0e04000, 0x1a0000} } },/* 14: ROMUSB */ + {{{1, 0x0f00000, 0x0f01000, 0x164000} } },/* 15: PEG4 */ + {{{0, 0x1000000, 0x1004000, 0x1a8000} } },/* 16: XDMA */ + {{{1, 0x1100000, 0x1101000, 0x160000} } },/* 17: PEG0 */ + {{{1, 0x1200000, 0x1201000, 0x161000} } },/* 18: PEG1 */ + {{{1, 0x1300000, 0x1301000, 0x162000} } },/* 19: PEG2 */ + {{{1, 0x1400000, 0x1401000, 0x163000} } },/* 20: PEG3 */ + {{{1, 0x1500000, 0x1501000, 0x165000} } },/* 21: P2ND */ + {{{1, 0x1600000, 0x1601000, 0x166000} } },/* 22: P2NI */ + {{{0, 0, 0, 0} } }, /* 23: */ + {{{0, 0, 0, 0} } }, /* 24: */ + {{{0, 0, 0, 0} } }, /* 25: */ + {{{0, 0, 0, 0} } }, /* 26: */ + {{{0, 0, 0, 0} } }, /* 27: */ + {{{0, 0, 0, 0} } }, /* 28: */ + {{{1, 0x1d00000, 0x1d10000, 0x190000} } },/* 29: MS */ + {{{1, 0x1e00000, 0x1e01000, 0x16a000} } },/* 30: P2NR2 */ + {{{1, 0x1f00000, 0x1f10000, 0x150000} } },/* 31: EPG */ + {{{0} } }, /* 32: PCI */ + {{{1, 0x2100000, 0x2102000, 0x120000}, /* 33: PCIE */ + {1, 0x2110000, 0x2120000, 0x130000}, + {1, 0x2120000, 0x2122000, 0x124000}, + {1, 0x2130000, 0x2132000, 0x126000}, + {1, 0x2140000, 0x2142000, 0x128000}, + {1, 0x2150000, 0x2152000, 0x12a000}, + {1, 0x2160000, 0x2170000, 0x110000}, + {1, 0x2170000, 0x2172000, 0x12e000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000} } }, + {{{1, 0x2200000, 0x2204000, 0x1b0000} } },/* 34: CAM */ + {{{0} } }, /* 35: */ + {{{0} } }, /* 36: */ + {{{0} } }, /* 37: */ + {{{0} } }, /* 38: */ + {{{0} } }, /* 39: */ + {{{1, 0x2800000, 0x2804000, 0x1a4000} } },/* 40: TMR */ + {{{1, 0x2900000, 0x2901000, 0x16b000} } },/* 41: P2NR3 */ + {{{1, 0x2a00000, 0x2a00400, 0x1ac400} } },/* 42: RPMX1 */ + {{{1, 0x2b00000, 0x2b00400, 0x1ac800} } },/* 43: RPMX2 */ + {{{1, 0x2c00000, 0x2c00400, 0x1acc00} } },/* 44: RPMX3 */ + {{{1, 0x2d00000, 0x2d00400, 0x1ad000} } },/* 45: RPMX4 */ + {{{1, 0x2e00000, 0x2e00400, 0x1ad400} } },/* 46: RPMX5 */ + {{{1, 0x2f00000, 0x2f00400, 0x1ad800} } },/* 47: RPMX6 */ + {{{1, 0x3000000, 0x3000400, 0x1adc00} } },/* 48: RPMX7 */ + {{{0, 0x3100000, 0x3104000, 0x1a8000} } },/* 49: XDMA */ + {{{1, 0x3200000, 0x3204000, 0x1d4000} } },/* 50: I2Q */ + {{{1, 0x3300000, 0x3304000, 0x1a0000} } },/* 51: ROMUSB */ + {{{0} } }, /* 52: */ + {{{1, 0x3500000, 0x3500400, 0x1ac000} } },/* 53: RPMX0 */ + {{{1, 0x3600000, 0x3600400, 0x1ae000} } },/* 54: RPMX8 */ + {{{1, 0x3700000, 0x3700400, 0x1ae400} } },/* 55: RPMX9 */ + {{{1, 0x3800000, 0x3804000, 0x1d0000} } },/* 56: OCM0 */ + {{{1, 0x3900000, 0x3904000, 0x1b4000} } },/* 57: CRYPTO */ + {{{1, 0x3a00000, 0x3a04000, 0x1d8000} } },/* 58: SMB */ + {{{0} } }, /* 59: I2C0 */ + {{{0} } }, /* 60: I2C1 */ + {{{1, 0x3d00000, 0x3d04000, 0x1d8000} } },/* 61: LPC */ + {{{1, 0x3e00000, 0x3e01000, 0x167000} } },/* 62: P2NC */ + {{{1, 0x3f00000, 0x3f01000, 0x168000} } } /* 63: P2NR0 */ }; -static 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)} +/* + * top 12 bits of crb internal address (hub, agent) + */ +static unsigned crb_hub_agt[64] = +{ + 0, + NETXEN_HW_CRB_HUB_AGT_ADR_PS, + NETXEN_HW_CRB_HUB_AGT_ADR_MN, + NETXEN_HW_CRB_HUB_AGT_ADR_MS, + 0, + NETXEN_HW_CRB_HUB_AGT_ADR_SRE, + NETXEN_HW_CRB_HUB_AGT_ADR_NIU, + NETXEN_HW_CRB_HUB_AGT_ADR_QMN, + NETXEN_HW_CRB_HUB_AGT_ADR_SQN0, + NETXEN_HW_CRB_HUB_AGT_ADR_SQN1, + NETXEN_HW_CRB_HUB_AGT_ADR_SQN2, + NETXEN_HW_CRB_HUB_AGT_ADR_SQN3, + NETXEN_HW_CRB_HUB_AGT_ADR_I2Q, + NETXEN_HW_CRB_HUB_AGT_ADR_TIMR, + NETXEN_HW_CRB_HUB_AGT_ADR_ROMUSB, + NETXEN_HW_CRB_HUB_AGT_ADR_PGN4, + NETXEN_HW_CRB_HUB_AGT_ADR_XDMA, + NETXEN_HW_CRB_HUB_AGT_ADR_PGN0, + NETXEN_HW_CRB_HUB_AGT_ADR_PGN1, + NETXEN_HW_CRB_HUB_AGT_ADR_PGN2, + NETXEN_HW_CRB_HUB_AGT_ADR_PGN3, + NETXEN_HW_CRB_HUB_AGT_ADR_PGND, + NETXEN_HW_CRB_HUB_AGT_ADR_PGNI, + NETXEN_HW_CRB_HUB_AGT_ADR_PGS0, + NETXEN_HW_CRB_HUB_AGT_ADR_PGS1, + NETXEN_HW_CRB_HUB_AGT_ADR_PGS2, + NETXEN_HW_CRB_HUB_AGT_ADR_PGS3, + 0, + NETXEN_HW_CRB_HUB_AGT_ADR_PGSI, + NETXEN_HW_CRB_HUB_AGT_ADR_SN, + 0, + NETXEN_HW_CRB_HUB_AGT_ADR_EG, + 0, + NETXEN_HW_CRB_HUB_AGT_ADR_PS, + NETXEN_HW_CRB_HUB_AGT_ADR_CAM, + 0, + 0, + 0, + 0, + 0, + NETXEN_HW_CRB_HUB_AGT_ADR_TIMR, + 0, + NETXEN_HW_CRB_HUB_AGT_ADR_RPMX1, + NETXEN_HW_CRB_HUB_AGT_ADR_RPMX2, + NETXEN_HW_CRB_HUB_AGT_ADR_RPMX3, + NETXEN_HW_CRB_HUB_AGT_ADR_RPMX4, + NETXEN_HW_CRB_HUB_AGT_ADR_RPMX5, + NETXEN_HW_CRB_HUB_AGT_ADR_RPMX6, + NETXEN_HW_CRB_HUB_AGT_ADR_RPMX7, + NETXEN_HW_CRB_HUB_AGT_ADR_XDMA, + NETXEN_HW_CRB_HUB_AGT_ADR_I2Q, + NETXEN_HW_CRB_HUB_AGT_ADR_ROMUSB, + 0, + NETXEN_HW_CRB_HUB_AGT_ADR_RPMX0, + NETXEN_HW_CRB_HUB_AGT_ADR_RPMX8, + NETXEN_HW_CRB_HUB_AGT_ADR_RPMX9, + NETXEN_HW_CRB_HUB_AGT_ADR_OCM0, + 0, + NETXEN_HW_CRB_HUB_AGT_ADR_SMB, + NETXEN_HW_CRB_HUB_AGT_ADR_I2C0, + NETXEN_HW_CRB_HUB_AGT_ADR_I2C1, + 0, + NETXEN_HW_CRB_HUB_AGT_ADR_PGNC, + 0, }; -#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]) - /* PCI Windowing for DDR regions. */ #define ADDR_IN_RANGE(addr, low, high) \ (((addr) <= (high)) && ((addr) >= (low))) -#define NETXEN_FLASH_BASE (NETXEN_BOOTLD_START) -#define NETXEN_PHANTOM_MEM_BASE (NETXEN_FLASH_BASE) #define NETXEN_MAX_MTU 8000 + NETXEN_ENET_HEADER_SIZE + NETXEN_ETH_FCS_SIZE #define NETXEN_MIN_MTU 64 #define NETXEN_ETH_FCS_SIZE 4 #define NETXEN_ENET_HEADER_SIZE 14 -#define NETXEN_WINDOW_ONE 0x2000000 /*CRB Window: bit 25 of CRB address */ +#define NETXEN_WINDOW_ONE 0x2000000 /*CRB Window: bit 25 of CRB address */ #define NETXEN_FIRMWARE_LEN ((16 * 1024) / 4) #define NETXEN_NIU_HDRSIZE (0x1 << 6) #define NETXEN_NIU_TLRSIZE (0x1 << 5) -#define lower32(x) ((u32)((x) & 0xffffffff)) -#define upper32(x) \ - ((u32)(((unsigned long long)(x) >> 32) & 0xffffffff)) - #define NETXEN_NIC_ZERO_PAUSE_ADDR 0ULL #define NETXEN_NIC_UNIT_PAUSE_ADDR 0x200ULL #define NETXEN_NIC_EPG_PAUSE_ADDR1 0x2200010000c28001ULL @@ -281,10 +301,6 @@ static u64 ctx_addr_sig_regs[][3] = { #define NETXEN_NIC_WINDOW_MARGIN 0x100000 -static unsigned long netxen_nic_pci_set_window(struct netxen_adapter *adapter, - unsigned long long addr); -void netxen_free_hw_resources(struct netxen_adapter *adapter); - int netxen_nic_set_mac(struct net_device *netdev, void *p) { struct netxen_adapter *adapter = netdev_priv(netdev); @@ -296,266 +312,370 @@ int netxen_nic_set_mac(struct net_device *netdev, void *p) if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; - DPRINTK(INFO, "valid ether addr\n"); memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); - if (adapter->macaddr_set) - adapter->macaddr_set(adapter, addr->sa_data); + /* For P3, MAC addr is not set in NIU */ + if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) + if (adapter->macaddr_set) + adapter->macaddr_set(adapter, addr->sa_data); return 0; } -/* - * netxen_nic_set_multi - Multicast - */ -void netxen_nic_set_multi(struct net_device *netdev) +#define NETXEN_UNICAST_ADDR(port, index) \ + (NETXEN_UNICAST_ADDR_BASE+(port*32)+(index*8)) +#define NETXEN_MCAST_ADDR(port, index) \ + (NETXEN_MULTICAST_ADDR_BASE+(port*0x80)+(index*8)) +#define MAC_HI(addr) \ + ((addr[2] << 16) | (addr[1] << 8) | (addr[0])) +#define MAC_LO(addr) \ + ((addr[5] << 16) | (addr[4] << 8) | (addr[3])) + +static int +netxen_nic_enable_mcast_filter(struct netxen_adapter *adapter) +{ + u32 val = 0; + u16 port = adapter->physical_port; + u8 *addr = adapter->netdev->dev_addr; + + if (adapter->mc_enabled) + return 0; + + adapter->hw_read_wx(adapter, NETXEN_MAC_ADDR_CNTL_REG, &val, 4); + val |= (1UL << (28+port)); + adapter->hw_write_wx(adapter, NETXEN_MAC_ADDR_CNTL_REG, &val, 4); + + /* add broadcast addr to filter */ + val = 0xffffff; + netxen_crb_writelit_adapter(adapter, NETXEN_UNICAST_ADDR(port, 0), val); + netxen_crb_writelit_adapter(adapter, + NETXEN_UNICAST_ADDR(port, 0)+4, val); + + /* add station addr to filter */ + val = MAC_HI(addr); + netxen_crb_writelit_adapter(adapter, NETXEN_UNICAST_ADDR(port, 1), val); + val = MAC_LO(addr); + netxen_crb_writelit_adapter(adapter, + NETXEN_UNICAST_ADDR(port, 1)+4, val); + + adapter->mc_enabled = 1; + return 0; +} + +static int +netxen_nic_disable_mcast_filter(struct netxen_adapter *adapter) +{ + u32 val = 0; + u16 port = adapter->physical_port; + u8 *addr = adapter->netdev->dev_addr; + + if (!adapter->mc_enabled) + return 0; + + adapter->hw_read_wx(adapter, NETXEN_MAC_ADDR_CNTL_REG, &val, 4); + val &= ~(1UL << (28+port)); + adapter->hw_write_wx(adapter, NETXEN_MAC_ADDR_CNTL_REG, &val, 4); + + val = MAC_HI(addr); + netxen_crb_writelit_adapter(adapter, NETXEN_UNICAST_ADDR(port, 0), val); + val = MAC_LO(addr); + netxen_crb_writelit_adapter(adapter, + NETXEN_UNICAST_ADDR(port, 0)+4, val); + + netxen_crb_writelit_adapter(adapter, NETXEN_UNICAST_ADDR(port, 1), 0); + netxen_crb_writelit_adapter(adapter, NETXEN_UNICAST_ADDR(port, 1)+4, 0); + + adapter->mc_enabled = 0; + return 0; +} + +static int +netxen_nic_set_mcast_addr(struct netxen_adapter *adapter, + int index, u8 *addr) +{ + u32 hi = 0, lo = 0; + u16 port = adapter->physical_port; + + lo = MAC_LO(addr); + hi = MAC_HI(addr); + + netxen_crb_writelit_adapter(adapter, + NETXEN_MCAST_ADDR(port, index), hi); + netxen_crb_writelit_adapter(adapter, + NETXEN_MCAST_ADDR(port, index)+4, lo); + + return 0; +} + +void netxen_p2_nic_set_multi(struct net_device *netdev) { struct netxen_adapter *adapter = netdev_priv(netdev); struct dev_mc_list *mc_ptr; + u8 null_addr[6]; + int index = 0; + + memset(null_addr, 0, 6); - mc_ptr = netdev->mc_list; if (netdev->flags & IFF_PROMISC) { - if (adapter->set_promisc) - adapter->set_promisc(adapter, - NETXEN_NIU_PROMISC_MODE); - } else { - if (adapter->unset_promisc) - adapter->unset_promisc(adapter, - NETXEN_NIU_NON_PROMISC_MODE); + + adapter->set_promisc(adapter, + NETXEN_NIU_PROMISC_MODE); + + /* Full promiscuous mode */ + netxen_nic_disable_mcast_filter(adapter); + + return; + } + + if (netdev->mc_count == 0) { + adapter->set_promisc(adapter, + NETXEN_NIU_NON_PROMISC_MODE); + netxen_nic_disable_mcast_filter(adapter); + return; } + + adapter->set_promisc(adapter, NETXEN_NIU_ALLMULTI_MODE); + if (netdev->flags & IFF_ALLMULTI || + netdev->mc_count > adapter->max_mc_count) { + netxen_nic_disable_mcast_filter(adapter); + return; + } + + netxen_nic_enable_mcast_filter(adapter); + + for (mc_ptr = netdev->mc_list; mc_ptr; mc_ptr = mc_ptr->next, index++) + netxen_nic_set_mcast_addr(adapter, index, mc_ptr->dmi_addr); + + if (index != netdev->mc_count) + printk(KERN_WARNING "%s: %s multicast address count mismatch\n", + netxen_nic_driver_name, netdev->name); + + /* Clear out remaining addresses */ + for (; index < adapter->max_mc_count; index++) + netxen_nic_set_mcast_addr(adapter, index, null_addr); } -/* - * netxen_nic_change_mtu - Change the Maximum Transfer Unit - * @returns 0 on success, negative on failure - */ -int netxen_nic_change_mtu(struct net_device *netdev, int mtu) +static int nx_p3_nic_add_mac(struct netxen_adapter *adapter, + u8 *addr, nx_mac_list_t **add_list, nx_mac_list_t **del_list) { - struct netxen_adapter *adapter = netdev_priv(netdev); - int eff_mtu = mtu + NETXEN_ENET_HEADER_SIZE + NETXEN_ETH_FCS_SIZE; + nx_mac_list_t *cur, *prev; + + /* if in del_list, move it to adapter->mac_list */ + for (cur = *del_list, prev = NULL; cur;) { + if (memcmp(addr, cur->mac_addr, ETH_ALEN) == 0) { + if (prev == NULL) + *del_list = cur->next; + else + prev->next = cur->next; + cur->next = adapter->mac_list; + adapter->mac_list = cur; + return 0; + } + prev = cur; + cur = cur->next; + } + + /* make sure to add each mac address only once */ + for (cur = adapter->mac_list; cur; cur = cur->next) { + if (memcmp(addr, cur->mac_addr, ETH_ALEN) == 0) + return 0; + } + /* not in del_list, create new entry and add to add_list */ + cur = kmalloc(sizeof(*cur), in_atomic()? GFP_ATOMIC : GFP_KERNEL); + if (cur == NULL) { + printk(KERN_ERR "%s: cannot allocate memory. MAC filtering may" + "not work properly from now.\n", __func__); + return -1; + } - if ((eff_mtu > NETXEN_MAX_MTU) || (eff_mtu < NETXEN_MIN_MTU)) { - printk(KERN_ERR "%s: %s %d is not supported.\n", - netxen_nic_driver_name, netdev->name, mtu); + memcpy(cur->mac_addr, addr, ETH_ALEN); + cur->next = *add_list; + *add_list = cur; + return 0; +} + +static int +netxen_send_cmd_descs(struct netxen_adapter *adapter, + struct cmd_desc_type0 *cmd_desc_arr, int nr_elements) +{ + uint32_t i, producer; + struct netxen_cmd_buffer *pbuf; + struct cmd_desc_type0 *cmd_desc; + + if (nr_elements > MAX_PENDING_DESC_BLOCK_SIZE || nr_elements == 0) { + printk(KERN_WARNING "%s: Too many command descriptors in a " + "request\n", __func__); return -EINVAL; } - if (adapter->set_mtu) - adapter->set_mtu(adapter, mtu); - netdev->mtu = mtu; + i = 0; + + producer = adapter->cmd_producer; + do { + cmd_desc = &cmd_desc_arr[i]; + + pbuf = &adapter->cmd_buf_arr[producer]; + pbuf->mss = 0; + pbuf->total_length = 0; + pbuf->skb = NULL; + pbuf->cmd = 0; + pbuf->frag_count = 0; + pbuf->port = 0; + + /* adapter->ahw.cmd_desc_head[producer] = *cmd_desc; */ + memcpy(&adapter->ahw.cmd_desc_head[producer], + &cmd_desc_arr[i], sizeof(struct cmd_desc_type0)); + + producer = get_next_index(producer, + adapter->max_tx_desc_count); + i++; + + } while (i != nr_elements); + + adapter->cmd_producer = producer; + + /* write producer index to start the xmit */ + + netxen_nic_update_cmd_producer(adapter, adapter->cmd_producer); return 0; } -/* - * check if the firmware has been downloaded and ready to run and - * setup the address for the descriptors in the adapter - */ -int netxen_nic_hw_resources(struct netxen_adapter *adapter) +#define NIC_REQUEST 0x14 +#define NETXEN_MAC_EVENT 0x1 + +static int nx_p3_sre_macaddr_change(struct net_device *dev, + u8 *addr, unsigned op) { - struct netxen_hardware_context *hw = &adapter->ahw; - u32 state = 0; - void *addr; - int loops = 0, err = 0; - int ctx, ring; - 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)); - DPRINTK(INFO, "cam base: %lx %x", NETXEN_CRB_CAM, - pci_base_offset(adapter, NETXEN_CRB_CAM)); - DPRINTK(INFO, "cam RAM: %lx %x", NETXEN_CAM_RAM_BASE, - pci_base_offset(adapter, NETXEN_CAM_RAM_BASE)); - - - for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) { - DPRINTK(INFO, "Command Peg ready..waiting for rcv peg\n"); - loops = 0; - state = 0; - /* Window 1 call */ - state = readl(NETXEN_CRB_NORMALIZE(adapter, - recv_crb_registers[ctx]. - crb_rcvpeg_state)); - while (state != PHAN_PEG_RCV_INITIALIZED && loops < 20) { - msleep(1); - /* Window 1 call */ - state = readl(NETXEN_CRB_NORMALIZE(adapter, - recv_crb_registers - [ctx]. - crb_rcvpeg_state)); - loops++; - } - if (loops >= 20) { - printk(KERN_ERR "Rcv Peg initialization not complete:" - "%x.\n", state); - err = -EIO; - return err; - } + struct netxen_adapter *adapter = (struct netxen_adapter *)dev->priv; + nx_nic_req_t req; + nx_mac_req_t mac_req; + int rv; + + memset(&req, 0, sizeof(nx_nic_req_t)); + req.qhdr |= (NIC_REQUEST << 23); + req.req_hdr |= NETXEN_MAC_EVENT; + req.req_hdr |= ((u64)adapter->portnum << 16); + mac_req.op = op; + memcpy(&mac_req.mac_addr, addr, 6); + req.words[0] = cpu_to_le64(*(u64 *)&mac_req); + + rv = netxen_send_cmd_descs(adapter, (struct cmd_desc_type0 *)&req, 1); + if (rv != 0) { + printk(KERN_ERR "ERROR. Could not send mac update\n"); + return rv; } - adapter->intr_scheme = readl( - NETXEN_CRB_NORMALIZE(adapter, CRB_NIC_CAPABILITIES_FW)); - adapter->msi_mode = readl( - NETXEN_CRB_NORMALIZE(adapter, CRB_NIC_MSI_MODE_FW)); - - addr = netxen_alloc(adapter->ahw.pdev, - sizeof(struct netxen_ring_ctx) + - sizeof(uint32_t), - (dma_addr_t *) & adapter->ctx_desc_phys_addr, - &adapter->ctx_desc_pdev); - - if (addr == NULL) { - DPRINTK(ERR, "bad return from pci_alloc_consistent\n"); - err = -ENOMEM; - return err; - } - 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 = (__le32 *) (((char *)addr) + - sizeof(struct netxen_ring_ctx)); - - 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); - - if (addr == NULL) { - DPRINTK(ERR, "bad return from pci_alloc_consistent\n"); - netxen_free_hw_resources(adapter); - return -ENOMEM; - } - - adapter->ctx_desc->cmd_ring_addr = - cpu_to_le64(hw->cmd_desc_phys_addr); - adapter->ctx_desc->cmd_ring_size = - cpu_to_le32(adapter->max_tx_desc_count); - - hw->cmd_desc_head = (struct cmd_desc_type0 *)addr; - - for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) { - recv_ctx = &adapter->recv_ctx[ctx]; - - for (ring = 0; ring < NUM_RCV_DESC_RINGS; ring++) { - rcv_desc = &recv_ctx->rcv_desc[ring]; - addr = netxen_alloc(adapter->ahw.pdev, - RCV_DESC_RINGSIZE, - &rcv_desc->phys_addr, - &rcv_desc->phys_pdev); - if (addr == NULL) { - DPRINTK(ERR, "bad return from " - "pci_alloc_consistent\n"); - netxen_free_hw_resources(adapter); - err = -ENOMEM; - return err; - } - rcv_desc->desc_head = (struct rcv_desc *)addr; - adapter->ctx_desc->rcv_ctx[ring].rcv_ring_addr = - cpu_to_le64(rcv_desc->phys_addr); - adapter->ctx_desc->rcv_ctx[ring].rcv_ring_size = - cpu_to_le32(rcv_desc->max_rx_desc_count); - } - addr = netxen_alloc(adapter->ahw.pdev, STATUS_DESC_RINGSIZE, - &recv_ctx->rcv_status_desc_phys_addr, - &recv_ctx->rcv_status_desc_pdev); - if (addr == NULL) { - DPRINTK(ERR, "bad return from" - " pci_alloc_consistent\n"); - netxen_free_hw_resources(adapter); - err = -ENOMEM; - return err; - } - recv_ctx->rcv_status_desc_head = (struct status_desc *)addr; - adapter->ctx_desc->sts_ring_addr = - cpu_to_le64(recv_ctx->rcv_status_desc_phys_addr); - adapter->ctx_desc->sts_ring_size = - cpu_to_le32(adapter->max_rx_desc_count); - - } - /* Window = 1 */ - - writel(lower32(adapter->ctx_desc_phys_addr), - 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(func_id))); - writel(NETXEN_CTX_SIGNATURE | func_id, - NETXEN_CRB_NORMALIZE(adapter, CRB_CTX_SIGNATURE_REG(func_id))); - return err; + return 0; } -void netxen_free_hw_resources(struct netxen_adapter *adapter) +void netxen_p3_nic_set_multi(struct net_device *netdev) { - struct netxen_recv_context *recv_ctx; - struct netxen_rcv_desc_ctx *rcv_desc; - int ctx, ring; - - if (adapter->ctx_desc != NULL) { - pci_free_consistent(adapter->ctx_desc_pdev, - sizeof(struct netxen_ring_ctx) + - sizeof(uint32_t), - adapter->ctx_desc, - adapter->ctx_desc_phys_addr); - adapter->ctx_desc = NULL; - } - - if (adapter->ahw.cmd_desc_head != NULL) { - pci_free_consistent(adapter->ahw.cmd_desc_pdev, - sizeof(struct cmd_desc_type0) * - adapter->max_tx_desc_count, - adapter->ahw.cmd_desc_head, - adapter->ahw.cmd_desc_phys_addr); - adapter->ahw.cmd_desc_head = NULL; - } - - for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) { - recv_ctx = &adapter->recv_ctx[ctx]; - for (ring = 0; ring < NUM_RCV_DESC_RINGS; ring++) { - rcv_desc = &recv_ctx->rcv_desc[ring]; - - if (rcv_desc->desc_head != NULL) { - pci_free_consistent(rcv_desc->phys_pdev, - RCV_DESC_RINGSIZE, - rcv_desc->desc_head, - rcv_desc->phys_addr); - rcv_desc->desc_head = NULL; - } - } + struct netxen_adapter *adapter = netdev_priv(netdev); + nx_mac_list_t *cur, *next, *del_list, *add_list = NULL; + struct dev_mc_list *mc_ptr; + u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + adapter->set_promisc(adapter, NETXEN_NIU_PROMISC_MODE); - if (recv_ctx->rcv_status_desc_head != NULL) { - pci_free_consistent(recv_ctx->rcv_status_desc_pdev, - STATUS_DESC_RINGSIZE, - recv_ctx->rcv_status_desc_head, - recv_ctx-> - rcv_status_desc_phys_addr); - recv_ctx->rcv_status_desc_head = NULL; + /* + * Programming mac addresses will automaticly enabling L2 filtering. + * HW will replace timestamp with L2 conid when L2 filtering is + * enabled. This causes problem for LSA. Do not enabling L2 filtering + * until that problem is fixed. + */ + if ((netdev->flags & IFF_PROMISC) || + (netdev->mc_count > adapter->max_mc_count)) + return; + + del_list = adapter->mac_list; + adapter->mac_list = NULL; + + nx_p3_nic_add_mac(adapter, netdev->dev_addr, &add_list, &del_list); + if (netdev->mc_count > 0) { + nx_p3_nic_add_mac(adapter, bcast_addr, &add_list, &del_list); + for (mc_ptr = netdev->mc_list; mc_ptr; + mc_ptr = mc_ptr->next) { + nx_p3_nic_add_mac(adapter, mc_ptr->dmi_addr, + &add_list, &del_list); } } + for (cur = del_list; cur;) { + nx_p3_sre_macaddr_change(netdev, cur->mac_addr, NETXEN_MAC_DEL); + next = cur->next; + kfree(cur); + cur = next; + } + for (cur = add_list; cur;) { + nx_p3_sre_macaddr_change(netdev, cur->mac_addr, NETXEN_MAC_ADD); + next = cur->next; + cur->next = adapter->mac_list; + adapter->mac_list = cur; + cur = next; + } } -void netxen_tso_check(struct netxen_adapter *adapter, - struct cmd_desc_type0 *desc, struct sk_buff *skb) +#define NETXEN_CONFIG_INTR_COALESCE 3 + +/* + * Send the interrupt coalescing parameter set by ethtool to the card. + */ +int netxen_config_intr_coalesce(struct netxen_adapter *adapter) { - if (desc->mss) { - 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 (ip_hdr(skb)->protocol == IPPROTO_TCP) { - netxen_set_cmd_desc_opcode(desc, TX_TCP_PKT); - } else if (ip_hdr(skb)->protocol == IPPROTO_UDP) { - netxen_set_cmd_desc_opcode(desc, TX_UDP_PKT); - } else { - return; - } + nx_nic_req_t req; + int rv; + + memset(&req, 0, sizeof(nx_nic_req_t)); + + req.qhdr |= (NIC_REQUEST << 23); + req.req_hdr |= NETXEN_CONFIG_INTR_COALESCE; + req.req_hdr |= ((u64)adapter->portnum << 16); + + memcpy(&req.words[0], &adapter->coal, sizeof(adapter->coal)); + + rv = netxen_send_cmd_descs(adapter, (struct cmd_desc_type0 *)&req, 1); + if (rv != 0) { + printk(KERN_ERR "ERROR. Could not send " + "interrupt coalescing parameters\n"); } - desc->tcp_hdr_offset = skb_transport_offset(skb); - desc->ip_hdr_offset = skb_network_offset(skb); + + return rv; +} + +/* + * netxen_nic_change_mtu - Change the Maximum Transfer Unit + * @returns 0 on success, negative on failure + */ + +#define MTU_FUDGE_FACTOR 100 + +int netxen_nic_change_mtu(struct net_device *netdev, int mtu) +{ + struct netxen_adapter *adapter = netdev_priv(netdev); + int max_mtu; + + if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) + max_mtu = P3_MAX_MTU; + else + max_mtu = P2_MAX_MTU; + + if (mtu > max_mtu) { + printk(KERN_ERR "%s: mtu > %d bytes unsupported\n", + netdev->name, max_mtu); + return -EINVAL; + } + + if (adapter->set_mtu) + adapter->set_mtu(adapter, mtu); + netdev->mtu = mtu; + + mtu += MTU_FUDGE_FACTOR; + if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) + nx_fw_cmd_set_mtu(adapter, mtu); + else if (adapter->set_mtu) + adapter->set_mtu(adapter, mtu); + + return 0; } int netxen_is_flash_supported(struct netxen_adapter *adapter) @@ -632,41 +752,49 @@ int netxen_get_flash_mac_addr(struct netxen_adapter *adapter, __le64 mac[]) return 0; } +#define CRB_WIN_LOCK_TIMEOUT 100000000 + +static int crb_win_lock(struct netxen_adapter *adapter) +{ + int done = 0, timeout = 0; + + while (!done) { + /* acquire semaphore3 from PCI HW block */ + adapter->hw_read_wx(adapter, + NETXEN_PCIE_REG(PCIE_SEM7_LOCK), &done, 4); + if (done == 1) + break; + if (timeout >= CRB_WIN_LOCK_TIMEOUT) + return -1; + timeout++; + udelay(1); + } + netxen_crb_writelit_adapter(adapter, + NETXEN_CRB_WIN_LOCK_ID, adapter->portnum); + return 0; +} + +static void crb_win_unlock(struct netxen_adapter *adapter) +{ + int val; + + adapter->hw_read_wx(adapter, + NETXEN_PCIE_REG(PCIE_SEM7_UNLOCK), &val, 4); +} + /* * Changes the CRB window to the specified window. */ -void netxen_nic_pci_change_crbwindow(struct netxen_adapter *adapter, u32 wndw) +void +netxen_nic_pci_change_crbwindow_128M(struct netxen_adapter *adapter, u32 wndw) { void __iomem *offset; u32 tmp; int count = 0; + uint8_t func = adapter->ahw.pci_func; 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 @@ -675,6 +803,8 @@ 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(PCIE_CRB_WINDOW_REG(func))); if (wndw & 0x1) wndw = NETXEN_WINDOW_ONE; @@ -685,7 +815,7 @@ void netxen_nic_pci_change_crbwindow(struct netxen_adapter *adapter, u32 wndw) while ((tmp = readl(offset)) != wndw) { printk(KERN_WARNING "%s: %s WARNING: CRB window value not " "registered properly: 0x%08x.\n", - netxen_nic_driver_name, __FUNCTION__, tmp); + netxen_nic_driver_name, __func__, tmp); mdelay(1); if (count >= 10) break; @@ -698,51 +828,119 @@ void netxen_nic_pci_change_crbwindow(struct netxen_adapter *adapter, u32 wndw) adapter->curr_window = 0; } +/* + * Return -1 if off is not valid, + * 1 if window access is needed. 'off' is set to offset from + * CRB space in 128M pci map + * 0 if no window access is needed. 'off' is set to 2M addr + * In: 'off' is offset from base in 128M pci map + */ +static int +netxen_nic_pci_get_crb_addr_2M(struct netxen_adapter *adapter, + ulong *off, int len) +{ + unsigned long end = *off + len; + crb_128M_2M_sub_block_map_t *m; + + + if (*off >= NETXEN_CRB_MAX) + return -1; + + if (*off >= NETXEN_PCI_CAMQM && (end <= NETXEN_PCI_CAMQM_2M_END)) { + *off = (*off - NETXEN_PCI_CAMQM) + NETXEN_PCI_CAMQM_2M_BASE + + (ulong)adapter->ahw.pci_base0; + return 0; + } + + if (*off < NETXEN_PCI_CRBSPACE) + return -1; + + *off -= NETXEN_PCI_CRBSPACE; + end = *off + len; + + /* + * Try direct map + */ + m = &crb_128M_2M_map[CRB_BLK(*off)].sub_block[CRB_SUBBLK(*off)]; + + if (m->valid && (m->start_128M <= *off) && (m->end_128M >= end)) { + *off = *off + m->start_2M - m->start_128M + + (ulong)adapter->ahw.pci_base0; + return 0; + } + + /* + * Not in direct map, use crb window + */ + return 1; +} + +/* + * In: 'off' is offset from CRB space in 128M pci map + * Out: 'off' is 2M pci map addr + * side effect: lock crb window + */ +static void +netxen_nic_pci_set_crbwindow_2M(struct netxen_adapter *adapter, ulong *off) +{ + u32 win_read; + + adapter->crb_win = CRB_HI(*off); + writel(adapter->crb_win, (void *)(CRB_WINDOW_2M + + adapter->ahw.pci_base0)); + /* + * Read back value to make sure write has gone through before trying + * to use it. + */ + win_read = readl((void *)(CRB_WINDOW_2M + adapter->ahw.pci_base0)); + if (win_read != adapter->crb_win) { + printk(KERN_ERR "%s: Written crbwin (0x%x) != " + "Read crbwin (0x%x), off=0x%lx\n", + __func__, adapter->crb_win, win_read, *off); + } + *off = (*off & MASK(16)) + CRB_INDIRECT_2M + + (ulong)adapter->ahw.pci_base0; +} + int netxen_load_firmware(struct netxen_adapter *adapter) { int i; u32 data, size = 0; - u32 flashaddr = NETXEN_FLASH_BASE, memaddr = NETXEN_PHANTOM_MEM_BASE; - u64 off; - void __iomem *addr; + u32 flashaddr = NETXEN_BOOTLD_START, memaddr = NETXEN_BOOTLD_START; - size = NETXEN_FIRMWARE_LEN; - writel(1, NETXEN_CRB_NORMALIZE(adapter, NETXEN_ROMUSB_GLB_CAS_RST)); + size = (NETXEN_IMAGE_START - NETXEN_BOOTLD_START)/4; + + if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) + adapter->pci_write_normalize(adapter, + NETXEN_ROMUSB_GLB_CAS_RST, 1); for (i = 0; i < size; i++) { - int retries = 10; if (netxen_rom_fast_read(adapter, flashaddr, (int *)&data) != 0) return -EIO; - off = netxen_nic_pci_set_window(adapter, memaddr); - addr = pci_base_offset(adapter, off); - writel(data, addr); - do { - if (readl(addr) == data) - break; - msleep(100); - writel(data, addr); - } while (--retries); - if (!retries) { - printk(KERN_ERR "%s: firmware load aborted, write failed at 0x%x\n", - netxen_nic_driver_name, memaddr); - return -EIO; - } + adapter->pci_mem_write(adapter, memaddr, &data, 4); flashaddr += 4; memaddr += 4; + cond_resched(); + } + msleep(1); + + if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) + adapter->pci_write_normalize(adapter, + NETXEN_ROMUSB_GLB_SW_RESET, 0x80001d); + else { + adapter->pci_write_normalize(adapter, + NETXEN_ROMUSB_GLB_CHIP_CLK_CTRL, 0x3fff); + adapter->pci_write_normalize(adapter, + NETXEN_ROMUSB_GLB_CAS_RST, 0); } - udelay(100); - /* make sure Casper is powered on */ - writel(0x3fff, - NETXEN_CRB_NORMALIZE(adapter, NETXEN_ROMUSB_GLB_CHIP_CLK_CTRL)); - writel(0, NETXEN_CRB_NORMALIZE(adapter, NETXEN_ROMUSB_GLB_CAS_RST)); return 0; } int -netxen_nic_hw_write_wx(struct netxen_adapter *adapter, u64 off, void *data, - int len) +netxen_nic_hw_write_wx_128M(struct netxen_adapter *adapter, + ulong off, void *data, int len) { void __iomem *addr; @@ -750,7 +948,7 @@ netxen_nic_hw_write_wx(struct netxen_adapter *adapter, u64 off, void *data, addr = NETXEN_CRB_NORMALIZE(adapter, off); } else { /* Window 0 */ addr = pci_base_offset(adapter, off); - netxen_nic_pci_change_crbwindow(adapter, 0); + netxen_nic_pci_change_crbwindow_128M(adapter, 0); } DPRINTK(INFO, "writing to base %lx offset %llx addr %p" @@ -758,7 +956,7 @@ netxen_nic_hw_write_wx(struct netxen_adapter *adapter, u64 off, void *data, pci_base(adapter, off), off, addr, *(unsigned long long *)data, len); if (!addr) { - netxen_nic_pci_change_crbwindow(adapter, 1); + netxen_nic_pci_change_crbwindow_128M(adapter, 1); return 1; } @@ -785,14 +983,14 @@ netxen_nic_hw_write_wx(struct netxen_adapter *adapter, u64 off, void *data, break; } if (!ADDR_IN_WINDOW1(off)) - netxen_nic_pci_change_crbwindow(adapter, 1); + netxen_nic_pci_change_crbwindow_128M(adapter, 1); return 0; } int -netxen_nic_hw_read_wx(struct netxen_adapter *adapter, u64 off, void *data, - int len) +netxen_nic_hw_read_wx_128M(struct netxen_adapter *adapter, + ulong off, void *data, int len) { void __iomem *addr; @@ -800,13 +998,13 @@ netxen_nic_hw_read_wx(struct netxen_adapter *adapter, u64 off, void *data, addr = NETXEN_CRB_NORMALIZE(adapter, off); } else { /* Window 0 */ addr = pci_base_offset(adapter, off); - netxen_nic_pci_change_crbwindow(adapter, 0); + netxen_nic_pci_change_crbwindow_128M(adapter, 0); } DPRINTK(INFO, "reading from base %lx offset %llx addr %p\n", pci_base(adapter, off), off, addr); if (!addr) { - netxen_nic_pci_change_crbwindow(adapter, 1); + netxen_nic_pci_change_crbwindow_128M(adapter, 1); return 1; } switch (len) { @@ -830,81 +1028,195 @@ netxen_nic_hw_read_wx(struct netxen_adapter *adapter, u64 off, void *data, DPRINTK(INFO, "read %lx\n", *(unsigned long *)data); if (!ADDR_IN_WINDOW1(off)) - netxen_nic_pci_change_crbwindow(adapter, 1); + netxen_nic_pci_change_crbwindow_128M(adapter, 1); return 0; } -void netxen_nic_reg_write(struct netxen_adapter *adapter, u64 off, u32 val) -{ /* Only for window 1 */ - void __iomem *addr; +int +netxen_nic_hw_write_wx_2M(struct netxen_adapter *adapter, + ulong off, void *data, int len) +{ + unsigned long flags = 0; + int rv; + + rv = netxen_nic_pci_get_crb_addr_2M(adapter, &off, len); + + if (rv == -1) { + printk(KERN_ERR "%s: invalid offset: 0x%016lx\n", + __func__, off); + dump_stack(); + return -1; + } + + if (rv == 1) { + write_lock_irqsave(&adapter->adapter_lock, flags); + crb_win_lock(adapter); + netxen_nic_pci_set_crbwindow_2M(adapter, &off); + } - addr = NETXEN_CRB_NORMALIZE(adapter, off); - DPRINTK(INFO, "writing to base %lx offset %llx addr %p data %x\n", - pci_base(adapter, off), off, addr, val); - writel(val, addr); + DPRINTK(1, INFO, "write data %lx to offset %llx, len=%d\n", + *(unsigned long *)data, off, len); + switch (len) { + case 1: + writeb(*(uint8_t *)data, (void *)off); + break; + case 2: + writew(*(uint16_t *)data, (void *)off); + break; + case 4: + writel(*(uint32_t *)data, (void *)off); + break; + case 8: + writeq(*(uint64_t *)data, (void *)off); + break; + default: + DPRINTK(1, INFO, + "writing data %lx to offset %llx, num words=%d\n", + *(unsigned long *)data, off, (len>>3)); + break; + } + if (rv == 1) { + crb_win_unlock(adapter); + write_unlock_irqrestore(&adapter->adapter_lock, flags); + } + + return 0; } -int netxen_nic_reg_read(struct netxen_adapter *adapter, u64 off) -{ /* Only for window 1 */ - void __iomem *addr; - int val; +int +netxen_nic_hw_read_wx_2M(struct netxen_adapter *adapter, + ulong off, void *data, int len) +{ + unsigned long flags = 0; + int rv; - addr = NETXEN_CRB_NORMALIZE(adapter, off); - DPRINTK(INFO, "reading from base %lx offset %llx addr %p\n", - pci_base(adapter, off), off, addr); - val = readl(addr); - writel(val, addr); + rv = netxen_nic_pci_get_crb_addr_2M(adapter, &off, len); + + if (rv == -1) { + printk(KERN_ERR "%s: invalid offset: 0x%016lx\n", + __func__, off); + dump_stack(); + return -1; + } + + if (rv == 1) { + write_lock_irqsave(&adapter->adapter_lock, flags); + crb_win_lock(adapter); + netxen_nic_pci_set_crbwindow_2M(adapter, &off); + } + + DPRINTK(1, INFO, "read from offset %lx, len=%d\n", off, len); + + switch (len) { + case 1: + *(uint8_t *)data = readb((void *)off); + break; + case 2: + *(uint16_t *)data = readw((void *)off); + break; + case 4: + *(uint32_t *)data = readl((void *)off); + break; + case 8: + *(uint64_t *)data = readq((void *)off); + break; + default: + break; + } + DPRINTK(1, INFO, "read %lx\n", *(unsigned long *)data); + + if (rv == 1) { + crb_win_unlock(adapter); + write_unlock_irqrestore(&adapter->adapter_lock, flags); + } + + return 0; +} + +void netxen_nic_reg_write(struct netxen_adapter *adapter, u64 off, u32 val) +{ + adapter->hw_write_wx(adapter, off, &val, 4); +} + +int netxen_nic_reg_read(struct netxen_adapter *adapter, u64 off) +{ + int val; + adapter->hw_read_wx(adapter, off, &val, 4); return val; } /* Change the window to 0, write and change back to window 1. */ void netxen_nic_write_w0(struct netxen_adapter *adapter, u32 index, u32 value) { - void __iomem *addr; - - netxen_nic_pci_change_crbwindow(adapter, 0); - addr = pci_base_offset(adapter, index); - writel(value, addr); - netxen_nic_pci_change_crbwindow(adapter, 1); + adapter->hw_write_wx(adapter, index, &value, 4); } /* Change the window to 0, read and change back to window 1. */ -void netxen_nic_read_w0(struct netxen_adapter *adapter, u32 index, u32 * value) +void netxen_nic_read_w0(struct netxen_adapter *adapter, u32 index, u32 *value) { - void __iomem *addr; + adapter->hw_read_wx(adapter, index, value, 4); +} - addr = pci_base_offset(adapter, index); +void netxen_nic_write_w1(struct netxen_adapter *adapter, u32 index, u32 value) +{ + adapter->hw_write_wx(adapter, index, &value, 4); +} + +void netxen_nic_read_w1(struct netxen_adapter *adapter, u32 index, u32 *value) +{ + adapter->hw_read_wx(adapter, index, value, 4); +} + +/* + * check memory access boundary. + * used by test agent. support ddr access only for now + */ +static unsigned long +netxen_nic_pci_mem_bound_check(struct netxen_adapter *adapter, + unsigned long long addr, int size) +{ + if (!ADDR_IN_RANGE(addr, + NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX) || + !ADDR_IN_RANGE(addr+size-1, + NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX) || + ((size != 1) && (size != 2) && (size != 4) && (size != 8))) { + return 0; + } - netxen_nic_pci_change_crbwindow(adapter, 0); - *value = readl(addr); - netxen_nic_pci_change_crbwindow(adapter, 1); + return 1; } static int netxen_pci_set_window_warning_count; -static unsigned long netxen_nic_pci_set_window(struct netxen_adapter *adapter, - unsigned long long addr) +unsigned long +netxen_nic_pci_set_window_128M(struct netxen_adapter *adapter, + unsigned long long addr) { - static int ddr_mn_window = -1; - static int qdr_sn_window = -1; + void __iomem *offset; int window; + unsigned long long qdr_max; + uint8_t func = adapter->ahw.pci_func; + + if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) { + qdr_max = NETXEN_ADDR_QDR_NET_MAX_P2; + } else { + qdr_max = NETXEN_ADDR_QDR_NET_MAX_P3; + } if (ADDR_IN_RANGE(addr, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) { /* DDR network side */ addr -= NETXEN_ADDR_DDR_NET; window = (addr >> 25) & 0x3ff; - if (ddr_mn_window != window) { - ddr_mn_window = window; - writel(window, PCI_OFFSET_SECOND_RANGE(adapter, - NETXEN_PCIX_PH_REG - (PCIX_MN_WINDOW(adapter->ahw.pci_func)))); + if (adapter->ahw.ddr_mn_window != window) { + adapter->ahw.ddr_mn_window = window; + offset = PCI_OFFSET_SECOND_RANGE(adapter, + NETXEN_PCIX_PH_REG(PCIE_MN_WINDOW_REG(func))); + writel(window, offset); /* MUST make sure window is set before we forge on... */ - readl(PCI_OFFSET_SECOND_RANGE(adapter, - NETXEN_PCIX_PH_REG - (PCIX_MN_WINDOW(adapter->ahw.pci_func)))); + readl(offset); } addr -= (window * NETXEN_WINDOW_ONE); addr += NETXEN_PCI_DDR_NET; @@ -914,22 +1226,17 @@ static unsigned long netxen_nic_pci_set_window(struct netxen_adapter *adapter, } else if (ADDR_IN_RANGE(addr, NETXEN_ADDR_OCM1, NETXEN_ADDR_OCM1_MAX)) { addr -= NETXEN_ADDR_OCM1; addr += NETXEN_PCI_OCM1; - } else - if (ADDR_IN_RANGE - (addr, NETXEN_ADDR_QDR_NET, NETXEN_ADDR_QDR_NET_MAX)) { + } else if (ADDR_IN_RANGE(addr, NETXEN_ADDR_QDR_NET, qdr_max)) { /* QDR network side */ addr -= NETXEN_ADDR_QDR_NET; window = (addr >> 22) & 0x3f; - if (qdr_sn_window != window) { - qdr_sn_window = window; - writel((window << 22), - PCI_OFFSET_SECOND_RANGE(adapter, - NETXEN_PCIX_PH_REG - (PCIX_SN_WINDOW(adapter->ahw.pci_func)))); + if (adapter->ahw.qdr_sn_window != window) { + adapter->ahw.qdr_sn_window = window; + offset = PCI_OFFSET_SECOND_RANGE(adapter, + NETXEN_PCIX_PH_REG(PCIE_SN_WINDOW_REG(func))); + writel((window << 22), offset); /* MUST make sure window is set before we forge on... */ - readl(PCI_OFFSET_SECOND_RANGE(adapter, - NETXEN_PCIX_PH_REG - (PCIX_SN_WINDOW(adapter->ahw.pci_func)))); + readl(offset); } addr -= (window * 0x400000); addr += NETXEN_PCI_QDR_NET; @@ -943,11 +1250,711 @@ static unsigned long netxen_nic_pci_set_window(struct netxen_adapter *adapter, printk("%s: Warning:netxen_nic_pci_set_window()" " Unknown address range!\n", netxen_nic_driver_name); + addr = -1UL; + } + return addr; +} + +/* + * Note : only 32-bit writes! + */ +int netxen_nic_pci_write_immediate_128M(struct netxen_adapter *adapter, + u64 off, u32 data) +{ + writel(data, (void __iomem *)(PCI_OFFSET_SECOND_RANGE(adapter, off))); + return 0; +} + +u32 netxen_nic_pci_read_immediate_128M(struct netxen_adapter *adapter, u64 off) +{ + return readl((void __iomem *)(pci_base_offset(adapter, off))); +} + +void netxen_nic_pci_write_normalize_128M(struct netxen_adapter *adapter, + u64 off, u32 data) +{ + writel(data, NETXEN_CRB_NORMALIZE(adapter, off)); +} + +u32 netxen_nic_pci_read_normalize_128M(struct netxen_adapter *adapter, u64 off) +{ + return readl(NETXEN_CRB_NORMALIZE(adapter, off)); +} + +unsigned long +netxen_nic_pci_set_window_2M(struct netxen_adapter *adapter, + unsigned long long addr) +{ + int window; + u32 win_read; + + if (ADDR_IN_RANGE(addr, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) { + /* DDR network side */ + window = MN_WIN(addr); + adapter->ahw.ddr_mn_window = window; + adapter->hw_write_wx(adapter, + adapter->ahw.mn_win_crb | NETXEN_PCI_CRBSPACE, + &window, 4); + adapter->hw_read_wx(adapter, + adapter->ahw.mn_win_crb | NETXEN_PCI_CRBSPACE, + &win_read, 4); + if ((win_read << 17) != window) { + printk(KERN_INFO "Written MNwin (0x%x) != " + "Read MNwin (0x%x)\n", window, win_read); + } + addr = GET_MEM_OFFS_2M(addr) + NETXEN_PCI_DDR_NET; + } else if (ADDR_IN_RANGE(addr, + NETXEN_ADDR_OCM0, NETXEN_ADDR_OCM0_MAX)) { + if ((addr & 0x00ff800) == 0xff800) { + printk("%s: QM access not handled.\n", __func__); + addr = -1UL; + } + + window = OCM_WIN(addr); + adapter->ahw.ddr_mn_window = window; + adapter->hw_write_wx(adapter, + adapter->ahw.mn_win_crb | NETXEN_PCI_CRBSPACE, + &window, 4); + adapter->hw_read_wx(adapter, + adapter->ahw.mn_win_crb | NETXEN_PCI_CRBSPACE, + &win_read, 4); + if ((win_read >> 7) != window) { + printk(KERN_INFO "%s: Written OCMwin (0x%x) != " + "Read OCMwin (0x%x)\n", + __func__, window, win_read); + } + addr = GET_MEM_OFFS_2M(addr) + NETXEN_PCI_OCM0_2M; + + } else if (ADDR_IN_RANGE(addr, + NETXEN_ADDR_QDR_NET, NETXEN_ADDR_QDR_NET_MAX_P3)) { + /* QDR network side */ + window = MS_WIN(addr); + adapter->ahw.qdr_sn_window = window; + adapter->hw_write_wx(adapter, + adapter->ahw.ms_win_crb | NETXEN_PCI_CRBSPACE, + &window, 4); + adapter->hw_read_wx(adapter, + adapter->ahw.ms_win_crb | NETXEN_PCI_CRBSPACE, + &win_read, 4); + if (win_read != window) { + printk(KERN_INFO "%s: Written MSwin (0x%x) != " + "Read MSwin (0x%x)\n", + __func__, window, win_read); + } + addr = GET_MEM_OFFS_2M(addr) + NETXEN_PCI_QDR_NET; + } else { + /* + * peg gdb frequently accesses memory that doesn't exist, + * this limits the chit chat so debugging isn't slowed down. + */ + if ((netxen_pci_set_window_warning_count++ < 8) + || (netxen_pci_set_window_warning_count%64 == 0)) { + printk("%s: Warning:%s Unknown address range!\n", + __func__, netxen_nic_driver_name); +} + addr = -1UL; } return addr; } +static int netxen_nic_pci_is_same_window(struct netxen_adapter *adapter, + unsigned long long addr) +{ + int window; + unsigned long long qdr_max; + + if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) + qdr_max = NETXEN_ADDR_QDR_NET_MAX_P2; + else + qdr_max = NETXEN_ADDR_QDR_NET_MAX_P3; + + if (ADDR_IN_RANGE(addr, + NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) { + /* DDR network side */ + BUG(); /* MN access can not come here */ + } else if (ADDR_IN_RANGE(addr, + NETXEN_ADDR_OCM0, NETXEN_ADDR_OCM0_MAX)) { + return 1; + } else if (ADDR_IN_RANGE(addr, + NETXEN_ADDR_OCM1, NETXEN_ADDR_OCM1_MAX)) { + return 1; + } else if (ADDR_IN_RANGE(addr, NETXEN_ADDR_QDR_NET, qdr_max)) { + /* QDR network side */ + window = ((addr - NETXEN_ADDR_QDR_NET) >> 22) & 0x3f; + if (adapter->ahw.qdr_sn_window == window) + return 1; + } + + return 0; +} + +static int netxen_nic_pci_mem_read_direct(struct netxen_adapter *adapter, + u64 off, void *data, int size) +{ + unsigned long flags; + void *addr; + int ret = 0; + u64 start; + uint8_t *mem_ptr = NULL; + unsigned long mem_base; + unsigned long mem_page; + + write_lock_irqsave(&adapter->adapter_lock, flags); + + /* + * If attempting to access unknown address or straddle hw windows, + * do not access. + */ + start = adapter->pci_set_window(adapter, off); + if ((start == -1UL) || + (netxen_nic_pci_is_same_window(adapter, off+size-1) == 0)) { + write_unlock_irqrestore(&adapter->adapter_lock, flags); + printk(KERN_ERR "%s out of bound pci memory access. " + "offset is 0x%llx\n", netxen_nic_driver_name, off); + return -1; + } + + addr = (void *)(pci_base_offset(adapter, start)); + if (!addr) { + write_unlock_irqrestore(&adapter->adapter_lock, flags); + mem_base = pci_resource_start(adapter->pdev, 0); + mem_page = start & PAGE_MASK; + /* Map two pages whenever user tries to access addresses in two + consecutive pages. + */ + if (mem_page != ((start + size - 1) & PAGE_MASK)) + mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE * 2); + else + mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE); + if (mem_ptr == 0UL) { + *(uint8_t *)data = 0; + return -1; + } + addr = mem_ptr; + addr += start & (PAGE_SIZE - 1); + write_lock_irqsave(&adapter->adapter_lock, flags); + } + + switch (size) { + case 1: + *(uint8_t *)data = readb(addr); + break; + case 2: + *(uint16_t *)data = readw(addr); + break; + case 4: + *(uint32_t *)data = readl(addr); + break; + case 8: + *(uint64_t *)data = readq(addr); + break; + default: + ret = -1; + break; + } + write_unlock_irqrestore(&adapter->adapter_lock, flags); + DPRINTK(1, INFO, "read %llx\n", *(unsigned long long *)data); + + if (mem_ptr) + iounmap(mem_ptr); + return ret; +} + +static int +netxen_nic_pci_mem_write_direct(struct netxen_adapter *adapter, u64 off, + void *data, int size) +{ + unsigned long flags; + void *addr; + int ret = 0; + u64 start; + uint8_t *mem_ptr = NULL; + unsigned long mem_base; + unsigned long mem_page; + + write_lock_irqsave(&adapter->adapter_lock, flags); + + /* + * If attempting to access unknown address or straddle hw windows, + * do not access. + */ + start = adapter->pci_set_window(adapter, off); + if ((start == -1UL) || + (netxen_nic_pci_is_same_window(adapter, off+size-1) == 0)) { + write_unlock_irqrestore(&adapter->adapter_lock, flags); + printk(KERN_ERR "%s out of bound pci memory access. " + "offset is 0x%llx\n", netxen_nic_driver_name, off); + return -1; + } + + addr = (void *)(pci_base_offset(adapter, start)); + if (!addr) { + write_unlock_irqrestore(&adapter->adapter_lock, flags); + mem_base = pci_resource_start(adapter->pdev, 0); + mem_page = start & PAGE_MASK; + /* Map two pages whenever user tries to access addresses in two + * consecutive pages. + */ + if (mem_page != ((start + size - 1) & PAGE_MASK)) + mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE*2); + else + mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE); + if (mem_ptr == 0UL) + return -1; + addr = mem_ptr; + addr += start & (PAGE_SIZE - 1); + write_lock_irqsave(&adapter->adapter_lock, flags); + } + + switch (size) { + case 1: + writeb(*(uint8_t *)data, addr); + break; + case 2: + writew(*(uint16_t *)data, addr); + break; + case 4: + writel(*(uint32_t *)data, addr); + break; + case 8: + writeq(*(uint64_t *)data, addr); + break; + default: + ret = -1; + break; + } + write_unlock_irqrestore(&adapter->adapter_lock, flags); + DPRINTK(1, INFO, "writing data %llx to offset %llx\n", + *(unsigned long long *)data, start); + if (mem_ptr) + iounmap(mem_ptr); + return ret; +} + +#define MAX_CTL_CHECK 1000 + +int +netxen_nic_pci_mem_write_128M(struct netxen_adapter *adapter, + u64 off, void *data, int size) +{ + unsigned long flags, mem_crb; + int i, j, ret = 0, loop, sz[2], off0; + uint32_t temp; + uint64_t off8, tmpw, word[2] = {0, 0}; + + /* + * If not MN, go check for MS or invalid. + */ + if (netxen_nic_pci_mem_bound_check(adapter, off, size) == 0) + return netxen_nic_pci_mem_write_direct(adapter, + off, data, size); + + off8 = off & 0xfffffff8; + off0 = off & 0x7; + sz[0] = (size < (8 - off0)) ? size : (8 - off0); + sz[1] = size - sz[0]; + loop = ((off0 + size - 1) >> 3) + 1; + mem_crb = (unsigned long)pci_base_offset(adapter, NETXEN_CRB_DDR_NET); + + if ((size != 8) || (off0 != 0)) { + for (i = 0; i < loop; i++) { + if (adapter->pci_mem_read(adapter, + off8 + (i << 3), &word[i], 8)) + return -1; + } + } + + switch (size) { + case 1: + tmpw = *((uint8_t *)data); + break; + case 2: + tmpw = *((uint16_t *)data); + break; + case 4: + tmpw = *((uint32_t *)data); + break; + case 8: + default: + tmpw = *((uint64_t *)data); + break; + } + word[0] &= ~((~(~0ULL << (sz[0] * 8))) << (off0 * 8)); + word[0] |= tmpw << (off0 * 8); + + if (loop == 2) { + word[1] &= ~(~0ULL << (sz[1] * 8)); + word[1] |= tmpw >> (sz[0] * 8); + } + + write_lock_irqsave(&adapter->adapter_lock, flags); + netxen_nic_pci_change_crbwindow_128M(adapter, 0); + + for (i = 0; i < loop; i++) { + writel((uint32_t)(off8 + (i << 3)), + (void *)(mem_crb+MIU_TEST_AGT_ADDR_LO)); + writel(0, + (void *)(mem_crb+MIU_TEST_AGT_ADDR_HI)); + writel(word[i] & 0xffffffff, + (void *)(mem_crb+MIU_TEST_AGT_WRDATA_LO)); + writel((word[i] >> 32) & 0xffffffff, + (void *)(mem_crb+MIU_TEST_AGT_WRDATA_HI)); + writel(MIU_TA_CTL_ENABLE|MIU_TA_CTL_WRITE, + (void *)(mem_crb+MIU_TEST_AGT_CTRL)); + writel(MIU_TA_CTL_START|MIU_TA_CTL_ENABLE|MIU_TA_CTL_WRITE, + (void *)(mem_crb+MIU_TEST_AGT_CTRL)); + + for (j = 0; j < MAX_CTL_CHECK; j++) { + temp = readl( + (void *)(mem_crb+MIU_TEST_AGT_CTRL)); + if ((temp & MIU_TA_CTL_BUSY) == 0) + break; + } + + if (j >= MAX_CTL_CHECK) { + printk("%s: %s Fail to write through agent\n", + __func__, netxen_nic_driver_name); + ret = -1; + break; + } + } + + netxen_nic_pci_change_crbwindow_128M(adapter, 1); + write_unlock_irqrestore(&adapter->adapter_lock, flags); + return ret; +} + +int +netxen_nic_pci_mem_read_128M(struct netxen_adapter *adapter, + u64 off, void *data, int size) +{ + unsigned long flags, mem_crb; + int i, j = 0, k, start, end, loop, sz[2], off0[2]; + uint32_t temp; + uint64_t off8, val, word[2] = {0, 0}; + + + /* + * If not MN, go check for MS or invalid. + */ + if (netxen_nic_pci_mem_bound_check(adapter, off, size) == 0) + return netxen_nic_pci_mem_read_direct(adapter, off, data, size); + + off8 = off & 0xfffffff8; + off0[0] = off & 0x7; + off0[1] = 0; + sz[0] = (size < (8 - off0[0])) ? size : (8 - off0[0]); + sz[1] = size - sz[0]; + loop = ((off0[0] + size - 1) >> 3) + 1; + mem_crb = (unsigned long)pci_base_offset(adapter, NETXEN_CRB_DDR_NET); + + write_lock_irqsave(&adapter->adapter_lock, flags); + netxen_nic_pci_change_crbwindow_128M(adapter, 0); + + for (i = 0; i < loop; i++) { + writel((uint32_t)(off8 + (i << 3)), + (void *)(mem_crb+MIU_TEST_AGT_ADDR_LO)); + writel(0, + (void *)(mem_crb+MIU_TEST_AGT_ADDR_HI)); + writel(MIU_TA_CTL_ENABLE, + (void *)(mem_crb+MIU_TEST_AGT_CTRL)); + writel(MIU_TA_CTL_START|MIU_TA_CTL_ENABLE, + (void *)(mem_crb+MIU_TEST_AGT_CTRL)); + + for (j = 0; j < MAX_CTL_CHECK; j++) { + temp = readl( + (void *)(mem_crb+MIU_TEST_AGT_CTRL)); + if ((temp & MIU_TA_CTL_BUSY) == 0) + break; + } + + if (j >= MAX_CTL_CHECK) { + printk(KERN_ERR "%s: %s Fail to read through agent\n", + __func__, netxen_nic_driver_name); + break; + } + + start = off0[i] >> 2; + end = (off0[i] + sz[i] - 1) >> 2; + for (k = start; k <= end; k++) { + word[i] |= ((uint64_t) readl( + (void *)(mem_crb + + MIU_TEST_AGT_RDDATA(k))) << (32*k)); + } + } + + netxen_nic_pci_change_crbwindow_128M(adapter, 1); + write_unlock_irqrestore(&adapter->adapter_lock, flags); + + if (j >= MAX_CTL_CHECK) + return -1; + + if (sz[0] == 8) { + val = word[0]; + } else { + val = ((word[0] >> (off0[0] * 8)) & (~(~0ULL << (sz[0] * 8)))) | + ((word[1] & (~(~0ULL << (sz[1] * 8)))) << (sz[0] * 8)); + } + + switch (size) { + case 1: + *(uint8_t *)data = val; + break; + case 2: + *(uint16_t *)data = val; + break; + case 4: + *(uint32_t *)data = val; + break; + case 8: + *(uint64_t *)data = val; + break; + } + DPRINTK(1, INFO, "read %llx\n", *(unsigned long long *)data); + return 0; +} + +int +netxen_nic_pci_mem_write_2M(struct netxen_adapter *adapter, + u64 off, void *data, int size) +{ + int i, j, ret = 0, loop, sz[2], off0; + uint32_t temp; + uint64_t off8, mem_crb, tmpw, word[2] = {0, 0}; + + /* + * If not MN, go check for MS or invalid. + */ + if (off >= NETXEN_ADDR_QDR_NET && off <= NETXEN_ADDR_QDR_NET_MAX_P3) + mem_crb = NETXEN_CRB_QDR_NET; + else { + mem_crb = NETXEN_CRB_DDR_NET; + if (netxen_nic_pci_mem_bound_check(adapter, off, size) == 0) + return netxen_nic_pci_mem_write_direct(adapter, + off, data, size); + } + + off8 = off & 0xfffffff8; + off0 = off & 0x7; + sz[0] = (size < (8 - off0)) ? size : (8 - off0); + sz[1] = size - sz[0]; + loop = ((off0 + size - 1) >> 3) + 1; + + if ((size != 8) || (off0 != 0)) { + for (i = 0; i < loop; i++) { + if (adapter->pci_mem_read(adapter, off8 + (i << 3), + &word[i], 8)) + return -1; + } + } + + switch (size) { + case 1: + tmpw = *((uint8_t *)data); + break; + case 2: + tmpw = *((uint16_t *)data); + break; + case 4: + tmpw = *((uint32_t *)data); + break; + case 8: + default: + tmpw = *((uint64_t *)data); + break; + } + + word[0] &= ~((~(~0ULL << (sz[0] * 8))) << (off0 * 8)); + word[0] |= tmpw << (off0 * 8); + + if (loop == 2) { + word[1] &= ~(~0ULL << (sz[1] * 8)); + word[1] |= tmpw >> (sz[0] * 8); + } + + /* + * don't lock here - write_wx gets the lock if each time + * write_lock_irqsave(&adapter->adapter_lock, flags); + * netxen_nic_pci_change_crbwindow_128M(adapter, 0); + */ + + for (i = 0; i < loop; i++) { + temp = off8 + (i << 3); + adapter->hw_write_wx(adapter, + mem_crb+MIU_TEST_AGT_ADDR_LO, &temp, 4); + temp = 0; + adapter->hw_write_wx(adapter, + mem_crb+MIU_TEST_AGT_ADDR_HI, &temp, 4); + temp = word[i] & 0xffffffff; + adapter->hw_write_wx(adapter, + mem_crb+MIU_TEST_AGT_WRDATA_LO, &temp, 4); + temp = (word[i] >> 32) & 0xffffffff; + adapter->hw_write_wx(adapter, + mem_crb+MIU_TEST_AGT_WRDATA_HI, &temp, 4); + temp = MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE; + adapter->hw_write_wx(adapter, + mem_crb+MIU_TEST_AGT_CTRL, &temp, 4); + temp = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE; + adapter->hw_write_wx(adapter, + mem_crb+MIU_TEST_AGT_CTRL, &temp, 4); + + for (j = 0; j < MAX_CTL_CHECK; j++) { + adapter->hw_read_wx(adapter, + mem_crb + MIU_TEST_AGT_CTRL, &temp, 4); + if ((temp & MIU_TA_CTL_BUSY) == 0) + break; + } + + if (j >= MAX_CTL_CHECK) { + printk(KERN_ERR "%s: Fail to write through agent\n", + netxen_nic_driver_name); + ret = -1; + break; + } + } + + /* + * netxen_nic_pci_change_crbwindow_128M(adapter, 1); + * write_unlock_irqrestore(&adapter->adapter_lock, flags); + */ + return ret; +} + +int +netxen_nic_pci_mem_read_2M(struct netxen_adapter *adapter, + u64 off, void *data, int size) +{ + int i, j = 0, k, start, end, loop, sz[2], off0[2]; + uint32_t temp; + uint64_t off8, val, mem_crb, word[2] = {0, 0}; + + /* + * If not MN, go check for MS or invalid. + */ + + if (off >= NETXEN_ADDR_QDR_NET && off <= NETXEN_ADDR_QDR_NET_MAX_P3) + mem_crb = NETXEN_CRB_QDR_NET; + else { + mem_crb = NETXEN_CRB_DDR_NET; + if (netxen_nic_pci_mem_bound_check(adapter, off, size) == 0) + return netxen_nic_pci_mem_read_direct(adapter, + off, data, size); + } + + off8 = off & 0xfffffff8; + off0[0] = off & 0x7; + off0[1] = 0; + sz[0] = (size < (8 - off0[0])) ? size : (8 - off0[0]); + sz[1] = size - sz[0]; + loop = ((off0[0] + size - 1) >> 3) + 1; + + /* + * don't lock here - write_wx gets the lock if each time + * write_lock_irqsave(&adapter->adapter_lock, flags); + * netxen_nic_pci_change_crbwindow_128M(adapter, 0); + */ + + for (i = 0; i < loop; i++) { + temp = off8 + (i << 3); + adapter->hw_write_wx(adapter, + mem_crb + MIU_TEST_AGT_ADDR_LO, &temp, 4); + temp = 0; + adapter->hw_write_wx(adapter, + mem_crb + MIU_TEST_AGT_ADDR_HI, &temp, 4); + temp = MIU_TA_CTL_ENABLE; + adapter->hw_write_wx(adapter, + mem_crb + MIU_TEST_AGT_CTRL, &temp, 4); + temp = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE; + adapter->hw_write_wx(adapter, + mem_crb + MIU_TEST_AGT_CTRL, &temp, 4); + + for (j = 0; j < MAX_CTL_CHECK; j++) { + adapter->hw_read_wx(adapter, + mem_crb + MIU_TEST_AGT_CTRL, &temp, 4); + if ((temp & MIU_TA_CTL_BUSY) == 0) + break; + } + + if (j >= MAX_CTL_CHECK) { + printk(KERN_ERR "%s: Fail to read through agent\n", + netxen_nic_driver_name); + break; + } + + start = off0[i] >> 2; + end = (off0[i] + sz[i] - 1) >> 2; + for (k = start; k <= end; k++) { + adapter->hw_read_wx(adapter, + mem_crb + MIU_TEST_AGT_RDDATA(k), &temp, 4); + word[i] |= ((uint64_t)temp << (32 * k)); + } + } + + /* + * netxen_nic_pci_change_crbwindow_128M(adapter, 1); + * write_unlock_irqrestore(&adapter->adapter_lock, flags); + */ + + if (j >= MAX_CTL_CHECK) + return -1; + + if (sz[0] == 8) { + val = word[0]; + } else { + val = ((word[0] >> (off0[0] * 8)) & (~(~0ULL << (sz[0] * 8)))) | + ((word[1] & (~(~0ULL << (sz[1] * 8)))) << (sz[0] * 8)); + } + + switch (size) { + case 1: + *(uint8_t *)data = val; + break; + case 2: + *(uint16_t *)data = val; + break; + case 4: + *(uint32_t *)data = val; + break; + case 8: + *(uint64_t *)data = val; + break; + } + DPRINTK(1, INFO, "read %llx\n", *(unsigned long long *)data); + return 0; +} + +/* + * Note : only 32-bit writes! + */ +int netxen_nic_pci_write_immediate_2M(struct netxen_adapter *adapter, + u64 off, u32 data) +{ + adapter->hw_write_wx(adapter, off, &data, 4); + + return 0; +} + +u32 netxen_nic_pci_read_immediate_2M(struct netxen_adapter *adapter, u64 off) +{ + u32 temp; + adapter->hw_read_wx(adapter, off, &temp, 4); + return temp; +} + +void netxen_nic_pci_write_normalize_2M(struct netxen_adapter *adapter, + u64 off, u32 data) +{ + adapter->hw_write_wx(adapter, off, &data, 4); +} + +u32 netxen_nic_pci_read_normalize_2M(struct netxen_adapter *adapter, u64 off) +{ + u32 temp; + adapter->hw_read_wx(adapter, off, &temp, 4); + return temp; +} + #if 0 int netxen_nic_erase_pxe(struct netxen_adapter *adapter) @@ -1003,12 +2010,25 @@ int netxen_nic_get_board_info(struct netxen_adapter *adapter) case NETXEN_BRDTYPE_P2_SB31_10G_IMEZ: case NETXEN_BRDTYPE_P2_SB31_10G_HMEZ: case NETXEN_BRDTYPE_P2_SB31_10G_CX4: + case NETXEN_BRDTYPE_P3_HMEZ: + case NETXEN_BRDTYPE_P3_XG_LOM: + case NETXEN_BRDTYPE_P3_10G_CX4: + case NETXEN_BRDTYPE_P3_10G_CX4_LP: + case NETXEN_BRDTYPE_P3_IMEZ: + case NETXEN_BRDTYPE_P3_10G_SFP_PLUS: + case NETXEN_BRDTYPE_P3_10G_XFP: + case NETXEN_BRDTYPE_P3_10000_BASE_T: + adapter->ahw.board_type = NETXEN_NIC_XGBE; break; case NETXEN_BRDTYPE_P1_BD: case NETXEN_BRDTYPE_P1_SB: case NETXEN_BRDTYPE_P1_SMAX: case NETXEN_BRDTYPE_P1_SOCK: + case NETXEN_BRDTYPE_P3_REF_QG: + case NETXEN_BRDTYPE_P3_4_GB: + case NETXEN_BRDTYPE_P3_4_GB_MM: + adapter->ahw.board_type = NETXEN_NIC_GBE; break; default: @@ -1042,25 +2062,11 @@ int netxen_nic_set_mtu_xgb(struct netxen_adapter *adapter, int new_mtu) return 0; } -void netxen_nic_init_niu_gb(struct netxen_adapter *adapter) -{ - netxen_niu_gbe_init_port(adapter, adapter->physical_port); -} - void -netxen_crb_writelit_adapter(struct netxen_adapter *adapter, unsigned long off, - int data) +netxen_crb_writelit_adapter(struct netxen_adapter *adapter, + unsigned long off, int data) { - void __iomem *addr; - - if (ADDR_IN_WINDOW1(off)) { - writel(data, NETXEN_CRB_NORMALIZE(adapter, off)); - } else { - netxen_nic_pci_change_crbwindow(adapter, 0); - addr = pci_base_offset(adapter, off); - writel(data, addr); - netxen_nic_pci_change_crbwindow(adapter, 1); - } + adapter->hw_write_wx(adapter, off, &data, 4); } void netxen_nic_set_link_parameters(struct netxen_adapter *adapter) @@ -1147,12 +2153,11 @@ void netxen_nic_flash_print(struct netxen_adapter *adapter) addr += sizeof(u32); } - fw_major = readl(NETXEN_CRB_NORMALIZE(adapter, - NETXEN_FW_VERSION_MAJOR)); - fw_minor = readl(NETXEN_CRB_NORMALIZE(adapter, - NETXEN_FW_VERSION_MINOR)); - fw_build = - readl(NETXEN_CRB_NORMALIZE(adapter, NETXEN_FW_VERSION_SUB)); + adapter->hw_read_wx(adapter, NETXEN_FW_VERSION_MAJOR, &fw_major, 4); + adapter->hw_read_wx(adapter, NETXEN_FW_VERSION_MINOR, &fw_minor, 4); + adapter->hw_read_wx(adapter, NETXEN_FW_VERSION_SUB, &fw_build, 4); + + adapter->fw_major = fw_major; if (adapter->portnum == 0) { get_brd_name_by_type(board_info->board_type, brd_name); @@ -1163,28 +2168,13 @@ void netxen_nic_flash_print(struct netxen_adapter *adapter) fw_minor, fw_build); } - if (fw_major != _NETXEN_NIC_LINUX_MAJOR) { - adapter->driver_mismatch = 1; - } - if (fw_minor != _NETXEN_NIC_LINUX_MINOR && - fw_minor != (_NETXEN_NIC_LINUX_MINOR + 1)) { + if (NETXEN_VERSION_CODE(fw_major, fw_minor, fw_build) < + NETXEN_VERSION_CODE(3, 4, 216)) { adapter->driver_mismatch = 1; - } - if (adapter->driver_mismatch) { - printk(KERN_ERR "%s: driver and firmware version mismatch\n", - adapter->netdev->name); + printk(KERN_ERR "%s: firmware version %d.%d.%d unsupported\n", + netxen_nic_driver_name, + fw_major, fw_minor, fw_build); return; } - - switch (adapter->ahw.board_type) { - case NETXEN_NIC_GBE: - dev_info(&adapter->pdev->dev, "%s: GbE port initialized\n", - adapter->netdev->name); - break; - case NETXEN_NIC_XGBE: - dev_info(&adapter->pdev->dev, "%s: XGbE port initialized\n", - adapter->netdev->name); - break; - } } diff --git a/drivers/net/netxen/netxen_nic_hw.h b/drivers/net/netxen/netxen_nic_hw.h index a3ea1dd98c41..b8e0030f03d7 100644 --- a/drivers/net/netxen/netxen_nic_hw.h +++ b/drivers/net/netxen/netxen_nic_hw.h @@ -82,19 +82,9 @@ struct netxen_adapter; #define NETXEN_PCI_MAPSIZE_BYTES (NETXEN_PCI_MAPSIZE << 20) -#define NETXEN_NIC_LOCKED_READ_REG(X, Y) \ - addr = pci_base_offset(adapter, X); \ - *(u32 *)Y = readl((void __iomem*) addr); - struct netxen_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); -void netxen_crb_writelit_adapter(struct netxen_adapter *adapter, - unsigned long off, int data); -int netxen_nic_hw_read_wx(struct netxen_adapter *adapter, u64 off, - void *data, int len); typedef u8 netxen_ethernet_macaddr_t[6]; @@ -432,7 +422,8 @@ typedef enum { /* Promiscous mode options (GbE mode only) */ typedef enum { NETXEN_NIU_PROMISC_MODE = 0, - NETXEN_NIU_NON_PROMISC_MODE + NETXEN_NIU_NON_PROMISC_MODE, + NETXEN_NIU_ALLMULTI_MODE } netxen_niu_prom_mode_t; /* @@ -478,42 +469,6 @@ typedef enum { #define netxen_xg_soft_reset(config_word) \ ((config_word) |= 1 << 4) -/* - * MAC Control Register - * - * Bit 0-1 : id_pool0 - * Bit 2 : enable_xtnd0 - * Bit 4-5 : id_pool1 - * Bit 6 : enable_xtnd1 - * Bit 8-9 : id_pool2 - * Bit 10 : enable_xtnd2 - * Bit 12-13 : id_pool3 - * Bit 14 : enable_xtnd3 - * Bit 24-25 : mode_select - * Bit 28-31 : enable_pool - */ - -#define netxen_nic_mcr_set_id_pool0(config, val) \ - ((config) |= ((val) &0x03)) -#define netxen_nic_mcr_set_enable_xtnd0(config) \ - ((config) |= 1 << 3) -#define netxen_nic_mcr_set_id_pool1(config, val) \ - ((config) |= (((val) & 0x03) << 4)) -#define netxen_nic_mcr_set_enable_xtnd1(config) \ - ((config) |= 1 << 6) -#define netxen_nic_mcr_set_id_pool2(config, val) \ - ((config) |= (((val) & 0x03) << 8)) -#define netxen_nic_mcr_set_enable_xtnd2(config) \ - ((config) |= 1 << 10) -#define netxen_nic_mcr_set_id_pool3(config, val) \ - ((config) |= (((val) & 0x03) << 12)) -#define netxen_nic_mcr_set_enable_xtnd3(config) \ - ((config) |= 1 << 14) -#define netxen_nic_mcr_set_mode_select(config, val) \ - ((config) |= (((val) & 0x03) << 24)) -#define netxen_nic_mcr_set_enable_pool(config, val) \ - ((config) |= (((val) & 0x0f) << 28)) - /* Set promiscuous mode for a GbE interface */ int netxen_niu_set_promiscuous_mode(struct netxen_adapter *adapter, netxen_niu_prom_mode_t mode); @@ -538,4 +493,15 @@ int netxen_niu_disable_gbe_port(struct netxen_adapter *adapter); int netxen_niu_disable_xg_port(struct netxen_adapter *adapter); +typedef struct { + unsigned valid; + unsigned start_128M; + unsigned end_128M; + unsigned start_2M; +} crb_128M_2M_sub_block_map_t; + +typedef struct { + crb_128M_2M_sub_block_map_t sub_block[16]; +} crb_128M_2M_block_map_t; + #endif /* __NETXEN_NIC_HW_H_ */ diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c index 70d1b22ced22..01ab31b34a85 100644 --- a/drivers/net/netxen/netxen_nic_init.c +++ b/drivers/net/netxen/netxen_nic_init.c @@ -42,8 +42,6 @@ struct crb_addr_pair { 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 (0xffffffff) @@ -117,6 +115,8 @@ static void crb_addr_transform_setup(void) crb_addr_transform(C2C1); crb_addr_transform(C2C0); crb_addr_transform(SMB); + crb_addr_transform(OCM0); + crb_addr_transform(I2C0); } int netxen_init_firmware(struct netxen_adapter *adapter) @@ -124,15 +124,15 @@ int netxen_init_firmware(struct netxen_adapter *adapter) u32 state = 0, loops = 0, err = 0; /* Window 1 call */ - state = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE)); + state = adapter->pci_read_normalize(adapter, CRB_CMDPEG_STATE); if (state == PHAN_INITIALIZE_ACK) return 0; while (state != PHAN_INITIALIZE_COMPLETE && loops < 2000) { - udelay(100); + msleep(1); /* Window 1 call */ - state = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE)); + state = adapter->pci_read_normalize(adapter, CRB_CMDPEG_STATE); loops++; } @@ -143,64 +143,193 @@ int netxen_init_firmware(struct netxen_adapter *adapter) return err; } /* Window 1 call */ - writel(INTR_SCHEME_PERPORT, - NETXEN_CRB_NORMALIZE(adapter, CRB_NIC_CAPABILITIES_HOST)); - writel(MSI_MODE_MULTIFUNC, - NETXEN_CRB_NORMALIZE(adapter, CRB_NIC_MSI_MODE_HOST)); - writel(MPORT_MULTI_FUNCTION_MODE, - NETXEN_CRB_NORMALIZE(adapter, CRB_MPORT_MODE)); - writel(PHAN_INITIALIZE_ACK, - NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE)); + adapter->pci_write_normalize(adapter, + CRB_NIC_CAPABILITIES_HOST, INTR_SCHEME_PERPORT); + adapter->pci_write_normalize(adapter, + CRB_NIC_MSI_MODE_HOST, MSI_MODE_MULTIFUNC); + adapter->pci_write_normalize(adapter, + CRB_MPORT_MODE, MPORT_MULTI_FUNCTION_MODE); + adapter->pci_write_normalize(adapter, + CRB_CMDPEG_STATE, PHAN_INITIALIZE_ACK); return err; } -#define NETXEN_ADDR_LIMIT 0xffffffffULL +void netxen_release_rx_buffers(struct netxen_adapter *adapter) +{ + struct netxen_recv_context *recv_ctx; + struct nx_host_rds_ring *rds_ring; + struct netxen_rx_buffer *rx_buf; + int i, ctxid, ring; + + for (ctxid = 0; ctxid < MAX_RCV_CTX; ++ctxid) { + recv_ctx = &adapter->recv_ctx[ctxid]; + for (ring = 0; ring < adapter->max_rds_rings; ring++) { + rds_ring = &recv_ctx->rds_rings[ring]; + for (i = 0; i < rds_ring->max_rx_desc_count; ++i) { + rx_buf = &(rds_ring->rx_buf_arr[i]); + if (rx_buf->state == NETXEN_BUFFER_FREE) + continue; + pci_unmap_single(adapter->pdev, + rx_buf->dma, + rds_ring->dma_size, + PCI_DMA_FROMDEVICE); + if (rx_buf->skb != NULL) + dev_kfree_skb_any(rx_buf->skb); + } + } + } +} -void *netxen_alloc(struct pci_dev *pdev, size_t sz, dma_addr_t * ptr, - struct pci_dev **used_dev) +void netxen_release_tx_buffers(struct netxen_adapter *adapter) { - void *addr; + struct netxen_cmd_buffer *cmd_buf; + struct netxen_skb_frag *buffrag; + int i, j; + + cmd_buf = adapter->cmd_buf_arr; + for (i = 0; i < adapter->max_tx_desc_count; i++) { + buffrag = cmd_buf->frag_array; + if (buffrag->dma) { + pci_unmap_single(adapter->pdev, buffrag->dma, + buffrag->length, PCI_DMA_TODEVICE); + buffrag->dma = 0ULL; + } + for (j = 0; j < cmd_buf->frag_count; j++) { + buffrag++; + if (buffrag->dma) { + pci_unmap_page(adapter->pdev, buffrag->dma, + buffrag->length, + PCI_DMA_TODEVICE); + buffrag->dma = 0ULL; + } + } + /* Free the skb we received in netxen_nic_xmit_frame */ + if (cmd_buf->skb) { + dev_kfree_skb_any(cmd_buf->skb); + cmd_buf->skb = NULL; + } + cmd_buf++; + } +} - addr = pci_alloc_consistent(pdev, sz, ptr); - if ((unsigned long long)(*ptr) < NETXEN_ADDR_LIMIT) { - *used_dev = pdev; - return addr; +void netxen_free_sw_resources(struct netxen_adapter *adapter) +{ + struct netxen_recv_context *recv_ctx; + struct nx_host_rds_ring *rds_ring; + int ctx, ring; + + for (ctx = 0; ctx < MAX_RCV_CTX; ctx++) { + recv_ctx = &adapter->recv_ctx[ctx]; + for (ring = 0; ring < adapter->max_rds_rings; ring++) { + rds_ring = &recv_ctx->rds_rings[ring]; + if (rds_ring->rx_buf_arr) { + vfree(rds_ring->rx_buf_arr); + rds_ring->rx_buf_arr = NULL; + } + } } - pci_free_consistent(pdev, sz, addr, *ptr); - addr = pci_alloc_consistent(NULL, sz, ptr); - *used_dev = NULL; - return addr; + if (adapter->cmd_buf_arr) + vfree(adapter->cmd_buf_arr); + return; } -void netxen_initialize_adapter_sw(struct netxen_adapter *adapter) +int netxen_alloc_sw_resources(struct netxen_adapter *adapter) { - int ctxid, ring; - u32 i; - u32 num_rx_bufs = 0; - struct netxen_rcv_desc_ctx *rcv_desc; + struct netxen_recv_context *recv_ctx; + struct nx_host_rds_ring *rds_ring; + struct netxen_rx_buffer *rx_buf; + int ctx, ring, i, num_rx_bufs; - DPRINTK(INFO, "initializing some queues: %p\n", adapter); - for (ctxid = 0; ctxid < MAX_RCV_CTX; ++ctxid) { - for (ring = 0; ring < NUM_RCV_DESC_RINGS; ring++) { - struct netxen_rx_buffer *rx_buf; - rcv_desc = &adapter->recv_ctx[ctxid].rcv_desc[ring]; - rcv_desc->begin_alloc = 0; - rx_buf = rcv_desc->rx_buf_arr; - num_rx_bufs = rcv_desc->max_rx_desc_count; + struct netxen_cmd_buffer *cmd_buf_arr; + struct net_device *netdev = adapter->netdev; + + cmd_buf_arr = (struct netxen_cmd_buffer *)vmalloc(TX_RINGSIZE); + if (cmd_buf_arr == NULL) { + printk(KERN_ERR "%s: Failed to allocate cmd buffer ring\n", + netdev->name); + return -ENOMEM; + } + memset(cmd_buf_arr, 0, TX_RINGSIZE); + adapter->cmd_buf_arr = cmd_buf_arr; + + for (ctx = 0; ctx < MAX_RCV_CTX; ctx++) { + recv_ctx = &adapter->recv_ctx[ctx]; + for (ring = 0; ring < adapter->max_rds_rings; ring++) { + rds_ring = &recv_ctx->rds_rings[ring]; + switch (RCV_DESC_TYPE(ring)) { + case RCV_DESC_NORMAL: + rds_ring->max_rx_desc_count = + adapter->max_rx_desc_count; + rds_ring->flags = RCV_DESC_NORMAL; + if (adapter->ahw.cut_through) { + rds_ring->dma_size = + NX_CT_DEFAULT_RX_BUF_LEN; + rds_ring->skb_size = + NX_CT_DEFAULT_RX_BUF_LEN; + } else { + rds_ring->dma_size = RX_DMA_MAP_LEN; + rds_ring->skb_size = + MAX_RX_BUFFER_LENGTH; + } + break; + + case RCV_DESC_JUMBO: + rds_ring->max_rx_desc_count = + adapter->max_jumbo_rx_desc_count; + rds_ring->flags = RCV_DESC_JUMBO; + if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) + rds_ring->dma_size = + NX_P3_RX_JUMBO_BUF_MAX_LEN; + else + rds_ring->dma_size = + NX_P2_RX_JUMBO_BUF_MAX_LEN; + rds_ring->skb_size = + rds_ring->dma_size + NET_IP_ALIGN; + break; + + case RCV_RING_LRO: + rds_ring->max_rx_desc_count = + adapter->max_lro_rx_desc_count; + rds_ring->flags = RCV_DESC_LRO; + rds_ring->dma_size = RX_LRO_DMA_MAP_LEN; + rds_ring->skb_size = MAX_RX_LRO_BUFFER_LENGTH; + break; + + } + rds_ring->rx_buf_arr = (struct netxen_rx_buffer *) + vmalloc(RCV_BUFFSIZE); + if (rds_ring->rx_buf_arr == NULL) { + printk(KERN_ERR "%s: Failed to allocate " + "rx buffer ring %d\n", + netdev->name, ring); + /* free whatever was already allocated */ + goto err_out; + } + memset(rds_ring->rx_buf_arr, 0, RCV_BUFFSIZE); + INIT_LIST_HEAD(&rds_ring->free_list); + rds_ring->begin_alloc = 0; /* * Now go through all of them, set reference handles * and put them in the queues. */ + num_rx_bufs = rds_ring->max_rx_desc_count; + rx_buf = rds_ring->rx_buf_arr; for (i = 0; i < num_rx_bufs; i++) { + list_add_tail(&rx_buf->list, + &rds_ring->free_list); rx_buf->ref_handle = i; rx_buf->state = NETXEN_BUFFER_FREE; - DPRINTK(INFO, "Rx buf:ctx%d i(%d) rx_buf:" - "%p\n", ctxid, i, rx_buf); rx_buf++; } } } + + return 0; + +err_out: + netxen_free_sw_resources(adapter); + return -ENOMEM; } void netxen_initialize_adapter_ops(struct netxen_adapter *adapter) @@ -211,14 +340,12 @@ void netxen_initialize_adapter_ops(struct netxen_adapter *adapter) netxen_niu_gbe_enable_phy_interrupts; adapter->disable_phy_interrupts = netxen_niu_gbe_disable_phy_interrupts; - adapter->handle_phy_intr = netxen_nic_gbe_handle_phy_intr; adapter->macaddr_set = netxen_niu_macaddr_set; adapter->set_mtu = netxen_nic_set_mtu_gb; adapter->set_promisc = netxen_niu_set_promiscuous_mode; - 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_niu = netxen_nic_init_niu_gb; + adapter->init_port = netxen_niu_gbe_init_port; adapter->stop_port = netxen_niu_disable_gbe_port; break; @@ -227,12 +354,10 @@ void netxen_initialize_adapter_ops(struct netxen_adapter *adapter) netxen_niu_xgbe_enable_phy_interrupts; adapter->disable_phy_interrupts = netxen_niu_xgbe_disable_phy_interrupts; - adapter->handle_phy_intr = netxen_nic_xgbe_handle_phy_intr; adapter->macaddr_set = netxen_niu_xg_macaddr_set; adapter->set_mtu = netxen_nic_set_mtu_xgb; adapter->init_port = netxen_niu_xg_init_port; adapter->set_promisc = netxen_niu_xg_set_promiscuous_mode; - adapter->unset_promisc = netxen_niu_xg_set_promiscuous_mode; adapter->stop_port = netxen_niu_disable_xg_port; break; @@ -270,7 +395,9 @@ static u32 netxen_decode_crb_addr(u32 addr) static long rom_max_timeout = 100; static long rom_lock_timeout = 10000; +#if 0 static long rom_write_timeout = 700; +#endif static int rom_lock(struct netxen_adapter *adapter) { @@ -319,6 +446,7 @@ static int netxen_wait_rom_done(struct netxen_adapter *adapter) return 0; } +#if 0 static int netxen_rom_wren(struct netxen_adapter *adapter) { /* Set write enable latch in ROM status register */ @@ -348,6 +476,7 @@ static int netxen_do_rom_rdsr(struct netxen_adapter *adapter) } return netxen_rdcrbreg(adapter, NETXEN_ROMUSB_ROM_RDATA); } +#endif static void netxen_rom_unlock(struct netxen_adapter *adapter) { @@ -358,6 +487,7 @@ static void netxen_rom_unlock(struct netxen_adapter *adapter) } +#if 0 static int netxen_rom_wip_poll(struct netxen_adapter *adapter) { long timeout = 0; @@ -393,6 +523,7 @@ static int do_rom_fast_write(struct netxen_adapter *adapter, int addr, return netxen_rom_wip_poll(adapter); } +#endif static int do_rom_fast_read(struct netxen_adapter *adapter, int addr, int *valp) @@ -475,7 +606,6 @@ int netxen_rom_fast_write(struct netxen_adapter *adapter, int addr, int data) netxen_rom_unlock(adapter); return ret; } -#endif /* 0 */ static int do_rom_fast_write_words(struct netxen_adapter *adapter, int addr, u8 *bytes, size_t size) @@ -740,28 +870,25 @@ int netxen_flash_unlock(struct netxen_adapter *adapter) return ret; } +#endif /* 0 */ #define NETXEN_BOARDTYPE 0x4008 #define NETXEN_BOARDNUM 0x400c #define NETXEN_CHIPNUM 0x4010 -#define NETXEN_ROMBUS_RESET 0xFFFFFFFF -#define NETXEN_ROM_FIRST_BARRIER 0x800000000ULL -#define NETXEN_ROM_FOUND_INIT 0x400 int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose) { int addr, val; - int n, i; - int init_delay = 0; + int i, init_delay = 0; struct crb_addr_pair *buf; + unsigned offset, n; u32 off; /* resetall */ netxen_crb_writelit_adapter(adapter, NETXEN_ROMUSB_GLB_SW_RESET, - NETXEN_ROMBUS_RESET); + 0xffffffff); if (verbose) { - int val; if (netxen_rom_fast_read(adapter, NETXEN_BOARDTYPE, &val) == 0) printk("P2 ROM board type: 0x%08x\n", val); else @@ -776,117 +903,141 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose) printk("Could not read chip number\n"); } - if (netxen_rom_fast_read(adapter, 0, &n) == 0 - && (n & NETXEN_ROM_FIRST_BARRIER)) { - n &= ~NETXEN_ROM_ROUNDUP; - if (n < NETXEN_ROM_FOUND_INIT) { - if (verbose) - printk("%s: %d CRB init values found" - " in ROM.\n", netxen_nic_driver_name, n); - } else { - printk("%s:n=0x%x Error! NetXen card flash not" - " initialized.\n", __FUNCTION__, n); + if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) { + if (netxen_rom_fast_read(adapter, 0, &n) != 0 || + (n != 0xcafecafeUL) || + netxen_rom_fast_read(adapter, 4, &n) != 0) { + printk(KERN_ERR "%s: ERROR Reading crb_init area: " + "n: %08x\n", netxen_nic_driver_name, n); return -EIO; } - buf = kcalloc(n, sizeof(struct crb_addr_pair), GFP_KERNEL); - if (buf == NULL) { - printk("%s: netxen_pinit_from_rom: Unable to calloc " - "memory.\n", netxen_nic_driver_name); - return -ENOMEM; - } - for (i = 0; i < n; i++) { - if (netxen_rom_fast_read(adapter, 8 * i + 4, &val) != 0 - || netxen_rom_fast_read(adapter, 8 * i + 8, - &addr) != 0) - return -EIO; - - buf[i].addr = addr; - buf[i].data = val; - - if (verbose) - printk("%s: PCI: 0x%08x == 0x%08x\n", - netxen_nic_driver_name, (unsigned int) - netxen_decode_crb_addr(addr), val); + offset = n & 0xffffU; + n = (n >> 16) & 0xffffU; + } else { + if (netxen_rom_fast_read(adapter, 0, &n) != 0 || + !(n & 0x80000000)) { + printk(KERN_ERR "%s: ERROR Reading crb_init area: " + "n: %08x\n", netxen_nic_driver_name, n); + return -EIO; } - for (i = 0; i < n; i++) { + offset = 1; + n &= ~0x80000000; + } + + if (n < 1024) { + if (verbose) + printk(KERN_DEBUG "%s: %d CRB init values found" + " in ROM.\n", netxen_nic_driver_name, n); + } else { + printk(KERN_ERR "%s:n=0x%x Error! NetXen card flash not" + " initialized.\n", __func__, n); + return -EIO; + } + + buf = kcalloc(n, sizeof(struct crb_addr_pair), GFP_KERNEL); + if (buf == NULL) { + printk("%s: netxen_pinit_from_rom: Unable to calloc memory.\n", + netxen_nic_driver_name); + return -ENOMEM; + } + for (i = 0; i < n; i++) { + if (netxen_rom_fast_read(adapter, 8*i + 4*offset, &val) != 0 || + netxen_rom_fast_read(adapter, 8*i + 4*offset + 4, &addr) != 0) + return -EIO; + + buf[i].addr = addr; + buf[i].data = val; - off = netxen_decode_crb_addr(buf[i].addr); - if (off == NETXEN_ADDR_ERROR) { - printk(KERN_ERR"CRB init value out of range %x\n", + if (verbose) + printk(KERN_DEBUG "%s: PCI: 0x%08x == 0x%08x\n", + netxen_nic_driver_name, + (u32)netxen_decode_crb_addr(addr), val); + } + for (i = 0; i < n; i++) { + + off = netxen_decode_crb_addr(buf[i].addr); + if (off == NETXEN_ADDR_ERROR) { + printk(KERN_ERR"CRB init value out of range %x\n", buf[i].addr); + continue; + } + off += NETXEN_PCI_CRBSPACE; + /* skipping cold reboot MAGIC */ + if (off == NETXEN_CAM_RAM(0x1fc)) + continue; + + if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) { + /* do not reset PCI */ + if (off == (ROMUSB_GLB + 0xbc)) continue; - } - off += NETXEN_PCI_CRBSPACE; - /* skipping cold reboot MAGIC */ - if (off == NETXEN_CAM_RAM(0x1fc)) + if (off == (NETXEN_CRB_PEG_NET_1 + 0x18)) + buf[i].data = 0x1020; + /* skip the function enable register */ + if (off == NETXEN_PCIE_REG(PCIE_SETUP_FUNCTION)) + continue; + if (off == NETXEN_PCIE_REG(PCIE_SETUP_FUNCTION2)) continue; + if ((off & 0x0ff00000) == NETXEN_CRB_SMB) + continue; + } - /* After writing this register, HW needs time for CRB */ - /* to quiet down (else crb_window returns 0xffffffff) */ - if (off == NETXEN_ROMUSB_GLB_SW_RESET) { - init_delay = 1; + if (off == NETXEN_ADDR_ERROR) { + printk(KERN_ERR "%s: Err: Unknown addr: 0x%08x\n", + netxen_nic_driver_name, buf[i].addr); + continue; + } + + /* After writing this register, HW needs time for CRB */ + /* to quiet down (else crb_window returns 0xffffffff) */ + if (off == NETXEN_ROMUSB_GLB_SW_RESET) { + init_delay = 1; + if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) { /* hold xdma in reset also */ buf[i].data = NETXEN_NIC_XDMA_RESET; } + } - if (ADDR_IN_WINDOW1(off)) { - writel(buf[i].data, - NETXEN_CRB_NORMALIZE(adapter, off)); - } else { - netxen_nic_pci_change_crbwindow(adapter, 0); - writel(buf[i].data, - pci_base_offset(adapter, off)); + adapter->hw_write_wx(adapter, off, &buf[i].data, 4); - netxen_nic_pci_change_crbwindow(adapter, 1); - } - if (init_delay == 1) { - msleep(1000); - init_delay = 0; - } - msleep(1); + if (init_delay == 1) { + msleep(1000); + init_delay = 0; } - kfree(buf); + msleep(1); + } + kfree(buf); - /* disable_peg_cache_all */ + /* disable_peg_cache_all */ - /* unreset_net_cache */ - netxen_nic_hw_read_wx(adapter, NETXEN_ROMUSB_GLB_SW_RESET, &val, - 4); - netxen_crb_writelit_adapter(adapter, NETXEN_ROMUSB_GLB_SW_RESET, - (val & 0xffffff0f)); - /* p2dn replyCount */ - netxen_crb_writelit_adapter(adapter, - NETXEN_CRB_PEG_NET_D + 0xec, 0x1e); - /* disable_peg_cache 0 */ + /* unreset_net_cache */ + if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) { + adapter->hw_read_wx(adapter, + NETXEN_ROMUSB_GLB_SW_RESET, &val, 4); netxen_crb_writelit_adapter(adapter, - NETXEN_CRB_PEG_NET_D + 0x4c, 8); - /* disable_peg_cache 1 */ - netxen_crb_writelit_adapter(adapter, - NETXEN_CRB_PEG_NET_I + 0x4c, 8); - - /* peg_clr_all */ - - /* peg_clr 0 */ - netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_0 + 0x8, - 0); - netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_0 + 0xc, - 0); - /* peg_clr 1 */ - netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_1 + 0x8, - 0); - netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_1 + 0xc, - 0); - /* peg_clr 2 */ - netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_2 + 0x8, - 0); - netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_2 + 0xc, - 0); - /* peg_clr 3 */ - netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_3 + 0x8, - 0); - netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_3 + 0xc, - 0); + NETXEN_ROMUSB_GLB_SW_RESET, (val & 0xffffff0f)); } + + /* p2dn replyCount */ + netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_D + 0xec, 0x1e); + /* disable_peg_cache 0 */ + netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_D + 0x4c, 8); + /* disable_peg_cache 1 */ + netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_I + 0x4c, 8); + + /* peg_clr_all */ + + /* peg_clr 0 */ + netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_0 + 0x8, 0); + netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_0 + 0xc, 0); + /* peg_clr 1 */ + netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_1 + 0x8, 0); + netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_1 + 0xc, 0); + /* peg_clr 2 */ + netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_2 + 0x8, 0); + netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_2 + 0xc, 0); + /* peg_clr 3 */ + netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_3 + 0x8, 0); + netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_3 + 0xc, 0); return 0; } @@ -897,12 +1048,12 @@ int netxen_initialize_adapter_offload(struct netxen_adapter *adapter) uint32_t lo; adapter->dummy_dma.addr = - pci_alloc_consistent(adapter->ahw.pdev, + pci_alloc_consistent(adapter->pdev, NETXEN_HOST_DUMMY_DMA_SIZE, &adapter->dummy_dma.phys_addr); if (adapter->dummy_dma.addr == NULL) { printk("%s: ERROR: Could not allocate dummy DMA memory\n", - __FUNCTION__); + __func__); return -ENOMEM; } @@ -910,8 +1061,13 @@ int netxen_initialize_adapter_offload(struct netxen_adapter *adapter) hi = (addr >> 32) & 0xffffffff; lo = addr & 0xffffffff; - writel(hi, NETXEN_CRB_NORMALIZE(adapter, CRB_HOST_DUMMY_BUF_ADDR_HI)); - writel(lo, NETXEN_CRB_NORMALIZE(adapter, CRB_HOST_DUMMY_BUF_ADDR_LO)); + adapter->pci_write_normalize(adapter, CRB_HOST_DUMMY_BUF_ADDR_HI, hi); + adapter->pci_write_normalize(adapter, CRB_HOST_DUMMY_BUF_ADDR_LO, lo); + + if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) { + uint32_t temp = 0; + adapter->hw_write_wx(adapter, CRB_HOST_DUMMY_BUF, &temp, 4); + } return 0; } @@ -931,7 +1087,7 @@ void netxen_free_adapter_offload(struct netxen_adapter *adapter) } while (--i); if (i) { - pci_free_consistent(adapter->ahw.pdev, + pci_free_consistent(adapter->pdev, NETXEN_HOST_DUMMY_DMA_SIZE, adapter->dummy_dma.addr, adapter->dummy_dma.phys_addr); @@ -946,22 +1102,24 @@ void netxen_free_adapter_offload(struct netxen_adapter *adapter) int netxen_phantom_init(struct netxen_adapter *adapter, int pegtune_val) { u32 val = 0; - int retries = 30; + int retries = 60; if (!pegtune_val) { do { - val = readl(NETXEN_CRB_NORMALIZE - (adapter, CRB_CMDPEG_STATE)); - pegtune_val = readl(NETXEN_CRB_NORMALIZE - (adapter, NETXEN_ROMUSB_GLB_PEGTUNE_DONE)); + val = adapter->pci_read_normalize(adapter, + CRB_CMDPEG_STATE); if (val == PHAN_INITIALIZE_COMPLETE || val == PHAN_INITIALIZE_ACK) return 0; - msleep(1000); + msleep(500); + } while (--retries); + if (!retries) { + pegtune_val = adapter->pci_read_normalize(adapter, + NETXEN_ROMUSB_GLB_PEGTUNE_DONE); printk(KERN_WARNING "netxen_phantom_init: init failed, " "pegtune_val=%x\n", pegtune_val); return -1; @@ -971,58 +1129,61 @@ int netxen_phantom_init(struct netxen_adapter *adapter, int pegtune_val) return 0; } -static int netxen_nic_check_temp(struct netxen_adapter *adapter) +int netxen_receive_peg_ready(struct netxen_adapter *adapter) { - struct net_device *netdev = adapter->netdev; - uint32_t temp, temp_state, temp_val; - int rv = 0; - - temp = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_TEMP_STATE)); - - temp_state = nx_get_temp_state(temp); - temp_val = nx_get_temp_val(temp); - - if (temp_state == NX_TEMP_PANIC) { - printk(KERN_ALERT - "%s: Device temperature %d degrees C exceeds" - " maximum allowed. Hardware has been shut down.\n", - netxen_nic_driver_name, temp_val); - - netif_carrier_off(netdev); - netif_stop_queue(netdev); - rv = 1; - } else if (temp_state == NX_TEMP_WARN) { - if (adapter->temp == NX_TEMP_NORMAL) { - printk(KERN_ALERT - "%s: Device temperature %d degrees C " - "exceeds operating range." - " Immediate action needed.\n", - netxen_nic_driver_name, temp_val); - } - } else { - if (adapter->temp == NX_TEMP_WARN) { - printk(KERN_INFO - "%s: Device temperature is now %d degrees C" - " in normal range.\n", netxen_nic_driver_name, - temp_val); - } + u32 val = 0; + int retries = 2000; + + do { + val = adapter->pci_read_normalize(adapter, CRB_RCVPEG_STATE); + + if (val == PHAN_PEG_RCV_INITIALIZED) + return 0; + + msleep(10); + + } while (--retries); + + if (!retries) { + printk(KERN_ERR "Receive Peg initialization not " + "complete, state: 0x%x.\n", val); + return -EIO; } - adapter->temp = temp_state; - return rv; + + return 0; } -void netxen_watchdog_task(struct work_struct *work) +static struct sk_buff *netxen_process_rxbuf(struct netxen_adapter *adapter, + struct nx_host_rds_ring *rds_ring, u16 index, u16 cksum) { - struct netxen_adapter *adapter = - container_of(work, struct netxen_adapter, watchdog_task); + struct netxen_rx_buffer *buffer; + struct sk_buff *skb; - if ((adapter->portnum == 0) && netxen_nic_check_temp(adapter)) - return; + buffer = &rds_ring->rx_buf_arr[index]; + + pci_unmap_single(adapter->pdev, buffer->dma, rds_ring->dma_size, + PCI_DMA_FROMDEVICE); - if (adapter->handle_phy_intr) - adapter->handle_phy_intr(adapter); + skb = buffer->skb; + if (!skb) + goto no_skb; - mod_timer(&adapter->watchdog_timer, jiffies + 2 * HZ); + if (likely(adapter->rx_csum && cksum == STATUS_CKSUM_OK)) { + adapter->stats.csummed++; + skb->ip_summed = CHECKSUM_UNNECESSARY; + } else + skb->ip_summed = CHECKSUM_NONE; + + skb->dev = adapter->netdev; + + buffer->skb = NULL; + +no_skb: + buffer->state = NETXEN_BUFFER_FREE; + buffer->lro_current_frags = 0; + buffer->lro_expected_frags = 0; + list_add_tail(&buffer->list, &rds_ring->free_list); + return skb; } /* @@ -1031,9 +1192,8 @@ void netxen_watchdog_task(struct work_struct *work) * invoke the routine to send more rx buffers to the Phantom... */ static void netxen_process_rcv(struct netxen_adapter *adapter, int ctxid, - struct status_desc *desc) + struct status_desc *desc, struct status_desc *frag_desc) { - struct pci_dev *pdev = adapter->pdev; struct net_device *netdev = adapter->netdev; u64 sts_data = le64_to_cpu(desc->status_desc_data); int index = netxen_get_sts_refhandle(sts_data); @@ -1042,8 +1202,8 @@ static void netxen_process_rcv(struct netxen_adapter *adapter, int ctxid, struct sk_buff *skb; u32 length = netxen_get_sts_totallength(sts_data); u32 desc_ctx; - struct netxen_rcv_desc_ctx *rcv_desc; - int ret; + u16 pkt_offset = 0, cksum; + struct nx_host_rds_ring *rds_ring; desc_ctx = netxen_get_sts_type(sts_data); if (unlikely(desc_ctx >= NUM_RCV_DESC_RINGS)) { @@ -1052,13 +1212,13 @@ static void netxen_process_rcv(struct netxen_adapter *adapter, int ctxid, return; } - rcv_desc = &recv_ctx->rcv_desc[desc_ctx]; - if (unlikely(index > rcv_desc->max_rx_desc_count)) { + rds_ring = &recv_ctx->rds_rings[desc_ctx]; + if (unlikely(index > rds_ring->max_rx_desc_count)) { DPRINTK(ERR, "Got a buffer index:%x Max is %x\n", - index, rcv_desc->max_rx_desc_count); + index, rds_ring->max_rx_desc_count); return; } - buffer = &rcv_desc->rx_buf_arr[index]; + buffer = &rds_ring->rx_buf_arr[index]; if (desc_ctx == RCV_DESC_LRO_CTXID) { buffer->lro_current_frags++; if (netxen_get_sts_desc_lro_last_frag(desc)) { @@ -1079,43 +1239,52 @@ static void netxen_process_rcv(struct netxen_adapter *adapter, int ctxid, } } - pci_unmap_single(pdev, buffer->dma, rcv_desc->dma_size, - PCI_DMA_FROMDEVICE); + cksum = netxen_get_sts_status(sts_data); - skb = (struct sk_buff *)buffer->skb; - - if (likely(adapter->rx_csum && - netxen_get_sts_status(sts_data) == STATUS_CKSUM_OK)) { - adapter->stats.csummed++; - skb->ip_summed = CHECKSUM_UNNECESSARY; - } else - skb->ip_summed = CHECKSUM_NONE; + skb = netxen_process_rxbuf(adapter, rds_ring, index, cksum); + if (!skb) + return; - 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); } else { - skb_put(skb, length); + if (length > rds_ring->skb_size) + skb_put(skb, rds_ring->skb_size); + else + skb_put(skb, length); + + pkt_offset = netxen_get_sts_pkt_offset(sts_data); + if (pkt_offset) + skb_pull(skb, pkt_offset); } skb->protocol = eth_type_trans(skb, netdev); - ret = netif_receive_skb(skb); - netdev->last_rx = jiffies; - - rcv_desc->rcv_pending--; - /* - * We just consumed one buffer so post a buffer. + * rx buffer chaining is disabled, walk and free + * any spurious rx buffer chain. */ - buffer->skb = NULL; - buffer->state = NETXEN_BUFFER_FREE; - buffer->lro_current_frags = 0; - buffer->lro_expected_frags = 0; + if (frag_desc) { + u16 i, nr_frags = desc->nr_frags; + + dev_kfree_skb_any(skb); + for (i = 0; i < nr_frags; i++) { + index = frag_desc->frag_handles[i]; + skb = netxen_process_rxbuf(adapter, + rds_ring, index, cksum); + if (skb) + dev_kfree_skb_any(skb); + } + adapter->stats.rxdropped++; + } else { - adapter->stats.no_rcv++; - adapter->stats.rxbytes += length; + netif_receive_skb(skb); + netdev->last_rx = jiffies; + + adapter->stats.no_rcv++; + adapter->stats.rxbytes += length; + } } /* Process Receive status ring */ @@ -1123,10 +1292,11 @@ u32 netxen_process_rcv_ring(struct netxen_adapter *adapter, int ctxid, int max) { struct netxen_recv_context *recv_ctx = &(adapter->recv_ctx[ctxid]); struct status_desc *desc_head = recv_ctx->rcv_status_desc_head; - struct status_desc *desc; /* used to read status desc here */ + struct status_desc *desc, *frag_desc; u32 consumer = recv_ctx->status_rx_consumer; - u32 producer = 0; int count = 0, ring; + u64 sts_data; + u16 opcode; while (count < max) { desc = &desc_head[consumer]; @@ -1135,24 +1305,38 @@ u32 netxen_process_rcv_ring(struct netxen_adapter *adapter, int ctxid, int max) netxen_get_sts_owner(desc)); break; } - netxen_process_rcv(adapter, ctxid, desc); + + sts_data = le64_to_cpu(desc->status_desc_data); + opcode = netxen_get_sts_opcode(sts_data); + frag_desc = NULL; + if (opcode == NETXEN_NIC_RXPKT_DESC) { + if (desc->nr_frags) { + consumer = get_next_index(consumer, + adapter->max_rx_desc_count); + frag_desc = &desc_head[consumer]; + netxen_set_sts_owner(frag_desc, + STATUS_OWNER_PHANTOM); + } + } + + netxen_process_rcv(adapter, ctxid, desc, frag_desc); + netxen_set_sts_owner(desc, STATUS_OWNER_PHANTOM); - consumer = (consumer + 1) & (adapter->max_rx_desc_count - 1); + + consumer = get_next_index(consumer, + adapter->max_rx_desc_count); count++; } - for (ring = 0; ring < NUM_RCV_DESC_RINGS; ring++) + for (ring = 0; ring < adapter->max_rds_rings; ring++) netxen_post_rx_buffers_nodb(adapter, ctxid, ring); /* update the consumer index in phantom */ if (count) { recv_ctx->status_rx_consumer = consumer; - recv_ctx->status_rx_producer = producer; /* Window = 1 */ - writel(consumer, - NETXEN_CRB_NORMALIZE(adapter, - recv_crb_registers[adapter->portnum]. - crb_rcv_status_consumer)); + adapter->pci_write_normalize(adapter, + recv_ctx->crb_sts_consumer, consumer); } return count; @@ -1231,10 +1415,10 @@ int netxen_process_cmd_ring(struct netxen_adapter *adapter) */ void netxen_post_rx_buffers(struct netxen_adapter *adapter, u32 ctx, u32 ringid) { - struct pci_dev *pdev = adapter->ahw.pdev; + struct pci_dev *pdev = adapter->pdev; struct sk_buff *skb; struct netxen_recv_context *recv_ctx = &(adapter->recv_ctx[ctx]); - struct netxen_rcv_desc_ctx *rcv_desc = NULL; + struct nx_host_rds_ring *rds_ring = NULL; uint producer; struct rcv_desc *pdesc; struct netxen_rx_buffer *buffer; @@ -1242,41 +1426,36 @@ void netxen_post_rx_buffers(struct netxen_adapter *adapter, u32 ctx, u32 ringid) int index = 0; netxen_ctx_msg msg = 0; dma_addr_t dma; + struct list_head *head; - rcv_desc = &recv_ctx->rcv_desc[ringid]; + rds_ring = &recv_ctx->rds_rings[ringid]; + + producer = rds_ring->producer; + index = rds_ring->begin_alloc; + head = &rds_ring->free_list; - producer = rcv_desc->producer; - index = rcv_desc->begin_alloc; - buffer = &rcv_desc->rx_buf_arr[index]; /* We can start writing rx descriptors into the phantom memory. */ - while (buffer->state == NETXEN_BUFFER_FREE) { - skb = dev_alloc_skb(rcv_desc->skb_size); + while (!list_empty(head)) { + + skb = dev_alloc_skb(rds_ring->skb_size); if (unlikely(!skb)) { - /* - * TODO - * We need to schedule the posting of buffers to the pegs. - */ - rcv_desc->begin_alloc = index; - DPRINTK(ERR, "netxen_post_rx_buffers: " - " allocated only %d buffers\n", count); + rds_ring->begin_alloc = index; break; } + buffer = list_entry(head->next, struct netxen_rx_buffer, list); + list_del(&buffer->list); + count++; /* now there should be no failure */ - pdesc = &rcv_desc->desc_head[producer]; + pdesc = &rds_ring->desc_head[producer]; -#if defined(XGB_DEBUG) - *(unsigned long *)(skb->head) = 0xc0debabe; - if (skb_is_nonlinear(skb)) { - printk("Allocated SKB @%p is nonlinear\n"); - } -#endif - skb_reserve(skb, 2); + if (!adapter->ahw.cut_through) + skb_reserve(skb, 2); /* This will be setup when we receive the * buffer after it has been filled FSL TBD TBD * skb->dev = netdev; */ - dma = pci_map_single(pdev, skb->data, rcv_desc->dma_size, + dma = pci_map_single(pdev, skb->data, rds_ring->dma_size, PCI_DMA_FROMDEVICE); pdesc->addr_buffer = cpu_to_le64(dma); buffer->skb = skb; @@ -1284,112 +1463,101 @@ void netxen_post_rx_buffers(struct netxen_adapter *adapter, u32 ctx, u32 ringid) buffer->dma = dma; /* make a rcv descriptor */ pdesc->reference_handle = cpu_to_le16(buffer->ref_handle); - pdesc->buffer_length = cpu_to_le32(rcv_desc->dma_size); + pdesc->buffer_length = cpu_to_le32(rds_ring->dma_size); DPRINTK(INFO, "done writing descripter\n"); producer = - get_next_index(producer, rcv_desc->max_rx_desc_count); - index = get_next_index(index, rcv_desc->max_rx_desc_count); - buffer = &rcv_desc->rx_buf_arr[index]; + get_next_index(producer, rds_ring->max_rx_desc_count); + index = get_next_index(index, rds_ring->max_rx_desc_count); } /* if we did allocate buffers, then write the count to Phantom */ if (count) { - rcv_desc->begin_alloc = index; - rcv_desc->rcv_pending += count; - rcv_desc->producer = producer; + rds_ring->begin_alloc = index; + rds_ring->producer = producer; /* Window = 1 */ - writel((producer - 1) & - (rcv_desc->max_rx_desc_count - 1), - NETXEN_CRB_NORMALIZE(adapter, - recv_crb_registers[ - adapter->portnum]. - rcv_desc_crb[ringid]. - crb_rcv_producer_offset)); + adapter->pci_write_normalize(adapter, + rds_ring->crb_rcv_producer, + (producer-1) & (rds_ring->max_rx_desc_count-1)); + + if (adapter->fw_major < 4) { /* * Write a doorbell msg to tell phanmon of change in * receive ring producer + * Only for firmware version < 4.0.0 */ netxen_set_msg_peg_id(msg, NETXEN_RCV_PEG_DB_ID); netxen_set_msg_privid(msg); netxen_set_msg_count(msg, ((producer - - 1) & (rcv_desc-> + 1) & (rds_ring-> max_rx_desc_count - 1))); netxen_set_msg_ctxid(msg, adapter->portnum); netxen_set_msg_opcode(msg, NETXEN_RCV_PRODUCER(ringid)); writel(msg, DB_NORMALIZE(adapter, NETXEN_RCV_PRODUCER_OFFSET)); + } } } static void netxen_post_rx_buffers_nodb(struct netxen_adapter *adapter, uint32_t ctx, uint32_t ringid) { - struct pci_dev *pdev = adapter->ahw.pdev; + struct pci_dev *pdev = adapter->pdev; struct sk_buff *skb; struct netxen_recv_context *recv_ctx = &(adapter->recv_ctx[ctx]); - struct netxen_rcv_desc_ctx *rcv_desc = NULL; + struct nx_host_rds_ring *rds_ring = NULL; u32 producer; struct rcv_desc *pdesc; struct netxen_rx_buffer *buffer; int count = 0; int index = 0; + struct list_head *head; - rcv_desc = &recv_ctx->rcv_desc[ringid]; + rds_ring = &recv_ctx->rds_rings[ringid]; - producer = rcv_desc->producer; - index = rcv_desc->begin_alloc; - buffer = &rcv_desc->rx_buf_arr[index]; + producer = rds_ring->producer; + index = rds_ring->begin_alloc; + head = &rds_ring->free_list; /* We can start writing rx descriptors into the phantom memory. */ - while (buffer->state == NETXEN_BUFFER_FREE) { - skb = dev_alloc_skb(rcv_desc->skb_size); + while (!list_empty(head)) { + + skb = dev_alloc_skb(rds_ring->skb_size); if (unlikely(!skb)) { - /* - * We need to schedule the posting of buffers to the pegs. - */ - rcv_desc->begin_alloc = index; - DPRINTK(ERR, "netxen_post_rx_buffers_nodb: " - " allocated only %d buffers\n", count); + rds_ring->begin_alloc = index; break; } + + buffer = list_entry(head->next, struct netxen_rx_buffer, list); + list_del(&buffer->list); + count++; /* now there should be no failure */ - pdesc = &rcv_desc->desc_head[producer]; - skb_reserve(skb, 2); - /* - * This will be setup when we receive the - * buffer after it has been filled - * skb->dev = netdev; - */ + pdesc = &rds_ring->desc_head[producer]; + if (!adapter->ahw.cut_through) + skb_reserve(skb, 2); buffer->skb = skb; buffer->state = NETXEN_BUFFER_BUSY; buffer->dma = pci_map_single(pdev, skb->data, - rcv_desc->dma_size, + rds_ring->dma_size, PCI_DMA_FROMDEVICE); /* make a rcv descriptor */ pdesc->reference_handle = cpu_to_le16(buffer->ref_handle); - pdesc->buffer_length = cpu_to_le32(rcv_desc->dma_size); + pdesc->buffer_length = cpu_to_le32(rds_ring->dma_size); pdesc->addr_buffer = cpu_to_le64(buffer->dma); - DPRINTK(INFO, "done writing descripter\n"); producer = - get_next_index(producer, rcv_desc->max_rx_desc_count); - index = get_next_index(index, rcv_desc->max_rx_desc_count); - buffer = &rcv_desc->rx_buf_arr[index]; + get_next_index(producer, rds_ring->max_rx_desc_count); + index = get_next_index(index, rds_ring->max_rx_desc_count); + buffer = &rds_ring->rx_buf_arr[index]; } /* if we did allocate buffers, then write the count to Phantom */ if (count) { - rcv_desc->begin_alloc = index; - rcv_desc->rcv_pending += count; - rcv_desc->producer = producer; + rds_ring->begin_alloc = index; + rds_ring->producer = producer; /* Window = 1 */ - writel((producer - 1) & - (rcv_desc->max_rx_desc_count - 1), - NETXEN_CRB_NORMALIZE(adapter, - recv_crb_registers[ - adapter->portnum]. - rcv_desc_crb[ringid]. - crb_rcv_producer_offset)); + adapter->pci_write_normalize(adapter, + rds_ring->crb_rcv_producer, + (producer-1) & (rds_ring->max_rx_desc_count-1)); wmb(); } } diff --git a/drivers/net/netxen/netxen_nic_isr.c b/drivers/net/netxen/netxen_nic_isr.c deleted file mode 100644 index 96cec41f9019..000000000000 --- a/drivers/net/netxen/netxen_nic_isr.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (C) 2003 - 2006 NetXen, Inc. - * All rights reserved. - * - * 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. - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE. - * - * Contact Information: - * info@netxen.com - * NetXen, - * 3965 Freedom Circle, Fourth floor, - * Santa Clara, CA 95054 - */ - -#include <linux/netdevice.h> -#include <linux/delay.h> - -#include "netxen_nic.h" -#include "netxen_nic_hw.h" -#include "netxen_nic_phan_reg.h" - -/* - * netxen_nic_get_stats - Get System Network Statistics - * @netdev: network interface device structure - */ -struct net_device_stats *netxen_nic_get_stats(struct net_device *netdev) -{ - 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 = adapter->stats.no_rcv; - /* total packets transmitted */ - stats->tx_packets = adapter->stats.xmitedframes + - adapter->stats.xmitfinished; - /* total bytes received */ - stats->rx_bytes = adapter->stats.rxbytes; - /* total bytes transmitted */ - stats->tx_bytes = adapter->stats.txbytes; - /* bad packets received */ - stats->rx_errors = adapter->stats.rcvdbadskb; - /* packet transmit problems */ - stats->tx_errors = adapter->stats.nocmddescriptor; - /* no space in linux buffers */ - stats->rx_dropped = adapter->stats.rxdropped; - /* no space available in linux */ - stats->tx_dropped = adapter->stats.txdropped; - - return stats; -} - -static void netxen_indicate_link_status(struct netxen_adapter *adapter, - u32 link) -{ - struct net_device *netdev = adapter->netdev; - - if (link) - netif_carrier_on(netdev); - else - netif_carrier_off(netdev); -} - -#if 0 -void netxen_handle_port_int(struct netxen_adapter *adapter, u32 enable) -{ - __u32 int_src; - - /* This should clear the interrupt source */ - if (adapter->phy_read) - adapter->phy_read(adapter, - NETXEN_NIU_GB_MII_MGMT_ADDR_INT_STATUS, - &int_src); - if (int_src == 0) { - DPRINTK(INFO, "No phy interrupts for port #%d\n", portno); - return; - } - if (adapter->disable_phy_interrupts) - adapter->disable_phy_interrupts(adapter); - - if (netxen_get_phy_int_jabber(int_src)) - DPRINTK(INFO, "Jabber interrupt \n"); - - if (netxen_get_phy_int_polarity_changed(int_src)) - DPRINTK(INFO, "POLARITY CHANGED int \n"); - - if (netxen_get_phy_int_energy_detect(int_src)) - DPRINTK(INFO, "ENERGY DETECT INT \n"); - - if (netxen_get_phy_int_downshift(int_src)) - DPRINTK(INFO, "DOWNSHIFT INT \n"); - /* write it down later.. */ - if ((netxen_get_phy_int_speed_changed(int_src)) - || (netxen_get_phy_int_link_status_changed(int_src))) { - __u32 status; - - DPRINTK(INFO, "SPEED CHANGED OR LINK STATUS CHANGED \n"); - - if (adapter->phy_read - && 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)) { - printk(KERN_INFO "%s: %s Link UP\n", - netxen_nic_driver_name, - adapter->netdev->name); - - } else { - printk(KERN_INFO "%s: %s Link DOWN\n", - netxen_nic_driver_name, - adapter->netdev->name); - } - netxen_indicate_link_status(adapter, - netxen_get_phy_link - (status)); - } - } - } - if (adapter->enable_phy_interrupts) - adapter->enable_phy_interrupts(adapter); -} -#endif /* 0 */ - -static void netxen_nic_isr_other(struct netxen_adapter *adapter) -{ - int portno = adapter->portnum; - u32 val, linkup, qg_linksup; - - /* verify the offset */ - val = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_XG_STATE)); - val = val >> adapter->physical_port; - 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); - - linkup = val & 1; - - 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) -{ - netxen_nic_isr_other(adapter); -} - -#if 0 -int netxen_nic_link_ok(struct netxen_adapter *adapter) -{ - switch (adapter->ahw.board_type) { - case NETXEN_NIC_GBE: - return ((adapter->ahw.qg_linksup) & 1); - - case NETXEN_NIC_XGBE: - return ((adapter->ahw.xg_linkup) & 1); - - default: - printk(KERN_ERR"%s: Function: %s, Unknown board type\n", - netxen_nic_driver_name, __FUNCTION__); - break; - } - - return 0; -} -#endif /* 0 */ - -void netxen_nic_xgbe_handle_phy_intr(struct netxen_adapter *adapter) -{ - struct net_device *netdev = adapter->netdev; - u32 val; - - /* WINDOW = 1 */ - val = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_XG_STATE)); - val >>= (adapter->physical_port * 8); - val &= 0xff; - - if (adapter->ahw.xg_linkup == 1 && val != XG_LINK_UP) { - printk(KERN_INFO "%s: %s NIC Link is down\n", - netxen_nic_driver_name, netdev->name); - adapter->ahw.xg_linkup = 0; - if (netif_running(netdev)) { - netif_carrier_off(netdev); - netif_stop_queue(netdev); - } - } else if (adapter->ahw.xg_linkup == 0 && val == XG_LINK_UP) { - printk(KERN_INFO "%s: %s NIC Link is up\n", - netxen_nic_driver_name, netdev->name); - adapter->ahw.xg_linkup = 1; - netif_carrier_on(netdev); - netif_wake_queue(netdev); - } -} diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index 63cd67b931e7..91d209a8f6cb 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -49,13 +49,18 @@ char netxen_nic_driver_name[] = "netxen_nic"; static char netxen_nic_driver_string[] = "NetXen Network Driver version " NETXEN_NIC_LINUX_VERSIONID; -#define NETXEN_NETDEV_WEIGHT 120 -#define NETXEN_ADAPTER_UP_MAGIC 777 -#define NETXEN_NIC_PEG_TUNE 0 +static int port_mode = NETXEN_PORT_MODE_AUTO_NEG; + +/* Default to restricted 1G auto-neg mode */ +static int wol_port_mode = 5; + +static int use_msi = 1; + +static int use_msi_x = 1; /* Local functions to NetXen NIC driver */ static int __devinit netxen_nic_probe(struct pci_dev *pdev, - const struct pci_device_id *ent); + const struct pci_device_id *ent); static void __devexit netxen_nic_remove(struct pci_dev *pdev); static int netxen_nic_open(struct net_device *netdev); static int netxen_nic_close(struct net_device *netdev); @@ -83,6 +88,7 @@ static struct pci_device_id netxen_pci_tbl[] __devinitdata = { ENTRY(0x0005), ENTRY(0x0024), ENTRY(0x0025), + ENTRY(0x0100), {0,} }; @@ -108,95 +114,61 @@ static struct workqueue_struct *netxen_workq; static void netxen_watchdog(unsigned long); -static void netxen_nic_update_cmd_producer(struct netxen_adapter *adapter, - uint32_t crb_producer) +static uint32_t crb_cmd_producer[4] = { + CRB_CMD_PRODUCER_OFFSET, CRB_CMD_PRODUCER_OFFSET_1, + CRB_CMD_PRODUCER_OFFSET_2, CRB_CMD_PRODUCER_OFFSET_3 +}; + +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; - } + adapter->pci_write_normalize(adapter, + adapter->crb_addr_cmd_producer, crb_producer); } -static void netxen_nic_update_cmd_consumer(struct netxen_adapter *adapter, - u32 crb_consumer) +static uint32_t crb_cmd_consumer[4] = { + CRB_CMD_CONSUMER_OFFSET, CRB_CMD_CONSUMER_OFFSET_1, + CRB_CMD_CONSUMER_OFFSET_2, CRB_CMD_CONSUMER_OFFSET_3 +}; + +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; - } + adapter->pci_write_normalize(adapter, + adapter->crb_addr_cmd_consumer, crb_consumer); } -#define ADAPTER_LIST_SIZE 12 - -static uint32_t msi_tgt_status[4] = { +static uint32_t msi_tgt_status[8] = { ISR_INT_TARGET_STATUS, ISR_INT_TARGET_STATUS_F1, - ISR_INT_TARGET_STATUS_F2, ISR_INT_TARGET_STATUS_F3 + ISR_INT_TARGET_STATUS_F2, ISR_INT_TARGET_STATUS_F3, + ISR_INT_TARGET_STATUS_F4, ISR_INT_TARGET_STATUS_F5, + ISR_INT_TARGET_STATUS_F6, ISR_INT_TARGET_STATUS_F7 }; -static uint32_t sw_int_mask[4] = { - CRB_SW_INT_MASK_0, CRB_SW_INT_MASK_1, - CRB_SW_INT_MASK_2, CRB_SW_INT_MASK_3 -}; +static struct netxen_legacy_intr_set legacy_intr[] = NX_LEGACY_INTR_CONFIG; static void netxen_nic_disable_int(struct netxen_adapter *adapter) { u32 mask = 0x7ff; int retries = 32; - int port = adapter->portnum; int pci_fn = adapter->ahw.pci_func; if (adapter->msi_mode != MSI_MODE_MULTIFUNC) - writel(0x0, NETXEN_CRB_NORMALIZE(adapter, sw_int_mask[port])); + adapter->pci_write_normalize(adapter, + adapter->crb_intr_mask, 0); if (adapter->intr_scheme != -1 && adapter->intr_scheme != INTR_SCHEME_PERPORT) - writel(mask,PCI_OFFSET_SECOND_RANGE(adapter, ISR_INT_MASK)); + adapter->pci_write_immediate(adapter, ISR_INT_MASK, mask); - if (!(adapter->flags & NETXEN_NIC_MSI_ENABLED)) { + if (!NETXEN_IS_MSI_FAMILY(adapter)) { do { - writel(0xffffffff, - PCI_OFFSET_SECOND_RANGE(adapter, ISR_INT_TARGET_STATUS)); - mask = readl(pci_base_offset(adapter, ISR_INT_VECTOR)); + adapter->pci_write_immediate(adapter, + ISR_INT_TARGET_STATUS, 0xffffffff); + mask = adapter->pci_read_immediate(adapter, + ISR_INT_VECTOR); if (!(mask & 0x80)) break; udelay(10); @@ -208,8 +180,8 @@ static void netxen_nic_disable_int(struct netxen_adapter *adapter) } } else { if (adapter->msi_mode == MSI_MODE_MULTIFUNC) { - writel(0xffffffff, PCI_OFFSET_SECOND_RANGE(adapter, - msi_tgt_status[pci_fn])); + adapter->pci_write_immediate(adapter, + msi_tgt_status[pci_fn], 0xffffffff); } } } @@ -217,7 +189,6 @@ static void netxen_nic_disable_int(struct netxen_adapter *adapter) static void netxen_nic_enable_int(struct netxen_adapter *adapter) { u32 mask; - int port = adapter->portnum; DPRINTK(1, INFO, "Entered ISR Enable \n"); @@ -235,24 +206,299 @@ static void netxen_nic_enable_int(struct netxen_adapter *adapter) break; } - writel(mask, PCI_OFFSET_SECOND_RANGE(adapter, ISR_INT_MASK)); + adapter->pci_write_immediate(adapter, ISR_INT_MASK, mask); } - writel(0x1, NETXEN_CRB_NORMALIZE(adapter, sw_int_mask[port])); + adapter->pci_write_normalize(adapter, adapter->crb_intr_mask, 0x1); - if (!(adapter->flags & NETXEN_NIC_MSI_ENABLED)) { + if (!NETXEN_IS_MSI_FAMILY(adapter)) { mask = 0xbff; if (adapter->intr_scheme != -1 && adapter->intr_scheme != INTR_SCHEME_PERPORT) { - writel(0X0, NETXEN_CRB_NORMALIZE(adapter, CRB_INT_VECTOR)); + adapter->pci_write_normalize(adapter, + CRB_INT_VECTOR, 0); } - writel(mask, - PCI_OFFSET_SECOND_RANGE(adapter, ISR_INT_TARGET_MASK)); + adapter->pci_write_immediate(adapter, + ISR_INT_TARGET_MASK, mask); } DPRINTK(1, INFO, "Done with enable Int\n"); } +static int nx_set_dma_mask(struct netxen_adapter *adapter, uint8_t revision_id) +{ + struct pci_dev *pdev = adapter->pdev; + int err; + uint64_t mask; + +#ifdef CONFIG_IA64 + adapter->dma_mask = DMA_32BIT_MASK; +#else + if (revision_id >= NX_P3_B0) { + /* should go to DMA_64BIT_MASK */ + adapter->dma_mask = DMA_39BIT_MASK; + mask = DMA_39BIT_MASK; + } else if (revision_id == NX_P3_A2) { + adapter->dma_mask = DMA_39BIT_MASK; + mask = DMA_39BIT_MASK; + } else if (revision_id == NX_P2_C1) { + adapter->dma_mask = DMA_35BIT_MASK; + mask = DMA_35BIT_MASK; + } else { + adapter->dma_mask = DMA_32BIT_MASK; + mask = DMA_32BIT_MASK; + goto set_32_bit_mask; + } + + /* + * Consistent DMA mask is set to 32 bit because it cannot be set to + * 35 bits. For P3 also leave it at 32 bits for now. Only the rings + * come off this pool. + */ + if (pci_set_dma_mask(pdev, mask) == 0 && + pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK) == 0) { + adapter->pci_using_dac = 1; + return 0; + } +#endif /* CONFIG_IA64 */ + +set_32_bit_mask: + err = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (!err) + err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + if (err) { + DPRINTK(ERR, "No usable DMA configuration, aborting:%d\n", err); + return err; + } + + adapter->pci_using_dac = 0; + return 0; +} + +static void netxen_check_options(struct netxen_adapter *adapter) +{ + switch (adapter->ahw.boardcfg.board_type) { + case NETXEN_BRDTYPE_P3_HMEZ: + case NETXEN_BRDTYPE_P3_XG_LOM: + case NETXEN_BRDTYPE_P3_10G_CX4: + case NETXEN_BRDTYPE_P3_10G_CX4_LP: + case NETXEN_BRDTYPE_P3_IMEZ: + case NETXEN_BRDTYPE_P3_10G_SFP_PLUS: + case NETXEN_BRDTYPE_P3_10G_XFP: + case NETXEN_BRDTYPE_P3_10000_BASE_T: + adapter->msix_supported = !!use_msi_x; + adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS_10G; + break; + + case NETXEN_BRDTYPE_P2_SB31_10G: + case NETXEN_BRDTYPE_P2_SB31_10G_CX4: + case NETXEN_BRDTYPE_P2_SB31_10G_IMEZ: + case NETXEN_BRDTYPE_P2_SB31_10G_HMEZ: + adapter->msix_supported = 0; + adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS_10G; + break; + + case NETXEN_BRDTYPE_P3_REF_QG: + case NETXEN_BRDTYPE_P3_4_GB: + case NETXEN_BRDTYPE_P3_4_GB_MM: + case NETXEN_BRDTYPE_P2_SB35_4G: + case NETXEN_BRDTYPE_P2_SB31_2G: + adapter->msix_supported = 0; + adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS_1G; + break; + + default: + adapter->msix_supported = 0; + adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS_1G; + + printk(KERN_WARNING "Unknown board type(0x%x)\n", + adapter->ahw.boardcfg.board_type); + break; + } + + adapter->max_tx_desc_count = MAX_CMD_DESCRIPTORS_HOST; + adapter->max_jumbo_rx_desc_count = MAX_JUMBO_RCV_DESCRIPTORS; + adapter->max_lro_rx_desc_count = MAX_LRO_RCV_DESCRIPTORS; + + adapter->max_possible_rss_rings = 1; + return; +} + +static int +netxen_check_hw_init(struct netxen_adapter *adapter, int first_boot) +{ + int ret = 0; + + if (first_boot == 0x55555555) { + /* This is the first boot after power up */ + + /* PCI bus master workaround */ + adapter->hw_read_wx(adapter, + NETXEN_PCIE_REG(0x4), &first_boot, 4); + if (!(first_boot & 0x4)) { + first_boot |= 0x4; + adapter->hw_write_wx(adapter, + NETXEN_PCIE_REG(0x4), &first_boot, 4); + adapter->hw_read_wx(adapter, + NETXEN_PCIE_REG(0x4), &first_boot, 4); + } + + /* This is the first boot after power up */ + adapter->hw_read_wx(adapter, + NETXEN_ROMUSB_GLB_SW_RESET, &first_boot, 4); + if (first_boot != 0x80000f) { + /* clear the register for future unloads/loads */ + adapter->pci_write_normalize(adapter, + NETXEN_CAM_RAM(0x1fc), 0); + ret = -1; + } + + if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) { + /* Start P2 boot loader */ + adapter->pci_write_normalize(adapter, + NETXEN_CAM_RAM(0x1fc), NETXEN_BDINFO_MAGIC); + adapter->pci_write_normalize(adapter, + NETXEN_ROMUSB_GLB_PEGTUNE_DONE, 1); + } + } + return ret; +} + +static void netxen_set_port_mode(struct netxen_adapter *adapter) +{ + u32 val, data; + + val = adapter->ahw.boardcfg.board_type; + if ((val == NETXEN_BRDTYPE_P3_HMEZ) || + (val == NETXEN_BRDTYPE_P3_XG_LOM)) { + if (port_mode == NETXEN_PORT_MODE_802_3_AP) { + data = NETXEN_PORT_MODE_802_3_AP; + adapter->hw_write_wx(adapter, + NETXEN_PORT_MODE_ADDR, &data, 4); + } else if (port_mode == NETXEN_PORT_MODE_XG) { + data = NETXEN_PORT_MODE_XG; + adapter->hw_write_wx(adapter, + NETXEN_PORT_MODE_ADDR, &data, 4); + } else if (port_mode == NETXEN_PORT_MODE_AUTO_NEG_1G) { + data = NETXEN_PORT_MODE_AUTO_NEG_1G; + adapter->hw_write_wx(adapter, + NETXEN_PORT_MODE_ADDR, &data, 4); + } else if (port_mode == NETXEN_PORT_MODE_AUTO_NEG_XG) { + data = NETXEN_PORT_MODE_AUTO_NEG_XG; + adapter->hw_write_wx(adapter, + NETXEN_PORT_MODE_ADDR, &data, 4); + } else { + data = NETXEN_PORT_MODE_AUTO_NEG; + adapter->hw_write_wx(adapter, + NETXEN_PORT_MODE_ADDR, &data, 4); + } + + if ((wol_port_mode != NETXEN_PORT_MODE_802_3_AP) && + (wol_port_mode != NETXEN_PORT_MODE_XG) && + (wol_port_mode != NETXEN_PORT_MODE_AUTO_NEG_1G) && + (wol_port_mode != NETXEN_PORT_MODE_AUTO_NEG_XG)) { + wol_port_mode = NETXEN_PORT_MODE_AUTO_NEG; + } + adapter->hw_write_wx(adapter, NETXEN_WOL_PORT_MODE, + &wol_port_mode, 4); + } +} + +#define PCI_CAP_ID_GEN 0x10 + +static void netxen_pcie_strap_init(struct netxen_adapter *adapter) +{ + u32 pdevfuncsave; + u32 c8c9value = 0; + u32 chicken = 0; + u32 control = 0; + int i, pos; + struct pci_dev *pdev; + + pdev = pci_get_device(0x1166, 0x0140, NULL); + if (pdev) { + pci_dev_put(pdev); + adapter->hw_read_wx(adapter, + NETXEN_PCIE_REG(PCIE_TGT_SPLIT_CHICKEN), &chicken, 4); + chicken |= 0x4000; + adapter->hw_write_wx(adapter, + NETXEN_PCIE_REG(PCIE_TGT_SPLIT_CHICKEN), &chicken, 4); + } + + pdev = adapter->pdev; + + adapter->hw_read_wx(adapter, + NETXEN_PCIE_REG(PCIE_CHICKEN3), &chicken, 4); + /* clear chicken3.25:24 */ + chicken &= 0xFCFFFFFF; + /* + * if gen1 and B0, set F1020 - if gen 2, do nothing + * if gen2 set to F1000 + */ + pos = pci_find_capability(pdev, PCI_CAP_ID_GEN); + if (pos == 0xC0) { + pci_read_config_dword(pdev, pos + 0x10, &control); + if ((control & 0x000F0000) != 0x00020000) { + /* set chicken3.24 if gen1 */ + chicken |= 0x01000000; + } + printk(KERN_INFO "%s Gen2 strapping detected\n", + netxen_nic_driver_name); + c8c9value = 0xF1000; + } else { + /* set chicken3.24 if gen1 */ + chicken |= 0x01000000; + printk(KERN_INFO "%s Gen1 strapping detected\n", + netxen_nic_driver_name); + if (adapter->ahw.revision_id == NX_P3_B0) + c8c9value = 0xF1020; + else + c8c9value = 0; + + } + adapter->hw_write_wx(adapter, + NETXEN_PCIE_REG(PCIE_CHICKEN3), &chicken, 4); + + if (!c8c9value) + return; + + pdevfuncsave = pdev->devfn; + if (pdevfuncsave & 0x07) + return; + + for (i = 0; i < 8; i++) { + pci_read_config_dword(pdev, pos + 8, &control); + pci_read_config_dword(pdev, pos + 8, &control); + pci_write_config_dword(pdev, pos + 8, c8c9value); + pdev->devfn++; + } + pdev->devfn = pdevfuncsave; +} + +static void netxen_set_msix_bit(struct pci_dev *pdev, int enable) +{ + u32 control; + int pos; + + pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX); + if (pos) { + pci_read_config_dword(pdev, pos, &control); + if (enable) + control |= PCI_MSIX_FLAGS_ENABLE; + else + control = 0; + pci_write_config_dword(pdev, pos, control); + } +} + +static void netxen_init_msix_entries(struct netxen_adapter *adapter) +{ + int i; + + for (i = 0; i < MSIX_ENTRIES_PER_ADAPTER; i++) + adapter->msix_entries[i].entry = i; +} + /* * netxen_nic_probe() * @@ -278,28 +524,28 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) u8 __iomem *db_ptr = NULL; - unsigned long mem_base, mem_len, db_base, db_len; - 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; + unsigned long mem_base, mem_len, db_base, db_len, pci_len0 = 0; + int i = 0, err; + int first_driver, first_boot; __le64 mac_addr[FLASH_NUM_PORTS + 1]; - int valid_mac = 0; u32 val; int pci_func_id = PCI_FUNC(pdev->devfn); DECLARE_MAC_BUF(mac); + struct netxen_legacy_intr_set *legacy_intrp; + uint8_t revision_id; if (pci_func_id == 0) - printk(KERN_INFO "%s \n", netxen_nic_driver_string); + printk(KERN_INFO "%s\n", netxen_nic_driver_string); if (pdev->class != 0x020000) { printk(KERN_DEBUG "NetXen function %d, class %x will not " "be enabled.\n",pci_func_id, pdev->class); return -ENODEV; } + if ((err = pci_enable_device(pdev))) return err; + if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { err = -ENODEV; goto err_out_disable_pdev; @@ -309,18 +555,6 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_disable_pdev; pci_set_master(pdev); - if (pdev->revision == NX_P2_C1 && - (pci_set_dma_mask(pdev, DMA_35BIT_MASK) == 0) && - (pci_set_consistent_dma_mask(pdev, DMA_35BIT_MASK) == 0)) { - pci_using_dac = 1; - } else { - if ((err = pci_set_dma_mask(pdev, DMA_32BIT_MASK)) || - (err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK))) - goto err_out_free_res; - - pci_using_dac = 0; - } - netdev = alloc_etherdev(sizeof(struct netxen_adapter)); if(!netdev) { @@ -333,13 +567,35 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) SET_NETDEV_DEV(netdev, &pdev->dev); adapter = netdev->priv; - - adapter->ahw.pdev = pdev; + adapter->netdev = netdev; + adapter->pdev = pdev; adapter->ahw.pci_func = pci_func_id; + revision_id = pdev->revision; + adapter->ahw.revision_id = revision_id; + + err = nx_set_dma_mask(adapter, revision_id); + if (err) + goto err_out_free_netdev; + + rwlock_init(&adapter->adapter_lock); + adapter->ahw.qdr_sn_window = -1; + adapter->ahw.ddr_mn_window = -1; + /* remap phys address */ mem_base = pci_resource_start(pdev, 0); /* 0 is for BAR 0 */ mem_len = pci_resource_len(pdev, 0); + pci_len0 = 0; + + adapter->hw_write_wx = netxen_nic_hw_write_wx_128M; + adapter->hw_read_wx = netxen_nic_hw_read_wx_128M; + adapter->pci_read_immediate = netxen_nic_pci_read_immediate_128M; + adapter->pci_write_immediate = netxen_nic_pci_write_immediate_128M; + adapter->pci_read_normalize = netxen_nic_pci_read_normalize_128M; + adapter->pci_write_normalize = netxen_nic_pci_write_normalize_128M; + adapter->pci_set_window = netxen_nic_pci_set_window_128M; + adapter->pci_mem_read = netxen_nic_pci_mem_read_128M; + adapter->pci_mem_write = netxen_nic_pci_mem_write_128M; /* 128 Meg of memory */ if (mem_len == NETXEN_PCI_128MB_SIZE) { @@ -356,27 +612,48 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) SECOND_PAGE_GROUP_START, THIRD_PAGE_GROUP_SIZE); first_page_group_start = 0; first_page_group_end = 0; + } else if (mem_len == NETXEN_PCI_2MB_SIZE) { + adapter->hw_write_wx = netxen_nic_hw_write_wx_2M; + adapter->hw_read_wx = netxen_nic_hw_read_wx_2M; + adapter->pci_read_immediate = netxen_nic_pci_read_immediate_2M; + adapter->pci_write_immediate = + netxen_nic_pci_write_immediate_2M; + adapter->pci_read_normalize = netxen_nic_pci_read_normalize_2M; + adapter->pci_write_normalize = + netxen_nic_pci_write_normalize_2M; + adapter->pci_set_window = netxen_nic_pci_set_window_2M; + adapter->pci_mem_read = netxen_nic_pci_mem_read_2M; + adapter->pci_mem_write = netxen_nic_pci_mem_write_2M; + + mem_ptr0 = ioremap(mem_base, mem_len); + pci_len0 = mem_len; + first_page_group_start = 0; + first_page_group_end = 0; + + adapter->ahw.ddr_mn_window = 0; + adapter->ahw.qdr_sn_window = 0; + + adapter->ahw.mn_win_crb = 0x100000 + PCIX_MN_WINDOW + + (pci_func_id * 0x20); + adapter->ahw.ms_win_crb = 0x100000 + PCIX_SN_WINDOW; + if (pci_func_id < 4) + adapter->ahw.ms_win_crb += (pci_func_id * 0x20); + else + adapter->ahw.ms_win_crb += + 0xA0 + ((pci_func_id - 4) * 0x10); } else { err = -EIO; goto err_out_free_netdev; } - if ((!mem_ptr0 && mem_len == NETXEN_PCI_128MB_SIZE) || - !mem_ptr1 || !mem_ptr2) { - DPRINTK(ERR, - "Cannot remap adapter memory aborting.:" - "0 -> %p, 1 -> %p, 2 -> %p\n", - mem_ptr0, mem_ptr1, mem_ptr2); + dev_info(&pdev->dev, "%dMB memory map\n", (int)(mem_len>>20)); - err = -EIO; - goto err_out_iounmap; - } db_base = pci_resource_start(pdev, 4); /* doorbell is on bar 4 */ db_len = pci_resource_len(pdev, 4); if (db_len == 0) { printk(KERN_ERR "%s: doorbell is disabled\n", - netxen_nic_driver_name); + netxen_nic_driver_name); err = -EIO; goto err_out_iounmap; } @@ -386,13 +663,14 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) db_ptr = ioremap(db_base, NETXEN_DB_MAPSIZE_BYTES); if (!db_ptr) { printk(KERN_ERR "%s: Failed to allocate doorbell map.", - netxen_nic_driver_name); + netxen_nic_driver_name); err = -EIO; goto err_out_iounmap; } DPRINTK(INFO, "doorbell ioremaped at %p\n", db_ptr); adapter->ahw.pci_base0 = mem_ptr0; + adapter->ahw.pci_len0 = pci_len0; 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; @@ -400,11 +678,18 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) adapter->ahw.db_base = db_ptr; adapter->ahw.db_len = db_len; - adapter->netdev = netdev; - adapter->pdev = pdev; - netif_napi_add(netdev, &adapter->napi, - netxen_nic_poll, NETXEN_NETDEV_WEIGHT); + netxen_nic_poll, NETXEN_NETDEV_WEIGHT); + + if (revision_id >= NX_P3_B0) + legacy_intrp = &legacy_intr[pci_func_id]; + else + legacy_intrp = &legacy_intr[0]; + + adapter->legacy_intr.int_vec_bit = legacy_intrp->int_vec_bit; + adapter->legacy_intr.tgt_status_reg = legacy_intrp->tgt_status_reg; + adapter->legacy_intr.tgt_mask_reg = legacy_intrp->tgt_mask_reg; + adapter->legacy_intr.pci_int_reg = legacy_intrp->pci_int_reg; /* this will be read from FW later */ adapter->intr_scheme = -1; @@ -414,12 +699,23 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) adapter->portnum = pci_func_id; adapter->status &= ~NETXEN_NETDEV_STATUS; adapter->rx_csum = 1; + adapter->mc_enabled = 0; + if (NX_IS_REVISION_P3(revision_id)) { + adapter->max_mc_count = 38; + adapter->max_rds_rings = 2; + } else { + adapter->max_mc_count = 16; + adapter->max_rds_rings = 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; + if (NX_IS_REVISION_P3(revision_id)) + netdev->set_multicast_list = netxen_p3_nic_set_multi; + else + netdev->set_multicast_list = netxen_p2_nic_set_multi; netdev->set_mac_address = netxen_nic_set_mac; netdev->change_mtu = netxen_nic_change_mtu; netdev->tx_timeout = netxen_tx_timeout; @@ -435,18 +731,14 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->features = NETIF_F_SG; netdev->features |= NETIF_F_IP_CSUM; netdev->features |= NETIF_F_TSO; + if (NX_IS_REVISION_P3(revision_id)) { + netdev->features |= NETIF_F_IPV6_CSUM; + netdev->features |= NETIF_F_TSO6; + } - if (pci_using_dac) + if (adapter->pci_using_dac) netdev->features |= NETIF_F_HIGHDMA; - if (pci_enable_msi(pdev)) - adapter->flags &= ~NETXEN_NIC_MSI_ENABLED; - else - adapter->flags |= NETXEN_NIC_MSI_ENABLED; - - 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. @@ -455,87 +747,13 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (netxen_nic_get_board_info(adapter) != 0) { printk("%s: Error getting board config info.\n", - netxen_nic_driver_name); + netxen_nic_driver_name); err = -EIO; goto err_out_iounmap; } - /* - * 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; - - cmd_buf_arr = (struct netxen_cmd_buffer *)vmalloc(TX_RINGSIZE); - if (cmd_buf_arr == NULL) { - printk(KERN_ERR - "%s: Could not allocate cmd_buf_arr memory:%d\n", - netxen_nic_driver_name, (int)TX_RINGSIZE); - err = -ENOMEM; - 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]; - for (ring = 0; ring < NUM_RCV_DESC_RINGS; ring++) { - rcv_desc = &recv_ctx->rcv_desc[ring]; - switch (RCV_DESC_TYPE(ring)) { - case RCV_DESC_NORMAL: - rcv_desc->max_rx_desc_count = - adapter->max_rx_desc_count; - rcv_desc->flags = RCV_DESC_NORMAL; - rcv_desc->dma_size = RX_DMA_MAP_LEN; - rcv_desc->skb_size = MAX_RX_BUFFER_LENGTH; - break; - - case RCV_DESC_JUMBO: - rcv_desc->max_rx_desc_count = - adapter->max_jumbo_rx_desc_count; - rcv_desc->flags = RCV_DESC_JUMBO; - rcv_desc->dma_size = RX_JUMBO_DMA_MAP_LEN; - rcv_desc->skb_size = MAX_RX_JUMBO_BUFFER_LENGTH; - break; - - case RCV_RING_LRO: - rcv_desc->max_rx_desc_count = - adapter->max_lro_rx_desc_count; - rcv_desc->flags = RCV_DESC_LRO; - rcv_desc->dma_size = RX_LRO_DMA_MAP_LEN; - rcv_desc->skb_size = MAX_RX_LRO_BUFFER_LENGTH; - break; - - } - rcv_desc->rx_buf_arr = (struct netxen_rx_buffer *) - vmalloc(RCV_BUFFSIZE); - - if (rcv_desc->rx_buf_arr == NULL) { - printk(KERN_ERR "%s: Could not allocate " - "rcv_desc->rx_buf_arr memory:%d\n", - netxen_nic_driver_name, - (int)RCV_BUFFSIZE); - err = -ENOMEM; - goto err_out_free_rx_buffer; - } - memset(rcv_desc->rx_buf_arr, 0, RCV_BUFFSIZE); - } - - } - - netxen_initialize_adapter_sw(adapter); /* initialize the buffers in adapter */ - /* Mezz cards have PCI function 0,2,3 enabled */ switch (adapter->ahw.boardcfg.board_type) { case NETXEN_BRDTYPE_P2_SB31_10G_IMEZ: @@ -547,90 +765,71 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) break; } - init_timer(&adapter->watchdog_timer); - adapter->ahw.xg_linkup = 0; - adapter->watchdog_timer.function = &netxen_watchdog; - adapter->watchdog_timer.data = (unsigned long)adapter; - INIT_WORK(&adapter->watchdog_task, netxen_watchdog_task); - adapter->ahw.pdev = pdev; - adapter->ahw.revision_id = pdev->revision; - - /* make sure Window == 1 */ - netxen_nic_pci_change_crbwindow(adapter, 1); + /* + * This call will setup various max rx/tx counts. + * It must be done before any buffer/ring allocations. + */ + netxen_check_options(adapter); + first_driver = 0; + if (NX_IS_REVISION_P3(revision_id)) { + if (adapter->ahw.pci_func == 0) + first_driver = 1; + } else { + if (adapter->portnum == 0) + first_driver = 1; + } + adapter->crb_addr_cmd_producer = crb_cmd_producer[adapter->portnum]; + adapter->crb_addr_cmd_consumer = crb_cmd_consumer[adapter->portnum]; 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) - valid_mac = 1; - else - valid_mac = 0; - - 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); + if (first_driver) { + first_boot = adapter->pci_read_normalize(adapter, + NETXEN_CAM_RAM(0x1fc)); - 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 %s.\n", - netxen_nic_driver_name, - print_mac(mac, netdev->dev_addr)); - } else { - if (adapter->macaddr_set) - adapter->macaddr_set(adapter, - netdev->dev_addr); + err = netxen_check_hw_init(adapter, first_boot); + if (err) { + printk(KERN_ERR "%s: error in init HW init sequence\n", + netxen_nic_driver_name); + goto err_out_iounmap; } - } - 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 */ - netxen_nic_read_w0(adapter, NETXEN_PCIE_REG(0x4), &val); - if (!(val & 0x4)) { - val |= 0x4; - netxen_nic_write_w0(adapter, NETXEN_PCIE_REG(0x4), val); - netxen_nic_read_w0(adapter, NETXEN_PCIE_REG(0x4), &val); - if (!(val & 0x4)) - printk(KERN_ERR "%s: failed to set MSI bit in PCI-e reg\n", - netxen_nic_driver_name); - } - 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; - } - } else { - writel(0, NETXEN_CRB_NORMALIZE(adapter, - CRB_CMDPEG_STATE)); + if (NX_IS_REVISION_P3(revision_id)) + netxen_set_port_mode(adapter); + + if (first_boot != 0x55555555) { + adapter->pci_write_normalize(adapter, + CRB_CMDPEG_STATE, 0); netxen_pinit_from_rom(adapter, 0); msleep(1); netxen_load_firmware(adapter); - netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE); } - /* clear the register for future unloads/loads */ - writel(0, NETXEN_CRB_NORMALIZE(adapter, NETXEN_CAM_RAM(0x1fc))); - dev_info(&pdev->dev, "cmdpeg state: 0x%0x\n", - readl(NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE))); + if (NX_IS_REVISION_P3(revision_id)) + netxen_pcie_strap_init(adapter); + + if (NX_IS_REVISION_P2(revision_id)) { + + /* Initialize multicast addr pool owners */ + val = 0x7654; + if (adapter->ahw.board_type == NETXEN_NIC_XGBE) + val |= 0x0f000000; + netxen_crb_writelit_adapter(adapter, + NETXEN_MAC_ADDR_CNTL_REG, val); + + } + + if ((first_boot == 0x55555555) && + (NX_IS_REVISION_P2(revision_id))) { + /* Unlock the HW, prompting the boot sequence */ + adapter->pci_write_normalize(adapter, + NETXEN_ROMUSB_GLB_PEGTUNE_DONE, 1); + } + + err = netxen_initialize_adapter_offload(adapter); + if (err) + goto err_out_iounmap; /* * Tell the hardware our version number. @@ -638,24 +837,101 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) i = (_NETXEN_NIC_LINUX_MAJOR << 16) | ((_NETXEN_NIC_LINUX_MINOR << 8)) | (_NETXEN_NIC_LINUX_SUBVERSION); - writel(i, NETXEN_CRB_NORMALIZE(adapter, CRB_DRIVER_VERSION)); + adapter->pci_write_normalize(adapter, CRB_DRIVER_VERSION, i); - /* 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); + + } /* first_driver */ + + netxen_nic_flash_print(adapter); + + if (NX_IS_REVISION_P3(revision_id)) { + adapter->hw_read_wx(adapter, + NETXEN_MIU_MN_CONTROL, &val, 4); + adapter->ahw.cut_through = (val & 0x4) ? 1 : 0; + dev_info(&pdev->dev, "firmware running in %s mode\n", + adapter->ahw.cut_through ? "cut through" : "legacy"); } /* * See if the firmware gave us a virtual-physical port mapping. */ adapter->physical_port = adapter->portnum; - i = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_V2P(adapter->portnum))); + i = adapter->pci_read_normalize(adapter, CRB_V2P(adapter->portnum)); if (i != 0x55555555) adapter->physical_port = i; + adapter->flags &= ~(NETXEN_NIC_MSI_ENABLED | NETXEN_NIC_MSIX_ENABLED); + + netxen_set_msix_bit(pdev, 0); + + if (NX_IS_REVISION_P3(revision_id)) { + if ((mem_len != NETXEN_PCI_128MB_SIZE) && + mem_len != NETXEN_PCI_2MB_SIZE) + adapter->msix_supported = 0; + } + + if (adapter->msix_supported) { + + netxen_init_msix_entries(adapter); + + if (pci_enable_msix(pdev, adapter->msix_entries, + MSIX_ENTRIES_PER_ADAPTER)) + goto request_msi; + + adapter->flags |= NETXEN_NIC_MSIX_ENABLED; + netxen_set_msix_bit(pdev, 1); + dev_info(&pdev->dev, "using msi-x interrupts\n"); + + } else { +request_msi: + if (use_msi && !pci_enable_msi(pdev)) { + adapter->flags |= NETXEN_NIC_MSI_ENABLED; + dev_info(&pdev->dev, "using msi interrupts\n"); + } else + dev_info(&pdev->dev, "using legacy interrupts\n"); + } + + if (adapter->flags & NETXEN_NIC_MSIX_ENABLED) + netdev->irq = adapter->msix_entries[0].vector; + else + netdev->irq = pdev->irq; + + err = netxen_receive_peg_ready(adapter); + if (err) + goto err_out_disable_msi; + + init_timer(&adapter->watchdog_timer); + adapter->ahw.linkup = 0; + adapter->watchdog_timer.function = &netxen_watchdog; + adapter->watchdog_timer.data = (unsigned long)adapter; + INIT_WORK(&adapter->watchdog_task, netxen_watchdog_task); + INIT_WORK(&adapter->tx_timeout_task, netxen_tx_timeout_task); + + if (netxen_is_flash_supported(adapter) == 0 && + netxen_get_flash_mac_addr(adapter, mac_addr) == 0) { + unsigned char *p; + + 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 %s.\n", + netxen_nic_driver_name, + print_mac(mac, netdev->dev_addr)); + } else { + adapter->macaddr_set(adapter, netdev->dev_addr); + } + } + netif_carrier_off(netdev); netif_stop_queue(netdev); @@ -664,41 +940,37 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) " aborting\n", netxen_nic_driver_name, adapter->portnum); err = -EIO; - goto err_out_free_dev; + goto err_out_disable_msi; } - netxen_nic_flash_print(adapter); pci_set_drvdata(pdev, adapter); - return 0; - -err_out_free_dev: - if (adapter->portnum == 0) - netxen_free_adapter_offload(adapter); - -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++) { - rcv_desc = &recv_ctx->rcv_desc[ring]; - if (rcv_desc->rx_buf_arr != NULL) { - vfree(rcv_desc->rx_buf_arr); - rcv_desc->rx_buf_arr = NULL; - } - } + switch (adapter->ahw.board_type) { + case NETXEN_NIC_GBE: + dev_info(&adapter->pdev->dev, "%s: GbE port initialized\n", + adapter->netdev->name); + break; + case NETXEN_NIC_XGBE: + dev_info(&adapter->pdev->dev, "%s: XGbE port initialized\n", + adapter->netdev->name); + break; } - vfree(cmd_buf_arr); -err_out_free_adapter: + return 0; + +err_out_disable_msi: + if (adapter->flags & NETXEN_NIC_MSIX_ENABLED) + pci_disable_msix(pdev); if (adapter->flags & NETXEN_NIC_MSI_ENABLED) pci_disable_msi(pdev); - pci_set_drvdata(pdev, NULL); + if (first_driver) + netxen_free_adapter_offload(adapter); +err_out_iounmap: if (db_ptr) iounmap(db_ptr); -err_out_iounmap: if (mem_ptr0) iounmap(mem_ptr0); if (mem_ptr1) @@ -713,6 +985,7 @@ err_out_free_res: pci_release_regions(pdev); err_out_disable_pdev: + pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); return err; } @@ -721,11 +994,6 @@ static void __devexit netxen_nic_remove(struct pci_dev *pdev) { struct netxen_adapter *adapter; struct net_device *netdev; - struct netxen_rx_buffer *buffer; - struct netxen_recv_context *recv_ctx; - struct netxen_rcv_desc_ctx *rcv_desc; - int i, ctxid, ring; - static int init_firmware_done = 0; adapter = pci_get_drvdata(pdev); if (adapter == NULL) @@ -736,36 +1004,18 @@ static void __devexit netxen_nic_remove(struct pci_dev *pdev) unregister_netdev(netdev); if (adapter->is_up == NETXEN_ADAPTER_UP_MAGIC) { - init_firmware_done++; netxen_free_hw_resources(adapter); + netxen_free_sw_resources(adapter); } - for (ctxid = 0; ctxid < MAX_RCV_CTX; ++ctxid) { - recv_ctx = &adapter->recv_ctx[ctxid]; - for (ring = 0; ring < NUM_RCV_DESC_RINGS; ring++) { - rcv_desc = &recv_ctx->rcv_desc[ring]; - for (i = 0; i < rcv_desc->max_rx_desc_count; ++i) { - buffer = &(rcv_desc->rx_buf_arr[i]); - if (buffer->state == NETXEN_BUFFER_FREE) - continue; - pci_unmap_single(pdev, buffer->dma, - rcv_desc->dma_size, - PCI_DMA_FROMDEVICE); - if (buffer->skb != NULL) - dev_kfree_skb_any(buffer->skb); - } - vfree(rcv_desc->rx_buf_arr); - } - } - - vfree(adapter->cmd_buf_arr); - if (adapter->portnum == 0) netxen_free_adapter_offload(adapter); if (adapter->irq) free_irq(adapter->irq, adapter); + if (adapter->flags & NETXEN_NIC_MSIX_ENABLED) + pci_disable_msix(pdev); if (adapter->flags & NETXEN_NIC_MSI_ENABLED) pci_disable_msi(pdev); @@ -803,51 +1053,69 @@ static int netxen_nic_open(struct net_device *netdev) return -EIO; } - /* setup all the resources for the Phantom... */ - /* this include the descriptors for rcv, tx, and status */ - netxen_nic_clear_stats(adapter); - err = netxen_nic_hw_resources(adapter); + err = netxen_alloc_sw_resources(adapter); if (err) { - printk(KERN_ERR "Error in setting hw resources:%d\n", - err); + printk(KERN_ERR "%s: Error in setting sw resources\n", + netdev->name); return err; } + + netxen_nic_clear_stats(adapter); + + err = netxen_alloc_hw_resources(adapter); + if (err) { + printk(KERN_ERR "%s: Error in setting hw resources\n", + netdev->name); + goto err_out_free_sw; + } + + if (adapter->fw_major < 4) { + adapter->crb_addr_cmd_producer = + crb_cmd_producer[adapter->portnum]; + adapter->crb_addr_cmd_consumer = + crb_cmd_consumer[adapter->portnum]; + } + + netxen_nic_update_cmd_producer(adapter, 0); + netxen_nic_update_cmd_consumer(adapter, 0); + for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) { - for (ring = 0; ring < NUM_RCV_DESC_RINGS; ring++) + for (ring = 0; ring < adapter->max_rds_rings; ring++) netxen_post_rx_buffers(adapter, ctx, ring); } - adapter->irq = adapter->ahw.pdev->irq; - if (adapter->flags & NETXEN_NIC_MSI_ENABLED) + if (NETXEN_IS_MSI_FAMILY(adapter)) handler = netxen_msi_intr; else { flags |= IRQF_SHARED; handler = netxen_intr; } + adapter->irq = netdev->irq; err = request_irq(adapter->irq, handler, flags, netdev->name, adapter); if (err) { printk(KERN_ERR "request_irq failed with: %d\n", err); - netxen_free_hw_resources(adapter); - return err; + goto err_out_free_hw; } adapter->is_up = NETXEN_ADAPTER_UP_MAGIC; } + /* Done here again so that even if phantom sw overwrote it, * we set it */ - if (adapter->init_port - && adapter->init_port(adapter, adapter->portnum) != 0) { + err = adapter->init_port(adapter, adapter->physical_port); + if (err) { printk(KERN_ERR "%s: Failed to initialize port %d\n", netxen_nic_driver_name, adapter->portnum); - return -EIO; + goto err_out_free_irq; } - if (adapter->macaddr_set) - adapter->macaddr_set(adapter, netdev->dev_addr); + adapter->macaddr_set(adapter, netdev->dev_addr); netxen_nic_set_link_parameters(adapter); - netxen_nic_set_multi(netdev); - if (adapter->set_mtu) + netdev->set_multicast_list(netdev); + if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) + nx_fw_cmd_set_mtu(adapter, netdev->mtu); + else adapter->set_mtu(adapter, netdev->mtu); mod_timer(&adapter->watchdog_timer, jiffies); @@ -858,6 +1126,14 @@ static int netxen_nic_open(struct net_device *netdev) netif_start_queue(netdev); return 0; + +err_out_free_irq: + free_irq(adapter->irq, adapter); +err_out_free_hw: + netxen_free_hw_resources(adapter); +err_out_free_sw: + netxen_free_sw_resources(adapter); + return err; } /* @@ -866,9 +1142,6 @@ static int netxen_nic_open(struct net_device *netdev) static int netxen_nic_close(struct net_device *netdev) { struct netxen_adapter *adapter = netdev_priv(netdev); - int i, j; - struct netxen_cmd_buffer *cmd_buff; - struct netxen_skb_frag *buffrag; netif_carrier_off(netdev); netif_stop_queue(netdev); @@ -879,30 +1152,8 @@ static int netxen_nic_close(struct net_device *netdev) netxen_nic_disable_int(adapter); - 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 = 0ULL; - } - for (j = 0; j < cmd_buff->frag_count; j++) { - buffrag++; - if (buffrag->dma) { - pci_unmap_page(adapter->pdev, buffrag->dma, - buffrag->length, - PCI_DMA_TODEVICE); - buffrag->dma = 0ULL; - } - } - /* 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++; - } + netxen_release_tx_buffers(adapter); + if (adapter->is_up == NETXEN_ADAPTER_UP_MAGIC) { FLUSH_SCHEDULED_WORK(); del_timer_sync(&adapter->watchdog_timer); @@ -911,6 +1162,31 @@ static int netxen_nic_close(struct net_device *netdev) return 0; } +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) + + ip_hdrlen(skb) + tcp_hdrlen(skb)); + + if ((NX_IS_REVISION_P3(adapter->ahw.revision_id)) && + (skb->protocol == htons(ETH_P_IPV6))) + netxen_set_cmd_desc_opcode(desc, TX_TCP_LSO6); + else + netxen_set_cmd_desc_opcode(desc, TX_TCP_LSO); + + } else if (skb->ip_summed == CHECKSUM_PARTIAL) { + if (ip_hdr(skb)->protocol == IPPROTO_TCP) + netxen_set_cmd_desc_opcode(desc, TX_TCP_PKT); + else if (ip_hdr(skb)->protocol == IPPROTO_UDP) + netxen_set_cmd_desc_opcode(desc, TX_UDP_PKT); + else + return; + } + desc->tcp_hdr_offset = skb_transport_offset(skb); + desc->ip_hdr_offset = skb_network_offset(skb); +} + static int netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct netxen_adapter *adapter = netdev_priv(netdev); @@ -932,7 +1208,7 @@ static int netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) /* There 4 fragments per descriptor */ no_of_desc = (frag_count + 3) >> 2; - if (netdev->features & NETIF_F_TSO) { + if (netdev->features & (NETIF_F_TSO | NETIF_F_TSO6)) { if (skb_shinfo(skb)->gso_size > 0) { no_of_desc++; @@ -959,7 +1235,8 @@ static int netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) memset(hwdesc, 0, sizeof(struct cmd_desc_type0)); /* Take skb->data itself */ pbuf = &adapter->cmd_buf_arr[producer]; - if ((netdev->features & NETIF_F_TSO) && skb_shinfo(skb)->gso_size > 0) { + if ((netdev->features & (NETIF_F_TSO | NETIF_F_TSO6)) && + skb_shinfo(skb)->gso_size > 0) { pbuf->mss = skb_shinfo(skb)->gso_size; hwdesc->mss = cpu_to_le16(skb_shinfo(skb)->gso_size); } else { @@ -1086,6 +1363,89 @@ static int netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) return NETDEV_TX_OK; } +static int netxen_nic_check_temp(struct netxen_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + uint32_t temp, temp_state, temp_val; + int rv = 0; + + temp = adapter->pci_read_normalize(adapter, CRB_TEMP_STATE); + + temp_state = nx_get_temp_state(temp); + temp_val = nx_get_temp_val(temp); + + if (temp_state == NX_TEMP_PANIC) { + printk(KERN_ALERT + "%s: Device temperature %d degrees C exceeds" + " maximum allowed. Hardware has been shut down.\n", + netxen_nic_driver_name, temp_val); + + netif_carrier_off(netdev); + netif_stop_queue(netdev); + rv = 1; + } else if (temp_state == NX_TEMP_WARN) { + if (adapter->temp == NX_TEMP_NORMAL) { + printk(KERN_ALERT + "%s: Device temperature %d degrees C " + "exceeds operating range." + " Immediate action needed.\n", + netxen_nic_driver_name, temp_val); + } + } else { + if (adapter->temp == NX_TEMP_WARN) { + printk(KERN_INFO + "%s: Device temperature is now %d degrees C" + " in normal range.\n", netxen_nic_driver_name, + temp_val); + } + } + adapter->temp = temp_state; + return rv; +} + +static void netxen_nic_handle_phy_intr(struct netxen_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + u32 val, port, linkup; + + port = adapter->physical_port; + + if (adapter->ahw.board_type == NETXEN_NIC_GBE) { + val = adapter->pci_read_normalize(adapter, CRB_XG_STATE); + linkup = (val >> port) & 1; + } else { + if (adapter->fw_major < 4) { + val = adapter->pci_read_normalize(adapter, + CRB_XG_STATE); + val = (val >> port*8) & 0xff; + linkup = (val == XG_LINK_UP); + } else { + val = adapter->pci_read_normalize(adapter, + CRB_XG_STATE_P3); + val = XG_LINK_STATE_P3(adapter->ahw.pci_func, val); + linkup = (val == XG_LINK_UP_P3); + } + } + + if (adapter->ahw.linkup && !linkup) { + printk(KERN_INFO "%s: %s NIC Link is down\n", + netxen_nic_driver_name, netdev->name); + adapter->ahw.linkup = 0; + if (netif_running(netdev)) { + netif_carrier_off(netdev); + netif_stop_queue(netdev); + } + } else if (!adapter->ahw.linkup && linkup) { + printk(KERN_INFO "%s: %s NIC Link is up\n", + netxen_nic_driver_name, netdev->name); + adapter->ahw.linkup = 1; + if (netif_running(netdev)) { + netif_carrier_on(netdev); + netif_wake_queue(netdev); + } + } +} + static void netxen_watchdog(unsigned long v) { struct netxen_adapter *adapter = (struct netxen_adapter *)v; @@ -1093,6 +1453,19 @@ static void netxen_watchdog(unsigned long v) SCHEDULE_WORK(&adapter->watchdog_task); } +void netxen_watchdog_task(struct work_struct *work) +{ + struct netxen_adapter *adapter = + container_of(work, struct netxen_adapter, watchdog_task); + + if ((adapter->portnum == 0) && netxen_nic_check_temp(adapter)) + return; + + netxen_nic_handle_phy_intr(adapter); + + mod_timer(&adapter->watchdog_timer, jiffies + 2 * HZ); +} + static void netxen_tx_timeout(struct net_device *netdev) { struct netxen_adapter *adapter = (struct netxen_adapter *) @@ -1118,6 +1491,38 @@ static void netxen_tx_timeout_task(struct work_struct *work) netif_wake_queue(adapter->netdev); } +/* + * netxen_nic_get_stats - Get System Network Statistics + * @netdev: network interface device structure + */ +struct net_device_stats *netxen_nic_get_stats(struct net_device *netdev) +{ + 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 = adapter->stats.no_rcv; + /* total packets transmitted */ + stats->tx_packets = adapter->stats.xmitedframes + + adapter->stats.xmitfinished; + /* total bytes received */ + stats->rx_bytes = adapter->stats.rxbytes; + /* total bytes transmitted */ + stats->tx_bytes = adapter->stats.txbytes; + /* bad packets received */ + stats->rx_errors = adapter->stats.rcvdbadskb; + /* packet transmit problems */ + stats->tx_errors = adapter->stats.nocmddescriptor; + /* no space in linux buffers */ + stats->rx_dropped = adapter->stats.rxdropped; + /* no space available in linux */ + stats->tx_dropped = adapter->stats.txdropped; + + return stats; +} + static inline void netxen_handle_int(struct netxen_adapter *adapter) { @@ -1125,20 +1530,20 @@ netxen_handle_int(struct netxen_adapter *adapter) napi_schedule(&adapter->napi); } -irqreturn_t netxen_intr(int irq, void *data) +static irqreturn_t netxen_intr(int irq, void *data) { struct netxen_adapter *adapter = data; u32 our_int = 0; - our_int = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_INT_VECTOR)); + our_int = adapter->pci_read_normalize(adapter, CRB_INT_VECTOR); /* not our interrupt */ if ((our_int & (0x80 << adapter->portnum)) == 0) return IRQ_NONE; if (adapter->intr_scheme == INTR_SCHEME_PERPORT) { /* claim interrupt */ - writel(our_int & ~((u32)(0x80 << adapter->portnum)), - NETXEN_CRB_NORMALIZE(adapter, CRB_INT_VECTOR)); + adapter->pci_write_normalize(adapter, CRB_INT_VECTOR, + our_int & ~((u32)(0x80 << adapter->portnum))); } netxen_handle_int(adapter); @@ -1146,7 +1551,7 @@ irqreturn_t netxen_intr(int irq, void *data) return IRQ_HANDLED; } -irqreturn_t netxen_msi_intr(int irq, void *data) +static irqreturn_t netxen_msi_intr(int irq, void *data) { struct netxen_adapter *adapter = data; @@ -1220,10 +1625,6 @@ module_init(netxen_init_module); static void __exit netxen_exit_module(void) { - /* - * Wait for some time to allow the dma to drain, if any. - */ - msleep(100); pci_unregister_driver(&netxen_driver); destroy_workqueue(netxen_workq); } diff --git a/drivers/net/netxen/netxen_nic_niu.c b/drivers/net/netxen/netxen_nic_niu.c index a3bc7cc67a6f..4cb8f4a1cf4b 100644 --- a/drivers/net/netxen/netxen_nic_niu.c +++ b/drivers/net/netxen/netxen_nic_niu.c @@ -46,9 +46,8 @@ static int phy_lock(struct netxen_adapter *adapter) int done = 0, timeout = 0; while (!done) { - done = - readl(pci_base_offset - (adapter, NETXEN_PCIE_REG(PCIE_SEM3_LOCK))); + done = netxen_nic_reg_read(adapter, + NETXEN_PCIE_REG(PCIE_SEM3_LOCK)); if (done == 1) break; if (timeout >= phy_lock_timeout) { @@ -63,14 +62,14 @@ static int phy_lock(struct netxen_adapter *adapter) } } - writel(PHY_LOCK_DRIVER, - NETXEN_CRB_NORMALIZE(adapter, NETXEN_PHY_LOCK_ID)); + netxen_crb_writelit_adapter(adapter, + NETXEN_PHY_LOCK_ID, PHY_LOCK_DRIVER); return 0; } static int phy_unlock(struct netxen_adapter *adapter) { - readl(pci_base_offset(adapter, NETXEN_PCIE_REG(PCIE_SEM3_UNLOCK))); + adapter->pci_read_immediate(adapter, NETXEN_PCIE_REG(PCIE_SEM3_UNLOCK)); return 0; } @@ -109,7 +108,7 @@ int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long reg, * so it cannot be in reset */ - if (netxen_nic_hw_read_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(0), + if (adapter->hw_read_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(0), &mac_cfg0, 4)) return -EIO; if (netxen_gb_get_soft_reset(mac_cfg0)) { @@ -119,7 +118,7 @@ int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long reg, netxen_gb_rx_reset_pb(temp); netxen_gb_tx_reset_mac(temp); netxen_gb_rx_reset_mac(temp); - if (netxen_nic_hw_write_wx(adapter, + if (adapter->hw_write_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(0), &temp, 4)) return -EIO; @@ -129,22 +128,22 @@ int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long reg, address = 0; netxen_gb_mii_mgmt_reg_addr(address, reg); netxen_gb_mii_mgmt_phy_addr(address, phy); - if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR(0), + if (adapter->hw_write_wx(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR(0), &address, 4)) return -EIO; command = 0; /* turn off any prior activity */ - if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_GB_MII_MGMT_COMMAND(0), + if (adapter->hw_write_wx(adapter, NETXEN_NIU_GB_MII_MGMT_COMMAND(0), &command, 4)) return -EIO; /* send read command */ netxen_gb_mii_mgmt_set_read_cycle(command); - if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_GB_MII_MGMT_COMMAND(0), + if (adapter->hw_write_wx(adapter, NETXEN_NIU_GB_MII_MGMT_COMMAND(0), &command, 4)) return -EIO; status = 0; do { - if (netxen_nic_hw_read_wx(adapter, + if (adapter->hw_read_wx(adapter, NETXEN_NIU_GB_MII_MGMT_INDICATE(0), &status, 4)) return -EIO; @@ -154,7 +153,7 @@ int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long reg, && (timeout++ < NETXEN_NIU_PHY_WAITMAX)); if (timeout < NETXEN_NIU_PHY_WAITMAX) { - if (netxen_nic_hw_read_wx(adapter, + if (adapter->hw_read_wx(adapter, NETXEN_NIU_GB_MII_MGMT_STATUS(0), readval, 4)) return -EIO; @@ -163,7 +162,7 @@ int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long reg, result = -1; if (restore) - if (netxen_nic_hw_write_wx(adapter, + if (adapter->hw_write_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(0), &mac_cfg0, 4)) return -EIO; @@ -201,7 +200,7 @@ int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter, long reg, * cannot be in reset */ - if (netxen_nic_hw_read_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(0), + if (adapter->hw_read_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(0), &mac_cfg0, 4)) return -EIO; if (netxen_gb_get_soft_reset(mac_cfg0)) { @@ -212,7 +211,7 @@ int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter, long reg, netxen_gb_tx_reset_mac(temp); netxen_gb_rx_reset_mac(temp); - if (netxen_nic_hw_write_wx(adapter, + if (adapter->hw_write_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(0), &temp, 4)) return -EIO; @@ -220,24 +219,24 @@ int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter, long reg, } command = 0; /* turn off any prior activity */ - if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_GB_MII_MGMT_COMMAND(0), + if (adapter->hw_write_wx(adapter, NETXEN_NIU_GB_MII_MGMT_COMMAND(0), &command, 4)) return -EIO; address = 0; netxen_gb_mii_mgmt_reg_addr(address, reg); netxen_gb_mii_mgmt_phy_addr(address, phy); - if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR(0), + if (adapter->hw_write_wx(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR(0), &address, 4)) return -EIO; - if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_GB_MII_MGMT_CTRL(0), + if (adapter->hw_write_wx(adapter, NETXEN_NIU_GB_MII_MGMT_CTRL(0), &val, 4)) return -EIO; status = 0; do { - if (netxen_nic_hw_read_wx(adapter, + if (adapter->hw_read_wx(adapter, NETXEN_NIU_GB_MII_MGMT_INDICATE(0), &status, 4)) return -EIO; @@ -252,7 +251,7 @@ int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter, long reg, /* restore the state of port 0 MAC in case we tampered with it */ if (restore) - if (netxen_nic_hw_write_wx(adapter, + if (adapter->hw_write_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(0), &mac_cfg0, 4)) return -EIO; @@ -401,14 +400,16 @@ int netxen_niu_gbe_init_port(struct netxen_adapter *adapter, int port) { int result = 0; __u32 status; + + if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) + return 0; + if (adapter->disable_phy_interrupts) adapter->disable_phy_interrupts(adapter); mdelay(2); - if (0 == - netxen_niu_gbe_phy_read(adapter, - NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, - &status)) { + if (0 == netxen_niu_gbe_phy_read(adapter, + NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, &status)) { if (netxen_get_phy_link(status)) { if (netxen_get_phy_speed(status) == 2) { netxen_niu_gbe_set_gmii_mode(adapter, port, 1); @@ -456,12 +457,12 @@ int netxen_niu_gbe_init_port(struct netxen_adapter *adapter, int port) int netxen_niu_xg_init_port(struct netxen_adapter *adapter, int port) { - u32 portnum = adapter->physical_port; - - netxen_crb_writelit_adapter(adapter, - NETXEN_NIU_XGE_CONFIG_1+(0x10000*portnum), 0x1447); - netxen_crb_writelit_adapter(adapter, - NETXEN_NIU_XGE_CONFIG_0+(0x10000*portnum), 0x5); + if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) { + netxen_crb_writelit_adapter(adapter, + NETXEN_NIU_XGE_CONFIG_1+(0x10000*port), 0x1447); + netxen_crb_writelit_adapter(adapter, + NETXEN_NIU_XGE_CONFIG_0+(0x10000*port), 0x5); + } return 0; } @@ -581,10 +582,10 @@ static int netxen_niu_macaddr_get(struct netxen_adapter *adapter, if ((phy < 0) || (phy > 3)) return -EINVAL; - if (netxen_nic_hw_read_wx(adapter, NETXEN_NIU_GB_STATION_ADDR_0(phy), + if (adapter->hw_read_wx(adapter, NETXEN_NIU_GB_STATION_ADDR_0(phy), &stationhigh, 4)) return -EIO; - if (netxen_nic_hw_read_wx(adapter, NETXEN_NIU_GB_STATION_ADDR_1(phy), + if (adapter->hw_read_wx(adapter, NETXEN_NIU_GB_STATION_ADDR_1(phy), &stationlow, 4)) return -EIO; ((__le32 *)val)[1] = cpu_to_le32(stationhigh); @@ -613,14 +614,14 @@ int netxen_niu_macaddr_set(struct netxen_adapter *adapter, 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_GB_STATION_ADDR_1(phy), &val, 4)) + if (adapter->hw_write_wx(adapter, + NETXEN_NIU_GB_STATION_ADDR_1(phy), &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_GB_STATION_ADDR_0(phy), &val, 4)) + if (adapter->hw_write_wx(adapter, + NETXEN_NIU_GB_STATION_ADDR_0(phy), &val, 4)) return -2; netxen_niu_macaddr_get(adapter, @@ -654,7 +655,7 @@ int netxen_niu_enable_gbe_port(struct netxen_adapter *adapter, mac_cfg0 = 0; netxen_gb_soft_reset(mac_cfg0); - if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(port), + if (adapter->hw_write_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(port), &mac_cfg0, 4)) return -EIO; mac_cfg0 = 0; @@ -666,7 +667,7 @@ int netxen_niu_enable_gbe_port(struct netxen_adapter *adapter, netxen_gb_tx_reset_mac(mac_cfg0); netxen_gb_rx_reset_mac(mac_cfg0); - if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(port), + if (adapter->hw_write_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(port), &mac_cfg0, 4)) return -EIO; mac_cfg1 = 0; @@ -679,7 +680,7 @@ int netxen_niu_enable_gbe_port(struct netxen_adapter *adapter, if (mode == NETXEN_NIU_10_100_MB) { netxen_gb_set_intfmode(mac_cfg1, 1); - if (netxen_nic_hw_write_wx(adapter, + if (adapter->hw_write_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_1(port), &mac_cfg1, 4)) return -EIO; @@ -692,7 +693,7 @@ int netxen_niu_enable_gbe_port(struct netxen_adapter *adapter, } else if (mode == NETXEN_NIU_1000_MB) { netxen_gb_set_intfmode(mac_cfg1, 2); - if (netxen_nic_hw_write_wx(adapter, + if (adapter->hw_write_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_1(port), &mac_cfg1, 4)) return -EIO; @@ -704,7 +705,7 @@ int netxen_niu_enable_gbe_port(struct netxen_adapter *adapter, } mii_cfg = 0; netxen_gb_set_mii_mgmt_clockselect(mii_cfg, 7); - if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_GB_MII_MGMT_CONFIG(port), + if (adapter->hw_write_wx(adapter, NETXEN_NIU_GB_MII_MGMT_CONFIG(port), &mii_cfg, 4)) return -EIO; mac_cfg0 = 0; @@ -713,7 +714,7 @@ int netxen_niu_enable_gbe_port(struct netxen_adapter *adapter, netxen_gb_unset_rx_flowctl(mac_cfg0); netxen_gb_unset_tx_flowctl(mac_cfg0); - if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(port), + if (adapter->hw_write_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(port), &mac_cfg0, 4)) return -EIO; return 0; @@ -730,7 +731,7 @@ int netxen_niu_disable_gbe_port(struct netxen_adapter *adapter) 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), + if (adapter->hw_write_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(port), &mac_cfg0, 4)) return -EIO; return 0; @@ -746,7 +747,7 @@ int netxen_niu_disable_xg_port(struct netxen_adapter *adapter) return -EINVAL; mac_cfg = 0; - if (netxen_nic_hw_write_wx(adapter, + if (adapter->hw_write_wx(adapter, NETXEN_NIU_XGE_CONFIG_0 + (0x10000 * port), &mac_cfg, 4)) return -EIO; return 0; @@ -763,7 +764,7 @@ int netxen_niu_set_promiscuous_mode(struct netxen_adapter *adapter, return -EINVAL; /* save previous contents */ - if (netxen_nic_hw_read_wx(adapter, NETXEN_NIU_GB_DROP_WRONGADDR, + if (adapter->hw_read_wx(adapter, NETXEN_NIU_GB_DROP_WRONGADDR, ®, 4)) return -EIO; if (mode == NETXEN_NIU_PROMISC_MODE) { @@ -801,7 +802,7 @@ int netxen_niu_set_promiscuous_mode(struct netxen_adapter *adapter, return -EIO; } } - if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_GB_DROP_WRONGADDR, + if (adapter->hw_write_wx(adapter, NETXEN_NIU_GB_DROP_WRONGADDR, ®, 4)) return -EIO; return 0; @@ -826,13 +827,13 @@ int netxen_niu_xg_macaddr_set(struct netxen_adapter *adapter, 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, + if (adapter->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, + if (adapter->hw_write_wx(adapter, NETXEN_NIU_XGE_STATION_ADDR_0_HI, &val, 4)) return -EIO; break; @@ -840,13 +841,13 @@ int netxen_niu_xg_macaddr_set(struct netxen_adapter *adapter, 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, + if (adapter->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, + if (adapter->hw_write_wx(adapter, NETXEN_NIU_XG1_STATION_ADDR_0_HI, &val, 4)) return -EIO; break; @@ -877,10 +878,10 @@ int netxen_niu_xg_macaddr_get(struct netxen_adapter *adapter, if (phy != 0) return -EINVAL; - if (netxen_nic_hw_read_wx(adapter, NETXEN_NIU_XGE_STATION_ADDR_0_HI, + if (adapter->hw_read_wx(adapter, NETXEN_NIU_XGE_STATION_ADDR_0_HI, &stationhigh, 4)) return -EIO; - if (netxen_nic_hw_read_wx(adapter, NETXEN_NIU_XGE_STATION_ADDR_0_1, + if (adapter->hw_read_wx(adapter, NETXEN_NIU_XGE_STATION_ADDR_0_1, &stationlow, 4)) return -EIO; ((__le32 *)val)[1] = cpu_to_le32(stationhigh); @@ -901,7 +902,7 @@ int netxen_niu_xg_set_promiscuous_mode(struct netxen_adapter *adapter, if (port > NETXEN_NIU_MAX_XG_PORTS) return -EINVAL; - if (netxen_nic_hw_read_wx(adapter, + if (adapter->hw_read_wx(adapter, NETXEN_NIU_XGE_CONFIG_1 + (0x10000 * port), ®, 4)) return -EIO; if (mode == NETXEN_NIU_PROMISC_MODE) @@ -909,6 +910,11 @@ int netxen_niu_xg_set_promiscuous_mode(struct netxen_adapter *adapter, else reg = (reg & ~0x2000UL); + if (mode == NETXEN_NIU_ALLMULTI_MODE) + reg = (reg | 0x1000UL); + else + reg = (reg & ~0x1000UL); + netxen_crb_writelit_adapter(adapter, NETXEN_NIU_XGE_CONFIG_1 + (0x10000 * port), reg); diff --git a/drivers/net/netxen/netxen_nic_phan_reg.h b/drivers/net/netxen/netxen_nic_phan_reg.h index a566b50f36f5..3bfa51b62a4f 100644 --- a/drivers/net/netxen/netxen_nic_phan_reg.h +++ b/drivers/net/netxen/netxen_nic_phan_reg.h @@ -42,8 +42,11 @@ #define CRB_CMD_CONSUMER_OFFSET NETXEN_NIC_REG(0x0c) #define CRB_PAUSE_ADDR_LO NETXEN_NIC_REG(0x10) /* C0 EPG BUG */ #define CRB_PAUSE_ADDR_HI NETXEN_NIC_REG(0x14) -#define CRB_HOST_CMD_ADDR_HI NETXEN_NIC_REG(0x18) /* host add:cmd ring */ -#define CRB_HOST_CMD_ADDR_LO NETXEN_NIC_REG(0x1c) +#define NX_CDRP_CRB_OFFSET NETXEN_NIC_REG(0x18) +#define NX_ARG1_CRB_OFFSET NETXEN_NIC_REG(0x1c) +#define NX_ARG2_CRB_OFFSET NETXEN_NIC_REG(0x20) +#define NX_ARG3_CRB_OFFSET NETXEN_NIC_REG(0x24) +#define NX_SIGN_CRB_OFFSET NETXEN_NIC_REG(0x28) #define CRB_CMD_INTR_LOOP NETXEN_NIC_REG(0x20) /* 4 regs for perf */ #define CRB_CMD_DMA_LOOP NETXEN_NIC_REG(0x24) #define CRB_RCV_INTR_LOOP NETXEN_NIC_REG(0x28) @@ -73,8 +76,8 @@ #define CRB_RX_LRO_MID_TIMER NETXEN_NIC_REG(0x88) #define CRB_DMA_MAX_RCV_BUFS NETXEN_NIC_REG(0x8c) #define CRB_MAX_DMA_ENTRIES NETXEN_NIC_REG(0x90) -#define CRB_XG_STATE NETXEN_NIC_REG(0x94) /* XG Link status */ -#define CRB_AGENT_GO NETXEN_NIC_REG(0x98) /* NIC pkt gen agent */ +#define CRB_XG_STATE NETXEN_NIC_REG(0x94) /* XG Link status */ +#define CRB_XG_STATE_P3 NETXEN_NIC_REG(0x98) /* XG PF Link status */ #define CRB_AGENT_TX_SIZE NETXEN_NIC_REG(0x9c) #define CRB_AGENT_TX_TYPE NETXEN_NIC_REG(0xa0) #define CRB_AGENT_TX_ADDR NETXEN_NIC_REG(0xa4) @@ -97,7 +100,9 @@ #define CRB_HOST_BUFFER_CONS NETXEN_NIC_REG(0xf0) #define CRB_JUMBO_BUFFER_PROD NETXEN_NIC_REG(0xf4) #define CRB_JUMBO_BUFFER_CONS NETXEN_NIC_REG(0xf8) +#define CRB_HOST_DUMMY_BUF NETXEN_NIC_REG(0xfc) +#define CRB_RCVPEG_STATE NETXEN_NIC_REG(0x13c) #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) @@ -147,29 +152,15 @@ #define nx_get_temp_state(x) ((x) & 0xffff) #define nx_encode_temp(val, state) (((val) << 16) | (state)) -/* CRB registers per Rcv Descriptor ring */ -struct netxen_rcv_desc_crb { - u32 crb_rcv_producer_offset __attribute__ ((aligned(512))); - u32 crb_rcv_consumer_offset; - u32 crb_globalrcv_ring; - u32 crb_rcv_ring_size; -}; - /* * CRB registers used by the receive peg logic. */ struct netxen_recv_crb { - struct netxen_rcv_desc_crb rcv_desc_crb[NUM_RCV_DESC_RINGS]; - u32 crb_rcvstatus_ring; - u32 crb_rcv_status_producer; - u32 crb_rcv_status_consumer; - u32 crb_rcvpeg_state; - u32 crb_status_ring_size; + u32 crb_rcv_producer[NUM_RCV_DESC_RINGS]; + u32 crb_sts_consumer; }; -extern struct netxen_recv_crb recv_crb_registers[]; - /* * Temperature control. */ diff --git a/drivers/net/pasemi_mac.c b/drivers/net/pasemi_mac.c index 993d87c9296f..edc0fd588985 100644 --- a/drivers/net/pasemi_mac.c +++ b/drivers/net/pasemi_mac.c @@ -650,7 +650,7 @@ static void pasemi_mac_replenish_rx_ring(const struct net_device *dev, mac->bufsz - LOCAL_SKB_ALIGN, PCI_DMA_FROMDEVICE); - if (unlikely(dma_mapping_error(dma))) { + if (unlikely(pci_dma_mapping_error(mac->dma_pdev, dma))) { dev_kfree_skb_irq(info->skb); break; } @@ -1519,7 +1519,7 @@ static int pasemi_mac_start_tx(struct sk_buff *skb, struct net_device *dev) map[0] = pci_map_single(mac->dma_pdev, skb->data, skb_headlen(skb), PCI_DMA_TODEVICE); map_size[0] = skb_headlen(skb); - if (dma_mapping_error(map[0])) + if (pci_dma_mapping_error(mac->dma_pdev, map[0])) goto out_err_nolock; for (i = 0; i < nfrags; i++) { @@ -1529,7 +1529,7 @@ static int pasemi_mac_start_tx(struct sk_buff *skb, struct net_device *dev) frag->page_offset, frag->size, PCI_DMA_TODEVICE); map_size[i+1] = frag->size; - if (dma_mapping_error(map[i+1])) { + if (pci_dma_mapping_error(mac->dma_pdev, map[i+1])) { nfrags = i; goto out_err_nolock; } diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 32a8503a7acd..4aa547947040 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -158,11 +158,10 @@ static int m88e1111_config_init(struct phy_device *phydev) { int err; int temp; - int mode; /* Enable Fiber/Copper auto selection */ temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); - temp |= MII_M1111_HWCFG_FIBER_COPPER_AUTO; + temp &= ~MII_M1111_HWCFG_FIBER_COPPER_AUTO; phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); temp = phy_read(phydev, MII_BMCR); @@ -198,9 +197,7 @@ static int m88e1111_config_init(struct phy_device *phydev) temp &= ~(MII_M1111_HWCFG_MODE_MASK); - mode = phy_read(phydev, MII_M1111_PHY_EXT_CR); - - if (mode & MII_M1111_HWCFG_FIBER_COPPER_RES) + if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES) temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII; else temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII; diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c index 6b1d7a8edf15..ddccc074a76a 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c @@ -581,12 +581,12 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (file == ppp->owner) ppp_shutdown_interface(ppp); } - if (atomic_read(&file->f_count) <= 2) { + if (atomic_long_read(&file->f_count) <= 2) { ppp_release(NULL, file); err = 0; } else - printk(KERN_DEBUG "PPPIOCDETACH file->f_count=%d\n", - atomic_read(&file->f_count)); + printk(KERN_DEBUG "PPPIOCDETACH file->f_count=%ld\n", + atomic_long_read(&file->f_count)); unlock_kernel(); return err; } @@ -866,7 +866,8 @@ static int __init ppp_init(void) err = PTR_ERR(ppp_class); goto out_chrdev; } - device_create(ppp_class, NULL, MKDEV(PPP_MAJOR, 0), "ppp"); + device_create_drvdata(ppp_class, NULL, MKDEV(PPP_MAJOR, 0), + NULL, "ppp"); } out: diff --git a/drivers/net/qla3xxx.c b/drivers/net/qla3xxx.c index e7d48a352beb..e82b37bbd6c3 100644 --- a/drivers/net/qla3xxx.c +++ b/drivers/net/qla3xxx.c @@ -328,7 +328,7 @@ 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); + err = pci_dma_mapping_error(qdev->pdev, map); if(err) { printk(KERN_ERR "%s: PCI mapping failed with error: %d\n", qdev->ndev->name, err); @@ -1919,7 +1919,7 @@ static int ql_populate_free_queue(struct ql3_adapter *qdev) QL_HEADER_SPACE, PCI_DMA_FROMDEVICE); - err = pci_dma_mapping_error(map); + err = pci_dma_mapping_error(qdev->pdev, map); if(err) { printk(KERN_ERR "%s: PCI mapping failed with error: %d\n", qdev->ndev->name, err); @@ -2454,7 +2454,7 @@ static int ql_send_map(struct ql3_adapter *qdev, */ map = pci_map_single(qdev->pdev, skb->data, len, PCI_DMA_TODEVICE); - err = pci_dma_mapping_error(map); + err = pci_dma_mapping_error(qdev->pdev, map); if(err) { printk(KERN_ERR "%s: PCI mapping failed with error: %d\n", qdev->ndev->name, err); @@ -2487,7 +2487,7 @@ static int ql_send_map(struct ql3_adapter *qdev, sizeof(struct oal), PCI_DMA_TODEVICE); - err = pci_dma_mapping_error(map); + err = pci_dma_mapping_error(qdev->pdev, map); if(err) { printk(KERN_ERR "%s: PCI mapping outbound address list with error: %d\n", @@ -2514,7 +2514,7 @@ static int ql_send_map(struct ql3_adapter *qdev, frag->page_offset, frag->size, PCI_DMA_TODEVICE); - err = pci_dma_mapping_error(map); + err = pci_dma_mapping_error(qdev->pdev, map); if(err) { printk(KERN_ERR "%s: PCI mapping frags failed with error: %d\n", qdev->ndev->name, err); @@ -2916,7 +2916,7 @@ static int ql_alloc_large_buffers(struct ql3_adapter *qdev) QL_HEADER_SPACE, PCI_DMA_FROMDEVICE); - err = pci_dma_mapping_error(map); + err = pci_dma_mapping_error(qdev->pdev, map); if(err) { printk(KERN_ERR "%s: PCI mapping failed with error: %d\n", qdev->ndev->name, err); diff --git a/drivers/net/r6040.c b/drivers/net/r6040.c index 504a48ff73c8..6531ff565c54 100644 --- a/drivers/net/r6040.c +++ b/drivers/net/r6040.c @@ -50,8 +50,8 @@ #include <asm/processor.h> #define DRV_NAME "r6040" -#define DRV_VERSION "0.16" -#define DRV_RELDATE "10Nov2007" +#define DRV_VERSION "0.18" +#define DRV_RELDATE "13Jul2008" /* PHY CHIP Address */ #define PHY1_ADDR 1 /* For MAC1 */ @@ -91,6 +91,14 @@ #define MISR 0x3C /* Status register */ #define MIER 0x40 /* INT enable register */ #define MSK_INT 0x0000 /* Mask off interrupts */ +#define RX_FINISH 0x0001 /* RX finished */ +#define RX_NO_DESC 0x0002 /* No RX descriptor available */ +#define RX_FIFO_FULL 0x0004 /* RX FIFO full */ +#define RX_EARLY 0x0008 /* RX early */ +#define TX_FINISH 0x0010 /* TX finished */ +#define TX_EARLY 0x0080 /* TX early */ +#define EVENT_OVRFL 0x0100 /* Event counter overflow */ +#define LINK_CHANGED 0x0200 /* PHY link changed */ #define ME_CISR 0x44 /* Event counter INT status */ #define ME_CIER 0x48 /* Event counter INT enable */ #define MR_CNT 0x50 /* Successfully received packet counter */ @@ -130,6 +138,21 @@ #define MBCR_DEFAULT 0x012A /* MAC Bus Control Register */ #define MCAST_MAX 4 /* Max number multicast addresses to filter */ +/* Descriptor status */ +#define DSC_OWNER_MAC 0x8000 /* MAC is the owner of this descriptor */ +#define DSC_RX_OK 0x4000 /* RX was successful */ +#define DSC_RX_ERR 0x0800 /* RX PHY error */ +#define DSC_RX_ERR_DRI 0x0400 /* RX dribble packet */ +#define DSC_RX_ERR_BUF 0x0200 /* RX length exceeds buffer size */ +#define DSC_RX_ERR_LONG 0x0100 /* RX length > maximum packet length */ +#define DSC_RX_ERR_RUNT 0x0080 /* RX packet length < 64 byte */ +#define DSC_RX_ERR_CRC 0x0040 /* RX CRC error */ +#define DSC_RX_BCAST 0x0020 /* RX broadcast (no error) */ +#define DSC_RX_MCAST 0x0010 /* RX multicast (no error) */ +#define DSC_RX_MCH_HIT 0x0008 /* RX multicast hit in hash table (no error) */ +#define DSC_RX_MIDH_HIT 0x0004 /* RX MID table hit (no error) */ +#define DSC_RX_IDX_MID_MASK 3 /* RX mask for the index of matched MIDx */ + /* PHY settings */ #define ICPLUS_PHY_ID 0x0243 @@ -139,10 +162,10 @@ MODULE_AUTHOR("Sten Wang <sten.wang@rdc.com.tw>," MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("RDC R6040 NAPI PCI FastEthernet driver"); -#define RX_INT 0x0001 -#define TX_INT 0x0010 -#define RX_NO_DESC_INT 0x0002 -#define INT_MASK (RX_INT | TX_INT) +/* RX and TX interrupts that we handle */ +#define RX_INTS (RX_FIFO_FULL | RX_NO_DESC | RX_FINISH) +#define TX_INTS (TX_FINISH) +#define INT_MASK (RX_INTS | TX_INTS) struct r6040_descriptor { u16 status, len; /* 0-3 */ @@ -167,7 +190,7 @@ struct r6040_private { struct r6040_descriptor *tx_ring; dma_addr_t rx_ring_dma; dma_addr_t tx_ring_dma; - u16 tx_free_desc, rx_free_desc, phy_addr, phy_mode; + u16 tx_free_desc, phy_addr, phy_mode; u16 mcr0, mcr1; u16 switch_sig; struct net_device *dev; @@ -183,7 +206,7 @@ static char version[] __devinitdata = KERN_INFO DRV_NAME static int phy_table[] = { PHY1_ADDR, PHY2_ADDR }; /* Read a word data from PHY Chip */ -static int phy_read(void __iomem *ioaddr, int phy_addr, int reg) +static int r6040_phy_read(void __iomem *ioaddr, int phy_addr, int reg) { int limit = 2048; u16 cmd; @@ -200,7 +223,7 @@ static int phy_read(void __iomem *ioaddr, int phy_addr, int reg) } /* Write a word data from PHY Chip */ -static void phy_write(void __iomem *ioaddr, int phy_addr, int reg, u16 val) +static void r6040_phy_write(void __iomem *ioaddr, int phy_addr, int reg, u16 val) { int limit = 2048; u16 cmd; @@ -216,20 +239,20 @@ static void phy_write(void __iomem *ioaddr, int phy_addr, int reg, u16 val) } } -static int mdio_read(struct net_device *dev, int mii_id, int reg) +static int r6040_mdio_read(struct net_device *dev, int mii_id, int reg) { struct r6040_private *lp = netdev_priv(dev); void __iomem *ioaddr = lp->base; - return (phy_read(ioaddr, lp->phy_addr, reg)); + return (r6040_phy_read(ioaddr, lp->phy_addr, reg)); } -static void mdio_write(struct net_device *dev, int mii_id, int reg, int val) +static void r6040_mdio_write(struct net_device *dev, int mii_id, int reg, int val) { struct r6040_private *lp = netdev_priv(dev); void __iomem *ioaddr = lp->base; - phy_write(ioaddr, lp->phy_addr, reg, val); + r6040_phy_write(ioaddr, lp->phy_addr, reg, val); } static void r6040_free_txbufs(struct net_device *dev) @@ -283,58 +306,101 @@ static void r6040_init_ring_desc(struct r6040_descriptor *desc_ring, desc->vndescp = desc_ring; } -/* Allocate skb buffer for rx descriptor */ -static void rx_buf_alloc(struct r6040_private *lp, struct net_device *dev) +static void r6040_init_txbufs(struct net_device *dev) { - struct r6040_descriptor *descptr; - void __iomem *ioaddr = lp->base; + struct r6040_private *lp = netdev_priv(dev); - descptr = lp->rx_insert_ptr; - while (lp->rx_free_desc < RX_DCNT) { - descptr->skb_ptr = netdev_alloc_skb(dev, MAX_BUF_SIZE); + lp->tx_free_desc = TX_DCNT; - if (!descptr->skb_ptr) - break; - descptr->buf = cpu_to_le32(pci_map_single(lp->pdev, - descptr->skb_ptr->data, - MAX_BUF_SIZE, PCI_DMA_FROMDEVICE)); - descptr->status = 0x8000; - descptr = descptr->vndescp; - lp->rx_free_desc++; - /* Trigger RX DMA */ - iowrite16(lp->mcr0 | 0x0002, ioaddr); - } - lp->rx_insert_ptr = descptr; + lp->tx_remove_ptr = lp->tx_insert_ptr = lp->tx_ring; + r6040_init_ring_desc(lp->tx_ring, lp->tx_ring_dma, TX_DCNT); } -static void r6040_alloc_txbufs(struct net_device *dev) +static int r6040_alloc_rxbufs(struct net_device *dev) { struct r6040_private *lp = netdev_priv(dev); - void __iomem *ioaddr = lp->base; + struct r6040_descriptor *desc; + struct sk_buff *skb; + int rc; - lp->tx_free_desc = TX_DCNT; + lp->rx_remove_ptr = lp->rx_insert_ptr = lp->rx_ring; + r6040_init_ring_desc(lp->rx_ring, lp->rx_ring_dma, RX_DCNT); - lp->tx_remove_ptr = lp->tx_insert_ptr = lp->tx_ring; - r6040_init_ring_desc(lp->tx_ring, lp->tx_ring_dma, TX_DCNT); + /* Allocate skbs for the rx descriptors */ + desc = lp->rx_ring; + do { + skb = netdev_alloc_skb(dev, MAX_BUF_SIZE); + if (!skb) { + printk(KERN_ERR "%s: failed to alloc skb for rx\n", dev->name); + rc = -ENOMEM; + goto err_exit; + } + desc->skb_ptr = skb; + desc->buf = cpu_to_le32(pci_map_single(lp->pdev, + desc->skb_ptr->data, + MAX_BUF_SIZE, PCI_DMA_FROMDEVICE)); + desc->status = DSC_OWNER_MAC; + desc = desc->vndescp; + } while (desc != lp->rx_ring); - iowrite16(lp->tx_ring_dma, ioaddr + MTD_SA0); - iowrite16(lp->tx_ring_dma >> 16, ioaddr + MTD_SA1); + return 0; + +err_exit: + /* Deallocate all previously allocated skbs */ + r6040_free_rxbufs(dev); + return rc; } -static void r6040_alloc_rxbufs(struct net_device *dev) +static void r6040_init_mac_regs(struct net_device *dev) { struct r6040_private *lp = netdev_priv(dev); void __iomem *ioaddr = lp->base; + int limit = 2048; + u16 cmd; - lp->rx_free_desc = 0; + /* Mask Off Interrupt */ + iowrite16(MSK_INT, ioaddr + MIER); - lp->rx_remove_ptr = lp->rx_insert_ptr = lp->rx_ring; - r6040_init_ring_desc(lp->rx_ring, lp->rx_ring_dma, RX_DCNT); + /* Reset RDC MAC */ + iowrite16(MAC_RST, ioaddr + MCR1); + while (limit--) { + cmd = ioread16(ioaddr + MCR1); + if (cmd & 0x1) + break; + } + /* Reset internal state machine */ + iowrite16(2, ioaddr + MAC_SM); + iowrite16(0, ioaddr + MAC_SM); + udelay(5000); - rx_buf_alloc(lp, dev); + /* MAC Bus Control Register */ + iowrite16(MBCR_DEFAULT, ioaddr + MBCR); + + /* Buffer Size Register */ + iowrite16(MAX_BUF_SIZE, ioaddr + MR_BSR); + + /* Write TX ring start address */ + iowrite16(lp->tx_ring_dma, ioaddr + MTD_SA0); + iowrite16(lp->tx_ring_dma >> 16, ioaddr + MTD_SA1); + /* Write RX ring start address */ iowrite16(lp->rx_ring_dma, ioaddr + MRD_SA0); iowrite16(lp->rx_ring_dma >> 16, ioaddr + MRD_SA1); + + /* Set interrupt waiting time and packet numbers */ + iowrite16(0, ioaddr + MT_ICR); + iowrite16(0, ioaddr + MR_ICR); + + /* Enable interrupts */ + iowrite16(INT_MASK, ioaddr + MIER); + + /* Enable TX and RX */ + iowrite16(lp->mcr0 | 0x0002, ioaddr); + + /* Let TX poll the descriptors + * we may got called by r6040_tx_timeout which has left + * some unsent tx buffers */ + iowrite16(0x01, ioaddr + MTPR); } static void r6040_tx_timeout(struct net_device *dev) @@ -342,27 +408,16 @@ static void r6040_tx_timeout(struct net_device *dev) struct r6040_private *priv = netdev_priv(dev); void __iomem *ioaddr = priv->base; - printk(KERN_WARNING "%s: transmit timed out, status %4.4x, PHY status " - "%4.4x\n", + printk(KERN_WARNING "%s: transmit timed out, int enable %4.4x " + "status %4.4x, PHY status %4.4x\n", dev->name, ioread16(ioaddr + MIER), - mdio_read(dev, priv->mii_if.phy_id, MII_BMSR)); - - disable_irq(dev->irq); - napi_disable(&priv->napi); - spin_lock(&priv->lock); - /* Clear all descriptors */ - r6040_free_txbufs(dev); - r6040_free_rxbufs(dev); - r6040_alloc_txbufs(dev); - r6040_alloc_rxbufs(dev); - - /* Reset MAC */ - iowrite16(MAC_RST, ioaddr + MCR1); - spin_unlock(&priv->lock); - enable_irq(dev->irq); + ioread16(ioaddr + MISR), + r6040_mdio_read(dev, priv->mii_if.phy_id, MII_BMSR)); dev->stats.tx_errors++; - netif_wake_queue(dev); + + /* Reset MAC and re-init all registers */ + r6040_init_mac_regs(dev); } static struct net_device_stats *r6040_get_stats(struct net_device *dev) @@ -424,6 +479,7 @@ static int r6040_close(struct net_device *dev) del_timer_sync(&lp->timer); spin_lock_irq(&lp->lock); + napi_disable(&lp->napi); netif_stop_queue(dev); r6040_down(dev); spin_unlock_irq(&lp->lock); @@ -432,23 +488,23 @@ static int r6040_close(struct net_device *dev) } /* Status of PHY CHIP */ -static int phy_mode_chk(struct net_device *dev) +static int r6040_phy_mode_chk(struct net_device *dev) { struct r6040_private *lp = netdev_priv(dev); void __iomem *ioaddr = lp->base; int phy_dat; /* PHY Link Status Check */ - phy_dat = phy_read(ioaddr, lp->phy_addr, 1); + phy_dat = r6040_phy_read(ioaddr, lp->phy_addr, 1); if (!(phy_dat & 0x4)) phy_dat = 0x8000; /* Link Failed, full duplex */ /* PHY Chip Auto-Negotiation Status */ - phy_dat = phy_read(ioaddr, lp->phy_addr, 1); + phy_dat = r6040_phy_read(ioaddr, lp->phy_addr, 1); if (phy_dat & 0x0020) { /* Auto Negotiation Mode */ - phy_dat = phy_read(ioaddr, lp->phy_addr, 5); - phy_dat &= phy_read(ioaddr, lp->phy_addr, 4); + phy_dat = r6040_phy_read(ioaddr, lp->phy_addr, 5); + phy_dat &= r6040_phy_read(ioaddr, lp->phy_addr, 4); if (phy_dat & 0x140) /* Force full duplex */ phy_dat = 0x8000; @@ -456,7 +512,7 @@ static int phy_mode_chk(struct net_device *dev) phy_dat = 0; } else { /* Force Mode */ - phy_dat = phy_read(ioaddr, lp->phy_addr, 0); + phy_dat = r6040_phy_read(ioaddr, lp->phy_addr, 0); if (phy_dat & 0x100) phy_dat = 0x8000; else @@ -468,12 +524,12 @@ static int phy_mode_chk(struct net_device *dev) static void r6040_set_carrier(struct mii_if_info *mii) { - if (phy_mode_chk(mii->dev)) { + if (r6040_phy_mode_chk(mii->dev)) { /* autoneg is off: Link is always assumed to be up */ if (!netif_carrier_ok(mii->dev)) netif_carrier_on(mii->dev); } else - phy_mode_chk(mii->dev); + r6040_phy_mode_chk(mii->dev); } static int r6040_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) @@ -494,73 +550,72 @@ static int r6040_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) static int r6040_rx(struct net_device *dev, int limit) { struct r6040_private *priv = netdev_priv(dev); - int count; - void __iomem *ioaddr = priv->base; + struct r6040_descriptor *descptr = priv->rx_remove_ptr; + struct sk_buff *skb_ptr, *new_skb; + int count = 0; u16 err; - for (count = 0; count < limit; ++count) { - struct r6040_descriptor *descptr = priv->rx_remove_ptr; - struct sk_buff *skb_ptr; - - /* Disable RX interrupt */ - iowrite16(ioread16(ioaddr + MIER) & (~RX_INT), ioaddr + MIER); - descptr = priv->rx_remove_ptr; - - /* Check for errors */ - err = ioread16(ioaddr + MLSR); - if (err & 0x0400) - dev->stats.rx_errors++; - /* RX FIFO over-run */ - if (err & 0x8000) - dev->stats.rx_fifo_errors++; - /* RX descriptor unavailable */ - if (err & 0x0080) - dev->stats.rx_frame_errors++; - /* Received packet with length over buffer lenght */ - if (err & 0x0020) - dev->stats.rx_over_errors++; - /* Received packet with too long or short */ - if (err & (0x0010 | 0x0008)) - dev->stats.rx_length_errors++; - /* Received packet with CRC errors */ - if (err & 0x0004) { - spin_lock(&priv->lock); - dev->stats.rx_crc_errors++; - spin_unlock(&priv->lock); - } - - while (priv->rx_free_desc) { - /* No RX packet */ - if (descptr->status & 0x8000) - break; - skb_ptr = descptr->skb_ptr; - if (!skb_ptr) { - printk(KERN_ERR "%s: Inconsistent RX" - "descriptor chain\n", - dev->name); - break; + /* Limit not reached and the descriptor belongs to the CPU */ + while (count < limit && !(descptr->status & DSC_OWNER_MAC)) { + /* Read the descriptor status */ + err = descptr->status; + /* Global error status set */ + if (err & DSC_RX_ERR) { + /* RX dribble */ + if (err & DSC_RX_ERR_DRI) + dev->stats.rx_frame_errors++; + /* Buffer lenght exceeded */ + if (err & DSC_RX_ERR_BUF) + dev->stats.rx_length_errors++; + /* Packet too long */ + if (err & DSC_RX_ERR_LONG) + dev->stats.rx_length_errors++; + /* Packet < 64 bytes */ + if (err & DSC_RX_ERR_RUNT) + dev->stats.rx_length_errors++; + /* CRC error */ + if (err & DSC_RX_ERR_CRC) { + spin_lock(&priv->lock); + dev->stats.rx_crc_errors++; + spin_unlock(&priv->lock); } - descptr->skb_ptr = NULL; - skb_ptr->dev = priv->dev; - /* Do not count the CRC */ - skb_put(skb_ptr, descptr->len - 4); - pci_unmap_single(priv->pdev, le32_to_cpu(descptr->buf), - MAX_BUF_SIZE, PCI_DMA_FROMDEVICE); - skb_ptr->protocol = eth_type_trans(skb_ptr, priv->dev); - /* Send to upper layer */ - netif_receive_skb(skb_ptr); - dev->last_rx = jiffies; - dev->stats.rx_packets++; - dev->stats.rx_bytes += descptr->len; - /* To next descriptor */ - descptr = descptr->vndescp; - priv->rx_free_desc--; + goto next_descr; + } + + /* Packet successfully received */ + new_skb = netdev_alloc_skb(dev, MAX_BUF_SIZE); + if (!new_skb) { + dev->stats.rx_dropped++; + goto next_descr; } - priv->rx_remove_ptr = descptr; + skb_ptr = descptr->skb_ptr; + skb_ptr->dev = priv->dev; + + /* Do not count the CRC */ + skb_put(skb_ptr, descptr->len - 4); + pci_unmap_single(priv->pdev, le32_to_cpu(descptr->buf), + MAX_BUF_SIZE, PCI_DMA_FROMDEVICE); + skb_ptr->protocol = eth_type_trans(skb_ptr, priv->dev); + + /* Send to upper layer */ + netif_receive_skb(skb_ptr); + dev->last_rx = jiffies; + dev->stats.rx_packets++; + dev->stats.rx_bytes += descptr->len - 4; + + /* put new skb into descriptor */ + descptr->skb_ptr = new_skb; + descptr->buf = cpu_to_le32(pci_map_single(priv->pdev, + descptr->skb_ptr->data, + MAX_BUF_SIZE, PCI_DMA_FROMDEVICE)); + +next_descr: + /* put the descriptor back to the MAC */ + descptr->status = DSC_OWNER_MAC; + descptr = descptr->vndescp; + count++; } - /* Allocate new RX buffer */ - if (priv->rx_free_desc < RX_DCNT) - rx_buf_alloc(priv, priv->dev); + priv->rx_remove_ptr = descptr; return count; } @@ -584,7 +639,7 @@ static void r6040_tx(struct net_device *dev) if (err & (0x2000 | 0x4000)) dev->stats.tx_carrier_errors++; - if (descptr->status & 0x8000) + if (descptr->status & DSC_OWNER_MAC) break; /* Not complete */ skb_ptr = descptr->skb_ptr; pci_unmap_single(priv->pdev, le32_to_cpu(descptr->buf), @@ -616,7 +671,7 @@ static int r6040_poll(struct napi_struct *napi, int budget) if (work_done < budget) { netif_rx_complete(dev, napi); /* Enable RX interrupt */ - iowrite16(ioread16(ioaddr + MIER) | RX_INT, ioaddr + MIER); + iowrite16(ioread16(ioaddr + MIER) | RX_INTS, ioaddr + MIER); } return work_done; } @@ -638,13 +693,22 @@ static irqreturn_t r6040_interrupt(int irq, void *dev_id) return IRQ_NONE; /* RX interrupt request */ - if (status & 0x01) { + if (status & RX_INTS) { + if (status & RX_NO_DESC) { + /* RX descriptor unavailable */ + dev->stats.rx_dropped++; + dev->stats.rx_missed_errors++; + } + if (status & RX_FIFO_FULL) + dev->stats.rx_fifo_errors++; + + /* Mask off RX interrupt */ + iowrite16(ioread16(ioaddr + MIER) & ~RX_INTS, ioaddr + MIER); netif_rx_schedule(dev, &lp->napi); - iowrite16(TX_INT, ioaddr + MIER); } /* TX interrupt request */ - if (status & 0x10) + if (status & TX_INTS) r6040_tx(dev); return IRQ_HANDLED; @@ -660,52 +724,48 @@ static void r6040_poll_controller(struct net_device *dev) #endif /* Init RDC MAC */ -static void r6040_up(struct net_device *dev) +static int r6040_up(struct net_device *dev) { struct r6040_private *lp = netdev_priv(dev); void __iomem *ioaddr = lp->base; + int ret; /* Initialise and alloc RX/TX buffers */ - r6040_alloc_txbufs(dev); - r6040_alloc_rxbufs(dev); + r6040_init_txbufs(dev); + ret = r6040_alloc_rxbufs(dev); + if (ret) + return ret; - /* Buffer Size Register */ - iowrite16(MAX_BUF_SIZE, ioaddr + MR_BSR); /* Read the PHY ID */ - lp->switch_sig = phy_read(ioaddr, 0, 2); + lp->switch_sig = r6040_phy_read(ioaddr, 0, 2); if (lp->switch_sig == ICPLUS_PHY_ID) { - phy_write(ioaddr, 29, 31, 0x175C); /* Enable registers */ + r6040_phy_write(ioaddr, 29, 31, 0x175C); /* Enable registers */ lp->phy_mode = 0x8000; } else { /* PHY Mode Check */ - phy_write(ioaddr, lp->phy_addr, 4, PHY_CAP); - phy_write(ioaddr, lp->phy_addr, 0, PHY_MODE); + r6040_phy_write(ioaddr, lp->phy_addr, 4, PHY_CAP); + r6040_phy_write(ioaddr, lp->phy_addr, 0, PHY_MODE); if (PHY_MODE == 0x3100) - lp->phy_mode = phy_mode_chk(dev); + lp->phy_mode = r6040_phy_mode_chk(dev); else lp->phy_mode = (PHY_MODE & 0x0100) ? 0x8000:0x0; } - /* MAC Bus Control Register */ - iowrite16(MBCR_DEFAULT, ioaddr + MBCR); - /* MAC TX/RX Enable */ + /* Set duplex mode */ lp->mcr0 |= lp->phy_mode; - iowrite16(lp->mcr0, ioaddr); - - /* set interrupt waiting time and packet numbers */ - iowrite16(0x0F06, ioaddr + MT_ICR); - iowrite16(0x0F06, ioaddr + MR_ICR); /* improve performance (by RDC guys) */ - phy_write(ioaddr, 30, 17, (phy_read(ioaddr, 30, 17) | 0x4000)); - phy_write(ioaddr, 30, 17, ~((~phy_read(ioaddr, 30, 17)) | 0x2000)); - phy_write(ioaddr, 0, 19, 0x0000); - phy_write(ioaddr, 0, 30, 0x01F0); + r6040_phy_write(ioaddr, 30, 17, (r6040_phy_read(ioaddr, 30, 17) | 0x4000)); + r6040_phy_write(ioaddr, 30, 17, ~((~r6040_phy_read(ioaddr, 30, 17)) | 0x2000)); + r6040_phy_write(ioaddr, 0, 19, 0x0000); + r6040_phy_write(ioaddr, 0, 30, 0x01F0); - /* Interrupt Mask Register */ - iowrite16(INT_MASK, ioaddr + MIER); + /* Initialize all MAC registers */ + r6040_init_mac_regs(dev); + + return 0; } /* @@ -721,7 +781,7 @@ static void r6040_timer(unsigned long data) /* Polling PHY Chip Status */ if (PHY_MODE == 0x3100) - phy_mode = phy_mode_chk(dev); + phy_mode = r6040_phy_mode_chk(dev); else phy_mode = (PHY_MODE & 0x0100) ? 0x8000:0x0; @@ -784,7 +844,14 @@ static int r6040_open(struct net_device *dev) return -ENOMEM; } - r6040_up(dev); + ret = r6040_up(dev); + if (ret) { + pci_free_consistent(lp->pdev, TX_DESC_SIZE, lp->tx_ring, + lp->tx_ring_dma); + pci_free_consistent(lp->pdev, RX_DESC_SIZE, lp->rx_ring, + lp->rx_ring_dma); + return ret; + } napi_enable(&lp->napi); netif_start_queue(dev); @@ -830,7 +897,7 @@ static int r6040_start_xmit(struct sk_buff *skb, struct net_device *dev) descptr->skb_ptr = skb; descptr->buf = cpu_to_le32(pci_map_single(lp->pdev, skb->data, skb->len, PCI_DMA_TODEVICE)); - descptr->status = 0x8000; + descptr->status = DSC_OWNER_MAC; /* Trigger the MAC to check the TX descriptor */ iowrite16(0x01, ioaddr + MTPR); lp->tx_insert_ptr = descptr->vndescp; @@ -987,24 +1054,27 @@ static int __devinit r6040_init_one(struct pci_dev *pdev, err = pci_enable_device(pdev); if (err) - return err; + goto err_out; /* this should always be supported */ - if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) { + err = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (err) { printk(KERN_ERR DRV_NAME "32-bit PCI DMA addresses" "not supported by the card\n"); - return -ENODEV; + goto err_out; } - if (pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) { + err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + if (err) { printk(KERN_ERR DRV_NAME "32-bit PCI DMA addresses" "not supported by the card\n"); - return -ENODEV; + goto err_out; } /* IO Size check */ if (pci_resource_len(pdev, 0) < io_size) { - printk(KERN_ERR "Insufficient PCI resources, aborting\n"); - return -EIO; + printk(KERN_ERR DRV_NAME "Insufficient PCI resources, aborting\n"); + err = -EIO; + goto err_out; } pioaddr = pci_resource_start(pdev, 0); /* IO map base address */ @@ -1012,24 +1082,26 @@ static int __devinit r6040_init_one(struct pci_dev *pdev, dev = alloc_etherdev(sizeof(struct r6040_private)); if (!dev) { - printk(KERN_ERR "Failed to allocate etherdev\n"); - return -ENOMEM; + printk(KERN_ERR DRV_NAME "Failed to allocate etherdev\n"); + err = -ENOMEM; + goto err_out; } SET_NETDEV_DEV(dev, &pdev->dev); lp = netdev_priv(dev); - lp->pdev = pdev; - if (pci_request_regions(pdev, DRV_NAME)) { + err = pci_request_regions(pdev, DRV_NAME); + + if (err) { printk(KERN_ERR DRV_NAME ": Failed to request PCI regions\n"); - err = -ENODEV; - goto err_out_disable; + goto err_out_free_dev; } ioaddr = pci_iomap(pdev, bar, io_size); if (!ioaddr) { printk(KERN_ERR "ioremap failed for device %s\n", pci_name(pdev)); - return -EIO; + err = -EIO; + goto err_out_free_res; } /* Init system & device */ @@ -1049,6 +1121,7 @@ static int __devinit r6040_init_one(struct pci_dev *pdev, /* Link new device into r6040_root_dev */ lp->pdev = pdev; + lp->dev = dev; /* Init RDC private data */ lp->mcr0 = 0x1002; @@ -1070,8 +1143,8 @@ static int __devinit r6040_init_one(struct pci_dev *pdev, #endif netif_napi_add(dev, &lp->napi, r6040_poll, 64); lp->mii_if.dev = dev; - lp->mii_if.mdio_read = mdio_read; - lp->mii_if.mdio_write = mdio_write; + lp->mii_if.mdio_read = r6040_mdio_read; + lp->mii_if.mdio_write = r6040_mdio_write; lp->mii_if.phy_id = lp->phy_addr; lp->mii_if.phy_id_mask = 0x1f; lp->mii_if.reg_num_mask = 0x1f; @@ -1080,17 +1153,17 @@ static int __devinit r6040_init_one(struct pci_dev *pdev, err = register_netdev(dev); if (err) { printk(KERN_ERR DRV_NAME ": Failed to register net device\n"); - goto err_out_res; + goto err_out_unmap; } return 0; -err_out_res: +err_out_unmap: + pci_iounmap(pdev, ioaddr); +err_out_free_res: pci_release_regions(pdev); -err_out_disable: - pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); +err_out_free_dev: free_netdev(dev); - +err_out: return err; } diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index cfe8829ed31f..a3e3895e5032 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -1418,8 +1418,10 @@ static void rtl8169_init_phy(struct net_device *dev, struct rtl8169_private *tp) rtl_hw_phy_config(dev); - dprintk("Set MAC Reg C+CR Offset 0x82h = 0x01h\n"); - RTL_W8(0x82, 0x01); + if (tp->mac_version <= RTL_GIGA_MAC_VER_06) { + dprintk("Set MAC Reg C+CR Offset 0x82h = 0x01h\n"); + RTL_W8(0x82, 0x01); + } pci_write_config_byte(tp->pci_dev, PCI_LATENCY_TIMER, 0x40); @@ -3032,13 +3034,7 @@ static void rtl_set_rx_mode(struct net_device *dev) tmp = rtl8169_rx_config | rx_mode | (RTL_R32(RxConfig) & rtl_chip_info[tp->chipset].RxConfigMask); - if ((tp->mac_version == RTL_GIGA_MAC_VER_11) || - (tp->mac_version == RTL_GIGA_MAC_VER_12) || - (tp->mac_version == RTL_GIGA_MAC_VER_13) || - (tp->mac_version == RTL_GIGA_MAC_VER_14) || - (tp->mac_version == RTL_GIGA_MAC_VER_15) || - (tp->mac_version == RTL_GIGA_MAC_VER_16) || - (tp->mac_version == RTL_GIGA_MAC_VER_17)) { + if (tp->mac_version > RTL_GIGA_MAC_VER_06) { u32 data = mc_filter[0]; mc_filter[0] = swab32(mc_filter[1]); diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index 9dae40ccf048..86d77d05190a 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -2512,8 +2512,8 @@ static void stop_nic(struct s2io_nic *nic) * Return Value: * SUCCESS on success or an appropriate -ve value on failure. */ - -static int fill_rx_buffers(struct ring_info *ring, int from_card_up) +static int fill_rx_buffers(struct s2io_nic *nic, struct ring_info *ring, + int from_card_up) { struct sk_buff *skb; struct RxD_t *rxdp; @@ -2602,7 +2602,8 @@ static int fill_rx_buffers(struct ring_info *ring, int from_card_up) rxdp1->Buffer0_ptr = pci_map_single (ring->pdev, skb->data, size - NET_IP_ALIGN, PCI_DMA_FROMDEVICE); - if(pci_dma_mapping_error(rxdp1->Buffer0_ptr)) + if (pci_dma_mapping_error(nic->pdev, + rxdp1->Buffer0_ptr)) goto pci_map_failed; rxdp->Control_2 = @@ -2636,7 +2637,8 @@ static int fill_rx_buffers(struct ring_info *ring, int from_card_up) rxdp3->Buffer0_ptr = pci_map_single(ring->pdev, ba->ba_0, BUF0_LEN, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rxdp3->Buffer0_ptr)) + if (pci_dma_mapping_error(nic->pdev, + rxdp3->Buffer0_ptr)) goto pci_map_failed; } else pci_dma_sync_single_for_device(ring->pdev, @@ -2655,7 +2657,8 @@ static int fill_rx_buffers(struct ring_info *ring, int from_card_up) (ring->pdev, skb->data, ring->mtu + 4, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rxdp3->Buffer2_ptr)) + if (pci_dma_mapping_error(nic->pdev, + rxdp3->Buffer2_ptr)) goto pci_map_failed; if (from_card_up) { @@ -2664,8 +2667,8 @@ static int fill_rx_buffers(struct ring_info *ring, int from_card_up) ba->ba_1, BUF1_LEN, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error - (rxdp3->Buffer1_ptr)) { + if (pci_dma_mapping_error(nic->pdev, + rxdp3->Buffer1_ptr)) { pci_unmap_single (ring->pdev, (dma_addr_t)(unsigned long) @@ -2806,9 +2809,9 @@ static void free_rx_buffers(struct s2io_nic *sp) } } -static int s2io_chk_rx_buffers(struct ring_info *ring) +static int s2io_chk_rx_buffers(struct s2io_nic *nic, struct ring_info *ring) { - if (fill_rx_buffers(ring, 0) == -ENOMEM) { + if (fill_rx_buffers(nic, ring, 0) == -ENOMEM) { DBG_PRINT(INFO_DBG, "%s:Out of memory", ring->dev->name); DBG_PRINT(INFO_DBG, " in Rx Intr!!\n"); } @@ -2848,7 +2851,7 @@ static int s2io_poll_msix(struct napi_struct *napi, int budget) return 0; pkts_processed = rx_intr_handler(ring, budget); - s2io_chk_rx_buffers(ring); + s2io_chk_rx_buffers(nic, ring); if (pkts_processed < budget_org) { netif_rx_complete(dev, napi); @@ -2882,7 +2885,7 @@ static int s2io_poll_inta(struct napi_struct *napi, int budget) for (i = 0; i < config->rx_ring_num; i++) { ring = &mac_control->rings[i]; ring_pkts_processed = rx_intr_handler(ring, budget); - s2io_chk_rx_buffers(ring); + s2io_chk_rx_buffers(nic, ring); pkts_processed += ring_pkts_processed; budget -= ring_pkts_processed; if (budget <= 0) @@ -2939,7 +2942,8 @@ static void s2io_netpoll(struct net_device *dev) rx_intr_handler(&mac_control->rings[i], 0); for (i = 0; i < config->rx_ring_num; i++) { - if (fill_rx_buffers(&mac_control->rings[i], 0) == -ENOMEM) { + if (fill_rx_buffers(nic, &mac_control->rings[i], 0) == + -ENOMEM) { DBG_PRINT(INFO_DBG, "%s:Out of memory", dev->name); DBG_PRINT(INFO_DBG, " in Rx Netpoll!!\n"); break; @@ -4235,14 +4239,14 @@ static int s2io_xmit(struct sk_buff *skb, struct net_device *dev) txdp->Buffer_Pointer = pci_map_single(sp->pdev, fifo->ufo_in_band_v, sizeof(u64), PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(txdp->Buffer_Pointer)) + if (pci_dma_mapping_error(sp->pdev, txdp->Buffer_Pointer)) goto pci_map_failed; txdp++; } txdp->Buffer_Pointer = pci_map_single (sp->pdev, skb->data, frg_len, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(txdp->Buffer_Pointer)) + if (pci_dma_mapping_error(sp->pdev, txdp->Buffer_Pointer)) goto pci_map_failed; txdp->Host_Control = (unsigned long) skb; @@ -4345,7 +4349,7 @@ static irqreturn_t s2io_msix_ring_handle(int irq, void *dev_id) netif_rx_schedule(dev, &ring->napi); } else { rx_intr_handler(ring, 0); - s2io_chk_rx_buffers(ring); + s2io_chk_rx_buffers(sp, ring); } return IRQ_HANDLED; @@ -4826,7 +4830,7 @@ static irqreturn_t s2io_isr(int irq, void *dev_id) */ if (!config->napi) { for (i = 0; i < config->rx_ring_num; i++) - s2io_chk_rx_buffers(&mac_control->rings[i]); + s2io_chk_rx_buffers(sp, &mac_control->rings[i]); } writeq(sp->general_int_mask, &bar0->general_int_mask); readl(&bar0->general_int_status); @@ -6859,7 +6863,7 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp, pci_map_single( sp->pdev, (*skb)->data, size - NET_IP_ALIGN, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rxdp1->Buffer0_ptr)) + if (pci_dma_mapping_error(sp->pdev, rxdp1->Buffer0_ptr)) goto memalloc_failed; rxdp->Host_Control = (unsigned long) (*skb); } @@ -6886,12 +6890,13 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp, pci_map_single(sp->pdev, (*skb)->data, dev->mtu + 4, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rxdp3->Buffer2_ptr)) + if (pci_dma_mapping_error(sp->pdev, rxdp3->Buffer2_ptr)) goto memalloc_failed; rxdp3->Buffer0_ptr = *temp0 = pci_map_single( sp->pdev, ba->ba_0, BUF0_LEN, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rxdp3->Buffer0_ptr)) { + if (pci_dma_mapping_error(sp->pdev, + rxdp3->Buffer0_ptr)) { pci_unmap_single (sp->pdev, (dma_addr_t)rxdp3->Buffer2_ptr, dev->mtu + 4, PCI_DMA_FROMDEVICE); @@ -6903,7 +6908,8 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp, rxdp3->Buffer1_ptr = *temp1 = pci_map_single(sp->pdev, ba->ba_1, BUF1_LEN, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rxdp3->Buffer1_ptr)) { + if (pci_dma_mapping_error(sp->pdev, + rxdp3->Buffer1_ptr)) { pci_unmap_single (sp->pdev, (dma_addr_t)rxdp3->Buffer0_ptr, BUF0_LEN, PCI_DMA_FROMDEVICE); @@ -7187,7 +7193,7 @@ static int s2io_card_up(struct s2io_nic * sp) for (i = 0; i < config->rx_ring_num; i++) { mac_control->rings[i].mtu = dev->mtu; - ret = fill_rx_buffers(&mac_control->rings[i], 1); + ret = fill_rx_buffers(sp, &mac_control->rings[i], 1); if (ret) { DBG_PRINT(ERR_DBG, "%s: Out of memory in Open\n", dev->name); diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index 7b2015f9e469..45c72eebb3a7 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -19,6 +19,7 @@ #include <linux/in.h> #include <linux/crc32.h> #include <linux/ethtool.h> +#include <linux/topology.h> #include "net_driver.h" #include "gmii.h" #include "ethtool.h" @@ -832,7 +833,23 @@ static void efx_probe_interrupts(struct efx_nic *efx) if (efx->interrupt_mode == EFX_INT_MODE_MSIX) { BUG_ON(!pci_find_capability(efx->pci_dev, PCI_CAP_ID_MSIX)); - efx->rss_queues = rss_cpus ? rss_cpus : num_online_cpus(); + if (rss_cpus == 0) { + cpumask_t core_mask; + int cpu; + + cpus_clear(core_mask); + efx->rss_queues = 0; + for_each_online_cpu(cpu) { + if (!cpu_isset(cpu, core_mask)) { + ++efx->rss_queues; + cpus_or(core_mask, core_mask, + topology_core_siblings(cpu)); + } + } + } else { + efx->rss_queues = rss_cpus; + } + efx->rss_queues = min(efx->rss_queues, max_channel + 1); efx->rss_queues = min(efx->rss_queues, EFX_MAX_CHANNELS); @@ -1762,7 +1779,7 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type) efx->reset_pending = method; - queue_work(efx->workqueue, &efx->reset_work); + queue_work(efx->reset_workqueue, &efx->reset_work); } /************************************************************************** @@ -1907,14 +1924,28 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type, goto fail1; } + efx->reset_workqueue = create_singlethread_workqueue("sfc_reset"); + if (!efx->reset_workqueue) { + rc = -ENOMEM; + goto fail2; + } + return 0; + fail2: + destroy_workqueue(efx->workqueue); + efx->workqueue = NULL; + fail1: return rc; } static void efx_fini_struct(struct efx_nic *efx) { + if (efx->reset_workqueue) { + destroy_workqueue(efx->reset_workqueue); + efx->reset_workqueue = NULL; + } if (efx->workqueue) { destroy_workqueue(efx->workqueue); efx->workqueue = NULL; @@ -1977,7 +2008,7 @@ static void efx_pci_remove(struct pci_dev *pci_dev) * scheduled from this point because efx_stop_all() has been * called, we are no longer registered with driverlink, and * the net_device's have been removed. */ - flush_workqueue(efx->workqueue); + flush_workqueue(efx->reset_workqueue); efx_pci_remove_main(efx); @@ -2098,7 +2129,7 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev, * scheduled since efx_stop_all() has been called, and we * have not and never have been registered with either * the rtnetlink or driverlink layers. */ - cancel_work_sync(&efx->reset_work); + flush_workqueue(efx->reset_workqueue); /* Retry if a recoverably reset event has been scheduled */ if ((efx->reset_pending != RESET_TYPE_INVISIBLE) && diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c index 630406e142e5..9138ee5b7b7b 100644 --- a/drivers/net/sfc/falcon.c +++ b/drivers/net/sfc/falcon.c @@ -223,13 +223,8 @@ static struct i2c_algo_bit_data falcon_i2c_bit_operations = { .getsda = falcon_getsda, .getscl = falcon_getscl, .udelay = 5, - /* - * This is the number of system clock ticks after which - * i2c-algo-bit gives up waiting for SCL to become high. - * It must be at least 2 since the first tick can happen - * immediately after it starts waiting. - */ - .timeout = 2, + /* Wait up to 50 ms for slave to let us pull SCL high */ + .timeout = DIV_ROUND_UP(HZ, 20), }; /************************************************************************** @@ -2479,12 +2474,11 @@ int falcon_probe_nic(struct efx_nic *efx) /* Initialise I2C adapter */ efx->i2c_adap.owner = THIS_MODULE; - efx->i2c_adap.class = I2C_CLASS_HWMON; nic_data->i2c_data = falcon_i2c_bit_operations; nic_data->i2c_data.data = efx; efx->i2c_adap.algo_data = &nic_data->i2c_data; efx->i2c_adap.dev.parent = &efx->pci_dev->dev; - strcpy(efx->i2c_adap.name, "SFC4000 GPIO"); + strlcpy(efx->i2c_adap.name, "SFC4000 GPIO", sizeof(efx->i2c_adap.name)); rc = i2c_bit_add_bus(&efx->i2c_adap); if (rc) goto fail5; diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h index d803b86c647c..219c74a772c3 100644 --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h @@ -616,7 +616,9 @@ union efx_multicast_hash { * @pci_dev: The PCI device * @type: Controller type attributes * @legacy_irq: IRQ number - * @workqueue: Workqueue for resets, port reconfigures and the HW monitor + * @workqueue: Workqueue for port reconfigures and the HW monitor. + * Work items do not hold and must not acquire RTNL. + * @reset_workqueue: Workqueue for resets. Work item will acquire RTNL. * @reset_work: Scheduled reset workitem * @monitor_work: Hardware monitor workitem * @membase_phys: Memory BAR value as physical address @@ -684,6 +686,7 @@ struct efx_nic { const struct efx_nic_type *type; int legacy_irq; struct workqueue_struct *workqueue; + struct workqueue_struct *reset_workqueue; struct work_struct reset_work; struct delayed_work monitor_work; resource_size_t membase_phys; diff --git a/drivers/net/sfc/rx.c b/drivers/net/sfc/rx.c index 601b001437c0..0d27dd39bc09 100644 --- a/drivers/net/sfc/rx.c +++ b/drivers/net/sfc/rx.c @@ -233,7 +233,7 @@ static inline int efx_init_rx_buffer_skb(struct efx_rx_queue *rx_queue, rx_buf->data, rx_buf->len, PCI_DMA_FROMDEVICE); - if (unlikely(pci_dma_mapping_error(rx_buf->dma_addr))) { + if (unlikely(pci_dma_mapping_error(efx->pci_dev, rx_buf->dma_addr))) { dev_kfree_skb_any(rx_buf->skb); rx_buf->skb = NULL; return -EIO; @@ -275,7 +275,7 @@ static inline int efx_init_rx_buffer_page(struct efx_rx_queue *rx_queue, 0, efx_rx_buf_size(efx), PCI_DMA_FROMDEVICE); - if (unlikely(pci_dma_mapping_error(dma_addr))) { + if (unlikely(pci_dma_mapping_error(efx->pci_dev, dma_addr))) { __free_pages(rx_buf->page, efx->rx_buffer_order); rx_buf->page = NULL; return -EIO; diff --git a/drivers/net/sfc/tx.c b/drivers/net/sfc/tx.c index 5cdd082ab8f6..5e8374ab28ee 100644 --- a/drivers/net/sfc/tx.c +++ b/drivers/net/sfc/tx.c @@ -172,7 +172,7 @@ static inline int efx_enqueue_skb(struct efx_tx_queue *tx_queue, /* Process all fragments */ while (1) { - if (unlikely(pci_dma_mapping_error(dma_addr))) + if (unlikely(pci_dma_mapping_error(pci_dev, dma_addr))) goto pci_err; /* Store fields for marking in the per-fragment final @@ -661,7 +661,8 @@ efx_tsoh_heap_alloc(struct efx_tx_queue *tx_queue, size_t header_len) tsoh->dma_addr = pci_map_single(tx_queue->efx->pci_dev, TSOH_BUFFER(tsoh), header_len, PCI_DMA_TODEVICE); - if (unlikely(pci_dma_mapping_error(tsoh->dma_addr))) { + if (unlikely(pci_dma_mapping_error(tx_queue->efx->pci_dev, + tsoh->dma_addr))) { kfree(tsoh); return NULL; } @@ -863,7 +864,7 @@ static inline int tso_get_fragment(struct tso_state *st, struct efx_nic *efx, st->ifc.unmap_addr = pci_map_page(efx->pci_dev, page, page_off, len, PCI_DMA_TODEVICE); - if (likely(!pci_dma_mapping_error(st->ifc.unmap_addr))) { + if (likely(!pci_dma_mapping_error(efx->pci_dev, st->ifc.unmap_addr))) { st->ifc.unmap_len = len; st->ifc.len = len; st->ifc.dma_addr = st->ifc.unmap_addr; diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c index a4bc812aa999..c69ba1395fa9 100644 --- a/drivers/net/sh_eth.c +++ b/drivers/net/sh_eth.c @@ -642,17 +642,12 @@ static void sh_eth_adjust_link(struct net_device *ndev) | ECMR_DM, ioaddr + ECMR); new_state = 1; mdp->link = phydev->link; - netif_tx_schedule_all(ndev); - netif_carrier_on(ndev); - netif_start_queue(ndev); } } else if (mdp->link) { new_state = 1; mdp->link = PHY_DOWN; mdp->speed = 0; mdp->duplex = -1; - netif_stop_queue(ndev); - netif_carrier_off(ndev); } if (new_state) diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 711e4a8948e0..5257cf464f1a 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1829,9 +1829,6 @@ static int sky2_down(struct net_device *dev) if (netif_msg_ifdown(sky2)) printk(KERN_INFO PFX "%s: disabling interface\n", dev->name); - /* Stop more packets from being queued */ - netif_stop_queue(dev); - /* Disable port IRQ */ imask = sky2_read32(hw, B0_IMSK); imask &= ~portirq_msk[port]; @@ -1887,8 +1884,6 @@ static int sky2_down(struct net_device *dev) sky2_phy_power_down(hw, port); - netif_carrier_off(dev); - /* turn off LED's */ sky2_write16(hw, B0_Y2LED, LED_STAT_OFF); diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index f2051b209da2..2040965d7724 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -308,7 +308,7 @@ static void smc_reset(struct net_device *dev) * can't handle it then there will be no recovery except for * a hard reset or power cycle */ - if (nowait) + if (lp->cfg.flags & SMC91X_NOWAIT) cfg |= CONFIG_NO_WAIT; /* @@ -1939,8 +1939,11 @@ static int __init smc_probe(struct net_device *dev, void __iomem *ioaddr, if (retval) goto err_out; -#ifdef SMC_USE_PXA_DMA - { +#ifdef CONFIG_ARCH_PXA +# ifdef SMC_USE_PXA_DMA + lp->cfg.flags |= SMC91X_USE_DMA; +# endif + if (lp->cfg.flags & SMC91X_USE_DMA) { int dma = pxa_request_dma(dev->name, DMA_PRIO_LOW, smc_pxa_dma_irq, NULL); if (dma >= 0) @@ -1980,7 +1983,7 @@ static int __init smc_probe(struct net_device *dev, void __iomem *ioaddr, } err_out: -#ifdef SMC_USE_PXA_DMA +#ifdef CONFIG_ARCH_PXA if (retval && dev->dma != (unsigned char)-1) pxa_free_dma(dev->dma); #endif @@ -2050,9 +2053,11 @@ static int smc_enable_device(struct platform_device *pdev) return 0; } -static int smc_request_attrib(struct platform_device *pdev) +static int smc_request_attrib(struct platform_device *pdev, + struct net_device *ndev) { struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib"); + struct smc_local *lp = netdev_priv(ndev); if (!res) return 0; @@ -2063,9 +2068,11 @@ static int smc_request_attrib(struct platform_device *pdev) return 0; } -static void smc_release_attrib(struct platform_device *pdev) +static void smc_release_attrib(struct platform_device *pdev, + struct net_device *ndev) { struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib"); + struct smc_local *lp = netdev_priv(ndev); if (res) release_mem_region(res->start, ATTRIB_SIZE); @@ -2123,27 +2130,14 @@ static int smc_drv_probe(struct platform_device *pdev) struct net_device *ndev; struct resource *res, *ires; unsigned int __iomem *addr; + unsigned long irq_flags = SMC_IRQ_FLAGS; int ret; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs"); - if (!res) - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENODEV; - goto out; - } - - - if (!request_mem_region(res->start, SMC_IO_EXTENT, CARDNAME)) { - ret = -EBUSY; - goto out; - } - ndev = alloc_etherdev(sizeof(struct smc_local)); if (!ndev) { printk("%s: could not allocate device.\n", CARDNAME); ret = -ENOMEM; - goto out_release_io; + goto out; } SET_NETDEV_DEV(ndev, &pdev->dev); @@ -2152,37 +2146,47 @@ static int smc_drv_probe(struct platform_device *pdev) */ lp = netdev_priv(ndev); - lp->cfg.irq_flags = SMC_IRQ_FLAGS; -#ifdef SMC_DYNAMIC_BUS_CONFIG - if (pd) + if (pd) { memcpy(&lp->cfg, pd, sizeof(lp->cfg)); - else { - lp->cfg.flags = SMC91X_USE_8BIT; - lp->cfg.flags |= SMC91X_USE_16BIT; - lp->cfg.flags |= SMC91X_USE_32BIT; + lp->io_shift = SMC91X_IO_SHIFT(lp->cfg.flags); + } else { + lp->cfg.flags |= (SMC_CAN_USE_8BIT) ? SMC91X_USE_8BIT : 0; + lp->cfg.flags |= (SMC_CAN_USE_16BIT) ? SMC91X_USE_16BIT : 0; + lp->cfg.flags |= (SMC_CAN_USE_32BIT) ? SMC91X_USE_32BIT : 0; + lp->cfg.flags |= (nowait) ? SMC91X_NOWAIT : 0; } - lp->cfg.flags &= ~(SMC_CAN_USE_8BIT ? 0 : SMC91X_USE_8BIT); - lp->cfg.flags &= ~(SMC_CAN_USE_16BIT ? 0 : SMC91X_USE_16BIT); - lp->cfg.flags &= ~(SMC_CAN_USE_32BIT ? 0 : SMC91X_USE_32BIT); -#endif - ndev->dma = (unsigned char)-1; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs"); + if (!res) + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + goto out_free_netdev; + } + + + if (!request_mem_region(res->start, SMC_IO_EXTENT, CARDNAME)) { + ret = -EBUSY; + goto out_free_netdev; + } + ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!ires) { ret = -ENODEV; - goto out_free_netdev; + goto out_release_io; } ndev->irq = ires->start; - if (SMC_IRQ_FLAGS == -1) - lp->cfg.irq_flags = ires->flags & IRQF_TRIGGER_MASK; - ret = smc_request_attrib(pdev); + if (ires->flags & IRQF_TRIGGER_MASK) + irq_flags = ires->flags & IRQF_TRIGGER_MASK; + + ret = smc_request_attrib(pdev, ndev); if (ret) - goto out_free_netdev; + goto out_release_io; #if defined(CONFIG_SA1100_ASSABET) NCR_0 |= NCR_ENET_OSC_EN; #endif @@ -2197,7 +2201,7 @@ static int smc_drv_probe(struct platform_device *pdev) goto out_release_attrib; } -#ifdef SMC_USE_PXA_DMA +#ifdef CONFIG_ARCH_PXA { struct smc_local *lp = netdev_priv(ndev); lp->device = &pdev->dev; @@ -2205,7 +2209,7 @@ static int smc_drv_probe(struct platform_device *pdev) } #endif - ret = smc_probe(ndev, addr, lp->cfg.irq_flags); + ret = smc_probe(ndev, addr, irq_flags); if (ret != 0) goto out_iounmap; @@ -2217,11 +2221,11 @@ static int smc_drv_probe(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); iounmap(addr); out_release_attrib: - smc_release_attrib(pdev); - out_free_netdev: - free_netdev(ndev); + smc_release_attrib(pdev, ndev); out_release_io: release_mem_region(res->start, SMC_IO_EXTENT); + out_free_netdev: + free_netdev(ndev); out: printk("%s: not found (%d).\n", CARDNAME, ret); @@ -2240,14 +2244,14 @@ static int smc_drv_remove(struct platform_device *pdev) free_irq(ndev->irq, ndev); -#ifdef SMC_USE_PXA_DMA +#ifdef CONFIG_ARCH_PXA if (ndev->dma != (unsigned char)-1) pxa_free_dma(ndev->dma); #endif iounmap(lp->base); smc_release_datacs(pdev,ndev); - smc_release_attrib(pdev); + smc_release_attrib(pdev,ndev); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs"); if (!res) diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h index 8606818653f8..22209b6f1405 100644 --- a/drivers/net/smc91x.h +++ b/drivers/net/smc91x.h @@ -40,23 +40,46 @@ * Define your architecture specific bus configuration parameters here. */ -#if defined(CONFIG_ARCH_LUBBOCK) +#if defined(CONFIG_ARCH_LUBBOCK) ||\ + defined(CONFIG_MACH_MAINSTONE) ||\ + defined(CONFIG_MACH_ZYLONITE) ||\ + defined(CONFIG_MACH_LITTLETON) -/* We can only do 16-bit reads and writes in the static memory space. */ -#define SMC_CAN_USE_8BIT 0 +#include <asm/mach-types.h> + +/* Now the bus width is specified in the platform data + * pretend here to support all I/O access types + */ +#define SMC_CAN_USE_8BIT 1 #define SMC_CAN_USE_16BIT 1 -#define SMC_CAN_USE_32BIT 0 +#define SMC_CAN_USE_32BIT 1 #define SMC_NOWAIT 1 -/* The first two address lines aren't connected... */ -#define SMC_IO_SHIFT 2 +#define SMC_IO_SHIFT (lp->io_shift) +#define SMC_inb(a, r) readb((a) + (r)) #define SMC_inw(a, r) readw((a) + (r)) -#define SMC_outw(v, a, r) writew(v, (a) + (r)) +#define SMC_inl(a, r) readl((a) + (r)) +#define SMC_outb(v, a, r) writeb(v, (a) + (r)) +#define SMC_outl(v, a, r) writel(v, (a) + (r)) #define SMC_insw(a, r, p, l) readsw((a) + (r), p, l) #define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l) +#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l) +#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l) #define SMC_IRQ_FLAGS (-1) /* from resource */ +/* We actually can't write halfwords properly if not word aligned */ +static inline void SMC_outw(u16 val, void __iomem *ioaddr, int reg) +{ + if (machine_is_mainstone() && reg & 2) { + unsigned int v = val << 16; + v |= readl(ioaddr + (reg & ~2)) & 0xffff; + writel(v, ioaddr + (reg & ~2)); + } else { + writew(val, ioaddr + reg); + } +} + #elif defined(CONFIG_BLACKFIN) #define SMC_IRQ_FLAGS IRQF_TRIGGER_HIGH @@ -195,7 +218,6 @@ #define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l) #elif defined(CONFIG_ARCH_INNOKOM) || \ - defined(CONFIG_MACH_MAINSTONE) || \ defined(CONFIG_ARCH_PXA_IDP) || \ defined(CONFIG_ARCH_RAMSES) || \ defined(CONFIG_ARCH_PCM027) @@ -229,22 +251,6 @@ SMC_outw(u16 val, void __iomem *ioaddr, int reg) } } -#elif defined(CONFIG_MACH_ZYLONITE) - -#define SMC_CAN_USE_8BIT 1 -#define SMC_CAN_USE_16BIT 1 -#define SMC_CAN_USE_32BIT 0 -#define SMC_IO_SHIFT 0 -#define SMC_NOWAIT 1 -#define SMC_USE_PXA_DMA 1 -#define SMC_inb(a, r) readb((a) + (r)) -#define SMC_inw(a, r) readw((a) + (r)) -#define SMC_insw(a, r, p, l) insw((a) + (r), p, l) -#define SMC_outsw(a, r, p, l) outsw((a) + (r), p, l) -#define SMC_outb(v, a, r) writeb(v, (a) + (r)) -#define SMC_outw(v, a, r) writew(v, (a) + (r)) -#define SMC_IRQ_FLAGS (-1) /* from resource */ - #elif defined(CONFIG_ARCH_OMAP) /* We can only do 16-bit reads and writes in the static memory space. */ @@ -454,7 +460,6 @@ static inline void LPD7_SMC_outsw (unsigned char* a, int r, #define RPC_LSA_DEFAULT RPC_LED_100_10 #define RPC_LSB_DEFAULT RPC_LED_TX_RX -#define SMC_DYNAMIC_BUS_CONFIG #endif @@ -493,7 +498,7 @@ struct smc_local { spinlock_t lock; -#ifdef SMC_USE_PXA_DMA +#ifdef CONFIG_ARCH_PXA /* DMA needs the physical address of the chip */ u_long physaddr; struct device *device; @@ -501,20 +506,17 @@ struct smc_local { void __iomem *base; void __iomem *datacs; + /* the low address lines on some platforms aren't connected... */ + int io_shift; + struct smc91x_platdata cfg; }; -#ifdef SMC_DYNAMIC_BUS_CONFIG -#define SMC_8BIT(p) (((p)->cfg.flags & SMC91X_USE_8BIT) && SMC_CAN_USE_8BIT) -#define SMC_16BIT(p) (((p)->cfg.flags & SMC91X_USE_16BIT) && SMC_CAN_USE_16BIT) -#define SMC_32BIT(p) (((p)->cfg.flags & SMC91X_USE_32BIT) && SMC_CAN_USE_32BIT) -#else -#define SMC_8BIT(p) SMC_CAN_USE_8BIT -#define SMC_16BIT(p) SMC_CAN_USE_16BIT -#define SMC_32BIT(p) SMC_CAN_USE_32BIT -#endif +#define SMC_8BIT(p) ((p)->cfg.flags & SMC91X_USE_8BIT) +#define SMC_16BIT(p) ((p)->cfg.flags & SMC91X_USE_16BIT) +#define SMC_32BIT(p) ((p)->cfg.flags & SMC91X_USE_32BIT) -#ifdef SMC_USE_PXA_DMA +#ifdef CONFIG_ARCH_PXA /* * Let's use the DMA engine on the XScale PXA2xx for RX packets. This is * always happening in irq context so no need to worry about races. TX is @@ -608,7 +610,7 @@ smc_pxa_dma_irq(int dma, void *dummy) { DCSR(dma) = 0; } -#endif /* SMC_USE_PXA_DMA */ +#endif /* CONFIG_ARCH_PXA */ /* diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c index 00aa0b108cb9..b6435d0d71f9 100644 --- a/drivers/net/spider_net.c +++ b/drivers/net/spider_net.c @@ -452,7 +452,7 @@ 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); - if (pci_dma_mapping_error(buf)) { + if (pci_dma_mapping_error(card->pdev, buf)) { dev_kfree_skb_any(descr->skb); descr->skb = NULL; if (netif_msg_rx_err(card) && net_ratelimit()) @@ -691,7 +691,7 @@ spider_net_prepare_tx_descr(struct spider_net_card *card, unsigned long flags; buf = pci_map_single(card->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(buf)) { + if (pci_dma_mapping_error(card->pdev, buf)) { if (netif_msg_tx_err(card) && net_ratelimit()) dev_err(&card->netdev->dev, "could not iommu-map packet (%p, %i). " "Dropping packet\n", skb->data, skb->len); diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c index 1aa425be3067..b79d5f018f79 100644 --- a/drivers/net/sunhme.c +++ b/drivers/net/sunhme.c @@ -2377,8 +2377,6 @@ static void happy_meal_set_multicast(struct net_device *dev) spin_lock_irq(&hp->happy_lock); - netif_stop_queue(dev); - if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 64)) { hme_write32(hp, bregs + BMAC_HTABLE0, 0xffff); hme_write32(hp, bregs + BMAC_HTABLE1, 0xffff); @@ -2410,8 +2408,6 @@ static void happy_meal_set_multicast(struct net_device *dev) hme_write32(hp, bregs + BMAC_HTABLE3, hash_table[3]); } - netif_wake_queue(dev); - spin_unlock_irq(&hp->happy_lock); } diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c index 41d3ac45685f..8487ace9d2e3 100644 --- a/drivers/net/tc35815.c +++ b/drivers/net/tc35815.c @@ -506,7 +506,7 @@ static void *alloc_rxbuf_page(struct pci_dev *hwdev, dma_addr_t *dma_handle) return NULL; *dma_handle = pci_map_single(hwdev, buf, PAGE_SIZE, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(*dma_handle)) { + if (pci_dma_mapping_error(hwdev, *dma_handle)) { free_page((unsigned long)buf); return NULL; } @@ -536,7 +536,7 @@ static struct sk_buff *alloc_rxbuf_skb(struct net_device *dev, return NULL; *dma_handle = pci_map_single(hwdev, skb->data, RX_BUF_SIZE, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(*dma_handle)) { + if (pci_dma_mapping_error(hwdev, *dma_handle)) { dev_kfree_skb_any(skb); return NULL; } @@ -672,7 +672,6 @@ static void tc_handle_link_change(struct net_device *dev) if (dev->flags & IFF_PROMISC) tc35815_set_multicast_list(dev); #endif - netif_tx_schedule_all(dev); } else { lp->speed = 0; lp->duplex = -1; diff --git a/drivers/net/tulip/de4x5.c b/drivers/net/tulip/de4x5.c index bc30c6e8fea2..617ef41bdfea 100644 --- a/drivers/net/tulip/de4x5.c +++ b/drivers/net/tulip/de4x5.c @@ -5514,22 +5514,6 @@ de4x5_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) netif_wake_queue(dev); /* Unlock the TX ring */ break; - case DE4X5_SET_PROM: /* Set Promiscuous Mode */ - if (!capable(CAP_NET_ADMIN)) return -EPERM; - omr = inl(DE4X5_OMR); - omr |= OMR_PR; - outl(omr, DE4X5_OMR); - dev->flags |= IFF_PROMISC; - break; - - case DE4X5_CLR_PROM: /* Clear Promiscuous Mode */ - if (!capable(CAP_NET_ADMIN)) return -EPERM; - omr = inl(DE4X5_OMR); - omr &= ~OMR_PR; - outl(omr, DE4X5_OMR); - dev->flags &= ~IFF_PROMISC; - break; - case DE4X5_SAY_BOO: /* Say "Boo!" to the kernel log file */ if (!capable(CAP_NET_ADMIN)) return -EPERM; printk("%s: Boo!\n", dev->name); diff --git a/drivers/net/tulip/de4x5.h b/drivers/net/tulip/de4x5.h index f5f33b3eb067..9f2877438fb0 100644 --- a/drivers/net/tulip/de4x5.h +++ b/drivers/net/tulip/de4x5.h @@ -1004,8 +1004,7 @@ struct de4x5_ioctl { */ #define DE4X5_GET_HWADDR 0x01 /* Get the hardware address */ #define DE4X5_SET_HWADDR 0x02 /* Set the hardware address */ -#define DE4X5_SET_PROM 0x03 /* Set Promiscuous Mode */ -#define DE4X5_CLR_PROM 0x04 /* Clear Promiscuous Mode */ +/* 0x03 and 0x04 were used before and are obsoleted now. Don't use them. */ #define DE4X5_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */ #define DE4X5_GET_MCA 0x06 /* Get a multicast address */ #define DE4X5_SET_MCA 0x07 /* Set a multicast address */ diff --git a/drivers/net/tun.c b/drivers/net/tun.c index a82b32b40131..e6bbc639c2d0 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -900,7 +900,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, if ((tun->flags & TUN_TYPE_MASK) != TUN_TAP_DEV) return -EINVAL; rtnl_lock(); - ret = update_filter(&tun->txflt, (void *) __user arg); + ret = update_filter(&tun->txflt, (void __user *)arg); rtnl_unlock(); return ret; diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c index 756ba10b79d6..8f944e57fd55 100644 --- a/drivers/net/ucc_geth.c +++ b/drivers/net/ucc_geth.c @@ -1588,7 +1588,6 @@ static void adjust_link(struct net_device *dev) if (!ugeth->oldlink) { new_state = 1; ugeth->oldlink = 1; - netif_tx_schedule_all(dev); } } else if (ugeth->oldlink) { new_state = 1; diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index a934428a5890..0e061dfea78d 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -50,10 +50,18 @@ static int is_activesync(struct usb_interface_descriptor *desc) && desc->bInterfaceProtocol == 1; } +static int is_wireless_rndis(struct usb_interface_descriptor *desc) +{ + return desc->bInterfaceClass == USB_CLASS_WIRELESS_CONTROLLER + && desc->bInterfaceSubClass == 1 + && desc->bInterfaceProtocol == 3; +} + #else #define is_rndis(desc) 0 #define is_activesync(desc) 0 +#define is_wireless_rndis(desc) 0 #endif @@ -110,7 +118,8 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) * of cdc-acm, it'll fail RNDIS requests cleanly. */ rndis = is_rndis(&intf->cur_altsetting->desc) - || is_activesync(&intf->cur_altsetting->desc); + || is_activesync(&intf->cur_altsetting->desc) + || is_wireless_rndis(&intf->cur_altsetting->desc); memset(info, 0, sizeof *info); info->control = intf; diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c index 61c98beb4d17..bcd858c567e0 100644 --- a/drivers/net/usb/rndis_host.c +++ b/drivers/net/usb/rndis_host.c @@ -576,6 +576,10 @@ static const struct usb_device_id products [] = { /* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */ USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1), .driver_info = (unsigned long) &rndis_info, +}, { + /* RNDIS for tethering */ + USB_INTERFACE_INFO(USB_CLASS_WIRELESS_CONTROLLER, 1, 3), + .driver_info = (unsigned long) &rndis_info, }, { }, // END }; diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index c28d7cb2035b..0196a0df9021 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -19,6 +19,7 @@ //#define DEBUG #include <linux/netdevice.h> #include <linux/etherdevice.h> +#include <linux/ethtool.h> #include <linux/module.h> #include <linux/virtio.h> #include <linux/virtio_net.h> @@ -54,9 +55,15 @@ struct virtnet_info struct tasklet_struct tasklet; bool free_in_tasklet; + /* I like... big packets and I cannot lie! */ + bool big_packets; + /* Receive & send queues. */ struct sk_buff_head recv; struct sk_buff_head send; + + /* Chain pages by the private ptr. */ + struct page *pages; }; static inline struct virtio_net_hdr *skb_vnet_hdr(struct sk_buff *skb) @@ -69,6 +76,23 @@ static inline void vnet_hdr_to_sg(struct scatterlist *sg, struct sk_buff *skb) sg_init_one(sg, skb_vnet_hdr(skb), sizeof(struct virtio_net_hdr)); } +static void give_a_page(struct virtnet_info *vi, struct page *page) +{ + page->private = (unsigned long)vi->pages; + vi->pages = page; +} + +static struct page *get_a_page(struct virtnet_info *vi, gfp_t gfp_mask) +{ + struct page *p = vi->pages; + + if (p) + vi->pages = (struct page *)p->private; + else + p = alloc_page(gfp_mask); + return p; +} + static void skb_xmit_done(struct virtqueue *svq) { struct virtnet_info *vi = svq->vdev->priv; @@ -88,6 +112,7 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb, unsigned len) { struct virtio_net_hdr *hdr = skb_vnet_hdr(skb); + int err; if (unlikely(len < sizeof(struct virtio_net_hdr) + ETH_HLEN)) { pr_debug("%s: short packet %i\n", dev->name, len); @@ -95,10 +120,23 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb, goto drop; } len -= sizeof(struct virtio_net_hdr); - BUG_ON(len > MAX_PACKET_LEN); - skb_trim(skb, len); + if (len <= MAX_PACKET_LEN) { + unsigned int i; + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) + give_a_page(dev->priv, skb_shinfo(skb)->frags[i].page); + skb->data_len = 0; + skb_shinfo(skb)->nr_frags = 0; + } + + err = pskb_trim(skb, len); + if (err) { + pr_debug("%s: pskb_trim failed %i %d\n", dev->name, len, err); + dev->stats.rx_dropped++; + goto drop; + } + skb->truesize += skb->data_len; dev->stats.rx_bytes += skb->len; dev->stats.rx_packets++; @@ -160,7 +198,7 @@ static void try_fill_recv(struct virtnet_info *vi) { struct sk_buff *skb; struct scatterlist sg[2+MAX_SKB_FRAGS]; - int num, err; + int num, err, i; sg_init_table(sg, 2+MAX_SKB_FRAGS); for (;;) { @@ -170,6 +208,24 @@ static void try_fill_recv(struct virtnet_info *vi) skb_put(skb, MAX_PACKET_LEN); vnet_hdr_to_sg(sg, skb); + + if (vi->big_packets) { + for (i = 0; i < MAX_SKB_FRAGS; i++) { + skb_frag_t *f = &skb_shinfo(skb)->frags[i]; + f->page = get_a_page(vi, GFP_ATOMIC); + if (!f->page) + break; + + f->page_offset = 0; + f->size = PAGE_SIZE; + + skb->data_len += PAGE_SIZE; + skb->len += PAGE_SIZE; + + skb_shinfo(skb)->nr_frags++; + } + } + num = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1; skb_queue_head(&vi->recv, skb); @@ -335,16 +391,11 @@ again: free_old_xmit_skbs(vi); /* If we has a buffer left over from last time, send it now. */ - if (unlikely(vi->last_xmit_skb)) { - if (xmit_skb(vi, vi->last_xmit_skb) != 0) { - /* Drop this skb: we only queue one. */ - vi->dev->stats.tx_dropped++; - kfree_skb(skb); - skb = NULL; - goto stop_queue; - } - vi->last_xmit_skb = NULL; - } + if (unlikely(vi->last_xmit_skb) && + xmit_skb(vi, vi->last_xmit_skb) != 0) + goto stop_queue; + + vi->last_xmit_skb = NULL; /* Put new one in send queue and do transmit */ if (likely(skb)) { @@ -370,6 +421,11 @@ stop_queue: netif_start_queue(dev); goto again; } + if (skb) { + /* Drop this skb: we only queue one. */ + vi->dev->stats.tx_dropped++; + kfree_skb(skb); + } goto done; } @@ -408,6 +464,22 @@ static int virtnet_close(struct net_device *dev) return 0; } +static int virtnet_set_tx_csum(struct net_device *dev, u32 data) +{ + struct virtnet_info *vi = netdev_priv(dev); + struct virtio_device *vdev = vi->vdev; + + if (data && !virtio_has_feature(vdev, VIRTIO_NET_F_CSUM)) + return -ENOSYS; + + return ethtool_op_set_tx_hw_csum(dev, data); +} + +static struct ethtool_ops virtnet_ethtool_ops = { + .set_tx_csum = virtnet_set_tx_csum, + .set_sg = ethtool_op_set_sg, +}; + static int virtnet_probe(struct virtio_device *vdev) { int err; @@ -427,6 +499,7 @@ static int virtnet_probe(struct virtio_device *vdev) #ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = virtnet_netpoll; #endif + SET_ETHTOOL_OPS(dev, &virtnet_ethtool_ops); SET_NETDEV_DEV(dev, &vdev->dev); /* Do we support "hardware" checksums? */ @@ -462,11 +535,18 @@ static int virtnet_probe(struct virtio_device *vdev) vi->dev = dev; vi->vdev = vdev; vdev->priv = vi; + vi->pages = NULL; /* If they give us a callback when all buffers are done, we don't need * the timer. */ vi->free_in_tasklet = virtio_has_feature(vdev,VIRTIO_F_NOTIFY_ON_EMPTY); + /* If we can receive ANY GSO packets, we must allocate large ones. */ + if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO4) + || virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO6) + || virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ECN)) + vi->big_packets = true; + /* We expect two virtqueues, receive then send. */ vi->rvq = vdev->config->find_vq(vdev, 0, skb_recv_done); if (IS_ERR(vi->rvq)) { @@ -541,6 +621,10 @@ static void virtnet_remove(struct virtio_device *vdev) vdev->config->del_vq(vi->svq); vdev->config->del_vq(vi->rvq); unregister_netdev(vi->dev); + + while (vi->pages) + __free_pages(get_a_page(vi, GFP_KERNEL), 0); + free_netdev(vi->dev); } @@ -553,7 +637,9 @@ static unsigned int features[] = { VIRTIO_NET_F_CSUM, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GSO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_HOST_TSO6, - VIRTIO_NET_F_HOST_ECN, VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, + VIRTIO_NET_F_GUEST_ECN, /* We don't yet handle UFO input. */ + VIRTIO_F_NOTIFY_ON_EMPTY, }; static struct virtio_driver virtio_net = { diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c index 5827324e9d9f..f7d3349dc3ec 100644 --- a/drivers/net/wan/cosa.c +++ b/drivers/net/wan/cosa.c @@ -397,9 +397,9 @@ static int __init cosa_init(void) err = PTR_ERR(cosa_class); goto out_chrdev; } - for (i=0; i<nr_cards; i++) { - device_create(cosa_class, NULL, MKDEV(cosa_major, i), "cosa%d", i); - } + for (i = 0; i < nr_cards; i++) + device_create_drvdata(cosa_class, NULL, MKDEV(cosa_major, i), + NULL, "cosa%d", i); err = 0; goto out; diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 217d506527a9..d9769c527346 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -1166,7 +1166,7 @@ ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) bf->skb = skb; bf->skbaddr = pci_map_single(sc->pdev, skb->data, sc->rxbufsize, PCI_DMA_FROMDEVICE); - if (unlikely(pci_dma_mapping_error(bf->skbaddr))) { + if (unlikely(pci_dma_mapping_error(sc->pdev, bf->skbaddr))) { ATH5K_ERR(sc, "%s: DMA mapping failed\n", __func__); dev_kfree_skb(skb); bf->skb = NULL; @@ -1918,7 +1918,7 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "skb %p [data %p len %u] " "skbaddr %llx\n", skb, skb->data, skb->len, (unsigned long long)bf->skbaddr); - if (pci_dma_mapping_error(bf->skbaddr)) { + if (pci_dma_mapping_error(sc->pdev, bf->skbaddr)) { ATH5K_ERR(sc, "beacon DMA mapping failed\n"); return -EIO; } diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c index 13d5882f1f21..3153fe9d7ce0 100644 --- a/drivers/net/wireless/hostap/hostap_hw.c +++ b/drivers/net/wireless/hostap/hostap_hw.c @@ -3101,6 +3101,7 @@ static void prism2_clear_set_tim_queue(local_info_t *local) * This is a natural nesting, which needs a split lock type. */ static struct lock_class_key hostap_netdev_xmit_lock_key; +static struct lock_class_key hostap_netdev_addr_lock_key; static void prism2_set_lockdep_class_one(struct net_device *dev, struct netdev_queue *txq, @@ -3112,6 +3113,8 @@ static void prism2_set_lockdep_class_one(struct net_device *dev, static void prism2_set_lockdep_class(struct net_device *dev) { + lockdep_set_class(&dev->addr_list_lock, + &hostap_netdev_addr_lock_key); netdev_for_each_tx_queue(dev, prism2_set_lockdep_class_one, NULL); } diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c index 6e704608947c..1acfbcd3703c 100644 --- a/drivers/net/wireless/ipw2200.c +++ b/drivers/net/wireless/ipw2200.c @@ -4972,8 +4972,7 @@ static int ipw_queue_tx_reclaim(struct ipw_priv *priv, } done: if ((ipw_tx_queue_space(q) > q->low_mark) && - (qindex >= 0) && - (priv->status & STATUS_ASSOCIATED) && netif_running(priv->net_dev)) + (qindex >= 0)) netif_wake_queue(priv->net_dev); used = q->first_empty - q->last_used; if (used < 0) @@ -10154,14 +10153,8 @@ static void init_sys_config(struct ipw_sys_config *sys_config) static int ipw_net_open(struct net_device *dev) { - struct ipw_priv *priv = ieee80211_priv(dev); IPW_DEBUG_INFO("dev->open\n"); - /* we should be verifying the device is ready to be opened */ - mutex_lock(&priv->mutex); - if (!(priv->status & STATUS_RF_KILL_MASK) && - (priv->status & STATUS_ASSOCIATED)) - netif_start_queue(dev); - mutex_unlock(&priv->mutex); + netif_start_queue(dev); return 0; } @@ -10481,13 +10474,6 @@ static int ipw_net_hard_start_xmit(struct ieee80211_txb *txb, IPW_DEBUG_TX("dev->xmit(%d bytes)\n", txb->payload_size); spin_lock_irqsave(&priv->lock, flags); - if (!(priv->status & STATUS_ASSOCIATED)) { - IPW_DEBUG_INFO("Tx attempt while not associated.\n"); - priv->ieee->stats.tx_carrier_errors++; - netif_stop_queue(dev); - goto fail_unlock; - } - #ifdef CONFIG_IPW2200_PROMISCUOUS if (rtap_iface && netif_running(priv->prom_net_dev)) ipw_handle_promiscuous_tx(priv, txb); @@ -10499,10 +10485,6 @@ static int ipw_net_hard_start_xmit(struct ieee80211_txb *txb, spin_unlock_irqrestore(&priv->lock, flags); return ret; - - fail_unlock: - spin_unlock_irqrestore(&priv->lock, flags); - return 1; } static struct net_device_stats *ipw_net_get_stats(struct net_device *dev) @@ -10703,13 +10685,6 @@ static void ipw_link_up(struct ipw_priv *priv) priv->last_packet_time = 0; netif_carrier_on(priv->net_dev); - if (netif_queue_stopped(priv->net_dev)) { - IPW_DEBUG_NOTIF("waking queue\n"); - netif_wake_queue(priv->net_dev); - } else { - IPW_DEBUG_NOTIF("starting queue\n"); - netif_start_queue(priv->net_dev); - } cancel_delayed_work(&priv->request_scan); cancel_delayed_work(&priv->request_direct_scan); @@ -10739,7 +10714,6 @@ static void ipw_link_down(struct ipw_priv *priv) { ipw_led_link_down(priv); netif_carrier_off(priv->net_dev); - netif_stop_queue(priv->net_dev); notify_wx_assoc_event(priv); /* Cancel any queued work ... */ @@ -11419,7 +11393,6 @@ static void ipw_down(struct ipw_priv *priv) /* Clear all bits but the RF Kill */ priv->status &= STATUS_RF_KILL_MASK | STATUS_EXIT_PENDING; netif_carrier_off(priv->net_dev); - netif_stop_queue(priv->net_dev); ipw_stop_nic(priv); @@ -11522,7 +11495,6 @@ static int ipw_prom_open(struct net_device *dev) IPW_DEBUG_INFO("prom dev->open\n"); netif_carrier_off(dev); - netif_stop_queue(dev); if (priv->ieee->iw_mode != IW_MODE_MONITOR) { priv->sys_config.accept_all_data_frames = 1; @@ -11558,7 +11530,6 @@ static int ipw_prom_stop(struct net_device *dev) static int ipw_prom_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { IPW_DEBUG_INFO("prom dev->xmit\n"); - netif_stop_queue(dev); return -EOPNOTSUPP; } diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 913dc9fe08f9..5816230d58f8 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -364,8 +364,7 @@ static void mac80211_hwsim_free(void) struct mac80211_hwsim_data *data; data = hwsim_radios[i]->priv; ieee80211_unregister_hw(hwsim_radios[i]); - if (!IS_ERR(data->dev)) - device_unregister(data->dev); + device_unregister(data->dev); ieee80211_free_hw(hwsim_radios[i]); } } @@ -437,7 +436,7 @@ static int __init init_mac80211_hwsim(void) "mac80211_hwsim: device_create_drvdata " "failed (%ld)\n", PTR_ERR(data->dev)); err = -ENOMEM; - goto failed; + goto failed_drvdata; } data->dev->driver = &mac80211_hwsim_driver; @@ -461,7 +460,7 @@ static int __init init_mac80211_hwsim(void) if (err < 0) { printk(KERN_DEBUG "mac80211_hwsim: " "ieee80211_register_hw failed (%d)\n", err); - goto failed; + goto failed_hw; } printk(KERN_DEBUG "%s: hwaddr %s registered\n", @@ -479,9 +478,9 @@ static int __init init_mac80211_hwsim(void) rtnl_lock(); err = dev_alloc_name(hwsim_mon, hwsim_mon->name); - if (err < 0) { + if (err < 0) goto failed_mon; - } + err = register_netdevice(hwsim_mon); if (err < 0) @@ -494,7 +493,14 @@ static int __init init_mac80211_hwsim(void) failed_mon: rtnl_unlock(); free_netdev(hwsim_mon); + mac80211_hwsim_free(); + return err; +failed_hw: + device_unregister(data->dev); +failed_drvdata: + ieee80211_free_hw(hw); + hwsim_radios[i] = 0; failed: mac80211_hwsim_free(); return err; diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index ef671d1a3bf0..902bbe788215 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -92,7 +92,7 @@ struct netfront_info { */ union skb_entry { struct sk_buff *skb; - unsigned link; + unsigned long link; } tx_skbs[NET_TX_RING_SIZE]; grant_ref_t gref_tx_head; grant_ref_t grant_tx_ref[NET_TX_RING_SIZE]; @@ -125,6 +125,17 @@ struct netfront_rx_info { struct xen_netif_extra_info extras[XEN_NETIF_EXTRA_TYPE_MAX - 1]; }; +static void skb_entry_set_link(union skb_entry *list, unsigned short id) +{ + list->link = id; +} + +static int skb_entry_is_link(const union skb_entry *list) +{ + BUILD_BUG_ON(sizeof(list->skb) != sizeof(list->link)); + return ((unsigned long)list->skb < PAGE_OFFSET); +} + /* * Access macros for acquiring freeing slots in tx_skbs[]. */ @@ -132,7 +143,7 @@ struct netfront_rx_info { static void add_id_to_freelist(unsigned *head, union skb_entry *list, unsigned short id) { - list[id].link = *head; + skb_entry_set_link(&list[id], *head); *head = id; } @@ -993,7 +1004,7 @@ static void xennet_release_tx_bufs(struct netfront_info *np) for (i = 0; i < NET_TX_RING_SIZE; i++) { /* Skip over entries which are actually freelist references */ - if ((unsigned long)np->tx_skbs[i].skb < PAGE_OFFSET) + if (skb_entry_is_link(&np->tx_skbs[i])) continue; skb = np->tx_skbs[i].skb; @@ -1123,7 +1134,7 @@ static struct net_device * __devinit xennet_create_dev(struct xenbus_device *dev /* Initialise tx_skbs as a free chain containing every entry. */ np->tx_skb_freelist = 0; for (i = 0; i < NET_TX_RING_SIZE; i++) { - np->tx_skbs[i].link = i+1; + skb_entry_set_link(&np->tx_skbs[i], i+1); np->grant_tx_ref[i] = GRANT_INVALID_REF; } diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 3a7a11a75fb4..f821dbc952a4 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -4,7 +4,7 @@ config OF_DEVICE config OF_GPIO def_bool y - depends on OF && PPC_OF && HAVE_GPIO_LIB + depends on OF && PPC_OF && GPIOLIB help OpenFirmware GPIO accessors @@ -13,3 +13,9 @@ config OF_I2C depends on PPC_OF && I2C help OpenFirmware I2C accessors + +config OF_SPI + def_tristate SPI + depends on OF && PPC_OF && SPI + help + OpenFirmware SPI accessors diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 548772e871fd..4c3c6f8e36f5 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -2,3 +2,4 @@ obj-y = base.o obj-$(CONFIG_OF_DEVICE) += device.o platform.o obj-$(CONFIG_OF_GPIO) += gpio.o obj-$(CONFIG_OF_I2C) += of_i2c.o +obj-$(CONFIG_OF_SPI) += of_spi.o diff --git a/drivers/of/base.c b/drivers/of/base.c index 23ffb7c0caf2..ad8ac1a8af28 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -385,3 +385,91 @@ struct device_node *of_find_matching_node(struct device_node *from, return np; } EXPORT_SYMBOL(of_find_matching_node); + +/** + * of_modalias_table: Table of explicit compatible ==> modalias mappings + * + * This table allows particulare compatible property values to be mapped + * to modalias strings. This is useful for busses which do not directly + * understand the OF device tree but are populated based on data contained + * within the device tree. SPI and I2C are the two current users of this + * table. + * + * In most cases, devices do not need to be listed in this table because + * the modalias value can be derived directly from the compatible table. + * However, if for any reason a value cannot be derived, then this table + * provides a method to override the implicit derivation. + * + * At the moment, a single table is used for all bus types because it is + * assumed that the data size is small and that the compatible values + * should already be distinct enough to differentiate between SPI, I2C + * and other devices. + */ +struct of_modalias_table { + char *of_device; + char *modalias; +}; +static struct of_modalias_table of_modalias_table[] = { + /* Empty for now; add entries as needed */ +}; + +/** + * of_modalias_node - Lookup appropriate modalias for a device node + * @node: pointer to a device tree node + * @modalias: Pointer to buffer that modalias value will be copied into + * @len: Length of modalias value + * + * Based on the value of the compatible property, this routine will determine + * an appropriate modalias value for a particular device tree node. Three + * separate methods are used to derive a modalias value. + * + * First method is to lookup the compatible value in of_modalias_table. + * Second is to look for a "linux,<modalias>" entry in the compatible list + * and used that for modalias. Third is to strip off the manufacturer + * prefix from the first compatible entry and use the remainder as modalias + * + * This routine returns 0 on success + */ +int of_modalias_node(struct device_node *node, char *modalias, int len) +{ + int i, cplen; + const char *compatible; + const char *p; + + /* 1. search for exception list entry */ + for (i = 0; i < ARRAY_SIZE(of_modalias_table); i++) { + compatible = of_modalias_table[i].of_device; + if (!of_device_is_compatible(node, compatible)) + continue; + strlcpy(modalias, of_modalias_table[i].modalias, len); + return 0; + } + + compatible = of_get_property(node, "compatible", &cplen); + if (!compatible) + return -ENODEV; + + /* 2. search for linux,<modalias> entry */ + p = compatible; + while (cplen > 0) { + if (!strncmp(p, "linux,", 6)) { + p += 6; + strlcpy(modalias, p, len); + return 0; + } + + i = strlen(p) + 1; + p += i; + cplen -= i; + } + + /* 3. take first compatible entry and strip manufacturer */ + p = strchr(compatible, ','); + if (!p) + return -ENODEV; + p++; + strlcpy(modalias, p, len); + return 0; +} +EXPORT_SYMBOL_GPL(of_modalias_node); + diff --git a/drivers/of/of_i2c.c b/drivers/of/of_i2c.c index 5c015d310d4a..6a98dc8aa30b 100644 --- a/drivers/of/of_i2c.c +++ b/drivers/of/of_i2c.c @@ -16,62 +16,6 @@ #include <linux/of_i2c.h> #include <linux/module.h> -struct i2c_driver_device { - char *of_device; - char *i2c_type; -}; - -static struct i2c_driver_device i2c_devices[] = { -}; - -static int of_find_i2c_driver(struct device_node *node, - struct i2c_board_info *info) -{ - int i, cplen; - const char *compatible; - const char *p; - - /* 1. search for exception list entry */ - for (i = 0; i < ARRAY_SIZE(i2c_devices); i++) { - if (!of_device_is_compatible(node, i2c_devices[i].of_device)) - continue; - if (strlcpy(info->type, i2c_devices[i].i2c_type, - I2C_NAME_SIZE) >= I2C_NAME_SIZE) - return -ENOMEM; - - return 0; - } - - compatible = of_get_property(node, "compatible", &cplen); - if (!compatible) - return -ENODEV; - - /* 2. search for linux,<i2c-type> entry */ - p = compatible; - while (cplen > 0) { - if (!strncmp(p, "linux,", 6)) { - p += 6; - if (strlcpy(info->type, p, - I2C_NAME_SIZE) >= I2C_NAME_SIZE) - return -ENOMEM; - return 0; - } - - i = strlen(p) + 1; - p += i; - cplen -= i; - } - - /* 3. take fist compatible entry and strip manufacturer */ - p = strchr(compatible, ','); - if (!p) - return -ENODEV; - p++; - if (strlcpy(info->type, p, I2C_NAME_SIZE) >= I2C_NAME_SIZE) - return -ENOMEM; - return 0; -} - void of_register_i2c_devices(struct i2c_adapter *adap, struct device_node *adap_node) { @@ -83,6 +27,9 @@ void of_register_i2c_devices(struct i2c_adapter *adap, const u32 *addr; int len; + if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) + continue; + addr = of_get_property(node, "reg", &len); if (!addr || len < sizeof(int) || *addr > (1 << 10) - 1) { printk(KERN_ERR @@ -91,13 +38,6 @@ void of_register_i2c_devices(struct i2c_adapter *adap, } info.irq = irq_of_parse_and_map(node, 0); - if (info.irq == NO_IRQ) - info.irq = -1; - - if (of_find_i2c_driver(node, &info) < 0) { - irq_dispose_mapping(info.irq); - continue; - } info.addr = *addr; diff --git a/drivers/of/of_spi.c b/drivers/of/of_spi.c new file mode 100644 index 000000000000..b01eec026f68 --- /dev/null +++ b/drivers/of/of_spi.c @@ -0,0 +1,93 @@ +/* + * SPI OF support routines + * Copyright (C) 2008 Secret Lab Technologies Ltd. + * + * Support routines for deriving SPI device attachments from the device + * tree. + */ + +#include <linux/of.h> +#include <linux/device.h> +#include <linux/spi/spi.h> +#include <linux/of_spi.h> + +/** + * of_register_spi_devices - Register child devices onto the SPI bus + * @master: Pointer to spi_master device + * @np: parent node of SPI device nodes + * + * Registers an spi_device for each child node of 'np' which has a 'reg' + * property. + */ +void of_register_spi_devices(struct spi_master *master, struct device_node *np) +{ + struct spi_device *spi; + struct device_node *nc; + const u32 *prop; + int rc; + int len; + + for_each_child_of_node(np, nc) { + /* Alloc an spi_device */ + spi = spi_alloc_device(master); + if (!spi) { + dev_err(&master->dev, "spi_device alloc error for %s\n", + nc->full_name); + spi_dev_put(spi); + continue; + } + + /* Select device driver */ + if (of_modalias_node(nc, spi->modalias, + sizeof(spi->modalias)) < 0) { + dev_err(&master->dev, "cannot find modalias for %s\n", + nc->full_name); + spi_dev_put(spi); + continue; + } + + /* Device address */ + prop = of_get_property(nc, "reg", &len); + if (!prop || len < sizeof(*prop)) { + dev_err(&master->dev, "%s has no 'reg' property\n", + nc->full_name); + spi_dev_put(spi); + continue; + } + spi->chip_select = *prop; + + /* Mode (clock phase/polarity/etc.) */ + if (of_find_property(nc, "spi-cpha", NULL)) + spi->mode |= SPI_CPHA; + if (of_find_property(nc, "spi-cpol", NULL)) + spi->mode |= SPI_CPOL; + + /* Device speed */ + prop = of_get_property(nc, "spi-max-frequency", &len); + if (!prop || len < sizeof(*prop)) { + dev_err(&master->dev, "%s has no 'spi-max-frequency' property\n", + nc->full_name); + spi_dev_put(spi); + continue; + } + spi->max_speed_hz = *prop; + + /* IRQ */ + spi->irq = irq_of_parse_and_map(nc, 0); + + /* Store a pointer to the node in the device structure */ + of_node_get(nc); + spi->dev.archdata.of_node = nc; + + /* Register the new device */ + request_module(spi->modalias); + rc = spi_add_device(spi); + if (rc) { + dev_err(&master->dev, "spi_device register error %s\n", + nc->full_name); + spi_dev_put(spi); + } + + } +} +EXPORT_SYMBOL(of_register_spi_devices); diff --git a/drivers/parport/ieee1284.c b/drivers/parport/ieee1284.c index 0338b0912674..e97059415ab4 100644 --- a/drivers/parport/ieee1284.c +++ b/drivers/parport/ieee1284.c @@ -199,8 +199,6 @@ int parport_wait_peripheral(struct parport *port, /* 40ms of slow polling. */ deadline = jiffies + msecs_to_jiffies(40); while (time_before (jiffies, deadline)) { - int ret; - if (signal_pending (current)) return -EINTR; diff --git a/drivers/parport/parport_ax88796.c b/drivers/parport/parport_ax88796.c index 4ec220b2eae7..6938d2e9f18f 100644 --- a/drivers/parport/parport_ax88796.c +++ b/drivers/parport/parport_ax88796.c @@ -406,6 +406,8 @@ static int parport_ax88796_resume(struct platform_device *dev) #define parport_ax88796_resume NULL #endif +MODULE_ALIAS("platform:ax88796-pp"); + static struct platform_driver axdrv = { .driver = { .name = "ax88796-pp", diff --git a/drivers/parport/parport_cs.c b/drivers/parport/parport_cs.c index 802a81d47367..00e1d9620f7c 100644 --- a/drivers/parport/parport_cs.c +++ b/drivers/parport/parport_cs.c @@ -235,7 +235,7 @@ failed: ======================================================================*/ -void parport_cs_release(struct pcmcia_device *link) +static void parport_cs_release(struct pcmcia_device *link) { parport_info_t *info = link->priv; diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index e0c2a4584ec6..8a846adf1dcf 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -2867,7 +2867,7 @@ static struct parport_pc_pci { * and 840 locks up if you write 1 to bit 2! */ /* oxsemi_952 */ { 1, { { 0, 1 }, } }, /* oxsemi_954 */ { 1, { { 0, -1 }, } }, - /* oxsemi_840 */ { 1, { { 0, -1 }, } }, + /* oxsemi_840 */ { 1, { { 0, 1 }, } }, /* aks_0100 */ { 1, { { 0, -1 }, } }, /* mobility_pp */ { 1, { { 0, 1 }, } }, /* netmos_9705 */ { 1, { { 0, -1 }, } }, /* untested */ diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c index d950fc34320a..554e11f9e1ce 100644 --- a/drivers/parport/procfs.c +++ b/drivers/parport/procfs.c @@ -429,9 +429,6 @@ struct parport_default_sysctl_table ctl_table dev_dir[2]; }; -extern unsigned long parport_default_timeslice; -extern int parport_default_spintime; - static struct parport_default_sysctl_table parport_default_sysctl_table = { .sysctl_header = NULL, diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index f941f609dbf3..8bf86ae2333f 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -317,10 +317,8 @@ int __init dmar_table_init(void) return -ENODEV; } - if (list_empty(&dmar_rmrr_units)) { + if (list_empty(&dmar_rmrr_units)) printk(KERN_INFO PREFIX "No RMRR found\n"); - return -ENODEV; - } return 0; } diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index eecf7cbf4139..5a58b075dd8d 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -36,7 +36,7 @@ #define _ACPIPHP_H #include <linux/acpi.h> -#include <linux/kobject.h> /* for KOBJ_NAME_LEN */ +#include <linux/kobject.h> #include <linux/mutex.h> #include <linux/pci_hotplug.h> @@ -51,7 +51,7 @@ #define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg) /* name size which is used for entries in pcihpfs */ -#define SLOT_NAME_SIZE KOBJ_NAME_LEN /* {_SUN} */ +#define SLOT_NAME_SIZE 20 /* {_SUN} */ struct acpiphp_bridge; struct acpiphp_slot; diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 1323a43285d7..ad27e9e225a6 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -1103,7 +1103,7 @@ static inline void dbg_ctrl(struct controller *ctrl) dbg(" Power Indicator : %3s\n", PWR_LED(ctrl) ? "yes" : "no"); dbg(" Hot-Plug Surprise : %3s\n", HP_SUPR_RM(ctrl) ? "yes" : "no"); dbg(" EMI Present : %3s\n", EMI(ctrl) ? "yes" : "no"); - dbg(" Comamnd Completed : %3s\n", NO_CMD_CMPL(ctrl)? "no" : "yes"); + dbg(" Command Completed : %3s\n", NO_CMD_CMPL(ctrl)? "no" : "yes"); pciehp_readw(ctrl, SLOTSTATUS, ®16); dbg("Slot Status : 0x%04x\n", reg16); pciehp_readw(ctrl, SLOTCTRL, ®16); diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 3f7b81c065d2..8d0e60ac849c 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -37,7 +37,7 @@ #include "intel-iommu.h" #include <asm/proto.h> /* force_iommu in this header in x86-64*/ #include <asm/cacheflush.h> -#include <asm/gart.h> +#include <asm/iommu.h> #include "pci.h" #define IS_GFX_DEVICE(pdev) ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY) diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 15af618d36e2..18354817173c 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -126,7 +126,16 @@ static void msix_flush_writes(unsigned int irq) } } -static void msi_set_mask_bits(unsigned int irq, u32 mask, u32 flag) +/* + * PCI 2.3 does not specify mask bits for each MSI interrupt. Attempting to + * mask all MSI interrupts by clearing the MSI enable bit does not work + * reliably as devices without an INTx disable bit will then generate a + * level IRQ which will never be cleared. + * + * Returns 1 if it succeeded in masking the interrupt and 0 if the device + * doesn't support MSI masking. + */ +static int msi_set_mask_bits(unsigned int irq, u32 mask, u32 flag) { struct msi_desc *entry; @@ -144,8 +153,7 @@ static void msi_set_mask_bits(unsigned int irq, u32 mask, u32 flag) mask_bits |= flag & mask; pci_write_config_dword(entry->dev, pos, mask_bits); } else { - __msi_set_enable(entry->dev, entry->msi_attrib.pos, - !flag); + return 0; } break; case PCI_CAP_ID_MSIX: @@ -161,6 +169,7 @@ static void msi_set_mask_bits(unsigned int irq, u32 mask, u32 flag) break; } entry->msi_attrib.masked = !!flag; + return 1; } void read_msi_msg(unsigned int irq, struct msi_msg *msg) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 7764768b6a0e..89a2f0fa10f9 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -11,6 +11,7 @@ #include <linux/init.h> #include <linux/pci.h> #include <linux/module.h> +#include <linux/pci-aspm.h> #include <acpi/acpi.h> #include <acpi/acnamesp.h> #include <acpi/acresrc.h> @@ -372,6 +373,12 @@ static int __init acpi_pci_init(void) printk(KERN_INFO"ACPI FADT declares the system doesn't support MSI, so disable it\n"); pci_no_msi(); } + + if (acpi_gbl_FADT.boot_flags & BAF_PCIE_ASPM_CONTROL) { + printk(KERN_INFO"ACPI FADT declares the system doesn't support PCIe ASPM, so disable it\n"); + pcie_no_aspm(); + } + ret = register_acpi_bus_type(&acpi_pci_bus); if (ret) return 0; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 44a46c92b721..0a3d856833fc 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -572,6 +572,10 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) if (!ret) pci_update_current_state(dev); } + /* This device is quirked not to be put into D3, so + don't put it in D3 */ + if (state == PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3)) + return 0; error = pci_raw_set_power_state(dev, state); @@ -1040,7 +1044,7 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state) * @dev: PCI device to handle. * @state: PCI state from which device will issue PME#. */ -static bool pci_pme_capable(struct pci_dev *dev, pci_power_t state) +bool pci_pme_capable(struct pci_dev *dev, pci_power_t state) { if (!dev->pm_cap) return false; @@ -1123,18 +1127,16 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) } /** - * pci_prepare_to_sleep - prepare PCI device for system-wide transition into - * a sleep state - * @dev: Device to handle. + * pci_target_state - find an appropriate low power state for a given PCI dev + * @dev: PCI device * - * Choose the power state appropriate for the device depending on whether - * it can wake up the system and/or is power manageable by the platform - * (PCI_D3hot is the default) and put the device into that state. + * Use underlying platform code to find a supported low power state for @dev. + * If the platform can't manage @dev, return the deepest state from which it + * can generate wake events, based on any available PME info. */ -int pci_prepare_to_sleep(struct pci_dev *dev) +pci_power_t pci_target_state(struct pci_dev *dev) { pci_power_t target_state = PCI_D3hot; - int error; if (platform_pci_power_manageable(dev)) { /* @@ -1161,7 +1163,7 @@ int pci_prepare_to_sleep(struct pci_dev *dev) * to generate PME#. */ if (!dev->pm_cap) - return -EIO; + return PCI_POWER_ERROR; if (dev->pme_support) { while (target_state @@ -1170,6 +1172,25 @@ int pci_prepare_to_sleep(struct pci_dev *dev) } } + return target_state; +} + +/** + * pci_prepare_to_sleep - prepare PCI device for system-wide transition into a sleep state + * @dev: Device to handle. + * + * Choose the power state appropriate for the device depending on whether + * it can wake up the system and/or is power manageable by the platform + * (PCI_D3hot is the default) and put the device into that state. + */ +int pci_prepare_to_sleep(struct pci_dev *dev) +{ + pci_power_t target_state = pci_target_state(dev); + int error; + + if (target_state == PCI_POWER_ERROR) + return -EIO; + pci_enable_wake(dev, target_state, true); error = pci_set_power_state(dev, target_state); @@ -1181,8 +1202,7 @@ int pci_prepare_to_sleep(struct pci_dev *dev) } /** - * pci_back_from_sleep - turn PCI device on during system-wide transition into - * the working state a sleep state + * pci_back_from_sleep - turn PCI device on during system-wide transition into working state * @dev: Device to handle. * * Disable device's sytem wake-up capability and put it into D0. @@ -1920,7 +1940,9 @@ EXPORT_SYMBOL(pci_select_bars); EXPORT_SYMBOL(pci_set_power_state); EXPORT_SYMBOL(pci_save_state); EXPORT_SYMBOL(pci_restore_state); +EXPORT_SYMBOL(pci_pme_capable); EXPORT_SYMBOL(pci_enable_wake); +EXPORT_SYMBOL(pci_target_state); EXPORT_SYMBOL(pci_prepare_to_sleep); EXPORT_SYMBOL(pci_back_from_sleep); EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state); diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index f82495583e63..9a7c9e1408a4 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -55,7 +55,7 @@ struct pcie_link_state { struct endpoint_state endpoints[8]; }; -static int aspm_disabled; +static int aspm_disabled, aspm_force; static DEFINE_MUTEX(aspm_lock); static LIST_HEAD(link_list); @@ -510,6 +510,7 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev) { struct pci_dev *child_dev; int child_pos; + u32 reg32; /* * Some functions in a slot might not all be PCIE functions, very @@ -519,6 +520,19 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev) child_pos = pci_find_capability(child_dev, PCI_CAP_ID_EXP); if (!child_pos) return -EINVAL; + + /* + * Disable ASPM for pre-1.1 PCIe device, we follow MS to use + * RBER bit to determine if a function is 1.1 version device + */ + pci_read_config_dword(child_dev, child_pos + PCI_EXP_DEVCAP, + ®32); + if (!(reg32 & PCI_EXP_DEVCAP_RBER && !aspm_force)) { + printk("Pre-1.1 PCIe device detected, " + "disable ASPM for %s. It can be enabled forcedly" + " with 'pcie_aspm=force'\n", pci_name(pdev)); + return -EINVAL; + } } return 0; } @@ -802,11 +816,23 @@ void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev) static int __init pcie_aspm_disable(char *str) { - aspm_disabled = 1; + if (!strcmp(str, "off")) { + aspm_disabled = 1; + printk(KERN_INFO "PCIe ASPM is disabled\n"); + } else if (!strcmp(str, "force")) { + aspm_force = 1; + printk(KERN_INFO "PCIe ASPM is forcedly enabled\n"); + } return 1; } -__setup("pcie_noaspm", pcie_aspm_disable); +__setup("pcie_aspm=", pcie_aspm_disable); + +void pcie_no_aspm(void) +{ + if (!aspm_force) + aspm_disabled = 1; +} #ifdef CONFIG_ACPI #include <acpi/acpi_bus.h> diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index b1724cf31b66..7098dfb07449 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -163,12 +163,9 @@ static inline unsigned int pci_calc_resource_flags(unsigned int flags) return IORESOURCE_MEM; } -/* - * Find the extent of a PCI decode.. - */ -static u32 pci_size(u32 base, u32 maxbase, u32 mask) +static u64 pci_size(u64 base, u64 maxbase, u64 mask) { - u32 size = mask & maxbase; /* Find the significant bits */ + u64 size = mask & maxbase; /* Find the significant bits */ if (!size) return 0; @@ -184,135 +181,142 @@ static u32 pci_size(u32 base, u32 maxbase, u32 mask) return size; } -static u64 pci_size64(u64 base, u64 maxbase, u64 mask) -{ - u64 size = mask & maxbase; /* Find the significant bits */ - if (!size) - return 0; +enum pci_bar_type { + pci_bar_unknown, /* Standard PCI BAR probe */ + pci_bar_io, /* An io port BAR */ + pci_bar_mem32, /* A 32-bit memory BAR */ + pci_bar_mem64, /* A 64-bit memory BAR */ +}; - /* Get the lowest of them to find the decode size, and - from that the extent. */ - size = (size & ~(size-1)) - 1; +static inline enum pci_bar_type decode_bar(struct resource *res, u32 bar) +{ + if ((bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { + res->flags = bar & ~PCI_BASE_ADDRESS_IO_MASK; + return pci_bar_io; + } - /* base == maxbase can be valid only if the BAR has - already been programmed with all 1s. */ - if (base == maxbase && ((base | size) & mask) != mask) - return 0; + res->flags = bar & ~PCI_BASE_ADDRESS_MEM_MASK; - return size; + if (res->flags == PCI_BASE_ADDRESS_MEM_TYPE_64) + return pci_bar_mem64; + return pci_bar_mem32; } -static inline int is_64bit_memory(u32 mask) +/* + * If the type is not unknown, we assume that the lowest bit is 'enable'. + * Returns 1 if the BAR was 64-bit and 0 if it was 32-bit. + */ +static int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, + struct resource *res, unsigned int pos) { - if ((mask & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK)) == - (PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64)) - return 1; - return 0; -} + u32 l, sz, mask; -static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) -{ - unsigned int pos, reg, next; - u32 l, sz; - struct resource *res; + mask = type ? ~PCI_ROM_ADDRESS_ENABLE : ~0; - for(pos=0; pos<howmany; pos = next) { - u64 l64; - u64 sz64; - u32 raw_sz; + res->name = pci_name(dev); - next = pos+1; - res = &dev->resource[pos]; - res->name = pci_name(dev); - reg = PCI_BASE_ADDRESS_0 + (pos << 2); - pci_read_config_dword(dev, reg, &l); - pci_write_config_dword(dev, reg, ~0); - pci_read_config_dword(dev, reg, &sz); - pci_write_config_dword(dev, reg, l); - if (!sz || sz == 0xffffffff) - continue; - if (l == 0xffffffff) - l = 0; - raw_sz = sz; - if ((l & PCI_BASE_ADDRESS_SPACE) == - PCI_BASE_ADDRESS_SPACE_MEMORY) { - sz = pci_size(l, sz, (u32)PCI_BASE_ADDRESS_MEM_MASK); - /* - * For 64bit prefetchable memory sz could be 0, if the - * real size is bigger than 4G, so we need to check - * szhi for that. - */ - if (!is_64bit_memory(l) && !sz) - continue; - res->start = l & PCI_BASE_ADDRESS_MEM_MASK; - res->flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK; + pci_read_config_dword(dev, pos, &l); + pci_write_config_dword(dev, pos, mask); + pci_read_config_dword(dev, pos, &sz); + pci_write_config_dword(dev, pos, l); + + /* + * All bits set in sz means the device isn't working properly. + * If the BAR isn't implemented, all bits must be 0. If it's a + * memory BAR or a ROM, bit 0 must be clear; if it's an io BAR, bit + * 1 must be clear. + */ + if (!sz || sz == 0xffffffff) + goto fail; + + /* + * I don't know how l can have all bits set. Copied from old code. + * Maybe it fixes a bug on some ancient platform. + */ + if (l == 0xffffffff) + l = 0; + + if (type == pci_bar_unknown) { + type = decode_bar(res, l); + res->flags |= pci_calc_resource_flags(l) | IORESOURCE_SIZEALIGN; + if (type == pci_bar_io) { + l &= PCI_BASE_ADDRESS_IO_MASK; + mask = PCI_BASE_ADDRESS_IO_MASK & 0xffff; } else { - sz = pci_size(l, sz, PCI_BASE_ADDRESS_IO_MASK & 0xffff); - if (!sz) - continue; - res->start = l & PCI_BASE_ADDRESS_IO_MASK; - res->flags |= l & ~PCI_BASE_ADDRESS_IO_MASK; + l &= PCI_BASE_ADDRESS_MEM_MASK; + mask = (u32)PCI_BASE_ADDRESS_MEM_MASK; } - res->end = res->start + (unsigned long) sz; - res->flags |= pci_calc_resource_flags(l) | IORESOURCE_SIZEALIGN; - if (is_64bit_memory(l)) { - u32 szhi, lhi; - - pci_read_config_dword(dev, reg+4, &lhi); - pci_write_config_dword(dev, reg+4, ~0); - pci_read_config_dword(dev, reg+4, &szhi); - pci_write_config_dword(dev, reg+4, lhi); - sz64 = ((u64)szhi << 32) | raw_sz; - l64 = ((u64)lhi << 32) | l; - sz64 = pci_size64(l64, sz64, PCI_BASE_ADDRESS_MEM_MASK); - next++; -#if BITS_PER_LONG == 64 - if (!sz64) { - res->start = 0; - res->end = 0; - res->flags = 0; - continue; - } - res->start = l64 & PCI_BASE_ADDRESS_MEM_MASK; - res->end = res->start + sz64; -#else - if (sz64 > 0x100000000ULL) { - dev_err(&dev->dev, "BAR %d: can't handle 64-bit" - " BAR\n", pos); - res->start = 0; - res->flags = 0; - } else if (lhi) { - /* 64-bit wide address, treat as disabled */ - pci_write_config_dword(dev, reg, - l & ~(u32)PCI_BASE_ADDRESS_MEM_MASK); - pci_write_config_dword(dev, reg+4, 0); - res->start = 0; - res->end = sz; - } -#endif + } else { + res->flags |= (l & IORESOURCE_ROM_ENABLE); + l &= PCI_ROM_ADDRESS_MASK; + mask = (u32)PCI_ROM_ADDRESS_MASK; + } + + if (type == pci_bar_mem64) { + u64 l64 = l; + u64 sz64 = sz; + u64 mask64 = mask | (u64)~0 << 32; + + pci_read_config_dword(dev, pos + 4, &l); + pci_write_config_dword(dev, pos + 4, ~0); + pci_read_config_dword(dev, pos + 4, &sz); + pci_write_config_dword(dev, pos + 4, l); + + l64 |= ((u64)l << 32); + sz64 |= ((u64)sz << 32); + + sz64 = pci_size(l64, sz64, mask64); + + if (!sz64) + goto fail; + + if ((sizeof(resource_size_t) < 8) && (sz64 > 0x100000000ULL)) { + dev_err(&dev->dev, "can't handle 64-bit BAR\n"); + goto fail; + } else if ((sizeof(resource_size_t) < 8) && l) { + /* Address above 32-bit boundary; disable the BAR */ + pci_write_config_dword(dev, pos, 0); + pci_write_config_dword(dev, pos + 4, 0); + res->start = 0; + res->end = sz64; + } else { + res->start = l64; + res->end = l64 + sz64; } + } else { + sz = pci_size(l, sz, mask); + + if (!sz) + goto fail; + + res->start = l; + res->end = l + sz; } + + out: + return (type == pci_bar_mem64) ? 1 : 0; + fail: + res->flags = 0; + goto out; +} + +static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) +{ + unsigned int pos, reg; + + for (pos = 0; pos < howmany; pos++) { + struct resource *res = &dev->resource[pos]; + reg = PCI_BASE_ADDRESS_0 + (pos << 2); + pos += __pci_read_base(dev, pci_bar_unknown, res, reg); + } + if (rom) { + struct resource *res = &dev->resource[PCI_ROM_RESOURCE]; dev->rom_base_reg = rom; - res = &dev->resource[PCI_ROM_RESOURCE]; - res->name = pci_name(dev); - pci_read_config_dword(dev, rom, &l); - pci_write_config_dword(dev, rom, ~PCI_ROM_ADDRESS_ENABLE); - pci_read_config_dword(dev, rom, &sz); - pci_write_config_dword(dev, rom, l); - if (l == 0xffffffff) - l = 0; - if (sz && sz != 0xffffffff) { - sz = pci_size(l, sz, (u32)PCI_ROM_ADDRESS_MASK); - if (sz) { - res->flags = (l & IORESOURCE_ROM_ENABLE) | - IORESOURCE_MEM | IORESOURCE_PREFETCH | - IORESOURCE_READONLY | IORESOURCE_CACHEABLE | - IORESOURCE_SIZEALIGN; - res->start = l & PCI_ROM_ADDRESS_MASK; - res->end = res->start + (unsigned long) sz; - } - } + res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH | + IORESOURCE_READONLY | IORESOURCE_CACHEABLE | + IORESOURCE_SIZEALIGN; + __pci_read_base(dev, pci_bar_mem32, res, rom); } } @@ -1053,7 +1057,8 @@ int pci_scan_slot(struct pci_bus *bus, int devfn) } } - if (bus->self) + /* only one slot has pcie device */ + if (bus->self && nr) pcie_aspm_init_link_state(bus->self); return nr; diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index 4400dffbd93a..e1098c302c45 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -88,7 +88,7 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp if ((pos & 3) && cnt > 2) { unsigned short val; pci_user_read_config_word(dev, pos, &val); - __put_user(cpu_to_le16(val), (unsigned short __user *) buf); + __put_user(cpu_to_le16(val), (__le16 __user *) buf); buf += 2; pos += 2; cnt -= 2; @@ -97,7 +97,7 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp while (cnt >= 4) { unsigned int val; pci_user_read_config_dword(dev, pos, &val); - __put_user(cpu_to_le32(val), (unsigned int __user *) buf); + __put_user(cpu_to_le32(val), (__le32 __user *) buf); buf += 4; pos += 4; cnt -= 4; @@ -106,7 +106,7 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp if (cnt >= 2) { unsigned short val; pci_user_read_config_word(dev, pos, &val); - __put_user(cpu_to_le16(val), (unsigned short __user *) buf); + __put_user(cpu_to_le16(val), (__le16 __user *) buf); buf += 2; pos += 2; cnt -= 2; @@ -156,8 +156,8 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof } if ((pos & 3) && cnt > 2) { - unsigned short val; - __get_user(val, (unsigned short __user *) buf); + __le16 val; + __get_user(val, (__le16 __user *) buf); pci_user_write_config_word(dev, pos, le16_to_cpu(val)); buf += 2; pos += 2; @@ -165,8 +165,8 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof } while (cnt >= 4) { - unsigned int val; - __get_user(val, (unsigned int __user *) buf); + __le32 val; + __get_user(val, (__le32 __user *) buf); pci_user_write_config_dword(dev, pos, le32_to_cpu(val)); buf += 4; pos += 4; @@ -174,8 +174,8 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof } if (cnt >= 2) { - unsigned short val; - __get_user(val, (unsigned short __user *) buf); + __le16 val; + __get_user(val, (__le16 __user *) buf); pci_user_write_config_word(dev, pos, le16_to_cpu(val)); buf += 2; pos += 2; diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 12d489395fad..0fb365074288 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -923,6 +923,19 @@ static void __init quirk_ide_samemode(struct pci_dev *pdev) } DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_10, quirk_ide_samemode); +/* + * Some ATA devices break if put into D3 + */ + +static void __devinit quirk_no_ata_d3(struct pci_dev *pdev) +{ + /* Quirk the legacy ATA devices only. The AHCI ones are ok */ + if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) + pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_ANY_ID, quirk_no_ata_d3); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_ATI, PCI_ANY_ID, quirk_no_ata_d3); + /* This was originally an Alpha specific thing, but it really fits here. * The i82375 PCI/EISA bridge appears as non-classified. Fix that. */ diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index e45402adac3f..e0f884034c9f 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -219,7 +219,8 @@ config PCMCIA_SA1111 config PCMCIA_PXA2XX tristate "PXA2xx support" depends on ARM && ARCH_PXA && PCMCIA - depends on ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL || MACH_ARMCORE + depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \ + || MACH_ARMCORE || ARCH_PXA_PALM) help Say Y here to include support for the PXA2xx PCMCIA controller diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 85c6cc931f97..269a9e913ba2 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -72,4 +72,5 @@ pxa2xx_cs-$(CONFIG_ARCH_LUBBOCK) += pxa2xx_lubbock.o sa1111_generic.o pxa2xx_cs-$(CONFIG_MACH_MAINSTONE) += pxa2xx_mainstone.o pxa2xx_cs-$(CONFIG_PXA_SHARPSL) += pxa2xx_sharpsl.o pxa2xx_cs-$(CONFIG_MACH_ARMCORE) += pxa2xx_cm_x270.o +pxa2xx_cs-$(CONFIG_MACH_PALMTX) += pxa2xx_palmtx.o diff --git a/drivers/pcmcia/electra_cf.c b/drivers/pcmcia/electra_cf.c index c21f9a9c3e3f..a34284b1482a 100644 --- a/drivers/pcmcia/electra_cf.c +++ b/drivers/pcmcia/electra_cf.c @@ -28,6 +28,7 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/mm.h> #include <linux/vmalloc.h> #include <linux/of_platform.h> diff --git a/drivers/pcmcia/pxa2xx_cm_x270.c b/drivers/pcmcia/pxa2xx_cm_x270.c index f123fce65f2e..bb95db7d2b76 100644 --- a/drivers/pcmcia/pxa2xx_cm_x270.c +++ b/drivers/pcmcia/pxa2xx_cm_x270.c @@ -5,83 +5,60 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * Compulab Ltd., 2003, 2007 + * Compulab Ltd., 2003, 2007, 2008 * Mike Rapoport <mike@compulab.co.il> * */ -#include <linux/kernel.h> -#include <linux/sched.h> #include <linux/platform_device.h> #include <linux/irq.h> #include <linux/delay.h> +#include <linux/gpio.h> -#include <pcmcia/ss.h> -#include <asm/hardware.h> #include <asm/mach-types.h> - #include <asm/arch/pxa-regs.h> -#include <asm/arch/pxa2xx-gpio.h> -#include <asm/arch/cm-x270.h> #include "soc_common.h" +#define GPIO_PCMCIA_S0_CD_VALID (84) +#define GPIO_PCMCIA_S0_RDYINT (82) +#define GPIO_PCMCIA_RESET (53) + +#define PCMCIA_S0_CD_VALID IRQ_GPIO(GPIO_PCMCIA_S0_CD_VALID) +#define PCMCIA_S0_RDYINT IRQ_GPIO(GPIO_PCMCIA_S0_RDYINT) + + static struct pcmcia_irqs irqs[] = { { 0, PCMCIA_S0_CD_VALID, "PCMCIA0 CD" }, - { 1, PCMCIA_S1_CD_VALID, "PCMCIA1 CD" }, }; static int cmx270_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { - GPSR(GPIO48_nPOE) = GPIO_bit(GPIO48_nPOE) | - GPIO_bit(GPIO49_nPWE) | - GPIO_bit(GPIO50_nPIOR) | - GPIO_bit(GPIO51_nPIOW) | - GPIO_bit(GPIO85_nPCE_1) | - GPIO_bit(GPIO54_nPCE_2); - - pxa_gpio_mode(GPIO48_nPOE_MD); - pxa_gpio_mode(GPIO49_nPWE_MD); - pxa_gpio_mode(GPIO50_nPIOR_MD); - pxa_gpio_mode(GPIO51_nPIOW_MD); - pxa_gpio_mode(GPIO85_nPCE_1_MD); - pxa_gpio_mode(GPIO54_nPCE_2_MD); - pxa_gpio_mode(GPIO55_nPREG_MD); - pxa_gpio_mode(GPIO56_nPWAIT_MD); - pxa_gpio_mode(GPIO57_nIOIS16_MD); - - /* Reset signal */ - pxa_gpio_mode(GPIO53_nPCE_2 | GPIO_OUT); - GPCR(GPIO53_nPCE_2) = GPIO_bit(GPIO53_nPCE_2); - - set_irq_type(PCMCIA_S0_CD_VALID, IRQ_TYPE_EDGE_BOTH); - set_irq_type(PCMCIA_S1_CD_VALID, IRQ_TYPE_EDGE_BOTH); - - /* irq's for slots: */ - set_irq_type(PCMCIA_S0_RDYINT, IRQ_TYPE_EDGE_FALLING); - set_irq_type(PCMCIA_S1_RDYINT, IRQ_TYPE_EDGE_FALLING); - - skt->irq = (skt->nr == 0) ? PCMCIA_S0_RDYINT : PCMCIA_S1_RDYINT; - return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); + int ret = gpio_request(GPIO_PCMCIA_RESET, "PCCard reset"); + if (ret) + return ret; + gpio_direction_output(GPIO_PCMCIA_RESET, 0); + + skt->irq = PCMCIA_S0_RDYINT; + ret = soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); + if (!ret) + gpio_free(GPIO_PCMCIA_RESET); + + return ret; } static void cmx270_pcmcia_shutdown(struct soc_pcmcia_socket *skt) { soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); - - set_irq_type(IRQ_TO_GPIO(PCMCIA_S0_CD_VALID), IRQ_TYPE_NONE); - set_irq_type(IRQ_TO_GPIO(PCMCIA_S1_CD_VALID), IRQ_TYPE_NONE); - - set_irq_type(IRQ_TO_GPIO(PCMCIA_S0_RDYINT), IRQ_TYPE_NONE); - set_irq_type(IRQ_TO_GPIO(PCMCIA_S1_RDYINT), IRQ_TYPE_NONE); + gpio_free(GPIO_PCMCIA_RESET); } static void cmx270_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { - state->detect = (PCC_DETECT(skt->nr) == 0) ? 1 : 0; - state->ready = (PCC_READY(skt->nr) == 0) ? 0 : 1; + state->detect = (gpio_get_value(GPIO_PCMCIA_S0_CD_VALID) == 0) ? 1 : 0; + state->ready = (gpio_get_value(GPIO_PCMCIA_S0_RDYINT) == 0) ? 0 : 1; state->bvd1 = 1; state->bvd2 = 1; state->vs_3v = 0; @@ -93,32 +70,16 @@ static void cmx270_pcmcia_socket_state(struct soc_pcmcia_socket *skt, static int cmx270_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state) { - GPSR(GPIO49_nPWE) = GPIO_bit(GPIO49_nPWE); - pxa_gpio_mode(GPIO49_nPWE | GPIO_OUT); - switch (skt->nr) { case 0: if (state->flags & SS_RESET) { - GPCR(GPIO49_nPWE) = GPIO_bit(GPIO49_nPWE); - GPSR(GPIO53_nPCE_2) = GPIO_bit(GPIO53_nPCE_2); - udelay(10); - GPCR(GPIO53_nPCE_2) = GPIO_bit(GPIO53_nPCE_2); - GPSR(GPIO49_nPWE) = GPIO_bit(GPIO49_nPWE); - } - break; - case 1: - if (state->flags & SS_RESET) { - GPCR(GPIO49_nPWE) = GPIO_bit(GPIO49_nPWE); - GPSR(GPIO53_nPCE_2) = GPIO_bit(GPIO53_nPCE_2); + gpio_set_value(GPIO_PCMCIA_RESET, 1); udelay(10); - GPCR(GPIO53_nPCE_2) = GPIO_bit(GPIO53_nPCE_2); - GPSR(GPIO49_nPWE) = GPIO_bit(GPIO49_nPWE); + gpio_set_value(GPIO_PCMCIA_RESET, 0); } break; } - pxa_gpio_mode(GPIO49_nPWE_MD); - return 0; } @@ -139,7 +100,7 @@ static struct pcmcia_low_level cmx270_pcmcia_ops __initdata = { .configure_socket = cmx270_pcmcia_configure_socket, .socket_init = cmx270_pcmcia_socket_init, .socket_suspend = cmx270_pcmcia_socket_suspend, - .nr = 2, + .nr = 1, }; static struct platform_device *cmx270_pcmcia_device; diff --git a/drivers/pcmcia/pxa2xx_palmtx.c b/drivers/pcmcia/pxa2xx_palmtx.c new file mode 100644 index 000000000000..4abde190c1f5 --- /dev/null +++ b/drivers/pcmcia/pxa2xx_palmtx.c @@ -0,0 +1,118 @@ +/* + * linux/drivers/pcmcia/pxa2xx_palmtx.c + * + * Driver for Palm T|X PCMCIA + * + * Copyright (C) 2007-2008 Marek Vasut <marek.vasut@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <asm/mach-types.h> + +#include <asm/arch/gpio.h> +#include <asm/arch/palmtx.h> + +#include "soc_common.h" + +static int palmtx_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + skt->irq = IRQ_GPIO(GPIO_NR_PALMTX_PCMCIA_READY); + return 0; +} + +static void palmtx_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) +{ +} + +static void palmtx_pcmcia_socket_state(struct soc_pcmcia_socket *skt, + struct pcmcia_state *state) +{ + state->detect = 1; /* always inserted */ + state->ready = !!gpio_get_value(GPIO_NR_PALMTX_PCMCIA_READY); + state->bvd1 = 1; + state->bvd2 = 1; + state->wrprot = 0; + state->vs_3v = 1; + state->vs_Xv = 0; +} + +static int +palmtx_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, + const socket_state_t *state) +{ + gpio_set_value(GPIO_NR_PALMTX_PCMCIA_POWER1, 1); + gpio_set_value(GPIO_NR_PALMTX_PCMCIA_POWER2, 1); + gpio_set_value(GPIO_NR_PALMTX_PCMCIA_RESET, + !!(state->flags & SS_RESET)); + + return 0; +} + +static void palmtx_pcmcia_socket_init(struct soc_pcmcia_socket *skt) +{ +} + +static void palmtx_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) +{ +} + +static struct pcmcia_low_level palmtx_pcmcia_ops = { + .owner = THIS_MODULE, + + .first = 0, + .nr = 1, + + .hw_init = palmtx_pcmcia_hw_init, + .hw_shutdown = palmtx_pcmcia_hw_shutdown, + + .socket_state = palmtx_pcmcia_socket_state, + .configure_socket = palmtx_pcmcia_configure_socket, + + .socket_init = palmtx_pcmcia_socket_init, + .socket_suspend = palmtx_pcmcia_socket_suspend, +}; + +static struct platform_device *palmtx_pcmcia_device; + +static int __init palmtx_pcmcia_init(void) +{ + int ret; + + if (!machine_is_palmtx()) + return -ENODEV; + + palmtx_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); + if (!palmtx_pcmcia_device) + return -ENOMEM; + + ret = platform_device_add_data(palmtx_pcmcia_device, &palmtx_pcmcia_ops, + sizeof(palmtx_pcmcia_ops)); + + if (!ret) + ret = platform_device_add(palmtx_pcmcia_device); + + if (ret) + platform_device_put(palmtx_pcmcia_device); + + return ret; +} + +static void __exit palmtx_pcmcia_exit(void) +{ + platform_device_unregister(palmtx_pcmcia_device); +} + +fs_initcall(palmtx_pcmcia_init); +module_exit(palmtx_pcmcia_exit); + +MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); +MODULE_DESCRIPTION("PCMCIA support for Palm T|X"); +MODULE_ALIAS("platform:pxa2xx-pcmcia"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c index 420a77540f41..8c21446996f2 100644 --- a/drivers/pcmcia/soc_common.c +++ b/drivers/pcmcia/soc_common.c @@ -149,10 +149,10 @@ soc_common_pcmcia_config_skt(struct soc_pcmcia_socket *skt, socket_state_t *stat */ if (skt->irq_state != 1 && state->io_irq) { skt->irq_state = 1; - set_irq_type(skt->irq, IRQT_FALLING); + set_irq_type(skt->irq, IRQ_TYPE_EDGE_FALLING); } else if (skt->irq_state == 1 && state->io_irq == 0) { skt->irq_state = 0; - set_irq_type(skt->irq, IRQT_NOEDGE); + set_irq_type(skt->irq, IRQ_TYPE_NONE); } skt->cs_state = *state; @@ -527,7 +527,7 @@ int soc_pcmcia_request_irqs(struct soc_pcmcia_socket *skt, IRQF_DISABLED, irqs[i].str, skt); if (res) break; - set_irq_type(irqs[i].irq, IRQT_NOEDGE); + set_irq_type(irqs[i].irq, IRQ_TYPE_NONE); } if (res) { @@ -560,7 +560,7 @@ void soc_pcmcia_disable_irqs(struct soc_pcmcia_socket *skt, for (i = 0; i < nr; i++) if (irqs[i].sock == skt->nr) - set_irq_type(irqs[i].irq, IRQT_NOEDGE); + set_irq_type(irqs[i].irq, IRQ_TYPE_NONE); } EXPORT_SYMBOL(soc_pcmcia_disable_irqs); @@ -571,8 +571,8 @@ void soc_pcmcia_enable_irqs(struct soc_pcmcia_socket *skt, for (i = 0; i < nr; i++) if (irqs[i].sock == skt->nr) { - set_irq_type(irqs[i].irq, IRQT_RISING); - set_irq_type(irqs[i].irq, IRQT_BOTHEDGE); + set_irq_type(irqs[i].irq, IRQ_TYPE_EDGE_RISING); + set_irq_type(irqs[i].irq, IRQ_TYPE_EDGE_BOTH); } } EXPORT_SYMBOL(soc_pcmcia_enable_irqs); diff --git a/drivers/pnp/base.h b/drivers/pnp/base.h index e3fa9a2d9a3d..9fd7bb9b7dce 100644 --- a/drivers/pnp/base.h +++ b/drivers/pnp/base.h @@ -19,7 +19,6 @@ struct pnp_id *pnp_add_id(struct pnp_dev *dev, char *id); int pnp_interface_attach_device(struct pnp_dev *dev); int pnp_add_card(struct pnp_card *card); -struct pnp_id *pnp_add_card_id(struct pnp_card *card, char *id); void pnp_remove_card(struct pnp_card *card); int pnp_add_card_device(struct pnp_card *card, struct pnp_dev *dev); void pnp_remove_card_device(struct pnp_dev *dev); diff --git a/drivers/pnp/card.c b/drivers/pnp/card.c index a762a4176736..e75b060daa95 100644 --- a/drivers/pnp/card.c +++ b/drivers/pnp/card.c @@ -8,6 +8,7 @@ #include <linux/ctype.h> #include <linux/slab.h> #include <linux/pnp.h> +#include <linux/dma-mapping.h> #include "base.h" LIST_HEAD(pnp_cards); @@ -101,7 +102,7 @@ static int card_probe(struct pnp_card *card, struct pnp_card_driver *drv) * @id: pointer to a pnp_id structure * @card: pointer to the desired card */ -struct pnp_id *pnp_add_card_id(struct pnp_card *card, char *id) +static struct pnp_id *pnp_add_card_id(struct pnp_card *card, char *id) { struct pnp_id *dev_id, *ptr; @@ -167,6 +168,9 @@ struct pnp_card *pnp_alloc_card(struct pnp_protocol *protocol, int id, char *pnp sprintf(card->dev.bus_id, "%02x:%02x", card->protocol->number, card->number); + card->dev.coherent_dma_mask = DMA_24BIT_MASK; + card->dev.dma_mask = &card->dev.coherent_dma_mask; + dev_id = pnp_add_card_id(card, pnpid); if (!dev_id) { kfree(card); diff --git a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c index 55f55ed72dc7..0bdf9b8a5e58 100644 --- a/drivers/pnp/quirks.c +++ b/drivers/pnp/quirks.c @@ -245,15 +245,17 @@ static void quirk_system_pci_resources(struct pnp_dev *dev) */ for_each_pci_dev(pdev) { for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { - if (!(pci_resource_flags(pdev, i) & IORESOURCE_MEM) || - pci_resource_len(pdev, i) == 0) + unsigned int type; + + type = pci_resource_flags(pdev, i) & + (IORESOURCE_IO | IORESOURCE_MEM); + if (!type || pci_resource_len(pdev, i) == 0) continue; pci_start = pci_resource_start(pdev, i); pci_end = pci_resource_end(pdev, i); for (j = 0; - (res = pnp_get_resource(dev, IORESOURCE_MEM, j)); - j++) { + (res = pnp_get_resource(dev, type, j)); j++) { if (res->start == 0 && res->end == 0) continue; @@ -283,9 +285,10 @@ static void quirk_system_pci_resources(struct pnp_dev *dev) * the PCI region, and that might prevent a PCI * driver from requesting its resources. */ - dev_warn(&dev->dev, "mem resource " + dev_warn(&dev->dev, "%s resource " "(0x%llx-0x%llx) overlaps %s BAR %d " "(0x%llx-0x%llx), disabling\n", + pnp_resource_type_name(res), (unsigned long long) pnp_start, (unsigned long long) pnp_end, pci_name(pdev), i, diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 58c806e9c58a..4d17d384578d 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -49,4 +49,10 @@ config BATTERY_OLPC help Say Y to enable support for the battery on the OLPC laptop. +config BATTERY_PALMTX + tristate "Palm T|X battery" + depends on MACH_PALMTX + help + Say Y to enable support for the battery in Palm T|X. + endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 6413ded5fe5f..6f43a54ee420 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -20,3 +20,4 @@ obj-$(CONFIG_APM_POWER) += apm_power.o obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o +obj-$(CONFIG_BATTERY_PALMTX) += palmtx_battery.o diff --git a/drivers/power/apm_power.c b/drivers/power/apm_power.c index a4892275659d..936bae560fa1 100644 --- a/drivers/power/apm_power.c +++ b/drivers/power/apm_power.c @@ -78,7 +78,7 @@ static void find_main_battery(void) main_battery = NULL; bp.main = main_battery; - error = class_for_each_device(power_supply_class, &bp, + error = class_for_each_device(power_supply_class, NULL, &bp, __find_main_battery); if (error) { main_battery = bp.main; diff --git a/drivers/power/ds2760_battery.c b/drivers/power/ds2760_battery.c index 71be36f18709..308ddb201b66 100644 --- a/drivers/power/ds2760_battery.c +++ b/drivers/power/ds2760_battery.c @@ -433,6 +433,8 @@ static int ds2760_battery_resume(struct platform_device *pdev) #endif /* CONFIG_PM */ +MODULE_ALIAS("platform:ds2760-battery"); + static struct platform_driver ds2760_battery_driver = { .driver = { .name = "ds2760-battery", diff --git a/drivers/power/palmtx_battery.c b/drivers/power/palmtx_battery.c new file mode 100644 index 000000000000..244bb273a637 --- /dev/null +++ b/drivers/power/palmtx_battery.c @@ -0,0 +1,198 @@ +/* + * linux/drivers/power/palmtx_battery.c + * + * Battery measurement code for Palm T|X Handheld computer + * + * based on tosa_battery.c + * + * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/power_supply.h> +#include <linux/wm97xx.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> + +#include <asm/mach-types.h> +#include <asm/arch/palmtx.h> + +static DEFINE_MUTEX(bat_lock); +static struct work_struct bat_work; +struct mutex work_lock; +int bat_status = POWER_SUPPLY_STATUS_DISCHARGING; + +static unsigned long palmtx_read_bat(struct power_supply *bat_ps) +{ + return wm97xx_read_aux_adc(bat_ps->dev->parent->driver_data, + WM97XX_AUX_ID3) * 1000 / 414; +} + +static unsigned long palmtx_read_temp(struct power_supply *bat_ps) +{ + return wm97xx_read_aux_adc(bat_ps->dev->parent->driver_data, + WM97XX_AUX_ID2); +} + +static int palmtx_bat_get_property(struct power_supply *bat_ps, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = bat_status; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = palmtx_read_bat(bat_ps); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = PALMTX_BAT_MAX_VOLTAGE; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = PALMTX_BAT_MIN_VOLTAGE; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = palmtx_read_temp(bat_ps); + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + default: + return -EINVAL; + } + return 0; +} + +static void palmtx_bat_external_power_changed(struct power_supply *bat_ps) +{ + schedule_work(&bat_work); +} + +static char *status_text[] = { + [POWER_SUPPLY_STATUS_UNKNOWN] = "Unknown", + [POWER_SUPPLY_STATUS_CHARGING] = "Charging", + [POWER_SUPPLY_STATUS_DISCHARGING] = "Discharging", +}; + +static void palmtx_bat_update(struct power_supply *bat_ps) +{ + int old_status = bat_status; + + mutex_lock(&work_lock); + + bat_status = gpio_get_value(GPIO_NR_PALMTX_POWER_DETECT) ? + POWER_SUPPLY_STATUS_CHARGING : + POWER_SUPPLY_STATUS_DISCHARGING; + + if (old_status != bat_status) { + pr_debug("%s %s -> %s\n", bat_ps->name, + status_text[old_status], + status_text[bat_status]); + power_supply_changed(bat_ps); + } + + mutex_unlock(&work_lock); +} + +static enum power_supply_property palmtx_bat_main_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_PRESENT, +}; + +struct power_supply bat_ps = { + .name = "main-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = palmtx_bat_main_props, + .num_properties = ARRAY_SIZE(palmtx_bat_main_props), + .get_property = palmtx_bat_get_property, + .external_power_changed = palmtx_bat_external_power_changed, + .use_for_apm = 1, +}; + +static void palmtx_bat_work(struct work_struct *work) +{ + palmtx_bat_update(&bat_ps); +} + +#ifdef CONFIG_PM +static int palmtx_bat_suspend(struct platform_device *dev, pm_message_t state) +{ + flush_scheduled_work(); + return 0; +} + +static int palmtx_bat_resume(struct platform_device *dev) +{ + schedule_work(&bat_work); + return 0; +} +#else +#define palmtx_bat_suspend NULL +#define palmtx_bat_resume NULL +#endif + +static int __devinit palmtx_bat_probe(struct platform_device *dev) +{ + int ret = 0; + + if (!machine_is_palmtx()) + return -ENODEV; + + mutex_init(&work_lock); + + INIT_WORK(&bat_work, palmtx_bat_work); + + ret = power_supply_register(&dev->dev, &bat_ps); + if (!ret) + schedule_work(&bat_work); + + return ret; +} + +static int __devexit palmtx_bat_remove(struct platform_device *dev) +{ + power_supply_unregister(&bat_ps); + return 0; +} + +static struct platform_driver palmtx_bat_driver = { + .driver.name = "wm97xx-battery", + .driver.owner = THIS_MODULE, + .probe = palmtx_bat_probe, + .remove = __devexit_p(palmtx_bat_remove), + .suspend = palmtx_bat_suspend, + .resume = palmtx_bat_resume, +}; + +static int __init palmtx_bat_init(void) +{ + return platform_driver_register(&palmtx_bat_driver); +} + +static void __exit palmtx_bat_exit(void) +{ + platform_driver_unregister(&palmtx_bat_driver); +} + +module_init(palmtx_bat_init); +module_exit(palmtx_bat_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); +MODULE_DESCRIPTION("Palm T|X battery driver"); diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index 82810b7bff9c..0471ec743ab9 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -362,6 +362,8 @@ static int pda_power_resume(struct platform_device *pdev) #define pda_power_resume NULL #endif /* CONFIG_PM */ +MODULE_ALIAS("platform:pda-power"); + static struct platform_driver pda_power_pdrv = { .driver = { .name = "pda-power", diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index af1633eb3b70..cb1ccb472921 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -41,7 +41,7 @@ static void power_supply_changed_work(struct work_struct *work) dev_dbg(psy->dev, "%s\n", __func__); - class_for_each_device(power_supply_class, psy, + class_for_each_device(power_supply_class, NULL, psy, __power_supply_changed_work); power_supply_update_leds(psy); @@ -79,7 +79,7 @@ int power_supply_am_i_supplied(struct power_supply *psy) { int error; - error = class_for_each_device(power_supply_class, psy, + error = class_for_each_device(power_supply_class, NULL, psy, __power_supply_am_i_supplied); dev_dbg(psy->dev, "%s %d\n", __func__, error); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index fc85bf2e4a97..90ab73825401 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -273,6 +273,25 @@ comment "SPI RTC drivers" if SPI_MASTER +config RTC_DRV_M41T94 + tristate "ST M41T94" + help + If you say yes here you will get support for the + ST M41T94 SPI RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-m41t94. + +config RTC_DRV_DS1305 + tristate "Dallas/Maxim DS1305/DS1306" + help + Select this driver to get support for the Dallas/Maxim DS1305 + and DS1306 real time clock chips. These support a trickle + charger, alarms, and NVRAM in addition to the clock. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1305. + config RTC_DRV_MAX6902 tristate "Maxim MAX6902" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index b5d9d67df887..18622ef84cab 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o +obj-$(CONFIG_RTC_DRV_DS1305) += rtc-ds1305.o obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o obj-$(CONFIG_RTC_DRV_DS1374) += rtc-ds1374.o obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o @@ -34,6 +35,7 @@ obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o +obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 58b7336640ff..d397fa5f3a91 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -345,7 +345,7 @@ struct rtc_device *rtc_class_open(char *name) struct device *dev; struct rtc_device *rtc = NULL; - dev = class_find_device(rtc_class, name, __rtc_match); + dev = class_find_device(rtc_class, NULL, name, __rtc_match); if (dev) rtc = to_rtc_device(dev); diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index 9c3db934cc24..cd32d05db773 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -171,8 +171,10 @@ 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) + if (alrm->enabled) { + at91_sys_write(AT91_RTC_SCCR, AT91_RTC_ALARM); at91_sys_write(AT91_RTC_IER, AT91_RTC_ALARM); + } pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__, at91_alarm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, @@ -191,28 +193,22 @@ static int at91_rtc_ioctl(struct device *dev, unsigned int cmd, pr_debug("%s(): cmd=%08x, arg=%08lx.\n", __func__, cmd, arg); + /* important: scrub old status before enabling IRQs */ switch (cmd) { case RTC_AIE_OFF: /* alarm off */ at91_sys_write(AT91_RTC_IDR, AT91_RTC_ALARM); break; case RTC_AIE_ON: /* alarm on */ + at91_sys_write(AT91_RTC_SCCR, AT91_RTC_ALARM); at91_sys_write(AT91_RTC_IER, AT91_RTC_ALARM); break; case RTC_UIE_OFF: /* update off */ - case RTC_PIE_OFF: /* periodic off */ at91_sys_write(AT91_RTC_IDR, AT91_RTC_SECEV); break; case RTC_UIE_ON: /* update on */ - case RTC_PIE_ON: /* periodic on */ + at91_sys_write(AT91_RTC_SCCR, AT91_RTC_SECEV); at91_sys_write(AT91_RTC_IER, AT91_RTC_SECEV); break; - case RTC_IRQP_READ: /* read periodic alarm frequency */ - ret = put_user(AT91_RTC_FREQ, (unsigned long *) arg); - break; - case RTC_IRQP_SET: /* set periodic alarm frequency */ - if (arg != AT91_RTC_FREQ) - ret = -EINVAL; - break; default: ret = -ENOIOCTLCMD; break; diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index d7bb9bac71df..6ea349aba3ba 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -36,25 +36,9 @@ #include <linux/platform_device.h> #include <linux/mod_devicetable.h> -#ifdef CONFIG_HPET_EMULATE_RTC -#include <asm/hpet.h> -#endif - /* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */ #include <asm-generic/rtc.h> -#ifndef CONFIG_HPET_EMULATE_RTC -#define is_hpet_enabled() 0 -#define hpet_set_alarm_time(hrs, min, sec) do { } while (0) -#define hpet_set_periodic_freq(arg) 0 -#define hpet_mask_rtc_irq_bit(arg) do { } while (0) -#define hpet_set_rtc_irq_bit(arg) do { } while (0) -#define hpet_rtc_timer_init() do { } while (0) -#define hpet_register_irq_handler(h) 0 -#define hpet_unregister_irq_handler(h) do { } while (0) -extern irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id); -#endif - struct cmos_rtc { struct rtc_device *rtc; struct device *dev; @@ -93,6 +77,72 @@ static inline int is_intr(u8 rtc_intr) /*----------------------------------------------------------------*/ +/* Much modern x86 hardware has HPETs (10+ MHz timers) which, because + * many BIOS programmers don't set up "sane mode" IRQ routing, are mostly + * used in a broken "legacy replacement" mode. The breakage includes + * HPET #1 hijacking the IRQ for this RTC, and being unavailable for + * other (better) use. + * + * When that broken mode is in use, platform glue provides a partial + * emulation of hardware RTC IRQ facilities using HPET #1. We don't + * want to use HPET for anything except those IRQs though... + */ +#ifdef CONFIG_HPET_EMULATE_RTC +#include <asm/hpet.h> +#else + +static inline int is_hpet_enabled(void) +{ + return 0; +} + +static inline int hpet_mask_rtc_irq_bit(unsigned long mask) +{ + return 0; +} + +static inline int hpet_set_rtc_irq_bit(unsigned long mask) +{ + return 0; +} + +static inline int +hpet_set_alarm_time(unsigned char hrs, unsigned char min, unsigned char sec) +{ + return 0; +} + +static inline int hpet_set_periodic_freq(unsigned long freq) +{ + return 0; +} + +static inline int hpet_rtc_dropped_irq(void) +{ + return 0; +} + +static inline int hpet_rtc_timer_init(void) +{ + return 0; +} + +extern irq_handler_t hpet_rtc_interrupt; + +static inline int hpet_register_irq_handler(irq_handler_t handler) +{ + return 0; +} + +static inline int hpet_unregister_irq_handler(irq_handler_t handler) +{ + return 0; +} + +#endif + +/*----------------------------------------------------------------*/ + static int cmos_read_time(struct device *dev, struct rtc_time *t) { /* REVISIT: if the clock has a "century" register, use @@ -185,11 +235,56 @@ static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t) return 0; } +static void cmos_checkintr(struct cmos_rtc *cmos, unsigned char rtc_control) +{ + unsigned char rtc_intr; + + /* NOTE after changing RTC_xIE bits we always read INTR_FLAGS; + * allegedly some older rtcs need that to handle irqs properly + */ + rtc_intr = CMOS_READ(RTC_INTR_FLAGS); + + if (is_hpet_enabled()) + return; + + rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; + if (is_intr(rtc_intr)) + rtc_update_irq(cmos->rtc, 1, rtc_intr); +} + +static void cmos_irq_enable(struct cmos_rtc *cmos, unsigned char mask) +{ + unsigned char rtc_control; + + /* flush any pending IRQ status, notably for update irqs, + * before we enable new IRQs + */ + rtc_control = CMOS_READ(RTC_CONTROL); + cmos_checkintr(cmos, rtc_control); + + rtc_control |= mask; + CMOS_WRITE(rtc_control, RTC_CONTROL); + hpet_set_rtc_irq_bit(mask); + + cmos_checkintr(cmos, rtc_control); +} + +static void cmos_irq_disable(struct cmos_rtc *cmos, unsigned char mask) +{ + unsigned char rtc_control; + + rtc_control = CMOS_READ(RTC_CONTROL); + rtc_control &= ~mask; + CMOS_WRITE(rtc_control, RTC_CONTROL); + hpet_mask_rtc_irq_bit(mask); + + cmos_checkintr(cmos, rtc_control); +} + static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) { struct cmos_rtc *cmos = dev_get_drvdata(dev); unsigned char mon, mday, hrs, min, sec; - unsigned char rtc_control, rtc_intr; if (!is_valid_irq(cmos->irq)) return -EIO; @@ -213,17 +308,10 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) sec = t->time.tm_sec; sec = (sec < 60) ? BIN2BCD(sec) : 0xff; - hpet_set_alarm_time(t->time.tm_hour, t->time.tm_min, t->time.tm_sec); spin_lock_irq(&rtc_lock); /* next rtc irq must not be from previous alarm setting */ - rtc_control = CMOS_READ(RTC_CONTROL); - rtc_control &= ~RTC_AIE; - CMOS_WRITE(rtc_control, RTC_CONTROL); - rtc_intr = CMOS_READ(RTC_INTR_FLAGS); - rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; - if (is_intr(rtc_intr)) - rtc_update_irq(cmos->rtc, 1, rtc_intr); + cmos_irq_disable(cmos, RTC_AIE); /* update alarm */ CMOS_WRITE(hrs, RTC_HOURS_ALARM); @@ -237,14 +325,13 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) CMOS_WRITE(mon, cmos->mon_alrm); } - if (t->enabled) { - rtc_control |= RTC_AIE; - CMOS_WRITE(rtc_control, RTC_CONTROL); - rtc_intr = CMOS_READ(RTC_INTR_FLAGS); - rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; - if (is_intr(rtc_intr)) - rtc_update_irq(cmos->rtc, 1, rtc_intr); - } + /* FIXME the HPET alarm glue currently ignores day_alrm + * and mon_alrm ... + */ + hpet_set_alarm_time(t->time.tm_hour, t->time.tm_min, t->time.tm_sec); + + if (t->enabled) + cmos_irq_enable(cmos, RTC_AIE); spin_unlock_irq(&rtc_lock); @@ -267,8 +354,8 @@ static int cmos_irq_set_freq(struct device *dev, int freq) f = 16 - f; spin_lock_irqsave(&rtc_lock, flags); - if (!hpet_set_periodic_freq(freq)) - CMOS_WRITE(RTC_REF_CLCK_32KHZ | f, RTC_FREQ_SELECT); + hpet_set_periodic_freq(freq); + CMOS_WRITE(RTC_REF_CLCK_32KHZ | f, RTC_FREQ_SELECT); spin_unlock_irqrestore(&rtc_lock, flags); return 0; @@ -277,26 +364,17 @@ static int cmos_irq_set_freq(struct device *dev, int freq) static int cmos_irq_set_state(struct device *dev, int enabled) { struct cmos_rtc *cmos = dev_get_drvdata(dev); - unsigned char rtc_control, rtc_intr; unsigned long flags; if (!is_valid_irq(cmos->irq)) return -ENXIO; spin_lock_irqsave(&rtc_lock, flags); - rtc_control = CMOS_READ(RTC_CONTROL); if (enabled) - rtc_control |= RTC_PIE; + cmos_irq_enable(cmos, RTC_PIE); else - rtc_control &= ~RTC_PIE; - - CMOS_WRITE(rtc_control, RTC_CONTROL); - - rtc_intr = CMOS_READ(RTC_INTR_FLAGS); - rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; - if (is_intr(rtc_intr)) - rtc_update_irq(cmos->rtc, 1, rtc_intr); + cmos_irq_disable(cmos, RTC_PIE); spin_unlock_irqrestore(&rtc_lock, flags); return 0; @@ -308,7 +386,6 @@ static int cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { struct cmos_rtc *cmos = dev_get_drvdata(dev); - unsigned char rtc_control, rtc_intr; unsigned long flags; switch (cmd) { @@ -316,51 +393,29 @@ cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) case RTC_AIE_ON: case RTC_UIE_OFF: case RTC_UIE_ON: - case RTC_PIE_OFF: - case RTC_PIE_ON: if (!is_valid_irq(cmos->irq)) return -EINVAL; break; + /* PIE ON/OFF is handled by cmos_irq_set_state() */ default: return -ENOIOCTLCMD; } spin_lock_irqsave(&rtc_lock, flags); - rtc_control = CMOS_READ(RTC_CONTROL); switch (cmd) { case RTC_AIE_OFF: /* alarm off */ - rtc_control &= ~RTC_AIE; - hpet_mask_rtc_irq_bit(RTC_AIE); + cmos_irq_disable(cmos, RTC_AIE); break; case RTC_AIE_ON: /* alarm on */ - rtc_control |= RTC_AIE; - hpet_set_rtc_irq_bit(RTC_AIE); + cmos_irq_enable(cmos, RTC_AIE); break; case RTC_UIE_OFF: /* update off */ - rtc_control &= ~RTC_UIE; - hpet_mask_rtc_irq_bit(RTC_UIE); + cmos_irq_disable(cmos, RTC_UIE); break; case RTC_UIE_ON: /* update on */ - rtc_control |= RTC_UIE; - hpet_set_rtc_irq_bit(RTC_UIE); - break; - case RTC_PIE_OFF: /* periodic off */ - rtc_control &= ~RTC_PIE; - hpet_mask_rtc_irq_bit(RTC_PIE); - break; - case RTC_PIE_ON: /* periodic on */ - rtc_control |= RTC_PIE; - hpet_set_rtc_irq_bit(RTC_PIE); + cmos_irq_enable(cmos, RTC_UIE); break; } - if (!is_hpet_enabled()) - CMOS_WRITE(rtc_control, RTC_CONTROL); - - rtc_intr = CMOS_READ(RTC_INTR_FLAGS); - rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; - if (is_intr(rtc_intr)) - rtc_update_irq(cmos->rtc, 1, rtc_intr); - spin_unlock_irqrestore(&rtc_lock, flags); return 0; } @@ -502,27 +557,29 @@ static irqreturn_t cmos_interrupt(int irq, void *p) u8 rtc_control; spin_lock(&rtc_lock); - /* - * In this case it is HPET RTC interrupt handler - * calling us, with the interrupt information - * passed as arg1, instead of irq. + + /* When the HPET interrupt handler calls us, the interrupt + * status is passed as arg1 instead of the irq number. But + * always clear irq status, even when HPET is in the way. + * + * Note that HPET and RTC are almost certainly out of phase, + * giving different IRQ status ... */ + irqstat = CMOS_READ(RTC_INTR_FLAGS); + rtc_control = CMOS_READ(RTC_CONTROL); if (is_hpet_enabled()) irqstat = (unsigned long)irq & 0xF0; - else { - irqstat = CMOS_READ(RTC_INTR_FLAGS); - rtc_control = CMOS_READ(RTC_CONTROL); - irqstat &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; - } + irqstat &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; /* All Linux RTC alarms should be treated as if they were oneshot. * Similar code may be needed in system wakeup paths, in case the * alarm woke the system. */ if (irqstat & RTC_AIE) { - rtc_control = CMOS_READ(RTC_CONTROL); rtc_control &= ~RTC_AIE; CMOS_WRITE(rtc_control, RTC_CONTROL); + hpet_mask_rtc_irq_bit(RTC_AIE); + CMOS_READ(RTC_INTR_FLAGS); } spin_unlock(&rtc_lock); @@ -629,18 +686,13 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) * do something about other clock frequencies. */ cmos_rtc.rtc->irq_freq = 1024; - if (!hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq)) - CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT); + hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq); + CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT); + + /* disable irqs */ + cmos_irq_disable(&cmos_rtc, RTC_PIE | RTC_AIE | RTC_UIE); - /* disable irqs. - * - * NOTE after changing RTC_xIE bits we always read INTR_FLAGS; - * allegedly some older rtcs need that to handle irqs properly - */ rtc_control = CMOS_READ(RTC_CONTROL); - rtc_control &= ~(RTC_PIE | RTC_AIE | RTC_UIE); - CMOS_WRITE(rtc_control, RTC_CONTROL); - CMOS_READ(RTC_INTR_FLAGS); spin_unlock_irq(&rtc_lock); @@ -687,7 +739,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) goto cleanup2; } - pr_info("%s: alarms up to one %s%s\n", + pr_info("%s: alarms up to one %s%s%s\n", cmos_rtc.rtc->dev.bus_id, is_valid_irq(rtc_irq) ? (cmos_rtc.mon_alrm @@ -695,8 +747,8 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) : (cmos_rtc.day_alrm ? "month" : "day")) : "no", - cmos_rtc.century ? ", y3k" : "" - ); + cmos_rtc.century ? ", y3k" : "", + is_hpet_enabled() ? ", hpet irqs" : ""); return 0; @@ -713,13 +765,8 @@ cleanup0: static void cmos_do_shutdown(void) { - unsigned char rtc_control; - spin_lock_irq(&rtc_lock); - rtc_control = CMOS_READ(RTC_CONTROL); - rtc_control &= ~(RTC_PIE|RTC_AIE|RTC_UIE); - CMOS_WRITE(rtc_control, RTC_CONTROL); - CMOS_READ(RTC_INTR_FLAGS); + cmos_irq_disable(&cmos_rtc, RTC_IRQMASK); spin_unlock_irq(&rtc_lock); } @@ -760,17 +807,17 @@ static int cmos_suspend(struct device *dev, pm_message_t mesg) spin_lock_irq(&rtc_lock); cmos->suspend_ctrl = tmp = CMOS_READ(RTC_CONTROL); if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) { - unsigned char irqstat; + unsigned char mask; if (do_wake) - tmp &= ~(RTC_PIE|RTC_UIE); + mask = RTC_IRQMASK & ~RTC_AIE; else - tmp &= ~(RTC_PIE|RTC_AIE|RTC_UIE); + mask = RTC_IRQMASK; + tmp &= ~mask; CMOS_WRITE(tmp, RTC_CONTROL); - irqstat = CMOS_READ(RTC_INTR_FLAGS); - irqstat &= (tmp & RTC_IRQMASK) | RTC_IRQF; - if (is_intr(irqstat)) - rtc_update_irq(cmos->rtc, 1, irqstat); + hpet_mask_rtc_irq_bit(mask); + + cmos_checkintr(cmos, tmp); } spin_unlock_irq(&rtc_lock); @@ -796,7 +843,8 @@ static int cmos_resume(struct device *dev) unsigned char tmp = cmos->suspend_ctrl; /* re-enable any irqs previously active */ - if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) { + if (tmp & RTC_IRQMASK) { + unsigned char mask; if (cmos->enabled_wake) { if (cmos->wake_off) @@ -807,18 +855,28 @@ static int cmos_resume(struct device *dev) } spin_lock_irq(&rtc_lock); - CMOS_WRITE(tmp, RTC_CONTROL); - tmp = CMOS_READ(RTC_INTR_FLAGS); - tmp &= (cmos->suspend_ctrl & RTC_IRQMASK) | RTC_IRQF; - if (is_intr(tmp)) - rtc_update_irq(cmos->rtc, 1, tmp); + do { + CMOS_WRITE(tmp, RTC_CONTROL); + hpet_set_rtc_irq_bit(tmp & RTC_IRQMASK); + + mask = CMOS_READ(RTC_INTR_FLAGS); + mask &= (tmp & RTC_IRQMASK) | RTC_IRQF; + if (!is_hpet_enabled() || !is_intr(mask)) + break; + + /* force one-shot behavior if HPET blocked + * the wake alarm's irq + */ + rtc_update_irq(cmos->rtc, 1, mask); + tmp &= ~RTC_AIE; + hpet_mask_rtc_irq_bit(RTC_AIE); + } while (mask & RTC_AIE); spin_unlock_irq(&rtc_lock); } pr_debug("%s: resume, ctrl %02x\n", cmos_rtc.rtc->dev.bus_id, - cmos->suspend_ctrl); - + tmp); return 0; } diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index 0114a78b7cbb..0a870b7e5c32 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -209,7 +209,7 @@ static unsigned int rtc_dev_poll(struct file *file, poll_table *wait) return (data != 0) ? (POLLIN | POLLRDNORM) : 0; } -static int rtc_dev_ioctl(struct inode *inode, struct file *file, +static long rtc_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int err = 0; @@ -219,6 +219,10 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, struct rtc_wkalrm alarm; void __user *uarg = (void __user *) arg; + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return -EBUSY; + /* check that the calling task has appropriate permissions * for certain ioctls. doing this check here is useful * to avoid duplicate code in each driver. @@ -227,26 +231,31 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, case RTC_EPOCH_SET: case RTC_SET_TIME: if (!capable(CAP_SYS_TIME)) - return -EACCES; + err = -EACCES; break; case RTC_IRQP_SET: if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE)) - return -EACCES; + err = -EACCES; break; case RTC_PIE_ON: if (rtc->irq_freq > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE)) - return -EACCES; + err = -EACCES; break; } + if (err) + goto done; + /* try the driver's ioctl interface */ if (ops->ioctl) { err = ops->ioctl(rtc->dev.parent, cmd, arg); - if (err != -ENOIOCTLCMD) + if (err != -ENOIOCTLCMD) { + mutex_unlock(&rtc->ops_lock); return err; + } } /* if the driver does not provide the ioctl interface @@ -265,15 +274,19 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, switch (cmd) { case RTC_ALM_READ: + mutex_unlock(&rtc->ops_lock); + err = rtc_read_alarm(rtc, &alarm); if (err < 0) return err; if (copy_to_user(uarg, &alarm.time, sizeof(tm))) - return -EFAULT; - break; + err = -EFAULT; + return err; case RTC_ALM_SET: + mutex_unlock(&rtc->ops_lock); + if (copy_from_user(&alarm.time, uarg, sizeof(tm))) return -EFAULT; @@ -321,24 +334,26 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, } } - err = rtc_set_alarm(rtc, &alarm); - break; + return rtc_set_alarm(rtc, &alarm); case RTC_RD_TIME: + mutex_unlock(&rtc->ops_lock); + err = rtc_read_time(rtc, &tm); if (err < 0) return err; if (copy_to_user(uarg, &tm, sizeof(tm))) - return -EFAULT; - break; + err = -EFAULT; + return err; case RTC_SET_TIME: + mutex_unlock(&rtc->ops_lock); + if (copy_from_user(&tm, uarg, sizeof(tm))) return -EFAULT; - err = rtc_set_time(rtc, &tm); - break; + return rtc_set_time(rtc, &tm); case RTC_PIE_ON: err = rtc_irq_set_state(rtc, NULL, 1); @@ -376,34 +391,37 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, break; #endif case RTC_WKALM_SET: + mutex_unlock(&rtc->ops_lock); if (copy_from_user(&alarm, uarg, sizeof(alarm))) return -EFAULT; - err = rtc_set_alarm(rtc, &alarm); - break; + return rtc_set_alarm(rtc, &alarm); case RTC_WKALM_RD: + mutex_unlock(&rtc->ops_lock); err = rtc_read_alarm(rtc, &alarm); if (err < 0) return err; if (copy_to_user(uarg, &alarm, sizeof(alarm))) - return -EFAULT; - break; + err = -EFAULT; + return err; #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL case RTC_UIE_OFF: clear_uie(rtc); - return 0; + break; case RTC_UIE_ON: - return set_uie(rtc); + err = set_uie(rtc); #endif default: err = -ENOTTY; break; } +done: + mutex_unlock(&rtc->ops_lock); return err; } @@ -432,7 +450,7 @@ static const struct file_operations rtc_dev_fops = { .llseek = no_llseek, .read = rtc_dev_read, .poll = rtc_dev_poll, - .ioctl = rtc_dev_ioctl, + .unlocked_ioctl = rtc_dev_ioctl, .open = rtc_dev_open, .release = rtc_dev_release, .fasync = rtc_dev_fasync, diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c new file mode 100644 index 000000000000..b91d02a3ace9 --- /dev/null +++ b/drivers/rtc/rtc-ds1305.c @@ -0,0 +1,847 @@ +/* + * rtc-ds1305.c -- driver for DS1305 and DS1306 SPI RTC chips + * + * Copyright (C) 2008 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/bcd.h> +#include <linux/rtc.h> +#include <linux/workqueue.h> + +#include <linux/spi/spi.h> +#include <linux/spi/ds1305.h> + + +/* + * Registers ... mask DS1305_WRITE into register address to write, + * otherwise you're reading it. All non-bitmask values are BCD. + */ +#define DS1305_WRITE 0x80 + + +/* RTC date/time ... the main special cases are that we: + * - Need fancy "hours" encoding in 12hour mode + * - Don't rely on the "day-of-week" field (or tm_wday) + * - Are a 21st-century clock (2000 <= year < 2100) + */ +#define DS1305_RTC_LEN 7 /* bytes for RTC regs */ + +#define DS1305_SEC 0x00 /* register addresses */ +#define DS1305_MIN 0x01 +#define DS1305_HOUR 0x02 +# define DS1305_HR_12 0x40 /* set == 12 hr mode */ +# define DS1305_HR_PM 0x20 /* set == PM (12hr mode) */ +#define DS1305_WDAY 0x03 +#define DS1305_MDAY 0x04 +#define DS1305_MON 0x05 +#define DS1305_YEAR 0x06 + + +/* The two alarms have only sec/min/hour/wday fields (ALM_LEN). + * DS1305_ALM_DISABLE disables a match field (some combos are bad). + * + * NOTE that since we don't use WDAY, we limit ourselves to alarms + * only one day into the future (vs potentially up to a week). + * + * NOTE ALSO that while we could generate once-a-second IRQs (UIE), we + * don't currently support them. We'd either need to do it only when + * no alarm is pending (not the standard model), or to use the second + * alarm (implying that this is a DS1305 not DS1306, *and* that either + * it's wired up a second IRQ we know, or that INTCN is set) + */ +#define DS1305_ALM_LEN 4 /* bytes for ALM regs */ +#define DS1305_ALM_DISABLE 0x80 + +#define DS1305_ALM0(r) (0x07 + (r)) /* register addresses */ +#define DS1305_ALM1(r) (0x0b + (r)) + + +/* three control registers */ +#define DS1305_CONTROL_LEN 3 /* bytes of control regs */ + +#define DS1305_CONTROL 0x0f /* register addresses */ +# define DS1305_nEOSC 0x80 /* low enables oscillator */ +# define DS1305_WP 0x40 /* write protect */ +# define DS1305_INTCN 0x04 /* clear == only int0 used */ +# define DS1306_1HZ 0x04 /* enable 1Hz output */ +# define DS1305_AEI1 0x02 /* enable ALM1 IRQ */ +# define DS1305_AEI0 0x01 /* enable ALM0 IRQ */ +#define DS1305_STATUS 0x10 +/* status has just AEIx bits, mirrored as IRQFx */ +#define DS1305_TRICKLE 0x11 +/* trickle bits are defined in <linux/spi/ds1305.h> */ + +/* a bunch of NVRAM */ +#define DS1305_NVRAM_LEN 96 /* bytes of NVRAM */ + +#define DS1305_NVRAM 0x20 /* register addresses */ + + +struct ds1305 { + struct spi_device *spi; + struct rtc_device *rtc; + + struct work_struct work; + + unsigned long flags; +#define FLAG_EXITING 0 + + bool hr12; + u8 ctrl[DS1305_CONTROL_LEN]; +}; + + +/*----------------------------------------------------------------------*/ + +/* + * Utilities ... tolerate 12-hour AM/PM notation in case of non-Linux + * software (like a bootloader) which may require it. + */ + +static unsigned bcd2hour(u8 bcd) +{ + if (bcd & DS1305_HR_12) { + unsigned hour = 0; + + bcd &= ~DS1305_HR_12; + if (bcd & DS1305_HR_PM) { + hour = 12; + bcd &= ~DS1305_HR_PM; + } + hour += BCD2BIN(bcd); + return hour - 1; + } + return BCD2BIN(bcd); +} + +static u8 hour2bcd(bool hr12, int hour) +{ + if (hr12) { + hour++; + if (hour <= 12) + return DS1305_HR_12 | BIN2BCD(hour); + hour -= 12; + return DS1305_HR_12 | DS1305_HR_PM | BIN2BCD(hour); + } + return BIN2BCD(hour); +} + +/*----------------------------------------------------------------------*/ + +/* + * Interface to RTC framework + */ + +#ifdef CONFIG_RTC_INTF_DEV + +/* + * Context: caller holds rtc->ops_lock (to protect ds1305->ctrl) + */ +static int ds1305_ioctl(struct device *dev, unsigned cmd, unsigned long arg) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + u8 buf[2]; + int status = -ENOIOCTLCMD; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + + switch (cmd) { + case RTC_AIE_OFF: + status = 0; + if (!(buf[1] & DS1305_AEI0)) + goto done; + buf[1] &= ~DS1305_AEI0; + break; + + case RTC_AIE_ON: + status = 0; + if (ds1305->ctrl[0] & DS1305_AEI0) + goto done; + buf[1] |= DS1305_AEI0; + break; + } + if (status == 0) { + status = spi_write_then_read(ds1305->spi, buf, sizeof buf, + NULL, 0); + if (status >= 0) + ds1305->ctrl[0] = buf[1]; + } + +done: + return status; +} + +#else +#define ds1305_ioctl NULL +#endif + +/* + * Get/set of date and time is pretty normal. + */ + +static int ds1305_get_time(struct device *dev, struct rtc_time *time) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + u8 addr = DS1305_SEC; + u8 buf[DS1305_RTC_LEN]; + int status; + + /* Use write-then-read to get all the date/time registers + * since dma from stack is nonportable + */ + status = spi_write_then_read(ds1305->spi, &addr, sizeof addr, + buf, sizeof buf); + if (status < 0) + return status; + + dev_vdbg(dev, "%s: %02x %02x %02x, %02x %02x %02x %02x\n", + "read", buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6]); + + /* Decode the registers */ + time->tm_sec = BCD2BIN(buf[DS1305_SEC]); + time->tm_min = BCD2BIN(buf[DS1305_MIN]); + time->tm_hour = bcd2hour(buf[DS1305_HOUR]); + time->tm_wday = buf[DS1305_WDAY] - 1; + time->tm_mday = BCD2BIN(buf[DS1305_MDAY]); + time->tm_mon = BCD2BIN(buf[DS1305_MON]) - 1; + time->tm_year = BCD2BIN(buf[DS1305_YEAR]) + 100; + + dev_vdbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "read", time->tm_sec, time->tm_min, + time->tm_hour, time->tm_mday, + time->tm_mon, time->tm_year, time->tm_wday); + + /* Time may not be set */ + return rtc_valid_tm(time); +} + +static int ds1305_set_time(struct device *dev, struct rtc_time *time) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + u8 buf[1 + DS1305_RTC_LEN]; + u8 *bp = buf; + + dev_vdbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "write", time->tm_sec, time->tm_min, + time->tm_hour, time->tm_mday, + time->tm_mon, time->tm_year, time->tm_wday); + + /* Write registers starting at the first time/date address. */ + *bp++ = DS1305_WRITE | DS1305_SEC; + + *bp++ = BIN2BCD(time->tm_sec); + *bp++ = BIN2BCD(time->tm_min); + *bp++ = hour2bcd(ds1305->hr12, time->tm_hour); + *bp++ = (time->tm_wday < 7) ? (time->tm_wday + 1) : 1; + *bp++ = BIN2BCD(time->tm_mday); + *bp++ = BIN2BCD(time->tm_mon + 1); + *bp++ = BIN2BCD(time->tm_year - 100); + + dev_dbg(dev, "%s: %02x %02x %02x, %02x %02x %02x %02x\n", + "write", buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + + /* use write-then-read since dma from stack is nonportable */ + return spi_write_then_read(ds1305->spi, buf, sizeof buf, + NULL, 0); +} + +/* + * Get/set of alarm is a bit funky: + * + * - First there's the inherent raciness of getting the (partitioned) + * status of an alarm that could trigger while we're reading parts + * of that status. + * + * - Second there's its limited range (we could increase it a bit by + * relying on WDAY), which means it will easily roll over. + * + * - Third there's the choice of two alarms and alarm signals. + * Here we use ALM0 and expect that nINT0 (open drain) is used; + * that's the only real option for DS1306 runtime alarms, and is + * natural on DS1305. + * + * - Fourth, there's also ALM1, and a second interrupt signal: + * + On DS1305 ALM1 uses nINT1 (when INTCN=1) else nINT0; + * + On DS1306 ALM1 only uses INT1 (an active high pulse) + * and it won't work when VCC1 is active. + * + * So to be most general, we should probably set both alarms to the + * same value, letting ALM1 be the wakeup event source on DS1306 + * and handling several wiring options on DS1305. + * + * - Fifth, we support the polled mode (as well as possible; why not?) + * even when no interrupt line is wired to an IRQ. + */ + +/* + * Context: caller holds rtc->ops_lock (to protect ds1305->ctrl) + */ +static int ds1305_get_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + struct spi_device *spi = ds1305->spi; + u8 addr; + int status; + u8 buf[DS1305_ALM_LEN]; + + /* Refresh control register cache BEFORE reading ALM0 registers, + * since reading alarm registers acks any pending IRQ. That + * makes returning "pending" status a bit of a lie, but that bit + * of EFI status is at best fragile anyway (given IRQ handlers). + */ + addr = DS1305_CONTROL; + status = spi_write_then_read(spi, &addr, sizeof addr, + ds1305->ctrl, sizeof ds1305->ctrl); + if (status < 0) + return status; + + alm->enabled = !!(ds1305->ctrl[0] & DS1305_AEI0); + alm->pending = !!(ds1305->ctrl[1] & DS1305_AEI0); + + /* get and check ALM0 registers */ + addr = DS1305_ALM0(DS1305_SEC); + status = spi_write_then_read(spi, &addr, sizeof addr, + buf, sizeof buf); + if (status < 0) + return status; + + dev_vdbg(dev, "%s: %02x %02x %02x %02x\n", + "alm0 read", buf[DS1305_SEC], buf[DS1305_MIN], + buf[DS1305_HOUR], buf[DS1305_WDAY]); + + if ((DS1305_ALM_DISABLE & buf[DS1305_SEC]) + || (DS1305_ALM_DISABLE & buf[DS1305_MIN]) + || (DS1305_ALM_DISABLE & buf[DS1305_HOUR])) + return -EIO; + + /* Stuff these values into alm->time and let RTC framework code + * fill in the rest ... and also handle rollover to tomorrow when + * that's needed. + */ + alm->time.tm_sec = BCD2BIN(buf[DS1305_SEC]); + alm->time.tm_min = BCD2BIN(buf[DS1305_MIN]); + alm->time.tm_hour = bcd2hour(buf[DS1305_HOUR]); + alm->time.tm_mday = -1; + alm->time.tm_mon = -1; + alm->time.tm_year = -1; + /* next three fields are unused by Linux */ + alm->time.tm_wday = -1; + alm->time.tm_mday = -1; + alm->time.tm_isdst = -1; + + return 0; +} + +/* + * Context: caller holds rtc->ops_lock (to protect ds1305->ctrl) + */ +static int ds1305_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + struct spi_device *spi = ds1305->spi; + unsigned long now, later; + struct rtc_time tm; + int status; + u8 buf[1 + DS1305_ALM_LEN]; + + /* convert desired alarm to time_t */ + status = rtc_tm_to_time(&alm->time, &later); + if (status < 0) + return status; + + /* Read current time as time_t */ + status = ds1305_get_time(dev, &tm); + if (status < 0) + return status; + status = rtc_tm_to_time(&tm, &now); + if (status < 0) + return status; + + /* make sure alarm fires within the next 24 hours */ + if (later <= now) + return -EINVAL; + if ((later - now) > 24 * 60 * 60) + return -EDOM; + + /* disable alarm if needed */ + if (ds1305->ctrl[0] & DS1305_AEI0) { + ds1305->ctrl[0] &= ~DS1305_AEI0; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + status = spi_write_then_read(ds1305->spi, buf, 2, NULL, 0); + if (status < 0) + return status; + } + + /* write alarm */ + buf[0] = DS1305_WRITE | DS1305_ALM0(DS1305_SEC); + buf[1 + DS1305_SEC] = BIN2BCD(alm->time.tm_sec); + buf[1 + DS1305_MIN] = BIN2BCD(alm->time.tm_min); + buf[1 + DS1305_HOUR] = hour2bcd(ds1305->hr12, alm->time.tm_hour); + buf[1 + DS1305_WDAY] = DS1305_ALM_DISABLE; + + dev_dbg(dev, "%s: %02x %02x %02x %02x\n", + "alm0 write", buf[1 + DS1305_SEC], buf[1 + DS1305_MIN], + buf[1 + DS1305_HOUR], buf[1 + DS1305_WDAY]); + + status = spi_write_then_read(spi, buf, sizeof buf, NULL, 0); + if (status < 0) + return status; + + /* enable alarm if requested */ + if (alm->enabled) { + ds1305->ctrl[0] |= DS1305_AEI0; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + status = spi_write_then_read(ds1305->spi, buf, 2, NULL, 0); + } + + return status; +} + +#ifdef CONFIG_PROC_FS + +static int ds1305_proc(struct device *dev, struct seq_file *seq) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + char *diodes = "no"; + char *resistors = ""; + + /* ctrl[2] is treated as read-only; no locking needed */ + if ((ds1305->ctrl[2] & 0xf0) == DS1305_TRICKLE_MAGIC) { + switch (ds1305->ctrl[2] & 0x0c) { + case DS1305_TRICKLE_DS2: + diodes = "2 diodes, "; + break; + case DS1305_TRICKLE_DS1: + diodes = "1 diode, "; + break; + default: + goto done; + } + switch (ds1305->ctrl[2] & 0x03) { + case DS1305_TRICKLE_2K: + resistors = "2k Ohm"; + break; + case DS1305_TRICKLE_4K: + resistors = "4k Ohm"; + break; + case DS1305_TRICKLE_8K: + resistors = "8k Ohm"; + break; + default: + diodes = "no"; + break; + } + } + +done: + return seq_printf(seq, + "trickle_charge\t: %s%s\n", + diodes, resistors); +} + +#else +#define ds1305_proc NULL +#endif + +static const struct rtc_class_ops ds1305_ops = { + .ioctl = ds1305_ioctl, + .read_time = ds1305_get_time, + .set_time = ds1305_set_time, + .read_alarm = ds1305_get_alarm, + .set_alarm = ds1305_set_alarm, + .proc = ds1305_proc, +}; + +static void ds1305_work(struct work_struct *work) +{ + struct ds1305 *ds1305 = container_of(work, struct ds1305, work); + struct mutex *lock = &ds1305->rtc->ops_lock; + struct spi_device *spi = ds1305->spi; + u8 buf[3]; + int status; + + /* lock to protect ds1305->ctrl */ + mutex_lock(lock); + + /* Disable the IRQ, and clear its status ... for now, we "know" + * that if more than one alarm is active, they're in sync. + * Note that reading ALM data registers also clears IRQ status. + */ + ds1305->ctrl[0] &= ~(DS1305_AEI1 | DS1305_AEI0); + ds1305->ctrl[1] = 0; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + buf[2] = 0; + + status = spi_write_then_read(spi, buf, sizeof buf, + NULL, 0); + if (status < 0) + dev_dbg(&spi->dev, "clear irq --> %d\n", status); + + mutex_unlock(lock); + + if (!test_bit(FLAG_EXITING, &ds1305->flags)) + enable_irq(spi->irq); + + /* rtc_update_irq() requires an IRQ-disabled context */ + local_irq_disable(); + rtc_update_irq(ds1305->rtc, 1, RTC_AF | RTC_IRQF); + local_irq_enable(); +} + +/* + * This "real" IRQ handler hands off to a workqueue mostly to allow + * mutex locking for ds1305->ctrl ... unlike I2C, we could issue async + * I/O requests in IRQ context (to clear the IRQ status). + */ +static irqreturn_t ds1305_irq(int irq, void *p) +{ + struct ds1305 *ds1305 = p; + + disable_irq(irq); + schedule_work(&ds1305->work); + return IRQ_HANDLED; +} + +/*----------------------------------------------------------------------*/ + +/* + * Interface for NVRAM + */ + +static void msg_init(struct spi_message *m, struct spi_transfer *x, + u8 *addr, size_t count, char *tx, char *rx) +{ + spi_message_init(m); + memset(x, 0, 2 * sizeof(*x)); + + x->tx_buf = addr; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + + x->tx_buf = tx; + x->rx_buf = rx; + x->len = count; + spi_message_add_tail(x, m); +} + +static ssize_t +ds1305_nvram_read(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct spi_device *spi; + u8 addr; + struct spi_message m; + struct spi_transfer x[2]; + int status; + + spi = container_of(kobj, struct spi_device, dev.kobj); + + if (unlikely(off >= DS1305_NVRAM_LEN)) + return 0; + if (count >= DS1305_NVRAM_LEN) + count = DS1305_NVRAM_LEN; + if ((off + count) > DS1305_NVRAM_LEN) + count = DS1305_NVRAM_LEN - off; + if (unlikely(!count)) + return count; + + addr = DS1305_NVRAM + off; + msg_init(&m, x, &addr, count, NULL, buf); + + status = spi_sync(spi, &m); + if (status < 0) + dev_err(&spi->dev, "nvram %s error %d\n", "read", status); + return (status < 0) ? status : count; +} + +static ssize_t +ds1305_nvram_write(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct spi_device *spi; + u8 addr; + struct spi_message m; + struct spi_transfer x[2]; + int status; + + spi = container_of(kobj, struct spi_device, dev.kobj); + + if (unlikely(off >= DS1305_NVRAM_LEN)) + return -EFBIG; + if (count >= DS1305_NVRAM_LEN) + count = DS1305_NVRAM_LEN; + if ((off + count) > DS1305_NVRAM_LEN) + count = DS1305_NVRAM_LEN - off; + if (unlikely(!count)) + return count; + + addr = (DS1305_WRITE | DS1305_NVRAM) + off; + msg_init(&m, x, &addr, count, buf, NULL); + + status = spi_sync(spi, &m); + if (status < 0) + dev_err(&spi->dev, "nvram %s error %d\n", "write", status); + return (status < 0) ? status : count; +} + +static struct bin_attribute nvram = { + .attr.name = "nvram", + .attr.mode = S_IRUGO | S_IWUSR, + .attr.owner = THIS_MODULE, + .read = ds1305_nvram_read, + .write = ds1305_nvram_write, + .size = DS1305_NVRAM_LEN, +}; + +/*----------------------------------------------------------------------*/ + +/* + * Interface to SPI stack + */ + +static int __devinit ds1305_probe(struct spi_device *spi) +{ + struct ds1305 *ds1305; + struct rtc_device *rtc; + int status; + u8 addr, value; + struct ds1305_platform_data *pdata = spi->dev.platform_data; + bool write_ctrl = false; + + /* Sanity check board setup data. This may be hooked up + * in 3wire mode, but we don't care. Note that unless + * there's an inverter in place, this needs SPI_CS_HIGH! + */ + if ((spi->bits_per_word && spi->bits_per_word != 8) + || (spi->max_speed_hz > 2000000) + || !(spi->mode & SPI_CPHA)) + return -EINVAL; + + /* set up driver data */ + ds1305 = kzalloc(sizeof *ds1305, GFP_KERNEL); + if (!ds1305) + return -ENOMEM; + ds1305->spi = spi; + spi_set_drvdata(spi, ds1305); + + /* read and cache control registers */ + addr = DS1305_CONTROL; + status = spi_write_then_read(spi, &addr, sizeof addr, + ds1305->ctrl, sizeof ds1305->ctrl); + if (status < 0) { + dev_dbg(&spi->dev, "can't %s, %d\n", + "read", status); + goto fail0; + } + + dev_dbg(&spi->dev, "ctrl %s: %02x %02x %02x\n", + "read", ds1305->ctrl[0], + ds1305->ctrl[1], ds1305->ctrl[2]); + + /* Sanity check register values ... partially compensating for the + * fact that SPI has no device handshake. A pullup on MISO would + * make these tests fail; but not all systems will have one. If + * some register is neither 0x00 nor 0xff, a chip is likely there. + */ + if ((ds1305->ctrl[0] & 0x38) != 0 || (ds1305->ctrl[1] & 0xfc) != 0) { + dev_dbg(&spi->dev, "RTC chip is not present\n"); + status = -ENODEV; + goto fail0; + } + if (ds1305->ctrl[2] == 0) + dev_dbg(&spi->dev, "chip may not be present\n"); + + /* enable writes if needed ... if we were paranoid it would + * make sense to enable them only when absolutely necessary. + */ + if (ds1305->ctrl[0] & DS1305_WP) { + u8 buf[2]; + + ds1305->ctrl[0] &= ~DS1305_WP; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + status = spi_write_then_read(spi, buf, sizeof buf, NULL, 0); + + dev_dbg(&spi->dev, "clear WP --> %d\n", status); + if (status < 0) + goto fail0; + } + + /* on DS1305, maybe start oscillator; like most low power + * oscillators, it may take a second to stabilize + */ + if (ds1305->ctrl[0] & DS1305_nEOSC) { + ds1305->ctrl[0] &= ~DS1305_nEOSC; + write_ctrl = true; + dev_warn(&spi->dev, "SET TIME!\n"); + } + + /* ack any pending IRQs */ + if (ds1305->ctrl[1]) { + ds1305->ctrl[1] = 0; + write_ctrl = true; + } + + /* this may need one-time (re)init */ + if (pdata) { + /* maybe enable trickle charge */ + if (((ds1305->ctrl[2] & 0xf0) != DS1305_TRICKLE_MAGIC)) { + ds1305->ctrl[2] = DS1305_TRICKLE_MAGIC + | pdata->trickle; + write_ctrl = true; + } + + /* on DS1306, configure 1 Hz signal */ + if (pdata->is_ds1306) { + if (pdata->en_1hz) { + if (!(ds1305->ctrl[0] & DS1306_1HZ)) { + ds1305->ctrl[0] |= DS1306_1HZ; + write_ctrl = true; + } + } else { + if (ds1305->ctrl[0] & DS1306_1HZ) { + ds1305->ctrl[0] &= ~DS1306_1HZ; + write_ctrl = true; + } + } + } + } + + if (write_ctrl) { + u8 buf[4]; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + buf[2] = ds1305->ctrl[1]; + buf[3] = ds1305->ctrl[2]; + status = spi_write_then_read(spi, buf, sizeof buf, NULL, 0); + if (status < 0) { + dev_dbg(&spi->dev, "can't %s, %d\n", + "write", status); + goto fail0; + } + + dev_dbg(&spi->dev, "ctrl %s: %02x %02x %02x\n", + "write", ds1305->ctrl[0], + ds1305->ctrl[1], ds1305->ctrl[2]); + } + + /* see if non-Linux software set up AM/PM mode */ + addr = DS1305_HOUR; + status = spi_write_then_read(spi, &addr, sizeof addr, + &value, sizeof value); + if (status < 0) { + dev_dbg(&spi->dev, "read HOUR --> %d\n", status); + goto fail0; + } + + ds1305->hr12 = (DS1305_HR_12 & value) != 0; + if (ds1305->hr12) + dev_dbg(&spi->dev, "AM/PM\n"); + + /* register RTC ... from here on, ds1305->ctrl needs locking */ + rtc = rtc_device_register("ds1305", &spi->dev, + &ds1305_ops, THIS_MODULE); + if (IS_ERR(rtc)) { + status = PTR_ERR(rtc); + dev_dbg(&spi->dev, "register rtc --> %d\n", status); + goto fail0; + } + ds1305->rtc = rtc; + + /* Maybe set up alarm IRQ; be ready to handle it triggering right + * away. NOTE that we don't share this. The signal is active low, + * and we can't ack it before a SPI message delay. We temporarily + * disable the IRQ until it's acked, which lets us work with more + * IRQ trigger modes (not all IRQ controllers can do falling edge). + */ + if (spi->irq) { + INIT_WORK(&ds1305->work, ds1305_work); + status = request_irq(spi->irq, ds1305_irq, + 0, dev_name(&rtc->dev), ds1305); + if (status < 0) { + dev_dbg(&spi->dev, "request_irq %d --> %d\n", + spi->irq, status); + goto fail1; + } + } + + /* export NVRAM */ + status = sysfs_create_bin_file(&spi->dev.kobj, &nvram); + if (status < 0) { + dev_dbg(&spi->dev, "register nvram --> %d\n", status); + goto fail2; + } + + return 0; + +fail2: + free_irq(spi->irq, ds1305); +fail1: + rtc_device_unregister(rtc); +fail0: + kfree(ds1305); + return status; +} + +static int __devexit ds1305_remove(struct spi_device *spi) +{ + struct ds1305 *ds1305 = spi_get_drvdata(spi); + + sysfs_remove_bin_file(&spi->dev.kobj, &nvram); + + /* carefully shut down irq and workqueue, if present */ + if (spi->irq) { + set_bit(FLAG_EXITING, &ds1305->flags); + free_irq(spi->irq, ds1305); + flush_scheduled_work(); + } + + rtc_device_unregister(ds1305->rtc); + spi_set_drvdata(spi, NULL); + kfree(ds1305); + return 0; +} + +static struct spi_driver ds1305_driver = { + .driver.name = "rtc-ds1305", + .driver.owner = THIS_MODULE, + .probe = ds1305_probe, + .remove = __devexit_p(ds1305_remove), + /* REVISIT add suspend/resume */ +}; + +static int __init ds1305_init(void) +{ + return spi_register_driver(&ds1305_driver); +} +module_init(ds1305_init); + +static void __exit ds1305_exit(void) +{ + spi_unregister_driver(&ds1305_driver); +} +module_exit(ds1305_exit); + +MODULE_DESCRIPTION("RTC driver for DS1305 and DS1306 chips"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 0a19c06019be..24bc1689fc74 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -13,21 +13,21 @@ * */ -#include <linux/module.h> +#include <linux/bcd.h> +#include <linux/i2c.h> #include <linux/init.h> #include <linux/kernel.h> +#include <linux/module.h> +#include <linux/rtc.h> #include <linux/slab.h> #include <linux/smp_lock.h> #include <linux/string.h> -#include <linux/i2c.h> -#include <linux/rtc.h> -#include <linux/bcd.h> #ifdef CONFIG_RTC_DRV_M41T80_WDT -#include <linux/miscdevice.h> -#include <linux/watchdog.h> -#include <linux/reboot.h> #include <linux/fs.h> #include <linux/ioctl.h> +#include <linux/miscdevice.h> +#include <linux/reboot.h> +#include <linux/watchdog.h> #endif #define M41T80_REG_SSEC 0 @@ -631,14 +631,12 @@ static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, return -EFAULT; if (rv & WDIOS_DISABLECARD) { - printk(KERN_INFO - "rtc-m41t80: disable watchdog\n"); + pr_info("rtc-m41t80: disable watchdog\n"); wdt_disable(); } if (rv & WDIOS_ENABLECARD) { - printk(KERN_INFO - "rtc-m41t80: enable watchdog\n"); + pr_info("rtc-m41t80: enable watchdog\n"); wdt_ping(); } diff --git a/drivers/rtc/rtc-m41t94.c b/drivers/rtc/rtc-m41t94.c new file mode 100644 index 000000000000..9b19499c829e --- /dev/null +++ b/drivers/rtc/rtc-m41t94.c @@ -0,0 +1,173 @@ +/* + * Driver for ST M41T94 SPI RTC + * + * Copyright (C) 2008 Kim B. Heino + * + * 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/platform_device.h> +#include <linux/rtc.h> +#include <linux/spi/spi.h> +#include <linux/bcd.h> + +#define M41T94_REG_SECONDS 0x01 +#define M41T94_REG_MINUTES 0x02 +#define M41T94_REG_HOURS 0x03 +#define M41T94_REG_WDAY 0x04 +#define M41T94_REG_DAY 0x05 +#define M41T94_REG_MONTH 0x06 +#define M41T94_REG_YEAR 0x07 +#define M41T94_REG_HT 0x0c + +#define M41T94_BIT_HALT 0x40 +#define M41T94_BIT_STOP 0x80 +#define M41T94_BIT_CB 0x40 +#define M41T94_BIT_CEB 0x80 + +static int m41t94_set_time(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + u8 buf[8]; /* write cmd + 7 registers */ + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "write", tm->tm_sec, tm->tm_min, + tm->tm_hour, tm->tm_mday, + tm->tm_mon, tm->tm_year, tm->tm_wday); + + buf[0] = 0x80 | M41T94_REG_SECONDS; /* write time + date */ + buf[M41T94_REG_SECONDS] = BIN2BCD(tm->tm_sec); + buf[M41T94_REG_MINUTES] = BIN2BCD(tm->tm_min); + buf[M41T94_REG_HOURS] = BIN2BCD(tm->tm_hour); + buf[M41T94_REG_WDAY] = BIN2BCD(tm->tm_wday + 1); + buf[M41T94_REG_DAY] = BIN2BCD(tm->tm_mday); + buf[M41T94_REG_MONTH] = BIN2BCD(tm->tm_mon + 1); + + buf[M41T94_REG_HOURS] |= M41T94_BIT_CEB; + if (tm->tm_year >= 100) + buf[M41T94_REG_HOURS] |= M41T94_BIT_CB; + buf[M41T94_REG_YEAR] = BIN2BCD(tm->tm_year % 100); + + return spi_write(spi, buf, 8); +} + +static int m41t94_read_time(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + u8 buf[2]; + int ret, hour; + + /* clear halt update bit */ + ret = spi_w8r8(spi, M41T94_REG_HT); + if (ret < 0) + return ret; + if (ret & M41T94_BIT_HALT) { + buf[0] = 0x80 | M41T94_REG_HT; + buf[1] = ret & ~M41T94_BIT_HALT; + spi_write(spi, buf, 2); + } + + /* clear stop bit */ + ret = spi_w8r8(spi, M41T94_REG_SECONDS); + if (ret < 0) + return ret; + if (ret & M41T94_BIT_STOP) { + buf[0] = 0x80 | M41T94_REG_SECONDS; + buf[1] = ret & ~M41T94_BIT_STOP; + spi_write(spi, buf, 2); + } + + tm->tm_sec = BCD2BIN(spi_w8r8(spi, M41T94_REG_SECONDS)); + tm->tm_min = BCD2BIN(spi_w8r8(spi, M41T94_REG_MINUTES)); + hour = spi_w8r8(spi, M41T94_REG_HOURS); + tm->tm_hour = BCD2BIN(hour & 0x3f); + tm->tm_wday = BCD2BIN(spi_w8r8(spi, M41T94_REG_WDAY)) - 1; + tm->tm_mday = BCD2BIN(spi_w8r8(spi, M41T94_REG_DAY)); + tm->tm_mon = BCD2BIN(spi_w8r8(spi, M41T94_REG_MONTH)) - 1; + tm->tm_year = BCD2BIN(spi_w8r8(spi, M41T94_REG_YEAR)); + if ((hour & M41T94_BIT_CB) || !(hour & M41T94_BIT_CEB)) + tm->tm_year += 100; + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "read", tm->tm_sec, tm->tm_min, + tm->tm_hour, tm->tm_mday, + tm->tm_mon, tm->tm_year, tm->tm_wday); + + /* initial clock setting can be undefined */ + return rtc_valid_tm(tm); +} + +static const struct rtc_class_ops m41t94_rtc_ops = { + .read_time = m41t94_read_time, + .set_time = m41t94_set_time, +}; + +static struct spi_driver m41t94_driver; + +static int __devinit m41t94_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + int res; + + spi->bits_per_word = 8; + spi_setup(spi); + + res = spi_w8r8(spi, M41T94_REG_SECONDS); + if (res < 0) { + dev_err(&spi->dev, "not found.\n"); + return res; + } + + rtc = rtc_device_register(m41t94_driver.driver.name, + &spi->dev, &m41t94_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + dev_set_drvdata(&spi->dev, rtc); + + return 0; +} + +static int __devexit m41t94_remove(struct spi_device *spi) +{ + struct rtc_device *rtc = platform_get_drvdata(spi); + + if (rtc) + rtc_device_unregister(rtc); + + return 0; +} + +static struct spi_driver m41t94_driver = { + .driver = { + .name = "rtc-m41t94", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = m41t94_probe, + .remove = __devexit_p(m41t94_remove), +}; + +static __init int m41t94_init(void) +{ + return spi_register_driver(&m41t94_driver); +} + +module_init(m41t94_init); + +static __exit void m41t94_exit(void) +{ + spi_unregister_driver(&m41t94_driver); +} + +module_exit(m41t94_exit); + +MODULE_AUTHOR("Kim B. Heino <Kim.Heino@bluegiga.com>"); +MODULE_DESCRIPTION("Driver for ST M41T94 SPI RTC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index eb23d8423f42..8876605d4d4b 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -92,18 +92,6 @@ #define rtc_write(val, addr) omap_writeb(val, OMAP_RTC_BASE + (addr)) -/* platform_bus isn't hotpluggable, so for static linkage it'd be safe - * to get rid of probe() and remove() code ... too bad the driver struct - * remembers probe(), that's about 25% of the runtime footprint!! - */ -#ifndef MODULE -#undef __devexit -#undef __devexit_p -#define __devexit __exit -#define __devexit_p __exit_p -#endif - - /* we rely on the rtc framework to handle locking (rtc->ops_lock), * so the only other requirement is that register accesses which * require BUSY to be clear are made with IRQs locally disabled @@ -324,7 +312,7 @@ static struct rtc_class_ops omap_rtc_ops = { static int omap_rtc_alarm; static int omap_rtc_timer; -static int __devinit omap_rtc_probe(struct platform_device *pdev) +static int __init omap_rtc_probe(struct platform_device *pdev) { struct resource *res, *mem; struct rtc_device *rtc; @@ -440,7 +428,7 @@ fail: return -EIO; } -static int __devexit omap_rtc_remove(struct platform_device *pdev) +static int __exit omap_rtc_remove(struct platform_device *pdev) { struct rtc_device *rtc = platform_get_drvdata(pdev);; @@ -498,8 +486,7 @@ static void omap_rtc_shutdown(struct platform_device *pdev) MODULE_ALIAS("platform:omap_rtc"); static struct platform_driver omap_rtc_driver = { - .probe = omap_rtc_probe, - .remove = __devexit_p(omap_rtc_remove), + .remove = __exit_p(omap_rtc_remove), .suspend = omap_rtc_suspend, .resume = omap_rtc_resume, .shutdown = omap_rtc_shutdown, @@ -511,7 +498,7 @@ static struct platform_driver omap_rtc_driver = { static int __init rtc_init(void) { - return platform_driver_register(&omap_rtc_driver); + return platform_driver_probe(&omap_rtc_driver, omap_rtc_probe); } module_init(rtc_init); diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c index 3d09d8f0b1f0..d388c662bf4b 100644 --- a/drivers/rtc/rtc-pcf8583.c +++ b/drivers/rtc/rtc-pcf8583.c @@ -2,6 +2,7 @@ * drivers/rtc/rtc-pcf8583.c * * Copyright (C) 2000 Russell King + * Copyright (C) 2008 Wolfram Sang & Juergen Beisert, Pengutronix * * 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 @@ -14,7 +15,6 @@ #include <linux/module.h> #include <linux/i2c.h> #include <linux/slab.h> -#include <linux/string.h> #include <linux/rtc.h> #include <linux/init.h> #include <linux/errno.h> @@ -27,7 +27,6 @@ struct rtc_mem { }; struct pcf8583 { - struct i2c_client client; struct rtc_device *rtc; unsigned char ctrl; }; @@ -40,10 +39,6 @@ struct pcf8583 { #define CTRL_ALARM 0x02 #define CTRL_TIMER 0x01 -static const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END }; - -/* Module parameters */ -I2C_CLIENT_INSMOD; static struct i2c_driver pcf8583_driver; @@ -269,106 +264,60 @@ static const struct rtc_class_ops pcf8583_rtc_ops = { .set_time = pcf8583_rtc_set_time, }; -static int pcf8583_probe(struct i2c_adapter *adap, int addr, int kind); - -static int pcf8583_attach(struct i2c_adapter *adap) -{ - return i2c_probe(adap, &addr_data, pcf8583_probe); -} - -static int pcf8583_detach(struct i2c_client *client) -{ - int err; - struct pcf8583 *pcf = i2c_get_clientdata(client); - struct rtc_device *rtc = pcf->rtc; - - if (rtc) - rtc_device_unregister(rtc); - - if ((err = i2c_detach_client(client))) - return err; - - kfree(pcf); - return 0; -} - -static struct i2c_driver pcf8583_driver = { - .driver = { - .name = "pcf8583", - }, - .id = I2C_DRIVERID_PCF8583, - .attach_adapter = pcf8583_attach, - .detach_client = pcf8583_detach, -}; - -static int pcf8583_probe(struct i2c_adapter *adap, int addr, int kind) +static int pcf8583_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - struct pcf8583 *pcf; - struct i2c_client *client; - struct rtc_device *rtc; - unsigned char buf[1], ad[1] = { 0 }; + struct pcf8583 *pcf8583; int err; - struct i2c_msg msgs[2] = { - { - .addr = addr, - .flags = 0, - .len = 1, - .buf = ad, - }, { - .addr = addr, - .flags = I2C_M_RD, - .len = 1, - .buf = buf, - } - }; - if (!i2c_check_functionality(adap, I2C_FUNC_I2C)) - return 0; + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; - pcf = kzalloc(sizeof(*pcf), GFP_KERNEL); - if (!pcf) + pcf8583 = kzalloc(sizeof(struct pcf8583), GFP_KERNEL); + if (!pcf8583) return -ENOMEM; - client = &pcf->client; + pcf8583->rtc = rtc_device_register(pcf8583_driver.driver.name, + &client->dev, &pcf8583_rtc_ops, THIS_MODULE); - client->addr = addr; - client->adapter = adap; - client->driver = &pcf8583_driver; - - strlcpy(client->name, pcf8583_driver.driver.name, I2C_NAME_SIZE); - - if (i2c_transfer(client->adapter, msgs, 2) != 2) { - err = -EIO; + if (IS_ERR(pcf8583->rtc)) { + err = PTR_ERR(pcf8583->rtc); goto exit_kfree; } - err = i2c_attach_client(client); - - if (err) - goto exit_kfree; - - rtc = rtc_device_register(pcf8583_driver.driver.name, &client->dev, - &pcf8583_rtc_ops, THIS_MODULE); + i2c_set_clientdata(client, pcf8583); + return 0; - if (IS_ERR(rtc)) { - err = PTR_ERR(rtc); - goto exit_detach; - } +exit_kfree: + kfree(pcf8583); + return err; +} - pcf->rtc = rtc; - i2c_set_clientdata(client, pcf); - set_ctrl(client, buf[0]); +static int __devexit pcf8583_remove(struct i2c_client *client) +{ + struct pcf8583 *pcf8583 = i2c_get_clientdata(client); + if (pcf8583->rtc) + rtc_device_unregister(pcf8583->rtc); + kfree(pcf8583); return 0; +} -exit_detach: - i2c_detach_client(client); - -exit_kfree: - kfree(pcf); +static const struct i2c_device_id pcf8583_id[] = { + { "pcf8583", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcf8583_id); - return err; -} +static struct i2c_driver pcf8583_driver = { + .driver = { + .name = "pcf8583", + .owner = THIS_MODULE, + }, + .probe = pcf8583_probe, + .remove = __devexit_p(pcf8583_remove), + .id_table = pcf8583_id, +}; static __init int pcf8583_init(void) { diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index fed86e507fdf..54b1ebb01502 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -36,10 +36,8 @@ static struct resource *s3c_rtc_mem; static void __iomem *s3c_rtc_base; static int s3c_rtc_alarmno = NO_IRQ; static int s3c_rtc_tickno = NO_IRQ; -static int s3c_rtc_freq = 1; static DEFINE_SPINLOCK(s3c_rtc_pie_lock); -static unsigned int tick_count; /* IRQ Handlers */ @@ -55,7 +53,7 @@ static irqreturn_t s3c_rtc_tickirq(int irq, void *id) { struct rtc_device *rdev = id; - rtc_update_irq(rdev, tick_count++, RTC_PF | RTC_IRQF); + rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF); return IRQ_HANDLED; } @@ -74,35 +72,37 @@ static void s3c_rtc_setaie(int to) writeb(tmp, s3c_rtc_base + S3C2410_RTCALM); } -static void s3c_rtc_setpie(int to) +static int s3c_rtc_setpie(struct device *dev, int enabled) { unsigned int tmp; - pr_debug("%s: pie=%d\n", __func__, to); + pr_debug("%s: pie=%d\n", __func__, enabled); spin_lock_irq(&s3c_rtc_pie_lock); tmp = readb(s3c_rtc_base + S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE; - if (to) + if (enabled) tmp |= S3C2410_TICNT_ENABLE; writeb(tmp, s3c_rtc_base + S3C2410_TICNT); spin_unlock_irq(&s3c_rtc_pie_lock); + + return 0; } -static void s3c_rtc_setfreq(int freq) +static int s3c_rtc_setfreq(struct device *dev, int freq) { unsigned int tmp; spin_lock_irq(&s3c_rtc_pie_lock); - tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE; - - s3c_rtc_freq = freq; + tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE; tmp |= (128 / freq)-1; writeb(tmp, s3c_rtc_base + S3C2410_TICNT); spin_unlock_irq(&s3c_rtc_pie_lock); + + return 0; } /* Time read/write */ @@ -267,12 +267,7 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) writeb(alrm_en, base + S3C2410_RTCALM); - if (0) { - alrm_en = readb(base + S3C2410_RTCALM); - alrm_en &= ~S3C2410_RTCALM_ALMEN; - writeb(alrm_en, base + S3C2410_RTCALM); - disable_irq_wake(s3c_rtc_alarmno); - } + s3c_rtc_setaie(alrm->enabled); if (alrm->enabled) enable_irq_wake(s3c_rtc_alarmno); @@ -282,59 +277,12 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) return 0; } -static int s3c_rtc_ioctl(struct device *dev, - unsigned int cmd, unsigned long arg) -{ - unsigned int ret = -ENOIOCTLCMD; - - switch (cmd) { - case RTC_AIE_OFF: - case RTC_AIE_ON: - s3c_rtc_setaie((cmd == RTC_AIE_ON) ? 1 : 0); - ret = 0; - break; - - case RTC_PIE_OFF: - case RTC_PIE_ON: - tick_count = 0; - s3c_rtc_setpie((cmd == RTC_PIE_ON) ? 1 : 0); - ret = 0; - break; - - case RTC_IRQP_READ: - ret = put_user(s3c_rtc_freq, (unsigned long __user *)arg); - break; - - case RTC_IRQP_SET: - if (!is_power_of_2(arg)) { - ret = -EINVAL; - goto exit; - } - - pr_debug("s3c2410_rtc: setting frequency %ld\n", arg); - - s3c_rtc_setfreq(arg); - ret = 0; - break; - - case RTC_UIE_ON: - case RTC_UIE_OFF: - ret = -EINVAL; - } - - exit: - return ret; -} - static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) { unsigned int ticnt = readb(s3c_rtc_base + S3C2410_TICNT); seq_printf(seq, "periodic_IRQ\t: %s\n", (ticnt & S3C2410_TICNT_ENABLE) ? "yes" : "no" ); - - seq_printf(seq, "periodic_freq\t: %d\n", s3c_rtc_freq); - return 0; } @@ -374,7 +322,7 @@ static void s3c_rtc_release(struct device *dev) /* do not clear AIE here, it may be needed for wake */ - s3c_rtc_setpie(0); + s3c_rtc_setpie(dev, 0); free_irq(s3c_rtc_alarmno, rtc_dev); free_irq(s3c_rtc_tickno, rtc_dev); } @@ -382,11 +330,12 @@ static void s3c_rtc_release(struct device *dev) static const struct rtc_class_ops s3c_rtcops = { .open = s3c_rtc_open, .release = s3c_rtc_release, - .ioctl = s3c_rtc_ioctl, .read_time = s3c_rtc_gettime, .set_time = s3c_rtc_settime, .read_alarm = s3c_rtc_getalarm, .set_alarm = s3c_rtc_setalarm, + .irq_set_freq = s3c_rtc_setfreq, + .irq_set_state = s3c_rtc_setpie, .proc = s3c_rtc_proc, }; @@ -430,14 +379,14 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en) } } -static int s3c_rtc_remove(struct platform_device *dev) +static int __devexit s3c_rtc_remove(struct platform_device *dev) { struct rtc_device *rtc = platform_get_drvdata(dev); platform_set_drvdata(dev, NULL); rtc_device_unregister(rtc); - s3c_rtc_setpie(0); + s3c_rtc_setpie(&dev->dev, 0); s3c_rtc_setaie(0); iounmap(s3c_rtc_base); @@ -447,7 +396,7 @@ static int s3c_rtc_remove(struct platform_device *dev) return 0; } -static int s3c_rtc_probe(struct platform_device *pdev) +static int __devinit s3c_rtc_probe(struct platform_device *pdev) { struct rtc_device *rtc; struct resource *res; @@ -504,7 +453,7 @@ static int s3c_rtc_probe(struct platform_device *pdev) pr_debug("s3c2410_rtc: RTCCON=%02x\n", readb(s3c_rtc_base + S3C2410_RTCCON)); - s3c_rtc_setfreq(s3c_rtc_freq); + s3c_rtc_setfreq(&pdev->dev, 1); /* register RTC and exit */ @@ -560,7 +509,7 @@ static int s3c_rtc_resume(struct platform_device *pdev) static struct platform_driver s3c2410_rtcdrv = { .probe = s3c_rtc_probe, - .remove = s3c_rtc_remove, + .remove = __devexit_p(s3c_rtc_remove), .suspend = s3c_rtc_suspend, .resume = s3c_rtc_resume, .driver = { diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c index be9c70d0b193..884b635f028b 100644 --- a/drivers/rtc/rtc-vr41xx.c +++ b/drivers/rtc/rtc-vr41xx.c @@ -1,7 +1,7 @@ /* * Driver for NEC VR4100 series Real Time Clock unit. * - * Copyright (C) 2003-2006 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> + * Copyright (C) 2003-2008 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> * * 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 @@ -34,7 +34,7 @@ MODULE_AUTHOR("Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>"); MODULE_DESCRIPTION("NEC VR4100 series RTC driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); /* RTC 1 registers */ #define ETIMELREG 0x00 @@ -82,7 +82,6 @@ static unsigned long epoch = 1970; /* Jan 1 1970 00:00:00 */ static DEFINE_SPINLOCK(rtc_lock); static char rtc_name[] = "RTC"; -static unsigned long periodic_frequency; static unsigned long periodic_count; static unsigned int alarm_enabled; static int aie_irq = -1; @@ -207,10 +206,37 @@ static int vr41xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) return 0; } -static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +static int vr41xx_rtc_irq_set_freq(struct device *dev, int freq) { unsigned long count; + count = RTC_FREQUENCY; + do_div(count, freq); + + periodic_count = count; + + spin_lock_irq(&rtc_lock); + + rtc1_write(RTCL1LREG, count); + rtc1_write(RTCL1HREG, count >> 16); + + spin_unlock_irq(&rtc_lock); + + return 0; +} + +static int vr41xx_rtc_irq_set_state(struct device *dev, int enabled) +{ + if (enabled) + enable_irq(pie_irq); + else + disable_irq(pie_irq); + + return 0; +} + +static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ switch (cmd) { case RTC_AIE_ON: spin_lock_irq(&rtc_lock); @@ -232,33 +258,6 @@ static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long spin_unlock_irq(&rtc_lock); break; - case RTC_PIE_ON: - enable_irq(pie_irq); - break; - case RTC_PIE_OFF: - disable_irq(pie_irq); - break; - case RTC_IRQP_READ: - return put_user(periodic_frequency, (unsigned long __user *)arg); - break; - case RTC_IRQP_SET: - if (arg > MAX_PERIODIC_RATE) - return -EINVAL; - - periodic_frequency = arg; - - count = RTC_FREQUENCY; - do_div(count, arg); - - periodic_count = count; - - spin_lock_irq(&rtc_lock); - - rtc1_write(RTCL1LREG, count); - rtc1_write(RTCL1HREG, count >> 16); - - spin_unlock_irq(&rtc_lock); - break; case RTC_EPOCH_READ: return put_user(epoch, (unsigned long __user *)arg); case RTC_EPOCH_SET: @@ -309,6 +308,8 @@ static const struct rtc_class_ops vr41xx_rtc_ops = { .set_time = vr41xx_rtc_set_time, .read_alarm = vr41xx_rtc_read_alarm, .set_alarm = vr41xx_rtc_set_alarm, + .irq_set_freq = vr41xx_rtc_irq_set_freq, + .irq_set_state = vr41xx_rtc_irq_set_state, }; static int __devinit rtc_probe(struct platform_device *pdev) @@ -346,6 +347,8 @@ static int __devinit rtc_probe(struct platform_device *pdev) goto err_iounmap_all; } + rtc->max_user_freq = MAX_PERIODIC_RATE; + spin_lock_irq(&rtc_lock); rtc1_write(ECMPLREG, 0); diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 81a96e019080..c3dee900a5c8 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -1168,17 +1168,19 @@ static int raw3270_create_attributes(struct raw3270 *rp) if (rc) goto out; - rp->clttydev = device_create(class3270, &rp->cdev->dev, - MKDEV(IBM_TTY3270_MAJOR, rp->minor), - "tty%s", rp->cdev->dev.bus_id); + rp->clttydev = device_create_drvdata(class3270, &rp->cdev->dev, + MKDEV(IBM_TTY3270_MAJOR, rp->minor), + NULL, + "tty%s", rp->cdev->dev.bus_id); if (IS_ERR(rp->clttydev)) { rc = PTR_ERR(rp->clttydev); goto out_ttydev; } - rp->cltubdev = device_create(class3270, &rp->cdev->dev, - MKDEV(IBM_FS3270_MAJOR, rp->minor), - "tub%s", rp->cdev->dev.bus_id); + rp->cltubdev = device_create_drvdata(class3270, &rp->cdev->dev, + MKDEV(IBM_FS3270_MAJOR, rp->minor), + NULL, + "tub%s", rp->cdev->dev.bus_id); if (!IS_ERR(rp->cltubdev)) goto out; diff --git a/drivers/s390/char/tape_class.c b/drivers/s390/char/tape_class.c index 6dfdb7c17981..12c2a5aaf31b 100644 --- a/drivers/s390/char/tape_class.c +++ b/drivers/s390/char/tape_class.c @@ -69,10 +69,9 @@ struct tape_class_device *register_tape_dev( if (rc) goto fail_with_cdev; - tcd->class_device = device_create(tape_class, device, - tcd->char_device->dev, - "%s", tcd->device_name - ); + tcd->class_device = device_create_drvdata(tape_class, device, + tcd->char_device->dev, + NULL, "%s", tcd->device_name); rc = IS_ERR(tcd->class_device) ? PTR_ERR(tcd->class_device) : 0; if (rc) goto fail_with_cdev; diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index b0ac44b27127..c1f352b84868 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -896,8 +896,9 @@ static int ur_set_online(struct ccw_device *cdev) goto fail_free_cdev; } - urd->device = device_create(vmur_class, NULL, urd->char_device->dev, - "%s", node_id); + urd->device = device_create_drvdata(vmur_class, NULL, + urd->char_device->dev, NULL, + "%s", node_id); if (IS_ERR(urd->device)) { rc = PTR_ERR(urd->device); TRACE("ur_set_online: device_create rc=%d\n", rc); diff --git a/drivers/s390/kvm/Makefile b/drivers/s390/kvm/Makefile index 4a5ec39f9ca6..0815690ac1e0 100644 --- a/drivers/s390/kvm/Makefile +++ b/drivers/s390/kvm/Makefile @@ -6,4 +6,4 @@ # it under the terms of the GNU General Public License (version 2 only) # as published by the Free Software Foundation. -obj-$(CONFIG_VIRTIO) += kvm_virtio.o +obj-$(CONFIG_S390_GUEST) += kvm_virtio.o diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index 5ab34340919b..79954bd6bfa5 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -15,6 +15,7 @@ #include <linux/err.h> #include <linux/virtio.h> #include <linux/virtio_config.h> +#include <linux/virtio_console.h> #include <linux/interrupt.h> #include <linux/virtio_ring.h> #include <linux/pfn.h> @@ -87,16 +88,20 @@ static u32 kvm_get_features(struct virtio_device *vdev) return features; } -static void kvm_set_features(struct virtio_device *vdev, u32 features) +static void kvm_finalize_features(struct virtio_device *vdev) { - unsigned int i; + unsigned int i, bits; struct kvm_device_desc *desc = to_kvmdev(vdev)->desc; /* Second half of bitmap is features we accept. */ u8 *out_features = kvm_vq_features(desc) + desc->feature_len; + /* Give virtio_ring a chance to accept features. */ + vring_transport_features(vdev); + memset(out_features, 0, desc->feature_len); - for (i = 0; i < min(desc->feature_len * 8, 32); i++) { - if (features & (1 << i)) + bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8; + for (i = 0; i < bits; i++) { + if (test_bit(i, vdev->features)) out_features[i / 8] |= (1 << (i % 8)); } } @@ -222,7 +227,7 @@ static void kvm_del_vq(struct virtqueue *vq) */ static struct virtio_config_ops kvm_vq_configspace_ops = { .get_features = kvm_get_features, - .set_features = kvm_set_features, + .finalize_features = kvm_finalize_features, .get = kvm_get, .set = kvm_set, .get_status = kvm_get_status, @@ -333,6 +338,25 @@ static int __init kvm_devices_init(void) return 0; } +/* code for early console output with virtio_console */ +static __init int early_put_chars(u32 vtermno, const char *buf, int count) +{ + char scratch[17]; + unsigned int len = count; + + if (len > sizeof(scratch) - 1) + len = sizeof(scratch) - 1; + scratch[len] = '\0'; + memcpy(scratch, buf, len); + kvm_hypercall1(KVM_S390_VIRTIO_NOTIFY, __pa(scratch)); + return len; +} + +void s390_virtio_console_init(void) +{ + virtio_cons_early_init(early_put_chars); +} + /* * We do this after core stuff, but before the drivers. */ diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index c644669a75c2..a08b1682c8e8 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -58,13 +58,13 @@ * 1.10 Changes for Buffer allocation * 1.15 Changed for 2.6 Kernel No longer compiles on 2.4 or lower * 1.25 Added Packing support + * 1.5 */ #include <asm/ccwdev.h> #include <asm/ccwgroup.h> #include <asm/debug.h> #include <asm/idals.h> #include <asm/io.h> - #include <linux/bitops.h> #include <linux/ctype.h> #include <linux/delay.h> @@ -90,36 +90,10 @@ #include "cu3088.h" #include "claw.h" -MODULE_AUTHOR("Andy Richter <richtera@us.ibm.com>"); -MODULE_DESCRIPTION("Linux for zSeries CLAW Driver\n" \ - "Copyright 2000,2005 IBM Corporation\n"); -MODULE_LICENSE("GPL"); - -/* Debugging is based on DEBUGMSG, IOTRACE, or FUNCTRACE options: - DEBUGMSG - Enables output of various debug messages in the code - IOTRACE - Enables output of CCW and other IO related traces - FUNCTRACE - Enables output of function entry/exit trace - Define any combination of above options to enable tracing - - CLAW also uses the s390dbf file system see claw_trace and claw_setup +/* + CLAW uses the s390dbf file system see claw_trace and claw_setup */ -/* following enables tracing */ -//#define DEBUGMSG -//#define IOTRACE -//#define FUNCTRACE - -#ifdef DEBUGMSG -#define DEBUG -#endif - -#ifdef IOTRACE -#define DEBUG -#endif - -#ifdef FUNCTRACE -#define DEBUG -#endif static char debug_buffer[255]; /** @@ -146,7 +120,6 @@ claw_register_debug_facility(void) claw_dbf_setup = debug_register("claw_setup", 2, 1, 8); claw_dbf_trace = debug_register("claw_trace", 2, 2, 8); if (claw_dbf_setup == NULL || claw_dbf_trace == NULL) { - printk(KERN_WARNING "Not enough memory for debug facility.\n"); claw_unregister_debug_facility(); return -ENOMEM; } @@ -232,9 +205,6 @@ static void probe_error( struct ccwgroup_device *cgdev); static struct net_device_stats *claw_stats(struct net_device *dev); static int pages_to_order_of_mag(int num_of_pages); static struct sk_buff *claw_pack_skb(struct claw_privbk *privptr); -#ifdef DEBUG -static void dumpit (char *buf, int len); -#endif /* sysfs Functions */ static ssize_t claw_hname_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t claw_hname_write(struct device *dev, struct device_attribute *attr, @@ -263,12 +233,12 @@ static int claw_snd_disc(struct net_device *dev, struct clawctl * p_ctl); static int claw_snd_sys_validate_rsp(struct net_device *dev, struct clawctl * p_ctl, __u32 return_code); static int claw_strt_conn_req(struct net_device *dev ); -static void claw_strt_read ( struct net_device *dev, int lock ); -static void claw_strt_out_IO( struct net_device *dev ); -static void claw_free_wrt_buf( struct net_device *dev ); +static void claw_strt_read(struct net_device *dev, int lock); +static void claw_strt_out_IO(struct net_device *dev); +static void claw_free_wrt_buf(struct net_device *dev); /* Functions for unpack reads */ -static void unpack_read (struct net_device *dev ); +static void unpack_read(struct net_device *dev); /* ccwgroup table */ @@ -284,7 +254,6 @@ static struct ccwgroup_driver claw_group_driver = { }; /* -* * Key functions */ @@ -298,23 +267,14 @@ claw_probe(struct ccwgroup_device *cgdev) int rc; struct claw_privbk *privptr=NULL; -#ifdef FUNCTRACE - printk(KERN_INFO "%s Enter\n",__func__); -#endif - CLAW_DBF_TEXT(2,setup,"probe"); + CLAW_DBF_TEXT(2, setup, "probe"); if (!get_device(&cgdev->dev)) return -ENODEV; -#ifdef DEBUGMSG - printk(KERN_INFO "claw: variable cgdev =\n"); - dumpit((char *)cgdev, sizeof(struct ccwgroup_device)); -#endif privptr = kzalloc(sizeof(struct claw_privbk), GFP_KERNEL); if (privptr == NULL) { probe_error(cgdev); put_device(&cgdev->dev); - printk(KERN_WARNING "Out of memory %s %s Exit Line %d \n", - cgdev->cdev[0]->dev.bus_id,__func__,__LINE__); - CLAW_DBF_TEXT_(2,setup,"probex%d",-ENOMEM); + CLAW_DBF_TEXT_(2, setup, "probex%d", -ENOMEM); return -ENOMEM; } privptr->p_mtc_envelope= kzalloc( MAX_ENVELOPE_SIZE, GFP_KERNEL); @@ -322,9 +282,7 @@ claw_probe(struct ccwgroup_device *cgdev) if ((privptr->p_mtc_envelope==NULL) || (privptr->p_env==NULL)) { probe_error(cgdev); put_device(&cgdev->dev); - printk(KERN_WARNING "Out of memory %s %s Exit Line %d \n", - cgdev->cdev[0]->dev.bus_id,__func__,__LINE__); - CLAW_DBF_TEXT_(2,setup,"probex%d",-ENOMEM); + CLAW_DBF_TEXT_(2, setup, "probex%d", -ENOMEM); return -ENOMEM; } memcpy(privptr->p_env->adapter_name,WS_NAME_NOT_DEF,8); @@ -341,19 +299,14 @@ claw_probe(struct ccwgroup_device *cgdev) put_device(&cgdev->dev); printk(KERN_WARNING "add_files failed %s %s Exit Line %d \n", cgdev->cdev[0]->dev.bus_id,__func__,__LINE__); - CLAW_DBF_TEXT_(2,setup,"probex%d",rc); + CLAW_DBF_TEXT_(2, setup, "probex%d", rc); return rc; } - printk(KERN_INFO "claw: sysfs files added for %s\n",cgdev->cdev[0]->dev.bus_id); privptr->p_env->p_priv = privptr; cgdev->cdev[0]->handler = claw_irq_handler; cgdev->cdev[1]->handler = claw_irq_handler; cgdev->dev.driver_data = privptr; -#ifdef FUNCTRACE - printk(KERN_INFO "claw:%s exit on line %d, " - "rc = 0\n",__func__,__LINE__); -#endif - CLAW_DBF_TEXT(2,setup,"prbext 0"); + CLAW_DBF_TEXT(2, setup, "prbext 0"); return 0; } /* end of claw_probe */ @@ -370,37 +323,18 @@ claw_tx(struct sk_buff *skb, struct net_device *dev) unsigned long saveflags; struct chbk *p_ch; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s enter\n",dev->name,__func__); -#endif - CLAW_DBF_TEXT(4,trace,"claw_tx"); + CLAW_DBF_TEXT(4, trace, "claw_tx"); p_ch=&privptr->channel[WRITE]; if (skb == NULL) { - printk(KERN_WARNING "%s: null pointer passed as sk_buffer\n", - dev->name); privptr->stats.tx_dropped++; -#ifdef FUNCTRACE - printk(KERN_INFO "%s: %s() exit on line %d, rc = EIO\n", - dev->name,__func__, __LINE__); -#endif - CLAW_DBF_TEXT_(2,trace,"clawtx%d",-EIO); + privptr->stats.tx_errors++; + CLAW_DBF_TEXT_(2, trace, "clawtx%d", -EIO); return -EIO; } - -#ifdef IOTRACE - printk(KERN_INFO "%s: variable sk_buff=\n",dev->name); - dumpit((char *) skb, sizeof(struct sk_buff)); - printk(KERN_INFO "%s: variable dev=\n",dev->name); - dumpit((char *) dev, sizeof(struct net_device)); -#endif spin_lock_irqsave(get_ccwdev_lock(p_ch->cdev), saveflags); rc=claw_hw_tx( skb, dev, 1 ); spin_unlock_irqrestore(get_ccwdev_lock(p_ch->cdev), saveflags); -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s exit on line %d, rc = %d\n", - dev->name, __func__, __LINE__, rc); -#endif - CLAW_DBF_TEXT_(4,trace,"clawtx%d",rc); + CLAW_DBF_TEXT_(4, trace, "clawtx%d", rc); return rc; } /* end of claw_tx */ @@ -419,7 +353,7 @@ claw_pack_skb(struct claw_privbk *privptr) new_skb = NULL; /* assume no dice */ pkt_cnt = 0; - CLAW_DBF_TEXT(4,trace,"PackSKBe"); + CLAW_DBF_TEXT(4, trace, "PackSKBe"); if (!skb_queue_empty(&p_ch->collect_queue)) { /* some data */ held_skb = skb_dequeue(&p_ch->collect_queue); @@ -457,13 +391,8 @@ claw_pack_skb(struct claw_privbk *privptr) skb_queue_head(&p_ch->collect_queue,held_skb); } } -#ifdef IOTRACE - printk(KERN_INFO "%s: %s() Packed %d len %d\n", - p_env->ndev->name, - __func__,pkt_cnt,new_skb->len); -#endif } - CLAW_DBF_TEXT(4,trace,"PackSKBx"); + CLAW_DBF_TEXT(4, trace, "PackSKBx"); return new_skb; } @@ -477,29 +406,12 @@ claw_change_mtu(struct net_device *dev, int new_mtu) { struct claw_privbk *privptr=dev->priv; int buff_size; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Enter \n",dev->name,__func__); -#endif -#ifdef DEBUGMSG - printk(KERN_INFO "variable dev =\n"); - dumpit((char *) dev, sizeof(struct net_device)); - printk(KERN_INFO "variable new_mtu = %d\n", new_mtu); -#endif - CLAW_DBF_TEXT(4,trace,"setmtu"); + CLAW_DBF_TEXT(4, trace, "setmtu"); buff_size = privptr->p_env->write_size; if ((new_mtu < 60) || (new_mtu > buff_size)) { -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d, rc=EINVAL\n", - dev->name, - __func__, __LINE__); -#endif return -EINVAL; } dev->mtu = new_mtu; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n",dev->name, - __func__, __LINE__); -#endif return 0; } /* end of claw_change_mtu */ @@ -521,24 +433,13 @@ claw_open(struct net_device *dev) struct timer_list timer; struct ccwbk *p_buf; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Enter \n",dev->name,__func__); -#endif - CLAW_DBF_TEXT(4,trace,"open"); - if (!dev || (dev->name[0] == 0x00)) { - CLAW_DBF_TEXT(2,trace,"BadDev"); - printk(KERN_WARNING "claw: Bad device at open failing \n"); - return -ENODEV; - } + CLAW_DBF_TEXT(4, trace, "open"); privptr = (struct claw_privbk *)dev->priv; /* allocate and initialize CCW blocks */ if (privptr->buffs_alloc == 0) { rc=init_ccw_bk(dev); if (rc) { - printk(KERN_INFO "%s:%s Exit on line %d, rc=ENOMEM\n", - dev->name, - __func__, __LINE__); - CLAW_DBF_TEXT(2,trace,"openmem"); + CLAW_DBF_TEXT(2, trace, "openmem"); return -ENOMEM; } } @@ -557,7 +458,7 @@ claw_open(struct net_device *dev) tasklet_init(&privptr->channel[READ].tasklet, claw_irq_tasklet, (unsigned long) &privptr->channel[READ]); for ( i = 0; i < 2; i++) { - CLAW_DBF_TEXT_(2,trace,"opn_ch%d",i); + CLAW_DBF_TEXT_(2, trace, "opn_ch%d", i); init_waitqueue_head(&privptr->channel[i].wait); /* skb_queue_head_init(&p_ch->io_queue); */ if (i == WRITE) @@ -595,15 +496,8 @@ claw_open(struct net_device *dev) ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) != 0x00) || (((privptr->channel[READ].flag | privptr->channel[WRITE].flag) & CLAW_TIMER) != 0x00)) { -#ifdef DEBUGMSG - printk(KERN_INFO "%s: channel problems during open - read:" - " %02x - write: %02x\n", - dev->name, - privptr->channel[READ].last_dstat, - privptr->channel[WRITE].last_dstat); -#endif printk(KERN_INFO "%s: remote side is not ready\n", dev->name); - CLAW_DBF_TEXT(2,trace,"notrdy"); + CLAW_DBF_TEXT(2, trace, "notrdy"); for ( i = 0; i < 2; i++) { spin_lock_irqsave( @@ -659,23 +553,14 @@ claw_open(struct net_device *dev) privptr->p_buff_read=NULL; privptr->p_buff_write=NULL; claw_clear_busy(dev); -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d, rc=EIO\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(2,trace,"open EIO"); + CLAW_DBF_TEXT(2, trace, "open EIO"); return -EIO; } /* Send SystemValidate command */ claw_clear_busy(dev); - -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d, rc=0\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(4,trace,"openok"); + CLAW_DBF_TEXT(4, trace, "openok"); return 0; } /* end of claw_open */ @@ -694,22 +579,14 @@ claw_irq_handler(struct ccw_device *cdev, struct claw_env *p_env; struct chbk *p_ch_r=NULL; - -#ifdef FUNCTRACE - printk(KERN_INFO "%s enter \n",__func__); -#endif - CLAW_DBF_TEXT(4,trace,"clawirq"); + CLAW_DBF_TEXT(4, trace, "clawirq"); /* Bypass all 'unsolicited interrupts' */ if (!cdev->dev.driver_data) { printk(KERN_WARNING "claw: unsolicited interrupt for device:" "%s received c-%02x d-%02x\n", cdev->dev.bus_id, irb->scsw.cmd.cstat, irb->scsw.cmd.dstat); -#ifdef FUNCTRACE - printk(KERN_INFO "claw: %s() " - "exit on line %d\n",__func__,__LINE__); -#endif - CLAW_DBF_TEXT(2,trace,"badirq"); + CLAW_DBF_TEXT(2, trace, "badirq"); return; } privptr = (struct claw_privbk *)cdev->dev.driver_data; @@ -722,41 +599,25 @@ claw_irq_handler(struct ccw_device *cdev, else { printk(KERN_WARNING "claw: Can't determine channel for " "interrupt, device %s\n", cdev->dev.bus_id); - CLAW_DBF_TEXT(2,trace,"badchan"); + CLAW_DBF_TEXT(2, trace, "badchan"); return; } - CLAW_DBF_TEXT_(4,trace,"IRQCH=%d",p_ch->flag); + CLAW_DBF_TEXT_(4, trace, "IRQCH=%d", p_ch->flag); dev = (struct net_device *) (p_ch->ndev); p_env=privptr->p_env; -#ifdef IOTRACE - printk(KERN_INFO "%s: interrupt for device: %04x " - "received c-%02x d-%02x state-%02x\n", - dev->name, p_ch->devno, irb->scsw.cmd.cstat, - irb->scsw.cmd.dstat, p_ch->claw_state); -#endif - /* Copy interruption response block. */ memcpy(p_ch->irb, irb, sizeof(struct irb)); - /* Check for good subchannel return code, otherwise error message */ + /* Check for good subchannel return code, otherwise info message */ if (irb->scsw.cmd.cstat && !(irb->scsw.cmd.cstat & SCHN_STAT_PCI)) { printk(KERN_INFO "%s: subchannel check for device: %04x -" " Sch Stat %02x Dev Stat %02x CPA - %04x\n", dev->name, p_ch->devno, irb->scsw.cmd.cstat, irb->scsw.cmd.dstat, irb->scsw.cmd.cpa); -#ifdef IOTRACE - dumpit((char *)irb,sizeof(struct irb)); - dumpit((char *)(unsigned long)irb->scsw.cmd.cpa, - sizeof(struct ccw1)); -#endif -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(2,trace,"chanchk"); + CLAW_DBF_TEXT(2, trace, "chanchk"); /* return; */ } @@ -768,233 +629,138 @@ claw_irq_handler(struct ccw_device *cdev, p_ch->last_dstat = irb->scsw.cmd.dstat; switch (p_ch->claw_state) { - case CLAW_STOP:/* HALT_IO by claw_release (halt sequence) */ -#ifdef DEBUGMSG - printk(KERN_INFO "%s: CLAW_STOP enter\n", dev->name); -#endif - if (!((p_ch->irb->scsw.cmd.stctl & - SCSW_STCTL_SEC_STATUS) || - (p_ch->irb->scsw.cmd.stctl == - SCSW_STCTL_STATUS_PEND) || - (p_ch->irb->scsw.cmd.stctl == - (SCSW_STCTL_ALERT_STATUS | - SCSW_STCTL_STATUS_PEND)))) { -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - return; - } - wake_up(&p_ch->wait); /* wake up claw_release */ - -#ifdef DEBUGMSG - printk(KERN_INFO "%s: CLAW_STOP exit\n", dev->name); -#endif -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(4,trace,"stop"); - return; - - case CLAW_START_HALT_IO: /* HALT_IO issued by claw_open */ -#ifdef DEBUGMSG - printk(KERN_INFO "%s: process CLAW_STAT_HALT_IO\n", - dev->name); -#endif - if (!((p_ch->irb->scsw.cmd.stctl & - SCSW_STCTL_SEC_STATUS) || - (p_ch->irb->scsw.cmd.stctl == - SCSW_STCTL_STATUS_PEND) || - (p_ch->irb->scsw.cmd.stctl == - (SCSW_STCTL_ALERT_STATUS | - SCSW_STCTL_STATUS_PEND)))) { -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(4,trace,"haltio"); - return; - } - if (p_ch->flag == CLAW_READ) { - p_ch->claw_state = CLAW_START_READ; - wake_up(&p_ch->wait); /* wake claw_open (READ)*/ - } + case CLAW_STOP:/* HALT_IO by claw_release (halt sequence) */ + if (!((p_ch->irb->scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS) || + (p_ch->irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND) || + (p_ch->irb->scsw.cmd.stctl == + (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) + return; + wake_up(&p_ch->wait); /* wake up claw_release */ + CLAW_DBF_TEXT(4, trace, "stop"); + return; + case CLAW_START_HALT_IO: /* HALT_IO issued by claw_open */ + if (!((p_ch->irb->scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS) || + (p_ch->irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND) || + (p_ch->irb->scsw.cmd.stctl == + (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) { + CLAW_DBF_TEXT(4, trace, "haltio"); + return; + } + if (p_ch->flag == CLAW_READ) { + p_ch->claw_state = CLAW_START_READ; + wake_up(&p_ch->wait); /* wake claw_open (READ)*/ + } else if (p_ch->flag == CLAW_WRITE) { + p_ch->claw_state = CLAW_START_WRITE; + /* send SYSTEM_VALIDATE */ + claw_strt_read(dev, LOCK_NO); + claw_send_control(dev, + SYSTEM_VALIDATE_REQUEST, + 0, 0, 0, + p_env->host_name, + p_env->adapter_name); + } else { + printk(KERN_WARNING "claw: unsolicited " + "interrupt for device:" + "%s received c-%02x d-%02x\n", + cdev->dev.bus_id, + irb->scsw.cmd.cstat, + irb->scsw.cmd.dstat); + return; + } + CLAW_DBF_TEXT(4, trace, "haltio"); + return; + case CLAW_START_READ: + CLAW_DBF_TEXT(4, trace, "ReadIRQ"); + if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { + clear_bit(0, (void *)&p_ch->IO_active); + if ((p_ch->irb->ecw[0] & 0x41) == 0x41 || + (p_ch->irb->ecw[0] & 0x40) == 0x40 || + (p_ch->irb->ecw[0]) == 0) { + privptr->stats.rx_errors++; + printk(KERN_INFO "%s: Restart is " + "required after remote " + "side recovers \n", + dev->name); + } + CLAW_DBF_TEXT(4, trace, "notrdy"); + return; + } + if ((p_ch->irb->scsw.cmd.cstat & SCHN_STAT_PCI) && + (p_ch->irb->scsw.cmd.dstat == 0)) { + if (test_and_set_bit(CLAW_BH_ACTIVE, + (void *)&p_ch->flag_a) == 0) + tasklet_schedule(&p_ch->tasklet); else - if (p_ch->flag == CLAW_WRITE) { - p_ch->claw_state = CLAW_START_WRITE; - /* send SYSTEM_VALIDATE */ - claw_strt_read(dev, LOCK_NO); - claw_send_control(dev, - SYSTEM_VALIDATE_REQUEST, - 0, 0, 0, - p_env->host_name, - p_env->adapter_name ); - } else { - printk(KERN_WARNING "claw: unsolicited " - "interrupt for device:" - "%s received c-%02x d-%02x\n", - cdev->dev.bus_id, - irb->scsw.cmd.cstat, - irb->scsw.cmd.dstat); - return; - } -#ifdef DEBUGMSG - printk(KERN_INFO "%s: process CLAW_STAT_HALT_IO exit\n", - dev->name); -#endif -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(4,trace,"haltio"); - return; - case CLAW_START_READ: - CLAW_DBF_TEXT(4,trace,"ReadIRQ"); - if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { - clear_bit(0, (void *)&p_ch->IO_active); - if ((p_ch->irb->ecw[0] & 0x41) == 0x41 || - (p_ch->irb->ecw[0] & 0x40) == 0x40 || - (p_ch->irb->ecw[0]) == 0) - { - privptr->stats.rx_errors++; - printk(KERN_INFO "%s: Restart is " - "required after remote " - "side recovers \n", - dev->name); - } -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(4,trace,"notrdy"); - return; - } - if ((p_ch->irb->scsw.cmd.cstat & SCHN_STAT_PCI) && - (p_ch->irb->scsw.cmd.dstat == 0)) { - if (test_and_set_bit(CLAW_BH_ACTIVE, - (void *)&p_ch->flag_a) == 0) { - tasklet_schedule(&p_ch->tasklet); - } - else { - CLAW_DBF_TEXT(4,trace,"PCINoBH"); - } -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(4,trace,"PCI_read"); - return; - } - if (!((p_ch->irb->scsw.cmd.stctl & - SCSW_STCTL_SEC_STATUS) || - (p_ch->irb->scsw.cmd.stctl == - SCSW_STCTL_STATUS_PEND) || - (p_ch->irb->scsw.cmd.stctl == - (SCSW_STCTL_ALERT_STATUS | - SCSW_STCTL_STATUS_PEND)))) { -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(4,trace,"SPend_rd"); - return; - } - clear_bit(0, (void *)&p_ch->IO_active); - claw_clearbit_busy(TB_RETRY,dev); - if (test_and_set_bit(CLAW_BH_ACTIVE, - (void *)&p_ch->flag_a) == 0) { - tasklet_schedule(&p_ch->tasklet); - } - else { - CLAW_DBF_TEXT(4,trace,"RdBHAct"); - } - -#ifdef DEBUGMSG - printk(KERN_INFO "%s: process CLAW_START_READ exit\n", - dev->name); -#endif -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(4,trace,"RdIRQXit"); - return; - case CLAW_START_WRITE: - if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { - printk(KERN_INFO "%s: Unit Check Occured in " - "write channel\n",dev->name); - clear_bit(0, (void *)&p_ch->IO_active); - if (p_ch->irb->ecw[0] & 0x80 ) { - printk(KERN_INFO "%s: Resetting Event " - "occurred:\n",dev->name); - init_timer(&p_ch->timer); - p_ch->timer.function = - (void *)claw_write_retry; - p_ch->timer.data = (unsigned long)p_ch; - p_ch->timer.expires = jiffies + 10*HZ; - add_timer(&p_ch->timer); - printk(KERN_INFO "%s: write connection " - "restarting\n",dev->name); - } -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(4,trace,"rstrtwrt"); - return; - } - if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) { - clear_bit(0, (void *)&p_ch->IO_active); - printk(KERN_INFO "%s: Unit Exception " - "Occured in write channel\n", - dev->name); - } - if (!((p_ch->irb->scsw.cmd.stctl & - SCSW_STCTL_SEC_STATUS) || - (p_ch->irb->scsw.cmd.stctl == - SCSW_STCTL_STATUS_PEND) || - (p_ch->irb->scsw.cmd.stctl == - (SCSW_STCTL_ALERT_STATUS | - SCSW_STCTL_STATUS_PEND)))) { -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(4,trace,"writeUE"); - return; - } - clear_bit(0, (void *)&p_ch->IO_active); - if (claw_test_and_setbit_busy(TB_TX,dev)==0) { - claw_write_next(p_ch); - claw_clearbit_busy(TB_TX,dev); - claw_clear_busy(dev); - } - p_ch_r=(struct chbk *)&privptr->channel[READ]; - if (test_and_set_bit(CLAW_BH_ACTIVE, - (void *)&p_ch_r->flag_a) == 0) { - tasklet_schedule(&p_ch_r->tasklet); - } - -#ifdef DEBUGMSG - printk(KERN_INFO "%s: process CLAW_START_WRITE exit\n", - dev->name); -#endif -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(4,trace,"StWtExit"); - return; - default: - printk(KERN_WARNING "%s: wrong selection code - irq " - "state=%d\n",dev->name,p_ch->claw_state); -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(2,trace,"badIRQ"); - return; + CLAW_DBF_TEXT(4, trace, "PCINoBH"); + CLAW_DBF_TEXT(4, trace, "PCI_read"); + return; + } + if (!((p_ch->irb->scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS) || + (p_ch->irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND) || + (p_ch->irb->scsw.cmd.stctl == + (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) { + CLAW_DBF_TEXT(4, trace, "SPend_rd"); + return; + } + clear_bit(0, (void *)&p_ch->IO_active); + claw_clearbit_busy(TB_RETRY, dev); + if (test_and_set_bit(CLAW_BH_ACTIVE, + (void *)&p_ch->flag_a) == 0) + tasklet_schedule(&p_ch->tasklet); + else + CLAW_DBF_TEXT(4, trace, "RdBHAct"); + CLAW_DBF_TEXT(4, trace, "RdIRQXit"); + return; + case CLAW_START_WRITE: + if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { + printk(KERN_INFO "%s: Unit Check Occured in " + "write channel\n", dev->name); + clear_bit(0, (void *)&p_ch->IO_active); + if (p_ch->irb->ecw[0] & 0x80) { + printk(KERN_INFO "%s: Resetting Event " + "occurred:\n", dev->name); + init_timer(&p_ch->timer); + p_ch->timer.function = + (void *)claw_write_retry; + p_ch->timer.data = (unsigned long)p_ch; + p_ch->timer.expires = jiffies + 10*HZ; + add_timer(&p_ch->timer); + printk(KERN_INFO "%s: write connection " + "restarting\n", dev->name); + } + CLAW_DBF_TEXT(4, trace, "rstrtwrt"); + return; + } + if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) { + clear_bit(0, (void *)&p_ch->IO_active); + printk(KERN_INFO "%s: Unit Exception " + "Occured in write channel\n", + dev->name); + } + if (!((p_ch->irb->scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS) || + (p_ch->irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND) || + (p_ch->irb->scsw.cmd.stctl == + (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) { + CLAW_DBF_TEXT(4, trace, "writeUE"); + return; + } + clear_bit(0, (void *)&p_ch->IO_active); + if (claw_test_and_setbit_busy(TB_TX, dev) == 0) { + claw_write_next(p_ch); + claw_clearbit_busy(TB_TX, dev); + claw_clear_busy(dev); + } + p_ch_r = (struct chbk *)&privptr->channel[READ]; + if (test_and_set_bit(CLAW_BH_ACTIVE, + (void *)&p_ch_r->flag_a) == 0) + tasklet_schedule(&p_ch_r->tasklet); + CLAW_DBF_TEXT(4, trace, "StWtExit"); + return; + default: + printk(KERN_WARNING "%s: wrong selection code - irq " + "state=%d\n", dev->name, p_ch->claw_state); + CLAW_DBF_TEXT(2, trace, "badIRQ"); + return; } } /* end of claw_irq_handler */ @@ -1013,29 +779,11 @@ claw_irq_tasklet ( unsigned long data ) p_ch = (struct chbk *) data; dev = (struct net_device *)p_ch->ndev; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Enter \n",dev->name,__func__); -#endif -#ifdef DEBUGMSG - printk(KERN_INFO "%s: variable p_ch =\n",dev->name); - dumpit((char *) p_ch, sizeof(struct chbk)); -#endif - CLAW_DBF_TEXT(4,trace,"IRQtask"); - + CLAW_DBF_TEXT(4, trace, "IRQtask"); privptr = (struct claw_privbk *) dev->priv; - -#ifdef DEBUGMSG - printk(KERN_INFO "%s: bh routine - state-%02x\n" , - dev->name, p_ch->claw_state); -#endif - unpack_read(dev); clear_bit(CLAW_BH_ACTIVE, (void *)&p_ch->flag_a); - CLAW_DBF_TEXT(4,trace,"TskletXt"); -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif + CLAW_DBF_TEXT(4, trace, "TskletXt"); return; } /* end of claw_irq_bh */ @@ -1060,16 +808,7 @@ claw_release(struct net_device *dev) privptr = (struct claw_privbk *) dev->priv; if (!privptr) return 0; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Enter \n",dev->name,__func__); -#endif - CLAW_DBF_TEXT(4,trace,"release"); -#ifdef DEBUGMSG - printk(KERN_INFO "%s: variable dev =\n",dev->name); - dumpit((char *) dev, sizeof(struct net_device)); - printk(KERN_INFO "Priv Buffalloc %d\n",privptr->buffs_alloc); - printk(KERN_INFO "Priv p_buff_ccw = %p\n",&privptr->p_buff_ccw); -#endif + CLAW_DBF_TEXT(4, trace, "release"); privptr->release_pend=1; claw_setbit_busy(TB_STOP,dev); for ( i = 1; i >=0 ; i--) { @@ -1101,19 +840,15 @@ claw_release(struct net_device *dev) privptr->pk_skb = NULL; } if(privptr->buffs_alloc != 1) { -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(4,trace,"none2fre"); + CLAW_DBF_TEXT(4, trace, "none2fre"); return 0; } - CLAW_DBF_TEXT(4,trace,"freebufs"); + CLAW_DBF_TEXT(4, trace, "freebufs"); if (privptr->p_buff_ccw != NULL) { free_pages((unsigned long)privptr->p_buff_ccw, (int)pages_to_order_of_mag(privptr->p_buff_ccw_num)); } - CLAW_DBF_TEXT(4,trace,"freeread"); + CLAW_DBF_TEXT(4, trace, "freeread"); if (privptr->p_env->read_size < PAGE_SIZE) { if (privptr->p_buff_read != NULL) { free_pages((unsigned long)privptr->p_buff_read, @@ -1129,7 +864,7 @@ claw_release(struct net_device *dev) p_buf=p_buf->next; } } - CLAW_DBF_TEXT(4,trace,"freewrit"); + CLAW_DBF_TEXT(4, trace, "freewrit"); if (privptr->p_env->write_size < PAGE_SIZE ) { free_pages((unsigned long)privptr->p_buff_write, (int)pages_to_order_of_mag(privptr->p_buff_write_num)); @@ -1143,7 +878,7 @@ claw_release(struct net_device *dev) p_buf=p_buf->next; } } - CLAW_DBF_TEXT(4,trace,"clearptr"); + CLAW_DBF_TEXT(4, trace, "clearptr"); privptr->buffs_alloc = 0; privptr->p_buff_ccw=NULL; privptr->p_buff_read=NULL; @@ -1180,18 +915,12 @@ claw_release(struct net_device *dev) dev->name, privptr->channel[READ].last_dstat, privptr->channel[WRITE].last_dstat); - CLAW_DBF_TEXT(2,trace,"badclose"); + CLAW_DBF_TEXT(2, trace, "badclose"); } -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(4,trace,"rlsexit"); + CLAW_DBF_TEXT(4, trace, "rlsexit"); return 0; } /* end of claw_release */ - - /*-------------------------------------------------------------------* * claw_write_retry * * * @@ -1203,32 +932,12 @@ claw_write_retry ( struct chbk *p_ch ) struct net_device *dev=p_ch->ndev; - -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Enter\n",dev->name,__func__); - printk(KERN_INFO "claw: variable p_ch =\n"); - dumpit((char *) p_ch, sizeof(struct chbk)); -#endif - CLAW_DBF_TEXT(4,trace,"w_retry"); + CLAW_DBF_TEXT(4, trace, "w_retry"); if (p_ch->claw_state == CLAW_STOP) { -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif return; } -#ifdef DEBUGMSG - printk( KERN_INFO "%s:%s state-%02x\n" , - dev->name, - __func__, - p_ch->claw_state); -#endif claw_strt_out_IO( dev ); -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(4,trace,"rtry_xit"); + CLAW_DBF_TEXT(4, trace, "rtry_xit"); return; } /* end of claw_write_retry */ @@ -1247,12 +956,7 @@ claw_write_next ( struct chbk * p_ch ) struct sk_buff *pk_skb; int rc; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Enter \n",p_ch->ndev->name,__func__); - printk(KERN_INFO "%s: variable p_ch =\n",p_ch->ndev->name); - dumpit((char *) p_ch, sizeof(struct chbk)); -#endif - CLAW_DBF_TEXT(4,trace,"claw_wrt"); + CLAW_DBF_TEXT(4, trace, "claw_wrt"); if (p_ch->claw_state == CLAW_STOP) return; dev = (struct net_device *) p_ch->ndev; @@ -1272,11 +976,6 @@ claw_write_next ( struct chbk * p_ch ) if (privptr->p_write_active_first!=NULL) { claw_strt_out_IO(dev); } - -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif return; } /* end of claw_write_next */ @@ -1288,22 +987,12 @@ claw_write_next ( struct chbk * p_ch ) static void claw_timer ( struct chbk * p_ch ) { -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Entry\n",p_ch->ndev->name,__func__); - printk(KERN_INFO "%s: variable p_ch =\n",p_ch->ndev->name); - dumpit((char *) p_ch, sizeof(struct chbk)); -#endif - CLAW_DBF_TEXT(4,trace,"timer"); + CLAW_DBF_TEXT(4, trace, "timer"); p_ch->flag |= CLAW_TIMER; wake_up(&p_ch->wait); -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - p_ch->ndev->name,__func__,__LINE__); -#endif return; } /* end of claw_timer */ - /* * * functions @@ -1324,10 +1013,8 @@ pages_to_order_of_mag(int num_of_pages) { int order_of_mag=1; /* assume 2 pages */ int nump=2; -#ifdef FUNCTRACE - printk(KERN_INFO "%s Enter pages = %d \n",__func__,num_of_pages); -#endif - CLAW_DBF_TEXT_(5,trace,"pages%d",num_of_pages); + + CLAW_DBF_TEXT_(5, trace, "pages%d", num_of_pages); if (num_of_pages == 1) {return 0; } /* magnitude of 0 = 1 page */ /* 512 pages = 2Meg on 4k page systems */ if (num_of_pages >= 512) {return 9; } @@ -1338,11 +1025,7 @@ pages_to_order_of_mag(int num_of_pages) order_of_mag +=1; } if (order_of_mag > 9) { order_of_mag = 9; } /* I know it's paranoid */ -#ifdef FUNCTRACE - printk(KERN_INFO "%s Exit on line %d, order = %d\n", - __func__,__LINE__, order_of_mag); -#endif - CLAW_DBF_TEXT_(5,trace,"mag%d",order_of_mag); + CLAW_DBF_TEXT_(5, trace, "mag%d", order_of_mag); return order_of_mag; } @@ -1358,21 +1041,7 @@ add_claw_reads(struct net_device *dev, struct ccwbk* p_first, struct claw_privbk *privptr; struct ccw1 temp_ccw; struct endccw * p_end; -#ifdef IOTRACE - struct ccwbk* p_buf; -#endif -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Enter \n",dev->name,__func__); -#endif -#ifdef DEBUGMSG - printk(KERN_INFO "dev\n"); - dumpit((char *) dev, sizeof(struct net_device)); - printk(KERN_INFO "p_first\n"); - dumpit((char *) p_first, sizeof(struct ccwbk)); - printk(KERN_INFO "p_last\n"); - dumpit((char *) p_last, sizeof(struct ccwbk)); -#endif - CLAW_DBF_TEXT(4,trace,"addreads"); + CLAW_DBF_TEXT(4, trace, "addreads"); privptr = dev->priv; p_end = privptr->p_end_ccw; @@ -1380,11 +1049,7 @@ add_claw_reads(struct net_device *dev, struct ccwbk* p_first, * to apend the running channel programs */ if ( p_first==NULL) { -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(4,trace,"addexit"); + CLAW_DBF_TEXT(4, trace, "addexit"); return 0; } @@ -1411,21 +1076,11 @@ add_claw_reads(struct net_device *dev, struct ccwbk* p_first, } if ( privptr-> p_read_active_first ==NULL ) { -#ifdef DEBUGMSG - printk(KERN_INFO "%s:%s p_read_active_first == NULL \n", - dev->name,__func__); - printk(KERN_INFO "%s:%s Read active first/last changed \n", - dev->name,__func__); -#endif privptr-> p_read_active_first= p_first; /* set new first */ privptr-> p_read_active_last = p_last; /* set new last */ } else { -#ifdef DEBUGMSG - printk(KERN_INFO "%s:%s Read in progress \n", - dev->name,__func__); -#endif /* set up TIC ccw */ temp_ccw.cda= (__u32)__pa(&p_first->read); temp_ccw.count=0; @@ -1462,27 +1117,7 @@ add_claw_reads(struct net_device *dev, struct ccwbk* p_first, privptr->p_read_active_last->next = p_first; privptr->p_read_active_last=p_last; } /* end of if ( privptr-> p_read_active_first ==NULL) */ -#ifdef IOTRACE - printk(KERN_INFO "%s:%s dump p_last CCW BK \n",dev->name,__func__); - dumpit((char *)p_last, sizeof(struct ccwbk)); - printk(KERN_INFO "%s:%s dump p_end CCW BK \n",dev->name,__func__); - dumpit((char *)p_end, sizeof(struct endccw)); - - printk(KERN_INFO "%s:%s dump p_first CCW BK \n",dev->name,__func__); - dumpit((char *)p_first, sizeof(struct ccwbk)); - printk(KERN_INFO "%s:%s Dump Active CCW chain \n", - dev->name,__func__); - p_buf=privptr->p_read_active_first; - while (p_buf!=NULL) { - dumpit((char *)p_buf, sizeof(struct ccwbk)); - p_buf=p_buf->next; - } -#endif -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(4,trace,"addexit"); + CLAW_DBF_TEXT(4, trace, "addexit"); return 0; } /* end of add_claw_reads */ @@ -1494,44 +1129,29 @@ add_claw_reads(struct net_device *dev, struct ccwbk* p_first, static void ccw_check_return_code(struct ccw_device *cdev, int return_code) { -#ifdef FUNCTRACE - printk(KERN_INFO "%s: %s() > enter \n", - cdev->dev.bus_id,__func__); -#endif - CLAW_DBF_TEXT(4,trace,"ccwret"); -#ifdef DEBUGMSG - printk(KERN_INFO "variable cdev =\n"); - dumpit((char *) cdev, sizeof(struct ccw_device)); - printk(KERN_INFO "variable return_code = %d\n",return_code); -#endif + CLAW_DBF_TEXT(4, trace, "ccwret"); if (return_code != 0) { switch (return_code) { - case -EBUSY: - printk(KERN_INFO "%s: Busy !\n", - cdev->dev.bus_id); - break; - case -ENODEV: - printk(KERN_EMERG "%s: Missing device called " - "for IO ENODEV\n", cdev->dev.bus_id); - break; - case -EIO: - printk(KERN_EMERG "%s: Status pending... EIO \n", - cdev->dev.bus_id); - break; - case -EINVAL: - printk(KERN_EMERG "%s: Invalid Dev State EINVAL \n", - cdev->dev.bus_id); - break; - default: - printk(KERN_EMERG "%s: Unknown error in " + case -EBUSY: /* BUSY is a transient state no action needed */ + break; + case -ENODEV: + printk(KERN_EMERG "%s: Missing device called " + "for IO ENODEV\n", cdev->dev.bus_id); + break; + case -EIO: + printk(KERN_EMERG "%s: Status pending... EIO \n", + cdev->dev.bus_id); + break; + case -EINVAL: + printk(KERN_EMERG "%s: Invalid Dev State EINVAL \n", + cdev->dev.bus_id); + break; + default: + printk(KERN_EMERG "%s: Unknown error in " "Do_IO %d\n",cdev->dev.bus_id, return_code); - } - } -#ifdef FUNCTRACE - printk(KERN_INFO "%s: %s() > exit on line %d\n", - cdev->dev.bus_id,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(4,trace,"ccwret"); + } + } + CLAW_DBF_TEXT(4, trace, "ccwret"); } /* end of ccw_check_return_code */ /*-------------------------------------------------------------------* @@ -1541,173 +1161,46 @@ ccw_check_return_code(struct ccw_device *cdev, int return_code) static void ccw_check_unit_check(struct chbk * p_ch, unsigned char sense ) { - struct net_device *dev = p_ch->ndev; - -#ifdef FUNCTRACE - printk(KERN_INFO "%s: %s() > enter\n",dev->name,__func__); -#endif -#ifdef DEBUGMSG - printk(KERN_INFO "%s: variable dev =\n",dev->name); - dumpit((char *)dev, sizeof(struct net_device)); - printk(KERN_INFO "%s: variable sense =\n",dev->name); - dumpit((char *)&sense, 2); -#endif - CLAW_DBF_TEXT(4,trace,"unitchek"); + struct net_device *ndev = p_ch->ndev; + CLAW_DBF_TEXT(4, trace, "unitchek"); printk(KERN_INFO "%s: Unit Check with sense byte:0x%04x\n", - dev->name, sense); + ndev->name, sense); if (sense & 0x40) { if (sense & 0x01) { printk(KERN_WARNING "%s: Interface disconnect or " "Selective reset " - "occurred (remote side)\n", dev->name); + "occurred (remote side)\n", ndev->name); } else { printk(KERN_WARNING "%s: System reset occured" - " (remote side)\n", dev->name); + " (remote side)\n", ndev->name); } } else if (sense & 0x20) { if (sense & 0x04) { printk(KERN_WARNING "%s: Data-streaming " - "timeout)\n", dev->name); + "timeout)\n", ndev->name); } else { printk(KERN_WARNING "%s: Data-transfer parity" - " error\n", dev->name); + " error\n", ndev->name); } } else if (sense & 0x10) { if (sense & 0x20) { printk(KERN_WARNING "%s: Hardware malfunction " - "(remote side)\n", dev->name); + "(remote side)\n", ndev->name); } else { printk(KERN_WARNING "%s: read-data parity error " - "(remote side)\n", dev->name); + "(remote side)\n", ndev->name); } } -#ifdef FUNCTRACE - printk(KERN_INFO "%s: %s() exit on line %d\n", - dev->name,__func__,__LINE__); -#endif } /* end of ccw_check_unit_check */ - - -/*-------------------------------------------------------------------* -* Dump buffer format * -* * -*--------------------------------------------------------------------*/ -#ifdef DEBUG -static void -dumpit(char* buf, int len) -{ - - __u32 ct, sw, rm, dup; - char *ptr, *rptr; - char tbuf[82], tdup[82]; -#if (CONFIG_64BIT) - char addr[22]; -#else - char addr[12]; -#endif - char boff[12]; - char bhex[82], duphex[82]; - char basc[40]; - - sw = 0; - rptr =ptr=buf; - rm = 16; - duphex[0] = 0x00; - dup = 0; - for ( ct=0; ct < len; ct++, ptr++, rptr++ ) { - if (sw == 0) { -#if (CONFIG_64BIT) - sprintf(addr, "%16.16lX",(unsigned long)rptr); -#else - sprintf(addr, "%8.8X",(__u32)rptr); -#endif - sprintf(boff, "%4.4X", (__u32)ct); - bhex[0] = '\0'; - basc[0] = '\0'; - } - if ((sw == 4) || (sw == 12)) { - strcat(bhex, " "); - } - if (sw == 8) { - strcat(bhex, " "); - } -#if (CONFIG_64BIT) - sprintf(tbuf,"%2.2lX", (unsigned long)*ptr); -#else - sprintf(tbuf,"%2.2X", (__u32)*ptr); -#endif - tbuf[2] = '\0'; - strcat(bhex, tbuf); - if ((0!=isprint(*ptr)) && (*ptr >= 0x20)) { - basc[sw] = *ptr; - } - else { - basc[sw] = '.'; - } - basc[sw+1] = '\0'; - sw++; - rm--; - if (sw==16) { - if ((strcmp(duphex, bhex)) !=0) { - if (dup !=0) { - sprintf(tdup,"Duplicate as above to" - " %s", addr); - printk( KERN_INFO " " - " --- %s ---\n",tdup); - } - printk( KERN_INFO " %s (+%s) : %s [%s]\n", - addr, boff, bhex, basc); - dup = 0; - strcpy(duphex, bhex); - } - else { - dup++; - } - sw = 0; - rm = 16; - } - } /* endfor */ - - if (sw != 0) { - for ( ; rm > 0; rm--, sw++ ) { - if ((sw==4) || (sw==12)) strcat(bhex, " "); - if (sw==8) strcat(bhex, " "); - strcat(bhex, " "); - strcat(basc, " "); - } - if (dup !=0) { - sprintf(tdup,"Duplicate as above to %s", addr); - printk( KERN_INFO " --- %s ---\n", - tdup); - } - printk( KERN_INFO " %s (+%s) : %s [%s]\n", - addr, boff, bhex, basc); - } - else { - if (dup >=1) { - sprintf(tdup,"Duplicate as above to %s", addr); - printk( KERN_INFO " --- %s ---\n", - tdup); - } - if (dup !=0) { - printk( KERN_INFO " %s (+%s) : %s [%s]\n", - addr, boff, bhex, basc); - } - } - return; - -} /* end of dumpit */ -#endif - /*-------------------------------------------------------------------* * find_link * *--------------------------------------------------------------------*/ @@ -1718,16 +1211,7 @@ find_link(struct net_device *dev, char *host_name, char *ws_name ) struct claw_env *p_env; int rc=0; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s > enter \n",dev->name,__func__); -#endif - CLAW_DBF_TEXT(2,setup,"findlink"); -#ifdef DEBUGMSG - printk(KERN_INFO "%s: variable dev = \n",dev->name); - dumpit((char *) dev, sizeof(struct net_device)); - printk(KERN_INFO "%s: variable host_name = %s\n",dev->name, host_name); - printk(KERN_INFO "%s: variable ws_name = %s\n",dev->name, ws_name); -#endif + CLAW_DBF_TEXT(2, setup, "findlink"); privptr=dev->priv; p_env=privptr->p_env; switch (p_env->packing) @@ -1750,10 +1234,6 @@ find_link(struct net_device *dev, char *host_name, char *ws_name ) break; } -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif return 0; } /* end of find_link */ @@ -1782,27 +1262,11 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid) int lock; struct clawph *pk_head; struct chbk *ch; -#ifdef IOTRACE - struct ccwbk *p_buf; -#endif -#ifdef FUNCTRACE - printk(KERN_INFO "%s: %s() > enter\n",dev->name,__func__); -#endif - CLAW_DBF_TEXT(4,trace,"hw_tx"); -#ifdef DEBUGMSG - printk(KERN_INFO "%s: variable dev skb =\n",dev->name); - dumpit((char *) skb, sizeof(struct sk_buff)); - printk(KERN_INFO "%s: variable dev =\n",dev->name); - dumpit((char *) dev, sizeof(struct net_device)); - printk(KERN_INFO "%s: variable linkid = %ld\n",dev->name,linkid); -#endif + + CLAW_DBF_TEXT(4, trace, "hw_tx"); privptr = (struct claw_privbk *) (dev->priv); p_ch=(struct chbk *)&privptr->channel[WRITE]; p_env =privptr->p_env; -#ifdef IOTRACE - printk(KERN_INFO "%s: %s() dump sk_buff \n",dev->name,__func__); - dumpit((char *)skb ,sizeof(struct sk_buff)); -#endif claw_free_wrt_buf(dev); /* Clean up free chain if posible */ /* scan the write queue to free any completed write packets */ p_first_ccw=NULL; @@ -1834,11 +1298,6 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid) claw_strt_out_IO(dev ); claw_free_wrt_buf( dev ); if (privptr->write_free_count==0) { -#ifdef IOTRACE - printk(KERN_INFO "%s: " - "(claw_check_busy) no free write " - "buffers\n", dev->name); -#endif ch = &privptr->channel[WRITE]; atomic_inc(&skb->users); skb_queue_tail(&ch->collect_queue, skb); @@ -1851,10 +1310,6 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid) } /* tx lock */ if (claw_test_and_setbit_busy(TB_TX,dev)) { /* set to busy */ -#ifdef DEBUGMSG - printk(KERN_INFO "%s: busy (claw_test_and_setbit_" - "busy)\n", dev->name); -#endif ch = &privptr->channel[WRITE]; atomic_inc(&skb->users); skb_queue_tail(&ch->collect_queue, skb); @@ -1871,28 +1326,16 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid) privptr->p_write_free_chain == NULL ) { claw_setbit_busy(TB_NOBUFFER,dev); - -#ifdef DEBUGMSG - printk(KERN_INFO "%s: busy (claw_setbit_busy" - "(TB_NOBUFFER))\n", dev->name); - printk(KERN_INFO " free_count: %d, numBuffers : %d\n", - (int)privptr->write_free_count,(int) numBuffers ); -#endif ch = &privptr->channel[WRITE]; atomic_inc(&skb->users); skb_queue_tail(&ch->collect_queue, skb); - CLAW_DBF_TEXT(2,trace,"clawbusy"); + CLAW_DBF_TEXT(2, trace, "clawbusy"); goto Done2; } pDataAddress=skb->data; len_of_data=skb->len; while (len_of_data > 0) { -#ifdef DEBUGMSG - printk(KERN_INFO "%s: %s() length-of-data is %ld \n", - dev->name ,__func__,len_of_data); - dumpit((char *)pDataAddress ,64); -#endif p_this_ccw=privptr->p_write_free_chain; /* get a block */ if (p_this_ccw == NULL) { /* lost the race */ ch = &privptr->channel[WRITE]; @@ -1924,12 +1367,6 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid) (__u32)__pa(&p_this_ccw->write); } p_last_ccw=p_this_ccw; /* save new last block */ -#ifdef IOTRACE - printk(KERN_INFO "%s: %s() > CCW and Buffer %ld bytes long \n", - dev->name,__func__,bytesInThisBuffer); - dumpit((char *)p_this_ccw, sizeof(struct ccwbk)); - dumpit((char *)p_this_ccw->p_buffer, 64); -#endif } /* FirstCCW and LastCCW now contain a new set of write channel @@ -1962,13 +1399,11 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid) pEnd->write1_nop2.count=1; } /* end if if (pEnd->write1) */ - if (privptr->p_write_active_first==NULL ) { privptr->p_write_active_first=p_first_ccw; privptr->p_write_active_last=p_last_ccw; } else { - /* set up Tic CCWs */ tempCCW.cda=(__u32)__pa(&p_first_ccw->write); @@ -2007,19 +1442,6 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid) } } /* endif (p_first_ccw!=NULL) */ - - -#ifdef IOTRACE - printk(KERN_INFO "%s: %s() > Dump Active CCW chain \n", - dev->name,__func__); - p_buf=privptr->p_write_active_first; - while (p_buf!=NULL) { - dumpit((char *)p_buf, sizeof(struct ccwbk)); - p_buf=p_buf->next; - } - p_buf=(struct ccwbk*)privptr->p_end_ccw; - dumpit((char *)p_buf, sizeof(struct endccw)); -#endif dev_kfree_skb_any(skb); if (linkid==0) { lock=LOCK_NO; @@ -2029,21 +1451,12 @@ claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid) } claw_strt_out_IO(dev ); /* if write free count is zero , set NOBUFFER */ -#ifdef DEBUGMSG - printk(KERN_INFO "%s: %s() > free_count is %d\n", - dev->name,__func__, - (int) privptr->write_free_count ); -#endif if (privptr->write_free_count==0) { claw_setbit_busy(TB_NOBUFFER,dev); } Done2: claw_clearbit_busy(TB_TX,dev); Done: -#ifdef FUNCTRACE - printk(KERN_INFO "%s: %s() > exit on line %d, rc = %d \n", - dev->name,__func__,__LINE__, rc); -#endif return(rc); } /* end of claw_hw_tx */ @@ -2075,14 +1488,7 @@ init_ccw_bk(struct net_device *dev) struct clawh *pClawH=NULL; addr_t real_TIC_address; int i,j; -#ifdef FUNCTRACE - printk(KERN_INFO "%s: %s() enter \n",dev->name,__func__); -#endif - CLAW_DBF_TEXT(4,trace,"init_ccw"); -#ifdef DEBUGMSG - printk(KERN_INFO "%s: variable dev =\n",dev->name); - dumpit((char *) dev, sizeof(struct net_device)); -#endif + CLAW_DBF_TEXT(4, trace, "init_ccw"); /* initialize statistics field */ privptr->active_link_ID=0; @@ -2107,20 +1513,6 @@ init_ccw_bk(struct net_device *dev) */ ccw_blocks_required = privptr->p_env->read_buffers+privptr->p_env->write_buffers+1; -#ifdef DEBUGMSG - printk(KERN_INFO "%s: %s() " - "ccw_blocks_required=%d\n", - dev->name,__func__, - ccw_blocks_required); - printk(KERN_INFO "%s: %s() " - "PAGE_SIZE=0x%x\n", - dev->name,__func__, - (unsigned int)PAGE_SIZE); - printk(KERN_INFO "%s: %s() > " - "PAGE_MASK=0x%x\n", - dev->name,__func__, - (unsigned int)PAGE_MASK); -#endif /* * compute number of CCW blocks that will fit in a page */ @@ -2128,14 +1520,6 @@ init_ccw_bk(struct net_device *dev) ccw_pages_required= DIV_ROUND_UP(ccw_blocks_required, ccw_blocks_perpage); -#ifdef DEBUGMSG - printk(KERN_INFO "%s: %s() > ccw_blocks_perpage=%d\n", - dev->name,__func__, - ccw_blocks_perpage); - printk(KERN_INFO "%s: %s() > ccw_pages_required=%d\n", - dev->name,__func__, - ccw_pages_required); -#endif /* * read and write sizes are set by 2 constants in claw.h * 4k and 32k. Unpacked values other than 4k are not going to @@ -2166,36 +1550,6 @@ init_ccw_bk(struct net_device *dev) claw_write_pages = privptr->p_env->write_buffers * privptr->p_buff_pages_perwrite; } -#ifdef DEBUGMSG - if (privptr->p_env->read_size < PAGE_SIZE) { - printk(KERN_INFO "%s: %s() reads_perpage=%d\n", - dev->name,__func__, - claw_reads_perpage); - } - else { - printk(KERN_INFO "%s: %s() pages_perread=%d\n", - dev->name,__func__, - privptr->p_buff_pages_perread); - } - printk(KERN_INFO "%s: %s() read_pages=%d\n", - dev->name,__func__, - claw_read_pages); - if (privptr->p_env->write_size < PAGE_SIZE) { - printk(KERN_INFO "%s: %s() writes_perpage=%d\n", - dev->name,__func__, - claw_writes_perpage); - } - else { - printk(KERN_INFO "%s: %s() pages_perwrite=%d\n", - dev->name,__func__, - privptr->p_buff_pages_perwrite); - } - printk(KERN_INFO "%s: %s() write_pages=%d\n", - dev->name,__func__, - claw_write_pages); -#endif - - /* * allocate ccw_pages_required */ @@ -2204,17 +1558,6 @@ init_ccw_bk(struct net_device *dev) (void *)__get_free_pages(__GFP_DMA, (int)pages_to_order_of_mag(ccw_pages_required )); if (privptr->p_buff_ccw==NULL) { - printk(KERN_INFO "%s: %s() " - "__get_free_pages for CCWs failed : " - "pages is %d\n", - dev->name,__func__, - ccw_pages_required ); -#ifdef FUNCTRACE - printk(KERN_INFO "%s: %s() > " - "exit on line %d, rc = ENOMEM\n", - dev->name,__func__, - __LINE__); -#endif return -ENOMEM; } privptr->p_buff_ccw_num=ccw_pages_required; @@ -2229,11 +1572,6 @@ init_ccw_bk(struct net_device *dev) privptr->p_end_ccw = (struct endccw *)&privptr->end_ccw; real_address = (__u32)__pa(privptr->p_end_ccw); /* Initialize ending CCW block */ -#ifdef DEBUGMSG - printk(KERN_INFO "%s: %s() begin initialize ending CCW blocks\n", - dev->name,__func__); -#endif - p_endccw=privptr->p_end_ccw; p_endccw->real=real_address; p_endccw->write1=0x00; @@ -2287,21 +1625,10 @@ init_ccw_bk(struct net_device *dev) p_endccw->read2_nop2.count = 1; p_endccw->read2_nop2.cda = 0; -#ifdef IOTRACE - printk(KERN_INFO "%s: %s() dump claw ending CCW BK \n", - dev->name,__func__); - dumpit((char *)p_endccw, sizeof(struct endccw)); -#endif - /* * Build a chain of CCWs * */ - -#ifdef DEBUGMSG - printk(KERN_INFO "%s: %s() Begin build a chain of CCW buffer \n", - dev->name,__func__); -#endif p_buff=privptr->p_buff_ccw; p_free_chain=NULL; @@ -2316,26 +1643,10 @@ init_ccw_bk(struct net_device *dev) } p_buff+=PAGE_SIZE; } -#ifdef DEBUGMSG - printk(KERN_INFO "%s: %s() " - "End build a chain of CCW buffer \n", - dev->name,__func__); - p_buf=p_free_chain; - while (p_buf!=NULL) { - dumpit((char *)p_buf, sizeof(struct ccwbk)); - p_buf=p_buf->next; - } -#endif - /* * Initialize ClawSignalBlock * */ -#ifdef DEBUGMSG - printk(KERN_INFO "%s: %s() " - "Begin initialize ClawSignalBlock \n", - dev->name,__func__); -#endif if (privptr->p_claw_signal_blk==NULL) { privptr->p_claw_signal_blk=p_free_chain; p_free_chain=p_free_chain->next; @@ -2344,12 +1655,6 @@ init_ccw_bk(struct net_device *dev) pClawH->opcode=0xff; pClawH->flag=CLAW_BUSY; } -#ifdef DEBUGMSG - printk(KERN_INFO "%s: %s() > End initialize " - "ClawSignalBlock\n", - dev->name,__func__); - dumpit((char *)privptr->p_claw_signal_blk, sizeof(struct ccwbk)); -#endif /* * allocate write_pages_required and add to free chain @@ -2360,17 +1665,7 @@ init_ccw_bk(struct net_device *dev) (void *)__get_free_pages(__GFP_DMA, (int)pages_to_order_of_mag(claw_write_pages )); if (privptr->p_buff_write==NULL) { - printk(KERN_INFO "%s: %s() __get_free_pages for write" - " bufs failed : get is for %d pages\n", - dev->name,__func__,claw_write_pages ); - free_pages((unsigned long)privptr->p_buff_ccw, - (int)pages_to_order_of_mag(privptr->p_buff_ccw_num)); privptr->p_buff_ccw=NULL; -#ifdef FUNCTRACE - printk(KERN_INFO "%s: %s() > exit on line %d," - "rc = ENOMEM\n", - dev->name,__func__,__LINE__); -#endif return -ENOMEM; } /* @@ -2380,10 +1675,6 @@ init_ccw_bk(struct net_device *dev) memset(privptr->p_buff_write, 0x00, ccw_pages_required * PAGE_SIZE); -#ifdef DEBUGMSG - printk(KERN_INFO "%s: %s() Begin build claw write free " - "chain \n",dev->name,__func__); -#endif privptr->p_write_free_chain=NULL; p_buff=privptr->p_buff_write; @@ -2419,18 +1710,7 @@ init_ccw_bk(struct net_device *dev) p_buff=(void *)__get_free_pages(__GFP_DMA, (int)pages_to_order_of_mag( privptr->p_buff_pages_perwrite) ); -#ifdef IOTRACE - printk(KERN_INFO "%s:%s __get_free_pages " - "for writes buf: get for %d pages\n", - dev->name,__func__, - privptr->p_buff_pages_perwrite); -#endif if (p_buff==NULL) { - printk(KERN_INFO "%s:%s __get_free_pages " - "for writes buf failed : get is for %d pages\n", - dev->name, - __func__, - privptr->p_buff_pages_perwrite ); free_pages((unsigned long)privptr->p_buff_ccw, (int)pages_to_order_of_mag( privptr->p_buff_ccw_num)); @@ -2443,12 +1723,6 @@ init_ccw_bk(struct net_device *dev) privptr->p_buff_pages_perwrite)); p_buf=p_buf->next; } -#ifdef FUNCTRACE - printk(KERN_INFO "%s: %s exit on line %d, rc = ENOMEM\n", - dev->name, - __func__, - __LINE__); -#endif return -ENOMEM; } /* Error on get_pages */ memset(p_buff, 0x00, privptr->p_env->write_size ); @@ -2477,15 +1751,6 @@ init_ccw_bk(struct net_device *dev) privptr->write_free_count=privptr->p_env->write_buffers; -#ifdef DEBUGMSG - printk(KERN_INFO "%s:%s End build claw write free chain \n", - dev->name,__func__); - p_buf=privptr->p_write_free_chain; - while (p_buf!=NULL) { - dumpit((char *)p_buf, sizeof(struct ccwbk)); - p_buf=p_buf->next; - } -#endif /* * allocate read_pages_required and chain to free chain */ @@ -2495,10 +1760,6 @@ init_ccw_bk(struct net_device *dev) (void *)__get_free_pages(__GFP_DMA, (int)pages_to_order_of_mag(claw_read_pages) ); if (privptr->p_buff_read==NULL) { - printk(KERN_INFO "%s: %s() " - "__get_free_pages for read buf failed : " - "get is for %d pages\n", - dev->name,__func__,claw_read_pages ); free_pages((unsigned long)privptr->p_buff_ccw, (int)pages_to_order_of_mag( privptr->p_buff_ccw_num)); @@ -2508,10 +1769,6 @@ init_ccw_bk(struct net_device *dev) privptr->p_buff_write_num)); privptr->p_buff_ccw=NULL; privptr->p_buff_write=NULL; -#ifdef FUNCTRACE - printk(KERN_INFO "%s: %s() > exit on line %d, rc =" - " ENOMEM\n",dev->name,__func__,__LINE__); -#endif return -ENOMEM; } memset(privptr->p_buff_read, 0x00, claw_read_pages * PAGE_SIZE); @@ -2520,10 +1777,6 @@ init_ccw_bk(struct net_device *dev) * Build CLAW read free chain * */ -#ifdef DEBUGMSG - printk(KERN_INFO "%s: %s() Begin build claw read free chain \n", - dev->name,__func__); -#endif p_buff=privptr->p_buff_read; for (i=0 ; i< privptr->p_env->read_buffers ; i++) { p_buf = p_free_chain; @@ -2600,19 +1853,10 @@ init_ccw_bk(struct net_device *dev) } /* for read_buffers */ } /* read_size < PAGE_SIZE */ else { /* read Size >= PAGE_SIZE */ - -#ifdef DEBUGMSG - printk(KERN_INFO "%s: %s() Begin build claw read free chain \n", - dev->name,__func__); -#endif for (i=0 ; i< privptr->p_env->read_buffers ; i++) { p_buff = (void *)__get_free_pages(__GFP_DMA, (int)pages_to_order_of_mag(privptr->p_buff_pages_perread) ); if (p_buff==NULL) { - printk(KERN_INFO "%s: %s() __get_free_pages for read " - "buf failed : get is for %d pages\n", - dev->name,__func__, - privptr->p_buff_pages_perread ); free_pages((unsigned long)privptr->p_buff_ccw, (int)pages_to_order_of_mag(privptr->p_buff_ccw_num)); /* free the write pages */ @@ -2633,11 +1877,6 @@ init_ccw_bk(struct net_device *dev) } privptr->p_buff_ccw=NULL; privptr->p_buff_write=NULL; -#ifdef FUNCTRACE - printk(KERN_INFO "%s: %s() exit on line %d, rc = ENOMEM\n", - dev->name,__func__, - __LINE__); -#endif return -ENOMEM; } memset(p_buff, 0x00, privptr->p_env->read_size); @@ -2706,22 +1945,9 @@ init_ccw_bk(struct net_device *dev) } /* For read_buffers */ } /* read_size >= PAGE_SIZE */ } /* pBuffread = NULL */ -#ifdef DEBUGMSG - printk(KERN_INFO "%s: %s() > End build claw read free chain \n", - dev->name,__func__); - p_buf=p_first_CCWB; - while (p_buf!=NULL) { - dumpit((char *)p_buf, sizeof(struct ccwbk)); - p_buf=p_buf->next; - } - -#endif add_claw_reads( dev ,p_first_CCWB , p_last_CCWB); privptr->buffs_alloc = 1; -#ifdef FUNCTRACE - printk(KERN_INFO "%s: %s() exit on line %d\n", - dev->name,__func__,__LINE__); -#endif + return 0; } /* end of init_ccw_bk */ @@ -2735,14 +1961,8 @@ static void probe_error( struct ccwgroup_device *cgdev) { struct claw_privbk *privptr; -#ifdef FUNCTRACE - printk(KERN_INFO "%s enter \n",__func__); -#endif - CLAW_DBF_TEXT(4,trace,"proberr"); -#ifdef DEBUGMSG - printk(KERN_INFO "%s variable cgdev =\n",__func__); - dumpit((char *) cgdev, sizeof(struct ccwgroup_device)); -#endif + + CLAW_DBF_TEXT(4, trace, "proberr"); privptr=(struct claw_privbk *)cgdev->dev.driver_data; if (privptr!=NULL) { kfree(privptr->p_env); @@ -2752,16 +1972,9 @@ probe_error( struct ccwgroup_device *cgdev) kfree(privptr); privptr=NULL; } -#ifdef FUNCTRACE - printk(KERN_INFO "%s > exit on line %d\n", - __func__,__LINE__); -#endif - return; } /* probe_error */ - - /*-------------------------------------------------------------------* * claw_process_control * * * @@ -2783,32 +1996,19 @@ claw_process_control( struct net_device *dev, struct ccwbk * p_ccw) struct conncmd *p_connect=NULL; int rc; struct chbk *p_ch = NULL; -#ifdef FUNCTRACE - printk(KERN_INFO "%s: %s() > enter \n", - dev->name,__func__); -#endif - CLAW_DBF_TEXT(2,setup,"clw_cntl"); -#ifdef DEBUGMSG - printk(KERN_INFO "%s: variable dev =\n",dev->name); - dumpit((char *) dev, sizeof(struct net_device)); - printk(KERN_INFO "%s: variable p_ccw =\n",dev->name); - dumpit((char *) p_ccw, sizeof(struct ccwbk *)); -#endif + struct device *tdev; + CLAW_DBF_TEXT(2, setup, "clw_cntl"); udelay(1000); /* Wait a ms for the control packets to *catch up to each other */ privptr=dev->priv; p_env=privptr->p_env; + tdev = &privptr->channel[READ].cdev->dev; memcpy( &temp_host_name, p_env->host_name, 8); memcpy( &temp_ws_name, p_env->adapter_name , 8); printk(KERN_INFO "%s: CLAW device %.8s: " "Received Control Packet\n", dev->name, temp_ws_name); if (privptr->release_pend==1) { -#ifdef FUNCTRACE - printk(KERN_INFO "%s: %s() > " - "exit on line %d, rc=0\n", - dev->name,__func__,__LINE__); -#endif return 0; } p_buf=p_ccw->p_buffer; @@ -2818,261 +2018,246 @@ claw_process_control( struct net_device *dev, struct ccwbk * p_ccw) } else { memcpy(p_ctlbk, p_buf, sizeof(struct clawctl)); } -#ifdef IOTRACE - printk(KERN_INFO "%s: dump claw control data inbound\n",dev->name); - dumpit((char *)p_ctlbk, sizeof(struct clawctl)); -#endif switch (p_ctlbk->command) { - case SYSTEM_VALIDATE_REQUEST: - if (p_ctlbk->version!=CLAW_VERSION_ID) { - claw_snd_sys_validate_rsp(dev, p_ctlbk, - CLAW_RC_WRONG_VERSION ); - printk("%s: %d is wrong version id. " - "Expected %d\n", - dev->name, p_ctlbk->version, - CLAW_VERSION_ID); - } - p_sysval=(struct sysval *)&(p_ctlbk->data); - printk( "%s: Recv Sys Validate Request: " - "Vers=%d,link_id=%d,Corr=%d,WS name=%." - "8s,Host name=%.8s\n", - dev->name, p_ctlbk->version, - p_ctlbk->linkid, - p_ctlbk->correlator, - p_sysval->WS_name, - p_sysval->host_name); - if (0!=memcmp(temp_host_name,p_sysval->host_name,8)) { - claw_snd_sys_validate_rsp(dev, p_ctlbk, - CLAW_RC_NAME_MISMATCH ); - CLAW_DBF_TEXT(2,setup,"HSTBAD"); - CLAW_DBF_TEXT_(2,setup,"%s",p_sysval->host_name); - CLAW_DBF_TEXT_(2,setup,"%s",temp_host_name); - printk(KERN_INFO "%s: Host name mismatch\n", - dev->name); - printk(KERN_INFO "%s: Received :%s: " - "expected :%s: \n", - dev->name, - p_sysval->host_name, - temp_host_name); - } - if (0!=memcmp(temp_ws_name,p_sysval->WS_name,8)) { - claw_snd_sys_validate_rsp(dev, p_ctlbk, - CLAW_RC_NAME_MISMATCH ); - CLAW_DBF_TEXT(2,setup,"WSNBAD"); - CLAW_DBF_TEXT_(2,setup,"%s",p_sysval->WS_name); - CLAW_DBF_TEXT_(2,setup,"%s",temp_ws_name); - printk(KERN_INFO "%s: WS name mismatch\n", - dev->name); - printk(KERN_INFO "%s: Received :%s: " - "expected :%s: \n", - dev->name, - p_sysval->WS_name, - temp_ws_name); - } - if (( p_sysval->write_frame_size < p_env->write_size) && - ( p_env->packing == 0)) { - claw_snd_sys_validate_rsp(dev, p_ctlbk, - CLAW_RC_HOST_RCV_TOO_SMALL ); - printk(KERN_INFO "%s: host write size is too " - "small\n", dev->name); - CLAW_DBF_TEXT(2,setup,"wrtszbad"); - } - if (( p_sysval->read_frame_size < p_env->read_size) && - ( p_env->packing == 0)) { - claw_snd_sys_validate_rsp(dev, p_ctlbk, - CLAW_RC_HOST_RCV_TOO_SMALL ); - printk(KERN_INFO "%s: host read size is too " - "small\n", dev->name); - CLAW_DBF_TEXT(2,setup,"rdsizbad"); - } - claw_snd_sys_validate_rsp(dev, p_ctlbk, 0 ); - printk("%s: CLAW device %.8s: System validate" - " completed.\n",dev->name, temp_ws_name); - printk("%s: sys Validate Rsize:%d Wsize:%d\n",dev->name, - p_sysval->read_frame_size,p_sysval->write_frame_size); - privptr->system_validate_comp=1; - if(strncmp(p_env->api_type,WS_APPL_NAME_PACKED,6) == 0) { - p_env->packing = PACKING_ASK; - } - claw_strt_conn_req(dev); - break; - - case SYSTEM_VALIDATE_RESPONSE: - p_sysval=(struct sysval *)&(p_ctlbk->data); - printk("%s: Recv Sys Validate Resp: Vers=%d,Corr=%d,RC=%d," - "WS name=%.8s,Host name=%.8s\n", - dev->name, - p_ctlbk->version, - p_ctlbk->correlator, - p_ctlbk->rc, - p_sysval->WS_name, - p_sysval->host_name); - switch (p_ctlbk->rc) - { - case 0: - printk(KERN_INFO "%s: CLAW device " - "%.8s: System validate " - "completed.\n", - dev->name, temp_ws_name); - if (privptr->system_validate_comp == 0) - claw_strt_conn_req(dev); - privptr->system_validate_comp=1; - break; - case CLAW_RC_NAME_MISMATCH: - printk(KERN_INFO "%s: Sys Validate " - "Resp : Host, WS name is " - "mismatch\n", - dev->name); - break; - case CLAW_RC_WRONG_VERSION: - printk(KERN_INFO "%s: Sys Validate " - "Resp : Wrong version\n", - dev->name); - break; - case CLAW_RC_HOST_RCV_TOO_SMALL: - printk(KERN_INFO "%s: Sys Validate " - "Resp : bad frame size\n", - dev->name); - break; - default: - printk(KERN_INFO "%s: Sys Validate " - "error code=%d \n", - dev->name, p_ctlbk->rc ); - break; - } - break; + case SYSTEM_VALIDATE_REQUEST: + if (p_ctlbk->version != CLAW_VERSION_ID) { + claw_snd_sys_validate_rsp(dev, p_ctlbk, + CLAW_RC_WRONG_VERSION); + printk("%s: %d is wrong version id. " + "Expected %d\n", + dev->name, p_ctlbk->version, + CLAW_VERSION_ID); + } + p_sysval = (struct sysval *)&(p_ctlbk->data); + printk("%s: Recv Sys Validate Request: " + "Vers=%d,link_id=%d,Corr=%d,WS name=%." + "8s,Host name=%.8s\n", + dev->name, p_ctlbk->version, + p_ctlbk->linkid, + p_ctlbk->correlator, + p_sysval->WS_name, + p_sysval->host_name); + if (memcmp(temp_host_name, p_sysval->host_name, 8)) { + claw_snd_sys_validate_rsp(dev, p_ctlbk, + CLAW_RC_NAME_MISMATCH); + CLAW_DBF_TEXT(2, setup, "HSTBAD"); + CLAW_DBF_TEXT_(2, setup, "%s", p_sysval->host_name); + CLAW_DBF_TEXT_(2, setup, "%s", temp_host_name); + printk(KERN_INFO "%s: Host name mismatch\n", + dev->name); + printk(KERN_INFO "%s: Received :%s: " + "expected :%s: \n", + dev->name, + p_sysval->host_name, + temp_host_name); + } + if (memcmp(temp_ws_name, p_sysval->WS_name, 8)) { + claw_snd_sys_validate_rsp(dev, p_ctlbk, + CLAW_RC_NAME_MISMATCH); + CLAW_DBF_TEXT(2, setup, "WSNBAD"); + CLAW_DBF_TEXT_(2, setup, "%s", p_sysval->WS_name); + CLAW_DBF_TEXT_(2, setup, "%s", temp_ws_name); + printk(KERN_INFO "%s: WS name mismatch\n", + dev->name); + printk(KERN_INFO "%s: Received :%s: " + "expected :%s: \n", + dev->name, + p_sysval->WS_name, + temp_ws_name); + } + if ((p_sysval->write_frame_size < p_env->write_size) && + (p_env->packing == 0)) { + claw_snd_sys_validate_rsp(dev, p_ctlbk, + CLAW_RC_HOST_RCV_TOO_SMALL); + printk(KERN_INFO "%s: host write size is too " + "small\n", dev->name); + CLAW_DBF_TEXT(2, setup, "wrtszbad"); + } + if ((p_sysval->read_frame_size < p_env->read_size) && + (p_env->packing == 0)) { + claw_snd_sys_validate_rsp(dev, p_ctlbk, + CLAW_RC_HOST_RCV_TOO_SMALL); + printk(KERN_INFO "%s: host read size is too " + "small\n", dev->name); + CLAW_DBF_TEXT(2, setup, "rdsizbad"); + } + claw_snd_sys_validate_rsp(dev, p_ctlbk, 0); + printk(KERN_INFO "%s: CLAW device %.8s: System validate " + "completed.\n", dev->name, temp_ws_name); + printk("%s: sys Validate Rsize:%d Wsize:%d\n", dev->name, + p_sysval->read_frame_size, p_sysval->write_frame_size); + privptr->system_validate_comp = 1; + if (strncmp(p_env->api_type, WS_APPL_NAME_PACKED, 6) == 0) + p_env->packing = PACKING_ASK; + claw_strt_conn_req(dev); + break; + case SYSTEM_VALIDATE_RESPONSE: + p_sysval = (struct sysval *)&(p_ctlbk->data); + printk("%s: Recv Sys Validate Resp: Vers=%d,Corr=%d,RC=%d," + "WS name=%.8s,Host name=%.8s\n", + dev->name, + p_ctlbk->version, + p_ctlbk->correlator, + p_ctlbk->rc, + p_sysval->WS_name, + p_sysval->host_name); + switch (p_ctlbk->rc) { + case 0: + printk(KERN_INFO "%s: CLAW device " + "%.8s: System validate " + "completed.\n", + dev->name, temp_ws_name); + if (privptr->system_validate_comp == 0) + claw_strt_conn_req(dev); + privptr->system_validate_comp = 1; + break; + case CLAW_RC_NAME_MISMATCH: + printk(KERN_INFO "%s: Sys Validate " + "Resp : Host, WS name is " + "mismatch\n", + dev->name); + break; + case CLAW_RC_WRONG_VERSION: + printk(KERN_INFO "%s: Sys Validate " + "Resp : Wrong version\n", + dev->name); + break; + case CLAW_RC_HOST_RCV_TOO_SMALL: + printk(KERN_INFO "%s: Sys Validate " + "Resp : bad frame size\n", + dev->name); + break; + default: + printk(KERN_INFO "%s: Sys Validate " + "error code=%d \n", + dev->name, p_ctlbk->rc); + break; + } + break; - case CONNECTION_REQUEST: - p_connect=(struct conncmd *)&(p_ctlbk->data); - printk(KERN_INFO "%s: Recv Conn Req: Vers=%d,link_id=%d," - "Corr=%d,HOST appl=%.8s,WS appl=%.8s\n", - dev->name, - p_ctlbk->version, - p_ctlbk->linkid, - p_ctlbk->correlator, - p_connect->host_name, - p_connect->WS_name); - if (privptr->active_link_ID!=0 ) { - claw_snd_disc(dev, p_ctlbk); - printk(KERN_INFO "%s: Conn Req error : " - "already logical link is active \n", - dev->name); - } - if (p_ctlbk->linkid!=1 ) { - claw_snd_disc(dev, p_ctlbk); - printk(KERN_INFO "%s: Conn Req error : " - "req logical link id is not 1\n", + case CONNECTION_REQUEST: + p_connect = (struct conncmd *)&(p_ctlbk->data); + printk(KERN_INFO "%s: Recv Conn Req: Vers=%d,link_id=%d," + "Corr=%d,HOST appl=%.8s,WS appl=%.8s\n", + dev->name, + p_ctlbk->version, + p_ctlbk->linkid, + p_ctlbk->correlator, + p_connect->host_name, + p_connect->WS_name); + if (privptr->active_link_ID != 0) { + claw_snd_disc(dev, p_ctlbk); + printk(KERN_INFO "%s: Conn Req error : " + "already logical link is active \n", + dev->name); + } + if (p_ctlbk->linkid != 1) { + claw_snd_disc(dev, p_ctlbk); + printk(KERN_INFO "%s: Conn Req error : " + "req logical link id is not 1\n", + dev->name); + } + rc = find_link(dev, p_connect->host_name, p_connect->WS_name); + if (rc != 0) { + claw_snd_disc(dev, p_ctlbk); + printk(KERN_INFO "%s: Conn Resp error: " + "req appl name does not match\n", + dev->name); + } + claw_send_control(dev, + CONNECTION_CONFIRM, p_ctlbk->linkid, + p_ctlbk->correlator, + 0, p_connect->host_name, + p_connect->WS_name); + if (p_env->packing == PACKING_ASK) { + p_env->packing = PACK_SEND; + claw_snd_conn_req(dev, 0); + } + printk(KERN_INFO "%s: CLAW device %.8s: Connection " + "completed link_id=%d.\n", + dev->name, temp_ws_name, + p_ctlbk->linkid); + privptr->active_link_ID = p_ctlbk->linkid; + p_ch = &privptr->channel[WRITE]; + wake_up(&p_ch->wait); /* wake up claw_open ( WRITE) */ + break; + case CONNECTION_RESPONSE: + p_connect = (struct conncmd *)&(p_ctlbk->data); + printk(KERN_INFO "%s: Revc Conn Resp: Vers=%d,link_id=%d," + "Corr=%d,RC=%d,Host appl=%.8s, WS appl=%.8s\n", + dev->name, + p_ctlbk->version, + p_ctlbk->linkid, + p_ctlbk->correlator, + p_ctlbk->rc, + p_connect->host_name, + p_connect->WS_name); + + if (p_ctlbk->rc != 0) { + printk(KERN_INFO "%s: Conn Resp error: rc=%d \n", + dev->name, p_ctlbk->rc); + return 1; + } + rc = find_link(dev, + p_connect->host_name, p_connect->WS_name); + if (rc != 0) { + claw_snd_disc(dev, p_ctlbk); + printk(KERN_INFO "%s: Conn Resp error: " + "req appl name does not match\n", + dev->name); + } + /* should be until CONNECTION_CONFIRM */ + privptr->active_link_ID = -(p_ctlbk->linkid); + break; + case CONNECTION_CONFIRM: + p_connect = (struct conncmd *)&(p_ctlbk->data); + printk(KERN_INFO "%s: Recv Conn Confirm:Vers=%d,link_id=%d," + "Corr=%d,Host appl=%.8s,WS appl=%.8s\n", + dev->name, + p_ctlbk->version, + p_ctlbk->linkid, + p_ctlbk->correlator, + p_connect->host_name, + p_connect->WS_name); + if (p_ctlbk->linkid == -(privptr->active_link_ID)) { + privptr->active_link_ID = p_ctlbk->linkid; + if (p_env->packing > PACKING_ASK) { + printk(KERN_INFO "%s: Confirmed Now packing\n", dev->name); - } - rc=find_link(dev, - p_connect->host_name, p_connect->WS_name); - if (rc!=0) { - claw_snd_disc(dev, p_ctlbk); - printk(KERN_INFO "%s: Conn Req error : " - "req appl name does not match\n", - dev->name); - } - claw_send_control(dev, - CONNECTION_CONFIRM, p_ctlbk->linkid, - p_ctlbk->correlator, - 0, p_connect->host_name, - p_connect->WS_name); - if (p_env->packing == PACKING_ASK) { - printk("%s: Now Pack ask\n",dev->name); - p_env->packing = PACK_SEND; - claw_snd_conn_req(dev,0); - } - printk(KERN_INFO "%s: CLAW device %.8s: Connection " - "completed link_id=%d.\n", - dev->name, temp_ws_name, - p_ctlbk->linkid); - privptr->active_link_ID=p_ctlbk->linkid; - p_ch=&privptr->channel[WRITE]; - wake_up(&p_ch->wait); /* wake up claw_open ( WRITE) */ - break; - case CONNECTION_RESPONSE: - p_connect=(struct conncmd *)&(p_ctlbk->data); - printk(KERN_INFO "%s: Revc Conn Resp: Vers=%d,link_id=%d," - "Corr=%d,RC=%d,Host appl=%.8s, WS appl=%.8s\n", - dev->name, - p_ctlbk->version, - p_ctlbk->linkid, - p_ctlbk->correlator, - p_ctlbk->rc, - p_connect->host_name, - p_connect->WS_name); - - if (p_ctlbk->rc !=0 ) { - printk(KERN_INFO "%s: Conn Resp error: rc=%d \n", - dev->name, p_ctlbk->rc); - return 1; - } - rc=find_link(dev, - p_connect->host_name, p_connect->WS_name); - if (rc!=0) { - claw_snd_disc(dev, p_ctlbk); - printk(KERN_INFO "%s: Conn Resp error: " - "req appl name does not match\n", - dev->name); - } - /* should be until CONNECTION_CONFIRM */ - privptr->active_link_ID = - (p_ctlbk->linkid); - break; - case CONNECTION_CONFIRM: - p_connect=(struct conncmd *)&(p_ctlbk->data); - printk(KERN_INFO "%s: Recv Conn Confirm:Vers=%d,link_id=%d," - "Corr=%d,Host appl=%.8s,WS appl=%.8s\n", - dev->name, - p_ctlbk->version, - p_ctlbk->linkid, - p_ctlbk->correlator, - p_connect->host_name, - p_connect->WS_name); - if (p_ctlbk->linkid== -(privptr->active_link_ID)) { - privptr->active_link_ID=p_ctlbk->linkid; - if (p_env->packing > PACKING_ASK) { - printk(KERN_INFO "%s: Confirmed Now packing\n",dev->name); - p_env->packing = DO_PACKED; - } - p_ch=&privptr->channel[WRITE]; - wake_up(&p_ch->wait); - } - else { - printk(KERN_INFO "%s: Conn confirm: " - "unexpected linkid=%d \n", - dev->name, p_ctlbk->linkid); - claw_snd_disc(dev, p_ctlbk); - } - break; - case DISCONNECT: - printk(KERN_INFO "%s: Disconnect: " - "Vers=%d,link_id=%d,Corr=%d\n", - dev->name, p_ctlbk->version, - p_ctlbk->linkid, p_ctlbk->correlator); - if ((p_ctlbk->linkid == 2) && - (p_env->packing == PACK_SEND)) { - privptr->active_link_ID = 1; p_env->packing = DO_PACKED; } - else - privptr->active_link_ID=0; - break; - case CLAW_ERROR: - printk(KERN_INFO "%s: CLAW ERROR detected\n", - dev->name); - break; - default: - printk(KERN_INFO "%s: Unexpected command code=%d \n", - dev->name, p_ctlbk->command); - break; + p_ch = &privptr->channel[WRITE]; + wake_up(&p_ch->wait); + } else { + printk(KERN_INFO "%s: Conn confirm: " + "unexpected linkid=%d \n", + dev->name, p_ctlbk->linkid); + claw_snd_disc(dev, p_ctlbk); + } + break; + case DISCONNECT: + printk(KERN_INFO "%s: Disconnect: " + "Vers=%d,link_id=%d,Corr=%d\n", + dev->name, p_ctlbk->version, + p_ctlbk->linkid, p_ctlbk->correlator); + if ((p_ctlbk->linkid == 2) && + (p_env->packing == PACK_SEND)) { + privptr->active_link_ID = 1; + p_env->packing = DO_PACKED; + } else + privptr->active_link_ID = 0; + break; + case CLAW_ERROR: + printk(KERN_INFO "%s: CLAW ERROR detected\n", + dev->name); + break; + default: + printk(KERN_INFO "%s: Unexpected command code=%d \n", + dev->name, p_ctlbk->command); + break; } -#ifdef FUNCTRACE - printk(KERN_INFO "%s: %s() exit on line %d, rc = 0\n", - dev->name,__func__,__LINE__); -#endif - return 0; } /* end of claw_process_control */ @@ -3092,18 +2277,7 @@ claw_send_control(struct net_device *dev, __u8 type, __u8 link, struct conncmd *p_connect; struct sk_buff *skb; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s > enter \n",dev->name,__func__); -#endif - CLAW_DBF_TEXT(2,setup,"sndcntl"); -#ifdef DEBUGMSG - printk(KERN_INFO "%s: Sending Control Packet \n",dev->name); - printk(KERN_INFO "%s: variable type = 0x%X, link = " - "%d, correlator = %d, rc = %d\n", - dev->name,type, link, correlator, rc); - printk(KERN_INFO "%s: variable local_name = %s, " - "remote_name = %s\n",dev->name, local_name, remote_name); -#endif + CLAW_DBF_TEXT(2, setup, "sndcntl"); privptr=dev->priv; p_ctl=(struct clawctl *)&privptr->ctl_bk; @@ -3125,7 +2299,7 @@ claw_send_control(struct net_device *dev, __u8 type, __u8 link, p_sysval->read_frame_size=DEF_PACK_BUFSIZE; p_sysval->write_frame_size=DEF_PACK_BUFSIZE; } else { - /* how big is the piggest group of packets */ + /* how big is the biggest group of packets */ p_sysval->read_frame_size=privptr->p_env->read_size; p_sysval->write_frame_size=privptr->p_env->write_size; } @@ -3155,29 +2329,14 @@ claw_send_control(struct net_device *dev, __u8 type, __u8 link, skb = dev_alloc_skb(sizeof(struct clawctl)); if (!skb) { - printk( "%s:%s low on mem, returning...\n", - dev->name,__func__); -#ifdef DEBUG - printk(KERN_INFO "%s:%s Exit, rc = ENOMEM\n", - dev->name,__func__); -#endif return -ENOMEM; } memcpy(skb_put(skb, sizeof(struct clawctl)), p_ctl, sizeof(struct clawctl)); -#ifdef IOTRACE - printk(KERN_INFO "%s: outbnd claw cntl data \n",dev->name); - dumpit((char *)p_ctl,sizeof(struct clawctl)); -#endif if (privptr->p_env->packing >= PACK_SEND) claw_hw_tx(skb, dev, 1); else claw_hw_tx(skb, dev, 0); -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - return 0; } /* end of claw_send_control */ @@ -3192,22 +2351,11 @@ claw_snd_conn_req(struct net_device *dev, __u8 link) struct claw_privbk *privptr=dev->priv; struct clawctl *p_ctl; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Enter \n",dev->name,__func__); -#endif - CLAW_DBF_TEXT(2,setup,"snd_conn"); -#ifdef DEBUGMSG - printk(KERN_INFO "%s: variable link = %X, dev =\n",dev->name, link); - dumpit((char *) dev, sizeof(struct net_device)); -#endif + CLAW_DBF_TEXT(2, setup, "snd_conn"); rc = 1; p_ctl=(struct clawctl *)&privptr->ctl_bk; p_ctl->linkid = link; if ( privptr->system_validate_comp==0x00 ) { -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d, rc = 1\n", - dev->name,__func__,__LINE__); -#endif return rc; } if (privptr->p_env->packing == PACKING_ASK ) @@ -3220,10 +2368,6 @@ claw_snd_conn_req(struct net_device *dev, __u8 link) if (privptr->p_env->packing == 0) rc=claw_send_control(dev, CONNECTION_REQUEST,0,0,0, HOST_APPL_NAME, privptr->p_env->api_type); -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d, rc = %d\n", - dev->name,__func__,__LINE__, rc); -#endif return rc; } /* end of claw_snd_conn_req */ @@ -3240,25 +2384,12 @@ claw_snd_disc(struct net_device *dev, struct clawctl * p_ctl) int rc; struct conncmd * p_connect; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Enter\n",dev->name,__func__); -#endif - CLAW_DBF_TEXT(2,setup,"snd_dsc"); -#ifdef DEBUGMSG - printk(KERN_INFO "%s: variable dev =\n",dev->name); - dumpit((char *) dev, sizeof(struct net_device)); - printk(KERN_INFO "%s: variable p_ctl",dev->name); - dumpit((char *) p_ctl, sizeof(struct clawctl)); -#endif + CLAW_DBF_TEXT(2, setup, "snd_dsc"); p_connect=(struct conncmd *)&p_ctl->data; rc=claw_send_control(dev, DISCONNECT, p_ctl->linkid, p_ctl->correlator, 0, p_connect->host_name, p_connect->WS_name); -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d, rc = %d\n", - dev->name,__func__, __LINE__, rc); -#endif return rc; } /* end of claw_snd_disc */ @@ -3276,18 +2407,7 @@ claw_snd_sys_validate_rsp(struct net_device *dev, struct claw_privbk *privptr; int rc; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Enter\n", - dev->name,__func__); -#endif - CLAW_DBF_TEXT(2,setup,"chkresp"); -#ifdef DEBUGMSG - printk(KERN_INFO "%s: variable return_code = %d, dev =\n", - dev->name, return_code); - dumpit((char *) dev, sizeof(struct net_device)); - printk(KERN_INFO "%s: variable p_ctl =\n",dev->name); - dumpit((char *) p_ctl, sizeof(struct clawctl)); -#endif + CLAW_DBF_TEXT(2, setup, "chkresp"); privptr = dev->priv; p_env=privptr->p_env; rc=claw_send_control(dev, SYSTEM_VALIDATE_RESPONSE, @@ -3296,10 +2416,6 @@ claw_snd_sys_validate_rsp(struct net_device *dev, return_code, p_env->host_name, p_env->adapter_name ); -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d, rc = %d\n", - dev->name,__func__,__LINE__, rc); -#endif return rc; } /* end of claw_snd_sys_validate_rsp */ @@ -3313,19 +2429,8 @@ claw_strt_conn_req(struct net_device *dev ) { int rc; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Enter\n",dev->name,__func__); -#endif - CLAW_DBF_TEXT(2,setup,"conn_req"); -#ifdef DEBUGMSG - printk(KERN_INFO "%s: variable dev =\n",dev->name); - dumpit((char *) dev, sizeof(struct net_device)); -#endif + CLAW_DBF_TEXT(2, setup, "conn_req"); rc=claw_snd_conn_req(dev, 1); -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d, rc = %d\n", - dev->name,__func__,__LINE__, rc); -#endif return rc; } /* end of claw_strt_conn_req */ @@ -3339,15 +2444,9 @@ static struct net_device_stats *claw_stats(struct net_device *dev) { struct claw_privbk *privptr; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Enter\n",dev->name,__func__); -#endif - CLAW_DBF_TEXT(4,trace,"stats"); + + CLAW_DBF_TEXT(4, trace, "stats"); privptr = dev->priv; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif return &privptr->stats; } /* end of claw_stats */ @@ -3368,36 +2467,28 @@ unpack_read(struct net_device *dev ) struct clawph *p_packh; void *p_packd; struct clawctl *p_ctlrec=NULL; + struct device *p_dev; __u32 len_of_data; __u32 pack_off; __u8 link_num; __u8 mtc_this_frm=0; __u32 bytes_to_mov; - struct chbk *p_ch = NULL; int i=0; int p=0; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s enter \n",dev->name,__func__); -#endif - CLAW_DBF_TEXT(4,trace,"unpkread"); + CLAW_DBF_TEXT(4, trace, "unpkread"); p_first_ccw=NULL; p_last_ccw=NULL; p_packh=NULL; p_packd=NULL; privptr=dev->priv; + + p_dev = &privptr->channel[READ].cdev->dev; p_env = privptr->p_env; p_this_ccw=privptr->p_read_active_first; i=0; while (p_this_ccw!=NULL && p_this_ccw->header.flag!=CLAW_PENDING) { -#ifdef IOTRACE - printk(KERN_INFO "%s p_this_ccw \n",dev->name); - dumpit((char*)p_this_ccw, sizeof(struct ccwbk)); - printk(KERN_INFO "%s Inbound p_this_ccw->p_buffer(64)" - " pk=%d \n",dev->name,p_env->packing); - dumpit((char *)p_this_ccw->p_buffer, 64 ); -#endif pack_off = 0; p = 0; p_this_ccw->header.flag=CLAW_PENDING; @@ -3419,10 +2510,6 @@ unpack_read(struct net_device *dev ) else link_num=p_this_ccw->header.opcode / 8; if ((p_this_ccw->header.opcode & MORE_to_COME_FLAG)!=0) { -#ifdef DEBUGMSG - printk(KERN_INFO "%s: %s > More_to_come is ON\n", - dev->name,__func__); -#endif mtc_this_frm=1; if (p_this_ccw->header.length!= privptr->p_env->read_size ) { @@ -3445,22 +2532,12 @@ unpack_read(struct net_device *dev ) privptr->mtc_skipping=0; /* Ok, the end */ privptr->mtc_logical_link=-1; } -#ifdef DEBUGMSG - printk(KERN_INFO "%s:%s goto next " - "frame from MoretoComeSkip \n", - dev->name,__func__); -#endif goto NextFrame; } if (link_num==0) { claw_process_control(dev, p_this_ccw); -#ifdef DEBUGMSG - printk(KERN_INFO "%s:%s goto next " - "frame from claw_process_control \n", - dev->name,__func__); -#endif - CLAW_DBF_TEXT(4,trace,"UnpkCntl"); + CLAW_DBF_TEXT(4, trace, "UnpkCntl"); goto NextFrame; } unpack_next: @@ -3479,10 +2556,6 @@ unpack_next: bytes_to_mov=p_this_ccw->header.length; } if (privptr->mtc_logical_link<0) { -#ifdef DEBUGMSG - printk(KERN_INFO "%s: %s mtc_logical_link < 0 \n", - dev->name,__func__); -#endif /* * if More-To-Come is set in this frame then we don't know @@ -3496,15 +2569,6 @@ unpack_next: if (bytes_to_mov > (MAX_ENVELOPE_SIZE- privptr->mtc_offset) ) { /* error */ -#ifdef DEBUGMSG - printk(KERN_INFO "%s: %s > goto next " - "frame from MoretoComeSkip \n", - dev->name, - __func__); - printk(KERN_INFO " bytes_to_mov %d > (MAX_ENVELOPE_" - "SIZE-privptr->mtc_offset %d)\n", - bytes_to_mov,(MAX_ENVELOPE_SIZE- privptr->mtc_offset)); -#endif privptr->stats.rx_frame_errors++; goto NextFrame; } @@ -3516,16 +2580,6 @@ unpack_next: memcpy( privptr->p_mtc_envelope+ privptr->mtc_offset, p_this_ccw->p_buffer, bytes_to_mov); } -#ifdef DEBUGMSG - printk(KERN_INFO "%s: %s() received data \n", - dev->name,__func__); - if (p_env->packing == DO_PACKED) - dumpit((char *)p_packd+sizeof(struct clawph),32); - else - dumpit((char *)p_this_ccw->p_buffer, 32); - printk(KERN_INFO "%s: %s() bytelength %d \n", - dev->name,__func__,bytes_to_mov); -#endif if (mtc_this_frm==0) { len_of_data=privptr->mtc_offset+bytes_to_mov; skb=dev_alloc_skb(len_of_data); @@ -3540,11 +2594,6 @@ unpack_next: privptr->stats.rx_packets++; privptr->stats.rx_bytes+=len_of_data; netif_rx(skb); -#ifdef DEBUGMSG - printk(KERN_INFO "%s: %s() netif_" - "rx(skb) completed \n", - dev->name,__func__); -#endif } else { privptr->stats.rx_dropped++; @@ -3581,28 +2630,14 @@ NextFrame: * chain to next block on active read queue */ p_this_ccw = privptr->p_read_active_first; - CLAW_DBF_TEXT_(4,trace,"rxpkt %d",p); + CLAW_DBF_TEXT_(4, trace, "rxpkt %d", p); } /* end of while */ /* check validity */ -#ifdef IOTRACE - printk(KERN_INFO "%s:%s processed frame is %d \n", - dev->name,__func__,i); - printk(KERN_INFO "%s:%s F:%lx L:%lx\n", - dev->name, - __func__, - (unsigned long)p_first_ccw, - (unsigned long)p_last_ccw); -#endif - CLAW_DBF_TEXT_(4,trace,"rxfrm %d",i); + CLAW_DBF_TEXT_(4, trace, "rxfrm %d", i); add_claw_reads(dev, p_first_ccw, p_last_ccw); - p_ch=&privptr->channel[READ]; claw_strt_read(dev, LOCK_YES); -#ifdef FUNCTRACE - printk(KERN_INFO "%s: %s exit on line %d\n", - dev->name, __func__, __LINE__); -#endif return; } /* end of unpack_read */ @@ -3622,12 +2657,7 @@ claw_strt_read (struct net_device *dev, int lock ) struct clawh *p_clawh; p_ch=&privptr->channel[READ]; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Enter \n",dev->name,__func__); - printk(KERN_INFO "%s: variable lock = %d, dev =\n",dev->name, lock); - dumpit((char *) dev, sizeof(struct net_device)); -#endif - CLAW_DBF_TEXT(4,trace,"StRdNter"); + CLAW_DBF_TEXT(4, trace, "StRdNter"); p_clawh=(struct clawh *)privptr->p_claw_signal_blk; p_clawh->flag=CLAW_IDLE; /* 0x00 */ @@ -3637,21 +2667,11 @@ claw_strt_read (struct net_device *dev, int lock ) privptr->p_read_active_first->header.flag!=CLAW_PENDING )) { p_clawh->flag=CLAW_BUSY; /* 0xff */ } -#ifdef DEBUGMSG - printk(KERN_INFO "%s:%s state-%02x\n" , - dev->name,__func__, p_ch->claw_state); -#endif if (lock==LOCK_YES) { spin_lock_irqsave(get_ccwdev_lock(p_ch->cdev), saveflags); } if (test_and_set_bit(0, (void *)&p_ch->IO_active) == 0) { -#ifdef DEBUGMSG - printk(KERN_INFO "%s: HOT READ started in %s\n" , - dev->name,__func__); - p_clawh=(struct clawh *)privptr->p_claw_signal_blk; - dumpit((char *)&p_clawh->flag , 1); -#endif - CLAW_DBF_TEXT(4,trace,"HotRead"); + CLAW_DBF_TEXT(4, trace, "HotRead"); p_ccwbk=privptr->p_read_active_first; parm = (unsigned long) p_ch; rc = ccw_device_start (p_ch->cdev, &p_ccwbk->read, parm, @@ -3661,21 +2681,13 @@ claw_strt_read (struct net_device *dev, int lock ) } } else { -#ifdef DEBUGMSG - printk(KERN_INFO "%s: No READ started by %s() In progress\n" , - dev->name,__func__); -#endif - CLAW_DBF_TEXT(2,trace,"ReadAct"); + CLAW_DBF_TEXT(2, trace, "ReadAct"); } if (lock==LOCK_YES) { spin_unlock_irqrestore(get_ccwdev_lock(p_ch->cdev), saveflags); } -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - CLAW_DBF_TEXT(4,trace,"StRdExit"); + CLAW_DBF_TEXT(4, trace, "StRdExit"); return; } /* end of claw_strt_read */ @@ -3693,38 +2705,23 @@ claw_strt_out_IO( struct net_device *dev ) struct chbk *p_ch; struct ccwbk *p_first_ccw; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Enter\n",dev->name,__func__); -#endif if (!dev) { return; } privptr=(struct claw_privbk *)dev->priv; p_ch=&privptr->channel[WRITE]; -#ifdef DEBUGMSG - printk(KERN_INFO "%s:%s state-%02x\n" , - dev->name,__func__,p_ch->claw_state); -#endif - CLAW_DBF_TEXT(4,trace,"strt_io"); + CLAW_DBF_TEXT(4, trace, "strt_io"); p_first_ccw=privptr->p_write_active_first; if (p_ch->claw_state == CLAW_STOP) return; if (p_first_ccw == NULL) { -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif return; } if (test_and_set_bit(0, (void *)&p_ch->IO_active) == 0) { parm = (unsigned long) p_ch; -#ifdef DEBUGMSG - printk(KERN_INFO "%s:%s do_io \n" ,dev->name,__func__); - dumpit((char *)p_first_ccw, sizeof(struct ccwbk)); -#endif - CLAW_DBF_TEXT(2,trace,"StWrtIO"); + CLAW_DBF_TEXT(2, trace, "StWrtIO"); rc = ccw_device_start (p_ch->cdev,&p_first_ccw->write, parm, 0xff, 0); if (rc != 0) { @@ -3732,11 +2729,6 @@ claw_strt_out_IO( struct net_device *dev ) } } dev->trans_start = jiffies; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - dev->name,__func__,__LINE__); -#endif - return; } /* end of claw_strt_out_IO */ @@ -3754,32 +2746,11 @@ claw_free_wrt_buf( struct net_device *dev ) struct ccwbk*p_last_ccw; struct ccwbk*p_this_ccw; struct ccwbk*p_next_ccw; -#ifdef IOTRACE - struct ccwbk*p_buf; -#endif -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Enter\n",dev->name,__func__); - printk(KERN_INFO "%s: free count = %d variable dev =\n", - dev->name,privptr->write_free_count); -#endif - CLAW_DBF_TEXT(4,trace,"freewrtb"); + + CLAW_DBF_TEXT(4, trace, "freewrtb"); /* scan the write queue to free any completed write packets */ p_first_ccw=NULL; p_last_ccw=NULL; -#ifdef IOTRACE - printk(KERN_INFO "%s: Dump current CCW chain \n",dev->name ); - p_buf=privptr->p_write_active_first; - while (p_buf!=NULL) { - dumpit((char *)p_buf, sizeof(struct ccwbk)); - p_buf=p_buf->next; - } - if (p_buf==NULL) { - printk(KERN_INFO "%s: privptr->p_write_" - "active_first==NULL\n",dev->name ); - } - p_buf=(struct ccwbk*)privptr->p_end_ccw; - dumpit((char *)p_buf, sizeof(struct endccw)); -#endif p_this_ccw=privptr->p_write_active_first; while ( (p_this_ccw!=NULL) && (p_this_ccw->header.flag!=CLAW_PENDING)) { @@ -3809,31 +2780,8 @@ claw_free_wrt_buf( struct net_device *dev ) /* whole chain removed? */ if (privptr->p_write_active_first==NULL) { privptr->p_write_active_last=NULL; -#ifdef DEBUGMSG - printk(KERN_INFO "%s:%s p_write_" - "active_first==NULL\n",dev->name,__func__); -#endif - } -#ifdef IOTRACE - printk(KERN_INFO "%s: Dump arranged CCW chain \n",dev->name ); - p_buf=privptr->p_write_active_first; - while (p_buf!=NULL) { - dumpit((char *)p_buf, sizeof(struct ccwbk)); - p_buf=p_buf->next; } - if (p_buf==NULL) { - printk(KERN_INFO "%s: privptr->p_write_active_" - "first==NULL\n",dev->name ); - } - p_buf=(struct ccwbk*)privptr->p_end_ccw; - dumpit((char *)p_buf, sizeof(struct endccw)); -#endif - - CLAW_DBF_TEXT_(4,trace,"FWC=%d",privptr->write_free_count); -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d free_count =%d\n", - dev->name,__func__, __LINE__,privptr->write_free_count); -#endif + CLAW_DBF_TEXT_(4, trace, "FWC=%d", privptr->write_free_count); return; } @@ -3845,14 +2793,11 @@ static void claw_free_netdevice(struct net_device * dev, int free_dev) { struct claw_privbk *privptr; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Enter\n",dev->name,__func__); -#endif - CLAW_DBF_TEXT(2,setup,"free_dev"); + CLAW_DBF_TEXT(2, setup, "free_dev"); if (!dev) return; - CLAW_DBF_TEXT_(2,setup,"%s",dev->name); + CLAW_DBF_TEXT_(2, setup, "%s", dev->name); privptr = dev->priv; if (dev->flags & IFF_RUNNING) claw_release(dev); @@ -3865,10 +2810,7 @@ claw_free_netdevice(struct net_device * dev, int free_dev) free_netdev(dev); } #endif - CLAW_DBF_TEXT(2,setup,"feee_ok"); -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit\n",dev->name,__func__); -#endif + CLAW_DBF_TEXT(2, setup, "free_ok"); } /** @@ -3879,17 +2821,8 @@ claw_free_netdevice(struct net_device * dev, int free_dev) static void claw_init_netdevice(struct net_device * dev) { -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Enter\n",dev->name,__func__); -#endif - CLAW_DBF_TEXT(2,setup,"init_dev"); - CLAW_DBF_TEXT_(2,setup,"%s",dev->name); - if (!dev) { - printk(KERN_WARNING "claw:%s BAD Device exit line %d\n", - __func__,__LINE__); - CLAW_DBF_TEXT(2,setup,"baddev"); - return; - } + CLAW_DBF_TEXT(2, setup, "init_dev"); + CLAW_DBF_TEXT_(2, setup, "%s", dev->name); dev->mtu = CLAW_DEFAULT_MTU_SIZE; dev->hard_start_xmit = claw_tx; dev->open = claw_open; @@ -3901,10 +2834,7 @@ claw_init_netdevice(struct net_device * dev) dev->type = ARPHRD_SLIP; dev->tx_queue_len = 1300; dev->flags = IFF_POINTOPOINT | IFF_NOARP; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit\n",dev->name,__func__); -#endif - CLAW_DBF_TEXT(2,setup,"initok"); + CLAW_DBF_TEXT(2, setup, "initok"); return; } @@ -3921,10 +2851,7 @@ add_channel(struct ccw_device *cdev,int i,struct claw_privbk *privptr) struct chbk *p_ch; struct ccw_dev_id dev_id; -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Enter\n",cdev->dev.bus_id,__func__); -#endif - CLAW_DBF_TEXT_(2,setup,"%s",cdev->dev.bus_id); + CLAW_DBF_TEXT_(2, setup, "%s", cdev->dev.bus_id); privptr->channel[i].flag = i+1; /* Read is 1 Write is 2 */ p_ch = &privptr->channel[i]; p_ch->cdev = cdev; @@ -3932,18 +2859,8 @@ add_channel(struct ccw_device *cdev,int i,struct claw_privbk *privptr) ccw_device_get_id(cdev, &dev_id); p_ch->devno = dev_id.devno; if ((p_ch->irb = kzalloc(sizeof (struct irb),GFP_KERNEL)) == NULL) { - printk(KERN_WARNING "%s Out of memory in %s for irb\n", - p_ch->id,__func__); -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - p_ch->id,__func__,__LINE__); -#endif return -ENOMEM; } -#ifdef FUNCTRACE - printk(KERN_INFO "%s:%s Exit on line %d\n", - cdev->dev.bus_id,__func__,__LINE__); -#endif return 0; } @@ -3965,9 +2882,8 @@ claw_new_device(struct ccwgroup_device *cgdev) int ret; struct ccw_dev_id dev_id; - pr_debug("%s() called\n", __func__); printk(KERN_INFO "claw: add for %s\n",cgdev->cdev[READ]->dev.bus_id); - CLAW_DBF_TEXT(2,setup,"new_dev"); + CLAW_DBF_TEXT(2, setup, "new_dev"); privptr = cgdev->dev.driver_data; cgdev->cdev[READ]->dev.driver_data = privptr; cgdev->cdev[WRITE]->dev.driver_data = privptr; @@ -3982,22 +2898,21 @@ claw_new_device(struct ccwgroup_device *cgdev) if (ret == 0) ret = add_channel(cgdev->cdev[1],1,privptr); if (ret != 0) { - printk(KERN_WARNING - "add channel failed " - "with ret = %d\n", ret); - goto out; + printk(KERN_WARNING + "add channel failed with ret = %d\n", ret); + goto out; } ret = ccw_device_set_online(cgdev->cdev[READ]); if (ret != 0) { printk(KERN_WARNING - "claw: ccw_device_set_online %s READ failed " + "claw: ccw_device_set_online %s READ failed " "with ret = %d\n",cgdev->cdev[READ]->dev.bus_id,ret); goto out; } ret = ccw_device_set_online(cgdev->cdev[WRITE]); if (ret != 0) { printk(KERN_WARNING - "claw: ccw_device_set_online %s WRITE failed " + "claw: ccw_device_set_online %s WRITE failed " "with ret = %d\n",cgdev->cdev[WRITE]->dev.bus_id, ret); goto out; } @@ -4014,18 +2929,16 @@ claw_new_device(struct ccwgroup_device *cgdev) SET_NETDEV_DEV(dev, &cgdev->dev); if (register_netdev(dev) != 0) { claw_free_netdevice(dev, 1); - CLAW_DBF_TEXT(2,trace,"regfail"); + CLAW_DBF_TEXT(2, trace, "regfail"); goto out; } dev->flags &=~IFF_RUNNING; if (privptr->buffs_alloc == 0) { ret=init_ccw_bk(dev); if (ret !=0) { - printk(KERN_WARNING - "claw: init_ccw_bk failed with ret=%d\n", ret); unregister_netdev(dev); claw_free_netdevice(dev,1); - CLAW_DBF_TEXT(2,trace,"ccwmem"); + CLAW_DBF_TEXT(2, trace, "ccwmem"); goto out; } } @@ -4047,7 +2960,6 @@ claw_new_device(struct ccwgroup_device *cgdev) out: ccw_device_set_offline(cgdev->cdev[1]); ccw_device_set_offline(cgdev->cdev[0]); - return -ENODEV; } @@ -4056,8 +2968,7 @@ claw_purge_skb_queue(struct sk_buff_head *q) { struct sk_buff *skb; - CLAW_DBF_TEXT(4,trace,"purgque"); - + CLAW_DBF_TEXT(4, trace, "purgque"); while ((skb = skb_dequeue(q))) { atomic_dec(&skb->users); dev_kfree_skb_any(skb); @@ -4078,8 +2989,7 @@ claw_shutdown_device(struct ccwgroup_device *cgdev) struct net_device *ndev; int ret; - pr_debug("%s() called\n", __func__); - CLAW_DBF_TEXT_(2,setup,"%s",cgdev->dev.bus_id); + CLAW_DBF_TEXT_(2, setup, "%s", cgdev->dev.bus_id); priv = cgdev->dev.driver_data; if (!priv) return -ENODEV; @@ -4108,13 +3018,10 @@ claw_remove_device(struct ccwgroup_device *cgdev) { struct claw_privbk *priv; - pr_debug("%s() called\n", __func__); - CLAW_DBF_TEXT_(2,setup,"%s",cgdev->dev.bus_id); + BUG_ON(!cgdev); + CLAW_DBF_TEXT_(2, setup, "%s", cgdev->dev.bus_id); priv = cgdev->dev.driver_data; - if (!priv) { - printk(KERN_WARNING "claw: %s() no Priv exiting\n",__func__); - return; - } + BUG_ON(!priv); printk(KERN_INFO "claw: %s() called %s will be removed.\n", __func__,cgdev->cdev[0]->dev.bus_id); if (cgdev->state == CCWGROUP_ONLINE) @@ -4133,6 +3040,8 @@ claw_remove_device(struct ccwgroup_device *cgdev) cgdev->cdev[READ]->dev.driver_data = NULL; cgdev->cdev[WRITE]->dev.driver_data = NULL; put_device(&cgdev->dev); + + return; } @@ -4168,8 +3077,8 @@ claw_hname_write(struct device *dev, struct device_attribute *attr, const char * strncpy(p_env->host_name,buf, count); p_env->host_name[count-1] = 0x20; /* clear extra 0x0a */ p_env->host_name[MAX_NAME_LEN] = 0x00; - CLAW_DBF_TEXT(2,setup,"HstnSet"); - CLAW_DBF_TEXT_(2,setup,"%s",p_env->host_name); + CLAW_DBF_TEXT(2, setup, "HstnSet"); + CLAW_DBF_TEXT_(2, setup, "%s", p_env->host_name); return count; } @@ -4186,7 +3095,7 @@ claw_adname_show(struct device *dev, struct device_attribute *attr, char *buf) if (!priv) return -ENODEV; p_env = priv->p_env; - return sprintf(buf, "%s\n",p_env->adapter_name); + return sprintf(buf, "%s\n", p_env->adapter_name); } static ssize_t @@ -4205,8 +3114,8 @@ claw_adname_write(struct device *dev, struct device_attribute *attr, const char strncpy(p_env->adapter_name,buf, count); p_env->adapter_name[count-1] = 0x20; /* clear extra 0x0a */ p_env->adapter_name[MAX_NAME_LEN] = 0x00; - CLAW_DBF_TEXT(2,setup,"AdnSet"); - CLAW_DBF_TEXT_(2,setup,"%s",p_env->adapter_name); + CLAW_DBF_TEXT(2, setup, "AdnSet"); + CLAW_DBF_TEXT_(2, setup, "%s", p_env->adapter_name); return count; } @@ -4247,15 +3156,15 @@ claw_apname_write(struct device *dev, struct device_attribute *attr, const char p_env->read_size=DEF_PACK_BUFSIZE; p_env->write_size=DEF_PACK_BUFSIZE; p_env->packing=PACKING_ASK; - CLAW_DBF_TEXT(2,setup,"PACKING"); + CLAW_DBF_TEXT(2, setup, "PACKING"); } else { p_env->packing=0; p_env->read_size=CLAW_FRAME_SIZE; p_env->write_size=CLAW_FRAME_SIZE; - CLAW_DBF_TEXT(2,setup,"ApiSet"); + CLAW_DBF_TEXT(2, setup, "ApiSet"); } - CLAW_DBF_TEXT_(2,setup,"%s",p_env->api_type); + CLAW_DBF_TEXT_(2, setup, "%s", p_env->api_type); return count; } @@ -4295,8 +3204,8 @@ claw_wbuff_write(struct device *dev, struct device_attribute *attr, const char * if ((nnn > max ) || (nnn < 2)) return -EINVAL; p_env->write_buffers = nnn; - CLAW_DBF_TEXT(2,setup,"Wbufset"); - CLAW_DBF_TEXT_(2,setup,"WB=%d",p_env->write_buffers); + CLAW_DBF_TEXT(2, setup, "Wbufset"); + CLAW_DBF_TEXT_(2, setup, "WB=%d", p_env->write_buffers); return count; } @@ -4336,8 +3245,8 @@ claw_rbuff_write(struct device *dev, struct device_attribute *attr, const char * if ((nnn > max ) || (nnn < 2)) return -EINVAL; p_env->read_buffers = nnn; - CLAW_DBF_TEXT(2,setup,"Rbufset"); - CLAW_DBF_TEXT_(2,setup,"RB=%d",p_env->read_buffers); + CLAW_DBF_TEXT(2, setup, "Rbufset"); + CLAW_DBF_TEXT_(2, setup, "RB=%d", p_env->read_buffers); return count; } @@ -4359,16 +3268,14 @@ static struct attribute_group claw_attr_group = { static int claw_add_files(struct device *dev) { - pr_debug("%s() called\n", __func__); - CLAW_DBF_TEXT(2,setup,"add_file"); + CLAW_DBF_TEXT(2, setup, "add_file"); return sysfs_create_group(&dev->kobj, &claw_attr_group); } static void claw_remove_files(struct device *dev) { - pr_debug("%s() called\n", __func__); - CLAW_DBF_TEXT(2,setup,"rem_file"); + CLAW_DBF_TEXT(2, setup, "rem_file"); sysfs_remove_group(&dev->kobj, &claw_attr_group); } @@ -4397,35 +3304,27 @@ claw_init(void) int ret = 0; printk(KERN_INFO "claw: starting driver\n"); -#ifdef FUNCTRACE - printk(KERN_INFO "claw: %s() enter \n",__func__); -#endif ret = claw_register_debug_facility(); if (ret) { printk(KERN_WARNING "claw: %s() debug_register failed %d\n", __func__,ret); return ret; } - CLAW_DBF_TEXT(2,setup,"init_mod"); + CLAW_DBF_TEXT(2, setup, "init_mod"); ret = register_cu3088_discipline(&claw_group_driver); if (ret) { + CLAW_DBF_TEXT(2, setup, "init_bad"); claw_unregister_debug_facility(); printk(KERN_WARNING "claw; %s() cu3088 register failed %d\n", __func__,ret); } -#ifdef FUNCTRACE - printk(KERN_INFO "claw: %s() exit \n",__func__); -#endif return ret; } module_init(claw_init); module_exit(claw_cleanup); - - -/*--------------------------------------------------------------------* -* End of File * -*---------------------------------------------------------------------*/ - - +MODULE_AUTHOR("Andy Richter <richtera@us.ibm.com>"); +MODULE_DESCRIPTION("Linux for System z CLAW Driver\n" \ + "Copyright 2000,2008 IBM Corporation\n"); +MODULE_LICENSE("GPL"); diff --git a/drivers/s390/net/ctcm_dbug.c b/drivers/s390/net/ctcm_dbug.c index 8eb25d00b2e7..1ca58f153470 100644 --- a/drivers/s390/net/ctcm_dbug.c +++ b/drivers/s390/net/ctcm_dbug.c @@ -7,6 +7,7 @@ */ #include <linux/stddef.h> +#include <linux/string.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/slab.h> @@ -22,15 +23,13 @@ * Debug Facility Stuff */ -DEFINE_PER_CPU(char[256], ctcm_dbf_txt_buf); - struct ctcm_dbf_info ctcm_dbf[CTCM_DBF_INFOS] = { - [CTCM_DBF_SETUP] = {"ctc_setup", 8, 1, 64, 5, NULL}, - [CTCM_DBF_ERROR] = {"ctc_error", 8, 1, 64, 3, NULL}, - [CTCM_DBF_TRACE] = {"ctc_trace", 8, 1, 64, 3, NULL}, - [CTCM_DBF_MPC_SETUP] = {"mpc_setup", 8, 1, 64, 5, NULL}, - [CTCM_DBF_MPC_ERROR] = {"mpc_error", 8, 1, 64, 3, NULL}, - [CTCM_DBF_MPC_TRACE] = {"mpc_trace", 8, 1, 64, 3, NULL}, + [CTCM_DBF_SETUP] = {"ctc_setup", 8, 1, 64, CTC_DBF_INFO, NULL}, + [CTCM_DBF_ERROR] = {"ctc_error", 8, 1, 64, CTC_DBF_ERROR, NULL}, + [CTCM_DBF_TRACE] = {"ctc_trace", 8, 1, 64, CTC_DBF_ERROR, NULL}, + [CTCM_DBF_MPC_SETUP] = {"mpc_setup", 8, 1, 80, CTC_DBF_INFO, NULL}, + [CTCM_DBF_MPC_ERROR] = {"mpc_error", 8, 1, 80, CTC_DBF_ERROR, NULL}, + [CTCM_DBF_MPC_TRACE] = {"mpc_trace", 8, 1, 80, CTC_DBF_ERROR, NULL}, }; void ctcm_unregister_dbf_views(void) @@ -65,3 +64,17 @@ int ctcm_register_dbf_views(void) return 0; } +void ctcm_dbf_longtext(enum ctcm_dbf_names dbf_nix, int level, char *fmt, ...) +{ + char dbf_txt_buf[64]; + va_list args; + + if (level > (ctcm_dbf[dbf_nix].id)->level) + return; + va_start(args, fmt); + vsnprintf(dbf_txt_buf, sizeof(dbf_txt_buf), fmt, args); + va_end(args); + + debug_text_event(ctcm_dbf[dbf_nix].id, level, dbf_txt_buf); +} + diff --git a/drivers/s390/net/ctcm_dbug.h b/drivers/s390/net/ctcm_dbug.h index fdff34fe59a2..26966d0b9abd 100644 --- a/drivers/s390/net/ctcm_dbug.h +++ b/drivers/s390/net/ctcm_dbug.h @@ -20,16 +20,17 @@ #else #define do_debug 0 #endif -#ifdef DEBUGDATA - #define do_debug_data 1 -#else - #define do_debug_data 0 -#endif #ifdef DEBUGCCW #define do_debug_ccw 1 + #define DEBUGDATA 1 #else #define do_debug_ccw 0 #endif +#ifdef DEBUGDATA + #define do_debug_data 1 +#else + #define do_debug_data 0 +#endif /* define dbf debug levels similar to kernel msg levels */ #define CTC_DBF_ALWAYS 0 /* always print this */ @@ -42,8 +43,6 @@ #define CTC_DBF_INFO 5 /* informational */ #define CTC_DBF_DEBUG 6 /* debug-level messages */ -DECLARE_PER_CPU(char[256], ctcm_dbf_txt_buf); - enum ctcm_dbf_names { CTCM_DBF_SETUP, CTCM_DBF_ERROR, @@ -67,6 +66,7 @@ extern struct ctcm_dbf_info ctcm_dbf[CTCM_DBF_INFOS]; int ctcm_register_dbf_views(void); void ctcm_unregister_dbf_views(void); +void ctcm_dbf_longtext(enum ctcm_dbf_names dbf_nix, int level, char *text, ...); static inline const char *strtail(const char *s, int n) { @@ -74,12 +74,6 @@ static inline const char *strtail(const char *s, int n) return (l > n) ? s + (l - n) : s; } -/* sort out levels early to avoid unnecessary sprintfs */ -static inline int ctcm_dbf_passes(debug_info_t *dbf_grp, int level) -{ - return (dbf_grp->level >= level); -} - #define CTCM_FUNTAIL strtail((char *)__func__, 16) #define CTCM_DBF_TEXT(name, level, text) \ @@ -94,16 +88,7 @@ static inline int ctcm_dbf_passes(debug_info_t *dbf_grp, int level) } while (0) #define CTCM_DBF_TEXT_(name, level, text...) \ - do { \ - if (ctcm_dbf_passes(ctcm_dbf[CTCM_DBF_##name].id, level)) { \ - char *ctcm_dbf_txt_buf = \ - get_cpu_var(ctcm_dbf_txt_buf); \ - sprintf(ctcm_dbf_txt_buf, text); \ - debug_text_event(ctcm_dbf[CTCM_DBF_##name].id, \ - level, ctcm_dbf_txt_buf); \ - put_cpu_var(ctcm_dbf_txt_buf); \ - } \ - } while (0) + ctcm_dbf_longtext(CTCM_DBF_##name, level, text) /* * cat : one of {setup, mpc_setup, trace, mpc_trace, error, mpc_error}. @@ -112,13 +97,13 @@ static inline int ctcm_dbf_passes(debug_info_t *dbf_grp, int level) */ #define CTCM_DBF_DEV_NAME(cat, dev, text) \ do { \ - CTCM_DBF_TEXT_(cat, CTC_DBF_INFO, "%s(%s) : %s", \ + CTCM_DBF_TEXT_(cat, CTC_DBF_INFO, "%s(%s) :- %s", \ CTCM_FUNTAIL, dev->name, text); \ } while (0) #define MPC_DBF_DEV_NAME(cat, dev, text) \ do { \ - CTCM_DBF_TEXT_(MPC_##cat, CTC_DBF_INFO, "%s(%s) : %s", \ + CTCM_DBF_TEXT_(MPC_##cat, CTC_DBF_INFO, "%s(%s) := %s", \ CTCM_FUNTAIL, dev->name, text); \ } while (0) @@ -137,13 +122,13 @@ static inline int ctcm_dbf_passes(debug_info_t *dbf_grp, int level) */ #define CTCM_DBF_DEV(cat, dev, text) \ do { \ - CTCM_DBF_TEXT_(cat, CTC_DBF_INFO, "%s(%p) : %s", \ + CTCM_DBF_TEXT_(cat, CTC_DBF_INFO, "%s(%p) :-: %s", \ CTCM_FUNTAIL, dev, text); \ } while (0) #define MPC_DBF_DEV(cat, dev, text) \ do { \ - CTCM_DBF_TEXT_(MPC_##cat, CTC_DBF_INFO, "%s(%p) : %s", \ + CTCM_DBF_TEXT_(MPC_##cat, CTC_DBF_INFO, "%s(%p) :=: %s", \ CTCM_FUNTAIL, dev, text); \ } while (0) diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c index 7e6bd387f4d8..0b4e6253abe4 100644 --- a/drivers/s390/net/ctcm_fsms.c +++ b/drivers/s390/net/ctcm_fsms.c @@ -190,7 +190,8 @@ static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg); void ctcm_ccw_check_rc(struct channel *ch, int rc, char *msg) { CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, - "ccw error %s (%s): %04x\n", ch->id, msg, rc); + "%s(%s): %s: %04x\n", + CTCM_FUNTAIL, ch->id, msg, rc); switch (rc) { case -EBUSY: ctcm_pr_warn("%s (%s): Busy !\n", ch->id, msg); @@ -212,7 +213,7 @@ void ctcm_purge_skb_queue(struct sk_buff_head *q) { struct sk_buff *skb; - CTCM_DBF_TEXT(TRACE, 3, __FUNCTION__); + CTCM_DBF_TEXT(TRACE, CTC_DBF_DEBUG, __func__); while ((skb = skb_dequeue(q))) { atomic_dec(&skb->users); @@ -251,6 +252,8 @@ static void chx_txdone(fsm_instance *fi, int event, void *arg) unsigned long duration; struct timespec done_stamp = current_kernel_time(); /* xtime */ + CTCM_PR_DEBUG("%s(%s): %s\n", __func__, ch->id, dev->name); + duration = (done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000 + (done_stamp.tv_nsec - ch->prof.send_stamp.tv_nsec) / 1000; @@ -258,8 +261,9 @@ static void chx_txdone(fsm_instance *fi, int event, void *arg) ch->prof.tx_time = duration; if (ch->irb->scsw.cmd.count != 0) - ctcm_pr_debug("%s: TX not complete, remaining %d bytes\n", - dev->name, ch->irb->scsw.cmd.count); + CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, + "%s(%s): TX not complete, remaining %d bytes", + CTCM_FUNTAIL, dev->name, ch->irb->scsw.cmd.count); fsm_deltimer(&ch->timer); while ((skb = skb_dequeue(&ch->io_queue))) { priv->stats.tx_packets++; @@ -334,7 +338,8 @@ void ctcm_chx_txidle(fsm_instance *fi, int event, void *arg) struct net_device *dev = ch->netdev; struct ctcm_priv *priv = dev->priv; - CTCM_DBF_TEXT(TRACE, 6, __FUNCTION__); + CTCM_PR_DEBUG("%s(%s): %s\n", __func__, ch->id, dev->name); + fsm_deltimer(&ch->timer); fsm_newstate(fi, CTC_STATE_TXIDLE); fsm_event(priv->fsm, DEV_EVENT_TXUP, ch->netdev); @@ -361,15 +366,17 @@ static void chx_rx(fsm_instance *fi, int event, void *arg) fsm_deltimer(&ch->timer); if (len < 8) { - ctcm_pr_debug("%s: got packet with length %d < 8\n", - dev->name, len); + CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, + "%s(%s): got packet with length %d < 8\n", + CTCM_FUNTAIL, dev->name, len); priv->stats.rx_dropped++; priv->stats.rx_length_errors++; goto again; } if (len > ch->max_bufsize) { - ctcm_pr_debug("%s: got packet with length %d > %d\n", - dev->name, len, ch->max_bufsize); + CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, + "%s(%s): got packet with length %d > %d\n", + CTCM_FUNTAIL, dev->name, len, ch->max_bufsize); priv->stats.rx_dropped++; priv->stats.rx_length_errors++; goto again; @@ -388,8 +395,9 @@ static void chx_rx(fsm_instance *fi, int event, void *arg) break; } if ((len < block_len) || (len > check_len)) { - ctcm_pr_debug("%s: got block length %d != rx length %d\n", - dev->name, block_len, len); + CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, + "%s(%s): got block length %d != rx length %d\n", + CTCM_FUNTAIL, dev->name, block_len, len); if (do_debug) ctcmpc_dump_skb(skb, 0); @@ -425,17 +433,23 @@ static void chx_rx(fsm_instance *fi, int event, void *arg) */ static void chx_firstio(fsm_instance *fi, int event, void *arg) { - struct channel *ch = arg; int rc; + struct channel *ch = arg; + int fsmstate = fsm_getstate(fi); - CTCM_DBF_TEXT(TRACE, 6, __FUNCTION__); + CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, + "%s(%s) : %02x", + CTCM_FUNTAIL, ch->id, fsmstate); - if (fsm_getstate(fi) == CTC_STATE_TXIDLE) - ctcm_pr_debug("%s: remote side issued READ?, init.\n", ch->id); + ch->sense_rc = 0; /* reset unit check report control */ + if (fsmstate == CTC_STATE_TXIDLE) + CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, + "%s(%s): remote side issued READ?, init.\n", + CTCM_FUNTAIL, ch->id); fsm_deltimer(&ch->timer); if (ctcm_checkalloc_buffer(ch)) return; - if ((fsm_getstate(fi) == CTC_STATE_SETUPWAIT) && + if ((fsmstate == CTC_STATE_SETUPWAIT) && (ch->protocol == CTCM_PROTO_OS390)) { /* OS/390 resp. z/OS */ if (CHANNEL_DIRECTION(ch->flags) == READ) { @@ -451,7 +465,6 @@ static void chx_firstio(fsm_instance *fi, int event, void *arg) } return; } - /* * Don't setup a timer for receiving the initial RX frame * if in compatibility mode, since VM TCP delays the initial @@ -505,11 +518,10 @@ static void chx_rxidle(fsm_instance *fi, int event, void *arg) __u16 buflen; int rc; - CTCM_DBF_TEXT(TRACE, 6, __FUNCTION__); fsm_deltimer(&ch->timer); buflen = *((__u16 *)ch->trans_skb->data); - if (do_debug) - ctcm_pr_debug("%s: Initial RX count %d\n", dev->name, buflen); + CTCM_PR_DEBUG("%s: %s: Initial RX count = %d\n", + __func__, dev->name, buflen); if (buflen >= CTCM_INITIAL_BLOCKLEN) { if (ctcm_checkalloc_buffer(ch)) @@ -524,9 +536,9 @@ static void chx_rxidle(fsm_instance *fi, int event, void *arg) } else fsm_event(priv->fsm, DEV_EVENT_RXUP, dev); } else { - if (do_debug) - ctcm_pr_debug("%s: Initial RX count %d not %d\n", - dev->name, buflen, CTCM_INITIAL_BLOCKLEN); + CTCM_PR_DEBUG("%s: %s: Initial RX count %d not %d\n", + __func__, dev->name, + buflen, CTCM_INITIAL_BLOCKLEN); chx_firstio(fi, event, arg); } } @@ -548,14 +560,12 @@ static void ctcm_chx_setmode(fsm_instance *fi, int event, void *arg) fsm_deltimer(&ch->timer); if (IS_MPC(ch)) { timeout = 1500; - if (do_debug) - ctcm_pr_debug("ctcm enter: %s(): cp=%i ch=0x%p id=%s\n", - __FUNCTION__, smp_processor_id(), ch, ch->id); + CTCM_PR_DEBUG("enter %s: cp=%i ch=0x%p id=%s\n", + __func__, smp_processor_id(), ch, ch->id); } fsm_addtimer(&ch->timer, timeout, CTC_EVENT_TIMER, ch); fsm_newstate(fi, CTC_STATE_SETUPWAIT); - if (do_debug_ccw && IS_MPC(ch)) - ctcmpc_dumpit((char *)&ch->ccw[6], sizeof(struct ccw1) * 2); + CTCM_CCW_DUMP((char *)&ch->ccw[6], sizeof(struct ccw1) * 2); if (event == CTC_EVENT_TIMER) /* only for timer not yet locked */ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags); @@ -583,24 +593,12 @@ static void ctcm_chx_setmode(fsm_instance *fi, int event, void *arg) */ static void ctcm_chx_start(fsm_instance *fi, int event, void *arg) { - struct channel *ch = arg; - int rc; - struct net_device *dev; + struct channel *ch = arg; unsigned long saveflags; + int rc; - CTCM_DBF_TEXT(TRACE, 5, __FUNCTION__); - if (ch == NULL) { - ctcm_pr_warn("chx_start ch=NULL\n"); - return; - } - if (ch->netdev == NULL) { - ctcm_pr_warn("chx_start dev=NULL, id=%s\n", ch->id); - return; - } - dev = ch->netdev; - - if (do_debug) - ctcm_pr_debug("%s: %s channel start\n", dev->name, + CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s(%s): %s", + CTCM_FUNTAIL, ch->id, (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX"); if (ch->trans_skb != NULL) { @@ -618,11 +616,12 @@ static void ctcm_chx_start(fsm_instance *fi, int event, void *arg) ch->ccw[1].count = 0; } if (ctcm_checkalloc_buffer(ch)) { - ctcm_pr_notice("%s: %s trans_skb allocation delayed " - "until first transfer\n", dev->name, + CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, + "%s(%s): %s trans_skb alloc delayed " + "until first transfer", + CTCM_FUNTAIL, ch->id, (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX"); } - ch->ccw[0].cmd_code = CCW_CMD_PREPARE; ch->ccw[0].flags = CCW_FLAG_SLI | CCW_FLAG_CC; ch->ccw[0].count = 0; @@ -661,7 +660,6 @@ static void ctcm_chx_haltio(fsm_instance *fi, int event, void *arg) int rc; int oldstate; - CTCM_DBF_TEXT(TRACE, 2, __FUNCTION__); fsm_deltimer(&ch->timer); if (IS_MPC(ch)) fsm_deltimer(&ch->sweep_timer); @@ -684,7 +682,7 @@ static void ctcm_chx_haltio(fsm_instance *fi, int event, void *arg) fsm_deltimer(&ch->timer); if (event != CTC_EVENT_STOP) { fsm_newstate(fi, oldstate); - ctcm_ccw_check_rc(ch, rc, (char *)__FUNCTION__); + ctcm_ccw_check_rc(ch, rc, (char *)__func__); } } } @@ -703,7 +701,9 @@ static void ctcm_chx_cleanup(fsm_instance *fi, int state, struct net_device *dev = ch->netdev; struct ctcm_priv *priv = dev->priv; - CTCM_DBF_TEXT(TRACE, 3, __FUNCTION__); + CTCM_DBF_TEXT_(SETUP, CTC_DBF_NOTICE, + "%s(%s): %s[%d]\n", + CTCM_FUNTAIL, dev->name, ch->id, state); fsm_deltimer(&ch->timer); if (IS_MPC(ch)) @@ -743,7 +743,6 @@ static void ctcm_chx_cleanup(fsm_instance *fi, int state, */ static void ctcm_chx_stopped(fsm_instance *fi, int event, void *arg) { - CTCM_DBF_TEXT(TRACE, 3, __FUNCTION__); ctcm_chx_cleanup(fi, CTC_STATE_STOPPED, arg); } @@ -771,7 +770,6 @@ static void ctcm_chx_stop(fsm_instance *fi, int event, void *arg) */ static void ctcm_chx_fail(fsm_instance *fi, int event, void *arg) { - CTCM_DBF_TEXT(TRACE, 3, __FUNCTION__); ctcm_chx_cleanup(fi, CTC_STATE_NOTOP, arg); } @@ -809,8 +807,8 @@ static void ctcm_chx_setuperr(fsm_instance *fi, int event, void *arg) } CTCM_DBF_TEXT_(ERROR, CTC_DBF_CRIT, - "%s : %s error during %s channel setup state=%s\n", - dev->name, ctc_ch_event_names[event], + "%s(%s) : %s error during %s channel setup state=%s\n", + CTCM_FUNTAIL, dev->name, ctc_ch_event_names[event], (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX", fsm_getstate_str(fi)); @@ -838,10 +836,12 @@ static void ctcm_chx_restart(fsm_instance *fi, int event, void *arg) int oldstate; int rc; - CTCM_DBF_TEXT(TRACE, CTC_DBF_NOTICE, __FUNCTION__); + CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, + "%s: %s[%d] of %s\n", + CTCM_FUNTAIL, ch->id, event, dev->name); + fsm_deltimer(&ch->timer); - ctcm_pr_debug("%s: %s channel restart\n", dev->name, - (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX"); + fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch); oldstate = fsm_getstate(fi); fsm_newstate(fi, CTC_STATE_STARTWAIT); @@ -876,13 +876,10 @@ static void ctcm_chx_rxiniterr(fsm_instance *fi, int event, void *arg) struct net_device *dev = ch->netdev; struct ctcm_priv *priv = dev->priv; - CTCM_DBF_TEXT(SETUP, 3, __FUNCTION__); if (event == CTC_EVENT_TIMER) { if (!IS_MPCDEV(dev)) /* TODO : check if MPC deletes timer somewhere */ fsm_deltimer(&ch->timer); - ctcm_pr_debug("%s: Timeout during RX init handshake\n", - dev->name); if (ch->retry++ < 3) ctcm_chx_restart(fi, event, arg); else { @@ -907,9 +904,10 @@ static void ctcm_chx_rxinitfail(fsm_instance *fi, int event, void *arg) struct net_device *dev = ch->netdev; struct ctcm_priv *priv = dev->priv; - CTCM_DBF_TEXT(SETUP, 3, __FUNCTION__); + CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, + "%s(%s): RX %s busy, init. fail", + CTCM_FUNTAIL, dev->name, ch->id); fsm_newstate(fi, CTC_STATE_RXERR); - ctcm_pr_warn("%s: RX busy. Initialization failed\n", dev->name); fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev); } @@ -927,11 +925,10 @@ static void ctcm_chx_rxdisc(fsm_instance *fi, int event, void *arg) struct net_device *dev = ch->netdev; struct ctcm_priv *priv = dev->priv; - CTCM_DBF_DEV_NAME(TRACE, dev, "Got remote disconnect, re-initializing"); + CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, + "%s: %s: remote disconnect - re-init ...", + CTCM_FUNTAIL, dev->name); fsm_deltimer(&ch->timer); - if (do_debug) - ctcm_pr_debug("%s: Got remote disconnect, " - "re-initializing ...\n", dev->name); /* * Notify device statemachine */ @@ -961,8 +958,6 @@ static void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg) if (event == CTC_EVENT_TIMER) { fsm_deltimer(&ch->timer); - CTCM_DBF_DEV_NAME(ERROR, dev, - "Timeout during TX init handshake"); if (ch->retry++ < 3) ctcm_chx_restart(fi, event, arg); else { @@ -971,9 +966,8 @@ static void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg) } } else { CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, - "%s : %s error during channel setup state=%s", - dev->name, ctc_ch_event_names[event], - fsm_getstate_str(fi)); + "%s(%s): %s in %s", CTCM_FUNTAIL, ch->id, + ctc_ch_event_names[event], fsm_getstate_str(fi)); ctcm_pr_warn("%s: Error during TX init handshake\n", dev->name); } @@ -993,15 +987,15 @@ static void ctcm_chx_txretry(fsm_instance *fi, int event, void *arg) struct ctcm_priv *priv = dev->priv; struct sk_buff *skb; - if (do_debug) - ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n", - __FUNCTION__, smp_processor_id(), ch, ch->id); + CTCM_PR_DEBUG("Enter: %s: cp=%i ch=0x%p id=%s\n", + __func__, smp_processor_id(), ch, ch->id); fsm_deltimer(&ch->timer); if (ch->retry++ > 3) { struct mpc_group *gptr = priv->mpcg; - ctcm_pr_debug("%s: TX retry failed, restarting channel\n", - dev->name); + CTCM_DBF_TEXT_(TRACE, CTC_DBF_INFO, + "%s: %s: retries exceeded", + CTCM_FUNTAIL, ch->id); fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev); /* call restart if not MPC or if MPC and mpcg fsm is ready. use gptr as mpc indicator */ @@ -1010,7 +1004,9 @@ static void ctcm_chx_txretry(fsm_instance *fi, int event, void *arg) goto done; } - ctcm_pr_debug("%s: TX retry %d\n", dev->name, ch->retry); + CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, + "%s : %s: retry %d", + CTCM_FUNTAIL, ch->id, ch->retry); skb = skb_peek(&ch->io_queue); if (skb) { int rc = 0; @@ -1018,8 +1014,9 @@ static void ctcm_chx_txretry(fsm_instance *fi, int event, void *arg) clear_normalized_cda(&ch->ccw[4]); ch->ccw[4].count = skb->len; if (set_normalized_cda(&ch->ccw[4], skb->data)) { - ctcm_pr_debug("%s: IDAL alloc failed, chan restart\n", - dev->name); + CTCM_DBF_TEXT_(TRACE, CTC_DBF_INFO, + "%s: %s: IDAL alloc failed", + CTCM_FUNTAIL, ch->id); fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev); ctcm_chx_restart(fi, event, arg); goto done; @@ -1061,22 +1058,21 @@ static void ctcm_chx_iofatal(fsm_instance *fi, int event, void *arg) struct channel *ch = arg; struct net_device *dev = ch->netdev; struct ctcm_priv *priv = dev->priv; + int rd = CHANNEL_DIRECTION(ch->flags); - CTCM_DBF_TEXT(TRACE, 3, __FUNCTION__); fsm_deltimer(&ch->timer); - ctcm_pr_warn("%s %s : unrecoverable channel error\n", - CTC_DRIVER_NAME, dev->name); + CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, + "%s: %s: %s unrecoverable channel error", + CTCM_FUNTAIL, ch->id, rd == READ ? "RX" : "TX"); + if (IS_MPC(ch)) { priv->stats.tx_dropped++; priv->stats.tx_errors++; } - - if (CHANNEL_DIRECTION(ch->flags) == READ) { - ctcm_pr_debug("%s: RX I/O error\n", dev->name); + if (rd == READ) { fsm_newstate(fi, CTC_STATE_RXERR); fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev); } else { - ctcm_pr_debug("%s: TX I/O error\n", dev->name); fsm_newstate(fi, CTC_STATE_TXERR); fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev); } @@ -1216,27 +1212,27 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg) struct sk_buff *skb; int first = 1; int i; - struct timespec done_stamp; __u32 data_space; unsigned long duration; struct sk_buff *peekskb; int rc; struct th_header *header; struct pdu *p_header; + struct timespec done_stamp = current_kernel_time(); /* xtime */ - if (do_debug) - ctcm_pr_debug("%s cp:%i enter: %s()\n", - dev->name, smp_processor_id(), __FUNCTION__); + CTCM_PR_DEBUG("Enter %s: %s cp:%i\n", + __func__, dev->name, smp_processor_id()); - done_stamp = current_kernel_time(); /* xtime */ - duration = (done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000 - + (done_stamp.tv_nsec - ch->prof.send_stamp.tv_nsec) / 1000; + duration = + (done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000 + + (done_stamp.tv_nsec - ch->prof.send_stamp.tv_nsec) / 1000; if (duration > ch->prof.tx_time) ch->prof.tx_time = duration; if (ch->irb->scsw.cmd.count != 0) - ctcm_pr_debug("%s: TX not complete, remaining %d bytes\n", - dev->name, ch->irb->scsw.cmd.count); + CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG, + "%s(%s): TX not complete, remaining %d bytes", + CTCM_FUNTAIL, dev->name, ch->irb->scsw.cmd.count); fsm_deltimer(&ch->timer); while ((skb = skb_dequeue(&ch->io_queue))) { priv->stats.tx_packets++; @@ -1250,7 +1246,6 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg) } spin_lock(&ch->collect_lock); clear_normalized_cda(&ch->ccw[4]); - if ((ch->collect_len <= 0) || (grp->in_sweep != 0)) { spin_unlock(&ch->collect_lock); fsm_newstate(fi, CTC_STATE_TXIDLE); @@ -1269,17 +1264,13 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg) if (ch->prof.maxcqueue < skb_queue_len(&ch->collect_queue)) ch->prof.maxcqueue = skb_queue_len(&ch->collect_queue); i = 0; - - if (do_debug_data) - ctcm_pr_debug("ctcmpc: %s() building " - "trans_skb from collect_q \n", __FUNCTION__); - + p_header = NULL; data_space = grp->group_max_buflen - TH_HEADER_LENGTH; - if (do_debug_data) - ctcm_pr_debug("ctcmpc: %s() building trans_skb from collect_q" - " data_space:%04x\n", __FUNCTION__, data_space); - p_header = NULL; + CTCM_PR_DBGDATA("%s: building trans_skb from collect_q" + " data_space:%04x\n", + __func__, data_space); + while ((skb = skb_dequeue(&ch->collect_queue))) { memcpy(skb_put(ch->trans_skb, skb->len), skb->data, skb->len); p_header = (struct pdu *) @@ -1290,15 +1281,12 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg) else p_header->pdu_flag |= 0x20; - if (do_debug_data) { - ctcm_pr_debug("ctcmpc: %s()trans_skb len:%04x \n", - __FUNCTION__, ch->trans_skb->len); - ctcm_pr_debug("ctcmpc: %s() pdu header and data" - " for up to 32 bytes sent to vtam\n", - __FUNCTION__); - ctcmpc_dumpit((char *)p_header, - min_t(int, skb->len, 32)); - } + CTCM_PR_DBGDATA("%s: trans_skb len:%04x \n", + __func__, ch->trans_skb->len); + CTCM_PR_DBGDATA("%s: pdu header and data for up" + " to 32 bytes sent to vtam\n", __func__); + CTCM_D3_DUMP((char *)p_header, min_t(int, skb->len, 32)); + ch->collect_len -= skb->len; data_space -= skb->len; priv->stats.tx_packets++; @@ -1314,46 +1302,38 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg) if (p_header) p_header->pdu_flag |= PDU_LAST; /*Say it's the last one*/ header = kzalloc(TH_HEADER_LENGTH, gfp_type()); - if (!header) { - printk(KERN_WARNING "ctcmpc: OUT OF MEMORY IN %s()" - ": Data Lost \n", __FUNCTION__); spin_unlock(&ch->collect_lock); fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev); - goto done; + goto done; } - header->th_ch_flag = TH_HAS_PDU; /* Normal data */ ch->th_seq_num++; header->th_seq_num = ch->th_seq_num; - if (do_debug_data) - ctcm_pr_debug("%s: ToVTAM_th_seq= %08x\n" , - __FUNCTION__, ch->th_seq_num); + CTCM_PR_DBGDATA("%s: ToVTAM_th_seq= %08x\n" , + __func__, ch->th_seq_num); memcpy(skb_push(ch->trans_skb, TH_HEADER_LENGTH), header, TH_HEADER_LENGTH); /* put the TH on the packet */ kfree(header); - if (do_debug_data) { - ctcm_pr_debug("ctcmpc: %s()trans_skb len:%04x \n", - __FUNCTION__, ch->trans_skb->len); - - ctcm_pr_debug("ctcmpc: %s() up-to-50 bytes of trans_skb " - "data to vtam from collect_q\n", __FUNCTION__); - ctcmpc_dumpit((char *)ch->trans_skb->data, + CTCM_PR_DBGDATA("%s: trans_skb len:%04x \n", + __func__, ch->trans_skb->len); + CTCM_PR_DBGDATA("%s: up-to-50 bytes of trans_skb " + "data to vtam from collect_q\n", __func__); + CTCM_D3_DUMP((char *)ch->trans_skb->data, min_t(int, ch->trans_skb->len, 50)); - } spin_unlock(&ch->collect_lock); clear_normalized_cda(&ch->ccw[1]); if (set_normalized_cda(&ch->ccw[1], ch->trans_skb->data)) { dev_kfree_skb_any(ch->trans_skb); ch->trans_skb = NULL; - printk(KERN_WARNING - "ctcmpc: %s()CCW failure - data lost\n", - __FUNCTION__); + CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ERROR, + "%s: %s: IDAL alloc failed", + CTCM_FUNTAIL, ch->id); fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev); return; } @@ -1373,7 +1353,6 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg) } done: ctcm_clear_busy(dev); - ctcm_pr_debug("ctcmpc exit: %s %s()\n", dev->name, __FUNCTION__); return; } @@ -1393,26 +1372,25 @@ static void ctcmpc_chx_rx(fsm_instance *fi, int event, void *arg) struct mpc_group *grp = priv->mpcg; struct sk_buff *skb = ch->trans_skb; struct sk_buff *new_skb; - unsigned long saveflags = 0; /* avoids compiler warning */ + unsigned long saveflags = 0; /* avoids compiler warning */ int len = ch->max_bufsize - ch->irb->scsw.cmd.count; - if (do_debug_data) { - CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, "mpc_ch_rx %s cp:%i %s\n", - dev->name, smp_processor_id(), ch->id); - CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, "mpc_ch_rx: maxbuf: %04x " - "len: %04x\n", ch->max_bufsize, len); - } + CTCM_PR_DEBUG("%s: %s: cp:%i %s maxbuf : %04x, len: %04x\n", + CTCM_FUNTAIL, dev->name, smp_processor_id(), + ch->id, ch->max_bufsize, len); fsm_deltimer(&ch->timer); if (skb == NULL) { - ctcm_pr_debug("ctcmpc exit: %s() TRANS_SKB = NULL \n", - __FUNCTION__); - goto again; + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): TRANS_SKB = NULL", + CTCM_FUNTAIL, dev->name); + goto again; } if (len < TH_HEADER_LENGTH) { - ctcm_pr_info("%s: got packet with invalid length %d\n", - dev->name, len); + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): packet length %d to short", + CTCM_FUNTAIL, dev->name, len); priv->stats.rx_dropped++; priv->stats.rx_length_errors++; } else { @@ -1422,11 +1400,9 @@ static void ctcmpc_chx_rx(fsm_instance *fi, int event, void *arg) new_skb = __dev_alloc_skb(ch->max_bufsize, GFP_ATOMIC); if (new_skb == NULL) { - printk(KERN_INFO "ctcmpc:%s() NEW_SKB = NULL\n", - __FUNCTION__); - printk(KERN_WARNING "ctcmpc: %s() MEMORY ALLOC FAILED" - " - DATA LOST - MPC FAILED\n", - __FUNCTION__); + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%d): skb allocation failed", + CTCM_FUNTAIL, dev->name); fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev); goto again; } @@ -1479,9 +1455,8 @@ again: break; } - if (do_debug) - ctcm_pr_debug("ctcmpc exit : %s %s(): ch=0x%p id=%s\n", - dev->name, __FUNCTION__, ch, ch->id); + CTCM_PR_DEBUG("Exit %s: %s, ch=0x%p, id=%s\n", + __func__, dev->name, ch, ch->id); } @@ -1497,15 +1472,16 @@ static void ctcmpc_chx_firstio(fsm_instance *fi, int event, void *arg) struct channel *ch = arg; struct net_device *dev = ch->netdev; struct ctcm_priv *priv = dev->priv; + struct mpc_group *gptr = priv->mpcg; + + CTCM_PR_DEBUG("Enter %s: id=%s, ch=0x%p\n", + __func__, ch->id, ch); + + CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_INFO, + "%s: %s: chstate:%i, grpstate:%i, prot:%i\n", + CTCM_FUNTAIL, ch->id, fsm_getstate(fi), + fsm_getstate(gptr->fsm), ch->protocol); - if (do_debug) { - struct mpc_group *gptr = priv->mpcg; - ctcm_pr_debug("ctcmpc enter: %s(): ch=0x%p id=%s\n", - __FUNCTION__, ch, ch->id); - ctcm_pr_debug("%s() %s chstate:%i grpstate:%i chprotocol:%i\n", - __FUNCTION__, ch->id, fsm_getstate(fi), - fsm_getstate(gptr->fsm), ch->protocol); - } if (fsm_getstate(fi) == CTC_STATE_TXIDLE) MPC_DBF_DEV_NAME(TRACE, dev, "remote side issued READ? "); @@ -1531,9 +1507,8 @@ static void ctcmpc_chx_firstio(fsm_instance *fi, int event, void *arg) ? CTC_STATE_RXINIT : CTC_STATE_TXINIT); done: - if (do_debug) - ctcm_pr_debug("ctcmpc exit : %s(): ch=0x%p id=%s\n", - __FUNCTION__, ch, ch->id); + CTCM_PR_DEBUG("Exit %s: id=%s, ch=0x%p\n", + __func__, ch->id, ch); return; } @@ -1556,12 +1531,9 @@ void ctcmpc_chx_rxidle(fsm_instance *fi, int event, void *arg) unsigned long saveflags = 0; /* avoids compiler warning */ fsm_deltimer(&ch->timer); - ctcm_pr_debug("%s cp:%i enter: %s()\n", - dev->name, smp_processor_id(), __FUNCTION__); - if (do_debug) - ctcm_pr_debug("%s() %s chstate:%i grpstate:%i\n", - __FUNCTION__, ch->id, - fsm_getstate(fi), fsm_getstate(grp->fsm)); + CTCM_PR_DEBUG("%s: %s: %s: cp:%i, chstate:%i grpstate:%i\n", + __func__, ch->id, dev->name, smp_processor_id(), + fsm_getstate(fi), fsm_getstate(grp->fsm)); fsm_newstate(fi, CTC_STATE_RXIDLE); /* XID processing complete */ @@ -1575,9 +1547,7 @@ void ctcmpc_chx_rxidle(fsm_instance *fi, int event, void *arg) skb_reset_tail_pointer(ch->trans_skb); ch->trans_skb->len = 0; ch->ccw[1].count = ch->max_bufsize; - if (do_debug_ccw) - ctcmpc_dumpit((char *)&ch->ccw[0], - sizeof(struct ccw1) * 3); + CTCM_CCW_DUMP((char *)&ch->ccw[0], sizeof(struct ccw1) * 3); if (event == CTC_EVENT_START) /* see remark about conditional locking */ spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags); @@ -1598,9 +1568,6 @@ void ctcmpc_chx_rxidle(fsm_instance *fi, int event, void *arg) fsm_event(priv->fsm, DEV_EVENT_RXUP, dev); done: - if (do_debug) - ctcm_pr_debug("ctcmpc exit: %s %s()\n", - dev->name, __FUNCTION__); return; } @@ -1616,13 +1583,9 @@ static void ctcmpc_chx_attn(fsm_instance *fsm, int event, void *arg) struct ctcm_priv *priv = dev->priv; struct mpc_group *grp = priv->mpcg; - if (do_debug) { - ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s" - "GrpState:%s ChState:%s\n", - __FUNCTION__, smp_processor_id(), ch, ch->id, - fsm_getstate_str(grp->fsm), - fsm_getstate_str(ch->fsm)); - } + CTCM_PR_DEBUG("%s(%s): %s(ch=0x%p), cp=%i, ChStat:%s, GrpStat:%s\n", + __func__, dev->name, ch->id, ch, smp_processor_id(), + fsm_getstate_str(ch->fsm), fsm_getstate_str(grp->fsm)); switch (fsm_getstate(grp->fsm)) { case MPCG_STATE_XID2INITW: @@ -1664,11 +1627,7 @@ static void ctcmpc_chx_attn(fsm_instance *fsm, int event, void *arg) break; } - if (do_debug) - ctcm_pr_debug("ctcmpc exit : %s(): cp=%i ch=0x%p id=%s\n", - __FUNCTION__, smp_processor_id(), ch, ch->id); return; - } /* @@ -1683,11 +1642,9 @@ static void ctcmpc_chx_attnbusy(fsm_instance *fsm, int event, void *arg) struct ctcm_priv *priv = dev->priv; struct mpc_group *grp = priv->mpcg; - ctcm_pr_debug("ctcmpc enter: %s %s() %s \nGrpState:%s ChState:%s\n", - dev->name, - __FUNCTION__, ch->id, - fsm_getstate_str(grp->fsm), - fsm_getstate_str(ch->fsm)); + CTCM_PR_DEBUG("%s(%s): %s\n ChState:%s GrpState:%s\n", + __func__, dev->name, ch->id, + fsm_getstate_str(ch->fsm), fsm_getstate_str(grp->fsm)); fsm_deltimer(&ch->timer); @@ -1750,16 +1707,12 @@ static void ctcmpc_chx_attnbusy(fsm_instance *fsm, int event, void *arg) if (ch->in_mpcgroup) fsm_event(grp->fsm, MPCG_EVENT_XID0DO, ch); else - printk(KERN_WARNING "ctcmpc: %s() Not all channels have" - " been added to group\n", __FUNCTION__); + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): channel %s not added to group", + CTCM_FUNTAIL, dev->name, ch->id); done: - if (do_debug) - ctcm_pr_debug("ctcmpc exit : %s()%s ch=0x%p id=%s\n", - __FUNCTION__, dev->name, ch, ch->id); - return; - } /* @@ -1774,13 +1727,7 @@ static void ctcmpc_chx_resend(fsm_instance *fsm, int event, void *arg) struct ctcm_priv *priv = dev->priv; struct mpc_group *grp = priv->mpcg; - ctcm_pr_debug("ctcmpc enter: %s %s() %s \nGrpState:%s ChState:%s\n", - dev->name, __FUNCTION__, ch->id, - fsm_getstate_str(grp->fsm), - fsm_getstate_str(ch->fsm)); - fsm_event(grp->fsm, MPCG_EVENT_XID0DO, ch); - return; } @@ -1802,19 +1749,16 @@ static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg) int rc = 0; unsigned long saveflags = 0; - if (do_debug) - ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n", - __FUNCTION__, smp_processor_id(), ach, ach->id); + CTCM_PR_DEBUG("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n", + __func__, smp_processor_id(), ach, ach->id); if (grp->in_sweep == 0) goto done; - if (do_debug_data) { - ctcm_pr_debug("ctcmpc: %s() 1: ToVTAM_th_seq= %08x\n" , - __FUNCTION__, wch->th_seq_num); - ctcm_pr_debug("ctcmpc: %s() 1: FromVTAM_th_seq= %08x\n" , - __FUNCTION__, rch->th_seq_num); - } + CTCM_PR_DBGDATA("%s: 1: ToVTAM_th_seq= %08x\n" , + __func__, wch->th_seq_num); + CTCM_PR_DBGDATA("%s: 1: FromVTAM_th_seq= %08x\n" , + __func__, rch->th_seq_num); if (fsm_getstate(wch->fsm) != CTC_STATE_TXIDLE) { /* give the previous IO time to complete */ @@ -1853,11 +1797,9 @@ static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg) header->sw.th_last_seq = wch->th_seq_num; - if (do_debug_ccw) - ctcmpc_dumpit((char *)&wch->ccw[3], sizeof(struct ccw1) * 3); - - ctcm_pr_debug("ctcmpc: %s() sweep packet\n", __FUNCTION__); - ctcmpc_dumpit((char *)header, TH_SWEEP_LENGTH); + CTCM_CCW_DUMP((char *)&wch->ccw[3], sizeof(struct ccw1) * 3); + CTCM_PR_DBGDATA("%s: sweep packet\n", __func__); + CTCM_D3_DUMP((char *)header, TH_SWEEP_LENGTH); fsm_addtimer(&wch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, wch); fsm_newstate(wch->fsm, CTC_STATE_TX); @@ -1876,19 +1818,13 @@ static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg) ctcm_clear_busy_do(dev); } - if (do_debug_data) { - ctcm_pr_debug("ctcmpc: %s()2: ToVTAM_th_seq= %08x\n" , - __FUNCTION__, wch->th_seq_num); - ctcm_pr_debug("ctcmpc: %s()2: FromVTAM_th_seq= %08x\n" , - __FUNCTION__, rch->th_seq_num); - } + CTCM_PR_DBGDATA("%s: To-/From-VTAM_th_seq = %08x/%08x\n" , + __func__, wch->th_seq_num, rch->th_seq_num); if (rc != 0) ctcm_ccw_check_rc(wch, rc, "send sweep"); done: - if (do_debug) - ctcm_pr_debug("ctcmpc exit: %s() %s\n", __FUNCTION__, ach->id); return; } @@ -2149,9 +2085,8 @@ static void dev_action_stop(fsm_instance *fi, int event, void *arg) struct channel *ch = priv->channel[direction]; fsm_event(ch->fsm, CTC_EVENT_STOP, ch); ch->th_seq_num = 0x00; - if (do_debug) - ctcm_pr_debug("ctcm: %s() CH_th_seq= %08x\n", - __FUNCTION__, ch->th_seq_num); + CTCM_PR_DEBUG("%s: CH_th_seq= %08x\n", + __func__, ch->th_seq_num); } if (IS_MPC(priv)) fsm_newstate(priv->mpcg->fsm, MPCG_STATE_RESET); @@ -2199,8 +2134,11 @@ static void dev_action_chup(fsm_instance *fi, int event, void *arg) { struct net_device *dev = arg; struct ctcm_priv *priv = dev->priv; + int dev_stat = fsm_getstate(fi); - CTCMY_DBF_DEV_NAME(SETUP, dev, ""); + CTCM_DBF_TEXT_(SETUP, CTC_DBF_NOTICE, + "%s(%s): priv = %p [%d,%d]\n ", CTCM_FUNTAIL, + dev->name, dev->priv, dev_stat, event); switch (fsm_getstate(fi)) { case DEV_STATE_STARTWAIT_RXTX: diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index 6b13c1c1beb8..126a3ebb8ab2 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -84,20 +84,19 @@ void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb) skb_pull(pskb, LL_HEADER_LENGTH); if ((ch->protocol == CTCM_PROTO_S390) && (header->type != ETH_P_IP)) { - if (!(ch->logflags & LOG_FLAG_ILLEGALPKT)) { + ch->logflags |= LOG_FLAG_ILLEGALPKT; /* * Check packet type only if we stick strictly * to S/390's protocol of OS390. This only * supports IP. Otherwise allow any packet * type. */ - ctcm_pr_warn("%s Illegal packet type 0x%04x " - "received, dropping\n", - dev->name, header->type); - ch->logflags |= LOG_FLAG_ILLEGALPKT; + CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, + "%s(%s): Illegal packet type 0x%04x" + " - dropping", + CTCM_FUNTAIL, dev->name, header->type); } - priv->stats.rx_dropped++; priv->stats.rx_frame_errors++; return; @@ -105,11 +104,11 @@ void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb) pskb->protocol = ntohs(header->type); if (header->length <= LL_HEADER_LENGTH) { if (!(ch->logflags & LOG_FLAG_ILLEGALSIZE)) { - ctcm_pr_warn( - "%s Illegal packet size %d " - "received (MTU=%d blocklen=%d), " - "dropping\n", dev->name, header->length, - dev->mtu, len); + CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, + "%s(%s): Illegal packet size %d(%d,%d)" + "- dropping", + CTCM_FUNTAIL, dev->name, + header->length, dev->mtu, len); ch->logflags |= LOG_FLAG_ILLEGALSIZE; } @@ -122,10 +121,10 @@ void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb) if ((header->length > skb_tailroom(pskb)) || (header->length > len)) { if (!(ch->logflags & LOG_FLAG_OVERRUN)) { - ctcm_pr_warn( - "%s Illegal packet size %d (beyond the" - " end of received data), dropping\n", - dev->name, header->length); + CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, + "%s(%s): Packet size %d (overrun)" + " - dropping", CTCM_FUNTAIL, + dev->name, header->length); ch->logflags |= LOG_FLAG_OVERRUN; } @@ -139,9 +138,9 @@ void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb) skb = dev_alloc_skb(pskb->len); if (!skb) { if (!(ch->logflags & LOG_FLAG_NOMEM)) { - ctcm_pr_warn( - "%s Out of memory in ctcm_unpack_skb\n", - dev->name); + CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, + "%s(%s): MEMORY allocation error", + CTCM_FUNTAIL, dev->name); ch->logflags |= LOG_FLAG_NOMEM; } priv->stats.rx_dropped++; @@ -184,7 +183,7 @@ void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb) */ static void channel_free(struct channel *ch) { - CTCM_DBF_TEXT(TRACE, 2, __FUNCTION__); + CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s(%s)", CTCM_FUNTAIL, ch->id); ch->flags &= ~CHANNEL_FLAGS_INUSE; fsm_newstate(ch->fsm, CTC_STATE_IDLE); } @@ -251,19 +250,12 @@ static struct channel *channel_get(enum channel_types type, { struct channel *ch = channels; - if (do_debug) { - char buf[64]; - sprintf(buf, "%s(%d, %s, %d)\n", - CTCM_FUNTAIL, type, id, direction); - CTCM_DBF_TEXT(TRACE, CTC_DBF_INFO, buf); - } while (ch && (strncmp(ch->id, id, CTCM_ID_SIZE) || (ch->type != type))) ch = ch->next; if (!ch) { - char buf[64]; - sprintf(buf, "%s(%d, %s, %d) not found in channel list\n", + CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, + "%s(%d, %s, %d) not found in channel list\n", CTCM_FUNTAIL, type, id, direction); - CTCM_DBF_TEXT(ERROR, CTC_DBF_ERROR, buf); } else { if (ch->flags & CHANNEL_FLAGS_INUSE) ch = NULL; @@ -283,8 +275,9 @@ static long ctcm_check_irb_error(struct ccw_device *cdev, struct irb *irb) if (!IS_ERR(irb)) return 0; - CTCM_DBF_TEXT_(ERROR, CTC_DBF_WARN, "irb error %ld on device %s\n", - PTR_ERR(irb), cdev->dev.bus_id); + CTCM_DBF_TEXT_(ERROR, CTC_DBF_WARN, + "irb error %ld on device %s\n", + PTR_ERR(irb), cdev->dev.bus_id); switch (PTR_ERR(irb)) { case -EIO: @@ -307,58 +300,85 @@ static long ctcm_check_irb_error(struct ccw_device *cdev, struct irb *irb) * ch The channel, the sense code belongs to. * sense The sense code to inspect. */ -static inline void ccw_unit_check(struct channel *ch, unsigned char sense) +static inline void ccw_unit_check(struct channel *ch, __u8 sense) { - CTCM_DBF_TEXT(TRACE, 5, __FUNCTION__); + CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, + "%s(%s): %02x", + CTCM_FUNTAIL, ch->id, sense); + if (sense & SNS0_INTERVENTION_REQ) { if (sense & 0x01) { - ctcm_pr_debug("%s: Interface disc. or Sel. reset " - "(remote)\n", ch->id); + if (ch->sense_rc != 0x01) { + ctcm_pr_debug("%s: Interface disc. or Sel. " + "reset (remote)\n", ch->id); + ch->sense_rc = 0x01; + } fsm_event(ch->fsm, CTC_EVENT_UC_RCRESET, ch); } else { - ctcm_pr_debug("%s: System reset (remote)\n", ch->id); + if (ch->sense_rc != SNS0_INTERVENTION_REQ) { + ctcm_pr_debug("%s: System reset (remote)\n", + ch->id); + ch->sense_rc = SNS0_INTERVENTION_REQ; + } fsm_event(ch->fsm, CTC_EVENT_UC_RSRESET, ch); } } else if (sense & SNS0_EQUIPMENT_CHECK) { if (sense & SNS0_BUS_OUT_CHECK) { - ctcm_pr_warn("%s: Hardware malfunction (remote)\n", - ch->id); + if (ch->sense_rc != SNS0_BUS_OUT_CHECK) { + CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN, + "%s(%s): remote HW error %02x", + CTCM_FUNTAIL, ch->id, sense); + ch->sense_rc = SNS0_BUS_OUT_CHECK; + } fsm_event(ch->fsm, CTC_EVENT_UC_HWFAIL, ch); } else { - ctcm_pr_warn("%s: Read-data parity error (remote)\n", - ch->id); + if (ch->sense_rc != SNS0_EQUIPMENT_CHECK) { + CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN, + "%s(%s): remote read parity error %02x", + CTCM_FUNTAIL, ch->id, sense); + ch->sense_rc = SNS0_EQUIPMENT_CHECK; + } fsm_event(ch->fsm, CTC_EVENT_UC_RXPARITY, ch); } } else if (sense & SNS0_BUS_OUT_CHECK) { - if (sense & 0x04) { - ctcm_pr_warn("%s: Data-streaming timeout)\n", ch->id); + if (ch->sense_rc != SNS0_BUS_OUT_CHECK) { + CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN, + "%s(%s): BUS OUT error %02x", + CTCM_FUNTAIL, ch->id, sense); + ch->sense_rc = SNS0_BUS_OUT_CHECK; + } + if (sense & 0x04) /* data-streaming timeout */ fsm_event(ch->fsm, CTC_EVENT_UC_TXTIMEOUT, ch); - } else { - ctcm_pr_warn("%s: Data-transfer parity error\n", - ch->id); + else /* Data-transfer parity error */ fsm_event(ch->fsm, CTC_EVENT_UC_TXPARITY, ch); - } } else if (sense & SNS0_CMD_REJECT) { - ctcm_pr_warn("%s: Command reject\n", ch->id); + if (ch->sense_rc != SNS0_CMD_REJECT) { + CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN, + "%s(%s): Command rejected", + CTCM_FUNTAIL, ch->id); + ch->sense_rc = SNS0_CMD_REJECT; + } } else if (sense == 0) { - ctcm_pr_debug("%s: Unit check ZERO\n", ch->id); + CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN, + "%s(%s): Unit check ZERO", + CTCM_FUNTAIL, ch->id); fsm_event(ch->fsm, CTC_EVENT_UC_ZERO, ch); } else { - ctcm_pr_warn("%s: Unit Check with sense code: %02x\n", - ch->id, sense); + CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN, + "%s(%s): Unit check code %02x unknown", + CTCM_FUNTAIL, ch->id, sense); fsm_event(ch->fsm, CTC_EVENT_UC_UNKNOWN, ch); } } int ctcm_ch_alloc_buffer(struct channel *ch) { - CTCM_DBF_TEXT(TRACE, 5, __FUNCTION__); - clear_normalized_cda(&ch->ccw[1]); ch->trans_skb = __dev_alloc_skb(ch->max_bufsize, GFP_ATOMIC | GFP_DMA); if (ch->trans_skb == NULL) { - ctcm_pr_warn("%s: Couldn't alloc %s trans_skb\n", - ch->id, + CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, + "%s(%s): %s trans_skb allocation error", + CTCM_FUNTAIL, ch->id, (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX"); return -ENOMEM; } @@ -367,9 +387,9 @@ int ctcm_ch_alloc_buffer(struct channel *ch) if (set_normalized_cda(&ch->ccw[1], ch->trans_skb->data)) { dev_kfree_skb(ch->trans_skb); ch->trans_skb = NULL; - ctcm_pr_warn("%s: set_normalized_cda for %s " - "trans_skb failed, dropping packets\n", - ch->id, + CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, + "%s(%s): %s set norm_cda failed", + CTCM_FUNTAIL, ch->id, (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX"); return -ENOMEM; } @@ -516,7 +536,7 @@ static int ctcm_transmit_skb(struct channel *ch, struct sk_buff *skb) atomic_dec(&skb->users); skb_pull(skb, LL_HEADER_LENGTH + 2); ctcm_clear_busy(ch->netdev); - return -EBUSY; + return -ENOMEM; } skb_reset_tail_pointer(ch->trans_skb); @@ -570,15 +590,12 @@ static void ctcmpc_send_sweep_req(struct channel *rch) struct th_sweep *header; struct sk_buff *sweep_skb; struct channel *ch; - int rc = 0; + /* int rc = 0; */ priv = dev->priv; grp = priv->mpcg; ch = priv->channel[WRITE]; - if (do_debug) - MPC_DBF_DEV_NAME(TRACE, dev, ch->id); - /* sweep processing is not complete until response and request */ /* has completed for all read channels in group */ if (grp->in_sweep == 0) { @@ -590,17 +607,16 @@ static void ctcmpc_send_sweep_req(struct channel *rch) sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC|GFP_DMA); if (sweep_skb == NULL) { - printk(KERN_INFO "Couldn't alloc sweep_skb\n"); - rc = -ENOMEM; - goto done; + /* rc = -ENOMEM; */ + goto nomem; } header = kmalloc(TH_SWEEP_LENGTH, gfp_type()); if (!header) { dev_kfree_skb_any(sweep_skb); - rc = -ENOMEM; - goto done; + /* rc = -ENOMEM; */ + goto nomem; } header->th.th_seg = 0x00 ; @@ -621,12 +637,10 @@ static void ctcmpc_send_sweep_req(struct channel *rch) return; -done: - if (rc != 0) { - grp->in_sweep = 0; - ctcm_clear_busy(dev); - fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); - } +nomem: + grp->in_sweep = 0; + ctcm_clear_busy(dev); + fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); return; } @@ -648,11 +662,9 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb) unsigned long saveflags = 0; /* avoids compiler warning */ __u16 block_len; - if (do_debug) - ctcm_pr_debug( - "ctcm enter: %s(): %s cp=%i ch=0x%p id=%s state=%s\n", - __FUNCTION__, dev->name, smp_processor_id(), ch, - ch->id, fsm_getstate_str(ch->fsm)); + CTCM_PR_DEBUG("Enter %s: %s, cp=%i ch=0x%p id=%s state=%s\n", + __func__, dev->name, smp_processor_id(), ch, + ch->id, fsm_getstate_str(ch->fsm)); if ((fsm_getstate(ch->fsm) != CTC_STATE_TXIDLE) || grp->in_sweep) { spin_lock_irqsave(&ch->collect_lock, saveflags); @@ -660,14 +672,8 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb) p_header = kmalloc(PDU_HEADER_LENGTH, gfp_type()); if (!p_header) { - printk(KERN_WARNING "ctcm: OUT OF MEMORY IN %s():" - " Data Lost \n", __FUNCTION__); - - atomic_dec(&skb->users); - dev_kfree_skb_any(skb); spin_unlock_irqrestore(&ch->collect_lock, saveflags); - fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev); - goto done; + goto nomem_exit; } p_header->pdu_offset = skb->len; @@ -682,13 +688,10 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb) memcpy(skb_push(skb, PDU_HEADER_LENGTH), p_header, PDU_HEADER_LENGTH); - if (do_debug_data) { - ctcm_pr_debug("ctcm: %s() Putting on collect_q" - " - skb len: %04x \n", __FUNCTION__, skb->len); - ctcm_pr_debug("ctcm: %s() pdu header and data" - " for up to 32 bytes\n", __FUNCTION__); - ctcmpc_dump32((char *)skb->data, skb->len); - } + CTCM_PR_DEBUG("%s(%s): Put on collect_q - skb len: %04x \n" + "pdu header and data for up to 32 bytes:\n", + __func__, dev->name, skb->len); + CTCM_D3_DUMP((char *)skb->data, min_t(int, 32, skb->len)); skb_queue_tail(&ch->collect_queue, skb); ch->collect_len += skb->len; @@ -713,12 +716,7 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb) if (hi) { nskb = __dev_alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA); if (!nskb) { - printk(KERN_WARNING "ctcm: %s() OUT OF MEMORY" - "- Data Lost \n", __FUNCTION__); - atomic_dec(&skb->users); - dev_kfree_skb_any(skb); - fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev); - goto done; + goto nomem_exit; } else { memcpy(skb_put(nskb, skb->len), skb->data, skb->len); atomic_inc(&nskb->users); @@ -730,15 +728,8 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb) p_header = kmalloc(PDU_HEADER_LENGTH, gfp_type()); - if (!p_header) { - printk(KERN_WARNING "ctcm: %s() OUT OF MEMORY" - ": Data Lost \n", __FUNCTION__); - - atomic_dec(&skb->users); - dev_kfree_skb_any(skb); - fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev); - goto done; - } + if (!p_header) + goto nomem_exit; p_header->pdu_offset = skb->len; p_header->pdu_proto = 0x01; @@ -768,15 +759,8 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb) ch->prof.txlen += skb->len - PDU_HEADER_LENGTH; header = kmalloc(TH_HEADER_LENGTH, gfp_type()); - - if (!header) { - printk(KERN_WARNING "ctcm: %s() OUT OF MEMORY: Data Lost \n", - __FUNCTION__); - atomic_dec(&skb->users); - dev_kfree_skb_any(skb); - fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev); - goto done; - } + if (!header) + goto nomem_exit; header->th_seg = 0x00; header->th_ch_flag = TH_HAS_PDU; /* Normal data */ @@ -785,41 +769,31 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb) ch->th_seq_num++; header->th_seq_num = ch->th_seq_num; - if (do_debug_data) - ctcm_pr_debug("ctcm: %s() ToVTAM_th_seq= %08x\n" , - __FUNCTION__, ch->th_seq_num); + CTCM_PR_DBGDATA("%s(%s) ToVTAM_th_seq= %08x\n" , + __func__, dev->name, ch->th_seq_num); /* put the TH on the packet */ memcpy(skb_push(skb, TH_HEADER_LENGTH), header, TH_HEADER_LENGTH); kfree(header); - if (do_debug_data) { - ctcm_pr_debug("ctcm: %s(): skb len: %04x \n", - __FUNCTION__, skb->len); - ctcm_pr_debug("ctcm: %s(): pdu header and data for up to 32 " - "bytes sent to vtam\n", __FUNCTION__); - ctcmpc_dump32((char *)skb->data, skb->len); - } + CTCM_PR_DBGDATA("%s(%s): skb len: %04x\n - pdu header and data for " + "up to 32 bytes sent to vtam:\n", + __func__, dev->name, skb->len); + CTCM_D3_DUMP((char *)skb->data, min_t(int, 32, skb->len)); ch->ccw[4].count = skb->len; if (set_normalized_cda(&ch->ccw[4], skb->data)) { /* - * idal allocation failed, try via copying to - * trans_skb. trans_skb usually has a pre-allocated - * idal. + * idal allocation failed, try via copying to trans_skb. + * trans_skb usually has a pre-allocated idal. */ if (ctcm_checkalloc_buffer(ch)) { /* - * Remove our header. It gets added - * again on retransmit. + * Remove our header. + * It gets added again on retransmit. */ - atomic_dec(&skb->users); - dev_kfree_skb_any(skb); - printk(KERN_WARNING "ctcm: %s()OUT OF MEMORY:" - " Data Lost \n", __FUNCTION__); - fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev); - goto done; + goto nomem_exit; } skb_reset_tail_pointer(ch->trans_skb); @@ -829,14 +803,11 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb) atomic_dec(&skb->users); dev_kfree_skb_irq(skb); ccw_idx = 0; - if (do_debug_data) { - ctcm_pr_debug("ctcm: %s() TRANS skb len: %d \n", - __FUNCTION__, ch->trans_skb->len); - ctcm_pr_debug("ctcm: %s up to 32 bytes of data" - " sent to vtam\n", __FUNCTION__); - ctcmpc_dump32((char *)ch->trans_skb->data, - ch->trans_skb->len); - } + CTCM_PR_DBGDATA("%s(%s): trans_skb len: %04x\n" + "up to 32 bytes sent to vtam:\n", + __func__, dev->name, ch->trans_skb->len); + CTCM_D3_DUMP((char *)ch->trans_skb->data, + min_t(int, 32, ch->trans_skb->len)); } else { skb_queue_tail(&ch->io_queue, skb); ccw_idx = 3; @@ -865,13 +836,21 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb) priv->stats.tx_packets++; priv->stats.tx_bytes += skb->len - TH_HEADER_LENGTH; } - if (ch->th_seq_num > 0xf0000000) /* Chose 4Billion at random. */ + if (ch->th_seq_num > 0xf0000000) /* Chose at random. */ ctcmpc_send_sweep_req(ch); + goto done; +nomem_exit: + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_CRIT, + "%s(%s): MEMORY allocation ERROR\n", + CTCM_FUNTAIL, ch->id); + rc = -ENOMEM; + atomic_dec(&skb->users); + dev_kfree_skb_any(skb); + fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev); done: - if (do_debug) - ctcm_pr_debug("ctcm exit: %s %s()\n", dev->name, __FUNCTION__); - return 0; + CTCM_PR_DEBUG("Exit %s(%s)\n", __func__, dev->name); + return rc; } /** @@ -888,20 +867,19 @@ done: /* first merge version - leaving both functions separated */ static int ctcm_tx(struct sk_buff *skb, struct net_device *dev) { - int rc = 0; - struct ctcm_priv *priv; - - CTCM_DBF_TEXT(TRACE, 5, __FUNCTION__); - priv = dev->priv; + struct ctcm_priv *priv = dev->priv; if (skb == NULL) { - ctcm_pr_warn("%s: NULL sk_buff passed\n", dev->name); + CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, + "%s(%s): NULL sk_buff passed", + CTCM_FUNTAIL, dev->name); priv->stats.tx_dropped++; return 0; } if (skb_headroom(skb) < (LL_HEADER_LENGTH + 2)) { - ctcm_pr_warn("%s: Got sk_buff with head room < %ld bytes\n", - dev->name, LL_HEADER_LENGTH + 2); + CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, + "%s(%s): Got sk_buff with head room < %ld bytes", + CTCM_FUNTAIL, dev->name, LL_HEADER_LENGTH + 2); dev_kfree_skb(skb); priv->stats.tx_dropped++; return 0; @@ -925,51 +903,43 @@ static int ctcm_tx(struct sk_buff *skb, struct net_device *dev) dev->trans_start = jiffies; if (ctcm_transmit_skb(priv->channel[WRITE], skb) != 0) - rc = 1; - return rc; + return 1; + return 0; } /* unmerged MPC variant of ctcm_tx */ static int ctcmpc_tx(struct sk_buff *skb, struct net_device *dev) { int len = 0; - struct ctcm_priv *priv = NULL; - struct mpc_group *grp = NULL; + struct ctcm_priv *priv = dev->priv; + struct mpc_group *grp = priv->mpcg; struct sk_buff *newskb = NULL; - if (do_debug) - ctcm_pr_debug("ctcmpc enter: %s(): skb:%0lx\n", - __FUNCTION__, (unsigned long)skb); - - CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG, - "ctcmpc enter: %s(): skb:%0lx\n", - __FUNCTION__, (unsigned long)skb); - - priv = dev->priv; - grp = priv->mpcg; /* * Some sanity checks ... */ if (skb == NULL) { - ctcm_pr_warn("ctcmpc: %s: NULL sk_buff passed\n", dev->name); + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): NULL sk_buff passed", + CTCM_FUNTAIL, dev->name); priv->stats.tx_dropped++; goto done; } if (skb_headroom(skb) < (TH_HEADER_LENGTH + PDU_HEADER_LENGTH)) { - CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_WARN, - "%s: Got sk_buff with head room < %ld bytes\n", - dev->name, TH_HEADER_LENGTH + PDU_HEADER_LENGTH); + CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ERROR, + "%s(%s): Got sk_buff with head room < %ld bytes", + CTCM_FUNTAIL, dev->name, + TH_HEADER_LENGTH + PDU_HEADER_LENGTH); - if (do_debug_data) - ctcmpc_dump32((char *)skb->data, skb->len); + CTCM_D3_DUMP((char *)skb->data, min_t(int, 32, skb->len)); len = skb->len + TH_HEADER_LENGTH + PDU_HEADER_LENGTH; newskb = __dev_alloc_skb(len, gfp_type() | GFP_DMA); if (!newskb) { - printk(KERN_WARNING "ctcmpc: %s() OUT OF MEMORY-" - "Data Lost\n", - __FUNCTION__); + CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ERROR, + "%s: %s: __dev_alloc_skb failed", + __func__, dev->name); dev_kfree_skb_any(skb); priv->stats.tx_dropped++; @@ -993,9 +963,9 @@ static int ctcmpc_tx(struct sk_buff *skb, struct net_device *dev) if ((fsm_getstate(priv->fsm) != DEV_STATE_RUNNING) || (fsm_getstate(grp->fsm) < MPCG_STATE_XID2INITW)) { dev_kfree_skb_any(skb); - printk(KERN_INFO "ctcmpc: %s() DATA RCVD - MPC GROUP " - "NOT ACTIVE - DROPPED\n", - __FUNCTION__); + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): inactive MPCGROUP - dropped", + CTCM_FUNTAIL, dev->name); priv->stats.tx_dropped++; priv->stats.tx_errors++; priv->stats.tx_carrier_errors++; @@ -1003,8 +973,9 @@ static int ctcmpc_tx(struct sk_buff *skb, struct net_device *dev) } if (ctcm_test_and_set_busy(dev)) { - printk(KERN_WARNING "%s:DEVICE ERR - UNRECOVERABLE DATA LOSS\n", - __FUNCTION__); + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): device busy - dropped", + CTCM_FUNTAIL, dev->name); dev_kfree_skb_any(skb); priv->stats.tx_dropped++; priv->stats.tx_errors++; @@ -1015,12 +986,9 @@ static int ctcmpc_tx(struct sk_buff *skb, struct net_device *dev) dev->trans_start = jiffies; if (ctcmpc_transmit_skb(priv->channel[WRITE], skb) != 0) { - printk(KERN_WARNING "ctcmpc: %s() DEVICE ERROR" - ": Data Lost \n", - __FUNCTION__); - printk(KERN_WARNING "ctcmpc: %s() DEVICE ERROR" - " - UNRECOVERABLE DATA LOSS\n", - __FUNCTION__); + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): device error - dropped", + CTCM_FUNTAIL, dev->name); dev_kfree_skb_any(skb); priv->stats.tx_dropped++; priv->stats.tx_errors++; @@ -1054,8 +1022,6 @@ static int ctcm_change_mtu(struct net_device *dev, int new_mtu) struct ctcm_priv *priv; int max_bufsize; - CTCM_DBF_TEXT(SETUP, CTC_DBF_INFO, __FUNCTION__); - if (new_mtu < 576 || new_mtu > 65527) return -EINVAL; @@ -1087,30 +1053,13 @@ static struct net_device_stats *ctcm_stats(struct net_device *dev) return &((struct ctcm_priv *)dev->priv)->stats; } - -static void ctcm_netdev_unregister(struct net_device *dev) -{ - CTCM_DBF_TEXT(SETUP, CTC_DBF_INFO, __FUNCTION__); - if (!dev) - return; - unregister_netdev(dev); -} - -static int ctcm_netdev_register(struct net_device *dev) -{ - CTCM_DBF_TEXT(SETUP, CTC_DBF_INFO, __FUNCTION__); - return register_netdev(dev); -} - static void ctcm_free_netdevice(struct net_device *dev) { struct ctcm_priv *priv; struct mpc_group *grp; - CTCM_DBF_TEXT(SETUP, CTC_DBF_INFO, __FUNCTION__); - - if (!dev) - return; + CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, + "%s(%s)", CTCM_FUNTAIL, dev->name); priv = dev->priv; if (priv) { grp = priv->mpcg; @@ -1171,7 +1120,9 @@ static struct net_device *ctcm_init_netdevice(struct ctcm_priv *priv) dev = alloc_netdev(0, CTC_DEVICE_GENE, ctcm_dev_setup); if (!dev) { - ctcm_pr_err("%s: Out of memory\n", __FUNCTION__); + CTCM_DBF_TEXT_(ERROR, CTC_DBF_CRIT, + "%s: MEMORY allocation ERROR", + CTCM_FUNTAIL); return NULL; } dev->priv = priv; @@ -1209,6 +1160,7 @@ static struct net_device *ctcm_init_netdevice(struct ctcm_priv *priv) } CTCMY_DBF_DEV(SETUP, dev, "finished"); + return dev; } @@ -1226,18 +1178,24 @@ static void ctcm_irq_handler(struct ccw_device *cdev, struct net_device *dev; struct ctcm_priv *priv; struct ccwgroup_device *cgdev; + int cstat; + int dstat; + + CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, + "Enter %s(%s)", CTCM_FUNTAIL, &cdev->dev.bus_id); - CTCM_DBF_TEXT(TRACE, CTC_DBF_DEBUG, __FUNCTION__); if (ctcm_check_irb_error(cdev, irb)) return; cgdev = dev_get_drvdata(&cdev->dev); + cstat = irb->scsw.cmd.cstat; + dstat = irb->scsw.cmd.dstat; + /* Check for unsolicited interrupts. */ if (cgdev == NULL) { - ctcm_pr_warn("ctcm: Got unsolicited irq: %s c-%02x d-%02x\n", - cdev->dev.bus_id, irb->scsw.cmd.cstat, - irb->scsw.cmd.dstat); + ctcm_pr_warn("ctcm: Got unsolicited irq: c-%02x d-%02x\n", + cstat, dstat); return; } @@ -1254,26 +1212,22 @@ static void ctcm_irq_handler(struct ccw_device *cdev, return; } - dev = (struct net_device *)(ch->netdev); + dev = ch->netdev; if (dev == NULL) { ctcm_pr_crit("ctcm: %s dev=NULL bus_id=%s, ch=0x%p\n", - __FUNCTION__, cdev->dev.bus_id, ch); + __func__, cdev->dev.bus_id, ch); return; } - if (do_debug) - ctcm_pr_debug("%s: interrupt for device: %s " - "received c-%02x d-%02x\n", - dev->name, - ch->id, - irb->scsw.cmd.cstat, - irb->scsw.cmd.dstat); + CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, + "%s(%s): int. for %s: cstat=%02x dstat=%02x", + CTCM_FUNTAIL, dev->name, ch->id, cstat, dstat); /* Copy interruption response block. */ memcpy(ch->irb, irb, sizeof(struct irb)); - /* Check for good subchannel return code, otherwise error message */ if (irb->scsw.cmd.cstat) { + /* Check for good subchannel return code, otherwise error message */ fsm_event(ch->fsm, CTC_EVENT_SC_UNKNOWN, ch); ctcm_pr_warn("%s: subchannel check for dev: %s - %02x %02x\n", dev->name, ch->id, irb->scsw.cmd.cstat, @@ -1283,6 +1237,11 @@ static void ctcm_irq_handler(struct ccw_device *cdev, /* Check the reason-code of a unit check */ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { + if ((irb->ecw[0] & ch->sense_rc) == 0) + /* print it only once */ + CTCM_DBF_TEXT_(TRACE, CTC_DBF_INFO, + "%s(%s): sense=%02x, ds=%02x", + CTCM_FUNTAIL, ch->id, irb->ecw[0], dstat); ccw_unit_check(ch, irb->ecw[0]); return; } @@ -1320,14 +1279,18 @@ static int ctcm_probe_device(struct ccwgroup_device *cgdev) struct ctcm_priv *priv; int rc; - CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s %p", __FUNCTION__, cgdev); + CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, + "%s %p", + __func__, cgdev); if (!get_device(&cgdev->dev)) return -ENODEV; priv = kzalloc(sizeof(struct ctcm_priv), GFP_KERNEL); if (!priv) { - ctcm_pr_err("%s: Out of memory\n", __FUNCTION__); + CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, + "%s: memory allocation failure", + CTCM_FUNTAIL); put_device(&cgdev->dev); return -ENOMEM; } @@ -1364,10 +1327,13 @@ static int add_channel(struct ccw_device *cdev, enum channel_types type, int ccw_num; int rc = 0; - CTCM_DBF_TEXT(TRACE, 2, __FUNCTION__); + CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, + "%s(%s), type %d, proto %d", + __func__, cdev->dev.bus_id, type, priv->protocol); + ch = kzalloc(sizeof(struct channel), GFP_KERNEL); if (ch == NULL) - goto nomem_return; + return -ENOMEM; ch->protocol = priv->protocol; if (IS_MPC(priv)) { @@ -1478,7 +1444,7 @@ static int add_channel(struct ccw_device *cdev, enum channel_types type, if (*c && (!strncmp((*c)->id, ch->id, CTCM_ID_SIZE))) { CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s (%s) already in list, using old entry", - __FUNCTION__, (*c)->id); + __func__, (*c)->id); goto free_return; } @@ -1498,11 +1464,10 @@ static int add_channel(struct ccw_device *cdev, enum channel_types type, return 0; nomem_return: - ctcm_pr_warn("ctcm: Out of memory in %s\n", __FUNCTION__); rc = -ENOMEM; free_return: /* note that all channel pointers are 0 or valid */ - kfree(ch->ccw); /* TODO: check that again */ + kfree(ch->ccw); kfree(ch->discontact_th); kfree_fsm(ch->fsm); kfree(ch->irb); @@ -1540,48 +1505,48 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev) enum channel_types type; struct ctcm_priv *priv; struct net_device *dev; + struct ccw_device *cdev0; + struct ccw_device *cdev1; int ret; - CTCM_DBF_TEXT(SETUP, CTC_DBF_INFO, __FUNCTION__); - priv = dev_get_drvdata(&cgdev->dev); if (!priv) return -ENODEV; - type = get_channel_type(&cgdev->cdev[0]->id); + cdev0 = cgdev->cdev[0]; + cdev1 = cgdev->cdev[1]; + + type = get_channel_type(&cdev0->id); - snprintf(read_id, CTCM_ID_SIZE, "ch-%s", cgdev->cdev[0]->dev.bus_id); - snprintf(write_id, CTCM_ID_SIZE, "ch-%s", cgdev->cdev[1]->dev.bus_id); + snprintf(read_id, CTCM_ID_SIZE, "ch-%s", cdev0->dev.bus_id); + snprintf(write_id, CTCM_ID_SIZE, "ch-%s", cdev1->dev.bus_id); - ret = add_channel(cgdev->cdev[0], type, priv); + ret = add_channel(cdev0, type, priv); if (ret) return ret; - ret = add_channel(cgdev->cdev[1], type, priv); + ret = add_channel(cdev1, type, priv); if (ret) return ret; - ret = ccw_device_set_online(cgdev->cdev[0]); + ret = ccw_device_set_online(cdev0); if (ret != 0) { - CTCM_DBF_TEXT(SETUP, CTC_DBF_WARN, - "ccw_device_set_online (cdev[0]) failed "); - ctcm_pr_warn("ccw_device_set_online (cdev[0]) failed " - "with ret = %d\n", ret); + /* may be ok to fail now - can be done later */ + CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, + "%s(%s) set_online rc=%d", + CTCM_FUNTAIL, read_id, ret); } - ret = ccw_device_set_online(cgdev->cdev[1]); + ret = ccw_device_set_online(cdev1); if (ret != 0) { - CTCM_DBF_TEXT(SETUP, CTC_DBF_WARN, - "ccw_device_set_online (cdev[1]) failed "); - ctcm_pr_warn("ccw_device_set_online (cdev[1]) failed " - "with ret = %d\n", ret); + /* may be ok to fail now - can be done later */ + CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, + "%s(%s) set_online rc=%d", + CTCM_FUNTAIL, write_id, ret); } dev = ctcm_init_netdevice(priv); - - if (dev == NULL) { - ctcm_pr_warn("ctcm_init_netdevice failed\n"); - goto out; - } + if (dev == NULL) + goto out; for (direction = READ; direction <= WRITE; direction++) { priv->channel[direction] = @@ -1590,8 +1555,7 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev) if (priv->channel[direction] == NULL) { if (direction == WRITE) channel_free(priv->channel[READ]); - ctcm_free_netdevice(dev); - goto out; + goto out_dev; } priv->channel[direction]->netdev = dev; priv->channel[direction]->protocol = priv->protocol; @@ -1600,26 +1564,24 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev) /* sysfs magic */ SET_NETDEV_DEV(dev, &cgdev->dev); - if (ctcm_netdev_register(dev) != 0) { - ctcm_free_netdevice(dev); - goto out; - } + if (register_netdev(dev)) + goto out_dev; if (ctcm_add_attributes(&cgdev->dev)) { - ctcm_netdev_unregister(dev); -/* dev->priv = NULL; why that ???? */ - ctcm_free_netdevice(dev); - goto out; + unregister_netdev(dev); + goto out_dev; } strlcpy(priv->fsm->name, dev->name, sizeof(priv->fsm->name)); CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, - "setup(%s) ok : r/w = %s / %s, proto : %d", - dev->name, priv->channel[READ]->id, + "setup(%s) OK : r/w = %s/%s, protocol : %d", dev->name, + priv->channel[READ]->id, priv->channel[WRITE]->id, priv->protocol); return 0; +out_dev: + ctcm_free_netdevice(dev); out: ccw_device_set_offline(cgdev->cdev[1]); ccw_device_set_offline(cgdev->cdev[0]); @@ -1658,8 +1620,7 @@ static int ctcm_shutdown_device(struct ccwgroup_device *cgdev) channel_free(priv->channel[WRITE]); if (dev) { - ctcm_netdev_unregister(dev); -/* dev->priv = NULL; why that ??? */ + unregister_netdev(dev); ctcm_free_netdevice(dev); } @@ -1682,13 +1643,16 @@ static int ctcm_shutdown_device(struct ccwgroup_device *cgdev) static void ctcm_remove_device(struct ccwgroup_device *cgdev) { - struct ctcm_priv *priv; + struct ctcm_priv *priv = dev_get_drvdata(&cgdev->dev); - CTCM_DBF_TEXT(SETUP, CTC_DBF_ERROR, __FUNCTION__); + BUG_ON(priv == NULL); + + CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, + "removing device %s, r/w = %s/%s, proto : %d", + priv->channel[READ]->netdev->name, + priv->channel[READ]->id, priv->channel[WRITE]->id, + priv->protocol); - priv = dev_get_drvdata(&cgdev->dev); - if (!priv) - return; if (cgdev->state == CCWGROUP_ONLINE) ctcm_shutdown_device(cgdev); ctcm_remove_files(&cgdev->dev); @@ -1748,8 +1712,6 @@ static int __init ctcm_init(void) ret = ctcm_register_dbf_views(); if (ret) { - ctcm_pr_crit("ctcm_init failed with ctcm_register_dbf_views " - "rc = %d\n", ret); return ret; } ret = register_cu3088_discipline(&ctcm_group_driver); diff --git a/drivers/s390/net/ctcm_main.h b/drivers/s390/net/ctcm_main.h index 95b0c0b6ebc6..a72e0feeb27f 100644 --- a/drivers/s390/net/ctcm_main.h +++ b/drivers/s390/net/ctcm_main.h @@ -22,9 +22,9 @@ #define CTC_DRIVER_NAME "ctcm" #define CTC_DEVICE_NAME "ctc" -#define CTC_DEVICE_GENE "ctc%d" #define MPC_DEVICE_NAME "mpc" -#define MPC_DEVICE_GENE "mpc%d" +#define CTC_DEVICE_GENE CTC_DEVICE_NAME "%d" +#define MPC_DEVICE_GENE MPC_DEVICE_NAME "%d" #define CHANNEL_FLAGS_READ 0 #define CHANNEL_FLAGS_WRITE 1 @@ -48,6 +48,30 @@ #define ctcm_pr_err(fmt, arg...) printk(KERN_ERR fmt, ##arg) #define ctcm_pr_crit(fmt, arg...) printk(KERN_CRIT fmt, ##arg) +#define CTCM_PR_DEBUG(fmt, arg...) \ + do { \ + if (do_debug) \ + printk(KERN_DEBUG fmt, ##arg); \ + } while (0) + +#define CTCM_PR_DBGDATA(fmt, arg...) \ + do { \ + if (do_debug_data) \ + printk(KERN_DEBUG fmt, ##arg); \ + } while (0) + +#define CTCM_D3_DUMP(buf, len) \ + do { \ + if (do_debug_data) \ + ctcmpc_dumpit(buf, len); \ + } while (0) + +#define CTCM_CCW_DUMP(buf, len) \ + do { \ + if (do_debug_ccw) \ + ctcmpc_dumpit(buf, len); \ + } while (0) + /* * CCW commands, used in this driver. */ @@ -161,8 +185,9 @@ struct channel { fsm_instance *fsm; /* finite state machine of this channel */ struct net_device *netdev; /* corresponding net_device */ struct ctcm_profile prof; - unsigned char *trans_skb_data; + __u8 *trans_skb_data; __u16 logflags; + __u8 sense_rc; /* last unit check sense code report control */ }; struct ctcm_priv { diff --git a/drivers/s390/net/ctcm_mpc.c b/drivers/s390/net/ctcm_mpc.c index 044addee64a2..49ae1cd25caa 100644 --- a/drivers/s390/net/ctcm_mpc.c +++ b/drivers/s390/net/ctcm_mpc.c @@ -149,7 +149,7 @@ void ctcmpc_dumpit(char *buf, int len) for (ct = 0; ct < len; ct++, ptr++, rptr++) { if (sw == 0) { #if (UTS_MACHINE == s390x) - sprintf(addr, "%16.16lx", (unsigned long)rptr); + sprintf(addr, "%16.16lx", (__u64)rptr); #else sprintf(addr, "%8.8X", (__u32)rptr); #endif @@ -164,7 +164,7 @@ void ctcmpc_dumpit(char *buf, int len) strcat(bhex, " "); #if (UTS_MACHINE == s390x) - sprintf(tbuf, "%2.2lX", (unsigned long)*ptr); + sprintf(tbuf, "%2.2lX", (__u64)*ptr); #else sprintf(tbuf, "%2.2X", (__u32)*ptr); #endif @@ -179,24 +179,24 @@ void ctcmpc_dumpit(char *buf, int len) basc[sw+1] = '\0'; sw++; rm--; - if (sw == 16) { - if ((strcmp(duphex, bhex)) != 0) { - if (dup != 0) { - sprintf(tdup, "Duplicate as above " - "to %s", addr); - printk(KERN_INFO " " - " --- %s ---\n", tdup); - } - printk(KERN_INFO " %s (+%s) : %s [%s]\n", + if (sw != 16) + continue; + if ((strcmp(duphex, bhex)) != 0) { + if (dup != 0) { + sprintf(tdup, + "Duplicate as above to %s", addr); + ctcm_pr_debug(" --- %s ---\n", + tdup); + } + ctcm_pr_debug(" %s (+%s) : %s [%s]\n", addr, boff, bhex, basc); - dup = 0; - strcpy(duphex, bhex); - } else - dup++; + dup = 0; + strcpy(duphex, bhex); + } else + dup++; - sw = 0; - rm = 16; - } + sw = 0; + rm = 16; } /* endfor */ if (sw != 0) { @@ -210,19 +210,17 @@ void ctcmpc_dumpit(char *buf, int len) } if (dup != 0) { sprintf(tdup, "Duplicate as above to %s", addr); - printk(KERN_INFO " " - " --- %s ---\n", tdup); + ctcm_pr_debug(" --- %s ---\n", tdup); } - printk(KERN_INFO " %s (+%s) : %s [%s]\n", - addr, boff, bhex, basc); + ctcm_pr_debug(" %s (+%s) : %s [%s]\n", + addr, boff, bhex, basc); } else { if (dup >= 1) { sprintf(tdup, "Duplicate as above to %s", addr); - printk(KERN_INFO " " - " --- %s ---\n", tdup); + ctcm_pr_debug(" --- %s ---\n", tdup); } if (dup != 0) { - printk(KERN_INFO " %s (+%s) : %s [%s]\n", + ctcm_pr_debug(" %s (+%s) : %s [%s]\n", addr, boff, bhex, basc); } } @@ -241,7 +239,7 @@ void ctcmpc_dumpit(char *buf, int len) */ void ctcmpc_dump_skb(struct sk_buff *skb, int offset) { - unsigned char *p = skb->data; + __u8 *p = skb->data; struct th_header *header; struct pdu *pheader; int bl = skb->len; @@ -253,8 +251,8 @@ void ctcmpc_dump_skb(struct sk_buff *skb, int offset) p += offset; header = (struct th_header *)p; - printk(KERN_INFO "dump:\n"); - printk(KERN_INFO "skb len=%d \n", skb->len); + ctcm_pr_debug("dump:\n"); + ctcm_pr_debug("skb len=%d \n", skb->len); if (skb->len > 2) { switch (header->th_ch_flag) { case TH_HAS_PDU: @@ -273,32 +271,64 @@ void ctcmpc_dump_skb(struct sk_buff *skb, int offset) } pheader = (struct pdu *)p; - printk(KERN_INFO "pdu->offset: %d hex: %04x\n", - pheader->pdu_offset, pheader->pdu_offset); - printk(KERN_INFO "pdu->flag : %02x\n", pheader->pdu_flag); - printk(KERN_INFO "pdu->proto : %02x\n", pheader->pdu_proto); - printk(KERN_INFO "pdu->seq : %02x\n", pheader->pdu_seq); + ctcm_pr_debug("pdu->offset: %d hex: %04x\n", + pheader->pdu_offset, pheader->pdu_offset); + ctcm_pr_debug("pdu->flag : %02x\n", pheader->pdu_flag); + ctcm_pr_debug("pdu->proto : %02x\n", pheader->pdu_proto); + ctcm_pr_debug("pdu->seq : %02x\n", pheader->pdu_seq); goto dumpdata; dumpth: - printk(KERN_INFO "th->seg : %02x\n", header->th_seg); - printk(KERN_INFO "th->ch : %02x\n", header->th_ch_flag); - printk(KERN_INFO "th->blk_flag: %02x\n", header->th_blk_flag); - printk(KERN_INFO "th->type : %s\n", - (header->th_is_xid) ? "DATA" : "XID"); - printk(KERN_INFO "th->seqnum : %04x\n", header->th_seq_num); + ctcm_pr_debug("th->seg : %02x\n", header->th_seg); + ctcm_pr_debug("th->ch : %02x\n", header->th_ch_flag); + ctcm_pr_debug("th->blk_flag: %02x\n", header->th_blk_flag); + ctcm_pr_debug("th->type : %s\n", + (header->th_is_xid) ? "DATA" : "XID"); + ctcm_pr_debug("th->seqnum : %04x\n", header->th_seq_num); } dumpdata: if (bl > 32) bl = 32; - printk(KERN_INFO "data: "); + ctcm_pr_debug("data: "); for (i = 0; i < bl; i++) - printk(KERN_INFO "%02x%s", *p++, (i % 16) ? " " : "\n<7>"); - printk(KERN_INFO "\n"); + ctcm_pr_debug("%02x%s", *p++, (i % 16) ? " " : "\n"); + ctcm_pr_debug("\n"); } #endif +static struct net_device *ctcmpc_get_dev(int port_num) +{ + char device[20]; + struct net_device *dev; + struct ctcm_priv *priv; + + sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num); + + dev = __dev_get_by_name(&init_net, device); + + if (dev == NULL) { + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s: Device not found by name: %s", + CTCM_FUNTAIL, device); + return NULL; + } + priv = dev->priv; + if (priv == NULL) { + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): dev->priv is NULL", + CTCM_FUNTAIL, device); + return NULL; + } + if (priv->mpcg == NULL) { + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): priv->mpcg is NULL", + CTCM_FUNTAIL, device); + return NULL; + } + return dev; +} + /* * ctc_mpc_alloc_channel * (exported interface) @@ -308,34 +338,23 @@ dumpdata: */ int ctc_mpc_alloc_channel(int port_num, void (*callback)(int, int)) { - char device[20]; struct net_device *dev; struct mpc_group *grp; struct ctcm_priv *priv; - ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__); - - sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num); - dev = __dev_get_by_name(&init_net, device); - - if (dev == NULL) { - printk(KERN_INFO "ctc_mpc_alloc_channel %s dev=NULL\n", device); + dev = ctcmpc_get_dev(port_num); + if (dev == NULL) return 1; - } - priv = dev->priv; grp = priv->mpcg; - if (!grp) - return 1; grp->allochanfunc = callback; grp->port_num = port_num; grp->port_persist = 1; - ctcm_pr_debug("ctcmpc: %s called for device %s state=%s\n", - __FUNCTION__, - dev->name, - fsm_getstate_str(grp->fsm)); + CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_INFO, + "%s(%s): state=%s", + CTCM_FUNTAIL, dev->name, fsm_getstate_str(grp->fsm)); switch (fsm_getstate(grp->fsm)) { case MPCG_STATE_INOP: @@ -377,12 +396,8 @@ int ctc_mpc_alloc_channel(int port_num, void (*callback)(int, int)) grp->allocchan_callback_retries = 0; } break; - default: - return 0; - } - ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__); return 0; } EXPORT_SYMBOL(ctc_mpc_alloc_channel); @@ -394,31 +409,22 @@ EXPORT_SYMBOL(ctc_mpc_alloc_channel); void ctc_mpc_establish_connectivity(int port_num, void (*callback)(int, int, int)) { - char device[20]; struct net_device *dev; struct mpc_group *grp; struct ctcm_priv *priv; struct channel *rch, *wch; - ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__); - - sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num); - dev = __dev_get_by_name(&init_net, device); - - if (dev == NULL) { - printk(KERN_INFO "ctc_mpc_establish_connectivity " - "%s dev=NULL\n", device); + dev = ctcmpc_get_dev(port_num); + if (dev == NULL) return; - } priv = dev->priv; + grp = priv->mpcg; rch = priv->channel[READ]; wch = priv->channel[WRITE]; - grp = priv->mpcg; - - ctcm_pr_debug("ctcmpc: %s() called for device %s state=%s\n", - __FUNCTION__, dev->name, - fsm_getstate_str(grp->fsm)); + CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_INFO, + "%s(%s): state=%s", + CTCM_FUNTAIL, dev->name, fsm_getstate_str(grp->fsm)); grp->estconnfunc = callback; grp->port_num = port_num; @@ -446,8 +452,10 @@ void ctc_mpc_establish_connectivity(int port_num, case MPCG_STATE_RESET: /* MPC Group is not ready to start XID - min num of */ /* 1 read and 1 write channel have not been acquired*/ - printk(KERN_WARNING "ctcmpc: %s() REJECTED ACTIVE XID Req" - "uest - Channel Pair is not Active\n", __FUNCTION__); + + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): REJECTED - inactive channels", + CTCM_FUNTAIL, dev->name); if (grp->estconnfunc) { grp->estconnfunc(grp->port_num, -1, 0); grp->estconnfunc = NULL; @@ -457,11 +465,12 @@ void ctc_mpc_establish_connectivity(int port_num, /* alloc channel was called but no XID exchange */ /* has occurred. initiate xside XID exchange */ /* make sure yside XID0 processing has not started */ + if ((fsm_getstate(rch->fsm) > CH_XID0_PENDING) || (fsm_getstate(wch->fsm) > CH_XID0_PENDING)) { - printk(KERN_WARNING "mpc: %s() ABORT ACTIVE XID" - " Request- PASSIVE XID in process\n" - , __FUNCTION__); + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): ABORT - PASSIVE XID", + CTCM_FUNTAIL, dev->name); break; } grp->send_qllc_disc = 1; @@ -476,9 +485,9 @@ void ctc_mpc_establish_connectivity(int port_num, (fsm_getstate(rch->fsm) == CH_XID0_PENDING)) fsm_event(grp->fsm, MPCG_EVENT_XID0DO, rch); else { - printk(KERN_WARNING "mpc: %s() Unable to start" - " ACTIVE XID0 on read channel\n", - __FUNCTION__); + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): RX-%s not ready for ACTIVE XID0", + CTCM_FUNTAIL, dev->name, rch->id); if (grp->estconnfunc) { grp->estconnfunc(grp->port_num, -1, 0); grp->estconnfunc = NULL; @@ -490,9 +499,9 @@ void ctc_mpc_establish_connectivity(int port_num, (fsm_getstate(wch->fsm) == CH_XID0_PENDING)) fsm_event(grp->fsm, MPCG_EVENT_XID0DO, wch); else { - printk(KERN_WARNING "mpc: %s() Unable to start" - " ACTIVE XID0 on write channel\n", - __FUNCTION__); + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): WX-%s not ready for ACTIVE XID0", + CTCM_FUNTAIL, dev->name, wch->id); if (grp->estconnfunc) { grp->estconnfunc(grp->port_num, -1, 0); grp->estconnfunc = NULL; @@ -508,7 +517,7 @@ void ctc_mpc_establish_connectivity(int port_num, } done: - ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__); + CTCM_PR_DEBUG("Exit %s()\n", __func__); return; } EXPORT_SYMBOL(ctc_mpc_establish_connectivity); @@ -520,40 +529,22 @@ EXPORT_SYMBOL(ctc_mpc_establish_connectivity); void ctc_mpc_dealloc_ch(int port_num) { struct net_device *dev; - char device[20]; struct ctcm_priv *priv; struct mpc_group *grp; - ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__); - sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num); - dev = __dev_get_by_name(&init_net, device); - - if (dev == NULL) { - printk(KERN_INFO "%s() %s dev=NULL\n", __FUNCTION__, device); - goto done; - } + dev = ctcmpc_get_dev(port_num); + if (dev == NULL) + return; + priv = dev->priv; + grp = priv->mpcg; - ctcm_pr_debug("ctcmpc:%s %s() called for device %s refcount=%d\n", - dev->name, __FUNCTION__, - dev->name, atomic_read(&dev->refcnt)); + CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_DEBUG, + "%s: %s: refcount = %d\n", + CTCM_FUNTAIL, dev->name, atomic_read(&dev->refcnt)); - priv = dev->priv; - if (priv == NULL) { - printk(KERN_INFO "%s() %s priv=NULL\n", - __FUNCTION__, device); - goto done; - } fsm_deltimer(&priv->restart_timer); - - grp = priv->mpcg; - if (grp == NULL) { - printk(KERN_INFO "%s() %s dev=NULL\n", __FUNCTION__, device); - goto done; - } grp->channels_terminating = 0; - fsm_deltimer(&grp->timer); - grp->allochanfunc = NULL; grp->estconnfunc = NULL; grp->port_persist = 0; @@ -561,8 +552,6 @@ void ctc_mpc_dealloc_ch(int port_num) fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); ctcm_close(dev); -done: - ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__); return; } EXPORT_SYMBOL(ctc_mpc_dealloc_ch); @@ -573,32 +562,22 @@ EXPORT_SYMBOL(ctc_mpc_dealloc_ch); */ void ctc_mpc_flow_control(int port_num, int flowc) { - char device[20]; struct ctcm_priv *priv; struct mpc_group *grp; struct net_device *dev; struct channel *rch; int mpcg_state; - ctcm_pr_debug("ctcmpc enter: %s() %i\n", __FUNCTION__, flowc); - - sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num); - dev = __dev_get_by_name(&init_net, device); - - if (dev == NULL) { - printk(KERN_INFO "ctc_mpc_flow_control %s dev=NULL\n", device); + dev = ctcmpc_get_dev(port_num); + if (dev == NULL) return; - } + priv = dev->priv; + grp = priv->mpcg; - ctcm_pr_debug("ctcmpc: %s %s called \n", dev->name, __FUNCTION__); + CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG, + "%s: %s: flowc = %d", + CTCM_FUNTAIL, dev->name, flowc); - priv = dev->priv; - if (priv == NULL) { - printk(KERN_INFO "ctcmpc:%s() %s priv=NULL\n", - __FUNCTION__, device); - return; - } - grp = priv->mpcg; rch = priv->channel[READ]; mpcg_state = fsm_getstate(grp->fsm); @@ -629,7 +608,6 @@ void ctc_mpc_flow_control(int port_num, int flowc) break; } - ctcm_pr_debug("ctcmpc exit: %s() %i\n", __FUNCTION__, flowc); } EXPORT_SYMBOL(ctc_mpc_flow_control); @@ -646,12 +624,8 @@ static void mpc_rcvd_sweep_resp(struct mpcg_info *mpcginfo) struct mpc_group *grp = priv->mpcg; struct channel *ch = priv->channel[WRITE]; - if (do_debug) - ctcm_pr_debug("ctcmpc enter: %s(): ch=0x%p id=%s\n", - __FUNCTION__, ch, ch->id); - - if (do_debug_data) - ctcmpc_dumpit((char *)mpcginfo->sweep, TH_SWEEP_LENGTH); + CTCM_PR_DEBUG("%s: ch=0x%p id=%s\n", __func__, ch, ch->id); + CTCM_D3_DUMP((char *)mpcginfo->sweep, TH_SWEEP_LENGTH); grp->sweep_rsp_pend_num--; @@ -684,14 +658,13 @@ static void ctcmpc_send_sweep_resp(struct channel *rch) struct sk_buff *sweep_skb; struct channel *ch = priv->channel[WRITE]; - if (do_debug) - ctcm_pr_debug("ctcmpc exit : %s(): ch=0x%p id=%s\n", - __FUNCTION__, rch, rch->id); + CTCM_PR_DEBUG("%s: ch=0x%p id=%s\n", __func__, rch, rch->id); - sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, - GFP_ATOMIC|GFP_DMA); + sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC | GFP_DMA); if (sweep_skb == NULL) { - printk(KERN_INFO "Couldn't alloc sweep_skb\n"); + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): sweep_skb allocation ERROR\n", + CTCM_FUNTAIL, rch->id); rc = -ENOMEM; goto done; } @@ -746,7 +719,7 @@ static void mpc_rcvd_sweep_req(struct mpcg_info *mpcginfo) if (do_debug) CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG, - " %s(): ch=0x%p id=%s\n", __FUNCTION__, ch, ch->id); + " %s(): ch=0x%p id=%s\n", __func__, ch, ch->id); if (grp->in_sweep == 0) { grp->in_sweep = 1; @@ -755,8 +728,7 @@ static void mpc_rcvd_sweep_req(struct mpcg_info *mpcginfo) grp->sweep_rsp_pend_num = grp->active_channels[READ]; } - if (do_debug_data) - ctcmpc_dumpit((char *)mpcginfo->sweep, TH_SWEEP_LENGTH); + CTCM_D3_DUMP((char *)mpcginfo->sweep, TH_SWEEP_LENGTH); grp->sweep_req_pend_num--; ctcmpc_send_sweep_resp(ch); @@ -875,25 +847,13 @@ static int mpcg_fsm_len = ARRAY_SIZE(mpcg_fsm); static void mpc_action_go_ready(fsm_instance *fsm, int event, void *arg) { struct net_device *dev = arg; - struct ctcm_priv *priv = NULL; - struct mpc_group *grp = NULL; - - if (dev == NULL) { - printk(KERN_INFO "%s() dev=NULL\n", __FUNCTION__); - return; - } - - ctcm_pr_debug("ctcmpc enter: %s %s()\n", dev->name, __FUNCTION__); - - priv = dev->priv; - if (priv == NULL) { - printk(KERN_INFO "%s() priv=NULL\n", __FUNCTION__); - return; - } + struct ctcm_priv *priv = dev->priv; + struct mpc_group *grp = priv->mpcg; - grp = priv->mpcg; if (grp == NULL) { - printk(KERN_INFO "%s() grp=NULL\n", __FUNCTION__); + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): No MPC group", + CTCM_FUNTAIL, dev->name); return; } @@ -907,7 +867,12 @@ static void mpc_action_go_ready(fsm_instance *fsm, int event, void *arg) grp->estconnfunc = NULL; } else if (grp->allochanfunc) grp->send_qllc_disc = 1; - goto done; + + fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): fails", + CTCM_FUNTAIL, dev->name); + return; } grp->port_persist = 1; @@ -916,14 +881,7 @@ static void mpc_action_go_ready(fsm_instance *fsm, int event, void *arg) tasklet_hi_schedule(&grp->mpc_tasklet2); - ctcm_pr_debug("ctcmpc exit: %s %s()\n", dev->name, __FUNCTION__); return; - -done: - fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); - - - ctcm_pr_info("ctcmpc: %s()failure occurred\n", __FUNCTION__); } /* @@ -933,42 +891,28 @@ done: void mpc_group_ready(unsigned long adev) { struct net_device *dev = (struct net_device *)adev; - struct ctcm_priv *priv = NULL; - struct mpc_group *grp = NULL; + struct ctcm_priv *priv = dev->priv; + struct mpc_group *grp = priv->mpcg; struct channel *ch = NULL; - - ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__); - - if (dev == NULL) { - printk(KERN_INFO "%s() dev=NULL\n", __FUNCTION__); - return; - } - - priv = dev->priv; - if (priv == NULL) { - printk(KERN_INFO "%s() priv=NULL\n", __FUNCTION__); - return; - } - - grp = priv->mpcg; if (grp == NULL) { - printk(KERN_INFO "ctcmpc:%s() grp=NULL\n", __FUNCTION__); + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): No MPC group", + CTCM_FUNTAIL, dev->name); return; } - printk(KERN_NOTICE "ctcmpc: %s GROUP TRANSITIONED TO READY" - " maxbuf:%d\n", - dev->name, grp->group_max_buflen); + CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_NOTICE, + "%s: %s: GROUP TRANSITIONED TO READY, maxbuf = %d\n", + CTCM_FUNTAIL, dev->name, grp->group_max_buflen); fsm_newstate(grp->fsm, MPCG_STATE_READY); /* Put up a read on the channel */ ch = priv->channel[READ]; ch->pdu_seq = 0; - if (do_debug_data) - ctcm_pr_debug("ctcmpc: %s() ToDCM_pdu_seq= %08x\n" , - __FUNCTION__, ch->pdu_seq); + CTCM_PR_DBGDATA("ctcmpc: %s() ToDCM_pdu_seq= %08x\n" , + __func__, ch->pdu_seq); ctcmpc_chx_rxidle(ch->fsm, CTC_EVENT_START, ch); /* Put the write channel in idle state */ @@ -980,22 +924,18 @@ void mpc_group_ready(unsigned long adev) spin_unlock(&ch->collect_lock); } ctcm_chx_txidle(ch->fsm, CTC_EVENT_START, ch); - ctcm_clear_busy(dev); if (grp->estconnfunc) { grp->estconnfunc(grp->port_num, 0, grp->group_max_buflen); grp->estconnfunc = NULL; - } else - if (grp->allochanfunc) - grp->allochanfunc(grp->port_num, - grp->group_max_buflen); + } else if (grp->allochanfunc) + grp->allochanfunc(grp->port_num, grp->group_max_buflen); grp->send_qllc_disc = 1; grp->changed_side = 0; - ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__); return; } @@ -1004,51 +944,26 @@ void mpc_group_ready(unsigned long adev) * Increment the MPC Group Active Channel Counts * helper of dev_action (called from channel fsm) */ -int mpc_channel_action(struct channel *ch, int direction, int action) +void mpc_channel_action(struct channel *ch, int direction, int action) { - struct net_device *dev = ch->netdev; - struct ctcm_priv *priv; - struct mpc_group *grp = NULL; - int rc = 0; - - if (do_debug) - ctcm_pr_debug("ctcmpc enter: %s(): ch=0x%p id=%s\n", - __FUNCTION__, ch, ch->id); - - if (dev == NULL) { - printk(KERN_INFO "ctcmpc_channel_action %i dev=NULL\n", - action); - rc = 1; - goto done; - } - - priv = dev->priv; - if (priv == NULL) { - printk(KERN_INFO - "ctcmpc_channel_action%i priv=NULL, dev=%s\n", - action, dev->name); - rc = 2; - goto done; - } - - grp = priv->mpcg; + struct net_device *dev = ch->netdev; + struct ctcm_priv *priv = dev->priv; + struct mpc_group *grp = priv->mpcg; if (grp == NULL) { - printk(KERN_INFO "ctcmpc: %s()%i mpcgroup=NULL, dev=%s\n", - __FUNCTION__, action, dev->name); - rc = 3; - goto done; + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): No MPC group", + CTCM_FUNTAIL, dev->name); + return; } - ctcm_pr_info( - "ctcmpc: %s() %i(): Grp:%s total_channel_paths=%i " - "active_channels read=%i, write=%i\n", - __FUNCTION__, - action, - fsm_getstate_str(grp->fsm), - grp->num_channel_paths, - grp->active_channels[READ], - grp->active_channels[WRITE]); + CTCM_PR_DEBUG("enter %s: ch=0x%p id=%s\n", __func__, ch, ch->id); + + CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE, + "%s: %i / Grp:%s total_channels=%i, active_channels: " + "read=%i, write=%i\n", __func__, action, + fsm_getstate_str(grp->fsm), grp->num_channel_paths, + grp->active_channels[READ], grp->active_channels[WRITE]); if ((action == MPC_CHANNEL_ADD) && (ch->in_mpcgroup == 0)) { grp->num_channel_paths++; @@ -1062,10 +977,11 @@ int mpc_channel_action(struct channel *ch, int direction, int action) ch->xid_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC | GFP_DMA); if (ch->xid_skb == NULL) { - printk(KERN_INFO "ctcmpc: %s()" - "Couldn't alloc ch xid_skb\n", __FUNCTION__); + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): Couldn't alloc ch xid_skb\n", + CTCM_FUNTAIL, dev->name); fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); - return 1; + return; } ch->xid_skb_data = ch->xid_skb->data; ch->xid_th = (struct th_header *)ch->xid_skb->data; @@ -1097,8 +1013,9 @@ int mpc_channel_action(struct channel *ch, int direction, int action) (grp->active_channels[WRITE] > 0) && (fsm_getstate(grp->fsm) < MPCG_STATE_XID2INITW)) { fsm_newstate(grp->fsm, MPCG_STATE_XID2INITW); - printk(KERN_NOTICE "ctcmpc: %s MPC GROUP " - "CHANNELS ACTIVE\n", dev->name); + CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_NOTICE, + "%s: %s: MPC GROUP CHANNELS ACTIVE\n", + __func__, dev->name); } } else if ((action == MPC_CHANNEL_REMOVE) && (ch->in_mpcgroup == 1)) { @@ -1119,25 +1036,14 @@ int mpc_channel_action(struct channel *ch, int direction, int action) (grp->active_channels[READ] > 0))) fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); } - done: + CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG, + "exit %s: %i / Grp:%s total_channels=%i, active_channels: " + "read=%i, write=%i\n", __func__, action, + fsm_getstate_str(grp->fsm), grp->num_channel_paths, + grp->active_channels[READ], grp->active_channels[WRITE]); - if (do_debug) { - ctcm_pr_debug( - "ctcmpc: %s() %i Grp:%s ttl_chan_paths=%i " - "active_chans read=%i, write=%i\n", - __FUNCTION__, - action, - fsm_getstate_str(grp->fsm), - grp->num_channel_paths, - grp->active_channels[READ], - grp->active_channels[WRITE]); - - ctcm_pr_debug("ctcmpc exit : %s(): ch=0x%p id=%s\n", - __FUNCTION__, ch, ch->id); - } - return rc; - + CTCM_PR_DEBUG("exit %s: ch=0x%p id=%s\n", __func__, ch, ch->id); } /** @@ -1163,9 +1069,8 @@ static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb) int skblen; int sendrc = 0; - if (do_debug) - ctcm_pr_debug("ctcmpc enter: %s() %s cp:%i ch:%s\n", - __FUNCTION__, dev->name, smp_processor_id(), ch->id); + CTCM_PR_DEBUG("ctcmpc enter: %s() %s cp:%i ch:%s\n", + __func__, dev->name, smp_processor_id(), ch->id); header = (struct th_header *)pskb->data; if ((header->th_seg == 0) && @@ -1174,21 +1079,16 @@ static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb) (header->th_seq_num == 0)) /* nothing for us */ goto done; - if (do_debug_data) { - ctcm_pr_debug("ctcmpc: %s() th_header\n", __FUNCTION__); - ctcmpc_dumpit((char *)header, TH_HEADER_LENGTH); - ctcm_pr_debug("ctcmpc: %s() pskb len: %04x \n", - __FUNCTION__, pskb->len); - } + CTCM_PR_DBGDATA("%s: th_header\n", __func__); + CTCM_D3_DUMP((char *)header, TH_HEADER_LENGTH); + CTCM_PR_DBGDATA("%s: pskb len: %04x \n", __func__, pskb->len); pskb->dev = dev; pskb->ip_summed = CHECKSUM_UNNECESSARY; skb_pull(pskb, TH_HEADER_LENGTH); if (likely(header->th_ch_flag == TH_HAS_PDU)) { - if (do_debug_data) - ctcm_pr_debug("ctcmpc: %s() came into th_has_pdu\n", - __FUNCTION__); + CTCM_PR_DBGDATA("%s: came into th_has_pdu\n", __func__); if ((fsm_getstate(grp->fsm) == MPCG_STATE_FLOWC) || ((fsm_getstate(grp->fsm) == MPCG_STATE_READY) && (header->th_seq_num != ch->th_seq_num + 1) && @@ -1202,33 +1102,29 @@ static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb) grp->out_of_sequence += 1; __skb_push(pskb, TH_HEADER_LENGTH); skb_queue_tail(&ch->io_queue, pskb); - if (do_debug_data) - ctcm_pr_debug("ctcmpc: %s() th_seq_num " - "expect:%08x got:%08x\n", __FUNCTION__, - ch->th_seq_num + 1, header->th_seq_num); + CTCM_PR_DBGDATA("%s: th_seq_num expect:%08x " + "got:%08x\n", __func__, + ch->th_seq_num + 1, header->th_seq_num); return; } grp->out_of_sequence = 0; ch->th_seq_num = header->th_seq_num; - if (do_debug_data) - ctcm_pr_debug("ctcmpc: %s() FromVTAM_th_seq=%08x\n", - __FUNCTION__, ch->th_seq_num); + CTCM_PR_DBGDATA("ctcmpc: %s() FromVTAM_th_seq=%08x\n", + __func__, ch->th_seq_num); if (unlikely(fsm_getstate(grp->fsm) != MPCG_STATE_READY)) goto done; pdu_last_seen = 0; while ((pskb->len > 0) && !pdu_last_seen) { curr_pdu = (struct pdu *)pskb->data; - if (do_debug_data) { - ctcm_pr_debug("ctcm: %s() pdu_header\n", - __FUNCTION__); - ctcmpc_dumpit((char *)pskb->data, - PDU_HEADER_LENGTH); - ctcm_pr_debug("ctcm: %s() pskb len: %04x \n", - __FUNCTION__, pskb->len); - } + + CTCM_PR_DBGDATA("%s: pdu_header\n", __func__); + CTCM_D3_DUMP((char *)pskb->data, PDU_HEADER_LENGTH); + CTCM_PR_DBGDATA("%s: pskb len: %04x \n", + __func__, pskb->len); + skb_pull(pskb, PDU_HEADER_LENGTH); if (curr_pdu->pdu_flag & PDU_LAST) @@ -1239,46 +1135,39 @@ static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb) pskb->protocol = htons(ETH_P_SNA_DIX); if ((pskb->len <= 0) || (pskb->len > ch->max_bufsize)) { - printk(KERN_INFO - "%s Illegal packet size %d " - "received " - "dropping\n", dev->name, - pskb->len); + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): Dropping packet with " + "illegal siize %d", + CTCM_FUNTAIL, dev->name, pskb->len); + priv->stats.rx_dropped++; priv->stats.rx_length_errors++; goto done; } skb_reset_mac_header(pskb); new_len = curr_pdu->pdu_offset; - if (do_debug_data) - ctcm_pr_debug("ctcmpc: %s() new_len: %04x \n", - __FUNCTION__, new_len); + CTCM_PR_DBGDATA("%s: new_len: %04x \n", + __func__, new_len); if ((new_len == 0) || (new_len > pskb->len)) { /* should never happen */ /* pskb len must be hosed...bail out */ - printk(KERN_INFO - "ctcmpc: %s(): invalid pdu" - " offset of %04x - data may be" - "lost\n", __FUNCTION__, new_len); - goto done; + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): non valid pdu_offset: %04x", + /* "data may be lost", */ + CTCM_FUNTAIL, dev->name, new_len); + goto done; } skb = __dev_alloc_skb(new_len+4, GFP_ATOMIC); if (!skb) { - printk(KERN_INFO - "ctcm: %s Out of memory in " - "%s()- request-len:%04x \n", - dev->name, - __FUNCTION__, - new_len+4); + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): MEMORY allocation error", + CTCM_FUNTAIL, dev->name); priv->stats.rx_dropped++; - fsm_event(grp->fsm, - MPCG_EVENT_INOP, dev); + fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); goto done; } - - memcpy(skb_put(skb, new_len), - pskb->data, new_len); + memcpy(skb_put(skb, new_len), pskb->data, new_len); skb_reset_mac_header(skb); skb->dev = pskb->dev; @@ -1287,17 +1176,14 @@ static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb) *((__u32 *) skb_push(skb, 4)) = ch->pdu_seq; ch->pdu_seq++; - if (do_debug_data) - ctcm_pr_debug("%s: ToDCM_pdu_seq= %08x\n", - __FUNCTION__, ch->pdu_seq); - - ctcm_pr_debug("ctcm: %s() skb:%0lx " - "skb len: %d \n", __FUNCTION__, - (unsigned long)skb, skb->len); if (do_debug_data) { - ctcm_pr_debug("ctcmpc: %s() up to 32 bytes" - " of pdu_data sent\n", - __FUNCTION__); + ctcm_pr_debug("%s: ToDCM_pdu_seq= %08x\n", + __func__, ch->pdu_seq); + ctcm_pr_debug("%s: skb:%0lx " + "skb len: %d \n", __func__, + (unsigned long)skb, skb->len); + ctcm_pr_debug("%s: up to 32 bytes " + "of pdu_data sent\n", __func__); ctcmpc_dump32((char *)skb->data, skb->len); } @@ -1316,8 +1202,8 @@ static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb) mpcginfo->ch = ch; mpcginfo->th = header; mpcginfo->skb = pskb; - ctcm_pr_debug("ctcmpc: %s() Not PDU - may be control pkt\n", - __FUNCTION__); + CTCM_PR_DEBUG("%s: Not PDU - may be control pkt\n", + __func__); /* it's a sweep? */ sweep = (struct th_sweep *)pskb->data; mpcginfo->sweep = sweep; @@ -1333,8 +1219,9 @@ static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb) } else if (header->th_blk_flag == TH_DISCONTACT) fsm_event(grp->fsm, MPCG_EVENT_DISCONC, mpcginfo); else if (header->th_seq_num != 0) { - printk(KERN_INFO "%s unexpected packet" - " expected control pkt\n", dev->name); + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): control pkt expected\n", + CTCM_FUNTAIL, dev->name); priv->stats.rx_dropped++; /* mpcginfo only used for non-data transfers */ kfree(mpcginfo); @@ -1347,13 +1234,12 @@ done: dev_kfree_skb_any(pskb); if (sendrc == NET_RX_DROP) { printk(KERN_WARNING "%s %s() NETWORK BACKLOG EXCEEDED" - " - PACKET DROPPED\n", dev->name, __FUNCTION__); + " - PACKET DROPPED\n", dev->name, __func__); fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); } - if (do_debug) - ctcm_pr_debug("ctcmpc exit : %s %s(): ch=0x%p id=%s\n", - dev->name, __FUNCTION__, ch, ch->id); + CTCM_PR_DEBUG("exit %s: %s: ch=0x%p id=%s\n", + __func__, dev->name, ch, ch->id); } /** @@ -1366,15 +1252,14 @@ done: */ void ctcmpc_bh(unsigned long thischan) { - struct channel *ch = (struct channel *)thischan; + struct channel *ch = (struct channel *)thischan; struct sk_buff *skb; - struct net_device *dev = ch->netdev; - struct ctcm_priv *priv = dev->priv; - struct mpc_group *grp = priv->mpcg; + struct net_device *dev = ch->netdev; + struct ctcm_priv *priv = dev->priv; + struct mpc_group *grp = priv->mpcg; - if (do_debug) - ctcm_pr_debug("%s cp:%i enter: %s() %s\n", - dev->name, smp_processor_id(), __FUNCTION__, ch->id); + CTCM_PR_DEBUG("%s cp:%i enter: %s() %s\n", + dev->name, smp_processor_id(), __func__, ch->id); /* caller has requested driver to throttle back */ while ((fsm_getstate(grp->fsm) != MPCG_STATE_FLOWC) && (skb = skb_dequeue(&ch->io_queue))) { @@ -1390,9 +1275,8 @@ void ctcmpc_bh(unsigned long thischan) if (skb == skb_peek(&ch->io_queue)) break; } - if (do_debug) - ctcm_pr_debug("ctcmpc exit : %s %s(): ch=0x%p id=%s\n", - dev->name, __FUNCTION__, ch, ch->id); + CTCM_PR_DEBUG("exit %s: %s: ch=0x%p id=%s\n", + __func__, dev->name, ch, ch->id); return; } @@ -1403,16 +1287,16 @@ struct mpc_group *ctcmpc_init_mpc_group(struct ctcm_priv *priv) { struct mpc_group *grp; - CTCM_DBF_TEXT(MPC_SETUP, 3, __FUNCTION__); + CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_INFO, + "Enter %s(%p)", CTCM_FUNTAIL, priv); grp = kzalloc(sizeof(struct mpc_group), GFP_KERNEL); if (grp == NULL) return NULL; - grp->fsm = - init_fsm("mpcg", mpcg_state_names, mpcg_event_names, - MPCG_NR_STATES, MPCG_NR_EVENTS, mpcg_fsm, - mpcg_fsm_len, GFP_KERNEL); + grp->fsm = init_fsm("mpcg", mpcg_state_names, mpcg_event_names, + MPCG_NR_STATES, MPCG_NR_EVENTS, mpcg_fsm, + mpcg_fsm_len, GFP_KERNEL); if (grp->fsm == NULL) { kfree(grp); return NULL; @@ -1424,7 +1308,6 @@ struct mpc_group *ctcmpc_init_mpc_group(struct ctcm_priv *priv) grp->xid_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC | GFP_DMA); if (grp->xid_skb == NULL) { - printk(KERN_INFO "Couldn't alloc MPCgroup xid_skb\n"); kfree_fsm(grp->fsm); kfree(grp); return NULL; @@ -1435,7 +1318,7 @@ struct mpc_group *ctcmpc_init_mpc_group(struct ctcm_priv *priv) memcpy(skb_put(grp->xid_skb, TH_HEADER_LENGTH), &thnorm, TH_HEADER_LENGTH); - grp->xid = (struct xid2 *) skb_tail_pointer(grp->xid_skb); + grp->xid = (struct xid2 *)skb_tail_pointer(grp->xid_skb); memcpy(skb_put(grp->xid_skb, XID2_LENGTH), &init_xid, XID2_LENGTH); grp->xid->xid2_adj_id = jiffies | 0xfff00000; grp->xid->xid2_sender_id = jiffies; @@ -1446,7 +1329,6 @@ struct mpc_group *ctcmpc_init_mpc_group(struct ctcm_priv *priv) grp->rcvd_xid_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC|GFP_DMA); if (grp->rcvd_xid_skb == NULL) { - printk(KERN_INFO "Couldn't alloc MPCgroup rcvd_xid_skb\n"); kfree_fsm(grp->fsm); dev_kfree_skb(grp->xid_skb); kfree(grp); @@ -1492,32 +1374,27 @@ static void mpc_action_go_inop(fsm_instance *fi, int event, void *arg) int rc = 0; struct channel *wch, *rch; - if (dev == NULL) { - printk(KERN_INFO "%s() dev=NULL\n", __FUNCTION__); - return; - } - - ctcm_pr_debug("ctcmpc enter: %s %s()\n", dev->name, __FUNCTION__); + BUG_ON(dev == NULL); + CTCM_PR_DEBUG("Enter %s: %s\n", __func__, dev->name); priv = dev->priv; grp = priv->mpcg; grp->flow_off_called = 0; - fsm_deltimer(&grp->timer); - if (grp->channels_terminating) - goto done; + return; grp->channels_terminating = 1; - grp->saved_state = fsm_getstate(grp->fsm); fsm_newstate(grp->fsm, MPCG_STATE_INOP); if (grp->saved_state > MPCG_STATE_XID7INITF) - printk(KERN_NOTICE "%s:MPC GROUP INOPERATIVE\n", dev->name); + CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE, + "%s(%s): MPC GROUP INOPERATIVE", + CTCM_FUNTAIL, dev->name); if ((grp->saved_state != MPCG_STATE_RESET) || /* dealloc_channel has been called */ - ((grp->saved_state == MPCG_STATE_RESET) && - (grp->port_persist == 0))) + ((grp->saved_state == MPCG_STATE_RESET) && + (grp->port_persist == 0))) fsm_deltimer(&priv->restart_timer); wch = priv->channel[WRITE]; @@ -1567,29 +1444,24 @@ static void mpc_action_go_inop(fsm_instance *fi, int event, void *arg) /* This can result in INOP of VTAM PU due to halting of */ /* outstanding IO which causes a sense to be returned */ /* Only about 3 senses are allowed and then IOS/VTAM will*/ - /* ebcome unreachable without manual intervention */ - if ((grp->port_persist == 1) || (grp->alloc_called)) { + /* become unreachable without manual intervention */ + if ((grp->port_persist == 1) || (grp->alloc_called)) { grp->alloc_called = 0; fsm_deltimer(&priv->restart_timer); - fsm_addtimer(&priv->restart_timer, - 500, - DEV_EVENT_RESTART, - dev); + fsm_addtimer(&priv->restart_timer, 500, DEV_EVENT_RESTART, dev); fsm_newstate(grp->fsm, MPCG_STATE_RESET); if (grp->saved_state > MPCG_STATE_XID7INITF) - printk(KERN_NOTICE "%s:MPC GROUP RECOVERY SCHEDULED\n", - dev->name); + CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ALWAYS, + "%s(%s): MPC GROUP RECOVERY SCHEDULED", + CTCM_FUNTAIL, dev->name); } else { fsm_deltimer(&priv->restart_timer); fsm_addtimer(&priv->restart_timer, 500, DEV_EVENT_STOP, dev); fsm_newstate(grp->fsm, MPCG_STATE_RESET); - printk(KERN_NOTICE "%s:MPC GROUP RECOVERY NOT ATTEMPTED\n", - dev->name); + CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ALWAYS, + "%s(%s): NO MPC GROUP RECOVERY ATTEMPTED", + CTCM_FUNTAIL, dev->name); } - -done: - ctcm_pr_debug("ctcmpc exit:%s %s()\n", dev->name, __FUNCTION__); - return; } /** @@ -1609,12 +1481,7 @@ static void mpc_action_timeout(fsm_instance *fi, int event, void *arg) struct channel *wch; struct channel *rch; - CTCM_DBF_TEXT(MPC_TRACE, 6, __FUNCTION__); - - if (dev == NULL) { - CTCM_DBF_TEXT_(MPC_ERROR, 4, "%s: dev=NULL\n", __FUNCTION__); - return; - } + BUG_ON(dev == NULL); priv = dev->priv; grp = priv->mpcg; @@ -1633,8 +1500,9 @@ static void mpc_action_timeout(fsm_instance *fi, int event, void *arg) fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); } - CTCM_DBF_TEXT_(MPC_TRACE, 6, "%s: dev=%s exit", - __FUNCTION__, dev->name); + CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG, + "%s: dev=%s exit", + CTCM_FUNTAIL, dev->name); return; } @@ -1646,25 +1514,25 @@ void mpc_action_discontact(fsm_instance *fi, int event, void *arg) { struct mpcg_info *mpcginfo = arg; struct channel *ch = mpcginfo->ch; - struct net_device *dev = ch->netdev; - struct ctcm_priv *priv = dev->priv; - struct mpc_group *grp = priv->mpcg; + struct net_device *dev; + struct ctcm_priv *priv; + struct mpc_group *grp; - if (ch == NULL) { - printk(KERN_INFO "%s() ch=NULL\n", __FUNCTION__); - return; - } - if (ch->netdev == NULL) { - printk(KERN_INFO "%s() dev=NULL\n", __FUNCTION__); - return; + if (ch) { + dev = ch->netdev; + if (dev) { + priv = dev->priv; + if (priv) { + CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE, + "%s: %s: %s\n", + CTCM_FUNTAIL, dev->name, ch->id); + grp = priv->mpcg; + grp->send_qllc_disc = 1; + fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); + } + } } - ctcm_pr_debug("ctcmpc enter: %s %s()\n", dev->name, __FUNCTION__); - - grp->send_qllc_disc = 1; - fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); - - ctcm_pr_debug("ctcmpc exit: %s %s()\n", dev->name, __FUNCTION__); return; } @@ -1675,26 +1543,9 @@ void mpc_action_discontact(fsm_instance *fi, int event, void *arg) */ void mpc_action_send_discontact(unsigned long thischan) { - struct channel *ch; - struct net_device *dev; - struct ctcm_priv *priv; - struct mpc_group *grp; - int rc = 0; - unsigned long saveflags; - - ch = (struct channel *)thischan; - dev = ch->netdev; - priv = dev->priv; - grp = priv->mpcg; - - ctcm_pr_info("ctcmpc: %s cp:%i enter: %s() GrpState:%s ChState:%s\n", - dev->name, - smp_processor_id(), - __FUNCTION__, - fsm_getstate_str(grp->fsm), - fsm_getstate_str(ch->fsm)); - saveflags = 0; /* avoids compiler warning with - spin_unlock_irqrestore */ + int rc; + struct channel *ch = (struct channel *)thischan; + unsigned long saveflags = 0; spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags); rc = ccw_device_start(ch->cdev, &ch->ccw[15], @@ -1702,16 +1553,9 @@ void mpc_action_send_discontact(unsigned long thischan) spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags); if (rc != 0) { - ctcm_pr_info("ctcmpc: %s() ch:%s IO failed \n", - __FUNCTION__, - ch->id); - ctcm_ccw_check_rc(ch, rc, "send discontact"); - /* Not checking return code value here */ - /* Making best effort to notify partner*/ - /* that MPC Group is going down */ + ctcm_ccw_check_rc(ch, rc, (char *)__func__); } - ctcm_pr_debug("ctcmpc exit: %s %s()\n", dev->name, __FUNCTION__); return; } @@ -1723,49 +1567,50 @@ void mpc_action_send_discontact(unsigned long thischan) */ static int mpc_validate_xid(struct mpcg_info *mpcginfo) { - struct channel *ch = mpcginfo->ch; - struct net_device *dev = ch->netdev; + struct channel *ch = mpcginfo->ch; + struct net_device *dev = ch->netdev; struct ctcm_priv *priv = dev->priv; struct mpc_group *grp = priv->mpcg; - struct xid2 *xid = mpcginfo->xid; - int failed = 0; - int rc = 0; - __u64 our_id, their_id = 0; - int len; - - len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH; + struct xid2 *xid = mpcginfo->xid; + int rc = 0; + __u64 our_id = 0; + __u64 their_id = 0; + int len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH; - ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__); + CTCM_PR_DEBUG("Enter %s: xid=%p\n", __func__, xid); - if (mpcginfo->xid == NULL) { - printk(KERN_INFO "%s() xid=NULL\n", __FUNCTION__); + if (xid == NULL) { rc = 1; - goto done; + /* XID REJECTED: xid == NULL */ + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): xid = NULL", + CTCM_FUNTAIL, ch->id); + goto done; } - ctcm_pr_debug("ctcmpc : %s xid received()\n", __FUNCTION__); - ctcmpc_dumpit((char *)mpcginfo->xid, XID2_LENGTH); + CTCM_D3_DUMP((char *)xid, XID2_LENGTH); /*the received direction should be the opposite of ours */ if (((CHANNEL_DIRECTION(ch->flags) == READ) ? XID2_WRITE_SIDE : XID2_READ_SIDE) != xid->xid2_dlc_type) { - failed = 1; - printk(KERN_INFO "ctcmpc:%s() XID REJECTED - READ-WRITE CH " - "Pairing Invalid \n", __FUNCTION__); + rc = 2; + /* XID REJECTED: r/w channel pairing mismatch */ + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): r/w channel pairing mismatch", + CTCM_FUNTAIL, ch->id); + goto done; } if (xid->xid2_dlc_type == XID2_READ_SIDE) { - ctcm_pr_debug("ctcmpc: %s(): grpmaxbuf:%d xid2buflen:%d\n", - __FUNCTION__, grp->group_max_buflen, - xid->xid2_buf_len); + CTCM_PR_DEBUG("%s: grpmaxbuf:%d xid2buflen:%d\n", __func__, + grp->group_max_buflen, xid->xid2_buf_len); - if (grp->group_max_buflen == 0 || - grp->group_max_buflen > xid->xid2_buf_len - len) + if (grp->group_max_buflen == 0 || grp->group_max_buflen > + xid->xid2_buf_len - len) grp->group_max_buflen = xid->xid2_buf_len - len; } - - if (grp->saved_xid2 == NULL) { + if (grp->saved_xid2 == NULL) { grp->saved_xid2 = (struct xid2 *)skb_tail_pointer(grp->rcvd_xid_skb); @@ -1786,49 +1631,54 @@ static int mpc_validate_xid(struct mpcg_info *mpcginfo) /* lower id assume the xside role */ if (our_id < their_id) { grp->roll = XSIDE; - ctcm_pr_debug("ctcmpc :%s() WE HAVE LOW ID-" - "TAKE XSIDE\n", __FUNCTION__); + CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE, + "%s(%s): WE HAVE LOW ID - TAKE XSIDE", + CTCM_FUNTAIL, ch->id); } else { grp->roll = YSIDE; - ctcm_pr_debug("ctcmpc :%s() WE HAVE HIGH ID-" - "TAKE YSIDE\n", __FUNCTION__); + CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE, + "%s(%s): WE HAVE HIGH ID - TAKE YSIDE", + CTCM_FUNTAIL, ch->id); } } else { if (xid->xid2_flag4 != grp->saved_xid2->xid2_flag4) { - failed = 1; - printk(KERN_INFO "%s XID REJECTED - XID Flag Byte4\n", - __FUNCTION__); + rc = 3; + /* XID REJECTED: xid flag byte4 mismatch */ + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): xid flag byte4 mismatch", + CTCM_FUNTAIL, ch->id); } if (xid->xid2_flag2 == 0x40) { - failed = 1; - printk(KERN_INFO "%s XID REJECTED - XID NOGOOD\n", - __FUNCTION__); + rc = 4; + /* XID REJECTED - xid NOGOOD */ + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): xid NOGOOD", + CTCM_FUNTAIL, ch->id); } if (xid->xid2_adj_id != grp->saved_xid2->xid2_adj_id) { - failed = 1; - printk(KERN_INFO "%s XID REJECTED - " - "Adjacent Station ID Mismatch\n", - __FUNCTION__); + rc = 5; + /* XID REJECTED - Adjacent Station ID Mismatch */ + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): Adjacent Station ID Mismatch", + CTCM_FUNTAIL, ch->id); } if (xid->xid2_sender_id != grp->saved_xid2->xid2_sender_id) { - failed = 1; - printk(KERN_INFO "%s XID REJECTED - " - "Sender Address Mismatch\n", __FUNCTION__); - + rc = 6; + /* XID REJECTED - Sender Address Mismatch */ + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): Sender Address Mismatch", + CTCM_FUNTAIL, ch->id); } } - if (failed) { +done: + if (rc) { ctcm_pr_info("ctcmpc : %s() failed\n", __FUNCTION__); priv->xid->xid2_flag2 = 0x40; grp->saved_xid2->xid2_flag2 = 0x40; - rc = 1; } -done: - - ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__); return rc; } @@ -1839,46 +1689,20 @@ done: static void mpc_action_side_xid(fsm_instance *fsm, void *arg, int side) { struct channel *ch = arg; - struct ctcm_priv *priv; - struct mpc_group *grp = NULL; - struct net_device *dev = NULL; int rc = 0; int gotlock = 0; unsigned long saveflags = 0; /* avoids compiler warning with - spin_unlock_irqrestore */ - - if (ch == NULL) { - printk(KERN_INFO "%s ch=NULL\n", __FUNCTION__); - goto done; - } - - if (do_debug) - ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n", - __FUNCTION__, smp_processor_id(), ch, ch->id); - - dev = ch->netdev; - if (dev == NULL) { - printk(KERN_INFO "%s dev=NULL\n", __FUNCTION__); - goto done; - } - - priv = dev->priv; - if (priv == NULL) { - printk(KERN_INFO "%s priv=NULL\n", __FUNCTION__); - goto done; - } + spin_unlock_irqrestore */ - grp = priv->mpcg; - if (grp == NULL) { - printk(KERN_INFO "%s grp=NULL\n", __FUNCTION__); - goto done; - } + CTCM_PR_DEBUG("Enter %s: cp=%i ch=0x%p id=%s\n", + __func__, smp_processor_id(), ch, ch->id); if (ctcm_checkalloc_buffer(ch)) goto done; - /* skb data-buffer referencing: */ - + /* + * skb data-buffer referencing: + */ ch->trans_skb->data = ch->trans_skb_data; skb_reset_tail_pointer(ch->trans_skb); ch->trans_skb->len = 0; @@ -1911,22 +1735,22 @@ static void mpc_action_side_xid(fsm_instance *fsm, void *arg, int side) ch->ccw[8].count = 0; ch->ccw[8].cda = 0x00; + if (!(ch->xid_th && ch->xid && ch->xid_id)) + CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_INFO, + "%s(%s): xid_th=%p, xid=%p, xid_id=%p", + CTCM_FUNTAIL, ch->id, ch->xid_th, ch->xid, ch->xid_id); + if (side == XSIDE) { /* mpc_action_xside_xid */ - if (ch->xid_th == NULL) { - printk(KERN_INFO "%s ch->xid_th=NULL\n", __FUNCTION__); - goto done; - } + if (ch->xid_th == NULL) + goto done; ch->ccw[9].cmd_code = CCW_CMD_WRITE; ch->ccw[9].flags = CCW_FLAG_SLI | CCW_FLAG_CC; ch->ccw[9].count = TH_HEADER_LENGTH; ch->ccw[9].cda = virt_to_phys(ch->xid_th); - if (ch->xid == NULL) { - printk(KERN_INFO "%s ch->xid=NULL\n", __FUNCTION__); - goto done; - } - + if (ch->xid == NULL) + goto done; ch->ccw[10].cmd_code = CCW_CMD_WRITE; ch->ccw[10].flags = CCW_FLAG_SLI | CCW_FLAG_CC; ch->ccw[10].count = XID2_LENGTH; @@ -1956,28 +1780,22 @@ static void mpc_action_side_xid(fsm_instance *fsm, void *arg, int side) ch->ccw[10].count = XID2_LENGTH; ch->ccw[10].cda = virt_to_phys(ch->rcvd_xid); - if (ch->xid_th == NULL) { - printk(KERN_INFO "%s ch->xid_th=NULL\n", __FUNCTION__); - goto done; - } + if (ch->xid_th == NULL) + goto done; ch->ccw[11].cmd_code = CCW_CMD_WRITE; ch->ccw[11].flags = CCW_FLAG_SLI | CCW_FLAG_CC; ch->ccw[11].count = TH_HEADER_LENGTH; ch->ccw[11].cda = virt_to_phys(ch->xid_th); - if (ch->xid == NULL) { - printk(KERN_INFO "%s ch->xid=NULL\n", __FUNCTION__); - goto done; - } + if (ch->xid == NULL) + goto done; ch->ccw[12].cmd_code = CCW_CMD_WRITE; ch->ccw[12].flags = CCW_FLAG_SLI | CCW_FLAG_CC; ch->ccw[12].count = XID2_LENGTH; ch->ccw[12].cda = virt_to_phys(ch->xid); - if (ch->xid_id == NULL) { - printk(KERN_INFO "%s ch->xid_id=NULL\n", __FUNCTION__); - goto done; - } + if (ch->xid_id == NULL) + goto done; ch->ccw[13].cmd_code = CCW_CMD_WRITE; ch->ccw[13].cda = virt_to_phys(ch->xid_id); @@ -1990,12 +1808,11 @@ static void mpc_action_side_xid(fsm_instance *fsm, void *arg, int side) ch->ccw[14].count = 0; ch->ccw[14].cda = 0; - if (do_debug_ccw) - ctcmpc_dumpit((char *)&ch->ccw[8], sizeof(struct ccw1) * 7); + CTCM_CCW_DUMP((char *)&ch->ccw[8], sizeof(struct ccw1) * 7); + CTCM_D3_DUMP((char *)ch->xid_th, TH_HEADER_LENGTH); + CTCM_D3_DUMP((char *)ch->xid, XID2_LENGTH); + CTCM_D3_DUMP((char *)ch->xid_id, 4); - ctcmpc_dumpit((char *)ch->xid_th, TH_HEADER_LENGTH); - ctcmpc_dumpit((char *)ch->xid, XID2_LENGTH); - ctcmpc_dumpit((char *)ch->xid_id, 4); if (!in_irq()) { /* Such conditional locking is a known problem for * sparse because its static undeterministic. @@ -2012,16 +1829,13 @@ static void mpc_action_side_xid(fsm_instance *fsm, void *arg, int side) spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags); if (rc != 0) { - ctcm_pr_info("ctcmpc: %s() ch:%s IO failed \n", - __FUNCTION__, ch->id); ctcm_ccw_check_rc(ch, rc, (side == XSIDE) ? "x-side XID" : "y-side XID"); } done: - if (do_debug) - ctcm_pr_debug("ctcmpc exit : %s(): ch=0x%p id=%s\n", - __FUNCTION__, ch, ch->id); + CTCM_PR_DEBUG("Exit %s: ch=0x%p id=%s\n", + __func__, ch, ch->id); return; } @@ -2050,41 +1864,19 @@ static void mpc_action_yside_xid(fsm_instance *fsm, int event, void *arg) */ static void mpc_action_doxid0(fsm_instance *fsm, int event, void *arg) { - struct channel *ch = arg; - struct ctcm_priv *priv; - struct mpc_group *grp = NULL; - struct net_device *dev = NULL; - - if (do_debug) - ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n", - __FUNCTION__, smp_processor_id(), ch, ch->id); - - if (ch == NULL) { - printk(KERN_WARNING "%s ch=NULL\n", __FUNCTION__); - goto done; - } - - dev = ch->netdev; - if (dev == NULL) { - printk(KERN_WARNING "%s dev=NULL\n", __FUNCTION__); - goto done; - } - - priv = dev->priv; - if (priv == NULL) { - printk(KERN_WARNING "%s priv=NULL\n", __FUNCTION__); - goto done; - } + struct channel *ch = arg; + struct net_device *dev = ch->netdev; + struct ctcm_priv *priv = dev->priv; + struct mpc_group *grp = priv->mpcg; - grp = priv->mpcg; - if (grp == NULL) { - printk(KERN_WARNING "%s grp=NULL\n", __FUNCTION__); - goto done; - } + CTCM_PR_DEBUG("Enter %s: cp=%i ch=0x%p id=%s\n", + __func__, smp_processor_id(), ch, ch->id); if (ch->xid == NULL) { - printk(KERN_WARNING "%s ch-xid=NULL\n", __FUNCTION__); - goto done; + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): ch->xid == NULL", + CTCM_FUNTAIL, dev->name); + return; } fsm_newstate(ch->fsm, CH_XID0_INPROGRESS); @@ -2104,12 +1896,7 @@ static void mpc_action_doxid0(fsm_instance *fsm, int event, void *arg) fsm_event(grp->fsm, MPCG_EVENT_DOIO, ch); -done: - if (do_debug) - ctcm_pr_debug("ctcmpc exit : %s(): ch=0x%p id=%s\n", - __FUNCTION__, ch, ch->id); return; - } /* @@ -2119,32 +1906,16 @@ done: static void mpc_action_doxid7(fsm_instance *fsm, int event, void *arg) { struct net_device *dev = arg; - struct ctcm_priv *priv = NULL; - struct mpc_group *grp = NULL; + struct ctcm_priv *priv = dev->priv; + struct mpc_group *grp = NULL; int direction; - int rc = 0; int send = 0; - ctcm_pr_debug("ctcmpc enter: %s() \n", __FUNCTION__); - - if (dev == NULL) { - printk(KERN_INFO "%s dev=NULL \n", __FUNCTION__); - rc = 1; - goto done; - } - - priv = dev->priv; - if (priv == NULL) { - printk(KERN_INFO "%s priv=NULL \n", __FUNCTION__); - rc = 1; - goto done; - } - - grp = priv->mpcg; + if (priv) + grp = priv->mpcg; if (grp == NULL) { - printk(KERN_INFO "%s grp=NULL \n", __FUNCTION__); - rc = 1; - goto done; + fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); + return; } for (direction = READ; direction <= WRITE; direction++) { @@ -2199,11 +1970,6 @@ static void mpc_action_doxid7(fsm_instance *fsm, int event, void *arg) fsm_event(grp->fsm, MPCG_EVENT_DOIO, ch); } -done: - - if (rc != 0) - fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); - return; } @@ -2214,24 +1980,15 @@ done: static void mpc_action_rcvd_xid0(fsm_instance *fsm, int event, void *arg) { - struct mpcg_info *mpcginfo = arg; - struct channel *ch = mpcginfo->ch; - struct net_device *dev = ch->netdev; - struct ctcm_priv *priv; - struct mpc_group *grp; - - if (do_debug) - ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n", - __FUNCTION__, smp_processor_id(), ch, ch->id); - - priv = dev->priv; - grp = priv->mpcg; + struct mpcg_info *mpcginfo = arg; + struct channel *ch = mpcginfo->ch; + struct net_device *dev = ch->netdev; + struct ctcm_priv *priv = dev->priv; + struct mpc_group *grp = priv->mpcg; - ctcm_pr_debug("ctcmpc in:%s() %s xid2:%i xid7:%i xidt_p2:%i \n", - __FUNCTION__, ch->id, - grp->outstanding_xid2, - grp->outstanding_xid7, - grp->outstanding_xid7_p2); + CTCM_PR_DEBUG("%s: ch-id:%s xid2:%i xid7:%i xidt_p2:%i \n", + __func__, ch->id, grp->outstanding_xid2, + grp->outstanding_xid7, grp->outstanding_xid7_p2); if (fsm_getstate(ch->fsm) < CH_XID7_PENDING) fsm_newstate(ch->fsm, CH_XID7_PENDING); @@ -2268,17 +2025,12 @@ static void mpc_action_rcvd_xid0(fsm_instance *fsm, int event, void *arg) } kfree(mpcginfo); - if (do_debug) { - ctcm_pr_debug("ctcmpc:%s() %s xid2:%i xid7:%i xidt_p2:%i \n", - __FUNCTION__, ch->id, - grp->outstanding_xid2, - grp->outstanding_xid7, - grp->outstanding_xid7_p2); - ctcm_pr_debug("ctcmpc:%s() %s grpstate: %s chanstate: %s \n", - __FUNCTION__, ch->id, - fsm_getstate_str(grp->fsm), - fsm_getstate_str(ch->fsm)); - } + CTCM_PR_DEBUG("ctcmpc:%s() %s xid2:%i xid7:%i xidt_p2:%i \n", + __func__, ch->id, grp->outstanding_xid2, + grp->outstanding_xid7, grp->outstanding_xid7_p2); + CTCM_PR_DEBUG("ctcmpc:%s() %s grpstate: %s chanstate: %s \n", + __func__, ch->id, + fsm_getstate_str(grp->fsm), fsm_getstate_str(ch->fsm)); return; } @@ -2296,15 +2048,10 @@ static void mpc_action_rcvd_xid7(fsm_instance *fsm, int event, void *arg) struct ctcm_priv *priv = dev->priv; struct mpc_group *grp = priv->mpcg; - if (do_debug) { - ctcm_pr_debug("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n", - __FUNCTION__, smp_processor_id(), ch, ch->id); - - ctcm_pr_debug("ctcmpc: outstanding_xid7: %i, " - " outstanding_xid7_p2: %i\n", - grp->outstanding_xid7, - grp->outstanding_xid7_p2); - } + CTCM_PR_DEBUG("Enter %s: cp=%i ch=0x%p id=%s\n", + __func__, smp_processor_id(), ch, ch->id); + CTCM_PR_DEBUG("%s: outstanding_xid7: %i, outstanding_xid7_p2: %i\n", + __func__, grp->outstanding_xid7, grp->outstanding_xid7_p2); grp->outstanding_xid7--; ch->xid_skb->data = ch->xid_skb_data; @@ -2337,14 +2084,8 @@ static void mpc_action_rcvd_xid7(fsm_instance *fsm, int event, void *arg) mpc_validate_xid(mpcginfo); break; } - kfree(mpcginfo); - - if (do_debug) - ctcm_pr_debug("ctcmpc exit: %s(): cp=%i ch=0x%p id=%s\n", - __FUNCTION__, smp_processor_id(), ch, ch->id); return; - } /* @@ -2353,36 +2094,14 @@ static void mpc_action_rcvd_xid7(fsm_instance *fsm, int event, void *arg) */ static int mpc_send_qllc_discontact(struct net_device *dev) { - int rc = 0; __u32 new_len = 0; struct sk_buff *skb; struct qllc *qllcptr; - struct ctcm_priv *priv; - struct mpc_group *grp; - - ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__); - - if (dev == NULL) { - printk(KERN_INFO "%s() dev=NULL\n", __FUNCTION__); - rc = 1; - goto done; - } - - priv = dev->priv; - if (priv == NULL) { - printk(KERN_INFO "%s() priv=NULL\n", __FUNCTION__); - rc = 1; - goto done; - } + struct ctcm_priv *priv = dev->priv; + struct mpc_group *grp = priv->mpcg; - grp = priv->mpcg; - if (grp == NULL) { - printk(KERN_INFO "%s() grp=NULL\n", __FUNCTION__); - rc = 1; - goto done; - } - ctcm_pr_info("ctcmpc: %s() GROUP STATE: %s\n", __FUNCTION__, - mpcg_state_names[grp->saved_state]); + CTCM_PR_DEBUG("%s: GROUP STATE: %s\n", + __func__, mpcg_state_names[grp->saved_state]); switch (grp->saved_state) { /* @@ -2408,11 +2127,10 @@ static int mpc_send_qllc_discontact(struct net_device *dev) new_len = sizeof(struct qllc); qllcptr = kzalloc(new_len, gfp_type() | GFP_DMA); if (qllcptr == NULL) { - printk(KERN_INFO - "ctcmpc: Out of memory in %s()\n", - dev->name); - rc = 1; - goto done; + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): qllcptr allocation error", + CTCM_FUNTAIL, dev->name); + return -ENOMEM; } qllcptr->qllc_address = 0xcc; @@ -2421,31 +2139,29 @@ static int mpc_send_qllc_discontact(struct net_device *dev) skb = __dev_alloc_skb(new_len, GFP_ATOMIC); if (skb == NULL) { - printk(KERN_INFO "%s Out of memory in mpc_send_qllc\n", - dev->name); + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): skb allocation error", + CTCM_FUNTAIL, dev->name); priv->stats.rx_dropped++; - rc = 1; kfree(qllcptr); - goto done; + return -ENOMEM; } memcpy(skb_put(skb, new_len), qllcptr, new_len); kfree(qllcptr); if (skb_headroom(skb) < 4) { - printk(KERN_INFO "ctcmpc: %s() Unable to" - " build discontact for %s\n", - __FUNCTION__, dev->name); - rc = 1; + CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, + "%s(%s): skb_headroom error", + CTCM_FUNTAIL, dev->name); dev_kfree_skb_any(skb); - goto done; + return -ENOMEM; } *((__u32 *)skb_push(skb, 4)) = priv->channel[READ]->pdu_seq; priv->channel[READ]->pdu_seq++; - if (do_debug_data) - ctcm_pr_debug("ctcmpc: %s ToDCM_pdu_seq= %08x\n", - __FUNCTION__, priv->channel[READ]->pdu_seq); + CTCM_PR_DBGDATA("ctcmpc: %s ToDCM_pdu_seq= %08x\n", + __func__, priv->channel[READ]->pdu_seq); /* receipt of CC03 resets anticipated sequence number on receiving side */ @@ -2455,7 +2171,7 @@ static int mpc_send_qllc_discontact(struct net_device *dev) skb->protocol = htons(ETH_P_SNAP); skb->ip_summed = CHECKSUM_UNNECESSARY; - ctcmpc_dumpit((char *)skb->data, (sizeof(struct qllc) + 4)); + CTCM_D3_DUMP(skb->data, (sizeof(struct qllc) + 4)); netif_rx(skb); break; @@ -2464,9 +2180,7 @@ static int mpc_send_qllc_discontact(struct net_device *dev) } -done: - ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__); - return rc; + return 0; } /* --- This is the END my friend --- */ diff --git a/drivers/s390/net/ctcm_mpc.h b/drivers/s390/net/ctcm_mpc.h index f99686069a91..5336120cddf1 100644 --- a/drivers/s390/net/ctcm_mpc.h +++ b/drivers/s390/net/ctcm_mpc.h @@ -231,7 +231,7 @@ static inline void ctcmpc_dump32(char *buf, int len) int ctcmpc_open(struct net_device *); void ctcm_ccw_check_rc(struct channel *, int, char *); void mpc_group_ready(unsigned long adev); -int mpc_channel_action(struct channel *ch, int direction, int action); +void mpc_channel_action(struct channel *ch, int direction, int action); void mpc_action_send_discontact(unsigned long thischan); void mpc_action_discontact(fsm_instance *fi, int event, void *arg); void ctcmpc_bh(unsigned long thischan); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index c3ad89e302bd..cebb25e36e82 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -3321,7 +3321,7 @@ int qeth_change_mtu(struct net_device *dev, int new_mtu) struct qeth_card *card; char dbf_text[15]; - card = netdev_priv(dev); + card = dev->ml_priv; QETH_DBF_TEXT(TRACE, 4, "chgmtu"); sprintf(dbf_text, "%8x", new_mtu); @@ -3343,7 +3343,7 @@ struct net_device_stats *qeth_get_stats(struct net_device *dev) { struct qeth_card *card; - card = netdev_priv(dev); + card = dev->ml_priv; QETH_DBF_TEXT(TRACE, 5, "getstat"); @@ -3395,7 +3395,7 @@ void qeth_tx_timeout(struct net_device *dev) { struct qeth_card *card; - card = netdev_priv(dev); + card = dev->ml_priv; card->stats.tx_errors++; qeth_schedule_recovery(card); } @@ -3403,7 +3403,7 @@ EXPORT_SYMBOL_GPL(qeth_tx_timeout); int qeth_mdio_read(struct net_device *dev, int phy_id, int regnum) { - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; int rc = 0; switch (regnum) { @@ -4253,7 +4253,7 @@ EXPORT_SYMBOL_GPL(qeth_core_get_stats_count); void qeth_core_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; data[0] = card->stats.rx_packets - card->perf_stats.initial_rx_packets; data[1] = card->perf_stats.bufs_rec; @@ -4313,7 +4313,7 @@ EXPORT_SYMBOL_GPL(qeth_core_get_strings); void qeth_core_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; if (card->options.layer2) strcpy(info->driver, "qeth_l2"); else @@ -4331,7 +4331,7 @@ EXPORT_SYMBOL_GPL(qeth_core_get_drvinfo); int qeth_core_ethtool_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { - struct qeth_card *card = netdev_priv(netdev); + struct qeth_card *card = netdev->ml_priv; enum qeth_link_types link_type; if ((card->info.type == QETH_CARD_TYPE_IQD) || (card->info.guestlan)) diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 3fbc3bdec0c5..a8b069cd9a4c 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -35,7 +35,7 @@ static int qeth_l2_recover(void *); static int qeth_l2_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; struct mii_ioctl_data *mii_data; int rc = 0; @@ -317,7 +317,7 @@ static void qeth_l2_process_vlans(struct qeth_card *card, int clear) static void qeth_l2_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) { - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; struct qeth_vlan_vid *id; QETH_DBF_TEXT_(TRACE, 4, "aid:%d", vid); @@ -334,7 +334,7 @@ static void qeth_l2_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) static void qeth_l2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) { struct qeth_vlan_vid *id, *tmpid = NULL; - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; QETH_DBF_TEXT_(TRACE, 4, "kid:%d", vid); spin_lock_bh(&card->vlanlock); @@ -566,7 +566,7 @@ static int qeth_l2_request_initial_mac(struct qeth_card *card) static int qeth_l2_set_mac_address(struct net_device *dev, void *p) { struct sockaddr *addr = p; - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; int rc = 0; QETH_DBF_TEXT(TRACE, 3, "setmac"); @@ -590,7 +590,7 @@ static int qeth_l2_set_mac_address(struct net_device *dev, void *p) static void qeth_l2_set_multicast_list(struct net_device *dev) { - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; struct dev_mc_list *dm; if (card->info.type == QETH_CARD_TYPE_OSN) @@ -612,7 +612,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) int rc; struct qeth_hdr *hdr = NULL; int elements = 0; - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; struct sk_buff *new_skb = skb; int ipv = qeth_get_ip_version(skb); int cast_type = qeth_get_cast_type(card, skb); @@ -767,7 +767,7 @@ static void qeth_l2_qdio_input_handler(struct ccw_device *ccwdev, static int qeth_l2_open(struct net_device *dev) { - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; QETH_DBF_TEXT(TRACE, 4, "qethopen"); if (card->state != CARD_STATE_SOFTSETUP) @@ -791,7 +791,7 @@ static int qeth_l2_open(struct net_device *dev) static int qeth_l2_stop(struct net_device *dev) { - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; QETH_DBF_TEXT(TRACE, 4, "qethstop"); netif_tx_disable(dev); @@ -838,7 +838,7 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev) static int qeth_l2_ethtool_set_tso(struct net_device *dev, u32 data) { - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; if (data) { if (card->options.large_send == QETH_LARGE_SEND_NO) { @@ -894,7 +894,7 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) if (!card->dev) return -ENODEV; - card->dev->priv = card; + card->dev->ml_priv = card; card->dev->tx_timeout = &qeth_tx_timeout; card->dev->watchdog_timeo = QETH_TX_TIMEOUT; card->dev->open = qeth_l2_open; @@ -1178,7 +1178,7 @@ int qeth_osn_assist(struct net_device *dev, void *data, int data_len) QETH_DBF_TEXT(TRACE, 2, "osnsdmc"); if (!dev) return -ENODEV; - card = netdev_priv(dev); + card = dev->ml_priv; if (!card) return -ENODEV; if ((card->state != CARD_STATE_UP) && @@ -1201,7 +1201,7 @@ int qeth_osn_register(unsigned char *read_dev_no, struct net_device **dev, *dev = qeth_l2_netdev_by_devno(read_dev_no); if (*dev == NULL) return -ENODEV; - card = netdev_priv(*dev); + card = (*dev)->ml_priv; if (!card) return -ENODEV; if ((assist_cb == NULL) || (data_cb == NULL)) @@ -1219,7 +1219,7 @@ void qeth_osn_deregister(struct net_device *dev) QETH_DBF_TEXT(TRACE, 2, "osndereg"); if (!dev) return; - card = netdev_priv(dev); + card = dev->ml_priv; if (!card) return; card->osn_info.assist_cb = NULL; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index b29afef5c7fb..3e1d13857350 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1813,7 +1813,7 @@ static void qeth_l3_free_vlan_addresses(struct qeth_card *card, static void qeth_l3_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) { - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; unsigned long flags; QETH_DBF_TEXT(TRACE, 4, "vlanreg"); @@ -1825,7 +1825,7 @@ static void qeth_l3_vlan_rx_register(struct net_device *dev, static void qeth_l3_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) { struct net_device *vlandev; - struct qeth_card *card = (struct qeth_card *) dev->priv; + struct qeth_card *card = dev->ml_priv; struct in_device *in_dev; if (card->info.type == QETH_CARD_TYPE_IQD) @@ -1851,7 +1851,7 @@ static void qeth_l3_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) static void qeth_l3_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) { - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; unsigned long flags; QETH_DBF_TEXT_(TRACE, 4, "kid:%d", vid); @@ -2013,7 +2013,7 @@ static int qeth_l3_verify_vlan_dev(struct net_device *dev, } } - if (rc && !(netdev_priv(vlan_dev_real_dev(dev)) == (void *)card)) + if (rc && !(vlan_dev_real_dev(dev)->ml_priv == (void *)card)) return 0; return rc; @@ -2047,9 +2047,9 @@ static struct qeth_card *qeth_l3_get_card_from_dev(struct net_device *dev) rc = qeth_l3_verify_dev(dev); if (rc == QETH_REAL_CARD) - card = netdev_priv(dev); + card = dev->ml_priv; else if (rc == QETH_VLAN_CARD) - card = netdev_priv(vlan_dev_real_dev(dev)); + card = vlan_dev_real_dev(dev)->ml_priv; if (card && card->options.layer2) card = NULL; QETH_DBF_TEXT_(TRACE, 4, "%d", rc); @@ -2110,7 +2110,7 @@ static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode) static void qeth_l3_set_multicast_list(struct net_device *dev) { - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; QETH_DBF_TEXT(TRACE, 3, "setmulti"); qeth_l3_delete_mc_addresses(card); @@ -2438,7 +2438,7 @@ static int qeth_l3_arp_flush_cache(struct qeth_card *card) static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; struct qeth_arp_cache_entry arp_entry; struct mii_ioctl_data *mii_data; int rc = 0; @@ -2595,7 +2595,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) u16 *tag; struct qeth_hdr *hdr = NULL; int elements_needed = 0; - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; struct sk_buff *new_skb = NULL; int ipv = qeth_get_ip_version(skb); int cast_type = qeth_get_cast_type(card, skb); @@ -2651,7 +2651,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) tag = (u16 *)(new_skb->data + 12); *tag = __constant_htons(ETH_P_8021Q); *(tag + 1) = htons(vlan_tx_tag_get(new_skb)); - VLAN_TX_SKB_CB(new_skb)->magic = 0; + new_skb->vlan_tci = 0; } } @@ -2763,7 +2763,7 @@ tx_drop: static int qeth_l3_open(struct net_device *dev) { - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; QETH_DBF_TEXT(TRACE, 4, "qethopen"); if (card->state != CARD_STATE_SOFTSETUP) @@ -2780,7 +2780,7 @@ static int qeth_l3_open(struct net_device *dev) static int qeth_l3_stop(struct net_device *dev) { - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; QETH_DBF_TEXT(TRACE, 4, "qethstop"); netif_tx_disable(dev); @@ -2792,14 +2792,14 @@ static int qeth_l3_stop(struct net_device *dev) static u32 qeth_l3_ethtool_get_rx_csum(struct net_device *dev) { - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; return (card->options.checksum_type == HW_CHECKSUMMING); } static int qeth_l3_ethtool_set_rx_csum(struct net_device *dev, u32 data) { - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; enum qeth_card_states old_state; enum qeth_checksum_types csum_type; @@ -2825,7 +2825,7 @@ static int qeth_l3_ethtool_set_rx_csum(struct net_device *dev, u32 data) static int qeth_l3_ethtool_set_tso(struct net_device *dev, u32 data) { - struct qeth_card *card = netdev_priv(dev); + struct qeth_card *card = dev->ml_priv; if (data) { if (card->options.large_send == QETH_LARGE_SEND_NO) { @@ -2915,7 +2915,7 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) return -ENODEV; card->dev->hard_start_xmit = qeth_l3_hard_start_xmit; - card->dev->priv = card; + card->dev->ml_priv = card; card->dev->tx_timeout = &qeth_tx_timeout; card->dev->watchdog_timeo = QETH_TX_TIMEOUT; card->dev->open = qeth_l3_open; diff --git a/drivers/sbus/char/uctrl.c b/drivers/sbus/char/uctrl.c index 513ba61ae966..777637594acd 100644 --- a/drivers/sbus/char/uctrl.c +++ b/drivers/sbus/char/uctrl.c @@ -195,8 +195,8 @@ struct uctrl_driver { static struct uctrl_driver drv; -void uctrl_get_event_status(void); -void uctrl_get_external_status(void); +static void uctrl_get_event_status(void); +static void uctrl_get_external_status(void); static int uctrl_ioctl(struct inode *inode, struct file *file, @@ -266,12 +266,6 @@ static struct miscdevice uctrl_dev = { driver->regs->uctrl_stat = UCTRL_STAT_RXNE_STA; \ } -void uctrl_set_video(int status) -{ - struct uctrl_driver *driver = &drv; - -} - static void uctrl_do_txn(struct uctrl_txn *txn) { struct uctrl_driver *driver = &drv; @@ -311,7 +305,7 @@ static void uctrl_do_txn(struct uctrl_txn *txn) } } -void uctrl_get_event_status(void) +static void uctrl_get_event_status(void) { struct uctrl_driver *driver = &drv; struct uctrl_txn txn; @@ -331,7 +325,7 @@ void uctrl_get_event_status(void) dprintk(("ev is %x\n", driver->status.event_status)); } -void uctrl_get_external_status(void) +static void uctrl_get_external_status(void) { struct uctrl_driver *driver = &drv; struct uctrl_txn txn; @@ -363,7 +357,7 @@ void uctrl_get_external_status(void) static int __init ts102_uctrl_init(void) { struct uctrl_driver *driver = &drv; - int len, i; + int len; struct linux_prom_irqs tmp_irq[2]; unsigned int vaddr[2] = { 0, 0 }; int tmpnode, uctrlnode = prom_getchild(prom_root_node); diff --git a/drivers/sbus/char/vfc.h b/drivers/sbus/char/vfc.h index f1aa1389ea4a..a5240c52aa0b 100644 --- a/drivers/sbus/char/vfc.h +++ b/drivers/sbus/char/vfc.h @@ -133,8 +133,6 @@ struct vfc_dev { unsigned char saa9051_state_array[VFC_SAA9051_NR]; }; -extern struct vfc_dev **vfc_dev_lst; - void captstat_reset(struct vfc_dev *); void memptr_reset(struct vfc_dev *); @@ -145,8 +143,6 @@ int vfc_i2c_sendbuf(struct vfc_dev *, unsigned char, char *, int) ; int vfc_i2c_recvbuf(struct vfc_dev *, unsigned char, char *, int) ; int vfc_i2c_reset_bus(struct vfc_dev *); int vfc_init_i2c_bus(struct vfc_dev *); -void vfc_lock_device(struct vfc_dev *); -void vfc_unlock_device(struct vfc_dev *); #define VFC_CONTROL_DIAGMODE 0x10000000 #define VFC_CONTROL_MEMPTR 0x20000000 diff --git a/drivers/sbus/char/vfc_dev.c b/drivers/sbus/char/vfc_dev.c index 1f6cb8ae2784..25181bb7d627 100644 --- a/drivers/sbus/char/vfc_dev.c +++ b/drivers/sbus/char/vfc_dev.c @@ -45,7 +45,7 @@ #include <asm/vfc_ioctls.h> static const struct file_operations vfc_fops; -struct vfc_dev **vfc_dev_lst; +static struct vfc_dev **vfc_dev_lst; static char vfcstr[]="vfc"; static unsigned char saa9051_init_array[VFC_SAA9051_NR] = { 0x00, 0x64, 0x72, 0x52, @@ -54,18 +54,18 @@ static unsigned char saa9051_init_array[VFC_SAA9051_NR] = { 0x3e }; -void vfc_lock_device(struct vfc_dev *dev) +static void vfc_lock_device(struct vfc_dev *dev) { mutex_lock(&dev->device_lock_mtx); } -void vfc_unlock_device(struct vfc_dev *dev) +static void vfc_unlock_device(struct vfc_dev *dev) { mutex_unlock(&dev->device_lock_mtx); } -void vfc_captstat_reset(struct vfc_dev *dev) +static void vfc_captstat_reset(struct vfc_dev *dev) { dev->control_reg |= VFC_CONTROL_CAPTRESET; sbus_writel(dev->control_reg, &dev->regs->control); @@ -75,7 +75,7 @@ void vfc_captstat_reset(struct vfc_dev *dev) sbus_writel(dev->control_reg, &dev->regs->control); } -void vfc_memptr_reset(struct vfc_dev *dev) +static void vfc_memptr_reset(struct vfc_dev *dev) { dev->control_reg |= VFC_CONTROL_MEMPTR; sbus_writel(dev->control_reg, &dev->regs->control); @@ -85,7 +85,7 @@ void vfc_memptr_reset(struct vfc_dev *dev) sbus_writel(dev->control_reg, &dev->regs->control); } -int vfc_csr_init(struct vfc_dev *dev) +static int vfc_csr_init(struct vfc_dev *dev) { dev->control_reg = 0x80000000; sbus_writel(dev->control_reg, &dev->regs->control); @@ -107,7 +107,7 @@ int vfc_csr_init(struct vfc_dev *dev) return 0; } -int vfc_saa9051_init(struct vfc_dev *dev) +static int vfc_saa9051_init(struct vfc_dev *dev) { int i; @@ -119,7 +119,7 @@ int vfc_saa9051_init(struct vfc_dev *dev) return 0; } -int init_vfc_hw(struct vfc_dev *dev) +static int init_vfc_hw(struct vfc_dev *dev) { vfc_lock_device(dev); vfc_csr_init(dev); @@ -132,7 +132,7 @@ int init_vfc_hw(struct vfc_dev *dev) return 0; } -int init_vfc_devstruct(struct vfc_dev *dev, int instance) +static int init_vfc_devstruct(struct vfc_dev *dev, int instance) { dev->instance=instance; mutex_init(&dev->device_lock_mtx); @@ -141,7 +141,8 @@ int init_vfc_devstruct(struct vfc_dev *dev, int instance) return 0; } -int init_vfc_device(struct sbus_dev *sdev,struct vfc_dev *dev, int instance) +static int init_vfc_device(struct sbus_dev *sdev,struct vfc_dev *dev, + int instance) { if(dev == NULL) { printk(KERN_ERR "VFC: Bogus pointer passed\n"); @@ -168,7 +169,7 @@ int init_vfc_device(struct sbus_dev *sdev,struct vfc_dev *dev, int instance) } -struct vfc_dev *vfc_get_dev_ptr(int instance) +static struct vfc_dev *vfc_get_dev_ptr(int instance) { return vfc_dev_lst[instance]; } @@ -292,7 +293,7 @@ static int vfc_debug(struct vfc_dev *dev, int cmd, void __user *argp) return 0; } -int vfc_capture_start(struct vfc_dev *dev) +static int vfc_capture_start(struct vfc_dev *dev) { vfc_captstat_reset(dev); dev->control_reg = sbus_readl(&dev->regs->control); @@ -314,7 +315,7 @@ int vfc_capture_start(struct vfc_dev *dev) return 0; } -int vfc_capture_poll(struct vfc_dev *dev) +static int vfc_capture_poll(struct vfc_dev *dev) { int timeout = 1000; @@ -390,8 +391,8 @@ static int vfc_set_control_ioctl(struct inode *inode, struct file *file, } -int vfc_port_change_ioctl(struct inode *inode, struct file *file, - struct vfc_dev *dev, unsigned long arg) +static int vfc_port_change_ioctl(struct inode *inode, struct file *file, + struct vfc_dev *dev, unsigned long arg) { int ret = 0; int cmd; @@ -460,8 +461,8 @@ int vfc_port_change_ioctl(struct inode *inode, struct file *file, return ret; } -int vfc_set_video_ioctl(struct inode *inode, struct file *file, - struct vfc_dev *dev, unsigned long arg) +static int vfc_set_video_ioctl(struct inode *inode, struct file *file, + struct vfc_dev *dev, unsigned long arg) { int ret = 0; int cmd; @@ -511,8 +512,8 @@ int vfc_set_video_ioctl(struct inode *inode, struct file *file, return ret; } -int vfc_get_video_ioctl(struct inode *inode, struct file *file, - struct vfc_dev *dev, unsigned long arg) +static int vfc_get_video_ioctl(struct inode *inode, struct file *file, + struct vfc_dev *dev, unsigned long arg) { int ret = 0; unsigned int status = NO_LOCK; diff --git a/drivers/sbus/char/vfc_i2c.c b/drivers/sbus/char/vfc_i2c.c index 9efed771f6c0..32b986e0ed78 100644 --- a/drivers/sbus/char/vfc_i2c.c +++ b/drivers/sbus/char/vfc_i2c.c @@ -114,7 +114,7 @@ int vfc_i2c_reset_bus(struct vfc_dev *dev) return 0; } -int vfc_i2c_wait_for_bus(struct vfc_dev *dev) +static int vfc_i2c_wait_for_bus(struct vfc_dev *dev) { int timeout = 1000; @@ -126,7 +126,7 @@ int vfc_i2c_wait_for_bus(struct vfc_dev *dev) return 0; } -int vfc_i2c_wait_for_pin(struct vfc_dev *dev, int ack) +static int vfc_i2c_wait_for_pin(struct vfc_dev *dev, int ack) { int timeout = 1000; int s1; @@ -144,7 +144,8 @@ int vfc_i2c_wait_for_pin(struct vfc_dev *dev, int ack) } #define SHIFT(a) ((a) << 24) -int vfc_i2c_xmit_addr(struct vfc_dev *dev, unsigned char addr, char mode) +static int vfc_i2c_xmit_addr(struct vfc_dev *dev, unsigned char addr, + char mode) { int ret, raddr; #if 1 @@ -195,7 +196,7 @@ int vfc_i2c_xmit_addr(struct vfc_dev *dev, unsigned char addr, char mode) return 0; } -int vfc_i2c_xmit_byte(struct vfc_dev *dev,unsigned char *byte) +static int vfc_i2c_xmit_byte(struct vfc_dev *dev,unsigned char *byte) { int ret; u32 val = SHIFT((unsigned int)*byte); @@ -218,7 +219,8 @@ int vfc_i2c_xmit_byte(struct vfc_dev *dev,unsigned char *byte) return ret; } -int vfc_i2c_recv_byte(struct vfc_dev *dev, unsigned char *byte, int last) +static int vfc_i2c_recv_byte(struct vfc_dev *dev, unsigned char *byte, + int last) { int ret; diff --git a/drivers/sbus/dvma.c b/drivers/sbus/dvma.c index 57e1526746a2..ab0d2de3324c 100644 --- a/drivers/sbus/dvma.c +++ b/drivers/sbus/dvma.c @@ -16,7 +16,7 @@ struct sbus_dma *dma_chain; -void __init init_one_dvma(struct sbus_dma *dma, int num_dma) +static void __init init_one_dvma(struct sbus_dma *dma, int num_dma) { printk("dma%d: ", num_dma); diff --git a/drivers/sbus/sbus.c b/drivers/sbus/sbus.c index c37d7c2587ff..73a86d09bba8 100644 --- a/drivers/sbus/sbus.c +++ b/drivers/sbus/sbus.c @@ -78,7 +78,7 @@ static void __init fill_sbus_device(struct device_node *dp, struct sbus_dev *sde else sdev->ofdev.dev.parent = &sdev->bus->ofdev.dev; sdev->ofdev.dev.bus = &sbus_bus_type; - sprintf(sdev->ofdev.dev.bus_id, "sbus[%08x]", dp->node); + dev_set_name(&sdev->ofdev.dev, "sbus[%08x]", dp->node); if (of_device_register(&sdev->ofdev) != 0) printk(KERN_DEBUG "sbus: device registration error for %s!\n", @@ -257,11 +257,11 @@ static void __init build_one_sbus(struct device_node *dp, int num_sbus) sbus->ofdev.node = dp; sbus->ofdev.dev.parent = NULL; sbus->ofdev.dev.bus = &sbus_bus_type; - sprintf(sbus->ofdev.dev.bus_id, "sbus%d", num_sbus); + dev_set_name(&sbus->ofdev.dev, "sbus%d", num_sbus); if (of_device_register(&sbus->ofdev) != 0) printk(KERN_DEBUG "sbus: device registration error for %s!\n", - sbus->ofdev.dev.bus_id); + dev_name(&sbus->ofdev.dev)); dev_dp = dp->child; while (dev_dp) { diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c index 7045511f9ad2..b92c19bb6876 100644 --- a/drivers/scsi/3w-9xxx.c +++ b/drivers/scsi/3w-9xxx.c @@ -4,7 +4,7 @@ Written By: Adam Radford <linuxraid@amcc.com> Modifications By: Tom Couch <linuxraid@amcc.com> - Copyright (C) 2004-2007 Applied Micro Circuits Corporation. + Copyright (C) 2004-2008 Applied Micro Circuits 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 @@ -71,6 +71,10 @@ Add support for 9650SE controllers. 2.26.02.009 - Fix dma mask setting to fallback to 32-bit if 64-bit fails. 2.26.02.010 - Add support for 9690SA controllers. + 2.26.02.011 - Increase max AENs drained to 256. + Add MSI support and "use_msi" module parameter. + Fix bug in twa_get_param() on 4GB+. + Use pci_resource_len() for ioremap(). */ #include <linux/module.h> @@ -95,7 +99,7 @@ #include "3w-9xxx.h" /* Globals */ -#define TW_DRIVER_VERSION "2.26.02.010" +#define TW_DRIVER_VERSION "2.26.02.011" static TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT]; static unsigned int twa_device_extension_count; static int twa_major = -1; @@ -107,6 +111,10 @@ MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(TW_DRIVER_VERSION); +static int use_msi = 0; +module_param(use_msi, int, S_IRUGO); +MODULE_PARM_DESC(use_msi, "Use Message Signaled Interrupts. Default: 0"); + /* Function prototypes */ static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header); static int twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id); @@ -1038,7 +1046,6 @@ static void *twa_get_param(TW_Device_Extension *tw_dev, int request_id, int tabl TW_Command_Full *full_command_packet; TW_Command *command_packet; TW_Param_Apache *param; - unsigned long param_value; void *retval = NULL; /* Setup the command packet */ @@ -1057,9 +1064,8 @@ static void *twa_get_param(TW_Device_Extension *tw_dev, int request_id, int tabl param->table_id = cpu_to_le16(table_id | 0x8000); param->parameter_id = cpu_to_le16(parameter_id); param->parameter_size_bytes = cpu_to_le16(parameter_size_bytes); - param_value = tw_dev->generic_buffer_phys[request_id]; - command_packet->byte8_offset.param.sgl[0].address = TW_CPU_TO_SGL(param_value); + command_packet->byte8_offset.param.sgl[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]); command_packet->byte8_offset.param.sgl[0].length = cpu_to_le32(TW_SECTOR_SIZE); /* Post the command packet to the board */ @@ -2000,7 +2006,7 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id { struct Scsi_Host *host = NULL; TW_Device_Extension *tw_dev; - u32 mem_addr; + unsigned long mem_addr, mem_len; int retval = -ENODEV; retval = pci_enable_device(pdev); @@ -2045,13 +2051,16 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id goto out_free_device_extension; } - if (pdev->device == PCI_DEVICE_ID_3WARE_9000) + if (pdev->device == PCI_DEVICE_ID_3WARE_9000) { mem_addr = pci_resource_start(pdev, 1); - else + mem_len = pci_resource_len(pdev, 1); + } else { mem_addr = pci_resource_start(pdev, 2); + mem_len = pci_resource_len(pdev, 2); + } /* Save base address */ - tw_dev->base_addr = ioremap(mem_addr, PAGE_SIZE); + tw_dev->base_addr = ioremap(mem_addr, mem_len); if (!tw_dev->base_addr) { TW_PRINTK(tw_dev->host, TW_DRIVER, 0x35, "Failed to ioremap"); goto out_release_mem_region; @@ -2086,7 +2095,7 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id pci_set_drvdata(pdev, host); - printk(KERN_WARNING "3w-9xxx: scsi%d: Found a 3ware 9000 Storage Controller at 0x%x, IRQ: %d.\n", + printk(KERN_WARNING "3w-9xxx: scsi%d: Found a 3ware 9000 Storage Controller at 0x%lx, IRQ: %d.\n", host->host_no, mem_addr, pdev->irq); printk(KERN_WARNING "3w-9xxx: scsi%d: Firmware %s, BIOS %s, Ports: %d.\n", host->host_no, @@ -2097,6 +2106,11 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id le32_to_cpu(*(int *)twa_get_param(tw_dev, 2, TW_INFORMATION_TABLE, TW_PARAM_PORTCOUNT, TW_PARAM_PORTCOUNT_LENGTH))); + /* Try to enable MSI */ + if (use_msi && (pdev->device != PCI_DEVICE_ID_3WARE_9000) && + !pci_enable_msi(pdev)) + set_bit(TW_USING_MSI, &tw_dev->flags); + /* Now setup the interrupt handler */ retval = request_irq(pdev->irq, twa_interrupt, IRQF_SHARED, "3w-9xxx", tw_dev); if (retval) { @@ -2120,6 +2134,8 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id return 0; out_remove_host: + if (test_bit(TW_USING_MSI, &tw_dev->flags)) + pci_disable_msi(pdev); scsi_remove_host(host); out_iounmap: iounmap(tw_dev->base_addr); @@ -2151,6 +2167,10 @@ static void twa_remove(struct pci_dev *pdev) /* Shutdown the card */ __twa_shutdown(tw_dev); + /* Disable MSI if enabled */ + if (test_bit(TW_USING_MSI, &tw_dev->flags)) + pci_disable_msi(pdev); + /* Free IO remapping */ iounmap(tw_dev->base_addr); diff --git a/drivers/scsi/3w-9xxx.h b/drivers/scsi/3w-9xxx.h index d14a9479e389..1729a8785fea 100644 --- a/drivers/scsi/3w-9xxx.h +++ b/drivers/scsi/3w-9xxx.h @@ -4,7 +4,7 @@ Written By: Adam Radford <linuxraid@amcc.com> Modifications By: Tom Couch <linuxraid@amcc.com> - Copyright (C) 2004-2007 Applied Micro Circuits Corporation. + Copyright (C) 2004-2008 Applied Micro Circuits 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 @@ -319,8 +319,8 @@ static twa_message_type twa_error_table[] = { /* Compatibility defines */ #define TW_9000_ARCH_ID 0x5 -#define TW_CURRENT_DRIVER_SRL 30 -#define TW_CURRENT_DRIVER_BUILD 80 +#define TW_CURRENT_DRIVER_SRL 35 +#define TW_CURRENT_DRIVER_BUILD 0 #define TW_CURRENT_DRIVER_BRANCH 0 /* Phase defines */ @@ -352,8 +352,9 @@ static twa_message_type twa_error_table[] = { #define TW_MAX_RESET_TRIES 2 #define TW_MAX_CMDS_PER_LUN 254 #define TW_MAX_RESPONSE_DRAIN 256 -#define TW_MAX_AEN_DRAIN 40 +#define TW_MAX_AEN_DRAIN 255 #define TW_IN_RESET 2 +#define TW_USING_MSI 3 #define TW_IN_ATTENTION_LOOP 4 #define TW_MAX_SECTORS 256 #define TW_AEN_WAIT_TIME 1000 diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 26be540d1dd3..c7f06298bd3c 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -63,6 +63,7 @@ comment "SCSI support type (disk, tape, CD-ROM)" config BLK_DEV_SD tristate "SCSI disk support" depends on SCSI + select CRC_T10DIF ---help--- If you want to use SCSI hard disks, Fibre Channel disks, Serial ATA (SATA) or Parallel ATA (PATA) hard disks, diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index a8149677de23..72fd5043cfa1 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -151,6 +151,8 @@ scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o scsi_tgt-y += scsi_tgt_lib.o scsi_tgt_if.o sd_mod-objs := sd.o +sd_mod-$(CONFIG_BLK_DEV_INTEGRITY) += sd_dif.o + sr_mod-objs := sr.o sr_ioctl.o sr_vendor.o ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \ := -DCONFIG_NCR53C8XX_PREFETCH -DSCSI_NCR_BIG_ENDIAN \ diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c index 8591585e5cc5..218777bfc143 100644 --- a/drivers/scsi/advansys.c +++ b/drivers/scsi/advansys.c @@ -2278,7 +2278,7 @@ do { \ #define ASC_DBG(lvl, format, arg...) { \ if (asc_dbglvl >= (lvl)) \ printk(KERN_DEBUG "%s: %s: " format, DRV_NAME, \ - __FUNCTION__ , ## arg); \ + __func__ , ## arg); \ } #define ASC_DBG_PRT_SCSI_HOST(lvl, s) \ diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c index 0899cb61e3dd..b5a868d85eb4 100644 --- a/drivers/scsi/aha152x.c +++ b/drivers/scsi/aha152x.c @@ -288,20 +288,20 @@ static LIST_HEAD(aha152x_host_list); #define DO_LOCK(flags) \ do { \ if(spin_is_locked(&QLOCK)) { \ - DPRINTK(debug_intr, DEBUG_LEAD "(%s:%d) already locked at %s:%d\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__, QLOCKER, QLOCKERL); \ + DPRINTK(debug_intr, DEBUG_LEAD "(%s:%d) already locked at %s:%d\n", CMDINFO(CURRENT_SC), __func__, __LINE__, QLOCKER, QLOCKERL); \ } \ - DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) locking\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__); \ + DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) locking\n", CMDINFO(CURRENT_SC), __func__, __LINE__); \ spin_lock_irqsave(&QLOCK,flags); \ - DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) locked\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__); \ - QLOCKER=__FUNCTION__; \ + DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) locked\n", CMDINFO(CURRENT_SC), __func__, __LINE__); \ + QLOCKER=__func__; \ QLOCKERL=__LINE__; \ } while(0) #define DO_UNLOCK(flags) \ do { \ - DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) unlocking (locked at %s:%d)\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__, QLOCKER, QLOCKERL); \ + DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) unlocking (locked at %s:%d)\n", CMDINFO(CURRENT_SC), __func__, __LINE__, QLOCKER, QLOCKERL); \ spin_unlock_irqrestore(&QLOCK,flags); \ - DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) unlocked\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__); \ + DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) unlocked\n", CMDINFO(CURRENT_SC), __func__, __LINE__); \ QLOCKER="(not locked)"; \ QLOCKERL=0; \ } while(0) diff --git a/drivers/scsi/aic94xx/aic94xx.h b/drivers/scsi/aic94xx/aic94xx.h index 2ef459e9cda1..2863a9d22851 100644 --- a/drivers/scsi/aic94xx/aic94xx.h +++ b/drivers/scsi/aic94xx/aic94xx.h @@ -39,9 +39,9 @@ #ifdef ASD_ENTER_EXIT #define ENTER printk(KERN_NOTICE "%s: ENTER %s\n", ASD_DRIVER_NAME, \ - __FUNCTION__) + __func__) #define EXIT printk(KERN_NOTICE "%s: --EXIT %s\n", ASD_DRIVER_NAME, \ - __FUNCTION__) + __func__) #else #define ENTER #define EXIT diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.c b/drivers/scsi/aic94xx/aic94xx_hwi.c index 83a78222896d..eb9dc3195fdf 100644 --- a/drivers/scsi/aic94xx/aic94xx_hwi.c +++ b/drivers/scsi/aic94xx/aic94xx_hwi.c @@ -1359,7 +1359,7 @@ int asd_enable_phys(struct asd_ha_struct *asd_ha, const u8 phy_mask) struct asd_ascb *ascb_list; if (!phy_mask) { - asd_printk("%s called with phy_mask of 0!?\n", __FUNCTION__); + asd_printk("%s called with phy_mask of 0!?\n", __func__); return 0; } diff --git a/drivers/scsi/aic94xx/aic94xx_scb.c b/drivers/scsi/aic94xx/aic94xx_scb.c index 46643319c520..ca55013b6ae5 100644 --- a/drivers/scsi/aic94xx/aic94xx_scb.c +++ b/drivers/scsi/aic94xx/aic94xx_scb.c @@ -211,7 +211,7 @@ static void asd_form_port(struct asd_ha_struct *asd_ha, struct asd_phy *phy) phy->asd_port = port; } ASD_DPRINTK("%s: updating phy_mask 0x%x for phy%d\n", - __FUNCTION__, phy->asd_port->phy_mask, sas_phy->id); + __func__, phy->asd_port->phy_mask, sas_phy->id); asd_update_port_links(asd_ha, phy); spin_unlock_irqrestore(&asd_ha->asd_ports_lock, flags); } @@ -294,7 +294,7 @@ static void asd_link_reset_err_tasklet(struct asd_ascb *ascb, struct asd_ascb *cp = asd_ascb_alloc_list(ascb->ha, &num, GFP_ATOMIC); if (!cp) { - asd_printk("%s: out of memory\n", __FUNCTION__); + asd_printk("%s: out of memory\n", __func__); goto out; } ASD_DPRINTK("phy%d: retries:0 performing link reset seq\n", @@ -446,7 +446,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, struct domain_device *failed_dev = NULL; ASD_DPRINTK("%s: REQ_TASK_ABORT, reason=0x%X\n", - __FUNCTION__, dl->status_block[3]); + __func__, dl->status_block[3]); /* * Find the task that caused the abort and abort it first. @@ -474,7 +474,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, if (!failed_dev) { ASD_DPRINTK("%s: Can't find task (tc=%d) to abort!\n", - __FUNCTION__, tc_abort); + __func__, tc_abort); goto out; } @@ -502,7 +502,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, conn_handle = *((u16*)(&dl->status_block[1])); conn_handle = le16_to_cpu(conn_handle); - ASD_DPRINTK("%s: REQ_DEVICE_RESET, reason=0x%X\n", __FUNCTION__, + ASD_DPRINTK("%s: REQ_DEVICE_RESET, reason=0x%X\n", __func__, dl->status_block[3]); /* Find the last pending task for the device... */ @@ -522,7 +522,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, if (!last_dev_task) { ASD_DPRINTK("%s: Device reset for idle device %d?\n", - __FUNCTION__, conn_handle); + __func__, conn_handle); goto out; } @@ -549,10 +549,10 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, goto out; } case SIGNAL_NCQ_ERROR: - ASD_DPRINTK("%s: SIGNAL_NCQ_ERROR\n", __FUNCTION__); + ASD_DPRINTK("%s: SIGNAL_NCQ_ERROR\n", __func__); goto out; case CLEAR_NCQ_ERROR: - ASD_DPRINTK("%s: CLEAR_NCQ_ERROR\n", __FUNCTION__); + ASD_DPRINTK("%s: CLEAR_NCQ_ERROR\n", __func__); goto out; } @@ -560,26 +560,26 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, switch (sb_opcode) { case BYTES_DMAED: - ASD_DPRINTK("%s: phy%d: BYTES_DMAED\n", __FUNCTION__, phy_id); + ASD_DPRINTK("%s: phy%d: BYTES_DMAED\n", __func__, phy_id); asd_bytes_dmaed_tasklet(ascb, dl, edb, phy_id); break; case PRIMITIVE_RECVD: - ASD_DPRINTK("%s: phy%d: PRIMITIVE_RECVD\n", __FUNCTION__, + ASD_DPRINTK("%s: phy%d: PRIMITIVE_RECVD\n", __func__, phy_id); asd_primitive_rcvd_tasklet(ascb, dl, phy_id); break; case PHY_EVENT: - ASD_DPRINTK("%s: phy%d: PHY_EVENT\n", __FUNCTION__, phy_id); + ASD_DPRINTK("%s: phy%d: PHY_EVENT\n", __func__, phy_id); asd_phy_event_tasklet(ascb, dl); break; case LINK_RESET_ERROR: - ASD_DPRINTK("%s: phy%d: LINK_RESET_ERROR\n", __FUNCTION__, + ASD_DPRINTK("%s: phy%d: LINK_RESET_ERROR\n", __func__, phy_id); asd_link_reset_err_tasklet(ascb, dl, phy_id); break; case TIMER_EVENT: ASD_DPRINTK("%s: phy%d: TIMER_EVENT, lost dw sync\n", - __FUNCTION__, phy_id); + __func__, phy_id); asd_turn_led(asd_ha, phy_id, 0); /* the device is gone */ sas_phy_disconnected(sas_phy); @@ -587,7 +587,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, sas_ha->notify_port_event(sas_phy, PORTE_TIMER_EVENT); break; default: - ASD_DPRINTK("%s: phy%d: unknown event:0x%x\n", __FUNCTION__, + ASD_DPRINTK("%s: phy%d: unknown event:0x%x\n", __func__, phy_id, sb_opcode); ASD_DPRINTK("edb is 0x%x! dl->opcode is 0x%x\n", edb, dl->opcode); @@ -654,7 +654,7 @@ static void control_phy_tasklet_complete(struct asd_ascb *ascb, if (status != 0) { ASD_DPRINTK("%s: phy%d status block opcode:0x%x\n", - __FUNCTION__, phy_id, status); + __func__, phy_id, status); goto out; } @@ -663,7 +663,7 @@ static void control_phy_tasklet_complete(struct asd_ascb *ascb, asd_ha->hw_prof.enabled_phys &= ~(1 << phy_id); asd_turn_led(asd_ha, phy_id, 0); asd_control_led(asd_ha, phy_id, 0); - ASD_DPRINTK("%s: disable phy%d\n", __FUNCTION__, phy_id); + ASD_DPRINTK("%s: disable phy%d\n", __func__, phy_id); break; case ENABLE_PHY: @@ -673,40 +673,40 @@ static void control_phy_tasklet_complete(struct asd_ascb *ascb, get_lrate_mode(phy, oob_mode); asd_turn_led(asd_ha, phy_id, 1); ASD_DPRINTK("%s: phy%d, lrate:0x%x, proto:0x%x\n", - __FUNCTION__, phy_id,phy->sas_phy.linkrate, + __func__, phy_id,phy->sas_phy.linkrate, phy->sas_phy.iproto); } else if (oob_status & CURRENT_SPINUP_HOLD) { asd_ha->hw_prof.enabled_phys |= (1 << phy_id); asd_turn_led(asd_ha, phy_id, 1); - ASD_DPRINTK("%s: phy%d, spinup hold\n", __FUNCTION__, + ASD_DPRINTK("%s: phy%d, spinup hold\n", __func__, phy_id); } else if (oob_status & CURRENT_ERR_MASK) { asd_turn_led(asd_ha, phy_id, 0); ASD_DPRINTK("%s: phy%d: error: oob status:0x%02x\n", - __FUNCTION__, phy_id, oob_status); + __func__, phy_id, oob_status); } else if (oob_status & (CURRENT_HOT_PLUG_CNCT | CURRENT_DEVICE_PRESENT)) { asd_ha->hw_prof.enabled_phys |= (1 << phy_id); asd_turn_led(asd_ha, phy_id, 1); ASD_DPRINTK("%s: phy%d: hot plug or device present\n", - __FUNCTION__, phy_id); + __func__, phy_id); } else { asd_ha->hw_prof.enabled_phys |= (1 << phy_id); asd_turn_led(asd_ha, phy_id, 0); ASD_DPRINTK("%s: phy%d: no device present: " "oob_status:0x%x\n", - __FUNCTION__, phy_id, oob_status); + __func__, phy_id, oob_status); } break; case RELEASE_SPINUP_HOLD: case PHY_NO_OP: case EXECUTE_HARD_RESET: - ASD_DPRINTK("%s: phy%d: sub_func:0x%x\n", __FUNCTION__, + ASD_DPRINTK("%s: phy%d: sub_func:0x%x\n", __func__, phy_id, control_phy->sub_func); /* XXX finish */ break; default: - ASD_DPRINTK("%s: phy%d: sub_func:0x%x?\n", __FUNCTION__, + ASD_DPRINTK("%s: phy%d: sub_func:0x%x?\n", __func__, phy_id, control_phy->sub_func); break; } diff --git a/drivers/scsi/aic94xx/aic94xx_task.c b/drivers/scsi/aic94xx/aic94xx_task.c index 326765c9caf8..75d20f72501f 100644 --- a/drivers/scsi/aic94xx/aic94xx_task.c +++ b/drivers/scsi/aic94xx/aic94xx_task.c @@ -320,7 +320,7 @@ Again: case TC_RESUME: case TC_PARTIAL_SG_LIST: default: - ASD_DPRINTK("%s: dl opcode: 0x%x?\n", __FUNCTION__, opcode); + ASD_DPRINTK("%s: dl opcode: 0x%x?\n", __func__, opcode); break; } diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c index 633ff40c736a..d4640ef6d44f 100644 --- a/drivers/scsi/aic94xx/aic94xx_tmf.c +++ b/drivers/scsi/aic94xx/aic94xx_tmf.c @@ -75,12 +75,12 @@ static void asd_clear_nexus_tasklet_complete(struct asd_ascb *ascb, struct done_list_struct *dl) { struct tasklet_completion_status *tcs = ascb->uldd_task; - ASD_DPRINTK("%s: here\n", __FUNCTION__); + ASD_DPRINTK("%s: here\n", __func__); if (!del_timer(&ascb->timer)) { - ASD_DPRINTK("%s: couldn't delete timer\n", __FUNCTION__); + ASD_DPRINTK("%s: couldn't delete timer\n", __func__); return; } - ASD_DPRINTK("%s: opcode: 0x%x\n", __FUNCTION__, dl->opcode); + ASD_DPRINTK("%s: opcode: 0x%x\n", __func__, dl->opcode); tcs->dl_opcode = dl->opcode; complete(ascb->completion); asd_ascb_free(ascb); @@ -91,7 +91,7 @@ static void asd_clear_nexus_timedout(unsigned long data) struct asd_ascb *ascb = (void *)data; struct tasklet_completion_status *tcs = ascb->uldd_task; - ASD_DPRINTK("%s: here\n", __FUNCTION__); + ASD_DPRINTK("%s: here\n", __func__); tcs->dl_opcode = TMF_RESP_FUNC_FAILED; complete(ascb->completion); } @@ -103,7 +103,7 @@ static void asd_clear_nexus_timedout(unsigned long data) DECLARE_COMPLETION_ONSTACK(completion); \ DECLARE_TCS(tcs); \ \ - ASD_DPRINTK("%s: PRE\n", __FUNCTION__); \ + ASD_DPRINTK("%s: PRE\n", __func__); \ res = 1; \ ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); \ if (!ascb) \ @@ -115,12 +115,12 @@ static void asd_clear_nexus_timedout(unsigned long data) scb->header.opcode = CLEAR_NEXUS #define CLEAR_NEXUS_POST \ - ASD_DPRINTK("%s: POST\n", __FUNCTION__); \ + ASD_DPRINTK("%s: POST\n", __func__); \ res = asd_enqueue_internal(ascb, asd_clear_nexus_tasklet_complete, \ asd_clear_nexus_timedout); \ if (res) \ goto out_err; \ - ASD_DPRINTK("%s: clear nexus posted, waiting...\n", __FUNCTION__); \ + ASD_DPRINTK("%s: clear nexus posted, waiting...\n", __func__); \ wait_for_completion(&completion); \ res = tcs.dl_opcode; \ if (res == TC_NO_ERROR) \ @@ -417,7 +417,7 @@ int asd_abort_task(struct sas_task *task) if (task->task_state_flags & SAS_TASK_STATE_DONE) { spin_unlock_irqrestore(&task->task_state_lock, flags); res = TMF_RESP_FUNC_COMPLETE; - ASD_DPRINTK("%s: task 0x%p done\n", __FUNCTION__, task); + ASD_DPRINTK("%s: task 0x%p done\n", __func__, task); goto out_done; } spin_unlock_irqrestore(&task->task_state_lock, flags); @@ -481,7 +481,7 @@ int asd_abort_task(struct sas_task *task) if (task->task_state_flags & SAS_TASK_STATE_DONE) { spin_unlock_irqrestore(&task->task_state_lock, flags); res = TMF_RESP_FUNC_COMPLETE; - ASD_DPRINTK("%s: task 0x%p done\n", __FUNCTION__, task); + ASD_DPRINTK("%s: task 0x%p done\n", __func__, task); goto out_done; } spin_unlock_irqrestore(&task->task_state_lock, flags); diff --git a/drivers/scsi/arm/fas216.c b/drivers/scsi/arm/fas216.c index a715632e19d4..477542602284 100644 --- a/drivers/scsi/arm/fas216.c +++ b/drivers/scsi/arm/fas216.c @@ -240,7 +240,7 @@ static void __fas216_checkmagic(FAS216_Info *info, const char *func) panic("scsi memory space corrupted in %s", func); } } -#define fas216_checkmagic(info) __fas216_checkmagic((info), __FUNCTION__) +#define fas216_checkmagic(info) __fas216_checkmagic((info), __func__) #else #define fas216_checkmagic(info) #endif @@ -2658,7 +2658,7 @@ int fas216_eh_host_reset(struct scsi_cmnd *SCpnt) fas216_checkmagic(info); printk("scsi%d.%c: %s: resetting host\n", - info->host->host_no, '0' + SCpnt->device->id, __FUNCTION__); + info->host->host_no, '0' + SCpnt->device->id, __func__); /* * Reset the SCSI chip. diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c index aa2011b64683..3c257fe0893e 100644 --- a/drivers/scsi/ch.c +++ b/drivers/scsi/ch.c @@ -930,6 +930,7 @@ static int ch_probe(struct device *dev) if (init) ch_init_elem(ch); + dev_set_drvdata(dev, ch); sdev_printk(KERN_INFO, sd, "Attached scsi changer %s\n", ch->name); return 0; diff --git a/drivers/scsi/device_handler/Kconfig b/drivers/scsi/device_handler/Kconfig index 2adc0f666b68..67070257919f 100644 --- a/drivers/scsi/device_handler/Kconfig +++ b/drivers/scsi/device_handler/Kconfig @@ -30,3 +30,11 @@ config SCSI_DH_EMC depends on SCSI_DH help If you have a EMC CLARiiON select y. Otherwise, say N. + +config SCSI_DH_ALUA + tristate "SPC-3 ALUA Device Handler (EXPERIMENTAL)" + depends on SCSI_DH && EXPERIMENTAL + help + SCSI Device handler for generic SPC-3 Asymmetric Logical Unit + Access (ALUA). + diff --git a/drivers/scsi/device_handler/Makefile b/drivers/scsi/device_handler/Makefile index 35272e93b1c8..e1d2ea083e15 100644 --- a/drivers/scsi/device_handler/Makefile +++ b/drivers/scsi/device_handler/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_SCSI_DH) += scsi_dh.o obj-$(CONFIG_SCSI_DH_RDAC) += scsi_dh_rdac.o obj-$(CONFIG_SCSI_DH_HP_SW) += scsi_dh_hp_sw.o obj-$(CONFIG_SCSI_DH_EMC) += scsi_dh_emc.o +obj-$(CONFIG_SCSI_DH_ALUA) += scsi_dh_alua.o diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index ab6c21cd9689..a518f2eff19a 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c @@ -24,8 +24,16 @@ #include <scsi/scsi_dh.h> #include "../scsi_priv.h" +struct scsi_dh_devinfo_list { + struct list_head node; + char vendor[9]; + char model[17]; + struct scsi_device_handler *handler; +}; + static DEFINE_SPINLOCK(list_lock); static LIST_HEAD(scsi_dh_list); +static LIST_HEAD(scsi_dh_dev_list); static struct scsi_device_handler *get_device_handler(const char *name) { @@ -33,7 +41,7 @@ static struct scsi_device_handler *get_device_handler(const char *name) spin_lock(&list_lock); list_for_each_entry(tmp, &scsi_dh_list, list) { - if (!strcmp(tmp->name, name)) { + if (!strncmp(tmp->name, name, strlen(tmp->name))) { found = tmp; break; } @@ -42,11 +50,307 @@ static struct scsi_device_handler *get_device_handler(const char *name) return found; } + +static struct scsi_device_handler * +scsi_dh_cache_lookup(struct scsi_device *sdev) +{ + struct scsi_dh_devinfo_list *tmp; + struct scsi_device_handler *found_dh = NULL; + + spin_lock(&list_lock); + list_for_each_entry(tmp, &scsi_dh_dev_list, node) { + if (!strncmp(sdev->vendor, tmp->vendor, strlen(tmp->vendor)) && + !strncmp(sdev->model, tmp->model, strlen(tmp->model))) { + found_dh = tmp->handler; + break; + } + } + spin_unlock(&list_lock); + + return found_dh; +} + +static int scsi_dh_handler_lookup(struct scsi_device_handler *scsi_dh, + struct scsi_device *sdev) +{ + int i, found = 0; + + for(i = 0; scsi_dh->devlist[i].vendor; i++) { + if (!strncmp(sdev->vendor, scsi_dh->devlist[i].vendor, + strlen(scsi_dh->devlist[i].vendor)) && + !strncmp(sdev->model, scsi_dh->devlist[i].model, + strlen(scsi_dh->devlist[i].model))) { + found = 1; + break; + } + } + return found; +} + +/* + * device_handler_match - Attach a device handler to a device + * @scsi_dh - The device handler to match against or NULL + * @sdev - SCSI device to be tested against @scsi_dh + * + * Tests @sdev against the device handler @scsi_dh or against + * all registered device_handler if @scsi_dh == NULL. + * Returns the found device handler or NULL if not found. + */ +static struct scsi_device_handler * +device_handler_match(struct scsi_device_handler *scsi_dh, + struct scsi_device *sdev) +{ + struct scsi_device_handler *found_dh = NULL; + struct scsi_dh_devinfo_list *tmp; + + found_dh = scsi_dh_cache_lookup(sdev); + if (found_dh) + return found_dh; + + if (scsi_dh) { + if (scsi_dh_handler_lookup(scsi_dh, sdev)) + found_dh = scsi_dh; + } else { + struct scsi_device_handler *tmp_dh; + + spin_lock(&list_lock); + list_for_each_entry(tmp_dh, &scsi_dh_list, list) { + if (scsi_dh_handler_lookup(tmp_dh, sdev)) + found_dh = tmp_dh; + } + spin_unlock(&list_lock); + } + + if (found_dh) { /* If device is found, add it to the cache */ + tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); + if (tmp) { + strncpy(tmp->vendor, sdev->vendor, 8); + strncpy(tmp->model, sdev->model, 16); + tmp->vendor[8] = '\0'; + tmp->model[16] = '\0'; + tmp->handler = found_dh; + spin_lock(&list_lock); + list_add(&tmp->node, &scsi_dh_dev_list); + spin_unlock(&list_lock); + } else { + found_dh = NULL; + } + } + + return found_dh; +} + +/* + * scsi_dh_handler_attach - Attach a device handler to a device + * @sdev - SCSI device the device handler should attach to + * @scsi_dh - The device handler to attach + */ +static int scsi_dh_handler_attach(struct scsi_device *sdev, + struct scsi_device_handler *scsi_dh) +{ + int err = 0; + + if (sdev->scsi_dh_data) { + if (sdev->scsi_dh_data->scsi_dh != scsi_dh) + err = -EBUSY; + } else if (scsi_dh->attach) + err = scsi_dh->attach(sdev); + + return err; +} + +/* + * scsi_dh_handler_detach - Detach a device handler from a device + * @sdev - SCSI device the device handler should be detached from + * @scsi_dh - Device handler to be detached + * + * Detach from a device handler. If a device handler is specified, + * only detach if the currently attached handler matches @scsi_dh. + */ +static void scsi_dh_handler_detach(struct scsi_device *sdev, + struct scsi_device_handler *scsi_dh) +{ + if (!sdev->scsi_dh_data) + return; + + if (scsi_dh && scsi_dh != sdev->scsi_dh_data->scsi_dh) + return; + + if (!scsi_dh) + scsi_dh = sdev->scsi_dh_data->scsi_dh; + + if (scsi_dh && scsi_dh->detach) + scsi_dh->detach(sdev); +} + +/* + * Functions for sysfs attribute 'dh_state' + */ +static ssize_t +store_dh_state(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct scsi_device_handler *scsi_dh; + int err = -EINVAL; + + if (!sdev->scsi_dh_data) { + /* + * Attach to a device handler + */ + if (!(scsi_dh = get_device_handler(buf))) + return err; + err = scsi_dh_handler_attach(sdev, scsi_dh); + } else { + scsi_dh = sdev->scsi_dh_data->scsi_dh; + if (!strncmp(buf, "detach", 6)) { + /* + * Detach from a device handler + */ + scsi_dh_handler_detach(sdev, scsi_dh); + err = 0; + } else if (!strncmp(buf, "activate", 8)) { + /* + * Activate a device handler + */ + if (scsi_dh->activate) + err = scsi_dh->activate(sdev); + else + err = 0; + } + } + + return err<0?err:count; +} + +static ssize_t +show_dh_state(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + + if (!sdev->scsi_dh_data) + return snprintf(buf, 20, "detached\n"); + + return snprintf(buf, 20, "%s\n", sdev->scsi_dh_data->scsi_dh->name); +} + +static struct device_attribute scsi_dh_state_attr = + __ATTR(dh_state, S_IRUGO | S_IWUSR, show_dh_state, + store_dh_state); + +/* + * scsi_dh_sysfs_attr_add - Callback for scsi_init_dh + */ +static int scsi_dh_sysfs_attr_add(struct device *dev, void *data) +{ + struct scsi_device *sdev; + int err; + + if (!scsi_is_sdev_device(dev)) + return 0; + + sdev = to_scsi_device(dev); + + err = device_create_file(&sdev->sdev_gendev, + &scsi_dh_state_attr); + + return 0; +} + +/* + * scsi_dh_sysfs_attr_remove - Callback for scsi_exit_dh + */ +static int scsi_dh_sysfs_attr_remove(struct device *dev, void *data) +{ + struct scsi_device *sdev; + + if (!scsi_is_sdev_device(dev)) + return 0; + + sdev = to_scsi_device(dev); + + device_remove_file(&sdev->sdev_gendev, + &scsi_dh_state_attr); + + return 0; +} + +/* + * scsi_dh_notifier - notifier chain callback + */ +static int scsi_dh_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + struct scsi_device *sdev; + int err = 0; + struct scsi_device_handler *devinfo = NULL; + + if (!scsi_is_sdev_device(dev)) + return 0; + + sdev = to_scsi_device(dev); + + if (action == BUS_NOTIFY_ADD_DEVICE) { + devinfo = device_handler_match(NULL, sdev); + if (!devinfo) + goto out; + + err = scsi_dh_handler_attach(sdev, devinfo); + if (!err) + err = device_create_file(dev, &scsi_dh_state_attr); + } else if (action == BUS_NOTIFY_DEL_DEVICE) { + device_remove_file(dev, &scsi_dh_state_attr); + scsi_dh_handler_detach(sdev, NULL); + } +out: + return err; +} + +/* + * scsi_dh_notifier_add - Callback for scsi_register_device_handler + */ static int scsi_dh_notifier_add(struct device *dev, void *data) { struct scsi_device_handler *scsi_dh = data; + struct scsi_device *sdev; + + if (!scsi_is_sdev_device(dev)) + return 0; + + if (!get_device(dev)) + return 0; + + sdev = to_scsi_device(dev); + + if (device_handler_match(scsi_dh, sdev)) + scsi_dh_handler_attach(sdev, scsi_dh); + + put_device(dev); + + return 0; +} + +/* + * scsi_dh_notifier_remove - Callback for scsi_unregister_device_handler + */ +static int scsi_dh_notifier_remove(struct device *dev, void *data) +{ + struct scsi_device_handler *scsi_dh = data; + struct scsi_device *sdev; + + if (!scsi_is_sdev_device(dev)) + return 0; + + if (!get_device(dev)) + return 0; + + sdev = to_scsi_device(dev); + + scsi_dh_handler_detach(sdev, scsi_dh); + + put_device(dev); - scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_ADD_DEVICE, dev); return 0; } @@ -59,33 +363,19 @@ static int scsi_dh_notifier_add(struct device *dev, void *data) */ int scsi_register_device_handler(struct scsi_device_handler *scsi_dh) { - int ret = -EBUSY; - struct scsi_device_handler *tmp; + if (get_device_handler(scsi_dh->name)) + return -EBUSY; - tmp = get_device_handler(scsi_dh->name); - if (tmp) - goto done; - - ret = bus_register_notifier(&scsi_bus_type, &scsi_dh->nb); - - bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add); spin_lock(&list_lock); list_add(&scsi_dh->list, &scsi_dh_list); spin_unlock(&list_lock); + bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add); + printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name); -done: - return ret; + return SCSI_DH_OK; } EXPORT_SYMBOL_GPL(scsi_register_device_handler); -static int scsi_dh_notifier_remove(struct device *dev, void *data) -{ - struct scsi_device_handler *scsi_dh = data; - - scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_DEL_DEVICE, dev); - return 0; -} - /* * scsi_unregister_device_handler - register a device handler personality * module. @@ -95,23 +385,26 @@ static int scsi_dh_notifier_remove(struct device *dev, void *data) */ int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) { - int ret = -ENODEV; - struct scsi_device_handler *tmp; - - tmp = get_device_handler(scsi_dh->name); - if (!tmp) - goto done; + struct scsi_dh_devinfo_list *tmp, *pos; - ret = bus_unregister_notifier(&scsi_bus_type, &scsi_dh->nb); + if (!get_device_handler(scsi_dh->name)) + return -ENODEV; bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, - scsi_dh_notifier_remove); + scsi_dh_notifier_remove); + spin_lock(&list_lock); list_del(&scsi_dh->list); + list_for_each_entry_safe(pos, tmp, &scsi_dh_dev_list, node) { + if (pos->handler == scsi_dh) { + list_del(&pos->node); + kfree(pos); + } + } spin_unlock(&list_lock); + printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name); -done: - return ret; + return SCSI_DH_OK; } EXPORT_SYMBOL_GPL(scsi_unregister_device_handler); @@ -157,6 +450,97 @@ int scsi_dh_handler_exist(const char *name) } EXPORT_SYMBOL_GPL(scsi_dh_handler_exist); +/* + * scsi_dh_handler_attach - Attach device handler + * @sdev - sdev the handler should be attached to + * @name - name of the handler to attach + */ +int scsi_dh_attach(struct request_queue *q, const char *name) +{ + unsigned long flags; + struct scsi_device *sdev; + struct scsi_device_handler *scsi_dh; + int err = 0; + + scsi_dh = get_device_handler(name); + if (!scsi_dh) + return -EINVAL; + + spin_lock_irqsave(q->queue_lock, flags); + sdev = q->queuedata; + if (!sdev || !get_device(&sdev->sdev_gendev)) + err = -ENODEV; + spin_unlock_irqrestore(q->queue_lock, flags); + + if (!err) { + err = scsi_dh_handler_attach(sdev, scsi_dh); + + put_device(&sdev->sdev_gendev); + } + return err; +} +EXPORT_SYMBOL_GPL(scsi_dh_attach); + +/* + * scsi_dh_handler_detach - Detach device handler + * @sdev - sdev the handler should be detached from + * + * This function will detach the device handler only + * if the sdev is not part of the internal list, ie + * if it has been attached manually. + */ +void scsi_dh_detach(struct request_queue *q) +{ + unsigned long flags; + struct scsi_device *sdev; + struct scsi_device_handler *scsi_dh = NULL; + + spin_lock_irqsave(q->queue_lock, flags); + sdev = q->queuedata; + if (!sdev || !get_device(&sdev->sdev_gendev)) + sdev = NULL; + spin_unlock_irqrestore(q->queue_lock, flags); + + if (!sdev) + return; + + if (sdev->scsi_dh_data) { + /* if sdev is not on internal list, detach */ + scsi_dh = sdev->scsi_dh_data->scsi_dh; + if (!device_handler_match(scsi_dh, sdev)) + scsi_dh_handler_detach(sdev, scsi_dh); + } + put_device(&sdev->sdev_gendev); +} +EXPORT_SYMBOL_GPL(scsi_dh_detach); + +static struct notifier_block scsi_dh_nb = { + .notifier_call = scsi_dh_notifier +}; + +static int __init scsi_dh_init(void) +{ + int r; + + r = bus_register_notifier(&scsi_bus_type, &scsi_dh_nb); + + if (!r) + bus_for_each_dev(&scsi_bus_type, NULL, NULL, + scsi_dh_sysfs_attr_add); + + return r; +} + +static void __exit scsi_dh_exit(void) +{ + bus_for_each_dev(&scsi_bus_type, NULL, NULL, + scsi_dh_sysfs_attr_remove); + bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb); +} + +module_init(scsi_dh_init); +module_exit(scsi_dh_exit); + MODULE_DESCRIPTION("SCSI device handler"); MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c new file mode 100644 index 000000000000..fcdd73f25625 --- /dev/null +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -0,0 +1,802 @@ +/* + * Generic SCSI-3 ALUA SCSI Device Handler + * + * Copyright (C) 2007, 2008 Hannes Reinecke, SUSE Linux Products GmbH. + * All rights reserved. + * + * 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 <scsi/scsi.h> +#include <scsi/scsi_eh.h> +#include <scsi/scsi_dh.h> + +#define ALUA_DH_NAME "alua" +#define ALUA_DH_VER "1.2" + +#define TPGS_STATE_OPTIMIZED 0x0 +#define TPGS_STATE_NONOPTIMIZED 0x1 +#define TPGS_STATE_STANDBY 0x2 +#define TPGS_STATE_UNAVAILABLE 0x3 +#define TPGS_STATE_OFFLINE 0xe +#define TPGS_STATE_TRANSITIONING 0xf + +#define TPGS_SUPPORT_NONE 0x00 +#define TPGS_SUPPORT_OPTIMIZED 0x01 +#define TPGS_SUPPORT_NONOPTIMIZED 0x02 +#define TPGS_SUPPORT_STANDBY 0x04 +#define TPGS_SUPPORT_UNAVAILABLE 0x08 +#define TPGS_SUPPORT_OFFLINE 0x40 +#define TPGS_SUPPORT_TRANSITION 0x80 + +#define TPGS_MODE_UNINITIALIZED -1 +#define TPGS_MODE_NONE 0x0 +#define TPGS_MODE_IMPLICIT 0x1 +#define TPGS_MODE_EXPLICIT 0x2 + +#define ALUA_INQUIRY_SIZE 36 +#define ALUA_FAILOVER_TIMEOUT (60 * HZ) +#define ALUA_FAILOVER_RETRIES 5 + +struct alua_dh_data { + int group_id; + int rel_port; + int tpgs; + int state; + unsigned char inq[ALUA_INQUIRY_SIZE]; + unsigned char *buff; + int bufflen; + unsigned char sense[SCSI_SENSE_BUFFERSIZE]; + int senselen; +}; + +#define ALUA_POLICY_SWITCH_CURRENT 0 +#define ALUA_POLICY_SWITCH_ALL 1 + +static inline struct alua_dh_data *get_alua_data(struct scsi_device *sdev) +{ + struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data; + BUG_ON(scsi_dh_data == NULL); + return ((struct alua_dh_data *) scsi_dh_data->buf); +} + +static int realloc_buffer(struct alua_dh_data *h, unsigned len) +{ + if (h->buff && h->buff != h->inq) + kfree(h->buff); + + h->buff = kmalloc(len, GFP_NOIO); + if (!h->buff) { + h->buff = h->inq; + h->bufflen = ALUA_INQUIRY_SIZE; + return 1; + } + h->bufflen = len; + return 0; +} + +static struct request *get_alua_req(struct scsi_device *sdev, + void *buffer, unsigned buflen, int rw) +{ + struct request *rq; + struct request_queue *q = sdev->request_queue; + + rq = blk_get_request(q, rw, GFP_NOIO); + + if (!rq) { + sdev_printk(KERN_INFO, sdev, + "%s: blk_get_request failed\n", __func__); + return NULL; + } + + if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_NOIO)) { + blk_put_request(rq); + sdev_printk(KERN_INFO, sdev, + "%s: blk_rq_map_kern failed\n", __func__); + return NULL; + } + + rq->cmd_type = REQ_TYPE_BLOCK_PC; + rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE; + rq->retries = ALUA_FAILOVER_RETRIES; + rq->timeout = ALUA_FAILOVER_TIMEOUT; + + return rq; +} + +/* + * submit_std_inquiry - Issue a standard INQUIRY command + * @sdev: sdev the command should be send to + */ +static int submit_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h) +{ + struct request *rq; + int err = SCSI_DH_RES_TEMP_UNAVAIL; + + rq = get_alua_req(sdev, h->inq, ALUA_INQUIRY_SIZE, READ); + if (!rq) + goto done; + + /* Prepare the command. */ + rq->cmd[0] = INQUIRY; + rq->cmd[1] = 0; + rq->cmd[2] = 0; + rq->cmd[4] = ALUA_INQUIRY_SIZE; + rq->cmd_len = COMMAND_SIZE(INQUIRY); + + rq->sense = h->sense; + memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); + rq->sense_len = h->senselen = 0; + + err = blk_execute_rq(rq->q, NULL, rq, 1); + if (err == -EIO) { + sdev_printk(KERN_INFO, sdev, + "%s: std inquiry failed with %x\n", + ALUA_DH_NAME, rq->errors); + h->senselen = rq->sense_len; + err = SCSI_DH_IO; + } + blk_put_request(rq); +done: + return err; +} + +/* + * submit_vpd_inquiry - Issue an INQUIRY VPD page 0x83 command + * @sdev: sdev the command should be sent to + */ +static int submit_vpd_inquiry(struct scsi_device *sdev, struct alua_dh_data *h) +{ + struct request *rq; + int err = SCSI_DH_RES_TEMP_UNAVAIL; + + rq = get_alua_req(sdev, h->buff, h->bufflen, READ); + if (!rq) + goto done; + + /* Prepare the command. */ + rq->cmd[0] = INQUIRY; + rq->cmd[1] = 1; + rq->cmd[2] = 0x83; + rq->cmd[4] = h->bufflen; + rq->cmd_len = COMMAND_SIZE(INQUIRY); + + rq->sense = h->sense; + memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); + rq->sense_len = h->senselen = 0; + + err = blk_execute_rq(rq->q, NULL, rq, 1); + if (err == -EIO) { + sdev_printk(KERN_INFO, sdev, + "%s: evpd inquiry failed with %x\n", + ALUA_DH_NAME, rq->errors); + h->senselen = rq->sense_len; + err = SCSI_DH_IO; + } + blk_put_request(rq); +done: + return err; +} + +/* + * submit_rtpg - Issue a REPORT TARGET GROUP STATES command + * @sdev: sdev the command should be sent to + */ +static unsigned submit_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) +{ + struct request *rq; + int err = SCSI_DH_RES_TEMP_UNAVAIL; + + rq = get_alua_req(sdev, h->buff, h->bufflen, READ); + if (!rq) + goto done; + + /* Prepare the command. */ + rq->cmd[0] = MAINTENANCE_IN; + rq->cmd[1] = MI_REPORT_TARGET_PGS; + rq->cmd[6] = (h->bufflen >> 24) & 0xff; + rq->cmd[7] = (h->bufflen >> 16) & 0xff; + rq->cmd[8] = (h->bufflen >> 8) & 0xff; + rq->cmd[9] = h->bufflen & 0xff; + rq->cmd_len = COMMAND_SIZE(MAINTENANCE_IN); + + rq->sense = h->sense; + memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); + rq->sense_len = h->senselen = 0; + + err = blk_execute_rq(rq->q, NULL, rq, 1); + if (err == -EIO) { + sdev_printk(KERN_INFO, sdev, + "%s: rtpg failed with %x\n", + ALUA_DH_NAME, rq->errors); + h->senselen = rq->sense_len; + err = SCSI_DH_IO; + } + blk_put_request(rq); +done: + return err; +} + +/* + * submit_stpg - Issue a SET TARGET GROUP STATES command + * @sdev: sdev the command should be sent to + * + * Currently we're only setting the current target port group state + * to 'active/optimized' and let the array firmware figure out + * the states of the remaining groups. + */ +static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h) +{ + struct request *rq; + int err = SCSI_DH_RES_TEMP_UNAVAIL; + int stpg_len = 8; + + /* Prepare the data buffer */ + memset(h->buff, 0, stpg_len); + h->buff[4] = TPGS_STATE_OPTIMIZED & 0x0f; + h->buff[6] = (h->group_id >> 8) & 0x0f; + h->buff[7] = h->group_id & 0x0f; + + rq = get_alua_req(sdev, h->buff, stpg_len, WRITE); + if (!rq) + goto done; + + /* Prepare the command. */ + rq->cmd[0] = MAINTENANCE_OUT; + rq->cmd[1] = MO_SET_TARGET_PGS; + rq->cmd[6] = (stpg_len >> 24) & 0xff; + rq->cmd[7] = (stpg_len >> 16) & 0xff; + rq->cmd[8] = (stpg_len >> 8) & 0xff; + rq->cmd[9] = stpg_len & 0xff; + rq->cmd_len = COMMAND_SIZE(MAINTENANCE_OUT); + + rq->sense = h->sense; + memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); + rq->sense_len = h->senselen = 0; + + err = blk_execute_rq(rq->q, NULL, rq, 1); + if (err == -EIO) { + sdev_printk(KERN_INFO, sdev, + "%s: stpg failed with %x\n", + ALUA_DH_NAME, rq->errors); + h->senselen = rq->sense_len; + err = SCSI_DH_IO; + } + blk_put_request(rq); +done: + return err; +} + +/* + * alua_std_inquiry - Evaluate standard INQUIRY command + * @sdev: device to be checked + * + * Just extract the TPGS setting to find out if ALUA + * is supported. + */ +static int alua_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h) +{ + int err; + + err = submit_std_inquiry(sdev, h); + + if (err != SCSI_DH_OK) + return err; + + /* Check TPGS setting */ + h->tpgs = (h->inq[5] >> 4) & 0x3; + switch (h->tpgs) { + case TPGS_MODE_EXPLICIT|TPGS_MODE_IMPLICIT: + sdev_printk(KERN_INFO, sdev, + "%s: supports implicit and explicit TPGS\n", + ALUA_DH_NAME); + break; + case TPGS_MODE_EXPLICIT: + sdev_printk(KERN_INFO, sdev, "%s: supports explicit TPGS\n", + ALUA_DH_NAME); + break; + case TPGS_MODE_IMPLICIT: + sdev_printk(KERN_INFO, sdev, "%s: supports implicit TPGS\n", + ALUA_DH_NAME); + break; + default: + h->tpgs = TPGS_MODE_NONE; + sdev_printk(KERN_INFO, sdev, "%s: not supported\n", + ALUA_DH_NAME); + err = SCSI_DH_DEV_UNSUPP; + break; + } + + return err; +} + +/* + * alua_vpd_inquiry - Evaluate INQUIRY vpd page 0x83 + * @sdev: device to be checked + * + * Extract the relative target port and the target port group + * descriptor from the list of identificators. + */ +static int alua_vpd_inquiry(struct scsi_device *sdev, struct alua_dh_data *h) +{ + int len; + unsigned err; + unsigned char *d; + + retry: + err = submit_vpd_inquiry(sdev, h); + + if (err != SCSI_DH_OK) + return err; + + /* Check if vpd page exceeds initial buffer */ + len = (h->buff[2] << 8) + h->buff[3] + 4; + if (len > h->bufflen) { + /* Resubmit with the correct length */ + if (realloc_buffer(h, len)) { + sdev_printk(KERN_WARNING, sdev, + "%s: kmalloc buffer failed\n", + ALUA_DH_NAME); + /* Temporary failure, bypass */ + return SCSI_DH_DEV_TEMP_BUSY; + } + goto retry; + } + + /* + * Now look for the correct descriptor. + */ + d = h->buff + 4; + while (d < h->buff + len) { + switch (d[1] & 0xf) { + case 0x4: + /* Relative target port */ + h->rel_port = (d[6] << 8) + d[7]; + break; + case 0x5: + /* Target port group */ + h->group_id = (d[6] << 8) + d[7]; + break; + default: + break; + } + d += d[3] + 4; + } + + if (h->group_id == -1) { + /* + * Internal error; TPGS supported but required + * VPD identification descriptors not present. + * Disable ALUA support + */ + sdev_printk(KERN_INFO, sdev, + "%s: No target port descriptors found\n", + ALUA_DH_NAME); + h->state = TPGS_STATE_OPTIMIZED; + h->tpgs = TPGS_MODE_NONE; + err = SCSI_DH_DEV_UNSUPP; + } else { + sdev_printk(KERN_INFO, sdev, + "%s: port group %02x rel port %02x\n", + ALUA_DH_NAME, h->group_id, h->rel_port); + } + + return err; +} + +static char print_alua_state(int state) +{ + switch (state) { + case TPGS_STATE_OPTIMIZED: + return 'A'; + case TPGS_STATE_NONOPTIMIZED: + return 'N'; + case TPGS_STATE_STANDBY: + return 'S'; + case TPGS_STATE_UNAVAILABLE: + return 'U'; + case TPGS_STATE_OFFLINE: + return 'O'; + case TPGS_STATE_TRANSITIONING: + return 'T'; + default: + return 'X'; + } +} + +static int alua_check_sense(struct scsi_device *sdev, + struct scsi_sense_hdr *sense_hdr) +{ + switch (sense_hdr->sense_key) { + case NOT_READY: + if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0a) + /* + * LUN Not Accessible - ALUA state transition + */ + return NEEDS_RETRY; + if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0b) + /* + * LUN Not Accessible -- Target port in standby state + */ + return SUCCESS; + if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0c) + /* + * LUN Not Accessible -- Target port in unavailable state + */ + return SUCCESS; + if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x12) + /* + * LUN Not Ready -- Offline + */ + return SUCCESS; + break; + case UNIT_ATTENTION: + if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00) + /* + * Power On, Reset, or Bus Device Reset, just retry. + */ + return NEEDS_RETRY; + if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x06) { + /* + * ALUA state changed + */ + return NEEDS_RETRY; + } + if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x07) { + /* + * Implicit ALUA state transition failed + */ + return NEEDS_RETRY; + } + break; + } + + return SCSI_RETURN_NOT_HANDLED; +} + +/* + * alua_stpg - Evaluate SET TARGET GROUP STATES + * @sdev: the device to be evaluated + * @state: the new target group state + * + * Send a SET TARGET GROUP STATES command to the device. + * We only have to test here if we should resubmit the command; + * any other error is assumed as a failure. + */ +static int alua_stpg(struct scsi_device *sdev, int state, + struct alua_dh_data *h) +{ + struct scsi_sense_hdr sense_hdr; + unsigned err; + int retry = ALUA_FAILOVER_RETRIES; + + retry: + err = submit_stpg(sdev, h); + if (err == SCSI_DH_IO && h->senselen > 0) { + err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, + &sense_hdr); + if (!err) + return SCSI_DH_IO; + err = alua_check_sense(sdev, &sense_hdr); + if (retry > 0 && err == NEEDS_RETRY) { + retry--; + goto retry; + } + sdev_printk(KERN_INFO, sdev, + "%s: stpg sense code: %02x/%02x/%02x\n", + ALUA_DH_NAME, sense_hdr.sense_key, + sense_hdr.asc, sense_hdr.ascq); + err = SCSI_DH_IO; + } + if (err == SCSI_DH_OK) { + h->state = state; + sdev_printk(KERN_INFO, sdev, + "%s: port group %02x switched to state %c\n", + ALUA_DH_NAME, h->group_id, + print_alua_state(h->state) ); + } + return err; +} + +/* + * alua_rtpg - Evaluate REPORT TARGET GROUP STATES + * @sdev: the device to be evaluated. + * + * Evaluate the Target Port Group State. + * Returns SCSI_DH_DEV_OFFLINED if the path is + * found to be unuseable. + */ +static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) +{ + struct scsi_sense_hdr sense_hdr; + int len, k, off, valid_states = 0; + char *ucp; + unsigned err; + + retry: + err = submit_rtpg(sdev, h); + + if (err == SCSI_DH_IO && h->senselen > 0) { + err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, + &sense_hdr); + if (!err) + return SCSI_DH_IO; + + err = alua_check_sense(sdev, &sense_hdr); + if (err == NEEDS_RETRY) + goto retry; + sdev_printk(KERN_INFO, sdev, + "%s: rtpg sense code %02x/%02x/%02x\n", + ALUA_DH_NAME, sense_hdr.sense_key, + sense_hdr.asc, sense_hdr.ascq); + err = SCSI_DH_IO; + } + if (err != SCSI_DH_OK) + return err; + + len = (h->buff[0] << 24) + (h->buff[1] << 16) + + (h->buff[2] << 8) + h->buff[3] + 4; + + if (len > h->bufflen) { + /* Resubmit with the correct length */ + if (realloc_buffer(h, len)) { + sdev_printk(KERN_WARNING, sdev, + "%s: kmalloc buffer failed\n",__func__); + /* Temporary failure, bypass */ + return SCSI_DH_DEV_TEMP_BUSY; + } + goto retry; + } + + for (k = 4, ucp = h->buff + 4; k < len; k += off, ucp += off) { + if (h->group_id == (ucp[2] << 8) + ucp[3]) { + h->state = ucp[0] & 0x0f; + valid_states = ucp[1]; + } + off = 8 + (ucp[7] * 4); + } + + sdev_printk(KERN_INFO, sdev, + "%s: port group %02x state %c supports %c%c%c%c%c%c\n", + ALUA_DH_NAME, h->group_id, print_alua_state(h->state), + valid_states&TPGS_SUPPORT_TRANSITION?'T':'t', + valid_states&TPGS_SUPPORT_OFFLINE?'O':'o', + valid_states&TPGS_SUPPORT_UNAVAILABLE?'U':'u', + valid_states&TPGS_SUPPORT_STANDBY?'S':'s', + valid_states&TPGS_SUPPORT_NONOPTIMIZED?'N':'n', + valid_states&TPGS_SUPPORT_OPTIMIZED?'A':'a'); + + if (h->tpgs & TPGS_MODE_EXPLICIT) { + switch (h->state) { + case TPGS_STATE_TRANSITIONING: + /* State transition, retry */ + goto retry; + break; + case TPGS_STATE_OFFLINE: + /* Path is offline, fail */ + err = SCSI_DH_DEV_OFFLINED; + break; + default: + break; + } + } else { + /* Only Implicit ALUA support */ + if (h->state == TPGS_STATE_OPTIMIZED || + h->state == TPGS_STATE_NONOPTIMIZED || + h->state == TPGS_STATE_STANDBY) + /* Useable path if active */ + err = SCSI_DH_OK; + else + /* Path unuseable for unavailable/offline */ + err = SCSI_DH_DEV_OFFLINED; + } + return err; +} + +/* + * alua_initialize - Initialize ALUA state + * @sdev: the device to be initialized + * + * For the prep_fn to work correctly we have + * to initialize the ALUA state for the device. + */ +static int alua_initialize(struct scsi_device *sdev, struct alua_dh_data *h) +{ + int err; + + err = alua_std_inquiry(sdev, h); + if (err != SCSI_DH_OK) + goto out; + + err = alua_vpd_inquiry(sdev, h); + if (err != SCSI_DH_OK) + goto out; + + err = alua_rtpg(sdev, h); + if (err != SCSI_DH_OK) + goto out; + +out: + return err; +} + +/* + * alua_activate - activate a path + * @sdev: device on the path to be activated + * + * We're currently switching the port group to be activated only and + * let the array figure out the rest. + * There may be other arrays which require us to switch all port groups + * based on a certain policy. But until we actually encounter them it + * should be okay. + */ +static int alua_activate(struct scsi_device *sdev) +{ + struct alua_dh_data *h = get_alua_data(sdev); + int err = SCSI_DH_OK; + + if (h->group_id != -1) { + err = alua_rtpg(sdev, h); + if (err != SCSI_DH_OK) + goto out; + } + + if (h->tpgs == TPGS_MODE_EXPLICIT && h->state != TPGS_STATE_OPTIMIZED) + err = alua_stpg(sdev, TPGS_STATE_OPTIMIZED, h); + +out: + return err; +} + +/* + * alua_prep_fn - request callback + * + * Fail I/O to all paths not in state + * active/optimized or active/non-optimized. + */ +static int alua_prep_fn(struct scsi_device *sdev, struct request *req) +{ + struct alua_dh_data *h = get_alua_data(sdev); + int ret = BLKPREP_OK; + + if (h->state != TPGS_STATE_OPTIMIZED && + h->state != TPGS_STATE_NONOPTIMIZED) { + ret = BLKPREP_KILL; + req->cmd_flags |= REQ_QUIET; + } + return ret; + +} + +const struct scsi_dh_devlist alua_dev_list[] = { + {"HP", "MSA VOLUME" }, + {"HP", "HSV101" }, + {"HP", "HSV111" }, + {"HP", "HSV200" }, + {"HP", "HSV210" }, + {"HP", "HSV300" }, + {"IBM", "2107900" }, + {"IBM", "2145" }, + {"Pillar", "Axiom" }, + {NULL, NULL} +}; + +static int alua_bus_attach(struct scsi_device *sdev); +static void alua_bus_detach(struct scsi_device *sdev); + +static struct scsi_device_handler alua_dh = { + .name = ALUA_DH_NAME, + .module = THIS_MODULE, + .devlist = alua_dev_list, + .attach = alua_bus_attach, + .detach = alua_bus_detach, + .prep_fn = alua_prep_fn, + .check_sense = alua_check_sense, + .activate = alua_activate, +}; + +/* + * alua_bus_attach - Attach device handler + * @sdev: device to be attached to + */ +static int alua_bus_attach(struct scsi_device *sdev) +{ + struct scsi_dh_data *scsi_dh_data; + struct alua_dh_data *h; + unsigned long flags; + int err = SCSI_DH_OK; + + scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) + + sizeof(*h) , GFP_KERNEL); + if (!scsi_dh_data) { + sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n", + ALUA_DH_NAME); + return -ENOMEM; + } + + scsi_dh_data->scsi_dh = &alua_dh; + h = (struct alua_dh_data *) scsi_dh_data->buf; + h->tpgs = TPGS_MODE_UNINITIALIZED; + h->state = TPGS_STATE_OPTIMIZED; + h->group_id = -1; + h->rel_port = -1; + h->buff = h->inq; + h->bufflen = ALUA_INQUIRY_SIZE; + + err = alua_initialize(sdev, h); + if (err != SCSI_DH_OK) + goto failed; + + if (!try_module_get(THIS_MODULE)) + goto failed; + + spin_lock_irqsave(sdev->request_queue->queue_lock, flags); + sdev->scsi_dh_data = scsi_dh_data; + spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); + + return 0; + +failed: + kfree(scsi_dh_data); + sdev_printk(KERN_ERR, sdev, "%s: not attached\n", ALUA_DH_NAME); + return -EINVAL; +} + +/* + * alua_bus_detach - Detach device handler + * @sdev: device to be detached from + */ +static void alua_bus_detach(struct scsi_device *sdev) +{ + struct scsi_dh_data *scsi_dh_data; + struct alua_dh_data *h; + unsigned long flags; + + spin_lock_irqsave(sdev->request_queue->queue_lock, flags); + scsi_dh_data = sdev->scsi_dh_data; + sdev->scsi_dh_data = NULL; + spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); + + h = (struct alua_dh_data *) scsi_dh_data->buf; + if (h->buff && h->inq != h->buff) + kfree(h->buff); + kfree(scsi_dh_data); + module_put(THIS_MODULE); + sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", ALUA_DH_NAME); +} + +static int __init alua_init(void) +{ + int r; + + r = scsi_register_device_handler(&alua_dh); + if (r != 0) + printk(KERN_ERR "%s: Failed to register scsi device handler", + ALUA_DH_NAME); + return r; +} + +static void __exit alua_exit(void) +{ + scsi_unregister_device_handler(&alua_dh); +} + +module_init(alua_init); +module_exit(alua_exit); + +MODULE_DESCRIPTION("DM Multipath ALUA support"); +MODULE_AUTHOR("Hannes Reinecke <hare@suse.de>"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(ALUA_DH_VER); diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c index f2467e936e55..aa46b131b20e 100644 --- a/drivers/scsi/device_handler/scsi_dh_emc.c +++ b/drivers/scsi/device_handler/scsi_dh_emc.c @@ -25,28 +25,31 @@ #include <scsi/scsi_dh.h> #include <scsi/scsi_device.h> -#define CLARIION_NAME "emc_clariion" +#define CLARIION_NAME "emc" #define CLARIION_TRESPASS_PAGE 0x22 -#define CLARIION_BUFFER_SIZE 0x80 +#define CLARIION_BUFFER_SIZE 0xFC #define CLARIION_TIMEOUT (60 * HZ) #define CLARIION_RETRIES 3 #define CLARIION_UNBOUND_LU -1 +#define CLARIION_SP_A 0 +#define CLARIION_SP_B 1 -static unsigned char long_trespass[] = { - 0, 0, 0, 0, - CLARIION_TRESPASS_PAGE, /* Page code */ - 0x09, /* Page length - 2 */ - 0x81, /* Trespass code + Honor reservation bit */ - 0xff, 0xff, /* Trespass target */ - 0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */ -}; +/* Flags */ +#define CLARIION_SHORT_TRESPASS 1 +#define CLARIION_HONOR_RESERVATIONS 2 -static unsigned char long_trespass_hr[] = { - 0, 0, 0, 0, +/* LUN states */ +#define CLARIION_LUN_UNINITIALIZED -1 +#define CLARIION_LUN_UNBOUND 0 +#define CLARIION_LUN_BOUND 1 +#define CLARIION_LUN_OWNED 2 + +static unsigned char long_trespass[] = { + 0, 0, 0, 0, 0, 0, 0, 0, CLARIION_TRESPASS_PAGE, /* Page code */ 0x09, /* Page length - 2 */ - 0x01, /* Trespass code + Honor reservation bit */ + 0x01, /* Trespass code */ 0xff, 0xff, /* Trespass target */ 0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */ }; @@ -55,39 +58,56 @@ static unsigned char short_trespass[] = { 0, 0, 0, 0, CLARIION_TRESPASS_PAGE, /* Page code */ 0x02, /* Page length - 2 */ - 0x81, /* Trespass code + Honor reservation bit */ + 0x01, /* Trespass code */ 0xff, /* Trespass target */ }; -static unsigned char short_trespass_hr[] = { - 0, 0, 0, 0, - CLARIION_TRESPASS_PAGE, /* Page code */ - 0x02, /* Page length - 2 */ - 0x01, /* Trespass code + Honor reservation bit */ - 0xff, /* Trespass target */ +static const char * lun_state[] = +{ + "not bound", + "bound", + "owned", }; struct clariion_dh_data { /* + * Flags: + * CLARIION_SHORT_TRESPASS * Use short trespass command (FC-series) or the long version * (default for AX/CX CLARiiON arrays). - */ - unsigned short_trespass; - /* + * + * CLARIION_HONOR_RESERVATIONS * Whether or not (default) to honor SCSI reservations when * initiating a switch-over. */ - unsigned hr; - /* I/O buffer for both MODE_SELECT and INQUIRY commands. */ + unsigned flags; + /* + * I/O buffer for both MODE_SELECT and INQUIRY commands. + */ char buffer[CLARIION_BUFFER_SIZE]; /* * SCSI sense buffer for commands -- assumes serial issuance * and completion sequence of all commands for same multipath. */ unsigned char sense[SCSI_SENSE_BUFFERSIZE]; - /* which SP (A=0,B=1,UNBOUND=-1) is dflt SP for path's mapped dev */ + unsigned int senselen; + /* + * LUN state + */ + int lun_state; + /* + * SP Port number + */ + int port; + /* + * which SP (A=0,B=1,UNBOUND=-1) is the default SP for this + * path's mapped LUN + */ int default_sp; - /* which SP (A=0,B=1,UNBOUND=-1) is active for path's mapped dev */ + /* + * which SP (A=0,B=1,UNBOUND=-1) is the active SP for this + * path's mapped LUN + */ int current_sp; }; @@ -102,19 +122,16 @@ static inline struct clariion_dh_data /* * Parse MODE_SELECT cmd reply. */ -static int trespass_endio(struct scsi_device *sdev, int result) +static int trespass_endio(struct scsi_device *sdev, char *sense) { - int err = SCSI_DH_OK; + int err = SCSI_DH_IO; struct scsi_sense_hdr sshdr; - struct clariion_dh_data *csdev = get_clariion_data(sdev); - char *sense = csdev->sense; - if (status_byte(result) == CHECK_CONDITION && - scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) { - sdev_printk(KERN_ERR, sdev, "Found valid sense data 0x%2x, " + if (!scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) { + sdev_printk(KERN_ERR, sdev, "%s: Found valid sense data 0x%2x, " "0x%2x, 0x%2x while sending CLARiiON trespass " - "command.\n", sshdr.sense_key, sshdr.asc, - sshdr.ascq); + "command.\n", CLARIION_NAME, sshdr.sense_key, + sshdr.asc, sshdr.ascq); if ((sshdr.sense_key == 0x05) && (sshdr.asc == 0x04) && (sshdr.ascq == 0x00)) { @@ -122,9 +139,9 @@ static int trespass_endio(struct scsi_device *sdev, int result) * Array based copy in progress -- do not send * mode_select or copy will be aborted mid-stream. */ - sdev_printk(KERN_INFO, sdev, "Array Based Copy in " + sdev_printk(KERN_INFO, sdev, "%s: Array Based Copy in " "progress while sending CLARiiON trespass " - "command.\n"); + "command.\n", CLARIION_NAME); err = SCSI_DH_DEV_TEMP_BUSY; } else if ((sshdr.sense_key == 0x02) && (sshdr.asc == 0x04) && (sshdr.ascq == 0x03)) { @@ -132,160 +149,153 @@ static int trespass_endio(struct scsi_device *sdev, int result) * LUN Not Ready - Manual Intervention Required * indicates in-progress ucode upgrade (NDU). */ - sdev_printk(KERN_INFO, sdev, "Detected in-progress " + sdev_printk(KERN_INFO, sdev, "%s: Detected in-progress " "ucode upgrade NDU operation while sending " - "CLARiiON trespass command.\n"); + "CLARiiON trespass command.\n", CLARIION_NAME); err = SCSI_DH_DEV_TEMP_BUSY; } else err = SCSI_DH_DEV_FAILED; - } else if (result) { - sdev_printk(KERN_ERR, sdev, "Error 0x%x while sending " - "CLARiiON trespass command.\n", result); - err = SCSI_DH_IO; + } else { + sdev_printk(KERN_INFO, sdev, + "%s: failed to send MODE SELECT, no sense available\n", + CLARIION_NAME); } - return err; } -static int parse_sp_info_reply(struct scsi_device *sdev, int result, - int *default_sp, int *current_sp, int *new_current_sp) +static int parse_sp_info_reply(struct scsi_device *sdev, + struct clariion_dh_data *csdev) { int err = SCSI_DH_OK; - struct clariion_dh_data *csdev = get_clariion_data(sdev); - if (result == 0) { - /* check for in-progress ucode upgrade (NDU) */ - if (csdev->buffer[48] != 0) { - sdev_printk(KERN_NOTICE, sdev, "Detected in-progress " - "ucode upgrade NDU operation while finding " - "current active SP."); - err = SCSI_DH_DEV_TEMP_BUSY; - } else { - *default_sp = csdev->buffer[5]; - - if (csdev->buffer[4] == 2) - /* SP for path is current */ - *current_sp = csdev->buffer[8]; - else { - if (csdev->buffer[4] == 1) - /* SP for this path is NOT current */ - if (csdev->buffer[8] == 0) - *current_sp = 1; - else - *current_sp = 0; - else - /* unbound LU or LUNZ */ - *current_sp = CLARIION_UNBOUND_LU; - } - *new_current_sp = csdev->buffer[8]; - } - } else { - struct scsi_sense_hdr sshdr; - - err = SCSI_DH_IO; - - if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE, - &sshdr)) - sdev_printk(KERN_ERR, sdev, "Found valid sense data " - "0x%2x, 0x%2x, 0x%2x while finding current " - "active SP.", sshdr.sense_key, sshdr.asc, - sshdr.ascq); - else - sdev_printk(KERN_ERR, sdev, "Error 0x%x finding " - "current active SP.", result); + /* check for in-progress ucode upgrade (NDU) */ + if (csdev->buffer[48] != 0) { + sdev_printk(KERN_NOTICE, sdev, "%s: Detected in-progress " + "ucode upgrade NDU operation while finding " + "current active SP.", CLARIION_NAME); + err = SCSI_DH_DEV_TEMP_BUSY; + goto out; + } + if (csdev->buffer[4] < 0 || csdev->buffer[4] > 2) { + /* Invalid buffer format */ + sdev_printk(KERN_NOTICE, sdev, + "%s: invalid VPD page 0xC0 format\n", + CLARIION_NAME); + err = SCSI_DH_NOSYS; + goto out; + } + switch (csdev->buffer[28] & 0x0f) { + case 6: + sdev_printk(KERN_NOTICE, sdev, + "%s: ALUA failover mode detected\n", + CLARIION_NAME); + break; + case 4: + /* Linux failover */ + break; + default: + sdev_printk(KERN_WARNING, sdev, + "%s: Invalid failover mode %d\n", + CLARIION_NAME, csdev->buffer[28] & 0x0f); + err = SCSI_DH_NOSYS; + goto out; } + csdev->default_sp = csdev->buffer[5]; + csdev->lun_state = csdev->buffer[4]; + csdev->current_sp = csdev->buffer[8]; + csdev->port = csdev->buffer[7]; + +out: return err; } -static int sp_info_endio(struct scsi_device *sdev, int result, - int mode_select_sent, int *done) +#define emc_default_str "FC (Legacy)" + +static char * parse_sp_model(struct scsi_device *sdev, unsigned char *buffer) { - struct clariion_dh_data *csdev = get_clariion_data(sdev); - int err_flags, default_sp, current_sp, new_current_sp; + unsigned char len = buffer[4] + 5; + char *sp_model = NULL; + unsigned char sp_len, serial_len; + + if (len < 160) { + sdev_printk(KERN_WARNING, sdev, + "%s: Invalid information section length %d\n", + CLARIION_NAME, len); + /* Check for old FC arrays */ + if (!strncmp(buffer + 8, "DGC", 3)) { + /* Old FC array, not supporting extended information */ + sp_model = emc_default_str; + } + goto out; + } - err_flags = parse_sp_info_reply(sdev, result, &default_sp, - ¤t_sp, &new_current_sp); + /* + * Parse extended information for SP model number + */ + serial_len = buffer[160]; + if (serial_len == 0 || serial_len + 161 > len) { + sdev_printk(KERN_WARNING, sdev, + "%s: Invalid array serial number length %d\n", + CLARIION_NAME, serial_len); + goto out; + } + sp_len = buffer[99]; + if (sp_len == 0 || serial_len + sp_len + 161 > len) { + sdev_printk(KERN_WARNING, sdev, + "%s: Invalid model number length %d\n", + CLARIION_NAME, sp_len); + goto out; + } + sp_model = &buffer[serial_len + 161]; + /* Strip whitespace at the end */ + while (sp_len > 1 && sp_model[sp_len - 1] == ' ') + sp_len--; - if (err_flags != SCSI_DH_OK) - goto done; + sp_model[sp_len] = '\0'; - if (mode_select_sent) { - csdev->default_sp = default_sp; - csdev->current_sp = current_sp; - } else { - /* - * Issue the actual module_selec request IFF either - * (1) we do not know the identity of the current SP OR - * (2) what we think we know is actually correct. - */ - if ((current_sp != CLARIION_UNBOUND_LU) && - (new_current_sp != current_sp)) { - - csdev->default_sp = default_sp; - csdev->current_sp = current_sp; - - sdev_printk(KERN_INFO, sdev, "Ignoring path group " - "switch-over command for CLARiiON SP%s since " - " mapped device is already initialized.", - current_sp ? "B" : "A"); - if (done) - *done = 1; /* as good as doing it */ - } - } -done: - return err_flags; +out: + return sp_model; } /* -* Get block request for REQ_BLOCK_PC command issued to path. Currently -* limited to MODE_SELECT (trespass) and INQUIRY (VPD page 0xC0) commands. -* -* Uses data and sense buffers in hardware handler context structure and -* assumes serial servicing of commands, both issuance and completion. -*/ -static struct request *get_req(struct scsi_device *sdev, int cmd) + * Get block request for REQ_BLOCK_PC command issued to path. Currently + * limited to MODE_SELECT (trespass) and INQUIRY (VPD page 0xC0) commands. + * + * Uses data and sense buffers in hardware handler context structure and + * assumes serial servicing of commands, both issuance and completion. + */ +static struct request *get_req(struct scsi_device *sdev, int cmd, + unsigned char *buffer) { - struct clariion_dh_data *csdev = get_clariion_data(sdev); struct request *rq; - unsigned char *page22; int len = 0; rq = blk_get_request(sdev->request_queue, - (cmd == MODE_SELECT) ? WRITE : READ, GFP_ATOMIC); + (cmd == MODE_SELECT) ? WRITE : READ, GFP_NOIO); if (!rq) { sdev_printk(KERN_INFO, sdev, "get_req: blk_get_request failed"); return NULL; } - memset(&rq->cmd, 0, BLK_MAX_CDB); + memset(rq->cmd, 0, BLK_MAX_CDB); + rq->cmd_len = COMMAND_SIZE(cmd); rq->cmd[0] = cmd; - rq->cmd_len = COMMAND_SIZE(rq->cmd[0]); switch (cmd) { case MODE_SELECT: - if (csdev->short_trespass) { - page22 = csdev->hr ? short_trespass_hr : short_trespass; - len = sizeof(short_trespass); - } else { - page22 = csdev->hr ? long_trespass_hr : long_trespass; - len = sizeof(long_trespass); - } - /* - * Can't DMA from kernel BSS -- must copy selected trespass - * command mode page contents to context buffer which is - * allocated by kmalloc. - */ - BUG_ON((len > CLARIION_BUFFER_SIZE)); - memcpy(csdev->buffer, page22, len); + len = sizeof(short_trespass); + rq->cmd_flags |= REQ_RW; + rq->cmd[1] = 0x10; + break; + case MODE_SELECT_10: + len = sizeof(long_trespass); rq->cmd_flags |= REQ_RW; rq->cmd[1] = 0x10; break; case INQUIRY: - rq->cmd[1] = 0x1; - rq->cmd[2] = 0xC0; len = CLARIION_BUFFER_SIZE; - memset(csdev->buffer, 0, CLARIION_BUFFER_SIZE); + memset(buffer, 0, len); break; default: BUG_ON(1); @@ -298,47 +308,94 @@ static struct request *get_req(struct scsi_device *sdev, int cmd) rq->timeout = CLARIION_TIMEOUT; rq->retries = CLARIION_RETRIES; - rq->sense = csdev->sense; - memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); - rq->sense_len = 0; - - if (blk_rq_map_kern(sdev->request_queue, rq, csdev->buffer, - len, GFP_ATOMIC)) { - __blk_put_request(rq->q, rq); + if (blk_rq_map_kern(rq->q, rq, buffer, len, GFP_NOIO)) { + blk_put_request(rq); return NULL; } return rq; } -static int send_cmd(struct scsi_device *sdev, int cmd) +static int send_inquiry_cmd(struct scsi_device *sdev, int page, + struct clariion_dh_data *csdev) { - struct request *rq = get_req(sdev, cmd); + struct request *rq = get_req(sdev, INQUIRY, csdev->buffer); + int err; if (!rq) return SCSI_DH_RES_TEMP_UNAVAIL; - return blk_execute_rq(sdev->request_queue, NULL, rq, 1); + rq->sense = csdev->sense; + memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); + rq->sense_len = csdev->senselen = 0; + + rq->cmd[0] = INQUIRY; + if (page != 0) { + rq->cmd[1] = 1; + rq->cmd[2] = page; + } + err = blk_execute_rq(sdev->request_queue, NULL, rq, 1); + if (err == -EIO) { + sdev_printk(KERN_INFO, sdev, + "%s: failed to send %s INQUIRY: %x\n", + CLARIION_NAME, page?"EVPD":"standard", + rq->errors); + csdev->senselen = rq->sense_len; + err = SCSI_DH_IO; + } + + blk_put_request(rq); + + return err; } -static int clariion_activate(struct scsi_device *sdev) +static int send_trespass_cmd(struct scsi_device *sdev, + struct clariion_dh_data *csdev) { - int result, done = 0; + struct request *rq; + unsigned char *page22; + int err, len, cmd; + + if (csdev->flags & CLARIION_SHORT_TRESPASS) { + page22 = short_trespass; + if (!(csdev->flags & CLARIION_HONOR_RESERVATIONS)) + /* Set Honor Reservations bit */ + page22[6] |= 0x80; + len = sizeof(short_trespass); + cmd = MODE_SELECT; + } else { + page22 = long_trespass; + if (!(csdev->flags & CLARIION_HONOR_RESERVATIONS)) + /* Set Honor Reservations bit */ + page22[10] |= 0x80; + len = sizeof(long_trespass); + cmd = MODE_SELECT_10; + } + BUG_ON((len > CLARIION_BUFFER_SIZE)); + memcpy(csdev->buffer, page22, len); - result = send_cmd(sdev, INQUIRY); - result = sp_info_endio(sdev, result, 0, &done); - if (result || done) - goto done; + rq = get_req(sdev, cmd, csdev->buffer); + if (!rq) + return SCSI_DH_RES_TEMP_UNAVAIL; - result = send_cmd(sdev, MODE_SELECT); - result = trespass_endio(sdev, result); - if (result) - goto done; + rq->sense = csdev->sense; + memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); + rq->sense_len = csdev->senselen = 0; - result = send_cmd(sdev, INQUIRY); - result = sp_info_endio(sdev, result, 1, NULL); -done: - return result; + err = blk_execute_rq(sdev->request_queue, NULL, rq, 1); + if (err == -EIO) { + if (rq->sense_len) { + err = trespass_endio(sdev, csdev->sense); + } else { + sdev_printk(KERN_INFO, sdev, + "%s: failed to send MODE SELECT: %x\n", + CLARIION_NAME, rq->errors); + } + } + + blk_put_request(rq); + + return err; } static int clariion_check_sense(struct scsi_device *sdev, @@ -386,99 +443,215 @@ static int clariion_check_sense(struct scsi_device *sdev, break; } - /* success just means we do not care what scsi-ml does */ - return SUCCESS; + return SCSI_RETURN_NOT_HANDLED; +} + +static int clariion_prep_fn(struct scsi_device *sdev, struct request *req) +{ + struct clariion_dh_data *h = get_clariion_data(sdev); + int ret = BLKPREP_OK; + + if (h->lun_state != CLARIION_LUN_OWNED) { + ret = BLKPREP_KILL; + req->cmd_flags |= REQ_QUIET; + } + return ret; + +} + +static int clariion_std_inquiry(struct scsi_device *sdev, + struct clariion_dh_data *csdev) +{ + int err; + char *sp_model; + + err = send_inquiry_cmd(sdev, 0, csdev); + if (err != SCSI_DH_OK && csdev->senselen) { + struct scsi_sense_hdr sshdr; + + if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE, + &sshdr)) { + sdev_printk(KERN_ERR, sdev, "%s: INQUIRY sense code " + "%02x/%02x/%02x\n", CLARIION_NAME, + sshdr.sense_key, sshdr.asc, sshdr.ascq); + } + err = SCSI_DH_IO; + goto out; + } + + sp_model = parse_sp_model(sdev, csdev->buffer); + if (!sp_model) { + err = SCSI_DH_DEV_UNSUPP; + goto out; + } + + /* + * FC Series arrays do not support long trespass + */ + if (!strlen(sp_model) || !strncmp(sp_model, "FC",2)) + csdev->flags |= CLARIION_SHORT_TRESPASS; + + sdev_printk(KERN_INFO, sdev, + "%s: detected Clariion %s, flags %x\n", + CLARIION_NAME, sp_model, csdev->flags); +out: + return err; } -static const struct { - char *vendor; - char *model; -} clariion_dev_list[] = { +static int clariion_send_inquiry(struct scsi_device *sdev, + struct clariion_dh_data *csdev) +{ + int err, retry = CLARIION_RETRIES; + +retry: + err = send_inquiry_cmd(sdev, 0xC0, csdev); + if (err != SCSI_DH_OK && csdev->senselen) { + struct scsi_sense_hdr sshdr; + + err = scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE, + &sshdr); + if (!err) + return SCSI_DH_IO; + + err = clariion_check_sense(sdev, &sshdr); + if (retry > 0 && err == NEEDS_RETRY) { + retry--; + goto retry; + } + sdev_printk(KERN_ERR, sdev, "%s: INQUIRY sense code " + "%02x/%02x/%02x\n", CLARIION_NAME, + sshdr.sense_key, sshdr.asc, sshdr.ascq); + err = SCSI_DH_IO; + } else { + err = parse_sp_info_reply(sdev, csdev); + } + return err; +} + +static int clariion_activate(struct scsi_device *sdev) +{ + struct clariion_dh_data *csdev = get_clariion_data(sdev); + int result; + + result = clariion_send_inquiry(sdev, csdev); + if (result != SCSI_DH_OK) + goto done; + + if (csdev->lun_state == CLARIION_LUN_OWNED) + goto done; + + result = send_trespass_cmd(sdev, csdev); + if (result != SCSI_DH_OK) + goto done; + sdev_printk(KERN_INFO, sdev,"%s: %s trespass command sent\n", + CLARIION_NAME, + csdev->flags&CLARIION_SHORT_TRESPASS?"short":"long" ); + + /* Update status */ + result = clariion_send_inquiry(sdev, csdev); + if (result != SCSI_DH_OK) + goto done; + +done: + sdev_printk(KERN_INFO, sdev, + "%s: at SP %c Port %d (%s, default SP %c)\n", + CLARIION_NAME, csdev->current_sp + 'A', + csdev->port, lun_state[csdev->lun_state], + csdev->default_sp + 'A'); + + return result; +} + +const struct scsi_dh_devlist clariion_dev_list[] = { {"DGC", "RAID"}, {"DGC", "DISK"}, + {"DGC", "VRAID"}, {NULL, NULL}, }; -static int clariion_bus_notify(struct notifier_block *, unsigned long, void *); +static int clariion_bus_attach(struct scsi_device *sdev); +static void clariion_bus_detach(struct scsi_device *sdev); static struct scsi_device_handler clariion_dh = { .name = CLARIION_NAME, .module = THIS_MODULE, - .nb.notifier_call = clariion_bus_notify, + .devlist = clariion_dev_list, + .attach = clariion_bus_attach, + .detach = clariion_bus_detach, .check_sense = clariion_check_sense, .activate = clariion_activate, + .prep_fn = clariion_prep_fn, }; /* * TODO: need some interface so we can set trespass values */ -static int clariion_bus_notify(struct notifier_block *nb, - unsigned long action, void *data) +static int clariion_bus_attach(struct scsi_device *sdev) { - struct device *dev = data; - struct scsi_device *sdev; struct scsi_dh_data *scsi_dh_data; struct clariion_dh_data *h; - int i, found = 0; unsigned long flags; + int err; - if (!scsi_is_sdev_device(dev)) - return 0; + scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) + + sizeof(*h) , GFP_KERNEL); + if (!scsi_dh_data) { + sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n", + CLARIION_NAME); + return -ENOMEM; + } - sdev = to_scsi_device(dev); + scsi_dh_data->scsi_dh = &clariion_dh; + h = (struct clariion_dh_data *) scsi_dh_data->buf; + h->lun_state = CLARIION_LUN_UNINITIALIZED; + h->default_sp = CLARIION_UNBOUND_LU; + h->current_sp = CLARIION_UNBOUND_LU; - if (action == BUS_NOTIFY_ADD_DEVICE) { - for (i = 0; clariion_dev_list[i].vendor; i++) { - if (!strncmp(sdev->vendor, clariion_dev_list[i].vendor, - strlen(clariion_dev_list[i].vendor)) && - !strncmp(sdev->model, clariion_dev_list[i].model, - strlen(clariion_dev_list[i].model))) { - found = 1; - break; - } - } - if (!found) - goto out; - - scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) - + sizeof(*h) , GFP_KERNEL); - if (!scsi_dh_data) { - sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n", - CLARIION_NAME); - goto out; - } + err = clariion_std_inquiry(sdev, h); + if (err != SCSI_DH_OK) + goto failed; - scsi_dh_data->scsi_dh = &clariion_dh; - h = (struct clariion_dh_data *) scsi_dh_data->buf; - h->default_sp = CLARIION_UNBOUND_LU; - h->current_sp = CLARIION_UNBOUND_LU; + err = clariion_send_inquiry(sdev, h); + if (err != SCSI_DH_OK) + goto failed; - spin_lock_irqsave(sdev->request_queue->queue_lock, flags); - sdev->scsi_dh_data = scsi_dh_data; - spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); + if (!try_module_get(THIS_MODULE)) + goto failed; - sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", CLARIION_NAME); - try_module_get(THIS_MODULE); + spin_lock_irqsave(sdev->request_queue->queue_lock, flags); + sdev->scsi_dh_data = scsi_dh_data; + spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); - } else if (action == BUS_NOTIFY_DEL_DEVICE) { - if (sdev->scsi_dh_data == NULL || - sdev->scsi_dh_data->scsi_dh != &clariion_dh) - goto out; + sdev_printk(KERN_INFO, sdev, + "%s: connected to SP %c Port %d (%s, default SP %c)\n", + CLARIION_NAME, h->current_sp + 'A', + h->port, lun_state[h->lun_state], + h->default_sp + 'A'); - spin_lock_irqsave(sdev->request_queue->queue_lock, flags); - scsi_dh_data = sdev->scsi_dh_data; - sdev->scsi_dh_data = NULL; - spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); + return 0; - sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n", - CLARIION_NAME); +failed: + kfree(scsi_dh_data); + sdev_printk(KERN_ERR, sdev, "%s: not attached\n", + CLARIION_NAME); + return -EINVAL; +} - kfree(scsi_dh_data); - module_put(THIS_MODULE); - } +static void clariion_bus_detach(struct scsi_device *sdev) +{ + struct scsi_dh_data *scsi_dh_data; + unsigned long flags; -out: - return 0; + spin_lock_irqsave(sdev->request_queue->queue_lock, flags); + scsi_dh_data = sdev->scsi_dh_data; + sdev->scsi_dh_data = NULL; + spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); + + sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", + CLARIION_NAME); + + kfree(scsi_dh_data); + module_put(THIS_MODULE); } static int __init clariion_init(void) @@ -487,7 +660,8 @@ static int __init clariion_init(void) r = scsi_register_device_handler(&clariion_dh); if (r != 0) - printk(KERN_ERR "Failed to register scsi device handler."); + printk(KERN_ERR "%s: Failed to register scsi device handler.", + CLARIION_NAME); return r; } diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c index ae6be87d6a83..9c7a1f8ebb72 100644 --- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c +++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c @@ -4,6 +4,7 @@ * * Copyright (C) 2006 Red Hat, Inc. All rights reserved. * Copyright (C) 2006 Mike Christie + * Copyright (C) 2008 Hannes Reinecke <hare@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 @@ -25,13 +26,18 @@ #include <scsi/scsi_eh.h> #include <scsi/scsi_dh.h> -#define HP_SW_NAME "hp_sw" +#define HP_SW_NAME "hp_sw" -#define HP_SW_TIMEOUT (60 * HZ) -#define HP_SW_RETRIES 3 +#define HP_SW_TIMEOUT (60 * HZ) +#define HP_SW_RETRIES 3 + +#define HP_SW_PATH_UNINITIALIZED -1 +#define HP_SW_PATH_ACTIVE 0 +#define HP_SW_PATH_PASSIVE 1 struct hp_sw_dh_data { unsigned char sense[SCSI_SENSE_BUFFERSIZE]; + int path_state; int retries; }; @@ -42,51 +48,161 @@ static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev) return ((struct hp_sw_dh_data *) scsi_dh_data->buf); } -static int hp_sw_done(struct scsi_device *sdev) +/* + * tur_done - Handle TEST UNIT READY return status + * @sdev: sdev the command has been sent to + * @errors: blk error code + * + * Returns SCSI_DH_DEV_OFFLINED if the sdev is on the passive path + */ +static int tur_done(struct scsi_device *sdev, unsigned char *sense) { - struct hp_sw_dh_data *h = get_hp_sw_data(sdev); struct scsi_sense_hdr sshdr; - int rc; - - sdev_printk(KERN_INFO, sdev, "hp_sw_done\n"); + int ret; - rc = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sshdr); - if (!rc) + ret = scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr); + if (!ret) { + sdev_printk(KERN_WARNING, sdev, + "%s: sending tur failed, no sense available\n", + HP_SW_NAME); + ret = SCSI_DH_IO; goto done; + } switch (sshdr.sense_key) { + case UNIT_ATTENTION: + ret = SCSI_DH_IMM_RETRY; + break; case NOT_READY: - if ((sshdr.asc == 0x04) && (sshdr.ascq == 3)) { - rc = SCSI_DH_RETRY; - h->retries++; + if ((sshdr.asc == 0x04) && (sshdr.ascq == 2)) { + /* + * LUN not ready - Initialization command required + * + * This is the passive path + */ + ret = SCSI_DH_DEV_OFFLINED; break; } - /* fall through */ + /* Fallthrough */ default: - h->retries++; - rc = SCSI_DH_IMM_RETRY; + sdev_printk(KERN_WARNING, sdev, + "%s: sending tur failed, sense %x/%x/%x\n", + HP_SW_NAME, sshdr.sense_key, sshdr.asc, + sshdr.ascq); + break; } done: - if (rc == SCSI_DH_OK || rc == SCSI_DH_IO) - h->retries = 0; - else if (h->retries > HP_SW_RETRIES) { - h->retries = 0; + return ret; +} + +/* + * hp_sw_tur - Send TEST UNIT READY + * @sdev: sdev command should be sent to + * + * Use the TEST UNIT READY command to determine + * the path state. + */ +static int hp_sw_tur(struct scsi_device *sdev, struct hp_sw_dh_data *h) +{ + struct request *req; + int ret; + + req = blk_get_request(sdev->request_queue, WRITE, GFP_NOIO); + if (!req) + return SCSI_DH_RES_TEMP_UNAVAIL; + + req->cmd_type = REQ_TYPE_BLOCK_PC; + req->cmd_flags |= REQ_FAILFAST; + req->cmd_len = COMMAND_SIZE(TEST_UNIT_READY); + memset(req->cmd, 0, MAX_COMMAND_SIZE); + req->cmd[0] = TEST_UNIT_READY; + req->timeout = HP_SW_TIMEOUT; + req->sense = h->sense; + memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE); + req->sense_len = 0; + +retry: + ret = blk_execute_rq(req->q, NULL, req, 1); + if (ret == -EIO) { + if (req->sense_len > 0) { + ret = tur_done(sdev, h->sense); + } else { + sdev_printk(KERN_WARNING, sdev, + "%s: sending tur failed with %x\n", + HP_SW_NAME, req->errors); + ret = SCSI_DH_IO; + } + } else { + h->path_state = HP_SW_PATH_ACTIVE; + ret = SCSI_DH_OK; + } + if (ret == SCSI_DH_IMM_RETRY) + goto retry; + if (ret == SCSI_DH_DEV_OFFLINED) { + h->path_state = HP_SW_PATH_PASSIVE; + ret = SCSI_DH_OK; + } + + blk_put_request(req); + + return ret; +} + +/* + * start_done - Handle START STOP UNIT return status + * @sdev: sdev the command has been sent to + * @errors: blk error code + */ +static int start_done(struct scsi_device *sdev, unsigned char *sense) +{ + struct scsi_sense_hdr sshdr; + int rc; + + rc = scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr); + if (!rc) { + sdev_printk(KERN_WARNING, sdev, + "%s: sending start_stop_unit failed, " + "no sense available\n", + HP_SW_NAME); + return SCSI_DH_IO; + } + switch (sshdr.sense_key) { + case NOT_READY: + if ((sshdr.asc == 0x04) && (sshdr.ascq == 3)) { + /* + * LUN not ready - manual intervention required + * + * Switch-over in progress, retry. + */ + rc = SCSI_DH_RETRY; + break; + } + /* fall through */ + default: + sdev_printk(KERN_WARNING, sdev, + "%s: sending start_stop_unit failed, sense %x/%x/%x\n", + HP_SW_NAME, sshdr.sense_key, sshdr.asc, + sshdr.ascq); rc = SCSI_DH_IO; } + return rc; } -static int hp_sw_activate(struct scsi_device *sdev) +/* + * hp_sw_start_stop - Send START STOP UNIT command + * @sdev: sdev command should be sent to + * + * Sending START STOP UNIT activates the SP. + */ +static int hp_sw_start_stop(struct scsi_device *sdev, struct hp_sw_dh_data *h) { - struct hp_sw_dh_data *h = get_hp_sw_data(sdev); struct request *req; - int ret = SCSI_DH_RES_TEMP_UNAVAIL; + int ret, retry; - req = blk_get_request(sdev->request_queue, WRITE, GFP_ATOMIC); + req = blk_get_request(sdev->request_queue, WRITE, GFP_NOIO); if (!req) - goto done; - - sdev_printk(KERN_INFO, sdev, "sending START_STOP."); + return SCSI_DH_RES_TEMP_UNAVAIL; req->cmd_type = REQ_TYPE_BLOCK_PC; req->cmd_flags |= REQ_FAILFAST; @@ -98,95 +214,153 @@ static int hp_sw_activate(struct scsi_device *sdev) req->sense = h->sense; memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE); req->sense_len = 0; + retry = h->retries; +retry: ret = blk_execute_rq(req->q, NULL, req, 1); - if (!ret) /* SUCCESS */ - ret = hp_sw_done(sdev); - else + if (ret == -EIO) { + if (req->sense_len > 0) { + ret = start_done(sdev, h->sense); + } else { + sdev_printk(KERN_WARNING, sdev, + "%s: sending start_stop_unit failed with %x\n", + HP_SW_NAME, req->errors); + ret = SCSI_DH_IO; + } + } else + ret = SCSI_DH_OK; + + if (ret == SCSI_DH_RETRY) { + if (--retry) + goto retry; ret = SCSI_DH_IO; -done: + } + + blk_put_request(req); + + return ret; +} + +static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req) +{ + struct hp_sw_dh_data *h = get_hp_sw_data(sdev); + int ret = BLKPREP_OK; + + if (h->path_state != HP_SW_PATH_ACTIVE) { + ret = BLKPREP_KILL; + req->cmd_flags |= REQ_QUIET; + } + return ret; + +} + +/* + * hp_sw_activate - Activate a path + * @sdev: sdev on the path to be activated + * + * The HP Active/Passive firmware is pretty simple; + * the passive path reports NOT READY with sense codes + * 0x04/0x02; a START STOP UNIT command will then + * activate the passive path (and deactivate the + * previously active one). + */ +static int hp_sw_activate(struct scsi_device *sdev) +{ + int ret = SCSI_DH_OK; + struct hp_sw_dh_data *h = get_hp_sw_data(sdev); + + ret = hp_sw_tur(sdev, h); + + if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE) { + ret = hp_sw_start_stop(sdev, h); + if (ret == SCSI_DH_OK) + sdev_printk(KERN_INFO, sdev, + "%s: activated path\n", + HP_SW_NAME); + } + return ret; } -static const struct { - char *vendor; - char *model; -} hp_sw_dh_data_list[] = { - {"COMPAQ", "MSA"}, - {"HP", "HSV"}, +const struct scsi_dh_devlist hp_sw_dh_data_list[] = { + {"COMPAQ", "MSA1000 VOLUME"}, + {"COMPAQ", "HSV110"}, + {"HP", "HSV100"}, {"DEC", "HSG80"}, {NULL, NULL}, }; -static int hp_sw_bus_notify(struct notifier_block *, unsigned long, void *); +static int hp_sw_bus_attach(struct scsi_device *sdev); +static void hp_sw_bus_detach(struct scsi_device *sdev); static struct scsi_device_handler hp_sw_dh = { .name = HP_SW_NAME, .module = THIS_MODULE, - .nb.notifier_call = hp_sw_bus_notify, + .devlist = hp_sw_dh_data_list, + .attach = hp_sw_bus_attach, + .detach = hp_sw_bus_detach, .activate = hp_sw_activate, + .prep_fn = hp_sw_prep_fn, }; -static int hp_sw_bus_notify(struct notifier_block *nb, - unsigned long action, void *data) +static int hp_sw_bus_attach(struct scsi_device *sdev) { - struct device *dev = data; - struct scsi_device *sdev; struct scsi_dh_data *scsi_dh_data; - int i, found = 0; + struct hp_sw_dh_data *h; unsigned long flags; + int ret; - if (!scsi_is_sdev_device(dev)) + scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) + + sizeof(struct hp_sw_dh_data) , GFP_KERNEL); + if (!scsi_dh_data) { + sdev_printk(KERN_ERR, sdev, "%s: Attach Failed\n", + HP_SW_NAME); return 0; + } - sdev = to_scsi_device(dev); - - if (action == BUS_NOTIFY_ADD_DEVICE) { - for (i = 0; hp_sw_dh_data_list[i].vendor; i++) { - if (!strncmp(sdev->vendor, hp_sw_dh_data_list[i].vendor, - strlen(hp_sw_dh_data_list[i].vendor)) && - !strncmp(sdev->model, hp_sw_dh_data_list[i].model, - strlen(hp_sw_dh_data_list[i].model))) { - found = 1; - break; - } - } - if (!found) - goto out; + scsi_dh_data->scsi_dh = &hp_sw_dh; + h = (struct hp_sw_dh_data *) scsi_dh_data->buf; + h->path_state = HP_SW_PATH_UNINITIALIZED; + h->retries = HP_SW_RETRIES; - scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) - + sizeof(struct hp_sw_dh_data) , GFP_KERNEL); - if (!scsi_dh_data) { - sdev_printk(KERN_ERR, sdev, "Attach Failed %s.\n", - HP_SW_NAME); - goto out; - } + ret = hp_sw_tur(sdev, h); + if (ret != SCSI_DH_OK || h->path_state == HP_SW_PATH_UNINITIALIZED) + goto failed; - scsi_dh_data->scsi_dh = &hp_sw_dh; - spin_lock_irqsave(sdev->request_queue->queue_lock, flags); - sdev->scsi_dh_data = scsi_dh_data; - spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); - try_module_get(THIS_MODULE); + if (!try_module_get(THIS_MODULE)) + goto failed; - sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", HP_SW_NAME); - } else if (action == BUS_NOTIFY_DEL_DEVICE) { - if (sdev->scsi_dh_data == NULL || - sdev->scsi_dh_data->scsi_dh != &hp_sw_dh) - goto out; + spin_lock_irqsave(sdev->request_queue->queue_lock, flags); + sdev->scsi_dh_data = scsi_dh_data; + spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); - spin_lock_irqsave(sdev->request_queue->queue_lock, flags); - scsi_dh_data = sdev->scsi_dh_data; - sdev->scsi_dh_data = NULL; - spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); - module_put(THIS_MODULE); + sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n", + HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE? + "active":"passive"); - sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n", HP_SW_NAME); + return 0; - kfree(scsi_dh_data); - } +failed: + kfree(scsi_dh_data); + sdev_printk(KERN_ERR, sdev, "%s: not attached\n", + HP_SW_NAME); + return -EINVAL; +} -out: - return 0; +static void hp_sw_bus_detach( struct scsi_device *sdev ) +{ + struct scsi_dh_data *scsi_dh_data; + unsigned long flags; + + spin_lock_irqsave(sdev->request_queue->queue_lock, flags); + scsi_dh_data = sdev->scsi_dh_data; + sdev->scsi_dh_data = NULL; + spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); + module_put(THIS_MODULE); + + sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", HP_SW_NAME); + + kfree(scsi_dh_data); } static int __init hp_sw_init(void) @@ -202,6 +376,6 @@ static void __exit hp_sw_exit(void) module_init(hp_sw_init); module_exit(hp_sw_exit); -MODULE_DESCRIPTION("HP MSA 1000"); +MODULE_DESCRIPTION("HP Active/Passive driver"); MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu"); MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c index fdf34b0ec6e1..b093a501f8ae 100644 --- a/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/drivers/scsi/device_handler/scsi_dh_rdac.c @@ -173,6 +173,11 @@ struct rdac_dh_data { #define RDAC_STATE_ACTIVE 0 #define RDAC_STATE_PASSIVE 1 unsigned char state; + +#define RDAC_LUN_UNOWNED 0 +#define RDAC_LUN_OWNED 1 +#define RDAC_LUN_AVT 2 + char lun_state; unsigned char sense[SCSI_SENSE_BUFFERSIZE]; union { struct c2_inquiry c2; @@ -182,6 +187,13 @@ struct rdac_dh_data { } inq; }; +static const char *lun_state[] = +{ + "unowned", + "owned", + "owned (AVT mode)", +}; + static LIST_HEAD(ctlr_list); static DEFINE_SPINLOCK(list_lock); @@ -197,9 +209,8 @@ static struct request *get_rdac_req(struct scsi_device *sdev, { struct request *rq; struct request_queue *q = sdev->request_queue; - struct rdac_dh_data *h = get_rdac_data(sdev); - rq = blk_get_request(q, rw, GFP_KERNEL); + rq = blk_get_request(q, rw, GFP_NOIO); if (!rq) { sdev_printk(KERN_INFO, sdev, @@ -207,17 +218,14 @@ static struct request *get_rdac_req(struct scsi_device *sdev, return NULL; } - if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_KERNEL)) { + if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_NOIO)) { blk_put_request(rq); sdev_printk(KERN_INFO, sdev, "get_rdac_req: blk_rq_map_kern failed.\n"); return NULL; } - memset(&rq->cmd, 0, BLK_MAX_CDB); - rq->sense = h->sense; - memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); - rq->sense_len = 0; + memset(rq->cmd, 0, BLK_MAX_CDB); rq->cmd_type = REQ_TYPE_BLOCK_PC; rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE; @@ -227,12 +235,12 @@ static struct request *get_rdac_req(struct scsi_device *sdev, return rq; } -static struct request *rdac_failover_get(struct scsi_device *sdev) +static struct request *rdac_failover_get(struct scsi_device *sdev, + struct rdac_dh_data *h) { struct request *rq; struct rdac_mode_common *common; unsigned data_size; - struct rdac_dh_data *h = get_rdac_data(sdev); if (h->ctlr->use_ms10) { struct rdac_pg_expanded *rdac_pg; @@ -277,6 +285,10 @@ static struct request *rdac_failover_get(struct scsi_device *sdev) } rq->cmd_len = COMMAND_SIZE(rq->cmd[0]); + rq->sense = h->sense; + memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); + rq->sense_len = 0; + return rq; } @@ -321,11 +333,10 @@ done: } static int submit_inquiry(struct scsi_device *sdev, int page_code, - unsigned int len) + unsigned int len, struct rdac_dh_data *h) { struct request *rq; struct request_queue *q = sdev->request_queue; - struct rdac_dh_data *h = get_rdac_data(sdev); int err = SCSI_DH_RES_TEMP_UNAVAIL; rq = get_rdac_req(sdev, &h->inq, len, READ); @@ -338,59 +349,68 @@ static int submit_inquiry(struct scsi_device *sdev, int page_code, rq->cmd[2] = page_code; rq->cmd[4] = len; rq->cmd_len = COMMAND_SIZE(INQUIRY); + + rq->sense = h->sense; + memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); + rq->sense_len = 0; + err = blk_execute_rq(q, NULL, rq, 1); if (err == -EIO) err = SCSI_DH_IO; + + blk_put_request(rq); done: return err; } -static int get_lun(struct scsi_device *sdev) +static int get_lun(struct scsi_device *sdev, struct rdac_dh_data *h) { int err; struct c8_inquiry *inqp; - struct rdac_dh_data *h = get_rdac_data(sdev); - err = submit_inquiry(sdev, 0xC8, sizeof(struct c8_inquiry)); + err = submit_inquiry(sdev, 0xC8, sizeof(struct c8_inquiry), h); if (err == SCSI_DH_OK) { inqp = &h->inq.c8; - h->lun = inqp->lun[7]; /* currently it uses only one byte */ + if (inqp->page_code != 0xc8) + return SCSI_DH_NOSYS; + if (inqp->page_id[0] != 'e' || inqp->page_id[1] != 'd' || + inqp->page_id[2] != 'i' || inqp->page_id[3] != 'd') + return SCSI_DH_NOSYS; + h->lun = scsilun_to_int((struct scsi_lun *)inqp->lun); } return err; } -#define RDAC_OWNED 0 -#define RDAC_UNOWNED 1 -#define RDAC_FAILED 2 -static int check_ownership(struct scsi_device *sdev) +static int check_ownership(struct scsi_device *sdev, struct rdac_dh_data *h) { int err; struct c9_inquiry *inqp; - struct rdac_dh_data *h = get_rdac_data(sdev); - err = submit_inquiry(sdev, 0xC9, sizeof(struct c9_inquiry)); + err = submit_inquiry(sdev, 0xC9, sizeof(struct c9_inquiry), h); if (err == SCSI_DH_OK) { - err = RDAC_UNOWNED; inqp = &h->inq.c9; - /* - * If in AVT mode or if the path already owns the LUN, - * return RDAC_OWNED; - */ - if (((inqp->avte_cvp >> 7) == 0x1) || - ((inqp->avte_cvp & 0x1) != 0)) - err = RDAC_OWNED; - } else - err = RDAC_FAILED; + if ((inqp->avte_cvp >> 7) == 0x1) { + /* LUN in AVT mode */ + sdev_printk(KERN_NOTICE, sdev, + "%s: AVT mode detected\n", + RDAC_NAME); + h->lun_state = RDAC_LUN_AVT; + } else if ((inqp->avte_cvp & 0x1) != 0) { + /* LUN was owned by the controller */ + h->lun_state = RDAC_LUN_OWNED; + } + } + return err; } -static int initialize_controller(struct scsi_device *sdev) +static int initialize_controller(struct scsi_device *sdev, + struct rdac_dh_data *h) { int err; struct c4_inquiry *inqp; - struct rdac_dh_data *h = get_rdac_data(sdev); - err = submit_inquiry(sdev, 0xC4, sizeof(struct c4_inquiry)); + err = submit_inquiry(sdev, 0xC4, sizeof(struct c4_inquiry), h); if (err == SCSI_DH_OK) { inqp = &h->inq.c4; h->ctlr = get_controller(inqp->subsys_id, inqp->slot_id); @@ -400,13 +420,12 @@ static int initialize_controller(struct scsi_device *sdev) return err; } -static int set_mode_select(struct scsi_device *sdev) +static int set_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h) { int err; struct c2_inquiry *inqp; - struct rdac_dh_data *h = get_rdac_data(sdev); - err = submit_inquiry(sdev, 0xC2, sizeof(struct c2_inquiry)); + err = submit_inquiry(sdev, 0xC2, sizeof(struct c2_inquiry), h); if (err == SCSI_DH_OK) { inqp = &h->inq.c2; /* @@ -421,13 +440,13 @@ static int set_mode_select(struct scsi_device *sdev) return err; } -static int mode_select_handle_sense(struct scsi_device *sdev) +static int mode_select_handle_sense(struct scsi_device *sdev, + unsigned char *sensebuf) { struct scsi_sense_hdr sense_hdr; - struct rdac_dh_data *h = get_rdac_data(sdev); int sense, err = SCSI_DH_IO, ret; - ret = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sense_hdr); + ret = scsi_normalize_sense(sensebuf, SCSI_SENSE_BUFFERSIZE, &sense_hdr); if (!ret) goto done; @@ -451,14 +470,13 @@ done: return err; } -static int send_mode_select(struct scsi_device *sdev) +static int send_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h) { struct request *rq; struct request_queue *q = sdev->request_queue; - struct rdac_dh_data *h = get_rdac_data(sdev); int err = SCSI_DH_RES_TEMP_UNAVAIL; - rq = rdac_failover_get(sdev); + rq = rdac_failover_get(sdev, h); if (!rq) goto done; @@ -466,9 +484,11 @@ static int send_mode_select(struct scsi_device *sdev) err = blk_execute_rq(q, NULL, rq, 1); if (err != SCSI_DH_OK) - err = mode_select_handle_sense(sdev); + err = mode_select_handle_sense(sdev, h->sense); if (err == SCSI_DH_OK) h->state = RDAC_STATE_ACTIVE; + + blk_put_request(rq); done: return err; } @@ -478,38 +498,23 @@ static int rdac_activate(struct scsi_device *sdev) struct rdac_dh_data *h = get_rdac_data(sdev); int err = SCSI_DH_OK; - if (h->lun == UNINITIALIZED_LUN) { - err = get_lun(sdev); - if (err != SCSI_DH_OK) - goto done; - } - - err = check_ownership(sdev); - switch (err) { - case RDAC_UNOWNED: - break; - case RDAC_OWNED: - err = SCSI_DH_OK; - goto done; - case RDAC_FAILED: - default: - err = SCSI_DH_IO; + err = check_ownership(sdev, h); + if (err != SCSI_DH_OK) goto done; - } if (!h->ctlr) { - err = initialize_controller(sdev); + err = initialize_controller(sdev, h); if (err != SCSI_DH_OK) goto done; } if (h->ctlr->use_ms10 == -1) { - err = set_mode_select(sdev); + err = set_mode_select(sdev, h); if (err != SCSI_DH_OK) goto done; } - - err = send_mode_select(sdev); + if (h->lun_state == RDAC_LUN_UNOWNED) + err = send_mode_select(sdev, h); done: return err; } @@ -569,10 +574,7 @@ static int rdac_check_sense(struct scsi_device *sdev, return SCSI_RETURN_NOT_HANDLED; } -static const struct { - char *vendor; - char *model; -} rdac_dev_list[] = { +const struct scsi_dh_devlist rdac_dev_list[] = { {"IBM", "1722"}, {"IBM", "1724"}, {"IBM", "1726"}, @@ -590,89 +592,89 @@ static const struct { {NULL, NULL}, }; -static int rdac_bus_notify(struct notifier_block *, unsigned long, void *); +static int rdac_bus_attach(struct scsi_device *sdev); +static void rdac_bus_detach(struct scsi_device *sdev); static struct scsi_device_handler rdac_dh = { .name = RDAC_NAME, .module = THIS_MODULE, - .nb.notifier_call = rdac_bus_notify, + .devlist = rdac_dev_list, .prep_fn = rdac_prep_fn, .check_sense = rdac_check_sense, + .attach = rdac_bus_attach, + .detach = rdac_bus_detach, .activate = rdac_activate, }; -/* - * TODO: need some interface so we can set trespass values - */ -static int rdac_bus_notify(struct notifier_block *nb, - unsigned long action, void *data) +static int rdac_bus_attach(struct scsi_device *sdev) { - struct device *dev = data; - struct scsi_device *sdev; struct scsi_dh_data *scsi_dh_data; struct rdac_dh_data *h; - int i, found = 0; unsigned long flags; + int err; - if (!scsi_is_sdev_device(dev)) + scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) + + sizeof(*h) , GFP_KERNEL); + if (!scsi_dh_data) { + sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n", + RDAC_NAME); return 0; + } - sdev = to_scsi_device(dev); - - if (action == BUS_NOTIFY_ADD_DEVICE) { - for (i = 0; rdac_dev_list[i].vendor; i++) { - if (!strncmp(sdev->vendor, rdac_dev_list[i].vendor, - strlen(rdac_dev_list[i].vendor)) && - !strncmp(sdev->model, rdac_dev_list[i].model, - strlen(rdac_dev_list[i].model))) { - found = 1; - break; - } - } - if (!found) - goto out; + scsi_dh_data->scsi_dh = &rdac_dh; + h = (struct rdac_dh_data *) scsi_dh_data->buf; + h->lun = UNINITIALIZED_LUN; + h->state = RDAC_STATE_ACTIVE; - scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) - + sizeof(*h) , GFP_KERNEL); - if (!scsi_dh_data) { - sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n", - RDAC_NAME); - goto out; - } + err = get_lun(sdev, h); + if (err != SCSI_DH_OK) + goto failed; - scsi_dh_data->scsi_dh = &rdac_dh; - h = (struct rdac_dh_data *) scsi_dh_data->buf; - h->lun = UNINITIALIZED_LUN; - h->state = RDAC_STATE_ACTIVE; - spin_lock_irqsave(sdev->request_queue->queue_lock, flags); - sdev->scsi_dh_data = scsi_dh_data; - spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); - try_module_get(THIS_MODULE); - - sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", RDAC_NAME); - - } else if (action == BUS_NOTIFY_DEL_DEVICE) { - if (sdev->scsi_dh_data == NULL || - sdev->scsi_dh_data->scsi_dh != &rdac_dh) - goto out; - - spin_lock_irqsave(sdev->request_queue->queue_lock, flags); - scsi_dh_data = sdev->scsi_dh_data; - sdev->scsi_dh_data = NULL; - spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); - - h = (struct rdac_dh_data *) scsi_dh_data->buf; - if (h->ctlr) - kref_put(&h->ctlr->kref, release_controller); - kfree(scsi_dh_data); - module_put(THIS_MODULE); - sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n", RDAC_NAME); - } + err = check_ownership(sdev, h); + if (err != SCSI_DH_OK) + goto failed; + + if (!try_module_get(THIS_MODULE)) + goto failed; + + spin_lock_irqsave(sdev->request_queue->queue_lock, flags); + sdev->scsi_dh_data = scsi_dh_data; + spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); + + sdev_printk(KERN_NOTICE, sdev, + "%s: LUN %d (%s)\n", + RDAC_NAME, h->lun, lun_state[(int)h->lun_state]); -out: return 0; + +failed: + kfree(scsi_dh_data); + sdev_printk(KERN_ERR, sdev, "%s: not attached\n", + RDAC_NAME); + return -EINVAL; +} + +static void rdac_bus_detach( struct scsi_device *sdev ) +{ + struct scsi_dh_data *scsi_dh_data; + struct rdac_dh_data *h; + unsigned long flags; + + spin_lock_irqsave(sdev->request_queue->queue_lock, flags); + scsi_dh_data = sdev->scsi_dh_data; + sdev->scsi_dh_data = NULL; + spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); + + h = (struct rdac_dh_data *) scsi_dh_data->buf; + if (h->ctlr) + kref_put(&h->ctlr->kref, release_controller); + kfree(scsi_dh_data); + module_put(THIS_MODULE); + sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", RDAC_NAME); } + + static int __init rdac_init(void) { int r; diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c index 2bc30e32b67a..1fe0901e8119 100644 --- a/drivers/scsi/dpt_i2o.c +++ b/drivers/scsi/dpt_i2o.c @@ -271,8 +271,8 @@ rebuild_sys_tab: pHba->initialized = TRUE; pHba->state &= ~DPTI_STATE_RESET; if (adpt_sysfs_class) { - struct device *dev = device_create(adpt_sysfs_class, - NULL, MKDEV(DPTI_I2O_MAJOR, pHba->unit), + struct device *dev = device_create_drvdata(adpt_sysfs_class, + NULL, MKDEV(DPTI_I2O_MAJOR, pHba->unit), NULL, "dpti%d", pHba->unit); if (IS_ERR(dev)) { printk(KERN_WARNING"dpti%d: unable to " diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 35cd892dce04..fed0b02ebc1d 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -232,8 +232,8 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev) } if (shost->transportt->create_work_queue) { - snprintf(shost->work_q_name, KOBJ_NAME_LEN, "scsi_wq_%d", - shost->host_no); + snprintf(shost->work_q_name, sizeof(shost->work_q_name), + "scsi_wq_%d", shost->host_no); shost->work_q = create_singlethread_workqueue( shost->work_q_name); if (!shost->work_q) { @@ -466,7 +466,8 @@ struct Scsi_Host *scsi_host_lookup(unsigned short hostnum) struct device *cdev; struct Scsi_Host *shost = ERR_PTR(-ENXIO); - cdev = class_find_device(&shost_class, &hostnum, __scsi_host_match); + cdev = class_find_device(&shost_class, NULL, &hostnum, + __scsi_host_match); if (cdev) { shost = scsi_host_get(class_to_shost(cdev)); put_device(cdev); diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index eb702b96d57c..ae560bc04f9d 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -521,9 +521,10 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost, static void ibmvfc_reinit_host(struct ibmvfc_host *vhost) { if (vhost->action == IBMVFC_HOST_ACTION_NONE) { - scsi_block_requests(vhost->host); - ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING); - ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY); + if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) { + scsi_block_requests(vhost->host); + ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY); + } } else vhost->reinit = 1; @@ -854,39 +855,41 @@ static void ibmvfc_retry_host_init(struct ibmvfc_host *vhost) } /** - * __ibmvfc_find_target - Find the specified scsi_target (no locking) + * __ibmvfc_get_target - Find the specified scsi_target (no locking) * @starget: scsi target struct * * Return value: * ibmvfc_target struct / NULL if not found **/ -static struct ibmvfc_target *__ibmvfc_find_target(struct scsi_target *starget) +static struct ibmvfc_target *__ibmvfc_get_target(struct scsi_target *starget) { struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); struct ibmvfc_host *vhost = shost_priv(shost); struct ibmvfc_target *tgt; list_for_each_entry(tgt, &vhost->targets, queue) - if (tgt->target_id == starget->id) + if (tgt->target_id == starget->id) { + kref_get(&tgt->kref); return tgt; + } return NULL; } /** - * ibmvfc_find_target - Find the specified scsi_target + * ibmvfc_get_target - Find the specified scsi_target * @starget: scsi target struct * * Return value: * ibmvfc_target struct / NULL if not found **/ -static struct ibmvfc_target *ibmvfc_find_target(struct scsi_target *starget) +static struct ibmvfc_target *ibmvfc_get_target(struct scsi_target *starget) { struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); struct ibmvfc_target *tgt; unsigned long flags; spin_lock_irqsave(shost->host_lock, flags); - tgt = __ibmvfc_find_target(starget); + tgt = __ibmvfc_get_target(starget); spin_unlock_irqrestore(shost->host_lock, flags); return tgt; } @@ -963,6 +966,9 @@ static void ibmvfc_get_host_port_state(struct Scsi_Host *shost) case IBMVFC_HALTED: fc_host_port_state(shost) = FC_PORTSTATE_BLOCKED; break; + case IBMVFC_NO_CRQ: + fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN; + break; default: ibmvfc_log(vhost, 3, "Unknown port state: %d\n", vhost->state); fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN; @@ -988,6 +994,17 @@ static void ibmvfc_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout) } /** + * ibmvfc_release_tgt - Free memory allocated for a target + * @kref: kref struct + * + **/ +static void ibmvfc_release_tgt(struct kref *kref) +{ + struct ibmvfc_target *tgt = container_of(kref, struct ibmvfc_target, kref); + kfree(tgt); +} + +/** * ibmvfc_get_starget_node_name - Get SCSI target's node name * @starget: scsi target struct * @@ -996,8 +1013,10 @@ static void ibmvfc_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout) **/ static void ibmvfc_get_starget_node_name(struct scsi_target *starget) { - struct ibmvfc_target *tgt = ibmvfc_find_target(starget); + struct ibmvfc_target *tgt = ibmvfc_get_target(starget); fc_starget_port_name(starget) = tgt ? tgt->ids.node_name : 0; + if (tgt) + kref_put(&tgt->kref, ibmvfc_release_tgt); } /** @@ -1009,8 +1028,10 @@ static void ibmvfc_get_starget_node_name(struct scsi_target *starget) **/ static void ibmvfc_get_starget_port_name(struct scsi_target *starget) { - struct ibmvfc_target *tgt = ibmvfc_find_target(starget); + struct ibmvfc_target *tgt = ibmvfc_get_target(starget); fc_starget_port_name(starget) = tgt ? tgt->ids.port_name : 0; + if (tgt) + kref_put(&tgt->kref, ibmvfc_release_tgt); } /** @@ -1022,8 +1043,10 @@ static void ibmvfc_get_starget_port_name(struct scsi_target *starget) **/ static void ibmvfc_get_starget_port_id(struct scsi_target *starget) { - struct ibmvfc_target *tgt = ibmvfc_find_target(starget); + struct ibmvfc_target *tgt = ibmvfc_get_target(starget); fc_starget_port_id(starget) = tgt ? tgt->scsi_id : -1; + if (tgt) + kref_put(&tgt->kref, ibmvfc_release_tgt); } /** @@ -1113,7 +1136,7 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost) login_info->max_cmds = max_requests + IBMVFC_NUM_INTERNAL_REQ; login_info->capabilities = IBMVFC_CAN_MIGRATE; login_info->async.va = vhost->async_crq.msg_token; - login_info->async.len = vhost->async_crq.size; + login_info->async.len = vhost->async_crq.size * sizeof(*vhost->async_crq.msgs); strncpy(login_info->partition_name, vhost->partition_name, IBMVFC_MAX_NAME); strncpy(login_info->device_name, vhost->host->shost_gendev.bus_id, IBMVFC_MAX_NAME); @@ -1404,7 +1427,7 @@ static void ibmvfc_log_error(struct ibmvfc_event *evt) err = cmd_status[index].name; } - if (!logerr && (vhost->log_level <= IBMVFC_DEFAULT_LOG_LEVEL)) + if (!logerr && (vhost->log_level <= (IBMVFC_DEFAULT_LOG_LEVEL + 1))) return; if (rsp->flags & FCP_RSP_LEN_VALID) @@ -2054,7 +2077,7 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq, { const char *desc = ibmvfc_get_ae_desc(crq->event); - ibmvfc_log(vhost, 2, "%s event received\n", desc); + ibmvfc_log(vhost, 3, "%s event received\n", desc); switch (crq->event) { case IBMVFC_AE_LINK_UP: @@ -2648,17 +2671,6 @@ static void ibmvfc_retry_tgt_init(struct ibmvfc_target *tgt, } /** - * ibmvfc_release_tgt - Free memory allocated for a target - * @kref: kref struct - * - **/ -static void ibmvfc_release_tgt(struct kref *kref) -{ - struct ibmvfc_target *tgt = container_of(kref, struct ibmvfc_target, kref); - kfree(tgt); -} - -/** * ibmvfc_tgt_prli_done - Completion handler for Process Login * @evt: ibmvfc event struct * @@ -2902,6 +2914,139 @@ static void ibmvfc_tgt_implicit_logout(struct ibmvfc_target *tgt) } /** + * ibmvfc_adisc_needs_plogi - Does device need PLOGI? + * @mad: ibmvfc passthru mad struct + * @tgt: ibmvfc target struct + * + * Returns: + * 1 if PLOGI needed / 0 if PLOGI not needed + **/ +static int ibmvfc_adisc_needs_plogi(struct ibmvfc_passthru_mad *mad, + struct ibmvfc_target *tgt) +{ + if (memcmp(&mad->fc_iu.response[2], &tgt->ids.port_name, + sizeof(tgt->ids.port_name))) + return 1; + if (memcmp(&mad->fc_iu.response[4], &tgt->ids.node_name, + sizeof(tgt->ids.node_name))) + return 1; + if (mad->fc_iu.response[6] != tgt->scsi_id) + return 1; + return 0; +} + +/** + * ibmvfc_tgt_adisc_done - Completion handler for ADISC + * @evt: ibmvfc event struct + * + **/ +static void ibmvfc_tgt_adisc_done(struct ibmvfc_event *evt) +{ + struct ibmvfc_target *tgt = evt->tgt; + struct ibmvfc_host *vhost = evt->vhost; + struct ibmvfc_passthru_mad *mad = &evt->xfer_iu->passthru; + u32 status = mad->common.status; + u8 fc_reason, fc_explain; + + vhost->discovery_threads--; + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); + + switch (status) { + case IBMVFC_MAD_SUCCESS: + tgt_dbg(tgt, "ADISC succeeded\n"); + if (ibmvfc_adisc_needs_plogi(mad, tgt)) + tgt->need_login = 1; + break; + case IBMVFC_MAD_DRIVER_FAILED: + break; + case IBMVFC_MAD_FAILED: + default: + tgt->need_login = 1; + fc_reason = (mad->fc_iu.response[1] & 0x00ff0000) >> 16; + fc_explain = (mad->fc_iu.response[1] & 0x0000ff00) >> 8; + tgt_info(tgt, "ADISC failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n", + ibmvfc_get_cmd_error(mad->iu.status, mad->iu.error), + mad->iu.status, mad->iu.error, + ibmvfc_get_fc_type(fc_reason), fc_reason, + ibmvfc_get_ls_explain(fc_explain), fc_explain, status); + break; + }; + + kref_put(&tgt->kref, ibmvfc_release_tgt); + ibmvfc_free_event(evt); + wake_up(&vhost->work_wait_q); +} + +/** + * ibmvfc_init_passthru - Initialize an event struct for FC passthru + * @evt: ibmvfc event struct + * + **/ +static void ibmvfc_init_passthru(struct ibmvfc_event *evt) +{ + struct ibmvfc_passthru_mad *mad = &evt->iu.passthru; + + memset(mad, 0, sizeof(*mad)); + mad->common.version = 1; + mad->common.opcode = IBMVFC_PASSTHRU; + mad->common.length = sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu); + mad->cmd_ioba.va = (u64)evt->crq.ioba + + offsetof(struct ibmvfc_passthru_mad, iu); + mad->cmd_ioba.len = sizeof(mad->iu); + mad->iu.cmd_len = sizeof(mad->fc_iu.payload); + mad->iu.rsp_len = sizeof(mad->fc_iu.response); + mad->iu.cmd.va = (u64)evt->crq.ioba + + offsetof(struct ibmvfc_passthru_mad, fc_iu) + + offsetof(struct ibmvfc_passthru_fc_iu, payload); + mad->iu.cmd.len = sizeof(mad->fc_iu.payload); + mad->iu.rsp.va = (u64)evt->crq.ioba + + offsetof(struct ibmvfc_passthru_mad, fc_iu) + + offsetof(struct ibmvfc_passthru_fc_iu, response); + mad->iu.rsp.len = sizeof(mad->fc_iu.response); +} + +/** + * ibmvfc_tgt_adisc - Initiate an ADISC for specified target + * @tgt: ibmvfc target struct + * + **/ +static void ibmvfc_tgt_adisc(struct ibmvfc_target *tgt) +{ + struct ibmvfc_passthru_mad *mad; + struct ibmvfc_host *vhost = tgt->vhost; + struct ibmvfc_event *evt; + + if (vhost->discovery_threads >= disc_threads) + return; + + kref_get(&tgt->kref); + evt = ibmvfc_get_event(vhost); + vhost->discovery_threads++; + ibmvfc_init_event(evt, ibmvfc_tgt_adisc_done, IBMVFC_MAD_FORMAT); + evt->tgt = tgt; + + ibmvfc_init_passthru(evt); + mad = &evt->iu.passthru; + mad->iu.flags = IBMVFC_FC_ELS; + mad->iu.scsi_id = tgt->scsi_id; + + mad->fc_iu.payload[0] = IBMVFC_ADISC; + memcpy(&mad->fc_iu.payload[2], &vhost->login_buf->resp.port_name, + sizeof(vhost->login_buf->resp.port_name)); + memcpy(&mad->fc_iu.payload[4], &vhost->login_buf->resp.node_name, + sizeof(vhost->login_buf->resp.node_name)); + mad->fc_iu.payload[6] = vhost->login_buf->resp.scsi_id & 0x00ffffff; + + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT); + if (ibmvfc_send_event(evt, vhost, default_timeout)) { + vhost->discovery_threads--; + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); + kref_put(&tgt->kref, ibmvfc_release_tgt); + } else + tgt_dbg(tgt, "Sent ADISC\n"); +} + +/** * ibmvfc_tgt_query_target_done - Completion handler for Query Target MAD * @evt: ibmvfc event struct * @@ -2921,6 +3066,8 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt) tgt->new_scsi_id = rsp->scsi_id; if (rsp->scsi_id != tgt->scsi_id) ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout); + else + ibmvfc_init_tgt(tgt, ibmvfc_tgt_adisc); break; case IBMVFC_MAD_DRIVER_FAILED: break; @@ -3336,6 +3483,7 @@ static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt) tgt_dbg(tgt, "rport add succeeded\n"); rport->maxframe_size = tgt->service_parms.common.bb_rcv_sz & 0x0fff; rport->supported_classes = 0; + tgt->target_id = rport->scsi_target_id; if (tgt->service_parms.class1_parms[0] & 0x80000000) rport->supported_classes |= FC_COS_CLASS1; if (tgt->service_parms.class2_parms[0] & 0x80000000) @@ -3525,7 +3673,7 @@ static int ibmvfc_init_crq(struct ibmvfc_host *vhost) crq->msg_token = dma_map_single(dev, crq->msgs, PAGE_SIZE, DMA_BIDIRECTIONAL); - if (dma_mapping_error(crq->msg_token)) + if (dma_mapping_error(dev, crq->msg_token)) goto map_failed; retrc = rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address, @@ -3618,7 +3766,7 @@ static int ibmvfc_alloc_mem(struct ibmvfc_host *vhost) async_q->size * sizeof(*async_q->msgs), DMA_BIDIRECTIONAL); - if (dma_mapping_error(async_q->msg_token)) { + if (dma_mapping_error(dev, async_q->msg_token)) { dev_err(dev, "Failed to map async queue\n"); goto free_async_crq; } @@ -3800,10 +3948,12 @@ static int ibmvfc_remove(struct vio_dev *vdev) ENTER; ibmvfc_remove_trace_file(&vhost->host->shost_dev.kobj, &ibmvfc_trace_attr); + ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE); + ibmvfc_wait_while_resetting(vhost); + ibmvfc_release_crq_queue(vhost); kthread_stop(vhost->work_thread); fc_remove_host(vhost->host); scsi_remove_host(vhost->host); - ibmvfc_release_crq_queue(vhost); spin_lock_irqsave(vhost->host->host_lock, flags); ibmvfc_purge_requests(vhost, DID_ERROR); @@ -3819,6 +3969,20 @@ static int ibmvfc_remove(struct vio_dev *vdev) return 0; } +/** + * ibmvfc_get_desired_dma - Calculate DMA resources needed by the driver + * @vdev: vio device struct + * + * Return value: + * Number of bytes the driver will need to DMA map at the same time in + * order to perform well. + */ +static unsigned long ibmvfc_get_desired_dma(struct vio_dev *vdev) +{ + unsigned long pool_dma = max_requests * sizeof(union ibmvfc_iu); + return pool_dma + ((512 * 1024) * driver_template.cmd_per_lun); +} + static struct vio_device_id ibmvfc_device_table[] __devinitdata = { {"fcp", "IBM,vfc-client"}, { "", "" } @@ -3829,6 +3993,7 @@ static struct vio_driver ibmvfc_driver = { .id_table = ibmvfc_device_table, .probe = ibmvfc_probe, .remove = ibmvfc_remove, + .get_desired_dma = ibmvfc_get_desired_dma, .driver = { .name = IBMVFC_NAME, .owner = THIS_MODULE, diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h index 057f3c01ed61..4bf6e374f076 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.h +++ b/drivers/scsi/ibmvscsi/ibmvfc.h @@ -29,8 +29,8 @@ #include "viosrp.h" #define IBMVFC_NAME "ibmvfc" -#define IBMVFC_DRIVER_VERSION "1.0.0" -#define IBMVFC_DRIVER_DATE "(July 1, 2008)" +#define IBMVFC_DRIVER_VERSION "1.0.1" +#define IBMVFC_DRIVER_DATE "(July 11, 2008)" #define IBMVFC_DEFAULT_TIMEOUT 15 #define IBMVFC_INIT_TIMEOUT 30 @@ -119,6 +119,7 @@ enum ibmvfc_mad_types { IBMVFC_PROCESS_LOGIN = 0x0008, IBMVFC_QUERY_TARGET = 0x0010, IBMVFC_IMPLICIT_LOGOUT = 0x0040, + IBMVFC_PASSTHRU = 0x0200, IBMVFC_TMF_MAD = 0x0100, }; @@ -439,6 +440,37 @@ struct ibmvfc_cmd { struct ibmvfc_fcp_rsp rsp; }__attribute__((packed, aligned (8))); +struct ibmvfc_passthru_fc_iu { + u32 payload[7]; +#define IBMVFC_ADISC 0x52000000 + u32 response[7]; +}; + +struct ibmvfc_passthru_iu { + u64 task_tag; + u32 cmd_len; + u32 rsp_len; + u16 status; + u16 error; + u32 flags; +#define IBMVFC_FC_ELS 0x01 + u32 cancel_key; + u32 reserved; + struct srp_direct_buf cmd; + struct srp_direct_buf rsp; + u64 correlation; + u64 scsi_id; + u64 tag; + u64 reserved2[2]; +}__attribute__((packed, aligned (8))); + +struct ibmvfc_passthru_mad { + struct ibmvfc_mad_common common; + struct srp_direct_buf cmd_ioba; + struct ibmvfc_passthru_iu iu; + struct ibmvfc_passthru_fc_iu fc_iu; +}__attribute__((packed, aligned (8))); + struct ibmvfc_trace_start_entry { u32 xfer_len; }__attribute__((packed)); @@ -531,6 +563,7 @@ union ibmvfc_iu { struct ibmvfc_implicit_logout implicit_logout; struct ibmvfc_tmf tmf; struct ibmvfc_cmd cmd; + struct ibmvfc_passthru_mad passthru; }__attribute__((packed, aligned (8))); enum ibmvfc_target_action { @@ -656,6 +689,9 @@ struct ibmvfc_host { #define tgt_dbg(t, fmt, ...) \ DBG_CMD(dev_info((t)->vhost->dev, "%lX: " fmt, (t)->scsi_id, ##__VA_ARGS__)) +#define tgt_info(t, fmt, ...) \ + dev_info((t)->vhost->dev, "%lX: " fmt, (t)->scsi_id, ##__VA_ARGS__) + #define tgt_err(t, fmt, ...) \ dev_err((t)->vhost->dev, "%lX: " fmt, (t)->scsi_id, ##__VA_ARGS__) @@ -668,8 +704,8 @@ struct ibmvfc_host { dev_err((vhost)->dev, ##__VA_ARGS__); \ } while (0) -#define ENTER DBG_CMD(printk(KERN_INFO IBMVFC_NAME": Entering %s\n", __FUNCTION__)) -#define LEAVE DBG_CMD(printk(KERN_INFO IBMVFC_NAME": Leaving %s\n", __FUNCTION__)) +#define ENTER DBG_CMD(printk(KERN_INFO IBMVFC_NAME": Entering %s\n", __func__)) +#define LEAVE DBG_CMD(printk(KERN_INFO IBMVFC_NAME": Leaving %s\n", __func__)) #ifdef CONFIG_SCSI_IBMVFC_TRACE #define ibmvfc_create_trace_file(kobj, attr) sysfs_create_bin_file(kobj, attr) diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index 5d23368a1bce..6b24b9cdb04c 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -72,6 +72,7 @@ #include <linux/delay.h> #include <asm/firmware.h> #include <asm/vio.h> +#include <asm/firmware.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_host.h> @@ -426,8 +427,10 @@ static int map_sg_data(struct scsi_cmnd *cmd, SG_ALL * sizeof(struct srp_direct_buf), &evt_struct->ext_list_token, 0); if (!evt_struct->ext_list) { - sdev_printk(KERN_ERR, cmd->device, - "Can't allocate memory for indirect table\n"); + if (!firmware_has_feature(FW_FEATURE_CMO)) + sdev_printk(KERN_ERR, cmd->device, + "Can't allocate memory " + "for indirect table\n"); return 0; } } @@ -743,7 +746,9 @@ static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd, srp_cmd->lun = ((u64) lun) << 48; if (!map_data_for_srp_cmd(cmnd, evt_struct, srp_cmd, hostdata->dev)) { - sdev_printk(KERN_ERR, cmnd->device, "couldn't convert cmd to srp_cmd\n"); + if (!firmware_has_feature(FW_FEATURE_CMO)) + sdev_printk(KERN_ERR, cmnd->device, + "couldn't convert cmd to srp_cmd\n"); free_event_struct(&hostdata->pool, evt_struct); return SCSI_MLQUEUE_HOST_BUSY; } @@ -854,8 +859,11 @@ static void send_mad_adapter_info(struct ibmvscsi_host_data *hostdata) sizeof(hostdata->madapter_info), DMA_BIDIRECTIONAL); - if (dma_mapping_error(req->buffer)) { - dev_err(hostdata->dev, "Unable to map request_buffer for adapter_info!\n"); + if (dma_mapping_error(hostdata->dev, req->buffer)) { + if (!firmware_has_feature(FW_FEATURE_CMO)) + dev_err(hostdata->dev, + "Unable to map request_buffer for " + "adapter_info!\n"); free_event_struct(&hostdata->pool, evt_struct); return; } @@ -1399,8 +1407,10 @@ static int ibmvscsi_do_host_config(struct ibmvscsi_host_data *hostdata, length, DMA_BIDIRECTIONAL); - if (dma_mapping_error(host_config->buffer)) { - dev_err(hostdata->dev, "dma_mapping error getting host config\n"); + if (dma_mapping_error(hostdata->dev, host_config->buffer)) { + if (!firmware_has_feature(FW_FEATURE_CMO)) + dev_err(hostdata->dev, + "dma_mapping error getting host config\n"); free_event_struct(&hostdata->pool, evt_struct); return -1; } @@ -1604,7 +1614,7 @@ static struct scsi_host_template driver_template = { .eh_host_reset_handler = ibmvscsi_eh_host_reset_handler, .slave_configure = ibmvscsi_slave_configure, .change_queue_depth = ibmvscsi_change_queue_depth, - .cmd_per_lun = 16, + .cmd_per_lun = IBMVSCSI_CMDS_PER_LUN_DEFAULT, .can_queue = IBMVSCSI_MAX_REQUESTS_DEFAULT, .this_id = -1, .sg_tablesize = SG_ALL, @@ -1613,6 +1623,26 @@ static struct scsi_host_template driver_template = { }; /** + * ibmvscsi_get_desired_dma - Calculate IO memory desired by the driver + * + * @vdev: struct vio_dev for the device whose desired IO mem is to be returned + * + * Return value: + * Number of bytes of IO data the driver will need to perform well. + */ +static unsigned long ibmvscsi_get_desired_dma(struct vio_dev *vdev) +{ + /* iu_storage data allocated in initialize_event_pool */ + unsigned long desired_io = max_requests * sizeof(union viosrp_iu); + + /* add io space for sg data */ + desired_io += (IBMVSCSI_MAX_SECTORS_DEFAULT * + IBMVSCSI_CMDS_PER_LUN_DEFAULT); + + return desired_io; +} + +/** * Called by bus code for each adapter */ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) @@ -1641,7 +1671,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) hostdata->host = host; hostdata->dev = dev; atomic_set(&hostdata->request_limit, -1); - hostdata->host->max_sectors = 32 * 8; /* default max I/O 32 pages */ + hostdata->host->max_sectors = IBMVSCSI_MAX_SECTORS_DEFAULT; rc = ibmvscsi_ops->init_crq_queue(&hostdata->queue, hostdata, max_requests); if (rc != 0 && rc != H_RESOURCE) { @@ -1735,6 +1765,7 @@ static struct vio_driver ibmvscsi_driver = { .id_table = ibmvscsi_device_table, .probe = ibmvscsi_probe, .remove = ibmvscsi_remove, + .get_desired_dma = ibmvscsi_get_desired_dma, .driver = { .name = "ibmvscsi", .owner = THIS_MODULE, diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.h b/drivers/scsi/ibmvscsi/ibmvscsi.h index 46e850e302c7..2d4339d5e16e 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.h +++ b/drivers/scsi/ibmvscsi/ibmvscsi.h @@ -45,6 +45,8 @@ struct Scsi_Host; #define MAX_INDIRECT_BUFS 10 #define IBMVSCSI_MAX_REQUESTS_DEFAULT 100 +#define IBMVSCSI_CMDS_PER_LUN_DEFAULT 16 +#define IBMVSCSI_MAX_SECTORS_DEFAULT 256 /* 32 * 8 = default max I/O 32 pages */ #define IBMVSCSI_MAX_CMDS_PER_LUN 64 /* ------------------------------------------------------------ diff --git a/drivers/scsi/ibmvscsi/ibmvstgt.c b/drivers/scsi/ibmvscsi/ibmvstgt.c index 3b9514c8f1f1..2a5b29d12172 100644 --- a/drivers/scsi/ibmvscsi/ibmvstgt.c +++ b/drivers/scsi/ibmvscsi/ibmvstgt.c @@ -55,7 +55,7 @@ /* tmp - will replace with SCSI logging stuff */ #define eprintk(fmt, args...) \ do { \ - printk("%s(%d) " fmt, __FUNCTION__, __LINE__, ##args); \ + printk("%s(%d) " fmt, __func__, __LINE__, ##args); \ } while (0) /* #define dprintk eprintk */ #define dprintk(fmt, args...) @@ -564,7 +564,7 @@ static int crq_queue_create(struct crq_queue *queue, struct srp_target *target) queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); - if (dma_mapping_error(queue->msg_token)) + if (dma_mapping_error(target->dev, queue->msg_token)) goto map_failed; err = h_reg_crq(vport->dma_dev->unit_address, queue->msg_token, diff --git a/drivers/scsi/ibmvscsi/rpa_vscsi.c b/drivers/scsi/ibmvscsi/rpa_vscsi.c index 182146100dc1..462a8574dad9 100644 --- a/drivers/scsi/ibmvscsi/rpa_vscsi.c +++ b/drivers/scsi/ibmvscsi/rpa_vscsi.c @@ -253,7 +253,7 @@ static int rpavscsi_init_crq_queue(struct crq_queue *queue, queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); - if (dma_mapping_error(queue->msg_token)) + if (dma_mapping_error(hostdata->dev, queue->msg_token)) goto map_failed; gather_partition_info(); diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index f843c1383a4b..b40a673985aa 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -84,7 +84,6 @@ typedef struct ide_scsi_obj { struct Scsi_Host *host; struct ide_atapi_pc *pc; /* Current packet command */ - unsigned long flags; /* Status/Action flags */ unsigned long transform; /* SCSI cmd translation layer */ unsigned long log; /* log flags */ } idescsi_scsi_t; @@ -102,8 +101,13 @@ static struct ide_scsi_obj *ide_scsi_get(struct gendisk *disk) mutex_lock(&idescsi_ref_mutex); scsi = ide_scsi_g(disk); - if (scsi) + if (scsi) { scsi_host_get(scsi->host); + if (ide_device_get(scsi->drive)) { + scsi_host_put(scsi->host); + scsi = NULL; + } + } mutex_unlock(&idescsi_ref_mutex); return scsi; } @@ -111,6 +115,7 @@ static struct ide_scsi_obj *ide_scsi_get(struct gendisk *disk) static void ide_scsi_put(struct ide_scsi_obj *scsi) { mutex_lock(&idescsi_ref_mutex); + ide_device_put(scsi->drive); scsi_host_put(scsi->host); mutex_unlock(&idescsi_ref_mutex); } @@ -126,23 +131,14 @@ static inline idescsi_scsi_t *drive_to_idescsi(ide_drive_t *ide_drive) } /* - * Per ATAPI device status bits. - */ -#define IDESCSI_DRQ_INTERRUPT 0 /* DRQ interrupt device */ - -/* - * ide-scsi requests. - */ -#define IDESCSI_PC_RQ 90 - -/* * PIO data transfer routine using the scatter gather table. */ static void ide_scsi_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, unsigned int bcount, int write) { ide_hwif_t *hwif = drive->hwif; - xfer_func_t *xf = write ? hwif->output_data : hwif->input_data; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; + xfer_func_t *xf = write ? tp_ops->output_data : tp_ops->input_data; char *buf; int count; @@ -211,15 +207,15 @@ static int idescsi_check_condition(ide_drive_t *drive, /* stuff a sense request in front of our current request */ pc = kzalloc(sizeof(struct ide_atapi_pc), GFP_ATOMIC); - rq = kmalloc(sizeof(struct request), GFP_ATOMIC); + rq = blk_get_request(drive->queue, READ, GFP_ATOMIC); buf = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_ATOMIC); if (!pc || !rq || !buf) { kfree(buf); - kfree(rq); + if (rq) + blk_put_request(rq); kfree(pc); return -ENOMEM; } - blk_rq_init(NULL, rq); rq->special = (char *) pc; pc->rq = rq; pc->buf = buf; @@ -228,7 +224,6 @@ static int idescsi_check_condition(ide_drive_t *drive, rq->cmd_type = REQ_TYPE_SENSE; rq->cmd_flags |= REQ_PREEMPT; pc->timeout = jiffies + WAIT_READY; - pc->callback = ide_scsi_callback; /* NOTE! Save the failed packet command in "rq->buffer" */ rq->buffer = (void *) failed_cmd->special; pc->scsi_cmd = ((struct ide_atapi_pc *) failed_cmd->special)->scsi_cmd; @@ -237,6 +232,8 @@ static int idescsi_check_condition(ide_drive_t *drive, ide_scsi_hex_dump(pc->c, 6); } rq->rq_disk = scsi->disk; + rq->ref_count++; + memcpy(rq->cmd, pc->c, 12); ide_do_drive_cmd(drive, rq); return 0; } @@ -246,10 +243,9 @@ idescsi_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err) { ide_hwif_t *hwif = drive->hwif; - if (ide_read_status(drive) & (BUSY_STAT | DRQ_STAT)) + if (hwif->tp_ops->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) /* force an abort */ - hwif->OUTBSYNC(hwif, WIN_IDLEIMMEDIATE, - hwif->io_ports.command_addr); + hwif->tp_ops->exec_command(hwif, WIN_IDLEIMMEDIATE); rq->errors++; @@ -283,7 +279,7 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs) SCSI_SENSE_BUFFERSIZE); kfree(pc->buf); kfree(pc); - kfree(rq); + blk_put_request(rq); pc = opc; rq = pc->rq; pc->scsi_cmd->result = (CHECK_CONDITION << 1) | @@ -314,7 +310,7 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs) pc->done(pc->scsi_cmd); spin_unlock_irqrestore(host->host_lock, flags); kfree(pc); - kfree(rq); + blk_put_request(rq); scsi->pc = NULL; return 0; } @@ -421,10 +417,6 @@ static ide_startstop_t idescsi_do_request (ide_drive_t *drive, struct request *r if (blk_sense_request(rq) || blk_special_request(rq)) { struct ide_atapi_pc *pc = (struct ide_atapi_pc *)rq->special; - idescsi_scsi_t *scsi = drive_to_idescsi(drive); - - if (test_bit(IDESCSI_DRQ_INTERRUPT, &scsi->flags)) - pc->flags |= PC_FLAG_DRQ_INTERRUPT; if (drive->using_dma && !idescsi_map_sg(drive, pc)) pc->flags |= PC_FLAG_DMA_OK; @@ -460,11 +452,14 @@ static inline void idescsi_add_settings(ide_drive_t *drive) { ; } static void idescsi_setup (ide_drive_t *drive, idescsi_scsi_t *scsi) { if (drive->id && (drive->id->config & 0x0060) == 0x20) - set_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags); + set_bit(IDE_AFLAG_DRQ_INTERRUPT, &drive->atapi_flags); clear_bit(IDESCSI_SG_TRANSFORM, &scsi->transform); #if IDESCSI_DEBUG_LOG set_bit(IDESCSI_LOG_CMD, &scsi->log); #endif /* IDESCSI_DEBUG_LOG */ + + drive->pc_callback = ide_scsi_callback; + idescsi_add_settings(drive); } @@ -589,6 +584,7 @@ static int idescsi_queue (struct scsi_cmnd *cmd, ide_drive_t *drive = scsi->drive; struct request *rq = NULL; struct ide_atapi_pc *pc = NULL; + int write = cmd->sc_data_direction == DMA_TO_DEVICE; if (!drive) { scmd_printk (KERN_ERR, cmd, "drive not present\n"); @@ -596,7 +592,7 @@ static int idescsi_queue (struct scsi_cmnd *cmd, } scsi = drive_to_idescsi(drive); pc = kmalloc(sizeof(struct ide_atapi_pc), GFP_ATOMIC); - rq = kmalloc(sizeof(struct request), GFP_ATOMIC); + rq = blk_get_request(drive->queue, write, GFP_ATOMIC); if (rq == NULL || pc == NULL) { printk (KERN_ERR "ide-scsi: %s: out of memory\n", drive->name); goto abort; @@ -616,7 +612,6 @@ static int idescsi_queue (struct scsi_cmnd *cmd, pc->scsi_cmd = cmd; pc->done = done; pc->timeout = jiffies + cmd->timeout_per_command; - pc->callback = ide_scsi_callback; if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) { printk ("ide-scsi: %s: que %lu, cmd = ", drive->name, cmd->serial_number); @@ -627,16 +622,18 @@ static int idescsi_queue (struct scsi_cmnd *cmd, } } - blk_rq_init(NULL, rq); rq->special = (char *) pc; rq->cmd_type = REQ_TYPE_SPECIAL; spin_unlock_irq(host->host_lock); + rq->ref_count++; + memcpy(rq->cmd, pc->c, 12); blk_execute_rq_nowait(drive->queue, scsi->disk, rq, 0, NULL); spin_lock_irq(host->host_lock); return 0; abort: kfree (pc); - kfree (rq); + if (rq) + blk_put_request(rq); cmd->result = DID_ERROR << 16; done(cmd); return 0; @@ -684,7 +681,9 @@ static int idescsi_eh_abort (struct scsi_cmnd *cmd) if (blk_sense_request(scsi->pc->rq)) kfree(scsi->pc->buf); - kfree(scsi->pc->rq); + /* we need to call blk_put_request twice. */ + blk_put_request(scsi->pc->rq); + blk_put_request(scsi->pc->rq); kfree(scsi->pc); scsi->pc = NULL; @@ -736,7 +735,7 @@ static int idescsi_eh_reset (struct scsi_cmnd *cmd) kfree(scsi->pc->buf); kfree(scsi->pc); scsi->pc = NULL; - kfree(req); + blk_put_request(req); /* now nuke the drive queue */ while ((req = elv_next_request(drive->queue))) { diff --git a/drivers/scsi/imm.c b/drivers/scsi/imm.c index f97d172844be..c2a9a13d788f 100644 --- a/drivers/scsi/imm.c +++ b/drivers/scsi/imm.c @@ -163,7 +163,7 @@ static int imm_proc_info(struct Scsi_Host *host, char *buffer, char **start, #if IMM_DEBUG > 0 #define imm_fail(x,y) printk("imm: imm_fail(%i) from %s at line %d\n",\ - y, __FUNCTION__, __LINE__); imm_fail_func(x,y); + y, __func__, __LINE__); imm_fail_func(x,y); static inline void imm_fail_func(imm_struct *dev, int error_code) #else diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index d93156671e93..4871dd1f2582 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -1403,10 +1403,10 @@ struct ipr_ucode_image_header { } #define ipr_trace ipr_dbg("%s: %s: Line: %d\n",\ - __FILE__, __FUNCTION__, __LINE__) + __FILE__, __func__, __LINE__) -#define ENTER IPR_DBG_CMD(printk(KERN_INFO IPR_NAME": Entering %s\n", __FUNCTION__)) -#define LEAVE IPR_DBG_CMD(printk(KERN_INFO IPR_NAME": Leaving %s\n", __FUNCTION__)) +#define ENTER IPR_DBG_CMD(printk(KERN_INFO IPR_NAME": Entering %s\n", __func__)) +#define LEAVE IPR_DBG_CMD(printk(KERN_INFO IPR_NAME": Leaving %s\n", __func__)) #define ipr_err_separator \ ipr_err("----------------------------------------------------------\n") diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 744f06d04a36..48ee8c7f5bdd 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -74,7 +74,7 @@ static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts) case SAS_OPEN_TO: case SAS_OPEN_REJECT: SAS_DPRINTK("%s: Saw error %d. What to do?\n", - __FUNCTION__, ts->stat); + __func__, ts->stat); return AC_ERR_OTHER; case SAS_ABORTED_TASK: @@ -115,7 +115,7 @@ static void sas_ata_task_done(struct sas_task *task) } else if (stat->stat != SAM_STAT_GOOD) { ac = sas_to_ata_err(stat); if (ac) { - SAS_DPRINTK("%s: SAS error %x\n", __FUNCTION__, + SAS_DPRINTK("%s: SAS error %x\n", __func__, stat->stat); /* We saw a SAS error. Send a vague error. */ qc->err_mask = ac; @@ -244,20 +244,20 @@ static void sas_ata_phy_reset(struct ata_port *ap) res = i->dft->lldd_I_T_nexus_reset(dev); if (res != TMF_RESP_FUNC_COMPLETE) - SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __FUNCTION__); + SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __func__); switch (dev->sata_dev.command_set) { case ATA_COMMAND_SET: - SAS_DPRINTK("%s: Found ATA device.\n", __FUNCTION__); + SAS_DPRINTK("%s: Found ATA device.\n", __func__); ap->link.device[0].class = ATA_DEV_ATA; break; case ATAPI_COMMAND_SET: - SAS_DPRINTK("%s: Found ATAPI device.\n", __FUNCTION__); + SAS_DPRINTK("%s: Found ATAPI device.\n", __func__); ap->link.device[0].class = ATA_DEV_ATAPI; break; default: SAS_DPRINTK("%s: Unknown SATA command set: %d.\n", - __FUNCTION__, + __func__, dev->sata_dev.command_set); ap->link.device[0].class = ATA_DEV_UNKNOWN; break; @@ -299,7 +299,7 @@ static int sas_ata_scr_write(struct ata_port *ap, unsigned int sc_reg_in, { struct domain_device *dev = ap->private_data; - SAS_DPRINTK("STUB %s\n", __FUNCTION__); + SAS_DPRINTK("STUB %s\n", __func__); switch (sc_reg_in) { case SCR_STATUS: dev->sata_dev.sstatus = val; @@ -324,7 +324,7 @@ static int sas_ata_scr_read(struct ata_port *ap, unsigned int sc_reg_in, { struct domain_device *dev = ap->private_data; - SAS_DPRINTK("STUB %s\n", __FUNCTION__); + SAS_DPRINTK("STUB %s\n", __func__); switch (sc_reg_in) { case SCR_STATUS: *val = dev->sata_dev.sstatus; diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index aefd865a5788..3da02e436788 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -121,7 +121,7 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, break; } else { SAS_DPRINTK("%s: task to dev %016llx response: 0x%x " - "status 0x%x\n", __FUNCTION__, + "status 0x%x\n", __func__, SAS_ADDR(dev->sas_addr), task->task_status.resp, task->task_status.stat); @@ -1279,7 +1279,7 @@ static int sas_configure_present(struct domain_device *dev, int phy_id, goto out; } else if (res != SMP_RESP_FUNC_ACC) { SAS_DPRINTK("%s: dev %016llx phy 0x%x index 0x%x " - "result 0x%x\n", __FUNCTION__, + "result 0x%x\n", __func__, SAS_ADDR(dev->sas_addr), phy_id, i, res); goto out; } @@ -1901,7 +1901,7 @@ int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, if (!rsp) { printk("%s: space for a smp response is missing\n", - __FUNCTION__); + __func__); return -EINVAL; } @@ -1914,20 +1914,20 @@ int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, if (type != SAS_EDGE_EXPANDER_DEVICE && type != SAS_FANOUT_EXPANDER_DEVICE) { printk("%s: can we send a smp request to a device?\n", - __FUNCTION__); + __func__); return -EINVAL; } dev = sas_find_dev_by_rphy(rphy); if (!dev) { - printk("%s: fail to find a domain_device?\n", __FUNCTION__); + printk("%s: fail to find a domain_device?\n", __func__); return -EINVAL; } /* do we need to support multiple segments? */ if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) { printk("%s: multiple segments req %u %u, rsp %u %u\n", - __FUNCTION__, req->bio->bi_vcnt, req->data_len, + __func__, req->bio->bi_vcnt, req->data_len, rsp->bio->bi_vcnt, rsp->data_len); return -EINVAL; } diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index 39ae68a3b0ef..139935a121b4 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -50,7 +50,7 @@ static void sas_form_port(struct asd_sas_phy *phy) sas_deform_port(phy); else { SAS_DPRINTK("%s: phy%d belongs to port%d already(%d)!\n", - __FUNCTION__, phy->id, phy->port->id, + __func__, phy->id, phy->port->id, phy->port->num_phys); return; } @@ -78,7 +78,7 @@ static void sas_form_port(struct asd_sas_phy *phy) if (i >= sas_ha->num_phys) { printk(KERN_NOTICE "%s: couldn't find a free port, bug?\n", - __FUNCTION__); + __func__); spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags); return; } diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 601ec5b6a7f6..a8e3ef309070 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -343,7 +343,7 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task) flags); SAS_DPRINTK("%s: task 0x%p aborted from " "task_queue\n", - __FUNCTION__, task); + __func__, task); return TASK_IS_ABORTED; } } @@ -351,13 +351,13 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task) } for (i = 0; i < 5; i++) { - SAS_DPRINTK("%s: aborting task 0x%p\n", __FUNCTION__, task); + SAS_DPRINTK("%s: aborting task 0x%p\n", __func__, task); res = si->dft->lldd_abort_task(task); spin_lock_irqsave(&task->task_state_lock, flags); if (task->task_state_flags & SAS_TASK_STATE_DONE) { spin_unlock_irqrestore(&task->task_state_lock, flags); - SAS_DPRINTK("%s: task 0x%p is done\n", __FUNCTION__, + SAS_DPRINTK("%s: task 0x%p is done\n", __func__, task); return TASK_IS_DONE; } @@ -365,24 +365,24 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task) if (res == TMF_RESP_FUNC_COMPLETE) { SAS_DPRINTK("%s: task 0x%p is aborted\n", - __FUNCTION__, task); + __func__, task); return TASK_IS_ABORTED; } else if (si->dft->lldd_query_task) { SAS_DPRINTK("%s: querying task 0x%p\n", - __FUNCTION__, task); + __func__, task); res = si->dft->lldd_query_task(task); switch (res) { case TMF_RESP_FUNC_SUCC: SAS_DPRINTK("%s: task 0x%p at LU\n", - __FUNCTION__, task); + __func__, task); return TASK_IS_AT_LU; case TMF_RESP_FUNC_COMPLETE: SAS_DPRINTK("%s: task 0x%p not at LU\n", - __FUNCTION__, task); + __func__, task); return TASK_IS_NOT_AT_LU; case TMF_RESP_FUNC_FAILED: SAS_DPRINTK("%s: task 0x%p failed to abort\n", - __FUNCTION__, task); + __func__, task); return TASK_ABORT_FAILED; } @@ -545,7 +545,7 @@ Again: if (need_reset) { SAS_DPRINTK("%s: task 0x%p requests reset\n", - __FUNCTION__, task); + __func__, task); goto reset; } @@ -556,13 +556,13 @@ Again: switch (res) { case TASK_IS_DONE: - SAS_DPRINTK("%s: task 0x%p is done\n", __FUNCTION__, + SAS_DPRINTK("%s: task 0x%p is done\n", __func__, task); sas_eh_finish_cmd(cmd); continue; case TASK_IS_ABORTED: SAS_DPRINTK("%s: task 0x%p is aborted\n", - __FUNCTION__, task); + __func__, task); sas_eh_finish_cmd(cmd); continue; case TASK_IS_AT_LU: @@ -633,7 +633,7 @@ Again: } return list_empty(work_q); clear_q: - SAS_DPRINTK("--- Exit %s -- clear_q\n", __FUNCTION__); + SAS_DPRINTK("--- Exit %s -- clear_q\n", __func__); list_for_each_entry_safe(cmd, n, work_q, eh_entry) sas_eh_finish_cmd(cmd); @@ -650,7 +650,7 @@ void sas_scsi_recover_host(struct Scsi_Host *shost) list_splice_init(&shost->eh_cmd_q, &eh_work_q); spin_unlock_irqrestore(shost->host_lock, flags); - SAS_DPRINTK("Enter %s\n", __FUNCTION__); + SAS_DPRINTK("Enter %s\n", __func__); /* * Deal with commands that still have SAS tasks (i.e. they didn't * complete via the normal sas_task completion mechanism) @@ -669,7 +669,7 @@ void sas_scsi_recover_host(struct Scsi_Host *shost) out: scsi_eh_flush_done_q(&ha->eh_done_q); - SAS_DPRINTK("--- Exit %s\n", __FUNCTION__); + SAS_DPRINTK("--- Exit %s\n", __func__); return; } @@ -990,7 +990,7 @@ int __sas_task_abort(struct sas_task *task) if (task->task_state_flags & SAS_TASK_STATE_ABORTED || task->task_state_flags & SAS_TASK_STATE_DONE) { spin_unlock_irqrestore(&task->task_state_lock, flags); - SAS_DPRINTK("%s: Task %p already finished.\n", __FUNCTION__, + SAS_DPRINTK("%s: Task %p already finished.\n", __func__, task); return 0; } diff --git a/drivers/scsi/libsrp.c b/drivers/scsi/libsrp.c index 6d6a76e65a6c..15e2d132e8b9 100644 --- a/drivers/scsi/libsrp.c +++ b/drivers/scsi/libsrp.c @@ -39,7 +39,7 @@ enum srp_task_attributes { /* tmp - will replace with SCSI logging stuff */ #define eprintk(fmt, args...) \ do { \ - printk("%s(%d) " fmt, __FUNCTION__, __LINE__, ##args); \ + printk("%s(%d) " fmt, __func__, __LINE__, ##args); \ } while (0) /* #define dprintk eprintk */ #define dprintk(fmt, args...) diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 5b6e5395c8eb..d51a2a4b43eb 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -2083,7 +2083,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) if (iocbq_entry == NULL) { printk(KERN_ERR "%s: only allocated %d iocbs of " "expected %d count. Unloading driver.\n", - __FUNCTION__, i, LPFC_IOCB_LIST_CNT); + __func__, i, LPFC_IOCB_LIST_CNT); error = -ENOMEM; goto out_free_iocbq; } @@ -2093,7 +2093,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) kfree (iocbq_entry); printk(KERN_ERR "%s: failed to allocate IOTAG. " "Unloading driver.\n", - __FUNCTION__); + __func__); error = -ENOMEM; goto out_free_iocbq; } diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index c94da4f2b8a6..1bcebbd3dfac 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -341,7 +341,7 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) { printk(KERN_ERR "%s: Too many sg segments from " "dma_map_sg. Config %d, seg_cnt %d", - __FUNCTION__, phba->cfg_sg_seg_cnt, + __func__, phba->cfg_sg_seg_cnt, lpfc_cmd->seg_cnt); scsi_dma_unmap(scsi_cmnd); return 1; diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index f40aa7b905f7..50fe07646738 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -219,7 +219,7 @@ lpfc_sli_iocb_cmd_type(uint8_t iocb_cmnd) case CMD_IOCB_LOGENTRY_CN: case CMD_IOCB_LOGENTRY_ASYNC_CN: printk("%s - Unhandled SLI-3 Command x%x\n", - __FUNCTION__, iocb_cmnd); + __func__, iocb_cmnd); type = LPFC_UNKNOWN_IOCB; break; default: @@ -1715,7 +1715,7 @@ lpfc_sli_handle_slow_ring_event(struct lpfc_hba *phba, rspiocbp = __lpfc_sli_get_iocbq(phba); if (rspiocbp == NULL) { printk(KERN_ERR "%s: out of buffers! Failing " - "completion.\n", __FUNCTION__); + "completion.\n", __func__); break; } @@ -3793,7 +3793,7 @@ lpfc_sli_validate_fcp_iocb(struct lpfc_iocbq *iocbq, struct lpfc_vport *vport, break; default: printk(KERN_ERR "%s: Unknown context cmd type, value %d\n", - __FUNCTION__, ctx_cmd); + __func__, ctx_cmd); break; } diff --git a/drivers/scsi/megaraid/mega_common.h b/drivers/scsi/megaraid/mega_common.h index f62ed468ada0..5ead1283a844 100644 --- a/drivers/scsi/megaraid/mega_common.h +++ b/drivers/scsi/megaraid/mega_common.h @@ -265,7 +265,7 @@ typedef struct { #define ASSERT(expression) \ if (!(expression)) { \ ASSERT_ACTION("assertion failed:(%s), file: %s, line: %d:%s\n", \ - #expression, __FILE__, __LINE__, __FUNCTION__); \ + #expression, __FILE__, __LINE__, __func__); \ } #else #define ASSERT(expression) diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c index 70a0f11f48b2..805bb61dde18 100644 --- a/drivers/scsi/megaraid/megaraid_mbox.c +++ b/drivers/scsi/megaraid/megaraid_mbox.c @@ -458,7 +458,7 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) if (adapter == NULL) { con_log(CL_ANN, (KERN_WARNING - "megaraid: out of memory, %s %d.\n", __FUNCTION__, __LINE__)); + "megaraid: out of memory, %s %d.\n", __func__, __LINE__)); goto out_probe_one; } @@ -1002,7 +1002,7 @@ megaraid_alloc_cmd_packets(adapter_t *adapter) if (!raid_dev->una_mbox64) { con_log(CL_ANN, (KERN_WARNING - "megaraid: out of memory, %s %d\n", __FUNCTION__, + "megaraid: out of memory, %s %d\n", __func__, __LINE__)); return -1; } @@ -1030,7 +1030,7 @@ megaraid_alloc_cmd_packets(adapter_t *adapter) if (!adapter->ibuf) { con_log(CL_ANN, (KERN_WARNING - "megaraid: out of memory, %s %d\n", __FUNCTION__, + "megaraid: out of memory, %s %d\n", __func__, __LINE__)); goto out_free_common_mbox; @@ -1052,7 +1052,7 @@ megaraid_alloc_cmd_packets(adapter_t *adapter) if (adapter->kscb_list == NULL) { con_log(CL_ANN, (KERN_WARNING - "megaraid: out of memory, %s %d\n", __FUNCTION__, + "megaraid: out of memory, %s %d\n", __func__, __LINE__)); goto out_free_ibuf; } @@ -1060,7 +1060,7 @@ megaraid_alloc_cmd_packets(adapter_t *adapter) // memory allocation for our command packets if (megaraid_mbox_setup_dma_pools(adapter) != 0) { con_log(CL_ANN, (KERN_WARNING - "megaraid: out of memory, %s %d\n", __FUNCTION__, + "megaraid: out of memory, %s %d\n", __func__, __LINE__)); goto out_free_scb_list; } @@ -2981,7 +2981,7 @@ megaraid_mbox_product_info(adapter_t *adapter) if (pinfo == NULL) { con_log(CL_ANN, (KERN_WARNING - "megaraid: out of memory, %s %d\n", __FUNCTION__, + "megaraid: out of memory, %s %d\n", __func__, __LINE__)); return -1; @@ -3508,7 +3508,7 @@ megaraid_cmm_register(adapter_t *adapter) if (adapter->uscb_list == NULL) { con_log(CL_ANN, (KERN_WARNING - "megaraid: out of memory, %s %d\n", __FUNCTION__, + "megaraid: out of memory, %s %d\n", __func__, __LINE__)); return -1; } @@ -3879,7 +3879,7 @@ megaraid_sysfs_alloc_resources(adapter_t *adapter) !raid_dev->sysfs_buffer) { con_log(CL_ANN, (KERN_WARNING - "megaraid: out of memory, %s %d\n", __FUNCTION__, + "megaraid: out of memory, %s %d\n", __func__, __LINE__)); rval = -ENOMEM; diff --git a/drivers/scsi/megaraid/megaraid_mm.c b/drivers/scsi/megaraid/megaraid_mm.c index ac3b280c2a72..f680561d2c6f 100644 --- a/drivers/scsi/megaraid/megaraid_mm.c +++ b/drivers/scsi/megaraid/megaraid_mm.c @@ -929,7 +929,7 @@ mraid_mm_register_adp(mraid_mmadp_t *lld_adp) !adapter->pthru_dma_pool) { con_log(CL_ANN, (KERN_WARNING - "megaraid cmm: out of memory, %s %d\n", __FUNCTION__, + "megaraid cmm: out of memory, %s %d\n", __func__, __LINE__)); rval = (-ENOMEM); @@ -957,7 +957,7 @@ mraid_mm_register_adp(mraid_mmadp_t *lld_adp) con_log(CL_ANN, (KERN_WARNING "megaraid cmm: out of memory, %s %d\n", - __FUNCTION__, __LINE__)); + __func__, __LINE__)); rval = (-ENOMEM); diff --git a/drivers/scsi/nsp32.c b/drivers/scsi/nsp32.c index 7fed35372150..edf9fdb3cb3c 100644 --- a/drivers/scsi/nsp32.c +++ b/drivers/scsi/nsp32.c @@ -299,9 +299,9 @@ static struct scsi_host_template nsp32_template = { #else # define NSP32_DEBUG_MASK 0xffffff # define nsp32_msg(type, args...) \ - nsp32_message (__FUNCTION__, __LINE__, (type), args) + nsp32_message (__func__, __LINE__, (type), args) # define nsp32_dbg(mask, args...) \ - nsp32_dmessage(__FUNCTION__, __LINE__, (mask), args) + nsp32_dmessage(__func__, __LINE__, (mask), args) #endif #define NSP32_DEBUG_QUEUECOMMAND BIT(0) diff --git a/drivers/scsi/nsp32_debug.c b/drivers/scsi/nsp32_debug.c index ef3c59cbcff6..2fb3fb58858d 100644 --- a/drivers/scsi/nsp32_debug.c +++ b/drivers/scsi/nsp32_debug.c @@ -88,7 +88,7 @@ static void print_commandk (unsigned char *command) int i,s; // printk(KERN_DEBUG); print_opcodek(command[0]); - /*printk(KERN_DEBUG "%s ", __FUNCTION__);*/ + /*printk(KERN_DEBUG "%s ", __func__);*/ if ((command[0] >> 5) == 6 || (command[0] >> 5) == 7 ) { s = 12; /* vender specific */ diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c index 5082ca3c6876..a221b6ef9fa9 100644 --- a/drivers/scsi/pcmcia/nsp_cs.c +++ b/drivers/scsi/pcmcia/nsp_cs.c @@ -107,9 +107,9 @@ static nsp_hw_data nsp_data_base; /* attach <-> detect glue */ #else # define NSP_DEBUG_MASK 0xffffff # define nsp_msg(type, args...) \ - nsp_cs_message (__FUNCTION__, __LINE__, (type), args) + nsp_cs_message (__func__, __LINE__, (type), args) # define nsp_dbg(mask, args...) \ - nsp_cs_dmessage(__FUNCTION__, __LINE__, (mask), args) + nsp_cs_dmessage(__func__, __LINE__, (mask), args) #endif #define NSP_DEBUG_QUEUECOMMAND BIT(0) diff --git a/drivers/scsi/pcmcia/nsp_debug.c b/drivers/scsi/pcmcia/nsp_debug.c index 2f75fe6e35a7..3c6ef64fcbff 100644 --- a/drivers/scsi/pcmcia/nsp_debug.c +++ b/drivers/scsi/pcmcia/nsp_debug.c @@ -90,7 +90,7 @@ static void print_commandk (unsigned char *command) int i, s; printk(KERN_DEBUG); print_opcodek(command[0]); - /*printk(KERN_DEBUG "%s ", __FUNCTION__);*/ + /*printk(KERN_DEBUG "%s ", __func__);*/ if ((command[0] >> 5) == 6 || (command[0] >> 5) == 7 ) { s = 12; /* vender specific */ diff --git a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c index f655ae320b48..8aa0bd987e29 100644 --- a/drivers/scsi/ppa.c +++ b/drivers/scsi/ppa.c @@ -171,7 +171,7 @@ static int device_check(ppa_struct *dev); #if PPA_DEBUG > 0 #define ppa_fail(x,y) printk("ppa: ppa_fail(%i) from %s at line %d\n",\ - y, __FUNCTION__, __LINE__); ppa_fail_func(x,y); + y, __func__, __LINE__); ppa_fail_func(x,y); static inline void ppa_fail_func(ppa_struct *dev, int error_code) #else static inline void ppa_fail(ppa_struct *dev, int error_code) diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c index 3754ab87f89a..37f9ba0cd798 100644 --- a/drivers/scsi/qla1280.c +++ b/drivers/scsi/qla1280.c @@ -1695,7 +1695,7 @@ qla1280_load_firmware_dma(struct scsi_qla_host *ha) risc_code_size = *ql1280_board_tbl[ha->devnum].fwlen; dprintk(1, "%s: DMA RISC code (%i) words\n", - __FUNCTION__, risc_code_size); + __func__, risc_code_size); num = 0; while (risc_code_size > 0) { @@ -1721,7 +1721,7 @@ qla1280_load_firmware_dma(struct scsi_qla_host *ha) mb[7] = pci_dma_hi32(ha->request_dma) & 0xffff; mb[6] = pci_dma_hi32(ha->request_dma) >> 16; dprintk(2, "%s: op=%d 0x%p = 0x%4x,0x%4x,0x%4x,0x%4x\n", - __FUNCTION__, mb[0], + __func__, mb[0], (void *)(long)ha->request_dma, mb[6], mb[7], mb[2], mb[3]); err = qla1280_mailbox_command(ha, BIT_4 | BIT_3 | BIT_2 | @@ -1753,10 +1753,10 @@ qla1280_load_firmware_dma(struct scsi_qla_host *ha) if (tbuf[i] != sp[i] && warn++ < 10) { printk(KERN_ERR "%s: FW compare error @ " "byte(0x%x) loop#=%x\n", - __FUNCTION__, i, num); + __func__, i, num); printk(KERN_ERR "%s: FWbyte=%x " "FWfromChip=%x\n", - __FUNCTION__, sp[i], tbuf[i]); + __func__, sp[i], tbuf[i]); /*break; */ } } @@ -1781,7 +1781,7 @@ qla1280_start_firmware(struct scsi_qla_host *ha) int err; dprintk(1, "%s: Verifying checksum of loaded RISC code.\n", - __FUNCTION__); + __func__); /* Verify checksum of loaded RISC code. */ mb[0] = MBC_VERIFY_CHECKSUM; @@ -1794,7 +1794,7 @@ qla1280_start_firmware(struct scsi_qla_host *ha) } /* Start firmware execution. */ - dprintk(1, "%s: start firmware running.\n", __FUNCTION__); + dprintk(1, "%s: start firmware running.\n", __func__); mb[0] = MBC_EXECUTE_FIRMWARE; mb[1] = *ql1280_board_tbl[ha->devnum].fwstart; err = qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]); diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 8dd88fc1244a..a319a20ed440 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -8,6 +8,7 @@ #include <linux/kthread.h> #include <linux/vmalloc.h> +#include <linux/delay.h> static int qla24xx_vport_disable(struct fc_vport *, bool); @@ -20,18 +21,12 @@ qla2x00_sysfs_read_fw_dump(struct kobject *kobj, { struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, struct device, kobj))); - char *rbuf = (char *)ha->fw_dump; if (ha->fw_dump_reading == 0) return 0; - if (off > ha->fw_dump_len) - return 0; - if (off + count > ha->fw_dump_len) - count = ha->fw_dump_len - off; - memcpy(buf, &rbuf[off], count); - - return (count); + return memory_read_from_buffer(buf, count, &off, ha->fw_dump, + ha->fw_dump_len); } static ssize_t @@ -94,20 +89,13 @@ qla2x00_sysfs_read_nvram(struct kobject *kobj, { struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, struct device, kobj))); - int size = ha->nvram_size; - char *nvram_cache = ha->nvram; - if (!capable(CAP_SYS_ADMIN) || off > size || count == 0) + if (!capable(CAP_SYS_ADMIN)) return 0; - if (off + count > size) { - size -= off; - count = size; - } /* Read NVRAM data from cache. */ - memcpy(buf, &nvram_cache[off], count); - - return count; + return memory_read_from_buffer(buf, count, &off, ha->nvram, + ha->nvram_size); } static ssize_t @@ -175,14 +163,9 @@ qla2x00_sysfs_read_optrom(struct kobject *kobj, if (ha->optrom_state != QLA_SREADING) return 0; - if (off > ha->optrom_region_size) - return 0; - if (off + count > ha->optrom_region_size) - count = ha->optrom_region_size - off; - - memcpy(buf, &ha->optrom_buffer[off], count); - return count; + return memory_read_from_buffer(buf, count, &off, ha->optrom_buffer, + ha->optrom_region_size); } static ssize_t @@ -374,20 +357,12 @@ qla2x00_sysfs_read_vpd(struct kobject *kobj, { struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, struct device, kobj))); - int size = ha->vpd_size; - char *vpd_cache = ha->vpd; - if (!capable(CAP_SYS_ADMIN) || off > size || count == 0) + if (!capable(CAP_SYS_ADMIN)) return 0; - if (off + count > size) { - size -= off; - count = size; - } /* Read NVRAM data from cache. */ - memcpy(buf, &vpd_cache[off], count); - - return count; + return memory_read_from_buffer(buf, count, &off, ha->vpd, ha->vpd_size); } static ssize_t @@ -557,8 +532,10 @@ qla2x00_serial_num_show(struct device *dev, struct device_attribute *attr, scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); uint32_t sn; - if (IS_FWI2_CAPABLE(ha)) - return snprintf(buf, PAGE_SIZE, "\n"); + if (IS_FWI2_CAPABLE(ha)) { + qla2xxx_get_vpd_field(ha, "SN", buf, PAGE_SIZE); + return snprintf(buf, PAGE_SIZE, "%s\n", buf); + } sn = ((ha->serial0 & 0x1f) << 16) | (ha->serial2 << 8) | ha->serial1; return snprintf(buf, PAGE_SIZE, "%c%05d\n", 'A' + sn / 100000, @@ -809,6 +786,16 @@ qla2x00_optrom_fw_version_show(struct device *dev, ha->fw_revision[3]); } +static ssize_t +qla2x00_total_isp_aborts_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + + return snprintf(buf, PAGE_SIZE, "%d\n", + ha->qla_stats.total_isp_aborts); +} + static DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, NULL); static DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL); static DEVICE_ATTR(serial_num, S_IRUGO, qla2x00_serial_num_show, NULL); @@ -831,6 +818,8 @@ static DEVICE_ATTR(optrom_fcode_version, S_IRUGO, qla2x00_optrom_fcode_version_show, NULL); static DEVICE_ATTR(optrom_fw_version, S_IRUGO, qla2x00_optrom_fw_version_show, NULL); +static DEVICE_ATTR(total_isp_aborts, S_IRUGO, qla2x00_total_isp_aborts_show, + NULL); struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_driver_version, @@ -849,6 +838,7 @@ struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_optrom_efi_version, &dev_attr_optrom_fcode_version, &dev_attr_optrom_fw_version, + &dev_attr_total_isp_aborts, NULL, }; @@ -972,26 +962,39 @@ qla2x00_get_starget_port_id(struct scsi_target *starget) } static void -qla2x00_get_rport_loss_tmo(struct fc_rport *rport) +qla2x00_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout) { - struct Scsi_Host *host = rport_to_shost(rport); - scsi_qla_host_t *ha = shost_priv(host); - - rport->dev_loss_tmo = ha->port_down_retry_count + 5; + if (timeout) + rport->dev_loss_tmo = timeout; + else + rport->dev_loss_tmo = 1; } static void -qla2x00_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout) +qla2x00_dev_loss_tmo_callbk(struct fc_rport *rport) { struct Scsi_Host *host = rport_to_shost(rport); - scsi_qla_host_t *ha = shost_priv(host); + fc_port_t *fcport = *(fc_port_t **)rport->dd_data; + + qla2x00_abort_fcport_cmds(fcport); + + /* + * Transport has effectively 'deleted' the rport, clear + * all local references. + */ + spin_lock_irq(host->host_lock); + fcport->rport = NULL; + *((fc_port_t **)rport->dd_data) = NULL; + spin_unlock_irq(host->host_lock); +} - if (timeout) - ha->port_down_retry_count = timeout; - else - ha->port_down_retry_count = 1; +static void +qla2x00_terminate_rport_io(struct fc_rport *rport) +{ + fc_port_t *fcport = *(fc_port_t **)rport->dd_data; - rport->dev_loss_tmo = ha->port_down_retry_count + 5; + qla2x00_abort_fcport_cmds(fcport); + scsi_target_unblock(&rport->dev); } static int @@ -1045,6 +1048,7 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost) pfc_host_stat->invalid_tx_word_count = stats->inval_xmit_word_cnt; pfc_host_stat->invalid_crc_count = stats->inval_crc_cnt; if (IS_FWI2_CAPABLE(ha)) { + pfc_host_stat->lip_count = stats->lip_cnt; pfc_host_stat->tx_frames = stats->tx_frames; pfc_host_stat->rx_frames = stats->rx_frames; pfc_host_stat->dumped_frames = stats->dumped_frames; @@ -1173,17 +1177,16 @@ vport_create_failed_2: static int qla24xx_vport_delete(struct fc_vport *fc_vport) { - scsi_qla_host_t *ha = shost_priv(fc_vport->shost); scsi_qla_host_t *vha = fc_vport->dd_data; + scsi_qla_host_t *pha = to_qla_parent(vha); + + while (test_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags) || + test_bit(FCPORT_UPDATE_NEEDED, &pha->dpc_flags)) + msleep(1000); qla24xx_disable_vp(vha); qla24xx_deallocate_vp_id(vha); - mutex_lock(&ha->vport_lock); - ha->cur_vport_count--; - clear_bit(vha->vp_idx, ha->vp_idx_map); - mutex_unlock(&ha->vport_lock); - kfree(vha->node_name); kfree(vha->port_name); @@ -1248,11 +1251,12 @@ struct fc_function_template qla2xxx_transport_functions = { .get_starget_port_id = qla2x00_get_starget_port_id, .show_starget_port_id = 1, - .get_rport_dev_loss_tmo = qla2x00_get_rport_loss_tmo, .set_rport_dev_loss_tmo = qla2x00_set_rport_loss_tmo, .show_rport_dev_loss_tmo = 1, .issue_fc_host_lip = qla2x00_issue_lip, + .dev_loss_tmo_callbk = qla2x00_dev_loss_tmo_callbk, + .terminate_rport_io = qla2x00_terminate_rport_io, .get_fc_host_stats = qla2x00_get_fc_host_stats, .vport_create = qla24xx_vport_create, @@ -1291,11 +1295,12 @@ struct fc_function_template qla2xxx_transport_vport_functions = { .get_starget_port_id = qla2x00_get_starget_port_id, .show_starget_port_id = 1, - .get_rport_dev_loss_tmo = qla2x00_get_rport_loss_tmo, .set_rport_dev_loss_tmo = qla2x00_set_rport_loss_tmo, .show_rport_dev_loss_tmo = 1, .issue_fc_host_lip = qla2x00_issue_lip, + .dev_loss_tmo_callbk = qla2x00_dev_loss_tmo_callbk, + .terminate_rport_io = qla2x00_terminate_rport_io, .get_fc_host_stats = qla2x00_get_fc_host_stats, }; diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index cbef785765cf..510ba64bc286 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -216,7 +216,7 @@ qla24xx_soft_reset(scsi_qla_host_t *ha) static int qla2xxx_dump_ram(scsi_qla_host_t *ha, uint32_t addr, uint16_t *ram, - uint16_t ram_words, void **nxt) + uint32_t ram_words, void **nxt) { int rval; uint32_t cnt, stat, timer, words, idx; diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 8dd600013bd1..6da31ba94404 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -864,7 +864,8 @@ struct link_statistics { uint32_t prim_seq_err_cnt; uint32_t inval_xmit_word_cnt; uint32_t inval_crc_cnt; - uint32_t unused1[0x1b]; + uint32_t lip_cnt; + uint32_t unused1[0x1a]; uint32_t tx_frames; uint32_t rx_frames; uint32_t dumped_frames; @@ -1544,7 +1545,6 @@ typedef struct fc_port { int login_retry; atomic_t port_down_timer; - spinlock_t rport_lock; struct fc_rport *rport, *drport; u32 supported_classes; @@ -2155,6 +2155,10 @@ struct qla_chip_state_84xx { uint32_t gold_fw_version; }; +struct qla_statistics { + uint32_t total_isp_aborts; +}; + /* * Linux Host Adapter structure */ @@ -2166,7 +2170,6 @@ typedef struct scsi_qla_host { struct pci_dev *pdev; unsigned long host_no; - unsigned long instance; volatile struct { uint32_t init_done :1; @@ -2515,7 +2518,7 @@ typedef struct scsi_qla_host { uint8_t model_number[16+1]; #define BINZERO "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - char *model_desc; + char model_desc[80]; uint8_t adapter_id[16+1]; uint8_t *node_name; @@ -2596,6 +2599,7 @@ typedef struct scsi_qla_host { int cur_vport_count; struct qla_chip_state_84xx *cs84xx; + struct qla_statistics qla_stats; } scsi_qla_host_t; diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 9b4bebee6879..0b156735e9a6 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -62,7 +62,7 @@ extern int ql2xfdmienable; extern int ql2xallocfwdump; extern int ql2xextended_error_logging; extern int ql2xqfullrampup; -extern int num_hosts; +extern int ql2xiidmaenable; extern int qla2x00_loop_reset(scsi_qla_host_t *); extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int); @@ -71,6 +71,8 @@ extern int qla2x00_post_aen_work(struct scsi_qla_host *, enum extern int qla2x00_post_hwe_work(struct scsi_qla_host *, uint16_t , uint16_t, uint16_t, uint16_t); +extern void qla2x00_abort_fcport_cmds(fc_port_t *); + /* * Global Functions in qla_mid.c source file. */ @@ -312,6 +314,7 @@ extern int qla2xxx_hw_event_log(scsi_qla_host_t *, uint16_t , uint16_t, uint16_t, uint16_t); extern void qla2xxx_get_flash_info(scsi_qla_host_t *); +extern int qla2xxx_get_vpd_field(scsi_qla_host_t *, char *, char *, size_t); /* * Global Function Prototypes in qla_dbg.c source file. diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index 4cb80b476c85..c2a4bfbcb05b 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -1661,6 +1661,12 @@ qla2x00_fdmi_register(scsi_qla_host_t *ha) { int rval; + if (IS_QLA2100(ha) || IS_QLA2200(ha)) { + DEBUG2(printk("scsi(%ld): FDMI unsupported on " + "ISP2100/ISP2200.\n", ha->host_no)); + return QLA_SUCCESS; + } + rval = qla2x00_mgmt_svr_login(ha); if (rval) return rval; diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index bbbc5a632a1d..601a6b29750c 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -334,6 +334,8 @@ static int qla2x00_isp_firmware(scsi_qla_host_t *ha) { int rval; + uint16_t loop_id, topo, sw_cap; + uint8_t domain, area, al_pa; /* Assume loading risc code */ rval = QLA_FUNCTION_FAILED; @@ -345,6 +347,11 @@ qla2x00_isp_firmware(scsi_qla_host_t *ha) /* Verify checksum of loaded RISC code. */ rval = qla2x00_verify_checksum(ha, ha->fw_srisc_address); + if (rval == QLA_SUCCESS) { + /* And, verify we are not in ROM code. */ + rval = qla2x00_get_adapter_id(ha, &loop_id, &al_pa, + &area, &domain, &topo, &sw_cap); + } } if (rval) { @@ -722,7 +729,7 @@ qla24xx_chip_diag(scsi_qla_host_t *ha) /* Perform RISC reset. */ qla24xx_reset_risc(ha); - ha->fw_transfer_size = REQUEST_ENTRY_SIZE * 1024; + ha->fw_transfer_size = REQUEST_ENTRY_SIZE * ha->request_q_length; rval = qla2x00_mbx_reg_test(ha); if (rval) { @@ -768,42 +775,16 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *ha) mem_size = (ha->fw_memory_size - 0x100000 + 1) * sizeof(uint32_t); - /* Allocate memory for Extended Trace Buffer. */ - tc = dma_alloc_coherent(&ha->pdev->dev, EFT_SIZE, &tc_dma, - GFP_KERNEL); - if (!tc) { - qla_printk(KERN_WARNING, ha, "Unable to allocate " - "(%d KB) for EFT.\n", EFT_SIZE / 1024); - goto cont_alloc; - } - - memset(tc, 0, EFT_SIZE); - rval = qla2x00_enable_eft_trace(ha, tc_dma, EFT_NUM_BUFFERS); - if (rval) { - qla_printk(KERN_WARNING, ha, "Unable to initialize " - "EFT (%d).\n", rval); - dma_free_coherent(&ha->pdev->dev, EFT_SIZE, tc, - tc_dma); - goto cont_alloc; - } - - qla_printk(KERN_INFO, ha, "Allocated (%d KB) for EFT...\n", - EFT_SIZE / 1024); - - eft_size = EFT_SIZE; - ha->eft_dma = tc_dma; - ha->eft = tc; - /* Allocate memory for Fibre Channel Event Buffer. */ if (!IS_QLA25XX(ha)) - goto cont_alloc; + goto try_eft; tc = dma_alloc_coherent(&ha->pdev->dev, FCE_SIZE, &tc_dma, GFP_KERNEL); if (!tc) { qla_printk(KERN_WARNING, ha, "Unable to allocate " "(%d KB) for FCE.\n", FCE_SIZE / 1024); - goto cont_alloc; + goto try_eft; } memset(tc, 0, FCE_SIZE); @@ -815,7 +796,7 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *ha) dma_free_coherent(&ha->pdev->dev, FCE_SIZE, tc, tc_dma); ha->flags.fce_enabled = 0; - goto cont_alloc; + goto try_eft; } qla_printk(KERN_INFO, ha, "Allocated (%d KB) for FCE...\n", @@ -825,6 +806,32 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *ha) ha->flags.fce_enabled = 1; ha->fce_dma = tc_dma; ha->fce = tc; +try_eft: + /* Allocate memory for Extended Trace Buffer. */ + tc = dma_alloc_coherent(&ha->pdev->dev, EFT_SIZE, &tc_dma, + GFP_KERNEL); + if (!tc) { + qla_printk(KERN_WARNING, ha, "Unable to allocate " + "(%d KB) for EFT.\n", EFT_SIZE / 1024); + goto cont_alloc; + } + + memset(tc, 0, EFT_SIZE); + rval = qla2x00_enable_eft_trace(ha, tc_dma, EFT_NUM_BUFFERS); + if (rval) { + qla_printk(KERN_WARNING, ha, "Unable to initialize " + "EFT (%d).\n", rval); + dma_free_coherent(&ha->pdev->dev, EFT_SIZE, tc, + tc_dma); + goto cont_alloc; + } + + qla_printk(KERN_INFO, ha, "Allocated (%d KB) for EFT...\n", + EFT_SIZE / 1024); + + eft_size = EFT_SIZE; + ha->eft_dma = tc_dma; + ha->eft = tc; } cont_alloc: req_q_size = ha->request_q_length * sizeof(request_t); @@ -1501,18 +1508,25 @@ qla2x00_set_model_info(scsi_qla_host_t *ha, uint8_t *model, size_t len, char *de index = (ha->pdev->subsystem_device & 0xff); if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC && index < QLA_MODEL_NAMES) - ha->model_desc = qla2x00_model_name[index * 2 + 1]; + strncpy(ha->model_desc, + qla2x00_model_name[index * 2 + 1], + sizeof(ha->model_desc) - 1); } else { index = (ha->pdev->subsystem_device & 0xff); if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC && index < QLA_MODEL_NAMES) { strcpy(ha->model_number, qla2x00_model_name[index * 2]); - ha->model_desc = qla2x00_model_name[index * 2 + 1]; + strncpy(ha->model_desc, + qla2x00_model_name[index * 2 + 1], + sizeof(ha->model_desc) - 1); } else { strcpy(ha->model_number, def); } } + if (IS_FWI2_CAPABLE(ha)) + qla2xxx_get_vpd_field(ha, "\x82", ha->model_desc, + sizeof(ha->model_desc)); } /* On sparc systems, obtain port and node WWN from firmware @@ -1864,12 +1878,11 @@ qla2x00_rport_del(void *data) { fc_port_t *fcport = data; struct fc_rport *rport; - unsigned long flags; - spin_lock_irqsave(&fcport->rport_lock, flags); + spin_lock_irq(fcport->ha->host->host_lock); rport = fcport->drport; fcport->drport = NULL; - spin_unlock_irqrestore(&fcport->rport_lock, flags); + spin_unlock_irq(fcport->ha->host->host_lock); if (rport) fc_remote_port_delete(rport); } @@ -1898,7 +1911,6 @@ qla2x00_alloc_fcport(scsi_qla_host_t *ha, gfp_t flags) atomic_set(&fcport->state, FCS_UNCONFIGURED); fcport->flags = FCF_RLC_SUPPORT; fcport->supported_classes = FC_COS_UNSPECIFIED; - spin_lock_init(&fcport->rport_lock); return fcport; } @@ -2007,8 +2019,10 @@ qla2x00_configure_loop(scsi_qla_host_t *ha) if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) { if (test_bit(LOCAL_LOOP_UPDATE, &save_flags)) set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); - if (test_bit(RSCN_UPDATE, &save_flags)) + if (test_bit(RSCN_UPDATE, &save_flags)) { + ha->flags.rscn_queue_overflow = 1; set_bit(RSCN_UPDATE, &ha->dpc_flags); + } } return (rval); @@ -2243,28 +2257,24 @@ qla2x00_reg_remote_port(scsi_qla_host_t *ha, fc_port_t *fcport) { struct fc_rport_identifiers rport_ids; struct fc_rport *rport; - unsigned long flags; if (fcport->drport) qla2x00_rport_del(fcport); - if (fcport->rport) - return; rport_ids.node_name = wwn_to_u64(fcport->node_name); rport_ids.port_name = wwn_to_u64(fcport->port_name); rport_ids.port_id = fcport->d_id.b.domain << 16 | fcport->d_id.b.area << 8 | fcport->d_id.b.al_pa; rport_ids.roles = FC_RPORT_ROLE_UNKNOWN; - rport = fc_remote_port_add(ha->host, 0, &rport_ids); + fcport->rport = rport = fc_remote_port_add(ha->host, 0, &rport_ids); if (!rport) { qla_printk(KERN_WARNING, ha, "Unable to allocate fc remote port!\n"); return; } - spin_lock_irqsave(&fcport->rport_lock, flags); - fcport->rport = rport; + spin_lock_irq(fcport->ha->host->host_lock); *((fc_port_t **)rport->dd_data) = fcport; - spin_unlock_irqrestore(&fcport->rport_lock, flags); + spin_unlock_irq(fcport->ha->host->host_lock); rport->supported_classes = fcport->supported_classes; @@ -2565,7 +2575,8 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) } else if (qla2x00_gnn_id(ha, swl) != QLA_SUCCESS) { kfree(swl); swl = NULL; - } else if (qla2x00_gfpn_id(ha, swl) == QLA_SUCCESS) { + } else if (ql2xiidmaenable && + qla2x00_gfpn_id(ha, swl) == QLA_SUCCESS) { qla2x00_gpsc(ha, swl); } } @@ -3220,7 +3231,8 @@ qla2x00_update_fcports(scsi_qla_host_t *ha) /* Go with deferred removal of rport references. */ list_for_each_entry(fcport, &ha->fcports, list) - if (fcport->drport) + if (fcport->drport && + atomic_read(&fcport->state) != FCS_UNCONFIGURED) qla2x00_rport_del(fcport); } @@ -3243,6 +3255,7 @@ qla2x00_abort_isp(scsi_qla_host_t *ha) if (ha->flags.online) { ha->flags.online = 0; clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + ha->qla_stats.total_isp_aborts++; qla_printk(KERN_INFO, ha, "Performing ISP error recovery - ha= %p.\n", ha); @@ -3283,17 +3296,6 @@ qla2x00_abort_isp(scsi_qla_host_t *ha) ha->isp_abort_cnt = 0; clear_bit(ISP_ABORT_RETRY, &ha->dpc_flags); - if (ha->eft) { - memset(ha->eft, 0, EFT_SIZE); - rval = qla2x00_enable_eft_trace(ha, - ha->eft_dma, EFT_NUM_BUFFERS); - if (rval) { - qla_printk(KERN_WARNING, ha, - "Unable to reinitialize EFT " - "(%d).\n", rval); - } - } - if (ha->fce) { ha->flags.fce_enabled = 1; memset(ha->fce, 0, @@ -3308,6 +3310,17 @@ qla2x00_abort_isp(scsi_qla_host_t *ha) ha->flags.fce_enabled = 0; } } + + if (ha->eft) { + memset(ha->eft, 0, EFT_SIZE); + rval = qla2x00_enable_eft_trace(ha, + ha->eft_dma, EFT_NUM_BUFFERS); + if (rval) { + qla_printk(KERN_WARNING, ha, + "Unable to reinitialize EFT " + "(%d).\n", rval); + } + } } else { /* failed the ISP abort */ ha->flags.online = 1; if (test_bit(ISP_ABORT_RETRY, &ha->dpc_flags)) { @@ -4026,8 +4039,8 @@ qla2x00_try_to_stop_firmware(scsi_qla_host_t *ha) ret = qla2x00_stop_firmware(ha); for (retries = 5; ret != QLA_SUCCESS && ret != QLA_FUNCTION_TIMEOUT && retries ; retries--) { - qla2x00_reset_chip(ha); - if (qla2x00_chip_diag(ha) != QLA_SUCCESS) + ha->isp_ops->reset_chip(ha); + if (ha->isp_ops->chip_diag(ha) != QLA_SUCCESS) continue; if (qla2x00_setup_chip(ha) != QLA_SUCCESS) continue; @@ -4049,7 +4062,7 @@ qla24xx_configure_vhba(scsi_qla_host_t *ha) rval = qla2x00_fw_ready(ha->parent); if (rval == QLA_SUCCESS) { clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); - qla2x00_marker(ha->parent, 0, 0, MK_SYNC_ALL); + qla2x00_marker(ha, 0, 0, MK_SYNC_ALL); } ha->flags.management_server_logged_in = 0; diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index 5489d5024673..d57669aa4615 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -454,10 +454,11 @@ qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun, { int ret; unsigned long flags = 0; + scsi_qla_host_t *pha = to_qla_parent(ha); - spin_lock_irqsave(&ha->hardware_lock, flags); + spin_lock_irqsave(&pha->hardware_lock, flags); ret = __qla2x00_marker(ha, loop_id, lun, type); - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&pha->hardware_lock, flags); return (ret); } @@ -672,7 +673,7 @@ qla24xx_start_scsi(srb_t *sp) { int ret, nseg; unsigned long flags; - scsi_qla_host_t *ha; + scsi_qla_host_t *ha, *pha; struct scsi_cmnd *cmd; uint32_t *clr_ptr; uint32_t index; @@ -686,6 +687,7 @@ qla24xx_start_scsi(srb_t *sp) /* Setup device pointers. */ ret = 0; ha = sp->ha; + pha = to_qla_parent(ha); reg = &ha->iobase->isp24; cmd = sp->cmd; /* So we know we haven't pci_map'ed anything yet */ @@ -700,7 +702,7 @@ qla24xx_start_scsi(srb_t *sp) } /* Acquire ring specific lock */ - spin_lock_irqsave(&ha->hardware_lock, flags); + spin_lock_irqsave(&pha->hardware_lock, flags); /* Check for room in outstanding command list. */ handle = ha->current_outstanding_cmd; @@ -795,14 +797,14 @@ qla24xx_start_scsi(srb_t *sp) ha->response_ring_ptr->signature != RESPONSE_PROCESSED) qla24xx_process_response_queue(ha); - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&pha->hardware_lock, flags); return QLA_SUCCESS; queuing_error: if (tot_dsds) scsi_dma_unmap(cmd); - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&pha->hardware_lock, flags); return QLA_FUNCTION_FAILED; } diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index ec63b79f900a..874d802edb7d 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -542,10 +542,6 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) break; case MBA_PORT_UPDATE: /* Port database update */ - /* Only handle SCNs for our Vport index. */ - if (ha->parent && ha->vp_idx != (mb[3] & 0xff)) - break; - /* * If PORT UPDATE is global (recieved LIP_OCCURED/LIP_RESET * event etc. earlier indicating loop is down) then process diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 250d2f604397..bc90d6b8d0a0 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -918,6 +918,8 @@ qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa, rval = qla2x00_mailbox_command(ha, mcp); if (mcp->mb[0] == MBS_COMMAND_ERROR) rval = QLA_COMMAND_ERROR; + else if (mcp->mb[0] == MBS_INVALID_COMMAND) + rval = QLA_INVALID_COMMAND; /* Return data. */ *id = mcp->mb[1]; @@ -2161,17 +2163,18 @@ qla24xx_abort_command(scsi_qla_host_t *ha, srb_t *sp) struct abort_entry_24xx *abt; dma_addr_t abt_dma; uint32_t handle; + scsi_qla_host_t *pha = to_qla_parent(ha); DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); fcport = sp->fcport; - spin_lock_irqsave(&ha->hardware_lock, flags); + spin_lock_irqsave(&pha->hardware_lock, flags); for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) { - if (ha->outstanding_cmds[handle] == sp) + if (pha->outstanding_cmds[handle] == sp) break; } - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&pha->hardware_lock, flags); if (handle == MAX_OUTSTANDING_COMMANDS) { /* Command not found. */ return QLA_FUNCTION_FAILED; diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c index 62a3ad6e8ecb..50baf6a1d67c 100644 --- a/drivers/scsi/qla2xxx/qla_mid.c +++ b/drivers/scsi/qla2xxx/qla_mid.c @@ -43,6 +43,7 @@ qla24xx_allocate_vp_id(scsi_qla_host_t *vha) set_bit(vp_id, ha->vp_idx_map); ha->num_vhosts++; + ha->cur_vport_count++; vha->vp_idx = vp_id; list_add_tail(&vha->vp_list, &ha->vp_list); mutex_unlock(&ha->vport_lock); @@ -58,6 +59,7 @@ qla24xx_deallocate_vp_id(scsi_qla_host_t *vha) mutex_lock(&ha->vport_lock); vp_id = vha->vp_idx; ha->num_vhosts--; + ha->cur_vport_count--; clear_bit(vp_id, ha->vp_idx_map); list_del(&vha->vp_list); mutex_unlock(&ha->vport_lock); @@ -103,8 +105,8 @@ qla2x00_mark_vp_devices_dead(scsi_qla_host_t *vha) "loop_id=0x%04x :%x\n", vha->host_no, fcport->loop_id, fcport->vp_idx)); - atomic_set(&fcport->state, FCS_DEVICE_DEAD); qla2x00_mark_device_lost(vha, fcport, 0, 0); + atomic_set(&fcport->state, FCS_UNCONFIGURED); } } @@ -276,7 +278,8 @@ qla2x00_do_dpc_vp(scsi_qla_host_t *vha) clear_bit(RESET_ACTIVE, &vha->dpc_flags); } - if (test_and_clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) { + if (atomic_read(&vha->vp_state) == VP_ACTIVE && + test_and_clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) { if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags))) { qla2x00_loop_resync(vha); clear_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags); @@ -390,7 +393,6 @@ qla24xx_create_vhost(struct fc_vport *fc_vport) vha->parent = ha; vha->fc_vport = fc_vport; vha->device_flags = 0; - vha->instance = num_hosts; vha->vp_idx = qla24xx_allocate_vp_id(vha); if (vha->vp_idx > ha->max_npiv_vports) { DEBUG15(printk("scsi(%ld): Couldn't allocate vp_id.\n", @@ -428,7 +430,7 @@ qla24xx_create_vhost(struct fc_vport *fc_vport) host->max_cmd_len = MAX_CMDSZ; host->max_channel = MAX_BUSES - 1; host->max_lun = MAX_LUNS; - host->unique_id = vha->instance; + host->unique_id = host->host_no; host->max_id = MAX_TARGETS_2200; host->transportt = qla2xxx_transport_vport_template; @@ -436,12 +438,6 @@ qla24xx_create_vhost(struct fc_vport *fc_vport) vha->host_no, vha)); vha->flags.init_done = 1; - num_hosts++; - - mutex_lock(&ha->vport_lock); - set_bit(vha->vp_idx, ha->vp_idx_map); - ha->cur_vport_count++; - mutex_unlock(&ha->vport_lock); return vha; diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 48eaa3bb5433..7c8af7ed2a5d 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -27,7 +27,6 @@ char qla2x00_version_str[40]; */ static struct kmem_cache *srb_cachep; -int num_hosts; int ql2xlogintimeout = 20; module_param(ql2xlogintimeout, int, S_IRUGO|S_IRUSR); MODULE_PARM_DESC(ql2xlogintimeout, @@ -87,6 +86,13 @@ MODULE_PARM_DESC(ql2xqfullrampup, "depth for a device after a queue-full condition has been " "detected. Default is 120 seconds."); +int ql2xiidmaenable=1; +module_param(ql2xiidmaenable, int, S_IRUGO|S_IRUSR); +MODULE_PARM_DESC(ql2xiidmaenable, + "Enables iIDMA settings " + "Default is 1 - perform iIDMA. 0 - no iIDMA."); + + /* * SCSI host template entry points */ @@ -388,7 +394,7 @@ qla2x00_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) } /* Close window on fcport/rport state-transitioning. */ - if (!*(fc_port_t **)rport->dd_data) { + if (fcport->drport) { cmd->result = DID_IMM_RETRY << 16; goto qc_fail_command; } @@ -443,7 +449,7 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) int rval; scsi_qla_host_t *pha = to_qla_parent(ha); - if (unlikely(pci_channel_offline(ha->pdev))) { + if (unlikely(pci_channel_offline(pha->pdev))) { cmd->result = DID_REQUEUE << 16; goto qc24_fail_command; } @@ -455,7 +461,7 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) } /* Close window on fcport/rport state-transitioning. */ - if (!*(fc_port_t **)rport->dd_data) { + if (fcport->drport) { cmd->result = DID_IMM_RETRY << 16; goto qc24_fail_command; } @@ -617,6 +623,40 @@ qla2x00_wait_for_loop_ready(scsi_qla_host_t *ha) return (return_status); } +void +qla2x00_abort_fcport_cmds(fc_port_t *fcport) +{ + int cnt; + unsigned long flags; + srb_t *sp; + scsi_qla_host_t *ha = fcport->ha; + scsi_qla_host_t *pha = to_qla_parent(ha); + + spin_lock_irqsave(&pha->hardware_lock, flags); + for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { + sp = pha->outstanding_cmds[cnt]; + if (!sp) + continue; + if (sp->fcport != fcport) + continue; + + spin_unlock_irqrestore(&pha->hardware_lock, flags); + if (ha->isp_ops->abort_command(ha, sp)) { + DEBUG2(qla_printk(KERN_WARNING, ha, + "Abort failed -- %lx\n", sp->cmd->serial_number)); + } else { + if (qla2x00_eh_wait_on_command(ha, sp->cmd) != + QLA_SUCCESS) + DEBUG2(qla_printk(KERN_WARNING, ha, + "Abort failed while waiting -- %lx\n", + sp->cmd->serial_number)); + + } + spin_lock_irqsave(&pha->hardware_lock, flags); + } + spin_unlock_irqrestore(&pha->hardware_lock, flags); +} + static void qla2x00_block_error_handler(struct scsi_cmnd *cmnd) { @@ -1073,7 +1113,7 @@ qla2xxx_slave_configure(struct scsi_device *sdev) else scsi_deactivate_tcq(sdev, ha->max_q_depth); - rport->dev_loss_tmo = ha->port_down_retry_count + 5; + rport->dev_loss_tmo = ha->port_down_retry_count; return 0; } @@ -1629,9 +1669,6 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) } host->can_queue = ha->request_q_length + 128; - /* load the F/W, read paramaters, and init the H/W */ - ha->instance = num_hosts; - mutex_init(&ha->vport_lock); init_completion(&ha->mbx_cmd_comp); complete(&ha->mbx_cmd_comp); @@ -1679,7 +1716,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) host->this_id = 255; host->cmd_per_lun = 3; - host->unique_id = ha->instance; + host->unique_id = host->host_no; host->max_cmd_len = MAX_CMDSZ; host->max_channel = MAX_BUSES - 1; host->max_lun = MAX_LUNS; @@ -1700,8 +1737,6 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->flags.init_done = 1; ha->flags.online = 1; - num_hosts++; - ret = scsi_add_host(host, &pdev->dev); if (ret) goto probe_failed; @@ -1813,27 +1848,21 @@ static inline void qla2x00_schedule_rport_del(struct scsi_qla_host *ha, fc_port_t *fcport, int defer) { - unsigned long flags; struct fc_rport *rport; + scsi_qla_host_t *pha = to_qla_parent(ha); if (!fcport->rport) return; rport = fcport->rport; if (defer) { - spin_lock_irqsave(&fcport->rport_lock, flags); + spin_lock_irq(ha->host->host_lock); fcport->drport = rport; - fcport->rport = NULL; - *(fc_port_t **)rport->dd_data = NULL; - spin_unlock_irqrestore(&fcport->rport_lock, flags); - set_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags); - } else { - spin_lock_irqsave(&fcport->rport_lock, flags); - fcport->rport = NULL; - *(fc_port_t **)rport->dd_data = NULL; - spin_unlock_irqrestore(&fcport->rport_lock, flags); + spin_unlock_irq(ha->host->host_lock); + set_bit(FCPORT_UPDATE_NEEDED, &pha->dpc_flags); + qla2xxx_wake_dpc(pha); + } else fc_remote_port_delete(rport); - } } /* @@ -1903,7 +1932,7 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer) scsi_qla_host_t *pha = to_qla_parent(ha); list_for_each_entry(fcport, &pha->fcports, list) { - if (ha->vp_idx != 0 && ha->vp_idx != fcport->vp_idx) + if (ha->vp_idx != fcport->vp_idx) continue; /* * No point in marking the device as lost, if the device is @@ -1911,17 +1940,10 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer) */ if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD) continue; - if (atomic_read(&fcport->state) == FCS_ONLINE) { - if (defer) - qla2x00_schedule_rport_del(ha, fcport, defer); - else if (ha->vp_idx == fcport->vp_idx) - qla2x00_schedule_rport_del(ha, fcport, defer); - } + if (atomic_read(&fcport->state) == FCS_ONLINE) + qla2x00_schedule_rport_del(ha, fcport, defer); atomic_set(&fcport->state, FCS_DEVICE_LOST); } - - if (defer) - qla2xxx_wake_dpc(ha); } /* @@ -2156,7 +2178,7 @@ qla2x00_alloc_work(struct scsi_qla_host *ha, enum qla_work_type type, static int qla2x00_post_work(struct scsi_qla_host *ha, struct qla_work_evt *e, int locked) { - unsigned long flags; + unsigned long uninitialized_var(flags); scsi_qla_host_t *pha = to_qla_parent(ha); if (!locked) @@ -2313,8 +2335,10 @@ qla2x00_do_dpc(void *data) ha->host_no)); } - if (test_and_clear_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags)) + if (test_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags)) { qla2x00_update_fcports(ha); + clear_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags); + } if (test_and_clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) && (!(test_and_set_bit(RESET_ACTIVE, &ha->dpc_flags)))) { diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index 1728ab3ccb20..1bca74474935 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -869,11 +869,9 @@ qla24xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, uint32_t i; uint32_t *dwptr; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - unsigned long flags; ret = QLA_SUCCESS; - spin_lock_irqsave(&ha->hardware_lock, flags); /* Enable flash write. */ WRT_REG_DWORD(®->ctrl_status, RD_REG_DWORD(®->ctrl_status) | CSRX_FLASH_ENABLE); @@ -907,7 +905,6 @@ qla24xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, WRT_REG_DWORD(®->ctrl_status, RD_REG_DWORD(®->ctrl_status) & ~CSRX_FLASH_ENABLE); RD_REG_DWORD(®->ctrl_status); /* PCI Posting. */ - spin_unlock_irqrestore(&ha->hardware_lock, flags); return ret; } @@ -2306,6 +2303,51 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf) } static int +qla2xxx_is_vpd_valid(uint8_t *pos, uint8_t *end) +{ + if (pos >= end || *pos != 0x82) + return 0; + + pos += 3 + pos[1]; + if (pos >= end || *pos != 0x90) + return 0; + + pos += 3 + pos[1]; + if (pos >= end || *pos != 0x78) + return 0; + + return 1; +} + +int +qla2xxx_get_vpd_field(scsi_qla_host_t *ha, char *key, char *str, size_t size) +{ + uint8_t *pos = ha->vpd; + uint8_t *end = pos + ha->vpd_size; + int len = 0; + + if (!IS_FWI2_CAPABLE(ha) || !qla2xxx_is_vpd_valid(pos, end)) + return 0; + + while (pos < end && *pos != 0x78) { + len = (*pos == 0x82) ? pos[1] : pos[2]; + + if (!strncmp(pos, key, strlen(key))) + break; + + if (*pos != 0x90 && *pos != 0x91) + pos += len; + + pos += 3; + } + + if (pos < end - len && *pos != 0x78) + return snprintf(str, size, "%.*s", len, pos + 3); + + return 0; +} + +static int qla2xxx_hw_event_store(scsi_qla_host_t *ha, uint32_t *fdata) { uint32_t d[2], faddr; diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index d058c8862b35..676c390db354 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -7,7 +7,7 @@ /* * Driver version */ -#define QLA2XXX_VERSION "8.02.01-k4" +#define QLA2XXX_VERSION "8.02.01-k6" #define QLA_DRIVER_MAJOR_VER 8 #define QLA_DRIVER_MINOR_VER 2 diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 5822dd595826..88bebb13bc52 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -46,6 +46,8 @@ MODULE_PARM_DESC(ql4xextended_error_logging, int ql4_mod_unload = 0; +#define QL4_DEF_QDEPTH 32 + /* * SCSI host template entry points */ @@ -1387,7 +1389,7 @@ static int qla4xxx_slave_alloc(struct scsi_device *sdev) sdev->hostdata = ddb; sdev->tagged_supported = 1; - scsi_activate_tcq(sdev, sdev->host->can_queue); + scsi_activate_tcq(sdev, QL4_DEF_QDEPTH); return 0; } diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 36c92f961e15..ee6be596503d 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -197,11 +197,43 @@ static void scsi_pool_free_command(struct scsi_host_cmd_pool *pool, struct scsi_cmnd *cmd) { + if (cmd->prot_sdb) + kmem_cache_free(scsi_sdb_cache, cmd->prot_sdb); + kmem_cache_free(pool->sense_slab, cmd->sense_buffer); kmem_cache_free(pool->cmd_slab, cmd); } /** + * scsi_host_alloc_command - internal function to allocate command + * @shost: SCSI host whose pool to allocate from + * @gfp_mask: mask for the allocation + * + * Returns a fully allocated command with sense buffer and protection + * data buffer (where applicable) or NULL on failure + */ +static struct scsi_cmnd * +scsi_host_alloc_command(struct Scsi_Host *shost, gfp_t gfp_mask) +{ + struct scsi_cmnd *cmd; + + cmd = scsi_pool_alloc_command(shost->cmd_pool, gfp_mask); + if (!cmd) + return NULL; + + if (scsi_host_get_prot(shost) >= SHOST_DIX_TYPE0_PROTECTION) { + cmd->prot_sdb = kmem_cache_zalloc(scsi_sdb_cache, gfp_mask); + + if (!cmd->prot_sdb) { + scsi_pool_free_command(shost->cmd_pool, cmd); + return NULL; + } + } + + return cmd; +} + +/** * __scsi_get_command - Allocate a struct scsi_cmnd * @shost: host to transmit command * @gfp_mask: allocation mask @@ -214,7 +246,7 @@ struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, gfp_t gfp_mask) struct scsi_cmnd *cmd; unsigned char *buf; - cmd = scsi_pool_alloc_command(shost->cmd_pool, gfp_mask); + cmd = scsi_host_alloc_command(shost, gfp_mask); if (unlikely(!cmd)) { unsigned long flags; @@ -457,7 +489,7 @@ int scsi_setup_command_freelist(struct Scsi_Host *shost) /* * Get one backup command for this host. */ - cmd = scsi_pool_alloc_command(shost->cmd_pool, gfp_mask); + cmd = scsi_host_alloc_command(shost, gfp_mask); if (!cmd) { scsi_put_host_cmd_pool(gfp_mask); shost->cmd_pool = NULL; @@ -902,11 +934,20 @@ void scsi_adjust_queue_depth(struct scsi_device *sdev, int tagged, int tags) spin_lock_irqsave(sdev->request_queue->queue_lock, flags); - /* Check to see if the queue is managed by the block layer. - * If it is, and we fail to adjust the depth, exit. */ - if (blk_queue_tagged(sdev->request_queue) && - blk_queue_resize_tags(sdev->request_queue, tags) != 0) - goto out; + /* + * Check to see if the queue is managed by the block layer. + * If it is, and we fail to adjust the depth, exit. + * + * Do not resize the tag map if it is a host wide share bqt, + * because the size should be the hosts's can_queue. If there + * is more IO than the LLD's can_queue (so there are not enuogh + * tags) request_fn's host queue ready check will handle it. + */ + if (!sdev->host->bqt) { + if (blk_queue_tagged(sdev->request_queue) && + blk_queue_resize_tags(sdev->request_queue, tags) != 0) + goto out; + } sdev->queue_depth = tags; switch (tagged) { diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 01d11a01ffbf..27c633f55794 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -1753,7 +1753,7 @@ static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev) open_devip = sdebug_device_create(sdbg_host, GFP_ATOMIC); if (!open_devip) { printk(KERN_ERR "%s: out of memory at line %d\n", - __FUNCTION__, __LINE__); + __func__, __LINE__); return NULL; } } @@ -2656,7 +2656,7 @@ static int sdebug_add_adapter(void) sdbg_host = kzalloc(sizeof(*sdbg_host),GFP_KERNEL); if (NULL == sdbg_host) { printk(KERN_ERR "%s: out of memory at line %d\n", - __FUNCTION__, __LINE__); + __func__, __LINE__); return -ENOMEM; } @@ -2667,7 +2667,7 @@ static int sdebug_add_adapter(void) sdbg_devinfo = sdebug_device_create(sdbg_host, GFP_KERNEL); if (!sdbg_devinfo) { printk(KERN_ERR "%s: out of memory at line %d\n", - __FUNCTION__, __LINE__); + __func__, __LINE__); error = -ENOMEM; goto clean; } @@ -2987,7 +2987,7 @@ static int sdebug_driver_probe(struct device * dev) hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host)); if (NULL == hpnt) { - printk(KERN_ERR "%s: scsi_register failed\n", __FUNCTION__); + printk(KERN_ERR "%s: scsi_register failed\n", __func__); error = -ENODEV; return error; } @@ -3002,7 +3002,7 @@ static int sdebug_driver_probe(struct device * dev) error = scsi_add_host(hpnt, &sdbg_host->dev); if (error) { - printk(KERN_ERR "%s: scsi_add_host failed\n", __FUNCTION__); + printk(KERN_ERR "%s: scsi_add_host failed\n", __func__); error = -ENODEV; scsi_host_put(hpnt); } else @@ -3021,7 +3021,7 @@ static int sdebug_driver_remove(struct device * dev) if (!sdbg_host) { printk(KERN_ERR "%s: Unable to locate host info\n", - __FUNCTION__); + __func__); return -ENODEV; } diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index a235802f2981..4969e4ec75ea 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -272,7 +272,7 @@ static void scsi_strcpy_devinfo(char *name, char *to, size_t to_length, } if (from_length > to_length) printk(KERN_WARNING "%s: %s string '%s' is too long\n", - __FUNCTION__, name, from); + __func__, name, from); } /** @@ -298,7 +298,7 @@ static int scsi_dev_info_list_add(int compatible, char *vendor, char *model, devinfo = kmalloc(sizeof(*devinfo), GFP_KERNEL); if (!devinfo) { - printk(KERN_ERR "%s: no memory\n", __FUNCTION__); + printk(KERN_ERR "%s: no memory\n", __func__); return -ENOMEM; } @@ -363,7 +363,7 @@ static int scsi_dev_info_list_add_str(char *dev_list) strflags = strsep(&next, next_check); if (!model || !strflags) { printk(KERN_ERR "%s: bad dev info string '%s' '%s'" - " '%s'\n", __FUNCTION__, vendor, model, + " '%s'\n", __func__, vendor, model, strflags); res = -EINVAL; } else diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 006a95916f72..880051c89bde 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -139,7 +139,7 @@ void scsi_add_timer(struct scsi_cmnd *scmd, int timeout, scmd->eh_timeout.function = (void (*)(unsigned long)) complete; SCSI_LOG_ERROR_RECOVERY(5, printk("%s: scmd: %p, time:" - " %d, (%p)\n", __FUNCTION__, + " %d, (%p)\n", __func__, scmd, timeout, complete)); add_timer(&scmd->eh_timeout); @@ -163,7 +163,7 @@ int scsi_delete_timer(struct scsi_cmnd *scmd) rtn = del_timer(&scmd->eh_timeout); SCSI_LOG_ERROR_RECOVERY(5, printk("%s: scmd: %p," - " rtn: %d\n", __FUNCTION__, + " rtn: %d\n", __func__, scmd, rtn)); scmd->eh_timeout.data = (unsigned long)NULL; @@ -233,7 +233,7 @@ int scsi_block_when_processing_errors(struct scsi_device *sdev) online = scsi_device_online(sdev); - SCSI_LOG_ERROR_RECOVERY(5, printk("%s: rtn: %d\n", __FUNCTION__, + SCSI_LOG_ERROR_RECOVERY(5, printk("%s: rtn: %d\n", __func__, online)); return online; @@ -271,7 +271,7 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost, SCSI_LOG_ERROR_RECOVERY(3, sdev_printk(KERN_INFO, sdev, "%s: cmds failed: %d, cancel: %d\n", - __FUNCTION__, cmd_failed, + __func__, cmd_failed, cmd_cancel)); cmd_cancel = 0; cmd_failed = 0; @@ -344,6 +344,9 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) return /* soft_error */ SUCCESS; case ABORTED_COMMAND: + if (sshdr.asc == 0x10) /* DIF */ + return SUCCESS; + return NEEDS_RETRY; case NOT_READY: case UNIT_ATTENTION: @@ -470,7 +473,7 @@ static void scsi_eh_done(struct scsi_cmnd *scmd) SCSI_LOG_ERROR_RECOVERY(3, printk("%s scmd: %p result: %x\n", - __FUNCTION__, scmd, scmd->result)); + __func__, scmd, scmd->result)); eh_action = scmd->device->host->eh_action; if (eh_action) @@ -487,7 +490,7 @@ static int scsi_try_host_reset(struct scsi_cmnd *scmd) int rtn; SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Host RST\n", - __FUNCTION__)); + __func__)); if (!scmd->device->host->hostt->eh_host_reset_handler) return FAILED; @@ -516,7 +519,7 @@ static int scsi_try_bus_reset(struct scsi_cmnd *scmd) int rtn; SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Bus RST\n", - __FUNCTION__)); + __func__)); if (!scmd->device->host->hostt->eh_bus_reset_handler) return FAILED; @@ -664,7 +667,10 @@ void scsi_eh_prep_cmnd(struct scsi_cmnd *scmd, struct scsi_eh_save *ses, ses->sdb = scmd->sdb; ses->next_rq = scmd->request->next_rq; ses->result = scmd->result; + ses->underflow = scmd->underflow; + ses->prot_op = scmd->prot_op; + scmd->prot_op = SCSI_PROT_NORMAL; scmd->cmnd = ses->eh_cmnd; memset(scmd->cmnd, 0, BLK_MAX_CDB); memset(&scmd->sdb, 0, sizeof(scmd->sdb)); @@ -722,6 +728,8 @@ void scsi_eh_restore_cmnd(struct scsi_cmnd* scmd, struct scsi_eh_save *ses) scmd->sdb = ses->sdb; scmd->request->next_rq = ses->next_rq; scmd->result = ses->result; + scmd->underflow = ses->underflow; + scmd->prot_op = ses->prot_op; } EXPORT_SYMBOL(scsi_eh_restore_cmnd); @@ -766,7 +774,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd: %p, timeleft: %ld\n", - __FUNCTION__, scmd, timeleft)); + __func__, scmd, timeleft)); /* * If there is time left scsi_eh_done got called, and we will @@ -778,7 +786,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, rtn = scsi_eh_completed_normally(scmd); SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scsi_eh_completed_normally %x\n", - __FUNCTION__, rtn)); + __func__, rtn)); switch (rtn) { case SUCCESS: @@ -913,7 +921,7 @@ retry_tur: rtn = scsi_send_eh_cmnd(scmd, tur_command, 6, SENSE_TIMEOUT, 0); SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd %p rtn %x\n", - __FUNCTION__, scmd, rtn)); + __func__, scmd, rtn)); switch (rtn) { case NEEDS_RETRY: @@ -1296,7 +1304,7 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) if (!scsi_device_online(scmd->device)) { SCSI_LOG_ERROR_RECOVERY(5, printk("%s: device offline - report" " as SUCCESS\n", - __FUNCTION__)); + __func__)); return SUCCESS; } @@ -1511,7 +1519,7 @@ static void scsi_restart_operations(struct Scsi_Host *shost) * ioctls to queued block devices. */ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: waking up host to restart\n", - __FUNCTION__)); + __func__)); spin_lock_irqsave(shost->host_lock, flags); if (scsi_host_set_state(shost, SHOST_RUNNING)) @@ -1835,7 +1843,7 @@ scsi_reset_provider(struct scsi_device *dev, int flag) */ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: waking up host to restart after TMF\n", - __FUNCTION__)); + __func__)); wake_up(&shost->host_wait); diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 88d1b5f44e59..ff5d56b3ee4d 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -65,7 +65,7 @@ static struct scsi_host_sg_pool scsi_sg_pools[] = { }; #undef SP -static struct kmem_cache *scsi_sdb_cache; +struct kmem_cache *scsi_sdb_cache; static void scsi_run_queue(struct request_queue *q); @@ -787,6 +787,9 @@ void scsi_release_buffers(struct scsi_cmnd *cmd) kmem_cache_free(scsi_sdb_cache, bidi_sdb); cmd->request->next_rq->special = NULL; } + + if (scsi_prot_sg_count(cmd)) + scsi_free_sgtable(cmd->prot_sdb); } EXPORT_SYMBOL(scsi_release_buffers); @@ -947,9 +950,14 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) * 6-byte command. */ scsi_requeue_command(q, cmd); - return; - } else { + } else if (sshdr.asc == 0x10) /* DIX */ + scsi_end_request(cmd, -EIO, this_count, 0); + else scsi_end_request(cmd, -EIO, this_count, 1); + return; + case ABORTED_COMMAND: + if (sshdr.asc == 0x10) { /* DIF */ + scsi_end_request(cmd, -EIO, this_count, 0); return; } break; @@ -1072,6 +1080,26 @@ int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask) goto err_exit; } + if (blk_integrity_rq(cmd->request)) { + struct scsi_data_buffer *prot_sdb = cmd->prot_sdb; + int ivecs, count; + + BUG_ON(prot_sdb == NULL); + ivecs = blk_rq_count_integrity_sg(cmd->request); + + if (scsi_alloc_sgtable(prot_sdb, ivecs, gfp_mask)) { + error = BLKPREP_DEFER; + goto err_exit; + } + + count = blk_rq_map_integrity_sg(cmd->request, + prot_sdb->table.sgl); + BUG_ON(unlikely(count > ivecs)); + + cmd->prot_sdb = prot_sdb; + cmd->prot_sdb->table.nents = count; + } + return BLKPREP_OK ; err_exit: @@ -1367,7 +1395,7 @@ static void scsi_kill_request(struct request *req, struct request_queue *q) if (unlikely(cmd == NULL)) { printk(KERN_CRIT "impossible request in %s.\n", - __FUNCTION__); + __func__); BUG(); } @@ -1491,12 +1519,27 @@ static void scsi_request_fn(struct request_queue *q) printk(KERN_CRIT "impossible request in %s.\n" "please mail a stack trace to " "linux-scsi@vger.kernel.org\n", - __FUNCTION__); + __func__); blk_dump_rq_flags(req, "foo"); BUG(); } spin_lock(shost->host_lock); + /* + * We hit this when the driver is using a host wide + * tag map. For device level tag maps the queue_depth check + * in the device ready fn would prevent us from trying + * to allocate a tag. Since the map is a shared host resource + * we add the dev to the starved list so it eventually gets + * a run when a tag is freed. + */ + if (blk_queue_tagged(q) && !blk_rq_tagged(req)) { + if (list_empty(&sdev->starved_entry)) + list_add_tail(&sdev->starved_entry, + &shost->starved_list); + goto not_ready; + } + if (!scsi_host_queue_ready(q, shost, sdev)) goto not_ready; if (scsi_target(sdev)->single_lun) { @@ -2486,7 +2529,7 @@ void *scsi_kmap_atomic_sg(struct scatterlist *sgl, int sg_count, if (unlikely(i == sg_count)) { printk(KERN_ERR "%s: Bytes in sg: %zu, requested offset %zu, " "elements %d\n", - __FUNCTION__, sg_len, *offset, sg_count); + __func__, sg_len, *offset, sg_count); WARN_ON(1); return NULL; } diff --git a/drivers/scsi/scsi_netlink.c b/drivers/scsi/scsi_netlink.c index 370c78cc1cb5..ae7ed9a22662 100644 --- a/drivers/scsi/scsi_netlink.c +++ b/drivers/scsi/scsi_netlink.c @@ -55,7 +55,7 @@ scsi_nl_rcv_msg(struct sk_buff *skb) if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) || (skb->len < nlh->nlmsg_len)) { printk(KERN_WARNING "%s: discarding partial skb\n", - __FUNCTION__); + __func__); return; } @@ -82,7 +82,7 @@ scsi_nl_rcv_msg(struct sk_buff *skb) if (nlh->nlmsg_len < (sizeof(*nlh) + hdr->msglen)) { printk(KERN_WARNING "%s: discarding partial message\n", - __FUNCTION__); + __func__); return; } @@ -139,7 +139,7 @@ scsi_netlink_init(void) error = netlink_register_notifier(&scsi_netlink_notifier); if (error) { printk(KERN_ERR "%s: register of event handler failed - %d\n", - __FUNCTION__, error); + __func__, error); return; } @@ -148,7 +148,7 @@ scsi_netlink_init(void) THIS_MODULE); if (!scsi_nl_sock) { printk(KERN_ERR "%s: register of recieve handler failed\n", - __FUNCTION__); + __func__); netlink_unregister_notifier(&scsi_netlink_notifier); } diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index b33e72516ef8..79f0f7511204 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -77,6 +77,7 @@ extern void scsi_exit_queue(void); struct request_queue; struct request; extern int scsi_prep_fn(struct request_queue *, struct request *); +extern struct kmem_cache *scsi_sdb_cache; /* scsi_proc.c */ #ifdef CONFIG_SCSI_PROC_FS diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c index e4a0d2f9b357..c6a904a45bf9 100644 --- a/drivers/scsi/scsi_proc.c +++ b/drivers/scsi/scsi_proc.c @@ -114,7 +114,7 @@ void scsi_proc_hostdir_add(struct scsi_host_template *sht) sht->proc_dir = proc_mkdir(sht->proc_name, proc_scsi); if (!sht->proc_dir) printk(KERN_ERR "%s: proc_mkdir failed for %s\n", - __FUNCTION__, sht->proc_name); + __func__, sht->proc_name); else sht->proc_dir->owner = sht->module; } @@ -157,7 +157,7 @@ void scsi_proc_host_add(struct Scsi_Host *shost) sht->proc_dir, proc_scsi_read, shost); if (!p) { printk(KERN_ERR "%s: Failed to register host %d in" - "%s\n", __FUNCTION__, shost->host_no, + "%s\n", __func__, shost->host_no, sht->proc_name); return; } diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 196fe3af0d5e..84b4879cff11 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -318,7 +318,7 @@ out_device_destroy: put_device(&sdev->sdev_gendev); out: if (display_failure_msg) - printk(ALLOC_FAILURE_MSG, __FUNCTION__); + printk(ALLOC_FAILURE_MSG, __func__); return NULL; } @@ -404,7 +404,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, starget = kzalloc(size, GFP_KERNEL); if (!starget) { - printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__); + printk(KERN_ERR "%s: allocation failure\n", __func__); return NULL; } dev = &starget->dev; @@ -1337,7 +1337,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, lun_data = kmalloc(length, GFP_ATOMIC | (sdev->host->unchecked_isa_dma ? __GFP_DMA : 0)); if (!lun_data) { - printk(ALLOC_FAILURE_MSG, __FUNCTION__); + printk(ALLOC_FAILURE_MSG, __func__); goto out; } @@ -1649,7 +1649,7 @@ int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel, { SCSI_LOG_SCAN_BUS(3, shost_printk (KERN_INFO, shost, "%s: <%u:%u:%u>\n", - __FUNCTION__, channel, id, lun)); + __func__, channel, id, lun)); if (((channel != SCAN_WILD_CARD) && (channel > shost->max_channel)) || ((id != SCAN_WILD_CARD) && (id >= shost->max_id)) || @@ -1703,7 +1703,7 @@ static struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost) return NULL; if (shost->async_scan) { - printk("%s called twice for host %d", __FUNCTION__, + printk("%s called twice for host %d", __func__, shost->host_no); dump_stack(); return NULL; @@ -1757,9 +1757,10 @@ static void scsi_finish_async_scan(struct async_scan_data *data) mutex_lock(&shost->scan_mutex); if (!shost->async_scan) { - printk("%s called twice for host %d", __FUNCTION__, + printk("%s called twice for host %d", __func__, shost->host_no); dump_stack(); + mutex_unlock(&shost->scan_mutex); return; } diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index b6e561059779..ab3c71869be5 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -249,6 +249,8 @@ shost_rd_attr(cmd_per_lun, "%hd\n"); shost_rd_attr(can_queue, "%hd\n"); shost_rd_attr(sg_tablesize, "%hu\n"); shost_rd_attr(unchecked_isa_dma, "%d\n"); +shost_rd_attr(prot_capabilities, "%u\n"); +shost_rd_attr(prot_guard_type, "%hd\n"); shost_rd_attr2(proc_name, hostt->proc_name, "%s\n"); static struct attribute *scsi_sysfs_shost_attrs[] = { @@ -263,6 +265,8 @@ static struct attribute *scsi_sysfs_shost_attrs[] = { &dev_attr_hstate.attr, &dev_attr_supported_mode.attr, &dev_attr_active_mode.attr, + &dev_attr_prot_capabilities.attr, + &dev_attr_prot_guard_type.attr, NULL }; diff --git a/drivers/scsi/scsi_tgt_priv.h b/drivers/scsi/scsi_tgt_priv.h index cb92888948f9..fe4c62177f78 100644 --- a/drivers/scsi/scsi_tgt_priv.h +++ b/drivers/scsi/scsi_tgt_priv.h @@ -6,7 +6,7 @@ struct task_struct; /* tmp - will replace with SCSI logging stuff */ #define eprintk(fmt, args...) \ do { \ - printk("%s(%d) " fmt, __FUNCTION__, __LINE__, ##args); \ + printk("%s(%d) " fmt, __func__, __LINE__, ##args); \ } while (0) #define dprintk(fmt, args...) diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 5fd64e70029d..56823fd1fb84 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -417,15 +417,16 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev, fc_host->next_vport_number = 0; fc_host->npiv_vports_inuse = 0; - snprintf(fc_host->work_q_name, KOBJ_NAME_LEN, "fc_wq_%d", - shost->host_no); + snprintf(fc_host->work_q_name, sizeof(fc_host->work_q_name), + "fc_wq_%d", shost->host_no); fc_host->work_q = create_singlethread_workqueue( fc_host->work_q_name); if (!fc_host->work_q) return -ENOMEM; - snprintf(fc_host->devloss_work_q_name, KOBJ_NAME_LEN, "fc_dl_%d", - shost->host_no); + snprintf(fc_host->devloss_work_q_name, + sizeof(fc_host->devloss_work_q_name), + "fc_dl_%d", shost->host_no); fc_host->devloss_work_q = create_singlethread_workqueue( fc_host->devloss_work_q_name); if (!fc_host->devloss_work_q) { @@ -570,7 +571,7 @@ send_fail: name = get_fc_host_event_code_name(event_code); printk(KERN_WARNING "%s: Dropped Event : host %d %s data 0x%08x - err %d\n", - __FUNCTION__, shost->host_no, + __func__, shost->host_no, (name) ? name : "<unknown>", event_data, err); return; } @@ -643,7 +644,7 @@ send_vendor_fail_skb: send_vendor_fail: printk(KERN_WARNING "%s: Dropped Event : host %d vendor_unique - err %d\n", - __FUNCTION__, shost->host_no, err); + __func__, shost->host_no, err); return; } EXPORT_SYMBOL(fc_host_post_vendor_event); @@ -2463,7 +2464,7 @@ fc_rport_create(struct Scsi_Host *shost, int channel, size = (sizeof(struct fc_rport) + fci->f->dd_fcrport_size); rport = kzalloc(size, GFP_KERNEL); if (unlikely(!rport)) { - printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__); + printk(KERN_ERR "%s: allocation failure\n", __func__); return NULL; } @@ -3136,7 +3137,7 @@ fc_vport_create(struct Scsi_Host *shost, int channel, struct device *pdev, size = (sizeof(struct fc_vport) + fci->f->dd_fcvport_size); vport = kzalloc(size, GFP_KERNEL); if (unlikely(!vport)) { - printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__); + printk(KERN_ERR "%s: allocation failure\n", __func__); return -ENOMEM; } @@ -3200,7 +3201,7 @@ fc_vport_create(struct Scsi_Host *shost, int channel, struct device *pdev, printk(KERN_ERR "%s: Cannot create vport symlinks for " "%s, err=%d\n", - __FUNCTION__, dev->bus_id, error); + __func__, dev->bus_id, error); } spin_lock_irqsave(shost->host_lock, flags); vport->flags &= ~FC_VPORT_CREATING; @@ -3313,7 +3314,7 @@ fc_vport_sched_delete(struct work_struct *work) if (stat) dev_printk(KERN_ERR, vport->dev.parent, "%s: %s could not be deleted created via " - "shost%d channel %d - error %d\n", __FUNCTION__, + "shost%d channel %d - error %d\n", __func__, vport->dev.bus_id, vport->shost->host_no, vport->channel, stat); } diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 3af7cbcc5c5d..043c3921164f 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -170,7 +170,7 @@ iscsi_create_endpoint(int dd_size) int err; for (id = 1; id < ISCSI_MAX_EPID; id++) { - dev = class_find_device(&iscsi_endpoint_class, &id, + dev = class_find_device(&iscsi_endpoint_class, NULL, &id, iscsi_match_epid); if (!dev) break; @@ -222,7 +222,7 @@ struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle) struct iscsi_endpoint *ep; struct device *dev; - dev = class_find_device(&iscsi_endpoint_class, &handle, + dev = class_find_device(&iscsi_endpoint_class, NULL, &handle, iscsi_match_epid); if (!dev) return NULL; @@ -247,8 +247,8 @@ static int iscsi_setup_host(struct transport_container *tc, struct device *dev, atomic_set(&ihost->nr_scans, 0); mutex_init(&ihost->mutex); - snprintf(ihost->scan_workq_name, KOBJ_NAME_LEN, "iscsi_scan_%d", - shost->host_no); + snprintf(ihost->scan_workq_name, sizeof(ihost->scan_workq_name), + "iscsi_scan_%d", shost->host_no); ihost->scan_workq = create_singlethread_workqueue( ihost->scan_workq_name); if (!ihost->scan_workq) diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index f4461d35ffb9..366609386be1 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -779,7 +779,7 @@ static void sas_port_create_link(struct sas_port *port, return; err: printk(KERN_ERR "%s: Cannot create port links, err=%d\n", - __FUNCTION__, res); + __func__, res); } static void sas_port_delete_link(struct sas_port *port, @@ -1029,7 +1029,7 @@ void sas_port_mark_backlink(struct sas_port *port) return; err: printk(KERN_ERR "%s: Cannot create port backlink, err=%d\n", - __FUNCTION__, res); + __func__, res); } EXPORT_SYMBOL(sas_port_mark_backlink); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 0c63947d8a9d..e5e7d7856454 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -99,8 +99,7 @@ static void scsi_disk_release(struct device *cdev); static void sd_print_sense_hdr(struct scsi_disk *, struct scsi_sense_hdr *); static void sd_print_result(struct scsi_disk *, int); -static DEFINE_IDR(sd_index_idr); -static DEFINE_SPINLOCK(sd_index_lock); +static DEFINE_IDA(sd_index_ida); /* This semaphore is used to mediate the 0->1 reference get in the * face of object destruction (i.e. we can't allow a get on an @@ -234,6 +233,24 @@ sd_show_allow_restart(struct device *dev, struct device_attribute *attr, return snprintf(buf, 40, "%d\n", sdkp->device->allow_restart); } +static ssize_t +sd_show_protection_type(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct scsi_disk *sdkp = to_scsi_disk(dev); + + return snprintf(buf, 20, "%u\n", sdkp->protection_type); +} + +static ssize_t +sd_show_app_tag_own(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct scsi_disk *sdkp = to_scsi_disk(dev); + + return snprintf(buf, 20, "%u\n", sdkp->ATO); +} + static struct device_attribute sd_disk_attrs[] = { __ATTR(cache_type, S_IRUGO|S_IWUSR, sd_show_cache_type, sd_store_cache_type), @@ -242,6 +259,8 @@ static struct device_attribute sd_disk_attrs[] = { sd_store_allow_restart), __ATTR(manage_start_stop, S_IRUGO|S_IWUSR, sd_show_manage_start_stop, sd_store_manage_start_stop), + __ATTR(protection_type, S_IRUGO, sd_show_protection_type, NULL), + __ATTR(app_tag_own, S_IRUGO, sd_show_app_tag_own, NULL), __ATTR_NULL, }; @@ -354,7 +373,9 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) struct scsi_cmnd *SCpnt; struct scsi_device *sdp = q->queuedata; struct gendisk *disk = rq->rq_disk; + struct scsi_disk *sdkp; sector_t block = rq->sector; + sector_t threshold; unsigned int this_count = rq->nr_sectors; unsigned int timeout = sdp->timeout; int ret; @@ -370,6 +391,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) if (ret != BLKPREP_OK) goto out; SCpnt = rq->special; + sdkp = scsi_disk(disk); /* from here on until we're complete, any goto out * is used for a killable error condition */ @@ -401,13 +423,21 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) } /* - * Some devices (some sdcards for one) don't like it if the - * last sector gets read in a larger then 1 sector read. + * Some SD card readers can't handle multi-sector accesses which touch + * the last one or two hardware sectors. Split accesses as needed. */ - if (unlikely(sdp->last_sector_bug && - rq->nr_sectors > sdp->sector_size / 512 && - block + this_count == get_capacity(disk))) - this_count -= sdp->sector_size / 512; + threshold = get_capacity(disk) - SD_LAST_BUGGY_SECTORS * + (sdp->sector_size / 512); + + if (unlikely(sdp->last_sector_bug && block + this_count > threshold)) { + if (block < threshold) { + /* Access up to the threshold but not beyond */ + this_count = threshold - block; + } else { + /* Access only a single hardware sector */ + this_count = sdp->sector_size / 512; + } + } SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "block=%llu\n", (unsigned long long)block)); @@ -459,6 +489,11 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) } SCpnt->cmnd[0] = WRITE_6; SCpnt->sc_data_direction = DMA_TO_DEVICE; + + if (blk_integrity_rq(rq) && + sd_dif_prepare(rq, block, sdp->sector_size) == -EIO) + goto out; + } else if (rq_data_dir(rq) == READ) { SCpnt->cmnd[0] = READ_6; SCpnt->sc_data_direction = DMA_FROM_DEVICE; @@ -473,8 +508,12 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) "writing" : "reading", this_count, rq->nr_sectors)); - SCpnt->cmnd[1] = 0; - + /* Set RDPROTECT/WRPROTECT if disk is formatted with DIF */ + if (scsi_host_dif_capable(sdp->host, sdkp->protection_type)) + SCpnt->cmnd[1] = 1 << 5; + else + SCpnt->cmnd[1] = 0; + if (block > 0xffffffff) { SCpnt->cmnd[0] += READ_16 - READ_6; SCpnt->cmnd[1] |= blk_fua_rq(rq) ? 0x8 : 0; @@ -492,6 +531,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) SCpnt->cmnd[13] = (unsigned char) this_count & 0xff; SCpnt->cmnd[14] = SCpnt->cmnd[15] = 0; } else if ((this_count > 0xff) || (block > 0x1fffff) || + scsi_device_protection(SCpnt->device) || SCpnt->device->use_10_for_rw) { if (this_count > 0xffff) this_count = 0xffff; @@ -526,6 +566,10 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) } SCpnt->sdb.length = this_count * sdp->sector_size; + /* If DIF or DIX is enabled, tell HBA how to handle request */ + if (sdkp->protection_type || scsi_prot_sg_count(SCpnt)) + sd_dif_op(SCpnt, sdkp->protection_type, scsi_prot_sg_count(SCpnt)); + /* * We shouldn't disconnect in the middle of a sector, so with a dumb * host adapter, it's safe to assume that we can at least transfer @@ -920,6 +964,48 @@ static struct block_device_operations sd_fops = { .revalidate_disk = sd_revalidate_disk, }; +static unsigned int sd_completed_bytes(struct scsi_cmnd *scmd) +{ + u64 start_lba = scmd->request->sector; + u64 end_lba = scmd->request->sector + (scsi_bufflen(scmd) / 512); + u64 bad_lba; + int info_valid; + + if (!blk_fs_request(scmd->request)) + return 0; + + info_valid = scsi_get_sense_info_fld(scmd->sense_buffer, + SCSI_SENSE_BUFFERSIZE, + &bad_lba); + if (!info_valid) + return 0; + + if (scsi_bufflen(scmd) <= scmd->device->sector_size) + return 0; + + if (scmd->device->sector_size < 512) { + /* only legitimate sector_size here is 256 */ + start_lba <<= 1; + end_lba <<= 1; + } else { + /* be careful ... don't want any overflows */ + u64 factor = scmd->device->sector_size / 512; + do_div(start_lba, factor); + do_div(end_lba, factor); + } + + /* The bad lba was reported incorrectly, we have no idea where + * the error is. + */ + if (bad_lba < start_lba || bad_lba >= end_lba) + return 0; + + /* This computation should always be done in terms of + * the resolution of the device's medium. + */ + return (bad_lba - start_lba) * scmd->device->sector_size; +} + /** * sd_done - bottom half handler: called when the lower level * driver has completed (successfully or otherwise) a scsi command. @@ -930,15 +1016,10 @@ static struct block_device_operations sd_fops = { static int sd_done(struct scsi_cmnd *SCpnt) { int result = SCpnt->result; - unsigned int xfer_size = scsi_bufflen(SCpnt); - unsigned int good_bytes = result ? 0 : xfer_size; - u64 start_lba = SCpnt->request->sector; - u64 end_lba = SCpnt->request->sector + (xfer_size / 512); - u64 bad_lba; + unsigned int good_bytes = result ? 0 : scsi_bufflen(SCpnt); struct scsi_sense_hdr sshdr; int sense_valid = 0; int sense_deferred = 0; - int info_valid; if (result) { sense_valid = scsi_command_normalize_sense(SCpnt, &sshdr); @@ -963,36 +1044,7 @@ static int sd_done(struct scsi_cmnd *SCpnt) switch (sshdr.sense_key) { case HARDWARE_ERROR: case MEDIUM_ERROR: - if (!blk_fs_request(SCpnt->request)) - goto out; - info_valid = scsi_get_sense_info_fld(SCpnt->sense_buffer, - SCSI_SENSE_BUFFERSIZE, - &bad_lba); - if (!info_valid) - goto out; - if (xfer_size <= SCpnt->device->sector_size) - goto out; - if (SCpnt->device->sector_size < 512) { - /* only legitimate sector_size here is 256 */ - start_lba <<= 1; - end_lba <<= 1; - } else { - /* be careful ... don't want any overflows */ - u64 factor = SCpnt->device->sector_size / 512; - do_div(start_lba, factor); - do_div(end_lba, factor); - } - - if (bad_lba < start_lba || bad_lba >= end_lba) - /* the bad lba was reported incorrectly, we have - * no idea where the error is - */ - goto out; - - /* This computation should always be done in terms of - * the resolution of the device's medium. - */ - good_bytes = (bad_lba - start_lba)*SCpnt->device->sector_size; + good_bytes = sd_completed_bytes(SCpnt); break; case RECOVERED_ERROR: case NO_SENSE: @@ -1002,10 +1054,23 @@ static int sd_done(struct scsi_cmnd *SCpnt) scsi_print_sense("sd", SCpnt); SCpnt->result = 0; memset(SCpnt->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); - good_bytes = xfer_size; + good_bytes = scsi_bufflen(SCpnt); + break; + case ABORTED_COMMAND: + if (sshdr.asc == 0x10) { /* DIF: Disk detected corruption */ + scsi_print_result(SCpnt); + scsi_print_sense("sd", SCpnt); + good_bytes = sd_completed_bytes(SCpnt); + } break; case ILLEGAL_REQUEST: - if (SCpnt->device->use_10_for_rw && + if (sshdr.asc == 0x10) { /* DIX: HBA detected corruption */ + scsi_print_result(SCpnt); + scsi_print_sense("sd", SCpnt); + good_bytes = sd_completed_bytes(SCpnt); + } + if (!scsi_device_protection(SCpnt->device) && + SCpnt->device->use_10_for_rw && (SCpnt->cmnd[0] == READ_10 || SCpnt->cmnd[0] == WRITE_10)) SCpnt->device->use_10_for_rw = 0; @@ -1018,6 +1083,9 @@ static int sd_done(struct scsi_cmnd *SCpnt) break; } out: + if (rq_data_dir(SCpnt->request) == READ && scsi_prot_sg_count(SCpnt)) + sd_dif_complete(SCpnt, good_bytes); + return good_bytes; } @@ -1165,6 +1233,49 @@ sd_spinup_disk(struct scsi_disk *sdkp) } } + +/* + * Determine whether disk supports Data Integrity Field. + */ +void sd_read_protection_type(struct scsi_disk *sdkp, unsigned char *buffer) +{ + struct scsi_device *sdp = sdkp->device; + u8 type; + + if (scsi_device_protection(sdp) == 0 || (buffer[12] & 1) == 0) + type = 0; + else + type = ((buffer[12] >> 1) & 7) + 1; /* P_TYPE 0 = Type 1 */ + + switch (type) { + case SD_DIF_TYPE0_PROTECTION: + sdkp->protection_type = 0; + break; + + case SD_DIF_TYPE1_PROTECTION: + case SD_DIF_TYPE3_PROTECTION: + sdkp->protection_type = type; + break; + + case SD_DIF_TYPE2_PROTECTION: + sd_printk(KERN_ERR, sdkp, "formatted with DIF Type 2 " \ + "protection which is currently unsupported. " \ + "Disabling disk!\n"); + goto disable; + + default: + sd_printk(KERN_ERR, sdkp, "formatted with unknown " \ + "protection type %d. Disabling disk!\n", type); + goto disable; + } + + return; + +disable: + sdkp->protection_type = 0; + sdkp->capacity = 0; +} + /* * read disk capacity */ @@ -1174,7 +1285,8 @@ sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer) unsigned char cmd[16]; int the_result, retries; int sector_size = 0; - int longrc = 0; + /* Force READ CAPACITY(16) when PROTECT=1 */ + int longrc = scsi_device_protection(sdkp->device) ? 1 : 0; struct scsi_sense_hdr sshdr; int sense_valid = 0; struct scsi_device *sdp = sdkp->device; @@ -1186,8 +1298,8 @@ repeat: memset((void *) cmd, 0, 16); cmd[0] = SERVICE_ACTION_IN; cmd[1] = SAI_READ_CAPACITY_16; - cmd[13] = 12; - memset((void *) buffer, 0, 12); + cmd[13] = 13; + memset((void *) buffer, 0, 13); } else { cmd[0] = READ_CAPACITY; memset((void *) &cmd[1], 0, 9); @@ -1195,7 +1307,7 @@ repeat: } the_result = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE, - buffer, longrc ? 12 : 8, &sshdr, + buffer, longrc ? 13 : 8, &sshdr, SD_TIMEOUT, SD_MAX_RETRIES); if (media_not_present(sdkp, &sshdr)) @@ -1270,6 +1382,8 @@ repeat: sector_size = (buffer[8] << 24) | (buffer[9] << 16) | (buffer[10] << 8) | buffer[11]; + + sd_read_protection_type(sdkp, buffer); } /* Some devices return the total number of sectors, not the @@ -1531,6 +1645,52 @@ defaults: sdkp->DPOFUA = 0; } +/* + * The ATO bit indicates whether the DIF application tag is available + * for use by the operating system. + */ +void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer) +{ + int res, offset; + struct scsi_device *sdp = sdkp->device; + struct scsi_mode_data data; + struct scsi_sense_hdr sshdr; + + if (sdp->type != TYPE_DISK) + return; + + if (sdkp->protection_type == 0) + return; + + res = scsi_mode_sense(sdp, 1, 0x0a, buffer, 36, SD_TIMEOUT, + SD_MAX_RETRIES, &data, &sshdr); + + if (!scsi_status_is_good(res) || !data.header_length || + data.length < 6) { + sd_printk(KERN_WARNING, sdkp, + "getting Control mode page failed, assume no ATO\n"); + + if (scsi_sense_valid(&sshdr)) + sd_print_sense_hdr(sdkp, &sshdr); + + return; + } + + offset = data.header_length + data.block_descriptor_length; + + if ((buffer[offset] & 0x3f) != 0x0a) { + sd_printk(KERN_ERR, sdkp, "ATO Got wrong page\n"); + return; + } + + if ((buffer[offset + 5] & 0x80) == 0) + return; + + sdkp->ATO = 1; + + return; +} + /** * sd_revalidate_disk - called the first time a new disk is seen, * performs disk spin up, read_capacity, etc. @@ -1567,6 +1727,7 @@ static int sd_revalidate_disk(struct gendisk *disk) sdkp->write_prot = 0; sdkp->WCE = 0; sdkp->RCD = 0; + sdkp->ATO = 0; sd_spinup_disk(sdkp); @@ -1578,6 +1739,7 @@ static int sd_revalidate_disk(struct gendisk *disk) sd_read_capacity(sdkp, buffer); sd_read_write_protect_flag(sdkp, buffer); sd_read_cache_type(sdkp, buffer); + sd_read_app_tag_own(sdkp, buffer); } /* @@ -1643,18 +1805,20 @@ static int sd_probe(struct device *dev) if (!gd) goto out_free; - if (!idr_pre_get(&sd_index_idr, GFP_KERNEL)) - goto out_put; + do { + if (!ida_pre_get(&sd_index_ida, GFP_KERNEL)) + goto out_put; - spin_lock(&sd_index_lock); - error = idr_get_new(&sd_index_idr, NULL, &index); - spin_unlock(&sd_index_lock); + error = ida_get_new(&sd_index_ida, &index); + } while (error == -EAGAIN); - if (index >= SD_MAX_DISKS) - error = -EBUSY; if (error) goto out_put; + error = -EBUSY; + if (index >= SD_MAX_DISKS) + goto out_free_index; + sdkp->device = sdp; sdkp->driver = &sd_template; sdkp->disk = gd; @@ -1675,7 +1839,7 @@ static int sd_probe(struct device *dev) strncpy(sdkp->dev.bus_id, sdp->sdev_gendev.bus_id, BUS_ID_SIZE); if (device_add(&sdkp->dev)) - goto out_put; + goto out_free_index; get_device(&sdp->sdev_gendev); @@ -1711,12 +1875,15 @@ static int sd_probe(struct device *dev) dev_set_drvdata(dev, sdkp); add_disk(gd); + sd_dif_config_host(sdkp); sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n", sdp->removable ? "removable " : ""); return 0; + out_free_index: + ida_remove(&sd_index_ida, index); out_put: put_disk(gd); out_free: @@ -1766,9 +1933,7 @@ static void scsi_disk_release(struct device *dev) struct scsi_disk *sdkp = to_scsi_disk(dev); struct gendisk *disk = sdkp->disk; - spin_lock(&sd_index_lock); - idr_remove(&sd_index_idr, sdkp->index); - spin_unlock(&sd_index_lock); + ida_remove(&sd_index_ida, sdkp->index); disk->private_data = NULL; put_disk(disk); diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 03a3d45cfa42..95b9f06534d5 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -31,6 +31,12 @@ */ #define SD_BUF_SIZE 512 +/* + * Number of sectors at the end of the device to avoid multi-sector + * accesses to in the case of last_sector_bug + */ +#define SD_LAST_BUGGY_SECTORS 8 + struct scsi_disk { struct scsi_driver *driver; /* always &sd_template */ struct scsi_device *device; @@ -41,7 +47,9 @@ struct scsi_disk { u32 index; u8 media_present; u8 write_prot; + u8 protection_type;/* Data Integrity Field */ unsigned previous_state : 1; + unsigned ATO : 1; /* state of disk ATO bit */ unsigned WCE : 1; /* state of disk WCE bit */ unsigned RCD : 1; /* state of disk RCD bit, unused */ unsigned DPOFUA : 1; /* state of disk DPOFUA bit */ @@ -59,4 +67,50 @@ static inline struct scsi_disk *scsi_disk(struct gendisk *disk) (sdsk)->disk->disk_name, ##a) : \ sdev_printk(prefix, (sdsk)->device, fmt, ##a) +/* + * A DIF-capable target device can be formatted with different + * protection schemes. Currently 0 through 3 are defined: + * + * Type 0 is regular (unprotected) I/O + * + * Type 1 defines the contents of the guard and reference tags + * + * Type 2 defines the contents of the guard and reference tags and + * uses 32-byte commands to seed the latter + * + * Type 3 defines the contents of the guard tag only + */ + +enum sd_dif_target_protection_types { + SD_DIF_TYPE0_PROTECTION = 0x0, + SD_DIF_TYPE1_PROTECTION = 0x1, + SD_DIF_TYPE2_PROTECTION = 0x2, + SD_DIF_TYPE3_PROTECTION = 0x3, +}; + +/* + * Data Integrity Field tuple. + */ +struct sd_dif_tuple { + __be16 guard_tag; /* Checksum */ + __be16 app_tag; /* Opaque storage */ + __be32 ref_tag; /* Target LBA or indirect LBA */ +}; + +#if defined(CONFIG_BLK_DEV_INTEGRITY) + +extern void sd_dif_op(struct scsi_cmnd *, unsigned int, unsigned int); +extern void sd_dif_config_host(struct scsi_disk *); +extern int sd_dif_prepare(struct request *rq, sector_t, unsigned int); +extern void sd_dif_complete(struct scsi_cmnd *, unsigned int); + +#else /* CONFIG_BLK_DEV_INTEGRITY */ + +#define sd_dif_op(a, b, c) do { } while (0) +#define sd_dif_config_host(a) do { } while (0) +#define sd_dif_prepare(a, b, c) (0) +#define sd_dif_complete(a, b) (0) + +#endif /* CONFIG_BLK_DEV_INTEGRITY */ + #endif /* _SCSI_DISK_H */ diff --git a/drivers/scsi/sd_dif.c b/drivers/scsi/sd_dif.c new file mode 100644 index 000000000000..4d17f3d35aac --- /dev/null +++ b/drivers/scsi/sd_dif.c @@ -0,0 +1,538 @@ +/* + * sd_dif.c - SCSI Data Integrity Field + * + * Copyright (C) 2007, 2008 Oracle Corporation + * Written by: Martin K. Petersen <martin.petersen@oracle.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + */ + +#include <linux/blkdev.h> +#include <linux/crc-t10dif.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_dbg.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_driver.h> +#include <scsi/scsi_eh.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_ioctl.h> +#include <scsi/scsicam.h> + +#include <net/checksum.h> + +#include "sd.h" + +typedef __u16 (csum_fn) (void *, unsigned int); + +static __u16 sd_dif_crc_fn(void *data, unsigned int len) +{ + return cpu_to_be16(crc_t10dif(data, len)); +} + +static __u16 sd_dif_ip_fn(void *data, unsigned int len) +{ + return ip_compute_csum(data, len); +} + +/* + * Type 1 and Type 2 protection use the same format: 16 bit guard tag, + * 16 bit app tag, 32 bit reference tag. + */ +static void sd_dif_type1_generate(struct blk_integrity_exchg *bix, csum_fn *fn) +{ + void *buf = bix->data_buf; + struct sd_dif_tuple *sdt = bix->prot_buf; + sector_t sector = bix->sector; + unsigned int i; + + for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) { + sdt->guard_tag = fn(buf, bix->sector_size); + sdt->ref_tag = cpu_to_be32(sector & 0xffffffff); + sdt->app_tag = 0; + + buf += bix->sector_size; + sector++; + } +} + +static void sd_dif_type1_generate_crc(struct blk_integrity_exchg *bix) +{ + sd_dif_type1_generate(bix, sd_dif_crc_fn); +} + +static void sd_dif_type1_generate_ip(struct blk_integrity_exchg *bix) +{ + sd_dif_type1_generate(bix, sd_dif_ip_fn); +} + +static int sd_dif_type1_verify(struct blk_integrity_exchg *bix, csum_fn *fn) +{ + void *buf = bix->data_buf; + struct sd_dif_tuple *sdt = bix->prot_buf; + sector_t sector = bix->sector; + unsigned int i; + __u16 csum; + + for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) { + /* Unwritten sectors */ + if (sdt->app_tag == 0xffff) + return 0; + + /* Bad ref tag received from disk */ + if (sdt->ref_tag == 0xffffffff) { + printk(KERN_ERR + "%s: bad phys ref tag on sector %lu\n", + bix->disk_name, (unsigned long)sector); + return -EIO; + } + + if (be32_to_cpu(sdt->ref_tag) != (sector & 0xffffffff)) { + printk(KERN_ERR + "%s: ref tag error on sector %lu (rcvd %u)\n", + bix->disk_name, (unsigned long)sector, + be32_to_cpu(sdt->ref_tag)); + return -EIO; + } + + csum = fn(buf, bix->sector_size); + + if (sdt->guard_tag != csum) { + printk(KERN_ERR "%s: guard tag error on sector %lu " \ + "(rcvd %04x, data %04x)\n", bix->disk_name, + (unsigned long)sector, + be16_to_cpu(sdt->guard_tag), be16_to_cpu(csum)); + return -EIO; + } + + buf += bix->sector_size; + sector++; + } + + return 0; +} + +static int sd_dif_type1_verify_crc(struct blk_integrity_exchg *bix) +{ + return sd_dif_type1_verify(bix, sd_dif_crc_fn); +} + +static int sd_dif_type1_verify_ip(struct blk_integrity_exchg *bix) +{ + return sd_dif_type1_verify(bix, sd_dif_ip_fn); +} + +/* + * Functions for interleaving and deinterleaving application tags + */ +static void sd_dif_type1_set_tag(void *prot, void *tag_buf, unsigned int sectors) +{ + struct sd_dif_tuple *sdt = prot; + char *tag = tag_buf; + unsigned int i, j; + + for (i = 0, j = 0 ; i < sectors ; i++, j += 2, sdt++) { + sdt->app_tag = tag[j] << 8 | tag[j+1]; + BUG_ON(sdt->app_tag == 0xffff); + } +} + +static void sd_dif_type1_get_tag(void *prot, void *tag_buf, unsigned int sectors) +{ + struct sd_dif_tuple *sdt = prot; + char *tag = tag_buf; + unsigned int i, j; + + for (i = 0, j = 0 ; i < sectors ; i++, j += 2, sdt++) { + tag[j] = (sdt->app_tag & 0xff00) >> 8; + tag[j+1] = sdt->app_tag & 0xff; + } +} + +static struct blk_integrity dif_type1_integrity_crc = { + .name = "T10-DIF-TYPE1-CRC", + .generate_fn = sd_dif_type1_generate_crc, + .verify_fn = sd_dif_type1_verify_crc, + .get_tag_fn = sd_dif_type1_get_tag, + .set_tag_fn = sd_dif_type1_set_tag, + .tuple_size = sizeof(struct sd_dif_tuple), + .tag_size = 0, +}; + +static struct blk_integrity dif_type1_integrity_ip = { + .name = "T10-DIF-TYPE1-IP", + .generate_fn = sd_dif_type1_generate_ip, + .verify_fn = sd_dif_type1_verify_ip, + .get_tag_fn = sd_dif_type1_get_tag, + .set_tag_fn = sd_dif_type1_set_tag, + .tuple_size = sizeof(struct sd_dif_tuple), + .tag_size = 0, +}; + + +/* + * Type 3 protection has a 16-bit guard tag and 16 + 32 bits of opaque + * tag space. + */ +static void sd_dif_type3_generate(struct blk_integrity_exchg *bix, csum_fn *fn) +{ + void *buf = bix->data_buf; + struct sd_dif_tuple *sdt = bix->prot_buf; + unsigned int i; + + for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) { + sdt->guard_tag = fn(buf, bix->sector_size); + sdt->ref_tag = 0; + sdt->app_tag = 0; + + buf += bix->sector_size; + } +} + +static void sd_dif_type3_generate_crc(struct blk_integrity_exchg *bix) +{ + sd_dif_type3_generate(bix, sd_dif_crc_fn); +} + +static void sd_dif_type3_generate_ip(struct blk_integrity_exchg *bix) +{ + sd_dif_type3_generate(bix, sd_dif_ip_fn); +} + +static int sd_dif_type3_verify(struct blk_integrity_exchg *bix, csum_fn *fn) +{ + void *buf = bix->data_buf; + struct sd_dif_tuple *sdt = bix->prot_buf; + sector_t sector = bix->sector; + unsigned int i; + __u16 csum; + + for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) { + /* Unwritten sectors */ + if (sdt->app_tag == 0xffff && sdt->ref_tag == 0xffffffff) + return 0; + + csum = fn(buf, bix->sector_size); + + if (sdt->guard_tag != csum) { + printk(KERN_ERR "%s: guard tag error on sector %lu " \ + "(rcvd %04x, data %04x)\n", bix->disk_name, + (unsigned long)sector, + be16_to_cpu(sdt->guard_tag), be16_to_cpu(csum)); + return -EIO; + } + + buf += bix->sector_size; + sector++; + } + + return 0; +} + +static int sd_dif_type3_verify_crc(struct blk_integrity_exchg *bix) +{ + return sd_dif_type3_verify(bix, sd_dif_crc_fn); +} + +static int sd_dif_type3_verify_ip(struct blk_integrity_exchg *bix) +{ + return sd_dif_type3_verify(bix, sd_dif_ip_fn); +} + +static void sd_dif_type3_set_tag(void *prot, void *tag_buf, unsigned int sectors) +{ + struct sd_dif_tuple *sdt = prot; + char *tag = tag_buf; + unsigned int i, j; + + for (i = 0, j = 0 ; i < sectors ; i++, j += 6, sdt++) { + sdt->app_tag = tag[j] << 8 | tag[j+1]; + sdt->ref_tag = tag[j+2] << 24 | tag[j+3] << 16 | + tag[j+4] << 8 | tag[j+5]; + } +} + +static void sd_dif_type3_get_tag(void *prot, void *tag_buf, unsigned int sectors) +{ + struct sd_dif_tuple *sdt = prot; + char *tag = tag_buf; + unsigned int i, j; + + for (i = 0, j = 0 ; i < sectors ; i++, j += 2, sdt++) { + tag[j] = (sdt->app_tag & 0xff00) >> 8; + tag[j+1] = sdt->app_tag & 0xff; + tag[j+2] = (sdt->ref_tag & 0xff000000) >> 24; + tag[j+3] = (sdt->ref_tag & 0xff0000) >> 16; + tag[j+4] = (sdt->ref_tag & 0xff00) >> 8; + tag[j+5] = sdt->ref_tag & 0xff; + BUG_ON(sdt->app_tag == 0xffff || sdt->ref_tag == 0xffffffff); + } +} + +static struct blk_integrity dif_type3_integrity_crc = { + .name = "T10-DIF-TYPE3-CRC", + .generate_fn = sd_dif_type3_generate_crc, + .verify_fn = sd_dif_type3_verify_crc, + .get_tag_fn = sd_dif_type3_get_tag, + .set_tag_fn = sd_dif_type3_set_tag, + .tuple_size = sizeof(struct sd_dif_tuple), + .tag_size = 0, +}; + +static struct blk_integrity dif_type3_integrity_ip = { + .name = "T10-DIF-TYPE3-IP", + .generate_fn = sd_dif_type3_generate_ip, + .verify_fn = sd_dif_type3_verify_ip, + .get_tag_fn = sd_dif_type3_get_tag, + .set_tag_fn = sd_dif_type3_set_tag, + .tuple_size = sizeof(struct sd_dif_tuple), + .tag_size = 0, +}; + +/* + * Configure exchange of protection information between OS and HBA. + */ +void sd_dif_config_host(struct scsi_disk *sdkp) +{ + struct scsi_device *sdp = sdkp->device; + struct gendisk *disk = sdkp->disk; + u8 type = sdkp->protection_type; + + /* If this HBA doesn't support DIX, resort to normal I/O or DIF */ + if (scsi_host_dix_capable(sdp->host, type) == 0) { + + if (type == SD_DIF_TYPE0_PROTECTION) + return; + + if (scsi_host_dif_capable(sdp->host, type) == 0) { + sd_printk(KERN_INFO, sdkp, "Type %d protection " \ + "unsupported by HBA. Disabling DIF.\n", type); + sdkp->protection_type = 0; + return; + } + + sd_printk(KERN_INFO, sdkp, "Enabling DIF Type %d protection\n", + type); + + return; + } + + /* Enable DMA of protection information */ + if (scsi_host_get_guard(sdkp->device->host) & SHOST_DIX_GUARD_IP) + if (type == SD_DIF_TYPE3_PROTECTION) + blk_integrity_register(disk, &dif_type3_integrity_ip); + else + blk_integrity_register(disk, &dif_type1_integrity_ip); + else + if (type == SD_DIF_TYPE3_PROTECTION) + blk_integrity_register(disk, &dif_type3_integrity_crc); + else + blk_integrity_register(disk, &dif_type1_integrity_crc); + + sd_printk(KERN_INFO, sdkp, + "Enabling %s integrity protection\n", disk->integrity->name); + + /* Signal to block layer that we support sector tagging */ + if (type && sdkp->ATO) { + if (type == SD_DIF_TYPE3_PROTECTION) + disk->integrity->tag_size = sizeof(u16) + sizeof(u32); + else + disk->integrity->tag_size = sizeof(u16); + + sd_printk(KERN_INFO, sdkp, "DIF application tag size %u\n", + disk->integrity->tag_size); + } +} + +/* + * DIF DMA operation magic decoder ring. + */ +void sd_dif_op(struct scsi_cmnd *scmd, unsigned int dif, unsigned int dix) +{ + int csum_convert, prot_op; + + prot_op = 0; + + /* Convert checksum? */ + if (scsi_host_get_guard(scmd->device->host) != SHOST_DIX_GUARD_CRC) + csum_convert = 1; + else + csum_convert = 0; + + switch (scmd->cmnd[0]) { + case READ_10: + case READ_12: + case READ_16: + if (dif && dix) + if (csum_convert) + prot_op = SCSI_PROT_READ_CONVERT; + else + prot_op = SCSI_PROT_READ_PASS; + else if (dif && !dix) + prot_op = SCSI_PROT_READ_STRIP; + else if (!dif && dix) + prot_op = SCSI_PROT_READ_INSERT; + + break; + + case WRITE_10: + case WRITE_12: + case WRITE_16: + if (dif && dix) + if (csum_convert) + prot_op = SCSI_PROT_WRITE_CONVERT; + else + prot_op = SCSI_PROT_WRITE_PASS; + else if (dif && !dix) + prot_op = SCSI_PROT_WRITE_INSERT; + else if (!dif && dix) + prot_op = SCSI_PROT_WRITE_STRIP; + + break; + } + + scsi_set_prot_op(scmd, prot_op); + scsi_set_prot_type(scmd, dif); +} + +/* + * The virtual start sector is the one that was originally submitted + * by the block layer. Due to partitioning, MD/DM cloning, etc. the + * actual physical start sector is likely to be different. Remap + * protection information to match the physical LBA. + * + * From a protocol perspective there's a slight difference between + * Type 1 and 2. The latter uses 32-byte CDBs exclusively, and the + * reference tag is seeded in the CDB. This gives us the potential to + * avoid virt->phys remapping during write. However, at read time we + * don't know whether the virt sector is the same as when we wrote it + * (we could be reading from real disk as opposed to MD/DM device. So + * we always remap Type 2 making it identical to Type 1. + * + * Type 3 does not have a reference tag so no remapping is required. + */ +int sd_dif_prepare(struct request *rq, sector_t hw_sector, unsigned int sector_sz) +{ + const int tuple_sz = sizeof(struct sd_dif_tuple); + struct bio *bio; + struct scsi_disk *sdkp; + struct sd_dif_tuple *sdt; + unsigned int i, j; + u32 phys, virt; + + /* Already remapped? */ + if (rq->cmd_flags & REQ_INTEGRITY) + return 0; + + sdkp = rq->bio->bi_bdev->bd_disk->private_data; + + if (sdkp->protection_type == SD_DIF_TYPE3_PROTECTION) + return 0; + + rq->cmd_flags |= REQ_INTEGRITY; + phys = hw_sector & 0xffffffff; + + __rq_for_each_bio(bio, rq) { + struct bio_vec *iv; + + virt = bio->bi_integrity->bip_sector & 0xffffffff; + + bip_for_each_vec(iv, bio->bi_integrity, i) { + sdt = kmap_atomic(iv->bv_page, KM_USER0) + + iv->bv_offset; + + for (j = 0 ; j < iv->bv_len ; j += tuple_sz, sdt++) { + + if (be32_to_cpu(sdt->ref_tag) != virt) + goto error; + + sdt->ref_tag = cpu_to_be32(phys); + virt++; + phys++; + } + + kunmap_atomic(sdt, KM_USER0); + } + } + + return 0; + +error: + kunmap_atomic(sdt, KM_USER0); + sd_printk(KERN_ERR, sdkp, "%s: virt %u, phys %u, ref %u\n", + __func__, virt, phys, be32_to_cpu(sdt->ref_tag)); + + return -EIO; +} + +/* + * Remap physical sector values in the reference tag to the virtual + * values expected by the block layer. + */ +void sd_dif_complete(struct scsi_cmnd *scmd, unsigned int good_bytes) +{ + const int tuple_sz = sizeof(struct sd_dif_tuple); + struct scsi_disk *sdkp; + struct bio *bio; + struct sd_dif_tuple *sdt; + unsigned int i, j, sectors, sector_sz; + u32 phys, virt; + + sdkp = scsi_disk(scmd->request->rq_disk); + + if (sdkp->protection_type == SD_DIF_TYPE3_PROTECTION || good_bytes == 0) + return; + + sector_sz = scmd->device->sector_size; + sectors = good_bytes / sector_sz; + + phys = scmd->request->sector & 0xffffffff; + if (sector_sz == 4096) + phys >>= 3; + + __rq_for_each_bio(bio, scmd->request) { + struct bio_vec *iv; + + virt = bio->bi_integrity->bip_sector & 0xffffffff; + + bip_for_each_vec(iv, bio->bi_integrity, i) { + sdt = kmap_atomic(iv->bv_page, KM_USER0) + + iv->bv_offset; + + for (j = 0 ; j < iv->bv_len ; j += tuple_sz, sdt++) { + + if (sectors == 0) { + kunmap_atomic(sdt, KM_USER0); + return; + } + + if (be32_to_cpu(sdt->ref_tag) != phys && + sdt->app_tag != 0xffff) + sdt->ref_tag = 0xffffffff; /* Bad ref */ + else + sdt->ref_tag = cpu_to_be32(virt); + + virt++; + phys++; + sectors--; + } + + kunmap_atomic(sdt, KM_USER0); + } + } +} + diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 4684cc716aa4..c2bb53e3d941 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -17,7 +17,7 @@ Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support */ -static const char *verstr = "20080224"; +static const char *verstr = "20080504"; #include <linux/module.h> @@ -631,7 +631,7 @@ static int cross_eof(struct scsi_tape * STp, int forward) /* Flush the write buffer (never need to write if variable blocksize). */ static int st_flush_write_buffer(struct scsi_tape * STp) { - int offset, transfer, blks; + int transfer, blks; int result; unsigned char cmd[MAX_COMMAND_SIZE]; struct st_request *SRpnt; @@ -644,14 +644,10 @@ static int st_flush_write_buffer(struct scsi_tape * STp) result = 0; if (STp->dirty == 1) { - offset = (STp->buffer)->buffer_bytes; - transfer = ((offset + STp->block_size - 1) / - STp->block_size) * STp->block_size; + transfer = STp->buffer->buffer_bytes; DEBC(printk(ST_DEB_MSG "%s: Flushing %d bytes.\n", tape_name(STp), transfer)); - memset((STp->buffer)->b_data + offset, 0, transfer - offset); - memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = WRITE_6; cmd[1] = 1; @@ -1670,6 +1666,7 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) if (undone <= do_count) { /* Only data from this write is not written */ count += undone; + b_point -= undone; do_count -= undone; if (STp->block_size) blks = (transfer - undone) / STp->block_size; diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c index f308a0308829..3790906a77d1 100644 --- a/drivers/scsi/stex.c +++ b/drivers/scsi/stex.c @@ -467,7 +467,7 @@ stex_slave_alloc(struct scsi_device *sdev) /* Cheat: usually extracted from Inquiry data */ sdev->tagged_supported = 1; - scsi_activate_tcq(sdev, sdev->host->can_queue); + scsi_activate_tcq(sdev, ST_CMD_PER_LUN); return 0; } diff --git a/drivers/scsi/sun_esp.c b/drivers/scsi/sun_esp.c index 2c87db98cdfb..f9cf70151366 100644 --- a/drivers/scsi/sun_esp.c +++ b/drivers/scsi/sun_esp.c @@ -7,6 +7,7 @@ #include <linux/types.h> #include <linux/delay.h> #include <linux/module.h> +#include <linux/mm.h> #include <linux/init.h> #include <asm/irq.h> diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.c b/drivers/scsi/sym53c8xx_2/sym_hipd.c index 22a6aae78699..98df1651404f 100644 --- a/drivers/scsi/sym53c8xx_2/sym_hipd.c +++ b/drivers/scsi/sym53c8xx_2/sym_hipd.c @@ -5741,6 +5741,8 @@ void sym_hcb_free(struct sym_hcb *np) for (target = 0; target < SYM_CONF_MAX_TARGET ; target++) { tp = &np->target[target]; + if (tp->luntbl) + sym_mfree_dma(tp->luntbl, 256, "LUNTBL"); #if SYM_CONF_MAX_LUN > 1 kfree(tp->lunmp); #endif diff --git a/drivers/scsi/tmscsim.c b/drivers/scsi/tmscsim.c index 5b04ddfed26c..1723d71cbf3f 100644 --- a/drivers/scsi/tmscsim.c +++ b/drivers/scsi/tmscsim.c @@ -452,7 +452,7 @@ static int dc390_pci_map (struct dc390_srb* pSRB) /* TODO: error handling */ if (pSRB->SGcount != 1) error = 1; - DEBUG1(printk("%s(): Mapped sense buffer %p at %x\n", __FUNCTION__, pcmd->sense_buffer, cmdp->saved_dma_handle)); + DEBUG1(printk("%s(): Mapped sense buffer %p at %x\n", __func__, pcmd->sense_buffer, cmdp->saved_dma_handle)); /* Map SG list */ } else if (scsi_sg_count(pcmd)) { int nseg; @@ -466,7 +466,7 @@ static int dc390_pci_map (struct dc390_srb* pSRB) if (nseg < 0) error = 1; DEBUG1(printk("%s(): Mapped SG %p with %d (%d) elements\n",\ - __FUNCTION__, scsi_sglist(pcmd), nseg, scsi_sg_count(pcmd))); + __func__, scsi_sglist(pcmd), nseg, scsi_sg_count(pcmd))); /* Map single segment */ } else pSRB->SGcount = 0; @@ -483,11 +483,11 @@ static void dc390_pci_unmap (struct dc390_srb* pSRB) if (pSRB->SRBFlag) { pci_unmap_sg(pdev, &pSRB->Segmentx, 1, DMA_FROM_DEVICE); - DEBUG1(printk("%s(): Unmapped sense buffer at %x\n", __FUNCTION__, cmdp->saved_dma_handle)); + DEBUG1(printk("%s(): Unmapped sense buffer at %x\n", __func__, cmdp->saved_dma_handle)); } else { scsi_dma_unmap(pcmd); DEBUG1(printk("%s(): Unmapped SG at %p with %d elements\n", - __FUNCTION__, scsi_sglist(pcmd), scsi_sg_count(pcmd))); + __func__, scsi_sglist(pcmd), scsi_sg_count(pcmd))); } } diff --git a/drivers/scsi/wd7000.c b/drivers/scsi/wd7000.c index c975c01b3a02..d4c13561f4a6 100644 --- a/drivers/scsi/wd7000.c +++ b/drivers/scsi/wd7000.c @@ -148,7 +148,7 @@ * * 2002/10/04 - Alan Cox <alan@redhat.com> * - * Use dev_id for interrupts, kill __FUNCTION__ pasting + * Use dev_id for interrupts, kill __func__ pasting * Add a lock for the scb pool, clean up all other cli/sti usage stuff * Use the adapter lock for the other places we had the cli's * @@ -640,12 +640,12 @@ static int __init wd7000_setup(char *str) (void) get_options(str, ARRAY_SIZE(ints), ints); if (wd7000_card_num >= NUM_CONFIGS) { - printk(KERN_ERR "%s: Too many \"wd7000=\" configurations in " "command line!\n", __FUNCTION__); + printk(KERN_ERR "%s: Too many \"wd7000=\" configurations in " "command line!\n", __func__); return 0; } if ((ints[0] < 3) || (ints[0] > 5)) { - printk(KERN_ERR "%s: Error in command line! " "Usage: wd7000=<IRQ>,<DMA>,IO>[,<BUS_ON>" "[,<BUS_OFF>]]\n", __FUNCTION__); + printk(KERN_ERR "%s: Error in command line! " "Usage: wd7000=<IRQ>,<DMA>,IO>[,<BUS_ON>" "[,<BUS_OFF>]]\n", __func__); } else { for (i = 0; i < NUM_IRQS; i++) if (ints[1] == wd7000_irq[i]) @@ -1642,7 +1642,7 @@ static int wd7000_biosparam(struct scsi_device *sdev, ip[2] = info[2]; if (info[0] == 255) - printk(KERN_INFO "%s: current partition table is " "using extended translation.\n", __FUNCTION__); + printk(KERN_INFO "%s: current partition table is " "using extended translation.\n", __func__); } } diff --git a/drivers/scsi/zalon.c b/drivers/scsi/zalon.c index 4b5f908d35c3..3c4a300494a4 100644 --- a/drivers/scsi/zalon.c +++ b/drivers/scsi/zalon.c @@ -68,11 +68,11 @@ lasi_scsi_clock(void * hpa, int defaultclock) if (status == PDC_RET_OK) { clock = (int) pdc_result[16]; } else { - printk(KERN_WARNING "%s: pdc_iodc_read returned %d\n", __FUNCTION__, status); + printk(KERN_WARNING "%s: pdc_iodc_read returned %d\n", __func__, status); clock = defaultclock; } - printk(KERN_DEBUG "%s: SCSI clock %d\n", __FUNCTION__, clock); + printk(KERN_DEBUG "%s: SCSI clock %d\n", __func__, clock); return clock; } #endif @@ -108,13 +108,13 @@ zalon_probe(struct parisc_device *dev) */ dev->irq = gsc_alloc_irq(&gsc_irq); - printk(KERN_INFO "%s: Zalon version %d, IRQ %d\n", __FUNCTION__, + printk(KERN_INFO "%s: Zalon version %d, IRQ %d\n", __func__, zalon_vers, dev->irq); __raw_writel(gsc_irq.txn_addr | gsc_irq.txn_data, zalon + IO_MODULE_EIM); if (zalon_vers == 0) - printk(KERN_WARNING "%s: Zalon 1.1 or earlier\n", __FUNCTION__); + printk(KERN_WARNING "%s: Zalon 1.1 or earlier\n", __func__); memset(&device, 0, sizeof(struct ncr_device)); diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index ce948b66bbd4..a97f1ae11f78 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -1293,7 +1293,18 @@ receive_chars(struct uart_8250_port *up, unsigned int *status) char flag; do { - ch = serial_inp(up, UART_RX); + if (likely(lsr & UART_LSR_DR)) + ch = serial_inp(up, UART_RX); + else + /* + * Intel 82571 has a Serial Over Lan device that will + * set UART_LSR_BI without setting UART_LSR_DR when + * it receives a break. To avoid reading from the + * receive buffer without UART_LSR_DR bit set, we + * just force the read character to be 0 + */ + ch = 0; + flag = TTY_NORMAL; up->port.icount.rx++; @@ -1342,7 +1353,7 @@ receive_chars(struct uart_8250_port *up, unsigned int *status) ignore_char: lsr = serial_inp(up, UART_LSR); - } while ((lsr & UART_LSR_DR) && (max_count-- > 0)); + } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0)); spin_unlock(&up->port.lock); tty_flip_buffer_push(tty); spin_lock(&up->port.lock); @@ -1425,7 +1436,7 @@ serial8250_handle_port(struct uart_8250_port *up) DEBUG_INTR("status = %x...", status); - if (status & UART_LSR_DR) + if (status & (UART_LSR_DR | UART_LSR_BI)) receive_chars(up, &status); check_modem_status(up); if (status & UART_LSR_THRE) @@ -1874,7 +1885,9 @@ static int serial8250_startup(struct uart_port *port) * the interrupt is enabled. Delays are necessary to * allow register changes to become visible. */ - spin_lock_irqsave(&up->port.lock, flags); + spin_lock(&up->port.lock); + if (up->port.flags & UPF_SHARE_IRQ) + disable_irq_nosync(up->port.irq); wait_for_xmitr(up, UART_LSR_THRE); serial_out_sync(up, UART_IER, UART_IER_THRI); @@ -1886,7 +1899,9 @@ static int serial8250_startup(struct uart_port *port) iir = serial_in(up, UART_IIR); serial_out(up, UART_IER, 0); - spin_unlock_irqrestore(&up->port.lock, flags); + if (up->port.flags & UPF_SHARE_IRQ) + enable_irq(up->port.irq); + spin_unlock(&up->port.lock); /* * If the interrupt is not reasserted, setup a timer to diff --git a/drivers/serial/8250_gsc.c b/drivers/serial/8250_gsc.c index 4eb7437a404a..0416ad3bc127 100644 --- a/drivers/serial/8250_gsc.c +++ b/drivers/serial/8250_gsc.c @@ -119,3 +119,5 @@ int __init probe_serial_gsc(void) } module_init(probe_serial_gsc); + +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index 1b36087665a2..c2f23933155b 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -767,6 +767,9 @@ pci_default_setup(struct serial_private *priv, struct pciserial_board *board, #define PCI_SUBDEVICE_ID_POCTAL232 0x0308 #define PCI_SUBDEVICE_ID_POCTAL422 0x0408 +/* Unknown vendors/cards - this should not be in linux/pci_ids.h */ +#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 + /* * Master list of serial port init/setup/exit quirks. * This does not describe the general nature of the port. @@ -882,6 +885,15 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { }, { .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_9050, + .subvendor = PCI_VENDOR_ID_PLX, + .subdevice = PCI_SUBDEVICE_ID_UNKNOWN_0x1584, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + { + .vendor = PCI_VENDOR_ID_PLX, .device = PCI_DEVICE_ID_PLX_ROMULUS, .subvendor = PCI_VENDOR_ID_PLX, .subdevice = PCI_DEVICE_ID_PLX_ROMULUS, @@ -2197,6 +2209,11 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_4_921600 }, + /* Unknown card - subdevice 0x1584 */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_PLX, + PCI_SUBDEVICE_ID_UNKNOWN_0x1584, 0, 0, + pbn_b0_4_115200 }, { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_SUBVENDOR_ID_KEYSPAN, PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0, diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 8fc7451c0049..3b4a14e355c1 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -942,22 +942,6 @@ config SERIAL_IP22_ZILOG_CONSOLE depends on SERIAL_IP22_ZILOG=y select SERIAL_CORE_CONSOLE -config V850E_UART - bool "NEC V850E on-chip UART support" - depends on V850E_MA1 || V850E_ME2 || V850E_TEG || V850E2_ANNA || V850E_AS85EP1 - select SERIAL_CORE - default y - -config V850E_UARTB - bool - depends on V850E_UART && V850E_ME2 - default y - -config V850E_UART_CONSOLE - bool "Use NEC V850E on-chip UART for console" - depends on V850E_UART - select SERIAL_CORE_CONSOLE - config SERIAL_SH_SCI tristate "SuperH SCI(F) serial port support" depends on SUPERH || H8300 diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c index abe129cc927a..a4f86927a74b 100644 --- a/drivers/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/serial/cpm_uart/cpm_uart_core.c @@ -201,6 +201,10 @@ static void cpm_uart_int_tx(struct uart_port *port) cpm_uart_tx_pump(port); } +#ifdef CONFIG_CONSOLE_POLL +static int serial_polled; +#endif + /* * Receive characters */ @@ -209,7 +213,7 @@ static void cpm_uart_int_rx(struct uart_port *port) int i; unsigned char ch; u8 *cp; - struct tty_struct *tty = port->info->tty; + struct tty_struct *tty = port->info->port.tty; struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; cbd_t __iomem *bdp; u16 status; @@ -222,6 +226,12 @@ static void cpm_uart_int_rx(struct uart_port *port) */ bdp = pinfo->rx_cur; for (;;) { +#ifdef CONFIG_CONSOLE_POLL + if (unlikely(serial_polled)) { + serial_polled = 0; + return; + } +#endif /* get status */ status = in_be16(&bdp->cbd_sc); /* If this one is empty, return happy */ @@ -253,7 +263,12 @@ static void cpm_uart_int_rx(struct uart_port *port) goto handle_error; if (uart_handle_sysrq_char(port, ch)) continue; - +#ifdef CONFIG_CONSOLE_POLL + if (unlikely(serial_polled)) { + serial_polled = 0; + return; + } +#endif error_return: tty_insert_flip_char(tty, ch, flg); @@ -420,10 +435,13 @@ static void cpm_uart_shutdown(struct uart_port *port) } /* Shut them really down and reinit buffer descriptors */ - if (IS_SMC(pinfo)) + if (IS_SMC(pinfo)) { + out_be16(&pinfo->smcup->smc_brkcr, 0); cpm_line_cr_cmd(pinfo, CPM_CR_STOP_TX); - else + } else { + out_be16(&pinfo->sccup->scc_brkcr, 0); cpm_line_cr_cmd(pinfo, CPM_CR_GRA_STOP_TX); + } cpm_uart_initbd(pinfo); } @@ -539,9 +557,11 @@ static void cpm_uart_set_termios(struct uart_port *port, * enables, because we want to put them back if they were * present. */ - prev_mode = in_be16(&smcp->smc_smcmr); - out_be16(&smcp->smc_smcmr, smcr_mk_clen(bits) | cval | SMCMR_SM_UART); - setbits16(&smcp->smc_smcmr, (prev_mode & (SMCMR_REN | SMCMR_TEN))); + prev_mode = in_be16(&smcp->smc_smcmr) & (SMCMR_REN | SMCMR_TEN); + /* Output in *one* operation, so we don't interrupt RX/TX if they + * were already enabled. */ + out_be16(&smcp->smc_smcmr, smcr_mk_clen(bits) | cval | + SMCMR_SM_UART | prev_mode); } else { out_be16(&sccp->scc_psmr, (sbits << 12) | scval); } @@ -865,6 +885,80 @@ static void cpm_uart_config_port(struct uart_port *port, int flags) cpm_uart_request_port(port); } } + +#ifdef CONFIG_CONSOLE_POLL +/* Serial polling routines for writing and reading from the uart while + * in an interrupt or debug context. + */ + +#define GDB_BUF_SIZE 512 /* power of 2, please */ + +static char poll_buf[GDB_BUF_SIZE]; +static char *pollp; +static int poll_chars; + +static int poll_wait_key(char *obuf, struct uart_cpm_port *pinfo) +{ + u_char c, *cp; + volatile cbd_t *bdp; + int i; + + /* Get the address of the host memory buffer. + */ + bdp = pinfo->rx_cur; + while (bdp->cbd_sc & BD_SC_EMPTY) + ; + + /* If the buffer address is in the CPM DPRAM, don't + * convert it. + */ + cp = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo); + + if (obuf) { + i = c = bdp->cbd_datlen; + while (i-- > 0) + *obuf++ = *cp++; + } else + c = *cp; + bdp->cbd_sc &= ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV | BD_SC_ID); + bdp->cbd_sc |= BD_SC_EMPTY; + + if (bdp->cbd_sc & BD_SC_WRAP) + bdp = pinfo->rx_bd_base; + else + bdp++; + pinfo->rx_cur = (cbd_t *)bdp; + + return (int)c; +} + +static int cpm_get_poll_char(struct uart_port *port) +{ + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + + if (!serial_polled) { + serial_polled = 1; + poll_chars = 0; + } + if (poll_chars <= 0) { + poll_chars = poll_wait_key(poll_buf, pinfo); + pollp = poll_buf; + } + poll_chars--; + return *pollp++; +} + +static void cpm_put_poll_char(struct uart_port *port, + unsigned char c) +{ + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + static char ch[2]; + + ch[0] = (char)c; + cpm_uart_early_write(pinfo->port.line, ch, 1); +} +#endif /* CONFIG_CONSOLE_POLL */ + static struct uart_ops cpm_uart_pops = { .tx_empty = cpm_uart_tx_empty, .set_mctrl = cpm_uart_set_mctrl, @@ -882,6 +976,10 @@ static struct uart_ops cpm_uart_pops = { .request_port = cpm_uart_request_port, .config_port = cpm_uart_config_port, .verify_port = cpm_uart_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = cpm_get_poll_char, + .poll_put_char = cpm_put_poll_char, +#endif }; struct uart_cpm_port cpm_uart_ports[UART_NR]; @@ -1105,12 +1203,14 @@ static int __init cpm_uart_console_setup(struct console *co, char *options) udbg_putc = NULL; #endif - cpm_line_cr_cmd(pinfo, CPM_CR_STOP_TX); - if (IS_SMC(pinfo)) { + out_be16(&pinfo->smcup->smc_brkcr, 0); + cpm_line_cr_cmd(pinfo, CPM_CR_STOP_TX); clrbits8(&pinfo->smcp->smc_smcm, SMCM_RX | SMCM_TX); clrbits16(&pinfo->smcp->smc_smcmr, SMCMR_REN | SMCMR_TEN); } else { + out_be16(&pinfo->sccup->scc_brkcr, 0); + cpm_line_cr_cmd(pinfo, CPM_CR_GRA_STOP_TX); clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_TX | UART_SCCM_RX); clrbits32(&pinfo->sccp->scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT); } diff --git a/drivers/serial/dz.c b/drivers/serial/dz.c index a81d2c2ff8a2..6042b87797a1 100644 --- a/drivers/serial/dz.c +++ b/drivers/serial/dz.c @@ -642,6 +642,26 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios, spin_unlock_irqrestore(&dport->port.lock, flags); } +/* + * Hack alert! + * Required solely so that the initial PROM-based console + * works undisturbed in parallel with this one. + */ +static void dz_pm(struct uart_port *uport, unsigned int state, + unsigned int oldstate) +{ + struct dz_port *dport = to_dport(uport); + unsigned long flags; + + spin_lock_irqsave(&dport->port.lock, flags); + if (state < 3) + dz_start_tx(&dport->port); + else + dz_stop_tx(&dport->port); + spin_unlock_irqrestore(&dport->port.lock, flags); +} + + static const char *dz_type(struct uart_port *uport) { return "DZ"; @@ -738,6 +758,7 @@ static struct uart_ops dz_ops = { .startup = dz_startup, .shutdown = dz_shutdown, .set_termios = dz_set_termios, + .pm = dz_pm, .type = dz_type, .release_port = dz_release_port, .request_port = dz_request_port, @@ -861,7 +882,10 @@ static int __init dz_console_setup(struct console *co, char *options) if (ret) return ret; + spin_lock_init(&dport->port.lock); /* For dz_pm(). */ + dz_reset(dport); + dz_pm(uport, 0, -1); if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); diff --git a/drivers/serial/icom.c b/drivers/serial/icom.c index 9c2df5c857cf..2b7531d9f6ab 100644 --- a/drivers/serial/icom.c +++ b/drivers/serial/icom.c @@ -730,7 +730,7 @@ static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port) static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port) { short int count, rcv_buff; - struct tty_struct *tty = icom_port->uart_port.info->tty; + struct tty_struct *tty = icom_port->uart_port.info->port.tty; unsigned short int status; struct uart_icount *icount; unsigned long offset; diff --git a/drivers/serial/mpsc.c b/drivers/serial/mpsc.c index c9f53e71f252..61d3ade5286c 100644 --- a/drivers/serial/mpsc.c +++ b/drivers/serial/mpsc.c @@ -921,6 +921,10 @@ static int mpsc_make_ready(struct mpsc_port_info *pi) return 0; } +#ifdef CONFIG_CONSOLE_POLL +static int serial_polled; +#endif + /* ****************************************************************************** * @@ -956,7 +960,12 @@ static int mpsc_rx_intr(struct mpsc_port_info *pi) while (!((cmdstat = be32_to_cpu(rxre->cmdstat)) & SDMA_DESC_CMDSTAT_O)) { bytes_in = be16_to_cpu(rxre->bytecnt); - +#ifdef CONFIG_CONSOLE_POLL + if (unlikely(serial_polled)) { + serial_polled = 0; + return 0; + } +#endif /* Following use of tty struct directly is deprecated */ if (unlikely(tty_buffer_request_room(tty, bytes_in) < bytes_in)) { @@ -1017,6 +1026,12 @@ static int mpsc_rx_intr(struct mpsc_port_info *pi) if (uart_handle_sysrq_char(&pi->port, *bp)) { bp++; bytes_in--; +#ifdef CONFIG_CONSOLE_POLL + if (unlikely(serial_polled)) { + serial_polled = 0; + return 0; + } +#endif goto next_frame; } @@ -1519,6 +1534,133 @@ static int mpsc_verify_port(struct uart_port *port, struct serial_struct *ser) return rc; } +#ifdef CONFIG_CONSOLE_POLL +/* Serial polling routines for writing and reading from the uart while + * in an interrupt or debug context. + */ + +static char poll_buf[2048]; +static int poll_ptr; +static int poll_cnt; +static void mpsc_put_poll_char(struct uart_port *port, + unsigned char c); + +static int mpsc_get_poll_char(struct uart_port *port) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + struct mpsc_rx_desc *rxre; + u32 cmdstat, bytes_in, i; + u8 *bp; + + if (!serial_polled) + serial_polled = 1; + + pr_debug("mpsc_rx_intr[%d]: Handling Rx intr\n", pi->port.line); + + if (poll_cnt) { + poll_cnt--; + return poll_buf[poll_ptr++]; + } + poll_ptr = 0; + poll_cnt = 0; + + while (poll_cnt == 0) { + rxre = (struct mpsc_rx_desc *)(pi->rxr + + (pi->rxr_posn*MPSC_RXRE_SIZE)); + dma_cache_sync(pi->port.dev, (void *)rxre, + MPSC_RXRE_SIZE, DMA_FROM_DEVICE); +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ + invalidate_dcache_range((ulong)rxre, + (ulong)rxre + MPSC_RXRE_SIZE); +#endif + /* + * Loop through Rx descriptors handling ones that have + * been completed. + */ + while (poll_cnt == 0 && + !((cmdstat = be32_to_cpu(rxre->cmdstat)) & + SDMA_DESC_CMDSTAT_O)){ + bytes_in = be16_to_cpu(rxre->bytecnt); + bp = pi->rxb + (pi->rxr_posn * MPSC_RXBE_SIZE); + dma_cache_sync(pi->port.dev, (void *) bp, + MPSC_RXBE_SIZE, DMA_FROM_DEVICE); +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ + invalidate_dcache_range((ulong)bp, + (ulong)bp + MPSC_RXBE_SIZE); +#endif + if ((unlikely(cmdstat & (SDMA_DESC_CMDSTAT_BR | + SDMA_DESC_CMDSTAT_FR | SDMA_DESC_CMDSTAT_OR))) && + !(cmdstat & pi->port.ignore_status_mask)) { + poll_buf[poll_cnt] = *bp; + poll_cnt++; + } else { + for (i = 0; i < bytes_in; i++) { + poll_buf[poll_cnt] = *bp++; + poll_cnt++; + } + pi->port.icount.rx += bytes_in; + } + rxre->bytecnt = cpu_to_be16(0); + wmb(); + rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O | + SDMA_DESC_CMDSTAT_EI | + SDMA_DESC_CMDSTAT_F | + SDMA_DESC_CMDSTAT_L); + wmb(); + dma_cache_sync(pi->port.dev, (void *)rxre, + MPSC_RXRE_SIZE, DMA_BIDIRECTIONAL); +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ + flush_dcache_range((ulong)rxre, + (ulong)rxre + MPSC_RXRE_SIZE); +#endif + + /* Advance to next descriptor */ + pi->rxr_posn = (pi->rxr_posn + 1) & + (MPSC_RXR_ENTRIES - 1); + rxre = (struct mpsc_rx_desc *)(pi->rxr + + (pi->rxr_posn * MPSC_RXRE_SIZE)); + dma_cache_sync(pi->port.dev, (void *)rxre, + MPSC_RXRE_SIZE, DMA_FROM_DEVICE); +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ + invalidate_dcache_range((ulong)rxre, + (ulong)rxre + MPSC_RXRE_SIZE); +#endif + } + + /* Restart rx engine, if its stopped */ + if ((readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_ERD) == 0) + mpsc_start_rx(pi); + } + if (poll_cnt) { + poll_cnt--; + return poll_buf[poll_ptr++]; + } + + return 0; +} + + +static void mpsc_put_poll_char(struct uart_port *port, + unsigned char c) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + u32 data; + + data = readl(pi->mpsc_base + MPSC_MPCR); + writeb(c, pi->mpsc_base + MPSC_CHR_1); + mb(); + data = readl(pi->mpsc_base + MPSC_CHR_2); + data |= MPSC_CHR_2_TTCS; + writel(data, pi->mpsc_base + MPSC_CHR_2); + mb(); + + while (readl(pi->mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_TTCS); +} +#endif static struct uart_ops mpsc_pops = { .tx_empty = mpsc_tx_empty, @@ -1537,6 +1679,10 @@ static struct uart_ops mpsc_pops = { .request_port = mpsc_request_port, .config_port = mpsc_config_port, .verify_port = mpsc_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = mpsc_get_poll_char, + .poll_put_char = mpsc_put_poll_char, +#endif }; /* diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c index 4a3ecaa629e6..d852f83f8900 100644 --- a/drivers/serial/samsung.c +++ b/drivers/serial/samsung.c @@ -202,7 +202,7 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id) { struct s3c24xx_uart_port *ourport = dev_id; struct uart_port *port = &ourport->port; - struct tty_struct *tty = port->info->tty; + struct tty_struct *tty = port->info->port.tty; unsigned int ufcon, ch, flag, ufstat, uerstat; int max_count = 64; diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 0bce1fe2c62a..f977c98cfa95 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -934,7 +934,7 @@ uart_tiocmset(struct tty_struct *tty, struct file *file, return ret; } -static void uart_break_ctl(struct tty_struct *tty, int break_state) +static int uart_break_ctl(struct tty_struct *tty, int break_state) { struct uart_state *state = tty->driver_data; struct uart_port *port = state->port; @@ -945,6 +945,7 @@ static void uart_break_ctl(struct tty_struct *tty, int break_state) port->ops->break_ctl(port, break_state); mutex_unlock(&state->mutex); + return 0; } static int uart_do_autoconfig(struct uart_state *state) diff --git a/drivers/serial/serial_txx9.c b/drivers/serial/serial_txx9.c index 7ad21925869a..8fcb4c5b9a26 100644 --- a/drivers/serial/serial_txx9.c +++ b/drivers/serial/serial_txx9.c @@ -272,7 +272,7 @@ static void serial_txx9_initialize(struct uart_port *port) static inline void receive_chars(struct uart_txx9_port *up, unsigned int *status) { - struct tty_struct *tty = up->port.info->tty; + struct tty_struct *tty = up->port.info->port.tty; unsigned char ch; unsigned int disr = *status; int max_count = 256; diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 208e42ba9455..3df2aaec829f 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -410,7 +410,6 @@ static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag) #endif #if defined(CONFIG_CPU_SUBTYPE_SH7760) || \ - defined(CONFIG_CPU_SUBTYPE_SH7763) || \ defined(CONFIG_CPU_SUBTYPE_SH7780) || \ defined(CONFIG_CPU_SUBTYPE_SH7785) static inline int scif_txroom(struct uart_port *port) @@ -422,6 +421,22 @@ static inline int scif_rxroom(struct uart_port *port) { return sci_in(port, SCRFDR) & 0xff; } +#elif defined(CONFIG_CPU_SUBTYPE_SH7763) +static inline int scif_txroom(struct uart_port *port) +{ + if((port->mapbase == 0xffe00000) || (port->mapbase == 0xffe08000)) /* SCIF0/1*/ + return SCIF_TXROOM_MAX - (sci_in(port, SCTFDR) & 0xff); + else /* SCIF2 */ + return SCIF2_TXROOM_MAX - (sci_in(port, SCFDR) >> 8); +} + +static inline int scif_rxroom(struct uart_port *port) +{ + if((port->mapbase == 0xffe00000) || (port->mapbase == 0xffe08000)) /* SCIF0/1*/ + return sci_in(port, SCRFDR) & 0xff; + else /* SCIF2 */ + return sci_in(port, SCFDR) & SCIF2_RFDC_MASK; +} #else static inline int scif_txroom(struct uart_port *port) { diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h index eb84833233fd..cd728df6a01a 100644 --- a/drivers/serial/sh-sci.h +++ b/drivers/serial/sh-sci.h @@ -123,8 +123,9 @@ #elif defined(CONFIG_CPU_SUBTYPE_SH7763) # define SCSPTR0 0xffe00024 /* 16 bit SCIF */ # define SCSPTR1 0xffe08024 /* 16 bit SCIF */ +# define SCSPTR2 0xffe10020 /* 16 bit SCIF/IRDA */ # define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ # define SCIF_ONLY #elif defined(CONFIG_CPU_SUBTYPE_SH7770) # define SCSPTR0 0xff923020 /* 16 bit SCIF */ @@ -188,6 +189,7 @@ defined(CONFIG_CPU_SUBTYPE_SH7750S) || \ defined(CONFIG_CPU_SUBTYPE_SH7751) || \ defined(CONFIG_CPU_SUBTYPE_SH7751R) || \ + defined(CONFIG_CPU_SUBTYPE_SH7763) || \ defined(CONFIG_CPU_SUBTYPE_SH7780) || \ defined(CONFIG_CPU_SUBTYPE_SH7785) || \ defined(CONFIG_CPU_SUBTYPE_SHX3) @@ -225,14 +227,21 @@ #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ defined(CONFIG_CPU_SUBTYPE_SH7721) -#define SCIF_ORER 0x0200 -#define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK | SCIF_ORER) -#define SCIF_RFDC_MASK 0x007f -#define SCIF_TXROOM_MAX 64 +# define SCIF_ORER 0x0200 +# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK | SCIF_ORER) +# define SCIF_RFDC_MASK 0x007f +# define SCIF_TXROOM_MAX 64 +#elif defined(CONFIG_CPU_SUBTYPE_SH7763) +# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK ) +# define SCIF_RFDC_MASK 0x007f +# define SCIF_TXROOM_MAX 64 +/* SH7763 SCIF2 support */ +# define SCIF2_RFDC_MASK 0x001f +# define SCIF2_TXROOM_MAX 16 #else -#define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) -#define SCIF_RFDC_MASK 0x001f -#define SCIF_TXROOM_MAX 16 +# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) +# define SCIF_RFDC_MASK 0x001f +# define SCIF_TXROOM_MAX 16 #endif #if defined(SCI_ONLY) @@ -445,11 +454,16 @@ SCIF_FNS(SCFCR, 0x0c, 8, 0x18, 16) defined(CONFIG_CPU_SUBTYPE_SH7763) || \ defined(CONFIG_CPU_SUBTYPE_SH7780) || \ defined(CONFIG_CPU_SUBTYPE_SH7785) -SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16) SCIF_FNS(SCTFDR, 0x0e, 16, 0x1C, 16) SCIF_FNS(SCRFDR, 0x0e, 16, 0x20, 16) SCIF_FNS(SCSPTR, 0, 0, 0x24, 16) SCIF_FNS(SCLSR, 0, 0, 0x28, 16) +#if defined(CONFIG_CPU_SUBTYPE_SH7763) +/* SH7763 SCIF2 */ +SCIF_FNS(SCFDR, 0, 0, 0x1C, 16) +SCIF_FNS(SCSPTR2, 0, 0, 0x20, 16) +SCIF_FNS(SCLSR2, 0, 0, 0x24, 16) +#endif /* CONFIG_CPU_SUBTYPE_SH7763 */ #else SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16) #if defined(CONFIG_CPU_SUBTYPE_SH7722) @@ -652,6 +666,9 @@ static inline int sci_rxd_in(struct uart_port *port) return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */ if (port->mapbase == 0xffe08000) return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */ + if (port->mapbase == 0xffe10000) + return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF/IRDA */ + return 1; } #elif defined(CONFIG_CPU_SUBTYPE_SH7770) @@ -764,8 +781,7 @@ static inline int sci_rxd_in(struct uart_port *port) * -- Mitch Davis - 15 Jul 2000 */ -#if defined(CONFIG_CPU_SUBTYPE_SH7763) || \ - defined(CONFIG_CPU_SUBTYPE_SH7780) || \ +#if defined(CONFIG_CPU_SUBTYPE_SH7780) || \ defined(CONFIG_CPU_SUBTYPE_SH7785) #define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(16*bps)-1) #elif defined(CONFIG_CPU_SUBTYPE_SH7705) || \ diff --git a/drivers/serial/zs.c b/drivers/serial/zs.c index bd45b6230fd8..9e6a873f8203 100644 --- a/drivers/serial/zs.c +++ b/drivers/serial/zs.c @@ -787,7 +787,6 @@ static int zs_startup(struct uart_port *uport) zport->regs[1] &= ~RxINT_MASK; zport->regs[1] |= RxINT_ALL | TxINT_ENAB | EXT_INT_ENAB; zport->regs[3] |= RxENABLE; - zport->regs[5] |= TxENAB; zport->regs[15] |= BRKIE; write_zsreg(zport, R1, zport->regs[1]); write_zsreg(zport, R3, zport->regs[3]); @@ -814,7 +813,6 @@ static void zs_shutdown(struct uart_port *uport) spin_lock_irqsave(&scc->zlock, flags); - zport->regs[5] &= ~TxENAB; zport->regs[3] &= ~RxENABLE; write_zsreg(zport, R5, zport->regs[5]); write_zsreg(zport, R3, zport->regs[3]); @@ -959,6 +957,23 @@ static void zs_set_termios(struct uart_port *uport, struct ktermios *termios, spin_unlock_irqrestore(&scc->zlock, flags); } +/* + * Hack alert! + * Required solely so that the initial PROM-based console + * works undisturbed in parallel with this one. + */ +static void zs_pm(struct uart_port *uport, unsigned int state, + unsigned int oldstate) +{ + struct zs_port *zport = to_zport(uport); + + if (state < 3) + zport->regs[5] |= TxENAB; + else + zport->regs[5] &= ~TxENAB; + write_zsreg(zport, R5, zport->regs[5]); +} + static const char *zs_type(struct uart_port *uport) { @@ -1041,6 +1056,7 @@ static struct uart_ops zs_ops = { .startup = zs_startup, .shutdown = zs_shutdown, .set_termios = zs_set_termios, + .pm = zs_pm, .type = zs_type, .release_port = zs_release_port, .request_port = zs_request_port, @@ -1190,6 +1206,7 @@ static int __init zs_console_setup(struct console *co, char *options) return ret; zs_reset(zport); + zs_pm(uport, 0, -1); if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 66ec5d8808de..2303521b4f09 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -49,25 +49,26 @@ config SPI_MASTER controller and the protocol drivers for the SPI slave chips that are connected. +if SPI_MASTER + comment "SPI Master Controller Drivers" - depends on SPI_MASTER config SPI_ATMEL tristate "Atmel SPI Controller" - depends on (ARCH_AT91 || AVR32) && SPI_MASTER + depends on (ARCH_AT91 || AVR32) help This selects a driver for the Atmel SPI Controller, present on many AT32 (AVR32) and AT91 (ARM) chips. config SPI_BFIN tristate "SPI controller driver for ADI Blackfin5xx" - depends on SPI_MASTER && BLACKFIN + depends on BLACKFIN help This is the SPI controller master driver for Blackfin 5xx processor. config SPI_AU1550 tristate "Au1550/Au12x0 SPI Controller" - depends on SPI_MASTER && (SOC_AU1550 || SOC_AU1200) && EXPERIMENTAL + depends on (SOC_AU1550 || SOC_AU1200) && EXPERIMENTAL select SPI_BITBANG help If you say yes to this option, support will be included for the @@ -78,7 +79,6 @@ config SPI_AU1550 config SPI_BITBANG tristate "Bitbanging SPI master" - depends on SPI_MASTER && EXPERIMENTAL help With a few GPIO pins, your system can bitbang the SPI protocol. Select this to get SPI support through I/O pins (GPIO, parallel @@ -92,7 +92,7 @@ config SPI_BITBANG config SPI_BUTTERFLY tristate "Parallel port adapter for AVR Butterfly (DEVELOPMENT)" - depends on SPI_MASTER && PARPORT && EXPERIMENTAL + depends on PARPORT select SPI_BITBANG help This uses a custom parallel port cable to connect to an AVR @@ -102,14 +102,14 @@ config SPI_BUTTERFLY config SPI_IMX tristate "Freescale iMX SPI controller" - depends on SPI_MASTER && ARCH_IMX && EXPERIMENTAL + depends on ARCH_IMX && EXPERIMENTAL help This enables using the Freescale iMX SPI controller in master mode. config SPI_LM70_LLP tristate "Parallel port adapter for LM70 eval board (DEVELOPMENT)" - depends on SPI_MASTER && PARPORT && EXPERIMENTAL + depends on PARPORT && EXPERIMENTAL select SPI_BITBANG help This driver supports the NS LM70 LLP Evaluation Board, @@ -118,14 +118,14 @@ config SPI_LM70_LLP config SPI_MPC52xx_PSC tristate "Freescale MPC52xx PSC SPI controller" - depends on SPI_MASTER && PPC_MPC52xx && EXPERIMENTAL + depends on PPC_MPC52xx && EXPERIMENTAL help This enables using the Freescale MPC52xx Programmable Serial Controller in master SPI mode. config SPI_MPC83xx tristate "Freescale MPC83xx/QUICC Engine SPI controller" - depends on SPI_MASTER && (PPC_83xx || QUICC_ENGINE) && EXPERIMENTAL + depends on (PPC_83xx || QUICC_ENGINE) && EXPERIMENTAL help This enables using the Freescale MPC83xx and QUICC Engine SPI controllers in master mode. @@ -137,21 +137,21 @@ config SPI_MPC83xx config SPI_OMAP_UWIRE tristate "OMAP1 MicroWire" - depends on SPI_MASTER && ARCH_OMAP1 + depends on ARCH_OMAP1 select SPI_BITBANG help This hooks up to the MicroWire controller on OMAP1 chips. config SPI_OMAP24XX tristate "McSPI driver for OMAP24xx/OMAP34xx" - depends on SPI_MASTER && (ARCH_OMAP24XX || ARCH_OMAP34XX) + depends on ARCH_OMAP24XX || ARCH_OMAP34XX help SPI master controller for OMAP24xx/OMAP34xx Multichannel SPI (McSPI) modules. config SPI_PXA2XX tristate "PXA2xx SSP SPI master" - depends on SPI_MASTER && ARCH_PXA && EXPERIMENTAL + depends on ARCH_PXA && EXPERIMENTAL select PXA_SSP help This enables using a PXA2xx SSP port as a SPI master controller. @@ -160,14 +160,14 @@ config SPI_PXA2XX config SPI_S3C24XX tristate "Samsung S3C24XX series SPI" - depends on SPI_MASTER && ARCH_S3C2410 && EXPERIMENTAL + depends on ARCH_S3C2410 && EXPERIMENTAL select SPI_BITBANG help SPI driver for Samsung S3C24XX series ARM SoCs config SPI_S3C24XX_GPIO tristate "Samsung S3C24XX series SPI by GPIO" - depends on SPI_MASTER && ARCH_S3C2410 && EXPERIMENTAL + depends on ARCH_S3C2410 && EXPERIMENTAL select SPI_BITBANG help SPI driver for Samsung S3C24XX series ARM SoCs using @@ -177,20 +177,20 @@ config SPI_S3C24XX_GPIO config SPI_SH_SCI tristate "SuperH SCI SPI controller" - depends on SPI_MASTER && SUPERH + depends on SUPERH select SPI_BITBANG help SPI driver for SuperH SCI blocks. config SPI_TXX9 tristate "Toshiba TXx9 SPI controller" - depends on SPI_MASTER && GENERIC_GPIO && CPU_TX49XX + depends on GENERIC_GPIO && CPU_TX49XX help SPI driver for Toshiba TXx9 MIPS SoCs config SPI_XILINX tristate "Xilinx SPI controller" - depends on SPI_MASTER && XILINX_VIRTEX && EXPERIMENTAL + depends on XILINX_VIRTEX && EXPERIMENTAL select SPI_BITBANG help This exposes the SPI controller IP from the Xilinx EDK. @@ -207,11 +207,10 @@ config SPI_XILINX # being probably the most widely used ones. # comment "SPI Protocol Masters" - depends on SPI_MASTER config SPI_AT25 tristate "SPI EEPROMs from most vendors" - depends on SPI_MASTER && SYSFS + depends on SYSFS help Enable this driver to get read/write support to most SPI EEPROMs, after you configure the board init code to know about each eeprom @@ -222,7 +221,7 @@ config SPI_AT25 config SPI_SPIDEV tristate "User mode SPI device driver support" - depends on SPI_MASTER && EXPERIMENTAL + depends on EXPERIMENTAL help This supports user mode SPI protocol drivers. @@ -231,7 +230,7 @@ config SPI_SPIDEV config SPI_TLE62X0 tristate "Infineon TLE62X0 (for power switching)" - depends on SPI_MASTER && SYSFS + depends on SYSFS help SPI driver for Infineon TLE62X0 series line driver chips, such as the TLE6220, TLE6230 and TLE6240. This provides a @@ -242,6 +241,8 @@ config SPI_TLE62X0 # Add new SPI protocol masters in alphabetical order above this line # +endif # SPI_MASTER + # (slave support would go here) endif # SPI diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index e81d59d78910..0c7165660853 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -313,14 +313,14 @@ atmel_spi_dma_map_xfer(struct atmel_spi *as, struct spi_transfer *xfer) xfer->tx_dma = dma_map_single(dev, (void *) xfer->tx_buf, xfer->len, DMA_TO_DEVICE); - if (dma_mapping_error(xfer->tx_dma)) + if (dma_mapping_error(dev, xfer->tx_dma)) return -ENOMEM; } if (xfer->rx_buf) { xfer->rx_dma = dma_map_single(dev, xfer->rx_buf, xfer->len, DMA_FROM_DEVICE); - if (dma_mapping_error(xfer->rx_dma)) { + if (dma_mapping_error(dev, xfer->rx_dma)) { if (xfer->tx_buf) dma_unmap_single(dev, xfer->tx_dma, xfer->len, diff --git a/drivers/spi/au1550_spi.c b/drivers/spi/au1550_spi.c index 072c4a595334..87b73e0169c5 100644 --- a/drivers/spi/au1550_spi.c +++ b/drivers/spi/au1550_spi.c @@ -26,6 +26,7 @@ #include <linux/errno.h> #include <linux/device.h> #include <linux/platform_device.h> +#include <linux/resource.h> #include <linux/spi/spi.h> #include <linux/spi/spi_bitbang.h> #include <linux/dma-mapping.h> @@ -81,6 +82,7 @@ struct au1550_spi { struct spi_master *master; struct device *dev; struct au1550_spi_info *pdata; + struct resource *ioarea; }; @@ -96,6 +98,8 @@ static dbdev_tab_t au1550_spi_mem_dbdev = .dev_intpolarity = 0 }; +static int ddma_memid; /* id to above mem dma device */ + static void au1550_spi_bits_handlers_set(struct au1550_spi *hw, int bpw); @@ -330,7 +334,7 @@ static int au1550_spi_dma_rxtmp_alloc(struct au1550_spi *hw, unsigned size) hw->dma_rx_tmpbuf_size = size; hw->dma_rx_tmpbuf_addr = dma_map_single(hw->dev, hw->dma_rx_tmpbuf, size, DMA_FROM_DEVICE); - if (dma_mapping_error(hw->dma_rx_tmpbuf_addr)) { + if (dma_mapping_error(hw->dev, hw->dma_rx_tmpbuf_addr)) { kfree(hw->dma_rx_tmpbuf); hw->dma_rx_tmpbuf = 0; hw->dma_rx_tmpbuf_size = 0; @@ -374,7 +378,7 @@ static int au1550_spi_dma_txrxb(struct spi_device *spi, struct spi_transfer *t) dma_rx_addr = dma_map_single(hw->dev, (void *)t->rx_buf, t->len, DMA_FROM_DEVICE); - if (dma_mapping_error(dma_rx_addr)) + if (dma_mapping_error(hw->dev, dma_rx_addr)) dev_err(hw->dev, "rx dma map error\n"); } } else { @@ -397,7 +401,7 @@ static int au1550_spi_dma_txrxb(struct spi_device *spi, struct spi_transfer *t) dma_tx_addr = dma_map_single(hw->dev, (void *)t->tx_buf, t->len, DMA_TO_DEVICE); - if (dma_mapping_error(dma_tx_addr)) + if (dma_mapping_error(hw->dev, dma_tx_addr)) dev_err(hw->dev, "tx dma map error\n"); } } else { @@ -480,9 +484,13 @@ static irqreturn_t au1550_spi_dma_irq_callback(struct au1550_spi *hw) au1xxx_dbdma_reset(hw->dma_tx_ch); au1550_spi_reset_fifos(hw); - dev_err(hw->dev, - "Unexpected SPI error: event=0x%x stat=0x%x!\n", - evnt, stat); + if (evnt == PSC_SPIEVNT_RO) + dev_err(hw->dev, + "dma transfer: receive FIFO overflow!\n"); + else + dev_err(hw->dev, + "dma transfer: unexpected SPI error " + "(event=0x%x stat=0x%x)!\n", evnt, stat); complete(&hw->master_done); return IRQ_HANDLED; @@ -592,17 +600,17 @@ static irqreturn_t au1550_spi_pio_irq_callback(struct au1550_spi *hw) if ((evnt & (PSC_SPIEVNT_MM | PSC_SPIEVNT_RO | PSC_SPIEVNT_RU | PSC_SPIEVNT_TO - | PSC_SPIEVNT_TU | PSC_SPIEVNT_SD)) + | PSC_SPIEVNT_SD)) != 0) { - dev_err(hw->dev, - "Unexpected SPI error: event=0x%x stat=0x%x!\n", - evnt, stat); /* * due to an error we consider transfer as done, * so mask all events until before next transfer start */ au1550_spi_mask_ack_all(hw); au1550_spi_reset_fifos(hw); + dev_err(hw->dev, + "pio transfer: unexpected SPI error " + "(event=0x%x stat=0x%x)!\n", evnt, stat); complete(&hw->master_done); return IRQ_HANDLED; } @@ -616,27 +624,50 @@ static irqreturn_t au1550_spi_pio_irq_callback(struct au1550_spi *hw) stat = hw->regs->psc_spistat; au_sync(); - if ((stat & PSC_SPISTAT_RE) == 0 && hw->rx_count < hw->len) { + /* + * Take care to not let the Rx FIFO overflow. + * + * We only write a byte if we have read one at least. Initially, + * the write fifo is full, so we should read from the read fifo + * first. + * In case we miss a word from the read fifo, we should get a + * RO event and should back out. + */ + if (!(stat & PSC_SPISTAT_RE) && hw->rx_count < hw->len) { hw->rx_word(hw); - /* ack the receive request event */ - hw->regs->psc_spievent = PSC_SPIEVNT_RR; - au_sync(); busy = 1; - } - if ((stat & PSC_SPISTAT_TF) == 0 && hw->tx_count < hw->len) { - hw->tx_word(hw); - /* ack the transmit request event */ - hw->regs->psc_spievent = PSC_SPIEVNT_TR; - au_sync(); - busy = 1; + if (!(stat & PSC_SPISTAT_TF) && hw->tx_count < hw->len) + hw->tx_word(hw); } } while (busy); - evnt = hw->regs->psc_spievent; + hw->regs->psc_spievent = PSC_SPIEVNT_RR | PSC_SPIEVNT_TR; au_sync(); - if (hw->rx_count >= hw->len || (evnt & PSC_SPIEVNT_MD) != 0) { + /* + * Restart the SPI transmission in case of a transmit underflow. + * This seems to work despite the notes in the Au1550 data book + * of Figure 8-4 with flowchart for SPI master operation: + * + * """Note 1: An XFR Error Interrupt occurs, unless masked, + * for any of the following events: Tx FIFO Underflow, + * Rx FIFO Overflow, or Multiple-master Error + * Note 2: In case of a Tx Underflow Error, all zeroes are + * transmitted.""" + * + * By simply restarting the spi transfer on Tx Underflow Error, + * we assume that spi transfer was paused instead of zeroes + * transmittion mentioned in the Note 2 of Au1550 data book. + */ + if (evnt & PSC_SPIEVNT_TU) { + hw->regs->psc_spievent = PSC_SPIEVNT_TU | PSC_SPIEVNT_MD; + au_sync(); + hw->regs->psc_spipcr = PSC_SPIPCR_MS; + au_sync(); + } + + if (hw->rx_count >= hw->len) { /* transfer completed successfully */ au1550_spi_mask_ack_all(hw); complete(&hw->master_done); @@ -725,6 +756,8 @@ static void __init au1550_spi_setup_psc_as_spi(struct au1550_spi *hw) stat = hw->regs->psc_spistat; au_sync(); } while ((stat & PSC_SPISTAT_DR) == 0); + + au1550_spi_reset_fifos(hw); } @@ -732,6 +765,7 @@ static int __init au1550_spi_probe(struct platform_device *pdev) { struct au1550_spi *hw; struct spi_master *master; + struct resource *r; int err = 0; master = spi_alloc_master(&pdev->dev, sizeof(struct au1550_spi)); @@ -753,76 +787,64 @@ static int __init au1550_spi_probe(struct platform_device *pdev) goto err_no_pdata; } - platform_set_drvdata(pdev, hw); - - init_completion(&hw->master_done); - - hw->bitbang.master = hw->master; - hw->bitbang.setup_transfer = au1550_spi_setupxfer; - hw->bitbang.chipselect = au1550_spi_chipsel; - hw->bitbang.master->setup = au1550_spi_setup; - hw->bitbang.txrx_bufs = au1550_spi_txrx_bufs; + r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!r) { + dev_err(&pdev->dev, "no IRQ\n"); + err = -ENODEV; + goto err_no_iores; + } + hw->irq = r->start; + + hw->usedma = 0; + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (r) { + hw->dma_tx_id = r->start; + r = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (r) { + hw->dma_rx_id = r->start; + if (usedma && ddma_memid) { + if (pdev->dev.dma_mask == NULL) + dev_warn(&pdev->dev, "no dma mask\n"); + else + hw->usedma = 1; + } + } + } - switch (hw->pdata->bus_num) { - case 0: - hw->irq = AU1550_PSC0_INT; - hw->regs = (volatile psc_spi_t *)PSC0_BASE_ADDR; - hw->dma_rx_id = DSCR_CMD0_PSC0_RX; - hw->dma_tx_id = DSCR_CMD0_PSC0_TX; - break; - case 1: - hw->irq = AU1550_PSC1_INT; - hw->regs = (volatile psc_spi_t *)PSC1_BASE_ADDR; - hw->dma_rx_id = DSCR_CMD0_PSC1_RX; - hw->dma_tx_id = DSCR_CMD0_PSC1_TX; - break; - case 2: - hw->irq = AU1550_PSC2_INT; - hw->regs = (volatile psc_spi_t *)PSC2_BASE_ADDR; - hw->dma_rx_id = DSCR_CMD0_PSC2_RX; - hw->dma_tx_id = DSCR_CMD0_PSC2_TX; - break; - case 3: - hw->irq = AU1550_PSC3_INT; - hw->regs = (volatile psc_spi_t *)PSC3_BASE_ADDR; - hw->dma_rx_id = DSCR_CMD0_PSC3_RX; - hw->dma_tx_id = DSCR_CMD0_PSC3_TX; - break; - default: - dev_err(&pdev->dev, "Wrong bus_num of SPI\n"); - err = -ENOENT; - goto err_no_pdata; + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no mmio resource\n"); + err = -ENODEV; + goto err_no_iores; } - if (request_mem_region((unsigned long)hw->regs, sizeof(psc_spi_t), - pdev->name) == NULL) { + hw->ioarea = request_mem_region(r->start, sizeof(psc_spi_t), + pdev->name); + if (!hw->ioarea) { dev_err(&pdev->dev, "Cannot reserve iomem region\n"); err = -ENXIO; goto err_no_iores; } - - if (usedma) { - if (pdev->dev.dma_mask == NULL) - dev_warn(&pdev->dev, "no dma mask\n"); - else - hw->usedma = 1; + hw->regs = (psc_spi_t __iomem *)ioremap(r->start, sizeof(psc_spi_t)); + if (!hw->regs) { + dev_err(&pdev->dev, "cannot ioremap\n"); + err = -ENXIO; + goto err_ioremap; } - if (hw->usedma) { - /* - * create memory device with 8 bits dev_devwidth - * needed for proper byte ordering to spi fifo - */ - int memid = au1xxx_ddma_add_device(&au1550_spi_mem_dbdev); - if (!memid) { - dev_err(&pdev->dev, - "Cannot create dma 8 bit mem device\n"); - err = -ENXIO; - goto err_dma_add_dev; - } + platform_set_drvdata(pdev, hw); + + init_completion(&hw->master_done); + + hw->bitbang.master = hw->master; + hw->bitbang.setup_transfer = au1550_spi_setupxfer; + hw->bitbang.chipselect = au1550_spi_chipsel; + hw->bitbang.master->setup = au1550_spi_setup; + hw->bitbang.txrx_bufs = au1550_spi_txrx_bufs; - hw->dma_tx_ch = au1xxx_dbdma_chan_alloc(memid, + if (hw->usedma) { + hw->dma_tx_ch = au1xxx_dbdma_chan_alloc(ddma_memid, hw->dma_tx_id, NULL, (void *)hw); if (hw->dma_tx_ch == 0) { dev_err(&pdev->dev, @@ -841,7 +863,7 @@ static int __init au1550_spi_probe(struct platform_device *pdev) hw->dma_rx_ch = au1xxx_dbdma_chan_alloc(hw->dma_rx_id, - memid, NULL, (void *)hw); + ddma_memid, NULL, (void *)hw); if (hw->dma_rx_ch == 0) { dev_err(&pdev->dev, "Cannot allocate rx dma channel\n"); @@ -874,7 +896,7 @@ static int __init au1550_spi_probe(struct platform_device *pdev) goto err_no_irq; } - master->bus_num = hw->pdata->bus_num; + master->bus_num = pdev->id; master->num_chipselect = hw->pdata->num_chipselect; /* @@ -924,8 +946,11 @@ err_no_txdma_descr: au1xxx_dbdma_chan_free(hw->dma_tx_ch); err_no_txdma: -err_dma_add_dev: - release_mem_region((unsigned long)hw->regs, sizeof(psc_spi_t)); + iounmap((void __iomem *)hw->regs); + +err_ioremap: + release_resource(hw->ioarea); + kfree(hw->ioarea); err_no_iores: err_no_pdata: @@ -944,7 +969,9 @@ static int __exit au1550_spi_remove(struct platform_device *pdev) spi_bitbang_stop(&hw->bitbang); free_irq(hw->irq, hw); - release_mem_region((unsigned long)hw->regs, sizeof(psc_spi_t)); + iounmap((void __iomem *)hw->regs); + release_resource(hw->ioarea); + kfree(hw->ioarea); if (hw->usedma) { au1550_spi_dma_rxtmp_free(hw); @@ -971,12 +998,24 @@ static struct platform_driver au1550_spi_drv = { static int __init au1550_spi_init(void) { + /* + * create memory device with 8 bits dev_devwidth + * needed for proper byte ordering to spi fifo + */ + if (usedma) { + ddma_memid = au1xxx_ddma_add_device(&au1550_spi_mem_dbdev); + if (!ddma_memid) + printk(KERN_ERR "au1550-spi: cannot add memory" + "dbdma device\n"); + } return platform_driver_probe(&au1550_spi_drv, au1550_spi_probe); } module_init(au1550_spi_init); static void __exit au1550_spi_exit(void) { + if (usedma && ddma_memid) + au1xxx_ddma_del_device(ddma_memid); platform_driver_unregister(&au1550_spi_drv); } module_exit(au1550_spi_exit); diff --git a/drivers/spi/mpc52xx_psc_spi.c b/drivers/spi/mpc52xx_psc_spi.c index 604e5f0a2d95..25eda71f4bf4 100644 --- a/drivers/spi/mpc52xx_psc_spi.c +++ b/drivers/spi/mpc52xx_psc_spi.c @@ -148,7 +148,6 @@ static int mpc52xx_psc_spi_transfer_rxtx(struct spi_device *spi, unsigned rfalarm; unsigned send_at_once = MPC52xx_PSC_BUFSIZE; unsigned recv_at_once; - unsigned bpw = mps->bits_per_word / 8; if (!t->tx_buf && !t->rx_buf && t->len) return -EINVAL; @@ -164,22 +163,15 @@ static int mpc52xx_psc_spi_transfer_rxtx(struct spi_device *spi, } dev_dbg(&spi->dev, "send %d bytes...\n", send_at_once); - if (tx_buf) { - for (; send_at_once; sb++, send_at_once--) { - /* set EOF flag */ - if (mps->bits_per_word - && (sb + 1) % bpw == 0) - out_8(&psc->ircr2, 0x01); + for (; send_at_once; sb++, send_at_once--) { + /* set EOF flag before the last word is sent */ + if (send_at_once == 1) + out_8(&psc->ircr2, 0x01); + + if (tx_buf) out_8(&psc->mpc52xx_psc_buffer_8, tx_buf[sb]); - } - } else { - for (; send_at_once; sb++, send_at_once--) { - /* set EOF flag */ - if (mps->bits_per_word - && ((sb + 1) % bpw) == 0) - out_8(&psc->ircr2, 0x01); + else out_8(&psc->mpc52xx_psc_buffer_8, 0); - } } diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index b1cc148036c1..f6f987bb71ca 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -836,7 +836,7 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m) if (tx_buf != NULL) { t->tx_dma = dma_map_single(&spi->dev, (void *) tx_buf, len, DMA_TO_DEVICE); - if (dma_mapping_error(t->tx_dma)) { + if (dma_mapping_error(&spi->dev, t->tx_dma)) { dev_dbg(&spi->dev, "dma %cX %d bytes error\n", 'T', len); return -EINVAL; @@ -845,7 +845,7 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m) if (rx_buf != NULL) { t->rx_dma = dma_map_single(&spi->dev, rx_buf, t->len, DMA_FROM_DEVICE); - if (dma_mapping_error(t->rx_dma)) { + if (dma_mapping_error(&spi->dev, t->rx_dma)) { dev_dbg(&spi->dev, "dma %cX %d bytes error\n", 'R', len); if (tx_buf != NULL) diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 0c452c46ab07..067299d6d192 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -353,7 +353,7 @@ static int map_dma_buffers(struct driver_data *drv_data) drv_data->rx_dma = dma_map_single(dev, drv_data->rx, drv_data->rx_map_len, DMA_FROM_DEVICE); - if (dma_mapping_error(drv_data->rx_dma)) + if (dma_mapping_error(dev, drv_data->rx_dma)) return 0; /* Stream map the tx buffer */ @@ -361,7 +361,7 @@ static int map_dma_buffers(struct driver_data *drv_data) drv_data->tx_map_len, DMA_TO_DEVICE); - if (dma_mapping_error(drv_data->tx_dma)) { + if (dma_mapping_error(dev, drv_data->tx_dma)) { dma_unmap_single(dev, drv_data->rx_dma, drv_data->rx_map_len, DMA_FROM_DEVICE); return 0; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 1ad12afc6ba0..964124b60db2 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -178,6 +178,96 @@ struct boardinfo { static LIST_HEAD(board_list); static DEFINE_MUTEX(board_lock); +/** + * spi_alloc_device - Allocate a new SPI device + * @master: Controller to which device is connected + * Context: can sleep + * + * Allows a driver to allocate and initialize a spi_device without + * registering it immediately. This allows a driver to directly + * fill the spi_device with device parameters before calling + * spi_add_device() on it. + * + * Caller is responsible to call spi_add_device() on the returned + * spi_device structure to add it to the SPI master. If the caller + * needs to discard the spi_device without adding it, then it should + * call spi_dev_put() on it. + * + * Returns a pointer to the new device, or NULL. + */ +struct spi_device *spi_alloc_device(struct spi_master *master) +{ + struct spi_device *spi; + struct device *dev = master->dev.parent; + + if (!spi_master_get(master)) + return NULL; + + spi = kzalloc(sizeof *spi, GFP_KERNEL); + if (!spi) { + dev_err(dev, "cannot alloc spi_device\n"); + spi_master_put(master); + return NULL; + } + + spi->master = master; + spi->dev.parent = dev; + spi->dev.bus = &spi_bus_type; + spi->dev.release = spidev_release; + device_initialize(&spi->dev); + return spi; +} +EXPORT_SYMBOL_GPL(spi_alloc_device); + +/** + * spi_add_device - Add spi_device allocated with spi_alloc_device + * @spi: spi_device to register + * + * Companion function to spi_alloc_device. Devices allocated with + * spi_alloc_device can be added onto the spi bus with this function. + * + * Returns 0 on success; non-zero on failure + */ +int spi_add_device(struct spi_device *spi) +{ + struct device *dev = spi->master->dev.parent; + int status; + + /* Chipselects are numbered 0..max; validate. */ + if (spi->chip_select >= spi->master->num_chipselect) { + dev_err(dev, "cs%d >= max %d\n", + spi->chip_select, + spi->master->num_chipselect); + return -EINVAL; + } + + /* Set the bus ID string */ + snprintf(spi->dev.bus_id, sizeof spi->dev.bus_id, + "%s.%u", spi->master->dev.bus_id, + spi->chip_select); + + /* drivers may modify this initial i/o setup */ + status = spi->master->setup(spi); + if (status < 0) { + dev_err(dev, "can't %s %s, status %d\n", + "setup", spi->dev.bus_id, status); + return status; + } + + /* driver core catches callers that misbehave by defining + * devices that already exist. + */ + status = device_add(&spi->dev); + if (status < 0) { + dev_err(dev, "can't %s %s, status %d\n", + "add", spi->dev.bus_id, status); + return status; + } + + dev_dbg(dev, "registered child %s\n", spi->dev.bus_id); + return 0; +} +EXPORT_SYMBOL_GPL(spi_add_device); /** * spi_new_device - instantiate one new SPI device @@ -197,7 +287,6 @@ struct spi_device *spi_new_device(struct spi_master *master, struct spi_board_info *chip) { struct spi_device *proxy; - struct device *dev = master->dev.parent; int status; /* NOTE: caller did any chip->bus_num checks necessary. @@ -207,64 +296,28 @@ struct spi_device *spi_new_device(struct spi_master *master, * suggests syslogged diagnostics are best here (ugh). */ - /* Chipselects are numbered 0..max; validate. */ - if (chip->chip_select >= master->num_chipselect) { - dev_err(dev, "cs%d > max %d\n", - chip->chip_select, - master->num_chipselect); + proxy = spi_alloc_device(master); + if (!proxy) return NULL; - } - if (!spi_master_get(master)) - return NULL; + WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias)); - proxy = kzalloc(sizeof *proxy, GFP_KERNEL); - if (!proxy) { - dev_err(dev, "can't alloc dev for cs%d\n", - chip->chip_select); - goto fail; - } - proxy->master = master; proxy->chip_select = chip->chip_select; proxy->max_speed_hz = chip->max_speed_hz; proxy->mode = chip->mode; proxy->irq = chip->irq; - proxy->modalias = chip->modalias; - - snprintf(proxy->dev.bus_id, sizeof proxy->dev.bus_id, - "%s.%u", master->dev.bus_id, - chip->chip_select); - proxy->dev.parent = dev; - proxy->dev.bus = &spi_bus_type; + strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias)); proxy->dev.platform_data = (void *) chip->platform_data; proxy->controller_data = chip->controller_data; proxy->controller_state = NULL; - proxy->dev.release = spidev_release; - /* drivers may modify this initial i/o setup */ - status = master->setup(proxy); + status = spi_add_device(proxy); if (status < 0) { - dev_err(dev, "can't %s %s, status %d\n", - "setup", proxy->dev.bus_id, status); - goto fail; + spi_dev_put(proxy); + return NULL; } - /* driver core catches callers that misbehave by defining - * devices that already exist. - */ - status = device_register(&proxy->dev); - if (status < 0) { - dev_err(dev, "can't %s %s, status %d\n", - "add", proxy->dev.bus_id, status); - goto fail; - } - dev_dbg(dev, "registered child %s\n", proxy->dev.bus_id); return proxy; - -fail: - spi_master_put(master); - kfree(proxy); - return NULL; } EXPORT_SYMBOL_GPL(spi_new_device); @@ -502,7 +555,7 @@ struct spi_master *spi_busnum_to_master(u16 bus_num) struct device *dev; struct spi_master *master = NULL; - dev = class_find_device(&spi_master_class, &bus_num, + dev = class_find_device(&spi_master_class, NULL, &bus_num, __spi_master_match); if (dev) master = container_of(dev, struct spi_master, dev); diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index 54ac7bea5f8c..6fb77fcc4971 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -491,7 +491,7 @@ static int map_dma_buffers(struct driver_data *drv_data) buf, drv_data->tx_map_len, DMA_TO_DEVICE); - if (dma_mapping_error(drv_data->tx_dma)) + if (dma_mapping_error(dev, drv_data->tx_dma)) return -1; drv_data->tx_dma_needs_unmap = 1; @@ -516,7 +516,7 @@ static int map_dma_buffers(struct driver_data *drv_data) buf, drv_data->len, DMA_FROM_DEVICE); - if (dma_mapping_error(drv_data->rx_dma)) + if (dma_mapping_error(dev, drv_data->rx_dma)) return -1; drv_data->rx_dma_needs_unmap = 1; } @@ -534,7 +534,7 @@ static int map_dma_buffers(struct driver_data *drv_data) buf, drv_data->tx_map_len, DMA_TO_DEVICE); - if (dma_mapping_error(drv_data->tx_dma)) { + if (dma_mapping_error(dev, drv_data->tx_dma)) { if (drv_data->rx_dma) { dma_unmap_single(dev, drv_data->rx_dma, diff --git a/drivers/spi/spi_mpc83xx.c b/drivers/spi/spi_mpc83xx.c index 6832da6f7109..070c6219e2d6 100644 --- a/drivers/spi/spi_mpc83xx.c +++ b/drivers/spi/spi_mpc83xx.c @@ -266,21 +266,24 @@ int mpc83xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) cs->hw_mode |= SPMODE_LEN(bits_per_word); - if ((mpc83xx_spi->spibrg / hz) >= 64) { - pm = mpc83xx_spi->spibrg / (hz * 64) - 1; - if (pm > 0x0f) { - dev_err(&spi->dev, "Requested speed is too " - "low: %d Hz. Will use %d Hz instead.\n", - hz, mpc83xx_spi->spibrg / 1024); - pm = 0x0f; + if ((mpc83xx_spi->spibrg / hz) > 64) { + pm = mpc83xx_spi->spibrg / (hz * 64); + if (pm > 16) { + cs->hw_mode |= SPMODE_DIV16; + pm /= 16; + if (pm > 16) { + dev_err(&spi->dev, "Requested speed is too " + "low: %d Hz. Will use %d Hz instead.\n", + hz, mpc83xx_spi->spibrg / 1024); + pm = 16; + } } - cs->hw_mode |= SPMODE_PM(pm) | SPMODE_DIV16; - } else { + } else pm = mpc83xx_spi->spibrg / (hz * 4); - if (pm) - pm--; - cs->hw_mode |= SPMODE_PM(pm); - } + if (pm) + pm--; + + cs->hw_mode |= SPMODE_PM(pm); regval = mpc83xx_spi_read_reg(&mpc83xx_spi->base->mode); if (cs->hw_mode != regval) { unsigned long flags; diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c index 0885cc357a37..1c643c9e1f15 100644 --- a/drivers/spi/spi_s3c24xx.c +++ b/drivers/spi/spi_s3c24xx.c @@ -270,6 +270,7 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev) /* setup the master state. */ master->num_chipselect = hw->pdata->num_cs; + master->bus_num = pdata->bus_num; /* setup the state for the bitbang driver */ diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index ddbe1a5e970e..e5e0cfed5e3b 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -228,7 +228,6 @@ static int spidev_message(struct spidev_data *spidev, * We walk the array of user-provided transfers, using each one * to initialize a kernel version of the same transfer. */ - mutex_lock(&spidev->buf_lock); buf = spidev->buffer; total = 0; for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers; @@ -296,14 +295,12 @@ static int spidev_message(struct spidev_data *spidev, status = total; done: - mutex_unlock(&spidev->buf_lock); kfree(k_xfers); return status; } -static int -spidev_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) +static long +spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int err = 0; int retval = 0; @@ -341,6 +338,14 @@ spidev_ioctl(struct inode *inode, struct file *filp, if (spi == NULL) return -ESHUTDOWN; + /* use the buffer lock here for triple duty: + * - prevent I/O (from us) so calling spi_setup() is safe; + * - prevent concurrent SPI_IOC_WR_* from morphing + * data fields while SPI_IOC_RD_* reads them; + * - SPI_IOC_MESSAGE needs the buffer locked "normally". + */ + mutex_lock(&spidev->buf_lock); + switch (cmd) { /* read requests */ case SPI_IOC_RD_MODE: @@ -456,6 +461,8 @@ spidev_ioctl(struct inode *inode, struct file *filp, kfree(ioc); break; } + + mutex_unlock(&spidev->buf_lock); spi_dev_put(spi); return retval; } @@ -533,7 +540,7 @@ static struct file_operations spidev_fops = { */ .write = spidev_write, .read = spidev_read, - .ioctl = spidev_ioctl, + .unlocked_ioctl = spidev_ioctl, .open = spidev_open, .release = spidev_release, }; @@ -576,7 +583,8 @@ static int spidev_probe(struct spi_device *spi) struct device *dev; spidev->devt = MKDEV(SPIDEV_MAJOR, minor); - dev = device_create(spidev_class, &spi->dev, spidev->devt, + dev = device_create_drvdata(spidev_class, &spi->dev, + spidev->devt, spidev, "spidev%d.%d", spi->master->bus_num, spi->chip_select); status = IS_ERR(dev) ? PTR_ERR(dev) : 0; @@ -586,7 +594,6 @@ static int spidev_probe(struct spi_device *spi) } if (status == 0) { set_bit(minor, minors); - spi_set_drvdata(spi, spidev); list_add(&spidev->device_entry, &device_list); } mutex_unlock(&device_list_lock); diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index 113a0468ffcb..68d6f4988fb5 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -353,11 +353,12 @@ static int __init xilinx_spi_probe(struct platform_device *dev) goto put_master; } - xspi->irq = platform_get_irq(dev, 0); - if (xspi->irq < 0) { + ret = platform_get_irq(dev, 0); + if (ret < 0) { ret = -ENXIO; goto unmap_io; } + xspi->irq = ret; master->bus_num = pdata->bus_num; master->num_chipselect = pdata->num_chipselect; diff --git a/drivers/telephony/ixj.c b/drivers/telephony/ixj.c index 49cd9793404f..ec7aeb502d15 100644 --- a/drivers/telephony/ixj.c +++ b/drivers/telephony/ixj.c @@ -6095,15 +6095,15 @@ static int capabilities_check(IXJ *j, struct phone_capability *pcreq) return retval; } -static int ixj_ioctl(struct inode *inode, struct file *file_p, unsigned int cmd, unsigned long arg) +static long do_ixj_ioctl(struct file *file_p, unsigned int cmd, unsigned long arg) { IXJ_TONE ti; IXJ_FILTER jf; IXJ_FILTER_RAW jfr; void __user *argp = (void __user *)arg; - - unsigned int raise, mant; + struct inode *inode = file_p->f_path.dentry->d_inode; unsigned int minor = iminor(inode); + unsigned int raise, mant; int board = NUM(inode); IXJ *j = get_ixj(NUM(inode)); @@ -6661,6 +6661,15 @@ static int ixj_ioctl(struct inode *inode, struct file *file_p, unsigned int cmd, return retval; } +static long ixj_ioctl(struct file *file_p, unsigned int cmd, unsigned long arg) +{ + long ret; + lock_kernel(); + ret = do_ixj_ioctl(file_p, cmd, arg); + unlock_kernel(); + return ret; +} + static int ixj_fasync(int fd, struct file *file_p, int mode) { IXJ *j = get_ixj(NUM(file_p->f_path.dentry->d_inode)); @@ -6674,7 +6683,7 @@ static const struct file_operations ixj_fops = .read = ixj_enhanced_read, .write = ixj_enhanced_write, .poll = ixj_poll, - .ioctl = ixj_ioctl, + .unlocked_ioctl = ixj_ioctl, .release = ixj_release, .fasync = ixj_fasync }; diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig index a4aaab9c7ddc..2e9079df26b3 100644 --- a/drivers/uio/Kconfig +++ b/drivers/uio/Kconfig @@ -15,7 +15,7 @@ if UIO config UIO_CIF tristate "generic Hilscher CIF Card driver" - depends on UIO && PCI + depends on PCI default n help Driver for Hilscher CIF DeviceNet and Profibus cards. This @@ -26,9 +26,15 @@ config UIO_CIF To compile this driver as a module, choose M here: the module will be called uio_cif. +config UIO_PDRV + tristate "Userspace I/O platform driver" + help + Generic platform driver for Userspace I/O devices. + + If you don't know what to do here, say N. + config UIO_SMX tristate "SMX cryptengine UIO interface" - depends on UIO default n help Userspace IO interface to the Cryptography engine found on the diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile index 18c45662431e..e00ce0def1a0 100644 --- a/drivers/uio/Makefile +++ b/drivers/uio/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_UIO) += uio.o obj-$(CONFIG_UIO_CIF) += uio_cif.o +obj-$(CONFIG_UIO_PDRV) += uio_pdrv.o obj-$(CONFIG_UIO_SMX) += uio_smx.o diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 5a7ca2e6094d..3a6934bf7131 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -427,6 +427,31 @@ static ssize_t uio_read(struct file *filep, char __user *buf, return retval; } +static ssize_t uio_write(struct file *filep, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct uio_listener *listener = filep->private_data; + struct uio_device *idev = listener->dev; + ssize_t retval; + s32 irq_on; + + if (idev->info->irq == UIO_IRQ_NONE) + return -EIO; + + if (count != sizeof(s32)) + return -EINVAL; + + if (!idev->info->irqcontrol) + return -ENOSYS; + + if (copy_from_user(&irq_on, buf, count)) + return -EFAULT; + + retval = idev->info->irqcontrol(idev->info, irq_on); + + return retval ? retval : sizeof(s32); +} + static int uio_find_mem_index(struct vm_area_struct *vma) { int mi; @@ -546,6 +571,7 @@ static const struct file_operations uio_fops = { .open = uio_open, .release = uio_release, .read = uio_read, + .write = uio_write, .mmap = uio_mmap, .poll = uio_poll, .fasync = uio_fasync, diff --git a/drivers/uio/uio_pdrv.c b/drivers/uio/uio_pdrv.c new file mode 100644 index 000000000000..5d0d2e85d982 --- /dev/null +++ b/drivers/uio/uio_pdrv.c @@ -0,0 +1,118 @@ +/* + * drivers/uio/uio_pdrv.c + * + * Copyright (C) 2008 by Digi International Inc. + * All rights reserved. + * + * 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/platform_device.h> +#include <linux/uio_driver.h> +#include <linux/stringify.h> + +#define DRIVER_NAME "uio" + +struct uio_platdata { + struct uio_info *uioinfo; +}; + +static int uio_pdrv_probe(struct platform_device *pdev) +{ + struct uio_info *uioinfo = pdev->dev.platform_data; + struct uio_platdata *pdata; + struct uio_mem *uiomem; + int ret = -ENODEV; + int i; + + if (!uioinfo || !uioinfo->name || !uioinfo->version) { + dev_dbg(&pdev->dev, "%s: err_uioinfo\n", __func__); + goto err_uioinfo; + } + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + ret = -ENOMEM; + dev_dbg(&pdev->dev, "%s: err_alloc_pdata\n", __func__); + goto err_alloc_pdata; + } + + pdata->uioinfo = uioinfo; + + uiomem = &uioinfo->mem[0]; + + for (i = 0; i < pdev->num_resources; ++i) { + struct resource *r = &pdev->resource[i]; + + if (r->flags != IORESOURCE_MEM) + continue; + + if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) { + dev_warn(&pdev->dev, "device has more than " + __stringify(MAX_UIO_MAPS) + " I/O memory resources.\n"); + break; + } + + uiomem->memtype = UIO_MEM_PHYS; + uiomem->addr = r->start; + uiomem->size = r->end - r->start + 1; + ++uiomem; + } + + while (uiomem < &uioinfo->mem[MAX_UIO_MAPS]) { + uiomem->size = 0; + ++uiomem; + } + + pdata->uioinfo->priv = pdata; + + ret = uio_register_device(&pdev->dev, pdata->uioinfo); + + if (ret) { + kfree(pdata); +err_alloc_pdata: +err_uioinfo: + return ret; + } + + platform_set_drvdata(pdev, pdata); + + return 0; +} + +static int uio_pdrv_remove(struct platform_device *pdev) +{ + struct uio_platdata *pdata = platform_get_drvdata(pdev); + + uio_unregister_device(pdata->uioinfo); + + return 0; +} + +static struct platform_driver uio_pdrv = { + .probe = uio_pdrv_probe, + .remove = uio_pdrv_remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init uio_pdrv_init(void) +{ + return platform_driver_register(&uio_pdrv); +} + +static void __exit uio_pdrv_exit(void) +{ + platform_driver_unregister(&uio_pdrv); +} +module_init(uio_pdrv_init); +module_exit(uio_pdrv_exit); + +MODULE_AUTHOR("Uwe Kleine-Koenig"); +MODULE_DESCRIPTION("Userspace I/O platform driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index 90583d6a5949..507a9bd0d77c 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -1052,7 +1052,6 @@ 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; diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index 7d27c9cf3c43..76fce44c2f9a 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -829,7 +829,6 @@ static int speedtch_bind(struct usbatm_data *usbatm, if (use_isoc) { const struct usb_host_interface *desc = data_intf->cur_altsetting; const __u8 target_address = USB_DIR_IN | usbatm->driver->isoc_in; - int i; use_isoc = 0; /* fall back to bulk if endpoint not found */ diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index c3201affa0b6..0725b1871f23 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -159,12 +159,34 @@ static void acm_write_done(struct acm *acm, struct acm_wb *wb) spin_lock_irqsave(&acm->write_lock, flags); acm->write_ready = 1; wb->use = 0; + acm->transmitting--; spin_unlock_irqrestore(&acm->write_lock, flags); } /* * Poke write. + * + * the caller is responsible for locking */ + +static int acm_start_wb(struct acm *acm, struct acm_wb *wb) +{ + int rc; + + acm->transmitting++; + + wb->urb->transfer_buffer = wb->buf; + wb->urb->transfer_dma = wb->dmah; + wb->urb->transfer_buffer_length = wb->len; + wb->urb->dev = acm->dev; + + if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) { + dbg("usb_submit_urb(write bulk) failed: %d", rc); + acm_write_done(acm, wb); + } + return rc; +} + static int acm_write_start(struct acm *acm, int wbn) { unsigned long flags; @@ -182,26 +204,31 @@ static int acm_write_start(struct acm *acm, int wbn) return 0; /* A white lie */ } + wb = &acm->wb[wbn]; + if(acm_wb_is_avail(acm) <= 1) + acm->write_ready = 0; + + dbg("%s susp_count: %d", __func__, acm->susp_count); + if (acm->susp_count) { + acm->old_ready = acm->write_ready; + acm->delayed_wb = wb; + acm->write_ready = 0; + schedule_work(&acm->waker); + spin_unlock_irqrestore(&acm->write_lock, flags); + return 0; /* A white lie */ + } + usb_mark_last_busy(acm->dev); + if (!acm_wb_is_used(acm, wbn)) { spin_unlock_irqrestore(&acm->write_lock, flags); return 0; } - wb = &acm->wb[wbn]; - if(acm_wb_is_avail(acm) <= 1) - acm->write_ready = 0; + rc = acm_start_wb(acm, wb); spin_unlock_irqrestore(&acm->write_lock, flags); - wb->urb->transfer_buffer = wb->buf; - wb->urb->transfer_dma = wb->dmah; - wb->urb->transfer_buffer_length = wb->len; - wb->urb->dev = acm->dev; - - if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) { - dbg("usb_submit_urb(write bulk) failed: %d", rc); - acm_write_done(acm, wb); - } return rc; + } /* * attributes exported through sysfs @@ -304,6 +331,7 @@ static void acm_ctrl_irq(struct urb *urb) break; } exit: + usb_mark_last_busy(acm->dev); retval = usb_submit_urb (urb, GFP_ATOMIC); if (retval) err ("%s - usb_submit_urb failed with result %d", @@ -320,8 +348,11 @@ static void acm_read_bulk(struct urb *urb) dbg("Entering acm_read_bulk with status %d", status); - if (!ACM_READY(acm)) + if (!ACM_READY(acm)) { + dev_dbg(&acm->data->dev, "Aborting, acm not ready"); return; + } + usb_mark_last_busy(acm->dev); if (status) dev_dbg(&acm->data->dev, "bulk rx status %d\n", status); @@ -331,6 +362,7 @@ static void acm_read_bulk(struct urb *urb) if (likely(status == 0)) { spin_lock(&acm->read_lock); + acm->processing++; list_add_tail(&rcv->list, &acm->spare_read_urbs); list_add_tail(&buf->list, &acm->filled_read_bufs); spin_unlock(&acm->read_lock); @@ -343,7 +375,8 @@ static void acm_read_bulk(struct urb *urb) /* nevertheless the tasklet must be kicked unconditionally so the queue cannot dry up */ } - tasklet_schedule(&acm->urb_task); + if (likely(!acm->susp_count)) + tasklet_schedule(&acm->urb_task); } static void acm_rx_tasklet(unsigned long _acm) @@ -354,16 +387,23 @@ static void acm_rx_tasklet(unsigned long _acm) struct acm_ru *rcv; unsigned long flags; unsigned char throttled; + dbg("Entering acm_rx_tasklet"); if (!ACM_READY(acm)) + { + dbg("acm_rx_tasklet: ACM not ready"); return; + } spin_lock_irqsave(&acm->throttle_lock, flags); throttled = acm->throttle; spin_unlock_irqrestore(&acm->throttle_lock, flags); if (throttled) + { + dbg("acm_rx_tasklet: throttled"); return; + } next_buffer: spin_lock_irqsave(&acm->read_lock, flags); @@ -403,6 +443,7 @@ urbs: while (!list_empty(&acm->spare_read_bufs)) { spin_lock_irqsave(&acm->read_lock, flags); if (list_empty(&acm->spare_read_urbs)) { + acm->processing = 0; spin_unlock_irqrestore(&acm->read_lock, flags); return; } @@ -425,18 +466,23 @@ urbs: rcv->urb->transfer_dma = buf->dma; rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf); - /* This shouldn't kill the driver as unsuccessful URBs are returned to the free-urbs-pool and resubmited ASAP */ - if (usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) { + spin_lock_irqsave(&acm->read_lock, flags); + if (acm->susp_count || usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) { list_add(&buf->list, &acm->spare_read_bufs); - spin_lock_irqsave(&acm->read_lock, flags); list_add(&rcv->list, &acm->spare_read_urbs); + acm->processing = 0; spin_unlock_irqrestore(&acm->read_lock, flags); return; + } else { + spin_unlock_irqrestore(&acm->read_lock, flags); + dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf); } } + spin_lock_irqsave(&acm->read_lock, flags); + acm->processing = 0; + spin_unlock_irqrestore(&acm->read_lock, flags); } /* data interface wrote those outgoing bytes */ @@ -463,6 +509,27 @@ static void acm_softint(struct work_struct *work) tty_wakeup(acm->tty); } +static void acm_waker(struct work_struct *waker) +{ + struct acm *acm = container_of(waker, struct acm, waker); + long flags; + int rv; + + rv = usb_autopm_get_interface(acm->control); + if (rv < 0) { + err("Autopm failure in %s", __func__); + return; + } + if (acm->delayed_wb) { + acm_start_wb(acm, acm->delayed_wb); + acm->delayed_wb = NULL; + } + spin_lock_irqsave(&acm->write_lock, flags); + acm->write_ready = acm->old_ready; + spin_unlock_irqrestore(&acm->write_lock, flags); + usb_autopm_put_interface(acm->control); +} + /* * TTY handlers */ @@ -492,6 +559,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) if (usb_autopm_get_interface(acm->control) < 0) goto early_bail; + else + acm->control->needs_remote_wakeup = 1; mutex_lock(&acm->mutex); if (acm->used++) { @@ -509,6 +578,7 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) && (acm->ctrl_caps & USB_CDC_CAP_LINE)) goto full_bailout; + usb_autopm_put_interface(acm->control); INIT_LIST_HEAD(&acm->spare_read_urbs); INIT_LIST_HEAD(&acm->spare_read_bufs); @@ -570,12 +640,14 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp) mutex_lock(&open_mutex); if (!--acm->used) { if (acm->dev) { + usb_autopm_get_interface(acm->control); acm_set_control(acm, acm->ctrlout = 0); usb_kill_urb(acm->ctrlurb); for (i = 0; i < ACM_NW; i++) usb_kill_urb(acm->wb[i].urb); for (i = 0; i < nr; i++) usb_kill_urb(acm->ru[i].urb); + acm->control->needs_remote_wakeup = 0; usb_autopm_put_interface(acm->control); } else acm_tty_unregister(acm); @@ -660,13 +732,16 @@ static void acm_tty_unthrottle(struct tty_struct *tty) tasklet_schedule(&acm->urb_task); } -static void acm_tty_break_ctl(struct tty_struct *tty, int state) +static int acm_tty_break_ctl(struct tty_struct *tty, int state) { struct acm *acm = tty->driver_data; + int retval; if (!ACM_READY(acm)) - return; - if (acm_send_break(acm, state ? 0xffff : 0)) + return -EINVAL; + retval = acm_send_break(acm, state ? 0xffff : 0); + if (retval < 0) dbg("send break failed"); + return retval; } static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file) @@ -766,7 +841,7 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios * USB probe and disconnect routines. */ -/* Little helper: write buffers free */ +/* Little helpers: write/read buffers free */ static void acm_write_buffers_free(struct acm *acm) { int i; @@ -777,6 +852,15 @@ static void acm_write_buffers_free(struct acm *acm) } } +static void acm_read_buffers_free(struct acm *acm) +{ + struct usb_device *usb_dev = interface_to_usbdev(acm->control); + int i, n = acm->rx_buflimit; + + for (i = 0; i < n; i++) + usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); +} + /* Little helper: write buffers allocate */ static int acm_write_buffers_alloc(struct acm *acm) { @@ -987,6 +1071,7 @@ skip_normal_probe: acm->urb_task.func = acm_rx_tasklet; acm->urb_task.data = (unsigned long) acm; INIT_WORK(&acm->work, acm_softint); + INIT_WORK(&acm->waker, acm_waker); spin_lock_init(&acm->throttle_lock); spin_lock_init(&acm->write_lock); spin_lock_init(&acm->read_lock); @@ -1098,8 +1183,7 @@ alloc_fail8: for (i = 0; i < ACM_NW; i++) usb_free_urb(acm->wb[i].urb); 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); + acm_read_buffers_free(acm); for (i = 0; i < num_rx_buf; i++) usb_free_urb(acm->ru[i].urb); usb_free_urb(acm->ctrlurb); @@ -1116,6 +1200,7 @@ alloc_fail: static void stop_data_traffic(struct acm *acm) { int i; + dbg("Entering stop_data_traffic"); tasklet_disable(&acm->urb_task); @@ -1128,21 +1213,16 @@ static void stop_data_traffic(struct acm *acm) tasklet_enable(&acm->urb_task); cancel_work_sync(&acm->work); + cancel_work_sync(&acm->waker); } static void acm_disconnect(struct usb_interface *intf) { struct acm *acm = usb_get_intfdata(intf); struct usb_device *usb_dev = interface_to_usbdev(intf); - int i; - - if (!acm || !acm->dev) { - dbg("disconnect on nonexisting interface"); - return; - } mutex_lock(&open_mutex); - if (!usb_get_intfdata(intf)) { + if (!acm || !acm->dev) { mutex_unlock(&open_mutex); return; } @@ -1161,10 +1241,10 @@ static void acm_disconnect(struct usb_interface *intf) acm_write_buffers_free(acm); usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); - for (i = 0; i < acm->rx_buflimit; i++) - usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); + acm_read_buffers_free(acm); - usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : intf); + usb_driver_release_interface(&acm_driver, intf == acm->control ? + acm->data : acm->control); if (!acm->used) { acm_tty_unregister(acm); @@ -1178,11 +1258,31 @@ static void acm_disconnect(struct usb_interface *intf) tty_hangup(acm->tty); } +#ifdef CONFIG_PM static int acm_suspend(struct usb_interface *intf, pm_message_t message) { struct acm *acm = usb_get_intfdata(intf); + int cnt; + + if (acm->dev->auto_pm) { + int b; + + spin_lock_irq(&acm->read_lock); + spin_lock(&acm->write_lock); + b = acm->processing + acm->transmitting; + spin_unlock(&acm->write_lock); + spin_unlock_irq(&acm->read_lock); + if (b) + return -EBUSY; + } + + spin_lock_irq(&acm->read_lock); + spin_lock(&acm->write_lock); + cnt = acm->susp_count++; + spin_unlock(&acm->write_lock); + spin_unlock_irq(&acm->read_lock); - if (acm->susp_count++) + if (cnt) return 0; /* we treat opened interfaces differently, @@ -1201,15 +1301,21 @@ static int acm_resume(struct usb_interface *intf) { struct acm *acm = usb_get_intfdata(intf); int rv = 0; + int cnt; + + spin_lock_irq(&acm->read_lock); + acm->susp_count -= 1; + cnt = acm->susp_count; + spin_unlock_irq(&acm->read_lock); - if (--acm->susp_count) + if (cnt) return 0; mutex_lock(&acm->mutex); if (acm->used) { rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO); if (rv < 0) - goto err_out; + goto err_out; tasklet_schedule(&acm->urb_task); } @@ -1218,6 +1324,8 @@ err_out: mutex_unlock(&acm->mutex); return rv; } + +#endif /* CONFIG_PM */ /* * USB driver structure. */ @@ -1273,10 +1381,14 @@ static struct usb_driver acm_driver = { .name = "cdc_acm", .probe = acm_probe, .disconnect = acm_disconnect, +#ifdef CONFIG_PM .suspend = acm_suspend, .resume = acm_resume, +#endif .id_table = acm_ids, +#ifdef CONFIG_PM .supports_autosuspend = 1, +#endif }; /* diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 046e064b033a..85c3aaaab7c5 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -107,10 +107,14 @@ struct acm { struct list_head filled_read_bufs; int write_used; /* number of non-empty write buffers */ int write_ready; /* write urb is not running */ + int old_ready; + int processing; + int transmitting; spinlock_t write_lock; struct mutex mutex; struct usb_cdc_line_coding line; /* bits, stop, parity */ struct work_struct work; /* work queue entry for line discipline waking up */ + struct work_struct waker; struct tasklet_struct urb_task; /* rx processing */ spinlock_t throttle_lock; /* synchronize throtteling and read callback */ unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ @@ -123,6 +127,7 @@ struct acm { unsigned char clocal; /* termios CLOCAL */ unsigned int ctrl_caps; /* control capabilities from the class specific header */ unsigned int susp_count; /* number of suspended interfaces */ + struct acm_wb *delayed_wb; /* write queued for a device about to be woken */ }; #define CDC_DATA_INTERFACE_TYPE 0x0a diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 731db051070a..7e8e1235e4e5 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -28,8 +28,9 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.02" +#define DRIVER_VERSION "v0.03" #define DRIVER_AUTHOR "Oliver Neukum" +#define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management" static struct usb_device_id wdm_ids[] = { { @@ -87,6 +88,7 @@ struct wdm_device { dma_addr_t ihandle; struct mutex wlock; struct mutex rlock; + struct mutex plock; wait_queue_head_t wait; struct work_struct rxwork; int werr; @@ -205,7 +207,7 @@ static void wdm_int_callback(struct urb *urb) req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; req->wValue = 0; req->wIndex = desc->inum; - req->wLength = cpu_to_le16(desc->bMaxPacketSize0); + req->wLength = cpu_to_le16(desc->wMaxCommand); usb_fill_control_urb( desc->response, @@ -214,7 +216,7 @@ static void wdm_int_callback(struct urb *urb) usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0), (unsigned char *)req, desc->inbuf, - desc->bMaxPacketSize0, + desc->wMaxCommand, wdm_in_callback, desc ); @@ -247,6 +249,7 @@ exit: static void kill_urbs(struct wdm_device *desc) { + /* the order here is essential */ usb_kill_urb(desc->command); usb_kill_urb(desc->validity); usb_kill_urb(desc->response); @@ -266,7 +269,7 @@ static void cleanup(struct wdm_device *desc) desc->sbuf, desc->validity->transfer_dma); usb_buffer_free(interface_to_usbdev(desc->intf), - desc->wMaxPacketSize, + desc->wMaxCommand, desc->inbuf, desc->response->transfer_dma); kfree(desc->orq); @@ -299,6 +302,9 @@ static ssize_t wdm_write if (r) goto outnl; + r = usb_autopm_get_interface(desc->intf); + if (r < 0) + goto outnp; r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE, &desc->flags)); if (r < 0) @@ -347,11 +353,14 @@ static ssize_t wdm_write if (rv < 0) { kfree(buf); clear_bit(WDM_IN_USE, &desc->flags); + err("Tx URB error: %d", rv); } else { dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d", req->wIndex); } out: + usb_autopm_put_interface(desc->intf); +outnp: mutex_unlock(&desc->wlock); outnl: return rv < 0 ? rv : count; @@ -376,6 +385,11 @@ retry: rv = wait_event_interruptible(desc->wait, test_bit(WDM_READ, &desc->flags)); + if (test_bit(WDM_DISCONNECTING, &desc->flags)) { + rv = -ENODEV; + goto err; + } + usb_mark_last_busy(interface_to_usbdev(desc->intf)); if (rv < 0) { rv = -ERESTARTSYS; goto err; @@ -418,6 +432,9 @@ retry: desc->ubuf[i] = desc->ubuf[i + cntr]; desc->length -= cntr; + /* in case we had outstanding data */ + if (!desc->length) + clear_bit(WDM_READ, &desc->flags); rv = cntr; err: @@ -480,18 +497,28 @@ static int wdm_open(struct inode *inode, struct file *file) if (test_bit(WDM_DISCONNECTING, &desc->flags)) goto out; - desc->count++; + ; file->private_data = desc; - rv = usb_submit_urb(desc->validity, GFP_KERNEL); - + rv = usb_autopm_get_interface(desc->intf); if (rv < 0) { - desc->count--; - err("Error submitting int urb - %d", rv); + err("Error autopm - %d", rv); goto out; } - rv = 0; + intf->needs_remote_wakeup = 1; + mutex_lock(&desc->plock); + if (!desc->count++) { + rv = usb_submit_urb(desc->validity, GFP_KERNEL); + if (rv < 0) { + desc->count--; + err("Error submitting int urb - %d", rv); + } + } else { + rv = 0; + } + mutex_unlock(&desc->plock); + usb_autopm_put_interface(desc->intf); out: mutex_unlock(&wdm_mutex); return rv; @@ -502,10 +529,15 @@ static int wdm_release(struct inode *inode, struct file *file) struct wdm_device *desc = file->private_data; mutex_lock(&wdm_mutex); + mutex_lock(&desc->plock); desc->count--; + mutex_unlock(&desc->plock); + if (!desc->count) { dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); kill_urbs(desc); + if (!test_bit(WDM_DISCONNECTING, &desc->flags)) + desc->intf->needs_remote_wakeup = 0; } mutex_unlock(&wdm_mutex); return 0; @@ -597,6 +629,7 @@ next_desc: goto out; mutex_init(&desc->wlock); mutex_init(&desc->rlock); + mutex_init(&desc->plock); spin_lock_init(&desc->iuspin); init_waitqueue_head(&desc->wait); desc->wMaxCommand = maxcom; @@ -698,6 +731,7 @@ static void wdm_disconnect(struct usb_interface *intf) spin_lock_irqsave(&desc->iuspin, flags); set_bit(WDM_DISCONNECTING, &desc->flags); set_bit(WDM_READ, &desc->flags); + /* to terminate pending flushes */ clear_bit(WDM_IN_USE, &desc->flags); spin_unlock_irqrestore(&desc->iuspin, flags); cancel_work_sync(&desc->rxwork); @@ -708,11 +742,81 @@ static void wdm_disconnect(struct usb_interface *intf) mutex_unlock(&wdm_mutex); } +static int wdm_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct wdm_device *desc = usb_get_intfdata(intf); + int rv = 0; + + dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); + + mutex_lock(&desc->plock); +#ifdef CONFIG_PM + if (interface_to_usbdev(desc->intf)->auto_pm && test_bit(WDM_IN_USE, &desc->flags)) { + rv = -EBUSY; + } else { +#endif + cancel_work_sync(&desc->rxwork); + kill_urbs(desc); +#ifdef CONFIG_PM + } +#endif + mutex_unlock(&desc->plock); + + return rv; +} + +static int recover_from_urb_loss(struct wdm_device *desc) +{ + int rv = 0; + + if (desc->count) { + rv = usb_submit_urb(desc->validity, GFP_NOIO); + if (rv < 0) + err("Error resume submitting int urb - %d", rv); + } + return rv; +} +static int wdm_resume(struct usb_interface *intf) +{ + struct wdm_device *desc = usb_get_intfdata(intf); + int rv; + + dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor); + mutex_lock(&desc->plock); + rv = recover_from_urb_loss(desc); + mutex_unlock(&desc->plock); + return rv; +} + +static int wdm_pre_reset(struct usb_interface *intf) +{ + struct wdm_device *desc = usb_get_intfdata(intf); + + mutex_lock(&desc->plock); + return 0; +} + +static int wdm_post_reset(struct usb_interface *intf) +{ + struct wdm_device *desc = usb_get_intfdata(intf); + int rv; + + rv = recover_from_urb_loss(desc); + mutex_unlock(&desc->plock); + return 0; +} + static struct usb_driver wdm_driver = { .name = "cdc_wdm", .probe = wdm_probe, .disconnect = wdm_disconnect, + .suspend = wdm_suspend, + .resume = wdm_resume, + .reset_resume = wdm_resume, + .pre_reset = wdm_pre_reset, + .post_reset = wdm_post_reset, .id_table = wdm_ids, + .supports_autosuspend = 1, }; /* --- low level module stuff --- */ @@ -735,6 +839,5 @@ module_init(wdm_init); module_exit(wdm_exit); MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION("USB Abstract Control Model driver for " - "USB WCM Device Management"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 83d9dc379d96..6ec38175a817 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -46,8 +46,6 @@ * 2000-07-05: Ashley Montanaro <ashley@compsoc.man.ac.uk> * Converted file reading routine to dump to buffer once * per device, not per bus - * - * $Id: devices.c,v 1.5 2000/01/11 13:58:21 tom Exp $ */ #include <linux/fs.h> @@ -63,8 +61,6 @@ #include "usb.h" #include "hcd.h" -#define MAX_TOPO_LEVEL 6 - /* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */ #define ALLOW_SERIAL_NUMBER diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 9218cca21043..20290c5b1562 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -19,8 +19,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $ - * * This file implements the usbfs/x/y files, where * x is the bus number and y the device number. * @@ -61,6 +59,22 @@ /* Mutual exclusion for removal, open, and release */ DEFINE_MUTEX(usbfs_mutex); +struct dev_state { + struct list_head list; /* state list */ + struct usb_device *dev; + struct file *file; + spinlock_t lock; /* protects the async urb lists */ + struct list_head async_pending; + struct list_head async_completed; + wait_queue_head_t wait; /* wake up if a request completed */ + unsigned int discsignr; + struct pid *disc_pid; + uid_t disc_uid, disc_euid; + void __user *disccontext; + unsigned long ifclaimed; + u32 secid; +}; + struct async { struct list_head asynclist; struct dev_state *ps; @@ -536,23 +550,19 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, return ret; } -static int __match_minor(struct device *dev, void *data) +static int match_devt(struct device *dev, void *data) { - int minor = *((int *)data); - - if (dev->devt == MKDEV(USB_DEVICE_MAJOR, minor)) - return 1; - return 0; + return dev->devt == (dev_t) (unsigned long) data; } -static struct usb_device *usbdev_lookup_by_minor(int minor) +static struct usb_device *usbdev_lookup_by_devt(dev_t devt) { struct device *dev; - dev = bus_find_device(&usb_bus_type, NULL, &minor, __match_minor); + dev = bus_find_device(&usb_bus_type, NULL, + (void *) (unsigned long) devt, match_devt); if (!dev) return NULL; - put_device(dev); return container_of(dev, struct usb_device, dev); } @@ -575,21 +585,27 @@ static int usbdev_open(struct inode *inode, struct file *file) goto out; ret = -ENOENT; + /* usbdev device-node */ if (imajor(inode) == USB_DEVICE_MAJOR) - dev = usbdev_lookup_by_minor(iminor(inode)); + dev = usbdev_lookup_by_devt(inode->i_rdev); #ifdef CONFIG_USB_DEVICEFS /* procfs file */ - if (!dev) + if (!dev) { dev = inode->i_private; + if (dev && dev->usbfs_dentry && + dev->usbfs_dentry->d_inode == inode) + usb_get_dev(dev); + else + dev = NULL; + } #endif - if (!dev) + if (!dev || dev->state == USB_STATE_NOTATTACHED) goto out; ret = usb_autoresume_device(dev); if (ret) goto out; - usb_get_dev(dev); ret = 0; ps->dev = dev; ps->file = file; @@ -609,8 +625,10 @@ static int usbdev_open(struct inode *inode, struct file *file) list_add_tail(&ps->list, &dev->filelist); file->private_data = ps; out: - if (ret) + if (ret) { kfree(ps); + usb_put_dev(dev); + } mutex_unlock(&usbfs_mutex); unlock_kernel(); return ret; @@ -874,7 +892,7 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg) static int proc_resetdevice(struct dev_state *ps) { - return usb_reset_composite_device(ps->dev, NULL); + return usb_reset_device(ps->dev); } static int proc_setintf(struct dev_state *ps, void __user *arg) @@ -1682,25 +1700,49 @@ const struct file_operations usbdev_file_operations = { .release = usbdev_release, }; +void usb_fs_classdev_common_remove(struct usb_device *udev) +{ + struct dev_state *ps; + struct siginfo sinfo; + + while (!list_empty(&udev->filelist)) { + ps = list_entry(udev->filelist.next, struct dev_state, list); + destroy_all_async(ps); + wake_up_all(&ps->wait); + list_del_init(&ps->list); + if (ps->discsignr) { + sinfo.si_signo = ps->discsignr; + sinfo.si_errno = EPIPE; + sinfo.si_code = SI_ASYNCIO; + sinfo.si_addr = ps->disccontext; + kill_pid_info_as_uid(ps->discsignr, &sinfo, + ps->disc_pid, ps->disc_uid, + ps->disc_euid, ps->secid); + } + } +} + #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->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->usb_classdev)) - return PTR_ERR(dev->usb_classdev); - + struct device *cldev; + + cldev = device_create_drvdata(usb_classdev_class, &dev->dev, + dev->dev.devt, NULL, "usbdev%d.%d", + dev->bus->busnum, dev->devnum); + if (IS_ERR(cldev)) + return PTR_ERR(cldev); + dev->usb_classdev = cldev; return 0; } static void usb_classdev_remove(struct usb_device *dev) { - device_unregister(dev->usb_classdev); + if (dev->usb_classdev) + device_unregister(dev->usb_classdev); + usb_fs_classdev_common_remove(dev); } static int usb_classdev_notify(struct notifier_block *self, @@ -1750,6 +1792,11 @@ int __init usb_devio_init(void) usb_classdev_class = NULL; goto out; } + /* devices of this class shadow the major:minor of their parent + * device, so clear ->dev_kobj to prevent adding duplicate entries + * to /sys/dev + */ + usb_classdev_class->dev_kobj = NULL; usb_register_notify(&usbdev_nb); #endif diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 1e56f1cfa6dc..ddb54e14a5c5 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -201,6 +201,7 @@ static int usb_probe_interface(struct device *dev) intf = to_usb_interface(dev); udev = interface_to_usbdev(intf); + intf->needs_binding = 0; if (udev->authorized == 0) { dev_err(&intf->dev, "Device is not authorized for usage\n"); @@ -257,15 +258,16 @@ static int usb_unbind_interface(struct device *dev) udev = interface_to_usbdev(intf); error = usb_autoresume_device(udev); - /* release all urbs for this interface */ - usb_disable_interface(interface_to_usbdev(intf), intf); + /* Terminate all URBs for this interface unless the driver + * supports "soft" unbinding. + */ + if (!driver->soft_unbind) + usb_disable_interface(udev, intf); driver->disconnect(intf); /* reset other interface state */ - usb_set_interface(interface_to_usbdev(intf), - intf->altsetting[0].desc.bInterfaceNumber, - 0); + usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0); usb_set_intfdata(intf, NULL); intf->condition = USB_INTERFACE_UNBOUND; @@ -310,6 +312,7 @@ int usb_driver_claim_interface(struct usb_driver *driver, dev->driver = &driver->drvwrap.driver; usb_set_intfdata(iface, priv); + iface->needs_binding = 0; usb_pm_lock(udev); iface->condition = USB_INTERFACE_BOUND; @@ -586,7 +589,7 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env) struct usb_device *usb_dev; /* driver is often null here; dev_dbg() would oops */ - pr_debug("usb %s: uevent\n", dev->bus_id); + pr_debug("usb %s: uevent\n", dev_name(dev)); if (is_usb_device(dev)) usb_dev = to_usb_device(dev); @@ -596,11 +599,11 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env) } if (usb_dev->devnum < 0) { - pr_debug("usb %s: already deleted?\n", dev->bus_id); + pr_debug("usb %s: already deleted?\n", dev_name(dev)); return -ENODEV; } if (!usb_dev->bus) { - pr_debug("usb %s: bus removed?\n", dev->bus_id); + pr_debug("usb %s: bus removed?\n", dev_name(dev)); return -ENODEV; } @@ -771,6 +774,104 @@ void usb_deregister(struct usb_driver *driver) } EXPORT_SYMBOL_GPL(usb_deregister); + +/* Forced unbinding of a USB interface driver, either because + * it doesn't support pre_reset/post_reset/reset_resume or + * because it doesn't support suspend/resume. + * + * The caller must hold @intf's device's lock, but not its pm_mutex + * and not @intf->dev.sem. + */ +void usb_forced_unbind_intf(struct usb_interface *intf) +{ + struct usb_driver *driver = to_usb_driver(intf->dev.driver); + + dev_dbg(&intf->dev, "forced unbind\n"); + usb_driver_release_interface(driver, intf); + + /* Mark the interface for later rebinding */ + intf->needs_binding = 1; +} + +/* Delayed forced unbinding of a USB interface driver and scan + * for rebinding. + * + * The caller must hold @intf's device's lock, but not its pm_mutex + * and not @intf->dev.sem. + * + * FIXME: The caller must block system sleep transitions. + */ +void usb_rebind_intf(struct usb_interface *intf) +{ + int rc; + + /* Delayed unbind of an existing driver */ + if (intf->dev.driver) { + struct usb_driver *driver = + to_usb_driver(intf->dev.driver); + + dev_dbg(&intf->dev, "forced unbind\n"); + usb_driver_release_interface(driver, intf); + } + + /* Try to rebind the interface */ + intf->needs_binding = 0; + rc = device_attach(&intf->dev); + if (rc < 0) + dev_warn(&intf->dev, "rebind failed: %d\n", rc); +} + +#define DO_UNBIND 0 +#define DO_REBIND 1 + +/* Unbind drivers for @udev's interfaces that don't support suspend/resume, + * or rebind interfaces that have been unbound, according to @action. + * + * The caller must hold @udev's device lock. + * FIXME: For rebinds, the caller must block system sleep transitions. + */ +static void do_unbind_rebind(struct usb_device *udev, int action) +{ + struct usb_host_config *config; + int i; + struct usb_interface *intf; + struct usb_driver *drv; + + config = udev->actconfig; + if (config) { + for (i = 0; i < config->desc.bNumInterfaces; ++i) { + intf = config->interface[i]; + switch (action) { + case DO_UNBIND: + if (intf->dev.driver) { + drv = to_usb_driver(intf->dev.driver); + if (!drv->suspend || !drv->resume) + usb_forced_unbind_intf(intf); + } + break; + case DO_REBIND: + if (intf->needs_binding) { + + /* FIXME: The next line is needed because we are going to probe + * the interface, but as far as the PM core is concerned the + * interface is still suspended. The problem wouldn't exist + * if we could rebind the interface during the interface's own + * resume() call, but at the time the usb_device isn't locked! + * + * The real solution will be to carry this out during the device's + * complete() callback. Until that is implemented, we have to + * use this hack. + */ +// intf->dev.power.sleeping = 0; + + usb_rebind_intf(intf); + } + break; + } + } + } +} + #ifdef CONFIG_PM /* Caller has locked udev's pm_mutex */ @@ -805,8 +906,6 @@ static int usb_resume_device(struct usb_device *udev) if (udev->state == USB_STATE_NOTATTACHED) goto done; - if (udev->state != USB_STATE_SUSPENDED && !udev->reset_resume) - goto done; /* Can't resume it if it doesn't have a driver. */ if (udev->dev.driver == NULL) { @@ -842,7 +941,7 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg) goto done; driver = to_usb_driver(intf->dev.driver); - if (driver->suspend && driver->resume) { + if (driver->suspend) { status = driver->suspend(intf, msg); if (status == 0) mark_quiesced(intf); @@ -850,12 +949,10 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg) dev_err(&intf->dev, "%s error %d\n", "suspend", status); } else { - /* - * FIXME else if there's no suspend method, disconnect... - * Not possible if auto_pm is set... - */ - dev_warn(&intf->dev, "no suspend for driver %s?\n", - driver->name); + /* Later we will unbind the driver and reprobe */ + intf->needs_binding = 1; + dev_warn(&intf->dev, "no %s for driver %s?\n", + "suspend", driver->name); mark_quiesced(intf); } @@ -879,10 +976,12 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) goto done; /* Can't resume it if it doesn't have a driver. */ - if (intf->condition == USB_INTERFACE_UNBOUND) { - status = -ENOTCONN; + if (intf->condition == USB_INTERFACE_UNBOUND) + goto done; + + /* Don't resume if the interface is marked for rebinding */ + if (intf->needs_binding) goto done; - } driver = to_usb_driver(intf->dev.driver); if (reset_resume) { @@ -892,7 +991,7 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) dev_err(&intf->dev, "%s error %d\n", "reset_resume", status); } else { - /* status = -EOPNOTSUPP; */ + intf->needs_binding = 1; dev_warn(&intf->dev, "no %s for driver %s?\n", "reset_resume", driver->name); } @@ -903,7 +1002,7 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) dev_err(&intf->dev, "%s error %d\n", "resume", status); } else { - /* status = -EOPNOTSUPP; */ + intf->needs_binding = 1; dev_warn(&intf->dev, "no %s for driver %s?\n", "resume", driver->name); } @@ -911,11 +1010,10 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) done: dev_vdbg(&intf->dev, "%s: status %d\n", __func__, status); - if (status == 0) + if (status == 0 && intf->condition == USB_INTERFACE_BOUND) mark_active(intf); - /* FIXME: Unbind the driver and reprobe if the resume failed - * (not possible if auto_pm is set) */ + /* Later we will unbind the driver and/or reprobe, if necessary */ return status; } @@ -1173,11 +1271,8 @@ static int usb_resume_both(struct usb_device *udev) * then we're stuck. */ status = usb_resume_device(udev); } - } else { - - /* Needed for reset-resume */ + } else if (udev->reset_resume) status = usb_resume_device(udev); - } if (status == 0 && udev->actconfig) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { @@ -1474,6 +1569,7 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg) { int status; + do_unbind_rebind(udev, DO_UNBIND); usb_pm_lock(udev); udev->auto_pm = 0; status = usb_suspend_both(udev, msg); @@ -1501,6 +1597,7 @@ int usb_external_resume_device(struct usb_device *udev) status = usb_resume_both(udev); udev->last_busy = jiffies; usb_pm_unlock(udev); + do_unbind_rebind(udev, DO_REBIND); /* Now that the device is awake, we can start trying to autosuspend * it again. */ @@ -1542,14 +1639,11 @@ static int usb_resume(struct device *dev) udev = to_usb_device(dev); /* If udev->skip_sys_resume is set then udev was already suspended - * when the system suspend started, so we don't want to resume - * udev during this system wakeup. However a reset-resume counts - * as a wakeup event, so allow a reset-resume to occur if remote - * wakeup is enabled. */ - if (udev->skip_sys_resume) { - if (!(udev->reset_resume && udev->do_remote_wakeup)) - return -EHOSTUNREACH; - } + * when the system sleep started, so we don't want to resume it + * during this system wakeup. + */ + if (udev->skip_sys_resume) + return 0; return usb_external_resume_device(udev); } diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index fae55a31e26d..22912136fc14 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -296,7 +296,7 @@ int usb_create_ep_files(struct device *parent, retval = endpoint_get_minor(ep_dev); if (retval) { dev_err(parent, "can not allocate minor number for %s\n", - ep_dev->dev.bus_id); + dev_name(&ep_dev->dev)); goto error_register; } @@ -307,7 +307,7 @@ int usb_create_ep_files(struct device *parent, ep_dev->dev.class = ep_class->class; ep_dev->dev.parent = parent; ep_dev->dev.release = ep_device_release; - snprintf(ep_dev->dev.bus_id, BUS_ID_SIZE, "usbdev%d.%d_ep%02x", + dev_set_name(&ep_dev->dev, "usbdev%d.%d_ep%02x", udev->bus->busnum, udev->devnum, endpoint->desc.bEndpointAddress); diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index c6a95395e52a..6b1b229e38cd 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -150,7 +150,7 @@ int usb_register_dev(struct usb_interface *intf, int retval = -EINVAL; int minor_base = class_driver->minor_base; int minor = 0; - char name[BUS_ID_SIZE]; + char name[20]; char *temp; #ifdef CONFIG_USB_DYNAMIC_MINORS @@ -190,14 +190,15 @@ int usb_register_dev(struct usb_interface *intf, intf->minor = minor; /* create a usb class device for this usb interface */ - snprintf(name, BUS_ID_SIZE, class_driver->name, minor - minor_base); + snprintf(name, sizeof(name), class_driver->name, minor - minor_base); temp = strrchr(name, '/'); - if (temp && (temp[1] != 0x00)) + if (temp && (temp[1] != '\0')) ++temp; else temp = name; - intf->usb_dev = device_create(usb_class->class, &intf->dev, - MKDEV(USB_MAJOR, minor), "%s", temp); + intf->usb_dev = device_create_drvdata(usb_class->class, &intf->dev, + MKDEV(USB_MAJOR, minor), NULL, + "%s", temp); if (IS_ERR(intf->usb_dev)) { down_write(&minor_rwsem); usb_minors[intf->minor] = NULL; @@ -227,7 +228,7 @@ void usb_deregister_dev(struct usb_interface *intf, struct usb_class_driver *class_driver) { int minor_base = class_driver->minor_base; - char name[BUS_ID_SIZE]; + char name[20]; #ifdef CONFIG_USB_DYNAMIC_MINORS minor_base = 0; @@ -242,7 +243,7 @@ void usb_deregister_dev(struct usb_interface *intf, usb_minors[intf->minor] = NULL; up_write(&minor_rwsem); - snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base); + snprintf(name, sizeof(name), class_driver->name, intf->minor - minor_base); device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor)); intf->usb_dev = NULL; intf->minor = -1; diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 42a436478b78..f7bfd72ef115 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -900,14 +900,14 @@ static int register_root_hub(struct usb_hcd *hcd) if (retval != sizeof usb_dev->descriptor) { mutex_unlock(&usb_bus_list_lock); dev_dbg (parent_dev, "can't read %s device descriptor %d\n", - usb_dev->dev.bus_id, retval); + dev_name(&usb_dev->dev), retval); return (retval < 0) ? retval : -EMSGSIZE; } retval = usb_new_device (usb_dev); if (retval) { dev_err (parent_dev, "can't register root hub for %s, %d\n", - usb_dev->dev.bus_id, retval); + dev_name(&usb_dev->dev), retval); } mutex_unlock(&usb_bus_list_lock); @@ -1764,7 +1764,7 @@ EXPORT_SYMBOL_GPL (usb_hc_died); * If memory is unavailable, returns NULL. */ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, - struct device *dev, char *bus_name) + struct device *dev, const char *bus_name) { struct usb_hcd *hcd; diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index b9de1569b39e..5b0b59b0d89b 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -21,6 +21,8 @@ #include <linux/rwsem.h> +#define MAX_TOPO_LEVEL 6 + /* This file contains declarations of usbcore internals that are mostly * used or exposed by Host Controller Drivers. */ @@ -235,7 +237,7 @@ extern void usb_hcd_disable_endpoint(struct usb_device *udev, extern int usb_hcd_get_frame_number(struct usb_device *udev); extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, - struct device *dev, char *bus_name); + struct device *dev, const char *bus_name); extern struct usb_hcd *usb_get_hcd(struct usb_hcd *hcd); extern void usb_put_hcd(struct usb_hcd *hcd); extern int usb_add_hcd(struct usb_hcd *hcd, diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 4cfe32a16c37..107e1d25ddec 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -72,7 +72,6 @@ struct usb_hub { unsigned limited_power:1; unsigned quiescing:1; - unsigned activating:1; unsigned disconnected:1; unsigned has_indicators:1; @@ -131,6 +130,12 @@ MODULE_PARM_DESC(use_both_schemes, DECLARE_RWSEM(ehci_cf_port_reset_rwsem); EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem); +#define HUB_DEBOUNCE_TIMEOUT 1500 +#define HUB_DEBOUNCE_STEP 25 +#define HUB_DEBOUNCE_STABLE 100 + + +static int usb_reset_and_verify_device(struct usb_device *udev); static inline char *portspeed(int portstatus) { @@ -535,37 +540,6 @@ static void hub_power_on(struct usb_hub *hub) msleep(max(pgood_delay, (unsigned) 100)); } -static void hub_quiesce(struct usb_hub *hub) -{ - /* (nonblocking) khubd and related activity won't re-trigger */ - hub->quiescing = 1; - hub->activating = 0; - - /* (blocking) stop khubd and related activity */ - usb_kill_urb(hub->urb); - if (hub->has_indicators) - cancel_delayed_work_sync(&hub->leds); - if (hub->tt.hub) - cancel_work_sync(&hub->tt.kevent); -} - -static void hub_activate(struct usb_hub *hub) -{ - int status; - - hub->quiescing = 0; - hub->activating = 1; - - status = usb_submit_urb(hub->urb, GFP_NOIO); - if (status < 0) - dev_err(hub->intfdev, "activate --> %d\n", status); - if (hub->has_indicators && blinkenlights) - schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); - - /* scan all ports ASAP */ - kick_khubd(hub); -} - static int hub_hub_status(struct usb_hub *hub, u16 *status, u16 *change) { @@ -624,136 +598,149 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) kick_khubd(hub); } -/* caller has locked the hub device */ -static void hub_stop(struct usb_hub *hub) +enum hub_activation_type { + HUB_INIT, HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME +}; + +static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) { struct usb_device *hdev = hub->hdev; - int i; + int port1; + int status; + bool need_debounce_delay = false; - /* Disconnect all the children */ - for (i = 0; i < hdev->maxchild; ++i) { - if (hdev->children[i]) - usb_disconnect(&hdev->children[i]); - } - hub_quiesce(hub); -} + /* After a resume, port power should still be on. + * For any other type of activation, turn it on. + */ + if (type != HUB_RESUME) + hub_power_on(hub); -#define HUB_RESET 1 -#define HUB_RESUME 2 -#define HUB_RESET_RESUME 3 + /* Check each port and set hub->change_bits to let khubd know + * which ports need attention. + */ + for (port1 = 1; port1 <= hdev->maxchild; ++port1) { + struct usb_device *udev = hdev->children[port1-1]; + u16 portstatus, portchange; -#ifdef CONFIG_PM + portstatus = portchange = 0; + status = hub_port_status(hub, port1, &portstatus, &portchange); + if (udev || (portstatus & USB_PORT_STAT_CONNECTION)) + dev_dbg(hub->intfdev, + "port %d: status %04x change %04x\n", + port1, portstatus, portchange); -/* Try to identify which devices need USB-PERSIST handling */ -static int persistent_device(struct usb_device *udev) -{ - int i; - int retval; - struct usb_host_config *actconfig; + /* After anything other than HUB_RESUME (i.e., initialization + * or any sort of reset), every port should be disabled. + * Unconnected ports should likewise be disabled (paranoia), + * and so should ports for which we have no usb_device. + */ + if ((portstatus & USB_PORT_STAT_ENABLE) && ( + type != HUB_RESUME || + !(portstatus & USB_PORT_STAT_CONNECTION) || + !udev || + udev->state == USB_STATE_NOTATTACHED)) { + clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); + portstatus &= ~USB_PORT_STAT_ENABLE; + } - /* Explicitly not marked persistent? */ - if (!udev->persist_enabled) - return 0; + /* Clear status-change flags; we'll debounce later */ + if (portchange & USB_PORT_STAT_C_CONNECTION) { + need_debounce_delay = true; + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_CONNECTION); + } + if (portchange & USB_PORT_STAT_C_ENABLE) { + need_debounce_delay = true; + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_ENABLE); + } - /* No active config? */ - actconfig = udev->actconfig; - if (!actconfig) - return 0; + if (!udev || udev->state == USB_STATE_NOTATTACHED) { + /* Tell khubd to disconnect the device or + * check for a new connection + */ + if (udev || (portstatus & USB_PORT_STAT_CONNECTION)) + set_bit(port1, hub->change_bits); + + } else if (portstatus & USB_PORT_STAT_ENABLE) { + /* The power session apparently survived the resume. + * If there was an overcurrent or suspend change + * (i.e., remote wakeup request), have khubd + * take care of it. + */ + if (portchange) + set_bit(port1, hub->change_bits); - /* FIXME! We should check whether it's open here or not! */ + } else if (udev->persist_enabled) { +#ifdef CONFIG_PM + udev->reset_resume = 1; +#endif + set_bit(port1, hub->change_bits); - /* - * Check that all the interface drivers have a - * 'reset_resume' entrypoint + } else { + /* The power session is gone; tell khubd */ + usb_set_device_state(udev, USB_STATE_NOTATTACHED); + set_bit(port1, hub->change_bits); + } + } + + /* If no port-status-change flags were set, we don't need any + * debouncing. If flags were set we can try to debounce the + * ports all at once right now, instead of letting khubd do them + * one at a time later on. + * + * If any port-status changes do occur during this delay, khubd + * will see them later and handle them normally. */ - retval = 0; - for (i = 0; i < actconfig->desc.bNumInterfaces; i++) { - struct usb_interface *intf; - struct usb_driver *driver; + if (need_debounce_delay) + msleep(HUB_DEBOUNCE_STABLE); - intf = actconfig->interface[i]; - if (!intf->dev.driver) - continue; - driver = to_usb_driver(intf->dev.driver); - if (!driver->reset_resume) - return 0; - /* - * We have at least one driver, and that one - * has a reset_resume method. - */ - retval = 1; - } - return retval; -} + hub->quiescing = 0; -static void hub_restart(struct usb_hub *hub, int type) -{ - struct usb_device *hdev = hub->hdev; - int port1; + status = usb_submit_urb(hub->urb, GFP_NOIO); + if (status < 0) + dev_err(hub->intfdev, "activate --> %d\n", status); + if (hub->has_indicators && blinkenlights) + schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); - /* Check each of the children to see if they require - * USB-PERSIST handling or disconnection. Also check - * each unoccupied port to make sure it is still disabled. - */ - for (port1 = 1; port1 <= hdev->maxchild; ++port1) { - struct usb_device *udev = hdev->children[port1-1]; - int status = 0; - u16 portstatus, portchange; + /* Scan all ports that need attention */ + kick_khubd(hub); +} - if (!udev || udev->state == USB_STATE_NOTATTACHED) { - if (type != HUB_RESET) { - status = hub_port_status(hub, port1, - &portstatus, &portchange); - if (status == 0 && (portstatus & - USB_PORT_STAT_ENABLE)) - clear_port_feature(hdev, port1, - USB_PORT_FEAT_ENABLE); - } - continue; - } +enum hub_quiescing_type { + HUB_DISCONNECT, HUB_PRE_RESET, HUB_SUSPEND +}; - /* Was the power session lost while we were suspended? */ - status = hub_port_status(hub, port1, &portstatus, &portchange); +static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) +{ + struct usb_device *hdev = hub->hdev; + int i; - /* If the device is gone, khubd will handle it later */ - if (status == 0 && !(portstatus & USB_PORT_STAT_CONNECTION)) - continue; + /* khubd and related activity won't re-trigger */ + hub->quiescing = 1; - /* For "USB_PERSIST"-enabled children we must - * mark the child device for reset-resume and - * turn off the various status changes to prevent - * khubd from disconnecting it later. - */ - if (status == 0 && !(portstatus & USB_PORT_STAT_ENABLE) && - persistent_device(udev)) { - if (portchange & USB_PORT_STAT_C_ENABLE) - clear_port_feature(hub->hdev, port1, - USB_PORT_FEAT_C_ENABLE); - if (portchange & USB_PORT_STAT_C_CONNECTION) - clear_port_feature(hub->hdev, port1, - USB_PORT_FEAT_C_CONNECTION); - udev->reset_resume = 1; + if (type != HUB_SUSPEND) { + /* Disconnect all the children */ + for (i = 0; i < hdev->maxchild; ++i) { + if (hdev->children[i]) + usb_disconnect(&hdev->children[i]); } - - /* Otherwise for a reset_resume we must disconnect the child, - * but as we may not lock the child device here - * we have to do a "logical" disconnect. - */ - else if (type == HUB_RESET_RESUME) - hub_port_logical_disconnect(hub, port1); } - hub_activate(hub); + /* Stop khubd and related activity */ + usb_kill_urb(hub->urb); + if (hub->has_indicators) + cancel_delayed_work_sync(&hub->leds); + if (hub->tt.hub) + cancel_work_sync(&hub->tt.kevent); } -#endif /* CONFIG_PM */ - /* caller has locked the hub device */ static int hub_pre_reset(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata(intf); - hub_stop(hub); + hub_quiesce(hub, HUB_PRE_RESET); return 0; } @@ -762,8 +749,7 @@ static int hub_post_reset(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata(intf); - hub_power_on(hub); - hub_activate(hub); + hub_activate(hub, HUB_POST_RESET); return 0; } @@ -1009,8 +995,7 @@ static int hub_configure(struct usb_hub *hub, if (hub->has_indicators && blinkenlights) hub->indicator [0] = INDICATOR_CYCLE; - hub_power_on(hub); - hub_activate(hub); + hub_activate(hub, HUB_INIT); return 0; fail: @@ -1042,7 +1027,7 @@ static void hub_disconnect(struct usb_interface *intf) /* Disconnect all children and quiesce the hub */ hub->error = 0; - hub_stop(hub); + hub_quiesce(hub, HUB_DISCONNECT); usb_set_intfdata (intf, NULL); @@ -1068,6 +1053,12 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) desc = intf->cur_altsetting; hdev = interface_to_usbdev(intf); + if (hdev->level == MAX_TOPO_LEVEL) { + dev_err(&intf->dev, "Unsupported bus topology: " + "hub nested too deep\n"); + return -E2BIG; + } + #ifdef CONFIG_USB_OTG_BLACKLIST_HUB if (hdev->parent) { dev_warn(&intf->dev, "ignoring external hub\n"); @@ -1814,6 +1805,51 @@ static int hub_port_reset(struct usb_hub *hub, int port1, #ifdef CONFIG_PM +#define MASK_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION | \ + USB_PORT_STAT_SUSPEND) +#define WANT_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION) + +/* Determine whether the device on a port is ready for a normal resume, + * is ready for a reset-resume, or should be disconnected. + */ +static int check_port_resume_type(struct usb_device *udev, + struct usb_hub *hub, int port1, + int status, unsigned portchange, unsigned portstatus) +{ + /* Is the device still present? */ + if (status || (portstatus & MASK_BITS) != WANT_BITS) { + if (status >= 0) + status = -ENODEV; + } + + /* Can't do a normal resume if the port isn't enabled, + * so try a reset-resume instead. + */ + else if (!(portstatus & USB_PORT_STAT_ENABLE) && !udev->reset_resume) { + if (udev->persist_enabled) + udev->reset_resume = 1; + else + status = -ENODEV; + } + + if (status) { + dev_dbg(hub->intfdev, + "port %d status %04x.%04x after resume, %d\n", + port1, portchange, portstatus, status); + } else if (udev->reset_resume) { + + /* Late port handoff can set status-change bits */ + if (portchange & USB_PORT_STAT_C_CONNECTION) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_CONNECTION); + if (portchange & USB_PORT_STAT_C_ENABLE) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_ENABLE); + } + + return status; +} + #ifdef CONFIG_USB_SUSPEND /* @@ -1943,7 +1979,8 @@ static int finish_port_resume(struct usb_device *udev) * resumed. */ if (udev->reset_resume) - status = usb_reset_device(udev); + retry_reset_resume: + status = usb_reset_and_verify_device(udev); /* 10.5.4.5 says be sure devices in the tree are still there. * For now let's assume the device didn't go crazy on resume, @@ -1954,6 +1991,13 @@ static int finish_port_resume(struct usb_device *udev) status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus); if (status >= 0) status = (status > 0 ? 0 : -ENODEV); + + /* If a normal resume failed, try doing a reset-resume */ + if (status && !udev->reset_resume && udev->persist_enabled) { + dev_dbg(&udev->dev, "retry with reset-resume\n"); + udev->reset_resume = 1; + goto retry_reset_resume; + } } if (status) { @@ -2002,7 +2046,7 @@ static int finish_port_resume(struct usb_device *udev) * to it will be lost. Using the USB_PERSIST facility, the device can be * made to appear as if it had not disconnected. * - * This facility can be dangerous. Although usb_reset_device() makes + * This facility can be dangerous. Although usb_reset_and_verify_device() makes * every effort to insure that the same device is present after the * reset as before, it cannot provide a 100% guarantee. Furthermore it's * quite possible for a device to remain unaltered but its media to be @@ -2018,7 +2062,6 @@ int usb_port_resume(struct usb_device *udev) int port1 = udev->portnum; int status; u16 portchange, portstatus; - unsigned mask_flags, want_flags; /* Skip the initial Clear-Suspend step for a remote wakeup */ status = hub_port_status(hub, port1, &portstatus, &portchange); @@ -2047,35 +2090,23 @@ int usb_port_resume(struct usb_device *udev) */ status = hub_port_status(hub, port1, &portstatus, &portchange); - SuspendCleared: - if (udev->reset_resume) - want_flags = USB_PORT_STAT_POWER - | USB_PORT_STAT_CONNECTION; - else - want_flags = USB_PORT_STAT_POWER - | USB_PORT_STAT_CONNECTION - | USB_PORT_STAT_ENABLE; - mask_flags = want_flags | USB_PORT_STAT_SUSPEND; + /* TRSMRCY = 10 msec */ + msleep(10); + } - if (status < 0 || (portstatus & mask_flags) != want_flags) { - dev_dbg(hub->intfdev, - "port %d status %04x.%04x after resume, %d\n", - port1, portchange, portstatus, status); - if (status >= 0) - status = -ENODEV; - } else { - if (portchange & USB_PORT_STAT_C_SUSPEND) - clear_port_feature(hub->hdev, port1, - USB_PORT_FEAT_C_SUSPEND); - /* TRSMRCY = 10 msec */ - msleep(10); - } + SuspendCleared: + if (status == 0) { + if (portchange & USB_PORT_STAT_C_SUSPEND) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_SUSPEND); } clear_bit(port1, hub->busy_bits); if (!hub->hdev->parent && !hub->busy_bits[0]) usb_enable_root_hub_irq(hub->hdev->bus); + status = check_port_resume_type(udev, + hub, port1, status, portchange, portstatus); if (status == 0) status = finish_port_resume(udev); if (status < 0) { @@ -2085,17 +2116,16 @@ int usb_port_resume(struct usb_device *udev) return status; } +/* caller has locked udev */ static int remote_wakeup(struct usb_device *udev) { int status = 0; - usb_lock_device(udev); if (udev->state == USB_STATE_SUSPENDED) { dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-"); usb_mark_last_busy(udev); status = usb_external_resume_device(udev); } - usb_unlock_device(udev); return status; } @@ -2108,14 +2138,25 @@ int usb_port_suspend(struct usb_device *udev) return 0; } +/* However we may need to do a reset-resume */ + int usb_port_resume(struct usb_device *udev) { - int status = 0; + struct usb_hub *hub = hdev_to_hub(udev->parent); + int port1 = udev->portnum; + int status; + u16 portchange, portstatus; - /* However we may need to do a reset-resume */ - if (udev->reset_resume) { + status = hub_port_status(hub, port1, &portstatus, &portchange); + status = check_port_resume_type(udev, + hub, port1, status, portchange, portstatus); + + if (status) { + dev_dbg(&udev->dev, "can't resume, status %d\n", status); + hub_port_logical_disconnect(hub, port1); + } else if (udev->reset_resume) { dev_dbg(&udev->dev, "reset-resume\n"); - status = usb_reset_device(udev); + status = usb_reset_and_verify_device(udev); } return status; } @@ -2149,7 +2190,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) dev_dbg(&intf->dev, "%s\n", __func__); /* stop khubd and related activity */ - hub_quiesce(hub); + hub_quiesce(hub, HUB_SUSPEND); return 0; } @@ -2158,7 +2199,7 @@ static int hub_resume(struct usb_interface *intf) struct usb_hub *hub = usb_get_intfdata(intf); dev_dbg(&intf->dev, "%s\n", __func__); - hub_restart(hub, HUB_RESUME); + hub_activate(hub, HUB_RESUME); return 0; } @@ -2167,8 +2208,7 @@ static int hub_reset_resume(struct usb_interface *intf) struct usb_hub *hub = usb_get_intfdata(intf); dev_dbg(&intf->dev, "%s\n", __func__); - hub_power_on(hub); - hub_restart(hub, HUB_RESET_RESUME); + hub_activate(hub, HUB_RESET_RESUME); return 0; } @@ -2218,11 +2258,6 @@ static inline int remote_wakeup(struct usb_device *udev) * every 25ms for transient disconnects. When the port status has been * unchanged for 100ms it returns the port status. */ - -#define HUB_DEBOUNCE_TIMEOUT 1500 -#define HUB_DEBOUNCE_STEP 25 -#define HUB_DEBOUNCE_STABLE 100 - static int hub_port_debounce(struct usb_hub *hub, int port1) { int ret; @@ -2302,7 +2337,7 @@ static int hub_set_address(struct usb_device *udev, int devnum) * Returns device in USB_STATE_ADDRESS, except on error. * * If this is called for an already-existing device (as part of - * usb_reset_device), the caller must own the device lock. For a + * usb_reset_and_verify_device), the caller must own the device lock. For a * newly detected device that is not accessible through any global * pointers, it's not necessary to lock the device. */ @@ -2619,7 +2654,7 @@ hub_power_remaining (struct usb_hub *hub) * This routine is called when: * a port connection-change occurs; * a port enable-change occurs (often caused by EMI); - * usb_reset_device() encounters changed descriptors (as from + * usb_reset_and_verify_device() encounters changed descriptors (as from * a firmware download) * caller already locked the hub */ @@ -2629,9 +2664,11 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, struct usb_device *hdev = hub->hdev; struct device *hub_dev = hub->intfdev; struct usb_hcd *hcd = bus_to_hcd(hdev->bus); - u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); + unsigned wHubCharacteristics = + le16_to_cpu(hub->descriptor->wHubCharacteristics); + struct usb_device *udev; int status, i; - + dev_dbg (hub_dev, "port %d, status %04x, change %04x, %s\n", port1, portstatus, portchange, portspeed (portstatus)); @@ -2640,30 +2677,73 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, set_port_led(hub, port1, HUB_LED_AUTO); hub->indicator[port1-1] = INDICATOR_AUTO; } - - /* Disconnect any existing devices under this port */ - if (hdev->children[port1-1]) - usb_disconnect(&hdev->children[port1-1]); - clear_bit(port1, hub->change_bits); #ifdef CONFIG_USB_OTG /* during HNP, don't repeat the debounce */ if (hdev->bus->is_b_host) - portchange &= ~USB_PORT_STAT_C_CONNECTION; + portchange &= ~(USB_PORT_STAT_C_CONNECTION | + USB_PORT_STAT_C_ENABLE); #endif - if (portchange & USB_PORT_STAT_C_CONNECTION) { + /* Try to use the debounce delay for protection against + * port-enable changes caused, for example, by EMI. + */ + if (portchange & (USB_PORT_STAT_C_CONNECTION | + USB_PORT_STAT_C_ENABLE)) { status = hub_port_debounce(hub, port1); if (status < 0) { if (printk_ratelimit()) dev_err (hub_dev, "connect-debounce failed, " "port %d disabled\n", port1); - goto done; + portstatus &= ~USB_PORT_STAT_CONNECTION; + } else { + portstatus = status; + } + } + + /* Try to resuscitate an existing device */ + udev = hdev->children[port1-1]; + if ((portstatus & USB_PORT_STAT_CONNECTION) && udev && + udev->state != USB_STATE_NOTATTACHED) { + + usb_lock_device(udev); + if (portstatus & USB_PORT_STAT_ENABLE) { + status = 0; /* Nothing to do */ + } else if (!udev->persist_enabled) { + status = -ENODEV; /* Mustn't resuscitate */ + +#ifdef CONFIG_USB_SUSPEND + } else if (udev->state == USB_STATE_SUSPENDED) { + /* For a suspended device, treat this as a + * remote wakeup event. + */ + if (udev->do_remote_wakeup) + status = remote_wakeup(udev); + + /* Otherwise leave it be; devices can't tell the + * difference between suspended and disabled. + */ + else + status = 0; +#endif + + } else { + status = usb_reset_device(udev); + } + usb_unlock_device(udev); + + if (status == 0) { + clear_bit(port1, hub->change_bits); + return; } - portstatus = status; } - /* Return now if nothing is connected */ + /* Disconnect any existing devices under this port */ + if (udev) + usb_disconnect(&hdev->children[port1-1]); + clear_bit(port1, hub->change_bits); + + /* Return now if debouncing failed or nothing is connected */ if (!(portstatus & USB_PORT_STAT_CONNECTION)) { /* maybe switch power back on (e.g. root hub was reset) */ @@ -2677,7 +2757,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, } for (i = 0; i < SET_CONFIG_TRIES; i++) { - struct usb_device *udev; /* reallocate for each attempt, since references * to the previous one can escape in various ways @@ -2858,7 +2937,7 @@ static void hub_events(void) /* If the hub has died, clean up after it */ if (hdev->state == USB_STATE_NOTATTACHED) { hub->error = -ENODEV; - hub_stop(hub); + hub_quiesce(hub, HUB_DISCONNECT); goto loop; } @@ -2877,7 +2956,7 @@ static void hub_events(void) dev_dbg (hub_dev, "resetting for error %d\n", hub->error); - ret = usb_reset_composite_device(hdev, intf); + ret = usb_reset_device(hdev); if (ret) { dev_dbg (hub_dev, "error resetting hub: %d\n", ret); @@ -2894,7 +2973,7 @@ static void hub_events(void) continue; connect_change = test_bit(i, hub->change_bits); if (!test_and_clear_bit(i, hub->event_bits) && - !connect_change && !hub->activating) + !connect_change) continue; ret = hub_port_status(hub, i, @@ -2902,11 +2981,6 @@ static void hub_events(void) if (ret < 0) continue; - if (hub->activating && !hdev->children[i-1] && - (portstatus & - USB_PORT_STAT_CONNECTION)) - connect_change = 1; - if (portchange & USB_PORT_STAT_C_CONNECTION) { clear_port_feature(hdev, i, USB_PORT_FEAT_C_CONNECTION); @@ -2941,11 +3015,16 @@ static void hub_events(void) } if (portchange & USB_PORT_STAT_C_SUSPEND) { + struct usb_device *udev; + clear_port_feature(hdev, i, USB_PORT_FEAT_C_SUSPEND); - if (hdev->children[i-1]) { + udev = hdev->children[i-1]; + if (udev) { + usb_lock_device(udev); ret = remote_wakeup(hdev-> children[i-1]); + usb_unlock_device(udev); if (ret < 0) connect_change = 1; } else { @@ -3002,8 +3081,6 @@ static void hub_events(void) } } - hub->activating = 0; - /* If this is a root hub, tell the HCD it's okay to * re-enable port-change interrupts now. */ if (!hdev->parent && !hub->busy_bits[0]) @@ -3172,12 +3249,12 @@ static int descriptors_changed(struct usb_device *udev, } /** - * usb_reset_device - perform a USB port reset to reinitialize a device + * usb_reset_and_verify_device - perform a USB port reset to reinitialize a device * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) * * WARNING - don't use this routine to reset a composite device * (one with multiple interfaces owned by separate drivers)! - * Use usb_reset_composite_device() instead. + * Use usb_reset_device() instead. * * Do a port reset, reassign the device's address, and establish its * former operating configuration. If the reset fails, or the device's @@ -3201,7 +3278,7 @@ static int descriptors_changed(struct usb_device *udev, * holding the device lock because these tasks should always call * usb_autopm_resume_device(), thereby preventing any unwanted autoresume. */ -int usb_reset_device(struct usb_device *udev) +static int usb_reset_and_verify_device(struct usb_device *udev) { struct usb_device *parent_hdev = udev->parent; struct usb_hub *parent_hub; @@ -3289,26 +3366,28 @@ re_enumerate: hub_port_logical_disconnect(parent_hub, port1); return -ENODEV; } -EXPORT_SYMBOL_GPL(usb_reset_device); /** - * usb_reset_composite_device - warn interface drivers and perform a USB port reset + * usb_reset_device - warn interface drivers and perform a USB port reset * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) - * @iface: interface bound to the driver making the request (optional) * * Warns all drivers bound to registered interfaces (using their pre_reset * method), performs the port reset, and then lets the drivers know that * the reset is over (using their post_reset method). * - * Return value is the same as for usb_reset_device(). + * Return value is the same as for usb_reset_and_verify_device(). * * The caller must own the device lock. For example, it's safe to use * this from a driver probe() routine after downloading new firmware. * For calls that might not occur during probe(), drivers should lock * the device using usb_lock_device_for_reset(). + * + * If an interface is currently being probed or disconnected, we assume + * its driver knows how to handle resets. For all other interfaces, + * if the driver doesn't have pre_reset and post_reset methods then + * we attempt to unbind it and rebind afterward. */ -int usb_reset_composite_device(struct usb_device *udev, - struct usb_interface *iface) +int usb_reset_device(struct usb_device *udev) { int ret; int i; @@ -3324,40 +3403,47 @@ int usb_reset_composite_device(struct usb_device *udev, /* Prevent autosuspend during the reset */ usb_autoresume_device(udev); - if (iface && iface->condition != USB_INTERFACE_BINDING) - iface = NULL; - if (config) { for (i = 0; i < config->desc.bNumInterfaces; ++i) { struct usb_interface *cintf = config->interface[i]; struct usb_driver *drv; + int unbind = 0; if (cintf->dev.driver) { drv = to_usb_driver(cintf->dev.driver); - if (drv->pre_reset) - (drv->pre_reset)(cintf); - /* FIXME: Unbind if pre_reset returns an error or isn't defined */ + if (drv->pre_reset && drv->post_reset) + unbind = (drv->pre_reset)(cintf); + else if (cintf->condition == + USB_INTERFACE_BOUND) + unbind = 1; + if (unbind) + usb_forced_unbind_intf(cintf); } } } - ret = usb_reset_device(udev); + ret = usb_reset_and_verify_device(udev); if (config) { for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) { struct usb_interface *cintf = config->interface[i]; struct usb_driver *drv; + int rebind = cintf->needs_binding; - if (cintf->dev.driver) { + if (!rebind && cintf->dev.driver) { drv = to_usb_driver(cintf->dev.driver); if (drv->post_reset) - (drv->post_reset)(cintf); - /* FIXME: Unbind if post_reset returns an error or isn't defined */ + rebind = (drv->post_reset)(cintf); + else if (cintf->condition == + USB_INTERFACE_BOUND) + rebind = 1; } + if (rebind) + usb_rebind_intf(cintf); } } usb_autosuspend_device(udev); return ret; } -EXPORT_SYMBOL_GPL(usb_reset_composite_device); +EXPORT_SYMBOL_GPL(usb_reset_device); diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index 1d253dd4ea81..db410e92c80d 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -712,25 +712,11 @@ static void usbfs_add_device(struct usb_device *dev) static void usbfs_remove_device(struct usb_device *dev) { - struct dev_state *ds; - struct siginfo sinfo; - if (dev->usbfs_dentry) { fs_remove_file (dev->usbfs_dentry); dev->usbfs_dentry = NULL; } - while (!list_empty(&dev->filelist)) { - ds = list_entry(dev->filelist.next, struct dev_state, list); - wake_up_all(&ds->wait); - list_del_init(&ds->list); - if (ds->discsignr) { - sinfo.si_signo = ds->discsignr; - sinfo.si_errno = EPIPE; - sinfo.si_code = SI_ASYNCIO; - sinfo.si_addr = ds->disccontext; - kill_pid_info_as_uid(ds->discsignr, &sinfo, ds->disc_pid, ds->disc_uid, ds->disc_euid, ds->secid); - } - } + usb_fs_classdev_common_remove(dev); } static int usbfs_notify(struct notifier_block *self, unsigned long action, void *dev) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index fe47d145255a..2fcc06eb5e60 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -400,7 +400,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, if (usb_pipein(pipe)) urb_flags |= URB_SHORT_NOT_OK; - for (i = 0; i < io->entries; i++) { + for_each_sg(sg, sg, io->entries, i) { unsigned len; io->urbs[i] = usb_alloc_urb(0, mem_flags); @@ -434,17 +434,17 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, * to prevent stale pointers and to help spot bugs. */ if (dma) { - io->urbs[i]->transfer_dma = sg_dma_address(sg + i); - len = sg_dma_len(sg + i); + io->urbs[i]->transfer_dma = sg_dma_address(sg); + len = sg_dma_len(sg); #if defined(CONFIG_HIGHMEM) || defined(CONFIG_GART_IOMMU) io->urbs[i]->transfer_buffer = NULL; #else - io->urbs[i]->transfer_buffer = sg_virt(&sg[i]); + io->urbs[i]->transfer_buffer = sg_virt(sg); #endif } else { /* hc may use _only_ transfer_buffer */ - io->urbs[i]->transfer_buffer = sg_virt(&sg[i]); - len = sg[i].length; + io->urbs[i]->transfer_buffer = sg_virt(sg); + len = sg->length; } if (length) { @@ -1090,7 +1090,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) if (!device_is_registered(&interface->dev)) continue; dev_dbg(&dev->dev, "unregistering interface %s\n", - interface->dev.bus_id); + dev_name(&interface->dev)); device_del(&interface->dev); usb_remove_sysfs_intf_files(interface); } @@ -1476,7 +1476,7 @@ static struct usb_interface_assoc_descriptor *find_iad(struct usb_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 mutex; usb device driver probe() methods cannot use this routine. + * bus mutex; usb interface 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 @@ -1611,7 +1611,7 @@ free_interfaces: intf->dev.dma_mask = dev->dev.dma_mask; device_initialize(&intf->dev); mark_quiesced(intf); - sprintf(&intf->dev.bus_id[0], "%d-%s:%d.%d", + dev_set_name(&intf->dev, "%d-%s:%d.%d", dev->bus->busnum, dev->devpath, configuration, alt->desc.bInterfaceNumber); } @@ -1631,12 +1631,12 @@ free_interfaces: dev_dbg(&dev->dev, "adding %s (config #%d, interface %d)\n", - intf->dev.bus_id, configuration, + dev_name(&intf->dev), configuration, intf->cur_altsetting->desc.bInterfaceNumber); ret = device_add(&intf->dev); if (ret != 0) { dev_err(&dev->dev, "device_add(%s) --> %d\n", - intf->dev.bus_id, ret); + dev_name(&intf->dev), ret); continue; } usb_create_sysfs_intf_files(intf); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 325774375837..84fcaa6a21ec 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -308,7 +308,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, * by location for diagnostics, tools, driver model, etc. The * string is a path along hub ports, from the root. Each device's * dev->devpath will be stable until USB is re-cabled, and hubs - * are often labeled with these port numbers. The bus_id isn't + * are often labeled with these port numbers. The name isn't * as stable: bus->busnum changes easily from modprobe order, * cardbus or pci hotplugging, and so on. */ @@ -316,7 +316,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, dev->devpath[0] = '0'; dev->dev.parent = bus->controller; - sprintf(&dev->dev.bus_id[0], "usb%d", bus->busnum); + dev_set_name(&dev->dev, "usb%d", bus->busnum); root_hub = 1; } else { /* match any labeling on the hubs; it's one-based */ @@ -328,8 +328,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, "%s.%d", parent->devpath, port1); dev->dev.parent = &parent->dev; - sprintf(&dev->dev.bus_id[0], "%d-%s", - bus->busnum, dev->devpath); + dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath); /* hub driver sets up TT records */ } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 1a8bc21c335e..d9a6e16dbf84 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -29,6 +29,8 @@ extern int usb_choose_configuration(struct usb_device *udev); extern void usb_kick_khubd(struct usb_device *dev); extern int usb_match_device(struct usb_device *dev, const struct usb_device_id *id); +extern void usb_forced_unbind_intf(struct usb_interface *intf); +extern void usb_rebind_intf(struct usb_interface *intf); extern int usb_hub_init(void); extern void usb_hub_cleanup(void); @@ -140,26 +142,11 @@ extern struct usb_driver usbfs_driver; extern const struct file_operations usbfs_devices_fops; extern const struct file_operations usbdev_file_operations; extern void usbfs_conn_disc_event(void); +extern void usb_fs_classdev_common_remove(struct usb_device *udev); extern int usb_devio_init(void); extern void usb_devio_cleanup(void); -struct dev_state { - struct list_head list; /* state list */ - struct usb_device *dev; - struct file *file; - spinlock_t lock; /* protects the async urb lists */ - struct list_head async_pending; - struct list_head async_completed; - wait_queue_head_t wait; /* wake up if a request completed */ - unsigned int discsignr; - struct pid *disc_pid; - uid_t disc_uid, disc_euid; - void __user *disccontext; - unsigned long ifclaimed; - u32 secid; -}; - /* internal notify stuff */ extern void usb_notify_add_device(struct usb_device *udev); extern void usb_notify_remove_device(struct usb_device *udev); diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index d6bab0d5f453..c6a8c6b1116a 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -586,6 +586,20 @@ config USB_G_PRINTER For more information, see Documentation/usb/gadget_printer.txt which includes sample code for accessing the device file. +config USB_CDC_COMPOSITE + tristate "CDC Composite Device (Ethernet and ACM)" + depends on NET + help + This driver provides two functions in one configuration: + a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link. + + This driver requires four bulk and two interrupt endpoints, + plus the ability to handle altsettings. Not all peripheral + controllers are that capable. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module. + # put drivers that need isochronous transfer support (for audio # or video class gadget drivers), or specific hardware, here. diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index e258afd25faf..fcb5cb9094d9 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -22,18 +22,22 @@ obj-$(CONFIG_USB_M66592) += m66592-udc.o # # USB gadget drivers # -g_zero-objs := zero.o usbstring.o config.o epautoconf.o -g_ether-objs := ether.o usbstring.o config.o epautoconf.o -g_serial-objs := serial.o usbstring.o config.o epautoconf.o +C_UTILS = composite.o usbstring.o config.o epautoconf.o + +g_zero-objs := zero.o f_sourcesink.o f_loopback.o $(C_UTILS) +g_ether-objs := ether.o u_ether.o f_subset.o f_ecm.o $(C_UTILS) +g_serial-objs := serial.o u_serial.o f_acm.o f_serial.o $(C_UTILS) g_midi-objs := gmidi.o usbstring.o config.o epautoconf.o gadgetfs-objs := inode.o g_file_storage-objs := file_storage.o usbstring.o config.o \ epautoconf.o g_printer-objs := printer.o usbstring.o config.o \ epautoconf.o +g_cdc-objs := cdc2.o u_ether.o f_ecm.o \ + u_serial.o f_acm.o $(C_UTILS) ifeq ($(CONFIG_USB_ETH_RNDIS),y) - g_ether-objs += rndis.o + g_ether-objs += f_rndis.o rndis.o endif obj-$(CONFIG_USB_ZERO) += g_zero.o @@ -43,4 +47,5 @@ obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o obj-$(CONFIG_USB_G_SERIAL) += g_serial.o obj-$(CONFIG_USB_G_PRINTER) += g_printer.o obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o +obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index f261d2a9a5f0..1500e1b3c302 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -3342,7 +3342,7 @@ static int udc_probe(struct udc *dev) spin_lock_init(&dev->lock); dev->gadget.ops = &udc_ops; - strcpy(dev->gadget.dev.bus_id, "gadget"); + dev_set_name(&dev->gadget.dev, "gadget"); dev->gadget.dev.release = gadget_release; dev->gadget.name = name; dev->gadget.name = name; diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index b6b2a0a5ba37..e2d8a5d86c40 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1687,6 +1687,19 @@ static int __init at91udc_probe(struct platform_device *pdev) udc->board.pullup_active_low); } + /* newer chips have more FIFO memory than rm9200 */ + if (cpu_is_at91sam9260()) { + udc->ep[0].maxpacket = 64; + udc->ep[3].maxpacket = 64; + udc->ep[4].maxpacket = 512; + udc->ep[5].maxpacket = 512; + } else if (cpu_is_at91sam9261()) { + udc->ep[3].maxpacket = 64; + } else if (cpu_is_at91sam9263()) { + udc->ep[0].maxpacket = 64; + udc->ep[3].maxpacket = 64; + } + udc->udp_baseaddr = ioremap(res->start, res->end - res->start + 1); if (!udc->udp_baseaddr) { retval = -ENOMEM; diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/at91_udc.h index a973f2a50fb9..c65d62295890 100644 --- a/drivers/usb/gadget/at91_udc.h +++ b/drivers/usb/gadget/at91_udc.h @@ -171,7 +171,7 @@ struct at91_request { #endif #define ERR(stuff...) pr_err("udc: " stuff) -#define WARN(stuff...) pr_warning("udc: " stuff) +#define WARNING(stuff...) pr_warning("udc: " stuff) #define INFO(stuff...) pr_info("udc: " stuff) #define DBG(stuff...) pr_debug("udc: " stuff) diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c new file mode 100644 index 000000000000..a39a4b940c33 --- /dev/null +++ b/drivers/usb/gadget/cdc2.c @@ -0,0 +1,246 @@ +/* + * cdc2.c -- CDC Composite driver, with ECM and ACM support + * + * Copyright (C) 2008 David Brownell + * Copyright (C) 2008 Nokia 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 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/utsname.h> + +#include "u_ether.h" +#include "u_serial.h" + + +#define DRIVER_DESC "CDC Composite Gadget" +#define DRIVER_VERSION "King Kamehameha Day 2008" + +/*-------------------------------------------------------------------------*/ + +/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. + */ + +/* Thanks to NetChip Technologies for donating this product ID. + * It's for devices with only this composite CDC configuration. + */ +#define CDC_VENDOR_NUM 0x0525 /* NetChip */ +#define CDC_PRODUCT_NUM 0xa4aa /* CDC Composite: ECM + ACM */ + +/*-------------------------------------------------------------------------*/ + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + + .bDeviceClass = USB_CLASS_COMM, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + /* .bMaxPacketSize0 = f(hardware) */ + + /* Vendor and product id can be overridden by module parameters. */ + .idVendor = __constant_cpu_to_le16(CDC_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16(CDC_PRODUCT_NUM), + /* .bcdDevice = f(hardware) */ + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ + /* NO SERIAL NUMBER */ + .bNumConfigurations = 1, +}; + +static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + + /* REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, +}; + +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, + NULL, +}; + + +/* string IDs are assigned dynamically */ + +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 + +static char manufacturer[50]; + +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = manufacturer, + [STRING_PRODUCT_IDX].s = DRIVER_DESC, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + +static u8 hostaddr[ETH_ALEN]; + +/*-------------------------------------------------------------------------*/ + +/* + * We _always_ have both CDC ECM and CDC ACM functions. + */ +static int __init cdc_do_config(struct usb_configuration *c) +{ + int status; + + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + status = ecm_bind_config(c, hostaddr); + if (status < 0) + return status; + + status = acm_bind_config(c, 0); + if (status < 0) + return status; + + return 0; +} + +static struct usb_configuration cdc_config_driver = { + .label = "CDC Composite (ECM + ACM)", + .bind = cdc_do_config, + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 1, /* 2 mA, minimal */ +}; + +/*-------------------------------------------------------------------------*/ + +static int __init cdc_bind(struct usb_composite_dev *cdev) +{ + int gcnum; + struct usb_gadget *gadget = cdev->gadget; + int status; + + if (!can_support_ecm(cdev->gadget)) { + ERROR(cdev, "controller '%s' not usable\n", gadget->name); + return -EINVAL; + } + + /* set up network link layer */ + status = gether_setup(cdev->gadget, hostaddr); + if (status < 0) + return status; + + /* set up serial link layer */ + status = gserial_setup(cdev->gadget, 1); + if (status < 0) + goto fail0; + + gcnum = usb_gadget_controller_number(gadget); + if (gcnum >= 0) + device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); + else { + /* We assume that can_support_ecm() tells the truth; + * but if the controller isn't recognized at all then + * that assumption is a bit more likely to be wrong. + */ + WARNING(cdev, "controller '%s' not recognized; trying %s\n", + gadget->name, + cdc_config_driver.label); + device_desc.bcdDevice = + __constant_cpu_to_le16(0x0300 | 0x0099); + } + + + /* Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. + */ + + /* device descriptor strings: manufacturer, product */ + snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", + init_utsname()->sysname, init_utsname()->release, + gadget->name); + status = usb_string_id(cdev); + if (status < 0) + goto fail1; + strings_dev[STRING_MANUFACTURER_IDX].id = status; + device_desc.iManufacturer = status; + + status = usb_string_id(cdev); + if (status < 0) + goto fail1; + strings_dev[STRING_PRODUCT_IDX].id = status; + device_desc.iProduct = status; + + /* register our configuration */ + status = usb_add_config(cdev, &cdc_config_driver); + if (status < 0) + goto fail1; + + INFO(cdev, "%s, version: " DRIVER_VERSION "\n", DRIVER_DESC); + + return 0; + +fail1: + gserial_cleanup(); +fail0: + gether_cleanup(); + return status; +} + +static int __exit cdc_unbind(struct usb_composite_dev *cdev) +{ + gserial_cleanup(); + gether_cleanup(); + return 0; +} + +static struct usb_composite_driver cdc_driver = { + .name = "g_cdc", + .dev = &device_desc, + .strings = dev_strings, + .bind = cdc_bind, + .unbind = __exit_p(cdc_unbind), +}; + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("David Brownell"); +MODULE_LICENSE("GPL"); + +static int __init init(void) +{ + return usb_composite_register(&cdc_driver); +} +module_init(init); + +static void __exit cleanup(void) +{ + usb_composite_unregister(&cdc_driver); +} +module_exit(cleanup); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c new file mode 100644 index 000000000000..85c876c1f150 --- /dev/null +++ b/drivers/usb/gadget/composite.c @@ -0,0 +1,1041 @@ +/* + * composite.c - infrastructure for Composite USB Gadgets + * + * Copyright (C) 2006-2008 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define VERBOSE_DEBUG */ + +#include <linux/kallsyms.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/device.h> + +#include <linux/usb/composite.h> + + +/* + * The code in this file is utility code, used to build a gadget driver + * from one or more "function" drivers, one or more "configuration" + * objects, and a "usb_composite_driver" by gluing them together along + * with the relevant device-wide data. + */ + +/* big enough to hold our biggest descriptor */ +#define USB_BUFSIZ 512 + +static struct usb_composite_driver *composite; + +/* Some systems will need runtime overrides for the product identifers + * published in the device descriptor, either numbers or strings or both. + * String parameters are in UTF-8 (superset of ASCII's 7 bit characters). + */ + +static ushort idVendor; +module_param(idVendor, ushort, 0); +MODULE_PARM_DESC(idVendor, "USB Vendor ID"); + +static ushort idProduct; +module_param(idProduct, ushort, 0); +MODULE_PARM_DESC(idProduct, "USB Product ID"); + +static ushort bcdDevice; +module_param(bcdDevice, ushort, 0); +MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); + +static char *iManufacturer; +module_param(iManufacturer, charp, 0); +MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); + +static char *iProduct; +module_param(iProduct, charp, 0); +MODULE_PARM_DESC(iProduct, "USB Product string"); + +static char *iSerialNumber; +module_param(iSerialNumber, charp, 0); +MODULE_PARM_DESC(iSerialNumber, "SerialNumber string"); + +/*-------------------------------------------------------------------------*/ + +/** + * usb_add_function() - add a function to a configuration + * @config: the configuration + * @function: the function being added + * Context: single threaded during gadget setup + * + * After initialization, each configuration must have one or more + * functions added to it. Adding a function involves calling its @bind() + * method to allocate resources such as interface and string identifiers + * and endpoints. + * + * This function returns the value of the function's bind(), which is + * zero for success else a negative errno value. + */ +int __init usb_add_function(struct usb_configuration *config, + struct usb_function *function) +{ + int value = -EINVAL; + + DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n", + function->name, function, + config->label, config); + + if (!function->set_alt || !function->disable) + goto done; + + function->config = config; + list_add_tail(&function->list, &config->functions); + + /* REVISIT *require* function->bind? */ + if (function->bind) { + value = function->bind(config, function); + if (value < 0) { + list_del(&function->list); + function->config = NULL; + } + } else + value = 0; + + /* We allow configurations that don't work at both speeds. + * If we run into a lowspeed Linux system, treat it the same + * as full speed ... it's the function drivers that will need + * to avoid bulk and ISO transfers. + */ + if (!config->fullspeed && function->descriptors) + config->fullspeed = true; + if (!config->highspeed && function->hs_descriptors) + config->highspeed = true; + +done: + if (value) + DBG(config->cdev, "adding '%s'/%p --> %d\n", + function->name, function, value); + return value; +} + +/** + * usb_interface_id() - allocate an unused interface ID + * @config: configuration associated with the interface + * @function: function handling the interface + * Context: single threaded during gadget setup + * + * usb_interface_id() is called from usb_function.bind() callbacks to + * allocate new interface IDs. The function driver will then store that + * ID in interface, association, CDC union, and other descriptors. It + * will also handle any control requests targetted at that interface, + * particularly changing its altsetting via set_alt(). There may + * also be class-specific or vendor-specific requests to handle. + * + * All interface identifier should be allocated using this routine, to + * ensure that for example different functions don't wrongly assign + * different meanings to the same identifier. Note that since interface + * identifers are configuration-specific, functions used in more than + * one configuration (or more than once in a given configuration) need + * multiple versions of the relevant descriptors. + * + * Returns the interface ID which was allocated; or -ENODEV if no + * more interface IDs can be allocated. + */ +int __init usb_interface_id(struct usb_configuration *config, + struct usb_function *function) +{ + unsigned id = config->next_interface_id; + + if (id < MAX_CONFIG_INTERFACES) { + config->interface[id] = function; + config->next_interface_id = id + 1; + return id; + } + return -ENODEV; +} + +static int config_buf(struct usb_configuration *config, + enum usb_device_speed speed, void *buf, u8 type) +{ + struct usb_config_descriptor *c = buf; + void *next = buf + USB_DT_CONFIG_SIZE; + int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE; + struct usb_function *f; + int status; + + /* write the config descriptor */ + c = buf; + c->bLength = USB_DT_CONFIG_SIZE; + c->bDescriptorType = type; + /* wTotalLength is written later */ + c->bNumInterfaces = config->next_interface_id; + c->bConfigurationValue = config->bConfigurationValue; + c->iConfiguration = config->iConfiguration; + c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes; + c->bMaxPower = config->bMaxPower; + + /* There may be e.g. OTG descriptors */ + if (config->descriptors) { + status = usb_descriptor_fillbuf(next, len, + config->descriptors); + if (status < 0) + return status; + len -= status; + next += status; + } + + /* add each function's descriptors */ + list_for_each_entry(f, &config->functions, list) { + struct usb_descriptor_header **descriptors; + + if (speed == USB_SPEED_HIGH) + descriptors = f->hs_descriptors; + else + descriptors = f->descriptors; + if (!descriptors) + continue; + status = usb_descriptor_fillbuf(next, len, + (const struct usb_descriptor_header **) descriptors); + if (status < 0) + return status; + len -= status; + next += status; + } + + len = next - buf; + c->wTotalLength = cpu_to_le16(len); + return len; +} + +static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) +{ + struct usb_gadget *gadget = cdev->gadget; + struct usb_configuration *c; + u8 type = w_value >> 8; + enum usb_device_speed speed = USB_SPEED_UNKNOWN; + + if (gadget_is_dualspeed(gadget)) { + int hs = 0; + + if (gadget->speed == USB_SPEED_HIGH) + hs = 1; + if (type == USB_DT_OTHER_SPEED_CONFIG) + hs = !hs; + if (hs) + speed = USB_SPEED_HIGH; + + } + + /* This is a lookup by config *INDEX* */ + w_value &= 0xff; + list_for_each_entry(c, &cdev->configs, list) { + /* ignore configs that won't work at this speed */ + if (speed == USB_SPEED_HIGH) { + if (!c->highspeed) + continue; + } else { + if (!c->fullspeed) + continue; + } + if (w_value == 0) + return config_buf(c, speed, cdev->req->buf, type); + w_value--; + } + return -EINVAL; +} + +static int count_configs(struct usb_composite_dev *cdev, unsigned type) +{ + struct usb_gadget *gadget = cdev->gadget; + struct usb_configuration *c; + unsigned count = 0; + int hs = 0; + + if (gadget_is_dualspeed(gadget)) { + if (gadget->speed == USB_SPEED_HIGH) + hs = 1; + if (type == USB_DT_DEVICE_QUALIFIER) + hs = !hs; + } + list_for_each_entry(c, &cdev->configs, list) { + /* ignore configs that won't work at this speed */ + if (hs) { + if (!c->highspeed) + continue; + } else { + if (!c->fullspeed) + continue; + } + count++; + } + return count; +} + +static void device_qual(struct usb_composite_dev *cdev) +{ + struct usb_qualifier_descriptor *qual = cdev->req->buf; + + qual->bLength = sizeof(*qual); + qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER; + /* POLICY: same bcdUSB and device type info at both speeds */ + qual->bcdUSB = cdev->desc.bcdUSB; + qual->bDeviceClass = cdev->desc.bDeviceClass; + qual->bDeviceSubClass = cdev->desc.bDeviceSubClass; + qual->bDeviceProtocol = cdev->desc.bDeviceProtocol; + /* ASSUME same EP0 fifo size at both speeds */ + qual->bMaxPacketSize0 = cdev->desc.bMaxPacketSize0; + qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER); + qual->bRESERVED = 0; +} + +/*-------------------------------------------------------------------------*/ + +static void reset_config(struct usb_composite_dev *cdev) +{ + struct usb_function *f; + + DBG(cdev, "reset config\n"); + + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->disable) + f->disable(f); + } + cdev->config = NULL; +} + +static int set_config(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl, unsigned number) +{ + struct usb_gadget *gadget = cdev->gadget; + struct usb_configuration *c = NULL; + int result = -EINVAL; + unsigned power = gadget_is_otg(gadget) ? 8 : 100; + int tmp; + + if (cdev->config) + reset_config(cdev); + + if (number) { + list_for_each_entry(c, &cdev->configs, list) { + if (c->bConfigurationValue == number) { + result = 0; + break; + } + } + if (result < 0) + goto done; + } else + result = 0; + + INFO(cdev, "%s speed config #%d: %s\n", + ({ char *speed; + switch (gadget->speed) { + case USB_SPEED_LOW: speed = "low"; break; + case USB_SPEED_FULL: speed = "full"; break; + case USB_SPEED_HIGH: speed = "high"; break; + default: speed = "?"; break; + } ; speed; }), number, c ? c->label : "unconfigured"); + + if (!c) + goto done; + + cdev->config = c; + + /* Initialize all interfaces by setting them to altsetting zero. */ + for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) { + struct usb_function *f = c->interface[tmp]; + + if (!f) + break; + + result = f->set_alt(f, tmp, 0); + if (result < 0) { + DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n", + tmp, f->name, f, result); + + reset_config(cdev); + goto done; + } + } + + /* when we return, be sure our power usage is valid */ + power = 2 * c->bMaxPower; +done: + usb_gadget_vbus_draw(gadget, power); + return result; +} + +/** + * usb_add_config() - add a configuration to a device. + * @cdev: wraps the USB gadget + * @config: the configuration, with bConfigurationValue assigned + * Context: single threaded during gadget setup + * + * One of the main tasks of a composite driver's bind() routine is to + * add each of the configurations it supports, using this routine. + * + * This function returns the value of the configuration's bind(), which + * is zero for success else a negative errno value. Binding configurations + * assigns global resources including string IDs, and per-configuration + * resources such as interface IDs and endpoints. + */ +int __init usb_add_config(struct usb_composite_dev *cdev, + struct usb_configuration *config) +{ + int status = -EINVAL; + struct usb_configuration *c; + + DBG(cdev, "adding config #%u '%s'/%p\n", + config->bConfigurationValue, + config->label, config); + + if (!config->bConfigurationValue || !config->bind) + goto done; + + /* Prevent duplicate configuration identifiers */ + list_for_each_entry(c, &cdev->configs, list) { + if (c->bConfigurationValue == config->bConfigurationValue) { + status = -EBUSY; + goto done; + } + } + + config->cdev = cdev; + list_add_tail(&config->list, &cdev->configs); + + INIT_LIST_HEAD(&config->functions); + config->next_interface_id = 0; + + status = config->bind(config); + if (status < 0) { + list_del(&config->list); + config->cdev = NULL; + } else { + unsigned i; + + DBG(cdev, "cfg %d/%p speeds:%s%s\n", + config->bConfigurationValue, config, + config->highspeed ? " high" : "", + config->fullspeed + ? (gadget_is_dualspeed(cdev->gadget) + ? " full" + : " full/low") + : ""); + + for (i = 0; i < MAX_CONFIG_INTERFACES; i++) { + struct usb_function *f = config->interface[i]; + + if (!f) + continue; + DBG(cdev, " interface %d = %s/%p\n", + i, f->name, f); + } + } + + /* set_alt(), or next config->bind(), sets up + * ep->driver_data as needed. + */ + usb_ep_autoconfig_reset(cdev->gadget); + +done: + if (status) + DBG(cdev, "added config '%s'/%u --> %d\n", config->label, + config->bConfigurationValue, status); + return status; +} + +/*-------------------------------------------------------------------------*/ + +/* We support strings in multiple languages ... string descriptor zero + * says which languages are supported. The typical case will be that + * only one language (probably English) is used, with I18N handled on + * the host side. + */ + +static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf) +{ + const struct usb_gadget_strings *s; + u16 language; + __le16 *tmp; + + while (*sp) { + s = *sp; + language = cpu_to_le16(s->language); + for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) { + if (*tmp == language) + goto repeat; + } + *tmp++ = language; +repeat: + sp++; + } +} + +static int lookup_string( + struct usb_gadget_strings **sp, + void *buf, + u16 language, + int id +) +{ + struct usb_gadget_strings *s; + int value; + + while (*sp) { + s = *sp++; + if (s->language != language) + continue; + value = usb_gadget_get_string(s, id, buf); + if (value > 0) + return value; + } + return -EINVAL; +} + +static int get_string(struct usb_composite_dev *cdev, + void *buf, u16 language, int id) +{ + struct usb_configuration *c; + struct usb_function *f; + int len; + + /* Yes, not only is USB's I18N support probably more than most + * folk will ever care about ... also, it's all supported here. + * (Except for UTF8 support for Unicode's "Astral Planes".) + */ + + /* 0 == report all available language codes */ + if (id == 0) { + struct usb_string_descriptor *s = buf; + struct usb_gadget_strings **sp; + + memset(s, 0, 256); + s->bDescriptorType = USB_DT_STRING; + + sp = composite->strings; + if (sp) + collect_langs(sp, s->wData); + + list_for_each_entry(c, &cdev->configs, list) { + sp = c->strings; + if (sp) + collect_langs(sp, s->wData); + + list_for_each_entry(f, &c->functions, list) { + sp = f->strings; + if (sp) + collect_langs(sp, s->wData); + } + } + + for (len = 0; s->wData[len] && len <= 126; len++) + continue; + if (!len) + return -EINVAL; + + s->bLength = 2 * (len + 1); + return s->bLength; + } + + /* Otherwise, look up and return a specified string. String IDs + * are device-scoped, so we look up each string table we're told + * about. These lookups are infrequent; simpler-is-better here. + */ + if (composite->strings) { + len = lookup_string(composite->strings, buf, language, id); + if (len > 0) + return len; + } + list_for_each_entry(c, &cdev->configs, list) { + if (c->strings) { + len = lookup_string(c->strings, buf, language, id); + if (len > 0) + return len; + } + list_for_each_entry(f, &c->functions, list) { + if (!f->strings) + continue; + len = lookup_string(f->strings, buf, language, id); + if (len > 0) + return len; + } + } + return -EINVAL; +} + +/** + * usb_string_id() - allocate an unused string ID + * @cdev: the device whose string descriptor IDs are being allocated + * Context: single threaded during gadget setup + * + * @usb_string_id() is called from bind() callbacks to allocate + * string IDs. Drivers for functions, configurations, or gadgets will + * then store that ID in the appropriate descriptors and string table. + * + * All string identifier should be allocated using this routine, to + * ensure that for example different functions don't wrongly assign + * different meanings to the same identifier. + */ +int __init usb_string_id(struct usb_composite_dev *cdev) +{ + if (cdev->next_string_id < 254) { + /* string id 0 is reserved */ + cdev->next_string_id++; + return cdev->next_string_id; + } + return -ENODEV; +} + +/*-------------------------------------------------------------------------*/ + +static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req) +{ + if (req->status || req->actual != req->length) + DBG((struct usb_composite_dev *) ep->driver_data, + "setup complete --> %d, %d/%d\n", + req->status, req->actual, req->length); +} + +/* + * The setup() callback implements all the ep0 functionality that's + * not handled lower down, in hardware or the hardware driver(like + * device and endpoint feature flags, and their status). It's all + * housekeeping for the gadget function we're implementing. Most of + * the work is in config and function specific setup. + */ +static int +composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + struct usb_function *f = NULL; + + /* partial re-init of the response message; the function or the + * gadget might need to intercept e.g. a control-OUT completion + * when we delegate to it. + */ + req->zero = 0; + req->complete = composite_setup_complete; + req->length = USB_BUFSIZ; + gadget->ep0->driver_data = cdev; + + switch (ctrl->bRequest) { + + /* we handle all standard USB descriptors */ + case USB_REQ_GET_DESCRIPTOR: + if (ctrl->bRequestType != USB_DIR_IN) + goto unknown; + switch (w_value >> 8) { + + case USB_DT_DEVICE: + cdev->desc.bNumConfigurations = + count_configs(cdev, USB_DT_DEVICE); + value = min(w_length, (u16) sizeof cdev->desc); + memcpy(req->buf, &cdev->desc, value); + break; + case USB_DT_DEVICE_QUALIFIER: + if (!gadget_is_dualspeed(gadget)) + break; + device_qual(cdev); + value = min_t(int, w_length, + sizeof(struct usb_qualifier_descriptor)); + break; + case USB_DT_OTHER_SPEED_CONFIG: + if (!gadget_is_dualspeed(gadget)) + break; + /* FALLTHROUGH */ + case USB_DT_CONFIG: + value = config_desc(cdev, w_value); + if (value >= 0) + value = min(w_length, (u16) value); + break; + case USB_DT_STRING: + value = get_string(cdev, req->buf, + w_index, w_value & 0xff); + if (value >= 0) + value = min(w_length, (u16) value); + break; + } + break; + + /* any number of configs can work */ + case USB_REQ_SET_CONFIGURATION: + if (ctrl->bRequestType != 0) + goto unknown; + if (gadget_is_otg(gadget)) { + if (gadget->a_hnp_support) + DBG(cdev, "HNP available\n"); + else if (gadget->a_alt_hnp_support) + DBG(cdev, "HNP on another port\n"); + else + VDBG(cdev, "HNP inactive\n"); + } + spin_lock(&cdev->lock); + value = set_config(cdev, ctrl, w_value); + spin_unlock(&cdev->lock); + break; + case USB_REQ_GET_CONFIGURATION: + if (ctrl->bRequestType != USB_DIR_IN) + goto unknown; + if (cdev->config) + *(u8 *)req->buf = cdev->config->bConfigurationValue; + else + *(u8 *)req->buf = 0; + value = min(w_length, (u16) 1); + break; + + /* function drivers must handle get/set altsetting; if there's + * no get() method, we know only altsetting zero works. + */ + case USB_REQ_SET_INTERFACE: + if (ctrl->bRequestType != USB_RECIP_INTERFACE) + goto unknown; + if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) + break; + f = cdev->config->interface[w_index]; + if (!f) + break; + if (w_value && !f->get_alt) + break; + value = f->set_alt(f, w_index, w_value); + break; + case USB_REQ_GET_INTERFACE: + if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) + goto unknown; + if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) + break; + f = cdev->config->interface[w_index]; + if (!f) + break; + /* lots of interfaces only need altsetting zero... */ + value = f->get_alt ? f->get_alt(f, w_index) : 0; + if (value < 0) + break; + *((u8 *)req->buf) = value; + value = min(w_length, (u16) 1); + break; + default: +unknown: + VDBG(cdev, + "non-core control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + + /* functions always handle their interfaces ... punt other + * recipients (endpoint, other, WUSB, ...) to the current + * configuration code. + * + * REVISIT it could make sense to let the composite device + * take such requests too, if that's ever needed: to work + * in config 0, etc. + */ + if ((ctrl->bRequestType & USB_RECIP_MASK) + == USB_RECIP_INTERFACE) { + f = cdev->config->interface[w_index]; + if (f && f->setup) + value = f->setup(f, ctrl); + else + f = NULL; + } + if (value < 0 && !f) { + struct usb_configuration *c; + + c = cdev->config; + if (c && c->setup) + value = c->setup(c, ctrl); + } + + goto done; + } + + /* respond with data transfer before status phase? */ + if (value >= 0) { + req->length = value; + req->zero = value < w_length; + value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); + if (value < 0) { + DBG(cdev, "ep_queue --> %d\n", value); + req->status = 0; + composite_setup_complete(gadget->ep0, req); + } + } + +done: + /* device either stalls (value < 0) or reports success */ + return value; +} + +static void composite_disconnect(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + unsigned long flags; + + /* REVISIT: should we have config and device level + * disconnect callbacks? + */ + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config) + reset_config(cdev); + spin_unlock_irqrestore(&cdev->lock, flags); +} + +/*-------------------------------------------------------------------------*/ + +static void /* __init_or_exit */ +composite_unbind(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + + /* composite_disconnect() must already have been called + * by the underlying peripheral controller driver! + * so there's no i/o concurrency that could affect the + * state protected by cdev->lock. + */ + WARN_ON(cdev->config); + + while (!list_empty(&cdev->configs)) { + struct usb_configuration *c; + + c = list_first_entry(&cdev->configs, + struct usb_configuration, list); + while (!list_empty(&c->functions)) { + struct usb_function *f; + + f = list_first_entry(&c->functions, + struct usb_function, list); + list_del(&f->list); + if (f->unbind) { + DBG(cdev, "unbind function '%s'/%p\n", + f->name, f); + f->unbind(c, f); + /* may free memory for "f" */ + } + } + list_del(&c->list); + if (c->unbind) { + DBG(cdev, "unbind config '%s'/%p\n", c->label, c); + c->unbind(c); + /* may free memory for "c" */ + } + } + if (composite->unbind) + composite->unbind(cdev); + + if (cdev->req) { + kfree(cdev->req->buf); + usb_ep_free_request(gadget->ep0, cdev->req); + } + kfree(cdev); + set_gadget_data(gadget, NULL); + composite = NULL; +} + +static void __init +string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s) +{ + struct usb_string *str = tab->strings; + + for (str = tab->strings; str->s; str++) { + if (str->id == id) { + str->s = s; + return; + } + } +} + +static void __init +string_override(struct usb_gadget_strings **tab, u8 id, const char *s) +{ + while (*tab) { + string_override_one(*tab, id, s); + tab++; + } +} + +static int __init composite_bind(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev; + int status = -ENOMEM; + + cdev = kzalloc(sizeof *cdev, GFP_KERNEL); + if (!cdev) + return status; + + spin_lock_init(&cdev->lock); + cdev->gadget = gadget; + set_gadget_data(gadget, cdev); + INIT_LIST_HEAD(&cdev->configs); + + /* preallocate control response and buffer */ + cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); + if (!cdev->req) + goto fail; + cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL); + if (!cdev->req->buf) + goto fail; + cdev->req->complete = composite_setup_complete; + gadget->ep0->driver_data = cdev; + + cdev->bufsiz = USB_BUFSIZ; + cdev->driver = composite; + + usb_gadget_set_selfpowered(gadget); + + /* interface and string IDs start at zero via kzalloc. + * we force endpoints to start unassigned; few controller + * drivers will zero ep->driver_data. + */ + usb_ep_autoconfig_reset(cdev->gadget); + + /* composite gadget needs to assign strings for whole device (like + * serial number), register function drivers, potentially update + * power state and consumption, etc + */ + status = composite->bind(cdev); + if (status < 0) + goto fail; + + cdev->desc = *composite->dev; + cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket; + + /* standardized runtime overrides for device ID data */ + if (idVendor) + cdev->desc.idVendor = cpu_to_le16(idVendor); + if (idProduct) + cdev->desc.idProduct = cpu_to_le16(idProduct); + if (bcdDevice) + cdev->desc.bcdDevice = cpu_to_le16(bcdDevice); + + /* strings can't be assigned before bind() allocates the + * releavnt identifiers + */ + if (cdev->desc.iManufacturer && iManufacturer) + string_override(composite->strings, + cdev->desc.iManufacturer, iManufacturer); + if (cdev->desc.iProduct && iProduct) + string_override(composite->strings, + cdev->desc.iProduct, iProduct); + if (cdev->desc.iSerialNumber && iSerialNumber) + string_override(composite->strings, + cdev->desc.iSerialNumber, iSerialNumber); + + INFO(cdev, "%s ready\n", composite->name); + return 0; + +fail: + composite_unbind(gadget); + return status; +} + +/*-------------------------------------------------------------------------*/ + +static void +composite_suspend(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_function *f; + + /* REVISIT: should we have config and device level + * suspend/resume callbacks? + */ + DBG(cdev, "suspend\n"); + if (cdev->config) { + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->suspend) + f->suspend(f); + } + } +} + +static void +composite_resume(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_function *f; + + /* REVISIT: should we have config and device level + * suspend/resume callbacks? + */ + DBG(cdev, "resume\n"); + if (cdev->config) { + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->resume) + f->resume(f); + } + } +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_gadget_driver composite_driver = { + .speed = USB_SPEED_HIGH, + + .bind = composite_bind, + .unbind = __exit_p(composite_unbind), + + .setup = composite_setup, + .disconnect = composite_disconnect, + + .suspend = composite_suspend, + .resume = composite_resume, + + .driver = { + .owner = THIS_MODULE, + }, +}; + +/** + * usb_composite_register() - register a composite driver + * @driver: the driver to register + * Context: single threaded during gadget setup + * + * This function is used to register drivers using the composite driver + * framework. The return value is zero, or a negative errno value. + * Those values normally come from the driver's @bind method, which does + * all the work of setting up the driver to match the hardware. + * + * On successful return, the gadget is ready to respond to requests from + * the host, unless one of its components invokes usb_gadget_disconnect() + * while it was binding. That would usually be done in order to wait for + * some userspace participation. + */ +int __init usb_composite_register(struct usb_composite_driver *driver) +{ + if (!driver || !driver->dev || !driver->bind || composite) + return -EINVAL; + + if (!driver->name) + driver->name = "composite"; + composite_driver.function = (char *) driver->name; + composite_driver.driver.name = driver->name; + composite = driver; + + return usb_gadget_register_driver(&composite_driver); +} + +/** + * usb_composite_unregister() - unregister a composite driver + * @driver: the driver to unregister + * + * This function is used to unregister drivers using the composite + * driver framework. + */ +void __exit usb_composite_unregister(struct usb_composite_driver *driver) +{ + if (composite != driver) + return; + usb_gadget_unregister_driver(&composite_driver); +} diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index a4e54b2743f0..1ca1c326392a 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -96,7 +96,7 @@ int usb_gadget_config_buf( /* config descriptor first */ if (length < USB_DT_CONFIG_SIZE || !desc) return -EINVAL; - *cp = *config; + *cp = *config; /* then interface/endpoint/class/vendor/... */ len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf, @@ -115,3 +115,77 @@ int usb_gadget_config_buf( return len; } +/** + * usb_copy_descriptors - copy a vector of USB descriptors + * @src: null-terminated vector to copy + * Context: initialization code, which may sleep + * + * This makes a copy of a vector of USB descriptors. Its primary use + * is to support usb_function objects which can have multiple copies, + * each needing different descriptors. Functions may have static + * tables of descriptors, which are used as templates and customized + * with identifiers (for interfaces, strings, endpoints, and more) + * as needed by a given function instance. + */ +struct usb_descriptor_header **__init +usb_copy_descriptors(struct usb_descriptor_header **src) +{ + struct usb_descriptor_header **tmp; + unsigned bytes; + unsigned n_desc; + void *mem; + struct usb_descriptor_header **ret; + + /* count descriptors and their sizes; then add vector size */ + for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++) + bytes += (*tmp)->bLength; + bytes += (n_desc + 1) * sizeof(*tmp); + + mem = kmalloc(bytes, GFP_KERNEL); + if (!mem) + return NULL; + + /* fill in pointers starting at "tmp", + * to descriptors copied starting at "mem"; + * and return "ret" + */ + tmp = mem; + ret = mem; + mem += (n_desc + 1) * sizeof(*tmp); + while (*src) { + memcpy(mem, *src, (*src)->bLength); + *tmp = mem; + tmp++; + mem += (*src)->bLength; + src++; + } + *tmp = NULL; + + return ret; +} + +/** + * usb_find_endpoint - find a copy of an endpoint descriptor + * @src: original vector of descriptors + * @copy: copy of @src + * @ep: endpoint descriptor found in @src + * + * This returns the copy of the @match descriptor made for @copy. Its + * intended use is to help remembering the endpoint descriptor to use + * when enabling a given endpoint. + */ +struct usb_endpoint_descriptor *__init +usb_find_endpoint( + struct usb_descriptor_header **src, + struct usb_descriptor_header **copy, + struct usb_endpoint_descriptor *match +) +{ + while (*src) { + if (*src == (void *) match) + return (void *)*copy; + src++; + copy++; + } + return NULL; +} diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 42036192a03c..21d1406af9ee 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -862,7 +862,7 @@ static int dummy_udc_probe (struct platform_device *pdev) /* maybe claim OTG support, though we won't complete HNP */ dum->gadget.is_otg = (dummy_to_hcd(dum)->self.otg_port != 0); - strcpy (dum->gadget.dev.bus_id, "gadget"); + dev_set_name(&dum->gadget.dev, "gadget"); dum->gadget.dev.parent = &pdev->dev; dum->gadget.dev.release = dummy_gadget_release; rc = device_register (&dum->gadget.dev); @@ -1865,7 +1865,7 @@ static int dummy_hcd_probe(struct platform_device *pdev) dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc); - hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, pdev->dev.bus_id); + hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) return -ENOMEM; the_controller = hcd_to_dummy (hcd); diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 8bdad221fa91..9462e30192d8 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -159,6 +159,7 @@ ep_matches ( /* MATCH!! */ /* report address */ + desc->bEndpointAddress &= USB_DIR_IN; if (isdigit (ep->name [2])) { u8 num = simple_strtol (&ep->name [2], NULL, 10); desc->bEndpointAddress |= num; diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 4ce3950b997f..bcac2e68660d 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -1,8 +1,9 @@ /* * ether.c -- Ethernet gadget driver, with CDC and non-CDC options * - * Copyright (C) 2003-2005 David Brownell + * Copyright (C) 2003-2005,2008 David Brownell * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger + * Copyright (C) 2008 Nokia 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 @@ -23,18 +24,9 @@ #include <linux/kernel.h> #include <linux/utsname.h> -#include <linux/device.h> -#include <linux/ctype.h> -#include <linux/etherdevice.h> -#include <linux/ethtool.h> -#include <linux/usb/ch9.h> -#include <linux/usb/cdc.h> -#include <linux/usb/gadget.h> +#include "u_ether.h" -#include "gadget_chips.h" - -/*-------------------------------------------------------------------------*/ /* * Ethernet gadget driver -- with CDC and non-CDC options @@ -46,7 +38,11 @@ * this USB-IF standard as its open-systems interoperability solution; * most host side USB stacks (except from Microsoft) support it. * - * There's some hardware that can't talk CDC. We make that hardware + * This is sometimes called "CDC ECM" (Ethernet Control Model) to support + * TLA-soup. "CDC ACM" (Abstract Control Model) is for modems, and a new + * "CDC EEM" (Ethernet Emulation Model) is starting to spread. + * + * There's some hardware that can't talk CDC ECM. We make that hardware * implement a "minimalist" vendor-agnostic CDC core: same framing, but * link-level setup only requires activating the configuration. Only the * endpoint descriptors, and product/vendor IDs, are relevant; no control @@ -64,70 +60,40 @@ * A third option is also in use. Rather than CDC Ethernet, or something * simpler, Microsoft pushes their own approach: RNDIS. The published * RNDIS specs are ambiguous and appear to be incomplete, and are also - * needlessly complex. + * needlessly complex. They borrow more from CDC ACM than CDC ECM. */ #define DRIVER_DESC "Ethernet Gadget" -#define DRIVER_VERSION "May Day 2005" - -static const char shortname [] = "ether"; -static const char driver_desc [] = DRIVER_DESC; - -#define RX_EXTRA 20 /* guard against rx overflows */ - -#include "rndis.h" +#define DRIVER_VERSION "Memorial Day 2008" -#ifndef CONFIG_USB_ETH_RNDIS -#define rndis_uninit(x) do{}while(0) -#define rndis_deregister(c) do{}while(0) -#define rndis_exit() do{}while(0) +#ifdef CONFIG_USB_ETH_RNDIS +#define PREFIX "RNDIS/" +#else +#define PREFIX "" #endif -/* CDC and RNDIS support the same host-chosen outgoing packet filters. */ -#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \ - |USB_CDC_PACKET_TYPE_ALL_MULTICAST \ - |USB_CDC_PACKET_TYPE_PROMISCUOUS \ - |USB_CDC_PACKET_TYPE_DIRECTED) - - -/*-------------------------------------------------------------------------*/ - -struct eth_dev { - spinlock_t lock; - struct usb_gadget *gadget; - struct usb_request *req; /* for control responses */ - struct usb_request *stat_req; /* for cdc & rndis status */ - - u8 config; - struct usb_ep *in_ep, *out_ep, *status_ep; - const struct usb_endpoint_descriptor - *in, *out, *status; - - spinlock_t req_lock; - struct list_head tx_reqs, rx_reqs; - - struct net_device *net; - struct net_device_stats stats; - atomic_t tx_qlen; - - struct work_struct work; - unsigned zlp:1; - unsigned cdc:1; - unsigned rndis:1; - unsigned suspended:1; - u16 cdc_filter; - unsigned long todo; -#define WORK_RX_MEMORY 0 - int rndis_config; - u8 host_mac [ETH_ALEN]; -}; - -/* This version autoconfigures as much as possible at run-time. +/* + * This driver aims for interoperability by using CDC ECM unless + * + * can_support_ecm() + * + * returns false, in which case it supports the CDC Subset. By default, + * that returns true; most hardware has no problems with CDC ECM, that's + * a good default. Previous versions of this driver had no default; this + * version changes that, removing overhead for new controller support. * - * It also ASSUMES a self-powered device, without remote wakeup, - * although remote wakeup support would make sense. + * IF YOUR HARDWARE CAN'T SUPPORT CDC ECM, UPDATE THAT ROUTINE! */ +static inline bool has_rndis(void) +{ +#ifdef CONFIG_USB_ETH_RNDIS + return true; +#else + return false; +#endif +} + /*-------------------------------------------------------------------------*/ /* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! @@ -137,8 +103,8 @@ struct eth_dev { /* Thanks to NetChip Technologies for donating this product ID. * It's for devices with only CDC Ethernet configurations. */ -#define CDC_VENDOR_NUM 0x0525 /* NetChip */ -#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ +#define CDC_VENDOR_NUM 0x0525 /* NetChip */ +#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ /* For hardware that can't talk CDC, we use the same vendor ID that * ARM Linux has used for ethernet-over-usb, both with sa1100 and @@ -162,274 +128,9 @@ struct eth_dev { #define RNDIS_VENDOR_NUM 0x0525 /* NetChip */ #define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */ - -/* Some systems will want different product identifers published in the - * device descriptor, either numbers or strings or both. These string - * parameters are in UTF-8 (superset of ASCII's 7 bit characters). - */ - -static ushort idVendor; -module_param(idVendor, ushort, S_IRUGO); -MODULE_PARM_DESC(idVendor, "USB Vendor ID"); - -static ushort idProduct; -module_param(idProduct, ushort, S_IRUGO); -MODULE_PARM_DESC(idProduct, "USB Product ID"); - -static ushort bcdDevice; -module_param(bcdDevice, ushort, S_IRUGO); -MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); - -static char *iManufacturer; -module_param(iManufacturer, charp, S_IRUGO); -MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); - -static char *iProduct; -module_param(iProduct, charp, S_IRUGO); -MODULE_PARM_DESC(iProduct, "USB Product string"); - -static char *iSerialNumber; -module_param(iSerialNumber, charp, S_IRUGO); -MODULE_PARM_DESC(iSerialNumber, "SerialNumber"); - -/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */ -static char *dev_addr; -module_param(dev_addr, charp, S_IRUGO); -MODULE_PARM_DESC(dev_addr, "Device Ethernet Address"); - -/* this address is invisible to ifconfig */ -static char *host_addr; -module_param(host_addr, charp, S_IRUGO); -MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); - - -/*-------------------------------------------------------------------------*/ - -/* Include CDC support if we could run on CDC-capable hardware. */ - -#ifdef CONFIG_USB_GADGET_NET2280 -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_DUMMY_HCD -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_GOKU -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_LH7A40X -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_MQ11XX -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_OMAP -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_N9604 -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_S3C2410 -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_AT91 -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_MUSBHSFC -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_MUSB_HDRC -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_ATMEL_USBA -#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. - */ -#ifdef CONFIG_USB_GADGET_PXA25X -#define DEV_CONFIG_SUBSET -#endif - -#ifdef CONFIG_USB_GADGET_PXA27X -#define DEV_CONFIG_SUBSET -#endif - -#ifdef CONFIG_USB_GADGET_SUPERH -#define DEV_CONFIG_SUBSET -#endif - -#ifdef CONFIG_USB_GADGET_SA1100 -/* use non-CDC for backwards compatibility */ -#define DEV_CONFIG_SUBSET -#endif - -#ifdef CONFIG_USB_GADGET_M66592 -#define DEV_CONFIG_CDC -#endif - -#ifdef CONFIG_USB_GADGET_AMD5536UDC -#define DEV_CONFIG_CDC -#endif - - -/*-------------------------------------------------------------------------*/ - -/* "main" config is either CDC, or its simple subset */ -static inline int is_cdc(struct eth_dev *dev) -{ -#if !defined(DEV_CONFIG_SUBSET) - return 1; /* only cdc possible */ -#elif !defined (DEV_CONFIG_CDC) - return 0; /* only subset possible */ -#else - return dev->cdc; /* depends on what hardware we found */ -#endif -} - -/* "secondary" RNDIS config may sometimes be activated */ -static inline int rndis_active(struct eth_dev *dev) -{ -#ifdef CONFIG_USB_ETH_RNDIS - return dev->rndis; -#else - return 0; -#endif -} - -#define subset_active(dev) (!is_cdc(dev) && !rndis_active(dev)) -#define cdc_active(dev) ( is_cdc(dev) && !rndis_active(dev)) - - - -#define DEFAULT_QLEN 2 /* double buffering by default */ - -/* peak bulk transfer bits-per-second */ -#define HS_BPS (13 * 512 * 8 * 1000 * 8) -#define FS_BPS (19 * 64 * 1 * 1000 * 8) - -#ifdef CONFIG_USB_GADGET_DUALSPEED -#define DEVSPEED USB_SPEED_HIGH - -static unsigned qmult = 5; -module_param (qmult, uint, S_IRUGO|S_IWUSR); - - -/* for dual-speed hardware, use deeper queues at highspeed */ -#define qlen(gadget) \ - (DEFAULT_QLEN*((gadget->speed == USB_SPEED_HIGH) ? qmult : 1)) - -static inline int BITRATE(struct usb_gadget *g) -{ - return (g->speed == USB_SPEED_HIGH) ? HS_BPS : FS_BPS; -} - -#else /* full speed (low speed doesn't do bulk) */ - -#define qmult 1 - -#define DEVSPEED USB_SPEED_FULL - -#define qlen(gadget) DEFAULT_QLEN - -static inline int BITRATE(struct usb_gadget *g) -{ - return FS_BPS; -} -#endif - - -/*-------------------------------------------------------------------------*/ - -#define xprintk(d,level,fmt,args...) \ - printk(level "%s: " fmt , (d)->net->name , ## args) - -#ifdef DEBUG -#undef DEBUG -#define DEBUG(dev,fmt,args...) \ - xprintk(dev , KERN_DEBUG , fmt , ## args) -#else -#define DEBUG(dev,fmt,args...) \ - do { } while (0) -#endif /* DEBUG */ - -#ifdef VERBOSE_DEBUG -#define VDEBUG DEBUG -#else -#define VDEBUG(dev,fmt,args...) \ - do { } while (0) -#endif /* DEBUG */ - -#define ERROR(dev,fmt,args...) \ - xprintk(dev , KERN_ERR , fmt , ## args) -#define WARN(dev,fmt,args...) \ - xprintk(dev , KERN_WARNING , fmt , ## args) -#define INFO(dev,fmt,args...) \ - xprintk(dev , KERN_INFO , fmt , ## args) - /*-------------------------------------------------------------------------*/ -/* USB DRIVER HOOKUP (to the hardware driver, below us), mostly - * ep0 implementation: descriptors, config management, setup(). - * also optional class-specific notification interrupt transfer. - */ - -/* - * DESCRIPTORS ... most are static, but strings and (full) configuration - * descriptors are built on demand. For now we do either full CDC, or - * our simple subset, with RNDIS as an optional second configuration. - * - * RNDIS includes some CDC ACM descriptors ... like CDC Ethernet. But - * the class descriptors match a modem (they're ignored; it's really just - * Ethernet functionality), they don't need the NOP altsetting, and the - * status transfer endpoint isn't optional. - */ - -#define STRING_MANUFACTURER 1 -#define STRING_PRODUCT 2 -#define STRING_ETHADDR 3 -#define STRING_DATA 4 -#define STRING_CONTROL 5 -#define STRING_RNDIS_CONTROL 6 -#define STRING_CDC 7 -#define STRING_SUBSET 8 -#define STRING_RNDIS 9 -#define STRING_SERIALNUMBER 10 - -/* holds our biggest descriptor (or RNDIS response) */ -#define USB_BUFSIZ 256 - -/* - * This device advertises one configuration, eth_config, unless RNDIS - * is enabled (rndis_config) on hardware supporting at least two configs. - * - * NOTE: Controllers like superh_udc should probably be able to use - * an RNDIS-only configuration. - * - * FIXME define some higher-powered configurations to make it easier - * to recharge batteries ... - */ - -#define DEV_CONFIG_VALUE 1 /* cdc or subset */ -#define DEV_RNDIS_CONFIG_VALUE 2 /* rndis; optional */ - -static struct usb_device_descriptor -device_desc = { +static struct usb_device_descriptor device_desc = { .bLength = sizeof device_desc, .bDescriptorType = USB_DT_DEVICE, @@ -438,2220 +139,234 @@ device_desc = { .bDeviceClass = USB_CLASS_COMM, .bDeviceSubClass = 0, .bDeviceProtocol = 0, + /* .bMaxPacketSize0 = f(hardware) */ + /* Vendor and product id defaults change according to what configs + * we support. (As does bNumConfigurations.) These values can + * also be overridden by module parameters. + */ .idVendor = __constant_cpu_to_le16 (CDC_VENDOR_NUM), .idProduct = __constant_cpu_to_le16 (CDC_PRODUCT_NUM), - .iManufacturer = STRING_MANUFACTURER, - .iProduct = STRING_PRODUCT, + /* .bcdDevice = f(hardware) */ + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ + /* NO SERIAL NUMBER */ .bNumConfigurations = 1, }; -static struct usb_otg_descriptor -otg_descriptor = { +static struct usb_otg_descriptor otg_descriptor = { .bLength = sizeof otg_descriptor, .bDescriptorType = USB_DT_OTG, - .bmAttributes = USB_OTG_SRP, -}; - -static struct usb_config_descriptor -eth_config = { - .bLength = sizeof eth_config, - .bDescriptorType = USB_DT_CONFIG, - - /* compute wTotalLength on the fly */ - .bNumInterfaces = 2, - .bConfigurationValue = DEV_CONFIG_VALUE, - .iConfiguration = STRING_CDC, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 50, -}; - -#ifdef CONFIG_USB_ETH_RNDIS -static struct usb_config_descriptor -rndis_config = { - .bLength = sizeof rndis_config, - .bDescriptorType = USB_DT_CONFIG, - - /* compute wTotalLength on the fly */ - .bNumInterfaces = 2, - .bConfigurationValue = DEV_RNDIS_CONFIG_VALUE, - .iConfiguration = STRING_RNDIS, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 50, -}; -#endif - -/* - * Compared to the simple CDC subset, the full CDC Ethernet model adds - * three class descriptors, two interface descriptors, optional status - * endpoint. Both have a "data" interface and two bulk endpoints. - * There are also differences in how control requests are handled. - * - * RNDIS shares a lot with CDC-Ethernet, since it's a variant of the - * CDC-ACM (modem) spec. Unfortunately MSFT's RNDIS driver is buggy; it - * may hang or oops. Since bugfixes (or accurate specs, letting Linux - * work around those bugs) are unlikely to ever come from MSFT, you may - * wish to avoid using RNDIS. - * - * MCCI offers an alternative to RNDIS if you need to connect to Windows - * but have hardware that can't support CDC Ethernet. We add descriptors - * to present the CDC Subset as a (nonconformant) CDC MDLM variant called - * "SAFE". That borrows from both CDC Ethernet and CDC MDLM. You can - * get those drivers from MCCI, or bundled with various products. - */ - -#ifdef DEV_CONFIG_CDC -static struct usb_interface_descriptor -control_intf = { - .bLength = sizeof control_intf, - .bDescriptorType = USB_DT_INTERFACE, - - .bInterfaceNumber = 0, - /* status endpoint is optional; this may be patched later */ - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, - .bInterfaceProtocol = USB_CDC_PROTO_NONE, - .iInterface = STRING_CONTROL, -}; -#endif - -#ifdef CONFIG_USB_ETH_RNDIS -static const struct usb_interface_descriptor -rndis_control_intf = { - .bLength = sizeof rndis_control_intf, - .bDescriptorType = USB_DT_INTERFACE, - - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, - .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR, - .iInterface = STRING_RNDIS_CONTROL, -}; -#endif - -static const struct usb_cdc_header_desc header_desc = { - .bLength = sizeof header_desc, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_HEADER_TYPE, - - .bcdCDC = __constant_cpu_to_le16 (0x0110), -}; - -#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) - -static const struct usb_cdc_union_desc union_desc = { - .bLength = sizeof union_desc, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_UNION_TYPE, - - .bMasterInterface0 = 0, /* index of control interface */ - .bSlaveInterface0 = 1, /* index of DATA interface */ -}; - -#endif /* CDC || RNDIS */ - -#ifdef CONFIG_USB_ETH_RNDIS - -static const struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = { - .bLength = sizeof call_mgmt_descriptor, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, - - .bmCapabilities = 0x00, - .bDataInterface = 0x01, -}; - -static const struct usb_cdc_acm_descriptor acm_descriptor = { - .bLength = sizeof acm_descriptor, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_ACM_TYPE, - - .bmCapabilities = 0x00, -}; - -#endif - -#ifndef DEV_CONFIG_CDC - -/* "SAFE" loosely follows CDC WMC MDLM, violating the spec in various - * ways: data endpoints live in the control interface, there's no data - * interface, and it's not used to talk to a cell phone radio. - */ - -static const struct usb_cdc_mdlm_desc mdlm_desc = { - .bLength = sizeof mdlm_desc, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_MDLM_TYPE, - - .bcdVersion = __constant_cpu_to_le16(0x0100), - .bGUID = { - 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6, - 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f, - }, -}; - -/* since "usb_cdc_mdlm_detail_desc" is a variable length structure, we - * can't really use its struct. All we do here is say that we're using - * the submode of "SAFE" which directly matches the CDC Subset. - */ -static const u8 mdlm_detail_desc[] = { - 6, - USB_DT_CS_INTERFACE, - USB_CDC_MDLM_DETAIL_TYPE, - - 0, /* "SAFE" */ - 0, /* network control capabilities (none) */ - 0, /* network data capabilities ("raw" encapsulation) */ -}; - -#endif - -static const struct usb_cdc_ether_desc ether_desc = { - .bLength = sizeof ether_desc, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, - - /* this descriptor actually adds value, surprise! */ - .iMACAddress = STRING_ETHADDR, - .bmEthernetStatistics = __constant_cpu_to_le32 (0), /* no statistics */ - .wMaxSegmentSize = __constant_cpu_to_le16 (ETH_FRAME_LEN), - .wNumberMCFilters = __constant_cpu_to_le16 (0), - .bNumberPowerFilters = 0, -}; - - -#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) - -/* include the status endpoint if we can, even where it's optional. - * use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one - * packet, to simplify cancellation; and a big transfer interval, to - * waste less bandwidth. - * - * some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even - * if they ignore the connect/disconnect notifications that real aether - * can provide. more advanced cdc configurations might want to support - * encapsulated commands (vendor-specific, using control-OUT). - * - * RNDIS requires the status endpoint, since it uses that encapsulation - * mechanism for its funky RPC scheme. - */ - -#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ -#define STATUS_BYTECOUNT 16 /* 8 byte header + data */ - -static struct usb_endpoint_descriptor -fs_status_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_BYTECOUNT), - .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, -}; -#endif - -#ifdef DEV_CONFIG_CDC - -/* the default data interface has no endpoints ... */ - -static const struct usb_interface_descriptor -data_nop_intf = { - .bLength = sizeof data_nop_intf, - .bDescriptorType = USB_DT_INTERFACE, - - .bInterfaceNumber = 1, - .bAlternateSetting = 0, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_CDC_DATA, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, -}; - -/* ... but the "real" data interface has two bulk endpoints */ - -static const struct usb_interface_descriptor -data_intf = { - .bLength = sizeof data_intf, - .bDescriptorType = USB_DT_INTERFACE, - - .bInterfaceNumber = 1, - .bAlternateSetting = 1, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_CDC_DATA, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = STRING_DATA, -}; - -#endif - -#ifdef CONFIG_USB_ETH_RNDIS - -/* RNDIS doesn't activate by changing to the "real" altsetting */ - -static const struct usb_interface_descriptor -rndis_data_intf = { - .bLength = sizeof rndis_data_intf, - .bDescriptorType = USB_DT_INTERFACE, - - .bInterfaceNumber = 1, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_CDC_DATA, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = STRING_DATA, -}; - -#endif - -#ifdef DEV_CONFIG_SUBSET - -/* - * "Simple" CDC-subset option is a simple vendor-neutral model that most - * full speed controllers can handle: one interface, two bulk endpoints. - * - * To assist host side drivers, we fancy it up a bit, and add descriptors - * so some host side drivers will understand it as a "SAFE" variant. - */ - -static const struct usb_interface_descriptor -subset_data_intf = { - .bLength = sizeof subset_data_intf, - .bDescriptorType = USB_DT_INTERFACE, - - .bInterfaceNumber = 0, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = USB_CDC_SUBCLASS_MDLM, - .bInterfaceProtocol = 0, - .iInterface = STRING_DATA, -}; - -#endif /* SUBSET */ - - -static struct usb_endpoint_descriptor -fs_source_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -static struct usb_endpoint_descriptor -fs_sink_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, }; -static const struct usb_descriptor_header *fs_eth_function [11] = { +static const struct usb_descriptor_header *otg_desc[] = { (struct usb_descriptor_header *) &otg_descriptor, -#ifdef DEV_CONFIG_CDC - /* "cdc" mode descriptors */ - (struct usb_descriptor_header *) &control_intf, - (struct usb_descriptor_header *) &header_desc, - (struct usb_descriptor_header *) &union_desc, - (struct usb_descriptor_header *) ðer_desc, - /* NOTE: status endpoint may need to be removed */ - (struct usb_descriptor_header *) &fs_status_desc, - /* data interface, with altsetting */ - (struct usb_descriptor_header *) &data_nop_intf, - (struct usb_descriptor_header *) &data_intf, - (struct usb_descriptor_header *) &fs_source_desc, - (struct usb_descriptor_header *) &fs_sink_desc, NULL, -#endif /* DEV_CONFIG_CDC */ }; -static inline void __init fs_subset_descriptors(void) -{ -#ifdef DEV_CONFIG_SUBSET - /* behavior is "CDC Subset"; extra descriptors say "SAFE" */ - fs_eth_function[1] = (struct usb_descriptor_header *) &subset_data_intf; - fs_eth_function[2] = (struct usb_descriptor_header *) &header_desc; - fs_eth_function[3] = (struct usb_descriptor_header *) &mdlm_desc; - fs_eth_function[4] = (struct usb_descriptor_header *) &mdlm_detail_desc; - fs_eth_function[5] = (struct usb_descriptor_header *) ðer_desc; - fs_eth_function[6] = (struct usb_descriptor_header *) &fs_source_desc; - fs_eth_function[7] = (struct usb_descriptor_header *) &fs_sink_desc; - fs_eth_function[8] = NULL; -#else - fs_eth_function[1] = NULL; -#endif -} -#ifdef CONFIG_USB_ETH_RNDIS -static const struct usb_descriptor_header *fs_rndis_function [] = { - (struct usb_descriptor_header *) &otg_descriptor, - /* control interface matches ACM, not Ethernet */ - (struct usb_descriptor_header *) &rndis_control_intf, - (struct usb_descriptor_header *) &header_desc, - (struct usb_descriptor_header *) &call_mgmt_descriptor, - (struct usb_descriptor_header *) &acm_descriptor, - (struct usb_descriptor_header *) &union_desc, - (struct usb_descriptor_header *) &fs_status_desc, - /* data interface has no altsetting */ - (struct usb_descriptor_header *) &rndis_data_intf, - (struct usb_descriptor_header *) &fs_source_desc, - (struct usb_descriptor_header *) &fs_sink_desc, - NULL, -}; -#endif - -/* - * usb 2.0 devices need to expose both high speed and full speed - * descriptors, unless they only run at full speed. - */ - -#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) -static struct usb_endpoint_descriptor -hs_status_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_BYTECOUNT), - .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, -}; -#endif /* DEV_CONFIG_CDC */ - -static struct usb_endpoint_descriptor -hs_source_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16 (512), -}; - -static struct usb_endpoint_descriptor -hs_sink_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16 (512), -}; +/* string IDs are assigned dynamically */ -static struct usb_qualifier_descriptor -dev_qualifier = { - .bLength = sizeof dev_qualifier, - .bDescriptorType = USB_DT_DEVICE_QUALIFIER, +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 - .bcdUSB = __constant_cpu_to_le16 (0x0200), - .bDeviceClass = USB_CLASS_COMM, +static char manufacturer[50]; - .bNumConfigurations = 1, +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = manufacturer, + [STRING_PRODUCT_IDX].s = PREFIX DRIVER_DESC, + { } /* end of list */ }; -static const struct usb_descriptor_header *hs_eth_function [11] = { - (struct usb_descriptor_header *) &otg_descriptor, -#ifdef DEV_CONFIG_CDC - /* "cdc" mode descriptors */ - (struct usb_descriptor_header *) &control_intf, - (struct usb_descriptor_header *) &header_desc, - (struct usb_descriptor_header *) &union_desc, - (struct usb_descriptor_header *) ðer_desc, - /* NOTE: status endpoint may need to be removed */ - (struct usb_descriptor_header *) &hs_status_desc, - /* data interface, with altsetting */ - (struct usb_descriptor_header *) &data_nop_intf, - (struct usb_descriptor_header *) &data_intf, - (struct usb_descriptor_header *) &hs_source_desc, - (struct usb_descriptor_header *) &hs_sink_desc, - NULL, -#endif /* DEV_CONFIG_CDC */ +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, }; -static inline void __init hs_subset_descriptors(void) -{ -#ifdef DEV_CONFIG_SUBSET - /* behavior is "CDC Subset"; extra descriptors say "SAFE" */ - hs_eth_function[1] = (struct usb_descriptor_header *) &subset_data_intf; - hs_eth_function[2] = (struct usb_descriptor_header *) &header_desc; - hs_eth_function[3] = (struct usb_descriptor_header *) &mdlm_desc; - hs_eth_function[4] = (struct usb_descriptor_header *) &mdlm_detail_desc; - hs_eth_function[5] = (struct usb_descriptor_header *) ðer_desc; - hs_eth_function[6] = (struct usb_descriptor_header *) &hs_source_desc; - hs_eth_function[7] = (struct usb_descriptor_header *) &hs_sink_desc; - hs_eth_function[8] = NULL; -#else - hs_eth_function[1] = NULL; -#endif -} - -#ifdef CONFIG_USB_ETH_RNDIS -static const struct usb_descriptor_header *hs_rndis_function [] = { - (struct usb_descriptor_header *) &otg_descriptor, - /* control interface matches ACM, not Ethernet */ - (struct usb_descriptor_header *) &rndis_control_intf, - (struct usb_descriptor_header *) &header_desc, - (struct usb_descriptor_header *) &call_mgmt_descriptor, - (struct usb_descriptor_header *) &acm_descriptor, - (struct usb_descriptor_header *) &union_desc, - (struct usb_descriptor_header *) &hs_status_desc, - /* data interface has no altsetting */ - (struct usb_descriptor_header *) &rndis_data_intf, - (struct usb_descriptor_header *) &hs_source_desc, - (struct usb_descriptor_header *) &hs_sink_desc, +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, NULL, }; -#endif - - -/* maxpacket and other transfer characteristics vary by speed. */ -static inline struct usb_endpoint_descriptor * -ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, - struct usb_endpoint_descriptor *fs) -{ - if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) - return hs; - return fs; -} +static u8 hostaddr[ETH_ALEN]; /*-------------------------------------------------------------------------*/ -/* descriptors that are built on-demand */ - -static char manufacturer [50]; -static char product_desc [40] = DRIVER_DESC; -static char serial_number [20]; - -/* address that the host will use ... usually assigned at random */ -static char ethaddr [2 * ETH_ALEN + 1]; - -/* static strings, in UTF-8 */ -static struct usb_string strings [] = { - { STRING_MANUFACTURER, manufacturer, }, - { STRING_PRODUCT, product_desc, }, - { STRING_SERIALNUMBER, serial_number, }, - { STRING_DATA, "Ethernet Data", }, - { STRING_ETHADDR, ethaddr, }, -#ifdef DEV_CONFIG_CDC - { STRING_CDC, "CDC Ethernet", }, - { STRING_CONTROL, "CDC Communications Control", }, -#endif -#ifdef DEV_CONFIG_SUBSET - { STRING_SUBSET, "CDC Ethernet Subset", }, -#endif -#ifdef CONFIG_USB_ETH_RNDIS - { STRING_RNDIS, "RNDIS", }, - { STRING_RNDIS_CONTROL, "RNDIS Communications Control", }, -#endif - { } /* end of list */ -}; - -static struct usb_gadget_strings stringtab = { - .language = 0x0409, /* en-us */ - .strings = strings, -}; - /* - * one config, two interfaces: control, data. - * complications: class descriptors, and an altsetting. - */ -static int -config_buf(struct usb_gadget *g, u8 *buf, u8 type, unsigned index, int is_otg) -{ - int len; - const struct usb_config_descriptor *config; - const struct usb_descriptor_header **function; - int hs = 0; - - if (gadget_is_dualspeed(g)) { - hs = (g->speed == USB_SPEED_HIGH); - if (type == USB_DT_OTHER_SPEED_CONFIG) - hs = !hs; - } -#define which_fn(t) (hs ? hs_ ## t ## _function : fs_ ## t ## _function) - - if (index >= device_desc.bNumConfigurations) - return -EINVAL; - -#ifdef CONFIG_USB_ETH_RNDIS - /* list the RNDIS config first, to make Microsoft's drivers - * happy. DOCSIS 1.0 needs this too. - */ - if (device_desc.bNumConfigurations == 2 && index == 0) { - config = &rndis_config; - function = which_fn (rndis); - } else -#endif - { - config = ð_config; - function = which_fn (eth); - } - - /* for now, don't advertise srp-only devices */ - if (!is_otg) - function++; - - len = usb_gadget_config_buf (config, buf, USB_BUFSIZ, function); - if (len < 0) - return len; - ((struct usb_config_descriptor *) buf)->bDescriptorType = type; - return len; -} - -/*-------------------------------------------------------------------------*/ - -static void eth_start (struct eth_dev *dev, gfp_t gfp_flags); -static int alloc_requests (struct eth_dev *dev, unsigned n, gfp_t gfp_flags); - -static int -set_ether_config (struct eth_dev *dev, gfp_t gfp_flags) -{ - int result = 0; - struct usb_gadget *gadget = dev->gadget; - -#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) - /* status endpoint used for RNDIS and (optionally) CDC */ - if (!subset_active(dev) && dev->status_ep) { - dev->status = ep_desc (gadget, &hs_status_desc, - &fs_status_desc); - dev->status_ep->driver_data = dev; - - result = usb_ep_enable (dev->status_ep, dev->status); - if (result != 0) { - DEBUG (dev, "enable %s --> %d\n", - dev->status_ep->name, result); - goto done; - } - } -#endif - - dev->in = ep_desc(gadget, &hs_source_desc, &fs_source_desc); - dev->in_ep->driver_data = dev; - - dev->out = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc); - dev->out_ep->driver_data = dev; - - /* With CDC, the host isn't allowed to use these two data - * endpoints in the default altsetting for the interface. - * so we don't activate them yet. Reset from SET_INTERFACE. - * - * Strictly speaking RNDIS should work the same: activation is - * a side effect of setting a packet filter. Deactivation is - * from REMOTE_NDIS_HALT_MSG, reset from REMOTE_NDIS_RESET_MSG. - */ - if (!cdc_active(dev)) { - result = usb_ep_enable (dev->in_ep, dev->in); - if (result != 0) { - DEBUG(dev, "enable %s --> %d\n", - dev->in_ep->name, result); - goto done; - } - - result = usb_ep_enable (dev->out_ep, dev->out); - if (result != 0) { - DEBUG (dev, "enable %s --> %d\n", - dev->out_ep->name, result); - goto done; - } - } - -done: - if (result == 0) - result = alloc_requests (dev, qlen (gadget), gfp_flags); - - /* on error, disable any endpoints */ - if (result < 0) { - if (!subset_active(dev) && dev->status_ep) - (void) usb_ep_disable (dev->status_ep); - dev->status = NULL; - (void) usb_ep_disable (dev->in_ep); - (void) usb_ep_disable (dev->out_ep); - dev->in = NULL; - dev->out = NULL; - } - - /* activate non-CDC configs right away - * this isn't strictly according to the RNDIS spec - */ - else if (!cdc_active (dev)) { - netif_carrier_on (dev->net); - if (netif_running (dev->net)) { - spin_unlock (&dev->lock); - eth_start (dev, GFP_ATOMIC); - spin_lock (&dev->lock); - } - } - - if (result == 0) - DEBUG (dev, "qlen %d\n", qlen (gadget)); - - /* caller is responsible for cleanup on error */ - return result; -} - -static void eth_reset_config (struct eth_dev *dev) -{ - struct usb_request *req; - - if (dev->config == 0) - return; - - DEBUG (dev, "%s\n", __func__); - - netif_stop_queue (dev->net); - netif_carrier_off (dev->net); - rndis_uninit(dev->rndis_config); - - /* disable endpoints, forcing (synchronous) completion of - * pending i/o. then free the requests. - */ - if (dev->in) { - usb_ep_disable (dev->in_ep); - spin_lock(&dev->req_lock); - while (likely (!list_empty (&dev->tx_reqs))) { - req = container_of (dev->tx_reqs.next, - struct usb_request, list); - list_del (&req->list); - - spin_unlock(&dev->req_lock); - usb_ep_free_request (dev->in_ep, req); - spin_lock(&dev->req_lock); - } - spin_unlock(&dev->req_lock); - } - if (dev->out) { - usb_ep_disable (dev->out_ep); - spin_lock(&dev->req_lock); - while (likely (!list_empty (&dev->rx_reqs))) { - req = container_of (dev->rx_reqs.next, - struct usb_request, list); - list_del (&req->list); - - spin_unlock(&dev->req_lock); - usb_ep_free_request (dev->out_ep, req); - spin_lock(&dev->req_lock); - } - spin_unlock(&dev->req_lock); - } - - if (dev->status) { - usb_ep_disable (dev->status_ep); - } - dev->rndis = 0; - dev->cdc_filter = 0; - dev->config = 0; -} - -/* change our operational config. must agree with the code - * that returns config descriptors, and altsetting code. + * We may not have an RNDIS configuration, but if we do it needs to be + * the first one present. That's to make Microsoft's drivers happy, + * and to follow DOCSIS 1.0 (cable modem standard). */ -static int -eth_set_config (struct eth_dev *dev, unsigned number, gfp_t gfp_flags) +static int __init rndis_do_config(struct usb_configuration *c) { - int result = 0; - struct usb_gadget *gadget = dev->gadget; - - if (gadget_is_sa1100 (gadget) - && dev->config - && atomic_read (&dev->tx_qlen) != 0) { - /* tx fifo is full, but we can't clear it...*/ - INFO (dev, "can't change configurations\n"); - return -ESPIPE; - } - eth_reset_config (dev); - - switch (number) { - case DEV_CONFIG_VALUE: - result = set_ether_config (dev, gfp_flags); - break; -#ifdef CONFIG_USB_ETH_RNDIS - case DEV_RNDIS_CONFIG_VALUE: - dev->rndis = 1; - result = set_ether_config (dev, gfp_flags); - break; -#endif - default: - result = -EINVAL; - /* FALL THROUGH */ - case 0: - break; - } - - if (result) { - if (number) - eth_reset_config (dev); - usb_gadget_vbus_draw(dev->gadget, - gadget_is_otg(dev->gadget) ? 8 : 100); - } else { - char *speed; - unsigned power; - - power = 2 * eth_config.bMaxPower; - usb_gadget_vbus_draw(dev->gadget, power); + /* FIXME alloc iConfiguration string, set it in c->strings */ - switch (gadget->speed) { - case USB_SPEED_FULL: speed = "full"; break; -#ifdef CONFIG_USB_GADGET_DUALSPEED - case USB_SPEED_HIGH: speed = "high"; break; -#endif - default: speed = "?"; break; - } - - dev->config = number; - INFO (dev, "%s speed config #%d: %d mA, %s, using %s\n", - speed, number, power, driver_desc, - rndis_active(dev) - ? "RNDIS" - : (cdc_active(dev) - ? "CDC Ethernet" - : "CDC Ethernet Subset")); + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - return result; -} - -/*-------------------------------------------------------------------------*/ - -#ifdef DEV_CONFIG_CDC -/* The interrupt endpoint is used in CDC networking models (Ethernet, ATM) - * only to notify the host about link status changes (which we support) or - * report completion of some encapsulated command (as used in RNDIS). Since - * we want this CDC Ethernet code to be vendor-neutral, we don't use that - * command mechanism; and only one status request is ever queued. - */ - -static void eth_status_complete (struct usb_ep *ep, struct usb_request *req) -{ - struct usb_cdc_notification *event = req->buf; - int value = req->status; - struct eth_dev *dev = ep->driver_data; - - /* issue the second notification if host reads the first */ - if (event->bNotificationType == USB_CDC_NOTIFY_NETWORK_CONNECTION - && value == 0) { - __le32 *data = req->buf + sizeof *event; - - event->bmRequestType = 0xA1; - event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE; - event->wValue = __constant_cpu_to_le16 (0); - event->wIndex = __constant_cpu_to_le16 (1); - event->wLength = __constant_cpu_to_le16 (8); - - /* SPEED_CHANGE data is up/down speeds in bits/sec */ - data [0] = data [1] = cpu_to_le32 (BITRATE (dev->gadget)); - - req->length = STATUS_BYTECOUNT; - value = usb_ep_queue (ep, req, GFP_ATOMIC); - DEBUG (dev, "send SPEED_CHANGE --> %d\n", value); - if (value == 0) - return; - } else if (value != -ECONNRESET) - DEBUG (dev, "event %02x --> %d\n", - event->bNotificationType, value); - req->context = NULL; + return rndis_bind_config(c, hostaddr); } -static void issue_start_status (struct eth_dev *dev) -{ - struct usb_request *req = dev->stat_req; - struct usb_cdc_notification *event; - int value; - - DEBUG (dev, "%s, flush old status first\n", __func__); - - /* flush old status - * - * FIXME ugly idiom, maybe we'd be better with just - * a "cancel the whole queue" primitive since any - * unlink-one primitive has way too many error modes. - * here, we "know" toggle is already clear... - * - * FIXME iff req->context != null just dequeue it - */ - usb_ep_disable (dev->status_ep); - usb_ep_enable (dev->status_ep, dev->status); - - /* 3.8.1 says to issue first NETWORK_CONNECTION, then - * a SPEED_CHANGE. could be useful in some configs. - */ - event = req->buf; - event->bmRequestType = 0xA1; - event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION; - event->wValue = __constant_cpu_to_le16 (1); /* connected */ - event->wIndex = __constant_cpu_to_le16 (1); - event->wLength = 0; - - req->length = sizeof *event; - req->complete = eth_status_complete; - req->context = dev; - - value = usb_ep_queue (dev->status_ep, req, GFP_ATOMIC); - if (value < 0) - DEBUG (dev, "status buf queue --> %d\n", value); -} - -#endif +static struct usb_configuration rndis_config_driver = { + .label = "RNDIS", + .bind = rndis_do_config, + .bConfigurationValue = 2, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 1, /* 2 mA, minimal */ +}; /*-------------------------------------------------------------------------*/ -static void eth_setup_complete (struct usb_ep *ep, struct usb_request *req) -{ - if (req->status || req->actual != req->length) - DEBUG ((struct eth_dev *) ep->driver_data, - "setup complete --> %d, %d/%d\n", - req->status, req->actual, req->length); -} - -#ifdef CONFIG_USB_ETH_RNDIS - -static void rndis_response_complete (struct usb_ep *ep, struct usb_request *req) -{ - if (req->status || req->actual != req->length) - DEBUG ((struct eth_dev *) ep->driver_data, - "rndis response complete --> %d, %d/%d\n", - req->status, req->actual, req->length); - - /* done sending after USB_CDC_GET_ENCAPSULATED_RESPONSE */ -} - -static void rndis_command_complete (struct usb_ep *ep, struct usb_request *req) -{ - struct eth_dev *dev = ep->driver_data; - int status; - - /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ - spin_lock(&dev->lock); - status = rndis_msg_parser (dev->rndis_config, (u8 *) req->buf); - if (status < 0) - ERROR(dev, "%s: rndis parse error %d\n", __func__, status); - spin_unlock(&dev->lock); -} - -#endif /* RNDIS */ - /* - * The setup() callback implements all the ep0 functionality that's not - * handled lower down. CDC has a number of less-common features: - * - * - two interfaces: control, and ethernet data - * - Ethernet data interface has two altsettings: default, and active - * - class-specific descriptors for the control interface - * - class-specific control requests + * We _always_ have an ECM or CDC Subset configuration. */ -static int -eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +static int __init eth_do_config(struct usb_configuration *c) { - struct eth_dev *dev = get_gadget_data (gadget); - struct usb_request *req = dev->req; - int value = -EOPNOTSUPP; - u16 wIndex = le16_to_cpu(ctrl->wIndex); - u16 wValue = le16_to_cpu(ctrl->wValue); - u16 wLength = le16_to_cpu(ctrl->wLength); - - /* descriptors just go into the pre-allocated ep0 buffer, - * while config change events may enable network traffic. - */ - req->complete = eth_setup_complete; - switch (ctrl->bRequest) { - - case USB_REQ_GET_DESCRIPTOR: - if (ctrl->bRequestType != USB_DIR_IN) - break; - switch (wValue >> 8) { - - case USB_DT_DEVICE: - value = min (wLength, (u16) sizeof device_desc); - memcpy (req->buf, &device_desc, value); - break; - case USB_DT_DEVICE_QUALIFIER: - if (!gadget_is_dualspeed(gadget)) - break; - value = min (wLength, (u16) sizeof dev_qualifier); - memcpy (req->buf, &dev_qualifier, value); - break; - - case USB_DT_OTHER_SPEED_CONFIG: - if (!gadget_is_dualspeed(gadget)) - break; - // FALLTHROUGH - case USB_DT_CONFIG: - value = config_buf(gadget, req->buf, - wValue >> 8, - wValue & 0xff, - gadget_is_otg(gadget)); - if (value >= 0) - value = min (wLength, (u16) value); - break; - - case USB_DT_STRING: - value = usb_gadget_get_string (&stringtab, - wValue & 0xff, req->buf); - if (value >= 0) - value = min (wLength, (u16) value); - break; - } - break; - - case USB_REQ_SET_CONFIGURATION: - if (ctrl->bRequestType != 0) - break; - if (gadget->a_hnp_support) - DEBUG (dev, "HNP available\n"); - else if (gadget->a_alt_hnp_support) - DEBUG (dev, "HNP needs a different root port\n"); - spin_lock (&dev->lock); - value = eth_set_config (dev, wValue, GFP_ATOMIC); - spin_unlock (&dev->lock); - break; - case USB_REQ_GET_CONFIGURATION: - if (ctrl->bRequestType != USB_DIR_IN) - break; - *(u8 *)req->buf = dev->config; - value = min (wLength, (u16) 1); - break; - - case USB_REQ_SET_INTERFACE: - if (ctrl->bRequestType != USB_RECIP_INTERFACE - || !dev->config - || wIndex > 1) - break; - if (!cdc_active(dev) && wIndex != 0) - break; - spin_lock (&dev->lock); - - /* PXA hardware partially handles SET_INTERFACE; - * we need to kluge around that interference. - */ - if (gadget_is_pxa (gadget)) { - value = eth_set_config (dev, DEV_CONFIG_VALUE, - GFP_ATOMIC); - goto done_set_intf; - } - -#ifdef DEV_CONFIG_CDC - switch (wIndex) { - case 0: /* control/master intf */ - if (wValue != 0) - break; - if (dev->status) { - usb_ep_disable (dev->status_ep); - usb_ep_enable (dev->status_ep, dev->status); - } - value = 0; - break; - case 1: /* data intf */ - if (wValue > 1) - break; - usb_ep_disable (dev->in_ep); - usb_ep_disable (dev->out_ep); - - /* CDC requires the data transfers not be done from - * the default interface setting ... also, setting - * the non-default interface resets filters etc. - */ - if (wValue == 1) { - if (!cdc_active (dev)) - break; - usb_ep_enable (dev->in_ep, dev->in); - usb_ep_enable (dev->out_ep, dev->out); - dev->cdc_filter = DEFAULT_FILTER; - netif_carrier_on (dev->net); - if (dev->status) - issue_start_status (dev); - if (netif_running (dev->net)) { - spin_unlock (&dev->lock); - eth_start (dev, GFP_ATOMIC); - spin_lock (&dev->lock); - } - } else { - netif_stop_queue (dev->net); - netif_carrier_off (dev->net); - } - value = 0; - break; - } -#else - /* FIXME this is wrong, as is the assumption that - * all non-PXA hardware talks real CDC ... - */ - dev_warn (&gadget->dev, "set_interface ignored!\n"); -#endif /* DEV_CONFIG_CDC */ - -done_set_intf: - spin_unlock (&dev->lock); - break; - case USB_REQ_GET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE) - || !dev->config - || wIndex > 1) - break; - if (!(cdc_active(dev) || rndis_active(dev)) && wIndex != 0) - break; - - /* for CDC, iff carrier is on, data interface is active. */ - if (rndis_active(dev) || wIndex != 1) - *(u8 *)req->buf = 0; - else - *(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0; - value = min (wLength, (u16) 1); - break; - -#ifdef DEV_CONFIG_CDC - case USB_CDC_SET_ETHERNET_PACKET_FILTER: - /* see 6.2.30: no data, wIndex = interface, - * wValue = packet filter bitmap - */ - if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) - || !cdc_active(dev) - || wLength != 0 - || wIndex > 1) - break; - DEBUG (dev, "packet filter %02x\n", wValue); - dev->cdc_filter = wValue; - value = 0; - break; - - /* and potentially: - * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS: - * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER: - * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER: - * case USB_CDC_GET_ETHERNET_STATISTIC: - */ - -#endif /* DEV_CONFIG_CDC */ + /* FIXME alloc iConfiguration string, set it in c->strings */ -#ifdef CONFIG_USB_ETH_RNDIS - /* RNDIS uses the CDC command encapsulation mechanism to implement - * an RPC scheme, with much getting/setting of attributes by OID. - */ - case USB_CDC_SEND_ENCAPSULATED_COMMAND: - if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) - || !rndis_active(dev) - || wLength > USB_BUFSIZ - || wValue - || rndis_control_intf.bInterfaceNumber - != wIndex) - break; - /* read the request, then process it */ - value = wLength; - req->complete = rndis_command_complete; - /* later, rndis_control_ack () sends a notification */ - break; - - case USB_CDC_GET_ENCAPSULATED_RESPONSE: - if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE) - == ctrl->bRequestType - && rndis_active(dev) - // && wLength >= 0x0400 - && !wValue - && rndis_control_intf.bInterfaceNumber - == wIndex) { - u8 *buf; - u32 n; - - /* return the result */ - buf = rndis_get_next_response(dev->rndis_config, &n); - if (buf) { - memcpy(req->buf, buf, n); - req->complete = rndis_response_complete; - rndis_free_response(dev->rndis_config, buf); - value = n; - } - /* else stalls ... spec says to avoid that */ - } - break; -#endif /* RNDIS */ - - default: - VDEBUG (dev, - "unknown control req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - wValue, wIndex, wLength); + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - /* respond with data transfer before status phase? */ - if (value >= 0) { - req->length = value; - req->zero = value < wLength - && (value % gadget->ep0->maxpacket) == 0; - value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC); - if (value < 0) { - DEBUG (dev, "ep_queue --> %d\n", value); - req->status = 0; - eth_setup_complete (gadget->ep0, req); - } - } - - /* host either stalls (value < 0) or reports success */ - return value; -} - -static void -eth_disconnect (struct usb_gadget *gadget) -{ - struct eth_dev *dev = get_gadget_data (gadget); - unsigned long flags; - - spin_lock_irqsave (&dev->lock, flags); - netif_stop_queue (dev->net); - netif_carrier_off (dev->net); - eth_reset_config (dev); - spin_unlock_irqrestore (&dev->lock, flags); - - /* FIXME RNDIS should enter RNDIS_UNINITIALIZED */ - - /* next we may get setup() calls to enumerate new connections; - * or an unbind() during shutdown (including removing module). - */ -} - -/*-------------------------------------------------------------------------*/ - -/* NETWORK DRIVER HOOKUP (to the layer above this driver) */ - -static int eth_change_mtu (struct net_device *net, int new_mtu) -{ - struct eth_dev *dev = netdev_priv(net); - - if (dev->rndis) - return -EBUSY; - - if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN) - return -ERANGE; - /* no zero-length packet read wanted after mtu-sized packets */ - if (((new_mtu + sizeof (struct ethhdr)) % dev->in_ep->maxpacket) == 0) - return -EDOM; - net->mtu = new_mtu; - return 0; -} - -static struct net_device_stats *eth_get_stats (struct net_device *net) -{ - return &((struct eth_dev *)netdev_priv(net))->stats; -} - -static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p) -{ - struct eth_dev *dev = netdev_priv(net); - strlcpy(p->driver, shortname, sizeof p->driver); - strlcpy(p->version, DRIVER_VERSION, sizeof p->version); - strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version); - strlcpy (p->bus_info, dev->gadget->dev.bus_id, sizeof p->bus_info); -} - -static u32 eth_get_link(struct net_device *net) -{ - struct eth_dev *dev = netdev_priv(net); - return dev->gadget->speed != USB_SPEED_UNKNOWN; -} - -static struct ethtool_ops ops = { - .get_drvinfo = eth_get_drvinfo, - .get_link = eth_get_link -}; - -static void defer_kevent (struct eth_dev *dev, int flag) -{ - if (test_and_set_bit (flag, &dev->todo)) - return; - if (!schedule_work (&dev->work)) - ERROR (dev, "kevent %d may have been dropped\n", flag); + if (can_support_ecm(c->cdev->gadget)) + return ecm_bind_config(c, hostaddr); else - DEBUG (dev, "kevent %d scheduled\n", flag); -} - -static void rx_complete (struct usb_ep *ep, struct usb_request *req); - -static int -rx_submit (struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags) -{ - struct sk_buff *skb; - int retval = -ENOMEM; - size_t size; - - /* Padding up to RX_EXTRA handles minor disagreements with host. - * Normally we use the USB "terminate on short read" convention; - * so allow up to (N*maxpacket), since that memory is normally - * already allocated. Some hardware doesn't deal well with short - * reads (e.g. DMA must be N*maxpacket), so for now don't trim a - * byte off the end (to force hardware errors on overflow). - * - * RNDIS uses internal framing, and explicitly allows senders to - * pad to end-of-packet. That's potentially nice for speed, - * but means receivers can't recover synch on their own. - */ - size = (sizeof (struct ethhdr) + dev->net->mtu + RX_EXTRA); - size += dev->out_ep->maxpacket - 1; - if (rndis_active(dev)) - size += sizeof (struct rndis_packet_msg_type); - size -= size % dev->out_ep->maxpacket; - - skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags); - if (skb == NULL) { - DEBUG (dev, "no rx skb\n"); - goto enomem; - } - - /* Some platforms perform better when IP packets are aligned, - * but on at least one, checksumming fails otherwise. Note: - * RNDIS headers involve variable numbers of LE32 values. - */ - skb_reserve(skb, NET_IP_ALIGN); - - req->buf = skb->data; - req->length = size; - req->complete = rx_complete; - req->context = skb; - - retval = usb_ep_queue (dev->out_ep, req, gfp_flags); - if (retval == -ENOMEM) -enomem: - defer_kevent (dev, WORK_RX_MEMORY); - if (retval) { - DEBUG (dev, "rx submit --> %d\n", retval); - if (skb) - dev_kfree_skb_any(skb); - spin_lock(&dev->req_lock); - list_add (&req->list, &dev->rx_reqs); - spin_unlock(&dev->req_lock); - } - return retval; -} - -static void rx_complete (struct usb_ep *ep, struct usb_request *req) -{ - struct sk_buff *skb = req->context; - struct eth_dev *dev = ep->driver_data; - int status = req->status; - - switch (status) { - - /* normal completion */ - case 0: - skb_put (skb, req->actual); - /* we know MaxPacketsPerTransfer == 1 here */ - if (rndis_active(dev)) - status = rndis_rm_hdr (skb); - if (status < 0 - || ETH_HLEN > skb->len - || skb->len > ETH_FRAME_LEN) { - dev->stats.rx_errors++; - dev->stats.rx_length_errors++; - DEBUG (dev, "rx length %d\n", skb->len); - break; - } - - skb->protocol = eth_type_trans (skb, dev->net); - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - - /* no buffer copies needed, unless hardware can't - * use skb buffers. - */ - status = netif_rx (skb); - skb = NULL; - break; - - /* software-driven interface shutdown */ - case -ECONNRESET: // unlink - case -ESHUTDOWN: // disconnect etc - VDEBUG (dev, "rx shutdown, code %d\n", status); - goto quiesce; - - /* for hardware automagic (such as pxa) */ - case -ECONNABORTED: // endpoint reset - DEBUG (dev, "rx %s reset\n", ep->name); - defer_kevent (dev, WORK_RX_MEMORY); -quiesce: - dev_kfree_skb_any (skb); - goto clean; - - /* data overrun */ - case -EOVERFLOW: - dev->stats.rx_over_errors++; - // FALLTHROUGH - - default: - dev->stats.rx_errors++; - DEBUG (dev, "rx status %d\n", status); - break; - } - - if (skb) - dev_kfree_skb_any (skb); - if (!netif_running (dev->net)) { -clean: - spin_lock(&dev->req_lock); - list_add (&req->list, &dev->rx_reqs); - spin_unlock(&dev->req_lock); - req = NULL; - } - if (req) - rx_submit (dev, req, GFP_ATOMIC); -} - -static int prealloc (struct list_head *list, struct usb_ep *ep, - unsigned n, gfp_t gfp_flags) -{ - unsigned i; - struct usb_request *req; - - if (!n) - return -ENOMEM; - - /* queue/recycle up to N requests */ - i = n; - list_for_each_entry (req, list, list) { - if (i-- == 0) - goto extra; - } - while (i--) { - req = usb_ep_alloc_request (ep, gfp_flags); - if (!req) - return list_empty (list) ? -ENOMEM : 0; - list_add (&req->list, list); - } - return 0; - -extra: - /* free extras */ - for (;;) { - struct list_head *next; - - next = req->list.next; - list_del (&req->list); - usb_ep_free_request (ep, req); - - if (next == list) - break; - - req = container_of (next, struct usb_request, list); - } - return 0; -} - -static int alloc_requests (struct eth_dev *dev, unsigned n, gfp_t gfp_flags) -{ - int status; - - spin_lock(&dev->req_lock); - status = prealloc (&dev->tx_reqs, dev->in_ep, n, gfp_flags); - if (status < 0) - goto fail; - status = prealloc (&dev->rx_reqs, dev->out_ep, n, gfp_flags); - if (status < 0) - goto fail; - goto done; -fail: - DEBUG (dev, "can't alloc requests\n"); -done: - spin_unlock(&dev->req_lock); - return status; -} - -static void rx_fill (struct eth_dev *dev, gfp_t gfp_flags) -{ - struct usb_request *req; - unsigned long flags; - - /* fill unused rxq slots with some skb */ - spin_lock_irqsave(&dev->req_lock, flags); - while (!list_empty (&dev->rx_reqs)) { - req = container_of (dev->rx_reqs.next, - struct usb_request, list); - list_del_init (&req->list); - spin_unlock_irqrestore(&dev->req_lock, flags); - - if (rx_submit (dev, req, gfp_flags) < 0) { - defer_kevent (dev, WORK_RX_MEMORY); - return; - } - - spin_lock_irqsave(&dev->req_lock, flags); - } - spin_unlock_irqrestore(&dev->req_lock, flags); -} - -static void eth_work (struct work_struct *work) -{ - struct eth_dev *dev = container_of(work, struct eth_dev, work); - - if (test_and_clear_bit (WORK_RX_MEMORY, &dev->todo)) { - if (netif_running (dev->net)) - rx_fill (dev, GFP_KERNEL); - } - - if (dev->todo) - DEBUG (dev, "work done, flags = 0x%lx\n", dev->todo); -} - -static void tx_complete (struct usb_ep *ep, struct usb_request *req) -{ - struct sk_buff *skb = req->context; - struct eth_dev *dev = ep->driver_data; - - switch (req->status) { - default: - dev->stats.tx_errors++; - VDEBUG (dev, "tx err %d\n", req->status); - /* FALLTHROUGH */ - case -ECONNRESET: // unlink - case -ESHUTDOWN: // disconnect etc - break; - case 0: - dev->stats.tx_bytes += skb->len; - } - dev->stats.tx_packets++; - - spin_lock(&dev->req_lock); - list_add (&req->list, &dev->tx_reqs); - spin_unlock(&dev->req_lock); - dev_kfree_skb_any (skb); - - atomic_dec (&dev->tx_qlen); - if (netif_carrier_ok (dev->net)) - netif_wake_queue (dev->net); -} - -static inline int eth_is_promisc (struct eth_dev *dev) -{ - /* no filters for the CDC subset; always promisc */ - if (subset_active (dev)) - return 1; - return dev->cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS; -} - -static int eth_start_xmit (struct sk_buff *skb, struct net_device *net) -{ - struct eth_dev *dev = netdev_priv(net); - int length = skb->len; - int retval; - struct usb_request *req = NULL; - unsigned long flags; - - /* apply outgoing CDC or RNDIS filters */ - if (!eth_is_promisc (dev)) { - u8 *dest = skb->data; - - if (is_multicast_ether_addr(dest)) { - u16 type; - - /* ignores USB_CDC_PACKET_TYPE_MULTICAST and host - * SET_ETHERNET_MULTICAST_FILTERS requests - */ - if (is_broadcast_ether_addr(dest)) - type = USB_CDC_PACKET_TYPE_BROADCAST; - else - type = USB_CDC_PACKET_TYPE_ALL_MULTICAST; - if (!(dev->cdc_filter & type)) { - dev_kfree_skb_any (skb); - return 0; - } - } - /* ignores USB_CDC_PACKET_TYPE_DIRECTED */ - } - - spin_lock_irqsave(&dev->req_lock, flags); - /* - * this freelist can be empty if an interrupt triggered disconnect() - * and reconfigured the gadget (shutting down this queue) after the - * network stack decided to xmit but before we got the spinlock. - */ - if (list_empty(&dev->tx_reqs)) { - spin_unlock_irqrestore(&dev->req_lock, flags); - return 1; - } - - req = container_of (dev->tx_reqs.next, struct usb_request, list); - list_del (&req->list); - - /* temporarily stop TX queue when the freelist empties */ - if (list_empty (&dev->tx_reqs)) - netif_stop_queue (net); - spin_unlock_irqrestore(&dev->req_lock, flags); - - /* no buffer copies needed, unless the network stack did it - * or the hardware can't use skb buffers. - * or there's not enough space for any RNDIS headers we need - */ - if (rndis_active(dev)) { - struct sk_buff *skb_rndis; - - skb_rndis = skb_realloc_headroom (skb, - sizeof (struct rndis_packet_msg_type)); - if (!skb_rndis) - goto drop; - - dev_kfree_skb_any (skb); - skb = skb_rndis; - rndis_add_hdr (skb); - length = skb->len; - } - req->buf = skb->data; - req->context = skb; - req->complete = tx_complete; - - /* use zlp framing on tx for strict CDC-Ether conformance, - * though any robust network rx path ignores extra padding. - * and some hardware doesn't like to write zlps. - */ - req->zero = 1; - if (!dev->zlp && (length % dev->in_ep->maxpacket) == 0) - length++; - - req->length = length; - - /* throttle highspeed IRQ rate back slightly */ - if (gadget_is_dualspeed(dev->gadget)) - req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH) - ? ((atomic_read(&dev->tx_qlen) % qmult) != 0) - : 0; - - retval = usb_ep_queue (dev->in_ep, req, GFP_ATOMIC); - switch (retval) { - default: - DEBUG (dev, "tx queue err %d\n", retval); - break; - case 0: - net->trans_start = jiffies; - atomic_inc (&dev->tx_qlen); - } - - if (retval) { -drop: - dev->stats.tx_dropped++; - dev_kfree_skb_any (skb); - spin_lock_irqsave(&dev->req_lock, flags); - if (list_empty (&dev->tx_reqs)) - netif_start_queue (net); - list_add (&req->list, &dev->tx_reqs); - spin_unlock_irqrestore(&dev->req_lock, flags); - } - return 0; -} - -/*-------------------------------------------------------------------------*/ - -#ifdef CONFIG_USB_ETH_RNDIS - -/* The interrupt endpoint is used in RNDIS to notify the host when messages - * other than data packets are available ... notably the REMOTE_NDIS_*_CMPLT - * messages, but also REMOTE_NDIS_INDICATE_STATUS_MSG and potentially even - * REMOTE_NDIS_KEEPALIVE_MSG. - * - * The RNDIS control queue is processed by GET_ENCAPSULATED_RESPONSE, and - * normally just one notification will be queued. - */ - -static struct usb_request *eth_req_alloc (struct usb_ep *, unsigned, gfp_t); -static void eth_req_free (struct usb_ep *ep, struct usb_request *req); - -static void -rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req) -{ - struct eth_dev *dev = ep->driver_data; - - if (req->status || req->actual != req->length) - DEBUG (dev, - "rndis control ack complete --> %d, %d/%d\n", - req->status, req->actual, req->length); - req->context = NULL; - - if (req != dev->stat_req) - eth_req_free(ep, req); -} - -static int rndis_control_ack (struct net_device *net) -{ - struct eth_dev *dev = netdev_priv(net); - int length; - struct usb_request *resp = dev->stat_req; - - /* in case RNDIS calls this after disconnect */ - if (!dev->status) { - DEBUG (dev, "status ENODEV\n"); - return -ENODEV; - } - - /* in case queue length > 1 */ - if (resp->context) { - resp = eth_req_alloc (dev->status_ep, 8, GFP_ATOMIC); - if (!resp) - return -ENOMEM; - } - - /* Send RNDIS RESPONSE_AVAILABLE notification; - * USB_CDC_NOTIFY_RESPONSE_AVAILABLE should work too - */ - resp->length = 8; - resp->complete = rndis_control_ack_complete; - resp->context = dev; - - *((__le32 *) resp->buf) = __constant_cpu_to_le32 (1); - *((__le32 *) resp->buf + 1) = __constant_cpu_to_le32 (0); - - length = usb_ep_queue (dev->status_ep, resp, GFP_ATOMIC); - if (length < 0) { - resp->status = 0; - rndis_control_ack_complete (dev->status_ep, resp); - } - - return 0; -} - -#else - -#define rndis_control_ack NULL - -#endif /* RNDIS */ - -static void eth_start (struct eth_dev *dev, gfp_t gfp_flags) -{ - DEBUG (dev, "%s\n", __func__); - - /* fill the rx queue */ - rx_fill (dev, gfp_flags); - - /* and open the tx floodgates */ - atomic_set (&dev->tx_qlen, 0); - netif_wake_queue (dev->net); - if (rndis_active(dev)) { - rndis_set_param_medium (dev->rndis_config, - NDIS_MEDIUM_802_3, - BITRATE(dev->gadget)/100); - (void) rndis_signal_connect (dev->rndis_config); - } -} - -static int eth_open (struct net_device *net) -{ - struct eth_dev *dev = netdev_priv(net); - - DEBUG (dev, "%s\n", __func__); - if (netif_carrier_ok (dev->net)) - eth_start (dev, GFP_KERNEL); - return 0; + return geth_bind_config(c, hostaddr); } -static int eth_stop (struct net_device *net) -{ - struct eth_dev *dev = netdev_priv(net); - - VDEBUG (dev, "%s\n", __func__); - netif_stop_queue (net); - - DEBUG (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n", - dev->stats.rx_packets, dev->stats.tx_packets, - dev->stats.rx_errors, dev->stats.tx_errors - ); - - /* ensure there are no more active requests */ - if (dev->config) { - usb_ep_disable (dev->in_ep); - usb_ep_disable (dev->out_ep); - if (netif_carrier_ok (dev->net)) { - DEBUG (dev, "host still using in/out endpoints\n"); - // FIXME idiom may leave toggle wrong here - usb_ep_enable (dev->in_ep, dev->in); - usb_ep_enable (dev->out_ep, dev->out); - } - if (dev->status_ep) { - usb_ep_disable (dev->status_ep); - usb_ep_enable (dev->status_ep, dev->status); - } - } - - if (rndis_active(dev)) { - rndis_set_param_medium(dev->rndis_config, NDIS_MEDIUM_802_3, 0); - (void) rndis_signal_disconnect (dev->rndis_config); - } - - return 0; -} +static struct usb_configuration eth_config_driver = { + /* .label = f(hardware) */ + .bind = eth_do_config, + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 1, /* 2 mA, minimal */ +}; /*-------------------------------------------------------------------------*/ -static struct usb_request * -eth_req_alloc (struct usb_ep *ep, unsigned size, gfp_t gfp_flags) +static int __init eth_bind(struct usb_composite_dev *cdev) { - struct usb_request *req; - - req = usb_ep_alloc_request (ep, gfp_flags); - if (!req) - return NULL; - - req->buf = kmalloc (size, gfp_flags); - if (!req->buf) { - usb_ep_free_request (ep, req); - req = NULL; - } - return req; -} - -static void -eth_req_free (struct usb_ep *ep, struct usb_request *req) -{ - kfree (req->buf); - usb_ep_free_request (ep, req); -} - - -static void /* __init_or_exit */ -eth_unbind (struct usb_gadget *gadget) -{ - struct eth_dev *dev = get_gadget_data (gadget); - - DEBUG (dev, "unbind\n"); - rndis_deregister (dev->rndis_config); - rndis_exit (); - - /* we've already been disconnected ... no i/o is active */ - if (dev->req) { - eth_req_free (gadget->ep0, dev->req); - dev->req = NULL; - } - if (dev->stat_req) { - eth_req_free (dev->status_ep, dev->stat_req); - dev->stat_req = NULL; - } - - unregister_netdev (dev->net); - free_netdev(dev->net); - - /* assuming we used keventd, it must quiesce too */ - flush_scheduled_work (); - set_gadget_data (gadget, NULL); -} - -static u8 __init nibble (unsigned char c) -{ - if (likely (isdigit (c))) - return c - '0'; - c = toupper (c); - if (likely (isxdigit (c))) - return 10 + c - 'A'; - return 0; -} + int gcnum; + struct usb_gadget *gadget = cdev->gadget; + int status; -static int __init get_ether_addr(const char *str, u8 *dev_addr) -{ - if (str) { - unsigned i; + /* set up network link layer */ + status = gether_setup(cdev->gadget, hostaddr); + if (status < 0) + return status; - for (i = 0; i < 6; i++) { - unsigned char num; + /* set up main config label and device descriptor */ + if (can_support_ecm(cdev->gadget)) { + /* ECM */ + eth_config_driver.label = "CDC Ethernet (ECM)"; + } else { + /* CDC Subset */ + eth_config_driver.label = "CDC Subset/SAFE"; - if((*str == '.') || (*str == ':')) - str++; - num = nibble(*str++) << 4; - num |= (nibble(*str++)); - dev_addr [i] = num; - } - if (is_valid_ether_addr (dev_addr)) - return 0; + device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM), + device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM), + device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; } - random_ether_addr(dev_addr); - return 1; -} - -static int __init -eth_bind (struct usb_gadget *gadget) -{ - struct eth_dev *dev; - struct net_device *net; - u8 cdc = 1, zlp = 1, rndis = 1; - struct usb_ep *in_ep, *out_ep, *status_ep = NULL; - int status = -ENOMEM; - int gcnum; - - /* these flags are only ever cleared; compiler take note */ -#ifndef DEV_CONFIG_CDC - cdc = 0; -#endif -#ifndef CONFIG_USB_ETH_RNDIS - rndis = 0; -#endif - /* Because most host side USB stacks handle CDC Ethernet, that - * standard protocol is _strongly_ preferred for interop purposes. - * (By everyone except Microsoft.) - */ - if (gadget_is_pxa (gadget)) { - /* pxa doesn't support altsettings */ - cdc = 0; - } else if (gadget_is_musbhdrc(gadget)) { - /* reduce tx dma overhead by avoiding special cases */ - zlp = 0; - } else if (gadget_is_sh(gadget)) { - /* sh doesn't support multiple interfaces or configs */ - cdc = 0; - rndis = 0; - } else if (gadget_is_sa1100 (gadget)) { - /* hardware can't write zlps */ - zlp = 0; - /* sa1100 CAN do CDC, without status endpoint ... we use - * non-CDC to be compatible with ARM Linux-2.4 "usb-eth". - */ - cdc = 0; + if (has_rndis()) { + /* RNDIS plus ECM-or-Subset */ + device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM), + device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM), + device_desc.bNumConfigurations = 2; } - gcnum = usb_gadget_controller_number (gadget); + gcnum = usb_gadget_controller_number(gadget); if (gcnum >= 0) - device_desc.bcdDevice = cpu_to_le16 (0x0200 + gcnum); + device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); else { - /* can't assume CDC works. don't want to default to - * anything less functional on CDC-capable hardware, - * so we fail in this case. + /* We assume that can_support_ecm() tells the truth; + * but if the controller isn't recognized at all then + * that assumption is a bit more likely to be wrong. */ - dev_err (&gadget->dev, - "controller '%s' not recognized\n", - gadget->name); - return -ENODEV; - } - snprintf (manufacturer, sizeof manufacturer, "%s %s/%s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); - - /* If there's an RNDIS configuration, that's what Windows wants to - * be using ... so use these product IDs here and in the "linux.inf" - * needed to install MSFT drivers. Current Linux kernels will use - * the second configuration if it's CDC Ethernet, and need some help - * to choose the right configuration otherwise. - */ - if (rndis) { - device_desc.idVendor = - __constant_cpu_to_le16(RNDIS_VENDOR_NUM); - device_desc.idProduct = - __constant_cpu_to_le16(RNDIS_PRODUCT_NUM); - snprintf (product_desc, sizeof product_desc, - "RNDIS/%s", driver_desc); - - /* CDC subset ... recognized by Linux since 2.4.10, but Windows - * drivers aren't widely available. (That may be improved by - * supporting one submode of the "SAFE" variant of MDLM.) - */ - } else if (!cdc) { - device_desc.idVendor = - __constant_cpu_to_le16(SIMPLE_VENDOR_NUM); - device_desc.idProduct = - __constant_cpu_to_le16(SIMPLE_PRODUCT_NUM); - } - - /* support optional vendor/distro customization */ - if (idVendor) { - if (!idProduct) { - dev_err (&gadget->dev, "idVendor needs idProduct!\n"); - return -ENODEV; - } - device_desc.idVendor = cpu_to_le16(idVendor); - device_desc.idProduct = cpu_to_le16(idProduct); - if (bcdDevice) - device_desc.bcdDevice = cpu_to_le16(bcdDevice); - } - if (iManufacturer) - strlcpy (manufacturer, iManufacturer, sizeof manufacturer); - if (iProduct) - strlcpy (product_desc, iProduct, sizeof product_desc); - if (iSerialNumber) { - device_desc.iSerialNumber = STRING_SERIALNUMBER, - strlcpy(serial_number, iSerialNumber, sizeof serial_number); - } - - /* all we really need is bulk IN/OUT */ - usb_ep_autoconfig_reset (gadget); - in_ep = usb_ep_autoconfig (gadget, &fs_source_desc); - if (!in_ep) { -autoconf_fail: - dev_err (&gadget->dev, - "can't autoconfigure on %s\n", - gadget->name); - return -ENODEV; - } - in_ep->driver_data = in_ep; /* claim */ - - out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc); - if (!out_ep) - goto autoconf_fail; - out_ep->driver_data = out_ep; /* claim */ - -#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) - /* CDC Ethernet control interface doesn't require a status endpoint. - * Since some hosts expect one, try to allocate one anyway. - */ - if (cdc || rndis) { - status_ep = usb_ep_autoconfig (gadget, &fs_status_desc); - if (status_ep) { - status_ep->driver_data = status_ep; /* claim */ - } else if (rndis) { - dev_err (&gadget->dev, - "can't run RNDIS on %s\n", - gadget->name); - return -ENODEV; -#ifdef DEV_CONFIG_CDC - /* pxa25x only does CDC subset; often used with RNDIS */ - } else if (cdc) { - control_intf.bNumEndpoints = 0; - /* FIXME remove endpoint from descriptor list */ -#endif - } - } -#endif - - /* one config: cdc, else minimal subset */ - if (!cdc) { - eth_config.bNumInterfaces = 1; - eth_config.iConfiguration = STRING_SUBSET; - - /* use functions to set these up, in case we're built to work - * with multiple controllers and must override CDC Ethernet. - */ - fs_subset_descriptors(); - hs_subset_descriptors(); - } - - device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; - usb_gadget_set_selfpowered (gadget); - - /* For now RNDIS is always a second config */ - if (rndis) - device_desc.bNumConfigurations = 2; - - if (gadget_is_dualspeed(gadget)) { - if (rndis) - dev_qualifier.bNumConfigurations = 2; - else if (!cdc) - dev_qualifier.bDeviceClass = USB_CLASS_VENDOR_SPEC; - - /* assumes ep0 uses the same value for both speeds ... */ - dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0; - - /* and that all endpoints are dual-speed */ - hs_source_desc.bEndpointAddress = - fs_source_desc.bEndpointAddress; - hs_sink_desc.bEndpointAddress = - fs_sink_desc.bEndpointAddress; -#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) - if (status_ep) - hs_status_desc.bEndpointAddress = - fs_status_desc.bEndpointAddress; -#endif + WARNING(cdev, "controller '%s' not recognized; trying %s\n", + gadget->name, + eth_config_driver.label); + device_desc.bcdDevice = + __constant_cpu_to_le16(0x0300 | 0x0099); } - if (gadget_is_otg(gadget)) { - otg_descriptor.bmAttributes |= USB_OTG_HNP, - eth_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - eth_config.bMaxPower = 4; -#ifdef CONFIG_USB_ETH_RNDIS - rndis_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - rndis_config.bMaxPower = 4; -#endif - } - - net = alloc_etherdev (sizeof *dev); - if (!net) - return status; - dev = netdev_priv(net); - spin_lock_init (&dev->lock); - spin_lock_init (&dev->req_lock); - INIT_WORK (&dev->work, eth_work); - INIT_LIST_HEAD (&dev->tx_reqs); - INIT_LIST_HEAD (&dev->rx_reqs); - - /* network device setup */ - dev->net = net; - strcpy (net->name, "usb%d"); - dev->cdc = cdc; - dev->zlp = zlp; - dev->in_ep = in_ep; - dev->out_ep = out_ep; - dev->status_ep = status_ep; - - /* Module params for these addresses should come from ID proms. - * The host side address is used with CDC and RNDIS, and commonly - * ends up in a persistent config database. It's not clear if - * host side code for the SAFE thing cares -- its original BLAN - * thing didn't, Sharp never assigned those addresses on Zaurii. + /* Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. */ - if (get_ether_addr(dev_addr, net->dev_addr)) - dev_warn(&gadget->dev, - "using random %s ethernet address\n", "self"); - if (get_ether_addr(host_addr, dev->host_mac)) - dev_warn(&gadget->dev, - "using random %s ethernet address\n", "host"); - snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X", - dev->host_mac [0], dev->host_mac [1], - dev->host_mac [2], dev->host_mac [3], - dev->host_mac [4], dev->host_mac [5]); - - if (rndis) { - status = rndis_init(); - if (status < 0) { - dev_err (&gadget->dev, "can't init RNDIS, %d\n", - status); - goto fail; - } - } - net->change_mtu = eth_change_mtu; - net->get_stats = eth_get_stats; - net->hard_start_xmit = eth_start_xmit; - net->open = eth_open; - net->stop = eth_stop; - // watchdog_timeo, tx_timeout ... - // set_multicast_list - SET_ETHTOOL_OPS(net, &ops); + /* device descriptor strings: manufacturer, product */ + snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", + init_utsname()->sysname, init_utsname()->release, + gadget->name); + status = usb_string_id(cdev); + if (status < 0) + goto fail; + strings_dev[STRING_MANUFACTURER_IDX].id = status; + device_desc.iManufacturer = status; - /* preallocate control message data and buffer */ - dev->req = eth_req_alloc (gadget->ep0, USB_BUFSIZ, GFP_KERNEL); - if (!dev->req) + status = usb_string_id(cdev); + if (status < 0) goto fail; - dev->req->complete = eth_setup_complete; + strings_dev[STRING_PRODUCT_IDX].id = status; + device_desc.iProduct = status; - /* ... and maybe likewise for status transfer */ -#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) - if (dev->status_ep) { - dev->stat_req = eth_req_alloc (dev->status_ep, - STATUS_BYTECOUNT, GFP_KERNEL); - if (!dev->stat_req) { - eth_req_free (gadget->ep0, dev->req); + /* register our configuration(s); RNDIS first, if it's used */ + if (has_rndis()) { + status = usb_add_config(cdev, &rndis_config_driver); + if (status < 0) goto fail; - } - dev->stat_req->context = NULL; } -#endif - - /* finish hookup to lower layer ... */ - dev->gadget = gadget; - set_gadget_data (gadget, dev); - gadget->ep0->driver_data = dev; - /* two kinds of host-initiated state changes: - * - iff DATA transfer is active, carrier is "on" - * - tx queueing enabled if open *and* carrier is "on" - */ - netif_stop_queue (dev->net); - netif_carrier_off (dev->net); - - SET_NETDEV_DEV (dev->net, &gadget->dev); - status = register_netdev (dev->net); + status = usb_add_config(cdev, ð_config_driver); if (status < 0) - goto fail1; - - INFO (dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); - INFO (dev, "using %s, OUT %s IN %s%s%s\n", gadget->name, - out_ep->name, in_ep->name, - status_ep ? " STATUS " : "", - status_ep ? status_ep->name : "" - ); - INFO (dev, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n", - net->dev_addr [0], net->dev_addr [1], - net->dev_addr [2], net->dev_addr [3], - net->dev_addr [4], net->dev_addr [5]); - - if (cdc || rndis) - INFO (dev, "HOST MAC %02x:%02x:%02x:%02x:%02x:%02x\n", - dev->host_mac [0], dev->host_mac [1], - dev->host_mac [2], dev->host_mac [3], - dev->host_mac [4], dev->host_mac [5]); - - if (rndis) { - u32 vendorID = 0; - - /* FIXME RNDIS vendor id == "vendor NIC code" == ? */ - - dev->rndis_config = rndis_register (rndis_control_ack); - if (dev->rndis_config < 0) { -fail0: - unregister_netdev (dev->net); - status = -ENODEV; - goto fail; - } + goto fail; - /* these set up a lot of the OIDs that RNDIS needs */ - rndis_set_host_mac (dev->rndis_config, dev->host_mac); - if (rndis_set_param_dev (dev->rndis_config, dev->net, - &dev->stats, &dev->cdc_filter)) - goto fail0; - if (rndis_set_param_vendor(dev->rndis_config, vendorID, - manufacturer)) - goto fail0; - if (rndis_set_param_medium(dev->rndis_config, - NDIS_MEDIUM_802_3, 0)) - goto fail0; - INFO (dev, "RNDIS ready\n"); - } + INFO(cdev, "%s, version: " DRIVER_VERSION "\n", DRIVER_DESC); - return status; + return 0; -fail1: - dev_dbg(&gadget->dev, "register_netdev failed, %d\n", status); fail: - eth_unbind (gadget); + gether_cleanup(); return status; } -/*-------------------------------------------------------------------------*/ - -static void -eth_suspend (struct usb_gadget *gadget) -{ - struct eth_dev *dev = get_gadget_data (gadget); - - DEBUG (dev, "suspend\n"); - dev->suspended = 1; -} - -static void -eth_resume (struct usb_gadget *gadget) +static int __exit eth_unbind(struct usb_composite_dev *cdev) { - struct eth_dev *dev = get_gadget_data (gadget); - - DEBUG (dev, "resume\n"); - dev->suspended = 0; + gether_cleanup(); + return 0; } -/*-------------------------------------------------------------------------*/ - -static struct usb_gadget_driver eth_driver = { - .speed = DEVSPEED, - - .function = (char *) driver_desc, +static struct usb_composite_driver eth_driver = { + .name = "g_ether", + .dev = &device_desc, + .strings = dev_strings, .bind = eth_bind, - .unbind = eth_unbind, - - .setup = eth_setup, - .disconnect = eth_disconnect, - - .suspend = eth_suspend, - .resume = eth_resume, - - .driver = { - .name = (char *) shortname, - .owner = THIS_MODULE, - }, + .unbind = __exit_p(eth_unbind), }; -MODULE_DESCRIPTION (DRIVER_DESC); -MODULE_AUTHOR ("David Brownell, Benedikt Spanger"); -MODULE_LICENSE ("GPL"); +MODULE_DESCRIPTION(PREFIX DRIVER_DESC); +MODULE_AUTHOR("David Brownell, Benedikt Spanger"); +MODULE_LICENSE("GPL"); - -static int __init init (void) +static int __init init(void) { - return usb_gadget_register_driver (ð_driver); + return usb_composite_register(ð_driver); } -module_init (init); +module_init(init); -static void __exit cleanup (void) +static void __exit cleanup(void) { - usb_gadget_unregister_driver (ð_driver); + usb_composite_unregister(ð_driver); } -module_exit (cleanup); - +module_exit(cleanup); diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c new file mode 100644 index 000000000000..d8faccf27895 --- /dev/null +++ b/drivers/usb/gadget/f_acm.c @@ -0,0 +1,589 @@ +/* + * f_acm.c -- USB CDC serial (ACM) function driver + * + * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) + * Copyright (C) 2008 by David Brownell + * Copyright (C) 2008 by Nokia Corporation + * + * This software is distributed under the terms of the GNU General + * Public License ("GPL") as published by the Free Software Foundation, + * either version 2 of that License or (at your option) any later version. + */ + +/* #define VERBOSE_DEBUG */ + +#include <linux/kernel.h> +#include <linux/device.h> + +#include "u_serial.h" +#include "gadget_chips.h" + + +/* + * This CDC ACM function support just wraps control functions and + * notifications around the generic serial-over-usb code. + * + * Because CDC ACM is standardized by the USB-IF, many host operating + * systems have drivers for it. Accordingly, ACM is the preferred + * interop solution for serial-port type connections. The control + * models are often not necessary, and in any case don't do much in + * this bare-bones implementation. + * + * Note that even MS-Windows has some support for ACM. However, that + * support is somewhat broken because when you use ACM in a composite + * device, having multiple interfaces confuses the poor OS. It doesn't + * seem to understand CDC Union descriptors. The new "association" + * descriptors (roughly equivalent to CDC Unions) may sometimes help. + */ + +struct acm_ep_descs { + struct usb_endpoint_descriptor *in; + struct usb_endpoint_descriptor *out; + struct usb_endpoint_descriptor *notify; +}; + +struct f_acm { + struct gserial port; + u8 ctrl_id, data_id; + u8 port_num; + + struct usb_descriptor_header **fs_function; + struct acm_ep_descs fs; + struct usb_descriptor_header **hs_function; + struct acm_ep_descs hs; + + struct usb_ep *notify; + struct usb_endpoint_descriptor *notify_desc; + + struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ + u16 port_handshake_bits; +#define RS232_RTS (1 << 1) /* unused with full duplex */ +#define RS232_DTR (1 << 0) /* host is ready for data r/w */ +}; + +static inline struct f_acm *func_to_acm(struct usb_function *f) +{ + return container_of(f, struct f_acm, port.func); +} + +/*-------------------------------------------------------------------------*/ + +/* notification endpoint uses smallish and infrequent fixed-size messages */ + +#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */ +#define GS_NOTIFY_MAXPACKET 8 + +/* interface and class descriptors: */ + +static struct usb_interface_descriptor acm_control_interface_desc __initdata = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, + /* .iInterface = DYNAMIC */ +}; + +static struct usb_interface_descriptor acm_data_interface_desc __initdata = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + /* .iInterface = DYNAMIC */ +}; + +static struct usb_cdc_header_desc acm_header_desc __initdata = { + .bLength = sizeof(acm_header_desc), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_HEADER_TYPE, + .bcdCDC = __constant_cpu_to_le16(0x0110), +}; + +static struct usb_cdc_call_mgmt_descriptor +acm_call_mgmt_descriptor __initdata = { + .bLength = sizeof(acm_call_mgmt_descriptor), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, + .bmCapabilities = 0, + /* .bDataInterface = DYNAMIC */ +}; + +static struct usb_cdc_acm_descriptor acm_descriptor __initdata = { + .bLength = sizeof(acm_descriptor), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_ACM_TYPE, + .bmCapabilities = (1 << 1), +}; + +static struct usb_cdc_union_desc acm_union_desc __initdata = { + .bLength = sizeof(acm_union_desc), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_UNION_TYPE, + /* .bMasterInterface0 = DYNAMIC */ + /* .bSlaveInterface0 = DYNAMIC */ +}; + +/* full speed support: */ + +static struct usb_endpoint_descriptor acm_fs_notify_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), + .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL, +}; + +static struct usb_endpoint_descriptor acm_fs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor acm_fs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *acm_fs_function[] __initdata = { + (struct usb_descriptor_header *) &acm_control_interface_desc, + (struct usb_descriptor_header *) &acm_header_desc, + (struct usb_descriptor_header *) &acm_call_mgmt_descriptor, + (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &acm_union_desc, + (struct usb_descriptor_header *) &acm_fs_notify_desc, + (struct usb_descriptor_header *) &acm_data_interface_desc, + (struct usb_descriptor_header *) &acm_fs_in_desc, + (struct usb_descriptor_header *) &acm_fs_out_desc, + NULL, +}; + +/* high speed support: */ + +static struct usb_endpoint_descriptor acm_hs_notify_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), + .bInterval = GS_LOG2_NOTIFY_INTERVAL+4, +}; + +static struct usb_endpoint_descriptor acm_hs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor acm_hs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_descriptor_header *acm_hs_function[] __initdata = { + (struct usb_descriptor_header *) &acm_control_interface_desc, + (struct usb_descriptor_header *) &acm_header_desc, + (struct usb_descriptor_header *) &acm_call_mgmt_descriptor, + (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &acm_union_desc, + (struct usb_descriptor_header *) &acm_hs_notify_desc, + (struct usb_descriptor_header *) &acm_data_interface_desc, + (struct usb_descriptor_header *) &acm_hs_in_desc, + (struct usb_descriptor_header *) &acm_hs_out_desc, + NULL, +}; + +/* string descriptors: */ + +#define ACM_CTRL_IDX 0 +#define ACM_DATA_IDX 1 + +/* static strings, in UTF-8 */ +static struct usb_string acm_string_defs[] = { + [ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)", + [ACM_DATA_IDX].s = "CDC ACM Data", + { /* ZEROES END LIST */ }, +}; + +static struct usb_gadget_strings acm_string_table = { + .language = 0x0409, /* en-us */ + .strings = acm_string_defs, +}; + +static struct usb_gadget_strings *acm_strings[] = { + &acm_string_table, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +/* ACM control ... data handling is delegated to tty library code. + * The main task of this function is to activate and deactivate + * that code based on device state; track parameters like line + * speed, handshake state, and so on; and issue notifications. + */ + +static void acm_complete_set_line_coding(struct usb_ep *ep, + struct usb_request *req) +{ + struct f_acm *acm = ep->driver_data; + struct usb_composite_dev *cdev = acm->port.func.config->cdev; + + if (req->status != 0) { + DBG(cdev, "acm ttyGS%d completion, err %d\n", + acm->port_num, req->status); + return; + } + + /* normal completion */ + if (req->actual != sizeof(acm->port_line_coding)) { + DBG(cdev, "acm ttyGS%d short resp, len %d\n", + acm->port_num, req->actual); + usb_ep_set_halt(ep); + } else { + struct usb_cdc_line_coding *value = req->buf; + + /* REVISIT: we currently just remember this data. + * If we change that, (a) validate it first, then + * (b) update whatever hardware needs updating, + * (c) worry about locking. This is information on + * the order of 9600-8-N-1 ... most of which means + * nothing unless we control a real RS232 line. + */ + acm->port_line_coding = *value; + } +} + +static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct f_acm *acm = func_to_acm(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + /* composite driver infrastructure handles everything except + * CDC class messages; interface activation uses set_alt(). + */ + switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { + + /* SET_LINE_CODING ... just read and save what the host sends */ + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_REQ_SET_LINE_CODING: + if (w_length != sizeof(struct usb_cdc_line_coding) + || w_index != acm->ctrl_id) + goto invalid; + + value = w_length; + cdev->gadget->ep0->driver_data = acm; + req->complete = acm_complete_set_line_coding; + break; + + /* GET_LINE_CODING ... return what host sent, or initial value */ + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_REQ_GET_LINE_CODING: + if (w_index != acm->ctrl_id) + goto invalid; + + value = min_t(unsigned, w_length, + sizeof(struct usb_cdc_line_coding)); + memcpy(req->buf, &acm->port_line_coding, value); + break; + + /* SET_CONTROL_LINE_STATE ... save what the host sent */ + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_REQ_SET_CONTROL_LINE_STATE: + if (w_index != acm->ctrl_id) + goto invalid; + + value = 0; + + /* FIXME we should not allow data to flow until the + * host sets the RS232_DTR bit; and when it clears + * that bit, we should return to that no-flow state. + */ + acm->port_handshake_bits = w_value; + break; + + default: +invalid: + VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + } + + /* respond with data transfer or status phase? */ + if (value >= 0) { + DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n", + acm->port_num, ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = 0; + req->length = value; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) + ERROR(cdev, "acm response on ttyGS%d, err %d\n", + acm->port_num, value); + } + + /* device either stalls (value < 0) or reports success */ + return value; +} + +static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_acm *acm = func_to_acm(f); + struct usb_composite_dev *cdev = f->config->cdev; + + /* we know alt == 0, so this is an activation or a reset */ + + if (intf == acm->ctrl_id) { + /* REVISIT this may need more work when we start to + * send notifications ... + */ + if (acm->notify->driver_data) { + VDBG(cdev, "reset acm control interface %d\n", intf); + usb_ep_disable(acm->notify); + } else { + VDBG(cdev, "init acm ctrl interface %d\n", intf); + acm->notify_desc = ep_choose(cdev->gadget, + acm->hs.notify, + acm->fs.notify); + } + usb_ep_enable(acm->notify, acm->notify_desc); + acm->notify->driver_data = acm; + + } else if (intf == acm->data_id) { + if (acm->port.in->driver_data) { + DBG(cdev, "reset acm ttyGS%d\n", acm->port_num); + gserial_disconnect(&acm->port); + } else { + DBG(cdev, "activate acm ttyGS%d\n", acm->port_num); + acm->port.in_desc = ep_choose(cdev->gadget, + acm->hs.in, acm->fs.in); + acm->port.out_desc = ep_choose(cdev->gadget, + acm->hs.out, acm->fs.out); + } + gserial_connect(&acm->port, acm->port_num); + + } else + return -EINVAL; + + return 0; +} + +static void acm_disable(struct usb_function *f) +{ + struct f_acm *acm = func_to_acm(f); + struct usb_composite_dev *cdev = f->config->cdev; + + DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num); + gserial_disconnect(&acm->port); + usb_ep_disable(acm->notify); + acm->notify->driver_data = NULL; +} + +/*-------------------------------------------------------------------------*/ + +/* ACM function driver setup/binding */ +static int __init +acm_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_acm *acm = func_to_acm(f); + int status; + struct usb_ep *ep; + + /* allocate instance-specific interface IDs, and patch descriptors */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + acm->ctrl_id = status; + + acm_control_interface_desc.bInterfaceNumber = status; + acm_union_desc .bMasterInterface0 = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + acm->data_id = status; + + acm_data_interface_desc.bInterfaceNumber = status; + acm_union_desc.bSlaveInterface0 = status; + acm_call_mgmt_descriptor.bDataInterface = status; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc); + if (!ep) + goto fail; + acm->port.in = ep; + ep->driver_data = cdev; /* claim */ + + ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc); + if (!ep) + goto fail; + acm->port.out = ep; + ep->driver_data = cdev; /* claim */ + + ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc); + if (!ep) + goto fail; + acm->notify = ep; + ep->driver_data = cdev; /* claim */ + + /* copy descriptors, and track endpoint copies */ + f->descriptors = usb_copy_descriptors(acm_fs_function); + + acm->fs.in = usb_find_endpoint(acm_fs_function, + f->descriptors, &acm_fs_in_desc); + acm->fs.out = usb_find_endpoint(acm_fs_function, + f->descriptors, &acm_fs_out_desc); + acm->fs.notify = usb_find_endpoint(acm_fs_function, + f->descriptors, &acm_fs_notify_desc); + + /* support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + acm_hs_in_desc.bEndpointAddress = + acm_fs_in_desc.bEndpointAddress; + acm_hs_out_desc.bEndpointAddress = + acm_fs_out_desc.bEndpointAddress; + acm_hs_notify_desc.bEndpointAddress = + acm_fs_notify_desc.bEndpointAddress; + + /* copy descriptors, and track endpoint copies */ + f->hs_descriptors = usb_copy_descriptors(acm_hs_function); + + acm->hs.in = usb_find_endpoint(acm_hs_function, + f->hs_descriptors, &acm_hs_in_desc); + acm->hs.out = usb_find_endpoint(acm_hs_function, + f->hs_descriptors, &acm_hs_out_desc); + acm->hs.notify = usb_find_endpoint(acm_hs_function, + f->hs_descriptors, &acm_hs_notify_desc); + } + + /* FIXME provide a callback for triggering notifications */ + + DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n", + acm->port_num, + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + acm->port.in->name, acm->port.out->name, + acm->notify->name); + return 0; + +fail: + /* we might as well release our claims on endpoints */ + if (acm->notify) + acm->notify->driver_data = NULL; + if (acm->port.out) + acm->port.out->driver_data = NULL; + if (acm->port.in) + acm->port.in->driver_data = NULL; + + ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status); + + return status; +} + +static void +acm_unbind(struct usb_configuration *c, struct usb_function *f) +{ + if (gadget_is_dualspeed(c->cdev->gadget)) + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->descriptors); + kfree(func_to_acm(f)); +} + +/* Some controllers can't support CDC ACM ... */ +static inline bool can_support_cdc(struct usb_configuration *c) +{ + /* SH3 doesn't support multiple interfaces */ + if (gadget_is_sh(c->cdev->gadget)) + return false; + + /* sa1100 doesn't have a third interrupt endpoint */ + if (gadget_is_sa1100(c->cdev->gadget)) + return false; + + /* everything else is *probably* fine ... */ + return true; +} + +/** + * acm_bind_config - add a CDC ACM function to a configuration + * @c: the configuration to support the CDC ACM instance + * @port_num: /dev/ttyGS* port this interface will use + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + * + * Caller must have called @gserial_setup() with enough ports to + * handle all the ones it binds. Caller is also responsible + * for calling @gserial_cleanup() before module unload. + */ +int __init acm_bind_config(struct usb_configuration *c, u8 port_num) +{ + struct f_acm *acm; + int status; + + if (!can_support_cdc(c)) + return -EINVAL; + + /* REVISIT might want instance-specific strings to help + * distinguish instances ... + */ + + /* maybe allocate device-global string IDs, and patch descriptors */ + if (acm_string_defs[ACM_CTRL_IDX].id == 0) { + status = usb_string_id(c->cdev); + if (status < 0) + return status; + acm_string_defs[ACM_CTRL_IDX].id = status; + + acm_control_interface_desc.iInterface = status; + + status = usb_string_id(c->cdev); + if (status < 0) + return status; + acm_string_defs[ACM_DATA_IDX].id = status; + + acm_data_interface_desc.iInterface = status; + } + + /* allocate and initialize one new instance */ + acm = kzalloc(sizeof *acm, GFP_KERNEL); + if (!acm) + return -ENOMEM; + + acm->port_num = port_num; + + acm->port.func.name = "acm"; + acm->port.func.strings = acm_strings; + /* descriptors are per-instance copies */ + acm->port.func.bind = acm_bind; + acm->port.func.unbind = acm_unbind; + acm->port.func.set_alt = acm_set_alt; + acm->port.func.setup = acm_setup; + acm->port.func.disable = acm_disable; + + status = usb_add_function(c, &acm->port.func); + if (status) + kfree(acm); + return status; +} diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c new file mode 100644 index 000000000000..0822e9d7693a --- /dev/null +++ b/drivers/usb/gadget/f_ecm.c @@ -0,0 +1,833 @@ +/* + * f_ecm.c -- USB CDC Ethernet (ECM) link function driver + * + * Copyright (C) 2003-2005,2008 David Brownell + * Copyright (C) 2008 Nokia 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define VERBOSE_DEBUG */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/etherdevice.h> + +#include "u_ether.h" + + +/* + * This function is a "CDC Ethernet Networking Control Model" (CDC ECM) + * Ethernet link. The data transfer model is simple (packets sent and + * received over bulk endpoints using normal short packet termination), + * and the control model exposes various data and optional notifications. + * + * ECM is well standardized and (except for Microsoft) supported by most + * operating systems with USB host support. It's the preferred interop + * solution for Ethernet over USB, at least for firmware based solutions. + * (Hardware solutions tend to be more minimalist.) A newer and simpler + * "Ethernet Emulation Model" (CDC EEM) hasn't yet caught on. + * + * Note that ECM requires the use of "alternate settings" for its data + * interface. This means that the set_alt() method has real work to do, + * and also means that a get_alt() method is required. + */ + +struct ecm_ep_descs { + struct usb_endpoint_descriptor *in; + struct usb_endpoint_descriptor *out; + struct usb_endpoint_descriptor *notify; +}; + +enum ecm_notify_state { + ECM_NOTIFY_NONE, /* don't notify */ + ECM_NOTIFY_CONNECT, /* issue CONNECT next */ + ECM_NOTIFY_SPEED, /* issue SPEED_CHANGE next */ +}; + +struct f_ecm { + struct gether port; + u8 ctrl_id, data_id; + + char ethaddr[14]; + + struct usb_descriptor_header **fs_function; + struct ecm_ep_descs fs; + struct usb_descriptor_header **hs_function; + struct ecm_ep_descs hs; + + struct usb_ep *notify; + struct usb_endpoint_descriptor *notify_desc; + struct usb_request *notify_req; + u8 notify_state; + bool is_open; + + /* FIXME is_open needs some irq-ish locking + * ... possibly the same as port.ioport + */ +}; + +static inline struct f_ecm *func_to_ecm(struct usb_function *f) +{ + return container_of(f, struct f_ecm, port.func); +} + +/* peak (theoretical) bulk transfer rate in bits-per-second */ +static inline unsigned bitrate(struct usb_gadget *g) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return 13 * 512 * 8 * 1000 * 8; + else + return 19 * 64 * 1 * 1000 * 8; +} + +/*-------------------------------------------------------------------------*/ + +/* + * Include the status endpoint if we can, even though it's optional. + * + * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one + * packet, to simplify cancellation; and a big transfer interval, to + * waste less bandwidth. + * + * Some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even + * if they ignore the connect/disconnect notifications that real aether + * can provide. More advanced cdc configurations might want to support + * encapsulated commands (vendor-specific, using control-OUT). + */ + +#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ +#define STATUS_BYTECOUNT 16 /* 8 byte header + data */ + + +/* interface descriptor: */ + +static struct usb_interface_descriptor ecm_control_intf __initdata = { + .bLength = sizeof ecm_control_intf, + .bDescriptorType = USB_DT_INTERFACE, + + /* .bInterfaceNumber = DYNAMIC */ + /* status endpoint is optional; this could be patched later */ + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, + .bInterfaceProtocol = USB_CDC_PROTO_NONE, + /* .iInterface = DYNAMIC */ +}; + +static struct usb_cdc_header_desc header_desc __initdata = { + .bLength = sizeof header_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_HEADER_TYPE, + + .bcdCDC = __constant_cpu_to_le16(0x0110), +}; + +static struct usb_cdc_union_desc ecm_union_desc __initdata = { + .bLength = sizeof(ecm_union_desc), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_UNION_TYPE, + /* .bMasterInterface0 = DYNAMIC */ + /* .bSlaveInterface0 = DYNAMIC */ +}; + +static struct usb_cdc_ether_desc ether_desc __initdata = { + .bLength = sizeof ether_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, + + /* this descriptor actually adds value, surprise! */ + /* .iMACAddress = DYNAMIC */ + .bmEthernetStatistics = __constant_cpu_to_le32(0), /* no statistics */ + .wMaxSegmentSize = __constant_cpu_to_le16(ETH_FRAME_LEN), + .wNumberMCFilters = __constant_cpu_to_le16(0), + .bNumberPowerFilters = 0, +}; + +/* the default data interface has no endpoints ... */ + +static struct usb_interface_descriptor ecm_data_nop_intf __initdata = { + .bLength = sizeof ecm_data_nop_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + /* .iInterface = DYNAMIC */ +}; + +/* ... but the "real" data interface has two bulk endpoints */ + +static struct usb_interface_descriptor ecm_data_intf __initdata = { + .bLength = sizeof ecm_data_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 1, + .bAlternateSetting = 1, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + /* .iInterface = DYNAMIC */ +}; + +/* full speed support: */ + +static struct usb_endpoint_descriptor fs_notify_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT), + .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, +}; + +static struct usb_endpoint_descriptor fs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor fs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *eth_fs_function[] __initdata = { + /* CDC ECM control descriptors */ + (struct usb_descriptor_header *) &ecm_control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &ecm_union_desc, + (struct usb_descriptor_header *) ðer_desc, + /* NOTE: status endpoint might need to be removed */ + (struct usb_descriptor_header *) &fs_notify_desc, + /* data interface, altsettings 0 and 1 */ + (struct usb_descriptor_header *) &ecm_data_nop_intf, + (struct usb_descriptor_header *) &ecm_data_intf, + (struct usb_descriptor_header *) &fs_in_desc, + (struct usb_descriptor_header *) &fs_out_desc, + NULL, +}; + +/* high speed support: */ + +static struct usb_endpoint_descriptor hs_notify_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT), + .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, +}; +static struct usb_endpoint_descriptor hs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_descriptor_header *eth_hs_function[] __initdata = { + /* CDC ECM control descriptors */ + (struct usb_descriptor_header *) &ecm_control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &ecm_union_desc, + (struct usb_descriptor_header *) ðer_desc, + /* NOTE: status endpoint might need to be removed */ + (struct usb_descriptor_header *) &hs_notify_desc, + /* data interface, altsettings 0 and 1 */ + (struct usb_descriptor_header *) &ecm_data_nop_intf, + (struct usb_descriptor_header *) &ecm_data_intf, + (struct usb_descriptor_header *) &hs_in_desc, + (struct usb_descriptor_header *) &hs_out_desc, + NULL, +}; + +/* string descriptors: */ + +static struct usb_string ecm_string_defs[] = { + [0].s = "CDC Ethernet Control Model (ECM)", + [1].s = NULL /* DYNAMIC */, + [2].s = "CDC Ethernet Data", + { } /* end of list */ +}; + +static struct usb_gadget_strings ecm_string_table = { + .language = 0x0409, /* en-us */ + .strings = ecm_string_defs, +}; + +static struct usb_gadget_strings *ecm_strings[] = { + &ecm_string_table, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static void ecm_do_notify(struct f_ecm *ecm) +{ + struct usb_request *req = ecm->notify_req; + struct usb_cdc_notification *event; + struct usb_composite_dev *cdev = ecm->port.func.config->cdev; + __le32 *data; + int status; + + /* notification already in flight? */ + if (!req) + return; + + event = req->buf; + switch (ecm->notify_state) { + case ECM_NOTIFY_NONE: + return; + + case ECM_NOTIFY_CONNECT: + event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION; + if (ecm->is_open) + event->wValue = cpu_to_le16(1); + else + event->wValue = cpu_to_le16(0); + event->wLength = 0; + req->length = sizeof *event; + + DBG(cdev, "notify connect %s\n", + ecm->is_open ? "true" : "false"); + ecm->notify_state = ECM_NOTIFY_SPEED; + break; + + case ECM_NOTIFY_SPEED: + event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE; + event->wValue = cpu_to_le16(0); + event->wLength = cpu_to_le16(8); + req->length = STATUS_BYTECOUNT; + + /* SPEED_CHANGE data is up/down speeds in bits/sec */ + data = req->buf + sizeof *event; + data[0] = cpu_to_le32(bitrate(cdev->gadget)); + data[1] = data[0]; + + DBG(cdev, "notify speed %d\n", bitrate(cdev->gadget)); + ecm->notify_state = ECM_NOTIFY_NONE; + break; + } + event->bmRequestType = 0xA1; + event->wIndex = cpu_to_le16(ecm->ctrl_id); + + ecm->notify_req = NULL; + status = usb_ep_queue(ecm->notify, req, GFP_ATOMIC); + if (status < 0) { + ecm->notify_req = req; + DBG(cdev, "notify --> %d\n", status); + } +} + +static void ecm_notify(struct f_ecm *ecm) +{ + /* NOTE on most versions of Linux, host side cdc-ethernet + * won't listen for notifications until its netdevice opens. + * The first notification then sits in the FIFO for a long + * time, and the second one is queued. + */ + ecm->notify_state = ECM_NOTIFY_CONNECT; + ecm_do_notify(ecm); +} + +static void ecm_notify_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_ecm *ecm = req->context; + struct usb_composite_dev *cdev = ecm->port.func.config->cdev; + struct usb_cdc_notification *event = req->buf; + + switch (req->status) { + case 0: + /* no fault */ + break; + case -ECONNRESET: + case -ESHUTDOWN: + ecm->notify_state = ECM_NOTIFY_NONE; + break; + default: + DBG(cdev, "event %02x --> %d\n", + event->bNotificationType, req->status); + break; + } + ecm->notify_req = req; + ecm_do_notify(ecm); +} + +static int ecm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct f_ecm *ecm = func_to_ecm(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + /* composite driver infrastructure handles everything except + * CDC class messages; interface activation uses set_alt(). + */ + switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_SET_ETHERNET_PACKET_FILTER: + /* see 6.2.30: no data, wIndex = interface, + * wValue = packet filter bitmap + */ + if (w_length != 0 || w_index != ecm->ctrl_id) + goto invalid; + DBG(cdev, "packet filter %02x\n", w_value); + /* REVISIT locking of cdc_filter. This assumes the UDC + * driver won't have a concurrent packet TX irq running on + * another CPU; or that if it does, this write is atomic... + */ + ecm->port.cdc_filter = w_value; + value = 0; + break; + + /* and optionally: + * case USB_CDC_SEND_ENCAPSULATED_COMMAND: + * case USB_CDC_GET_ENCAPSULATED_RESPONSE: + * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS: + * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER: + * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER: + * case USB_CDC_GET_ETHERNET_STATISTIC: + */ + + default: +invalid: + DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + } + + /* respond with data transfer or status phase? */ + if (value >= 0) { + DBG(cdev, "ecm req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = 0; + req->length = value; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) + ERROR(cdev, "ecm req %02x.%02x response err %d\n", + ctrl->bRequestType, ctrl->bRequest, + value); + } + + /* device either stalls (value < 0) or reports success */ + return value; +} + + +static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_ecm *ecm = func_to_ecm(f); + struct usb_composite_dev *cdev = f->config->cdev; + + /* Control interface has only altsetting 0 */ + if (intf == ecm->ctrl_id) { + if (alt != 0) + goto fail; + + if (ecm->notify->driver_data) { + VDBG(cdev, "reset ecm control %d\n", intf); + usb_ep_disable(ecm->notify); + } else { + VDBG(cdev, "init ecm ctrl %d\n", intf); + ecm->notify_desc = ep_choose(cdev->gadget, + ecm->hs.notify, + ecm->fs.notify); + } + usb_ep_enable(ecm->notify, ecm->notify_desc); + ecm->notify->driver_data = ecm; + + /* Data interface has two altsettings, 0 and 1 */ + } else if (intf == ecm->data_id) { + if (alt > 1) + goto fail; + + if (ecm->port.in_ep->driver_data) { + DBG(cdev, "reset ecm\n"); + gether_disconnect(&ecm->port); + } + + if (!ecm->port.in) { + DBG(cdev, "init ecm\n"); + ecm->port.in = ep_choose(cdev->gadget, + ecm->hs.in, ecm->fs.in); + ecm->port.out = ep_choose(cdev->gadget, + ecm->hs.out, ecm->fs.out); + } + + /* CDC Ethernet only sends data in non-default altsettings. + * Changing altsettings resets filters, statistics, etc. + */ + if (alt == 1) { + struct net_device *net; + + /* Enable zlps by default for ECM conformance; + * override for musb_hdrc (avoids txdma ovhead) + * and sa1100 (can't). + */ + ecm->port.is_zlp_ok = !( + gadget_is_sa1100(cdev->gadget) + || gadget_is_musbhdrc(cdev->gadget) + ); + ecm->port.cdc_filter = DEFAULT_FILTER; + DBG(cdev, "activate ecm\n"); + net = gether_connect(&ecm->port); + if (IS_ERR(net)) + return PTR_ERR(net); + } + + /* NOTE this can be a minor disagreement with the ECM spec, + * which says speed notifications will "always" follow + * connection notifications. But we allow one connect to + * follow another (if the first is in flight), and instead + * just guarantee that a speed notification is always sent. + */ + ecm_notify(ecm); + } else + goto fail; + + return 0; +fail: + return -EINVAL; +} + +/* Because the data interface supports multiple altsettings, + * this ECM function *MUST* implement a get_alt() method. + */ +static int ecm_get_alt(struct usb_function *f, unsigned intf) +{ + struct f_ecm *ecm = func_to_ecm(f); + + if (intf == ecm->ctrl_id) + return 0; + return ecm->port.in_ep->driver_data ? 1 : 0; +} + +static void ecm_disable(struct usb_function *f) +{ + struct f_ecm *ecm = func_to_ecm(f); + struct usb_composite_dev *cdev = f->config->cdev; + + DBG(cdev, "ecm deactivated\n"); + + if (ecm->port.in_ep->driver_data) + gether_disconnect(&ecm->port); + + if (ecm->notify->driver_data) { + usb_ep_disable(ecm->notify); + ecm->notify->driver_data = NULL; + ecm->notify_desc = NULL; + } +} + +/*-------------------------------------------------------------------------*/ + +/* + * Callbacks let us notify the host about connect/disconnect when the + * net device is opened or closed. + * + * For testing, note that link states on this side include both opened + * and closed variants of: + * + * - disconnected/unconfigured + * - configured but inactive (data alt 0) + * - configured and active (data alt 1) + * + * Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and + * SET_INTERFACE (altsetting). Remember also that "configured" doesn't + * imply the host is actually polling the notification endpoint, and + * likewise that "active" doesn't imply it's actually using the data + * endpoints for traffic. + */ + +static void ecm_open(struct gether *geth) +{ + struct f_ecm *ecm = func_to_ecm(&geth->func); + + DBG(ecm->port.func.config->cdev, "%s\n", __func__); + + ecm->is_open = true; + ecm_notify(ecm); +} + +static void ecm_close(struct gether *geth) +{ + struct f_ecm *ecm = func_to_ecm(&geth->func); + + DBG(ecm->port.func.config->cdev, "%s\n", __func__); + + ecm->is_open = false; + ecm_notify(ecm); +} + +/*-------------------------------------------------------------------------*/ + +/* ethernet function driver setup/binding */ + +static int __init +ecm_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_ecm *ecm = func_to_ecm(f); + int status; + struct usb_ep *ep; + + /* allocate instance-specific interface IDs */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ecm->ctrl_id = status; + + ecm_control_intf.bInterfaceNumber = status; + ecm_union_desc.bMasterInterface0 = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ecm->data_id = status; + + ecm_data_nop_intf.bInterfaceNumber = status; + ecm_data_intf.bInterfaceNumber = status; + ecm_union_desc.bSlaveInterface0 = status; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc); + if (!ep) + goto fail; + ecm->port.in_ep = ep; + ep->driver_data = cdev; /* claim */ + + ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); + if (!ep) + goto fail; + ecm->port.out_ep = ep; + ep->driver_data = cdev; /* claim */ + + /* NOTE: a status/notification endpoint is *OPTIONAL* but we + * don't treat it that way. It's simpler, and some newer CDC + * profiles (wireless handsets) no longer treat it as optional. + */ + ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc); + if (!ep) + goto fail; + ecm->notify = ep; + ep->driver_data = cdev; /* claim */ + + status = -ENOMEM; + + /* allocate notification request and buffer */ + ecm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!ecm->notify_req) + goto fail; + ecm->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL); + if (!ecm->notify_req->buf) + goto fail; + ecm->notify_req->context = ecm; + ecm->notify_req->complete = ecm_notify_complete; + + /* copy descriptors, and track endpoint copies */ + f->descriptors = usb_copy_descriptors(eth_fs_function); + if (!f->descriptors) + goto fail; + + ecm->fs.in = usb_find_endpoint(eth_fs_function, + f->descriptors, &fs_in_desc); + ecm->fs.out = usb_find_endpoint(eth_fs_function, + f->descriptors, &fs_out_desc); + ecm->fs.notify = usb_find_endpoint(eth_fs_function, + f->descriptors, &fs_notify_desc); + + /* support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + hs_in_desc.bEndpointAddress = + fs_in_desc.bEndpointAddress; + hs_out_desc.bEndpointAddress = + fs_out_desc.bEndpointAddress; + hs_notify_desc.bEndpointAddress = + fs_notify_desc.bEndpointAddress; + + /* copy descriptors, and track endpoint copies */ + f->hs_descriptors = usb_copy_descriptors(eth_hs_function); + if (!f->hs_descriptors) + goto fail; + + ecm->hs.in = usb_find_endpoint(eth_hs_function, + f->hs_descriptors, &hs_in_desc); + ecm->hs.out = usb_find_endpoint(eth_hs_function, + f->hs_descriptors, &hs_out_desc); + ecm->hs.notify = usb_find_endpoint(eth_hs_function, + f->hs_descriptors, &hs_notify_desc); + } + + /* NOTE: all that is done without knowing or caring about + * the network link ... which is unavailable to this code + * until we're activated via set_alt(). + */ + + ecm->port.open = ecm_open; + ecm->port.close = ecm_close; + + DBG(cdev, "CDC Ethernet: %s speed IN/%s OUT/%s NOTIFY/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + ecm->port.in_ep->name, ecm->port.out_ep->name, + ecm->notify->name); + return 0; + +fail: + if (f->descriptors) + usb_free_descriptors(f->descriptors); + + if (ecm->notify_req) { + kfree(ecm->notify_req->buf); + usb_ep_free_request(ecm->notify, ecm->notify_req); + } + + /* we might as well release our claims on endpoints */ + if (ecm->notify) + ecm->notify->driver_data = NULL; + if (ecm->port.out) + ecm->port.out_ep->driver_data = NULL; + if (ecm->port.in) + ecm->port.in_ep->driver_data = NULL; + + ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); + + return status; +} + +static void +ecm_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_ecm *ecm = func_to_ecm(f); + + DBG(c->cdev, "ecm unbind\n"); + + if (gadget_is_dualspeed(c->cdev->gadget)) + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->descriptors); + + kfree(ecm->notify_req->buf); + usb_ep_free_request(ecm->notify, ecm->notify_req); + + ecm_string_defs[1].s = NULL; + kfree(ecm); +} + +/** + * ecm_bind_config - add CDC Ethernet network link to a configuration + * @c: the configuration to support the network link + * @ethaddr: a buffer in which the ethernet address of the host side + * side of the link was recorded + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + * + * Caller must have called @gether_setup(). Caller is also responsible + * for calling @gether_cleanup() before module unload. + */ +int __init ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) +{ + struct f_ecm *ecm; + int status; + + if (!can_support_ecm(c->cdev->gadget) || !ethaddr) + return -EINVAL; + + /* maybe allocate device-global string IDs */ + if (ecm_string_defs[0].id == 0) { + + /* control interface label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + ecm_string_defs[0].id = status; + ecm_control_intf.iInterface = status; + + /* data interface label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + ecm_string_defs[2].id = status; + ecm_data_intf.iInterface = status; + + /* MAC address */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + ecm_string_defs[1].id = status; + ether_desc.iMACAddress = status; + } + + /* allocate and initialize one new instance */ + ecm = kzalloc(sizeof *ecm, GFP_KERNEL); + if (!ecm) + return -ENOMEM; + + /* export host's Ethernet address in CDC format */ + snprintf(ecm->ethaddr, sizeof ecm->ethaddr, + "%02X%02X%02X%02X%02X%02X", + ethaddr[0], ethaddr[1], ethaddr[2], + ethaddr[3], ethaddr[4], ethaddr[5]); + ecm_string_defs[1].s = ecm->ethaddr; + + ecm->port.cdc_filter = DEFAULT_FILTER; + + ecm->port.func.name = "cdc_ethernet"; + ecm->port.func.strings = ecm_strings; + /* descriptors are per-instance copies */ + ecm->port.func.bind = ecm_bind; + ecm->port.func.unbind = ecm_unbind; + ecm->port.func.set_alt = ecm_set_alt; + ecm->port.func.get_alt = ecm_get_alt; + ecm->port.func.setup = ecm_setup; + ecm->port.func.disable = ecm_disable; + + status = usb_add_function(c, &ecm->port.func); + if (status) { + ecm_string_defs[1].s = NULL; + kfree(ecm); + } + return status; +} diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c new file mode 100644 index 000000000000..eda4cde72c82 --- /dev/null +++ b/drivers/usb/gadget/f_loopback.c @@ -0,0 +1,381 @@ +/* + * f_loopback.c - USB peripheral loopback configuration driver + * + * Copyright (C) 2003-2008 David Brownell + * Copyright (C) 2008 by Nokia 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define VERBOSE_DEBUG */ + +#include <linux/kernel.h> +#include <linux/utsname.h> +#include <linux/device.h> + +#include "g_zero.h" +#include "gadget_chips.h" + + +/* + * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals, + * + * This takes messages of various sizes written OUT to a device, and loops + * them back so they can be read IN from it. It has been used by certain + * test applications. It supports limited testing of data queueing logic. + * + * + * This is currently packaged as a configuration driver, which can't be + * combined with other functions to make composite devices. However, it + * can be combined with other independent configurations. + */ +struct f_loopback { + struct usb_function function; + + struct usb_ep *in_ep; + struct usb_ep *out_ep; +}; + +static inline struct f_loopback *func_to_loop(struct usb_function *f) +{ + return container_of(f, struct f_loopback, function); +} + +static unsigned qlen = 32; +module_param(qlen, uint, 0); +MODULE_PARM_DESC(qlenn, "depth of loopback queue"); + +/*-------------------------------------------------------------------------*/ + +static struct usb_interface_descriptor loopback_intf = { + .bLength = sizeof loopback_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + /* .iInterface = DYNAMIC */ +}; + +/* full speed support: */ + +static struct usb_endpoint_descriptor fs_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor fs_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *fs_loopback_descs[] = { + (struct usb_descriptor_header *) &loopback_intf, + (struct usb_descriptor_header *) &fs_sink_desc, + (struct usb_descriptor_header *) &fs_source_desc, + NULL, +}; + +/* high speed support: */ + +static struct usb_endpoint_descriptor hs_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_descriptor_header *hs_loopback_descs[] = { + (struct usb_descriptor_header *) &loopback_intf, + (struct usb_descriptor_header *) &hs_source_desc, + (struct usb_descriptor_header *) &hs_sink_desc, + NULL, +}; + +/* function-specific strings: */ + +static struct usb_string strings_loopback[] = { + [0].s = "loop input to output", + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_loop = { + .language = 0x0409, /* en-us */ + .strings = strings_loopback, +}; + +static struct usb_gadget_strings *loopback_strings[] = { + &stringtab_loop, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init +loopback_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_loopback *loop = func_to_loop(f); + int id; + + /* allocate interface ID(s) */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + loopback_intf.bInterfaceNumber = id; + + /* allocate endpoints */ + + loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc); + if (!loop->in_ep) { +autoconf_fail: + ERROR(cdev, "%s: can't autoconfigure on %s\n", + f->name, cdev->gadget->name); + return -ENODEV; + } + loop->in_ep->driver_data = cdev; /* claim */ + + loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc); + if (!loop->out_ep) + goto autoconf_fail; + loop->out_ep->driver_data = cdev; /* claim */ + + /* support high speed hardware */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + hs_source_desc.bEndpointAddress = + fs_source_desc.bEndpointAddress; + hs_sink_desc.bEndpointAddress = + fs_sink_desc.bEndpointAddress; + f->hs_descriptors = hs_loopback_descs; + } + + DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + f->name, loop->in_ep->name, loop->out_ep->name); + return 0; +} + +static void +loopback_unbind(struct usb_configuration *c, struct usb_function *f) +{ + kfree(func_to_loop(f)); +} + +static void loopback_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_loopback *loop = ep->driver_data; + struct usb_composite_dev *cdev = loop->function.config->cdev; + int status = req->status; + + switch (status) { + + case 0: /* normal completion? */ + if (ep == loop->out_ep) { + /* loop this OUT packet back IN to the host */ + req->zero = (req->actual < req->length); + req->length = req->actual; + status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC); + if (status == 0) + return; + + /* "should never get here" */ + ERROR(cdev, "can't loop %s to %s: %d\n", + ep->name, loop->in_ep->name, + status); + } + + /* queue the buffer for some later OUT packet */ + req->length = buflen; + status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC); + if (status == 0) + return; + + /* "should never get here" */ + /* FALLTHROUGH */ + + default: + ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name, + status, req->actual, req->length); + /* FALLTHROUGH */ + + /* NOTE: since this driver doesn't maintain an explicit record + * of requests it submitted (just maintains qlen count), we + * rely on the hardware driver to clean up on disconnect or + * endpoint disable. + */ + case -ECONNABORTED: /* hardware forced ep reset */ + case -ECONNRESET: /* request dequeued */ + case -ESHUTDOWN: /* disconnect from host */ + free_ep_req(ep, req); + return; + } +} + +static void disable_loopback(struct f_loopback *loop) +{ + struct usb_composite_dev *cdev; + + cdev = loop->function.config->cdev; + disable_endpoints(cdev, loop->in_ep, loop->out_ep); + VDBG(cdev, "%s disabled\n", loop->function.name); +} + +static int +enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop) +{ + int result = 0; + const struct usb_endpoint_descriptor *src, *sink; + struct usb_ep *ep; + struct usb_request *req; + unsigned i; + + src = ep_choose(cdev->gadget, &hs_source_desc, &fs_source_desc); + sink = ep_choose(cdev->gadget, &hs_sink_desc, &fs_sink_desc); + + /* one endpoint writes data back IN to the host */ + ep = loop->in_ep; + result = usb_ep_enable(ep, src); + if (result < 0) + return result; + ep->driver_data = loop; + + /* one endpoint just reads OUT packets */ + ep = loop->out_ep; + result = usb_ep_enable(ep, sink); + if (result < 0) { +fail0: + ep = loop->in_ep; + usb_ep_disable(ep); + ep->driver_data = NULL; + return result; + } + ep->driver_data = loop; + + /* allocate a bunch of read buffers and queue them all at once. + * we buffer at most 'qlen' transfers; fewer if any need more + * than 'buflen' bytes each. + */ + for (i = 0; i < qlen && result == 0; i++) { + req = alloc_ep_req(ep); + if (req) { + req->complete = loopback_complete; + result = usb_ep_queue(ep, req, GFP_ATOMIC); + if (result) + ERROR(cdev, "%s queue req --> %d\n", + ep->name, result); + } else { + usb_ep_disable(ep); + ep->driver_data = NULL; + result = -ENOMEM; + goto fail0; + } + } + + DBG(cdev, "%s enabled\n", loop->function.name); + return result; +} + +static int loopback_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct f_loopback *loop = func_to_loop(f); + struct usb_composite_dev *cdev = f->config->cdev; + + /* we know alt is zero */ + if (loop->in_ep->driver_data) + disable_loopback(loop); + return enable_loopback(cdev, loop); +} + +static void loopback_disable(struct usb_function *f) +{ + struct f_loopback *loop = func_to_loop(f); + + disable_loopback(loop); +} + +/*-------------------------------------------------------------------------*/ + +static int __init loopback_bind_config(struct usb_configuration *c) +{ + struct f_loopback *loop; + int status; + + loop = kzalloc(sizeof *loop, GFP_KERNEL); + if (!loop) + return -ENOMEM; + + loop->function.name = "loopback"; + loop->function.descriptors = fs_loopback_descs; + loop->function.bind = loopback_bind; + loop->function.unbind = loopback_unbind; + loop->function.set_alt = loopback_set_alt; + loop->function.disable = loopback_disable; + + status = usb_add_function(c, &loop->function); + if (status) + kfree(loop); + return status; +} + +static struct usb_configuration loopback_driver = { + .label = "loopback", + .strings = loopback_strings, + .bind = loopback_bind_config, + .bConfigurationValue = 2, + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 1, /* 2 mA, minimal */ + /* .iConfiguration = DYNAMIC */ +}; + +/** + * loopback_add - add a loopback testing configuration to a device + * @cdev: the device to support the loopback configuration + */ +int __init loopback_add(struct usb_composite_dev *cdev) +{ + int id; + + /* allocate string ID(s) */ + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_loopback[0].id = id; + + loopback_intf.iInterface = id; + loopback_driver.iConfiguration = id; + + /* support OTG systems */ + if (gadget_is_otg(cdev->gadget)) { + loopback_driver.descriptors = otg_desc; + loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + return usb_add_config(cdev, &loopback_driver); +} diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c new file mode 100644 index 000000000000..61652f0f13fd --- /dev/null +++ b/drivers/usb/gadget/f_rndis.c @@ -0,0 +1,827 @@ +/* + * f_rndis.c -- RNDIS link function driver + * + * Copyright (C) 2003-2005,2008 David Brownell + * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger + * Copyright (C) 2008 Nokia 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define VERBOSE_DEBUG */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/etherdevice.h> + +#include <asm/atomic.h> + +#include "u_ether.h" +#include "rndis.h" + + +/* + * This function is an RNDIS Ethernet port -- a Microsoft protocol that's + * been promoted instead of the standard CDC Ethernet. The published RNDIS + * spec is ambiguous, incomplete, and needlessly complex. Variants such as + * ActiveSync have even worse status in terms of specification. + * + * In short: it's a protocol controlled by (and for) Microsoft, not for an + * Open ecosystem or markets. Linux supports it *only* because Microsoft + * doesn't support the CDC Ethernet standard. + * + * The RNDIS data transfer model is complex, with multiple Ethernet packets + * per USB message, and out of band data. The control model is built around + * what's essentially an "RNDIS RPC" protocol. It's all wrapped in a CDC ACM + * (modem, not Ethernet) veneer, with those ACM descriptors being entirely + * useless (they're ignored). RNDIS expects to be the only function in its + * configuration, so it's no real help if you need composite devices; and + * it expects to be the first configuration too. + * + * There is a single technical advantage of RNDIS over CDC Ethernet, if you + * discount the fluff that its RPC can be made to deliver: it doesn't need + * a NOP altsetting for the data interface. That lets it work on some of the + * "so smart it's stupid" hardware which takes over configuration changes + * from the software, and adds restrictions like "no altsettings". + * + * Unfortunately MSFT's RNDIS drivers are buggy. They hang or oops, and + * have all sorts of contrary-to-specification oddities that can prevent + * them from working sanely. Since bugfixes (or accurate specs, letting + * Linux work around those bugs) are unlikely to ever come from MSFT, you + * may want to avoid using RNDIS on purely operational grounds. + * + * Omissions from the RNDIS 1.0 specification include: + * + * - Power management ... references data that's scattered around lots + * of other documentation, which is incorrect/incomplete there too. + * + * - There are various undocumented protocol requirements, like the need + * to send garbage in some control-OUT messages. + * + * - MS-Windows drivers sometimes emit undocumented requests. + */ + +struct rndis_ep_descs { + struct usb_endpoint_descriptor *in; + struct usb_endpoint_descriptor *out; + struct usb_endpoint_descriptor *notify; +}; + +struct f_rndis { + struct gether port; + u8 ctrl_id, data_id; + u8 ethaddr[ETH_ALEN]; + int config; + + struct usb_descriptor_header **fs_function; + struct rndis_ep_descs fs; + struct usb_descriptor_header **hs_function; + struct rndis_ep_descs hs; + + struct usb_ep *notify; + struct usb_endpoint_descriptor *notify_desc; + struct usb_request *notify_req; + atomic_t notify_count; +}; + +static inline struct f_rndis *func_to_rndis(struct usb_function *f) +{ + return container_of(f, struct f_rndis, port.func); +} + +/* peak (theoretical) bulk transfer rate in bits-per-second */ +static unsigned int bitrate(struct usb_gadget *g) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return 13 * 512 * 8 * 1000 * 8; + else + return 19 * 64 * 1 * 1000 * 8; +} + +/*-------------------------------------------------------------------------*/ + +/* + */ + +#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ +#define STATUS_BYTECOUNT 8 /* 8 bytes data */ + + +/* interface descriptor: */ + +static struct usb_interface_descriptor rndis_control_intf __initdata = { + .bLength = sizeof rndis_control_intf, + .bDescriptorType = USB_DT_INTERFACE, + + /* .bInterfaceNumber = DYNAMIC */ + /* status endpoint is optional; this could be patched later */ + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR, + /* .iInterface = DYNAMIC */ +}; + +static struct usb_cdc_header_desc header_desc __initdata = { + .bLength = sizeof header_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_HEADER_TYPE, + + .bcdCDC = __constant_cpu_to_le16(0x0110), +}; + +static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = { + .bLength = sizeof call_mgmt_descriptor, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, + + .bmCapabilities = 0x00, + .bDataInterface = 0x01, +}; + +static struct usb_cdc_acm_descriptor acm_descriptor __initdata = { + .bLength = sizeof acm_descriptor, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_ACM_TYPE, + + .bmCapabilities = 0x00, +}; + +static struct usb_cdc_union_desc rndis_union_desc __initdata = { + .bLength = sizeof(rndis_union_desc), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_UNION_TYPE, + /* .bMasterInterface0 = DYNAMIC */ + /* .bSlaveInterface0 = DYNAMIC */ +}; + +/* the data interface has two bulk endpoints */ + +static struct usb_interface_descriptor rndis_data_intf __initdata = { + .bLength = sizeof rndis_data_intf, + .bDescriptorType = USB_DT_INTERFACE, + + /* .bInterfaceNumber = DYNAMIC */ + .bAlternateSetting = 1, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + /* .iInterface = DYNAMIC */ +}; + +/* full speed support: */ + +static struct usb_endpoint_descriptor fs_notify_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT), + .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, +}; + +static struct usb_endpoint_descriptor fs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor fs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *eth_fs_function[] __initdata = { + /* control interface matches ACM, not Ethernet */ + (struct usb_descriptor_header *) &rndis_control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &call_mgmt_descriptor, + (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &rndis_union_desc, + (struct usb_descriptor_header *) &fs_notify_desc, + /* data interface has no altsetting */ + (struct usb_descriptor_header *) &rndis_data_intf, + (struct usb_descriptor_header *) &fs_in_desc, + (struct usb_descriptor_header *) &fs_out_desc, + NULL, +}; + +/* high speed support: */ + +static struct usb_endpoint_descriptor hs_notify_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT), + .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, +}; +static struct usb_endpoint_descriptor hs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_descriptor_header *eth_hs_function[] __initdata = { + /* control interface matches ACM, not Ethernet */ + (struct usb_descriptor_header *) &rndis_control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &call_mgmt_descriptor, + (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &rndis_union_desc, + (struct usb_descriptor_header *) &hs_notify_desc, + /* data interface has no altsetting */ + (struct usb_descriptor_header *) &rndis_data_intf, + (struct usb_descriptor_header *) &hs_in_desc, + (struct usb_descriptor_header *) &hs_out_desc, + NULL, +}; + +/* string descriptors: */ + +static struct usb_string rndis_string_defs[] = { + [0].s = "RNDIS Communications Control", + [1].s = "RNDIS Ethernet Data", + { } /* end of list */ +}; + +static struct usb_gadget_strings rndis_string_table = { + .language = 0x0409, /* en-us */ + .strings = rndis_string_defs, +}; + +static struct usb_gadget_strings *rndis_strings[] = { + &rndis_string_table, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static struct sk_buff *rndis_add_header(struct sk_buff *skb) +{ + skb = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type)); + if (skb) + rndis_add_hdr(skb); + return skb; +} + +static void rndis_response_available(void *_rndis) +{ + struct f_rndis *rndis = _rndis; + struct usb_request *req = rndis->notify_req; + struct usb_composite_dev *cdev = rndis->port.func.config->cdev; + __le32 *data = req->buf; + int status; + + if (atomic_inc_return(&rndis->notify_count)) + return; + + /* Send RNDIS RESPONSE_AVAILABLE notification; a + * USB_CDC_NOTIFY_RESPONSE_AVAILABLE "should" work too + * + * This is the only notification defined by RNDIS. + */ + data[0] = cpu_to_le32(1); + data[1] = cpu_to_le32(0); + + status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC); + if (status) { + atomic_dec(&rndis->notify_count); + DBG(cdev, "notify/0 --> %d\n", status); + } +} + +static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_rndis *rndis = req->context; + struct usb_composite_dev *cdev = rndis->port.func.config->cdev; + int status = req->status; + + /* after TX: + * - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control) + * - RNDIS_RESPONSE_AVAILABLE (status/irq) + */ + switch (status) { + case -ECONNRESET: + case -ESHUTDOWN: + /* connection gone */ + atomic_set(&rndis->notify_count, 0); + break; + default: + DBG(cdev, "RNDIS %s response error %d, %d/%d\n", + ep->name, status, + req->actual, req->length); + /* FALLTHROUGH */ + case 0: + if (ep != rndis->notify) + break; + + /* handle multiple pending RNDIS_RESPONSE_AVAILABLE + * notifications by resending until we're done + */ + if (atomic_dec_and_test(&rndis->notify_count)) + break; + status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC); + if (status) { + atomic_dec(&rndis->notify_count); + DBG(cdev, "notify/1 --> %d\n", status); + } + break; + } +} + +static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_rndis *rndis = req->context; + struct usb_composite_dev *cdev = rndis->port.func.config->cdev; + int status; + + /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ +// spin_lock(&dev->lock); + status = rndis_msg_parser(rndis->config, (u8 *) req->buf); + if (status < 0) + ERROR(cdev, "RNDIS command error %d, %d/%d\n", + status, req->actual, req->length); +// spin_unlock(&dev->lock); +} + +static int +rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct f_rndis *rndis = func_to_rndis(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + /* composite driver infrastructure handles everything except + * CDC class messages; interface activation uses set_alt(). + */ + switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { + + /* RNDIS uses the CDC command encapsulation mechanism to implement + * an RPC scheme, with much getting/setting of attributes by OID. + */ + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_SEND_ENCAPSULATED_COMMAND: + if (w_length > req->length || w_value + || w_index != rndis->ctrl_id) + goto invalid; + /* read the request; process it later */ + value = w_length; + req->complete = rndis_command_complete; + req->context = rndis; + /* later, rndis_response_available() sends a notification */ + break; + + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) + | USB_CDC_GET_ENCAPSULATED_RESPONSE: + if (w_value || w_index != rndis->ctrl_id) + goto invalid; + else { + u8 *buf; + u32 n; + + /* return the result */ + buf = rndis_get_next_response(rndis->config, &n); + if (buf) { + memcpy(req->buf, buf, n); + req->complete = rndis_response_complete; + rndis_free_response(rndis->config, buf); + value = n; + } + /* else stalls ... spec says to avoid that */ + } + break; + + default: +invalid: + VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + } + + /* respond with data transfer or status phase? */ + if (value >= 0) { + DBG(cdev, "rndis req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = 0; + req->length = value; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) + ERROR(cdev, "rndis response on err %d\n", value); + } + + /* device either stalls (value < 0) or reports success */ + return value; +} + + +static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_rndis *rndis = func_to_rndis(f); + struct usb_composite_dev *cdev = f->config->cdev; + + /* we know alt == 0 */ + + if (intf == rndis->ctrl_id) { + if (rndis->notify->driver_data) { + VDBG(cdev, "reset rndis control %d\n", intf); + usb_ep_disable(rndis->notify); + } else { + VDBG(cdev, "init rndis ctrl %d\n", intf); + rndis->notify_desc = ep_choose(cdev->gadget, + rndis->hs.notify, + rndis->fs.notify); + } + usb_ep_enable(rndis->notify, rndis->notify_desc); + rndis->notify->driver_data = rndis; + + } else if (intf == rndis->data_id) { + struct net_device *net; + + if (rndis->port.in_ep->driver_data) { + DBG(cdev, "reset rndis\n"); + gether_disconnect(&rndis->port); + } else { + DBG(cdev, "init rndis\n"); + rndis->port.in = ep_choose(cdev->gadget, + rndis->hs.in, rndis->fs.in); + rndis->port.out = ep_choose(cdev->gadget, + rndis->hs.out, rndis->fs.out); + } + + /* Avoid ZLPs; they can be troublesome. */ + rndis->port.is_zlp_ok = false; + + /* RNDIS should be in the "RNDIS uninitialized" state, + * either never activated or after rndis_uninit(). + * + * We don't want data to flow here until a nonzero packet + * filter is set, at which point it enters "RNDIS data + * initialized" state ... but we do want the endpoints + * to be activated. It's a strange little state. + * + * REVISIT the RNDIS gadget code has done this wrong for a + * very long time. We need another call to the link layer + * code -- gether_updown(...bool) maybe -- to do it right. + */ + rndis->port.cdc_filter = 0; + + DBG(cdev, "RNDIS RX/TX early activation ... \n"); + net = gether_connect(&rndis->port); + if (IS_ERR(net)) + return PTR_ERR(net); + + rndis_set_param_dev(rndis->config, net, + &rndis->port.cdc_filter); + } else + goto fail; + + return 0; +fail: + return -EINVAL; +} + +static void rndis_disable(struct usb_function *f) +{ + struct f_rndis *rndis = func_to_rndis(f); + struct usb_composite_dev *cdev = f->config->cdev; + + if (!rndis->notify->driver_data) + return; + + DBG(cdev, "rndis deactivated\n"); + + rndis_uninit(rndis->config); + gether_disconnect(&rndis->port); + + usb_ep_disable(rndis->notify); + rndis->notify->driver_data = NULL; +} + +/*-------------------------------------------------------------------------*/ + +/* + * This isn't quite the same mechanism as CDC Ethernet, since the + * notification scheme passes less data, but the same set of link + * states must be tested. A key difference is that altsettings are + * not used to tell whether the link should send packets or not. + */ + +static void rndis_open(struct gether *geth) +{ + struct f_rndis *rndis = func_to_rndis(&geth->func); + struct usb_composite_dev *cdev = geth->func.config->cdev; + + DBG(cdev, "%s\n", __func__); + + rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, + bitrate(cdev->gadget) / 100); + rndis_signal_connect(rndis->config); +} + +static void rndis_close(struct gether *geth) +{ + struct f_rndis *rndis = func_to_rndis(&geth->func); + + DBG(geth->func.config->cdev, "%s\n", __func__); + + rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0); + rndis_signal_disconnect(rndis->config); +} + +/*-------------------------------------------------------------------------*/ + +/* ethernet function driver setup/binding */ + +static int __init +rndis_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_rndis *rndis = func_to_rndis(f); + int status; + struct usb_ep *ep; + + /* allocate instance-specific interface IDs */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + rndis->ctrl_id = status; + + rndis_control_intf.bInterfaceNumber = status; + rndis_union_desc.bMasterInterface0 = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + rndis->data_id = status; + + rndis_data_intf.bInterfaceNumber = status; + rndis_union_desc.bSlaveInterface0 = status; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc); + if (!ep) + goto fail; + rndis->port.in_ep = ep; + ep->driver_data = cdev; /* claim */ + + ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); + if (!ep) + goto fail; + rndis->port.out_ep = ep; + ep->driver_data = cdev; /* claim */ + + /* NOTE: a status/notification endpoint is, strictly speaking, + * optional. We don't treat it that way though! It's simpler, + * and some newer profiles don't treat it as optional. + */ + ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc); + if (!ep) + goto fail; + rndis->notify = ep; + ep->driver_data = cdev; /* claim */ + + status = -ENOMEM; + + /* allocate notification request and buffer */ + rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!rndis->notify_req) + goto fail; + rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL); + if (!rndis->notify_req->buf) + goto fail; + rndis->notify_req->length = STATUS_BYTECOUNT; + rndis->notify_req->context = rndis; + rndis->notify_req->complete = rndis_response_complete; + + /* copy descriptors, and track endpoint copies */ + f->descriptors = usb_copy_descriptors(eth_fs_function); + if (!f->descriptors) + goto fail; + + rndis->fs.in = usb_find_endpoint(eth_fs_function, + f->descriptors, &fs_in_desc); + rndis->fs.out = usb_find_endpoint(eth_fs_function, + f->descriptors, &fs_out_desc); + rndis->fs.notify = usb_find_endpoint(eth_fs_function, + f->descriptors, &fs_notify_desc); + + /* support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + hs_in_desc.bEndpointAddress = + fs_in_desc.bEndpointAddress; + hs_out_desc.bEndpointAddress = + fs_out_desc.bEndpointAddress; + + /* copy descriptors, and track endpoint copies */ + f->hs_descriptors = usb_copy_descriptors(eth_hs_function); + + if (!f->hs_descriptors) + goto fail; + + rndis->hs.in = usb_find_endpoint(eth_hs_function, + f->hs_descriptors, &hs_in_desc); + rndis->hs.out = usb_find_endpoint(eth_hs_function, + f->hs_descriptors, &hs_out_desc); + } + + rndis->port.open = rndis_open; + rndis->port.close = rndis_close; + + status = rndis_register(rndis_response_available, rndis); + if (status < 0) + goto fail; + rndis->config = status; + + rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0); + rndis_set_host_mac(rndis->config, rndis->ethaddr); + +#if 0 +// FIXME + if (rndis_set_param_vendor(rndis->config, vendorID, + manufacturer)) + goto fail0; +#endif + + /* NOTE: all that is done without knowing or caring about + * the network link ... which is unavailable to this code + * until we're activated via set_alt(). + */ + + DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + rndis->port.in_ep->name, rndis->port.out_ep->name, + rndis->notify->name); + return 0; + +fail: + if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors) + usb_free_descriptors(f->hs_descriptors); + if (f->descriptors) + usb_free_descriptors(f->descriptors); + + if (rndis->notify_req) { + kfree(rndis->notify_req->buf); + usb_ep_free_request(rndis->notify, rndis->notify_req); + } + + /* we might as well release our claims on endpoints */ + if (rndis->notify) + rndis->notify->driver_data = NULL; + if (rndis->port.out) + rndis->port.out_ep->driver_data = NULL; + if (rndis->port.in) + rndis->port.in_ep->driver_data = NULL; + + ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); + + return status; +} + +static void +rndis_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_rndis *rndis = func_to_rndis(f); + + rndis_deregister(rndis->config); + rndis_exit(); + + if (gadget_is_dualspeed(c->cdev->gadget)) + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->descriptors); + + kfree(rndis->notify_req->buf); + usb_ep_free_request(rndis->notify, rndis->notify_req); + + kfree(rndis); +} + +/* Some controllers can't support RNDIS ... */ +static inline bool can_support_rndis(struct usb_configuration *c) +{ + /* only two endpoints on sa1100 */ + if (gadget_is_sa1100(c->cdev->gadget)) + return false; + + /* everything else is *presumably* fine */ + return true; +} + +/** + * rndis_bind_config - add RNDIS network link to a configuration + * @c: the configuration to support the network link + * @ethaddr: a buffer in which the ethernet address of the host side + * side of the link was recorded + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + * + * Caller must have called @gether_setup(). Caller is also responsible + * for calling @gether_cleanup() before module unload. + */ +int __init rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) +{ + struct f_rndis *rndis; + int status; + + if (!can_support_rndis(c) || !ethaddr) + return -EINVAL; + + /* maybe allocate device-global string IDs */ + if (rndis_string_defs[0].id == 0) { + + /* ... and setup RNDIS itself */ + status = rndis_init(); + if (status < 0) + return status; + + /* control interface label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + rndis_string_defs[0].id = status; + rndis_control_intf.iInterface = status; + + /* data interface label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + rndis_string_defs[1].id = status; + rndis_data_intf.iInterface = status; + } + + /* allocate and initialize one new instance */ + status = -ENOMEM; + rndis = kzalloc(sizeof *rndis, GFP_KERNEL); + if (!rndis) + goto fail; + + memcpy(rndis->ethaddr, ethaddr, ETH_ALEN); + + /* RNDIS activates when the host changes this filter */ + rndis->port.cdc_filter = 0; + + /* RNDIS has special (and complex) framing */ + rndis->port.header_len = sizeof(struct rndis_packet_msg_type); + rndis->port.wrap = rndis_add_header; + rndis->port.unwrap = rndis_rm_hdr; + + rndis->port.func.name = "rndis"; + rndis->port.func.strings = rndis_strings; + /* descriptors are per-instance copies */ + rndis->port.func.bind = rndis_bind; + rndis->port.func.unbind = rndis_unbind; + rndis->port.func.set_alt = rndis_set_alt; + rndis->port.func.setup = rndis_setup; + rndis->port.func.disable = rndis_disable; + + status = usb_add_function(c, &rndis->port.func); + if (status) { + kfree(rndis); +fail: + rndis_exit(); + } + return status; +} diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c new file mode 100644 index 000000000000..1b6bde9aaed5 --- /dev/null +++ b/drivers/usb/gadget/f_serial.c @@ -0,0 +1,296 @@ +/* + * f_serial.c - generic USB serial function driver + * + * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) + * Copyright (C) 2008 by David Brownell + * Copyright (C) 2008 by Nokia Corporation + * + * This software is distributed under the terms of the GNU General + * Public License ("GPL") as published by the Free Software Foundation, + * either version 2 of that License or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/device.h> + +#include "u_serial.h" +#include "gadget_chips.h" + + +/* + * This function packages a simple "generic serial" port with no real + * control mechanisms, just raw data transfer over two bulk endpoints. + * + * Because it's not standardized, this isn't as interoperable as the + * CDC ACM driver. However, for many purposes it's just as functional + * if you can arrange appropriate host side drivers. + */ + +struct gser_descs { + struct usb_endpoint_descriptor *in; + struct usb_endpoint_descriptor *out; +}; + +struct f_gser { + struct gserial port; + u8 data_id; + u8 port_num; + + struct usb_descriptor_header **fs_function; + struct gser_descs fs; + struct usb_descriptor_header **hs_function; + struct gser_descs hs; +}; + +static inline struct f_gser *func_to_gser(struct usb_function *f) +{ + return container_of(f, struct f_gser, port.func); +} + +/*-------------------------------------------------------------------------*/ + +/* interface descriptor: */ + +static struct usb_interface_descriptor gser_interface_desc __initdata = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + /* .iInterface = DYNAMIC */ +}; + +/* full speed support: */ + +static struct usb_endpoint_descriptor gser_fs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor gser_fs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *gser_fs_function[] __initdata = { + (struct usb_descriptor_header *) &gser_interface_desc, + (struct usb_descriptor_header *) &gser_fs_in_desc, + (struct usb_descriptor_header *) &gser_fs_out_desc, + NULL, +}; + +/* high speed support: */ + +static struct usb_endpoint_descriptor gser_hs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor gser_hs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_descriptor_header *gser_hs_function[] __initdata = { + (struct usb_descriptor_header *) &gser_interface_desc, + (struct usb_descriptor_header *) &gser_hs_in_desc, + (struct usb_descriptor_header *) &gser_hs_out_desc, + NULL, +}; + +/* string descriptors: */ + +static struct usb_string gser_string_defs[] = { + [0].s = "Generic Serial", + { } /* end of list */ +}; + +static struct usb_gadget_strings gser_string_table = { + .language = 0x0409, /* en-us */ + .strings = gser_string_defs, +}; + +static struct usb_gadget_strings *gser_strings[] = { + &gser_string_table, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_gser *gser = func_to_gser(f); + struct usb_composite_dev *cdev = f->config->cdev; + + /* we know alt == 0, so this is an activation or a reset */ + + if (gser->port.in->driver_data) { + DBG(cdev, "reset generic ttyGS%d\n", gser->port_num); + gserial_disconnect(&gser->port); + } else { + DBG(cdev, "activate generic ttyGS%d\n", gser->port_num); + gser->port.in_desc = ep_choose(cdev->gadget, + gser->hs.in, gser->fs.in); + gser->port.out_desc = ep_choose(cdev->gadget, + gser->hs.out, gser->fs.out); + } + gserial_connect(&gser->port, gser->port_num); + return 0; +} + +static void gser_disable(struct usb_function *f) +{ + struct f_gser *gser = func_to_gser(f); + struct usb_composite_dev *cdev = f->config->cdev; + + DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num); + gserial_disconnect(&gser->port); +} + +/*-------------------------------------------------------------------------*/ + +/* serial function driver setup/binding */ + +static int __init +gser_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_gser *gser = func_to_gser(f); + int status; + struct usb_ep *ep; + + /* allocate instance-specific interface IDs */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + gser->data_id = status; + gser_interface_desc.bInterfaceNumber = status; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_in_desc); + if (!ep) + goto fail; + gser->port.in = ep; + ep->driver_data = cdev; /* claim */ + + ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_out_desc); + if (!ep) + goto fail; + gser->port.out = ep; + ep->driver_data = cdev; /* claim */ + + /* copy descriptors, and track endpoint copies */ + f->descriptors = usb_copy_descriptors(gser_fs_function); + + gser->fs.in = usb_find_endpoint(gser_fs_function, + f->descriptors, &gser_fs_in_desc); + gser->fs.out = usb_find_endpoint(gser_fs_function, + f->descriptors, &gser_fs_out_desc); + + + /* support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + gser_hs_in_desc.bEndpointAddress = + gser_fs_in_desc.bEndpointAddress; + gser_hs_out_desc.bEndpointAddress = + gser_fs_out_desc.bEndpointAddress; + + /* copy descriptors, and track endpoint copies */ + f->hs_descriptors = usb_copy_descriptors(gser_hs_function); + + gser->hs.in = usb_find_endpoint(gser_hs_function, + f->hs_descriptors, &gser_hs_in_desc); + gser->hs.out = usb_find_endpoint(gser_hs_function, + f->hs_descriptors, &gser_hs_out_desc); + } + + DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n", + gser->port_num, + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + gser->port.in->name, gser->port.out->name); + return 0; + +fail: + /* we might as well release our claims on endpoints */ + if (gser->port.out) + gser->port.out->driver_data = NULL; + if (gser->port.in) + gser->port.in->driver_data = NULL; + + ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); + + return status; +} + +static void +gser_unbind(struct usb_configuration *c, struct usb_function *f) +{ + if (gadget_is_dualspeed(c->cdev->gadget)) + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->descriptors); + kfree(func_to_gser(f)); +} + +/** + * gser_bind_config - add a generic serial function to a configuration + * @c: the configuration to support the serial instance + * @port_num: /dev/ttyGS* port this interface will use + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + * + * Caller must have called @gserial_setup() with enough ports to + * handle all the ones it binds. Caller is also responsible + * for calling @gserial_cleanup() before module unload. + */ +int __init gser_bind_config(struct usb_configuration *c, u8 port_num) +{ + struct f_gser *gser; + int status; + + /* REVISIT might want instance-specific strings to help + * distinguish instances ... + */ + + /* maybe allocate device-global string ID */ + if (gser_string_defs[0].id == 0) { + status = usb_string_id(c->cdev); + if (status < 0) + return status; + gser_string_defs[0].id = status; + } + + /* allocate and initialize one new instance */ + gser = kzalloc(sizeof *gser, GFP_KERNEL); + if (!gser) + return -ENOMEM; + + gser->port_num = port_num; + + gser->port.func.name = "gser"; + gser->port.func.strings = gser_strings; + gser->port.func.bind = gser_bind; + gser->port.func.unbind = gser_unbind; + gser->port.func.set_alt = gser_set_alt; + gser->port.func.disable = gser_disable; + + status = usb_add_function(c, &gser->port.func); + if (status) + kfree(gser); + return status; +} diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c new file mode 100644 index 000000000000..f18c3a14d72a --- /dev/null +++ b/drivers/usb/gadget/f_sourcesink.c @@ -0,0 +1,587 @@ +/* + * f_sourcesink.c - USB peripheral source/sink configuration driver + * + * Copyright (C) 2003-2008 David Brownell + * Copyright (C) 2008 by Nokia 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define VERBOSE_DEBUG */ + +#include <linux/kernel.h> +#include <linux/utsname.h> +#include <linux/device.h> + +#include "g_zero.h" +#include "gadget_chips.h" + + +/* + * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral + * controller drivers. + * + * This just sinks bulk packets OUT to the peripheral and sources them IN + * to the host, optionally with specific data patterns for integrity tests. + * As such it supports basic functionality and load tests. + * + * In terms of control messaging, this supports all the standard requests + * plus two that support control-OUT tests. If the optional "autoresume" + * mode is enabled, it provides good functional coverage for the "USBCV" + * test harness from USB-IF. + * + * Note that because this doesn't queue more than one request at a time, + * some other function must be used to test queueing logic. The network + * link (g_ether) is the best overall option for that, since its TX and RX + * queues are relatively independent, will receive a range of packet sizes, + * and can often be made to run out completely. Those issues are important + * when stress testing peripheral controller drivers. + * + * + * This is currently packaged as a configuration driver, which can't be + * combined with other functions to make composite devices. However, it + * can be combined with other independent configurations. + */ +struct f_sourcesink { + struct usb_function function; + + struct usb_ep *in_ep; + struct usb_ep *out_ep; + struct timer_list resume; +}; + +static inline struct f_sourcesink *func_to_ss(struct usb_function *f) +{ + return container_of(f, struct f_sourcesink, function); +} + +static unsigned autoresume; +module_param(autoresume, uint, 0); +MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup"); + +static unsigned pattern; +module_param(pattern, uint, 0); +MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63 "); + +/*-------------------------------------------------------------------------*/ + +static struct usb_interface_descriptor source_sink_intf = { + .bLength = sizeof source_sink_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + /* .iInterface = DYNAMIC */ +}; + +/* full speed support: */ + +static struct usb_endpoint_descriptor fs_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor fs_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *fs_source_sink_descs[] = { + (struct usb_descriptor_header *) &source_sink_intf, + (struct usb_descriptor_header *) &fs_sink_desc, + (struct usb_descriptor_header *) &fs_source_desc, + NULL, +}; + +/* high speed support: */ + +static struct usb_endpoint_descriptor hs_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_descriptor_header *hs_source_sink_descs[] = { + (struct usb_descriptor_header *) &source_sink_intf, + (struct usb_descriptor_header *) &hs_source_desc, + (struct usb_descriptor_header *) &hs_sink_desc, + NULL, +}; + +/* function-specific strings: */ + +static struct usb_string strings_sourcesink[] = { + [0].s = "source and sink data", + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_sourcesink = { + .language = 0x0409, /* en-us */ + .strings = strings_sourcesink, +}; + +static struct usb_gadget_strings *sourcesink_strings[] = { + &stringtab_sourcesink, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static void sourcesink_autoresume(unsigned long _c) +{ + struct usb_composite_dev *cdev = (void *)_c; + struct usb_gadget *g = cdev->gadget; + + /* Normally the host would be woken up for something + * more significant than just a timer firing; likely + * because of some direct user request. + */ + if (g->speed != USB_SPEED_UNKNOWN) { + int status = usb_gadget_wakeup(g); + DBG(cdev, "%s --> %d\n", __func__, status); + } +} + +static int __init +sourcesink_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_sourcesink *ss = func_to_ss(f); + int id; + + /* allocate interface ID(s) */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + source_sink_intf.bInterfaceNumber = id; + + /* allocate endpoints */ + ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc); + if (!ss->in_ep) { +autoconf_fail: + ERROR(cdev, "%s: can't autoconfigure on %s\n", + f->name, cdev->gadget->name); + return -ENODEV; + } + ss->in_ep->driver_data = cdev; /* claim */ + + ss->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc); + if (!ss->out_ep) + goto autoconf_fail; + ss->out_ep->driver_data = cdev; /* claim */ + + setup_timer(&ss->resume, sourcesink_autoresume, + (unsigned long) c->cdev); + + /* support high speed hardware */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + hs_source_desc.bEndpointAddress = + fs_source_desc.bEndpointAddress; + hs_sink_desc.bEndpointAddress = + fs_sink_desc.bEndpointAddress; + f->hs_descriptors = hs_source_sink_descs; + } + + DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + f->name, ss->in_ep->name, ss->out_ep->name); + return 0; +} + +static void +sourcesink_unbind(struct usb_configuration *c, struct usb_function *f) +{ + kfree(func_to_ss(f)); +} + +/* optionally require specific source/sink data patterns */ +static int check_read_data(struct f_sourcesink *ss, struct usb_request *req) +{ + unsigned i; + u8 *buf = req->buf; + struct usb_composite_dev *cdev = ss->function.config->cdev; + + for (i = 0; i < req->actual; i++, buf++) { + switch (pattern) { + + /* all-zeroes has no synchronization issues */ + case 0: + if (*buf == 0) + continue; + break; + + /* "mod63" stays in sync with short-terminated transfers, + * OR otherwise when host and gadget agree on how large + * each usb transfer request should be. Resync is done + * with set_interface or set_config. (We *WANT* it to + * get quickly out of sync if controllers or their drivers + * stutter for any reason, including buffer duplcation...) + */ + case 1: + if (*buf == (u8)(i % 63)) + continue; + break; + } + ERROR(cdev, "bad OUT byte, buf[%d] = %d\n", i, *buf); + usb_ep_set_halt(ss->out_ep); + return -EINVAL; + } + return 0; +} + +static void reinit_write_data(struct usb_ep *ep, struct usb_request *req) +{ + unsigned i; + u8 *buf = req->buf; + + switch (pattern) { + case 0: + memset(req->buf, 0, req->length); + break; + case 1: + for (i = 0; i < req->length; i++) + *buf++ = (u8) (i % 63); + break; + } +} + +static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_sourcesink *ss = ep->driver_data; + struct usb_composite_dev *cdev = ss->function.config->cdev; + int status = req->status; + + switch (status) { + + case 0: /* normal completion? */ + if (ep == ss->out_ep) { + check_read_data(ss, req); + memset(req->buf, 0x55, req->length); + } else + reinit_write_data(ep, req); + break; + + /* this endpoint is normally active while we're configured */ + case -ECONNABORTED: /* hardware forced ep reset */ + case -ECONNRESET: /* request dequeued */ + case -ESHUTDOWN: /* disconnect from host */ + VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status, + req->actual, req->length); + if (ep == ss->out_ep) + check_read_data(ss, req); + free_ep_req(ep, req); + return; + + case -EOVERFLOW: /* buffer overrun on read means that + * we didn't provide a big enough + * buffer. + */ + default: +#if 1 + DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name, + status, req->actual, req->length); +#endif + case -EREMOTEIO: /* short read */ + break; + } + + status = usb_ep_queue(ep, req, GFP_ATOMIC); + if (status) { + ERROR(cdev, "kill %s: resubmit %d bytes --> %d\n", + ep->name, req->length, status); + usb_ep_set_halt(ep); + /* FIXME recover later ... somehow */ + } +} + +static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in) +{ + struct usb_ep *ep; + struct usb_request *req; + int status; + + ep = is_in ? ss->in_ep : ss->out_ep; + req = alloc_ep_req(ep); + if (!req) + return -ENOMEM; + + req->complete = source_sink_complete; + if (is_in) + reinit_write_data(ep, req); + else + memset(req->buf, 0x55, req->length); + + status = usb_ep_queue(ep, req, GFP_ATOMIC); + if (status) { + struct usb_composite_dev *cdev; + + cdev = ss->function.config->cdev; + ERROR(cdev, "start %s %s --> %d\n", + is_in ? "IN" : "OUT", + ep->name, status); + free_ep_req(ep, req); + } + + return status; +} + +static void disable_source_sink(struct f_sourcesink *ss) +{ + struct usb_composite_dev *cdev; + + cdev = ss->function.config->cdev; + disable_endpoints(cdev, ss->in_ep, ss->out_ep); + del_timer(&ss->resume); + VDBG(cdev, "%s disabled\n", ss->function.name); +} + +static int +enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss) +{ + int result = 0; + const struct usb_endpoint_descriptor *src, *sink; + struct usb_ep *ep; + + src = ep_choose(cdev->gadget, &hs_source_desc, &fs_source_desc); + sink = ep_choose(cdev->gadget, &hs_sink_desc, &fs_sink_desc); + + /* one endpoint writes (sources) zeroes IN (to the host) */ + ep = ss->in_ep; + result = usb_ep_enable(ep, src); + if (result < 0) + return result; + ep->driver_data = ss; + + result = source_sink_start_ep(ss, true); + if (result < 0) { +fail: + ep = ss->in_ep; + usb_ep_disable(ep); + ep->driver_data = NULL; + return result; + } + + /* one endpoint reads (sinks) anything OUT (from the host) */ + ep = ss->out_ep; + result = usb_ep_enable(ep, sink); + if (result < 0) + goto fail; + ep->driver_data = ss; + + result = source_sink_start_ep(ss, false); + if (result < 0) { + usb_ep_disable(ep); + ep->driver_data = NULL; + goto fail; + } + + DBG(cdev, "%s enabled\n", ss->function.name); + return result; +} + +static int sourcesink_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct f_sourcesink *ss = func_to_ss(f); + struct usb_composite_dev *cdev = f->config->cdev; + + /* we know alt is zero */ + if (ss->in_ep->driver_data) + disable_source_sink(ss); + return enable_source_sink(cdev, ss); +} + +static void sourcesink_disable(struct usb_function *f) +{ + struct f_sourcesink *ss = func_to_ss(f); + + disable_source_sink(ss); +} + +static void sourcesink_suspend(struct usb_function *f) +{ + struct f_sourcesink *ss = func_to_ss(f); + struct usb_composite_dev *cdev = f->config->cdev; + + if (cdev->gadget->speed == USB_SPEED_UNKNOWN) + return; + + if (autoresume) { + mod_timer(&ss->resume, jiffies + (HZ * autoresume)); + DBG(cdev, "suspend, wakeup in %d seconds\n", autoresume); + } else + DBG(cdev, "%s\n", __func__); +} + +static void sourcesink_resume(struct usb_function *f) +{ + struct f_sourcesink *ss = func_to_ss(f); + struct usb_composite_dev *cdev = f->config->cdev; + + DBG(cdev, "%s\n", __func__); + del_timer(&ss->resume); +} + +/*-------------------------------------------------------------------------*/ + +static int __init sourcesink_bind_config(struct usb_configuration *c) +{ + struct f_sourcesink *ss; + int status; + + ss = kzalloc(sizeof *ss, GFP_KERNEL); + if (!ss) + return -ENOMEM; + + ss->function.name = "source/sink"; + ss->function.descriptors = fs_source_sink_descs; + ss->function.bind = sourcesink_bind; + ss->function.unbind = sourcesink_unbind; + ss->function.set_alt = sourcesink_set_alt; + ss->function.disable = sourcesink_disable; + ss->function.suspend = sourcesink_suspend; + ss->function.resume = sourcesink_resume; + + status = usb_add_function(c, &ss->function); + if (status) + kfree(ss); + return status; +} + +static int sourcesink_setup(struct usb_configuration *c, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_request *req = c->cdev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + /* composite driver infrastructure handles everything except + * the two control test requests. + */ + switch (ctrl->bRequest) { + + /* + * These are the same vendor-specific requests supported by + * Intel's USB 2.0 compliance test devices. We exceed that + * device spec by allowing multiple-packet requests. + * + * NOTE: the Control-OUT data stays in req->buf ... better + * would be copying it into a scratch buffer, so that other + * requests may safely intervene. + */ + case 0x5b: /* control WRITE test -- fill the buffer */ + if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR)) + goto unknown; + if (w_value || w_index) + break; + /* just read that many bytes into the buffer */ + if (w_length > req->length) + break; + value = w_length; + break; + case 0x5c: /* control READ test -- return the buffer */ + if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR)) + goto unknown; + if (w_value || w_index) + break; + /* expect those bytes are still in the buffer; send back */ + if (w_length > req->length) + break; + value = w_length; + break; + + default: +unknown: + VDBG(c->cdev, + "unknown control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + } + + /* respond with data transfer or status phase? */ + if (value >= 0) { + VDBG(c->cdev, "source/sink req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = 0; + req->length = value; + value = usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) + ERROR(c->cdev, "source/sinkc response, err %d\n", + value); + } + + /* device either stalls (value < 0) or reports success */ + return value; +} + +static struct usb_configuration sourcesink_driver = { + .label = "source/sink", + .strings = sourcesink_strings, + .bind = sourcesink_bind_config, + .setup = sourcesink_setup, + .bConfigurationValue = 3, + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 1, /* 2 mA, minimal */ + /* .iConfiguration = DYNAMIC */ +}; + +/** + * sourcesink_add - add a source/sink testing configuration to a device + * @cdev: the device to support the configuration + */ +int __init sourcesink_add(struct usb_composite_dev *cdev) +{ + int id; + + /* allocate string ID(s) */ + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_sourcesink[0].id = id; + + source_sink_intf.iInterface = id; + sourcesink_driver.iConfiguration = id; + + /* support autoresume for remote wakeup testing */ + if (autoresume) + sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + + /* support OTG systems */ + if (gadget_is_otg(cdev->gadget)) { + sourcesink_driver.descriptors = otg_desc; + sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + return usb_add_config(cdev, &sourcesink_driver); +} diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c new file mode 100644 index 000000000000..afeab9a0523f --- /dev/null +++ b/drivers/usb/gadget/f_subset.c @@ -0,0 +1,423 @@ +/* + * f_subset.c -- "CDC Subset" Ethernet link function driver + * + * Copyright (C) 2003-2005,2008 David Brownell + * Copyright (C) 2008 Nokia 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 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/device.h> +#include <linux/etherdevice.h> + +#include "u_ether.h" + + +/* + * This function packages a simple "CDC Subset" Ethernet port with no real + * control mechanisms; just raw data transfer over two bulk endpoints. + * The data transfer model is exactly that of CDC Ethernet, which is + * why we call it the "CDC Subset". + * + * Because it's not standardized, this has some interoperability issues. + * They mostly relate to driver binding, since the data transfer model is + * so simple (CDC Ethernet). The original versions of this protocol used + * specific product/vendor IDs: byteswapped IDs for Digital Equipment's + * SA-1100 "Itsy" board, which could run Linux 2.4 kernels and supported + * daughtercards with USB peripheral connectors. (It was used more often + * with other boards, using the Itsy identifiers.) Linux hosts recognized + * this with CONFIG_USB_ARMLINUX; these devices have only one configuration + * and one interface. + * + * At some point, MCCI defined a (nonconformant) CDC MDLM variant called + * "SAFE", which happens to have a mode which is identical to the "CDC + * Subset" in terms of data transfer and lack of control model. This was + * adopted by later Sharp Zaurus models, and by some other software which + * Linux hosts recognize with CONFIG_USB_NET_ZAURUS. + * + * Because Microsoft's RNDIS drivers are far from robust, we added a few + * descriptors to the CDC Subset code, making this code look like a SAFE + * implementation. This lets you use MCCI's host side MS-Windows drivers + * if you get fed up with RNDIS. It also makes it easier for composite + * drivers to work, since they can use class based binding instead of + * caring about specific product and vendor IDs. + */ + +struct geth_descs { + struct usb_endpoint_descriptor *in; + struct usb_endpoint_descriptor *out; +}; + +struct f_gether { + struct gether port; + + char ethaddr[14]; + + struct usb_descriptor_header **fs_function; + struct geth_descs fs; + struct usb_descriptor_header **hs_function; + struct geth_descs hs; +}; + +static inline struct f_gether *func_to_geth(struct usb_function *f) +{ + return container_of(f, struct f_gether, port.func); +} + +/*-------------------------------------------------------------------------*/ + +/* + * "Simple" CDC-subset option is a simple vendor-neutral model that most + * full speed controllers can handle: one interface, two bulk endpoints. + * To assist host side drivers, we fancy it up a bit, and add descriptors so + * some host side drivers will understand it as a "SAFE" variant. + * + * "SAFE" loosely follows CDC WMC MDLM, violating the spec in various ways. + * Data endpoints live in the control interface, there's no data interface. + * And it's not used to talk to a cell phone radio. + */ + +/* interface descriptor: */ + +static struct usb_interface_descriptor subset_data_intf __initdata = { + .bLength = sizeof subset_data_intf, + .bDescriptorType = USB_DT_INTERFACE, + + /* .bInterfaceNumber = DYNAMIC */ + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_MDLM, + .bInterfaceProtocol = 0, + /* .iInterface = DYNAMIC */ +}; + +static struct usb_cdc_header_desc header_desc __initdata = { + .bLength = sizeof header_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_HEADER_TYPE, + + .bcdCDC = __constant_cpu_to_le16(0x0110), +}; + +static struct usb_cdc_mdlm_desc mdlm_desc __initdata = { + .bLength = sizeof mdlm_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_MDLM_TYPE, + + .bcdVersion = __constant_cpu_to_le16(0x0100), + .bGUID = { + 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6, + 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f, + }, +}; + +/* since "usb_cdc_mdlm_detail_desc" is a variable length structure, we + * can't really use its struct. All we do here is say that we're using + * the submode of "SAFE" which directly matches the CDC Subset. + */ +static u8 mdlm_detail_desc[] __initdata = { + 6, + USB_DT_CS_INTERFACE, + USB_CDC_MDLM_DETAIL_TYPE, + + 0, /* "SAFE" */ + 0, /* network control capabilities (none) */ + 0, /* network data capabilities ("raw" encapsulation) */ +}; + +static struct usb_cdc_ether_desc ether_desc __initdata = { + .bLength = sizeof ether_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, + + /* this descriptor actually adds value, surprise! */ + /* .iMACAddress = DYNAMIC */ + .bmEthernetStatistics = __constant_cpu_to_le32(0), /* no statistics */ + .wMaxSegmentSize = __constant_cpu_to_le16(ETH_FRAME_LEN), + .wNumberMCFilters = __constant_cpu_to_le16(0), + .bNumberPowerFilters = 0, +}; + +/* full speed support: */ + +static struct usb_endpoint_descriptor fs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor fs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *fs_eth_function[] __initdata = { + (struct usb_descriptor_header *) &subset_data_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &mdlm_desc, + (struct usb_descriptor_header *) &mdlm_detail_desc, + (struct usb_descriptor_header *) ðer_desc, + (struct usb_descriptor_header *) &fs_in_desc, + (struct usb_descriptor_header *) &fs_out_desc, + NULL, +}; + +/* high speed support: */ + +static struct usb_endpoint_descriptor hs_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_out_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_descriptor_header *hs_eth_function[] __initdata = { + (struct usb_descriptor_header *) &subset_data_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &mdlm_desc, + (struct usb_descriptor_header *) &mdlm_detail_desc, + (struct usb_descriptor_header *) ðer_desc, + (struct usb_descriptor_header *) &hs_in_desc, + (struct usb_descriptor_header *) &hs_out_desc, + NULL, +}; + +/* string descriptors: */ + +static struct usb_string geth_string_defs[] = { + [0].s = "CDC Ethernet Subset/SAFE", + [1].s = NULL /* DYNAMIC */, + { } /* end of list */ +}; + +static struct usb_gadget_strings geth_string_table = { + .language = 0x0409, /* en-us */ + .strings = geth_string_defs, +}; + +static struct usb_gadget_strings *geth_strings[] = { + &geth_string_table, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_gether *geth = func_to_geth(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct net_device *net; + + /* we know alt == 0, so this is an activation or a reset */ + + if (geth->port.in_ep->driver_data) { + DBG(cdev, "reset cdc subset\n"); + gether_disconnect(&geth->port); + } + + DBG(cdev, "init + activate cdc subset\n"); + geth->port.in = ep_choose(cdev->gadget, + geth->hs.in, geth->fs.in); + geth->port.out = ep_choose(cdev->gadget, + geth->hs.out, geth->fs.out); + + net = gether_connect(&geth->port); + return IS_ERR(net) ? PTR_ERR(net) : 0; +} + +static void geth_disable(struct usb_function *f) +{ + struct f_gether *geth = func_to_geth(f); + struct usb_composite_dev *cdev = f->config->cdev; + + DBG(cdev, "net deactivated\n"); + gether_disconnect(&geth->port); +} + +/*-------------------------------------------------------------------------*/ + +/* serial function driver setup/binding */ + +static int __init +geth_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_gether *geth = func_to_geth(f); + int status; + struct usb_ep *ep; + + /* allocate instance-specific interface IDs */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + subset_data_intf.bInterfaceNumber = status; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc); + if (!ep) + goto fail; + geth->port.in_ep = ep; + ep->driver_data = cdev; /* claim */ + + ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); + if (!ep) + goto fail; + geth->port.out_ep = ep; + ep->driver_data = cdev; /* claim */ + + /* copy descriptors, and track endpoint copies */ + f->descriptors = usb_copy_descriptors(fs_eth_function); + + geth->fs.in = usb_find_endpoint(fs_eth_function, + f->descriptors, &fs_in_desc); + geth->fs.out = usb_find_endpoint(fs_eth_function, + f->descriptors, &fs_out_desc); + + + /* support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + hs_in_desc.bEndpointAddress = + fs_in_desc.bEndpointAddress; + hs_out_desc.bEndpointAddress = + fs_out_desc.bEndpointAddress; + + /* copy descriptors, and track endpoint copies */ + f->hs_descriptors = usb_copy_descriptors(hs_eth_function); + + geth->hs.in = usb_find_endpoint(hs_eth_function, + f->hs_descriptors, &hs_in_desc); + geth->hs.out = usb_find_endpoint(hs_eth_function, + f->hs_descriptors, &hs_out_desc); + } + + /* NOTE: all that is done without knowing or caring about + * the network link ... which is unavailable to this code + * until we're activated via set_alt(). + */ + + DBG(cdev, "CDC Subset: %s speed IN/%s OUT/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + geth->port.in_ep->name, geth->port.out_ep->name); + return 0; + +fail: + /* we might as well release our claims on endpoints */ + if (geth->port.out) + geth->port.out_ep->driver_data = NULL; + if (geth->port.in) + geth->port.in_ep->driver_data = NULL; + + ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); + + return status; +} + +static void +geth_unbind(struct usb_configuration *c, struct usb_function *f) +{ + if (gadget_is_dualspeed(c->cdev->gadget)) + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->descriptors); + geth_string_defs[1].s = NULL; + kfree(func_to_geth(f)); +} + +/** + * geth_bind_config - add CDC Subset network link to a configuration + * @c: the configuration to support the network link + * @ethaddr: a buffer in which the ethernet address of the host side + * side of the link was recorded + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + * + * Caller must have called @gether_setup(). Caller is also responsible + * for calling @gether_cleanup() before module unload. + */ +int __init geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) +{ + struct f_gether *geth; + int status; + + if (!ethaddr) + return -EINVAL; + + /* maybe allocate device-global string IDs */ + if (geth_string_defs[0].id == 0) { + + /* interface label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + geth_string_defs[0].id = status; + subset_data_intf.iInterface = status; + + /* MAC address */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + geth_string_defs[1].id = status; + ether_desc.iMACAddress = status; + } + + /* allocate and initialize one new instance */ + geth = kzalloc(sizeof *geth, GFP_KERNEL); + if (!geth) + return -ENOMEM; + + /* export host's Ethernet address in CDC format */ + snprintf(geth->ethaddr, sizeof geth->ethaddr, + "%02X%02X%02X%02X%02X%02X", + ethaddr[0], ethaddr[1], ethaddr[2], + ethaddr[3], ethaddr[4], ethaddr[5]); + geth_string_defs[1].s = geth->ethaddr; + + geth->port.cdc_filter = DEFAULT_FILTER; + + geth->port.func.name = "cdc_subset"; + geth->port.func.strings = geth_strings; + geth->port.func.bind = geth_bind; + geth->port.func.unbind = geth_unbind; + geth->port.func.set_alt = geth_set_alt; + geth->port.func.disable = geth_disable; + + status = usb_add_function(c, &geth->port.func); + if (status) { + geth_string_defs[1].s = NULL; + kfree(geth); + } + return status; +} diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 47bb9f09a1aa..ea2c31d18080 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -308,7 +308,7 @@ MODULE_LICENSE("Dual BSD/GPL"); dev_vdbg(&(d)->gadget->dev , fmt , ## args) #define ERROR(d, fmt, args...) \ dev_err(&(d)->gadget->dev , fmt , ## args) -#define WARN(d, fmt, args...) \ +#define WARNING(d, fmt, args...) \ dev_warn(&(d)->gadget->dev , fmt , ## args) #define INFO(d, fmt, args...) \ dev_info(&(d)->gadget->dev , fmt , ## args) @@ -1091,7 +1091,7 @@ static int ep0_queue(struct fsg_dev *fsg) if (rc != 0 && rc != -ESHUTDOWN) { /* We can't do much more than wait for a reset */ - WARN(fsg, "error in submission: %s --> %d\n", + WARNING(fsg, "error in submission: %s --> %d\n", fsg->ep0->name, rc); } return rc; @@ -1227,7 +1227,7 @@ static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh) /* Save the command for later */ if (fsg->cbbuf_cmnd_size) - WARN(fsg, "CB[I] overwriting previous command\n"); + WARNING(fsg, "CB[I] overwriting previous command\n"); fsg->cbbuf_cmnd_size = req->actual; memcpy(fsg->cbbuf_cmnd, req->buf, fsg->cbbuf_cmnd_size); @@ -1506,7 +1506,7 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, * submissions if DMA is enabled. */ if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && req->length == 0)) - WARN(fsg, "error in submission: %s --> %d\n", + WARNING(fsg, "error in submission: %s --> %d\n", ep->name, rc); } } @@ -2294,7 +2294,7 @@ static int halt_bulk_in_endpoint(struct fsg_dev *fsg) VDBG(fsg, "delayed bulk-in endpoint halt\n"); while (rc != 0) { if (rc != -EAGAIN) { - WARN(fsg, "usb_ep_set_halt -> %d\n", rc); + WARNING(fsg, "usb_ep_set_halt -> %d\n", rc); rc = 0; break; } @@ -2317,7 +2317,7 @@ static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) VDBG(fsg, "delayed bulk-in endpoint wedge\n"); while (rc != 0) { if (rc != -EAGAIN) { - WARN(fsg, "usb_ep_set_wedge -> %d\n", rc); + WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc); rc = 0; break; } @@ -3755,7 +3755,7 @@ static int __init check_parameters(struct fsg_dev *fsg) if (gcnum >= 0) mod_data.release = 0x0300 + gcnum; else { - WARN(fsg, "controller '%s' not recognized\n", + WARNING(fsg, "controller '%s' not recognized\n", fsg->gadget->name); mod_data.release = 0x0399; } @@ -3867,8 +3867,8 @@ static int __init fsg_bind(struct usb_gadget *gadget) curlun->dev.parent = &gadget->dev; curlun->dev.driver = &fsg_driver.driver; dev_set_drvdata(&curlun->dev, fsg); - snprintf(curlun->dev.bus_id, BUS_ID_SIZE, - "%s-lun%d", gadget->dev.bus_id, i); + dev_set_name(&curlun->dev,"%s-lun%d", + dev_name(&gadget->dev), i); if ((rc = device_register(&curlun->dev)) != 0) { INFO(fsg, "failed to register LUN%d: %d\n", i, rc); diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_usb2_udc.c index 18687543d7fa..1cfccf102a2d 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.c +++ b/drivers/usb/gadget/fsl_usb2_udc.c @@ -1538,7 +1538,7 @@ static void dtd_complete_irq(struct fsl_udc *udc) /* If the ep is configured */ if (curr_ep->name == NULL) { - WARN("Invalid EP?"); + WARNING("Invalid EP?"); continue; } @@ -2331,7 +2331,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) udc_controller->gadget.name = driver_name; /* Setup gadget.dev and register with kernel */ - strcpy(udc_controller->gadget.dev.bus_id, "gadget"); + dev_set_name(&udc_controller->gadget.dev, "gadget"); udc_controller->gadget.dev.release = fsl_udc_release; udc_controller->gadget.dev.parent = &pdev->dev; ret = device_register(&udc_controller->gadget.dev); diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h index 98b1483ef6a5..6131752a38bc 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/fsl_usb2_udc.h @@ -552,7 +552,7 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length) #endif #define ERR(stuff...) pr_err("udc: " stuff) -#define WARN(stuff...) pr_warning("udc: " stuff) +#define WARNING(stuff...) pr_warning("udc: " stuff) #define INFO(stuff...) pr_info("udc: " stuff) /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/g_zero.h new file mode 100644 index 000000000000..dd2f16ad5a88 --- /dev/null +++ b/drivers/usb/gadget/g_zero.h @@ -0,0 +1,25 @@ +/* + * This header declares the utility functions used by "Gadget Zero", plus + * interfaces to its two single-configuration function drivers. + */ + +#ifndef __G_ZERO_H +#define __G_ZERO_H + +#include <linux/usb/composite.h> + +/* global state */ +extern unsigned buflen; +extern const struct usb_descriptor_header *otg_desc[]; + +/* common utilities */ +struct usb_request *alloc_ep_req(struct usb_ep *ep); +void free_ep_req(struct usb_ep *ep, struct usb_request *req); +void disable_endpoints(struct usb_composite_dev *cdev, + struct usb_ep *in, struct usb_ep *out); + +/* configuration-specific linkup */ +int sourcesink_add(struct usb_composite_dev *cdev); +int loopback_add(struct usb_composite_dev *cdev); + +#endif /* __G_ZERO_H */ diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index ca5149ea7312..5246e8fef2b2 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -214,3 +214,26 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x21; return -ENOENT; } + + +/** + * gadget_supports_altsettings - return true if altsettings work + * @gadget: the gadget in question + */ +static inline bool gadget_supports_altsettings(struct usb_gadget *gadget) +{ + /* PXA 21x/25x/26x has no altsettings at all */ + if (gadget_is_pxa(gadget)) + return false; + + /* PXA 27x and 3xx have *broken* altsetting support */ + if (gadget_is_pxa27x(gadget)) + return false; + + /* SH3 hardware just doesn't do altsettings */ + if (gadget_is_sh(gadget)) + return false; + + /* Everything else is *presumably* fine ... */ + return true; +} diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index 7f4d4828e3aa..ea8651e3da1a 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -138,8 +138,6 @@ static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req); dev_vdbg(&(d)->gadget->dev , fmt , ## args) #define ERROR(d, fmt, args...) \ dev_err(&(d)->gadget->dev , fmt , ## args) -#define WARN(d, fmt, args...) \ - dev_warn(&(d)->gadget->dev , fmt , ## args) #define INFO(d, fmt, args...) \ dev_info(&(d)->gadget->dev , fmt , ## args) diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index be6613afedbf..60aa04847b18 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -1768,7 +1768,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) * usb_gadget_driver_{register,unregister}() must change. */ if (the_controller) { - WARN(dev, "ignoring %s\n", pci_name(pdev)); + WARNING(dev, "ignoring %s\n", pci_name(pdev)); return -EBUSY; } if (!pdev->irq) { @@ -1790,7 +1790,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev->gadget.ops = &goku_ops; /* the "gadget" abstracts/virtualizes the controller */ - strcpy(dev->gadget.dev.bus_id, "gadget"); + dev_set_name(&dev->gadget.dev, "gadget"); dev->gadget.dev.parent = &pdev->dev; dev->gadget.dev.dma_mask = pdev->dev.dma_mask; dev->gadget.dev.release = gadget_release; diff --git a/drivers/usb/gadget/goku_udc.h b/drivers/usb/gadget/goku_udc.h index bc4eb1e0b507..566cb2319056 100644 --- a/drivers/usb/gadget/goku_udc.h +++ b/drivers/usb/gadget/goku_udc.h @@ -285,7 +285,7 @@ struct goku_udc { #define ERROR(dev,fmt,args...) \ xprintk(dev , KERN_ERR , fmt , ## args) -#define WARN(dev,fmt,args...) \ +#define WARNING(dev,fmt,args...) \ xprintk(dev , KERN_WARNING , fmt , ## args) #define INFO(dev,fmt,args...) \ xprintk(dev , KERN_INFO , fmt , ## args) diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index f132a9219e11..f4585d3e90d7 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -32,6 +32,7 @@ #include <asm/uaccess.h> #include <linux/slab.h> #include <linux/poll.h> +#include <linux/smp_lock.h> #include <linux/device.h> #include <linux/moduleparam.h> @@ -261,8 +262,6 @@ static const char *CHIP; #define ERROR(dev,fmt,args...) \ xprintk(dev , KERN_ERR , fmt , ## args) -#define WARN(dev,fmt,args...) \ - xprintk(dev , KERN_WARNING , fmt , ## args) #define INFO(dev,fmt,args...) \ xprintk(dev , KERN_INFO , fmt , ## args) @@ -483,8 +482,7 @@ ep_release (struct inode *inode, struct file *fd) return 0; } -static int ep_ioctl (struct inode *inode, struct file *fd, - unsigned code, unsigned long value) +static long ep_ioctl(struct file *fd, unsigned code, unsigned long value) { struct ep_data *data = fd->private_data; int status; @@ -740,7 +738,7 @@ static const struct file_operations ep_io_operations = { .read = ep_read, .write = ep_write, - .ioctl = ep_ioctl, + .unlocked_ioctl = ep_ioctl, .release = ep_release, .aio_read = ep_aio_read, @@ -1294,15 +1292,18 @@ out: return mask; } -static int dev_ioctl (struct inode *inode, struct file *fd, - unsigned code, unsigned long value) +static long dev_ioctl (struct file *fd, unsigned code, unsigned long value) { struct dev_data *dev = fd->private_data; struct usb_gadget *gadget = dev->gadget; + long ret = -ENOTTY; - if (gadget->ops->ioctl) - return gadget->ops->ioctl (gadget, code, value); - return -ENOTTY; + if (gadget->ops->ioctl) { + lock_kernel(); + ret = gadget->ops->ioctl (gadget, code, value); + unlock_kernel(); + } + return ret; } /* used after device configuration */ @@ -1314,7 +1315,7 @@ static const struct file_operations ep0_io_operations = { .write = ep0_write, .fasync = ep0_fasync, .poll = ep0_poll, - .ioctl = dev_ioctl, + .unlocked_ioctl = dev_ioctl, .release = dev_release, }; @@ -1964,7 +1965,7 @@ static const struct file_operations dev_init_operations = { .open = dev_open, .write = dev_config, .fasync = ep0_fasync, - .ioctl = dev_ioctl, + .unlocked_ioctl = dev_ioctl, .release = dev_release, }; diff --git a/drivers/usb/gadget/lh7a40x_udc.c b/drivers/usb/gadget/lh7a40x_udc.c index 825abd2621b3..c6e7df04c69a 100644 --- a/drivers/usb/gadget/lh7a40x_udc.c +++ b/drivers/usb/gadget/lh7a40x_udc.c @@ -1970,7 +1970,7 @@ static const struct usb_gadget_ops lh7a40x_udc_ops = { static void nop_release(struct device *dev) { - DEBUG("%s %s\n", __func__, dev->bus_id); + DEBUG("%s %s\n", __func__, dev_name(dev)); } static struct lh7a40x_udc memory = { diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index ee6b35fa870f..8da7535c0c70 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -1593,7 +1593,7 @@ static int __init m66592_probe(struct platform_device *pdev) m66592->gadget.ops = &m66592_gadget_ops; device_initialize(&m66592->gadget.dev); - strcpy(m66592->gadget.dev.bus_id, "gadget"); + dev_set_name(&m66592->gadget, "gadget"); m66592->gadget.is_dualspeed = 1; m66592->gadget.dev.parent = &pdev->dev; m66592->gadget.dev.dma_mask = pdev->dev.dma_mask; diff --git a/drivers/usb/gadget/ndis.h b/drivers/usb/gadget/ndis.h index 09e3ee4eeae1..df886cec5ef4 100644 --- a/drivers/usb/gadget/ndis.h +++ b/drivers/usb/gadget/ndis.h @@ -1,11 +1,11 @@ /* - * ndis.h - * + * ndis.h + * * ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de> - * - * Thanks to the cygwin development team, + * + * Thanks to the cygwin development team, * espacially to Casper S. Hornstrup <chorns@users.sourceforge.net> - * + * * THIS SOFTWARE IS NOT COPYRIGHTED * * This source code is offered for use in the public domain. You may diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index e01862300169..5cfb5ebf3881 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -1007,7 +1007,7 @@ static void scan_dma_completions (struct net2280_ep *ep) * 0122, and 0124; not all cases trigger the warning. */ if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) { - WARN (ep->dev, "%s lost packet sync!\n", + WARNING (ep->dev, "%s lost packet sync!\n", ep->ep.name); req->req.status = -EOVERFLOW; } else if ((tmp = readl (&ep->regs->ep_avail)) != 0) { @@ -2768,7 +2768,7 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) dev->gadget.is_dualspeed = 1; /* the "gadget" abstracts/virtualizes the controller */ - strcpy (dev->gadget.dev.bus_id, "gadget"); + dev_set_name(&dev->gadget.dev, "gadget"); dev->gadget.dev.parent = &pdev->dev; dev->gadget.dev.dma_mask = pdev->dev.dma_mask; dev->gadget.dev.release = gadget_release; diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h index 1f2af398a9a4..81a71dbdc2c6 100644 --- a/drivers/usb/gadget/net2280.h +++ b/drivers/usb/gadget/net2280.h @@ -272,7 +272,7 @@ static inline void net2280_led_shutdown (struct net2280 *dev) #define ERROR(dev,fmt,args...) \ xprintk(dev , KERN_ERR , fmt , ## args) -#define WARN(dev,fmt,args...) \ +#define WARNING(dev,fmt,args...) \ xprintk(dev , KERN_WARNING , fmt , ## args) #define INFO(dev,fmt,args...) \ xprintk(dev , KERN_INFO , fmt , ## args) diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 03a7f49d207d..395bd1844482 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -1120,7 +1120,7 @@ static int omap_ep_set_halt(struct usb_ep *_ep, int value) status = -EINVAL; else if (value) { if (ep->udc->ep0_set_config) { - WARN("error changing config?\n"); + WARNING("error changing config?\n"); omap_writew(UDC_CLR_CFG, UDC_SYSCON2); } omap_writew(UDC_STALL_CMD, UDC_SYSCON2); @@ -1764,7 +1764,7 @@ do_stall: u.r.bRequestType, u.r.bRequest, status); if (udc->ep0_set_config) { if (udc->ep0_reset_config) - WARN("error resetting config?\n"); + WARNING("error resetting config?\n"); else omap_writew(UDC_CLR_CFG, UDC_SYSCON2); } @@ -2686,7 +2686,7 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv) udc->gadget.name = driver_name; device_initialize(&udc->gadget.dev); - strcpy (udc->gadget.dev.bus_id, "gadget"); + dev_set_name(&udc->gadget.dev, "gadget"); udc->gadget.dev.release = omap_udc_release; udc->gadget.dev.parent = &odev->dev; if (use_dma) @@ -3076,7 +3076,7 @@ static int omap_udc_suspend(struct platform_device *dev, pm_message_t message) * which would prevent entry to deep sleep... */ if ((devstat & UDC_ATT) != 0 && (devstat & UDC_SUS) == 0) { - WARN("session active; suspend requires disconnect\n"); + WARNING("session active; suspend requires disconnect\n"); omap_pullup(&udc->gadget, 0); } diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/omap_udc.h index 8522bbb12278..29edc51b6b22 100644 --- a/drivers/usb/gadget/omap_udc.h +++ b/drivers/usb/gadget/omap_udc.h @@ -188,7 +188,7 @@ struct omap_udc { #endif #define ERR(stuff...) pr_err("udc: " stuff) -#define WARN(stuff...) pr_warning("udc: " stuff) +#define WARNING(stuff...) pr_warning("udc: " stuff) #define INFO(stuff...) pr_info("udc: " stuff) #define DBG(stuff...) pr_debug("udc: " stuff) diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index ec8f2eb041ca..e0090085b78e 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -179,7 +179,7 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR); #define ERROR(dev, fmt, args...) \ xprintk(dev, KERN_ERR, fmt, ## args) -#define WARN(dev, fmt, args...) \ +#define WARNING(dev, fmt, args...) \ xprintk(dev, KERN_WARNING, fmt, ## args) #define INFO(dev, fmt, args...) \ xprintk(dev, KERN_INFO, fmt, ## args) @@ -828,9 +828,8 @@ printer_poll(struct file *fd, poll_table *wait) return status; } -static int -printer_ioctl(struct inode *inode, struct file *fd, unsigned int code, - unsigned long arg) +static long +printer_ioctl(struct file *fd, unsigned int code, unsigned long arg) { struct printer_dev *dev = fd->private_data; unsigned long flags; @@ -869,7 +868,7 @@ static struct file_operations printer_io_operations = { .write = printer_write, .fsync = printer_fsync, .poll = printer_poll, - .ioctl = printer_ioctl, + .unlocked_ioctl = printer_ioctl, .release = printer_close }; @@ -1361,8 +1360,8 @@ printer_bind(struct usb_gadget *gadget) /* Setup the sysfs files for the printer gadget. */ - dev->pdev = device_create(usb_gadget_class, NULL, g_printer_devno, - "g_printer"); + dev->pdev = device_create_drvdata(usb_gadget_class, NULL, + g_printer_devno, NULL, "g_printer"); if (IS_ERR(dev->pdev)) { ERROR(dev, "Failed to create device: g_printer\n"); goto fail; diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 031dceb93023..7e6725d89976 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -152,9 +152,10 @@ static int is_vbus_present(void) static void pullup_off(void) { struct pxa2xx_udc_mach_info *mach = the_controller->mach; + int off_level = mach->gpio_pullup_inverted; if (mach->gpio_pullup) - gpio_set_value(mach->gpio_pullup, 0); + gpio_set_value(mach->gpio_pullup, off_level); else if (mach->udc_command) mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); } @@ -162,9 +163,10 @@ static void pullup_off(void) static void pullup_on(void) { struct pxa2xx_udc_mach_info *mach = the_controller->mach; + int on_level = !mach->gpio_pullup_inverted; if (mach->gpio_pullup) - gpio_set_value(mach->gpio_pullup, 1); + gpio_set_value(mach->gpio_pullup, on_level); else if (mach->udc_command) mach->udc_command(PXA2XX_UDC_CMD_CONNECT); } @@ -340,7 +342,7 @@ pxa25x_ep_free_request (struct usb_ep *_ep, struct usb_request *_req) struct pxa25x_request *req; req = container_of (_req, struct pxa25x_request, req); - WARN_ON (!list_empty (&req->queue)); + WARN_ON(!list_empty (&req->queue)); kfree(req); } @@ -1554,7 +1556,7 @@ config_change: * tell us about config change events, * so later ones may fail... */ - WARN("config change %02x fail %d?\n", + WARNING("config change %02x fail %d?\n", u.r.bRequest, i); return; /* TODO experiment: if has_cfr, @@ -1818,7 +1820,7 @@ pxa25x_udc_irq(int irq, void *_dev) static void nop_release (struct device *dev) { - DMSG("%s %s\n", __func__, dev->bus_id); + DMSG("%s %s\n", __func__, dev_name(dev)); } /* this uses load-time allocation and initialization (instead of @@ -2328,7 +2330,7 @@ static int pxa25x_udc_suspend(struct platform_device *dev, pm_message_t state) unsigned long flags; if (!udc->mach->gpio_pullup && !udc->mach->udc_command) - WARN("USB host won't detect disconnect!\n"); + WARNING("USB host won't detect disconnect!\n"); udc->suspended = 1; local_irq_save(flags); diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/pxa25x_udc.h index 4d11ece7c95f..c8a13215e02c 100644 --- a/drivers/usb/gadget/pxa25x_udc.h +++ b/drivers/usb/gadget/pxa25x_udc.h @@ -259,7 +259,7 @@ dump_state(struct pxa25x_udc *dev) #define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0) #define ERR(stuff...) pr_err("udc: " stuff) -#define WARN(stuff...) pr_warning("udc: " stuff) +#define WARNING(stuff...) pr_warning("udc: " stuff) #define INFO(stuff...) pr_info("udc: " stuff) diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 9c0e82ec5c43..9d447d8cfc0c 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -1575,7 +1575,6 @@ static void udc_enable(struct pxa_udc *udc) { udc_writel(udc, UDCICR0, 0); udc_writel(udc, UDCICR1, 0); - udc_writel(udc, UP2OCR, UP2OCR_HXOE); udc_clear_mask_UDCCR(udc, UDCCR_UDE); clk_enable(udc->clk); diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index d0677f5d3cd5..7228e8562236 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -1,8 +1,6 @@ /* * RNDIS MSG parser * - * Version: $Id: rndis.c,v 1.19 2004/03/25 21:33:46 robert Exp $ - * * Authors: Benedikt Spranger, Pengutronix * Robert Schwebel, Pengutronix * @@ -30,6 +28,7 @@ #include <linux/init.h> #include <linux/list.h> #include <linux/proc_fs.h> +#include <linux/seq_file.h> #include <linux/netdevice.h> #include <asm/io.h> @@ -38,9 +37,7 @@ #include <asm/unaligned.h> -#undef RNDIS_PM -#undef RNDIS_WAKEUP -#undef VERBOSE +#undef VERBOSE_DEBUG #include "rndis.h" @@ -96,9 +93,6 @@ static const u32 oid_supported_list [] = OID_GEN_MAXIMUM_TOTAL_SIZE, OID_GEN_MEDIA_CONNECT_STATUS, OID_GEN_PHYSICAL_MEDIUM, -#if 0 - OID_GEN_RNDIS_CONFIG_PARAMETER, -#endif /* the statistical stuff */ OID_GEN_XMIT_OK, @@ -146,7 +140,14 @@ static const u32 oid_supported_list [] = #endif /* RNDIS_OPTIONAL_STATS */ #ifdef RNDIS_PM - /* PM and wakeup are mandatory for USB: */ + /* PM and wakeup are "mandatory" for USB, but the RNDIS specs + * don't say what they mean ... and the NDIS specs are often + * confusing and/or ambiguous in this context. (That is, more + * so than their specs for the other OIDs.) + * + * FIXME someone who knows what these should do, please + * implement them! + */ /* power management */ OID_PNP_CAPABILITIES, @@ -173,6 +174,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, __le32 *outbuf; int i, count; rndis_query_cmplt_type *resp; + struct net_device *net; + struct net_device_stats *stats; if (!r) return -ENOMEM; resp = (rndis_query_cmplt_type *) r->buf; @@ -194,6 +197,12 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, outbuf = (__le32 *) &resp[1]; resp->InformationBufferOffset = __constant_cpu_to_le32 (16); + net = rndis_per_dev_params[configNr].dev; + if (net->get_stats) + stats = net->get_stats(net); + else + stats = NULL; + switch (OID) { /* general oids (table 4-1) */ @@ -350,11 +359,9 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, case OID_GEN_XMIT_OK: if (rndis_debug > 1) DBG("%s: OID_GEN_XMIT_OK\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 ( - rndis_per_dev_params [configNr].stats->tx_packets - - rndis_per_dev_params [configNr].stats->tx_errors - - rndis_per_dev_params [configNr].stats->tx_dropped); + if (stats) { + *outbuf = cpu_to_le32(stats->tx_packets + - stats->tx_errors - stats->tx_dropped); retval = 0; } break; @@ -363,11 +370,9 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, case OID_GEN_RCV_OK: if (rndis_debug > 1) DBG("%s: OID_GEN_RCV_OK\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 ( - rndis_per_dev_params [configNr].stats->rx_packets - - rndis_per_dev_params [configNr].stats->rx_errors - - rndis_per_dev_params [configNr].stats->rx_dropped); + if (stats) { + *outbuf = cpu_to_le32(stats->rx_packets + - stats->rx_errors - stats->rx_dropped); retval = 0; } break; @@ -376,9 +381,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, case OID_GEN_XMIT_ERROR: if (rndis_debug > 1) DBG("%s: OID_GEN_XMIT_ERROR\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->tx_errors); + if (stats) { + *outbuf = cpu_to_le32(stats->tx_errors); retval = 0; } break; @@ -387,9 +391,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, case OID_GEN_RCV_ERROR: if (rndis_debug > 1) DBG("%s: OID_GEN_RCV_ERROR\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->rx_errors); + if (stats) { + *outbuf = cpu_to_le32(stats->rx_errors); retval = 0; } break; @@ -397,150 +400,12 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_RCV_NO_BUFFER: DBG("%s: OID_GEN_RCV_NO_BUFFER\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->rx_dropped); - retval = 0; - } - break; - -#ifdef RNDIS_OPTIONAL_STATS - case OID_GEN_DIRECTED_BYTES_XMIT: - DBG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __func__); - /* - * Aunt Tilly's size of shoes - * minus antarctica count of penguins - * divided by weight of Alpha Centauri - */ - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 ( - (rndis_per_dev_params [configNr] - .stats->tx_packets - - rndis_per_dev_params [configNr] - .stats->tx_errors - - rndis_per_dev_params [configNr] - .stats->tx_dropped) - * 123); - retval = 0; - } - break; - - case OID_GEN_DIRECTED_FRAMES_XMIT: - DBG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __func__); - /* dito */ - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 ( - (rndis_per_dev_params [configNr] - .stats->tx_packets - - rndis_per_dev_params [configNr] - .stats->tx_errors - - rndis_per_dev_params [configNr] - .stats->tx_dropped) - / 123); - retval = 0; - } - break; - - case OID_GEN_MULTICAST_BYTES_XMIT: - DBG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->multicast*1234); - retval = 0; - } - break; - - case OID_GEN_MULTICAST_FRAMES_XMIT: - DBG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->multicast); - retval = 0; - } - break; - - case OID_GEN_BROADCAST_BYTES_XMIT: - DBG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->tx_packets/42*255); - retval = 0; - } - break; - - case OID_GEN_BROADCAST_FRAMES_XMIT: - DBG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->tx_packets/42); - retval = 0; - } - break; - - case OID_GEN_DIRECTED_BYTES_RCV: - DBG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __func__); - *outbuf = __constant_cpu_to_le32 (0); - retval = 0; - break; - - case OID_GEN_DIRECTED_FRAMES_RCV: - DBG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __func__); - *outbuf = __constant_cpu_to_le32 (0); - retval = 0; - break; - - case OID_GEN_MULTICAST_BYTES_RCV: - DBG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->multicast * 1111); - retval = 0; - } - break; - - case OID_GEN_MULTICAST_FRAMES_RCV: - DBG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->multicast); - retval = 0; - } - break; - - case OID_GEN_BROADCAST_BYTES_RCV: - DBG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->rx_packets/42*255); - retval = 0; - } - break; - - case OID_GEN_BROADCAST_FRAMES_RCV: - DBG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->rx_packets/42); + if (stats) { + *outbuf = cpu_to_le32(stats->rx_dropped); retval = 0; } break; - case OID_GEN_RCV_CRC_ERROR: - DBG("%s: OID_GEN_RCV_CRC_ERROR\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->rx_crc_errors); - retval = 0; - } - break; - - case OID_GEN_TRANSMIT_QUEUE_LENGTH: - DBG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __func__); - *outbuf = __constant_cpu_to_le32 (0); - retval = 0; - break; -#endif /* RNDIS_OPTIONAL_STATS */ - /* ieee802.3 OIDs (table 4-3) */ /* mandatory */ @@ -592,9 +457,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_802_3_RCV_ERROR_ALIGNMENT: DBG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__); - if (rndis_per_dev_params [configNr].stats) { - *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] - .stats->rx_frame_errors); + if (stats) { + *outbuf = cpu_to_le32(stats->rx_frame_errors); retval = 0; } break; @@ -613,64 +477,6 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, retval = 0; break; -#ifdef RNDIS_OPTIONAL_STATS - case OID_802_3_XMIT_DEFERRED: - DBG("%s: OID_802_3_XMIT_DEFERRED\n", __func__); - /* TODO */ - break; - - case OID_802_3_XMIT_MAX_COLLISIONS: - DBG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __func__); - /* TODO */ - break; - - case OID_802_3_RCV_OVERRUN: - DBG("%s: OID_802_3_RCV_OVERRUN\n", __func__); - /* TODO */ - break; - - case OID_802_3_XMIT_UNDERRUN: - DBG("%s: OID_802_3_XMIT_UNDERRUN\n", __func__); - /* TODO */ - break; - - case OID_802_3_XMIT_HEARTBEAT_FAILURE: - DBG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __func__); - /* TODO */ - break; - - case OID_802_3_XMIT_TIMES_CRS_LOST: - DBG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __func__); - /* TODO */ - break; - - case OID_802_3_XMIT_LATE_COLLISIONS: - DBG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __func__); - /* TODO */ - break; -#endif /* RNDIS_OPTIONAL_STATS */ - -#ifdef RNDIS_PM - /* power management OIDs (table 4-5) */ - case OID_PNP_CAPABILITIES: - DBG("%s: OID_PNP_CAPABILITIES\n", __func__); - - /* for now, no wakeup capabilities */ - length = sizeof (struct NDIS_PNP_CAPABILITIES); - memset(outbuf, 0, length); - retval = 0; - break; - case OID_PNP_QUERY_POWER: - DBG("%s: OID_PNP_QUERY_POWER D%d\n", __func__, - get_unaligned_le32(buf) - 1); - /* only suspend is a real power state, and - * it can't be entered by OID_PNP_SET_POWER... - */ - length = 0; - retval = 0; - break; -#endif - default: pr_warning("%s: query unknown OID 0x%08X\n", __func__, OID); @@ -726,9 +532,6 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, * what makes the packet flow start and stop, like * activating the CDC Ethernet altsetting. */ -#ifdef RNDIS_PM -update_linkstate: -#endif retval = 0; if (*params->filter) { params->state = RNDIS_DATA_INITIALIZED; @@ -747,49 +550,6 @@ update_linkstate: DBG("%s: OID_802_3_MULTICAST_LIST\n", __func__); retval = 0; break; -#if 0 - case OID_GEN_RNDIS_CONFIG_PARAMETER: - { - struct rndis_config_parameter *param; - param = (struct rndis_config_parameter *) buf; - DBG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n", - __func__, - min(cpu_to_le32(param->ParameterNameLength),80), - buf + param->ParameterNameOffset); - retval = 0; - } - break; -#endif - -#ifdef RNDIS_PM - case OID_PNP_SET_POWER: - /* The only real power state is USB suspend, and RNDIS requests - * can't enter it; this one isn't really about power. After - * resuming, Windows forces a reset, and then SET_POWER D0. - * FIXME ... then things go batty; Windows wedges itself. - */ - i = get_unaligned_le32(buf); - DBG("%s: OID_PNP_SET_POWER D%d\n", __func__, i - 1); - switch (i) { - case NdisDeviceStateD0: - *params->filter = params->saved_filter; - goto update_linkstate; - case NdisDeviceStateD3: - case NdisDeviceStateD2: - case NdisDeviceStateD1: - params->saved_filter = *params->filter; - retval = 0; - break; - } - break; - -#ifdef RNDIS_WAKEUP - // no wakeup support advertised, so wakeup OIDs always fail: - // - OID_PNP_ENABLE_WAKE_UP - // - OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN -#endif - -#endif /* RNDIS_PM */ default: pr_warning("%s: set unknown OID 0x%08X, size %d\n", @@ -807,8 +567,10 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf) { rndis_init_cmplt_type *resp; rndis_resp_t *r; + struct rndis_params *params = rndis_per_dev_params + configNr; - if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; + if (!params->dev) + return -ENOTSUPP; r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type)); if (!r) @@ -826,7 +588,7 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf) resp->Medium = __constant_cpu_to_le32 (RNDIS_MEDIUM_802_3); resp->MaxPacketsPerTransfer = __constant_cpu_to_le32 (1); resp->MaxTransferSize = cpu_to_le32 ( - rndis_per_dev_params [configNr].dev->mtu + params->dev->mtu + sizeof (struct ethhdr) + sizeof (struct rndis_packet_msg_type) + 22); @@ -834,10 +596,7 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf) resp->AFListOffset = __constant_cpu_to_le32 (0); resp->AFListSize = __constant_cpu_to_le32 (0); - if (rndis_per_dev_params [configNr].ack) - rndis_per_dev_params [configNr].ack ( - rndis_per_dev_params [configNr].dev); - + params->resp_avail(params->v); return 0; } @@ -845,9 +604,11 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf) { rndis_query_cmplt_type *resp; rndis_resp_t *r; + struct rndis_params *params = rndis_per_dev_params + configNr; // DBG("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); - if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; + if (!params->dev) + return -ENOTSUPP; /* * we need more memory: @@ -878,9 +639,7 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf) } else resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); - if (rndis_per_dev_params [configNr].ack) - rndis_per_dev_params [configNr].ack ( - rndis_per_dev_params [configNr].dev); + params->resp_avail(params->v); return 0; } @@ -889,6 +648,7 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf) u32 BufLength, BufOffset; rndis_set_cmplt_type *resp; rndis_resp_t *r; + struct rndis_params *params = rndis_per_dev_params + configNr; r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type)); if (!r) @@ -898,7 +658,7 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf) BufLength = le32_to_cpu (buf->InformationBufferLength); BufOffset = le32_to_cpu (buf->InformationBufferOffset); -#ifdef VERBOSE +#ifdef VERBOSE_DEBUG DBG("%s: Length: %d\n", __func__, BufLength); DBG("%s: Offset: %d\n", __func__, BufOffset); DBG("%s: InfoBuffer: ", __func__); @@ -919,10 +679,7 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf) else resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); - if (rndis_per_dev_params [configNr].ack) - rndis_per_dev_params [configNr].ack ( - rndis_per_dev_params [configNr].dev); - + params->resp_avail(params->v); return 0; } @@ -930,6 +687,7 @@ static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf) { rndis_reset_cmplt_type *resp; rndis_resp_t *r; + struct rndis_params *params = rndis_per_dev_params + configNr; r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type)); if (!r) @@ -942,10 +700,7 @@ static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf) /* resent information */ resp->AddressingReset = __constant_cpu_to_le32 (1); - if (rndis_per_dev_params [configNr].ack) - rndis_per_dev_params [configNr].ack ( - rndis_per_dev_params [configNr].dev); - + params->resp_avail(params->v); return 0; } @@ -954,6 +709,7 @@ static int rndis_keepalive_response (int configNr, { rndis_keepalive_cmplt_type *resp; rndis_resp_t *r; + struct rndis_params *params = rndis_per_dev_params + configNr; /* host "should" check only in RNDIS_DATA_INITIALIZED state */ @@ -968,10 +724,7 @@ static int rndis_keepalive_response (int configNr, resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); - if (rndis_per_dev_params [configNr].ack) - rndis_per_dev_params [configNr].ack ( - rndis_per_dev_params [configNr].dev); - + params->resp_avail(params->v); return 0; } @@ -983,8 +736,9 @@ static int rndis_indicate_status_msg (int configNr, u32 status) { rndis_indicate_status_msg_type *resp; rndis_resp_t *r; + struct rndis_params *params = rndis_per_dev_params + configNr; - if (rndis_per_dev_params [configNr].state == RNDIS_UNINITIALIZED) + if (params->state == RNDIS_UNINITIALIZED) return -ENOTSUPP; r = rndis_add_response (configNr, @@ -1000,9 +754,7 @@ static int rndis_indicate_status_msg (int configNr, u32 status) resp->StatusBufferLength = __constant_cpu_to_le32 (0); resp->StatusBufferOffset = __constant_cpu_to_le32 (0); - if (rndis_per_dev_params [configNr].ack) - rndis_per_dev_params [configNr].ack ( - rndis_per_dev_params [configNr].dev); + params->resp_avail(params->v); return 0; } @@ -1029,7 +781,6 @@ void rndis_uninit (int configNr) if (configNr >= RNDIS_MAX_CONFIGS) return; - rndis_per_dev_params [configNr].used = 0; rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED; /* drain the response queue */ @@ -1142,21 +893,25 @@ int rndis_msg_parser (u8 configNr, u8 *buf) return -ENOTSUPP; } -int rndis_register (int (* rndis_control_ack) (struct net_device *)) +int rndis_register(void (*resp_avail)(void *v), void *v) { u8 i; + if (!resp_avail) + return -EINVAL; + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { if (!rndis_per_dev_params [i].used) { rndis_per_dev_params [i].used = 1; - rndis_per_dev_params [i].ack = rndis_control_ack; + rndis_per_dev_params [i].resp_avail = resp_avail; + rndis_per_dev_params [i].v = v; DBG("%s: configNr = %d\n", __func__, i); return i; } } DBG("failed\n"); - return -1; + return -ENODEV; } void rndis_deregister (int configNr) @@ -1169,16 +924,14 @@ void rndis_deregister (int configNr) return; } -int rndis_set_param_dev (u8 configNr, struct net_device *dev, - struct net_device_stats *stats, - u16 *cdc_filter) +int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter) { DBG("%s:\n", __func__ ); - if (!dev || !stats) return -1; + if (!dev) + return -EINVAL; if (configNr >= RNDIS_MAX_CONFIGS) return -1; rndis_per_dev_params [configNr].dev = dev; - rndis_per_dev_params [configNr].stats = stats; rndis_per_dev_params [configNr].filter = cdc_filter; return 0; @@ -1296,14 +1049,11 @@ int rndis_rm_hdr(struct sk_buff *skb) #ifdef CONFIG_USB_GADGET_DEBUG_FILES -static int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof, - void *data) +static int rndis_proc_show(struct seq_file *m, void *v) { - char *out = page; - int len; - rndis_params *param = (rndis_params *) data; + rndis_params *param = m->private; - out += snprintf (out, count, + seq_printf(m, "Config Nr. %d\n" "used : %s\n" "state : %s\n" @@ -1326,25 +1076,13 @@ static int rndis_proc_read (char *page, char **start, off_t off, int count, int (param->media_state) ? 0 : param->speed*100, (param->media_state) ? "disconnected" : "connected", param->vendorID, param->vendorDescr); - - len = out - page; - len -= off; - - if (len < count) { - *eof = 1; - if (len <= 0) - return 0; - } else - len = count; - - *start = page + off; - return len; + return 0; } -static int rndis_proc_write (struct file *file, const char __user *buffer, - unsigned long count, void *data) +static ssize_t rndis_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) { - rndis_params *p = data; + rndis_params *p = PDE(file->f_path.dentry->d_inode)->data; u32 speed = 0; int i, fl_speed = 0; @@ -1386,6 +1124,20 @@ static int rndis_proc_write (struct file *file, const char __user *buffer, return count; } +static int rndis_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, rndis_proc_show, PDE(inode)->data); +} + +static const struct file_operations rndis_proc_fops = { + .owner = THIS_MODULE, + .open = rndis_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = rndis_proc_write, +}; + #define NAME_TEMPLATE "driver/rndis-%03d" static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; @@ -1403,7 +1155,9 @@ int __init rndis_init (void) sprintf (name, NAME_TEMPLATE, i); if (!(rndis_connect_state [i] - = create_proc_entry (name, 0660, NULL))) + = proc_create_data(name, 0660, NULL, + &rndis_proc_fops, + (void *)(rndis_per_dev_params + i)))) { DBG("%s :remove entries", __func__); while (i) { @@ -1413,11 +1167,6 @@ int __init rndis_init (void) DBG("\n"); return -EIO; } - - rndis_connect_state [i]->write_proc = rndis_proc_write; - rndis_connect_state [i]->read_proc = rndis_proc_read; - rndis_connect_state [i]->data = (void *) - (rndis_per_dev_params + i); #endif rndis_per_dev_params [i].confignr = i; rndis_per_dev_params [i].used = 0; diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h index 397b149f3ca7..aac61dfe0f03 100644 --- a/drivers/usb/gadget/rndis.h +++ b/drivers/usb/gadget/rndis.h @@ -1,8 +1,6 @@ /* * RNDIS Definitions for Remote NDIS * - * Version: $Id: rndis.h,v 1.15 2004/03/25 21:33:46 robert Exp $ - * * Authors: Benedikt Spranger, Pengutronix * Robert Schwebel, Pengutronix * @@ -235,20 +233,19 @@ typedef struct rndis_params const u8 *host_mac; u16 *filter; struct net_device *dev; - struct net_device_stats *stats; u32 vendorID; const char *vendorDescr; - int (*ack) (struct net_device *); + void (*resp_avail)(void *v); + void *v; struct list_head resp_queue; } rndis_params; /* RNDIS Message parser and other useless functions */ int rndis_msg_parser (u8 configNr, u8 *buf); -int rndis_register (int (*rndis_control_ack) (struct net_device *)); +int rndis_register(void (*resp_avail)(void *v), void *v); void rndis_deregister (int configNr); int rndis_set_param_dev (u8 configNr, struct net_device *dev, - struct net_device_stats *stats, u16 *cdc_filter); int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr); diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index fa019fa73334..b3699afff002 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -1,15 +1,9 @@ /* - * g_serial.c -- USB gadget serial driver + * serial.c -- USB gadget serial driver * - * Copyright 2003 (C) Al Borchers (alborchers@steinerpoint.com) - * - * This code is based in part on the Gadget Zero driver, which - * is Copyright (C) 2003 by David Brownell, all rights reserved. - * - * This code also borrows from usbserial.c, which is - * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (C) 2000 Peter Berger (pberger@brimson.com) - * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com) + * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) + * Copyright (C) 2008 by David Brownell + * Copyright (C) 2008 by Nokia Corporation * * This software is distributed under the terms of the GNU General * Public License ("GPL") as published by the Free Software Foundation, @@ -22,2254 +16,237 @@ #include <linux/tty.h> #include <linux/tty_flip.h> -#include <linux/usb/ch9.h> -#include <linux/usb/cdc.h> -#include <linux/usb/gadget.h> - +#include "u_serial.h" #include "gadget_chips.h" /* Defines */ -#define GS_VERSION_STR "v2.2" -#define GS_VERSION_NUM 0x2200 +#define GS_VERSION_STR "v2.4" +#define GS_VERSION_NUM 0x2400 #define GS_LONG_NAME "Gadget Serial" -#define GS_SHORT_NAME "g_serial" - -#define GS_MAJOR 127 -#define GS_MINOR_START 0 - -/* REVISIT only one port is supported for now; - * see gs_{send,recv}_packet() ... no multiplexing, - * and no support for multiple ACM devices. - */ -#define GS_NUM_PORTS 1 - -#define GS_NUM_CONFIGS 1 -#define GS_NO_CONFIG_ID 0 -#define GS_BULK_CONFIG_ID 1 -#define GS_ACM_CONFIG_ID 2 - -#define GS_MAX_NUM_INTERFACES 2 -#define GS_BULK_INTERFACE_ID 0 -#define GS_CONTROL_INTERFACE_ID 0 -#define GS_DATA_INTERFACE_ID 1 - -#define GS_MAX_DESC_LEN 256 - -#define GS_DEFAULT_READ_Q_SIZE 32 -#define GS_DEFAULT_WRITE_Q_SIZE 32 - -#define GS_DEFAULT_WRITE_BUF_SIZE 8192 -#define GS_TMP_BUF_SIZE 8192 - -#define GS_CLOSE_TIMEOUT 15 - -#define GS_DEFAULT_USE_ACM 0 - -/* 9600-8-N-1 ... matches init_termios.c_cflag and defaults - * expected by "usbser.sys" on MS-Windows. - */ -#define GS_DEFAULT_DTE_RATE 9600 -#define GS_DEFAULT_DATA_BITS 8 -#define GS_DEFAULT_PARITY USB_CDC_NO_PARITY -#define GS_DEFAULT_CHAR_FORMAT USB_CDC_1_STOP_BITS - -/* maxpacket and other transfer characteristics vary by speed. */ -static inline struct usb_endpoint_descriptor * -choose_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, - struct usb_endpoint_descriptor *fs) -{ - if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) - return hs; - return fs; -} - - -/* debug settings */ -#ifdef DEBUG -static int debug = 1; -#else -#define debug 0 -#endif - -#define gs_debug(format, arg...) \ - do { if (debug) pr_debug(format, ## arg); } while (0) -#define gs_debug_level(level, format, arg...) \ - do { if (debug >= level) pr_debug(format, ## arg); } while (0) +#define GS_VERSION_NAME GS_LONG_NAME " " GS_VERSION_STR +/*-------------------------------------------------------------------------*/ /* Thanks to NetChip Technologies for donating this product ID. - * - * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! - * Instead: allocate your own, using normal USB-IF procedures. - */ +* +* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! +* Instead: allocate your own, using normal USB-IF procedures. +*/ #define GS_VENDOR_ID 0x0525 /* NetChip */ #define GS_PRODUCT_ID 0xa4a6 /* Linux-USB Serial Gadget */ #define GS_CDC_PRODUCT_ID 0xa4a7 /* ... as CDC-ACM */ -#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */ -#define GS_NOTIFY_MAXPACKET 8 +/* string IDs are assigned dynamically */ +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 +#define STRING_DESCRIPTION_IDX 2 -/* circular buffer */ -struct gs_buf { - unsigned int buf_size; - char *buf_buf; - char *buf_get; - char *buf_put; -}; +static char manufacturer[50]; -/* the port structure holds info for each port, one for each minor number */ -struct gs_port { - struct gs_dev *port_dev; /* pointer to device struct */ - struct tty_struct *port_tty; /* pointer to tty struct */ - spinlock_t port_lock; - int port_num; - int port_open_count; - int port_in_use; /* open/close in progress */ - wait_queue_head_t port_write_wait;/* waiting to write */ - struct gs_buf *port_write_buf; - struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ - u16 port_handshake_bits; -#define RS232_RTS (1 << 1) -#define RS232_DTE (1 << 0) +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = manufacturer, + [STRING_PRODUCT_IDX].s = GS_VERSION_NAME, + [STRING_DESCRIPTION_IDX].s = NULL /* updated; f(use_acm) */, + { } /* end of list */ }; -/* the device structure holds info for the USB device */ -struct gs_dev { - struct usb_gadget *dev_gadget; /* gadget device pointer */ - spinlock_t dev_lock; /* lock for set/reset config */ - int dev_config; /* configuration number */ - struct usb_ep *dev_notify_ep; /* address of notify endpoint */ - struct usb_ep *dev_in_ep; /* address of in endpoint */ - struct usb_ep *dev_out_ep; /* address of out endpoint */ - struct usb_endpoint_descriptor /* descriptor of notify ep */ - *dev_notify_ep_desc; - struct usb_endpoint_descriptor /* descriptor of in endpoint */ - *dev_in_ep_desc; - struct usb_endpoint_descriptor /* descriptor of out endpoint */ - *dev_out_ep_desc; - struct usb_request *dev_ctrl_req; /* control request */ - struct list_head dev_req_list; /* list of write requests */ - int dev_sched_port; /* round robin port scheduled */ - struct gs_port *dev_port[GS_NUM_PORTS]; /* the ports */ +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, }; - -/* Functions */ - -/* tty driver internals */ -static int gs_send(struct gs_dev *dev); -static int gs_send_packet(struct gs_dev *dev, char *packet, - unsigned int size); -static int gs_recv_packet(struct gs_dev *dev, char *packet, - unsigned int size); -static void gs_read_complete(struct usb_ep *ep, struct usb_request *req); -static void gs_write_complete(struct usb_ep *ep, struct usb_request *req); - -/* gadget driver internals */ -static int gs_set_config(struct gs_dev *dev, unsigned config); -static void gs_reset_config(struct gs_dev *dev); -static int gs_build_config_buf(u8 *buf, struct usb_gadget *g, - u8 type, unsigned int index, int is_otg); - -static struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned int len, - gfp_t kmalloc_flags); -static void gs_free_req(struct usb_ep *ep, struct usb_request *req); - -static int gs_alloc_ports(struct gs_dev *dev, gfp_t kmalloc_flags); -static void gs_free_ports(struct gs_dev *dev); - -/* circular buffer */ -static struct gs_buf *gs_buf_alloc(unsigned int size, gfp_t kmalloc_flags); -static void gs_buf_free(struct gs_buf *gb); -static void gs_buf_clear(struct gs_buf *gb); -static unsigned int gs_buf_data_avail(struct gs_buf *gb); -static unsigned int gs_buf_space_avail(struct gs_buf *gb); -static unsigned int gs_buf_put(struct gs_buf *gb, const char *buf, - unsigned int count); -static unsigned int gs_buf_get(struct gs_buf *gb, char *buf, - unsigned int count); - - -/* Globals */ - -static struct gs_dev *gs_device; - -static struct mutex gs_open_close_lock[GS_NUM_PORTS]; - - -/*-------------------------------------------------------------------------*/ - -/* USB descriptors */ - -#define GS_MANUFACTURER_STR_ID 1 -#define GS_PRODUCT_STR_ID 2 -#define GS_SERIAL_STR_ID 3 -#define GS_BULK_CONFIG_STR_ID 4 -#define GS_ACM_CONFIG_STR_ID 5 -#define GS_CONTROL_STR_ID 6 -#define GS_DATA_STR_ID 7 - -/* static strings, in UTF-8 */ -static char manufacturer[50]; -static struct usb_string gs_strings[] = { - { GS_MANUFACTURER_STR_ID, manufacturer }, - { GS_PRODUCT_STR_ID, GS_LONG_NAME }, - { GS_BULK_CONFIG_STR_ID, "Gadget Serial Bulk" }, - { GS_ACM_CONFIG_STR_ID, "Gadget Serial CDC ACM" }, - { GS_CONTROL_STR_ID, "Gadget Serial Control" }, - { GS_DATA_STR_ID, "Gadget Serial Data" }, - { } /* end of list */ -}; - -static struct usb_gadget_strings gs_string_table = { - .language = 0x0409, /* en-us */ - .strings = gs_strings, +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, }; -static struct usb_device_descriptor gs_device_desc = { +static struct usb_device_descriptor device_desc = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, .bcdUSB = __constant_cpu_to_le16(0x0200), + /* .bDeviceClass = f(use_acm) */ .bDeviceSubClass = 0, .bDeviceProtocol = 0, + /* .bMaxPacketSize0 = f(hardware) */ .idVendor = __constant_cpu_to_le16(GS_VENDOR_ID), - .idProduct = __constant_cpu_to_le16(GS_PRODUCT_ID), - .iManufacturer = GS_MANUFACTURER_STR_ID, - .iProduct = GS_PRODUCT_STR_ID, - .bNumConfigurations = GS_NUM_CONFIGS, + /* .idProduct = f(use_acm) */ + /* .bcdDevice = f(hardware) */ + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ + .bNumConfigurations = 1, }; -static struct usb_otg_descriptor gs_otg_descriptor = { - .bLength = sizeof(gs_otg_descriptor), +static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, .bDescriptorType = USB_DT_OTG, - .bmAttributes = USB_OTG_SRP, -}; - -static struct usb_config_descriptor gs_bulk_config_desc = { - .bLength = USB_DT_CONFIG_SIZE, - .bDescriptorType = USB_DT_CONFIG, - /* .wTotalLength computed dynamically */ - .bNumInterfaces = 1, - .bConfigurationValue = GS_BULK_CONFIG_ID, - .iConfiguration = GS_BULK_CONFIG_STR_ID, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 1, -}; - -static struct usb_config_descriptor gs_acm_config_desc = { - .bLength = USB_DT_CONFIG_SIZE, - .bDescriptorType = USB_DT_CONFIG, - /* .wTotalLength computed dynamically */ - .bNumInterfaces = 2, - .bConfigurationValue = GS_ACM_CONFIG_ID, - .iConfiguration = GS_ACM_CONFIG_STR_ID, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 1, -}; - -static const struct usb_interface_descriptor gs_bulk_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = GS_BULK_INTERFACE_ID, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = GS_DATA_STR_ID, -}; - -static const struct usb_interface_descriptor gs_control_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = GS_CONTROL_INTERFACE_ID, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, - .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, - .iInterface = GS_CONTROL_STR_ID, -}; - -static const struct usb_interface_descriptor gs_data_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = GS_DATA_INTERFACE_ID, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_CDC_DATA, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = GS_DATA_STR_ID, -}; - -static const struct usb_cdc_header_desc gs_header_desc = { - .bLength = sizeof(gs_header_desc), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_HEADER_TYPE, - .bcdCDC = __constant_cpu_to_le16(0x0110), -}; - -static const struct usb_cdc_call_mgmt_descriptor gs_call_mgmt_descriptor = { - .bLength = sizeof(gs_call_mgmt_descriptor), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, - .bmCapabilities = 0, - .bDataInterface = 1, /* index of data interface */ -}; - -static struct usb_cdc_acm_descriptor gs_acm_descriptor = { - .bLength = sizeof(gs_acm_descriptor), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_ACM_TYPE, - .bmCapabilities = (1 << 1), -}; - -static const struct usb_cdc_union_desc gs_union_desc = { - .bLength = sizeof(gs_union_desc), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_UNION_TYPE, - .bMasterInterface0 = 0, /* index of control interface */ - .bSlaveInterface0 = 1, /* index of data interface */ -}; - -static struct usb_endpoint_descriptor gs_fullspeed_notify_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), - .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL, -}; - -static struct usb_endpoint_descriptor gs_fullspeed_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -static struct usb_endpoint_descriptor gs_fullspeed_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -static const struct usb_descriptor_header *gs_bulk_fullspeed_function[] = { - (struct usb_descriptor_header *) &gs_otg_descriptor, - (struct usb_descriptor_header *) &gs_bulk_interface_desc, - (struct usb_descriptor_header *) &gs_fullspeed_in_desc, - (struct usb_descriptor_header *) &gs_fullspeed_out_desc, - NULL, -}; - -static const struct usb_descriptor_header *gs_acm_fullspeed_function[] = { - (struct usb_descriptor_header *) &gs_otg_descriptor, - (struct usb_descriptor_header *) &gs_control_interface_desc, - (struct usb_descriptor_header *) &gs_header_desc, - (struct usb_descriptor_header *) &gs_call_mgmt_descriptor, - (struct usb_descriptor_header *) &gs_acm_descriptor, - (struct usb_descriptor_header *) &gs_union_desc, - (struct usb_descriptor_header *) &gs_fullspeed_notify_desc, - (struct usb_descriptor_header *) &gs_data_interface_desc, - (struct usb_descriptor_header *) &gs_fullspeed_in_desc, - (struct usb_descriptor_header *) &gs_fullspeed_out_desc, - NULL, -}; -static struct usb_endpoint_descriptor gs_highspeed_notify_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), - .bInterval = GS_LOG2_NOTIFY_INTERVAL+4, -}; - -static struct usb_endpoint_descriptor gs_highspeed_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), -}; - -static struct usb_endpoint_descriptor gs_highspeed_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), -}; - -static struct usb_qualifier_descriptor gs_qualifier_desc = { - .bLength = sizeof(struct usb_qualifier_descriptor), - .bDescriptorType = USB_DT_DEVICE_QUALIFIER, - .bcdUSB = __constant_cpu_to_le16 (0x0200), - /* assumes ep0 uses the same value for both speeds ... */ - .bNumConfigurations = GS_NUM_CONFIGS, -}; - -static const struct usb_descriptor_header *gs_bulk_highspeed_function[] = { - (struct usb_descriptor_header *) &gs_otg_descriptor, - (struct usb_descriptor_header *) &gs_bulk_interface_desc, - (struct usb_descriptor_header *) &gs_highspeed_in_desc, - (struct usb_descriptor_header *) &gs_highspeed_out_desc, - NULL, + /* REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, }; -static const struct usb_descriptor_header *gs_acm_highspeed_function[] = { - (struct usb_descriptor_header *) &gs_otg_descriptor, - (struct usb_descriptor_header *) &gs_control_interface_desc, - (struct usb_descriptor_header *) &gs_header_desc, - (struct usb_descriptor_header *) &gs_call_mgmt_descriptor, - (struct usb_descriptor_header *) &gs_acm_descriptor, - (struct usb_descriptor_header *) &gs_union_desc, - (struct usb_descriptor_header *) &gs_highspeed_notify_desc, - (struct usb_descriptor_header *) &gs_data_interface_desc, - (struct usb_descriptor_header *) &gs_highspeed_in_desc, - (struct usb_descriptor_header *) &gs_highspeed_out_desc, +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, NULL, }; - /*-------------------------------------------------------------------------*/ /* Module */ -MODULE_DESCRIPTION(GS_LONG_NAME); +MODULE_DESCRIPTION(GS_VERSION_NAME); MODULE_AUTHOR("Al Borchers"); +MODULE_AUTHOR("David Brownell"); MODULE_LICENSE("GPL"); -#ifdef DEBUG -module_param(debug, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(debug, "Enable debugging, 0=off, 1=on"); -#endif - -static unsigned int read_q_size = GS_DEFAULT_READ_Q_SIZE; -module_param(read_q_size, uint, S_IRUGO); -MODULE_PARM_DESC(read_q_size, "Read request queue size, default=32"); - -static unsigned int write_q_size = GS_DEFAULT_WRITE_Q_SIZE; -module_param(write_q_size, uint, S_IRUGO); -MODULE_PARM_DESC(write_q_size, "Write request queue size, default=32"); +static int use_acm = true; +module_param(use_acm, bool, 0); +MODULE_PARM_DESC(use_acm, "Use CDC ACM, default=yes"); -static unsigned int write_buf_size = GS_DEFAULT_WRITE_BUF_SIZE; -module_param(write_buf_size, uint, S_IRUGO); -MODULE_PARM_DESC(write_buf_size, "Write buffer size, default=8192"); - -static unsigned int use_acm = GS_DEFAULT_USE_ACM; -module_param(use_acm, uint, S_IRUGO); -MODULE_PARM_DESC(use_acm, "Use CDC ACM, 0=no, 1=yes, default=no"); +static unsigned n_ports = 1; +module_param(n_ports, uint, 0); +MODULE_PARM_DESC(n_ports, "number of ports to create, default=1"); /*-------------------------------------------------------------------------*/ -/* TTY Driver */ - -/* - * gs_open - */ -static int gs_open(struct tty_struct *tty, struct file *file) -{ - int port_num; - unsigned long flags; - struct gs_port *port; - struct gs_dev *dev; - struct gs_buf *buf; - struct mutex *mtx; - int ret; - - port_num = tty->index; - - gs_debug("gs_open: (%d,%p,%p)\n", port_num, tty, file); - - if (port_num < 0 || port_num >= GS_NUM_PORTS) { - pr_err("gs_open: (%d,%p,%p) invalid port number\n", - port_num, tty, file); - return -ENODEV; - } - - dev = gs_device; - - if (dev == NULL) { - pr_err("gs_open: (%d,%p,%p) NULL device pointer\n", - port_num, tty, file); - return -ENODEV; - } - - mtx = &gs_open_close_lock[port_num]; - if (mutex_lock_interruptible(mtx)) { - pr_err("gs_open: (%d,%p,%p) interrupted waiting for mutex\n", - port_num, tty, file); - return -ERESTARTSYS; - } - - spin_lock_irqsave(&dev->dev_lock, flags); - - if (dev->dev_config == GS_NO_CONFIG_ID) { - pr_err("gs_open: (%d,%p,%p) device is not connected\n", - port_num, tty, file); - ret = -ENODEV; - goto exit_unlock_dev; - } - - port = dev->dev_port[port_num]; - - if (port == NULL) { - pr_err("gs_open: (%d,%p,%p) NULL port pointer\n", - port_num, tty, file); - ret = -ENODEV; - goto exit_unlock_dev; - } - - spin_lock(&port->port_lock); - spin_unlock(&dev->dev_lock); - - if (port->port_dev == NULL) { - pr_err("gs_open: (%d,%p,%p) port disconnected (1)\n", - port_num, tty, file); - ret = -EIO; - goto exit_unlock_port; - } - - if (port->port_open_count > 0) { - ++port->port_open_count; - gs_debug("gs_open: (%d,%p,%p) already open\n", - port_num, tty, file); - ret = 0; - goto exit_unlock_port; - } - - tty->driver_data = NULL; - - /* mark port as in use, we can drop port lock and sleep if necessary */ - port->port_in_use = 1; - - /* allocate write buffer on first open */ - if (port->port_write_buf == NULL) { - spin_unlock_irqrestore(&port->port_lock, flags); - buf = gs_buf_alloc(write_buf_size, GFP_KERNEL); - spin_lock_irqsave(&port->port_lock, flags); - - /* might have been disconnected while asleep, check */ - if (port->port_dev == NULL) { - pr_err("gs_open: (%d,%p,%p) port disconnected (2)\n", - port_num, tty, file); - port->port_in_use = 0; - ret = -EIO; - goto exit_unlock_port; - } - - if ((port->port_write_buf=buf) == NULL) { - pr_err("gs_open: (%d,%p,%p) cannot allocate " - "port write buffer\n", - port_num, tty, file); - port->port_in_use = 0; - ret = -ENOMEM; - goto exit_unlock_port; - } - - } - - /* wait for carrier detect (not implemented) */ - - /* might have been disconnected while asleep, check */ - if (port->port_dev == NULL) { - pr_err("gs_open: (%d,%p,%p) port disconnected (3)\n", - port_num, tty, file); - port->port_in_use = 0; - ret = -EIO; - goto exit_unlock_port; - } - - tty->driver_data = port; - port->port_tty = tty; - port->port_open_count = 1; - port->port_in_use = 0; - - gs_debug("gs_open: (%d,%p,%p) completed\n", port_num, tty, file); - - ret = 0; - -exit_unlock_port: - spin_unlock_irqrestore(&port->port_lock, flags); - mutex_unlock(mtx); - return ret; - -exit_unlock_dev: - spin_unlock_irqrestore(&dev->dev_lock, flags); - mutex_unlock(mtx); - return ret; - -} - -/* - * gs_close - */ - -static int gs_write_finished_event_safely(struct gs_port *p) -{ - int cond; - - spin_lock_irq(&(p)->port_lock); - cond = !(p)->port_dev || !gs_buf_data_avail((p)->port_write_buf); - spin_unlock_irq(&(p)->port_lock); - return cond; -} - -static void gs_close(struct tty_struct *tty, struct file *file) -{ - struct gs_port *port = tty->driver_data; - struct mutex *mtx; - - if (port == NULL) { - pr_err("gs_close: NULL port pointer\n"); - return; - } - - gs_debug("gs_close: (%d,%p,%p)\n", port->port_num, tty, file); - - mtx = &gs_open_close_lock[port->port_num]; - mutex_lock(mtx); - - spin_lock_irq(&port->port_lock); - - if (port->port_open_count == 0) { - pr_err("gs_close: (%d,%p,%p) port is already closed\n", - port->port_num, tty, file); - goto exit; - } - - if (port->port_open_count > 1) { - --port->port_open_count; - goto exit; - } - - /* free disconnected port on final close */ - if (port->port_dev == NULL) { - kfree(port); - goto exit; - } - - /* mark port as closed but in use, we can drop port lock */ - /* and sleep if necessary */ - port->port_in_use = 1; - port->port_open_count = 0; - - /* wait for write buffer to drain, or */ - /* at most GS_CLOSE_TIMEOUT seconds */ - if (gs_buf_data_avail(port->port_write_buf) > 0) { - spin_unlock_irq(&port->port_lock); - wait_event_interruptible_timeout(port->port_write_wait, - gs_write_finished_event_safely(port), - GS_CLOSE_TIMEOUT * HZ); - spin_lock_irq(&port->port_lock); - } - - /* free disconnected port on final close */ - /* (might have happened during the above sleep) */ - if (port->port_dev == NULL) { - kfree(port); - goto exit; - } - - gs_buf_clear(port->port_write_buf); - - tty->driver_data = NULL; - port->port_tty = NULL; - port->port_in_use = 0; - - gs_debug("gs_close: (%d,%p,%p) completed\n", - port->port_num, tty, file); - -exit: - spin_unlock_irq(&port->port_lock); - mutex_unlock(mtx); -} - -/* - * gs_write - */ -static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count) +static int __init serial_bind_config(struct usb_configuration *c) { - unsigned long flags; - struct gs_port *port = tty->driver_data; - int ret; - - if (port == NULL) { - pr_err("gs_write: NULL port pointer\n"); - return -EIO; - } - - gs_debug("gs_write: (%d,%p) writing %d bytes\n", port->port_num, tty, - count); - - if (count == 0) - return 0; - - spin_lock_irqsave(&port->port_lock, flags); - - if (port->port_dev == NULL) { - pr_err("gs_write: (%d,%p) port is not connected\n", - port->port_num, tty); - ret = -EIO; - goto exit; - } - - if (port->port_open_count == 0) { - pr_err("gs_write: (%d,%p) port is closed\n", - port->port_num, tty); - ret = -EBADF; - goto exit; - } - - count = gs_buf_put(port->port_write_buf, buf, count); - - spin_unlock_irqrestore(&port->port_lock, flags); - - gs_send(gs_device); - - gs_debug("gs_write: (%d,%p) wrote %d bytes\n", port->port_num, tty, - count); + unsigned i; + int status = 0; - return count; - -exit: - spin_unlock_irqrestore(&port->port_lock, flags); - return ret; -} - -/* - * gs_put_char - */ -static int gs_put_char(struct tty_struct *tty, unsigned char ch) -{ - unsigned long flags; - struct gs_port *port = tty->driver_data; - int ret = 0; - - if (port == NULL) { - pr_err("gs_put_char: NULL port pointer\n"); - return 0; - } - - gs_debug("gs_put_char: (%d,%p) char=0x%x, called from %p\n", - port->port_num, tty, ch, __builtin_return_address(0)); - - spin_lock_irqsave(&port->port_lock, flags); - - if (port->port_dev == NULL) { - pr_err("gs_put_char: (%d,%p) port is not connected\n", - port->port_num, tty); - goto exit; - } - - if (port->port_open_count == 0) { - pr_err("gs_put_char: (%d,%p) port is closed\n", - port->port_num, tty); - goto exit; - } - - ret = gs_buf_put(port->port_write_buf, &ch, 1); - -exit: - spin_unlock_irqrestore(&port->port_lock, flags); - return ret; -} - -/* - * gs_flush_chars - */ -static void gs_flush_chars(struct tty_struct *tty) -{ - unsigned long flags; - struct gs_port *port = tty->driver_data; - - if (port == NULL) { - pr_err("gs_flush_chars: NULL port pointer\n"); - return; - } - - gs_debug("gs_flush_chars: (%d,%p)\n", port->port_num, tty); - - spin_lock_irqsave(&port->port_lock, flags); - - if (port->port_dev == NULL) { - pr_err("gs_flush_chars: (%d,%p) port is not connected\n", - port->port_num, tty); - goto exit; - } - - if (port->port_open_count == 0) { - pr_err("gs_flush_chars: (%d,%p) port is closed\n", - port->port_num, tty); - goto exit; - } - - spin_unlock_irqrestore(&port->port_lock, flags); - - gs_send(gs_device); - - return; - -exit: - spin_unlock_irqrestore(&port->port_lock, flags); -} - -/* - * gs_write_room - */ -static int gs_write_room(struct tty_struct *tty) -{ - - int room = 0; - unsigned long flags; - struct gs_port *port = tty->driver_data; - - - if (port == NULL) - return 0; - - spin_lock_irqsave(&port->port_lock, flags); - - if (port->port_dev != NULL && port->port_open_count > 0 - && port->port_write_buf != NULL) - room = gs_buf_space_avail(port->port_write_buf); - - spin_unlock_irqrestore(&port->port_lock, flags); - - gs_debug("gs_write_room: (%d,%p) room=%d\n", - port->port_num, tty, room); - - return room; -} - -/* - * gs_chars_in_buffer - */ -static int gs_chars_in_buffer(struct tty_struct *tty) -{ - int chars = 0; - unsigned long flags; - struct gs_port *port = tty->driver_data; - - if (port == NULL) - return 0; - - spin_lock_irqsave(&port->port_lock, flags); - - if (port->port_dev != NULL && port->port_open_count > 0 - && port->port_write_buf != NULL) - chars = gs_buf_data_avail(port->port_write_buf); - - spin_unlock_irqrestore(&port->port_lock, flags); - - gs_debug("gs_chars_in_buffer: (%d,%p) chars=%d\n", - port->port_num, tty, chars); - - return chars; -} - -/* - * gs_throttle - */ -static void gs_throttle(struct tty_struct *tty) -{ -} - -/* - * gs_unthrottle - */ -static void gs_unthrottle(struct tty_struct *tty) -{ -} - -/* - * gs_break - */ -static void gs_break(struct tty_struct *tty, int break_state) -{ -} - -/* - * gs_ioctl - */ -static int gs_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct gs_port *port = tty->driver_data; - - if (port == NULL) { - pr_err("gs_ioctl: NULL port pointer\n"); - return -EIO; + for (i = 0; i < n_ports && status == 0; i++) { + if (use_acm) + status = acm_bind_config(c, i); + else + status = gser_bind_config(c, i); } - - gs_debug("gs_ioctl: (%d,%p,%p) cmd=0x%4.4x, arg=%lu\n", - port->port_num, tty, file, cmd, arg); - - /* handle ioctls */ - - /* could not handle ioctl */ - return -ENOIOCTLCMD; -} - -/* - * gs_set_termios - */ -static void gs_set_termios(struct tty_struct *tty, struct ktermios *old) -{ + return status; } -static const struct tty_operations gs_tty_ops = { - .open = gs_open, - .close = gs_close, - .write = gs_write, - .put_char = gs_put_char, - .flush_chars = gs_flush_chars, - .write_room = gs_write_room, - .ioctl = gs_ioctl, - .set_termios = gs_set_termios, - .throttle = gs_throttle, - .unthrottle = gs_unthrottle, - .break_ctl = gs_break, - .chars_in_buffer = gs_chars_in_buffer, +static struct usb_configuration serial_config_driver = { + /* .label = f(use_acm) */ + .bind = serial_bind_config, + /* .bConfigurationValue = f(use_acm) */ + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 1, /* 2 mA, minimal */ }; -/*-------------------------------------------------------------------------*/ - -/* -* gs_send -* -* This function finds available write requests, calls -* gs_send_packet to fill these packets with data, and -* continues until either there are no more write requests -* available or no more data to send. This function is -* run whenever data arrives or write requests are available. -*/ -static int gs_send(struct gs_dev *dev) -{ - int ret,len; - unsigned long flags; - struct usb_ep *ep; - struct usb_request *req; - - if (dev == NULL) { - pr_err("gs_send: NULL device pointer\n"); - return -ENODEV; - } - - spin_lock_irqsave(&dev->dev_lock, flags); - - ep = dev->dev_in_ep; - - while(!list_empty(&dev->dev_req_list)) { - - req = list_entry(dev->dev_req_list.next, - struct usb_request, list); - - len = gs_send_packet(dev, req->buf, ep->maxpacket); - - if (len > 0) { - gs_debug_level(3, "gs_send: len=%d, 0x%2.2x " - "0x%2.2x 0x%2.2x ...\n", len, - *((unsigned char *)req->buf), - *((unsigned char *)req->buf+1), - *((unsigned char *)req->buf+2)); - list_del(&req->list); - req->length = len; - spin_unlock_irqrestore(&dev->dev_lock, flags); - if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) { - pr_err( - "gs_send: cannot queue read request, ret=%d\n", - ret); - spin_lock_irqsave(&dev->dev_lock, flags); - break; - } - spin_lock_irqsave(&dev->dev_lock, flags); - } else { - break; - } - - } - - spin_unlock_irqrestore(&dev->dev_lock, flags); - - return 0; -} - -/* - * gs_send_packet - * - * If there is data to send, a packet is built in the given - * buffer and the size is returned. If there is no data to - * send, 0 is returned. If there is any error a negative - * error number is returned. - * - * Called during USB completion routine, on interrupt time. - * - * We assume that disconnect will not happen until all completion - * routines have completed, so we can assume that the dev_port - * array does not change during the lifetime of this function. - */ -static int gs_send_packet(struct gs_dev *dev, char *packet, unsigned int size) -{ - unsigned int len; - struct gs_port *port; - - /* TEMPORARY -- only port 0 is supported right now */ - port = dev->dev_port[0]; - - if (port == NULL) { - pr_err("gs_send_packet: port=%d, NULL port pointer\n", 0); - return -EIO; - } - - spin_lock(&port->port_lock); - - len = gs_buf_data_avail(port->port_write_buf); - if (len < size) - size = len; - - if (size == 0) - goto exit; - - size = gs_buf_get(port->port_write_buf, packet, size); - - if (port->port_tty) - wake_up_interruptible(&port->port_tty->write_wait); - -exit: - spin_unlock(&port->port_lock); - return size; -} - -/* - * gs_recv_packet - * - * Called for each USB packet received. Reads the packet - * header and stuffs the data in the appropriate tty buffer. - * Returns 0 if successful, or a negative error number. - * - * Called during USB completion routine, on interrupt time. - * - * We assume that disconnect will not happen until all completion - * routines have completed, so we can assume that the dev_port - * array does not change during the lifetime of this function. - */ -static int gs_recv_packet(struct gs_dev *dev, char *packet, unsigned int size) -{ - unsigned int len; - struct gs_port *port; - int ret; - struct tty_struct *tty; - - /* TEMPORARY -- only port 0 is supported right now */ - port = dev->dev_port[0]; - - if (port == NULL) { - pr_err("gs_recv_packet: port=%d, NULL port pointer\n", - port->port_num); - return -EIO; - } - - spin_lock(&port->port_lock); - - if (port->port_open_count == 0) { - pr_err("gs_recv_packet: port=%d, port is closed\n", - port->port_num); - ret = -EIO; - goto exit; - } - - - tty = port->port_tty; - - if (tty == NULL) { - pr_err("gs_recv_packet: port=%d, NULL tty pointer\n", - port->port_num); - ret = -EIO; - goto exit; - } - - if (port->port_tty->magic != TTY_MAGIC) { - pr_err("gs_recv_packet: port=%d, bad tty magic\n", - port->port_num); - ret = -EIO; - goto exit; - } - - len = tty_buffer_request_room(tty, size); - if (len > 0) { - tty_insert_flip_string(tty, packet, len); - tty_flip_buffer_push(port->port_tty); - wake_up_interruptible(&port->port_tty->read_wait); - } - ret = 0; -exit: - spin_unlock(&port->port_lock); - return ret; -} - -/* -* gs_read_complete -*/ -static void gs_read_complete(struct usb_ep *ep, struct usb_request *req) +static int __init gs_bind(struct usb_composite_dev *cdev) { - int ret; - struct gs_dev *dev = ep->driver_data; - - if (dev == NULL) { - pr_err("gs_read_complete: NULL device pointer\n"); - return; - } + int gcnum; + struct usb_gadget *gadget = cdev->gadget; + int status; - switch(req->status) { - case 0: - /* normal completion */ - gs_recv_packet(dev, req->buf, req->actual); -requeue: - req->length = ep->maxpacket; - if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) { - pr_err( - "gs_read_complete: cannot queue read request, ret=%d\n", - ret); - } - break; - - case -ESHUTDOWN: - /* disconnect */ - gs_debug("gs_read_complete: shutdown\n"); - gs_free_req(ep, req); - break; - - default: - /* unexpected */ - pr_err( - "gs_read_complete: unexpected status error, status=%d\n", - req->status); - goto requeue; - break; - } -} - -/* -* gs_write_complete -*/ -static void gs_write_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct gs_dev *dev = ep->driver_data; - - if (dev == NULL) { - pr_err("gs_write_complete: NULL device pointer\n"); - return; - } + status = gserial_setup(cdev->gadget, n_ports); + if (status < 0) + return status; - switch(req->status) { - case 0: - /* normal completion */ -requeue: - spin_lock(&dev->dev_lock); - list_add(&req->list, &dev->dev_req_list); - spin_unlock(&dev->dev_lock); - - gs_send(dev); - - break; - - case -ESHUTDOWN: - /* disconnect */ - gs_debug("gs_write_complete: shutdown\n"); - gs_free_req(ep, req); - break; - - default: - pr_err( - "gs_write_complete: unexpected status error, status=%d\n", - req->status); - goto requeue; - break; - } -} + /* Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. + */ -/*-------------------------------------------------------------------------*/ + /* device description: manufacturer, product */ + snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", + init_utsname()->sysname, init_utsname()->release, + gadget->name); + status = usb_string_id(cdev); + if (status < 0) + goto fail; + strings_dev[STRING_MANUFACTURER_IDX].id = status; -/* Gadget Driver */ + device_desc.iManufacturer = status; -/* - * gs_unbind - * - * Called on module unload. Frees the control request and device - * structure. - */ -static void /* __init_or_exit */ gs_unbind(struct usb_gadget *gadget) -{ - struct gs_dev *dev = get_gadget_data(gadget); + status = usb_string_id(cdev); + if (status < 0) + goto fail; + strings_dev[STRING_PRODUCT_IDX].id = status; - gs_device = NULL; + device_desc.iProduct = status; - /* read/write requests already freed, only control request remains */ - if (dev != NULL) { - if (dev->dev_ctrl_req != NULL) { - gs_free_req(gadget->ep0, dev->dev_ctrl_req); - dev->dev_ctrl_req = NULL; - } - gs_reset_config(dev); - gs_free_ports(dev); - kfree(dev); - set_gadget_data(gadget, NULL); - } + /* config description */ + status = usb_string_id(cdev); + if (status < 0) + goto fail; + strings_dev[STRING_DESCRIPTION_IDX].id = status; - pr_info("gs_unbind: %s %s unbound\n", GS_LONG_NAME, - GS_VERSION_STR); -} - -/* - * gs_bind - * - * Called on module load. Allocates and initializes the device - * structure and a control request. - */ -static int __init gs_bind(struct usb_gadget *gadget) -{ - int ret; - struct usb_ep *ep; - struct gs_dev *dev; - int gcnum; - - /* Some controllers can't support CDC ACM: - * - sh doesn't support multiple interfaces or configs; - * - sa1100 doesn't have a third interrupt endpoint - */ - if (gadget_is_sh(gadget) || gadget_is_sa1100(gadget)) - use_acm = 0; + serial_config_driver.iConfiguration = status; + /* set up other descriptors */ gcnum = usb_gadget_controller_number(gadget); if (gcnum >= 0) - gs_device_desc.bcdDevice = - cpu_to_le16(GS_VERSION_NUM | gcnum); + device_desc.bcdDevice = cpu_to_le16(GS_VERSION_NUM | gcnum); else { + /* this is so simple (for now, no altsettings) that it + * SHOULD NOT have problems with bulk-capable hardware. + * so warn about unrcognized controllers -- don't panic. + * + * things like configuration and altsetting numbering + * can need hardware-specific attention though. + */ pr_warning("gs_bind: controller '%s' not recognized\n", gadget->name); - /* unrecognized, but safe unless bulk is REALLY quirky */ - gs_device_desc.bcdDevice = - __constant_cpu_to_le16(GS_VERSION_NUM|0x0099); - } - - dev = kzalloc(sizeof(struct gs_dev), GFP_KERNEL); - if (dev == NULL) - return -ENOMEM; - - usb_ep_autoconfig_reset(gadget); - - ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc); - if (!ep) - goto autoconf_fail; - dev->dev_in_ep = ep; - ep->driver_data = dev; /* claim the endpoint */ - - ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc); - if (!ep) - goto autoconf_fail; - dev->dev_out_ep = ep; - ep->driver_data = dev; /* claim the endpoint */ - - if (use_acm) { - ep = usb_ep_autoconfig(gadget, &gs_fullspeed_notify_desc); - if (!ep) { - pr_err("gs_bind: cannot run ACM on %s\n", gadget->name); - goto autoconf_fail; - } - gs_device_desc.idProduct = __constant_cpu_to_le16( - GS_CDC_PRODUCT_ID), - dev->dev_notify_ep = ep; - ep->driver_data = dev; /* claim the endpoint */ - } - - gs_device_desc.bDeviceClass = use_acm - ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC; - gs_device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; - - if (gadget_is_dualspeed(gadget)) { - gs_qualifier_desc.bDeviceClass = use_acm - ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC; - /* assume ep0 uses the same packet size for both speeds */ - gs_qualifier_desc.bMaxPacketSize0 = - gs_device_desc.bMaxPacketSize0; - /* assume endpoints are dual-speed */ - gs_highspeed_notify_desc.bEndpointAddress = - gs_fullspeed_notify_desc.bEndpointAddress; - gs_highspeed_in_desc.bEndpointAddress = - gs_fullspeed_in_desc.bEndpointAddress; - gs_highspeed_out_desc.bEndpointAddress = - gs_fullspeed_out_desc.bEndpointAddress; - } - - usb_gadget_set_selfpowered(gadget); - - if (gadget_is_otg(gadget)) { - gs_otg_descriptor.bmAttributes |= USB_OTG_HNP, - gs_bulk_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - gs_acm_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + device_desc.bcdDevice = + __constant_cpu_to_le16(GS_VERSION_NUM | 0x0099); } - gs_device = dev; - - snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); - - dev->dev_gadget = gadget; - spin_lock_init(&dev->dev_lock); - INIT_LIST_HEAD(&dev->dev_req_list); - set_gadget_data(gadget, dev); - - if ((ret=gs_alloc_ports(dev, GFP_KERNEL)) != 0) { - pr_err("gs_bind: cannot allocate ports\n"); - gs_unbind(gadget); - return ret; + if (gadget_is_otg(cdev->gadget)) { + serial_config_driver.descriptors = otg_desc; + serial_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - /* preallocate control response and buffer */ - dev->dev_ctrl_req = gs_alloc_req(gadget->ep0, GS_MAX_DESC_LEN, - GFP_KERNEL); - if (dev->dev_ctrl_req == NULL) { - gs_unbind(gadget); - return -ENOMEM; - } - gadget->ep0->driver_data = dev; + /* register our configuration */ + status = usb_add_config(cdev, &serial_config_driver); + if (status < 0) + goto fail; - pr_info("gs_bind: %s %s bound\n", - GS_LONG_NAME, GS_VERSION_STR); + INFO(cdev, "%s\n", GS_VERSION_NAME); return 0; -autoconf_fail: - kfree(dev); - pr_err("gs_bind: cannot autoconfigure on %s\n", gadget->name); - return -ENODEV; -} - -static int gs_setup_standard(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl) -{ - int ret = -EOPNOTSUPP; - struct gs_dev *dev = get_gadget_data(gadget); - struct usb_request *req = dev->dev_ctrl_req; - u16 wIndex = le16_to_cpu(ctrl->wIndex); - u16 wValue = le16_to_cpu(ctrl->wValue); - u16 wLength = le16_to_cpu(ctrl->wLength); - - switch (ctrl->bRequest) { - case USB_REQ_GET_DESCRIPTOR: - if (ctrl->bRequestType != USB_DIR_IN) - break; - - switch (wValue >> 8) { - case USB_DT_DEVICE: - ret = min(wLength, - (u16)sizeof(struct usb_device_descriptor)); - memcpy(req->buf, &gs_device_desc, ret); - break; - - case USB_DT_DEVICE_QUALIFIER: - if (!gadget_is_dualspeed(gadget)) - break; - ret = min(wLength, - (u16)sizeof(struct usb_qualifier_descriptor)); - memcpy(req->buf, &gs_qualifier_desc, ret); - break; - - case USB_DT_OTHER_SPEED_CONFIG: - if (!gadget_is_dualspeed(gadget)) - break; - /* fall through */ - case USB_DT_CONFIG: - ret = gs_build_config_buf(req->buf, gadget, - wValue >> 8, wValue & 0xff, - gadget_is_otg(gadget)); - if (ret >= 0) - ret = min(wLength, (u16)ret); - break; - - case USB_DT_STRING: - /* wIndex == language code. */ - ret = usb_gadget_get_string(&gs_string_table, - wValue & 0xff, req->buf); - if (ret >= 0) - ret = min(wLength, (u16)ret); - break; - } - break; - - case USB_REQ_SET_CONFIGURATION: - if (ctrl->bRequestType != 0) - break; - spin_lock(&dev->dev_lock); - ret = gs_set_config(dev, wValue); - spin_unlock(&dev->dev_lock); - break; - - case USB_REQ_GET_CONFIGURATION: - if (ctrl->bRequestType != USB_DIR_IN) - break; - *(u8 *)req->buf = dev->dev_config; - ret = min(wLength, (u16)1); - break; - - case USB_REQ_SET_INTERFACE: - if (ctrl->bRequestType != USB_RECIP_INTERFACE - || !dev->dev_config - || wIndex >= GS_MAX_NUM_INTERFACES) - break; - if (dev->dev_config == GS_BULK_CONFIG_ID - && wIndex != GS_BULK_INTERFACE_ID) - break; - /* no alternate interface settings */ - if (wValue != 0) - break; - spin_lock(&dev->dev_lock); - /* PXA hardware partially handles SET_INTERFACE; - * we need to kluge around that interference. */ - if (gadget_is_pxa(gadget)) { - ret = gs_set_config(dev, use_acm ? - GS_ACM_CONFIG_ID : GS_BULK_CONFIG_ID); - goto set_interface_done; - } - if (dev->dev_config != GS_BULK_CONFIG_ID - && wIndex == GS_CONTROL_INTERFACE_ID) { - if (dev->dev_notify_ep) { - usb_ep_disable(dev->dev_notify_ep); - usb_ep_enable(dev->dev_notify_ep, dev->dev_notify_ep_desc); - } - } else { - usb_ep_disable(dev->dev_in_ep); - usb_ep_disable(dev->dev_out_ep); - usb_ep_enable(dev->dev_in_ep, dev->dev_in_ep_desc); - usb_ep_enable(dev->dev_out_ep, dev->dev_out_ep_desc); - } - ret = 0; -set_interface_done: - spin_unlock(&dev->dev_lock); - break; - - case USB_REQ_GET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE) - || dev->dev_config == GS_NO_CONFIG_ID) - break; - if (wIndex >= GS_MAX_NUM_INTERFACES - || (dev->dev_config == GS_BULK_CONFIG_ID - && wIndex != GS_BULK_INTERFACE_ID)) { - ret = -EDOM; - break; - } - /* no alternate interface settings */ - *(u8 *)req->buf = 0; - ret = min(wLength, (u16)1); - break; - - default: - pr_err("gs_setup: unknown standard request, type=%02x, " - "request=%02x, value=%04x, index=%04x, length=%d\n", - ctrl->bRequestType, ctrl->bRequest, - wValue, wIndex, wLength); - break; - } - - return ret; +fail: + gserial_cleanup(); + return status; } -static void gs_setup_complete_set_line_coding(struct usb_ep *ep, - struct usb_request *req) -{ - struct gs_dev *dev = ep->driver_data; - struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */ - - switch (req->status) { - case 0: - /* normal completion */ - if (req->actual != sizeof(port->port_line_coding)) - usb_ep_set_halt(ep); - else if (port) { - struct usb_cdc_line_coding *value = req->buf; - - /* REVISIT: we currently just remember this data. - * If we change that, (a) validate it first, then - * (b) update whatever hardware needs updating. - */ - spin_lock(&port->port_lock); - port->port_line_coding = *value; - spin_unlock(&port->port_lock); - } - break; - - case -ESHUTDOWN: - /* disconnect */ - gs_free_req(ep, req); - break; - - default: - /* unexpected */ - break; - } - return; -} - -static int gs_setup_class(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl) -{ - int ret = -EOPNOTSUPP; - struct gs_dev *dev = get_gadget_data(gadget); - struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */ - struct usb_request *req = dev->dev_ctrl_req; - u16 wIndex = le16_to_cpu(ctrl->wIndex); - u16 wValue = le16_to_cpu(ctrl->wValue); - u16 wLength = le16_to_cpu(ctrl->wLength); - - switch (ctrl->bRequest) { - case USB_CDC_REQ_SET_LINE_CODING: - if (wLength != sizeof(struct usb_cdc_line_coding)) - break; - ret = wLength; - req->complete = gs_setup_complete_set_line_coding; - break; - - case USB_CDC_REQ_GET_LINE_CODING: - ret = min_t(int, wLength, sizeof(struct usb_cdc_line_coding)); - if (port) { - spin_lock(&port->port_lock); - memcpy(req->buf, &port->port_line_coding, ret); - spin_unlock(&port->port_lock); - } - break; - - case USB_CDC_REQ_SET_CONTROL_LINE_STATE: - if (wLength != 0) - break; - ret = 0; - if (port) { - /* REVISIT: we currently just remember this data. - * If we change that, update whatever hardware needs - * updating. - */ - spin_lock(&port->port_lock); - port->port_handshake_bits = wValue; - spin_unlock(&port->port_lock); - } - break; - - default: - /* NOTE: strictly speaking, we should accept AT-commands - * using SEND_ENCPSULATED_COMMAND/GET_ENCAPSULATED_RESPONSE. - * But our call management descriptor says we don't handle - * call management, so we should be able to get by without - * handling those "required" commands (except by stalling). - */ - pr_err("gs_setup: unknown class request, " - "type=%02x, request=%02x, value=%04x, " - "index=%04x, length=%d\n", - ctrl->bRequestType, ctrl->bRequest, - wValue, wIndex, wLength); - break; - } - - return ret; -} - -/* - * gs_setup_complete - */ -static void gs_setup_complete(struct usb_ep *ep, struct usb_request *req) -{ - if (req->status || req->actual != req->length) { - pr_err("gs_setup_complete: status error, status=%d, " - "actual=%d, length=%d\n", - req->status, req->actual, req->length); - } -} - -/* - * gs_setup - * - * Implements all the control endpoint functionality that's not - * handled in hardware or the hardware driver. - * - * Returns the size of the data sent to the host, or a negative - * error number. - */ -static int gs_setup(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl) -{ - int ret = -EOPNOTSUPP; - struct gs_dev *dev = get_gadget_data(gadget); - struct usb_request *req = dev->dev_ctrl_req; - u16 wIndex = le16_to_cpu(ctrl->wIndex); - u16 wValue = le16_to_cpu(ctrl->wValue); - u16 wLength = le16_to_cpu(ctrl->wLength); - - req->complete = gs_setup_complete; - - switch (ctrl->bRequestType & USB_TYPE_MASK) { - case USB_TYPE_STANDARD: - ret = gs_setup_standard(gadget, ctrl); - break; - - case USB_TYPE_CLASS: - ret = gs_setup_class(gadget, ctrl); - break; - - default: - pr_err("gs_setup: unknown request, type=%02x, request=%02x, " - "value=%04x, index=%04x, length=%d\n", - ctrl->bRequestType, ctrl->bRequest, - wValue, wIndex, wLength); - break; - } - - /* respond with data transfer before status phase? */ - if (ret >= 0) { - req->length = ret; - req->zero = ret < wLength - && (ret % gadget->ep0->maxpacket) == 0; - ret = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); - if (ret < 0) { - pr_err("gs_setup: cannot queue response, ret=%d\n", - ret); - req->status = 0; - gs_setup_complete(gadget->ep0, req); - } - } - - /* device either stalls (ret < 0) or reports success */ - return ret; -} - -/* - * gs_disconnect - * - * Called when the device is disconnected. Frees the closed - * ports and disconnects open ports. Open ports will be freed - * on close. Then reallocates the ports for the next connection. - */ -static void gs_disconnect(struct usb_gadget *gadget) -{ - unsigned long flags; - struct gs_dev *dev = get_gadget_data(gadget); - - spin_lock_irqsave(&dev->dev_lock, flags); - - gs_reset_config(dev); - - /* free closed ports and disconnect open ports */ - /* (open ports will be freed when closed) */ - gs_free_ports(dev); - - /* re-allocate ports for the next connection */ - if (gs_alloc_ports(dev, GFP_ATOMIC) != 0) - pr_err("gs_disconnect: cannot re-allocate ports\n"); - - spin_unlock_irqrestore(&dev->dev_lock, flags); - - pr_info("gs_disconnect: %s disconnected\n", GS_LONG_NAME); -} - -static struct usb_gadget_driver gs_gadget_driver = { -#ifdef CONFIG_USB_GADGET_DUALSPEED - .speed = USB_SPEED_HIGH, -#else - .speed = USB_SPEED_FULL, -#endif /* CONFIG_USB_GADGET_DUALSPEED */ - .function = GS_LONG_NAME, - .bind = gs_bind, - .unbind = gs_unbind, - .setup = gs_setup, - .disconnect = gs_disconnect, - .driver = { - .name = GS_SHORT_NAME, - .owner = THIS_MODULE, - }, +static struct usb_composite_driver gserial_driver = { + .name = "g_serial", + .dev = &device_desc, + .strings = dev_strings, + .bind = gs_bind, }; -/* - * gs_set_config - * - * Configures the device by enabling device specific - * optimizations, setting up the endpoints, allocating - * read and write requests and queuing read requests. - * - * The device lock must be held when calling this function. - */ -static int gs_set_config(struct gs_dev *dev, unsigned config) +static int __init init(void) { - int i; - int ret = 0; - struct usb_gadget *gadget = dev->dev_gadget; - struct usb_ep *ep; - struct usb_endpoint_descriptor *out, *in, *notify; - struct usb_request *req; - - if (dev == NULL) { - pr_err("gs_set_config: NULL device pointer\n"); - return 0; - } - - if (config == dev->dev_config) - return 0; - - gs_reset_config(dev); - - switch (config) { - case GS_NO_CONFIG_ID: - return 0; - case GS_BULK_CONFIG_ID: - if (use_acm) - return -EINVAL; - break; - case GS_ACM_CONFIG_ID: - if (!use_acm) - return -EINVAL; - break; - default: - return -EINVAL; - } - - in = choose_ep_desc(gadget, - &gs_highspeed_in_desc, - &gs_fullspeed_in_desc); - out = choose_ep_desc(gadget, - &gs_highspeed_out_desc, - &gs_fullspeed_out_desc); - notify = dev->dev_notify_ep - ? choose_ep_desc(gadget, - &gs_highspeed_notify_desc, - &gs_fullspeed_notify_desc) - : NULL; - - ret = usb_ep_enable(dev->dev_in_ep, in); - if (ret == 0) { - dev->dev_in_ep_desc = in; - } else { - pr_debug("%s: cannot enable %s %s, ret=%d\n", - __func__, "IN", dev->dev_in_ep->name, ret); - return ret; - } - - ret = usb_ep_enable(dev->dev_out_ep, out); - if (ret == 0) { - dev->dev_out_ep_desc = out; - } else { - pr_debug("%s: cannot enable %s %s, ret=%d\n", - __func__, "OUT", dev->dev_out_ep->name, ret); -fail0: - usb_ep_disable(dev->dev_in_ep); - return ret; - } - - if (notify) { - ret = usb_ep_enable(dev->dev_notify_ep, notify); - if (ret == 0) { - dev->dev_notify_ep_desc = notify; - } else { - pr_debug("%s: cannot enable %s %s, ret=%d\n", - __func__, "NOTIFY", - dev->dev_notify_ep->name, ret); - usb_ep_disable(dev->dev_out_ep); - goto fail0; - } - } - - dev->dev_config = config; - - /* allocate and queue read requests */ - ep = dev->dev_out_ep; - for (i=0; i<read_q_size && ret == 0; i++) { - if ((req=gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC))) { - req->complete = gs_read_complete; - if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) { - pr_err("gs_set_config: cannot queue read " - "request, ret=%d\n", ret); - } - } else { - pr_err("gs_set_config: cannot allocate " - "read requests\n"); - ret = -ENOMEM; - goto exit_reset_config; - } - } - - /* allocate write requests, and put on free list */ - ep = dev->dev_in_ep; - for (i=0; i<write_q_size; i++) { - req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC); - if (req) { - req->complete = gs_write_complete; - list_add(&req->list, &dev->dev_req_list); - } else { - pr_err("gs_set_config: cannot allocate " - "write requests\n"); - ret = -ENOMEM; - goto exit_reset_config; - } - } - - /* REVISIT the ACM mode should be able to actually *issue* some - * notifications, for at least serial state change events if - * not also for network connection; say so in bmCapabilities. + /* We *could* export two configs; that'd be much cleaner... + * but neither of these product IDs was defined that way. */ - - pr_info("gs_set_config: %s configured, %s speed %s config\n", - GS_LONG_NAME, - gadget->speed == USB_SPEED_HIGH ? "high" : "full", - config == GS_BULK_CONFIG_ID ? "BULK" : "CDC-ACM"); - - return 0; - -exit_reset_config: - gs_reset_config(dev); - return ret; -} - -/* - * gs_reset_config - * - * Mark the device as not configured, disable all endpoints, - * which forces completion of pending I/O and frees queued - * requests, and free the remaining write requests on the - * free list. - * - * The device lock must be held when calling this function. - */ -static void gs_reset_config(struct gs_dev *dev) -{ - struct usb_request *req; - - if (dev == NULL) { - pr_err("gs_reset_config: NULL device pointer\n"); - return; - } - - if (dev->dev_config == GS_NO_CONFIG_ID) - return; - - dev->dev_config = GS_NO_CONFIG_ID; - - /* free write requests on the free list */ - while(!list_empty(&dev->dev_req_list)) { - req = list_entry(dev->dev_req_list.next, - struct usb_request, list); - list_del(&req->list); - gs_free_req(dev->dev_in_ep, req); - } - - /* disable endpoints, forcing completion of pending i/o; */ - /* completion handlers free their requests in this case */ - if (dev->dev_notify_ep) - usb_ep_disable(dev->dev_notify_ep); - usb_ep_disable(dev->dev_in_ep); - usb_ep_disable(dev->dev_out_ep); -} - -/* - * gs_build_config_buf - * - * Builds the config descriptors in the given buffer and returns the - * length, or a negative error number. - */ -static int gs_build_config_buf(u8 *buf, struct usb_gadget *g, - u8 type, unsigned int index, int is_otg) -{ - int len; - int high_speed = 0; - const struct usb_config_descriptor *config_desc; - const struct usb_descriptor_header **function; - - if (index >= gs_device_desc.bNumConfigurations) - return -EINVAL; - - /* other speed switches high and full speed */ - if (gadget_is_dualspeed(g)) { - high_speed = (g->speed == USB_SPEED_HIGH); - if (type == USB_DT_OTHER_SPEED_CONFIG) - high_speed = !high_speed; - } - if (use_acm) { - config_desc = &gs_acm_config_desc; - function = high_speed - ? gs_acm_highspeed_function - : gs_acm_fullspeed_function; + serial_config_driver.label = "CDC ACM config"; + serial_config_driver.bConfigurationValue = 2; + device_desc.bDeviceClass = USB_CLASS_COMM; + device_desc.idProduct = + __constant_cpu_to_le16(GS_CDC_PRODUCT_ID); } else { - config_desc = &gs_bulk_config_desc; - function = high_speed - ? gs_bulk_highspeed_function - : gs_bulk_fullspeed_function; - } - - /* for now, don't advertise srp-only devices */ - if (!is_otg) - function++; - - len = usb_gadget_config_buf(config_desc, buf, GS_MAX_DESC_LEN, function); - if (len < 0) - return len; - - ((struct usb_config_descriptor *)buf)->bDescriptorType = type; - - return len; -} - -/* - * gs_alloc_req - * - * Allocate a usb_request and its buffer. Returns a pointer to the - * usb_request or NULL if there is an error. - */ -static struct usb_request * -gs_alloc_req(struct usb_ep *ep, unsigned int len, gfp_t kmalloc_flags) -{ - struct usb_request *req; - - if (ep == NULL) - return NULL; - - req = usb_ep_alloc_request(ep, kmalloc_flags); - - if (req != NULL) { - req->length = len; - req->buf = kmalloc(len, kmalloc_flags); - if (req->buf == NULL) { - usb_ep_free_request(ep, req); - return NULL; - } + serial_config_driver.label = "Generic Serial config"; + serial_config_driver.bConfigurationValue = 1; + device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; + device_desc.idProduct = + __constant_cpu_to_le16(GS_PRODUCT_ID); } + strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label; - return req; + return usb_composite_register(&gserial_driver); } +module_init(init); -/* - * gs_free_req - * - * Free a usb_request and its buffer. - */ -static void gs_free_req(struct usb_ep *ep, struct usb_request *req) +static void __exit cleanup(void) { - if (ep != NULL && req != NULL) { - kfree(req->buf); - usb_ep_free_request(ep, req); - } -} - -/* - * gs_alloc_ports - * - * Allocate all ports and set the gs_dev struct to point to them. - * Return 0 if successful, or a negative error number. - * - * The device lock is normally held when calling this function. - */ -static int gs_alloc_ports(struct gs_dev *dev, gfp_t kmalloc_flags) -{ - int i; - struct gs_port *port; - - if (dev == NULL) - return -EIO; - - for (i=0; i<GS_NUM_PORTS; i++) { - if ((port=kzalloc(sizeof(struct gs_port), kmalloc_flags)) == NULL) - return -ENOMEM; - - port->port_dev = dev; - port->port_num = i; - port->port_line_coding.dwDTERate = cpu_to_le32(GS_DEFAULT_DTE_RATE); - port->port_line_coding.bCharFormat = GS_DEFAULT_CHAR_FORMAT; - port->port_line_coding.bParityType = GS_DEFAULT_PARITY; - port->port_line_coding.bDataBits = GS_DEFAULT_DATA_BITS; - spin_lock_init(&port->port_lock); - init_waitqueue_head(&port->port_write_wait); - - dev->dev_port[i] = port; - } - - return 0; -} - -/* - * gs_free_ports - * - * Free all closed ports. Open ports are disconnected by - * freeing their write buffers, setting their device pointers - * and the pointers to them in the device to NULL. These - * ports will be freed when closed. - * - * The device lock is normally held when calling this function. - */ -static void gs_free_ports(struct gs_dev *dev) -{ - int i; - unsigned long flags; - struct gs_port *port; - - if (dev == NULL) - return; - - for (i=0; i<GS_NUM_PORTS; i++) { - if ((port=dev->dev_port[i]) != NULL) { - dev->dev_port[i] = NULL; - - spin_lock_irqsave(&port->port_lock, flags); - - if (port->port_write_buf != NULL) { - gs_buf_free(port->port_write_buf); - port->port_write_buf = NULL; - } - - if (port->port_open_count > 0 || port->port_in_use) { - port->port_dev = NULL; - wake_up_interruptible(&port->port_write_wait); - if (port->port_tty) { - tty_hangup(port->port_tty); - } - spin_unlock_irqrestore(&port->port_lock, flags); - } else { - spin_unlock_irqrestore(&port->port_lock, flags); - kfree(port); - } - - } - } -} - -/*-------------------------------------------------------------------------*/ - -/* Circular Buffer */ - -/* - * gs_buf_alloc - * - * Allocate a circular buffer and all associated memory. - */ -static struct gs_buf *gs_buf_alloc(unsigned int size, gfp_t kmalloc_flags) -{ - struct gs_buf *gb; - - if (size == 0) - return NULL; - - gb = kmalloc(sizeof(struct gs_buf), kmalloc_flags); - if (gb == NULL) - return NULL; - - gb->buf_buf = kmalloc(size, kmalloc_flags); - if (gb->buf_buf == NULL) { - kfree(gb); - return NULL; - } - - gb->buf_size = size; - gb->buf_get = gb->buf_put = gb->buf_buf; - - return gb; -} - -/* - * gs_buf_free - * - * Free the buffer and all associated memory. - */ -static void gs_buf_free(struct gs_buf *gb) -{ - if (gb) { - kfree(gb->buf_buf); - kfree(gb); - } -} - -/* - * gs_buf_clear - * - * Clear out all data in the circular buffer. - */ -static void gs_buf_clear(struct gs_buf *gb) -{ - if (gb != NULL) - gb->buf_get = gb->buf_put; - /* equivalent to a get of all data available */ -} - -/* - * gs_buf_data_avail - * - * Return the number of bytes of data available in the circular - * buffer. - */ -static unsigned int gs_buf_data_avail(struct gs_buf *gb) -{ - if (gb != NULL) - return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size; - else - return 0; -} - -/* - * gs_buf_space_avail - * - * Return the number of bytes of space available in the circular - * buffer. - */ -static unsigned int gs_buf_space_avail(struct gs_buf *gb) -{ - if (gb != NULL) - return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size; - else - return 0; -} - -/* - * gs_buf_put - * - * Copy data data from a user buffer and put it into the circular buffer. - * Restrict to the amount of space available. - * - * Return the number of bytes copied. - */ -static unsigned int -gs_buf_put(struct gs_buf *gb, const char *buf, unsigned int count) -{ - unsigned int len; - - if (gb == NULL) - return 0; - - len = gs_buf_space_avail(gb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = gb->buf_buf + gb->buf_size - gb->buf_put; - if (count > len) { - memcpy(gb->buf_put, buf, len); - memcpy(gb->buf_buf, buf+len, count - len); - gb->buf_put = gb->buf_buf + count - len; - } else { - memcpy(gb->buf_put, buf, count); - if (count < len) - gb->buf_put += count; - else /* count == len */ - gb->buf_put = gb->buf_buf; - } - - return count; -} - -/* - * gs_buf_get - * - * Get data from the circular buffer and copy to the given buffer. - * Restrict to the amount of data available. - * - * Return the number of bytes copied. - */ -static unsigned int -gs_buf_get(struct gs_buf *gb, char *buf, unsigned int count) -{ - unsigned int len; - - if (gb == NULL) - return 0; - - len = gs_buf_data_avail(gb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = gb->buf_buf + gb->buf_size - gb->buf_get; - if (count > len) { - memcpy(buf, gb->buf_get, len); - memcpy(buf+len, gb->buf_buf, count - len); - gb->buf_get = gb->buf_buf + count - len; - } else { - memcpy(buf, gb->buf_get, count); - if (count < len) - gb->buf_get += count; - else /* count == len */ - gb->buf_get = gb->buf_buf; - } - - return count; -} - -/*-------------------------------------------------------------------------*/ - -static struct tty_driver *gs_tty_driver; - -/* - * gs_module_init - * - * Register as a USB gadget driver and a tty driver. - */ -static int __init gs_module_init(void) -{ - int i; - int retval; - - retval = usb_gadget_register_driver(&gs_gadget_driver); - if (retval) { - pr_err("gs_module_init: cannot register gadget driver, " - "ret=%d\n", retval); - return retval; - } - - gs_tty_driver = alloc_tty_driver(GS_NUM_PORTS); - if (!gs_tty_driver) - return -ENOMEM; - gs_tty_driver->owner = THIS_MODULE; - gs_tty_driver->driver_name = GS_SHORT_NAME; - gs_tty_driver->name = "ttygs"; - gs_tty_driver->major = GS_MAJOR; - gs_tty_driver->minor_start = GS_MINOR_START; - gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; - gs_tty_driver->subtype = SERIAL_TYPE_NORMAL; - gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - gs_tty_driver->init_termios = tty_std_termios; - /* must match GS_DEFAULT_DTE_RATE and friends */ - gs_tty_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - gs_tty_driver->init_termios.c_ispeed = GS_DEFAULT_DTE_RATE; - gs_tty_driver->init_termios.c_ospeed = GS_DEFAULT_DTE_RATE; - tty_set_operations(gs_tty_driver, &gs_tty_ops); - - for (i = 0; i < GS_NUM_PORTS; i++) - mutex_init(&gs_open_close_lock[i]); - - retval = tty_register_driver(gs_tty_driver); - if (retval) { - usb_gadget_unregister_driver(&gs_gadget_driver); - put_tty_driver(gs_tty_driver); - pr_err("gs_module_init: cannot register tty driver, " - "ret=%d\n", retval); - return retval; - } - - pr_info("gs_module_init: %s %s loaded\n", - GS_LONG_NAME, GS_VERSION_STR); - return 0; -} -module_init(gs_module_init); - -/* - * gs_module_exit - * - * Unregister as a tty driver and a USB gadget driver. - */ -static void __exit gs_module_exit(void) -{ - tty_unregister_driver(gs_tty_driver); - put_tty_driver(gs_tty_driver); - usb_gadget_unregister_driver(&gs_gadget_driver); - - pr_info("gs_module_exit: %s %s unloaded\n", - GS_LONG_NAME, GS_VERSION_STR); + usb_composite_unregister(&gserial_driver); + gserial_cleanup(); } -module_exit(gs_module_exit); +module_exit(cleanup); diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c new file mode 100644 index 000000000000..3791e6271903 --- /dev/null +++ b/drivers/usb/gadget/u_ether.c @@ -0,0 +1,964 @@ +/* + * u_ether.c -- Ethernet-over-USB link layer utilities for Gadget stack + * + * Copyright (C) 2003-2005,2008 David Brownell + * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger + * Copyright (C) 2008 Nokia 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define VERBOSE_DEBUG */ + +#include <linux/kernel.h> +#include <linux/utsname.h> +#include <linux/device.h> +#include <linux/ctype.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> + +#include "u_ether.h" + + +/* + * This component encapsulates the Ethernet link glue needed to provide + * one (!) network link through the USB gadget stack, normally "usb0". + * + * The control and data models are handled by the function driver which + * connects to this code; such as CDC Ethernet, "CDC Subset", or RNDIS. + * That includes all descriptor and endpoint management. + * + * Link level addressing is handled by this component using module + * parameters; if no such parameters are provided, random link level + * addresses are used. Each end of the link uses one address. The + * host end address is exported in various ways, and is often recorded + * in configuration databases. + * + * The driver which assembles each configuration using such a link is + * responsible for ensuring that each configuration includes at most one + * instance of is network link. (The network layer provides ways for + * this single "physical" link to be used by multiple virtual links.) + */ + +#define DRIVER_VERSION "29-May-2008" + +struct eth_dev { + /* lock is held while accessing port_usb + * or updating its backlink port_usb->ioport + */ + spinlock_t lock; + struct gether *port_usb; + + struct net_device *net; + struct usb_gadget *gadget; + + spinlock_t req_lock; /* guard {rx,tx}_reqs */ + struct list_head tx_reqs, rx_reqs; + atomic_t tx_qlen; + + unsigned header_len; + struct sk_buff *(*wrap)(struct sk_buff *skb); + int (*unwrap)(struct sk_buff *skb); + + struct work_struct work; + + unsigned long todo; +#define WORK_RX_MEMORY 0 + + bool zlp; + u8 host_mac[ETH_ALEN]; +}; + +/*-------------------------------------------------------------------------*/ + +#define RX_EXTRA 20 /* bytes guarding against rx overflows */ + +#define DEFAULT_QLEN 2 /* double buffering by default */ + + +#ifdef CONFIG_USB_GADGET_DUALSPEED + +static unsigned qmult = 5; +module_param(qmult, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(qmult, "queue length multiplier at high speed"); + +#else /* full speed (low speed doesn't do bulk) */ +#define qmult 1 +#endif + +/* for dual-speed hardware, use deeper queues at highspeed */ +static inline int qlen(struct usb_gadget *gadget) +{ + if (gadget_is_dualspeed(gadget) && gadget->speed == USB_SPEED_HIGH) + return qmult * DEFAULT_QLEN; + else + return DEFAULT_QLEN; +} + +/*-------------------------------------------------------------------------*/ + +/* REVISIT there must be a better way than having two sets + * of debug calls ... + */ + +#undef DBG +#undef VDBG +#undef ERROR +#undef INFO + +#define xprintk(d, level, fmt, args...) \ + printk(level "%s: " fmt , (d)->net->name , ## args) + +#ifdef DEBUG +#undef DEBUG +#define DBG(dev, fmt, args...) \ + xprintk(dev , KERN_DEBUG , fmt , ## args) +#else +#define DBG(dev, fmt, args...) \ + do { } while (0) +#endif /* DEBUG */ + +#ifdef VERBOSE_DEBUG +#define VDBG DBG +#else +#define VDBG(dev, fmt, args...) \ + do { } while (0) +#endif /* DEBUG */ + +#define ERROR(dev, fmt, args...) \ + xprintk(dev , KERN_ERR , fmt , ## args) +#define INFO(dev, fmt, args...) \ + xprintk(dev , KERN_INFO , fmt , ## args) + +/*-------------------------------------------------------------------------*/ + +/* NETWORK DRIVER HOOKUP (to the layer above this driver) */ + +static int eth_change_mtu(struct net_device *net, int new_mtu) +{ + struct eth_dev *dev = netdev_priv(net); + unsigned long flags; + int status = 0; + + /* don't change MTU on "live" link (peer won't know) */ + spin_lock_irqsave(&dev->lock, flags); + if (dev->port_usb) + status = -EBUSY; + else if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN) + status = -ERANGE; + else + net->mtu = new_mtu; + spin_unlock_irqrestore(&dev->lock, flags); + + return status; +} + +static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p) +{ + struct eth_dev *dev = netdev_priv(net); + + strlcpy(p->driver, "g_ether", sizeof p->driver); + strlcpy(p->version, DRIVER_VERSION, sizeof p->version); + strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version); + strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof p->bus_info); +} + +static u32 eth_get_link(struct net_device *net) +{ + struct eth_dev *dev = netdev_priv(net); + return dev->gadget->speed != USB_SPEED_UNKNOWN; +} + +/* REVISIT can also support: + * - WOL (by tracking suspends and issuing remote wakeup) + * - msglevel (implies updated messaging) + * - ... probably more ethtool ops + */ + +static struct ethtool_ops ops = { + .get_drvinfo = eth_get_drvinfo, + .get_link = eth_get_link +}; + +static void defer_kevent(struct eth_dev *dev, int flag) +{ + if (test_and_set_bit(flag, &dev->todo)) + return; + if (!schedule_work(&dev->work)) + ERROR(dev, "kevent %d may have been dropped\n", flag); + else + DBG(dev, "kevent %d scheduled\n", flag); +} + +static void rx_complete(struct usb_ep *ep, struct usb_request *req); + +static int +rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags) +{ + struct sk_buff *skb; + int retval = -ENOMEM; + size_t size = 0; + struct usb_ep *out; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + if (dev->port_usb) + out = dev->port_usb->out_ep; + else + out = NULL; + spin_unlock_irqrestore(&dev->lock, flags); + + if (!out) + return -ENOTCONN; + + + /* Padding up to RX_EXTRA handles minor disagreements with host. + * Normally we use the USB "terminate on short read" convention; + * so allow up to (N*maxpacket), since that memory is normally + * already allocated. Some hardware doesn't deal well with short + * reads (e.g. DMA must be N*maxpacket), so for now don't trim a + * byte off the end (to force hardware errors on overflow). + * + * RNDIS uses internal framing, and explicitly allows senders to + * pad to end-of-packet. That's potentially nice for speed, but + * means receivers can't recover lost synch on their own (because + * new packets don't only start after a short RX). + */ + size += sizeof(struct ethhdr) + dev->net->mtu + RX_EXTRA; + size += dev->port_usb->header_len; + size += out->maxpacket - 1; + size -= size % out->maxpacket; + + skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags); + if (skb == NULL) { + DBG(dev, "no rx skb\n"); + goto enomem; + } + + /* Some platforms perform better when IP packets are aligned, + * but on at least one, checksumming fails otherwise. Note: + * RNDIS headers involve variable numbers of LE32 values. + */ + skb_reserve(skb, NET_IP_ALIGN); + + req->buf = skb->data; + req->length = size; + req->complete = rx_complete; + req->context = skb; + + retval = usb_ep_queue(out, req, gfp_flags); + if (retval == -ENOMEM) +enomem: + defer_kevent(dev, WORK_RX_MEMORY); + if (retval) { + DBG(dev, "rx submit --> %d\n", retval); + if (skb) + dev_kfree_skb_any(skb); + spin_lock_irqsave(&dev->req_lock, flags); + list_add(&req->list, &dev->rx_reqs); + spin_unlock_irqrestore(&dev->req_lock, flags); + } + return retval; +} + +static void rx_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct sk_buff *skb = req->context; + struct eth_dev *dev = ep->driver_data; + int status = req->status; + + switch (status) { + + /* normal completion */ + case 0: + skb_put(skb, req->actual); + if (dev->unwrap) + status = dev->unwrap(skb); + if (status < 0 + || ETH_HLEN > skb->len + || skb->len > ETH_FRAME_LEN) { + dev->net->stats.rx_errors++; + dev->net->stats.rx_length_errors++; + DBG(dev, "rx length %d\n", skb->len); + break; + } + + skb->protocol = eth_type_trans(skb, dev->net); + dev->net->stats.rx_packets++; + dev->net->stats.rx_bytes += skb->len; + + /* no buffer copies needed, unless hardware can't + * use skb buffers. + */ + status = netif_rx(skb); + skb = NULL; + break; + + /* software-driven interface shutdown */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + VDBG(dev, "rx shutdown, code %d\n", status); + goto quiesce; + + /* for hardware automagic (such as pxa) */ + case -ECONNABORTED: /* endpoint reset */ + DBG(dev, "rx %s reset\n", ep->name); + defer_kevent(dev, WORK_RX_MEMORY); +quiesce: + dev_kfree_skb_any(skb); + goto clean; + + /* data overrun */ + case -EOVERFLOW: + dev->net->stats.rx_over_errors++; + /* FALLTHROUGH */ + + default: + dev->net->stats.rx_errors++; + DBG(dev, "rx status %d\n", status); + break; + } + + if (skb) + dev_kfree_skb_any(skb); + if (!netif_running(dev->net)) { +clean: + spin_lock(&dev->req_lock); + list_add(&req->list, &dev->rx_reqs); + spin_unlock(&dev->req_lock); + req = NULL; + } + if (req) + rx_submit(dev, req, GFP_ATOMIC); +} + +static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n) +{ + unsigned i; + struct usb_request *req; + + if (!n) + return -ENOMEM; + + /* queue/recycle up to N requests */ + i = n; + list_for_each_entry(req, list, list) { + if (i-- == 0) + goto extra; + } + while (i--) { + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (!req) + return list_empty(list) ? -ENOMEM : 0; + list_add(&req->list, list); + } + return 0; + +extra: + /* free extras */ + for (;;) { + struct list_head *next; + + next = req->list.next; + list_del(&req->list); + usb_ep_free_request(ep, req); + + if (next == list) + break; + + req = container_of(next, struct usb_request, list); + } + return 0; +} + +static int alloc_requests(struct eth_dev *dev, struct gether *link, unsigned n) +{ + int status; + + spin_lock(&dev->req_lock); + status = prealloc(&dev->tx_reqs, link->in_ep, n); + if (status < 0) + goto fail; + status = prealloc(&dev->rx_reqs, link->out_ep, n); + if (status < 0) + goto fail; + goto done; +fail: + DBG(dev, "can't alloc requests\n"); +done: + spin_unlock(&dev->req_lock); + return status; +} + +static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags) +{ + struct usb_request *req; + unsigned long flags; + + /* fill unused rxq slots with some skb */ + spin_lock_irqsave(&dev->req_lock, flags); + while (!list_empty(&dev->rx_reqs)) { + req = container_of(dev->rx_reqs.next, + struct usb_request, list); + list_del_init(&req->list); + spin_unlock_irqrestore(&dev->req_lock, flags); + + if (rx_submit(dev, req, gfp_flags) < 0) { + defer_kevent(dev, WORK_RX_MEMORY); + return; + } + + spin_lock_irqsave(&dev->req_lock, flags); + } + spin_unlock_irqrestore(&dev->req_lock, flags); +} + +static void eth_work(struct work_struct *work) +{ + struct eth_dev *dev = container_of(work, struct eth_dev, work); + + if (test_and_clear_bit(WORK_RX_MEMORY, &dev->todo)) { + if (netif_running(dev->net)) + rx_fill(dev, GFP_KERNEL); + } + + if (dev->todo) + DBG(dev, "work done, flags = 0x%lx\n", dev->todo); +} + +static void tx_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct sk_buff *skb = req->context; + struct eth_dev *dev = ep->driver_data; + + switch (req->status) { + default: + dev->net->stats.tx_errors++; + VDBG(dev, "tx err %d\n", req->status); + /* FALLTHROUGH */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + break; + case 0: + dev->net->stats.tx_bytes += skb->len; + } + dev->net->stats.tx_packets++; + + spin_lock(&dev->req_lock); + list_add(&req->list, &dev->tx_reqs); + spin_unlock(&dev->req_lock); + dev_kfree_skb_any(skb); + + atomic_dec(&dev->tx_qlen); + if (netif_carrier_ok(dev->net)) + netif_wake_queue(dev->net); +} + +static inline int is_promisc(u16 cdc_filter) +{ + return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS; +} + +static int eth_start_xmit(struct sk_buff *skb, struct net_device *net) +{ + struct eth_dev *dev = netdev_priv(net); + int length = skb->len; + int retval; + struct usb_request *req = NULL; + unsigned long flags; + struct usb_ep *in; + u16 cdc_filter; + + spin_lock_irqsave(&dev->lock, flags); + if (dev->port_usb) { + in = dev->port_usb->in_ep; + cdc_filter = dev->port_usb->cdc_filter; + } else { + in = NULL; + cdc_filter = 0; + } + spin_unlock_irqrestore(&dev->lock, flags); + + if (!in) { + dev_kfree_skb_any(skb); + return 0; + } + + /* apply outgoing CDC or RNDIS filters */ + if (!is_promisc(cdc_filter)) { + u8 *dest = skb->data; + + if (is_multicast_ether_addr(dest)) { + u16 type; + + /* ignores USB_CDC_PACKET_TYPE_MULTICAST and host + * SET_ETHERNET_MULTICAST_FILTERS requests + */ + if (is_broadcast_ether_addr(dest)) + type = USB_CDC_PACKET_TYPE_BROADCAST; + else + type = USB_CDC_PACKET_TYPE_ALL_MULTICAST; + if (!(cdc_filter & type)) { + dev_kfree_skb_any(skb); + return 0; + } + } + /* ignores USB_CDC_PACKET_TYPE_DIRECTED */ + } + + spin_lock_irqsave(&dev->req_lock, flags); + /* + * this freelist can be empty if an interrupt triggered disconnect() + * and reconfigured the gadget (shutting down this queue) after the + * network stack decided to xmit but before we got the spinlock. + */ + if (list_empty(&dev->tx_reqs)) { + spin_unlock_irqrestore(&dev->req_lock, flags); + return 1; + } + + req = container_of(dev->tx_reqs.next, struct usb_request, list); + list_del(&req->list); + + /* temporarily stop TX queue when the freelist empties */ + if (list_empty(&dev->tx_reqs)) + netif_stop_queue(net); + spin_unlock_irqrestore(&dev->req_lock, flags); + + /* no buffer copies needed, unless the network stack did it + * or the hardware can't use skb buffers. + * or there's not enough space for extra headers we need + */ + if (dev->wrap) { + struct sk_buff *skb_new; + + skb_new = dev->wrap(skb); + if (!skb_new) + goto drop; + + dev_kfree_skb_any(skb); + skb = skb_new; + length = skb->len; + } + req->buf = skb->data; + req->context = skb; + req->complete = tx_complete; + + /* use zlp framing on tx for strict CDC-Ether conformance, + * though any robust network rx path ignores extra padding. + * and some hardware doesn't like to write zlps. + */ + req->zero = 1; + if (!dev->zlp && (length % in->maxpacket) == 0) + length++; + + req->length = length; + + /* throttle highspeed IRQ rate back slightly */ + if (gadget_is_dualspeed(dev->gadget)) + req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH) + ? ((atomic_read(&dev->tx_qlen) % qmult) != 0) + : 0; + + retval = usb_ep_queue(in, req, GFP_ATOMIC); + switch (retval) { + default: + DBG(dev, "tx queue err %d\n", retval); + break; + case 0: + net->trans_start = jiffies; + atomic_inc(&dev->tx_qlen); + } + + if (retval) { +drop: + dev->net->stats.tx_dropped++; + dev_kfree_skb_any(skb); + spin_lock_irqsave(&dev->req_lock, flags); + if (list_empty(&dev->tx_reqs)) + netif_start_queue(net); + list_add(&req->list, &dev->tx_reqs); + spin_unlock_irqrestore(&dev->req_lock, flags); + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static void eth_start(struct eth_dev *dev, gfp_t gfp_flags) +{ + DBG(dev, "%s\n", __func__); + + /* fill the rx queue */ + rx_fill(dev, gfp_flags); + + /* and open the tx floodgates */ + atomic_set(&dev->tx_qlen, 0); + netif_wake_queue(dev->net); +} + +static int eth_open(struct net_device *net) +{ + struct eth_dev *dev = netdev_priv(net); + struct gether *link; + + DBG(dev, "%s\n", __func__); + if (netif_carrier_ok(dev->net)) + eth_start(dev, GFP_KERNEL); + + spin_lock_irq(&dev->lock); + link = dev->port_usb; + if (link && link->open) + link->open(link); + spin_unlock_irq(&dev->lock); + + return 0; +} + +static int eth_stop(struct net_device *net) +{ + struct eth_dev *dev = netdev_priv(net); + unsigned long flags; + + VDBG(dev, "%s\n", __func__); + netif_stop_queue(net); + + DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n", + dev->net->stats.rx_packets, dev->net->stats.tx_packets, + dev->net->stats.rx_errors, dev->net->stats.tx_errors + ); + + /* ensure there are no more active requests */ + spin_lock_irqsave(&dev->lock, flags); + if (dev->port_usb) { + struct gether *link = dev->port_usb; + + if (link->close) + link->close(link); + + /* NOTE: we have no abort-queue primitive we could use + * to cancel all pending I/O. Instead, we disable then + * reenable the endpoints ... this idiom may leave toggle + * wrong, but that's a self-correcting error. + * + * REVISIT: we *COULD* just let the transfers complete at + * their own pace; the network stack can handle old packets. + * For the moment we leave this here, since it works. + */ + usb_ep_disable(link->in_ep); + usb_ep_disable(link->out_ep); + if (netif_carrier_ok(net)) { + DBG(dev, "host still using in/out endpoints\n"); + usb_ep_enable(link->in_ep, link->in); + usb_ep_enable(link->out_ep, link->out); + } + } + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */ +static char *dev_addr; +module_param(dev_addr, charp, S_IRUGO); +MODULE_PARM_DESC(dev_addr, "Device Ethernet Address"); + +/* this address is invisible to ifconfig */ +static char *host_addr; +module_param(host_addr, charp, S_IRUGO); +MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); + + +static u8 __init nibble(unsigned char c) +{ + if (isdigit(c)) + return c - '0'; + c = toupper(c); + if (isxdigit(c)) + return 10 + c - 'A'; + return 0; +} + +static int __init get_ether_addr(const char *str, u8 *dev_addr) +{ + if (str) { + unsigned i; + + for (i = 0; i < 6; i++) { + unsigned char num; + + if ((*str == '.') || (*str == ':')) + str++; + num = nibble(*str++) << 4; + num |= (nibble(*str++)); + dev_addr [i] = num; + } + if (is_valid_ether_addr(dev_addr)) + return 0; + } + random_ether_addr(dev_addr); + return 1; +} + +static struct eth_dev *the_dev; + + +/** + * gether_setup - initialize one ethernet-over-usb link + * @g: gadget to associated with these links + * @ethaddr: NULL, or a buffer in which the ethernet address of the + * host side of the link is recorded + * Context: may sleep + * + * This sets up the single network link that may be exported by a + * gadget driver using this framework. The link layer addresses are + * set up using module parameters. + * + * Returns negative errno, or zero on success + */ +int __init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]) +{ + struct eth_dev *dev; + struct net_device *net; + int status; + + if (the_dev) + return -EBUSY; + + net = alloc_etherdev(sizeof *dev); + if (!net) + return -ENOMEM; + + dev = netdev_priv(net); + spin_lock_init(&dev->lock); + spin_lock_init(&dev->req_lock); + INIT_WORK(&dev->work, eth_work); + INIT_LIST_HEAD(&dev->tx_reqs); + INIT_LIST_HEAD(&dev->rx_reqs); + + /* network device setup */ + dev->net = net; + strcpy(net->name, "usb%d"); + + if (get_ether_addr(dev_addr, net->dev_addr)) + dev_warn(&g->dev, + "using random %s ethernet address\n", "self"); + if (get_ether_addr(host_addr, dev->host_mac)) + dev_warn(&g->dev, + "using random %s ethernet address\n", "host"); + + if (ethaddr) + memcpy(ethaddr, dev->host_mac, ETH_ALEN); + + net->change_mtu = eth_change_mtu; + net->hard_start_xmit = eth_start_xmit; + net->open = eth_open; + net->stop = eth_stop; + /* watchdog_timeo, tx_timeout ... */ + /* set_multicast_list */ + SET_ETHTOOL_OPS(net, &ops); + + /* two kinds of host-initiated state changes: + * - iff DATA transfer is active, carrier is "on" + * - tx queueing enabled if open *and* carrier is "on" + */ + netif_stop_queue(net); + netif_carrier_off(net); + + dev->gadget = g; + SET_NETDEV_DEV(net, &g->dev); + + status = register_netdev(net); + if (status < 0) { + dev_dbg(&g->dev, "register_netdev failed, %d\n", status); + free_netdev(net); + } else { + DECLARE_MAC_BUF(tmp); + + INFO(dev, "MAC %s\n", print_mac(tmp, net->dev_addr)); + INFO(dev, "HOST MAC %s\n", print_mac(tmp, dev->host_mac)); + + the_dev = dev; + } + + return status; +} + +/** + * gether_cleanup - remove Ethernet-over-USB device + * Context: may sleep + * + * This is called to free all resources allocated by @gether_setup(). + */ +void gether_cleanup(void) +{ + if (!the_dev) + return; + + unregister_netdev(the_dev->net); + free_netdev(the_dev->net); + + /* assuming we used keventd, it must quiesce too */ + flush_scheduled_work(); + + the_dev = NULL; +} + + +/** + * gether_connect - notify network layer that USB link is active + * @link: the USB link, set up with endpoints, descriptors matching + * current device speed, and any framing wrapper(s) set up. + * Context: irqs blocked + * + * This is called to activate endpoints and let the network layer know + * the connection is active ("carrier detect"). It may cause the I/O + * queues to open and start letting network packets flow, but will in + * any case activate the endpoints so that they respond properly to the + * USB host. + * + * Verify net_device pointer returned using IS_ERR(). If it doesn't + * indicate some error code (negative errno), ep->driver_data values + * have been overwritten. + */ +struct net_device *gether_connect(struct gether *link) +{ + struct eth_dev *dev = the_dev; + int result = 0; + + if (!dev) + return ERR_PTR(-EINVAL); + + link->in_ep->driver_data = dev; + result = usb_ep_enable(link->in_ep, link->in); + if (result != 0) { + DBG(dev, "enable %s --> %d\n", + link->in_ep->name, result); + goto fail0; + } + + link->out_ep->driver_data = dev; + result = usb_ep_enable(link->out_ep, link->out); + if (result != 0) { + DBG(dev, "enable %s --> %d\n", + link->out_ep->name, result); + goto fail1; + } + + if (result == 0) + result = alloc_requests(dev, link, qlen(dev->gadget)); + + if (result == 0) { + dev->zlp = link->is_zlp_ok; + DBG(dev, "qlen %d\n", qlen(dev->gadget)); + + dev->header_len = link->header_len; + dev->unwrap = link->unwrap; + dev->wrap = link->wrap; + + spin_lock(&dev->lock); + dev->port_usb = link; + link->ioport = dev; + spin_unlock(&dev->lock); + + netif_carrier_on(dev->net); + if (netif_running(dev->net)) + eth_start(dev, GFP_ATOMIC); + + /* on error, disable any endpoints */ + } else { + (void) usb_ep_disable(link->out_ep); +fail1: + (void) usb_ep_disable(link->in_ep); + } +fail0: + /* caller is responsible for cleanup on error */ + if (result < 0) + return ERR_PTR(result); + return dev->net; +} + +/** + * gether_disconnect - notify network layer that USB link is inactive + * @link: the USB link, on which gether_connect() was called + * Context: irqs blocked + * + * This is called to deactivate endpoints and let the network layer know + * the connection went inactive ("no carrier"). + * + * On return, the state is as if gether_connect() had never been called. + * The endpoints are inactive, and accordingly without active USB I/O. + * Pointers to endpoint descriptors and endpoint private data are nulled. + */ +void gether_disconnect(struct gether *link) +{ + struct eth_dev *dev = link->ioport; + struct usb_request *req; + + WARN_ON(!dev); + if (!dev) + return; + + DBG(dev, "%s\n", __func__); + + netif_stop_queue(dev->net); + netif_carrier_off(dev->net); + + /* disable endpoints, forcing (synchronous) completion + * of all pending i/o. then free the request objects + * and forget about the endpoints. + */ + usb_ep_disable(link->in_ep); + spin_lock(&dev->req_lock); + while (!list_empty(&dev->tx_reqs)) { + req = container_of(dev->tx_reqs.next, + struct usb_request, list); + list_del(&req->list); + + spin_unlock(&dev->req_lock); + usb_ep_free_request(link->in_ep, req); + spin_lock(&dev->req_lock); + } + spin_unlock(&dev->req_lock); + link->in_ep->driver_data = NULL; + link->in = NULL; + + usb_ep_disable(link->out_ep); + spin_lock(&dev->req_lock); + while (!list_empty(&dev->rx_reqs)) { + req = container_of(dev->rx_reqs.next, + struct usb_request, list); + list_del(&req->list); + + spin_unlock(&dev->req_lock); + usb_ep_free_request(link->out_ep, req); + spin_lock(&dev->req_lock); + } + spin_unlock(&dev->req_lock); + link->out_ep->driver_data = NULL; + link->out = NULL; + + /* finish forgetting about this USB link episode */ + dev->header_len = 0; + dev->unwrap = NULL; + dev->wrap = NULL; + + spin_lock(&dev->lock); + dev->port_usb = NULL; + link->ioport = NULL; + spin_unlock(&dev->lock); +} diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h new file mode 100644 index 000000000000..0d1f7ae3b071 --- /dev/null +++ b/drivers/usb/gadget/u_ether.h @@ -0,0 +1,127 @@ +/* + * u_ether.h -- interface to USB gadget "ethernet link" utilities + * + * Copyright (C) 2003-2005,2008 David Brownell + * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger + * Copyright (C) 2008 Nokia 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 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 __U_ETHER_H +#define __U_ETHER_H + +#include <linux/err.h> +#include <linux/if_ether.h> +#include <linux/usb/composite.h> +#include <linux/usb/cdc.h> + +#include "gadget_chips.h" + + +/* + * This represents the USB side of an "ethernet" link, managed by a USB + * function which provides control and (maybe) framing. Two functions + * in different configurations could share the same ethernet link/netdev, + * using different host interaction models. + * + * There is a current limitation that only one instance of this link may + * be present in any given configuration. When that's a problem, network + * layer facilities can be used to package multiple logical links on this + * single "physical" one. + */ +struct gether { + struct usb_function func; + + /* updated by gether_{connect,disconnect} */ + struct eth_dev *ioport; + + /* endpoints handle full and/or high speeds */ + struct usb_ep *in_ep; + struct usb_ep *out_ep; + + /* descriptors match device speed at gether_connect() time */ + struct usb_endpoint_descriptor *in; + struct usb_endpoint_descriptor *out; + + bool is_zlp_ok; + + u16 cdc_filter; + + /* hooks for added framing, as needed for RNDIS and EEM. + * we currently don't support multiple frames per SKB. + */ + u32 header_len; + struct sk_buff *(*wrap)(struct sk_buff *skb); + int (*unwrap)(struct sk_buff *skb); + + /* called on network open/close */ + void (*open)(struct gether *); + void (*close)(struct gether *); +}; + +#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \ + |USB_CDC_PACKET_TYPE_ALL_MULTICAST \ + |USB_CDC_PACKET_TYPE_PROMISCUOUS \ + |USB_CDC_PACKET_TYPE_DIRECTED) + + +/* netdev setup/teardown as directed by the gadget driver */ +int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]); +void gether_cleanup(void); + +/* connect/disconnect is handled by individual functions */ +struct net_device *gether_connect(struct gether *); +void gether_disconnect(struct gether *); + +/* Some controllers can't support CDC Ethernet (ECM) ... */ +static inline bool can_support_ecm(struct usb_gadget *gadget) +{ + if (!gadget_supports_altsettings(gadget)) + return false; + + /* SA1100 can do ECM, *without* status endpoint ... but we'll + * only use it in non-ECM mode for backwards compatibility + * (and since we currently require a status endpoint) + */ + if (gadget_is_sa1100(gadget)) + return false; + + /* Everything else is *presumably* fine ... but this is a bit + * chancy, so be **CERTAIN** there are no hardware issues with + * your controller. Add it above if it can't handle CDC. + */ + return true; +} + +/* each configuration may bind one instance of an ethernet link */ +int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); +int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); + +#ifdef CONFIG_USB_ETH_RNDIS + +int rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); + +#else + +static inline int +rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) +{ + return 0; +} + +#endif + +#endif /* __U_ETHER_H */ diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c new file mode 100644 index 000000000000..abf9505d3a75 --- /dev/null +++ b/drivers/usb/gadget/u_serial.c @@ -0,0 +1,1246 @@ +/* + * u_serial.c - utilities for USB gadget "serial port"/TTY support + * + * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) + * Copyright (C) 2008 David Brownell + * Copyright (C) 2008 by Nokia Corporation + * + * This code also borrows from usbserial.c, which is + * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2000 Peter Berger (pberger@brimson.com) + * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com) + * + * This software is distributed under the terms of the GNU General + * Public License ("GPL") as published by the Free Software Foundation, + * either version 2 of that License or (at your option) any later version. + */ + +/* #define VERBOSE_DEBUG */ + +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> + +#include "u_serial.h" + + +/* + * This component encapsulates the TTY layer glue needed to provide basic + * "serial port" functionality through the USB gadget stack. Each such + * port is exposed through a /dev/ttyGS* node. + * + * After initialization (gserial_setup), these TTY port devices stay + * available until they are removed (gserial_cleanup). Each one may be + * connected to a USB function (gserial_connect), or disconnected (with + * gserial_disconnect) when the USB host issues a config change event. + * Data can only flow when the port is connected to the host. + * + * A given TTY port can be made available in multiple configurations. + * For example, each one might expose a ttyGS0 node which provides a + * login application. In one case that might use CDC ACM interface 0, + * while another configuration might use interface 3 for that. The + * work to handle that (including descriptor management) is not part + * of this component. + * + * Configurations may expose more than one TTY port. For example, if + * ttyGS0 provides login service, then ttyGS1 might provide dialer access + * for a telephone or fax link. And ttyGS2 might be something that just + * needs a simple byte stream interface for some messaging protocol that + * is managed in userspace ... OBEX, PTP, and MTP have been mentioned. + */ + +/* + * gserial is the lifecycle interface, used by USB functions + * gs_port is the I/O nexus, used by the tty driver + * tty_struct links to the tty/filesystem framework + * + * gserial <---> gs_port ... links will be null when the USB link is + * inactive; managed by gserial_{connect,disconnect}(). + * gserial->ioport == usb_ep->driver_data ... gs_port + * gs_port->port_usb ... gserial + * + * gs_port <---> tty_struct ... links will be null when the TTY file + * isn't opened; managed by gs_open()/gs_close() + * gserial->port_tty ... tty_struct + * tty_struct->driver_data ... gserial + */ + +/* RX and TX queues can buffer QUEUE_SIZE packets before they hit the + * next layer of buffering. For TX that's a circular buffer; for RX + * consider it a NOP. A third layer is provided by the TTY code. + */ +#define QUEUE_SIZE 16 +#define WRITE_BUF_SIZE 8192 /* TX only */ + +/* circular buffer */ +struct gs_buf { + unsigned buf_size; + char *buf_buf; + char *buf_get; + char *buf_put; +}; + +/* + * The port structure holds info for each port, one for each minor number + * (and thus for each /dev/ node). + */ +struct gs_port { + spinlock_t port_lock; /* guard port_* access */ + + struct gserial *port_usb; + struct tty_struct *port_tty; + + unsigned open_count; + bool openclose; /* open/close in progress */ + u8 port_num; + + wait_queue_head_t close_wait; /* wait for last close */ + + struct list_head read_pool; + struct tasklet_struct push; + + struct list_head write_pool; + struct gs_buf port_write_buf; + wait_queue_head_t drain_wait; /* wait while writes drain */ + + /* REVISIT this state ... */ + struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ +}; + +/* increase N_PORTS if you need more */ +#define N_PORTS 4 +static struct portmaster { + struct mutex lock; /* protect open/close */ + struct gs_port *port; +} ports[N_PORTS]; +static unsigned n_ports; + +#define GS_CLOSE_TIMEOUT 15 /* seconds */ + + + +#ifdef VERBOSE_DEBUG +#define pr_vdebug(fmt, arg...) \ + pr_debug(fmt, ##arg) +#else +#define pr_vdebug(fmt, arg...) \ + ({ if (0) pr_debug(fmt, ##arg); }) +#endif + +/*-------------------------------------------------------------------------*/ + +/* Circular Buffer */ + +/* + * gs_buf_alloc + * + * Allocate a circular buffer and all associated memory. + */ +static int gs_buf_alloc(struct gs_buf *gb, unsigned size) +{ + gb->buf_buf = kmalloc(size, GFP_KERNEL); + if (gb->buf_buf == NULL) + return -ENOMEM; + + gb->buf_size = size; + gb->buf_put = gb->buf_buf; + gb->buf_get = gb->buf_buf; + + return 0; +} + +/* + * gs_buf_free + * + * Free the buffer and all associated memory. + */ +static void gs_buf_free(struct gs_buf *gb) +{ + kfree(gb->buf_buf); + gb->buf_buf = NULL; +} + +/* + * gs_buf_clear + * + * Clear out all data in the circular buffer. + */ +static void gs_buf_clear(struct gs_buf *gb) +{ + gb->buf_get = gb->buf_put; + /* equivalent to a get of all data available */ +} + +/* + * gs_buf_data_avail + * + * Return the number of bytes of data available in the circular + * buffer. + */ +static unsigned gs_buf_data_avail(struct gs_buf *gb) +{ + return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size; +} + +/* + * gs_buf_space_avail + * + * Return the number of bytes of space available in the circular + * buffer. + */ +static unsigned gs_buf_space_avail(struct gs_buf *gb) +{ + return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size; +} + +/* + * gs_buf_put + * + * Copy data data from a user buffer and put it into the circular buffer. + * Restrict to the amount of space available. + * + * Return the number of bytes copied. + */ +static unsigned +gs_buf_put(struct gs_buf *gb, const char *buf, unsigned count) +{ + unsigned len; + + len = gs_buf_space_avail(gb); + if (count > len) + count = len; + + if (count == 0) + return 0; + + len = gb->buf_buf + gb->buf_size - gb->buf_put; + if (count > len) { + memcpy(gb->buf_put, buf, len); + memcpy(gb->buf_buf, buf+len, count - len); + gb->buf_put = gb->buf_buf + count - len; + } else { + memcpy(gb->buf_put, buf, count); + if (count < len) + gb->buf_put += count; + else /* count == len */ + gb->buf_put = gb->buf_buf; + } + + return count; +} + +/* + * gs_buf_get + * + * Get data from the circular buffer and copy to the given buffer. + * Restrict to the amount of data available. + * + * Return the number of bytes copied. + */ +static unsigned +gs_buf_get(struct gs_buf *gb, char *buf, unsigned count) +{ + unsigned len; + + len = gs_buf_data_avail(gb); + if (count > len) + count = len; + + if (count == 0) + return 0; + + len = gb->buf_buf + gb->buf_size - gb->buf_get; + if (count > len) { + memcpy(buf, gb->buf_get, len); + memcpy(buf+len, gb->buf_buf, count - len); + gb->buf_get = gb->buf_buf + count - len; + } else { + memcpy(buf, gb->buf_get, count); + if (count < len) + gb->buf_get += count; + else /* count == len */ + gb->buf_get = gb->buf_buf; + } + + return count; +} + +/*-------------------------------------------------------------------------*/ + +/* I/O glue between TTY (upper) and USB function (lower) driver layers */ + +/* + * gs_alloc_req + * + * Allocate a usb_request and its buffer. Returns a pointer to the + * usb_request or NULL if there is an error. + */ +static struct usb_request * +gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, kmalloc_flags); + + if (req != NULL) { + req->length = len; + req->buf = kmalloc(len, kmalloc_flags); + if (req->buf == NULL) { + usb_ep_free_request(ep, req); + return NULL; + } + } + + return req; +} + +/* + * gs_free_req + * + * Free a usb_request and its buffer. + */ +static void gs_free_req(struct usb_ep *ep, struct usb_request *req) +{ + kfree(req->buf); + usb_ep_free_request(ep, req); +} + +/* + * gs_send_packet + * + * If there is data to send, a packet is built in the given + * buffer and the size is returned. If there is no data to + * send, 0 is returned. + * + * Called with port_lock held. + */ +static unsigned +gs_send_packet(struct gs_port *port, char *packet, unsigned size) +{ + unsigned len; + + len = gs_buf_data_avail(&port->port_write_buf); + if (len < size) + size = len; + if (size != 0) + size = gs_buf_get(&port->port_write_buf, packet, size); + return size; +} + +/* + * gs_start_tx + * + * This function finds available write requests, calls + * gs_send_packet to fill these packets with data, and + * continues until either there are no more write requests + * available or no more data to send. This function is + * run whenever data arrives or write requests are available. + * + * Context: caller owns port_lock; port_usb is non-null. + */ +static int gs_start_tx(struct gs_port *port) +/* +__releases(&port->port_lock) +__acquires(&port->port_lock) +*/ +{ + struct list_head *pool = &port->write_pool; + struct usb_ep *in = port->port_usb->in; + int status = 0; + bool do_tty_wake = false; + + while (!list_empty(pool)) { + struct usb_request *req; + int len; + + req = list_entry(pool->next, struct usb_request, list); + len = gs_send_packet(port, req->buf, in->maxpacket); + if (len == 0) { + wake_up_interruptible(&port->drain_wait); + break; + } + do_tty_wake = true; + + req->length = len; + list_del(&req->list); + +#ifdef VERBOSE_DEBUG + pr_debug("%s: %s, len=%d, 0x%02x 0x%02x 0x%02x ...\n", + __func__, in->name, len, *((u8 *)req->buf), + *((u8 *)req->buf+1), *((u8 *)req->buf+2)); +#endif + + /* Drop lock while we call out of driver; completions + * could be issued while we do so. Disconnection may + * happen too; maybe immediately before we queue this! + * + * NOTE that we may keep sending data for a while after + * the TTY closed (dev->ioport->port_tty is NULL). + */ + spin_unlock(&port->port_lock); + status = usb_ep_queue(in, req, GFP_ATOMIC); + spin_lock(&port->port_lock); + + if (status) { + pr_debug("%s: %s %s err %d\n", + __func__, "queue", in->name, status); + list_add(&req->list, pool); + break; + } + + /* abort immediately after disconnect */ + if (!port->port_usb) + break; + } + + if (do_tty_wake && port->port_tty) + tty_wakeup(port->port_tty); + return status; +} + +static void gs_rx_push(unsigned long _port) +{ + struct gs_port *port = (void *)_port; + struct tty_struct *tty = port->port_tty; + + /* With low_latency, tty_flip_buffer_push() doesn't put its + * real work through a workqueue, so the ldisc has a better + * chance to keep up with peak USB data rates. + */ + if (tty) { + tty_flip_buffer_push(tty); + wake_up_interruptible(&tty->read_wait); + } +} + +/* + * gs_recv_packet + * + * Called for each USB packet received. Reads the packet + * header and stuffs the data in the appropriate tty buffer. + * Returns 0 if successful, or a negative error number. + * + * Called during USB completion routine, on interrupt time. + * With port_lock. + */ +static int gs_recv_packet(struct gs_port *port, char *packet, unsigned size) +{ + unsigned len; + struct tty_struct *tty; + + /* I/O completions can continue for a while after close(), until the + * request queue empties. Just discard any data we receive, until + * something reopens this TTY ... as if there were no HW flow control. + */ + tty = port->port_tty; + if (tty == NULL) { + pr_vdebug("%s: ttyGS%d, after close\n", + __func__, port->port_num); + return -EIO; + } + + len = tty_insert_flip_string(tty, packet, size); + if (len > 0) + tasklet_schedule(&port->push); + if (len < size) + pr_debug("%s: ttyGS%d, drop %d bytes\n", + __func__, port->port_num, size - len); + return 0; +} + +/* + * Context: caller owns port_lock, and port_usb is set + */ +static unsigned gs_start_rx(struct gs_port *port) +/* +__releases(&port->port_lock) +__acquires(&port->port_lock) +*/ +{ + struct list_head *pool = &port->read_pool; + struct usb_ep *out = port->port_usb->out; + unsigned started = 0; + + while (!list_empty(pool)) { + struct usb_request *req; + int status; + struct tty_struct *tty; + + /* no more rx if closed or throttled */ + tty = port->port_tty; + if (!tty || test_bit(TTY_THROTTLED, &tty->flags)) + break; + + req = list_entry(pool->next, struct usb_request, list); + list_del(&req->list); + req->length = out->maxpacket; + + /* drop lock while we call out; the controller driver + * may need to call us back (e.g. for disconnect) + */ + spin_unlock(&port->port_lock); + status = usb_ep_queue(out, req, GFP_ATOMIC); + spin_lock(&port->port_lock); + + if (status) { + pr_debug("%s: %s %s err %d\n", + __func__, "queue", out->name, status); + list_add(&req->list, pool); + break; + } + started++; + + /* abort immediately after disconnect */ + if (!port->port_usb) + break; + } + return started; +} + +static void gs_read_complete(struct usb_ep *ep, struct usb_request *req) +{ + int status; + struct gs_port *port = ep->driver_data; + + spin_lock(&port->port_lock); + list_add(&req->list, &port->read_pool); + + switch (req->status) { + case 0: + /* normal completion */ + status = gs_recv_packet(port, req->buf, req->actual); + if (status && status != -EIO) + pr_debug("%s: %s %s err %d\n", + __func__, "recv", ep->name, status); + gs_start_rx(port); + break; + + case -ESHUTDOWN: + /* disconnect */ + pr_vdebug("%s: %s shutdown\n", __func__, ep->name); + break; + + default: + /* presumably a transient fault */ + pr_warning("%s: unexpected %s status %d\n", + __func__, ep->name, req->status); + gs_start_rx(port); + break; + } + spin_unlock(&port->port_lock); +} + +static void gs_write_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct gs_port *port = ep->driver_data; + + spin_lock(&port->port_lock); + list_add(&req->list, &port->write_pool); + + switch (req->status) { + default: + /* presumably a transient fault */ + pr_warning("%s: unexpected %s status %d\n", + __func__, ep->name, req->status); + /* FALL THROUGH */ + case 0: + /* normal completion */ + gs_start_tx(port); + break; + + case -ESHUTDOWN: + /* disconnect */ + pr_vdebug("%s: %s shutdown\n", __func__, ep->name); + break; + } + + spin_unlock(&port->port_lock); +} + +static void gs_free_requests(struct usb_ep *ep, struct list_head *head) +{ + struct usb_request *req; + + while (!list_empty(head)) { + req = list_entry(head->next, struct usb_request, list); + list_del(&req->list); + gs_free_req(ep, req); + } +} + +static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head, + void (*fn)(struct usb_ep *, struct usb_request *)) +{ + int i; + struct usb_request *req; + + /* Pre-allocate up to QUEUE_SIZE transfers, but if we can't + * do quite that many this time, don't fail ... we just won't + * be as speedy as we might otherwise be. + */ + for (i = 0; i < QUEUE_SIZE; i++) { + req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC); + if (!req) + return list_empty(head) ? -ENOMEM : 0; + req->complete = fn; + list_add_tail(&req->list, head); + } + return 0; +} + +/** + * gs_start_io - start USB I/O streams + * @dev: encapsulates endpoints to use + * Context: holding port_lock; port_tty and port_usb are non-null + * + * We only start I/O when something is connected to both sides of + * this port. If nothing is listening on the host side, we may + * be pointlessly filling up our TX buffers and FIFO. + */ +static int gs_start_io(struct gs_port *port) +{ + struct list_head *head = &port->read_pool; + struct usb_ep *ep = port->port_usb->out; + int status; + unsigned started; + + /* Allocate RX and TX I/O buffers. We can't easily do this much + * earlier (with GFP_KERNEL) because the requests are coupled to + * endpoints, as are the packet sizes we'll be using. Different + * configurations may use different endpoints with a given port; + * and high speed vs full speed changes packet sizes too. + */ + status = gs_alloc_requests(ep, head, gs_read_complete); + if (status) + return status; + + status = gs_alloc_requests(port->port_usb->in, &port->write_pool, + gs_write_complete); + if (status) { + gs_free_requests(ep, head); + return status; + } + + /* queue read requests */ + started = gs_start_rx(port); + + /* unblock any pending writes into our circular buffer */ + if (started) { + tty_wakeup(port->port_tty); + } else { + gs_free_requests(ep, head); + gs_free_requests(port->port_usb->in, &port->write_pool); + } + + return started ? 0 : status; +} + +/*-------------------------------------------------------------------------*/ + +/* TTY Driver */ + +/* + * gs_open sets up the link between a gs_port and its associated TTY. + * That link is broken *only* by TTY close(), and all driver methods + * know that. + */ +static int gs_open(struct tty_struct *tty, struct file *file) +{ + int port_num = tty->index; + struct gs_port *port; + int status; + + if (port_num < 0 || port_num >= n_ports) + return -ENXIO; + + do { + mutex_lock(&ports[port_num].lock); + port = ports[port_num].port; + if (!port) + status = -ENODEV; + else { + spin_lock_irq(&port->port_lock); + + /* already open? Great. */ + if (port->open_count) { + status = 0; + port->open_count++; + + /* currently opening/closing? wait ... */ + } else if (port->openclose) { + status = -EBUSY; + + /* ... else we do the work */ + } else { + status = -EAGAIN; + port->openclose = true; + } + spin_unlock_irq(&port->port_lock); + } + mutex_unlock(&ports[port_num].lock); + + switch (status) { + default: + /* fully handled */ + return status; + case -EAGAIN: + /* must do the work */ + break; + case -EBUSY: + /* wait for EAGAIN task to finish */ + msleep(1); + /* REVISIT could have a waitchannel here, if + * concurrent open performance is important + */ + break; + } + } while (status != -EAGAIN); + + /* Do the "real open" */ + spin_lock_irq(&port->port_lock); + + /* allocate circular buffer on first open */ + if (port->port_write_buf.buf_buf == NULL) { + + spin_unlock_irq(&port->port_lock); + status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE); + spin_lock_irq(&port->port_lock); + + if (status) { + pr_debug("gs_open: ttyGS%d (%p,%p) no buffer\n", + port->port_num, tty, file); + port->openclose = false; + goto exit_unlock_port; + } + } + + /* REVISIT if REMOVED (ports[].port NULL), abort the open + * to let rmmod work faster (but this way isn't wrong). + */ + + /* REVISIT maybe wait for "carrier detect" */ + + tty->driver_data = port; + port->port_tty = tty; + + port->open_count = 1; + port->openclose = false; + + /* low_latency means ldiscs work in tasklet context, without + * needing a workqueue schedule ... easier to keep up. + */ + tty->low_latency = 1; + + /* if connected, start the I/O stream */ + if (port->port_usb) { + pr_debug("gs_open: start ttyGS%d\n", port->port_num); + gs_start_io(port); + + /* REVISIT for ACM, issue "network connected" event */ + } + + pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file); + + status = 0; + +exit_unlock_port: + spin_unlock_irq(&port->port_lock); + return status; +} + +static int gs_writes_finished(struct gs_port *p) +{ + int cond; + + /* return true on disconnect or empty buffer */ + spin_lock_irq(&p->port_lock); + cond = (p->port_usb == NULL) || !gs_buf_data_avail(&p->port_write_buf); + spin_unlock_irq(&p->port_lock); + + return cond; +} + +static void gs_close(struct tty_struct *tty, struct file *file) +{ + struct gs_port *port = tty->driver_data; + + spin_lock_irq(&port->port_lock); + + if (port->open_count != 1) { + if (port->open_count == 0) + WARN_ON(1); + else + --port->open_count; + goto exit; + } + + pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file); + + /* mark port as closing but in use; we can drop port lock + * and sleep if necessary + */ + port->openclose = true; + port->open_count = 0; + + if (port->port_usb) + /* REVISIT for ACM, issue "network disconnected" event */; + + /* wait for circular write buffer to drain, disconnect, or at + * most GS_CLOSE_TIMEOUT seconds; then discard the rest + */ + if (gs_buf_data_avail(&port->port_write_buf) > 0 + && port->port_usb) { + spin_unlock_irq(&port->port_lock); + wait_event_interruptible_timeout(port->drain_wait, + gs_writes_finished(port), + GS_CLOSE_TIMEOUT * HZ); + spin_lock_irq(&port->port_lock); + } + + /* Iff we're disconnected, there can be no I/O in flight so it's + * ok to free the circular buffer; else just scrub it. And don't + * let the push tasklet fire again until we're re-opened. + */ + if (port->port_usb == NULL) + gs_buf_free(&port->port_write_buf); + else + gs_buf_clear(&port->port_write_buf); + + tasklet_kill(&port->push); + + tty->driver_data = NULL; + port->port_tty = NULL; + + port->openclose = false; + + pr_debug("gs_close: ttyGS%d (%p,%p) done!\n", + port->port_num, tty, file); + + wake_up_interruptible(&port->close_wait); +exit: + spin_unlock_irq(&port->port_lock); +} + +static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count) +{ + struct gs_port *port = tty->driver_data; + unsigned long flags; + int status; + + pr_vdebug("gs_write: ttyGS%d (%p) writing %d bytes\n", + port->port_num, tty, count); + + spin_lock_irqsave(&port->port_lock, flags); + if (count) + count = gs_buf_put(&port->port_write_buf, buf, count); + /* treat count == 0 as flush_chars() */ + if (port->port_usb) + status = gs_start_tx(port); + spin_unlock_irqrestore(&port->port_lock, flags); + + return count; +} + +static int gs_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct gs_port *port = tty->driver_data; + unsigned long flags; + int status; + + pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %p\n", + port->port_num, tty, ch, __builtin_return_address(0)); + + spin_lock_irqsave(&port->port_lock, flags); + status = gs_buf_put(&port->port_write_buf, &ch, 1); + spin_unlock_irqrestore(&port->port_lock, flags); + + return status; +} + +static void gs_flush_chars(struct tty_struct *tty) +{ + struct gs_port *port = tty->driver_data; + unsigned long flags; + + pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty); + + spin_lock_irqsave(&port->port_lock, flags); + if (port->port_usb) + gs_start_tx(port); + spin_unlock_irqrestore(&port->port_lock, flags); +} + +static int gs_write_room(struct tty_struct *tty) +{ + struct gs_port *port = tty->driver_data; + unsigned long flags; + int room = 0; + + spin_lock_irqsave(&port->port_lock, flags); + if (port->port_usb) + room = gs_buf_space_avail(&port->port_write_buf); + spin_unlock_irqrestore(&port->port_lock, flags); + + pr_vdebug("gs_write_room: (%d,%p) room=%d\n", + port->port_num, tty, room); + + return room; +} + +static int gs_chars_in_buffer(struct tty_struct *tty) +{ + struct gs_port *port = tty->driver_data; + unsigned long flags; + int chars = 0; + + spin_lock_irqsave(&port->port_lock, flags); + chars = gs_buf_data_avail(&port->port_write_buf); + spin_unlock_irqrestore(&port->port_lock, flags); + + pr_vdebug("gs_chars_in_buffer: (%d,%p) chars=%d\n", + port->port_num, tty, chars); + + return chars; +} + +/* undo side effects of setting TTY_THROTTLED */ +static void gs_unthrottle(struct tty_struct *tty) +{ + struct gs_port *port = tty->driver_data; + unsigned long flags; + unsigned started = 0; + + spin_lock_irqsave(&port->port_lock, flags); + if (port->port_usb) + started = gs_start_rx(port); + spin_unlock_irqrestore(&port->port_lock, flags); + + pr_vdebug("gs_unthrottle: ttyGS%d, %d packets\n", + port->port_num, started); +} + +static const struct tty_operations gs_tty_ops = { + .open = gs_open, + .close = gs_close, + .write = gs_write, + .put_char = gs_put_char, + .flush_chars = gs_flush_chars, + .write_room = gs_write_room, + .chars_in_buffer = gs_chars_in_buffer, + .unthrottle = gs_unthrottle, +}; + +/*-------------------------------------------------------------------------*/ + +static struct tty_driver *gs_tty_driver; + +static int __init +gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding) +{ + struct gs_port *port; + + port = kzalloc(sizeof(struct gs_port), GFP_KERNEL); + if (port == NULL) + return -ENOMEM; + + spin_lock_init(&port->port_lock); + init_waitqueue_head(&port->close_wait); + init_waitqueue_head(&port->drain_wait); + + tasklet_init(&port->push, gs_rx_push, (unsigned long) port); + + INIT_LIST_HEAD(&port->read_pool); + INIT_LIST_HEAD(&port->write_pool); + + port->port_num = port_num; + port->port_line_coding = *coding; + + ports[port_num].port = port; + + return 0; +} + +/** + * gserial_setup - initialize TTY driver for one or more ports + * @g: gadget to associate with these ports + * @count: how many ports to support + * Context: may sleep + * + * The TTY stack needs to know in advance how many devices it should + * plan to manage. Use this call to set up the ports you will be + * exporting through USB. Later, connect them to functions based + * on what configuration is activated by the USB host; and disconnect + * them as appropriate. + * + * An example would be a two-configuration device in which both + * configurations expose port 0, but through different functions. + * One configuration could even expose port 1 while the other + * one doesn't. + * + * Returns negative errno or zero. + */ +int __init gserial_setup(struct usb_gadget *g, unsigned count) +{ + unsigned i; + struct usb_cdc_line_coding coding; + int status; + + if (count == 0 || count > N_PORTS) + return -EINVAL; + + gs_tty_driver = alloc_tty_driver(count); + if (!gs_tty_driver) + return -ENOMEM; + + gs_tty_driver->owner = THIS_MODULE; + gs_tty_driver->driver_name = "g_serial"; + gs_tty_driver->name = "ttyGS"; + /* uses dynamically assigned dev_t values */ + + gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + gs_tty_driver->subtype = SERIAL_TYPE_NORMAL; + gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + gs_tty_driver->init_termios = tty_std_termios; + + /* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on + * MS-Windows. Otherwise, most of these flags shouldn't affect + * anything unless we were to actually hook up to a serial line. + */ + gs_tty_driver->init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + gs_tty_driver->init_termios.c_ispeed = 9600; + gs_tty_driver->init_termios.c_ospeed = 9600; + + coding.dwDTERate = __constant_cpu_to_le32(9600); + coding.bCharFormat = 8; + coding.bParityType = USB_CDC_NO_PARITY; + coding.bDataBits = USB_CDC_1_STOP_BITS; + + tty_set_operations(gs_tty_driver, &gs_tty_ops); + + /* make devices be openable */ + for (i = 0; i < count; i++) { + mutex_init(&ports[i].lock); + status = gs_port_alloc(i, &coding); + if (status) { + count = i; + goto fail; + } + } + n_ports = count; + + /* export the driver ... */ + status = tty_register_driver(gs_tty_driver); + if (status) { + put_tty_driver(gs_tty_driver); + pr_err("%s: cannot register, err %d\n", + __func__, status); + goto fail; + } + + /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */ + for (i = 0; i < count; i++) { + struct device *tty_dev; + + tty_dev = tty_register_device(gs_tty_driver, i, &g->dev); + if (IS_ERR(tty_dev)) + pr_warning("%s: no classdev for port %d, err %ld\n", + __func__, i, PTR_ERR(tty_dev)); + } + + pr_debug("%s: registered %d ttyGS* device%s\n", __func__, + count, (count == 1) ? "" : "s"); + + return status; +fail: + while (count--) + kfree(ports[count].port); + put_tty_driver(gs_tty_driver); + gs_tty_driver = NULL; + return status; +} + +static int gs_closed(struct gs_port *port) +{ + int cond; + + spin_lock_irq(&port->port_lock); + cond = (port->open_count == 0) && !port->openclose; + spin_unlock_irq(&port->port_lock); + return cond; +} + +/** + * gserial_cleanup - remove TTY-over-USB driver and devices + * Context: may sleep + * + * This is called to free all resources allocated by @gserial_setup(). + * Accordingly, it may need to wait until some open /dev/ files have + * closed. + * + * The caller must have issued @gserial_disconnect() for any ports + * that had previously been connected, so that there is never any + * I/O pending when it's called. + */ +void gserial_cleanup(void) +{ + unsigned i; + struct gs_port *port; + + if (!gs_tty_driver) + return; + + /* start sysfs and /dev/ttyGS* node removal */ + for (i = 0; i < n_ports; i++) + tty_unregister_device(gs_tty_driver, i); + + for (i = 0; i < n_ports; i++) { + /* prevent new opens */ + mutex_lock(&ports[i].lock); + port = ports[i].port; + ports[i].port = NULL; + mutex_unlock(&ports[i].lock); + + /* wait for old opens to finish */ + wait_event(port->close_wait, gs_closed(port)); + + WARN_ON(port->port_usb != NULL); + + kfree(port); + } + n_ports = 0; + + tty_unregister_driver(gs_tty_driver); + gs_tty_driver = NULL; + + pr_debug("%s: cleaned up ttyGS* support\n", __func__); +} + +/** + * gserial_connect - notify TTY I/O glue that USB link is active + * @gser: the function, set up with endpoints and descriptors + * @port_num: which port is active + * Context: any (usually from irq) + * + * This is called activate endpoints and let the TTY layer know that + * the connection is active ... not unlike "carrier detect". It won't + * necessarily start I/O queues; unless the TTY is held open by any + * task, there would be no point. However, the endpoints will be + * activated so the USB host can perform I/O, subject to basic USB + * hardware flow control. + * + * Caller needs to have set up the endpoints and USB function in @dev + * before calling this, as well as the appropriate (speed-specific) + * endpoint descriptors, and also have set up the TTY driver by calling + * @gserial_setup(). + * + * Returns negative errno or zero. + * On success, ep->driver_data will be overwritten. + */ +int gserial_connect(struct gserial *gser, u8 port_num) +{ + struct gs_port *port; + unsigned long flags; + int status; + + if (!gs_tty_driver || port_num >= n_ports) + return -ENXIO; + + /* we "know" gserial_cleanup() hasn't been called */ + port = ports[port_num].port; + + /* activate the endpoints */ + status = usb_ep_enable(gser->in, gser->in_desc); + if (status < 0) + return status; + gser->in->driver_data = port; + + status = usb_ep_enable(gser->out, gser->out_desc); + if (status < 0) + goto fail_out; + gser->out->driver_data = port; + + /* then tell the tty glue that I/O can work */ + spin_lock_irqsave(&port->port_lock, flags); + gser->ioport = port; + port->port_usb = gser; + + /* REVISIT unclear how best to handle this state... + * we don't really couple it with the Linux TTY. + */ + gser->port_line_coding = port->port_line_coding; + + /* REVISIT if waiting on "carrier detect", signal. */ + + /* REVISIT for ACM, issue "network connection" status notification: + * connected if open_count, else disconnected. + */ + + /* if it's already open, start I/O */ + if (port->open_count) { + pr_debug("gserial_connect: start ttyGS%d\n", port->port_num); + gs_start_io(port); + } + + spin_unlock_irqrestore(&port->port_lock, flags); + + return status; + +fail_out: + usb_ep_disable(gser->in); + gser->in->driver_data = NULL; + return status; +} + +/** + * gserial_disconnect - notify TTY I/O glue that USB link is inactive + * @gser: the function, on which gserial_connect() was called + * Context: any (usually from irq) + * + * This is called to deactivate endpoints and let the TTY layer know + * that the connection went inactive ... not unlike "hangup". + * + * On return, the state is as if gserial_connect() had never been called; + * there is no active USB I/O on these endpoints. + */ +void gserial_disconnect(struct gserial *gser) +{ + struct gs_port *port = gser->ioport; + unsigned long flags; + + if (!port) + return; + + /* tell the TTY glue not to do I/O here any more */ + spin_lock_irqsave(&port->port_lock, flags); + + /* REVISIT as above: how best to track this? */ + port->port_line_coding = gser->port_line_coding; + + port->port_usb = NULL; + gser->ioport = NULL; + if (port->open_count > 0 || port->openclose) { + wake_up_interruptible(&port->drain_wait); + if (port->port_tty) + tty_hangup(port->port_tty); + } + spin_unlock_irqrestore(&port->port_lock, flags); + + /* disable endpoints, aborting down any active I/O */ + usb_ep_disable(gser->out); + gser->out->driver_data = NULL; + + usb_ep_disable(gser->in); + gser->in->driver_data = NULL; + + /* finally, free any unused/unusable I/O buffers */ + spin_lock_irqsave(&port->port_lock, flags); + if (port->open_count == 0 && !port->openclose) + gs_buf_free(&port->port_write_buf); + gs_free_requests(gser->out, &port->read_pool); + gs_free_requests(gser->in, &port->write_pool); + spin_unlock_irqrestore(&port->port_lock, flags); +} diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h new file mode 100644 index 000000000000..7b561138f90e --- /dev/null +++ b/drivers/usb/gadget/u_serial.h @@ -0,0 +1,58 @@ +/* + * u_serial.h - interface to USB gadget "serial port"/TTY utilities + * + * Copyright (C) 2008 David Brownell + * Copyright (C) 2008 by Nokia Corporation + * + * This software is distributed under the terms of the GNU General + * Public License ("GPL") as published by the Free Software Foundation, + * either version 2 of that License or (at your option) any later version. + */ + +#ifndef __U_SERIAL_H +#define __U_SERIAL_H + +#include <linux/usb/composite.h> +#include <linux/usb/cdc.h> + +/* + * One non-multiplexed "serial" I/O port ... there can be several of these + * on any given USB peripheral device, if it provides enough endpoints. + * + * The "u_serial" utility component exists to do one thing: manage TTY + * style I/O using the USB peripheral endpoints listed here, including + * hookups to sysfs and /dev for each logical "tty" device. + * + * REVISIT need TTY --> USB event flow too, so ACM can report open/close + * as carrier detect events. Model after ECM. There's more ACM state too. + * + * REVISIT someday, allow multiplexing several TTYs over these endpoints. + */ +struct gserial { + struct usb_function func; + + /* port is managed by gserial_{connect,disconnect} */ + struct gs_port *ioport; + + struct usb_ep *in; + struct usb_ep *out; + struct usb_endpoint_descriptor *in_desc; + struct usb_endpoint_descriptor *out_desc; + + /* REVISIT avoid this CDC-ACM support harder ... */ + struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */ +}; + +/* port setup/teardown is handled by gadget driver */ +int gserial_setup(struct usb_gadget *g, unsigned n_ports); +void gserial_cleanup(void); + +/* connect/disconnect is handled by individual functions */ +int gserial_connect(struct gserial *, u8 port_num); +void gserial_disconnect(struct gserial *); + +/* functions are bound to configurations by a config or gadget driver */ +int acm_bind_config(struct usb_configuration *c, u8 port_num); +int gser_bind_config(struct usb_configuration *c, u8 port_num); + +#endif /* __U_SERIAL_H */ diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index fce4924dbbe8..aa0bd4f126a1 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -1,8 +1,8 @@ /* * zero.c -- Gadget Zero, for USB development * - * Copyright (C) 2003-2007 David Brownell - * All rights reserved. + * Copyright (C) 2003-2008 David Brownell + * Copyright (C) 2008 by Nokia 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 @@ -30,12 +30,7 @@ * * It supports two similar configurations. One sinks whatever the usb host * writes, and in return sources zeroes. The other loops whatever the host - * writes back, so the host can read it. Module options include: - * - * buflen=N default N=4096, buffer size used - * qlen=N default N=32, how many buffers in the loopback queue - * loopdefault default false, list loopback config first - * autoresume=N default N=0, seconds before triggering remote wakeup + * writes back, so the host can read it. * * Many drivers will only have one configuration, letting them be much * simpler if they also don't support high speed operation (like this @@ -47,94 +42,35 @@ * work with low capability USB controllers without four bulk endpoints. */ +/* + * driver assumes self-powered hardware, and + * has no way for users to trigger remote wakeup. + */ + /* #define VERBOSE_DEBUG */ #include <linux/kernel.h> #include <linux/utsname.h> #include <linux/device.h> -#include <linux/usb/ch9.h> -#include <linux/usb/gadget.h> - +#include "g_zero.h" #include "gadget_chips.h" /*-------------------------------------------------------------------------*/ -#define DRIVER_VERSION "Earth Day 2008" +#define DRIVER_VERSION "Cinco de Mayo 2008" -static const char shortname[] = "zero"; static const char longname[] = "Gadget Zero"; -static const char source_sink[] = "source and sink data"; -static const char loopback[] = "loop input to output"; - -/*-------------------------------------------------------------------------*/ - -/* - * driver assumes self-powered hardware, and - * has no way for users to trigger remote wakeup. - * - * this version autoconfigures as much as possible, - * which is reasonable for most "bulk-only" drivers. - */ -static const char *EP_IN_NAME; /* source */ -static const char *EP_OUT_NAME; /* sink */ - -/*-------------------------------------------------------------------------*/ - -/* big enough to hold our biggest descriptor */ -#define USB_BUFSIZ 256 - -struct zero_dev { - spinlock_t lock; - struct usb_gadget *gadget; - struct usb_request *req; /* for control responses */ - - /* when configured, we have one of two configs: - * - source data (in to host) and sink it (out from host) - * - or loop it back (out from host back in to host) - */ - u8 config; - struct usb_ep *in_ep, *out_ep; - - /* autoresume timer */ - struct timer_list resume; -}; - -#define DBG(d, fmt, args...) \ - dev_dbg(&(d)->gadget->dev , fmt , ## args) -#define VDBG(d, fmt, args...) \ - dev_vdbg(&(d)->gadget->dev , fmt , ## args) -#define ERROR(d, fmt, args...) \ - dev_err(&(d)->gadget->dev , fmt , ## args) -#define WARN(d, fmt, args...) \ - dev_warn(&(d)->gadget->dev , fmt , ## args) -#define INFO(d, fmt, args...) \ - dev_info(&(d)->gadget->dev , fmt , ## args) - -/*-------------------------------------------------------------------------*/ - -static unsigned buflen = 4096; -static unsigned qlen = 32; -static unsigned pattern = 0; - -module_param(buflen, uint, S_IRUGO); -module_param(qlen, uint, S_IRUGO); -module_param(pattern, uint, S_IRUGO|S_IWUSR); - -/* - * if it's nonzero, autoresume says how many seconds to wait - * before trying to wake up the host after suspend. - */ -static unsigned autoresume = 0; -module_param(autoresume, uint, 0); +unsigned buflen = 4096; +module_param(buflen, uint, 0); /* * Normally the "loopback" configuration is second (index 1) so * it's not the default. Here's where to change that order, to - * work better with hosts where config changes are problematic. - * Or controllers (like superh) that only support one config. + * work better with hosts where config changes are problematic or + * controllers (like original superh) that only support one config. */ static int loopdefault = 0; module_param(loopdefault, bool, S_IRUGO|S_IWUSR); @@ -156,24 +92,6 @@ module_param(loopdefault, bool, S_IRUGO|S_IWUSR); /*-------------------------------------------------------------------------*/ -/* - * DESCRIPTORS ... most are static, but strings and (full) - * configuration descriptors are built on demand. - */ - -#define STRING_MANUFACTURER 25 -#define STRING_PRODUCT 42 -#define STRING_SERIAL 101 -#define STRING_SOURCE_SINK 250 -#define STRING_LOOPBACK 251 - -/* - * This device advertises two configurations; these numbers work - * on a pxa250 as well as more flexible hardware. - */ -#define CONFIG_SOURCE_SINK 3 -#define CONFIG_LOOPBACK 2 - static struct usb_device_descriptor device_desc = { .bLength = sizeof device_desc, .bDescriptorType = USB_DT_DEVICE, @@ -183,248 +101,64 @@ static struct usb_device_descriptor device_desc = { .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM), .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM), - .iManufacturer = STRING_MANUFACTURER, - .iProduct = STRING_PRODUCT, - .iSerialNumber = STRING_SERIAL, .bNumConfigurations = 2, }; -static struct usb_config_descriptor source_sink_config = { - .bLength = sizeof source_sink_config, - .bDescriptorType = USB_DT_CONFIG, - - /* compute wTotalLength on the fly */ - .bNumInterfaces = 1, - .bConfigurationValue = CONFIG_SOURCE_SINK, - .iConfiguration = STRING_SOURCE_SINK, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 1, /* self-powered */ -}; - -static struct usb_config_descriptor loopback_config = { - .bLength = sizeof loopback_config, - .bDescriptorType = USB_DT_CONFIG, - - /* compute wTotalLength on the fly */ - .bNumInterfaces = 1, - .bConfigurationValue = CONFIG_LOOPBACK, - .iConfiguration = STRING_LOOPBACK, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 1, /* self-powered */ -}; - +#ifdef CONFIG_USB_OTG static struct usb_otg_descriptor otg_descriptor = { .bLength = sizeof otg_descriptor, .bDescriptorType = USB_DT_OTG, - .bmAttributes = USB_OTG_SRP, -}; - -/* one interface in each configuration */ - -static const struct usb_interface_descriptor source_sink_intf = { - .bLength = sizeof source_sink_intf, - .bDescriptorType = USB_DT_INTERFACE, - - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .iInterface = STRING_SOURCE_SINK, -}; - -static const struct usb_interface_descriptor loopback_intf = { - .bLength = sizeof loopback_intf, - .bDescriptorType = USB_DT_INTERFACE, - - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .iInterface = STRING_LOOPBACK, -}; - -/* two full speed bulk endpoints; their use is config-dependent */ - -static struct usb_endpoint_descriptor fs_source_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -static struct usb_endpoint_descriptor fs_sink_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -static const struct usb_descriptor_header *fs_source_sink_function[] = { - (struct usb_descriptor_header *) &otg_descriptor, - (struct usb_descriptor_header *) &source_sink_intf, - (struct usb_descriptor_header *) &fs_sink_desc, - (struct usb_descriptor_header *) &fs_source_desc, - NULL, -}; - -static const struct usb_descriptor_header *fs_loopback_function[] = { - (struct usb_descriptor_header *) &otg_descriptor, - (struct usb_descriptor_header *) &loopback_intf, - (struct usb_descriptor_header *) &fs_sink_desc, - (struct usb_descriptor_header *) &fs_source_desc, - NULL, -}; - -/* - * usb 2.0 devices need to expose both high speed and full speed - * descriptors, unless they only run at full speed. - * - * that means alternate endpoint descriptors (bigger packets) - * and a "device qualifier" ... plus more construction options - * for the config descriptor. - */ - -static struct usb_endpoint_descriptor hs_source_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), -}; - -static struct usb_endpoint_descriptor hs_sink_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), -}; - -static struct usb_qualifier_descriptor dev_qualifier = { - .bLength = sizeof dev_qualifier, - .bDescriptorType = USB_DT_DEVICE_QUALIFIER, - - .bcdUSB = __constant_cpu_to_le16(0x0200), - .bDeviceClass = USB_CLASS_VENDOR_SPEC, - - .bNumConfigurations = 2, + /* REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, }; -static const struct usb_descriptor_header *hs_source_sink_function[] = { +const struct usb_descriptor_header *otg_desc[] = { (struct usb_descriptor_header *) &otg_descriptor, - (struct usb_descriptor_header *) &source_sink_intf, - (struct usb_descriptor_header *) &hs_source_desc, - (struct usb_descriptor_header *) &hs_sink_desc, NULL, }; +#endif -static const struct usb_descriptor_header *hs_loopback_function[] = { - (struct usb_descriptor_header *) &otg_descriptor, - (struct usb_descriptor_header *) &loopback_intf, - (struct usb_descriptor_header *) &hs_source_desc, - (struct usb_descriptor_header *) &hs_sink_desc, - NULL, -}; +/* string IDs are assigned dynamically */ -/* maxpacket and other transfer characteristics vary by speed. */ -static inline struct usb_endpoint_descriptor * -ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, - struct usb_endpoint_descriptor *fs) -{ - if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) - return hs; - return fs; -} +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 +#define STRING_SERIAL_IDX 2 static char manufacturer[50]; /* default serial number takes at least two packets */ static char serial[] = "0123456789.0123456789.0123456789"; - -/* static strings, in UTF-8 */ -static struct usb_string strings[] = { - { STRING_MANUFACTURER, manufacturer, }, - { STRING_PRODUCT, longname, }, - { STRING_SERIAL, serial, }, - { STRING_LOOPBACK, loopback, }, - { STRING_SOURCE_SINK, source_sink, }, +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = manufacturer, + [STRING_PRODUCT_IDX].s = longname, + [STRING_SERIAL_IDX].s = serial, { } /* end of list */ }; -static struct usb_gadget_strings stringtab = { +static struct usb_gadget_strings stringtab_dev = { .language = 0x0409, /* en-us */ - .strings = strings, + .strings = strings_dev, }; -/* - * config descriptors are also handcrafted. these must agree with code - * that sets configurations, and with code managing interfaces and their - * altsettings. other complexity may come from: - * - * - high speed support, including "other speed config" rules - * - multiple configurations - * - interfaces with alternate settings - * - embedded class or vendor-specific descriptors - * - * this handles high speed, and has a second config that could as easily - * have been an alternate interface setting (on most hardware). - * - * NOTE: to demonstrate (and test) more USB capabilities, this driver - * should include an altsetting to test interrupt transfers, including - * high bandwidth modes at high speed. (Maybe work like Intel's test - * device?) - */ -static int config_buf(struct usb_gadget *gadget, - u8 *buf, u8 type, unsigned index) -{ - int is_source_sink; - int len; - const struct usb_descriptor_header **function; - int hs = 0; - - /* two configurations will always be index 0 and index 1 */ - if (index > 1) - return -EINVAL; - is_source_sink = loopdefault ? (index == 1) : (index == 0); - - if (gadget_is_dualspeed(gadget)) { - hs = (gadget->speed == USB_SPEED_HIGH); - if (type == USB_DT_OTHER_SPEED_CONFIG) - hs = !hs; - } - if (hs) - function = is_source_sink - ? hs_source_sink_function - : hs_loopback_function; - else - function = is_source_sink - ? fs_source_sink_function - : fs_loopback_function; - - /* for now, don't advertise srp-only devices */ - if (!gadget_is_otg(gadget)) - function++; - - len = usb_gadget_config_buf(is_source_sink - ? &source_sink_config - : &loopback_config, - buf, USB_BUFSIZ, function); - if (len < 0) - return len; - ((struct usb_config_descriptor *) buf)->bDescriptorType = type; - return len; -} +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; /*-------------------------------------------------------------------------*/ -static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) +struct usb_request *alloc_ep_req(struct usb_ep *ep) { struct usb_request *req; req = usb_ep_alloc_request(ep, GFP_ATOMIC); if (req) { - req->length = length; - req->buf = kmalloc(length, GFP_ATOMIC); + req->length = buflen; + req->buf = kmalloc(buflen, GFP_ATOMIC); if (!req->buf) { usb_ep_free_request(ep, req); req = NULL; @@ -433,681 +167,73 @@ static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) return req; } -static void free_ep_req(struct usb_ep *ep, struct usb_request *req) +void free_ep_req(struct usb_ep *ep, struct usb_request *req) { kfree(req->buf); usb_ep_free_request(ep, req); } -/*-------------------------------------------------------------------------*/ - -/* - * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripherals, - * this just sinks bulk packets OUT to the peripheral and sources them IN - * to the host, optionally with specific data patterns. - * - * In terms of control messaging, this supports all the standard requests - * plus two that support control-OUT tests. - * - * Note that because this doesn't queue more than one request at a time, - * some other function must be used to test queueing logic. The network - * link (g_ether) is probably the best option for that. - */ - -/* optionally require specific source/sink data patterns */ - -static int -check_read_data( - struct zero_dev *dev, - struct usb_ep *ep, - struct usb_request *req -) +static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep) { - unsigned i; - u8 *buf = req->buf; - - for (i = 0; i < req->actual; i++, buf++) { - switch (pattern) { - /* all-zeroes has no synchronization issues */ - case 0: - if (*buf == 0) - continue; - break; - /* mod63 stays in sync with short-terminated transfers, - * or otherwise when host and gadget agree on how large - * each usb transfer request should be. resync is done - * with set_interface or set_config. - */ - case 1: - if (*buf == (u8)(i % 63)) - continue; - break; - } - ERROR(dev, "bad OUT byte, buf[%d] = %d\n", i, *buf); - usb_ep_set_halt(ep); - return -EINVAL; + int value; + + if (ep->driver_data) { + value = usb_ep_disable(ep); + if (value < 0) + DBG(cdev, "disable %s --> %d\n", + ep->name, value); + ep->driver_data = NULL; } - return 0; } -static void reinit_write_data(struct usb_ep *ep, struct usb_request *req) +void disable_endpoints(struct usb_composite_dev *cdev, + struct usb_ep *in, struct usb_ep *out) { - unsigned i; - u8 *buf = req->buf; - - switch (pattern) { - case 0: - memset(req->buf, 0, req->length); - break; - case 1: - for (i = 0; i < req->length; i++) - *buf++ = (u8) (i % 63); - break; - } -} - -/* if there is only one request in the queue, there'll always be an - * irq delay between end of one request and start of the next. - * that prevents using hardware dma queues. - */ -static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct zero_dev *dev = ep->driver_data; - int status = req->status; - - switch (status) { - - case 0: /* normal completion? */ - if (ep == dev->out_ep) { - check_read_data(dev, ep, req); - memset(req->buf, 0x55, req->length); - } else - reinit_write_data(ep, req); - break; - - /* this endpoint is normally active while we're configured */ - case -ECONNABORTED: /* hardware forced ep reset */ - case -ECONNRESET: /* request dequeued */ - case -ESHUTDOWN: /* disconnect from host */ - VDBG(dev, "%s gone (%d), %d/%d\n", ep->name, status, - req->actual, req->length); - if (ep == dev->out_ep) - check_read_data(dev, ep, req); - free_ep_req(ep, req); - return; - - case -EOVERFLOW: /* buffer overrun on read means that - * we didn't provide a big enough - * buffer. - */ - default: -#if 1 - DBG(dev, "%s complete --> %d, %d/%d\n", ep->name, - status, req->actual, req->length); -#endif - case -EREMOTEIO: /* short read */ - break; - } - - status = usb_ep_queue(ep, req, GFP_ATOMIC); - if (status) { - ERROR(dev, "kill %s: resubmit %d bytes --> %d\n", - ep->name, req->length, status); - usb_ep_set_halt(ep); - /* FIXME recover later ... somehow */ - } -} - -static struct usb_request *source_sink_start_ep(struct usb_ep *ep) -{ - struct usb_request *req; - int status; - - req = alloc_ep_req(ep, buflen); - if (!req) - return NULL; - - memset(req->buf, 0, req->length); - req->complete = source_sink_complete; - - if (strcmp(ep->name, EP_IN_NAME) == 0) - reinit_write_data(ep, req); - else - memset(req->buf, 0x55, req->length); - - status = usb_ep_queue(ep, req, GFP_ATOMIC); - if (status) { - struct zero_dev *dev = ep->driver_data; - - ERROR(dev, "start %s --> %d\n", ep->name, status); - free_ep_req(ep, req); - req = NULL; - } - - return req; -} - -static int set_source_sink_config(struct zero_dev *dev) -{ - int result = 0; - struct usb_ep *ep; - struct usb_gadget *gadget = dev->gadget; - - gadget_for_each_ep(ep, gadget) { - const struct usb_endpoint_descriptor *d; - - /* one endpoint writes (sources) zeroes in (to the host) */ - if (strcmp(ep->name, EP_IN_NAME) == 0) { - d = ep_desc(gadget, &hs_source_desc, &fs_source_desc); - result = usb_ep_enable(ep, d); - if (result == 0) { - ep->driver_data = dev; - if (source_sink_start_ep(ep) != NULL) { - dev->in_ep = ep; - continue; - } - usb_ep_disable(ep); - result = -EIO; - } - - /* one endpoint reads (sinks) anything out (from the host) */ - } else if (strcmp(ep->name, EP_OUT_NAME) == 0) { - d = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc); - result = usb_ep_enable(ep, d); - if (result == 0) { - ep->driver_data = dev; - if (source_sink_start_ep(ep) != NULL) { - dev->out_ep = ep; - continue; - } - usb_ep_disable(ep); - result = -EIO; - } - - /* ignore any other endpoints */ - } else - continue; - - /* stop on error */ - ERROR(dev, "can't start %s, result %d\n", ep->name, result); - break; - } - if (result == 0) - DBG(dev, "buflen %d\n", buflen); - - /* caller is responsible for cleanup on error */ - return result; -} - -/*-------------------------------------------------------------------------*/ - -static void loopback_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct zero_dev *dev = ep->driver_data; - int status = req->status; - - switch (status) { - - case 0: /* normal completion? */ - if (ep == dev->out_ep) { - /* loop this OUT packet back IN to the host */ - req->zero = (req->actual < req->length); - req->length = req->actual; - status = usb_ep_queue(dev->in_ep, req, GFP_ATOMIC); - if (status == 0) - return; - - /* "should never get here" */ - ERROR(dev, "can't loop %s to %s: %d\n", - ep->name, dev->in_ep->name, - status); - } - - /* queue the buffer for some later OUT packet */ - req->length = buflen; - status = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC); - if (status == 0) - return; - - /* "should never get here" */ - /* FALLTHROUGH */ - - default: - ERROR(dev, "%s loop complete --> %d, %d/%d\n", ep->name, - status, req->actual, req->length); - /* FALLTHROUGH */ - - /* NOTE: since this driver doesn't maintain an explicit record - * of requests it submitted (just maintains qlen count), we - * rely on the hardware driver to clean up on disconnect or - * endpoint disable. - */ - case -ECONNABORTED: /* hardware forced ep reset */ - case -ECONNRESET: /* request dequeued */ - case -ESHUTDOWN: /* disconnect from host */ - free_ep_req(ep, req); - return; - } -} - -static int set_loopback_config(struct zero_dev *dev) -{ - int result = 0; - struct usb_ep *ep; - struct usb_gadget *gadget = dev->gadget; - - gadget_for_each_ep(ep, gadget) { - const struct usb_endpoint_descriptor *d; - - /* one endpoint writes data back IN to the host */ - if (strcmp(ep->name, EP_IN_NAME) == 0) { - d = ep_desc(gadget, &hs_source_desc, &fs_source_desc); - result = usb_ep_enable(ep, d); - if (result == 0) { - ep->driver_data = dev; - dev->in_ep = ep; - continue; - } - - /* one endpoint just reads OUT packets */ - } else if (strcmp(ep->name, EP_OUT_NAME) == 0) { - d = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc); - result = usb_ep_enable(ep, d); - if (result == 0) { - ep->driver_data = dev; - dev->out_ep = ep; - continue; - } - - /* ignore any other endpoints */ - } else - continue; - - /* stop on error */ - ERROR(dev, "can't enable %s, result %d\n", ep->name, result); - break; - } - - /* allocate a bunch of read buffers and queue them all at once. - * we buffer at most 'qlen' transfers; fewer if any need more - * than 'buflen' bytes each. - */ - if (result == 0) { - struct usb_request *req; - unsigned i; - - ep = dev->out_ep; - for (i = 0; i < qlen && result == 0; i++) { - req = alloc_ep_req(ep, buflen); - if (req) { - req->complete = loopback_complete; - result = usb_ep_queue(ep, req, GFP_ATOMIC); - if (result) - DBG(dev, "%s queue req --> %d\n", - ep->name, result); - } else - result = -ENOMEM; - } - } - if (result == 0) - DBG(dev, "qlen %d, buflen %d\n", qlen, buflen); - - /* caller is responsible for cleanup on error */ - return result; -} - -/*-------------------------------------------------------------------------*/ - -static void zero_reset_config(struct zero_dev *dev) -{ - if (dev->config == 0) - return; - - DBG(dev, "reset config\n"); - - /* just disable endpoints, forcing completion of pending i/o. - * all our completion handlers free their requests in this case. - */ - if (dev->in_ep) { - usb_ep_disable(dev->in_ep); - dev->in_ep = NULL; - } - if (dev->out_ep) { - usb_ep_disable(dev->out_ep); - dev->out_ep = NULL; - } - dev->config = 0; - del_timer(&dev->resume); -} - -/* change our operational config. this code must agree with the code - * that returns config descriptors, and altsetting code. - * - * it's also responsible for power management interactions. some - * configurations might not work with our current power sources. - * - * note that some device controller hardware will constrain what this - * code can do, perhaps by disallowing more than one configuration or - * by limiting configuration choices (like the pxa2xx). - */ -static int zero_set_config(struct zero_dev *dev, unsigned number) -{ - int result = 0; - struct usb_gadget *gadget = dev->gadget; - - if (number == dev->config) - return 0; - - if (gadget_is_sa1100(gadget) && dev->config) { - /* tx fifo is full, but we can't clear it...*/ - ERROR(dev, "can't change configurations\n"); - return -ESPIPE; - } - zero_reset_config(dev); - - switch (number) { - case CONFIG_SOURCE_SINK: - result = set_source_sink_config(dev); - break; - case CONFIG_LOOPBACK: - result = set_loopback_config(dev); - break; - default: - result = -EINVAL; - /* FALL THROUGH */ - case 0: - return result; - } - - if (!result && (!dev->in_ep || !dev->out_ep)) - result = -ENODEV; - if (result) - zero_reset_config(dev); - else { - char *speed; - - switch (gadget->speed) { - case USB_SPEED_LOW: speed = "low"; break; - case USB_SPEED_FULL: speed = "full"; break; - case USB_SPEED_HIGH: speed = "high"; break; - default: speed = "?"; break; - } - - dev->config = number; - INFO(dev, "%s speed config #%d: %s\n", speed, number, - (number == CONFIG_SOURCE_SINK) - ? source_sink : loopback); - } - return result; -} - -/*-------------------------------------------------------------------------*/ - -static void zero_setup_complete(struct usb_ep *ep, struct usb_request *req) -{ - if (req->status || req->actual != req->length) - DBG((struct zero_dev *) ep->driver_data, - "setup complete --> %d, %d/%d\n", - req->status, req->actual, req->length); -} - -/* - * The setup() callback implements all the ep0 functionality that's - * not handled lower down, in hardware or the hardware driver (like - * device and endpoint feature flags, and their status). It's all - * housekeeping for the gadget function we're implementing. Most of - * the work is in config-specific setup. - */ -static int -zero_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) -{ - struct zero_dev *dev = get_gadget_data(gadget); - struct usb_request *req = dev->req; - int value = -EOPNOTSUPP; - u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_value = le16_to_cpu(ctrl->wValue); - u16 w_length = le16_to_cpu(ctrl->wLength); - - /* usually this stores reply data in the pre-allocated ep0 buffer, - * but config change events will reconfigure hardware. - */ - req->zero = 0; - switch (ctrl->bRequest) { - - case USB_REQ_GET_DESCRIPTOR: - if (ctrl->bRequestType != USB_DIR_IN) - goto unknown; - switch (w_value >> 8) { - - case USB_DT_DEVICE: - value = min(w_length, (u16) sizeof device_desc); - memcpy(req->buf, &device_desc, value); - break; - case USB_DT_DEVICE_QUALIFIER: - if (!gadget_is_dualspeed(gadget)) - break; - value = min(w_length, (u16) sizeof dev_qualifier); - memcpy(req->buf, &dev_qualifier, value); - break; - - case USB_DT_OTHER_SPEED_CONFIG: - if (!gadget_is_dualspeed(gadget)) - break; - // FALLTHROUGH - case USB_DT_CONFIG: - value = config_buf(gadget, req->buf, - w_value >> 8, - w_value & 0xff); - if (value >= 0) - value = min(w_length, (u16) value); - break; - - case USB_DT_STRING: - /* wIndex == language code. - * this driver only handles one language, you can - * add string tables for other languages, using - * any UTF-8 characters - */ - value = usb_gadget_get_string(&stringtab, - w_value & 0xff, req->buf); - if (value >= 0) - value = min(w_length, (u16) value); - break; - } - break; - - /* currently two configs, two speeds */ - case USB_REQ_SET_CONFIGURATION: - if (ctrl->bRequestType != 0) - goto unknown; - if (gadget->a_hnp_support) - DBG(dev, "HNP available\n"); - else if (gadget->a_alt_hnp_support) - DBG(dev, "HNP needs a different root port\n"); - else - VDBG(dev, "HNP inactive\n"); - spin_lock(&dev->lock); - value = zero_set_config(dev, w_value); - spin_unlock(&dev->lock); - break; - case USB_REQ_GET_CONFIGURATION: - if (ctrl->bRequestType != USB_DIR_IN) - goto unknown; - *(u8 *)req->buf = dev->config; - value = min(w_length, (u16) 1); - break; - - /* until we add altsetting support, or other interfaces, - * only 0/0 are possible. pxa2xx only supports 0/0 (poorly) - * and already killed pending endpoint I/O. - */ - case USB_REQ_SET_INTERFACE: - if (ctrl->bRequestType != USB_RECIP_INTERFACE) - goto unknown; - spin_lock(&dev->lock); - if (dev->config && w_index == 0 && w_value == 0) { - u8 config = dev->config; - - /* resets interface configuration, forgets about - * previous transaction state (queued bufs, etc) - * and re-inits endpoint state (toggle etc) - * no response queued, just zero status == success. - * if we had more than one interface we couldn't - * use this "reset the config" shortcut. - */ - zero_reset_config(dev); - zero_set_config(dev, config); - value = 0; - } - spin_unlock(&dev->lock); - break; - case USB_REQ_GET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) - goto unknown; - if (!dev->config) - break; - if (w_index != 0) { - value = -EDOM; - break; - } - *(u8 *)req->buf = 0; - value = min(w_length, (u16) 1); - break; - - /* - * These are the same vendor-specific requests supported by - * Intel's USB 2.0 compliance test devices. We exceed that - * device spec by allowing multiple-packet requests. - */ - case 0x5b: /* control WRITE test -- fill the buffer */ - if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR)) - goto unknown; - if (w_value || w_index) - break; - /* just read that many bytes into the buffer */ - if (w_length > USB_BUFSIZ) - break; - value = w_length; - break; - case 0x5c: /* control READ test -- return the buffer */ - if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR)) - goto unknown; - if (w_value || w_index) - break; - /* expect those bytes are still in the buffer; send back */ - if (w_length > USB_BUFSIZ - || w_length != req->length) - break; - value = w_length; - break; - - default: -unknown: - VDBG(dev, - "unknown control req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, w_length); - } - - /* respond with data transfer before status phase? */ - if (value >= 0) { - req->length = value; - req->zero = value < w_length; - value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); - if (value < 0) { - DBG(dev, "ep_queue --> %d\n", value); - req->status = 0; - zero_setup_complete(gadget->ep0, req); - } - } - - /* device either stalls (value < 0) or reports success */ - return value; -} - -static void zero_disconnect(struct usb_gadget *gadget) -{ - struct zero_dev *dev = get_gadget_data(gadget); - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); - zero_reset_config(dev); - - /* a more significant application might have some non-usb - * activities to quiesce here, saving resources like power - * or pushing the notification up a network stack. - */ - spin_unlock_irqrestore(&dev->lock, flags); - - /* next we may get setup() calls to enumerate new connections; - * or an unbind() during shutdown (including removing module). - */ -} - -static void zero_autoresume(unsigned long _dev) -{ - struct zero_dev *dev = (struct zero_dev *) _dev; - int status; - - /* normally the host would be woken up for something - * more significant than just a timer firing... - */ - if (dev->gadget->speed != USB_SPEED_UNKNOWN) { - status = usb_gadget_wakeup(dev->gadget); - DBG(dev, "wakeup --> %d\n", status); - } + disable_ep(cdev, in); + disable_ep(cdev, out); } /*-------------------------------------------------------------------------*/ -static void zero_unbind(struct usb_gadget *gadget) +static int __init zero_bind(struct usb_composite_dev *cdev) { - struct zero_dev *dev = get_gadget_data(gadget); - - DBG(dev, "unbind\n"); - - /* we've already been disconnected ... no i/o is active */ - if (dev->req) { - dev->req->length = USB_BUFSIZ; - free_ep_req(gadget->ep0, dev->req); - } - del_timer_sync(&dev->resume); - kfree(dev); - set_gadget_data(gadget, NULL); -} - -static int __init zero_bind(struct usb_gadget *gadget) -{ - struct zero_dev *dev; - struct usb_ep *ep; int gcnum; + struct usb_gadget *gadget = cdev->gadget; + int id; - /* FIXME this can't yet work right with SH ... it has only - * one configuration, numbered one. + /* Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. */ - if (gadget_is_sh(gadget)) - return -ENODEV; - - /* Bulk-only drivers like this one SHOULD be able to - * autoconfigure on any sane usb controller driver, - * but there may also be important quirks to address. + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_dev[STRING_MANUFACTURER_IDX].id = id; + device_desc.iManufacturer = id; + + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_dev[STRING_PRODUCT_IDX].id = id; + device_desc.iProduct = id; + + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_dev[STRING_SERIAL_IDX].id = id; + device_desc.iSerialNumber = id; + + /* Register primary, then secondary configuration. Note that + * SH3 only allows one config... */ - usb_ep_autoconfig_reset(gadget); - ep = usb_ep_autoconfig(gadget, &fs_source_desc); - if (!ep) { -autoconf_fail: - pr_err("%s: can't autoconfigure on %s\n", - shortname, gadget->name); - return -ENODEV; + if (loopdefault) { + loopback_add(cdev); + if (!gadget_is_sh(gadget)) + sourcesink_add(cdev); + } else { + sourcesink_add(cdev); + if (!gadget_is_sh(gadget)) + loopback_add(cdev); } - EP_IN_NAME = ep->name; - ep->driver_data = ep; /* claim */ - - ep = usb_ep_autoconfig(gadget, &fs_sink_desc); - if (!ep) - goto autoconf_fail; - EP_OUT_NAME = ep->name; - ep->driver_data = ep; /* claim */ gcnum = usb_gadget_controller_number(gadget); if (gcnum >= 0) @@ -1115,144 +241,44 @@ autoconf_fail: else { /* gadget zero is so simple (for now, no altsettings) that * it SHOULD NOT have problems with bulk-capable hardware. - * so warn about unrcognized controllers, don't panic. + * so just warn about unrcognized controllers -- don't panic. * * things like configuration and altsetting numbering * can need hardware-specific attention though. */ pr_warning("%s: controller '%s' not recognized\n", - shortname, gadget->name); + longname, gadget->name); device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); } - /* ok, we made sense of the hardware ... */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - spin_lock_init(&dev->lock); - dev->gadget = gadget; - set_gadget_data(gadget, dev); - - init_timer(&dev->resume); - dev->resume.function = zero_autoresume; - dev->resume.data = (unsigned long) dev; - - /* preallocate control response and buffer */ - dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); - if (!dev->req) - goto enomem; - dev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL); - if (!dev->req->buf) - goto enomem; - - dev->req->complete = zero_setup_complete; - - device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; - - if (gadget_is_dualspeed(gadget)) { - /* assume ep0 uses the same value for both speeds ... */ - dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0; - - /* and that all endpoints are dual-speed */ - hs_source_desc.bEndpointAddress = - fs_source_desc.bEndpointAddress; - hs_sink_desc.bEndpointAddress = - fs_sink_desc.bEndpointAddress; - } - - if (gadget_is_otg(gadget)) { - otg_descriptor.bmAttributes |= USB_OTG_HNP, - source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - } - - usb_gadget_set_selfpowered(gadget); - - if (autoresume) { - source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - } - - gadget->ep0->driver_data = dev; - - INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname); - INFO(dev, "using %s, OUT %s IN %s\n", gadget->name, - EP_OUT_NAME, EP_IN_NAME); + INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname); snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", init_utsname()->sysname, init_utsname()->release, gadget->name); return 0; - -enomem: - zero_unbind(gadget); - return -ENOMEM; } -/*-------------------------------------------------------------------------*/ - -static void zero_suspend(struct usb_gadget *gadget) -{ - struct zero_dev *dev = get_gadget_data(gadget); - - if (gadget->speed == USB_SPEED_UNKNOWN) - return; - - if (autoresume) { - mod_timer(&dev->resume, jiffies + (HZ * autoresume)); - DBG(dev, "suspend, wakeup in %d seconds\n", autoresume); - } else - DBG(dev, "suspend\n"); -} - -static void zero_resume(struct usb_gadget *gadget) -{ - struct zero_dev *dev = get_gadget_data(gadget); - - DBG(dev, "resume\n"); - del_timer(&dev->resume); -} - - -/*-------------------------------------------------------------------------*/ - -static struct usb_gadget_driver zero_driver = { -#ifdef CONFIG_USB_GADGET_DUALSPEED - .speed = USB_SPEED_HIGH, -#else - .speed = USB_SPEED_FULL, -#endif - .function = (char *) longname, +static struct usb_composite_driver zero_driver = { + .name = "zero", + .dev = &device_desc, + .strings = dev_strings, .bind = zero_bind, - .unbind = __exit_p(zero_unbind), - - .setup = zero_setup, - .disconnect = zero_disconnect, - - .suspend = zero_suspend, - .resume = zero_resume, - - .driver = { - .name = (char *) shortname, - .owner = THIS_MODULE, - }, }; MODULE_AUTHOR("David Brownell"); MODULE_LICENSE("GPL"); - static int __init init(void) { - return usb_gadget_register_driver(&zero_driver); + return usb_composite_register(&zero_driver); } module_init(init); static void __exit cleanup(void) { - usb_gadget_unregister_driver(&zero_driver); + usb_composite_unregister(&zero_driver); } module_exit(cleanup); - diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index 08a4335401a9..bf69f4739107 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -19,263 +19,304 @@ #define USB_MCFG_RDCOMB (1<<30) #define USB_MCFG_SSDEN (1<<23) #define USB_MCFG_PHYPLLEN (1<<19) +#define USB_MCFG_UCECLKEN (1<<18) #define USB_MCFG_EHCCLKEN (1<<17) +#ifdef CONFIG_DMA_COHERENT #define USB_MCFG_UCAM (1<<7) +#else +#define USB_MCFG_UCAM (0) +#endif #define USB_MCFG_EBMEN (1<<3) #define USB_MCFG_EMEMEN (1<<2) -#define USBH_ENABLE_CE (USB_MCFG_PHYPLLEN | USB_MCFG_EHCCLKEN) +#define USBH_ENABLE_CE (USB_MCFG_PHYPLLEN | USB_MCFG_EHCCLKEN) +#define USBH_ENABLE_INIT (USB_MCFG_PFEN | USB_MCFG_RDCOMB | \ + USBH_ENABLE_CE | USB_MCFG_SSDEN | \ + USB_MCFG_UCAM | USB_MCFG_EBMEN | \ + USB_MCFG_EMEMEN) -#ifdef CONFIG_DMA_COHERENT -#define USBH_ENABLE_INIT (USBH_ENABLE_CE \ - | USB_MCFG_PFEN | USB_MCFG_RDCOMB \ - | USB_MCFG_SSDEN | USB_MCFG_UCAM \ - | USB_MCFG_EBMEN | USB_MCFG_EMEMEN) -#else -#define USBH_ENABLE_INIT (USBH_ENABLE_CE \ - | USB_MCFG_PFEN | USB_MCFG_RDCOMB \ - | USB_MCFG_SSDEN \ - | USB_MCFG_EBMEN | USB_MCFG_EMEMEN) -#endif #define USBH_DISABLE (USB_MCFG_EBMEN | USB_MCFG_EMEMEN) extern int usb_disabled(void); -/*-------------------------------------------------------------------------*/ - -static void au1xxx_start_ehc(struct platform_device *dev) +static void au1xxx_start_ehc(void) { - pr_debug(__FILE__ ": starting Au1xxx EHCI USB Controller\n"); - - /* write HW defaults again in case Yamon cleared them */ - if (au_readl(USB_HOST_CONFIG) == 0) { - au_writel(0x00d02000, USB_HOST_CONFIG); - au_readl(USB_HOST_CONFIG); - udelay(1000); - } - /* enable host controller */ - au_writel(USBH_ENABLE_CE | au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG); - au_readl(USB_HOST_CONFIG); - udelay(1000); - au_writel(USBH_ENABLE_INIT | au_readl(USB_HOST_CONFIG), - USB_HOST_CONFIG); - au_readl(USB_HOST_CONFIG); + /* enable clock to EHCI block and HS PHY PLL*/ + au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_CE, USB_HOST_CONFIG); + au_sync(); udelay(1000); - pr_debug(__FILE__ ": Clock to USB host has been enabled\n"); + /* enable EHCI mmio */ + au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_INIT, USB_HOST_CONFIG); + au_sync(); + udelay(1000); } -static void au1xxx_stop_ehc(struct platform_device *dev) +static void au1xxx_stop_ehc(void) { - pr_debug(__FILE__ ": stopping Au1xxx EHCI USB Controller\n"); + unsigned long c; /* Disable mem */ - au_writel(~USBH_DISABLE & au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG); + au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_DISABLE, USB_HOST_CONFIG); + au_sync(); udelay(1000); - /* Disable clock */ - au_writel(~USB_MCFG_EHCCLKEN & au_readl(USB_HOST_CONFIG), - USB_HOST_CONFIG); - au_readl(USB_HOST_CONFIG); + + /* Disable EHC clock. If the HS PHY is unused disable it too. */ + c = au_readl(USB_HOST_CONFIG) & ~USB_MCFG_EHCCLKEN; + if (!(c & USB_MCFG_UCECLKEN)) /* UDC disabled? */ + c &= ~USB_MCFG_PHYPLLEN; /* yes: disable HS PHY PLL */ + au_writel(c, USB_HOST_CONFIG); + au_sync(); } -/*-------------------------------------------------------------------------*/ +static const struct hc_driver ehci_au1xxx_hc_driver = { + .description = hcd_name, + .product_desc = "Au1xxx EHCI", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + * + * FIXME -- ehci_init() doesn't do enough here. + * See ehci-ppc-soc for a complete implementation. + */ + .reset = ehci_init, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, -/* configure so an HC device and id are always provided */ -/* always called with process context; sleeping is OK */ + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, -/** - * usb_ehci_au1xxx_probe - initialize Au1xxx-based HCDs - * Context: !in_interrupt() - * - * Allocates basic resources for this USB host controller, and - * then invokes the start() method for the HCD associated with it - * through the hotplug entry's driver_data. - * - */ -int usb_ehci_au1xxx_probe(const struct hc_driver *driver, - struct usb_hcd **hcd_out, struct platform_device *dev) + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, +}; + +static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev) { - int retval; struct usb_hcd *hcd; struct ehci_hcd *ehci; + int ret; -#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT) + if (usb_disabled()) + return -ENODEV; +#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT) /* Au1200 AB USB does not support coherent memory */ if (!(read_c0_prid() & 0xff)) { - pr_info("%s: this is chip revision AB!\n", dev->name); - pr_info("%s: update your board or re-configure the kernel\n", - dev->name); + printk(KERN_INFO "%s: this is chip revision AB!\n", pdev->name); + printk(KERN_INFO "%s: update your board or re-configure" + " the kernel\n", pdev->name); return -ENODEV; } #endif - au1xxx_start_ehc(dev); - - if (dev->resource[1].flags != IORESOURCE_IRQ) { + if (pdev->resource[1].flags != IORESOURCE_IRQ) { pr_debug("resource[1] is not IORESOURCE_IRQ"); - retval = -ENOMEM; + return -ENOMEM; } - hcd = usb_create_hcd(driver, &dev->dev, "Au1xxx"); + hcd = usb_create_hcd(&ehci_au1xxx_hc_driver, &pdev->dev, "Au1xxx"); if (!hcd) return -ENOMEM; - hcd->rsrc_start = dev->resource[0].start; - hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1; + + hcd->rsrc_start = pdev->resource[0].start; + hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1; if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { pr_debug("request_mem_region failed"); - retval = -EBUSY; + ret = -EBUSY; goto err1; } hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); if (!hcd->regs) { pr_debug("ioremap failed"); - retval = -ENOMEM; + ret = -ENOMEM; goto err2; } + au1xxx_start_ehc(); + ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase)); /* cache this readonly data; minimize chip reads */ ehci->hcs_params = readl(&ehci->caps->hcs_params); - /* ehci_hcd_init(hcd_to_ehci(hcd)); */ - - retval = - usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED | IRQF_SHARED); - if (retval == 0) - return retval; + ret = usb_add_hcd(hcd, pdev->resource[1].start, + IRQF_DISABLED | IRQF_SHARED); + if (ret == 0) { + platform_set_drvdata(pdev, hcd); + return ret; + } - au1xxx_stop_ehc(dev); + au1xxx_stop_ehc(); iounmap(hcd->regs); err2: release_mem_region(hcd->rsrc_start, hcd->rsrc_len); err1: usb_put_hcd(hcd); - return retval; + return ret; } -/* may be called without controller electrically present */ -/* may be called with controller, bus, and devices active */ - -/** - * usb_ehci_hcd_au1xxx_remove - shutdown processing for Au1xxx-based HCDs - * @dev: USB Host Controller being removed - * Context: !in_interrupt() - * - * Reverses the effect of usb_ehci_hcd_au1xxx_probe(), first invoking - * the HCD's stop() method. It is always called from a thread - * context, normally "rmmod", "apmd", or something similar. - * - */ -void usb_ehci_au1xxx_remove(struct usb_hcd *hcd, struct platform_device *dev) +static int ehci_hcd_au1xxx_drv_remove(struct platform_device *pdev) { + struct usb_hcd *hcd = platform_get_drvdata(pdev); + usb_remove_hcd(hcd); iounmap(hcd->regs); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); - au1xxx_stop_ehc(dev); + au1xxx_stop_ehc(); + platform_set_drvdata(pdev, NULL); + + return 0; } -/*-------------------------------------------------------------------------*/ +#ifdef CONFIG_PM +static int ehci_hcd_au1xxx_drv_suspend(struct platform_device *pdev, + pm_message_t message) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + unsigned long flags; + int rc; -static const struct hc_driver ehci_au1xxx_hc_driver = { - .description = hcd_name, - .product_desc = "Au1xxx EHCI", - .hcd_priv_size = sizeof(struct ehci_hcd), + return 0; + rc = 0; - /* - * generic hardware linkage - */ - .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, + if (time_before(jiffies, ehci->next_statechange)) + msleep(10); - /* - * basic lifecycle operations + /* Root hub was already suspended. Disable irq emission and + * mark HW unaccessible, bail out if RH has been resumed. Use + * the spinlock to properly synchronize with possible pending + * RH suspend or resume activity. * - * FIXME -- ehci_init() doesn't do enough here. - * See ehci-ppc-soc for a complete implementation. - */ - .reset = ehci_init, - .start = ehci_run, - .stop = ehci_stop, - .shutdown = ehci_shutdown, - - /* - * managing i/o requests and associated device resources + * This is still racy as hcd->state is manipulated outside of + * any locks =P But that will be a different fix. */ - .urb_enqueue = ehci_urb_enqueue, - .urb_dequeue = ehci_urb_dequeue, - .endpoint_disable = ehci_endpoint_disable, + spin_lock_irqsave(&ehci->lock, flags); + if (hcd->state != HC_STATE_SUSPENDED) { + rc = -EINVAL; + goto bail; + } + ehci_writel(ehci, 0, &ehci->regs->intr_enable); + (void)ehci_readl(ehci, &ehci->regs->intr_enable); - /* - * scheduling support - */ - .get_frame_number = ehci_get_frame, + /* make sure snapshot being resumed re-enumerates everything */ + if (message.event == PM_EVENT_PRETHAW) { + ehci_halt(ehci); + ehci_reset(ehci); + } - /* - * root hub support - */ - .hub_status_data = ehci_hub_status_data, - .hub_control = ehci_hub_control, - .bus_suspend = ehci_bus_suspend, - .bus_resume = ehci_bus_resume, - .relinquish_port = ehci_relinquish_port, - .port_handed_over = ehci_port_handed_over, -}; + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); -/*-------------------------------------------------------------------------*/ + au1xxx_stop_ehc(); -static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev) -{ - struct usb_hcd *hcd = NULL; - int ret; +bail: + spin_unlock_irqrestore(&ehci->lock, flags); - pr_debug("In ehci_hcd_au1xxx_drv_probe\n"); + // could save FLADJ in case of Vaux power loss + // ... we'd only use it to handle clock skew - if (usb_disabled()) - return -ENODEV; - - /* FIXME we only want one one probe() not two */ - ret = usb_ehci_au1xxx_probe(&ehci_au1xxx_hc_driver, &hcd, pdev); - return ret; + return rc; } -static int ehci_hcd_au1xxx_drv_remove(struct platform_device *pdev) + +static int ehci_hcd_au1xxx_drv_resume(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); - /* FIXME we only want one one remove() not two */ - usb_ehci_au1xxx_remove(hcd, pdev); - return 0; -} + au1xxx_start_ehc(); - /*TBD*/ -/*static int ehci_hcd_au1xxx_drv_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct usb_hcd *hcd = dev_get_drvdata(dev); + // maybe restore FLADJ - return 0; -} -static int ehci_hcd_au1xxx_drv_resume(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct usb_hcd *hcd = dev_get_drvdata(dev); + if (time_before(jiffies, ehci->next_statechange)) + msleep(100); + + /* Mark hardware accessible again as we are out of D3 state by now */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + /* If CF is still set, we maintained PCI Vaux power. + * Just undo the effect of ehci_pci_suspend(). + */ + if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { + int mask = INTR_MASK; + + if (!hcd->self.root_hub->do_remote_wakeup) + mask &= ~STS_PCD; + ehci_writel(ehci, mask, &ehci->regs->intr_enable); + ehci_readl(ehci, &ehci->regs->intr_enable); + return 0; + } + + ehci_dbg(ehci, "lost power, restarting\n"); + usb_root_hub_lost_power(hcd->self.root_hub); + + /* Else reset, to cope with power loss or flush-to-storage + * style "resume" having let BIOS kick in during reboot. + */ + (void) ehci_halt(ehci); + (void) ehci_reset(ehci); + + /* emptying the schedule aborts any urbs */ + spin_lock_irq(&ehci->lock); + if (ehci->reclaim) + end_unlink_async(ehci); + ehci_work(ehci); + spin_unlock_irq(&ehci->lock); + + ehci_writel(ehci, ehci->command, &ehci->regs->command); + ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); + ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ + + /* here we "know" root ports should always stay powered */ + ehci_port_power(ehci, 1); + + hcd->state = HC_STATE_SUSPENDED; return 0; } -*/ -MODULE_ALIAS("platform:au1xxx-ehci"); + +#else +#define ehci_hcd_au1xxx_drv_suspend NULL +#define ehci_hcd_au1xxx_drv_resume NULL +#endif + static struct platform_driver ehci_hcd_au1xxx_driver = { - .probe = ehci_hcd_au1xxx_drv_probe, - .remove = ehci_hcd_au1xxx_drv_remove, - .shutdown = usb_hcd_platform_shutdown, - /*.suspend = ehci_hcd_au1xxx_drv_suspend, */ - /*.resume = ehci_hcd_au1xxx_drv_resume, */ + .probe = ehci_hcd_au1xxx_drv_probe, + .remove = ehci_hcd_au1xxx_drv_remove, + .shutdown = usb_hcd_platform_shutdown, + .suspend = ehci_hcd_au1xxx_drv_suspend, + .resume = ehci_hcd_au1xxx_drv_resume, .driver = { - .name = "au1xxx-ehci", + .name = "au1xxx-ehci", + .owner = THIS_MODULE, } }; + +MODULE_ALIAS("platform:au1xxx-ehci"); diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 0f82fdcaef09..b0f8ed5a7fb9 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -676,7 +676,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) "%s\n" "SUSPENDED (no register access)\n", hcd->self.controller->bus->name, - hcd->self.controller->bus_id, + dev_name(hcd->self.controller), hcd->product_desc); goto done; } @@ -688,7 +688,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) "%s\n" "EHCI %x.%02x, hcd state %d\n", hcd->self.controller->bus->name, - hcd->self.controller->bus_id, + dev_name(hcd->self.controller), hcd->product_desc, i >> 8, i & 0x0ff, hcd->state); size -= temp; diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 7370d6187c64..01c3da34f678 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -56,7 +56,7 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver, pdata = (struct fsl_usb2_platform_data *)pdev->dev.platform_data; if (!pdata) { dev_err(&pdev->dev, - "No platform data for %s.\n", pdev->dev.bus_id); + "No platform data for %s.\n", dev_name(&pdev->dev)); return -ENODEV; } @@ -69,7 +69,7 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver, (pdata->operating_mode == FSL_USB2_DR_OTG))) { dev_err(&pdev->dev, "Non Host Mode configured for %s. Wrong driver linked.\n", - pdev->dev.bus_id); + dev_name(&pdev->dev)); return -ENODEV; } @@ -77,12 +77,12 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver, if (!res) { dev_err(&pdev->dev, "Found HC with no IRQ. Check %s setup!\n", - pdev->dev.bus_id); + dev_name(&pdev->dev)); return -ENODEV; } irq = res->start; - hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id); + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { retval = -ENOMEM; goto err1; @@ -92,7 +92,7 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver, if (!res) { dev_err(&pdev->dev, "Found HC with no register addr. Check %s setup!\n", - pdev->dev.bus_id); + dev_name(&pdev->dev)); retval = -ENODEV; goto err2; } @@ -132,7 +132,7 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver, err2: usb_put_hcd(hcd); err1: - dev_err(&pdev->dev, "init %s fail, %d\n", pdev->dev.bus_id, retval); + dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval); return retval; } @@ -230,8 +230,13 @@ static void mpc83xx_usb_setup(struct usb_hcd *hcd) /* put controller in host mode. */ ehci_writel(ehci, 0x00000003, non_ehci + FSL_SOC_USB_USBMODE); +#ifdef CONFIG_PPC_85xx + out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008); + out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080); +#else out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c); out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040); +#endif out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001); } diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 369a8a5ea7bb..d9d53f289caf 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -84,7 +84,7 @@ static const char hcd_name [] = "ehci_hcd"; #define EHCI_IAA_MSECS 10 /* arbitrary */ #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ #define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ -#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */ +#define EHCI_SHRINK_FRAMES 5 /* async qh unlink delay */ /* Initial IRQ latency: faster than hw default */ static int log2_irq_thresh = 0; // 0 to 6 diff --git a/drivers/usb/host/ehci-ixp4xx.c b/drivers/usb/host/ehci-ixp4xx.c index 9d042f220097..f9575c409124 100644 --- a/drivers/usb/host/ehci-ixp4xx.c +++ b/drivers/usb/host/ehci-ixp4xx.c @@ -77,12 +77,12 @@ static int ixp4xx_ehci_probe(struct platform_device *pdev) if (!res) { dev_err(&pdev->dev, "Found HC with no IRQ. Check %s setup!\n", - pdev->dev.bus_id); + dev_name(&pdev->dev)); return -ENODEV; } irq = res->start; - hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id); + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { retval = -ENOMEM; goto fail_create_hcd; @@ -92,7 +92,7 @@ static int ixp4xx_ehci_probe(struct platform_device *pdev) if (!res) { dev_err(&pdev->dev, "Found HC with no register addr. Check %s setup!\n", - pdev->dev.bus_id); + dev_name(&pdev->dev)); retval = -ENODEV; goto fail_request_resource; } @@ -126,7 +126,7 @@ fail_ioremap: fail_request_resource: usb_put_hcd(hcd); fail_create_hcd: - dev_err(&pdev->dev, "init %s fail, %d\n", pdev->dev.bus_id, retval); + dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval); return retval; } diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index ab625f0ba1d9..5fbdc14e63b3 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -204,7 +204,7 @@ static int __init ehci_orion_drv_probe(struct platform_device *pdev) if (irq <= 0) { dev_err(&pdev->dev, "Found HC with no IRQ. Check %s setup!\n", - pdev->dev.bus_id); + dev_name(&pdev->dev)); err = -ENODEV; goto err1; } @@ -213,7 +213,7 @@ static int __init ehci_orion_drv_probe(struct platform_device *pdev) if (!res) { dev_err(&pdev->dev, "Found HC with no register addr. Check %s setup!\n", - pdev->dev.bus_id); + dev_name(&pdev->dev)); err = -ENODEV; goto err1; } @@ -233,7 +233,7 @@ static int __init ehci_orion_drv_probe(struct platform_device *pdev) } hcd = usb_create_hcd(&ehci_orion_hc_driver, - &pdev->dev, pdev->dev.bus_id); + &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { err = -ENOMEM; goto err3; @@ -276,7 +276,7 @@ err2: release_mem_region(res->start, res->end - res->start + 1); err1: dev_err(&pdev->dev, "init %s fail, %d\n", - pdev->dev.bus_id, err); + dev_name(&pdev->dev), err); return err; } diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 37e6abeb794c..0eba894bcb01 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -128,7 +128,7 @@ static int ps3_ehci_probe(struct ps3_system_bus_device *dev) dev->core.dma_mask = &dummy_mask; /* FIXME: for improper usb code */ - hcd = usb_create_hcd(&ps3_ehci_hc_driver, &dev->core, dev->core.bus_id); + hcd = usb_create_hcd(&ps3_ehci_hc_driver, &dev->core, dev_name(&dev->core)); if (!hcd) { dev_dbg(&dev->core, "%s:%d: usb_create_hcd failed\n", __func__, diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index b85b54160cda..2622b6596d7c 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1116,8 +1116,7 @@ static void scan_async (struct ehci_hcd *ehci) struct ehci_qh *qh; enum ehci_timer_action action = TIMER_IO_WATCHDOG; - if (!++(ehci->stamp)) - ehci->stamp++; + ehci->stamp = ehci_readl(ehci, &ehci->regs->frame_index); timer_action_done (ehci, TIMER_ASYNC_SHRINK); rescan: qh = ehci->async->qh_next.qh; @@ -1142,18 +1141,20 @@ rescan: } } - /* unlink idle entries, reducing HC PCI usage as well + /* unlink idle entries, reducing DMA usage as well * as HCD schedule-scanning costs. delay for any qh * we just scanned, there's a not-unusual case that it * doesn't stay idle for long. * (plus, avoids some kind of re-activation race.) */ - if (list_empty (&qh->qtd_list)) { - if (qh->stamp == ehci->stamp) + if (list_empty(&qh->qtd_list) + && qh->qh_state == QH_STATE_LINKED) { + if (!ehci->reclaim + && ((ehci->stamp - qh->stamp) & 0x1fff) + >= (EHCI_SHRINK_FRAMES * 8)) + start_unlink_async(ehci, qh); + else action = TIMER_ASYNC_SHRINK; - else if (!ehci->reclaim - && qh->qh_state == QH_STATE_LINKED) - start_unlink_async (ehci, qh); } qh = qh->qh_next.qh; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 90245fd8bac4..5799298364fb 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -198,7 +198,10 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) break; // case TIMER_ASYNC_SHRINK: default: - t = EHCI_SHRINK_JIFFIES; + /* add a jiffie since we synch against the + * 8 KHz uframe counter. + */ + t = DIV_ROUND_UP(EHCI_SHRINK_FRAMES * HZ, 1000) + 1; break; } mod_timer(&ehci->watchdog, t + jiffies); diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 20b9a0d07420..ce1ca0ba0515 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -94,6 +94,10 @@ static void write_ptddata_to_fifo(struct isp116x *isp116x, void *buf, int len) u16 w; int quot = len % 4; + /* buffer is already in 'usb data order', which is LE. */ + /* When reading buffer as u16, we have to take care byte order */ + /* doesn't get mixed up */ + if ((unsigned long)dp2 & 1) { /* not aligned */ for (; len > 1; len -= 2) { @@ -105,8 +109,11 @@ static void write_ptddata_to_fifo(struct isp116x *isp116x, void *buf, int len) isp116x_write_data16(isp116x, (u16) * dp); } else { /* aligned */ - for (; len > 1; len -= 2) - isp116x_raw_write_data16(isp116x, *dp2++); + for (; len > 1; len -= 2) { + /* Keep byte order ! */ + isp116x_raw_write_data16(isp116x, cpu_to_le16(*dp2++)); + } + if (len) isp116x_write_data16(isp116x, 0xff & *((u8 *) dp2)); } @@ -124,6 +131,10 @@ static void read_ptddata_from_fifo(struct isp116x *isp116x, void *buf, int len) u16 w; int quot = len % 4; + /* buffer is already in 'usb data order', which is LE. */ + /* When reading buffer as u16, we have to take care byte order */ + /* doesn't get mixed up */ + if ((unsigned long)dp2 & 1) { /* not aligned */ for (; len > 1; len -= 2) { @@ -131,12 +142,16 @@ static void read_ptddata_from_fifo(struct isp116x *isp116x, void *buf, int len) *dp++ = w & 0xff; *dp++ = (w >> 8) & 0xff; } + if (len) *dp = 0xff & isp116x_read_data16(isp116x); } else { /* aligned */ - for (; len > 1; len -= 2) - *dp2++ = isp116x_raw_read_data16(isp116x); + for (; len > 1; len -= 2) { + /* Keep byte order! */ + *dp2++ = le16_to_cpu(isp116x_raw_read_data16(isp116x)); + } + if (len) *(u8 *) dp2 = 0xff & isp116x_read_data16(isp116x); } @@ -867,7 +882,7 @@ static void isp116x_endpoint_disable(struct usb_hcd *hcd, for (i = 0; i < 100 && !list_empty(&hep->urb_list); i++) msleep(3); if (!list_empty(&hep->urb_list)) - WARN("ep %p not empty?\n", ep); + WARNING("ep %p not empty?\n", ep); kfree(ep); hep->hcpriv = NULL; @@ -1592,7 +1607,7 @@ static int __devinit isp116x_probe(struct platform_device *pdev) } /* allocate and initialize hcd */ - hcd = usb_create_hcd(&isp116x_hc_driver, &pdev->dev, pdev->dev.bus_id); + hcd = usb_create_hcd(&isp116x_hc_driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { ret = -ENOMEM; goto err5; diff --git a/drivers/usb/host/isp116x.h b/drivers/usb/host/isp116x.h index 595b90a99848..aa211bafcff9 100644 --- a/drivers/usb/host/isp116x.h +++ b/drivers/usb/host/isp116x.h @@ -338,7 +338,7 @@ struct isp116x_ep { #endif #define ERR(stuff...) printk(KERN_ERR "116x: " stuff) -#define WARN(stuff...) printk(KERN_WARNING "116x: " stuff) +#define WARNING(stuff...) printk(KERN_WARNING "116x: " stuff) #define INFO(stuff...) printk(KERN_INFO "116x: " stuff) /* ------------------------------------------------- */ diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 65aa5ecf569a..c858f2adb929 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -38,6 +38,7 @@ struct isp1760_hcd { unsigned i_thresh; unsigned long reset_done; unsigned long next_statechange; + unsigned int devflags; }; static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd) @@ -378,9 +379,31 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); int result; - u32 scratch; + u32 scratch, hwmode; + + /* Setup HW Mode Control: This assumes a level active-low interrupt */ + hwmode = HW_DATA_BUS_32BIT; + + if (priv->devflags & ISP1760_FLAG_BUS_WIDTH_16) + hwmode &= ~HW_DATA_BUS_32BIT; + if (priv->devflags & ISP1760_FLAG_ANALOG_OC) + hwmode |= HW_ANA_DIGI_OC; + if (priv->devflags & ISP1760_FLAG_DACK_POL_HIGH) + hwmode |= HW_DACK_POL_HIGH; + if (priv->devflags & ISP1760_FLAG_DREQ_POL_HIGH) + hwmode |= HW_DREQ_POL_HIGH; + + /* + * We have to set this first in case we're in 16-bit mode. + * Write it twice to ensure correct upper bits if switching + * to 16-bit mode. + */ + isp1760_writel(hwmode, hcd->regs + HC_HW_MODE_CTRL); + isp1760_writel(hwmode, hcd->regs + HC_HW_MODE_CTRL); isp1760_writel(0xdeadbabe, hcd->regs + HC_SCRATCH_REG); + /* Change bus pattern */ + scratch = isp1760_readl(hcd->regs + HC_CHIP_ID_REG); scratch = isp1760_readl(hcd->regs + HC_SCRATCH_REG); if (scratch != 0xdeadbabe) { printk(KERN_ERR "ISP1760: Scratch test failed.\n"); @@ -403,17 +426,29 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) /* Step 11 passed */ - isp1760_writel(INTERRUPT_ENABLE_MASK, hcd->regs + HC_INTERRUPT_REG); - isp1760_writel(INTERRUPT_ENABLE_MASK, hcd->regs + HC_INTERRUPT_ENABLE); + isp1760_info(priv, "bus width: %d, oc: %s\n", + (priv->devflags & ISP1760_FLAG_BUS_WIDTH_16) ? + 16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ? + "analog" : "digital"); /* ATL reset */ - scratch = isp1760_readl(hcd->regs + HC_HW_MODE_CTRL); - isp1760_writel(scratch | ALL_ATX_RESET, hcd->regs + HC_HW_MODE_CTRL); + isp1760_writel(hwmode | ALL_ATX_RESET, hcd->regs + HC_HW_MODE_CTRL); mdelay(10); - isp1760_writel(scratch, hcd->regs + HC_HW_MODE_CTRL); + isp1760_writel(hwmode, hcd->regs + HC_HW_MODE_CTRL); - isp1760_writel(PORT1_POWER | PORT1_INIT2, hcd->regs + HC_PORT1_CTRL); - mdelay(10); + isp1760_writel(INTERRUPT_ENABLE_MASK, hcd->regs + HC_INTERRUPT_REG); + isp1760_writel(INTERRUPT_ENABLE_MASK, hcd->regs + HC_INTERRUPT_ENABLE); + + /* + * PORT 1 Control register of the ISP1760 is the OTG control + * register on ISP1761. + */ + if (!(priv->devflags & ISP1760_FLAG_ISP1761) && + !(priv->devflags & ISP1760_FLAG_PORT1_DIS)) { + isp1760_writel(PORT1_POWER | PORT1_INIT2, + hcd->regs + HC_PORT1_CTRL); + mdelay(10); + } priv->hcs_params = isp1760_readl(hcd->regs + HC_HCSPARAMS); @@ -453,8 +488,7 @@ static int isp1760_run(struct usb_hcd *hcd) hcd->state = HC_STATE_RUNNING; isp1760_enable_interrupts(hcd); temp = isp1760_readl(hcd->regs + HC_HW_MODE_CTRL); - temp |= FINAL_HW_CONFIG; - isp1760_writel(temp, hcd->regs + HC_HW_MODE_CTRL); + isp1760_writel(temp | HW_GLOBAL_INTR_EN, hcd->regs + HC_HW_MODE_CTRL); command = isp1760_readl(hcd->regs + HC_USBCMD); command &= ~(CMD_LRESET|CMD_RESET); @@ -782,8 +816,8 @@ static void enqueue_one_int_qtd(u32 int_regs, u32 payload, qtd->status |= slot << 16; } -void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, - struct isp1760_qtd *qtd) +static void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, + struct isp1760_qtd *qtd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); u32 skip_map, or_map; @@ -816,8 +850,8 @@ void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, isp1760_writel(buffstatus, hcd->regs + HC_BUFFER_STATUS_REG); } -void enqueue_an_INT_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, - struct isp1760_qtd *qtd) +static void enqueue_an_INT_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, + struct isp1760_qtd *qtd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); u32 skip_map, or_map; @@ -1592,7 +1626,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, struct inter_packet_info *ints; u32 i; u32 reg_base, or_reg, skip_reg; - int flags; + unsigned long flags; struct ptd ptd; switch (usb_pipetype(urb->pipe)) { @@ -2061,7 +2095,7 @@ static void isp1760_endpoint_disable(struct usb_hcd *usb_hcd, struct isp1760_hcd *priv = hcd_to_priv(usb_hcd); struct isp1760_qh *qh; struct isp1760_qtd *qtd; - u32 flags; + unsigned long flags; spin_lock_irqsave(&priv->lock, flags); qh = ep->hcpriv; @@ -2112,6 +2146,7 @@ static int isp1760_get_frame(struct usb_hcd *hcd) static void isp1760_stop(struct usb_hcd *hcd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); + u32 temp; isp1760_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER, 1, NULL, 0); @@ -2120,7 +2155,8 @@ static void isp1760_stop(struct usb_hcd *hcd) spin_lock_irq(&priv->lock); ehci_reset(priv); /* Disable IRQ */ - isp1760_writel(HW_DATA_BUS_32BIT, hcd->regs + HC_HW_MODE_CTRL); + temp = isp1760_readl(hcd->regs + HC_HW_MODE_CTRL); + isp1760_writel(temp &= ~HW_GLOBAL_INTR_EN, hcd->regs + HC_HW_MODE_CTRL); spin_unlock_irq(&priv->lock); isp1760_writel(0, hcd->regs + HC_CONFIGFLAG); @@ -2128,10 +2164,11 @@ static void isp1760_stop(struct usb_hcd *hcd) static void isp1760_shutdown(struct usb_hcd *hcd) { - u32 command; + u32 command, temp; isp1760_stop(hcd); - isp1760_writel(HW_DATA_BUS_32BIT, hcd->regs + HC_HW_MODE_CTRL); + temp = isp1760_readl(hcd->regs + HC_HW_MODE_CTRL); + isp1760_writel(temp &= ~HW_GLOBAL_INTR_EN, hcd->regs + HC_HW_MODE_CTRL); command = isp1760_readl(hcd->regs + HC_USBCMD); command &= ~CMD_RUN; @@ -2183,7 +2220,8 @@ void deinit_kmem_cache(void) } struct usb_hcd *isp1760_register(u64 res_start, u64 res_len, int irq, - u64 irqflags, struct device *dev, const char *busname) + u64 irqflags, struct device *dev, const char *busname, + unsigned int devflags) { struct usb_hcd *hcd; struct isp1760_hcd *priv; @@ -2195,11 +2233,12 @@ struct usb_hcd *isp1760_register(u64 res_start, u64 res_len, int irq, /* prevent usb-core allocating DMA pages */ dev->dma_mask = NULL; - hcd = usb_create_hcd(&isp1760_hc_driver, dev, dev->bus_id); + hcd = usb_create_hcd(&isp1760_hc_driver, dev, dev_name(dev)); if (!hcd) return ERR_PTR(-ENOMEM); priv = hcd_to_priv(hcd); + priv->devflags = devflags; init_memory(priv); hcd->regs = ioremap(res_start, res_len); if (!hcd->regs) { diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index 3d86d0f6b147..6473dd86993c 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -3,7 +3,8 @@ /* exports for if */ struct usb_hcd *isp1760_register(u64 res_start, u64 res_len, int irq, - u64 irqflags, struct device *dev, const char *busname); + u64 irqflags, struct device *dev, const char *busname, + unsigned int devflags); int init_kmem_once(void); void deinit_kmem_cache(void); @@ -31,6 +32,7 @@ void deinit_kmem_cache(void); /* Configuration Register */ #define HC_HW_MODE_CTRL 0x300 #define ALL_ATX_RESET (1 << 31) +#define HW_ANA_DIGI_OC (1 << 15) #define HW_DATA_BUS_32BIT (1 << 8) #define HW_DACK_POL_HIGH (1 << 6) #define HW_DREQ_POL_HIGH (1 << 5) @@ -56,13 +58,14 @@ void deinit_kmem_cache(void); #define PORT1_POWER (3 << 3) #define PORT1_INIT1 (1 << 7) #define PORT1_INIT2 (1 << 23) +#define HW_OTG_CTRL_SET 0x374 +#define HW_OTG_CTRL_CLR 0x376 /* Interrupt Register */ #define HC_INTERRUPT_REG 0x310 #define HC_INTERRUPT_ENABLE 0x314 #define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT | HC_EOT_INT) -#define FINAL_HW_CONFIG (HW_GLOBAL_INTR_EN | HW_DATA_BUS_32BIT) #define HC_ISO_INT (1 << 9) #define HC_ATL_INT (1 << 8) @@ -122,6 +125,19 @@ typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh, #define isp1760_err(priv, fmt, args...) \ dev_err(priv_to_hcd(priv)->self.controller, fmt, ##args) +/* + * Device flags that can vary from board to board. All of these + * indicate the most "atypical" case, so that a devflags of 0 is + * a sane default configuration. + */ +#define ISP1760_FLAG_PORT1_DIS 0x00000001 /* Port 1 disabled */ +#define ISP1760_FLAG_BUS_WIDTH_16 0x00000002 /* 16-bit data bus width */ +#define ISP1760_FLAG_OTG_EN 0x00000004 /* Port 1 supports OTG */ +#define ISP1760_FLAG_ANALOG_OC 0x00000008 /* Analog overcurrent */ +#define ISP1760_FLAG_DACK_POL_HIGH 0x00000010 /* DACK active high */ +#define ISP1760_FLAG_DREQ_POL_HIGH 0x00000020 /* DREQ active high */ +#define ISP1760_FLAG_ISP1761 0x00000040 /* Chip is ISP1761 */ + /* chip memory management */ struct memory_chunk { unsigned int start; diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index c9db3fe98726..051ef7b6bdc6 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -35,13 +35,15 @@ static int of_isp1760_probe(struct of_device *dev, int virq; u64 res_len; int ret; + const unsigned int *prop; + unsigned int devflags = 0; ret = of_address_to_resource(dp, 0, &memory); if (ret) return -ENXIO; res = request_mem_region(memory.start, memory.end - memory.start + 1, - dev->dev.bus_id); + dev_name(&dev->dev)); if (!res) return -EBUSY; @@ -55,8 +57,32 @@ static int of_isp1760_probe(struct of_device *dev, virq = irq_create_of_mapping(oirq.controller, oirq.specifier, oirq.size); + if (of_device_is_compatible(dp, "nxp,usb-isp1761")) + devflags |= ISP1760_FLAG_ISP1761; + + if (of_get_property(dp, "port1-disable", NULL) != NULL) + devflags |= ISP1760_FLAG_PORT1_DIS; + + /* Some systems wire up only 16 of the 32 data lines */ + prop = of_get_property(dp, "bus-width", NULL); + if (prop && *prop == 16) + devflags |= ISP1760_FLAG_BUS_WIDTH_16; + + if (of_get_property(dp, "port1-otg", NULL) != NULL) + devflags |= ISP1760_FLAG_OTG_EN; + + if (of_get_property(dp, "analog-oc", NULL) != NULL) + devflags |= ISP1760_FLAG_ANALOG_OC; + + if (of_get_property(dp, "dack-polarity", NULL) != NULL) + devflags |= ISP1760_FLAG_DACK_POL_HIGH; + + if (of_get_property(dp, "dreq-polarity", NULL) != NULL) + devflags |= ISP1760_FLAG_DREQ_POL_HIGH; + hcd = isp1760_register(memory.start, res_len, virq, - IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev->dev.bus_id); + IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev), + devflags); if (IS_ERR(hcd)) { ret = PTR_ERR(hcd); goto release_reg; @@ -87,6 +113,9 @@ static struct of_device_id of_isp1760_match[] = { { .compatible = "nxp,usb-isp1760", }, + { + .compatible = "nxp,usb-isp1761", + }, { }, }; MODULE_DEVICE_TABLE(of, of_isp1760_match); @@ -116,6 +145,7 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev, int length; int status = 1; struct usb_hcd *hcd; + unsigned int devflags = 0; if (usb_disabled()) return -ENODEV; @@ -200,7 +230,8 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev, dev->dev.dma_mask = NULL; hcd = isp1760_register(pci_mem_phy0, length, dev->irq, - IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev->dev.bus_id); + IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev), + devflags); pci_set_drvdata(dev, hcd); if (!hcd) return 0; diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index e534f9de0f05..a5d8e550d897 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -91,7 +91,7 @@ static void at91_stop_hc(struct platform_device *pdev) /*-------------------------------------------------------------------------*/ -static int usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *); +static void usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *); /* configure so an HC device and id are always provided */ /* always called with process context; sleeping is OK */ @@ -184,13 +184,14 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, * context, "rmmod" or something similar. * */ -static int usb_hcd_at91_remove(struct usb_hcd *hcd, +static void usb_hcd_at91_remove(struct usb_hcd *hcd, struct platform_device *pdev) { usb_remove_hcd(hcd); at91_stop_hc(pdev); iounmap(hcd->regs); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); if (cpu_is_at91sam9261()) clk_put(hclk); @@ -199,7 +200,6 @@ static int usb_hcd_at91_remove(struct usb_hcd *hcd, fclk = iclk = hclk = NULL; dev_set_drvdata(&pdev->dev, NULL); - return 0; } /*-------------------------------------------------------------------------*/ @@ -309,7 +309,8 @@ static int ohci_hcd_at91_drv_remove(struct platform_device *pdev) } device_init_wakeup(&pdev->dev, 0); - return usb_hcd_at91_remove(platform_get_drvdata(pdev), pdev); + usb_hcd_at91_remove(platform_get_drvdata(pdev), pdev); + return 0; } #ifdef CONFIG_PM diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c index 68c17f5ea8ea..c0948008fe3d 100644 --- a/drivers/usb/host/ohci-au1xxx.c +++ b/drivers/usb/host/ohci-au1xxx.c @@ -34,7 +34,8 @@ #ifdef __LITTLE_ENDIAN #define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C) #elif __BIG_ENDIAN -#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | USBH_ENABLE_BE) +#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | \ + USBH_ENABLE_BE) #else #error not byte order defined #endif @@ -46,213 +47,87 @@ #define USB_MCFG_RDCOMB (1<<30) #define USB_MCFG_SSDEN (1<<23) #define USB_MCFG_OHCCLKEN (1<<16) +#ifdef CONFIG_DMA_COHERENT #define USB_MCFG_UCAM (1<<7) +#else +#define USB_MCFG_UCAM (0) +#endif #define USB_MCFG_OBMEN (1<<1) #define USB_MCFG_OMEMEN (1<<0) #define USBH_ENABLE_CE USB_MCFG_OHCCLKEN -#ifdef CONFIG_DMA_COHERENT -#define USBH_ENABLE_INIT (USB_MCFG_OHCCLKEN \ - | USB_MCFG_PFEN | USB_MCFG_RDCOMB \ - | USB_MCFG_SSDEN | USB_MCFG_UCAM \ - | USB_MCFG_OBMEN | USB_MCFG_OMEMEN) -#else -#define USBH_ENABLE_INIT (USB_MCFG_OHCCLKEN \ - | USB_MCFG_PFEN | USB_MCFG_RDCOMB \ - | USB_MCFG_SSDEN \ - | USB_MCFG_OBMEN | USB_MCFG_OMEMEN) -#endif + +#define USBH_ENABLE_INIT (USB_MCFG_PFEN | USB_MCFG_RDCOMB | \ + USBH_ENABLE_CE | USB_MCFG_SSDEN | \ + USB_MCFG_UCAM | \ + USB_MCFG_OBMEN | USB_MCFG_OMEMEN) + #define USBH_DISABLE (USB_MCFG_OBMEN | USB_MCFG_OMEMEN) #endif /* Au1200 */ extern int usb_disabled(void); -/*-------------------------------------------------------------------------*/ - -static void au1xxx_start_ohc(struct platform_device *dev) +static void au1xxx_start_ohc(void) { - printk(KERN_DEBUG __FILE__ - ": starting Au1xxx OHCI USB Controller\n"); - /* enable host controller */ - #ifndef CONFIG_SOC_AU1200 - au_writel(USBH_ENABLE_CE, USB_HOST_CONFIG); + au_sync(); udelay(1000); - au_writel(USBH_ENABLE_INIT, USB_HOST_CONFIG); - udelay(1000); - -#else /* Au1200 */ - /* write HW defaults again in case Yamon cleared them */ - if (au_readl(USB_HOST_CONFIG) == 0) { - au_writel(0x00d02000, USB_HOST_CONFIG); - au_readl(USB_HOST_CONFIG); - udelay(1000); - } - au_writel(USBH_ENABLE_CE | au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG); - au_readl(USB_HOST_CONFIG); + au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_INIT, USB_HOST_CONFIG); + au_sync(); udelay(1000); - au_writel(USBH_ENABLE_INIT | au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG); - au_readl(USB_HOST_CONFIG); - udelay(1000); - -#endif /* Au1200 */ -#ifndef CONFIG_SOC_AU1200 /* wait for reset complete (read register twice; see au1500 errata) */ while (au_readl(USB_HOST_CONFIG), !(au_readl(USB_HOST_CONFIG) & USBH_ENABLE_RD)) -#endif udelay(1000); - printk(KERN_DEBUG __FILE__ - ": Clock to USB host has been enabled \n"); -} - -static void au1xxx_stop_ohc(struct platform_device *dev) -{ - printk(KERN_DEBUG __FILE__ - ": stopping Au1xxx OHCI USB Controller\n"); - -#ifndef CONFIG_SOC_AU1200 - - /* Disable clock */ - au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG); - #else /* Au1200 */ - - /* Disable mem */ - au_writel(~USBH_DISABLE & au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG); + au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_CE, USB_HOST_CONFIG); + au_sync(); udelay(1000); - /* Disable clock */ - au_writel(~USBH_ENABLE_CE & au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG); - au_readl(USB_HOST_CONFIG); + + au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_INIT, USB_HOST_CONFIG); + au_sync(); + udelay(2000); #endif /* Au1200 */ } - -/*-------------------------------------------------------------------------*/ - -/* configure so an HC device and id are always provided */ -/* always called with process context; sleeping is OK */ - - -/** - * usb_ohci_au1xxx_probe - initialize Au1xxx-based HCDs - * Context: !in_interrupt() - * - * Allocates basic resources for this USB host controller, and - * then invokes the start() method for the HCD associated with it - * through the hotplug entry's driver_data. - * - */ -static int usb_ohci_au1xxx_probe(const struct hc_driver *driver, - struct platform_device *dev) +static void au1xxx_stop_ohc(void) { - int retval; - struct usb_hcd *hcd; - -#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT) - /* Au1200 AB USB does not support coherent memory */ - if (!(read_c0_prid() & 0xff)) { - pr_info("%s: this is chip revision AB !!\n", - dev->name); - pr_info("%s: update your board or re-configure the kernel\n", - dev->name); - return -ENODEV; - } +#ifdef CONFIG_SOC_AU1200 + /* Disable mem */ + au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_DISABLE, USB_HOST_CONFIG); + au_sync(); + udelay(1000); #endif - - if (dev->resource[1].flags != IORESOURCE_IRQ) { - pr_debug("resource[1] is not IORESOURCE_IRQ\n"); - return -ENOMEM; - } - - hcd = usb_create_hcd(driver, &dev->dev, "au1xxx"); - if (!hcd) - return -ENOMEM; - hcd->rsrc_start = dev->resource[0].start; - hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1; - - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { - pr_debug("request_mem_region failed\n"); - retval = -EBUSY; - goto err1; - } - - hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); - if (!hcd->regs) { - pr_debug("ioremap failed\n"); - retval = -ENOMEM; - goto err2; - } - - au1xxx_start_ohc(dev); - ohci_hcd_init(hcd_to_ohci(hcd)); - - retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED | IRQF_SHARED); - if (retval == 0) - return retval; - - au1xxx_stop_ohc(dev); - iounmap(hcd->regs); - err2: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - err1: - usb_put_hcd(hcd); - return retval; -} - - -/* may be called without controller electrically present */ -/* may be called with controller, bus, and devices active */ - -/** - * usb_hcd_au1xxx_remove - shutdown processing for Au1xxx-based HCDs - * @dev: USB Host Controller being removed - * Context: !in_interrupt() - * - * Reverses the effect of usb_hcd_au1xxx_probe(), first invoking - * the HCD's stop() method. It is always called from a thread - * context, normally "rmmod", "apmd", or something similar. - * - */ -static void usb_ohci_au1xxx_remove(struct usb_hcd *hcd, struct platform_device *dev) -{ - usb_remove_hcd(hcd); - au1xxx_stop_ohc(dev); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - usb_put_hcd(hcd); + /* Disable clock */ + au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG); + au_sync(); } -/*-------------------------------------------------------------------------*/ - -static int __devinit -ohci_au1xxx_start (struct usb_hcd *hcd) +static int __devinit ohci_au1xxx_start(struct usb_hcd *hcd) { - struct ohci_hcd *ohci = hcd_to_ohci (hcd); - int ret; + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int ret; - ohci_dbg (ohci, "ohci_au1xxx_start, ohci:%p", ohci); + ohci_dbg(ohci, "ohci_au1xxx_start, ohci:%p", ohci); - if ((ret = ohci_init (ohci)) < 0) + if ((ret = ohci_init(ohci)) < 0) return ret; - if ((ret = ohci_run (ohci)) < 0) { + if ((ret = ohci_run(ohci)) < 0) { err ("can't start %s", hcd->self.bus_name); - ohci_stop (hcd); + ohci_stop(hcd); return ret; } return 0; } -/*-------------------------------------------------------------------------*/ - static const struct hc_driver ohci_au1xxx_hc_driver = { .description = hcd_name, .product_desc = "Au1xxx OHCI", @@ -296,18 +171,66 @@ static const struct hc_driver ohci_au1xxx_hc_driver = { .start_port_reset = ohci_start_port_reset, }; -/*-------------------------------------------------------------------------*/ - static int ohci_hcd_au1xxx_drv_probe(struct platform_device *pdev) { int ret; - - pr_debug ("In ohci_hcd_au1xxx_drv_probe"); + struct usb_hcd *hcd; if (usb_disabled()) return -ENODEV; - ret = usb_ohci_au1xxx_probe(&ohci_au1xxx_hc_driver, pdev); +#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT) + /* Au1200 AB USB does not support coherent memory */ + if (!(read_c0_prid() & 0xff)) { + printk(KERN_INFO "%s: this is chip revision AB !!\n", + pdev->name); + printk(KERN_INFO "%s: update your board or re-configure " + "the kernel\n", pdev->name); + return -ENODEV; + } +#endif + + if (pdev->resource[1].flags != IORESOURCE_IRQ) { + pr_debug("resource[1] is not IORESOURCE_IRQ\n"); + return -ENOMEM; + } + + hcd = usb_create_hcd(&ohci_au1xxx_hc_driver, &pdev->dev, "au1xxx"); + if (!hcd) + return -ENOMEM; + + hcd->rsrc_start = pdev->resource[0].start; + hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + pr_debug("request_mem_region failed\n"); + ret = -EBUSY; + goto err1; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + pr_debug("ioremap failed\n"); + ret = -ENOMEM; + goto err2; + } + + au1xxx_start_ohc(); + ohci_hcd_init(hcd_to_ohci(hcd)); + + ret = usb_add_hcd(hcd, pdev->resource[1].start, + IRQF_DISABLED | IRQF_SHARED); + if (ret == 0) { + platform_set_drvdata(pdev, hcd); + return ret; + } + + au1xxx_stop_ohc(); + iounmap(hcd->regs); +err2: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err1: + usb_put_hcd(hcd); return ret; } @@ -315,30 +238,78 @@ static int ohci_hcd_au1xxx_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); - usb_ohci_au1xxx_remove(hcd, pdev); + usb_remove_hcd(hcd); + au1xxx_stop_ohc(); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + platform_set_drvdata(pdev, NULL); + return 0; } - /*TBD*/ -/*static int ohci_hcd_au1xxx_drv_suspend(struct platform_device *dev) + +#ifdef CONFIG_PM +static int ohci_hcd_au1xxx_drv_suspend(struct platform_device *pdev, + pm_message_t message) { - struct usb_hcd *hcd = platform_get_drvdata(dev); + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + unsigned long flags; + int rc; + + rc = 0; + + /* Root hub was already suspended. Disable irq emission and + * mark HW unaccessible, bail out if RH has been resumed. Use + * the spinlock to properly synchronize with possible pending + * RH suspend or resume activity. + * + * This is still racy as hcd->state is manipulated outside of + * any locks =P But that will be a different fix. + */ + spin_lock_irqsave(&ohci->lock, flags); + if (hcd->state != HC_STATE_SUSPENDED) { + rc = -EINVAL; + goto bail; + } + ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); + (void)ohci_readl(ohci, &ohci->regs->intrdisable); - return 0; + /* make sure snapshot being resumed re-enumerates everything */ + if (message.event == PM_EVENT_PRETHAW) + ohci_usb_reset(ohci); + + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + au1xxx_stop_ohc(); +bail: + spin_unlock_irqrestore(&ohci->lock, flags); + + return rc; } -static int ohci_hcd_au1xxx_drv_resume(struct platform_device *dev) + +static int ohci_hcd_au1xxx_drv_resume(struct platform_device *pdev) { - struct usb_hcd *hcd = platform_get_drvdata(dev); + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + au1xxx_start_ohc(); + + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + ohci_finish_controller_resume(hcd); return 0; } -*/ +#else +#define ohci_hcd_au1xxx_drv_suspend NULL +#define ohci_hcd_au1xxx_drv_resume NULL +#endif static struct platform_driver ohci_hcd_au1xxx_driver = { .probe = ohci_hcd_au1xxx_drv_probe, .remove = ohci_hcd_au1xxx_drv_remove, .shutdown = usb_hcd_platform_shutdown, - /*.suspend = ohci_hcd_au1xxx_drv_suspend, */ - /*.resume = ohci_hcd_au1xxx_drv_resume, */ + .suspend = ohci_hcd_au1xxx_drv_suspend, + .resume = ohci_hcd_au1xxx_drv_resume, .driver = { .name = "au1xxx-ohci", .owner = THIS_MODULE, diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index e06bfaebec54..7cef1d2f7ccc 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -651,7 +651,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) "%s\n" "%s version " DRIVER_VERSION "\n", hcd->self.controller->bus->name, - hcd->self.controller->bus_id, + dev_name(hcd->self.controller), hcd->product_desc, hcd_name); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index a8160d65f32b..26bc47941d01 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -974,7 +974,7 @@ MODULE_LICENSE ("GPL"); #define PCI_DRIVER ohci_pci_driver #endif -#ifdef CONFIG_SA1111 +#if defined(CONFIG_ARCH_SA1100) && defined(CONFIG_SA1111) #include "ohci-sa1111.c" #define SA1111_DRIVER ohci_hcd_sa1111_driver #endif diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index a19a4f80a6e1..6e5e5f81ac90 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -329,7 +329,7 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver, } - hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id); + hcd = usb_create_hcd (driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { retval = -ENOMEM; goto err0; diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c index 28b458f20cc3..6ad8f2fc57b9 100644 --- a/drivers/usb/host/ohci-pnx4008.c +++ b/drivers/usb/host/ohci-pnx4008.c @@ -109,8 +109,6 @@ static struct clk *usb_clk; static int isp1301_probe(struct i2c_adapter *adap); static int isp1301_detach(struct i2c_client *client); -static int isp1301_command(struct i2c_client *client, unsigned int cmd, - void *arg); static const unsigned short normal_i2c[] = { ISP1301_I2C_ADDR, ISP1301_I2C_ADDR + 1, I2C_CLIENT_END }; @@ -123,30 +121,37 @@ static struct i2c_client_address_data addr_data = { }; struct i2c_driver isp1301_driver = { - .class = I2C_CLASS_HWMON, + .driver = { + .name = "isp1301_pnx", + }, .attach_adapter = isp1301_probe, .detach_client = isp1301_detach, - .command = isp1301_command }; static int isp1301_attach(struct i2c_adapter *adap, int addr, int kind) { struct i2c_client *c; + int err; c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) return -ENOMEM; - strcpy(c->name, "isp1301"); + strlcpy(c->name, "isp1301_pnx", I2C_NAME_SIZE); c->flags = 0; c->addr = addr; c->adapter = adap; c->driver = &isp1301_driver; + err = i2c_attach_client(c); + if (err) { + kfree(c); + return err; + } + isp1301_i2c_client = c; - return i2c_attach_client(c); + return 0; } static int isp1301_probe(struct i2c_adapter *adap) @@ -161,13 +166,6 @@ static int isp1301_detach(struct i2c_client *client) return 0; } -/* No commands defined */ -static int isp1301_command(struct i2c_client *client, unsigned int cmd, - void *arg) -{ - return 0; -} - static void i2c_write(u8 buf, u8 subaddr) { char tmpbuf[2]; @@ -389,7 +387,7 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev) while ((__raw_readl(USB_OTG_CLK_STAT) & USB_CLOCK_MASK) != USB_CLOCK_MASK) ; - hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id); + hcd = usb_create_hcd (driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { err("Failed to allocate HC buffer"); ret = -ENOMEM; diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index a67252791223..91e6e101a4cc 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -14,8 +14,8 @@ */ #include <linux/signal.h> +#include <linux/of_platform.h> -#include <asm/of_platform.h> #include <asm/prom.h> diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c index c1935ae537f8..55c95647f008 100644 --- a/drivers/usb/host/ohci-ps3.c +++ b/drivers/usb/host/ohci-ps3.c @@ -129,7 +129,7 @@ static int ps3_ohci_probe(struct ps3_system_bus_device *dev) dev->core.dma_mask = &dummy_mask; /* FIXME: for improper usb code */ - hcd = usb_create_hcd(&ps3_ohci_hc_driver, &dev->core, dev->core.bus_id); + hcd = usb_create_hcd(&ps3_ohci_hc_driver, &dev->core, dev_name(&dev->core)); if (!hcd) { dev_dbg(&dev->core, "%s:%d: usb_create_hcd failed\n", __func__, diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 9b547407c934..6a9b4c557953 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -159,9 +159,6 @@ static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed) { int branch; - if (ohci_to_hcd(ohci)->state == HC_STATE_QUIESCING) - return -EAGAIN; - ed->state = ED_OPER; ed->ed_prev = NULL; ed->ed_next = NULL; diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index e610698c6b60..21b164e4abeb 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c @@ -143,7 +143,7 @@ static int ohci_hcd_sm501_drv_probe(struct platform_device *pdev) goto err2; } - hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id); + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { retval = -ENOMEM; goto err2; diff --git a/drivers/usb/host/ohci-ssb.c b/drivers/usb/host/ohci-ssb.c index 7275186db315..3660c83d80af 100644 --- a/drivers/usb/host/ohci-ssb.c +++ b/drivers/usb/host/ohci-ssb.c @@ -113,7 +113,7 @@ static int ssb_ohci_attach(struct ssb_device *dev) ssb_device_enable(dev, flags); hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev, - dev->dev->bus_id); + dev_name(dev->dev)); if (!hcd) goto err_dev_disable; ohcidev = hcd_to_ssb_ohci(hcd); diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 16667342b3c3..d5f02dddb120 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -312,9 +312,9 @@ static void put_child_connect_map(struct r8a66597 *r8a66597, int address) static void set_pipe_reg_addr(struct r8a66597_pipe *pipe, u8 dma_ch) { u16 pipenum = pipe->info.pipenum; - unsigned long fifoaddr[] = {D0FIFO, D1FIFO, CFIFO}; - unsigned long fifosel[] = {D0FIFOSEL, D1FIFOSEL, CFIFOSEL}; - unsigned long fifoctr[] = {D0FIFOCTR, D1FIFOCTR, CFIFOCTR}; + const unsigned long fifoaddr[] = {D0FIFO, D1FIFO, CFIFO}; + const unsigned long fifosel[] = {D0FIFOSEL, D1FIFOSEL, CFIFOSEL}; + const unsigned long fifoctr[] = {D0FIFOCTR, D1FIFOCTR, CFIFOCTR}; if (dma_ch > R8A66597_PIPE_NO_DMA) /* dma fifo not use? */ dma_ch = R8A66597_PIPE_NO_DMA; @@ -863,6 +863,32 @@ static void disable_r8a66597_pipe_all(struct r8a66597 *r8a66597, dev->dma_map = 0; } +static u16 get_interval(struct urb *urb, __u8 interval) +{ + u16 time = 1; + int i; + + if (urb->dev->speed == USB_SPEED_HIGH) { + if (interval > IITV) + time = IITV; + else + time = interval ? interval - 1 : 0; + } else { + if (interval > 128) { + time = IITV; + } else { + /* calculate the nearest value for PIPEPERI */ + for (i = 0; i < 7; i++) { + if ((1 << i) < interval && + (1 << (i + 1) > interval)) + time = 1 << i; + } + } + } + + return time; +} + static unsigned long get_timer_interval(struct urb *urb, __u8 interval) { __u8 i; @@ -901,10 +927,7 @@ static void init_pipe_info(struct r8a66597 *r8a66597, struct urb *urb, info.interval = 0; info.timer_interval = 0; } else { - if (ep->bInterval > IITV) - info.interval = IITV; - else - info.interval = ep->bInterval ? ep->bInterval - 1 : 0; + info.interval = get_interval(urb, ep->bInterval); info.timer_interval = get_timer_interval(urb, ep->bInterval); } if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) @@ -2244,6 +2267,7 @@ static int __init r8a66597_probe(struct platform_device *pdev) struct r8a66597 *r8a66597; int ret = 0; int i; + unsigned long irq_trigger; if (pdev->dev.dma_mask) { ret = -EINVAL; @@ -2302,7 +2326,11 @@ static int __init r8a66597_probe(struct platform_device *pdev) INIT_LIST_HEAD(&r8a66597->child_device); hcd->rsrc_start = res->start; - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); + if (irq_sense == INTL) + irq_trigger = IRQF_TRIGGER_LOW; + else + irq_trigger = IRQF_TRIGGER_FALLING; + ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | irq_trigger); if (ret != 0) { err("Failed to add hcd"); goto clean_up; diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 426575247b23..8a74bbb57d08 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1026,7 +1026,7 @@ sl811h_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) if (!list_empty(&hep->urb_list)) msleep(3); if (!list_empty(&hep->urb_list)) - WARN("ep %p not empty?\n", ep); + WARNING("ep %p not empty?\n", ep); kfree(ep); hep->hcpriv = NULL; @@ -1674,7 +1674,7 @@ sl811h_probe(struct platform_device *dev) } /* allocate and initialize hcd */ - hcd = usb_create_hcd(&sl811h_hc_driver, &dev->dev, dev->dev.bus_id); + hcd = usb_create_hcd(&sl811h_hc_driver, &dev->dev, dev_name(&dev->dev)); if (!hcd) { retval = -ENOMEM; goto err5; diff --git a/drivers/usb/host/sl811.h b/drivers/usb/host/sl811.h index 7690d98e42a7..b6b8c1f233dd 100644 --- a/drivers/usb/host/sl811.h +++ b/drivers/usb/host/sl811.h @@ -261,6 +261,6 @@ sl811_read_buf(struct sl811 *sl811, int addr, void *buf, size_t count) #endif #define ERR(stuff...) printk(KERN_ERR "sl811: " stuff) -#define WARN(stuff...) printk(KERN_WARNING "sl811: " stuff) +#define WARNING(stuff...) printk(KERN_WARNING "sl811: " stuff) #define INFO(stuff...) printk(KERN_INFO "sl811: " stuff) diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index 9b6323f768b2..20ad3c48fcb2 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -3124,7 +3124,7 @@ static int __devinit u132_probe(struct platform_device *pdev) if (pdev->dev.dma_mask) return -EINVAL; - hcd = usb_create_hcd(&u132_hc_driver, &pdev->dev, pdev->dev.bus_id); + hcd = usb_create_hcd(&u132_hc_driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { printk(KERN_ERR "failed to create the usb hcd struct for U132\n" ); diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index 8e4427aebb14..885b585360b9 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -12,7 +12,7 @@ * (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu */ -static __u8 root_hub_hub_des[] = +static const __u8 root_hub_hub_des[] = { 0x09, /* __u8 bLength; */ 0x29, /* __u8 bDescriptorType; Hub-descriptor */ diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c index 093938697426..d2f61d5510e7 100644 --- a/drivers/usb/misc/auerswald.c +++ b/drivers/usb/misc/auerswald.c @@ -1421,7 +1421,8 @@ ofail: mutex_unlock(&cp->mutex); /* IOCTL functions */ -static int auerchar_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static long auerchar_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { pauerchar_t ccp = (pauerchar_t) file->private_data; int ret = 0; @@ -1452,7 +1453,7 @@ static int auerchar_ioctl (struct inode *inode, struct file *file, unsigned int mutex_unlock(&ccp->mutex); return -ENODEV; } - + lock_kernel(); switch (cmd) { /* return != 0 if Transmitt channel ready to send */ @@ -1547,9 +1548,10 @@ static int auerchar_ioctl (struct inode *inode, struct file *file, unsigned int default: dbg ("IOCTL_AU_UNKNOWN"); - ret = -ENOIOCTLCMD; + ret = -ENOTTY; break; } + unlock_kernel(); /* release the mutexes */ mutex_unlock(&cp->mutex); mutex_unlock(&ccp->mutex); @@ -1860,7 +1862,7 @@ static const struct file_operations auerswald_fops = .llseek = no_llseek, .read = auerchar_read, .write = auerchar_write, - .ioctl = auerchar_ioctl, + .unlocked_ioctl = auerchar_ioctl, .open = auerchar_open, .release = auerchar_release, }; diff --git a/drivers/usb/misc/emi62.c b/drivers/usb/misc/emi62.c index 20886c21e739..5d859ded5bbf 100644 --- a/drivers/usb/misc/emi62.c +++ b/drivers/usb/misc/emi62.c @@ -6,8 +6,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, as published by * the Free Software Foundation, version 2. - * - * $Id: emi62.c,v 1.15 2002/04/23 06:13:59 tapio Exp $ */ #include <linux/kernel.h> #include <linux/errno.h> diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c index ec88b3bfee46..97c280971532 100644 --- a/drivers/usb/misc/ftdi-elan.c +++ b/drivers/usb/misc/ftdi-elan.c @@ -656,29 +656,6 @@ static int ftdi_elan_release(struct inode *inode, struct file *file) } -#define FTDI_ELAN_IOC_MAGIC 0xA1 -#define FTDI_ELAN_IOCDEBUG _IOC(_IOC_WRITE, FTDI_ELAN_IOC_MAGIC, 1, 132) -static int ftdi_elan_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case FTDI_ELAN_IOCDEBUG:{ - char line[132]; - int size = strncpy_from_user(line, - (const char __user *)arg, sizeof(line)); - if (size < 0) { - return -EINVAL; - } else { - printk(KERN_ERR "TODO: ioctl %s\n", line); - return 0; - } - } - default: - return -EFAULT; - } -} - - /* * * blocking bulk reads are used to get data from the device @@ -1222,7 +1199,6 @@ error_1: static const struct file_operations ftdi_elan_fops = { .owner = THIS_MODULE, .llseek = no_llseek, - .ioctl = ftdi_elan_ioctl, .read = ftdi_elan_read, .write = ftdi_elan_write, .open = ftdi_elan_open, diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 1cb54a28347f..e6ca9979e3ae 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -474,8 +474,8 @@ exit: /** * iowarrior_ioctl */ -static int iowarrior_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long iowarrior_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { struct iowarrior *dev = NULL; __u8 *buffer; @@ -493,6 +493,7 @@ static int iowarrior_ioctl(struct inode *inode, struct file *file, return -ENOMEM; /* lock this object */ + lock_kernel(); mutex_lock(&dev->mutex); /* verify that the device wasn't unplugged */ @@ -584,6 +585,7 @@ static int iowarrior_ioctl(struct inode *inode, struct file *file, error_out: /* unlock the device */ mutex_unlock(&dev->mutex); + unlock_kernel(); kfree(buffer); return retval; } @@ -719,7 +721,7 @@ static const struct file_operations iowarrior_fops = { .owner = THIS_MODULE, .write = iowarrior_write, .read = iowarrior_read, - .ioctl = iowarrior_ioctl, + .unlocked_ioctl = iowarrior_ioctl, .open = iowarrior_open, .release = iowarrior_release, .poll = iowarrior_poll, diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c index 330c18e390b8..248a12aacef6 100644 --- a/drivers/usb/misc/rio500.c +++ b/drivers/usb/misc/rio500.c @@ -104,9 +104,7 @@ static int close_rio(struct inode *inode, struct file *file) return 0; } -static int -ioctl_rio(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) +static long ioctl_rio(struct file *file, unsigned int cmd, unsigned long arg) { struct RioCommand rio_cmd; struct rio_usb_data *rio = &rio_instance; @@ -116,6 +114,7 @@ ioctl_rio(struct inode *inode, struct file *file, unsigned int cmd, int retries; int retval=0; + lock_kernel(); mutex_lock(&(rio->lock)); /* Sanity check to make sure rio is connected, powered, etc */ if (rio->present == 0 || rio->rio_dev == NULL) { @@ -254,6 +253,7 @@ ioctl_rio(struct inode *inode, struct file *file, unsigned int cmd, err_out: mutex_unlock(&(rio->lock)); + unlock_kernel(); return retval; } @@ -433,7 +433,7 @@ file_operations usb_rio_fops = { .owner = THIS_MODULE, .read = read_rio, .write = write_rio, - .ioctl = ioctl_rio, + .unlocked_ioctl = ioctl_rio, .open = open_rio, .release = close_rio, }; diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index 33182f4c2267..fbace41a7cba 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -2982,9 +2982,8 @@ sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y, return retval; } -static int -sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) +static long +sisusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct sisusb_usb_data *sisusb; struct sisusb_info x; @@ -2995,6 +2994,7 @@ sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) return -ENODEV; + lock_kernel(); mutex_lock(&sisusb->lock); /* Sanity check */ @@ -3053,6 +3053,7 @@ sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, err_out: mutex_unlock(&sisusb->lock); + unlock_kernel(); return retval; } @@ -3066,9 +3067,7 @@ sisusb_compat_ioctl(struct file *f, unsigned int cmd, unsigned long arg) case SISUSB_GET_CONFIG_SIZE: case SISUSB_GET_CONFIG: case SISUSB_COMMAND: - lock_kernel(); - retval = sisusb_ioctl(f->f_path.dentry->d_inode, f, cmd, arg); - unlock_kernel(); + retval = sisusb_ioctl(f, cmd, arg); return retval; default: @@ -3087,7 +3086,7 @@ static const struct file_operations usb_sisusb_fops = { #ifdef SISUSB_NEW_CONFIG_COMPAT .compat_ioctl = sisusb_compat_ioctl, #endif - .ioctl = sisusb_ioctl + .unlocked_ioctl = sisusb_ioctl }; static struct usb_class_driver usb_sisusb_class = { diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 7f7021ee4189..2db4228fbb01 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -146,7 +146,7 @@ static ssize_t lcd_read(struct file *file, char __user * buffer, size_t count, l return retval; } -static int lcd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct usb_lcd *dev; u16 bcdDevice; @@ -158,12 +158,14 @@ static int lcd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, u switch (cmd) { case IOCTL_GET_HARD_VERSION: + lock_kernel(); bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice); sprintf(buf,"%1d%1d.%1d%1d", (bcdDevice & 0xF000)>>12, (bcdDevice & 0xF00)>>8, (bcdDevice & 0xF0)>>4, (bcdDevice & 0xF)); + unlock_kernel(); if (copy_to_user((void __user *)arg,buf,strlen(buf))!=0) return -EFAULT; break; @@ -272,7 +274,7 @@ static const struct file_operations lcd_fops = { .read = lcd_read, .write = lcd_write, .open = lcd_open, - .ioctl = lcd_ioctl, + .unlocked_ioctl = lcd_ioctl, .release = lcd_release, }; diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 054dedd28127..b358c4e1cf21 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -81,7 +81,7 @@ static struct usb_device *testdev_to_usbdev (struct usbtest_dev *test) #define ERROR(tdev, fmt, args...) \ dev_err(&(tdev)->intf->dev , fmt , ## args) -#define WARN(tdev, fmt, args...) \ +#define WARNING(tdev, fmt, args...) \ dev_warn(&(tdev)->intf->dev , fmt , ## args) /*-------------------------------------------------------------------------*/ @@ -1946,7 +1946,7 @@ usbtest_probe (struct usb_interface *intf, const struct usb_device_id *id) status = get_endpoints (dev, intf); if (status < 0) { - WARN(dev, "couldn't get endpoints, %d\n", + WARNING(dev, "couldn't get endpoints, %d\n", status); return status; } diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index 293a46247c3b..6566fc0a3228 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -1162,8 +1162,9 @@ int mon_bin_add(struct mon_bus *mbus, const struct usb_bus *ubus) if (minor >= MON_BIN_MAX_MINOR) return 0; - dev = device_create(mon_bin_class, ubus? ubus->controller: NULL, - MKDEV(MAJOR(mon_bin_dev0), minor), "usbmon%d", minor); + dev = device_create_drvdata(mon_bin_class, ubus? ubus->controller: NULL, + MKDEV(MAJOR(mon_bin_dev0), minor), NULL, + "usbmon%d", minor); if (IS_ERR(dev)) return 0; diff --git a/drivers/usb/mon/mon_stat.c b/drivers/usb/mon/mon_stat.c index c7a595cd648a..ac8b0d5ce7f8 100644 --- a/drivers/usb/mon/mon_stat.c +++ b/drivers/usb/mon/mon_stat.c @@ -9,6 +9,7 @@ #include <linux/kernel.h> #include <linux/usb.h> +#include <linux/fs.h> #include <asm/uaccess.h> #include "usb_mon.h" @@ -42,19 +43,8 @@ static ssize_t mon_stat_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { struct snap *sp = file->private_data; - loff_t pos = *ppos; - int cnt; - if (pos < 0 || pos >= sp->slen) - return 0; - if (nbytes == 0) - return 0; - if ((cnt = sp->slen - pos) > nbytes) - cnt = nbytes; - if (copy_to_user(buf, sp->str + pos, cnt)) - return -EFAULT; - *ppos = pos + cnt; - return cnt; + return simple_read_from_buffer(buf, nbytes, ppos, sp->str, sp->slen); } static int mon_stat_release(struct inode *inode, struct file *file) diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index 5e3e4e9b6c77..1f715436d6d3 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -87,7 +87,7 @@ struct mon_reader_text { static struct dentry *mon_dir; /* Usually /sys/kernel/debug/usbmon */ -static void mon_text_ctor(struct kmem_cache *, void *); +static void mon_text_ctor(void *); struct mon_text_ptr { int cnt, limit; @@ -720,7 +720,7 @@ void mon_text_del(struct mon_bus *mbus) /* * Slab interface: constructor. */ -static void mon_text_ctor(struct kmem_cache *slab, void *mem) +static void mon_text_ctor(void *mem) { /* * Nothing to initialize. No, really! diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index 9a7681b55266..8878c1767fc8 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -64,14 +64,6 @@ config USB_SERIAL_AIRCABLE To compile this driver as a module, choose M here: the module will be called aircable. -config USB_SERIAL_AIRPRIME - tristate "USB AirPrime CDMA Wireless Driver" - help - Say Y here if you want to use a AirPrime CDMA Wireless PC card. - - To compile this driver as a module, choose M here: the - module will be called airprime. - config USB_SERIAL_ARK3116 tristate "USB ARK Micro 3116 USB Serial Driver" help diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index 17a762ab6769..6047f818adfe 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -12,7 +12,6 @@ usbserial-obj-$(CONFIG_USB_EZUSB) += ezusb.o usbserial-objs := usb-serial.o generic.o bus.o $(usbserial-obj-y) obj-$(CONFIG_USB_SERIAL_AIRCABLE) += aircable.o -obj-$(CONFIG_USB_SERIAL_AIRPRIME) += airprime.o obj-$(CONFIG_USB_SERIAL_ARK3116) += ark3116.o obj-$(CONFIG_USB_SERIAL_BELKIN) += belkin_sa.o obj-$(CONFIG_USB_SERIAL_CH341) += ch341.o diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index db6f97a93c02..79ea98c66fa8 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -272,7 +272,7 @@ static void aircable_read(struct work_struct *work) * 64 bytes, to ensure I do not get throttled. * Ask USB mailing list for better aproach. */ - tty = port->tty; + tty = port->port.tty; if (!tty) { schedule_work(&priv->rx_work); @@ -378,13 +378,14 @@ static void aircable_shutdown(struct usb_serial *serial) } } -static int aircable_write_room(struct usb_serial_port *port) +static int aircable_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct aircable_private *priv = usb_get_serial_port_data(port); return serial_buf_data_avail(priv->tx_buf); } -static int aircable_write(struct usb_serial_port *port, +static int aircable_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *source, int count) { struct aircable_private *priv = usb_get_serial_port_data(port); @@ -466,7 +467,7 @@ static void aircable_read_bulk_callback(struct urb *urb) if (status) { dbg("%s - urb status = %d", __func__, status); - if (!port->open_count) { + if (!port->port.count) { dbg("%s - port is closed, exiting.", __func__); return; } @@ -494,7 +495,7 @@ static void aircable_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, urb->transfer_buffer); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { if (urb->actual_length <= 2) { /* This is an incomplete package */ @@ -528,7 +529,7 @@ static void aircable_read_bulk_callback(struct urb *urb) } /* Schedule the next read _if_ we are still open */ - if (port->open_count) { + if (port->port.count) { usb_fill_bulk_urb(port->read_urb, port->serial->dev, usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), @@ -547,8 +548,9 @@ static void aircable_read_bulk_callback(struct urb *urb) } /* Based on ftdi_sio.c throttle */ -static void aircable_throttle(struct usb_serial_port *port) +static void aircable_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct aircable_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -560,8 +562,9 @@ static void aircable_throttle(struct usb_serial_port *port) } /* Based on ftdi_sio.c unthrottle */ -static void aircable_unthrottle(struct usb_serial_port *port) +static void aircable_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct aircable_private *priv = usb_get_serial_port_data(port); int actually_throttled; unsigned long flags; diff --git a/drivers/usb/serial/airprime.c b/drivers/usb/serial/airprime.c deleted file mode 100644 index 0798c14ce787..000000000000 --- a/drivers/usb/serial/airprime.c +++ /dev/null @@ -1,353 +0,0 @@ -/* - * AirPrime CDMA Wireless Serial USB driver - * - * Copyright (C) 2005-2006 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 version - * 2 as published by the Free Software Foundation. - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/module.h> -#include <linux/usb.h> -#include <linux/usb/serial.h> - -static struct usb_device_id id_table [] = { - { USB_DEVICE(0x0c88, 0x17da) }, /* Kyocera Wireless KPC650/Passport */ - { }, -}; -MODULE_DEVICE_TABLE(usb, id_table); - -#define URB_TRANSFER_BUFFER_SIZE 4096 -#define NUM_READ_URBS 4 -#define NUM_WRITE_URBS 4 -#define NUM_BULK_EPS 3 -#define MAX_BULK_EPS 6 - -/* if overridden by the user, then use their value for the size of the - * read and write urbs, and the number of endpoints */ -static int buffer_size = URB_TRANSFER_BUFFER_SIZE; -static int endpoints = NUM_BULK_EPS; -static int debug; -struct airprime_private { - spinlock_t lock; - 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", __func__); - - 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; - unsigned char *data = urb->transfer_buffer; - struct tty_struct *tty; - int result; - int status = urb->status; - - dbg("%s - port %d", __func__, port->number); - - if (status) { - dbg("%s - nonzero read bulk status received: %d", - __func__, status); - return; - } - usb_serial_debug_data(debug, &port->dev, __func__, - urb->actual_length, data); - - tty = port->tty; - if (tty && urb->actual_length) { - tty_insert_flip_string(tty, data, urb->actual_length); - tty_flip_buffer_push(tty); - } - - result = usb_submit_urb(urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, - "%s - failed resubmitting read urb, error %d\n", - __func__, result); - return; -} - -static void airprime_write_bulk_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - struct airprime_private *priv = usb_get_serial_port_data(port); - int status = urb->status; - unsigned long flags; - - dbg("%s - port %d", __func__, port->number); - - /* free up the transfer buffer, as usb_free_urb() does not do this */ - kfree(urb->transfer_buffer); - - if (status) - dbg("%s - nonzero write bulk status received: %d", - __func__, status); - spin_lock_irqsave(&priv->lock, flags); - --priv->outstanding_urbs; - spin_unlock_irqrestore(&priv->lock, flags); - - usb_serial_port_softint(port); -} - -static int airprime_open(struct usb_serial_port *port, struct file *filp) -{ - struct airprime_private *priv = usb_get_serial_port_data(port); - struct usb_serial *serial = port->serial; - struct urb *urb; - char *buffer = NULL; - int i; - int result = 0; - - dbg("%s - port %d", __func__, port->number); - - /* initialize our private data structure if it isn't already created */ - if (!priv) { - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { - result = -ENOMEM; - goto out; - } - spin_lock_init(&priv->lock); - 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) { - dev_err(&port->dev, "%s - out of memory.\n", - __func__); - result = -ENOMEM; - goto errout; - } - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) { - kfree(buffer); - dev_err(&port->dev, "%s - no more urbs?\n", - __func__); - result = -ENOMEM; - goto errout; - } - usb_fill_bulk_urb(urb, serial->dev, - usb_rcvbulkpipe(serial->dev, - port->bulk_out_endpointAddress), - buffer, buffer_size, - 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", - __func__, i, port->number, result); - goto errout; - } - /* 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 */ - - while (i-- != 0) { - urb = priv->read_urbp[i]; - buffer = urb->transfer_buffer; - usb_kill_urb(urb); - usb_free_urb(urb); - kfree(buffer); - } - - out: - return result; -} - -static void airprime_close(struct usb_serial_port *port, struct file *filp) -{ - struct airprime_private *priv = usb_get_serial_port_data(port); - int i; - - dbg("%s - port %d", __func__, port->number); - - priv->rts_state = 0; - priv->dtr_state = 0; - - mutex_lock(&port->serial->disc_mutex); - if (!port->serial->disconnected) - airprime_send_setup(port); - mutex_unlock(&port->serial->disc_mutex); - - 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]); - } - - /* free up private structure */ - kfree(priv); - usb_set_serial_port_data(port, NULL); -} - -static int airprime_write(struct usb_serial_port *port, - const unsigned char *buf, int count) -{ - struct airprime_private *priv = usb_get_serial_port_data(port); - struct usb_serial *serial = port->serial; - struct urb *urb; - unsigned char *buffer; - unsigned long flags; - int status; - dbg("%s - port %d", __func__, port->number); - - spin_lock_irqsave(&priv->lock, flags); - if (priv->outstanding_urbs > NUM_WRITE_URBS) { - spin_unlock_irqrestore(&priv->lock, flags); - dbg("%s - write limit hit\n", __func__); - return 0; - } - spin_unlock_irqrestore(&priv->lock, flags); - buffer = kmalloc(count, GFP_ATOMIC); - if (!buffer) { - dev_err(&port->dev, "out of memory\n"); - return -ENOMEM; - } - urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!urb) { - dev_err(&port->dev, "no more free urbs\n"); - kfree(buffer); - return -ENOMEM; - } - memcpy(buffer, buf, count); - - usb_serial_debug_data(debug, &port->dev, __func__, count, buffer); - - usb_fill_bulk_urb(urb, serial->dev, - usb_sndbulkpipe(serial->dev, - port->bulk_out_endpointAddress), - buffer, count, - airprime_write_bulk_callback, port); - - /* send it down the pipe */ - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) { - dev_err(&port->dev, - "%s - usb_submit_urb(write bulk) failed with status = %d\n", - __func__, status); - count = status; - kfree(buffer); - } else { - spin_lock_irqsave(&priv->lock, flags); - ++priv->outstanding_urbs; - 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); - return count; -} - -static struct usb_driver airprime_driver = { - .name = "airprime", - .probe = usb_serial_probe, - .disconnect = usb_serial_disconnect, - .id_table = id_table, - .no_dynamic_id = 1, -}; - -static struct usb_serial_driver airprime_device = { - .driver = { - .owner = THIS_MODULE, - .name = "airprime", - }, - .usb_driver = &airprime_driver, - .id_table = id_table, - .open = airprime_open, - .close = airprime_close, - .write = airprime_write, -}; - -static int __init airprime_init(void) -{ - int retval; - - airprime_device.num_ports = endpoints; - if (endpoints < 0 || endpoints >= MAX_BULK_EPS) - airprime_device.num_ports = NUM_BULK_EPS; - - retval = usb_serial_register(&airprime_device); - if (retval) - return retval; - retval = usb_register(&airprime_driver); - if (retval) - usb_serial_deregister(&airprime_device); - return retval; -} - -static void __exit airprime_exit(void) -{ - dbg("%s", __func__); - - usb_deregister(&airprime_driver); - usb_serial_deregister(&airprime_device); -} - -module_init(airprime_init); -module_exit(airprime_exit); -MODULE_LICENSE("GPL"); - -module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug enabled"); -module_param(buffer_size, int, 0); -MODULE_PARM_DESC(buffer_size, - "Size of the transfer buffers in bytes (default 4096)"); -module_param(endpoints, int, 0); -MODULE_PARM_DESC(endpoints, "Number of bulk EPs to configure (default 3)"); diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 77895c8f8f31..aec61880f36c 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -158,12 +158,13 @@ cleanup: return -ENOMEM; } -static void ark3116_set_termios(struct usb_serial_port *port, +static void ark3116_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; struct ark3116_private *priv = usb_get_serial_port_data(port); - struct ktermios *termios = port->tty->termios; + struct ktermios *termios = tty->termios; unsigned int cflag = termios->c_cflag; unsigned long flags; int baud; @@ -177,8 +178,8 @@ static void ark3116_set_termios(struct usb_serial_port *port, spin_lock_irqsave(&priv->lock, flags); if (!priv->termios_initialized) { - *(port->tty->termios) = tty_std_termios; - port->tty->termios->c_cflag = B9600 | CS8 + *termios = tty_std_termios; + termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; termios->c_ispeed = 9600; termios->c_ospeed = 9600; @@ -192,7 +193,7 @@ static void ark3116_set_termios(struct usb_serial_port *port, buf = kmalloc(1, GFP_KERNEL); if (!buf) { dbg("error kmalloc"); - *port->tty->termios = *old_termios; + *termios = *old_termios; return; } @@ -243,7 +244,7 @@ static void ark3116_set_termios(struct usb_serial_port *port, } /* set baudrate */ - baud = tty_get_baud_rate(port->tty); + baud = tty_get_baud_rate(tty); switch (baud) { case 75: @@ -262,11 +263,11 @@ static void ark3116_set_termios(struct usb_serial_port *port, case 230400: case 460800: /* Report the resulting rate back to the caller */ - tty_encode_baud_rate(port->tty, baud, baud); + tty_encode_baud_rate(tty, baud, baud); break; /* set 9600 as default (if given baudrate is invalid for example) */ default: - tty_encode_baud_rate(port->tty, 9600, 9600); + tty_encode_baud_rate(tty, 9600, 9600); case 0: baud = 9600; } @@ -317,7 +318,8 @@ static void ark3116_set_termios(struct usb_serial_port *port, return; } -static int ark3116_open(struct usb_serial_port *port, struct file *filp) +static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { struct ktermios tmp_termios; struct usb_serial *serial = port->serial; @@ -332,7 +334,7 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp) return -ENOMEM; } - result = usb_serial_generic_open(port, filp); + result = usb_serial_generic_open(tty, port, filp); if (result) goto err_out; @@ -362,8 +364,8 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp) ARK3116_RCV(serial, 124, 0xFE, 0xC0, 0x0000, 0x0006, 0xFF, buf); /* initialise termios */ - if (port->tty) - ark3116_set_termios(port, &tmp_termios); + if (tty) + ark3116_set_termios(tty, port, &tmp_termios); err_out: kfree(buf); @@ -371,9 +373,10 @@ err_out: return result; } -static int ark3116_ioctl(struct usb_serial_port *port, struct file *file, +static int ark3116_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct serial_struct serstruct; void __user *user_arg = (void __user *)arg; @@ -403,8 +406,9 @@ static int ark3116_ioctl(struct usb_serial_port *port, struct file *file, return -ENOIOCTLCMD; } -static int ark3116_tiocmget(struct usb_serial_port *port, struct file *file) +static int ark3116_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; char *buf; char temp; diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 0a322fc53d6e..2ebe06c3405a 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -7,13 +7,14 @@ * This program is largely derived from work by the linux-usb group * and associated source files. Please see the usb/serial files for * individual credits and copyrights. - * + * * 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. * - * See Documentation/usb/usb-serial.txt for more information on using this driver + * See Documentation/usb/usb-serial.txt for more information on using this + * driver * * TODO: * -- Add true modem contol line query capability. Currently we track the @@ -28,7 +29,8 @@ * compressed all the differnent device entries into 1. * * 30-May-2001 gkh - * switched from using spinlock to a semaphore, which fixes lots of problems. + * switched from using spinlock to a semaphore, which fixes lots of + * problems. * * 08-Apr-2001 gb * - Identify version on module load. @@ -41,7 +43,7 @@ * - Added support for the old Belkin and Peracom devices. * - Made the port able to be opened multiple times. * - Added some defaults incase the line settings are things these devices - * can't support. + * can't support. * * 18-Oct-2000 William Greathouse * Released into the wild (linux-usb-devel) @@ -72,7 +74,7 @@ #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include "belkin_sa.h" @@ -87,16 +89,19 @@ static int debug; #define DRIVER_DESC "USB Belkin Serial converter driver" /* function prototypes for a Belkin USB Serial Adapter F5U103 */ -static int belkin_sa_startup (struct usb_serial *serial); -static void belkin_sa_shutdown (struct usb_serial *serial); -static int belkin_sa_open (struct usb_serial_port *port, struct file *filp); -static void belkin_sa_close (struct usb_serial_port *port, struct file *filp); -static void belkin_sa_read_int_callback (struct urb *urb); -static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios * old); -static int belkin_sa_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); -static void belkin_sa_break_ctl (struct usb_serial_port *port, int break_state ); -static int belkin_sa_tiocmget (struct usb_serial_port *port, struct file *file); -static int belkin_sa_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); +static int belkin_sa_startup(struct usb_serial *serial); +static void belkin_sa_shutdown(struct usb_serial *serial); +static int belkin_sa_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void belkin_sa_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void belkin_sa_read_int_callback(struct urb *urb); +static void belkin_sa_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios * old); +static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state); +static int belkin_sa_tiocmget(struct tty_struct *tty, struct file *file); +static int belkin_sa_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear); static struct usb_device_id id_table_combined [] = { @@ -106,10 +111,10 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(GOHUBS_VID, GOHUBS_PID) }, { USB_DEVICE(GOHUBS_VID, HANDYLINK_PID) }, { USB_DEVICE(BELKIN_DOCKSTATION_VID, BELKIN_DOCKSTATION_PID) }, - { } /* Terminating entry */ + { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table_combined); +MODULE_DEVICE_TABLE(usb, id_table_combined); static struct usb_driver belkin_driver = { .name = "belkin", @@ -131,8 +136,8 @@ static struct usb_serial_driver belkin_device = { .num_ports = 1, .open = belkin_sa_open, .close = belkin_sa_close, - .read_int_callback = belkin_sa_read_int_callback, /* How we get the status info */ - .ioctl = belkin_sa_ioctl, + .read_int_callback = belkin_sa_read_int_callback, + /* How we get the status info */ .set_termios = belkin_sa_set_termios, .break_ctl = belkin_sa_break_ctl, .tiocmget = belkin_sa_tiocmget, @@ -160,12 +165,12 @@ struct belkin_sa_private { #define WDR_TIMEOUT 5000 /* default urb timeout */ /* assumes that struct usb_serial *serial is available */ -#define BSA_USB_CMD(c,v) usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), \ +#define BSA_USB_CMD(c, v) usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), \ (c), BELKIN_SA_SET_REQUEST_TYPE, \ (v), 0, NULL, 0, WDR_TIMEOUT) /* do some startup allocations not currently performed by usb_serial_probe() */ -static int belkin_sa_startup (struct usb_serial *serial) +static int belkin_sa_startup(struct usb_serial *serial) { struct usb_device *dev = serial->dev; struct belkin_sa_private *priv; @@ -173,32 +178,35 @@ static int belkin_sa_startup (struct usb_serial *serial) /* allocate the private data structure */ priv = kmalloc(sizeof(struct belkin_sa_private), GFP_KERNEL); if (!priv) - return (-1); /* error */ + return -1; /* error */ /* set initial values for control structures */ spin_lock_init(&priv->lock); priv->control_state = 0; priv->last_lsr = 0; priv->last_msr = 0; /* see comments at top of file */ - priv->bad_flow_control = (le16_to_cpu(dev->descriptor.bcdDevice) <= 0x0206) ? 1 : 0; - info("bcdDevice: %04x, bfc: %d", le16_to_cpu(dev->descriptor.bcdDevice), priv->bad_flow_control); + priv->bad_flow_control = + (le16_to_cpu(dev->descriptor.bcdDevice) <= 0x0206) ? 1 : 0; + info("bcdDevice: %04x, bfc: %d", + le16_to_cpu(dev->descriptor.bcdDevice), + priv->bad_flow_control); init_waitqueue_head(&serial->port[0]->write_wait); usb_set_serial_port_data(serial->port[0], priv); - - return (0); + + return 0; } -static void belkin_sa_shutdown (struct usb_serial *serial) +static void belkin_sa_shutdown(struct usb_serial *serial) { struct belkin_sa_private *priv; int i; - - dbg ("%s", __func__); + + dbg("%s", __func__); /* stop reads and writes on all ports */ - for (i=0; i < serial->num_ports; ++i) { + for (i = 0; i < serial->num_ports; ++i) { /* My special items, the standard routines free my urbs */ priv = usb_get_serial_port_data(serial->port[i]); kfree(priv); @@ -206,7 +214,8 @@ static void belkin_sa_shutdown (struct usb_serial *serial) } -static int belkin_sa_open (struct usb_serial_port *port, struct file *filp) +static int belkin_sa_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int retval = 0; @@ -235,7 +244,8 @@ exit: } /* belkin_sa_open */ -static void belkin_sa_close (struct usb_serial_port *port, struct file *filp) +static void belkin_sa_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { dbg("%s port %d", __func__, port->number); @@ -246,7 +256,7 @@ static void belkin_sa_close (struct usb_serial_port *port, struct file *filp) } /* belkin_sa_close */ -static void belkin_sa_read_int_callback (struct urb *urb) +static void belkin_sa_read_int_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct belkin_sa_private *priv; @@ -272,7 +282,8 @@ static void belkin_sa_read_int_callback (struct urb *urb) goto exit; } - usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, data); /* Handle known interrupt data */ /* ignore data[0] and data[1] */ @@ -280,7 +291,7 @@ static void belkin_sa_read_int_callback (struct urb *urb) priv = usb_get_serial_port_data(port); spin_lock_irqsave(&priv->lock, flags); priv->last_msr = data[BELKIN_SA_MSR_INDEX]; - + /* Record Control Line states */ if (priv->last_msr & BELKIN_SA_MSR_DSR) priv->control_state |= TIOCM_DSR; @@ -311,7 +322,7 @@ static void belkin_sa_read_int_callback (struct urb *urb) * to look in to this before committing any code. */ if (priv->last_lsr & BELKIN_SA_LSR_ERR) { - tty = port->tty; + tty = port->port.tty; /* Overrun Error */ if (priv->last_lsr & BELKIN_SA_LSR_OE) { } @@ -328,13 +339,14 @@ static void belkin_sa_read_int_callback (struct urb *urb) #endif spin_unlock_irqrestore(&priv->lock, flags); exit: - retval = usb_submit_urb (urb, GFP_ATOMIC); + retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) - err ("%s - usb_submit_urb failed with result %d", + err("%s - usb_submit_urb failed with result %d", __func__, retval); } -static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) +static void belkin_sa_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; struct belkin_sa_private *priv = usb_get_serial_port_data(port); @@ -347,8 +359,8 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios unsigned long control_state; int bad_flow_control; speed_t baud; - struct ktermios *termios = port->tty->termios; - + struct ktermios *termios = tty->termios; + iflag = termios->c_iflag; cflag = termios->c_cflag; @@ -359,25 +371,26 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios control_state = priv->control_state; bad_flow_control = priv->bad_flow_control; spin_unlock_irqrestore(&priv->lock, flags); - + old_iflag = old_termios->c_iflag; old_cflag = old_termios->c_cflag; /* Set the baud rate */ if ((cflag & CBAUD) != (old_cflag & CBAUD)) { /* reassert DTR and (maybe) RTS on transition from B0 */ - if( (old_cflag&CBAUD) == B0 ) { + if ((old_cflag & CBAUD) == B0) { control_state |= (TIOCM_DTR|TIOCM_RTS); if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 1) < 0) err("Set DTR error"); /* don't set RTS if using hardware flow control */ if (!(old_cflag & CRTSCTS)) - if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, 1) < 0) + if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST + , 1) < 0) err("Set RTS error"); } } - baud = tty_get_baud_rate(port->tty); + baud = tty_get_baud_rate(tty); if (baud) { urb_value = BELKIN_SA_BAUD(baud); /* Clip to maximum speed */ @@ -387,12 +400,13 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios baud = BELKIN_SA_BAUD(urb_value); /* Report the actual baud rate back to the caller */ - tty_encode_baud_rate(port->tty, baud, baud); + tty_encode_baud_rate(tty, baud, baud); if (BSA_USB_CMD(BELKIN_SA_SET_BAUDRATE_REQUEST, urb_value) < 0) err("Set baudrate error"); } else { /* Disable flow control */ - if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST, BELKIN_SA_FLOW_NONE) < 0) + if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST, + BELKIN_SA_FLOW_NONE) < 0) err("Disable flowcontrol error"); /* Drop RTS and DTR */ control_state &= ~(TIOCM_DTR | TIOCM_RTS); @@ -403,9 +417,10 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios } /* set the parity */ - if( (cflag&(PARENB|PARODD)) != (old_cflag&(PARENB|PARODD)) ) { + if ((cflag ^ old_cflag) & (PARENB | PARODD)) { if (cflag & PARENB) - urb_value = (cflag & PARODD) ? BELKIN_SA_PARITY_ODD : BELKIN_SA_PARITY_EVEN; + urb_value = (cflag & PARODD) ? BELKIN_SA_PARITY_ODD + : BELKIN_SA_PARITY_EVEN; else urb_value = BELKIN_SA_PARITY_NONE; if (BSA_USB_CMD(BELKIN_SA_SET_PARITY_REQUEST, urb_value) < 0) @@ -413,31 +428,40 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios } /* set the number of data bits */ - if( (cflag&CSIZE) != (old_cflag&CSIZE) ) { + if ((cflag & CSIZE) != (old_cflag & CSIZE)) { switch (cflag & CSIZE) { - case CS5: urb_value = BELKIN_SA_DATA_BITS(5); break; - case CS6: urb_value = BELKIN_SA_DATA_BITS(6); break; - case CS7: urb_value = BELKIN_SA_DATA_BITS(7); break; - case CS8: urb_value = BELKIN_SA_DATA_BITS(8); break; - default: dbg("CSIZE was not CS5-CS8, using default of 8"); - urb_value = BELKIN_SA_DATA_BITS(8); - break; + case CS5: + urb_value = BELKIN_SA_DATA_BITS(5); + break; + case CS6: + urb_value = BELKIN_SA_DATA_BITS(6); + break; + case CS7: + urb_value = BELKIN_SA_DATA_BITS(7); + break; + case CS8: + urb_value = BELKIN_SA_DATA_BITS(8); + break; + default: dbg("CSIZE was not CS5-CS8, using default of 8"); + urb_value = BELKIN_SA_DATA_BITS(8); + break; } if (BSA_USB_CMD(BELKIN_SA_SET_DATA_BITS_REQUEST, urb_value) < 0) err("Set data bits error"); } /* set the number of stop bits */ - if( (cflag&CSTOPB) != (old_cflag&CSTOPB) ) { - urb_value = (cflag & CSTOPB) ? BELKIN_SA_STOP_BITS(2) : BELKIN_SA_STOP_BITS(1); - if (BSA_USB_CMD(BELKIN_SA_SET_STOP_BITS_REQUEST, urb_value) < 0) + if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) { + urb_value = (cflag & CSTOPB) ? BELKIN_SA_STOP_BITS(2) + : BELKIN_SA_STOP_BITS(1); + if (BSA_USB_CMD(BELKIN_SA_SET_STOP_BITS_REQUEST, + urb_value) < 0) err("Set stop bits error"); } /* Set flow control */ - if( (iflag&IXOFF) != (old_iflag&IXOFF) - || (iflag&IXON) != (old_iflag&IXON) - || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) ) { + if (((iflag ^ old_iflag) & (IXOFF | IXON)) || + ((cflag ^ old_cflag) & CRTSCTS)) { urb_value = 0; if ((iflag & IXOFF) || (iflag & IXON)) urb_value |= (BELKIN_SA_FLOW_OXON | BELKIN_SA_FLOW_IXON); @@ -463,8 +487,9 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios } /* belkin_sa_set_termios */ -static void belkin_sa_break_ctl( struct usb_serial_port *port, int break_state ) +static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; if (BSA_USB_CMD(BELKIN_SA_SET_BREAK_REQUEST, break_state ? 1 : 0) < 0) @@ -472,12 +497,13 @@ static void belkin_sa_break_ctl( struct usb_serial_port *port, int break_state ) } -static int belkin_sa_tiocmget (struct usb_serial_port *port, struct file *file) +static int belkin_sa_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct belkin_sa_private *priv = usb_get_serial_port_data(port); unsigned long control_state; unsigned long flags; - + dbg("%s", __func__); spin_lock_irqsave(&priv->lock, flags); @@ -488,9 +514,10 @@ static int belkin_sa_tiocmget (struct usb_serial_port *port, struct file *file) } -static int belkin_sa_tiocmset (struct usb_serial_port *port, struct file *file, +static int belkin_sa_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; struct belkin_sa_private *priv = usb_get_serial_port_data(port); unsigned long control_state; @@ -498,7 +525,7 @@ static int belkin_sa_tiocmset (struct usb_serial_port *port, struct file *file, int retval; int rts = 0; int dtr = 0; - + dbg("%s", __func__); spin_lock_irqsave(&priv->lock, flags); @@ -540,29 +567,7 @@ exit: } -static int belkin_sa_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case TIOCMIWAIT: - /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ - /* TODO */ - return( 0 ); - - case TIOCGICOUNT: - /* return count of modemline transitions */ - /* TODO */ - return 0; - - default: - dbg("belkin_sa_ioctl arg not supported - 0x%04x",cmd); - return(-ENOIOCTLCMD); - break; - } - return 0; -} /* belkin_sa_ioctl */ - - -static int __init belkin_sa_init (void) +static int __init belkin_sa_init(void) { int retval; retval = usb_serial_register(&belkin_device); @@ -582,17 +587,17 @@ failed_usb_serial_register: static void __exit belkin_sa_exit (void) { - usb_deregister (&belkin_driver); - usb_serial_deregister (&belkin_device); + usb_deregister(&belkin_driver); + usb_serial_deregister(&belkin_device); } -module_init (belkin_sa_init); -module_exit (belkin_sa_exit); +module_init(belkin_sa_init); +module_exit(belkin_sa_exit); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_VERSION( DRIVER_VERSION ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); diff --git a/drivers/usb/serial/belkin_sa.h b/drivers/usb/serial/belkin_sa.h index 9116b92f4622..c66a6730d38c 100644 --- a/drivers/usb/serial/belkin_sa.h +++ b/drivers/usb/serial/belkin_sa.h @@ -7,13 +7,14 @@ * This program is largely derived from work by the linux-usb group * and associated source files. Please see the usb/serial files for * individual credits and copyrights. - * + * * 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. * - * See Documentation/usb/usb-serial.txt for more information on using this driver + * See Documentation/usb/usb-serial.txt for more information on using this + * driver * * 12-Mar-2001 gkh * Added GoHubs GO-COM232 device id. @@ -27,7 +28,7 @@ * adapter, so pardon any stupid mistakes. All of the information * I am using to write this driver was acquired by using a modified * UsbSnoop on Windows2000. - * + * */ #ifndef __LINUX_USB_SERIAL_BSA_H @@ -96,20 +97,20 @@ /* * It seems that the interrupt pipe is closely modelled after the - * 16550 register layout. This is probably because the adapter can + * 16550 register layout. This is probably because the adapter can * be used in a "DOS" environment to simulate a standard hardware port. */ -#define BELKIN_SA_LSR_INDEX 2 /* Line Status Register */ +#define BELKIN_SA_LSR_INDEX 2 /* Line Status Register */ #define BELKIN_SA_LSR_RDR 0x01 /* receive data ready */ #define BELKIN_SA_LSR_OE 0x02 /* overrun error */ #define BELKIN_SA_LSR_PE 0x04 /* parity error */ #define BELKIN_SA_LSR_FE 0x08 /* framing error */ #define BELKIN_SA_LSR_BI 0x10 /* break indicator */ -#define BELKIN_SA_LSR_THE 0x20 /* transmit holding register empty */ +#define BELKIN_SA_LSR_THE 0x20 /* tx holding register empty */ #define BELKIN_SA_LSR_TE 0x40 /* transmit register empty */ #define BELKIN_SA_LSR_ERR 0x80 /* OE | PE | FE | BI */ -#define BELKIN_SA_MSR_INDEX 3 /* Modem Status Register */ +#define BELKIN_SA_MSR_INDEX 3 /* Modem Status Register */ #define BELKIN_SA_MSR_DCTS 0x01 /* Delta CTS */ #define BELKIN_SA_MSR_DDSR 0x02 /* Delta DSR */ #define BELKIN_SA_MSR_DRI 0x04 /* Delta RI */ diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index 0b14aea8ebd5..83bbb5bca2ef 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -15,7 +15,8 @@ #include <linux/usb.h> #include <linux/usb/serial.h> -static int usb_serial_device_match (struct device *dev, struct device_driver *drv) +static int usb_serial_device_match(struct device *dev, + struct device_driver *drv) { struct usb_serial_driver *driver; const struct usb_serial_port *port; @@ -46,7 +47,7 @@ static ssize_t show_port_number(struct device *dev, static DEVICE_ATTR(port_number, S_IRUGO, show_port_number, NULL); -static int usb_serial_device_probe (struct device *dev) +static int usb_serial_device_probe(struct device *dev) { struct usb_serial_driver *driver; struct usb_serial_port *port; @@ -66,7 +67,7 @@ static int usb_serial_device_probe (struct device *dev) retval = -EIO; goto exit; } - retval = driver->port_probe (port); + retval = driver->port_probe(port); module_put(driver->driver.owner); if (retval) goto exit; @@ -77,8 +78,8 @@ static int usb_serial_device_probe (struct device *dev) goto exit; minor = port->number; - tty_register_device (usb_serial_tty_driver, minor, dev); - dev_info(&port->serial->dev->dev, + tty_register_device(usb_serial_tty_driver, minor, dev); + dev_info(&port->serial->dev->dev, "%s converter now attached to ttyUSB%d\n", driver->description, minor); @@ -86,7 +87,7 @@ exit: return retval; } -static int usb_serial_device_remove (struct device *dev) +static int usb_serial_device_remove(struct device *dev) { struct usb_serial_driver *driver; struct usb_serial_port *port; @@ -94,9 +95,8 @@ static int usb_serial_device_remove (struct device *dev) int minor; port = to_usb_serial_port(dev); - if (!port) { + if (!port) return -ENODEV; - } device_remove_file(&port->dev, &dev_attr_port_number); @@ -107,12 +107,12 @@ static int usb_serial_device_remove (struct device *dev) retval = -EIO; goto exit; } - retval = driver->port_remove (port); + retval = driver->port_remove(port); module_put(driver->driver.owner); } exit: minor = port->number; - tty_unregister_device (usb_serial_tty_driver, minor); + tty_unregister_device(usb_serial_tty_driver, minor); dev_info(dev, "%s converter now disconnected from ttyUSB%d\n", driver->description, minor); diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 1f7c86bd8297..f61e3ca64305 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -232,7 +232,8 @@ error: kfree(priv); } /* open this device, set default parameters */ -static int ch341_open(struct usb_serial_port *port, struct file *filp) +static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { struct usb_serial *serial = port->serial; struct ch341_private *priv = usb_get_serial_port_data(serial->port[0]); @@ -256,7 +257,7 @@ static int ch341_open(struct usb_serial_port *port, struct file *filp) if (r) goto out; - r = usb_serial_generic_open(port, filp); + r = usb_serial_generic_open(tty, port, filp); out: return r; } @@ -264,11 +265,10 @@ out: return r; /* Old_termios contains the original termios settings and * tty->termios contains the new setting to be used. */ -static void ch341_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void ch341_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct ch341_private *priv = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; unsigned baud_rate; dbg("ch341_set_termios()"); diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index 201184c3fb87..7b74238ad1c7 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -6,7 +6,7 @@ * 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. - * + * * Thanks to Randy Dunlap for the original version of this code. * */ @@ -67,7 +67,7 @@ static int usb_console_setup(struct console *co, char *options) struct tty_struct *tty = NULL; struct ktermios *termios = NULL, dummy; - dbg ("%s", __func__); + dbg("%s", __func__); if (options) { baud = simple_strtoul(options, NULL, 10); @@ -81,55 +81,27 @@ static int usb_console_setup(struct console *co, char *options) if (*s) doflow = (*s++ == 'r'); } + + /* Sane default */ + if (baud == 0) + baud = 9600; - /* build a cflag setting */ - switch (baud) { - case 1200: - cflag |= B1200; - break; - case 2400: - cflag |= B2400; - break; - case 4800: - cflag |= B4800; - break; - case 19200: - cflag |= B19200; - break; - case 38400: - cflag |= B38400; - break; - case 57600: - cflag |= B57600; - break; - case 115200: - cflag |= B115200; - break; - case 9600: - default: - cflag |= B9600; - /* - * Set this to a sane value to prevent a divide error - */ - baud = 9600; - break; - } switch (bits) { - case 7: - cflag |= CS7; - break; - default: - case 8: - cflag |= CS8; - break; + case 7: + cflag |= CS7; + break; + default: + case 8: + cflag |= CS8; + break; } switch (parity) { - case 'o': case 'O': - cflag |= PARODD; - break; - case 'e': case 'E': - cflag |= PARENB; - break; + case 'o': case 'O': + cflag |= PARODD; + break; + case 'e': case 'E': + cflag |= PARENB; + break; } co->cflag = cflag; @@ -140,17 +112,17 @@ static int usb_console_setup(struct console *co, char *options) serial = usb_serial_get_by_index(co->index); if (serial == NULL) { /* no device is connected yet, sorry :( */ - err ("No USB device connected to ttyUSB%i", co->index); + err("No USB device connected to ttyUSB%i", co->index); return -ENODEV; } port = serial->port[0]; - port->tty = NULL; + port->port.tty = NULL; info->port = port; - - ++port->open_count; - if (port->open_count == 1) { + + ++port->port.count; + if (port->port.count == 1) { if (serial->type->set_termios) { /* * allocate a fake tty so the driver can initialize @@ -171,15 +143,15 @@ static int usb_console_setup(struct console *co, char *options) } memset(&dummy, 0, sizeof(struct ktermios)); tty->termios = termios; - port->tty = tty; + port->port.tty = tty; } - /* only call the device specific open if this + /* only call the device specific open if this * is the first time the port is opened */ if (serial->type->open) - retval = serial->type->open(port, NULL); + retval = serial->type->open(NULL, port, NULL); else - retval = usb_serial_generic_open(port, NULL); + retval = usb_serial_generic_open(NULL, port, NULL); if (retval) { err("could not open USB console port"); @@ -188,9 +160,10 @@ static int usb_console_setup(struct console *co, char *options) if (serial->type->set_termios) { termios->c_cflag = cflag; - serial->type->set_termios(port, &dummy); + tty_termios_encode_baud_rate(termios, baud, baud); + serial->type->set_termios(NULL, port, &dummy); - port->tty = NULL; + port->port.tty = NULL; kfree(termios); kfree(tty); } @@ -203,15 +176,16 @@ out: return retval; free_termios: kfree(termios); - port->tty = NULL; + port->port.tty = NULL; free_tty: kfree(tty); reset_open_count: - port->open_count = 0; + port->port.count = 0; goto out; } -static void usb_console_write(struct console *co, const char *buf, unsigned count) +static void usb_console_write(struct console *co, + const char *buf, unsigned count) { static struct usbcons_info *info = &usbcons_info; struct usb_serial_port *port = info->port; @@ -227,8 +201,8 @@ static void usb_console_write(struct console *co, const char *buf, unsigned coun dbg("%s - port %d, %d byte(s)", __func__, port->number, count); - if (!port->open_count) { - dbg ("%s - port not opened", __func__); + if (!port->port.count) { + dbg("%s - port not opened", __func__); return; } @@ -236,26 +210,29 @@ static void usb_console_write(struct console *co, const char *buf, unsigned coun unsigned int i; unsigned int lf; /* search for LF so we can insert CR if necessary */ - for (i=0, lf=0 ; i < count ; i++) { + for (i = 0, lf = 0 ; i < count ; i++) { if (*(buf + i) == 10) { lf = 1; i++; break; } } - /* pass on to the driver specific version of this function if it is available */ + /* pass on to the driver specific version of this function if + it is available */ if (serial->type->write) - retval = serial->type->write(port, buf, i); + retval = serial->type->write(NULL, port, buf, i); else - retval = usb_serial_generic_write(port, buf, i); + retval = usb_serial_generic_write(NULL, port, buf, i); dbg("%s - return value : %d", __func__, retval); if (lf) { /* append CR after LF */ unsigned char cr = 13; if (serial->type->write) - retval = serial->type->write(port, &cr, 1); + retval = serial->type->write(NULL, + port, &cr, 1); else - retval = usb_serial_generic_write(port, &cr, 1); + retval = usb_serial_generic_write(NULL, + port, &cr, 1); dbg("%s - return value : %d", __func__, retval); } buf += i; @@ -273,18 +250,19 @@ static struct console usbcons = { void usb_serial_console_disconnect(struct usb_serial *serial) { - if (serial && serial->port && serial->port[0] && serial->port[0] == usbcons_info.port) { + if (serial && serial->port && serial->port[0] + && serial->port[0] == usbcons_info.port) { usb_serial_console_exit(); usb_serial_put(serial); } } -void usb_serial_console_init (int serial_debug, int minor) +void usb_serial_console_init(int serial_debug, int minor) { debug = serial_debug; if (minor == 0) { - /* + /* * Call register_console() if this is the first device plugged * in. If we call it earlier, then the callback to * console_setup() will fail, as there is not a device seen by @@ -293,21 +271,21 @@ void usb_serial_console_init (int serial_debug, int minor) /* * Register console. * NOTES: - * console_setup() is called (back) immediately (from register_console). - * console_write() is called immediately from register_console iff - * CON_PRINTBUFFER is set in flags. + * console_setup() is called (back) immediately (from + * register_console). console_write() is called immediately + * from register_console iff CON_PRINTBUFFER is set in flags. */ - dbg ("registering the USB serial console."); + dbg("registering the USB serial console."); register_console(&usbcons); } } -void usb_serial_console_exit (void) +void usb_serial_console_exit(void) { if (usbcons_info.port) { unregister_console(&usbcons); - if (usbcons_info.port->open_count) - usbcons_info.port->open_count--; + if (usbcons_info.port->port.count) + usbcons_info.port->port.count--; usbcons_info.port = NULL; } } diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index f5b57b196c5a..442cba69cce5 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -25,7 +25,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/usb.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb/serial.h> /* @@ -37,17 +37,20 @@ /* * Function Prototypes */ -static int cp2101_open(struct usb_serial_port*, struct file*); -static void cp2101_cleanup(struct usb_serial_port*); -static void cp2101_close(struct usb_serial_port*, struct file*); -static void cp2101_get_termios(struct usb_serial_port*); -static void cp2101_set_termios(struct usb_serial_port*, struct ktermios*); -static int cp2101_tiocmget (struct usb_serial_port *, struct file *); -static int cp2101_tiocmset (struct usb_serial_port *, struct file *, +static int cp2101_open(struct tty_struct *, struct usb_serial_port *, + struct file *); +static void cp2101_cleanup(struct usb_serial_port *); +static void cp2101_close(struct tty_struct *, struct usb_serial_port *, + struct file*); +static void cp2101_get_termios(struct tty_struct *); +static void cp2101_set_termios(struct tty_struct *, struct usb_serial_port *, + struct ktermios*); +static int cp2101_tiocmget(struct tty_struct *, struct file *); +static int cp2101_tiocmset(struct tty_struct *, struct file *, unsigned int, unsigned int); -static void cp2101_break_ctl(struct usb_serial_port*, int); -static int cp2101_startup (struct usb_serial *); -static void cp2101_shutdown(struct usb_serial*); +static void cp2101_break_ctl(struct tty_struct *, int); +static int cp2101_startup(struct usb_serial *); +static void cp2101_shutdown(struct usb_serial *); static int debug; @@ -93,7 +96,7 @@ static struct usb_device_id id_table [] = { { } /* Terminating Entry */ }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, id_table); static struct usb_driver cp2101_driver = { .name = "cp2101", @@ -182,7 +185,7 @@ static struct usb_serial_driver cp2101_device = { * 'data' is a pointer to a pre-allocated array of integers large * enough to hold 'size' bytes (with 4 bytes to each integer) */ -static int cp2101_get_config(struct usb_serial_port* port, u8 request, +static int cp2101_get_config(struct usb_serial_port *port, u8 request, unsigned int *data, int size) { struct usb_serial *serial = port->serial; @@ -202,12 +205,12 @@ static int cp2101_get_config(struct usb_serial_port* port, u8 request, request++; /* Issue the request, attempting to read 'size' bytes */ - result = usb_control_msg (serial->dev,usb_rcvctrlpipe (serial->dev, 0), + result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), request, REQTYPE_DEVICE_TO_HOST, 0x0000, 0, buf, size, 300); /* Convert data into an array of integers */ - for (i=0; i<length; i++) + for (i = 0; i < length; i++) data[i] = le32_to_cpu(buf[i]); kfree(buf); @@ -228,7 +231,7 @@ static int cp2101_get_config(struct usb_serial_port* port, u8 request, * Values less than 16 bits wide are sent directly * 'size' is specified in bytes. */ -static int cp2101_set_config(struct usb_serial_port* port, u8 request, +static int cp2101_set_config(struct usb_serial_port *port, u8 request, unsigned int *data, int size) { struct usb_serial *serial = port->serial; @@ -250,12 +253,12 @@ static int cp2101_set_config(struct usb_serial_port* port, u8 request, buf[i] = cpu_to_le32(data[i]); if (size > 2) { - result = usb_control_msg (serial->dev, + result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), request, REQTYPE_HOST_TO_DEVICE, 0x0000, 0, buf, size, 300); } else { - result = usb_control_msg (serial->dev, + result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), request, REQTYPE_HOST_TO_DEVICE, data[0], 0, NULL, 0, 300); @@ -271,7 +274,7 @@ static int cp2101_set_config(struct usb_serial_port* port, u8 request, } /* Single data value */ - result = usb_control_msg (serial->dev, + result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), request, REQTYPE_HOST_TO_DEVICE, data[0], 0, NULL, 0, 300); @@ -283,13 +286,14 @@ static int cp2101_set_config(struct usb_serial_port* port, u8 request, * Convenience function for calling cp2101_set_config on single data values * without requiring an integer pointer */ -static inline int cp2101_set_config_single(struct usb_serial_port* port, +static inline int cp2101_set_config_single(struct usb_serial_port *port, u8 request, unsigned int data) { return cp2101_set_config(port, request, &data, 2); } -static int cp2101_open (struct usb_serial_port *port, struct file *filp) +static int cp2101_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { struct usb_serial *serial = port->serial; int result; @@ -303,7 +307,7 @@ static int cp2101_open (struct usb_serial_port *port, struct file *filp) } /* Start reading from the device */ - usb_fill_bulk_urb (port->read_urb, serial->dev, + usb_fill_bulk_urb(port->read_urb, serial->dev, usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), port->read_urb->transfer_buffer, @@ -318,15 +322,15 @@ static int cp2101_open (struct usb_serial_port *port, struct file *filp) } /* Configure the termios structure */ - cp2101_get_termios(port); + cp2101_get_termios(tty); /* Set the DTR and RTS pins low */ - cp2101_tiocmset(port, NULL, TIOCM_DTR | TIOCM_RTS, 0); + cp2101_tiocmset(tty, NULL, TIOCM_DTR | TIOCM_RTS, 0); return 0; } -static void cp2101_cleanup (struct usb_serial_port *port) +static void cp2101_cleanup(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; @@ -341,7 +345,8 @@ static void cp2101_cleanup (struct usb_serial_port *port) } } -static void cp2101_close (struct usb_serial_port *port, struct file * filp) +static void cp2101_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { dbg("%s - port %d", __func__, port->number); @@ -362,19 +367,15 @@ static void cp2101_close (struct usb_serial_port *port, struct file * filp) * from the device, corrects any unsupported values, and configures the * termios structure to reflect the state of the device */ -static void cp2101_get_termios (struct usb_serial_port *port) +static void cp2101_get_termios (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; unsigned int cflag, modem_ctl[4]; - int baud; - int bits; + unsigned int baud; + unsigned int bits; dbg("%s - port %d", __func__, port->number); - if (!port->tty || !port->tty->termios) { - dbg("%s - no tty structures", __func__); - return; - } - cp2101_get_config(port, CP2101_BAUDRATE, &baud, 2); /* Convert to baudrate */ if (baud) @@ -382,104 +383,102 @@ static void cp2101_get_termios (struct usb_serial_port *port) dbg("%s - baud rate = %d", __func__, baud); - tty_encode_baud_rate(port->tty, baud, baud); - cflag = port->tty->termios->c_cflag; + tty_encode_baud_rate(tty, baud, baud); + cflag = tty->termios->c_cflag; cp2101_get_config(port, CP2101_BITS, &bits, 2); cflag &= ~CSIZE; - switch(bits & BITS_DATA_MASK) { - case BITS_DATA_5: - dbg("%s - data bits = 5", __func__); - cflag |= CS5; - break; - case BITS_DATA_6: - dbg("%s - data bits = 6", __func__); - cflag |= CS6; - break; - case BITS_DATA_7: - dbg("%s - data bits = 7", __func__); - cflag |= CS7; - break; - case BITS_DATA_8: - dbg("%s - data bits = 8", __func__); - cflag |= CS8; - break; - case BITS_DATA_9: - dbg("%s - data bits = 9 (not supported, " - "using 8 data bits)", __func__); - cflag |= CS8; - bits &= ~BITS_DATA_MASK; - bits |= BITS_DATA_8; - cp2101_set_config(port, CP2101_BITS, &bits, 2); - break; - default: - dbg("%s - Unknown number of data bits, " - "using 8", __func__); - cflag |= CS8; - bits &= ~BITS_DATA_MASK; - bits |= BITS_DATA_8; - cp2101_set_config(port, CP2101_BITS, &bits, 2); - break; + switch (bits & BITS_DATA_MASK) { + case BITS_DATA_5: + dbg("%s - data bits = 5", __func__); + cflag |= CS5; + break; + case BITS_DATA_6: + dbg("%s - data bits = 6", __func__); + cflag |= CS6; + break; + case BITS_DATA_7: + dbg("%s - data bits = 7", __func__); + cflag |= CS7; + break; + case BITS_DATA_8: + dbg("%s - data bits = 8", __func__); + cflag |= CS8; + break; + case BITS_DATA_9: + dbg("%s - data bits = 9 (not supported, using 8 data bits)", + __func__); + cflag |= CS8; + bits &= ~BITS_DATA_MASK; + bits |= BITS_DATA_8; + cp2101_set_config(port, CP2101_BITS, &bits, 2); + break; + default: + dbg("%s - Unknown number of data bits, using 8", __func__); + cflag |= CS8; + bits &= ~BITS_DATA_MASK; + bits |= BITS_DATA_8; + cp2101_set_config(port, CP2101_BITS, &bits, 2); + break; } - switch(bits & BITS_PARITY_MASK) { - case BITS_PARITY_NONE: - dbg("%s - parity = NONE", __func__); - cflag &= ~PARENB; - break; - case BITS_PARITY_ODD: - dbg("%s - parity = ODD", __func__); - cflag |= (PARENB|PARODD); - break; - case BITS_PARITY_EVEN: - dbg("%s - parity = EVEN", __func__); - cflag &= ~PARODD; - cflag |= PARENB; - break; - case BITS_PARITY_MARK: - dbg("%s - parity = MARK (not supported, " - "disabling parity)", __func__); - cflag &= ~PARENB; - bits &= ~BITS_PARITY_MASK; - cp2101_set_config(port, CP2101_BITS, &bits, 2); - break; - case BITS_PARITY_SPACE: - dbg("%s - parity = SPACE (not supported, " - "disabling parity)", __func__); - cflag &= ~PARENB; - bits &= ~BITS_PARITY_MASK; - cp2101_set_config(port, CP2101_BITS, &bits, 2); - break; - default: - dbg("%s - Unknown parity mode, " - "disabling parity", __func__); - cflag &= ~PARENB; - bits &= ~BITS_PARITY_MASK; - cp2101_set_config(port, CP2101_BITS, &bits, 2); - break; + switch (bits & BITS_PARITY_MASK) { + case BITS_PARITY_NONE: + dbg("%s - parity = NONE", __func__); + cflag &= ~PARENB; + break; + case BITS_PARITY_ODD: + dbg("%s - parity = ODD", __func__); + cflag |= (PARENB|PARODD); + break; + case BITS_PARITY_EVEN: + dbg("%s - parity = EVEN", __func__); + cflag &= ~PARODD; + cflag |= PARENB; + break; + case BITS_PARITY_MARK: + dbg("%s - parity = MARK (not supported, disabling parity)", + __func__); + cflag &= ~PARENB; + bits &= ~BITS_PARITY_MASK; + cp2101_set_config(port, CP2101_BITS, &bits, 2); + break; + case BITS_PARITY_SPACE: + dbg("%s - parity = SPACE (not supported, disabling parity)", + __func__); + cflag &= ~PARENB; + bits &= ~BITS_PARITY_MASK; + cp2101_set_config(port, CP2101_BITS, &bits, 2); + break; + default: + dbg("%s - Unknown parity mode, disabling parity", __func__); + cflag &= ~PARENB; + bits &= ~BITS_PARITY_MASK; + cp2101_set_config(port, CP2101_BITS, &bits, 2); + break; } cflag &= ~CSTOPB; - switch(bits & BITS_STOP_MASK) { - case BITS_STOP_1: - dbg("%s - stop bits = 1", __func__); - break; - case BITS_STOP_1_5: - dbg("%s - stop bits = 1.5 (not supported, " - "using 1 stop bit)", __func__); - bits &= ~BITS_STOP_MASK; - cp2101_set_config(port, CP2101_BITS, &bits, 2); - break; - case BITS_STOP_2: - dbg("%s - stop bits = 2", __func__); - cflag |= CSTOPB; - break; - default: - dbg("%s - Unknown number of stop bits, " - "using 1 stop bit", __func__); - bits &= ~BITS_STOP_MASK; - cp2101_set_config(port, CP2101_BITS, &bits, 2); - break; + switch (bits & BITS_STOP_MASK) { + case BITS_STOP_1: + dbg("%s - stop bits = 1", __func__); + break; + case BITS_STOP_1_5: + dbg("%s - stop bits = 1.5 (not supported, using 1 stop bit)", + __func__); + bits &= ~BITS_STOP_MASK; + cp2101_set_config(port, CP2101_BITS, &bits, 2); + break; + case BITS_STOP_2: + dbg("%s - stop bits = 2", __func__); + cflag |= CSTOPB; + break; + default: + dbg("%s - Unknown number of stop bits, using 1 stop bit", + __func__); + bits &= ~BITS_STOP_MASK; + cp2101_set_config(port, CP2101_BITS, &bits, 2); + break; } cp2101_get_config(port, CP2101_MODEMCTL, modem_ctl, 16); @@ -491,55 +490,53 @@ static void cp2101_get_termios (struct usb_serial_port *port) cflag &= ~CRTSCTS; } - port->tty->termios->c_cflag = cflag; + tty->termios->c_cflag = cflag; } -static void cp2101_set_termios (struct usb_serial_port *port, - struct ktermios *old_termios) +static void cp2101_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { unsigned int cflag, old_cflag; - int baud=0, bits; + unsigned int baud = 0, bits; unsigned int modem_ctl[4]; dbg("%s - port %d", __func__, port->number); - if (!port->tty || !port->tty->termios) { - dbg("%s - no tty structures", __func__); + if (!tty) return; - } - port->tty->termios->c_cflag &= ~CMSPAR; - cflag = port->tty->termios->c_cflag; + tty->termios->c_cflag &= ~CMSPAR; + cflag = tty->termios->c_cflag; old_cflag = old_termios->c_cflag; - baud = tty_get_baud_rate(port->tty); + baud = tty_get_baud_rate(tty); /* If the baud rate is to be updated*/ if (baud != tty_termios_baud_rate(old_termios)) { switch (baud) { - case 0: - case 600: - case 1200: - case 1800: - case 2400: - case 4800: - case 7200: - case 9600: - case 14400: - case 19200: - case 28800: - case 38400: - case 55854: - case 57600: - case 115200: - case 127117: - case 230400: - case 460800: - case 921600: - case 3686400: - break; - default: - baud = 9600; - break; + case 0: + case 600: + case 1200: + case 1800: + case 2400: + case 4800: + case 7200: + case 9600: + case 14400: + case 19200: + case 28800: + case 38400: + case 55854: + case 57600: + case 115200: + case 127117: + case 230400: + case 460800: + case 921600: + case 3686400: + break; + default: + baud = 9600; + break; } if (baud) { @@ -554,35 +551,35 @@ static void cp2101_set_termios (struct usb_serial_port *port, } } /* Report back the resulting baud rate */ - tty_encode_baud_rate(port->tty, baud, baud); + tty_encode_baud_rate(tty, baud, baud); /* If the number of data bits is to be updated */ if ((cflag & CSIZE) != (old_cflag & CSIZE)) { cp2101_get_config(port, CP2101_BITS, &bits, 2); bits &= ~BITS_DATA_MASK; switch (cflag & CSIZE) { - case CS5: - bits |= BITS_DATA_5; - dbg("%s - data bits = 5", __func__); - break; - case CS6: - bits |= BITS_DATA_6; - dbg("%s - data bits = 6", __func__); - break; - case CS7: - bits |= BITS_DATA_7; - dbg("%s - data bits = 7", __func__); - break; - case CS8: - bits |= BITS_DATA_8; - dbg("%s - data bits = 8", __func__); - break; - /*case CS9: - bits |= BITS_DATA_9; - dbg("%s - data bits = 9", __func__); - break;*/ - default: - dev_err(&port->dev, "cp2101 driver does not " + case CS5: + bits |= BITS_DATA_5; + dbg("%s - data bits = 5", __func__); + break; + case CS6: + bits |= BITS_DATA_6; + dbg("%s - data bits = 6", __func__); + break; + case CS7: + bits |= BITS_DATA_7; + dbg("%s - data bits = 7", __func__); + break; + case CS8: + bits |= BITS_DATA_8; + dbg("%s - data bits = 8", __func__); + break; + /*case CS9: + bits |= BITS_DATA_9; + dbg("%s - data bits = 9", __func__); + break;*/ + default: + dev_err(&port->dev, "cp2101 driver does not " "support the number of bits requested," " using 8 bit mode\n"); bits |= BITS_DATA_8; @@ -651,10 +648,11 @@ static void cp2101_set_termios (struct usb_serial_port *port, } -static int cp2101_tiocmset (struct usb_serial_port *port, struct file *file, +static int cp2101_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { - int control = 0; + struct usb_serial_port *port = tty->driver_data; + unsigned int control = 0; dbg("%s - port %d", __func__, port->number); @@ -681,9 +679,11 @@ static int cp2101_tiocmset (struct usb_serial_port *port, struct file *file, } -static int cp2101_tiocmget (struct usb_serial_port *port, struct file *file) +static int cp2101_tiocmget (struct tty_struct *tty, struct file *file) { - int control, result; + struct usb_serial_port *port = tty->driver_data; + unsigned int control; + int result; dbg("%s - port %d", __func__, port->number); @@ -701,9 +701,10 @@ static int cp2101_tiocmget (struct usb_serial_port *port, struct file *file) return result; } -static void cp2101_break_ctl (struct usb_serial_port *port, int break_state) +static void cp2101_break_ctl (struct tty_struct *tty, int break_state) { - int state; + struct usb_serial_port *port = tty->driver_data; + unsigned int state; dbg("%s - port %d", __func__, port->number); if (break_state == 0) @@ -711,30 +712,29 @@ static void cp2101_break_ctl (struct usb_serial_port *port, int break_state) else state = BREAK_ON; dbg("%s - turning break %s", __func__, - state==BREAK_OFF ? "off" : "on"); + state == BREAK_OFF ? "off" : "on"); cp2101_set_config(port, CP2101_BREAK, &state, 2); } -static int cp2101_startup (struct usb_serial *serial) +static int cp2101_startup(struct usb_serial *serial) { /* CP2101 buffers behave strangely unless device is reset */ usb_reset_device(serial->dev); return 0; } -static void cp2101_shutdown (struct usb_serial *serial) +static void cp2101_shutdown(struct usb_serial *serial) { int i; dbg("%s", __func__); /* Stop reads and writes on all ports */ - for (i=0; i < serial->num_ports; ++i) { + for (i = 0; i < serial->num_ports; ++i) cp2101_cleanup(serial->port[i]); - } } -static int __init cp2101_init (void) +static int __init cp2101_init(void) { int retval; @@ -754,10 +754,10 @@ static int __init cp2101_init (void) return 0; } -static void __exit cp2101_exit (void) +static void __exit cp2101_exit(void) { - usb_deregister (&cp2101_driver); - usb_serial_deregister (&cp2101_device); + usb_deregister(&cp2101_driver); + usb_serial_deregister(&cp2101_device); } module_init(cp2101_init); diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index c164e2cf2752..b4d72351cb96 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -37,7 +37,7 @@ #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> @@ -57,22 +57,25 @@ static int debug; #define CYBERJACK_PRODUCT_ID 0x0100 /* Function prototypes */ -static int cyberjack_startup (struct usb_serial *serial); -static void cyberjack_shutdown (struct usb_serial *serial); -static int cyberjack_open (struct usb_serial_port *port, struct file *filp); -static void cyberjack_close (struct usb_serial_port *port, struct file *filp); -static int cyberjack_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static int cyberjack_write_room( struct usb_serial_port *port ); -static void cyberjack_read_int_callback (struct urb *urb); -static void cyberjack_read_bulk_callback (struct urb *urb); -static void cyberjack_write_bulk_callback (struct urb *urb); +static int cyberjack_startup(struct usb_serial *serial); +static void cyberjack_shutdown(struct usb_serial *serial); +static int cyberjack_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void cyberjack_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static int cyberjack_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count); +static int cyberjack_write_room(struct tty_struct *tty); +static void cyberjack_read_int_callback(struct urb *urb); +static void cyberjack_read_bulk_callback(struct urb *urb); +static void cyberjack_write_bulk_callback(struct urb *urb); static struct usb_device_id id_table [] = { { USB_DEVICE(CYBERJACK_VENDOR_ID, CYBERJACK_PRODUCT_ID) }, { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, id_table); static struct usb_driver cyberjack_driver = { .name = "cyberjack", @@ -111,7 +114,7 @@ struct cyberjack_private { }; /* do some startup allocations not currently performed by usb_serial_probe() */ -static int cyberjack_startup (struct usb_serial *serial) +static int cyberjack_startup(struct usb_serial *serial) { struct cyberjack_private *priv; int i; @@ -135,20 +138,20 @@ static int cyberjack_startup (struct usb_serial *serial) for (i = 0; i < serial->num_ports; ++i) { int result; serial->port[i]->interrupt_in_urb->dev = serial->dev; - result = usb_submit_urb(serial->port[i]->interrupt_in_urb, + result = usb_submit_urb(serial->port[i]->interrupt_in_urb, GFP_KERNEL); if (result) err(" usb_submit_urb(read int) failed"); dbg("%s - usb_submit_urb(int urb)", __func__); } - return( 0 ); + return 0; } -static void cyberjack_shutdown (struct usb_serial *serial) +static void cyberjack_shutdown(struct usb_serial *serial) { int i; - + dbg("%s", __func__); for (i = 0; i < serial->num_ports; ++i) { @@ -158,8 +161,9 @@ static void cyberjack_shutdown (struct usb_serial *serial) usb_set_serial_port_data(serial->port[i], NULL); } } - -static int cyberjack_open (struct usb_serial_port *port, struct file *filp) + +static int cyberjack_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct cyberjack_private *priv; unsigned long flags; @@ -167,14 +171,15 @@ static int cyberjack_open (struct usb_serial_port *port, struct file *filp) dbg("%s - port %d", __func__, port->number); - dbg("%s - usb_clear_halt", __func__ ); + dbg("%s - usb_clear_halt", __func__); usb_clear_halt(port->serial->dev, port->write_urb->pipe); /* force low_latency on so that our tty_push actually forces * the data through, otherwise it is scheduled, and with high * data rates (like with OHCI) data can get lost. */ - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; priv = usb_get_serial_port_data(port); spin_lock_irqsave(&priv->lock, flags); @@ -186,7 +191,8 @@ static int cyberjack_open (struct usb_serial_port *port, struct file *filp) return result; } -static void cyberjack_close (struct usb_serial_port *port, struct file *filp) +static void cyberjack_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { dbg("%s - port %d", __func__, port->number); @@ -197,7 +203,8 @@ static void cyberjack_close (struct usb_serial_port *port, struct file *filp) } } -static int cyberjack_write (struct usb_serial_port *port, const unsigned char *buf, int count) +static int cyberjack_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; struct cyberjack_private *priv = usb_get_serial_port_data(port); @@ -223,7 +230,7 @@ static int cyberjack_write (struct usb_serial_port *port, const unsigned char *b spin_lock_irqsave(&priv->lock, flags); - if( (count+priv->wrfilled) > sizeof(priv->wrbuf) ) { + if (count+priv->wrfilled > sizeof(priv->wrbuf)) { /* To much data for buffer. Reset buffer. */ priv->wrfilled = 0; port->write_urb_busy = 0; @@ -232,42 +239,43 @@ static int cyberjack_write (struct usb_serial_port *port, const unsigned char *b } /* Copy data */ - memcpy (priv->wrbuf+priv->wrfilled, buf, count); + memcpy(priv->wrbuf + priv->wrfilled, buf, count); usb_serial_debug_data(debug, &port->dev, __func__, count, - priv->wrbuf+priv->wrfilled); + priv->wrbuf + priv->wrfilled); priv->wrfilled += count; - if( priv->wrfilled >= 3 ) { + if (priv->wrfilled >= 3) { wrexpected = ((int)priv->wrbuf[2]<<8)+priv->wrbuf[1]+3; dbg("%s - expected data: %d", __func__, wrexpected); - } else { + } else wrexpected = sizeof(priv->wrbuf); - } - if( priv->wrfilled >= wrexpected ) { + if (priv->wrfilled >= wrexpected) { /* We have enough data to begin transmission */ int length; dbg("%s - transmitting data (frame 1)", __func__); - length = (wrexpected > port->bulk_out_size) ? port->bulk_out_size : wrexpected; + length = (wrexpected > port->bulk_out_size) ? + port->bulk_out_size : wrexpected; - memcpy (port->write_urb->transfer_buffer, priv->wrbuf, length ); - priv->wrsent=length; + memcpy(port->write_urb->transfer_buffer, priv->wrbuf, length); + priv->wrsent = length; /* set up our urb */ - usb_fill_bulk_urb(port->write_urb, serial->dev, + usb_fill_bulk_urb(port->write_urb, serial->dev, usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress), port->write_urb->transfer_buffer, length, - ((serial->type->write_bulk_callback) ? - serial->type->write_bulk_callback : - cyberjack_write_bulk_callback), + ((serial->type->write_bulk_callback) ? + serial->type->write_bulk_callback : + cyberjack_write_bulk_callback), port); /* send the data out the bulk port */ result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result) { - err("%s - failed submitting write urb, error %d", __func__, result); + err("%s - failed submitting write urb, error %d", + __func__, result); /* Throw away data. No better idea what to do with it. */ priv->wrfilled = 0; priv->wrsent = 0; @@ -276,12 +284,12 @@ static int cyberjack_write (struct usb_serial_port *port, const unsigned char *b return 0; } - dbg("%s - priv->wrsent=%d", __func__,priv->wrsent); - dbg("%s - priv->wrfilled=%d", __func__,priv->wrfilled); + dbg("%s - priv->wrsent=%d", __func__, priv->wrsent); + dbg("%s - priv->wrfilled=%d", __func__, priv->wrfilled); - if( priv->wrsent>=priv->wrfilled ) { + if (priv->wrsent >= priv->wrfilled) { dbg("%s - buffer cleaned", __func__); - memset( priv->wrbuf, 0, sizeof(priv->wrbuf) ); + memset(priv->wrbuf, 0, sizeof(priv->wrbuf)); priv->wrfilled = 0; priv->wrsent = 0; } @@ -289,16 +297,16 @@ static int cyberjack_write (struct usb_serial_port *port, const unsigned char *b spin_unlock_irqrestore(&priv->lock, flags); - return (count); -} + return count; +} -static int cyberjack_write_room( struct usb_serial_port *port ) +static int cyberjack_write_room(struct tty_struct *tty) { /* FIXME: .... */ return CYBERJACK_LOCAL_BUF_SIZE; } -static void cyberjack_read_int_callback( struct urb *urb ) +static void cyberjack_read_int_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct cyberjack_private *priv = usb_get_serial_port_data(port); @@ -312,10 +320,11 @@ static void cyberjack_read_int_callback( struct urb *urb ) if (status) return; - usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, data); /* React only to interrupts signaling a bulk_in transfer */ - if( (urb->actual_length == 4) && (data[0] == 0x01) ) { + if (urb->actual_length == 4 && data[0] == 0x01) { short old_rdtodo; /* This is a announcement of coming bulk_ins. */ @@ -325,8 +334,8 @@ static void cyberjack_read_int_callback( struct urb *urb ) old_rdtodo = priv->rdtodo; - if( (old_rdtodo+size)<(old_rdtodo) ) { - dbg( "To many bulk_in urbs to do." ); + if (old_rdtodo + size < old_rdtodo) { + dbg("To many bulk_in urbs to do."); spin_unlock(&priv->lock); goto resubmit; } @@ -338,10 +347,10 @@ static void cyberjack_read_int_callback( struct urb *urb ) spin_unlock(&priv->lock); - if( !old_rdtodo ) { + if (!old_rdtodo) { port->read_urb->dev = port->serial->dev; result = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if( result ) + if (result) err("%s - failed resubmitting read urb, error %d", __func__, result); dbg("%s - usb_submit_urb(read urb)", __func__); } @@ -355,7 +364,7 @@ resubmit: dbg("%s - usb_submit_urb(int urb)", __func__); } -static void cyberjack_read_bulk_callback (struct urb *urb) +static void cyberjack_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct cyberjack_private *priv = usb_get_serial_port_data(port); @@ -367,14 +376,15 @@ static void cyberjack_read_bulk_callback (struct urb *urb) dbg("%s - port %d", __func__, port->number); - usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, data); if (status) { dbg("%s - nonzero read bulk status received: %d", __func__, status); return; } - tty = port->tty; + tty = port->port.tty; if (!tty) { dbg("%s - ignoring since device not open\n", __func__); return; @@ -382,15 +392,16 @@ static void cyberjack_read_bulk_callback (struct urb *urb) if (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_flip_buffer_push(tty); } spin_lock(&priv->lock); /* Reduce urbs to do by one. */ - priv->rdtodo-=urb->actual_length; + priv->rdtodo -= urb->actual_length; /* Just to be sure */ - if ( priv->rdtodo<0 ) priv->rdtodo = 0; + if (priv->rdtodo < 0) + priv->rdtodo = 0; todo = priv->rdtodo; spin_unlock(&priv->lock); @@ -398,16 +409,17 @@ static void cyberjack_read_bulk_callback (struct urb *urb) dbg("%s - rdtodo: %d", __func__, todo); /* Continue to read if we have still urbs to do. */ - if( todo /* || (urb->actual_length==port->bulk_in_endpointAddress)*/ ) { + if (todo /* || (urb->actual_length==port->bulk_in_endpointAddress)*/) { port->read_urb->dev = port->serial->dev; result = usb_submit_urb(port->read_urb, GFP_ATOMIC); if (result) - err("%s - failed resubmitting read urb, error %d", __func__, result); + err("%s - failed resubmitting read urb, error %d", + __func__, result); dbg("%s - usb_submit_urb(read urb)", __func__); } } -static void cyberjack_write_bulk_callback (struct urb *urb) +static void cyberjack_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct cyberjack_private *priv = usb_get_serial_port_data(port); @@ -425,7 +437,7 @@ static void cyberjack_write_bulk_callback (struct urb *urb) spin_lock(&priv->lock); /* only do something if we have more data to send */ - if( priv->wrfilled ) { + if (priv->wrfilled) { int length, blksize, result; dbg("%s - transmitting data (frame n)", __func__); @@ -433,37 +445,39 @@ static void cyberjack_write_bulk_callback (struct urb *urb) length = ((priv->wrfilled - priv->wrsent) > port->bulk_out_size) ? port->bulk_out_size : (priv->wrfilled - priv->wrsent); - memcpy (port->write_urb->transfer_buffer, priv->wrbuf + priv->wrsent, - length ); - priv->wrsent+=length; + memcpy(port->write_urb->transfer_buffer, + priv->wrbuf + priv->wrsent, length); + priv->wrsent += length; /* set up our urb */ - usb_fill_bulk_urb(port->write_urb, port->serial->dev, + usb_fill_bulk_urb(port->write_urb, port->serial->dev, usb_sndbulkpipe(port->serial->dev, port->bulk_out_endpointAddress), port->write_urb->transfer_buffer, length, - ((port->serial->type->write_bulk_callback) ? - port->serial->type->write_bulk_callback : - cyberjack_write_bulk_callback), + ((port->serial->type->write_bulk_callback) ? + port->serial->type->write_bulk_callback : + cyberjack_write_bulk_callback), port); /* send the data out the bulk port */ result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result) { - err("%s - failed submitting write urb, error %d", __func__, result); + err("%s - failed submitting write urb, error %d", + __func__, result); /* Throw away data. No better idea what to do with it. */ priv->wrfilled = 0; priv->wrsent = 0; goto exit; } - dbg("%s - priv->wrsent=%d", __func__,priv->wrsent); - dbg("%s - priv->wrfilled=%d", __func__,priv->wrfilled); + dbg("%s - priv->wrsent=%d", __func__, priv->wrsent); + dbg("%s - priv->wrfilled=%d", __func__, priv->wrfilled); blksize = ((int)priv->wrbuf[2]<<8)+priv->wrbuf[1]+3; - if( (priv->wrsent>=priv->wrfilled) || (priv->wrsent>=blksize) ) { + if (priv->wrsent >= priv->wrfilled || + priv->wrsent >= blksize) { dbg("%s - buffer cleaned", __func__); - memset( priv->wrbuf, 0, sizeof(priv->wrbuf) ); + memset(priv->wrbuf, 0, sizeof(priv->wrbuf)); priv->wrfilled = 0; priv->wrsent = 0; } @@ -474,14 +488,14 @@ exit: usb_serial_port_softint(port); } -static int __init cyberjack_init (void) +static int __init cyberjack_init(void) { int retval; retval = usb_serial_register(&cyberjack_device); if (retval) goto failed_usb_serial_register; retval = usb_register(&cyberjack_driver); - if (retval) + if (retval) goto failed_usb_register; info(DRIVER_VERSION " " DRIVER_AUTHOR); @@ -494,18 +508,18 @@ failed_usb_serial_register: return retval; } -static void __exit cyberjack_exit (void) +static void __exit cyberjack_exit(void) { - usb_deregister (&cyberjack_driver); - usb_serial_deregister (&cyberjack_device); + usb_deregister(&cyberjack_driver); + usb_serial_deregister(&cyberjack_device); } module_init(cyberjack_init); module_exit(cyberjack_exit); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_VERSION( DRIVER_VERSION ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 0230d3c0888a..22837a3f2f89 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -2,7 +2,7 @@ * USB Cypress M8 driver * * Copyright (C) 2004 - * Lonnie Mendez (dignome@gmail.com) + * Lonnie Mendez (dignome@gmail.com) * Copyright (C) 2003,2004 * Neil Whelchel (koyama@firstlight.net) * @@ -11,19 +11,21 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * See Documentation/usb/usb-serial.txt for more information on using this driver + * See Documentation/usb/usb-serial.txt for more information on using this + * driver * * See http://geocities.com/i0xox0i for information on this driver and the * earthmate usb device. * * Lonnie Mendez <dignome@gmail.com> * 4-29-2005 - * Fixed problem where setting or retreiving the serial config would fail with - * EPIPE. Removed CRTS toggling so the driver behaves more like other usbserial - * adapters. Issued new interval of 1ms instead of the default 10ms. As a - * result, transfer speed has been substantially increased. From avg. 850bps to - * avg. 3300bps. initial termios has also been modified. Cleaned up code and - * formatting issues so it is more readable. Replaced the C++ style comments. + * Fixed problem where setting or retreiving the serial config would fail + * with EPIPE. Removed CRTS toggling so the driver behaves more like + * other usbserial adapters. Issued new interval of 1ms instead of the + * default 10ms. As a result, transfer speed has been substantially + * increased from avg. 850bps to avg. 3300bps. initial termios has also + * been modified. Cleaned up code and formatting issues so it is more + * readable. Replaced the C++ style comments. * * Lonnie Mendez <dignome@gmail.com> * 12-15-2004 @@ -42,10 +44,11 @@ * */ -/* Thanks to Neil Whelchel for writing the first cypress m8 implementation for linux. */ +/* Thanks to Neil Whelchel for writing the first cypress m8 implementation + for linux. */ /* Thanks to cypress for providing references for the hid reports. */ /* Thanks to Jiang Zhang for providing links and for general help. */ -/* Code originates and was built up from ftdi_sio, belkin, pl2303 and others. */ +/* Code originates and was built up from ftdi_sio, belkin, pl2303 and others.*/ #include <linux/kernel.h> @@ -62,7 +65,7 @@ #include <linux/usb/serial.h> #include <linux/serial.h> #include <linux/delay.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include "cypress_m8.h" @@ -112,7 +115,7 @@ static struct usb_device_id id_table_combined [] = { { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table_combined); +MODULE_DEVICE_TABLE(usb, id_table_combined); static struct usb_driver cypress_driver = { .name = "cypress", @@ -146,11 +149,13 @@ struct cypress_private { __u8 rx_flags; /* throttling - used from whiteheat/ftdi_sio */ enum packet_format pkt_fmt; /* format to use for packet send / receive */ int get_cfg_unsafe; /* If true, the CYPRESS_GET_CONFIG is unsafe */ - int baud_rate; /* stores current baud rate in integer form */ + int baud_rate; /* stores current baud rate in + integer form */ int isthrottled; /* if throttled, discard reads */ wait_queue_head_t delta_msr_wait; /* used for TIOCMIWAIT */ char prev_status, diff_status; /* used for TIOCMIWAIT */ - /* we pass a pointer to this as the arguement sent to cypress_set_termios old_termios */ + /* we pass a pointer to this as the arguement sent to + cypress_set_termios old_termios */ struct ktermios tmp_termios; /* stores the old termios settings */ }; @@ -163,33 +168,41 @@ struct cypress_buf { }; /* function prototypes for the Cypress USB to serial device */ -static int cypress_earthmate_startup (struct usb_serial *serial); -static int cypress_hidcom_startup (struct usb_serial *serial); -static int cypress_ca42v2_startup (struct usb_serial *serial); -static void cypress_shutdown (struct usb_serial *serial); -static int cypress_open (struct usb_serial_port *port, struct file *filp); -static void cypress_close (struct usb_serial_port *port, struct file *filp); -static int cypress_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static void cypress_send (struct usb_serial_port *port); -static int cypress_write_room (struct usb_serial_port *port); -static int cypress_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); -static void cypress_set_termios (struct usb_serial_port *port, struct ktermios * old); -static int cypress_tiocmget (struct usb_serial_port *port, struct file *file); -static int cypress_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); -static int cypress_chars_in_buffer (struct usb_serial_port *port); -static void cypress_throttle (struct usb_serial_port *port); -static void cypress_unthrottle (struct usb_serial_port *port); -static void cypress_set_dead (struct usb_serial_port *port); -static void cypress_read_int_callback (struct urb *urb); -static void cypress_write_int_callback (struct urb *urb); +static int cypress_earthmate_startup(struct usb_serial *serial); +static int cypress_hidcom_startup(struct usb_serial *serial); +static int cypress_ca42v2_startup(struct usb_serial *serial); +static void cypress_shutdown(struct usb_serial *serial); +static int cypress_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void cypress_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); +static void cypress_send(struct usb_serial_port *port); +static int cypress_write_room(struct tty_struct *tty); +static int cypress_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg); +static void cypress_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); +static int cypress_tiocmget(struct tty_struct *tty, struct file *file); +static int cypress_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear); +static int cypress_chars_in_buffer(struct tty_struct *tty); +static void cypress_throttle(struct tty_struct *tty); +static void cypress_unthrottle(struct tty_struct *tty); +static void cypress_set_dead(struct usb_serial_port *port); +static void cypress_read_int_callback(struct urb *urb); +static void cypress_write_int_callback(struct urb *urb); /* write buffer functions */ static struct cypress_buf *cypress_buf_alloc(unsigned int size); -static void cypress_buf_free(struct cypress_buf *cb); -static void cypress_buf_clear(struct cypress_buf *cb); -static unsigned int cypress_buf_data_avail(struct cypress_buf *cb); -static unsigned int cypress_buf_space_avail(struct cypress_buf *cb); -static unsigned int cypress_buf_put(struct cypress_buf *cb, const char *buf, unsigned int count); -static unsigned int cypress_buf_get(struct cypress_buf *cb, char *buf, unsigned int count); +static void cypress_buf_free(struct cypress_buf *cb); +static void cypress_buf_clear(struct cypress_buf *cb); +static unsigned int cypress_buf_data_avail(struct cypress_buf *cb); +static unsigned int cypress_buf_space_avail(struct cypress_buf *cb); +static unsigned int cypress_buf_put(struct cypress_buf *cb, + const char *buf, unsigned int count); +static unsigned int cypress_buf_get(struct cypress_buf *cb, + char *buf, unsigned int count); static struct usb_serial_driver cypress_earthmate_device = { @@ -247,7 +260,7 @@ static struct usb_serial_driver cypress_hidcom_device = { static struct usb_serial_driver cypress_ca42v2_device = { .driver = { .owner = THIS_MODULE, - .name = "nokiaca42v2", + .name = "nokiaca42v2", }, .description = "Nokia CA-42 V2 Adapter", .usb_driver = &cypress_driver, @@ -322,8 +335,10 @@ static int analyze_baud_rate(struct usb_serial_port *port, speed_t new_rate) /* This function can either set or retrieve the current serial line settings */ -static int cypress_serial_control (struct usb_serial_port *port, speed_t baud_rate, int data_bits, int stop_bits, - int parity_enable, int parity_type, int reset, int cypress_request_type) +static int cypress_serial_control(struct tty_struct *tty, + struct usb_serial_port *port, speed_t baud_rate, int data_bits, + int stop_bits, int parity_enable, int parity_type, int reset, + int cypress_request_type) { int new_baudrate = 0, retval = 0, tries = 0; struct cypress_private *priv; @@ -331,111 +346,114 @@ static int cypress_serial_control (struct usb_serial_port *port, speed_t baud_ra unsigned long flags; dbg("%s", __func__); - + priv = usb_get_serial_port_data(port); if (!priv->comm_is_ok) return -ENODEV; - switch(cypress_request_type) { - case CYPRESS_SET_CONFIG: + switch (cypress_request_type) { + case CYPRESS_SET_CONFIG: + new_baudrate = priv->baud_rate; + /* 0 means 'Hang up' so doesn't change the true bit rate */ + if (baud_rate == 0) new_baudrate = priv->baud_rate; - /* 0 means 'Hang up' so doesn't change the true bit rate */ - if (baud_rate == 0) - new_baudrate = priv->baud_rate; - /* Change of speed ? */ - else if (baud_rate != priv->baud_rate) { - dbg("%s - baud rate is changing", __func__); - retval = analyze_baud_rate(port, baud_rate); - if (retval >= 0) { - new_baudrate = retval; - dbg("%s - New baud rate set to %d", - __func__, new_baudrate); - } - } - dbg("%s - baud rate is being sent as %d", __func__, new_baudrate); - - memset(feature_buffer, 0, sizeof(feature_buffer)); - /* fill the feature_buffer with new configuration */ - *((u_int32_t *)feature_buffer) = new_baudrate; - - feature_buffer[4] |= data_bits; /* assign data bits in 2 bit space ( max 3 ) */ - /* 1 bit gap */ - feature_buffer[4] |= (stop_bits << 3); /* assign stop bits in 1 bit space */ - feature_buffer[4] |= (parity_enable << 4); /* assign parity flag in 1 bit space */ - feature_buffer[4] |= (parity_type << 5); /* assign parity type in 1 bit space */ - /* 1 bit gap */ - feature_buffer[4] |= (reset << 7); /* assign reset at end of byte, 1 bit space */ - - dbg("%s - device is being sent this feature report:", __func__); - dbg("%s - %02X - %02X - %02X - %02X - %02X", __func__, feature_buffer[0], feature_buffer[1], - feature_buffer[2], feature_buffer[3], feature_buffer[4]); - - do { - retval = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - HID_REQ_SET_REPORT, - USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS, - 0x0300, 0, feature_buffer, - sizeof(feature_buffer), 500); - - if (tries++ >= 3) - break; - - } while (retval != sizeof(feature_buffer) && - retval != -ENODEV); - - if (retval != sizeof(feature_buffer)) { - err("%s - failed sending serial line settings - %d", __func__, retval); - cypress_set_dead(port); - } else { - spin_lock_irqsave(&priv->lock, flags); - priv->baud_rate = new_baudrate; - priv->current_config = feature_buffer[4]; - spin_unlock_irqrestore(&priv->lock, flags); - /* If we asked for a speed change encode it */ - if (baud_rate) - tty_encode_baud_rate(port->tty, - new_baudrate, new_baudrate); - } - break; - case CYPRESS_GET_CONFIG: - if (priv->get_cfg_unsafe) { - /* Not implemented for this device, - and if we try to do it we're likely - to crash the hardware. */ - return -ENOTTY; - } - dbg("%s - retreiving serial line settings", __func__); - /* set initial values in feature buffer */ - memset(feature_buffer, 0, sizeof(feature_buffer)); - - do { - retval = usb_control_msg(port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0), - HID_REQ_GET_REPORT, - USB_DIR_IN | USB_RECIP_INTERFACE | USB_TYPE_CLASS, - 0x0300, 0, feature_buffer, - sizeof(feature_buffer), 500); - - if (tries++ >= 3) - break; - - } while (retval != sizeof(feature_buffer) && - retval != -ENODEV); - - if (retval != sizeof(feature_buffer)) { - err("%s - failed to retrieve serial line settings - %d", __func__, retval); - cypress_set_dead(port); - return retval; - } else { - spin_lock_irqsave(&priv->lock, flags); - - /* store the config in one byte, and later use bit masks to check values */ - priv->current_config = feature_buffer[4]; - priv->baud_rate = *((u_int32_t *)feature_buffer); - spin_unlock_irqrestore(&priv->lock, flags); + /* Change of speed ? */ + else if (baud_rate != priv->baud_rate) { + dbg("%s - baud rate is changing", __func__); + retval = analyze_baud_rate(port, baud_rate); + if (retval >= 0) { + new_baudrate = retval; + dbg("%s - New baud rate set to %d", + __func__, new_baudrate); } + } + dbg("%s - baud rate is being sent as %d", + __func__, new_baudrate); + + memset(feature_buffer, 0, sizeof(feature_buffer)); + /* fill the feature_buffer with new configuration */ + *((u_int32_t *)feature_buffer) = new_baudrate; + feature_buffer[4] |= data_bits; /* assign data bits in 2 bit space ( max 3 ) */ + /* 1 bit gap */ + feature_buffer[4] |= (stop_bits << 3); /* assign stop bits in 1 bit space */ + feature_buffer[4] |= (parity_enable << 4); /* assign parity flag in 1 bit space */ + feature_buffer[4] |= (parity_type << 5); /* assign parity type in 1 bit space */ + /* 1 bit gap */ + feature_buffer[4] |= (reset << 7); /* assign reset at end of byte, 1 bit space */ + + dbg("%s - device is being sent this feature report:", + __func__); + dbg("%s - %02X - %02X - %02X - %02X - %02X", __func__, + feature_buffer[0], feature_buffer[1], + feature_buffer[2], feature_buffer[3], + feature_buffer[4]); + + do { + retval = usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + HID_REQ_SET_REPORT, + USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS, + 0x0300, 0, feature_buffer, + sizeof(feature_buffer), 500); + + if (tries++ >= 3) + break; + + } while (retval != sizeof(feature_buffer) && + retval != -ENODEV); + + if (retval != sizeof(feature_buffer)) { + err("%s - failed sending serial line settings - %d", + __func__, retval); + cypress_set_dead(port); + } else { + spin_lock_irqsave(&priv->lock, flags); + priv->baud_rate = new_baudrate; + priv->current_config = feature_buffer[4]; + spin_unlock_irqrestore(&priv->lock, flags); + /* If we asked for a speed change encode it */ + if (baud_rate) + tty_encode_baud_rate(tty, + new_baudrate, new_baudrate); + } + break; + case CYPRESS_GET_CONFIG: + if (priv->get_cfg_unsafe) { + /* Not implemented for this device, + and if we try to do it we're likely + to crash the hardware. */ + return -ENOTTY; + } + dbg("%s - retreiving serial line settings", __func__); + /* set initial values in feature buffer */ + memset(feature_buffer, 0, sizeof(feature_buffer)); + + do { + retval = usb_control_msg(port->serial->dev, + usb_rcvctrlpipe(port->serial->dev, 0), + HID_REQ_GET_REPORT, + USB_DIR_IN | USB_RECIP_INTERFACE | USB_TYPE_CLASS, + 0x0300, 0, feature_buffer, + sizeof(feature_buffer), 500); + + if (tries++ >= 3) + break; + } while (retval != sizeof(feature_buffer) + && retval != -ENODEV); + + if (retval != sizeof(feature_buffer)) { + err("%s - failed to retrieve serial line settings - %d", __func__, retval); + cypress_set_dead(port); + return retval; + } else { + spin_lock_irqsave(&priv->lock, flags); + /* store the config in one byte, and later + use bit masks to check values */ + priv->current_config = feature_buffer[4]; + priv->baud_rate = *((u_int32_t *)feature_buffer); + spin_unlock_irqrestore(&priv->lock, flags); + } } spin_lock_irqsave(&priv->lock, flags); ++priv->cmd_count; @@ -468,14 +486,14 @@ static void cypress_set_dead(struct usb_serial_port *port) *****************************************************************************/ -static int generic_startup (struct usb_serial *serial) +static int generic_startup(struct usb_serial *serial) { struct cypress_private *priv; struct usb_serial_port *port = serial->port[0]; dbg("%s - port %d", __func__, port->number); - priv = kzalloc(sizeof (struct cypress_private), GFP_KERNEL); + priv = kzalloc(sizeof(struct cypress_private), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -487,9 +505,9 @@ static int generic_startup (struct usb_serial *serial) return -ENOMEM; } init_waitqueue_head(&priv->delta_msr_wait); - - usb_reset_configuration (serial->dev); - + + usb_reset_configuration(serial->dev); + priv->cmd_ctrl = 0; priv->line_control = 0; priv->termios_initialized = 0; @@ -500,30 +518,30 @@ static int generic_startup (struct usb_serial *serial) small. Otherwise we can use the slightly more compact format. This is in accordance with the cypress_m8 serial converter app note. */ - if (port->interrupt_out_size > 9) { + if (port->interrupt_out_size > 9) priv->pkt_fmt = packet_format_1; - } else { + else priv->pkt_fmt = packet_format_2; - } + if (interval > 0) { priv->write_urb_interval = interval; priv->read_urb_interval = interval; dbg("%s - port %d read & write intervals forced to %d", - __func__,port->number,interval); + __func__, port->number, interval); } else { priv->write_urb_interval = port->interrupt_out_urb->interval; priv->read_urb_interval = port->interrupt_in_urb->interval; dbg("%s - port %d intervals: read=%d write=%d", - __func__,port->number, - priv->read_urb_interval,priv->write_urb_interval); + __func__, port->number, + priv->read_urb_interval, priv->write_urb_interval); } usb_set_serial_port_data(port, priv); - + return 0; } -static int cypress_earthmate_startup (struct usb_serial *serial) +static int cypress_earthmate_startup(struct usb_serial *serial) { struct cypress_private *priv; struct usb_serial_port *port = serial->port[0]; @@ -541,7 +559,8 @@ static int cypress_earthmate_startup (struct usb_serial *serial) /* All Earthmate devices use the separated-count packet format! Idiotic. */ priv->pkt_fmt = packet_format_1; - if (serial->dev->descriptor.idProduct != cpu_to_le16(PRODUCT_ID_EARTHMATEUSB)) { + if (serial->dev->descriptor.idProduct != + cpu_to_le16(PRODUCT_ID_EARTHMATEUSB)) { /* The old original USB Earthmate seemed able to handle GET_CONFIG requests; everything they've produced since that time crashes if this command is @@ -555,7 +574,7 @@ static int cypress_earthmate_startup (struct usb_serial *serial) } /* cypress_earthmate_startup */ -static int cypress_hidcom_startup (struct usb_serial *serial) +static int cypress_hidcom_startup(struct usb_serial *serial) { struct cypress_private *priv; @@ -569,12 +588,12 @@ static int cypress_hidcom_startup (struct usb_serial *serial) priv = usb_get_serial_port_data(serial->port[0]); priv->chiptype = CT_CYPHIDCOM; - + return 0; } /* cypress_hidcom_startup */ -static int cypress_ca42v2_startup (struct usb_serial *serial) +static int cypress_ca42v2_startup(struct usb_serial *serial) { struct cypress_private *priv; @@ -593,11 +612,11 @@ static int cypress_ca42v2_startup (struct usb_serial *serial) } /* cypress_ca42v2_startup */ -static void cypress_shutdown (struct usb_serial *serial) +static void cypress_shutdown(struct usb_serial *serial) { struct cypress_private *priv; - dbg ("%s - port %d", __func__, serial->port[0]->number); + dbg("%s - port %d", __func__, serial->port[0]->number); /* all open ports are closed at this point */ @@ -611,7 +630,8 @@ static void cypress_shutdown (struct usb_serial *serial) } -static int cypress_open (struct usb_serial_port *port, struct file *filp) +static int cypress_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct cypress_private *priv = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; @@ -636,37 +656,44 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp) spin_unlock_irqrestore(&priv->lock, flags); /* setting to zero could cause data loss */ - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; /* raise both lines and set termios */ spin_lock_irqsave(&priv->lock, flags); priv->line_control = CONTROL_DTR | CONTROL_RTS; priv->cmd_ctrl = 1; spin_unlock_irqrestore(&priv->lock, flags); - result = cypress_write(port, NULL, 0); + result = cypress_write(tty, port, NULL, 0); if (result) { - dev_err(&port->dev, "%s - failed setting the control lines - error %d\n", __func__, result); + dev_err(&port->dev, + "%s - failed setting the control lines - error %d\n", + __func__, result); return result; } else dbg("%s - success setting the control lines", __func__); - cypress_set_termios(port, &priv->tmp_termios); + if (tty) + cypress_set_termios(tty, port, &priv->tmp_termios); /* setup the port and start reading from the device */ - if(!port->interrupt_in_urb){ + if (!port->interrupt_in_urb) { err("%s - interrupt_in_urb is empty!", __func__); - return(-1); + return -1; } usb_fill_int_urb(port->interrupt_in_urb, serial->dev, usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress), - port->interrupt_in_urb->transfer_buffer, port->interrupt_in_urb->transfer_buffer_length, + port->interrupt_in_urb->transfer_buffer, + port->interrupt_in_urb->transfer_buffer_length, cypress_read_int_callback, port, priv->read_urb_interval); result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); - if (result){ - dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __func__, result); + if (result) { + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", + __func__, result); cypress_set_dead(port); } @@ -674,7 +701,8 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp) } /* cypress_open */ -static void cypress_close(struct usb_serial_port *port, struct file * filp) +static void cypress_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct cypress_private *priv = usb_get_serial_port_data(port); unsigned int c_cflag; @@ -688,7 +716,7 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) spin_lock_irq(&priv->lock); timeout = CYPRESS_CLOSING_WAIT; init_waitqueue_entry(&wait, current); - add_wait_queue(&port->tty->write_wait, &wait); + add_wait_queue(&tty->write_wait, &wait); for (;;) { set_current_state(TASK_INTERRUPTIBLE); if (cypress_buf_data_avail(priv->buf) == 0 @@ -701,7 +729,7 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) spin_lock_irq(&priv->lock); } set_current_state(TASK_RUNNING); - remove_wait_queue(&port->tty->write_wait, &wait); + remove_wait_queue(&tty->write_wait, &wait); /* clear out any remaining data in the buffer */ cypress_buf_clear(priv->buf); spin_unlock_irq(&priv->lock); @@ -713,19 +741,21 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) return; } /* wait for characters to drain from device */ - bps = tty_get_baud_rate(port->tty); - if (bps > 1200) - timeout = max((HZ*2560)/bps,HZ/10); - else - timeout = 2*HZ; - schedule_timeout_interruptible(timeout); + if (tty) { + bps = tty_get_baud_rate(tty); + if (bps > 1200) + timeout = max((HZ * 2560) / bps, HZ / 10); + else + timeout = 2 * HZ; + schedule_timeout_interruptible(timeout); + } dbg("%s - stopping urbs", __func__); - usb_kill_urb (port->interrupt_in_urb); - usb_kill_urb (port->interrupt_out_urb); + usb_kill_urb(port->interrupt_in_urb); + usb_kill_urb(port->interrupt_out_urb); - if (port->tty) { - c_cflag = port->tty->termios->c_cflag; + if (tty) { + c_cflag = tty->termios->c_cflag; if (c_cflag & HUPCL) { /* drop dtr and rts */ priv = usb_get_serial_port_data(port); @@ -733,22 +763,23 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) priv->line_control = 0; priv->cmd_ctrl = 1; spin_unlock_irq(&priv->lock); - cypress_write(port, NULL, 0); + cypress_write(tty, port, NULL, 0); } } if (stats) - dev_info (&port->dev, "Statistics: %d Bytes In | %d Bytes Out | %d Commands Issued\n", - priv->bytes_in, priv->bytes_out, priv->cmd_count); + dev_info(&port->dev, "Statistics: %d Bytes In | %d Bytes Out | %d Commands Issued\n", + priv->bytes_in, priv->bytes_out, priv->cmd_count); mutex_unlock(&port->serial->disc_mutex); } /* cypress_close */ -static int cypress_write(struct usb_serial_port *port, const unsigned char *buf, int count) +static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { struct cypress_private *priv = usb_get_serial_port_data(port); unsigned long flags; - + dbg("%s - port %d, %d bytes", __func__, port->number, count); /* line control commands, which need to be executed immediately, @@ -758,10 +789,10 @@ static int cypress_write(struct usb_serial_port *port, const unsigned char *buf, count = 0; goto finish; } - + if (!count) return count; - + spin_lock_irqsave(&priv->lock, flags); count = cypress_buf_put(priv->buf, buf, count); spin_unlock_irqrestore(&priv->lock, flags); @@ -778,13 +809,14 @@ static void cypress_send(struct usb_serial_port *port) int count = 0, result, offset, actual_size; struct cypress_private *priv = usb_get_serial_port_data(port); unsigned long flags; - + if (!priv->comm_is_ok) return; dbg("%s - port %d", __func__, port->number); - dbg("%s - interrupt out size is %d", __func__, port->interrupt_out_size); - + dbg("%s - interrupt out size is %d", __func__, + port->interrupt_out_size); + spin_lock_irqsave(&priv->lock, flags); if (priv->write_urb_in_use) { dbg("%s - can't write, urb in use", __func__); @@ -794,7 +826,8 @@ static void cypress_send(struct usb_serial_port *port) spin_unlock_irqrestore(&priv->lock, flags); /* clear buffer */ - memset(port->interrupt_out_urb->transfer_buffer, 0, port->interrupt_out_size); + memset(port->interrupt_out_urb->transfer_buffer, 0, + port->interrupt_out_size); spin_lock_irqsave(&priv->lock, flags); switch (priv->pkt_fmt) { @@ -825,9 +858,8 @@ static void cypress_send(struct usb_serial_port *port) count = cypress_buf_get(priv->buf, &port->interrupt_out_buffer[offset], port->interrupt_out_size-offset); - if (count == 0) { + if (count == 0) return; - } switch (priv->pkt_fmt) { default: @@ -851,26 +883,29 @@ send: actual_size = count + (priv->pkt_fmt == packet_format_1 ? 2 : 1); - usb_serial_debug_data(debug, &port->dev, __func__, port->interrupt_out_size, - port->interrupt_out_urb->transfer_buffer); + usb_serial_debug_data(debug, &port->dev, __func__, + port->interrupt_out_size, + port->interrupt_out_urb->transfer_buffer); usb_fill_int_urb(port->interrupt_out_urb, port->serial->dev, usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress), port->interrupt_out_buffer, port->interrupt_out_size, cypress_write_int_callback, port, priv->write_urb_interval); - result = usb_submit_urb (port->interrupt_out_urb, GFP_ATOMIC); + result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC); if (result) { - dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __func__, - result); + dev_err(&port->dev, + "%s - failed submitting write urb, error %d\n", + __func__, result); priv->write_urb_in_use = 0; cypress_set_dead(port); } spin_lock_irqsave(&priv->lock, flags); - if (priv->cmd_ctrl) { + if (priv->cmd_ctrl) priv->cmd_ctrl = 0; - } - priv->bytes_out += count; /* do not count the line control and size bytes */ + + /* do not count the line control and size bytes */ + priv->bytes_out += count; spin_unlock_irqrestore(&priv->lock, flags); usb_serial_port_softint(port); @@ -878,8 +913,9 @@ send: /* returns how much space is available in the soft buffer */ -static int cypress_write_room(struct usb_serial_port *port) +static int cypress_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); int room = 0; unsigned long flags; @@ -895,13 +931,14 @@ static int cypress_write_room(struct usb_serial_port *port) } -static int cypress_tiocmget (struct usb_serial_port *port, struct file *file) +static int cypress_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); __u8 status, control; unsigned int result = 0; unsigned long flags; - + dbg("%s - port %d", __func__, port->number); spin_lock_irqsave(&priv->lock, flags); @@ -922,12 +959,13 @@ static int cypress_tiocmget (struct usb_serial_port *port, struct file *file) } -static int cypress_tiocmset (struct usb_serial_port *port, struct file *file, +static int cypress_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); unsigned long flags; - + dbg("%s - port %d", __func__, port->number); spin_lock_irqsave(&priv->lock, flags); @@ -942,63 +980,60 @@ static int cypress_tiocmset (struct usb_serial_port *port, struct file *file, priv->cmd_ctrl = 1; spin_unlock_irqrestore(&priv->lock, flags); - return cypress_write(port, NULL, 0); + return cypress_write(tty, port, NULL, 0); } -static int cypress_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) +static int cypress_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd); switch (cmd) { - /* This code comes from drivers/char/serial.c and ftdi_sio.c */ - case TIOCMIWAIT: - while (priv != NULL) { - interruptible_sleep_on(&priv->delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - else { - char diff = priv->diff_status; - - if (diff == 0) { - return -EIO; /* no change => error */ - } - - /* consume all events */ - priv->diff_status = 0; - - /* return 0 if caller wanted to know about these bits */ - if ( ((arg & TIOCM_RNG) && (diff & UART_RI)) || - ((arg & TIOCM_DSR) && (diff & UART_DSR)) || - ((arg & TIOCM_CD) && (diff & UART_CD)) || - ((arg & TIOCM_CTS) && (diff & UART_CTS)) ) { - return 0; - } - /* otherwise caller can't care less about what happened, - * and so we continue to wait for more events. - */ - } + /* This code comes from drivers/char/serial.c and ftdi_sio.c */ + case TIOCMIWAIT: + while (priv != NULL) { + interruptible_sleep_on(&priv->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + else { + char diff = priv->diff_status; + if (diff == 0) + return -EIO; /* no change => error */ + + /* consume all events */ + priv->diff_status = 0; + + /* return 0 if caller wanted to know about + these bits */ + if (((arg & TIOCM_RNG) && (diff & UART_RI)) || + ((arg & TIOCM_DSR) && (diff & UART_DSR)) || + ((arg & TIOCM_CD) && (diff & UART_CD)) || + ((arg & TIOCM_CTS) && (diff & UART_CTS))) + return 0; + /* otherwise caller can't care less about what + * happened, and so we continue to wait for + * more events. + */ } - return 0; - break; - default: - break; + } + return 0; + default: + break; } - dbg("%s - arg not supported - it was 0x%04x - check include/asm/ioctls.h", __func__, cmd); - return -ENOIOCTLCMD; } /* cypress_ioctl */ -static void cypress_set_termios (struct usb_serial_port *port, - struct ktermios *old_termios) +static void cypress_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct cypress_private *priv = usb_get_serial_port_data(port); - struct tty_struct *tty; int data_bits, stop_bits, parity_type, parity_enable; unsigned cflag, iflag; unsigned long flags; @@ -1007,8 +1042,6 @@ static void cypress_set_termios (struct usb_serial_port *port, dbg("%s - port %d", __func__, port->number); - tty = port->tty; - spin_lock_irqsave(&priv->lock, flags); if (!priv->termios_initialized) { if (priv->chiptype == CT_EARTHMATE) { @@ -1060,28 +1093,24 @@ static void cypress_set_termios (struct usb_serial_port *port, } else parity_enable = parity_type = 0; - if (cflag & CSIZE) { - switch (cflag & CSIZE) { - case CS5: - data_bits = 0; - break; - case CS6: - data_bits = 1; - break; - case CS7: - data_bits = 2; - break; - case CS8: - data_bits = 3; - break; - default: - err("%s - CSIZE was set, but not CS5-CS8", - __func__); - data_bits = 3; - } - } else + switch (cflag & CSIZE) { + case CS5: + data_bits = 0; + break; + case CS6: + data_bits = 1; + break; + case CS7: + data_bits = 2; + break; + case CS8: data_bits = 3; - + break; + default: + err("%s - CSIZE was set, but not CS5-CS8", + __func__); + data_bits = 3; + } spin_lock_irqsave(&priv->lock, flags); oldlines = priv->line_control; if ((cflag & CBAUD) == B0) { @@ -1096,19 +1125,21 @@ static void cypress_set_termios (struct usb_serial_port *port, "%d data_bits (+5)", __func__, stop_bits, parity_enable, parity_type, data_bits); - cypress_serial_control(port, tty_get_baud_rate(tty), data_bits, stop_bits, - parity_enable, parity_type, 0, CYPRESS_SET_CONFIG); + cypress_serial_control(tty, port, tty_get_baud_rate(tty), + data_bits, stop_bits, + parity_enable, parity_type, + 0, CYPRESS_SET_CONFIG); /* we perform a CYPRESS_GET_CONFIG so that the current settings are * filled into the private structure this should confirm that all is * working if it returns what we just set */ - cypress_serial_control(port, 0, 0, 0, 0, 0, 0, CYPRESS_GET_CONFIG); + cypress_serial_control(tty, port, 0, 0, 0, 0, 0, 0, CYPRESS_GET_CONFIG); /* Here we can define custom tty settings for devices; the main tty * termios flag base comes from empeg.c */ spin_lock_irqsave(&priv->lock, flags); - if ( (priv->chiptype == CT_EARTHMATE) && (priv->baud_rate == 4800) ) { + if (priv->chiptype == CT_EARTHMATE && priv->baud_rate == 4800) { dbg("Using custom termios settings for a baud rate of " "4800bps."); /* define custom termios settings for NMEA protocol */ @@ -1142,20 +1173,21 @@ static void cypress_set_termios (struct usb_serial_port *port, /* if necessary, set lines */ if (linechange) { priv->cmd_ctrl = 1; - cypress_write(port, NULL, 0); + cypress_write(tty, port, NULL, 0); } } /* cypress_set_termios */ /* returns amount of data still left in soft buffer */ -static int cypress_chars_in_buffer(struct usb_serial_port *port) +static int cypress_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; dbg("%s - port %d", __func__, port->number); - + spin_lock_irqsave(&priv->lock, flags); chars = cypress_buf_data_avail(priv->buf); spin_unlock_irqrestore(&priv->lock, flags); @@ -1165,8 +1197,9 @@ static int cypress_chars_in_buffer(struct usb_serial_port *port) } -static void cypress_throttle (struct usb_serial_port *port) +static void cypress_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -1178,8 +1211,9 @@ static void cypress_throttle (struct usb_serial_port *port) } -static void cypress_unthrottle (struct usb_serial_port *port) +static void cypress_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); int actually_throttled, result; unsigned long flags; @@ -1232,12 +1266,13 @@ static void cypress_read_int_callback(struct urb *urb) /* precursor to disconnect so just go away */ return; case -EPIPE: - usb_clear_halt(port->serial->dev,0x81); + usb_clear_halt(port->serial->dev, 0x81); break; default: /* something ugly is going on... */ - dev_err(&urb->dev->dev,"%s - unexpected nonzero read status received: %d\n", - __func__, status); + dev_err(&urb->dev->dev, + "%s - unexpected nonzero read status received: %d\n", + __func__, status); cypress_set_dead(port); return; } @@ -1251,7 +1286,7 @@ static void cypress_read_int_callback(struct urb *urb) } spin_unlock_irqrestore(&priv->lock, flags); - tty = port->tty; + tty = port->port.tty; if (!tty) { dbg("%s - bad tty pointer - exiting", __func__); return; @@ -1285,8 +1320,8 @@ static void cypress_read_int_callback(struct urb *urb) goto continue_read; } - usb_serial_debug_data (debug, &port->dev, __func__, - urb->actual_length, data); + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, data); spin_lock_irqsave(&priv->lock, flags); /* check to see if status has changed */ @@ -1327,7 +1362,7 @@ static void cypress_read_int_callback(struct urb *urb) data[i]); tty_insert_flip_char(tty, data[i], tty_flag); } - tty_flip_buffer_push(port->tty); + tty_flip_buffer_push(port->port.tty); } spin_lock_irqsave(&priv->lock, flags); @@ -1339,13 +1374,14 @@ continue_read: /* Continue trying to always read... unless the port has closed. */ - if (port->open_count > 0 && priv->comm_is_ok) { + if (port->port.count > 0 && priv->comm_is_ok) { usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev, usb_rcvintpipe(port->serial->dev, port->interrupt_in_endpointAddress), port->interrupt_in_urb->transfer_buffer, port->interrupt_in_urb->transfer_buffer_length, - cypress_read_int_callback, port, priv->read_urb_interval); + cypress_read_int_callback, port, + priv->read_urb_interval); result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); if (result) { dev_err(&urb->dev->dev, "%s - failed resubmitting " @@ -1369,42 +1405,43 @@ static void cypress_write_int_callback(struct urb *urb) dbg("%s - port %d", __func__, port->number); switch (status) { - case 0: - /* success */ + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __func__, status); + priv->write_urb_in_use = 0; + return; + case -EPIPE: /* no break needed; clear halt and resubmit */ + if (!priv->comm_is_ok) break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", - __func__, status); - priv->write_urb_in_use = 0; + usb_clear_halt(port->serial->dev, 0x02); + /* error in the urb, so we have to resubmit it */ + dbg("%s - nonzero write bulk status received: %d", + __func__, status); + port->interrupt_out_urb->transfer_buffer_length = 1; + port->interrupt_out_urb->dev = port->serial->dev; + result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC); + if (!result) return; - case -EPIPE: /* no break needed; clear halt and resubmit */ - if (!priv->comm_is_ok) - break; - usb_clear_halt(port->serial->dev, 0x02); - /* error in the urb, so we have to resubmit it */ - dbg("%s - nonzero write bulk status received: %d", - __func__, status); - port->interrupt_out_urb->transfer_buffer_length = 1; - port->interrupt_out_urb->dev = port->serial->dev; - result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC); - if (!result) - return; - dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", - __func__, result); - cypress_set_dead(port); - break; - default: - dev_err(&urb->dev->dev,"%s - unexpected nonzero write status received: %d\n", - __func__, status); - cypress_set_dead(port); - break; + dev_err(&urb->dev->dev, + "%s - failed resubmitting write urb, error %d\n", + __func__, result); + cypress_set_dead(port); + break; + default: + dev_err(&urb->dev->dev, + "%s - unexpected nonzero write status received: %d\n", + __func__, status); + cypress_set_dead(port); + break; } - priv->write_urb_in_use = 0; - + /* send any buffered data */ cypress_send(port); } @@ -1486,7 +1523,8 @@ static void cypress_buf_clear(struct cypress_buf *cb) static unsigned int cypress_buf_data_avail(struct cypress_buf *cb) { if (cb != NULL) - return ((cb->buf_size + cb->buf_put - cb->buf_get) % cb->buf_size); + return (cb->buf_size + cb->buf_put - cb->buf_get) + % cb->buf_size; else return 0; } @@ -1502,7 +1540,8 @@ static unsigned int cypress_buf_data_avail(struct cypress_buf *cb) static unsigned int cypress_buf_space_avail(struct cypress_buf *cb) { if (cb != NULL) - return ((cb->buf_size + cb->buf_get - cb->buf_put - 1) % cb->buf_size); + return (cb->buf_size + cb->buf_get - cb->buf_put - 1) + % cb->buf_size; else return 0; } @@ -1602,9 +1641,9 @@ static unsigned int cypress_buf_get(struct cypress_buf *cb, char *buf, static int __init cypress_init(void) { int retval; - + dbg("%s", __func__); - + retval = usb_serial_register(&cypress_earthmate_device); if (retval) goto failed_em_register; @@ -1632,23 +1671,23 @@ failed_em_register: } -static void __exit cypress_exit (void) +static void __exit cypress_exit(void) { dbg("%s", __func__); - usb_deregister (&cypress_driver); - usb_serial_deregister (&cypress_earthmate_device); - usb_serial_deregister (&cypress_hidcom_device); - usb_serial_deregister (&cypress_ca42v2_device); + usb_deregister(&cypress_driver); + usb_serial_deregister(&cypress_earthmate_device); + usb_serial_deregister(&cypress_hidcom_device); + usb_serial_deregister(&cypress_ca42v2_device); } module_init(cypress_init); module_exit(cypress_exit); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_VERSION( DRIVER_VERSION ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); diff --git a/drivers/usb/serial/cypress_m8.h b/drivers/usb/serial/cypress_m8.h index 0388065bb794..e772b01ac3ac 100644 --- a/drivers/usb/serial/cypress_m8.h +++ b/drivers/usb/serial/cypress_m8.h @@ -54,7 +54,7 @@ #define UART_DSR 0x20 /* data set ready - flow control - device to host */ #define CONTROL_RTS 0x10 /* request to send - flow control - host to device */ #define UART_CTS 0x10 /* clear to send - flow control - device to host */ -#define UART_RI 0x10 /* ring indicator - modem - device to host */ +#define UART_RI 0x10 /* ring indicator - modem - device to host */ #define UART_CD 0x40 /* carrier detect - modem - device to host */ #define CYP_ERROR 0x08 /* received from input report - device to host */ /* Note - the below has nothing to to with the "feature report" reset */ diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 04a56f300ea6..240aad1acaab 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -15,7 +15,7 @@ * Al Borchers (borchers@steinerpoint.com) * * (12/03/2001) gkh -* switched to using port->open_count instead of private version. +* switched to using port->port.count instead of private version. * Removed port->active * * (04/08/2001) gb @@ -229,8 +229,6 @@ * in case a wake up is lost. * - Following Documentation/DocBook/kernel-locking.pdf no spin locks * are held when calling copy_to/from_user or printk. -* -* $Id: digi_acceleport.c,v 1.80.1.2 2000/11/02 05:45:08 root Exp $ */ #include <linux/kernel.h> @@ -243,7 +241,7 @@ #include <linux/module.h> #include <linux/spinlock.h> #include <linux/workqueue.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/wait.h> #include <linux/usb/serial.h> @@ -443,22 +441,23 @@ static int digi_set_modem_signals(struct usb_serial_port *port, unsigned int modem_signals, int interruptible); static int digi_transmit_idle(struct usb_serial_port *port, unsigned long timeout); -static void digi_rx_throttle (struct usb_serial_port *port); -static void digi_rx_unthrottle (struct usb_serial_port *port); -static void digi_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios); -static void digi_break_ctl(struct usb_serial_port *port, int break_state); -static int digi_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg); -static int digi_tiocmget(struct usb_serial_port *port, struct file *file); -static int digi_tiocmset(struct usb_serial_port *port, struct file *file, +static void digi_rx_throttle(struct tty_struct *tty); +static void digi_rx_unthrottle(struct tty_struct *tty); +static void digi_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios); +static void digi_break_ctl(struct tty_struct *tty, int break_state); +static int digi_tiocmget(struct tty_struct *tty, struct file *file); +static int digi_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); -static int digi_write(struct usb_serial_port *port, const unsigned char *buf, int count); +static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); static void digi_write_bulk_callback(struct urb *urb); -static int digi_write_room(struct usb_serial_port *port); -static int digi_chars_in_buffer(struct usb_serial_port *port); -static int digi_open(struct usb_serial_port *port, struct file *filp); -static void digi_close(struct usb_serial_port *port, struct file *filp); +static int digi_write_room(struct tty_struct *tty); +static int digi_chars_in_buffer(struct tty_struct *tty); +static int digi_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); +static void digi_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); static int digi_startup_device(struct usb_serial *serial); static int digi_startup(struct usb_serial *serial); static void digi_shutdown(struct usb_serial *serial); @@ -487,7 +486,7 @@ static struct usb_device_id id_table_4 [] = { { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table_combined); +MODULE_DEVICE_TABLE(usb, id_table_combined); static struct usb_driver digi_driver = { .name = "digi_acceleport", @@ -518,7 +517,6 @@ static struct usb_serial_driver digi_acceleport_2_device = { .chars_in_buffer = digi_chars_in_buffer, .throttle = digi_rx_throttle, .unthrottle = digi_rx_unthrottle, - .ioctl = digi_ioctl, .set_termios = digi_set_termios, .break_ctl = digi_break_ctl, .tiocmget = digi_tiocmget, @@ -545,7 +543,6 @@ static struct usb_serial_driver digi_acceleport_4_device = { .chars_in_buffer = digi_chars_in_buffer, .throttle = digi_rx_throttle, .unthrottle = digi_rx_unthrottle, - .ioctl = digi_ioctl, .set_termios = digi_set_termios, .break_ctl = digi_break_ctl, .tiocmget = digi_tiocmget, @@ -558,21 +555,22 @@ static struct usb_serial_driver digi_acceleport_4_device = { /* Functions */ /* -* Cond Wait Interruptible Timeout Irqrestore -* -* Do spin_unlock_irqrestore and interruptible_sleep_on_timeout -* so that wake ups are not lost if they occur between the unlock -* and the sleep. In other words, spin_unlock_irqrestore and -* interruptible_sleep_on_timeout are "atomic" with respect to -* wake ups. This is used to implement condition variables. -* -* interruptible_sleep_on_timeout is deprecated and has been replaced -* with the equivalent code. -*/ + * Cond Wait Interruptible Timeout Irqrestore + * + * Do spin_unlock_irqrestore and interruptible_sleep_on_timeout + * so that wake ups are not lost if they occur between the unlock + * and the sleep. In other words, spin_unlock_irqrestore and + * interruptible_sleep_on_timeout are "atomic" with respect to + * wake ups. This is used to implement condition variables. + * + * interruptible_sleep_on_timeout is deprecated and has been replaced + * with the equivalent code. + */ static long cond_wait_interruptible_timeout_irqrestore( wait_queue_head_t *q, long timeout, spinlock_t *lock, unsigned long flags) +__releases(lock) { DEFINE_WAIT(wait); @@ -586,15 +584,16 @@ static long cond_wait_interruptible_timeout_irqrestore( /* -* Digi Wakeup Write -* -* Wake up port, line discipline, and tty processes sleeping -* on writes. -*/ + * Digi Wakeup Write + * + * Wake up port, line discipline, and tty processes sleeping + * on writes. + */ static void digi_wakeup_write_lock(struct work_struct *work) { - struct digi_port *priv = container_of(work, struct digi_port, dp_wakeup_work); + struct digi_port *priv = + container_of(work, struct digi_port, dp_wakeup_work); struct usb_serial_port *port = priv->dp_port; unsigned long flags; @@ -605,20 +604,20 @@ static void digi_wakeup_write_lock(struct work_struct *work) static void digi_wakeup_write(struct usb_serial_port *port) { - tty_wakeup(port->tty); + tty_wakeup(port->port.tty); } /* -* Digi Write OOB Command -* -* Write commands on the out of band port. Commands are 4 -* bytes each, multiple commands can be sent at once, and -* no command will be split across USB packets. Returns 0 -* if successful, -EINTR if interrupted while sleeping and -* the interruptible flag is true, or a negative error -* returned by usb_submit_urb. -*/ + * Digi Write OOB Command + * + * Write commands on the out of band port. Commands are 4 + * bytes each, multiple commands can be sent at once, and + * no command will be split across USB packets. Returns 0 + * if successful, -EINTR if interrupted while sleeping and + * the interruptible flag is true, or a negative error + * returned by usb_submit_urb. + */ static int digi_write_oob_command(struct usb_serial_port *port, unsigned char *buf, int count, int interruptible) @@ -633,8 +632,8 @@ static int digi_write_oob_command(struct usb_serial_port *port, dbg("digi_write_oob_command: TOP: port=%d, count=%d", oob_priv->dp_port_num, count); spin_lock_irqsave(&oob_priv->dp_port_lock, flags); - while(count > 0) { - while(oob_port->write_urb->status == -EINPROGRESS + while (count > 0) { + while (oob_port->write_urb->status == -EINPROGRESS || oob_priv->dp_write_urb_in_use) { cond_wait_interruptible_timeout_irqrestore( &oob_port->write_wait, DIGI_RETRY_TIMEOUT, @@ -651,7 +650,8 @@ static int digi_write_oob_command(struct usb_serial_port *port, memcpy(oob_port->write_urb->transfer_buffer, buf, len); oob_port->write_urb->transfer_buffer_length = len; oob_port->write_urb->dev = port->serial->dev; - if ((ret = usb_submit_urb(oob_port->write_urb, GFP_ATOMIC)) == 0) { + ret = usb_submit_urb(oob_port->write_urb, GFP_ATOMIC); + if (ret == 0) { oob_priv->dp_write_urb_in_use = 1; count -= len; buf += len; @@ -666,16 +666,16 @@ static int digi_write_oob_command(struct usb_serial_port *port, /* -* Digi Write In Band Command -* -* Write commands on the given port. Commands are 4 -* bytes each, multiple commands can be sent at once, and -* no command will be split across USB packets. If timeout -* is non-zero, write in band command will return after -* waiting unsuccessfully for the URB status to clear for -* timeout ticks. Returns 0 if successful, or a negative -* error returned by digi_write. -*/ + * Digi Write In Band Command + * + * Write commands on the given port. Commands are 4 + * bytes each, multiple commands can be sent at once, and + * no command will be split across USB packets. If timeout + * is non-zero, write in band command will return after + * waiting unsuccessfully for the URB status to clear for + * timeout ticks. Returns 0 if successful, or a negative + * error returned by digi_write. + */ static int digi_write_inb_command(struct usb_serial_port *port, unsigned char *buf, int count, unsigned long timeout) @@ -695,9 +695,10 @@ static int digi_write_inb_command(struct usb_serial_port *port, timeout = ULONG_MAX; spin_lock_irqsave(&priv->dp_port_lock, flags); - while(count > 0 && ret == 0) { - while((port->write_urb->status == -EINPROGRESS - || priv->dp_write_urb_in_use) && time_before(jiffies, timeout)) { + while (count > 0 && ret == 0) { + while ((port->write_urb->status == -EINPROGRESS + || priv->dp_write_urb_in_use) + && time_before(jiffies, timeout)) { cond_wait_interruptible_timeout_irqrestore( &port->write_wait, DIGI_RETRY_TIMEOUT, &priv->dp_port_lock, flags); @@ -728,7 +729,8 @@ static int digi_write_inb_command(struct usb_serial_port *port, } port->write_urb->dev = port->serial->dev; - if ((ret = usb_submit_urb(port->write_urb, GFP_ATOMIC)) == 0) { + ret = usb_submit_urb(port->write_urb, GFP_ATOMIC); + if (ret == 0) { priv->dp_write_urb_in_use = 1; priv->dp_out_buf_len = 0; count -= len; @@ -746,14 +748,14 @@ static int digi_write_inb_command(struct usb_serial_port *port, /* -* Digi Set Modem Signals -* -* Sets or clears DTR and RTS on the port, according to the -* modem_signals argument. Use TIOCM_DTR and TIOCM_RTS flags -* for the modem_signals argument. Returns 0 if successful, -* -EINTR if interrupted while sleeping, or a non-zero error -* returned by usb_submit_urb. -*/ + * Digi Set Modem Signals + * + * Sets or clears DTR and RTS on the port, according to the + * modem_signals argument. Use TIOCM_DTR and TIOCM_RTS flags + * for the modem_signals argument. Returns 0 if successful, + * -EINTR if interrupted while sleeping, or a non-zero error + * returned by usb_submit_urb. + */ static int digi_set_modem_signals(struct usb_serial_port *port, unsigned int modem_signals, int interruptible) @@ -761,7 +763,7 @@ static int digi_set_modem_signals(struct usb_serial_port *port, int ret; struct digi_port *port_priv = usb_get_serial_port_data(port); - struct usb_serial_port *oob_port = (struct usb_serial_port *)((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port; + struct usb_serial_port *oob_port = (struct usb_serial_port *) ((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port; struct digi_port *oob_priv = usb_get_serial_port_data(oob_port); unsigned char *data = oob_port->write_urb->transfer_buffer; unsigned long flags = 0; @@ -773,7 +775,8 @@ static int digi_set_modem_signals(struct usb_serial_port *port, spin_lock_irqsave(&oob_priv->dp_port_lock, flags); spin_lock(&port_priv->dp_port_lock); - while(oob_port->write_urb->status == -EINPROGRESS || oob_priv->dp_write_urb_in_use) { + while (oob_port->write_urb->status == -EINPROGRESS || + oob_priv->dp_write_urb_in_use) { spin_unlock(&port_priv->dp_port_lock); cond_wait_interruptible_timeout_irqrestore( &oob_port->write_wait, DIGI_RETRY_TIMEOUT, @@ -785,17 +788,20 @@ static int digi_set_modem_signals(struct usb_serial_port *port, } data[0] = DIGI_CMD_SET_DTR_SIGNAL; data[1] = port_priv->dp_port_num; - data[2] = (modem_signals&TIOCM_DTR) ? DIGI_DTR_ACTIVE : DIGI_DTR_INACTIVE; + data[2] = (modem_signals & TIOCM_DTR) ? + DIGI_DTR_ACTIVE : DIGI_DTR_INACTIVE; data[3] = 0; data[4] = DIGI_CMD_SET_RTS_SIGNAL; data[5] = port_priv->dp_port_num; - data[6] = (modem_signals&TIOCM_RTS) ? DIGI_RTS_ACTIVE : DIGI_RTS_INACTIVE; + data[6] = (modem_signals & TIOCM_RTS) ? + DIGI_RTS_ACTIVE : DIGI_RTS_INACTIVE; data[7] = 0; oob_port->write_urb->transfer_buffer_length = 8; oob_port->write_urb->dev = port->serial->dev; - if ((ret = usb_submit_urb(oob_port->write_urb, GFP_ATOMIC)) == 0) { + ret = usb_submit_urb(oob_port->write_urb, GFP_ATOMIC); + if (ret == 0) { oob_priv->dp_write_urb_in_use = 1; port_priv->dp_modem_signals = (port_priv->dp_modem_signals&~(TIOCM_DTR|TIOCM_RTS)) @@ -809,16 +815,16 @@ static int digi_set_modem_signals(struct usb_serial_port *port, } /* -* Digi Transmit Idle -* -* Digi transmit idle waits, up to timeout ticks, for the transmitter -* to go idle. It returns 0 if successful or a negative error. -* -* There are race conditions here if more than one process is calling -* digi_transmit_idle on the same port at the same time. However, this -* is only called from close, and only one process can be in close on a -* port at a time, so its ok. -*/ + * Digi Transmit Idle + * + * Digi transmit idle waits, up to timeout ticks, for the transmitter + * to go idle. It returns 0 if successful or a negative error. + * + * There are race conditions here if more than one process is calling + * digi_transmit_idle on the same port at the same time. However, this + * is only called from close, and only one process can be in close on a + * port at a time, so its ok. + */ static int digi_transmit_idle(struct usb_serial_port *port, unsigned long timeout) @@ -837,12 +843,13 @@ static int digi_transmit_idle(struct usb_serial_port *port, timeout += jiffies; - if ((ret = digi_write_inb_command(port, buf, 2, timeout - jiffies)) != 0) + ret = digi_write_inb_command(port, buf, 2, timeout - jiffies); + if (ret != 0) return ret; spin_lock_irqsave(&priv->dp_port_lock, flags); - while(time_before(jiffies, timeout) && !priv->dp_transmit_idle) { + while (time_before(jiffies, timeout) && !priv->dp_transmit_idle) { cond_wait_interruptible_timeout_irqrestore( &priv->dp_transmit_idle_wait, DIGI_RETRY_TIMEOUT, &priv->dp_port_lock, flags); @@ -857,9 +864,10 @@ static int digi_transmit_idle(struct usb_serial_port *port, } -static void digi_rx_throttle(struct usb_serial_port *port) +static void digi_rx_throttle(struct tty_struct *tty) { unsigned long flags; + struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); @@ -873,10 +881,11 @@ static void digi_rx_throttle(struct usb_serial_port *port) } -static void digi_rx_unthrottle(struct usb_serial_port *port) +static void digi_rx_unthrottle(struct tty_struct *tty) { int ret = 0; unsigned long flags; + struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); dbg("digi_rx_unthrottle: TOP: port=%d", priv->dp_port_num); @@ -901,26 +910,25 @@ static void digi_rx_unthrottle(struct usb_serial_port *port) } -static void digi_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void digi_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { - struct digi_port *priv = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; unsigned int iflag = tty->termios->c_iflag; unsigned int cflag = tty->termios->c_cflag; unsigned int old_iflag = old_termios->c_iflag; unsigned int old_cflag = old_termios->c_cflag; unsigned char buf[32]; unsigned int modem_signals; - int arg,ret; + int arg, ret; int i = 0; speed_t baud; dbg("digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, old_cflag=0x%x", priv->dp_port_num, iflag, old_iflag, cflag, old_cflag); /* set baud rate */ - if ((baud = tty_get_baud_rate(tty)) != tty_termios_baud_rate(old_termios)) { + baud = tty_get_baud_rate(tty); + if (baud != tty_termios_baud_rate(old_termios)) { arg = -1; /* reassert DTR and (maybe) RTS on transition from B0 */ @@ -934,30 +942,30 @@ static void digi_set_termios(struct usb_serial_port *port, digi_set_modem_signals(port, modem_signals, 1); } switch (baud) { - /* drop DTR and RTS on transition to B0 */ - case 0: digi_set_modem_signals(port, 0, 1); break; - case 50: arg = DIGI_BAUD_50; break; - case 75: arg = DIGI_BAUD_75; break; - case 110: arg = DIGI_BAUD_110; break; - case 150: arg = DIGI_BAUD_150; break; - case 200: arg = DIGI_BAUD_200; break; - case 300: arg = DIGI_BAUD_300; break; - case 600: arg = DIGI_BAUD_600; break; - case 1200: arg = DIGI_BAUD_1200; break; - case 1800: arg = DIGI_BAUD_1800; break; - case 2400: arg = DIGI_BAUD_2400; break; - case 4800: arg = DIGI_BAUD_4800; break; - case 9600: arg = DIGI_BAUD_9600; break; - case 19200: arg = DIGI_BAUD_19200; break; - case 38400: arg = DIGI_BAUD_38400; break; - case 57600: arg = DIGI_BAUD_57600; break; - case 115200: arg = DIGI_BAUD_115200; break; - case 230400: arg = DIGI_BAUD_230400; break; - case 460800: arg = DIGI_BAUD_460800; break; - default: - arg = DIGI_BAUD_9600; - baud = 9600; - break; + /* drop DTR and RTS on transition to B0 */ + case 0: digi_set_modem_signals(port, 0, 1); break; + case 50: arg = DIGI_BAUD_50; break; + case 75: arg = DIGI_BAUD_75; break; + case 110: arg = DIGI_BAUD_110; break; + case 150: arg = DIGI_BAUD_150; break; + case 200: arg = DIGI_BAUD_200; break; + case 300: arg = DIGI_BAUD_300; break; + case 600: arg = DIGI_BAUD_600; break; + case 1200: arg = DIGI_BAUD_1200; break; + case 1800: arg = DIGI_BAUD_1800; break; + case 2400: arg = DIGI_BAUD_2400; break; + case 4800: arg = DIGI_BAUD_4800; break; + case 9600: arg = DIGI_BAUD_9600; break; + case 19200: arg = DIGI_BAUD_19200; break; + case 38400: arg = DIGI_BAUD_38400; break; + case 57600: arg = DIGI_BAUD_57600; break; + case 115200: arg = DIGI_BAUD_115200; break; + case 230400: arg = DIGI_BAUD_230400; break; + case 460800: arg = DIGI_BAUD_460800; break; + default: + arg = DIGI_BAUD_9600; + baud = 9600; + break; } if (arg != -1) { buf[i++] = DIGI_CMD_SET_BAUD_RATE; @@ -1083,14 +1091,16 @@ static void digi_set_termios(struct usb_serial_port *port, buf[i++] = arg; buf[i++] = 0; } - if ((ret = digi_write_oob_command(port, buf, i, 1)) != 0) + ret = digi_write_oob_command(port, buf, i, 1); + if (ret != 0) dbg("digi_set_termios: write oob failed, ret=%d", ret); tty_encode_baud_rate(tty, baud, baud); } -static void digi_break_ctl(struct usb_serial_port *port, int break_state) +static void digi_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; unsigned char buf[4]; buf[0] = DIGI_CMD_BREAK_CONTROL; @@ -1101,8 +1111,9 @@ static void digi_break_ctl(struct usb_serial_port *port, int break_state) } -static int digi_tiocmget(struct usb_serial_port *port, struct file *file) +static int digi_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); unsigned int val; unsigned long flags; @@ -1116,9 +1127,10 @@ static int digi_tiocmget(struct usb_serial_port *port, struct file *file) } -static int digi_tiocmset(struct usb_serial_port *port, struct file *file, +static int digi_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); unsigned int val; unsigned long flags; @@ -1132,30 +1144,11 @@ static int digi_tiocmset(struct usb_serial_port *port, struct file *file, } -static int digi_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct digi_port *priv = usb_get_serial_port_data(port); - dbg("digi_ioctl: TOP: port=%d, cmd=0x%x", priv->dp_port_num, cmd); - - switch (cmd) { - case TIOCMIWAIT: - /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ - /* TODO */ - return 0; - case TIOCGICOUNT: - /* return count of modemline transitions */ - /* TODO */ - return 0; - } - return -ENOIOCTLCMD; - -} - -static int digi_write(struct usb_serial_port *port, const unsigned char *buf, int count) +static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { - int ret,data_len,new_len; + int ret, data_len, new_len; struct digi_port *priv = usb_get_serial_port_data(port); unsigned char *data = port->write_urb->transfer_buffer; unsigned long flags = 0; @@ -1173,7 +1166,8 @@ static int digi_write(struct usb_serial_port *port, const unsigned char *buf, in spin_lock_irqsave(&priv->dp_port_lock, flags); /* wait for urb status clear to submit another urb */ - if (port->write_urb->status == -EINPROGRESS || priv->dp_write_urb_in_use) { + if (port->write_urb->status == -EINPROGRESS || + priv->dp_write_urb_in_use) { /* buffer data if count is 1 (probably put_char) if possible */ if (count == 1 && priv->dp_out_buf_len < DIGI_OUT_BUF_SIZE) { priv->dp_out_buf[priv->dp_out_buf_len++] = *buf; @@ -1208,7 +1202,8 @@ static int digi_write(struct usb_serial_port *port, const unsigned char *buf, in /* copy in new data */ memcpy(data, buf, new_len); - if ((ret = usb_submit_urb(port->write_urb, GFP_ATOMIC)) == 0) { + ret = usb_submit_urb(port->write_urb, GFP_ATOMIC); + if (ret == 0) { priv->dp_write_urb_in_use = 1; ret = new_len; priv->dp_out_buf_len = 0; @@ -1222,7 +1217,7 @@ static int digi_write(struct usb_serial_port *port, const unsigned char *buf, in dbg("digi_write: returning %d", ret); return ret; -} +} static void digi_write_bulk_callback(struct urb *urb) { @@ -1237,13 +1232,13 @@ static void digi_write_bulk_callback(struct urb *urb) dbg("digi_write_bulk_callback: TOP, urb->status=%d", status); /* port and serial sanity check */ - if (port == NULL || (priv=usb_get_serial_port_data(port)) == NULL) { + if (port == NULL || (priv = usb_get_serial_port_data(port)) == NULL) { err("%s: port or port->private is NULL, status=%d", __func__, status); return; } serial = port->serial; - if (serial == NULL || (serial_priv=usb_get_serial_data(serial)) == NULL) { + if (serial == NULL || (serial_priv = usb_get_serial_data(serial)) == NULL) { err("%s: serial or serial->private is NULL, status=%d", __func__, status); return; @@ -1262,17 +1257,19 @@ static void digi_write_bulk_callback(struct urb *urb) /* try to send any buffered data on this port, if it is open */ spin_lock(&priv->dp_port_lock); priv->dp_write_urb_in_use = 0; - if (port->open_count && port->write_urb->status != -EINPROGRESS + if (port->port.count && port->write_urb->status != -EINPROGRESS && priv->dp_out_buf_len > 0) { *((unsigned char *)(port->write_urb->transfer_buffer)) = (unsigned char)DIGI_CMD_SEND_DATA; - *((unsigned char *)(port->write_urb->transfer_buffer)+1) + *((unsigned char *)(port->write_urb->transfer_buffer) + 1) = (unsigned char)priv->dp_out_buf_len; - port->write_urb->transfer_buffer_length = priv->dp_out_buf_len+2; + port->write_urb->transfer_buffer_length = + priv->dp_out_buf_len + 2; port->write_urb->dev = serial->dev; - memcpy(port->write_urb->transfer_buffer+2, priv->dp_out_buf, + memcpy(port->write_urb->transfer_buffer + 2, priv->dp_out_buf, priv->dp_out_buf_len); - if ((ret = usb_submit_urb(port->write_urb, GFP_ATOMIC)) == 0) { + ret = usb_submit_urb(port->write_urb, GFP_ATOMIC); + if (ret == 0) { priv->dp_write_urb_in_use = 1; priv->dp_out_buf_len = 0; } @@ -1289,16 +1286,17 @@ static void digi_write_bulk_callback(struct urb *urb) __func__, ret, priv->dp_port_num); } -static int digi_write_room(struct usb_serial_port *port) +static int digi_write_room(struct tty_struct *tty) { - - int room; + struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); + int room; unsigned long flags = 0; spin_lock_irqsave(&priv->dp_port_lock, flags); - if (port->write_urb->status == -EINPROGRESS || priv->dp_write_urb_in_use) + if (port->write_urb->status == -EINPROGRESS || + priv->dp_write_urb_in_use) room = 0; else room = port->bulk_out_size - 2 - priv->dp_out_buf_len; @@ -1309,12 +1307,11 @@ static int digi_write_room(struct usb_serial_port *port) } -static int digi_chars_in_buffer(struct usb_serial_port *port) +static int digi_chars_in_buffer(struct tty_struct *tty) { - + struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); - if (port->write_urb->status == -EINPROGRESS || priv->dp_write_urb_in_use) { dbg("digi_chars_in_buffer: port=%d, chars=%d", @@ -1330,7 +1327,8 @@ static int digi_chars_in_buffer(struct usb_serial_port *port) } -static int digi_open(struct usb_serial_port *port, struct file *filp) +static int digi_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { int ret; unsigned char buf[32]; @@ -1339,7 +1337,7 @@ static int digi_open(struct usb_serial_port *port, struct file *filp) unsigned long flags = 0; dbg("digi_open: TOP: port=%d, open_count=%d", - priv->dp_port_num, port->open_count); + priv->dp_port_num, port->port.count); /* be sure the device is started up */ if (digi_startup_device(port->serial) != 0) @@ -1354,7 +1352,7 @@ static int digi_open(struct usb_serial_port *port, struct file *filp) } /* wait for a close in progress to finish */ - while(priv->dp_in_close) { + while (priv->dp_in_close) { cond_wait_interruptible_timeout_irqrestore( &priv->dp_close_wait, DIGI_RETRY_TIMEOUT, &priv->dp_port_lock, flags); @@ -1364,7 +1362,7 @@ static int digi_open(struct usb_serial_port *port, struct file *filp) } spin_unlock_irqrestore(&priv->dp_port_lock, flags); - + /* read modem signals automatically whenever they change */ buf[0] = DIGI_CMD_READ_INPUT_SIGNALS; buf[1] = priv->dp_port_num; @@ -1377,13 +1375,16 @@ static int digi_open(struct usb_serial_port *port, struct file *filp) buf[6] = DIGI_FLUSH_TX | DIGI_FLUSH_RX; buf[7] = 0; - if ((ret = digi_write_oob_command(port, buf, 8, 1)) != 0) + ret = digi_write_oob_command(port, buf, 8, 1); + if (ret != 0) dbg("digi_open: write oob failed, ret=%d", ret); /* set termios settings */ - not_termios.c_cflag = ~port->tty->termios->c_cflag; - not_termios.c_iflag = ~port->tty->termios->c_iflag; - digi_set_termios(port, ¬_termios); + if (tty) { + not_termios.c_cflag = ~tty->termios->c_cflag; + not_termios.c_iflag = ~tty->termios->c_iflag; + digi_set_termios(tty, port, ¬_termios); + } /* set DTR and RTS */ digi_set_modem_signals(port, TIOCM_DTR|TIOCM_RTS, 1); @@ -1392,16 +1393,16 @@ static int digi_open(struct usb_serial_port *port, struct file *filp) } -static void digi_close(struct usb_serial_port *port, struct file *filp) +static void digi_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { DEFINE_WAIT(wait); int ret; unsigned char buf[32]; - struct tty_struct *tty = port->tty; struct digi_port *priv = usb_get_serial_port_data(port); dbg("digi_close: TOP: port=%d, open_count=%d", - priv->dp_port_num, port->open_count); + priv->dp_port_num, port->port.count); mutex_lock(&port->serial->disc_mutex); /* if disconnected, just clear flags */ @@ -1426,9 +1427,8 @@ static void digi_close(struct usb_serial_port *port, struct file *filp) if (port->serial->dev) { /* wait for transmit idle */ - if ((filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0) { + if ((filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0) digi_transmit_idle(port, DIGI_CLOSE_TIMEOUT); - } /* drop DTR and RTS */ digi_set_modem_signals(port, 0, 0); @@ -1462,11 +1462,13 @@ static void digi_close(struct usb_serial_port *port, struct file *filp) buf[18] = DIGI_FLUSH_TX | DIGI_FLUSH_RX; buf[19] = 0; - if ((ret = digi_write_oob_command(port, buf, 20, 0)) != 0) + ret = digi_write_oob_command(port, buf, 20, 0); + if (ret != 0) dbg("digi_close: write oob failed, ret=%d", ret); /* wait for final commands on oob port to complete */ - prepare_to_wait(&priv->dp_flush_wait, &wait, TASK_INTERRUPTIBLE); + prepare_to_wait(&priv->dp_flush_wait, &wait, + TASK_INTERRUPTIBLE); schedule_timeout(DIGI_CLOSE_TIMEOUT); finish_wait(&priv->dp_flush_wait, &wait); @@ -1486,15 +1488,15 @@ exit: /* -* Digi Startup Device -* -* Starts reads on all ports. Must be called AFTER startup, with -* urbs initialized. Returns 0 if successful, non-zero error otherwise. -*/ + * Digi Startup Device + * + * Starts reads on all ports. Must be called AFTER startup, with + * urbs initialized. Returns 0 if successful, non-zero error otherwise. + */ static int digi_startup_device(struct usb_serial *serial) { - int i,ret = 0; + int i, ret = 0; struct digi_serial *serial_priv = usb_get_serial_data(serial); struct usb_serial_port *port; @@ -1512,7 +1514,8 @@ static int digi_startup_device(struct usb_serial *serial) for (i = 0; i < serial->type->num_ports + 1; i++) { port = serial->port[i]; port->write_urb->dev = port->serial->dev; - if ((ret = usb_submit_urb(port->read_urb, GFP_KERNEL)) != 0) { + ret = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (ret != 0) { err("%s: usb_submit_urb failed, ret=%d, port=%d", __func__, ret, i); break; @@ -1533,7 +1536,7 @@ static int digi_startup(struct usb_serial *serial) /* allocate the private data structures for all ports */ /* number of regular ports + 1 for the out-of-band port */ - for(i = 0; i < serial->type->num_ports + 1; i++) { + for (i = 0; i < serial->type->num_ports + 1; i++) { /* allocate port private structure */ priv = kmalloc(sizeof(struct digi_port), GFP_KERNEL); if (priv == NULL) { @@ -1596,7 +1599,7 @@ static void digi_shutdown(struct usb_serial *serial) /* free the private data structures for all ports */ /* number of regular ports + 1 for the out-of-band port */ - for(i = 0; i < serial->type->num_ports + 1; i++) + for (i = 0; i < serial->type->num_ports + 1; i++) kfree(usb_get_serial_port_data(serial->port[i])); kfree(usb_get_serial_data(serial)); } @@ -1619,7 +1622,7 @@ static void digi_read_bulk_callback(struct urb *urb) return; } if (port->serial == NULL || - (serial_priv=usb_get_serial_data(port->serial)) == NULL) { + (serial_priv = usb_get_serial_data(port->serial)) == NULL) { err("%s: serial is bad or serial->private is NULL, status=%d", __func__, status); return; @@ -1643,45 +1646,46 @@ static void digi_read_bulk_callback(struct urb *urb) /* continue read */ urb->dev = port->serial->dev; - if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret != 0) { err("%s: failed resubmitting urb, ret=%d, port=%d", __func__, ret, priv->dp_port_num); } } -/* -* Digi Read INB Callback -* -* Digi Read INB Callback handles reads on the in band ports, sending -* the data on to the tty subsystem. When called we know port and -* port->private are not NULL and port->serial has been validated. -* It returns 0 if successful, 1 if successful but the port is -* throttled, and -1 if the sanity checks failed. -*/ +/* + * Digi Read INB Callback + * + * Digi Read INB Callback handles reads on the in band ports, sending + * the data on to the tty subsystem. When called we know port and + * port->private are not NULL and port->serial has been validated. + * It returns 0 if successful, 1 if successful but the port is + * throttled, and -1 if the sanity checks failed. + */ static int digi_read_inb_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; - struct tty_struct *tty = port->tty; + struct tty_struct *tty = port->port.tty; struct digi_port *priv = usb_get_serial_port_data(port); int opcode = ((unsigned char *)urb->transfer_buffer)[0]; int len = ((unsigned char *)urb->transfer_buffer)[1]; int port_status = ((unsigned char *)urb->transfer_buffer)[2]; - unsigned char *data = ((unsigned char *)urb->transfer_buffer)+3; - int flag,throttled; + unsigned char *data = ((unsigned char *)urb->transfer_buffer) + 3; + int flag, throttled; int i; int status = urb->status; /* do not process callbacks on closed ports */ /* but do continue the read chain */ - if (port->open_count == 0) + if (port->port.count == 0) return 0; /* short/multiple packet check */ if (urb->actual_length != len + 2) { - err("%s: INCOMPLETE OR MULTIPLE PACKET, urb->status=%d, " + err("%s: INCOMPLETE OR MULTIPLE PACKET, urb->status=%d, " "port=%d, opcode=%d, len=%d, actual_length=%d, " "status=%d", __func__, status, priv->dp_port_num, opcode, len, urb->actual_length, port_status); @@ -1723,8 +1727,9 @@ static int digi_read_inb_callback(struct urb *urb) if (flag == TTY_NORMAL) tty_insert_flip_string(tty, data, len); else { - for(i = 0; i < len; i++) - tty_insert_flip_char(tty, data[i], flag); + for (i = 0; i < len; i++) + tty_insert_flip_char(tty, + data[i], flag); } tty_flip_buffer_push(tty); } @@ -1736,19 +1741,19 @@ static int digi_read_inb_callback(struct urb *urb) else if (opcode != DIGI_CMD_RECEIVE_DATA) dbg("%s: unknown opcode: %d", __func__, opcode); - return(throttled ? 1 : 0); + return throttled ? 1 : 0; } -/* -* Digi Read OOB Callback -* -* Digi Read OOB Callback handles reads on the out of band port. -* When called we know port and port->private are not NULL and -* the port->serial is valid. It returns 0 if successful, and -* -1 if the sanity checks failed. -*/ +/* + * Digi Read OOB Callback + * + * Digi Read OOB Callback handles reads on the out of band port. + * When called we know port and port->private are not NULL and + * the port->serial is valid. It returns 0 if successful, and + * -1 if the sanity checks failed. + */ static int digi_read_oob_callback(struct urb *urb) { @@ -1758,12 +1763,13 @@ static int digi_read_oob_callback(struct urb *urb) struct digi_port *priv = usb_get_serial_port_data(port); int opcode, line, status, val; int i; + unsigned int rts; dbg("digi_read_oob_callback: port=%d, len=%d", priv->dp_port_num, urb->actual_length); /* handle each oob command */ - for(i = 0; i < urb->actual_length - 3;) { + for (i = 0; i < urb->actual_length - 3;) { opcode = ((unsigned char *)urb->transfer_buffer)[i++]; line = ((unsigned char *)urb->transfer_buffer)[i++]; status = ((unsigned char *)urb->transfer_buffer)[i++]; @@ -1777,27 +1783,29 @@ static int digi_read_oob_callback(struct urb *urb) port = serial->port[line]; - if ((priv=usb_get_serial_port_data(port)) == NULL) + priv = usb_get_serial_port_data(port); + if (priv == NULL) return -1; + rts = 0; + if (port->port.count) + rts = port->port.tty->termios->c_cflag & CRTSCTS; + if (opcode == DIGI_CMD_READ_INPUT_SIGNALS) { spin_lock(&priv->dp_port_lock); /* convert from digi flags to termiox flags */ if (val & DIGI_READ_INPUT_SIGNALS_CTS) { priv->dp_modem_signals |= TIOCM_CTS; /* port must be open to use tty struct */ - if (port->open_count - && port->tty->termios->c_cflag & CRTSCTS) { - port->tty->hw_stopped = 0; + if (rts) { + port->port.tty->hw_stopped = 0; digi_wakeup_write(port); } } else { priv->dp_modem_signals &= ~TIOCM_CTS; /* port must be open to use tty struct */ - if (port->open_count - && port->tty->termios->c_cflag & CRTSCTS) { - port->tty->hw_stopped = 1; - } + if (rts) + port->port.tty->hw_stopped = 1; } if (val & DIGI_READ_INPUT_SIGNALS_DSR) priv->dp_modem_signals |= TIOCM_DSR; @@ -1834,7 +1842,7 @@ static int __init digi_init(void) if (retval) goto failed_acceleport_2_device; retval = usb_serial_register(&digi_acceleport_4_device); - if (retval) + if (retval) goto failed_acceleport_4_device; retval = usb_register(&digi_driver); if (retval) diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index c5ec309a3cb1..a6ab5b58d9ca 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -11,36 +11,39 @@ * it under the terms of the GNU General Public License, as published by * the Free Software Foundation, version 2. * - * See Documentation/usb/usb-serial.txt for more information on using this driver - * + * See Documentation/usb/usb-serial.txt for more information on using this + * driver + * * (07/16/2001) gb - * remove unused code in empeg_close() (thanks to Oliver Neukum for pointing this - * out) and rewrote empeg_set_termios(). - * + * remove unused code in empeg_close() (thanks to Oliver Neukum for + * pointing this out) and rewrote empeg_set_termios(). + * * (05/30/2001) gkh - * switched from using spinlock to a semaphore, which fixes lots of problems. + * switched from using spinlock to a semaphore, which fixes lots of + * problems. * * (04/08/2001) gb * Identify version on module load. - * + * * (01/22/2001) gb - * Added write_room() and chars_in_buffer() support. - * + * Added write_room() and chars_in_buffer() support. + * * (12/21/2000) gb * Moved termio stuff inside the port->active check. * Moved MOD_DEC_USE_COUNT to end of empeg_close(). - * + * * (12/03/2000) gb - * Added port->tty->ldisc.set_termios(port->tty, NULL) to empeg_open() - * This notifies the tty driver that the termios have changed. - * + * Added port->port.tty->ldisc.set_termios(port->port.tty, NULL) to + * empeg_open(). This notifies the tty driver that the termios have + * changed. + * * (11/13/2000) gb - * Moved tty->low_latency = 1 from empeg_read_bulk_callback() to empeg_open() - * (It only needs to be set once - Doh!) - * + * Moved tty->low_latency = 1 from empeg_read_bulk_callback() to + * empeg_open() (It only needs to be set once - Doh!) + * * (11/11/2000) gb * Updated to work with id_table structure. - * + * * (11/04/2000) gb * Forked this from visor.c, and hacked it up to work with an * Empeg ltd. empeg-car player. Constructive criticism welcomed. @@ -48,7 +51,7 @@ * use of his code, and for his guidance, advice and patience. :) * A 'Thank You' is in order for John Ripley of Empeg ltd for his * advice, and patience too. - * + * */ #include <linux/kernel.h> @@ -60,7 +63,7 @@ #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> @@ -77,31 +80,30 @@ static int debug; #define EMPEG_PRODUCT_ID 0x0001 /* function prototypes for an empeg-car player */ -static int empeg_open (struct usb_serial_port *port, struct file *filp); -static void empeg_close (struct usb_serial_port *port, struct file *filp); -static int empeg_write (struct usb_serial_port *port, - const unsigned char *buf, - int count); -static int empeg_write_room (struct usb_serial_port *port); -static int empeg_chars_in_buffer (struct usb_serial_port *port); -static void empeg_throttle (struct usb_serial_port *port); -static void empeg_unthrottle (struct usb_serial_port *port); -static int empeg_startup (struct usb_serial *serial); -static void empeg_shutdown (struct usb_serial *serial); -static int empeg_ioctl (struct usb_serial_port *port, - struct file * file, - unsigned int cmd, - unsigned long arg); -static void empeg_set_termios (struct usb_serial_port *port, struct ktermios *old_termios); -static void empeg_write_bulk_callback (struct urb *urb); -static void empeg_read_bulk_callback (struct urb *urb); +static int empeg_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); +static void empeg_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); +static int empeg_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, + int count); +static int empeg_write_room(struct tty_struct *tty); +static int empeg_chars_in_buffer(struct tty_struct *tty); +static void empeg_throttle(struct tty_struct *tty); +static void empeg_unthrottle(struct tty_struct *tty); +static int empeg_startup(struct usb_serial *serial); +static void empeg_shutdown(struct usb_serial *serial); +static void empeg_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios); +static void empeg_write_bulk_callback(struct urb *urb); +static void empeg_read_bulk_callback(struct urb *urb); static struct usb_device_id id_table [] = { { USB_DEVICE(EMPEG_VENDOR_ID, EMPEG_PRODUCT_ID) }, { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, id_table); static struct usb_driver empeg_driver = { .name = "empeg", @@ -125,7 +127,6 @@ static struct usb_serial_driver empeg_device = { .unthrottle = empeg_unthrottle, .attach = empeg_startup, .shutdown = empeg_shutdown, - .ioctl = empeg_ioctl, .set_termios = empeg_set_termios, .write = empeg_write, .write_room = empeg_write_room, @@ -145,7 +146,8 @@ static int bytes_out; /****************************************************************************** * Empeg specific driver functions ******************************************************************************/ -static int empeg_open (struct usb_serial_port *port, struct file *filp) +static int empeg_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { struct usb_serial *serial = port->serial; int result = 0; @@ -153,7 +155,7 @@ static int empeg_open (struct usb_serial_port *port, struct file *filp) dbg("%s - port %d", __func__, port->number); /* Force default termio settings */ - empeg_set_termios (port, NULL) ; + empeg_set_termios(tty, port, NULL) ; bytes_in = 0; bytes_out = 0; @@ -161,7 +163,7 @@ static int empeg_open (struct usb_serial_port *port, struct file *filp) /* Start reading from the device */ usb_fill_bulk_urb( port->read_urb, - serial->dev, + serial->dev, usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), port->read_urb->transfer_buffer, @@ -172,13 +174,16 @@ static int empeg_open (struct usb_serial_port *port, struct file *filp) result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result) - dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __func__, result); + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", + __func__, result); return result; } -static void empeg_close (struct usb_serial_port *port, struct file * filp) +static void empeg_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { dbg("%s - port %d", __func__, port->number); @@ -189,7 +194,8 @@ static void empeg_close (struct usb_serial_port *port, struct file * filp) } -static int empeg_write (struct usb_serial_port *port, const unsigned char *buf, int count) +static int empeg_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; struct urb *urb; @@ -203,11 +209,10 @@ static int empeg_write (struct usb_serial_port *port, const unsigned char *buf, dbg("%s - port %d", __func__, port->number); while (count > 0) { - /* try to find a free urb in our list of them */ urb = NULL; - spin_lock_irqsave (&write_urb_pool_lock, flags); + spin_lock_irqsave(&write_urb_pool_lock, flags); for (i = 0; i < NUM_URBS; ++i) { if (write_urb_pool[i]->status != -EINPROGRESS) { @@ -216,7 +221,7 @@ static int empeg_write (struct usb_serial_port *port, const unsigned char *buf, } } - spin_unlock_irqrestore (&write_urb_pool_lock, flags); + spin_unlock_irqrestore(&write_urb_pool_lock, flags); if (urb == NULL) { dbg("%s - no more free urbs", __func__); @@ -224,25 +229,27 @@ static int empeg_write (struct usb_serial_port *port, const unsigned char *buf, } if (urb->transfer_buffer == NULL) { - urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_ATOMIC); + urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_ATOMIC); if (urb->transfer_buffer == NULL) { - dev_err(&port->dev, "%s no more kernel memory...\n", __func__); + dev_err(&port->dev, + "%s no more kernel memory...\n", + __func__); goto exit; } } - transfer_size = min (count, URB_TRANSFER_BUFFER_SIZE); + transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE); - memcpy (urb->transfer_buffer, current_position, transfer_size); + memcpy(urb->transfer_buffer, current_position, transfer_size); usb_serial_debug_data(debug, &port->dev, __func__, transfer_size, urb->transfer_buffer); /* build up our urb */ - usb_fill_bulk_urb ( + usb_fill_bulk_urb( urb, serial->dev, usb_sndbulkpipe(serial->dev, - port->bulk_out_endpointAddress), + port->bulk_out_endpointAddress), urb->transfer_buffer, transfer_size, empeg_write_bulk_callback, @@ -262,66 +269,57 @@ static int empeg_write (struct usb_serial_port *port, const unsigned char *buf, bytes_out += transfer_size; } - exit: return bytes_sent; - -} +} -static int empeg_write_room (struct usb_serial_port *port) +static int empeg_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; unsigned long flags; int i; int room = 0; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave (&write_urb_pool_lock, flags); - + spin_lock_irqsave(&write_urb_pool_lock, flags); /* tally up the number of bytes available */ for (i = 0; i < NUM_URBS; ++i) { - if (write_urb_pool[i]->status != -EINPROGRESS) { + if (write_urb_pool[i]->status != -EINPROGRESS) room += URB_TRANSFER_BUFFER_SIZE; - } - } - - spin_unlock_irqrestore (&write_urb_pool_lock, flags); - + } + spin_unlock_irqrestore(&write_urb_pool_lock, flags); dbg("%s - returns %d", __func__, room); - - return (room); + return room; } -static int empeg_chars_in_buffer (struct usb_serial_port *port) +static int empeg_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; unsigned long flags; int i; int chars = 0; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave (&write_urb_pool_lock, flags); + spin_lock_irqsave(&write_urb_pool_lock, flags); /* tally up the number of bytes waiting */ for (i = 0; i < NUM_URBS; ++i) { - if (write_urb_pool[i]->status == -EINPROGRESS) { + if (write_urb_pool[i]->status == -EINPROGRESS) chars += URB_TRANSFER_BUFFER_SIZE; - } } - spin_unlock_irqrestore (&write_urb_pool_lock, flags); - + spin_unlock_irqrestore(&write_urb_pool_lock, flags); dbg("%s - returns %d", __func__, chars); - - return (chars); - + return chars; } -static void empeg_write_bulk_callback (struct urb *urb) +static void empeg_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; int status = urb->status; @@ -338,7 +336,7 @@ static void empeg_write_bulk_callback (struct urb *urb) } -static void empeg_read_bulk_callback (struct urb *urb) +static void empeg_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct tty_struct *tty; @@ -354,9 +352,9 @@ static void empeg_read_bulk_callback (struct urb *urb) return; } - usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - - tty = port->tty; + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, data); + tty = port->port.tty; if (urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); @@ -368,7 +366,7 @@ static void empeg_read_bulk_callback (struct urb *urb) /* Continue trying to always read */ usb_fill_bulk_urb( port->read_urb, - port->serial->dev, + port->serial->dev, usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), port->read_urb->transfer_buffer, @@ -379,38 +377,39 @@ static void empeg_read_bulk_callback (struct urb *urb) result = usb_submit_urb(port->read_urb, GFP_ATOMIC); if (result) - dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __func__, result); + dev_err(&urb->dev->dev, + "%s - failed resubmitting read urb, error %d\n", + __func__, result); return; } -static void empeg_throttle (struct usb_serial_port *port) +static void empeg_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); usb_kill_urb(port->read_urb); } -static void empeg_unthrottle (struct usb_serial_port *port) +static void empeg_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int result; - dbg("%s - port %d", __func__, port->number); port->read_urb->dev = port->serial->dev; - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __func__, result); - - return; + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", + __func__, result); } -static int empeg_startup (struct usb_serial *serial) +static int empeg_startup(struct usb_serial *serial) { int r; @@ -422,7 +421,7 @@ static int empeg_startup (struct usb_serial *serial) return -ENODEV; } dbg("%s - reset config", __func__); - r = usb_reset_configuration (serial->dev); + r = usb_reset_configuration(serial->dev); /* continue on with initialization */ return r; @@ -430,34 +429,27 @@ static int empeg_startup (struct usb_serial *serial) } -static void empeg_shutdown (struct usb_serial *serial) +static void empeg_shutdown(struct usb_serial *serial) { - dbg ("%s", __func__); -} - - -static int empeg_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) -{ - dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd); - - return -ENOIOCTLCMD; + dbg("%s", __func__); } -static void empeg_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) +static void empeg_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { - struct ktermios *termios = port->tty->termios; + struct ktermios *termios = tty->termios; dbg("%s - port %d", __func__, port->number); /* - * The empeg-car player wants these particular tty settings. - * You could, for example, change the baud rate, however the - * player only supports 115200 (currently), so there is really - * no point in support for changes to the tty settings. - * (at least for now) - * - * The default requirements for this device are: - */ + * The empeg-car player wants these particular tty settings. + * You could, for example, change the baud rate, however the + * player only supports 115200 (currently), so there is really + * no point in support for changes to the tty settings. + * (at least for now) + * + * The default requirements for this device are: + */ termios->c_iflag &= ~(IGNBRK /* disable ignore break */ | BRKINT /* disable break causes interrupt */ @@ -491,18 +483,18 @@ static void empeg_set_termios (struct usb_serial_port *port, struct ktermios *ol * this is bad as it opens up the possibility of dropping bytes * on the floor. We don't want to drop bytes on the floor. :) */ - port->tty->low_latency = 1; - tty_encode_baud_rate(port->tty, 115200, 115200); + tty->low_latency = 1; + tty_encode_baud_rate(tty, 115200, 115200); } -static int __init empeg_init (void) +static int __init empeg_init(void) { struct urb *urb; int i, retval; - /* create our write urb pool and transfer buffers */ - spin_lock_init (&write_urb_pool_lock); + /* create our write urb pool and transfer buffers */ + spin_lock_init(&write_urb_pool_lock); for (i = 0; i < NUM_URBS; ++i) { urb = usb_alloc_urb(0, GFP_KERNEL); write_urb_pool[i] = urb; @@ -511,9 +503,10 @@ static int __init empeg_init (void) continue; } - urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); + urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, + GFP_KERNEL); if (!urb->transfer_buffer) { - err("%s - out of memory for urb buffers.", + err("%s - out of memory for urb buffers.", __func__); continue; } @@ -542,36 +535,36 @@ failed_usb_serial_register: } -static void __exit empeg_exit (void) +static void __exit empeg_exit(void) { int i; unsigned long flags; usb_deregister(&empeg_driver); - usb_serial_deregister (&empeg_device); + usb_serial_deregister(&empeg_device); - spin_lock_irqsave (&write_urb_pool_lock, flags); + spin_lock_irqsave(&write_urb_pool_lock, flags); for (i = 0; i < NUM_URBS; ++i) { if (write_urb_pool[i]) { - /* FIXME - uncomment the following usb_kill_urb call when - * the host controllers get fixed to set urb->dev = NULL after - * the urb is finished. Otherwise this call oopses. */ + /* FIXME - uncomment the following usb_kill_urb call + * when the host controllers get fixed to set urb->dev + * = NULL after the urb is finished. Otherwise this + * call oopses. */ /* usb_kill_urb(write_urb_pool[i]); */ kfree(write_urb_pool[i]->transfer_buffer); - usb_free_urb (write_urb_pool[i]); + usb_free_urb(write_urb_pool[i]); } } - - spin_unlock_irqrestore (&write_urb_pool_lock, flags); + spin_unlock_irqrestore(&write_urb_pool_lock, flags); } module_init(empeg_init); module_exit(empeg_exit); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); diff --git a/drivers/usb/serial/ezusb.c b/drivers/usb/serial/ezusb.c index cc4fbd9d60be..711e84f6ed82 100644 --- a/drivers/usb/serial/ezusb.c +++ b/drivers/usb/serial/ezusb.c @@ -20,7 +20,8 @@ /* EZ-USB Control and Status Register. Bit 0 controls 8051 reset */ #define CPUCS_REG 0x7F92 -int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *data, int length, __u8 bRequest) +int ezusb_writememory(struct usb_serial *serial, int address, + unsigned char *data, int length, __u8 request) { int result; unsigned char *transfer_buffer; @@ -33,26 +34,27 @@ int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *da transfer_buffer = kmemdup(data, length, GFP_KERNEL); if (!transfer_buffer) { - dev_err(&serial->dev->dev, "%s - kmalloc(%d) failed.\n", __func__, length); + dev_err(&serial->dev->dev, "%s - kmalloc(%d) failed.\n", + __func__, length); return -ENOMEM; } - result = usb_control_msg (serial->dev, usb_sndctrlpipe(serial->dev, 0), bRequest, 0x40, address, 0, transfer_buffer, length, 3000); - kfree (transfer_buffer); + result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + request, 0x40, address, 0, transfer_buffer, length, 3000); + kfree(transfer_buffer); return result; } +EXPORT_SYMBOL_GPL(ezusb_writememory); -int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit) +int ezusb_set_reset(struct usb_serial *serial, unsigned char reset_bit) { int response; /* dbg("%s - %d", __func__, reset_bit); */ - response = ezusb_writememory (serial, CPUCS_REG, &reset_bit, 1, 0xa0); + response = ezusb_writememory(serial, CPUCS_REG, &reset_bit, 1, 0xa0); if (response < 0) - dev_err(&serial->dev->dev, "%s- %d failed\n", __func__, reset_bit); + dev_err(&serial->dev->dev, "%s- %d failed\n", + __func__, reset_bit); return response; } - - -EXPORT_SYMBOL_GPL(ezusb_writememory); EXPORT_SYMBOL_GPL(ezusb_set_reset); diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 0ff4a3971e45..838717250145 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -12,7 +12,8 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * See Documentation/usb/usb-serial.txt for more information on using this driver + * See Documentation/usb/usb-serial.txt for more information on using this + * driver * * See http://ftdi-usb-sio.sourceforge.net for upto date testing info * and extra documentation @@ -25,7 +26,8 @@ /* Bill Ryder - bryder@sgi.com - wrote the FTDI_SIO implementation */ /* Thanx to FTDI for so kindly providing details of the protocol required */ /* to talk to the device */ -/* Thanx to gkh and the rest of the usb dev group for all code I have assimilated :-) */ +/* Thanx to gkh and the rest of the usb dev group for all code I have + assimilated :-) */ #include <linux/kernel.h> #include <linux/errno.h> @@ -36,7 +38,7 @@ #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/serial.h> #include <linux/usb/serial.h> @@ -55,17 +57,22 @@ static __u16 product; struct ftdi_private { ftdi_chip_type_t chip_type; - /* type of the device, either SIO or FT8U232AM */ + /* type of device, either SIO or FT8U232AM */ int baud_base; /* baud base clock for divisor setting */ - int custom_divisor; /* custom_divisor kludge, this is for baud_base (different from what goes to the chip!) */ + int custom_divisor; /* custom_divisor kludge, this is for + baud_base (different from what goes to the + chip!) */ __u16 last_set_data_urb_value ; - /* the last data state set - needed for doing a break */ - int write_offset; /* This is the offset in the usb data block to write the serial data - - * it is different between devices + /* the last data state set - needed for doing + * a break + */ + int write_offset; /* This is the offset in the usb data block to + * write the serial data - it varies between + * devices */ int flags; /* some ASYNC_xxxx flags are supported */ unsigned long last_dtr_rts; /* saved modem control outputs */ - wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */ + wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */ char prev_status, diff_status; /* Used for TIOCMIWAIT */ __u8 rx_flags; /* receive state flags (throttling) */ spinlock_t rx_lock; /* spinlock for receive state */ @@ -76,8 +83,10 @@ struct ftdi_private { __u16 interface; /* FT2232C port interface (0 for FT232/245) */ - speed_t force_baud; /* if non-zero, force the baud rate to this value */ - int force_rtscts; /* if non-zero, force RTS-CTS to always be enabled */ + speed_t force_baud; /* if non-zero, force the baud rate to + this value */ + int force_rtscts; /* if non-zero, force RTS-CTS to always + be enabled */ spinlock_t tx_lock; /* spinlock for transmit state */ unsigned long tx_bytes; @@ -88,13 +97,14 @@ struct ftdi_private { /* struct ftdi_sio_quirk is used by devices requiring special attention. */ struct ftdi_sio_quirk { int (*probe)(struct usb_serial *); - void (*port_probe)(struct ftdi_private *); /* Special settings for probed ports. */ + /* Special settings for probed ports. */ + void (*port_probe)(struct ftdi_private *); }; -static int ftdi_jtag_probe (struct usb_serial *serial); -static int ftdi_mtxorb_hack_setup (struct usb_serial *serial); -static void ftdi_USB_UIRT_setup (struct ftdi_private *priv); -static void ftdi_HE_TIRA1_setup (struct ftdi_private *priv); +static int ftdi_jtag_probe(struct usb_serial *serial); +static int ftdi_mtxorb_hack_setup(struct usb_serial *serial); +static void ftdi_USB_UIRT_setup(struct ftdi_private *priv); +static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv); static struct ftdi_sio_quirk ftdi_jtag_quirk = { .probe = ftdi_jtag_probe, @@ -174,270 +184,270 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_MTXORB_4_PID) }, { USB_DEVICE(FTDI_VID, FTDI_MTXORB_5_PID) }, { USB_DEVICE(FTDI_VID, FTDI_MTXORB_6_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0100_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0101_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0102_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0103_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0104_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0105_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0106_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0107_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0108_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0109_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_010A_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_010B_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_010C_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_010D_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_010E_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_010F_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0110_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0111_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0112_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0113_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0114_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0115_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0116_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0117_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0118_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0119_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_011A_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_011B_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_011C_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_011D_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_011E_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_011F_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0120_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0121_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0122_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0123_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0124_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0125_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0126_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0127_PID), + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0100_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0101_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0102_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0103_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0104_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0105_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0106_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0107_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0108_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0109_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0110_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0111_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0112_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0113_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0114_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0115_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0116_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0117_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0118_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0119_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0120_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0121_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0122_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0123_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0124_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0125_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0126_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0127_PID), .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0128_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0129_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_012A_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_012B_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_012C_PID), + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0128_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0129_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012C_PID), .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_012D_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_012E_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_012F_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0130_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0131_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0132_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0133_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0134_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0135_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0136_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0137_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0138_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0139_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_013A_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_013B_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_013C_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_013D_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_013E_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_013F_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0140_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0141_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0142_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0143_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0144_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0145_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0146_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0147_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0148_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0149_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_014A_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_014B_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_014C_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_014D_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_014E_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_014F_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0150_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0151_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0152_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0153_PID), + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0130_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0131_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0132_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0133_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0134_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0135_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0136_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0137_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0138_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0139_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0140_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0141_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0142_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0143_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0144_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0145_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0146_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0147_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0148_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0149_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0150_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0151_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0152_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0153_PID), .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0154_PID), + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0154_PID), .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0155_PID), + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0155_PID), .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0156_PID), + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0156_PID), .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0157_PID), + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0157_PID), .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0158_PID), + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0158_PID), .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0159_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_015A_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_015B_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_015C_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_015D_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_015E_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_015F_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0160_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0161_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0162_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0163_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0164_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0165_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0166_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0167_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0168_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0169_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_016A_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_016B_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_016C_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_016D_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_016E_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_016F_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0170_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0171_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0172_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0173_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0174_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0175_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0176_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0177_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0178_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0179_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_017A_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_017B_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_017C_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_017D_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_017E_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_017F_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0180_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0181_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0182_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0183_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0184_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0185_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0186_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0187_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0188_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0189_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_018A_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_018B_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_018C_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_018D_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_018E_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_018F_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0190_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0191_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0192_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0193_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0194_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0195_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0196_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0197_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0198_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_0199_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_019A_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_019B_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_019C_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_019D_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_019E_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_019F_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01A0_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01A1_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01A2_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01A3_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01A4_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01A5_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01A6_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01A7_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01A8_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01A9_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01AA_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01AB_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01AC_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01AD_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01AE_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01AF_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01B0_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01B1_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01B2_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01B3_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01B4_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01B5_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01B6_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01B7_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01B8_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01B9_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01BA_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01BB_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01BC_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01BD_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01BE_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01BF_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01C0_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01C1_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01C2_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01C3_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01C4_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01C5_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01C6_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01C7_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01C8_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01C9_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01CA_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01CB_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01CC_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01CD_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01CE_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01CF_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01D0_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01D1_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01D2_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01D3_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01D4_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01D5_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01D6_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01D7_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01D8_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01D9_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01DA_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01DB_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01DC_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01DD_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01DE_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01DF_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01E0_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01E1_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01E2_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01E3_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01E4_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01E5_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01E6_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01E7_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01E8_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01E9_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01EA_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01EB_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01EC_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01ED_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01EE_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01EF_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01F0_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01F1_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01F2_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01F3_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01F4_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01F5_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01F6_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01F7_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01F8_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01F9_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01FA_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01FB_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01FC_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01FD_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01FE_PID) }, - { USB_DEVICE(MTXORB_VID,MTXORB_FTDI_RANGE_01FF_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0159_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0160_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0161_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0162_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0163_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0164_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0165_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0166_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0167_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0168_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0169_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0170_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0171_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0172_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0173_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0174_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0175_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0176_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0177_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0178_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0179_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0180_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0181_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0182_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0183_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0184_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0185_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0186_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0187_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0188_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0189_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0190_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0191_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0192_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0193_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0194_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0195_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0196_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0197_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0198_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0199_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A0_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A1_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A2_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A3_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A4_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A5_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A6_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A7_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A8_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A9_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AA_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AB_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AC_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AD_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AE_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AF_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B0_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B1_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B2_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B3_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B4_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B5_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B6_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B7_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B8_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B9_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BA_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BB_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BC_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BD_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BE_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BF_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C0_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C1_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C2_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C3_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C4_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C5_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C6_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C7_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C8_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C9_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CA_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CB_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CC_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CD_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CE_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CF_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D0_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D1_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D2_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D3_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D4_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D5_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D6_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D7_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D8_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D9_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DA_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DB_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DC_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DD_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DE_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DF_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E0_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E1_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E2_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E3_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E4_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E5_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E6_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E7_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E8_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E9_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EA_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EB_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EC_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01ED_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EE_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EF_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F0_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F1_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F2_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F3_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F4_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F5_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F6_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F7_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F8_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F9_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FA_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FB_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FC_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FD_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FE_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FF_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PIEGROUP_PID) }, { USB_DEVICE(FTDI_VID, FTDI_TNC_X_PID) }, @@ -642,7 +652,7 @@ static struct usb_device_id id_table_combined [] = { { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table_combined); +MODULE_DEVICE_TABLE(usb, id_table_combined); static struct usb_driver ftdi_driver = { .name = "ftdi_sio", @@ -678,30 +688,37 @@ static const char *ftdi_chip_name[] = { | ASYNC_SPD_CUST | ASYNC_SPD_SHI | ASYNC_SPD_WARP) /* function prototypes for a FTDI serial converter */ -static int ftdi_sio_probe (struct usb_serial *serial, const struct usb_device_id *id); -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); -static int ftdi_write_room (struct usb_serial_port *port); -static int ftdi_chars_in_buffer (struct usb_serial_port *port); -static void ftdi_write_bulk_callback (struct urb *urb); -static void ftdi_read_bulk_callback (struct urb *urb); -static void ftdi_process_read (struct work_struct *work); -static void ftdi_set_termios (struct usb_serial_port *port, struct ktermios * old); -static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file); -static int ftdi_tiocmset (struct usb_serial_port *port, struct file * file, unsigned int set, unsigned int clear); -static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); -static void ftdi_break_ctl (struct usb_serial_port *port, int break_state ); -static void ftdi_throttle (struct usb_serial_port *port); -static void ftdi_unthrottle (struct usb_serial_port *port); - -static unsigned short int ftdi_232am_baud_base_to_divisor (int baud, int base); -static unsigned short int ftdi_232am_baud_to_divisor (int baud); -static __u32 ftdi_232bm_baud_base_to_divisor (int baud, int base); -static __u32 ftdi_232bm_baud_to_divisor (int baud); +static int ftdi_sio_probe(struct usb_serial *serial, + const struct usb_device_id *id); +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 tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void ftdi_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); +static int ftdi_write_room(struct tty_struct *tty); +static int ftdi_chars_in_buffer(struct tty_struct *tty); +static void ftdi_write_bulk_callback(struct urb *urb); +static void ftdi_read_bulk_callback(struct urb *urb); +static void ftdi_process_read(struct work_struct *work); +static void ftdi_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); +static int ftdi_tiocmget(struct tty_struct *tty, struct file *file); +static int ftdi_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear); +static int ftdi_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg); +static void ftdi_break_ctl(struct tty_struct *tty, int break_state); +static void ftdi_throttle(struct tty_struct *tty); +static void ftdi_unthrottle(struct tty_struct *tty); + +static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base); +static unsigned short int ftdi_232am_baud_to_divisor(int baud); +static __u32 ftdi_232bm_baud_base_to_divisor(int baud, int base); +static __u32 ftdi_232bm_baud_to_divisor(int baud); static struct usb_serial_driver ftdi_sio_device = { .driver = { @@ -752,44 +769,54 @@ static struct usb_serial_driver ftdi_sio_device = { static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base) { unsigned short int divisor; - int divisor3 = base / 2 / baud; // divisor shifted 3 bits to the left - if ((divisor3 & 0x7) == 7) divisor3 ++; // round x.7/8 up to x+1 + /* divisor shifted 3 bits to the left */ + int divisor3 = base / 2 / baud; + if ((divisor3 & 0x7) == 7) + divisor3++; /* round x.7/8 up to x+1 */ divisor = divisor3 >> 3; divisor3 &= 0x7; - if (divisor3 == 1) divisor |= 0xc000; else // 0.125 - if (divisor3 >= 4) divisor |= 0x4000; else // 0.5 - if (divisor3 != 0) divisor |= 0x8000; // 0.25 - if (divisor == 1) divisor = 0; /* special case for maximum baud rate */ + if (divisor3 == 1) + divisor |= 0xc000; + else if (divisor3 >= 4) + divisor |= 0x4000; + else if (divisor3 != 0) + divisor |= 0x8000; + else if (divisor == 1) + divisor = 0; /* special case for maximum baud rate */ return divisor; } static unsigned short int ftdi_232am_baud_to_divisor(int baud) { - return(ftdi_232am_baud_base_to_divisor(baud, 48000000)); + return ftdi_232am_baud_base_to_divisor(baud, 48000000); } static __u32 ftdi_232bm_baud_base_to_divisor(int baud, int base) { static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 }; __u32 divisor; - int divisor3 = base / 2 / baud; // divisor shifted 3 bits to the left + /* divisor shifted 3 bits to the left */ + int divisor3 = base / 2 / baud; divisor = divisor3 >> 3; divisor |= (__u32)divfrac[divisor3 & 0x7] << 14; /* Deal with special cases for highest baud rates. */ - if (divisor == 1) divisor = 0; else // 1.0 - if (divisor == 0x4001) divisor = 1; // 1.5 + if (divisor == 1) + divisor = 0; + else if (divisor == 0x4001) + divisor = 1; return divisor; } static __u32 ftdi_232bm_baud_to_divisor(int baud) { - return(ftdi_232bm_baud_base_to_divisor(baud, 48000000)); + return ftdi_232bm_baud_base_to_divisor(baud, 48000000); } #define set_mctrl(port, set) update_mctrl((port), (set), 0) #define clear_mctrl(port, clear) update_mctrl((port), 0, (clear)) -static int update_mctrl(struct usb_serial_port *port, unsigned int set, unsigned int clear) +static int update_mctrl(struct usb_serial_port *port, unsigned int set, + unsigned int clear) { struct ftdi_private *priv = usb_get_serial_port_data(port); char *buf; @@ -843,42 +870,8 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set, unsigned } -static __u32 get_ftdi_divisor(struct usb_serial_port * port); - - -static int change_speed(struct usb_serial_port *port) -{ - struct ftdi_private *priv = usb_get_serial_port_data(port); - char *buf; - __u16 urb_value; - __u16 urb_index; - __u32 urb_index_value; - int rv; - - buf = kmalloc(1, GFP_NOIO); - if (!buf) - return -ENOMEM; - - urb_index_value = get_ftdi_divisor(port); - urb_value = (__u16)urb_index_value; - urb_index = (__u16)(urb_index_value >> 16); - if (priv->interface) { /* FT2232C */ - urb_index = (__u16)((urb_index << 8) | priv->interface); - } - - rv = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - FTDI_SIO_SET_BAUDRATE_REQUEST, - FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE, - urb_value, urb_index, - buf, 0, WDR_SHORT_TIMEOUT); - - kfree(buf); - return rv; -} - - -static __u32 get_ftdi_divisor(struct usb_serial_port * port) +static __u32 get_ftdi_divisor(struct tty_struct *tty, + struct usb_serial_port *port) { /* get_ftdi_divisor */ struct ftdi_private *priv = usb_get_serial_port_data(port); __u32 div_value = 0; @@ -886,48 +879,56 @@ static __u32 get_ftdi_divisor(struct usb_serial_port * port) int baud; /* - * The logic involved in setting the baudrate can be cleanly split in 3 steps. - * Obtaining the actual baud rate is a little tricky since unix traditionally - * somehow ignored the possibility to set non-standard baud rates. + * The logic involved in setting the baudrate can be cleanly split into + * 3 steps. * 1. Standard baud rates are set in tty->termios->c_cflag - * 2. If these are not enough, you can set any speed using alt_speed as follows: + * 2. If these are not enough, you can set any speed using alt_speed as + * follows: * - set tty->termios->c_cflag speed to B38400 * - set your real speed in tty->alt_speed; it gets ignored when * alt_speed==0, (or) - * - call TIOCSSERIAL ioctl with (struct serial_struct) set as follows: - * flags & ASYNC_SPD_MASK == ASYNC_SPD_[HI, VHI, SHI, WARP], this just - * sets alt_speed to (HI: 57600, VHI: 115200, SHI: 230400, WARP: 460800) + * - call TIOCSSERIAL ioctl with (struct serial_struct) set as + * follows: + * flags & ASYNC_SPD_MASK == ASYNC_SPD_[HI, VHI, SHI, WARP], + * this just sets alt_speed to (HI: 57600, VHI: 115200, + * SHI: 230400, WARP: 460800) * ** Steps 1, 2 are done courtesy of tty_get_baud_rate * 3. You can also set baud rate by setting custom divisor as follows * - set tty->termios->c_cflag speed to B38400 - * - call TIOCSSERIAL ioctl with (struct serial_struct) set as follows: + * - call TIOCSSERIAL ioctl with (struct serial_struct) set as + * follows: * o flags & ASYNC_SPD_MASK == ASYNC_SPD_CUST * o custom_divisor set to baud_base / your_new_baudrate - * ** Step 3 is done courtesy of code borrowed from serial.c - I should really - * spend some time and separate+move this common code to serial.c, it is - * replicated in nearly every serial driver you see. + * ** Step 3 is done courtesy of code borrowed from serial.c + * I should really spend some time and separate + move this common + * code to serial.c, it is replicated in nearly every serial driver + * you see. */ - /* 1. Get the baud rate from the tty settings, this observes alt_speed hack */ + /* 1. Get the baud rate from the tty settings, this observes + alt_speed hack */ - baud = tty_get_baud_rate(port->tty); + baud = tty_get_baud_rate(tty); dbg("%s - tty_get_baud_rate reports speed %d", __func__, baud); - /* 2. Observe async-compatible custom_divisor hack, update baudrate if needed */ + /* 2. Observe async-compatible custom_divisor hack, update baudrate + if needed */ if (baud == 38400 && ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) && (priv->custom_divisor)) { baud = priv->baud_base / priv->custom_divisor; - dbg("%s - custom divisor %d sets baud rate to %d", __func__, priv->custom_divisor, baud); + dbg("%s - custom divisor %d sets baud rate to %d", + __func__, priv->custom_divisor, baud); } /* 3. Convert baudrate to device-specific divisor */ - if (!baud) baud = 9600; - switch(priv->chip_type) { + if (!baud) + baud = 9600; + switch (priv->chip_type) { case SIO: /* SIO chip */ - switch(baud) { + switch (baud) { case 300: div_value = ftdi_sio_b300; break; case 600: div_value = ftdi_sio_b600; break; case 1200: div_value = ftdi_sio_b1200; break; @@ -940,7 +941,8 @@ static __u32 get_ftdi_divisor(struct usb_serial_port * port) case 115200: div_value = ftdi_sio_b115200; break; } /* baud */ if (div_value == 0) { - dbg("%s - Baudrate (%d) requested is not supported", __func__, baud); + dbg("%s - Baudrate (%d) requested is not supported", + __func__, baud); div_value = ftdi_sio_b9600; baud = 9600; div_okay = 0; @@ -950,7 +952,7 @@ static __u32 get_ftdi_divisor(struct usb_serial_port * port) if (baud <= 3000000) { div_value = ftdi_232am_baud_to_divisor(baud); } else { - dbg("%s - Baud rate too high!", __func__); + dbg("%s - Baud rate too high!", __func__); baud = 9600; div_value = ftdi_232am_baud_to_divisor(9600); div_okay = 0; @@ -962,7 +964,7 @@ static __u32 get_ftdi_divisor(struct usb_serial_port * port) if (baud <= 3000000) { div_value = ftdi_232bm_baud_to_divisor(baud); } else { - dbg("%s - Baud rate too high!", __func__); + dbg("%s - Baud rate too high!", __func__); div_value = ftdi_232bm_baud_to_divisor(9600); div_okay = 0; baud = 9600; @@ -976,12 +978,45 @@ static __u32 get_ftdi_divisor(struct usb_serial_port * port) ftdi_chip_name[priv->chip_type]); } - tty_encode_baud_rate(port->tty, baud, baud); - return(div_value); + tty_encode_baud_rate(tty, baud, baud); + return div_value; +} + +static int change_speed(struct tty_struct *tty, struct usb_serial_port *port) +{ + struct ftdi_private *priv = usb_get_serial_port_data(port); + char *buf; + __u16 urb_value; + __u16 urb_index; + __u32 urb_index_value; + int rv; + + buf = kmalloc(1, GFP_NOIO); + if (!buf) + return -ENOMEM; + + urb_index_value = get_ftdi_divisor(tty, port); + urb_value = (__u16)urb_index_value; + urb_index = (__u16)(urb_index_value >> 16); + if (priv->interface) { /* FT2232C */ + urb_index = (__u16)((urb_index << 8) | priv->interface); + } + + rv = usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + FTDI_SIO_SET_BAUDRATE_REQUEST, + FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE, + urb_value, urb_index, + buf, 0, WDR_SHORT_TIMEOUT); + + kfree(buf); + return rv; } -static int get_serial_info(struct usb_serial_port * port, struct serial_struct __user * retinfo) + +static int get_serial_info(struct usb_serial_port *port, + struct serial_struct __user *retinfo) { struct ftdi_private *priv = usb_get_serial_port_data(port); struct serial_struct tmp; @@ -998,7 +1033,8 @@ static int get_serial_info(struct usb_serial_port * port, struct serial_struct _ } /* get_serial_info */ -static int set_serial_info(struct usb_serial_port * port, struct serial_struct __user * newinfo) +static int set_serial_info(struct tty_struct *tty, + struct usb_serial_port *port, struct serial_struct __user *newinfo) { /* set_serial_info */ struct ftdi_private *priv = usb_get_serial_port_data(port); struct serial_struct new_serial; @@ -1006,7 +1042,7 @@ static int set_serial_info(struct usb_serial_port * port, struct serial_struct _ if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) return -EFAULT; - old_priv = * priv; + old_priv = *priv; /* Do error checking and permission checking */ @@ -1027,33 +1063,32 @@ static int set_serial_info(struct usb_serial_port * port, struct serial_struct _ /* Make the changes - these are privileged changes! */ priv->flags = ((priv->flags & ~ASYNC_FLAGS) | - (new_serial.flags & ASYNC_FLAGS)); + (new_serial.flags & ASYNC_FLAGS)); priv->custom_divisor = new_serial.custom_divisor; - port->tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0; check_and_exit: if ((old_priv.flags & ASYNC_SPD_MASK) != (priv->flags & ASYNC_SPD_MASK)) { if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - port->tty->alt_speed = 57600; + tty->alt_speed = 57600; else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - port->tty->alt_speed = 115200; + tty->alt_speed = 115200; else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - port->tty->alt_speed = 230400; + tty->alt_speed = 230400; else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - port->tty->alt_speed = 460800; + tty->alt_speed = 460800; else - port->tty->alt_speed = 0; + tty->alt_speed = 0; } if (((old_priv.flags & ASYNC_SPD_MASK) != (priv->flags & ASYNC_SPD_MASK)) || (((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) && (old_priv.custom_divisor != priv->custom_divisor))) { - change_speed(port); + change_speed(tty, port); } - - return (0); + return 0; } /* set_serial_info */ @@ -1082,11 +1117,10 @@ static void ftdi_determine_type(struct usb_serial_port *port) priv->chip_type = FT2232C; /* Determine interface code. */ inter = serial->interface->altsetting->desc.bInterfaceNumber; - if (inter == 0) { + if (inter == 0) priv->interface = PIT_SIOA; - } else { + else priv->interface = PIT_SIOB; - } /* BM-type devices have a bug where bcdDevice gets set * to 0x200 when iSerialNumber is 0. */ if (version < 0x500) { @@ -1120,7 +1154,8 @@ static void ftdi_determine_type(struct usb_serial_port *port) * *************************************************************************** */ -static ssize_t show_latency_timer(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_latency_timer(struct device *dev, + struct device_attribute *attr, char *buf) { struct usb_serial_port *port = to_usb_serial_port(dev); struct ftdi_private *priv = usb_get_serial_port_data(port); @@ -1129,14 +1164,14 @@ static ssize_t show_latency_timer(struct device *dev, struct device_attribute *a int rv = 0; - dbg("%s",__func__); + dbg("%s", __func__); rv = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), FTDI_SIO_GET_LATENCY_TIMER_REQUEST, FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE, 0, priv->interface, - (char*) &latency, 1, WDR_TIMEOUT); + (char *) &latency, 1, WDR_TIMEOUT); if (rv < 0) { dev_err(dev, "Unable to read latency timer: %i\n", rv); @@ -1146,8 +1181,9 @@ static ssize_t show_latency_timer(struct device *dev, struct device_attribute *a } /* Write a new value of the latency timer, in units of milliseconds. */ -static ssize_t store_latency_timer(struct device *dev, struct device_attribute *attr, const char *valbuf, - size_t count) +static ssize_t store_latency_timer(struct device *dev, + struct device_attribute *attr, const char *valbuf, + size_t count) { struct usb_serial_port *port = to_usb_serial_port(dev); struct ftdi_private *priv = usb_get_serial_port_data(port); @@ -1175,8 +1211,8 @@ static ssize_t store_latency_timer(struct device *dev, struct device_attribute * /* Write an event character directly to the FTDI register. The ASCII value is in the low 8 bits, with the enable bit in the 9th bit. */ -static ssize_t store_event_char(struct device *dev, struct device_attribute *attr, const char *valbuf, - size_t count) +static ssize_t store_event_char(struct device *dev, + struct device_attribute *attr, const char *valbuf, size_t count) { struct usb_serial_port *port = to_usb_serial_port(dev); struct ftdi_private *priv = usb_get_serial_port_data(port); @@ -1202,7 +1238,8 @@ static ssize_t store_event_char(struct device *dev, struct device_attribute *att return count; } -static DEVICE_ATTR(latency_timer, S_IWUSR | S_IRUGO, show_latency_timer, store_latency_timer); +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_port *port) @@ -1210,7 +1247,7 @@ static int create_sysfs_attrs(struct usb_serial_port *port) struct ftdi_private *priv = usb_get_serial_port_data(port); int retval = 0; - dbg("%s",__func__); + dbg("%s", __func__); /* XXX I've no idea if the original SIO supports the event_char * sysfs parameter, so I'm playing it safe. */ @@ -1232,7 +1269,7 @@ static void remove_sysfs_attrs(struct usb_serial_port *port) { struct ftdi_private *priv = usb_get_serial_port_data(port); - dbg("%s",__func__); + dbg("%s", __func__); /* XXX see create_sysfs_attrs */ if (priv->chip_type != SIO) { @@ -1253,9 +1290,11 @@ static void remove_sysfs_attrs(struct usb_serial_port *port) */ /* Probe function to check for special devices */ -static int ftdi_sio_probe (struct usb_serial *serial, const struct usb_device_id *id) +static int ftdi_sio_probe(struct usb_serial *serial, + const struct usb_device_id *id) { - struct ftdi_sio_quirk *quirk = (struct ftdi_sio_quirk *)id->driver_info; + struct ftdi_sio_quirk *quirk = + (struct ftdi_sio_quirk *)id->driver_info; if (quirk && quirk->probe) { int ret = quirk->probe(serial); @@ -1274,17 +1313,18 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) struct ftdi_sio_quirk *quirk = usb_get_serial_data(port->serial); - dbg("%s",__func__); + dbg("%s", __func__); priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL); - if (!priv){ - err("%s- kmalloc(%Zd) failed.", __func__, sizeof(struct ftdi_private)); + if (!priv) { + err("%s- kmalloc(%Zd) failed.", __func__, + sizeof(struct ftdi_private)); return -ENOMEM; } spin_lock_init(&priv->rx_lock); spin_lock_init(&priv->tx_lock); - init_waitqueue_head(&priv->delta_msr_wait); + init_waitqueue_head(&priv->delta_msr_wait); /* This will push the characters through immediately rather than queue a task to deliver them */ priv->flags = ASYNC_LOW_LATENCY; @@ -1294,9 +1334,9 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) /* Increase the size of read buffers */ kfree(port->bulk_in_buffer); - port->bulk_in_buffer = kmalloc (BUFSZ, GFP_KERNEL); + port->bulk_in_buffer = kmalloc(BUFSZ, GFP_KERNEL); if (!port->bulk_in_buffer) { - kfree (priv); + kfree(priv); return -ENOMEM; } if (port->read_urb) { @@ -1309,7 +1349,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) /* Free port's existing write urb and transfer buffer. */ if (port->write_urb) { - usb_free_urb (port->write_urb); + usb_free_urb(port->write_urb); port->write_urb = NULL; } kfree(port->bulk_out_buffer); @@ -1317,7 +1357,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) usb_set_serial_port_data(port, priv); - ftdi_determine_type (port); + ftdi_determine_type(port); create_sysfs_attrs(port); return 0; } @@ -1325,9 +1365,9 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) /* Setup for the USB-UIRT device, which requires hardwired * baudrate (38400 gets mapped to 312500) */ /* Called from usbserial:serial_probe */ -static void ftdi_USB_UIRT_setup (struct ftdi_private *priv) +static void ftdi_USB_UIRT_setup(struct ftdi_private *priv) { - dbg("%s",__func__); + dbg("%s", __func__); priv->flags |= ASYNC_SPD_CUST; priv->custom_divisor = 77; @@ -1336,9 +1376,10 @@ static void ftdi_USB_UIRT_setup (struct ftdi_private *priv) /* Setup for the HE-TIRA1 device, which requires hardwired * baudrate (38400 gets mapped to 100000) and RTS-CTS enabled. */ -static void ftdi_HE_TIRA1_setup (struct ftdi_private *priv) + +static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv) { - dbg("%s",__func__); + dbg("%s", __func__); priv->flags |= ASYNC_SPD_CUST; priv->custom_divisor = 240; @@ -1356,7 +1397,7 @@ static int ftdi_jtag_probe(struct usb_serial *serial) struct usb_device *udev = serial->dev; struct usb_interface *interface = serial->interface; - dbg("%s",__func__); + dbg("%s", __func__); if (interface == udev->actconfig->interface[0]) { info("Ignoring serial port reserved for JTAG"); @@ -1390,7 +1431,7 @@ static int ftdi_mtxorb_hack_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) +static void ftdi_shutdown(struct usb_serial *serial) { dbg("%s", __func__); } @@ -1404,7 +1445,7 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port) remove_sysfs_attrs(port); /* all open ports are closed at this point - * (by usbserial.c:__serial_close, which calls ftdi_close) + * (by usbserial.c:__serial_close, which calls ftdi_close) */ if (priv) { @@ -1415,7 +1456,8 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port) return 0; } -static int ftdi_open (struct usb_serial_port *port, struct file *filp) +static int ftdi_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { /* ftdi_open */ struct usb_device *dev = port->serial->dev; struct ftdi_private *priv = usb_get_serial_port_data(port); @@ -1433,8 +1475,8 @@ static int ftdi_open (struct usb_serial_port *port, struct file *filp) priv->rx_bytes = 0; spin_unlock_irqrestore(&priv->rx_lock, flags); - if (port->tty) - port->tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + if (tty) + tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0; /* No error checking for this (will get errors later anyway) */ /* See ftdi_sio.h for description of what is reset */ @@ -1448,8 +1490,8 @@ static int ftdi_open (struct usb_serial_port *port, struct file *filp) This is same behaviour as serial.c/rs_open() - Kuba */ /* ftdi_set_termios will send usb control messages */ - if (port->tty) - ftdi_set_termios(port, port->tty->termios); + if (tty) + ftdi_set_termios(tty, port, tty->termios); /* FIXME: Flow control might be enabled, so it should be checked - we have no control of defaults! */ @@ -1464,12 +1506,14 @@ static int ftdi_open (struct usb_serial_port *port, struct file *filp) /* Start reading from the device */ priv->rx_processed = 0; usb_fill_bulk_urb(port->read_urb, dev, - usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, - ftdi_read_bulk_callback, port); + usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, + port->read_urb->transfer_buffer_length, + ftdi_read_bulk_callback, port); result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result) - err("%s - failed submitting read urb, error %d", __func__, result); + err("%s - failed submitting read urb, error %d", + __func__, result); return result; @@ -1485,16 +1529,17 @@ 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 void ftdi_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { /* ftdi_close */ - unsigned int c_cflag = port->tty->termios->c_cflag; + unsigned int c_cflag = tty->termios->c_cflag; struct ftdi_private *priv = usb_get_serial_port_data(port); char buf[1]; dbg("%s", __func__); mutex_lock(&port->serial->disc_mutex); - if (c_cflag & HUPCL && !port->serial->disconnected){ + if (c_cflag & HUPCL && !port->serial->disconnected) { /* Disable flow control */ if (usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0), @@ -1527,7 +1572,7 @@ static void ftdi_close (struct usb_serial_port *port, struct file *filp) * * The new devices do not require this byte */ -static int ftdi_write (struct usb_serial_port *port, +static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { /* ftdi_write */ struct ftdi_private *priv = usb_get_serial_port_data(port); @@ -1554,7 +1599,7 @@ static int ftdi_write (struct usb_serial_port *port, spin_unlock_irqrestore(&priv->tx_lock, flags); data_offset = priv->write_offset; - dbg("data_offset set to %d",data_offset); + dbg("data_offset set to %d", data_offset); /* Determine total transfer size */ transfer_size = count; @@ -1565,7 +1610,7 @@ static int ftdi_write (struct usb_serial_port *port, (PKTSZ - data_offset))); } - buffer = kmalloc (transfer_size, GFP_ATOMIC); + buffer = kmalloc(transfer_size, GFP_ATOMIC); if (!buffer) { err("%s ran out of kernel memory for urb ...", __func__); count = -ENOMEM; @@ -1581,20 +1626,20 @@ static int ftdi_write (struct usb_serial_port *port, /* Copy data */ if (data_offset > 0) { - /* Original sio requires control byte at start of each packet. */ + /* Original sio requires control byte at start of + each packet. */ int user_pktsz = PKTSZ - data_offset; int todo = count; unsigned char *first_byte = buffer; const unsigned char *current_position = buf; while (todo > 0) { - if (user_pktsz > todo) { + if (user_pktsz > todo) user_pktsz = todo; - } /* Write the control byte at the front of the packet*/ *first_byte = 1 | ((user_pktsz) << 2); /* Copy data for packet */ - memcpy (first_byte + data_offset, + memcpy(first_byte + data_offset, current_position, user_pktsz); first_byte += user_pktsz + data_offset; current_position += user_pktsz; @@ -1603,20 +1648,23 @@ static int ftdi_write (struct usb_serial_port *port, } else { /* No control byte required. */ /* Copy in the data to send */ - memcpy (buffer, buf, count); + memcpy(buffer, buf, count); } - usb_serial_debug_data(debug, &port->dev, __func__, transfer_size, buffer); + usb_serial_debug_data(debug, &port->dev, __func__, + transfer_size, buffer); /* fill the buffer and send it */ usb_fill_bulk_urb(urb, port->serial->dev, - usb_sndbulkpipe(port->serial->dev, port->bulk_out_endpointAddress), - buffer, transfer_size, - ftdi_write_bulk_callback, port); + usb_sndbulkpipe(port->serial->dev, + port->bulk_out_endpointAddress), + buffer, transfer_size, + ftdi_write_bulk_callback, port); status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { - err("%s - failed submitting write urb, error %d", __func__, status); + err("%s - failed submitting write urb, error %d", + __func__, status); count = status; goto error; } else { @@ -1635,7 +1683,7 @@ static int ftdi_write (struct usb_serial_port *port, error: usb_free_urb(urb); error_no_urb: - kfree (buffer); + kfree(buffer); error_no_buffer: spin_lock_irqsave(&priv->tx_lock, flags); priv->tx_outstanding_urbs--; @@ -1646,7 +1694,7 @@ error_no_buffer: /* This function may get called when the device is closed */ -static void ftdi_write_bulk_callback (struct urb *urb) +static void ftdi_write_bulk_callback(struct urb *urb) { unsigned long flags; struct usb_serial_port *port = urb->context; @@ -1656,7 +1704,7 @@ static void ftdi_write_bulk_callback (struct urb *urb) int status = urb->status; /* free up the transfer buffer, as usb_free_urb() does not do this */ - kfree (urb->transfer_buffer); + kfree(urb->transfer_buffer); dbg("%s - port %d", __func__, port->number); @@ -1686,8 +1734,9 @@ static void ftdi_write_bulk_callback (struct urb *urb) } /* ftdi_write_bulk_callback */ -static int ftdi_write_room( struct usb_serial_port *port ) +static int ftdi_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); int room; unsigned long flags; @@ -1707,11 +1756,11 @@ static int ftdi_write_room( struct usb_serial_port *port ) } spin_unlock_irqrestore(&priv->tx_lock, flags); return room; -} /* ftdi_write_room */ - +} -static int ftdi_chars_in_buffer (struct usb_serial_port *port) -{ /* ftdi_chars_in_buffer */ +static int ftdi_chars_in_buffer(struct tty_struct *tty) +{ + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); int buffered; unsigned long flags; @@ -1726,12 +1775,10 @@ static int ftdi_chars_in_buffer (struct usb_serial_port *port) buffered = 0; } return buffered; -} /* ftdi_chars_in_buffer */ - - +} -static void ftdi_read_bulk_callback (struct urb *urb) -{ /* ftdi_read_bulk_callback */ +static void ftdi_read_bulk_callback(struct urb *urb) +{ struct usb_serial_port *port = urb->context; struct tty_struct *tty; struct ftdi_private *priv; @@ -1740,19 +1787,21 @@ static void ftdi_read_bulk_callback (struct urb *urb) int status = urb->status; if (urb->number_of_packets > 0) { - err("%s transfer_buffer_length %d actual_length %d number of packets %d",__func__, - urb->transfer_buffer_length, urb->actual_length, urb->number_of_packets ); - err("%s transfer_flags %x ", __func__,urb->transfer_flags ); + err("%s transfer_buffer_length %d actual_length %d number of packets %d", + __func__, + urb->transfer_buffer_length, + urb->actual_length, urb->number_of_packets); + err("%s transfer_flags %x ", __func__, urb->transfer_flags); } dbg("%s - port %d", __func__, port->number); - if (port->open_count <= 0) + if (port->port.count <= 0) return; - tty = port->tty; + tty = port->port.tty; if (!tty) { - dbg("%s - bad tty pointer - exiting",__func__); + dbg("%s - bad tty pointer - exiting", __func__); return; } @@ -1762,14 +1811,13 @@ static void ftdi_read_bulk_callback (struct urb *urb) return; } - if (urb != port->read_urb) { + if (urb != port->read_urb) err("%s - Not my urb!", __func__); - } if (status) { - /* This will happen at close every time so it is a dbg not an err */ - dbg("(this is ok on close) nonzero read bulk status received: " - "%d", status); + /* This will happen at close every time so it is a dbg not an + err */ + dbg("(this is ok on close) nonzero read bulk status received: %d", status); return; } @@ -1785,7 +1833,7 @@ static void ftdi_read_bulk_callback (struct urb *urb) } /* ftdi_read_bulk_callback */ -static void ftdi_process_read (struct work_struct *work) +static void ftdi_process_read(struct work_struct *work) { /* ftdi_process_read */ struct ftdi_private *priv = container_of(work, struct ftdi_private, rx_work.work); @@ -1803,12 +1851,12 @@ static void ftdi_process_read (struct work_struct *work) dbg("%s - port %d", __func__, port->number); - if (port->open_count <= 0) + if (port->port.count <= 0) return; - tty = port->tty; + tty = port->port.tty; if (!tty) { - dbg("%s - bad tty pointer - exiting",__func__); + dbg("%s - bad tty pointer - exiting", __func__); return; } @@ -1832,11 +1880,11 @@ static void ftdi_process_read (struct work_struct *work) urb->actual_length - priv->rx_processed); } else { /* The first two bytes of every read packet are status */ - if (urb->actual_length > 2) { - usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - } else { - dbg("Status only: %03oo %03oo",data[0],data[1]); - } + if (urb->actual_length > 2) + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, data); + else + dbg("Status only: %03oo %03oo", data[0], data[1]); } @@ -1846,16 +1894,19 @@ static void ftdi_process_read (struct work_struct *work) /* if CD is dropped and the line is not CLOCAL then we should hangup */ need_flip = 0; - for (packet_offset = priv->rx_processed; packet_offset < urb->actual_length; packet_offset += PKTSZ) { + for (packet_offset = priv->rx_processed; + packet_offset < urb->actual_length; packet_offset += PKTSZ) { int length; - /* Compare new line status to the old one, signal if different */ - /* N.B. packet may be processed more than once, but differences - * are only processed once. */ + /* Compare new line status to the old one, signal if different/ + N.B. packet may be processed more than once, but differences + are only processed once. */ if (priv != NULL) { - char new_status = data[packet_offset+0] & FTDI_STATUS_B0_MASK; + char new_status = data[packet_offset + 0] & + FTDI_STATUS_B0_MASK; if (new_status != priv->prev_status) { - priv->diff_status |= new_status ^ priv->prev_status; + priv->diff_status |= + new_status ^ priv->prev_status; wake_up_interruptible(&priv->delta_msr_wait); priv->prev_status = new_status; } @@ -1872,30 +1923,31 @@ static void ftdi_process_read (struct work_struct *work) break; } if (tty_buffer_request_room(tty, length) < length) { - /* break out & wait for throttling/unthrottling to happen */ + /* break out & wait for throttling/unthrottling to + happen */ dbg("%s - receive room low", __func__); break; } /* Handle errors and break */ error_flag = TTY_NORMAL; - /* Although the device uses a bitmask and hence can have multiple */ - /* errors on a packet - the order here sets the priority the */ - /* error is returned to the tty layer */ + /* Although the device uses a bitmask and hence can have + multiple errors on a packet - the order here sets the + priority the error is returned to the tty layer */ - if ( data[packet_offset+1] & FTDI_RS_OE ) { + if (data[packet_offset+1] & FTDI_RS_OE) { error_flag = TTY_OVERRUN; dbg("OVERRRUN error"); } - if ( data[packet_offset+1] & FTDI_RS_BI ) { + if (data[packet_offset+1] & FTDI_RS_BI) { error_flag = TTY_BREAK; dbg("BREAK received"); } - if ( data[packet_offset+1] & FTDI_RS_PE ) { + if (data[packet_offset+1] & FTDI_RS_PE) { error_flag = TTY_PARITY; dbg("PARITY error"); } - if ( data[packet_offset+1] & FTDI_RS_FE ) { + if (data[packet_offset+1] & FTDI_RS_FE) { error_flag = TTY_FRAME; dbg("FRAMING error"); } @@ -1904,7 +1956,8 @@ static void ftdi_process_read (struct work_struct *work) /* Note that the error flag is duplicated for every character received since we don't know which character it applied to */ - tty_insert_flip_char(tty, data[packet_offset+i], error_flag); + tty_insert_flip_char(tty, + data[packet_offset + i], error_flag); } need_flip = 1; } @@ -1912,19 +1965,19 @@ static void ftdi_process_read (struct work_struct *work) #ifdef NOT_CORRECT_BUT_KEEPING_IT_FOR_NOW /* if a parity error is detected you get status packets forever until a character is sent without a parity error. - This doesn't work well since the application receives a never - ending stream of bad data - even though new data hasn't been sent. - Therefore I (bill) have taken this out. + This doesn't work well since the application receives a + never ending stream of bad data - even though new data + hasn't been sent. Therefore I (bill) have taken this out. However - this might make sense for framing errors and so on so I am leaving the code in for now. */ else { - if (error_flag != TTY_NORMAL){ + if (error_flag != TTY_NORMAL) { dbg("error_flag is not normal"); - /* In this case it is just status - if that is an error send a bad character */ - if(tty->flip.count >= TTY_FLIPBUF_SIZE) { + /* In this case it is just status - if that is + an error send a bad character */ + if (tty->flip.count >= TTY_FLIPBUF_SIZE) tty_flip_buffer_push(tty); - } tty_insert_flip_char(tty, 0xff, error_flag); need_flip = 1; } @@ -1933,9 +1986,8 @@ static void ftdi_process_read (struct work_struct *work) } /* "for(packet_offset=0..." */ /* Low latency */ - if (need_flip) { + if (need_flip) tty_flip_buffer_push(tty); - } if (packet_offset < urb->actual_length) { /* not completely processed - record progress */ @@ -1954,12 +2006,11 @@ static void ftdi_process_read (struct work_struct *work) } spin_unlock_irqrestore(&priv->rx_lock, flags); /* if the port is closed stop trying to read */ - if (port->open_count > 0){ + if (port->port.count > 0) /* delay processing of remainder */ schedule_delayed_work(&priv->rx_work, 1); - } else { + else dbg("%s - port is closed", __func__); - } return; } @@ -1967,24 +2018,26 @@ static void ftdi_process_read (struct work_struct *work) priv->rx_processed = 0; /* if the port is closed stop trying to read */ - if (port->open_count > 0){ + if (port->port.count > 0) { /* Continue trying to always read */ usb_fill_bulk_urb(port->read_urb, port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, - ftdi_read_bulk_callback, port); + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, + port->read_urb->transfer_buffer_length, + ftdi_read_bulk_callback, port); result = usb_submit_urb(port->read_urb, GFP_ATOMIC); if (result) - err("%s - failed resubmitting read urb, error %d", __func__, result); + err("%s - failed resubmitting read urb, error %d", + __func__, result); } - - return; } /* ftdi_process_read */ -static void ftdi_break_ctl( struct usb_serial_port *port, int break_state ) +static void ftdi_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); __u16 urb_value = 0; char buf[1]; @@ -1993,22 +2046,23 @@ static void ftdi_break_ctl( struct usb_serial_port *port, int break_state ) /* see drivers/char/tty_io.c to see it used */ /* last_set_data_urb_value NEVER has the break bit set in it */ - if (break_state) { + if (break_state) urb_value = priv->last_set_data_urb_value | FTDI_SIO_SET_BREAK; - } else { + else urb_value = priv->last_set_data_urb_value; - } - - if (usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0), - FTDI_SIO_SET_DATA_REQUEST, - FTDI_SIO_SET_DATA_REQUEST_TYPE, - urb_value , priv->interface, - buf, 0, WDR_TIMEOUT) < 0) { - err("%s FAILED to enable/disable break state (state was %d)", __func__,break_state); + if (usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + FTDI_SIO_SET_DATA_REQUEST, + FTDI_SIO_SET_DATA_REQUEST_TYPE, + urb_value , priv->interface, + buf, 0, WDR_TIMEOUT) < 0) { + err("%s FAILED to enable/disable break state (state was %d)", + __func__, break_state); } - dbg("%s break state is %d - urb is %d", __func__,break_state, urb_value); + dbg("%s break state is %d - urb is %d", __func__, + break_state, urb_value); } @@ -2018,26 +2072,28 @@ static void ftdi_break_ctl( struct usb_serial_port *port, int break_state ) * WARNING: set_termios calls this with old_termios in kernel space */ -static void ftdi_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) +static void ftdi_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { /* ftdi_termios */ struct usb_device *dev = port->serial->dev; struct ftdi_private *priv = usb_get_serial_port_data(port); - struct ktermios *termios = port->tty->termios; + struct ktermios *termios = tty->termios; unsigned int cflag = termios->c_cflag; __u16 urb_value; /* will hold the new flags */ char buf[1]; /* Perhaps I should dynamically alloc this? */ - // Added for xon/xoff support + /* Added for xon/xoff support */ unsigned int iflag = termios->c_iflag; unsigned char vstop; unsigned char vstart; dbg("%s", __func__); - /* Force baud rate if this device requires it, unless it is set to B0. */ + /* Force baud rate if this device requires it, unless it is set to + B0. */ if (priv->force_baud && ((termios->c_cflag & CBAUD) != B0)) { dbg("%s: forcing baud rate for this device", __func__); - tty_encode_baud_rate(port->tty, priv->force_baud, + tty_encode_baud_rate(tty, priv->force_baud, priv->force_baud); } @@ -2053,8 +2109,8 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct ktermios *old not - so just do the change regardless - should be able to compare old_termios and tty->termios */ /* NOTE These routines can get interrupted by - ftdi_sio_read_bulk_callback - need to examine what this - means - don't see any problems yet */ + ftdi_sio_read_bulk_callback - need to examine what this means - + don't see any problems yet */ /* Set number of data bits, parity, stop bits */ @@ -2078,8 +2134,8 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct ktermios *old } } - /* This is needed by the break command since it uses the same command - but is - * or'ed with this value */ + /* This is needed by the break command since it uses the same command + - but is or'ed with this value */ priv->last_set_data_urb_value = urb_value; if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), @@ -2091,7 +2147,7 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct ktermios *old } /* Now do the baudrate */ - if ((cflag & CBAUD) == B0 ) { + if ((cflag & CBAUD) == B0) { /* Disable flow control */ if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), FTDI_SIO_SET_FLOW_CTRL_REQUEST, @@ -2104,13 +2160,11 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct ktermios *old clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); } else { /* set the baudrate determined before */ - if (change_speed(port)) { + if (change_speed(tty, port)) err("%s urb failed to set baudrate", __func__); - } /* Ensure RTS and DTR are raised when baudrate changed from 0 */ - if (!old_termios || (old_termios->c_cflag & CBAUD) == B0) { + if (!old_termios || (old_termios->c_cflag & CBAUD) == B0) set_mctrl(port, TIOCM_DTR | TIOCM_RTS); - } } /* Set flow control */ @@ -2130,18 +2184,22 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct ktermios *old /* * Xon/Xoff code * - * Check the IXOFF status in the iflag component of the termios structure - * if IXOFF is not set, the pre-xon/xoff code is executed. - */ + * Check the IXOFF status in the iflag component of the + * termios structure. If IXOFF is not set, the pre-xon/xoff + * code is executed. + */ if (iflag & IXOFF) { - dbg("%s request to enable xonxoff iflag=%04x",__func__,iflag); - // Try to enable the XON/XOFF on the ftdi_sio - // Set the vstart and vstop -- could have been done up above where - // a lot of other dereferencing is done but that would be very - // inefficient as vstart and vstop are not always needed + dbg("%s request to enable xonxoff iflag=%04x", + __func__, iflag); + /* Try to enable the XON/XOFF on the ftdi_sio + * Set the vstart and vstop -- could have been done up + * above where a lot of other dereferencing is done but + * that would be very inefficient as vstart and vstop + * are not always needed. + */ vstart = termios->c_cc[VSTART]; vstop = termios->c_cc[VSTOP]; - urb_value=(vstop << 8) | (vstart); + urb_value = (vstop << 8) | (vstart); if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), @@ -2153,8 +2211,9 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct ktermios *old err("urb failed to set to xon/xoff flow control"); } } else { - /* else clause to only run if cfag ! CRTSCTS and iflag ! XOFF */ - /* CHECKME Assuming XON/XOFF handled by tty stack - not by device */ + /* else clause to only run if cflag ! CRTSCTS and iflag + * ! XOFF. CHECKME Assuming XON/XOFF handled by tty + * stack - not by device */ dbg("%s Turning off hardware flow control", __func__); if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), @@ -2168,11 +2227,11 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct ktermios *old } return; -} /* ftdi_termios */ - +} -static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file) +static int ftdi_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); unsigned char buf[2]; int ret; @@ -2181,32 +2240,35 @@ static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file) switch (priv->chip_type) { case SIO: /* Request the status from the device */ - if ((ret = usb_control_msg(port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0), - FTDI_SIO_GET_MODEM_STATUS_REQUEST, - FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE, - 0, 0, - buf, 1, WDR_TIMEOUT)) < 0 ) { + ret = usb_control_msg(port->serial->dev, + usb_rcvctrlpipe(port->serial->dev, 0), + FTDI_SIO_GET_MODEM_STATUS_REQUEST, + FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE, + 0, 0, + buf, 1, WDR_TIMEOUT); + if (ret < 0) { err("%s Could not get modem status of device - err: %d", __func__, ret); - return(ret); + return ret; } break; case FT8U232AM: case FT232BM: case FT2232C: case FT232RL: - /* the 8U232AM returns a two byte value (the sio is a 1 byte value) - in the same - format as the data returned from the in point */ - if ((ret = usb_control_msg(port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0), - FTDI_SIO_GET_MODEM_STATUS_REQUEST, - FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE, - 0, priv->interface, - buf, 2, WDR_TIMEOUT)) < 0 ) { + /* the 8U232AM returns a two byte value (the sio is a 1 byte + value) - in the same format as the data returned from the in + point */ + ret = usb_control_msg(port->serial->dev, + usb_rcvctrlpipe(port->serial->dev, 0), + FTDI_SIO_GET_MODEM_STATUS_REQUEST, + FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE, + 0, priv->interface, + buf, 2, WDR_TIMEOUT); + if (ret < 0) { err("%s Could not get modem status of device - err: %d", __func__, ret); - return(ret); + return ret; } break; default: @@ -2221,15 +2283,19 @@ static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file) priv->last_dtr_rts; } -static int ftdi_tiocmset(struct usb_serial_port *port, struct file * file, unsigned int set, unsigned int clear) +static int ftdi_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; dbg("%s TIOCMSET", __func__); return update_mctrl(port, set, clear); } -static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) +static int ftdi_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); dbg("%s cmd 0x%04x", __func__, cmd); @@ -2238,10 +2304,12 @@ static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigne switch (cmd) { case TIOCGSERIAL: /* gets serial port data */ - return get_serial_info(port, (struct serial_struct __user *) arg); + return get_serial_info(port, + (struct serial_struct __user *) arg); case TIOCSSERIAL: /* sets serial port data */ - return set_serial_info(port, (struct serial_struct __user *) arg); + return set_serial_info(tty, port, + (struct serial_struct __user *) arg); /* * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change @@ -2260,45 +2328,41 @@ static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigne else { char diff = priv->diff_status; - if (diff == 0) { + if (diff == 0) return -EIO; /* no change => error */ - } /* Consume all events */ priv->diff_status = 0; - /* Return 0 if caller wanted to know about these bits */ - if ( ((arg & TIOCM_RNG) && (diff & FTDI_RS0_RI)) || - ((arg & TIOCM_DSR) && (diff & FTDI_RS0_DSR)) || - ((arg & TIOCM_CD) && (diff & FTDI_RS0_RLSD)) || - ((arg & TIOCM_CTS) && (diff & FTDI_RS0_CTS)) ) { + /* Return 0 if caller wanted to know about + these bits */ + if (((arg & TIOCM_RNG) && (diff & FTDI_RS0_RI)) || + ((arg & TIOCM_DSR) && (diff & FTDI_RS0_DSR)) || + ((arg & TIOCM_CD) && (diff & FTDI_RS0_RLSD)) || + ((arg & TIOCM_CTS) && (diff & FTDI_RS0_CTS))) { return 0; } /* - * Otherwise caller can't care less about what happened, - * and so we continue to wait for more events. + * Otherwise caller can't care less about what + * happened,and so we continue to wait for more + * events. */ } } - return(0); - break; + return 0; default: break; - } - - - /* This is not necessarily an error - turns out the higher layers will do - * some ioctls itself (see comment above) + /* This is not necessarily an error - turns out the higher layers + * will do some ioctls themselves (see comment above) */ dbg("%s arg not supported - it was 0x%04x - check /usr/include/asm/ioctls.h", __func__, cmd); + return -ENOIOCTLCMD; +} - return(-ENOIOCTLCMD); -} /* ftdi_ioctl */ - - -static void ftdi_throttle (struct usb_serial_port *port) +static void ftdi_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -2310,8 +2374,9 @@ static void ftdi_throttle (struct usb_serial_port *port) } -static void ftdi_unthrottle (struct usb_serial_port *port) +static void ftdi_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); int actually_throttled; unsigned long flags; @@ -2327,7 +2392,7 @@ static void ftdi_unthrottle (struct usb_serial_port *port) schedule_delayed_work(&priv->rx_work, 0); } -static int __init ftdi_init (void) +static int __init ftdi_init(void) { int retval; @@ -2357,13 +2422,13 @@ failed_sio_register: } -static void __exit ftdi_exit (void) +static void __exit ftdi_exit(void) { dbg("%s", __func__); - usb_deregister (&ftdi_driver); - usb_serial_deregister (&ftdi_sio_device); + usb_deregister(&ftdi_driver); + usb_serial_deregister(&ftdi_sio_device); } @@ -2371,8 +2436,8 @@ static void __exit ftdi_exit (void) module_init(ftdi_init); module_exit(ftdi_exit); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 8302eca893ea..a577ea44dcf9 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -1,20 +1,20 @@ /* - * Definitions for the FTDI USB Single Port Serial Converter - - * known as FTDI_SIO (Serial Input/Output application of the chipset) + * Definitions for the FTDI USB Single Port Serial Converter - + * known as FTDI_SIO (Serial Input/Output application of the chipset) * * The example I have is known as the USC-1000 which is available from * http://www.dse.co.nz - cat no XH4214 It looks similar to this: * http://www.dansdata.com/usbser.htm but I can't be sure There are other * USC-1000s which don't look like my device though so beware! * - * The device is based on the FTDI FT8U100AX chip. It has a DB25 on one side, + * The device is based on the FTDI FT8U100AX chip. It has a DB25 on one side, * USB on the other. * * Thanx to FTDI (http://www.ftdi.co.uk) for so kindly providing details * of the protocol required to talk to the device and ongoing assistence * during development. * - * Bill Ryder - bryder@sgi.com formerly of Silicon Graphics, Inc.- wrote the + * Bill Ryder - bryder@sgi.com formerly of Silicon Graphics, Inc.- wrote the * FTDI_SIO implementation. * * Philipp Gühring - pg@futureware.at - added the Device ID of the USB relais @@ -472,7 +472,7 @@ /* * DSS-20 Sync Station for Sony Ericsson P800 */ -#define FTDI_DSS20_PID 0xFC82 +#define FTDI_DSS20_PID 0xFC82 /* * Home Electronics (www.home-electro.com) USB gadgets @@ -884,7 +884,7 @@ /* * BmRequestType: 0100 0000B * bRequest: FTDI_SIO_RESET - * wValue: Control Value + * wValue: Control Value * 0 = Reset SIO * 1 = Purge RX buffer * 2 = Purge TX buffer @@ -952,7 +952,7 @@ * 101 - add .625 to divisor * 110 - add .750 to divisor * 111 - add .875 to divisor - * Bits 15 to 0 of the 17-bit divisor are placed in the urb value. Bit 16 is + * Bits 15 to 0 of the 17-bit divisor are placed in the urb value. Bit 16 is * placed in bit 0 of the urb index. * * Note that there are a couple of special cases to support the highest baud @@ -971,8 +971,8 @@ typedef enum { } ftdi_chip_type_t; typedef enum { - ftdi_sio_b300 = 0, - ftdi_sio_b600 = 1, + ftdi_sio_b300 = 0, + ftdi_sio_b600 = 1, ftdi_sio_b1200 = 2, ftdi_sio_b2400 = 3, ftdi_sio_b4800 = 4, @@ -981,7 +981,7 @@ typedef enum { ftdi_sio_b38400 = 7, ftdi_sio_b57600 = 8, ftdi_sio_b115200 = 9 -} FTDI_SIO_baudrate_t ; +} FTDI_SIO_baudrate_t; /* * The ftdi_8U232AM_xxMHz_byyy constants have been removed. The encoded divisor values @@ -990,19 +990,19 @@ typedef enum { #define FTDI_SIO_SET_DATA_REQUEST FTDI_SIO_SET_DATA #define FTDI_SIO_SET_DATA_REQUEST_TYPE 0x40 -#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8 ) -#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8 ) -#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8 ) -#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8 ) -#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8 ) -#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11 ) -#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11 ) -#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11 ) +#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8) +#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8) +#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8) +#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8) +#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8) +#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11) +#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11) +#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11) #define FTDI_SIO_SET_BREAK (0x1 << 14) /* FTDI_SIO_SET_DATA */ /* - * BmRequestType: 0100 0000B + * BmRequestType: 0100 0000B * bRequest: FTDI_SIO_SET_DATA * wValue: Data characteristics (see below) * wIndex: Port @@ -1035,7 +1035,7 @@ typedef enum { #define FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE 0x40 #define FTDI_SIO_SET_MODEM_CTRL_REQUEST FTDI_SIO_MODEM_CTRL -/* +/* * BmRequestType: 0100 0000B * bRequest: FTDI_SIO_MODEM_CTRL * wValue: ControlValue (see below) @@ -1049,11 +1049,11 @@ typedef enum { */ #define FTDI_SIO_SET_DTR_MASK 0x1 -#define FTDI_SIO_SET_DTR_HIGH ( 1 | ( FTDI_SIO_SET_DTR_MASK << 8)) -#define FTDI_SIO_SET_DTR_LOW ( 0 | ( FTDI_SIO_SET_DTR_MASK << 8)) +#define FTDI_SIO_SET_DTR_HIGH (1 | (FTDI_SIO_SET_DTR_MASK << 8)) +#define FTDI_SIO_SET_DTR_LOW (0 | (FTDI_SIO_SET_DTR_MASK << 8)) #define FTDI_SIO_SET_RTS_MASK 0x2 -#define FTDI_SIO_SET_RTS_HIGH ( 2 | ( FTDI_SIO_SET_RTS_MASK << 8 )) -#define FTDI_SIO_SET_RTS_LOW ( 0 | ( FTDI_SIO_SET_RTS_MASK << 8 )) +#define FTDI_SIO_SET_RTS_HIGH (2 | (FTDI_SIO_SET_RTS_MASK << 8)) +#define FTDI_SIO_SET_RTS_LOW (0 | (FTDI_SIO_SET_RTS_MASK << 8)) /* * ControlValue @@ -1076,7 +1076,7 @@ typedef enum { /* FTDI_SIO_SET_FLOW_CTRL */ #define FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE 0x40 #define FTDI_SIO_SET_FLOW_CTRL_REQUEST FTDI_SIO_SET_FLOW_CTRL -#define FTDI_SIO_DISABLE_FLOW_CTRL 0x0 +#define FTDI_SIO_DISABLE_FLOW_CTRL 0x0 #define FTDI_SIO_RTS_CTS_HS (0x1 << 8) #define FTDI_SIO_DTR_DSR_HS (0x2 << 8) #define FTDI_SIO_XON_XOFF_HS (0x4 << 8) @@ -1085,7 +1085,7 @@ typedef enum { * bRequest: FTDI_SIO_SET_FLOW_CTRL * wValue: Xoff/Xon * wIndex: Protocol/Port - hIndex is protocl / lIndex is port - * wLength: 0 + * wLength: 0 * Data: None * * hIndex protocol is: @@ -1101,10 +1101,10 @@ typedef enum { * * A value of zero in the hIndex field disables handshaking * - * If Xon/Xoff handshaking is specified, the hValue field should contain the XOFF character + * If Xon/Xoff handshaking is specified, the hValue field should contain the XOFF character * and the lValue field contains the XON character. - */ - + */ + /* * FTDI_SIO_GET_LATENCY_TIMER * @@ -1118,7 +1118,7 @@ typedef enum { #define FTDI_SIO_GET_LATENCY_TIMER_REQUEST FTDI_SIO_GET_LATENCY_TIMER #define FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE 0xC0 -/* +/* * BmRequestType: 1100 0000b * bRequest: FTDI_SIO_GET_LATENCY_TIMER * wValue: 0 @@ -1127,7 +1127,7 @@ typedef enum { * Data: latency (on return) */ -/* +/* * FTDI_SIO_SET_LATENCY_TIMER * * Set the timeout interval. The FTDI collects data from the slave @@ -1140,7 +1140,7 @@ typedef enum { #define FTDI_SIO_SET_LATENCY_TIMER_REQUEST FTDI_SIO_SET_LATENCY_TIMER #define FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE 0x40 -/* +/* * BmRequestType: 0100 0000b * bRequest: FTDI_SIO_SET_LATENCY_TIMER * wValue: Latency (milliseconds) @@ -1155,7 +1155,7 @@ typedef enum { */ /* - * FTDI_SIO_SET_EVENT_CHAR + * FTDI_SIO_SET_EVENT_CHAR * * Set the special event character for the specified communications port. * If the device sees this character it will immediately return the @@ -1168,7 +1168,7 @@ typedef enum { #define FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE 0x40 -/* +/* * BmRequestType: 0100 0000b * bRequest: FTDI_SIO_SET_EVENT_CHAR * wValue: EventChar @@ -1184,12 +1184,12 @@ typedef enum { * B9..15 Reserved * */ - + /* FTDI_SIO_SET_ERROR_CHAR */ /* Set the parity error replacement character for the specified communications port */ -/* +/* * BmRequestType: 0100 0000b * bRequest: FTDI_SIO_SET_EVENT_CHAR * wValue: Error Char @@ -1215,15 +1215,15 @@ typedef enum { #define FTDI_SIO_DSR_MASK 0x20 #define FTDI_SIO_RI_MASK 0x40 #define FTDI_SIO_RLSD_MASK 0x80 -/* +/* * BmRequestType: 1100 0000b * bRequest: FTDI_SIO_GET_MODEM_STATUS * wValue: zero * wIndex: Port * wLength: 1 * Data: Status - * - * One byte of data is returned + * + * One byte of data is returned * B0..3 0 * B4 CTS * 0 = inactive @@ -1236,15 +1236,15 @@ typedef enum { * 1 = active * B7 Receive Line Signal Detect (RLSD) * 0 = inactive - * 1 = active + * 1 = active */ -/* Descriptors returned by the device - * +/* Descriptors returned by the device + * * Device Descriptor - * + * * Offset Field Size Value Description * 0 bLength 1 0x12 Size of descriptor in bytes * 1 bDescriptorType 1 0x01 DEVICE Descriptor Type @@ -1260,9 +1260,9 @@ typedef enum { * 15 iProduct 1 0x02 Index of prod string desc * 16 iSerialNumber 1 0x02 Index of serial nmr string desc * 17 bNumConfigurations 1 0x01 Number of possible configurations - * + * * Configuration Descriptor - * + * * Offset Field Size Value * 0 bLength 1 0x09 Size of descriptor in bytes * 1 bDescriptorType 1 0x02 CONFIGURATION Descriptor Type @@ -1272,9 +1272,9 @@ typedef enum { * 6 iConfiguration 1 0x02 Index of config string descriptor * 7 bmAttributes 1 0x20 Config characteristics Remote Wakeup * 8 MaxPower 1 0x1E Max power consumption - * + * * Interface Descriptor - * + * * Offset Field Size Value * 0 bLength 1 0x09 Size of descriptor in bytes * 1 bDescriptorType 1 0x04 INTERFACE Descriptor Type @@ -1285,9 +1285,9 @@ typedef enum { * 6 bInterfaceSubClass 1 0xFF Subclass Code * 7 bInterfaceProtocol 1 0xFF Protocol Code * 8 iInterface 1 0x02 Index of interface string description - * + * * IN Endpoint Descriptor - * + * * Offset Field Size Value * 0 bLength 1 0x07 Size of descriptor in bytes * 1 bDescriptorType 1 0x05 ENDPOINT descriptor type @@ -1295,9 +1295,9 @@ typedef enum { * 3 bmAttributes 1 0x02 Endpoint attributes - Bulk * 4 bNumEndpoints 2 0x0040 maximum packet size * 5 bInterval 1 0x00 Interval for polling endpoint - * + * * OUT Endpoint Descriptor - * + * * Offset Field Size Value * 0 bLength 1 0x07 Size of descriptor in bytes * 1 bDescriptorType 1 0x05 ENDPOINT descriptor type @@ -1305,17 +1305,17 @@ typedef enum { * 3 bmAttributes 1 0x02 Endpoint attributes - Bulk * 4 bNumEndpoints 2 0x0040 maximum packet size * 5 bInterval 1 0x00 Interval for polling endpoint - * + * * DATA FORMAT - * + * * IN Endpoint - * + * * The device reserves the first two bytes of data on this endpoint to contain the current * values of the modem and line status registers. In the absence of data, the device * generates a message consisting of these two status bytes every 40 ms - * + * * Byte 0: Modem Status - * + * * Offset Description * B0 Reserved - must be 1 * B1 Reserved - must be 0 @@ -1325,9 +1325,9 @@ typedef enum { * B5 Data Set Ready (DSR) * B6 Ring Indicator (RI) * B7 Receive Line Signal Detect (RLSD) - * + * * Byte 1: Line Status - * + * * Offset Description * B0 Data Ready (DR) * B1 Overrun Error (OE) @@ -1337,7 +1337,7 @@ typedef enum { * B5 Transmitter Holding Register (THRE) * B6 Transmitter Empty (TEMT) * B7 Error in RCVR FIFO - * + * */ #define FTDI_RS0_CTS (1 << 4) #define FTDI_RS0_DSR (1 << 5) @@ -1355,17 +1355,17 @@ typedef enum { /* * OUT Endpoint - * + * * This device reserves the first bytes of data on this endpoint contain the length * and port identifier of the message. For the FTDI USB Serial converter the port * identifier is always 1. - * + * * Byte 0: Line Status - * + * * Offset Description * B0 Reserved - must be 1 * B1 Reserved - must be 0 * B2..7 Length of message - (not including Byte 0) - * + * */ diff --git a/drivers/usb/serial/funsoft.c b/drivers/usb/serial/funsoft.c index e8ba2cb5995d..d30f736d2cc5 100644 --- a/drivers/usb/serial/funsoft.c +++ b/drivers/usb/serial/funsoft.c @@ -14,7 +14,7 @@ #include <linux/module.h> #include <linux/usb.h> #include <linux/usb/serial.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> static int debug; diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 8ce5a56a48e3..2e663f1afd5e 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -33,7 +33,7 @@ #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <asm/atomic.h> #include <linux/usb.h> #include <linux/usb/serial.h> @@ -44,7 +44,7 @@ static int initial_mode = 1; /* debug flag */ -static int debug = 0; +static int debug; #define GARMIN_VENDOR_ID 0x091E @@ -56,7 +56,7 @@ static int debug = 0; #define VERSION_MINOR 31 #define _STR(s) #s -#define _DRIVER_VERSION(a,b) "v" _STR(a) "." _STR(b) +#define _DRIVER_VERSION(a, b) "v" _STR(a) "." _STR(b) #define DRIVER_VERSION _DRIVER_VERSION(VERSION_MAJOR, VERSION_MINOR) #define DRIVER_AUTHOR "hermann kneissel" #define DRIVER_DESC "garmin gps driver" @@ -65,37 +65,37 @@ static int debug = 0; #define EINVPKT 1000 /* invalid packet structure */ -// size of the header of a packet using the usb protocol +/* size of the header of a packet using the usb protocol */ #define GARMIN_PKTHDR_LENGTH 12 -// max. possible size of a packet using the serial protocol -#define MAX_SERIAL_PKT_SIZ (3+255+3) +/* max. possible size of a packet using the serial protocol */ +#define MAX_SERIAL_PKT_SIZ (3 + 255 + 3) -// max. possible size of a packet with worst case stuffing -#define MAX_SERIAL_PKT_SIZ_STUFFED MAX_SERIAL_PKT_SIZ+256 +/* max. possible size of a packet with worst case stuffing */ +#define MAX_SERIAL_PKT_SIZ_STUFFED (MAX_SERIAL_PKT_SIZ + 256) -// size of a buffer able to hold a complete (no stuffing) packet -// (the document protocol does not contain packets with a larger -// size, but in theory a packet may be 64k+12 bytes - if in -// later protocol versions larger packet sizes occur, this value -// should be increased accordingly, so the input buffer is always -// large enough the store a complete packet inclusive header) -#define GPS_IN_BUFSIZ (GARMIN_PKTHDR_LENGTH+MAX_SERIAL_PKT_SIZ) +/* size of a buffer able to hold a complete (no stuffing) packet + * (the document protocol does not contain packets with a larger + * size, but in theory a packet may be 64k+12 bytes - if in + * later protocol versions larger packet sizes occur, this value + * should be increased accordingly, so the input buffer is always + * large enough the store a complete packet inclusive header) */ +#define GPS_IN_BUFSIZ (GARMIN_PKTHDR_LENGTH+MAX_SERIAL_PKT_SIZ) -// size of a buffer able to hold a complete (incl. stuffing) packet -#define GPS_OUT_BUFSIZ (GARMIN_PKTHDR_LENGTH+MAX_SERIAL_PKT_SIZ_STUFFED) +/* size of a buffer able to hold a complete (incl. stuffing) packet */ +#define GPS_OUT_BUFSIZ (GARMIN_PKTHDR_LENGTH+MAX_SERIAL_PKT_SIZ_STUFFED) -// where to place the packet id of a serial packet, so we can -// prepend the usb-packet header without the need to move the -// packets data +/* where to place the packet id of a serial packet, so we can + * prepend the usb-packet header without the need to move the + * packets data */ #define GSP_INITIAL_OFFSET (GARMIN_PKTHDR_LENGTH-2) -// max. size of incoming private packets (header+1 param) +/* max. size of incoming private packets (header+1 param) */ #define PRIVPKTSIZ (GARMIN_PKTHDR_LENGTH+4) #define GARMIN_LAYERID_TRANSPORT 0 #define GARMIN_LAYERID_APPL 20 -// our own layer-id to use for some control mechanisms +/* our own layer-id to use for some control mechanisms */ #define GARMIN_LAYERID_PRIVATE 0x01106E4B #define GARMIN_PKTID_PVT_DATA 51 @@ -103,7 +103,7 @@ static int debug = 0; #define CMND_ABORT_TRANSFER 0 -// packet ids used in private layer +/* packet ids used in private layer */ #define PRIV_PKTID_SET_DEBUG 1 #define PRIV_PKTID_SET_MODE 2 #define PRIV_PKTID_INFO_REQ 3 @@ -121,7 +121,8 @@ static int debug = 0; struct garmin_packet { struct list_head list; int seq; - int size; // the real size of the data array, always > 0 + /* the real size of the data array, always > 0 */ + int size; __u8 data[1]; }; @@ -164,7 +165,7 @@ struct garmin_data { #define MODE_NATIVE 0 #define MODE_GARMIN_SERIAL 1 -// Flags used in garmin_data.flags: +/* Flags used in garmin_data.flags: */ #define FLAGS_SESSION_REPLY_MASK 0x00C0 #define FLAGS_SESSION_REPLY1_SEEN 0x0080 #define FLAGS_SESSION_REPLY2_SEEN 0x0040 @@ -185,7 +186,7 @@ struct garmin_data { /* function prototypes */ -static void gsp_next_packet(struct garmin_data * garmin_data_p); +static void gsp_next_packet(struct garmin_data *garmin_data_p); static int garmin_write_bulk(struct usb_serial_port *port, const unsigned char *buf, int count, int dismiss_ack); @@ -217,12 +218,13 @@ static unsigned char const PRIVATE_REQ[] static struct usb_device_id id_table [] = { - /* the same device id seems to be used by all usb enabled gps devices */ - { USB_DEVICE(GARMIN_VENDOR_ID, 3 ) }, + /* the same device id seems to be used by all + usb enabled GPS devices */ + { USB_DEVICE(GARMIN_VENDOR_ID, 3) }, { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, id_table); static struct usb_driver garmin_driver = { .name = "garmin_gps", @@ -233,9 +235,10 @@ static struct usb_driver garmin_driver = { }; -static inline int noResponseFromAppLayer(struct garmin_data * garmin_data_p) +static inline int noResponseFromAppLayer(struct garmin_data *garmin_data_p) { - return atomic_read(&garmin_data_p->req_count) == atomic_read(&garmin_data_p->resp_count); + return atomic_read(&garmin_data_p->req_count) == + atomic_read(&garmin_data_p->resp_count); } @@ -261,10 +264,10 @@ static inline int getDataLength(const __u8 *usbPacket) */ static inline int isAbortTrfCmnd(const unsigned char *buf) { - if (0 == memcmp(buf, GARMIN_STOP_TRANSFER_REQ, - sizeof(GARMIN_STOP_TRANSFER_REQ)) || - 0 == memcmp(buf, GARMIN_STOP_TRANSFER_REQ_V2, - sizeof(GARMIN_STOP_TRANSFER_REQ_V2))) + if (0 == memcmp(buf, GARMIN_STOP_TRANSFER_REQ, + sizeof(GARMIN_STOP_TRANSFER_REQ)) || + 0 == memcmp(buf, GARMIN_STOP_TRANSFER_REQ_V2, + sizeof(GARMIN_STOP_TRANSFER_REQ_V2))) return 1; else return 0; @@ -275,11 +278,11 @@ static inline int isAbortTrfCmnd(const unsigned char *buf) static void send_to_tty(struct usb_serial_port *port, char *data, unsigned int actual_length) { - struct tty_struct *tty = port->tty; + struct tty_struct *tty = port->port.tty; if (tty && actual_length) { - usb_serial_debug_data(debug, &port->dev, + usb_serial_debug_data(debug, &port->dev, __func__, actual_length, data); tty_buffer_request_room(tty, actual_length); @@ -296,7 +299,7 @@ static void send_to_tty(struct usb_serial_port *port, /* * queue a received (usb-)packet for later processing */ -static int pkt_add(struct garmin_data * garmin_data_p, +static int pkt_add(struct garmin_data *garmin_data_p, unsigned char *data, unsigned int data_length) { int state = 0; @@ -307,7 +310,7 @@ static int pkt_add(struct garmin_data * garmin_data_p, /* process only packets containg data ... */ if (data_length) { pkt = kmalloc(sizeof(struct garmin_packet)+data_length, - GFP_ATOMIC); + GFP_ATOMIC); if (pkt == NULL) { dev_err(&garmin_data_p->port->dev, "out of memory\n"); return 0; @@ -325,16 +328,15 @@ static int pkt_add(struct garmin_data * garmin_data_p, /* in serial mode, if someone is waiting for data from the device, iconvert and send the next packet to tty. */ - if (result && (state == STATE_GSP_WAIT_DATA)) { + if (result && (state == STATE_GSP_WAIT_DATA)) gsp_next_packet(garmin_data_p); - } } return result; } /* get the next pending packet */ -static struct garmin_packet *pkt_pop(struct garmin_data * garmin_data_p) +static struct garmin_packet *pkt_pop(struct garmin_data *garmin_data_p) { unsigned long flags; struct garmin_packet *result = NULL; @@ -350,7 +352,7 @@ static struct garmin_packet *pkt_pop(struct garmin_data * garmin_data_p) /* free up all queued data */ -static void pkt_clear(struct garmin_data * garmin_data_p) +static void pkt_clear(struct garmin_data *garmin_data_p) { unsigned long flags; struct garmin_packet *result = NULL; @@ -372,7 +374,7 @@ static void pkt_clear(struct garmin_data * garmin_data_p) ******************************************************************************/ /* send an ack packet back to the tty */ -static int gsp_send_ack(struct garmin_data * garmin_data_p, __u8 pkt_id) +static int gsp_send_ack(struct garmin_data *garmin_data_p, __u8 pkt_id) { __u8 pkt[10]; __u8 cksum = 0; @@ -391,9 +393,8 @@ static int gsp_send_ack(struct garmin_data * garmin_data_p, __u8 pkt_id) *ptr++ = pkt_id; cksum += pkt_id; - if (pkt_id == DLE) { + if (pkt_id == DLE) *ptr++ = DLE; - } *ptr++ = 0; *ptr++ = 0xFF & (-cksum); @@ -415,12 +416,12 @@ static int gsp_send_ack(struct garmin_data * garmin_data_p, __u8 pkt_id) * at GSP_INITIAL_OFFSET. * * count - number of bytes in the input buffer including space reserved for - * the usb header: GSP_INITIAL_OFFSET + number of bytes in packet + * the usb header: GSP_INITIAL_OFFSET + number of bytes in packet * (including pkt-id, data-length a. cksum) */ -static int gsp_rec_packet(struct garmin_data * garmin_data_p, int count) +static int gsp_rec_packet(struct garmin_data *garmin_data_p, int count) { - const __u8* recpkt = garmin_data_p->inbuffer+GSP_INITIAL_OFFSET; + const __u8 *recpkt = garmin_data_p->inbuffer+GSP_INITIAL_OFFSET; __le32 *usbdata = (__le32 *) garmin_data_p->inbuffer; int cksum = 0; @@ -440,8 +441,8 @@ static int gsp_rec_packet(struct garmin_data * garmin_data_p, int count) cksum += *recpkt++; cksum += *recpkt++; - // sanity check, remove after test ... - if ((__u8*)&(usbdata[3]) != recpkt) { + /* sanity check, remove after test ... */ + if ((__u8 *)&(usbdata[3]) != recpkt) { dbg("%s - ptr mismatch %p - %p", __func__, &(usbdata[4]), recpkt); return -EINVPKT; @@ -462,7 +463,7 @@ static int gsp_rec_packet(struct garmin_data * garmin_data_p, int count) usbdata[1] = __cpu_to_le32(pktid); usbdata[2] = __cpu_to_le32(size); - garmin_write_bulk (garmin_data_p->port, garmin_data_p->inbuffer, + garmin_write_bulk(garmin_data_p->port, garmin_data_p->inbuffer, GARMIN_PKTHDR_LENGTH+size, 0); /* if this was an abort-transfer command, flush all @@ -495,7 +496,7 @@ static int gsp_rec_packet(struct garmin_data * garmin_data_p, int count) * if the input is an abort command, drop all queued data. */ -static int gsp_receive(struct garmin_data * garmin_data_p, +static int gsp_receive(struct garmin_data *garmin_data_p, const unsigned char *buf, int count) { unsigned long flags; @@ -504,10 +505,11 @@ static int gsp_receive(struct garmin_data * garmin_data_p, int i = 0; __u8 *dest; int size; - // dleSeen: set if last byte read was a DLE + /* dleSeen: set if last byte read was a DLE */ int dleSeen; - // skip: if set, skip incoming data until possible start of - // new packet + /* skip: if set, skip incoming data until possible start of + * new packet + */ int skip; __u8 data; @@ -521,14 +523,13 @@ static int gsp_receive(struct garmin_data * garmin_data_p, dbg("%s - dle=%d skip=%d size=%d count=%d", __func__, dleSeen, skip, size, count); - if (size == 0) { + if (size == 0) size = GSP_INITIAL_OFFSET; - } while (offs < count) { data = *(buf+offs); - offs ++; + offs++; if (data == DLE) { if (skip) { /* start of a new pkt */ @@ -554,9 +555,8 @@ static int gsp_receive(struct garmin_data * garmin_data_p, ack_or_nak_seen = NAK; dbg("NAK packet complete."); } else { - dbg("packet complete " - "- id=0x%X.", - 0xFF & data); + dbg("packet complete - id=0x%X.", + 0xFF & data); gsp_rec_packet(garmin_data_p, size); } @@ -589,7 +589,7 @@ static int gsp_receive(struct garmin_data * garmin_data_p, garmin_data_p->insize = size; - // copy flags back to structure + /* copy flags back to structure */ if (skip) garmin_data_p->flags |= FLAGS_GSP_SKIP; else @@ -600,16 +600,13 @@ static int gsp_receive(struct garmin_data * garmin_data_p, else garmin_data_p->flags &= ~FLAGS_GSP_DLESEEN; - if (ack_or_nak_seen) { + if (ack_or_nak_seen) garmin_data_p->state = STATE_GSP_WAIT_DATA; - } spin_unlock_irqrestore(&garmin_data_p->lock, flags); - if (ack_or_nak_seen) { + if (ack_or_nak_seen) gsp_next_packet(garmin_data_p); - } - return count; } @@ -623,7 +620,7 @@ static int gsp_receive(struct garmin_data * garmin_data_p, * * return <0 on error, 0 if packet is incomplete or > 0 if packet was sent */ -static int gsp_send(struct garmin_data * garmin_data_p, +static int gsp_send(struct garmin_data *garmin_data_p, const unsigned char *buf, int count) { const unsigned char *src; @@ -631,11 +628,11 @@ static int gsp_send(struct garmin_data * garmin_data_p, int pktid = 0; int datalen = 0; int cksum = 0; - int i=0; + int i = 0; int k; dbg("%s - state %d - %d bytes.", __func__, - garmin_data_p->state, count); + garmin_data_p->state, count); k = garmin_data_p->outsize; if ((k+count) > GPS_OUT_BUFSIZ) { @@ -650,7 +647,7 @@ static int gsp_send(struct garmin_data * garmin_data_p, if (k >= GARMIN_PKTHDR_LENGTH) { pktid = getPacketId(garmin_data_p->outbuffer); - datalen= getDataLength(garmin_data_p->outbuffer); + datalen = getDataLength(garmin_data_p->outbuffer); i = GARMIN_PKTHDR_LENGTH + datalen; if (k < i) return 0; @@ -658,19 +655,18 @@ static int gsp_send(struct garmin_data * garmin_data_p, return 0; } - dbg("%s - %d bytes in buffer, %d bytes in pkt.", __func__, - k, i); + dbg("%s - %d bytes in buffer, %d bytes in pkt.", __func__, k, i); /* garmin_data_p->outbuffer now contains a complete packet */ usb_serial_debug_data(debug, &garmin_data_p->port->dev, - __func__, k, garmin_data_p->outbuffer); + __func__, k, garmin_data_p->outbuffer); garmin_data_p->outsize = 0; if (GARMIN_LAYERID_APPL != getLayerId(garmin_data_p->outbuffer)) { - dbg("not an application packet (%d)", - getLayerId(garmin_data_p->outbuffer)); + dbg("not an application packet (%d)", + getLayerId(garmin_data_p->outbuffer)); return -1; } @@ -688,14 +684,14 @@ static int gsp_send(struct garmin_data * garmin_data_p, k = 0; src = garmin_data_p->outbuffer+GARMIN_PKTHDR_LENGTH; - for (i=0; i<datalen; i++) { + for (i = 0; i < datalen; i++) { if (*src++ == DLE) k++; } src = garmin_data_p->outbuffer+GARMIN_PKTHDR_LENGTH; if (k > (GARMIN_PKTHDR_LENGTH-2)) { - /* can't add stuffing DLEs in place, move data to end + /* can't add stuffing DLEs in place, move data to end of buffer ... */ dst = garmin_data_p->outbuffer+GPS_OUT_BUFSIZ-datalen; memcpy(dst, src, datalen); @@ -712,14 +708,14 @@ static int gsp_send(struct garmin_data * garmin_data_p, if (datalen == DLE) *dst++ = DLE; - for (i=0; i<datalen; i++) { + for (i = 0; i < datalen; i++) { __u8 c = *src++; *dst++ = c; cksum += c; if (c == DLE) *dst++ = DLE; } - + cksum = 0xFF & -cksum; *dst++ = cksum; if (cksum == DLE) @@ -744,7 +740,7 @@ static int gsp_send(struct garmin_data * garmin_data_p, /* * Process the next pending data packet - if there is one */ -static void gsp_next_packet(struct garmin_data * garmin_data_p) +static void gsp_next_packet(struct garmin_data *garmin_data_p) { struct garmin_packet *pkt = NULL; @@ -774,17 +770,17 @@ static void gsp_next_packet(struct garmin_data * garmin_data_p) * buf contains the data read, it may span more than one packet * or even incomplete packets */ -static int nat_receive(struct garmin_data * garmin_data_p, +static int nat_receive(struct garmin_data *garmin_data_p, const unsigned char *buf, int count) { unsigned long flags; - __u8 * dest; + __u8 *dest; int offs = 0; int result = count; int len; while (offs < count) { - // if buffer contains header, copy rest of data + /* if buffer contains header, copy rest of data */ if (garmin_data_p->insize >= GARMIN_PKTHDR_LENGTH) len = GARMIN_PKTHDR_LENGTH +getDataLength(garmin_data_p->inbuffer); @@ -792,9 +788,9 @@ static int nat_receive(struct garmin_data * garmin_data_p, len = GARMIN_PKTHDR_LENGTH; if (len >= GPS_IN_BUFSIZ) { - /* seem to be an invalid packet, ignore rest of input */ - dbg("%s - packet size too large: %d", - __func__, len); + /* seems to be an invalid packet, ignore rest + of input */ + dbg("%s - packet size too large: %d", __func__, len); garmin_data_p->insize = 0; count = 0; result = -EINVPKT; @@ -804,7 +800,7 @@ static int nat_receive(struct garmin_data * garmin_data_p, len = (count-offs); if (len > 0) { dest = garmin_data_p->inbuffer - +garmin_data_p->insize; + + garmin_data_p->insize; memcpy(dest, buf+offs, len); garmin_data_p->insize += len; offs += len; @@ -816,17 +812,19 @@ static int nat_receive(struct garmin_data * garmin_data_p, len = GARMIN_PKTHDR_LENGTH+ getDataLength(garmin_data_p->inbuffer); if (garmin_data_p->insize >= len) { - garmin_write_bulk (garmin_data_p->port, - garmin_data_p->inbuffer, - len, 0); + garmin_write_bulk(garmin_data_p->port, + garmin_data_p->inbuffer, + len, 0); garmin_data_p->insize = 0; /* if this was an abort-transfer command, flush all queued data. */ if (isAbortTrfCmnd(garmin_data_p->inbuffer)) { - spin_lock_irqsave(&garmin_data_p->lock, flags); + spin_lock_irqsave(&garmin_data_p->lock, + flags); garmin_data_p->flags |= FLAGS_DROP_DATA; - spin_unlock_irqrestore(&garmin_data_p->lock, flags); + spin_unlock_irqrestore( + &garmin_data_p->lock, flags); pkt_clear(garmin_data_p); } } @@ -842,7 +840,7 @@ static int nat_receive(struct garmin_data * garmin_data_p, static void priv_status_resp(struct usb_serial_port *port) { - struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); + struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); __le32 *pkt = (__le32 *)garmin_data_p->privpkt; pkt[0] = __cpu_to_le32(GARMIN_LAYERID_PRIVATE); @@ -852,7 +850,7 @@ static void priv_status_resp(struct usb_serial_port *port) pkt[4] = __cpu_to_le32(garmin_data_p->mode); pkt[5] = __cpu_to_le32(garmin_data_p->serial_num); - send_to_tty(port, (__u8*)pkt, 6*4); + send_to_tty(port, (__u8 *)pkt, 6 * 4); } @@ -864,7 +862,7 @@ static int process_resetdev_request(struct usb_serial_port *port) { unsigned long flags; int status; - struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); + struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags &= ~(CLEAR_HALT_REQUIRED); @@ -872,8 +870,8 @@ static int process_resetdev_request(struct usb_serial_port *port) garmin_data_p->serial_num = 0; spin_unlock_irqrestore(&garmin_data_p->lock, flags); - usb_kill_urb (port->interrupt_in_urb); - dbg("%s - usb_reset_device", __func__ ); + usb_kill_urb(port->interrupt_in_urb); + dbg("%s - usb_reset_device", __func__); status = usb_reset_device(port->serial->dev); if (status) dbg("%s - usb_reset_device failed: %d", @@ -886,7 +884,7 @@ static int process_resetdev_request(struct usb_serial_port *port) /* * clear all cached data */ -static int garmin_clear(struct garmin_data * garmin_data_p) +static int garmin_clear(struct garmin_data *garmin_data_p) { unsigned long flags; int status = 0; @@ -896,8 +894,7 @@ static int garmin_clear(struct garmin_data * garmin_data_p) if (port != NULL && atomic_read(&garmin_data_p->resp_count)) { /* send a terminate command */ status = garmin_write_bulk(port, GARMIN_STOP_TRANSFER_REQ, - sizeof(GARMIN_STOP_TRANSFER_REQ), - 1); + sizeof(GARMIN_STOP_TRANSFER_REQ), 1); } /* flush all queued data */ @@ -920,28 +917,26 @@ static int garmin_init_session(struct usb_serial_port *port) { unsigned long flags; struct usb_serial *serial = port->serial; - struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); + struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); int status = 0; if (status == 0) { - usb_kill_urb (port->interrupt_in_urb); + usb_kill_urb(port->interrupt_in_urb); dbg("%s - adding interrupt input", __func__); port->interrupt_in_urb->dev = serial->dev; status = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (status) dev_err(&serial->dev->dev, - "%s - failed submitting interrupt urb," - " error %d\n", - __func__, status); + "%s - failed submitting interrupt urb, error %d\n", + __func__, status); } if (status == 0) { dbg("%s - starting session ...", __func__); garmin_data_p->state = STATE_ACTIVE; status = garmin_write_bulk(port, GARMIN_START_SESSION_REQ, - sizeof(GARMIN_START_SESSION_REQ), - 0); + sizeof(GARMIN_START_SESSION_REQ), 0); if (status >= 0) { @@ -951,14 +946,14 @@ static int garmin_init_session(struct usb_serial_port *port) /* not needed, but the win32 driver does it too ... */ status = garmin_write_bulk(port, - GARMIN_START_SESSION_REQ2, - sizeof(GARMIN_START_SESSION_REQ2), - 0); + GARMIN_START_SESSION_REQ2, + sizeof(GARMIN_START_SESSION_REQ2), 0); if (status >= 0) { status = 0; spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->ignorePkts++; - spin_unlock_irqrestore(&garmin_data_p->lock, flags); + spin_unlock_irqrestore(&garmin_data_p->lock, + flags); } } } @@ -970,11 +965,12 @@ static int garmin_init_session(struct usb_serial_port *port) -static int garmin_open (struct usb_serial_port *port, struct file *filp) +static int garmin_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { unsigned long flags; int status = 0; - struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); + struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); dbg("%s - port %d", __func__, port->number); @@ -983,8 +979,8 @@ static int garmin_open (struct usb_serial_port *port, struct file *filp) * through, otherwise it is scheduled, and with high data rates (like * with OHCI) data can get lost. */ - if (port->tty) - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->mode = initial_mode; @@ -995,23 +991,22 @@ static int garmin_open (struct usb_serial_port *port, struct file *filp) spin_unlock_irqrestore(&garmin_data_p->lock, flags); /* shutdown any bulk reads that might be going on */ - usb_kill_urb (port->write_urb); - usb_kill_urb (port->read_urb); + usb_kill_urb(port->write_urb); + usb_kill_urb(port->read_urb); - if (garmin_data_p->state == STATE_RESET) { + if (garmin_data_p->state == STATE_RESET) status = garmin_init_session(port); - } garmin_data_p->state = STATE_ACTIVE; - return status; } -static void garmin_close (struct usb_serial_port *port, struct file * filp) +static void garmin_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; - struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); + struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); dbg("%s - port %d - mode=%d state=%d flags=0x%X", __func__, port->number, garmin_data_p->mode, @@ -1025,8 +1020,8 @@ static void garmin_close (struct usb_serial_port *port, struct file * filp) garmin_clear(garmin_data_p); /* shutdown our urbs */ - usb_kill_urb (port->read_urb); - usb_kill_urb (port->write_urb); + usb_kill_urb(port->read_urb); + usb_kill_urb(port->write_urb); if (!port->serial->disconnected) { if (noResponseFromAppLayer(garmin_data_p) || @@ -1042,21 +1037,22 @@ static void garmin_close (struct usb_serial_port *port, struct file * filp) mutex_unlock(&port->serial->disc_mutex); } - -static void garmin_write_bulk_callback (struct urb *urb) +static void garmin_write_bulk_callback(struct urb *urb) { unsigned long flags; struct usb_serial_port *port = urb->context; int status = urb->status; if (port) { - struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); + struct garmin_data *garmin_data_p = + usb_get_serial_port_data(port); dbg("%s - port %d", __func__, port->number); if (GARMIN_LAYERID_APPL == getLayerId(urb->transfer_buffer) && (garmin_data_p->mode == MODE_GARMIN_SERIAL)) { - gsp_send_ack(garmin_data_p, ((__u8 *)urb->transfer_buffer)[4]); + gsp_send_ack(garmin_data_p, + ((__u8 *)urb->transfer_buffer)[4]); } if (status) { @@ -1070,20 +1066,21 @@ static void garmin_write_bulk_callback (struct urb *urb) usb_serial_port_softint(port); } - /* Ignore errors that resulted from garmin_write_bulk with dismiss_ack=1 */ + /* Ignore errors that resulted from garmin_write_bulk with + dismiss_ack = 1 */ /* free up the transfer buffer, as usb_free_urb() does not do this */ - kfree (urb->transfer_buffer); + kfree(urb->transfer_buffer); } -static int garmin_write_bulk (struct usb_serial_port *port, +static int garmin_write_bulk(struct usb_serial_port *port, const unsigned char *buf, int count, int dismiss_ack) { unsigned long flags; struct usb_serial *serial = port->serial; - struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); + struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); struct urb *urb; unsigned char *buffer; int status; @@ -1095,7 +1092,7 @@ static int garmin_write_bulk (struct usb_serial_port *port, garmin_data_p->flags &= ~FLAGS_DROP_DATA; spin_unlock_irqrestore(&garmin_data_p->lock, flags); - buffer = kmalloc (count, GFP_ATOMIC); + buffer = kmalloc(count, GFP_ATOMIC); if (!buffer) { dev_err(&port->dev, "out of memory\n"); return -ENOMEM; @@ -1104,17 +1101,17 @@ static int garmin_write_bulk (struct usb_serial_port *port, urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { dev_err(&port->dev, "no more free urbs\n"); - kfree (buffer); + kfree(buffer); return -ENOMEM; } - memcpy (buffer, buf, count); + memcpy(buffer, buf, count); usb_serial_debug_data(debug, &port->dev, __func__, count, buffer); - usb_fill_bulk_urb (urb, serial->dev, - usb_sndbulkpipe (serial->dev, - port->bulk_out_endpointAddress), + usb_fill_bulk_urb(urb, serial->dev, + usb_sndbulkpipe(serial->dev, + port->bulk_out_endpointAddress), buffer, count, garmin_write_bulk_callback, dismiss_ack ? NULL : port); @@ -1132,33 +1129,29 @@ static int garmin_write_bulk (struct usb_serial_port *port, status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { dev_err(&port->dev, - "%s - usb_submit_urb(write bulk) " - "failed with status = %d\n", + "%s - usb_submit_urb(write bulk) failed with status = %d\n", __func__, status); count = status; } /* 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; } - - -static int garmin_write (struct usb_serial_port *port, - const unsigned char *buf, int count) +static int garmin_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { int pktid, pktsiz, len; - struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); + struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); __le32 *privpkt = (__le32 *)garmin_data_p->privpkt; usb_serial_debug_data(debug, &port->dev, __func__, count, buf); /* check for our private packets */ if (count >= GARMIN_PKTHDR_LENGTH) { - len = PRIVPKTSIZ; if (count < len) len = count; @@ -1169,15 +1162,16 @@ static int garmin_write (struct usb_serial_port *port, pktid = getPacketId(garmin_data_p->privpkt); if (count == (GARMIN_PKTHDR_LENGTH+pktsiz) - && GARMIN_LAYERID_PRIVATE == getLayerId(garmin_data_p->privpkt)) { + && GARMIN_LAYERID_PRIVATE == + getLayerId(garmin_data_p->privpkt)) { dbg("%s - processing private request %d", __func__, pktid); - // drop all unfinished transfers + /* drop all unfinished transfers */ garmin_clear(garmin_data_p); - switch(pktid) { + switch (pktid) { case PRIV_PKTID_SET_DEBUG: if (pktsiz != 4) @@ -1226,44 +1220,31 @@ static int garmin_write (struct usb_serial_port *port, } -static int garmin_write_room (struct usb_serial_port *port) +static int garmin_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; /* * Report back the bytes currently available in the output buffer. */ - struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); + struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); return GPS_OUT_BUFSIZ-garmin_data_p->outsize; } -static int garmin_chars_in_buffer (struct usb_serial_port *port) -{ - /* - * Report back the number of bytes currently in our input buffer. - * Will this lock up the driver - the buffer contains an incomplete - * package which will not be written to the device until it - * has been completed ? - */ - //struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); - //return garmin_data_p->insize; - return 0; -} - - -static void garmin_read_process(struct garmin_data * garmin_data_p, +static void garmin_read_process(struct garmin_data *garmin_data_p, unsigned char *data, unsigned data_length) { if (garmin_data_p->flags & FLAGS_DROP_DATA) { /* abort-transfer cmd is actice */ dbg("%s - pkt dropped", __func__); } else if (garmin_data_p->state != STATE_DISCONNECTED && - garmin_data_p->state != STATE_RESET ) { + garmin_data_p->state != STATE_RESET) { /* remember any appl.layer packets, so we know if a reset is required or not when closing the device */ if (0 == memcmp(data, GARMIN_APP_LAYER_REPLY, - sizeof(GARMIN_APP_LAYER_REPLY))) { + sizeof(GARMIN_APP_LAYER_REPLY))) { atomic_inc(&garmin_data_p->resp_count); } @@ -1273,9 +1254,8 @@ static void garmin_read_process(struct garmin_data * garmin_data_p, if (garmin_data_p->flags & FLAGS_QUEUING) { pkt_add(garmin_data_p, data, data_length); } else if (garmin_data_p->mode == MODE_GARMIN_SERIAL) { - if (getLayerId(data) == GARMIN_LAYERID_APPL) { + if (getLayerId(data) == GARMIN_LAYERID_APPL) pkt_add(garmin_data_p, data, data_length); - } } else { send_to_tty(garmin_data_p->port, data, data_length); } @@ -1283,12 +1263,12 @@ static void garmin_read_process(struct garmin_data * garmin_data_p, } -static void garmin_read_bulk_callback (struct urb *urb) +static void garmin_read_bulk_callback(struct urb *urb) { unsigned long flags; struct usb_serial_port *port = urb->context; struct usb_serial *serial = port->serial; - struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); + struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; int status = urb->status; int retval; @@ -1306,7 +1286,7 @@ static void garmin_read_bulk_callback (struct urb *urb) return; } - usb_serial_debug_data(debug, &port->dev, + usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); garmin_read_process(garmin_data_p, data, urb->actual_length); @@ -1340,13 +1320,13 @@ static void garmin_read_bulk_callback (struct urb *urb) } -static void garmin_read_int_callback (struct urb *urb) +static void garmin_read_int_callback(struct urb *urb) { unsigned long flags; int retval; struct usb_serial_port *port = urb->context; struct usb_serial *serial = port->serial; - struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); + struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; int status = urb->status; @@ -1372,30 +1352,31 @@ static void garmin_read_int_callback (struct urb *urb) if (urb->actual_length == sizeof(GARMIN_BULK_IN_AVAIL_REPLY) && 0 == memcmp(data, GARMIN_BULK_IN_AVAIL_REPLY, - sizeof(GARMIN_BULK_IN_AVAIL_REPLY))) { + sizeof(GARMIN_BULK_IN_AVAIL_REPLY))) { dbg("%s - bulk data available.", __func__); if (0 == (garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE)) { /* bulk data available */ - usb_fill_bulk_urb (port->read_urb, serial->dev, - usb_rcvbulkpipe (serial->dev, - port->bulk_in_endpointAddress), + usb_fill_bulk_urb(port->read_urb, serial->dev, + usb_rcvbulkpipe(serial->dev, + port->bulk_in_endpointAddress), port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, garmin_read_bulk_callback, port); retval = usb_submit_urb(port->read_urb, GFP_ATOMIC); if (retval) { dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", - __func__, retval); + "%s - failed submitting read urb, error %d\n", + __func__, retval); } else { spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= FLAGS_BULK_IN_ACTIVE; /* do not send this packet to the user */ garmin_data_p->ignorePkts = 1; - spin_unlock_irqrestore(&garmin_data_p->lock, flags); + spin_unlock_irqrestore(&garmin_data_p->lock, + flags); } } else { /* bulk-in transfer still active */ @@ -1406,15 +1387,15 @@ static void garmin_read_int_callback (struct urb *urb) } else if (urb->actual_length == (4+sizeof(GARMIN_START_SESSION_REPLY)) && 0 == memcmp(data, GARMIN_START_SESSION_REPLY, - sizeof(GARMIN_START_SESSION_REPLY))) { + sizeof(GARMIN_START_SESSION_REPLY))) { spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= FLAGS_SESSION_REPLY1_SEEN; spin_unlock_irqrestore(&garmin_data_p->lock, flags); /* save the serial number */ - garmin_data_p->serial_num - = __le32_to_cpup((__le32*)(data+GARMIN_PKTHDR_LENGTH)); + garmin_data_p->serial_num = __le32_to_cpup( + (__le32 *)(data+GARMIN_PKTHDR_LENGTH)); dbg("%s - start-of-session reply seen - serial %u.", __func__, garmin_data_p->serial_num); @@ -1433,7 +1414,7 @@ static void garmin_read_int_callback (struct urb *urb) } port->interrupt_in_urb->dev = port->serial->dev; - retval = usb_submit_urb (urb, GFP_ATOMIC); + retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) dev_err(&urb->dev->dev, "%s - Error %d submitting interrupt urb\n", @@ -1446,7 +1427,7 @@ static void garmin_read_int_callback (struct urb *urb) * and then sets a timer to call itself again until all queued data * is sent. */ -static int garmin_flush_queue(struct garmin_data * garmin_data_p) +static int garmin_flush_queue(struct garmin_data *garmin_data_p) { unsigned long flags; struct garmin_packet *pkt; @@ -1468,10 +1449,11 @@ static int garmin_flush_queue(struct garmin_data * garmin_data_p) } -static void garmin_throttle (struct usb_serial_port *port) +static void garmin_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; + struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); unsigned long flags; - struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); dbg("%s - port %d", __func__, port->number); /* set flag, data received will be put into a queue @@ -1482,10 +1464,11 @@ static void garmin_throttle (struct usb_serial_port *port) } -static void garmin_unthrottle (struct usb_serial_port *port) +static void garmin_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; + struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); unsigned long flags; - struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); int status; dbg("%s - port %d", __func__, port->number); @@ -1507,8 +1490,6 @@ static void garmin_unthrottle (struct usb_serial_port *port) } } - - /* * The timer is currently only used to send queued packets to * the tty in cases where the protocol provides no own handshaking @@ -1526,11 +1507,11 @@ static void timeout_handler(unsigned long data) -static int garmin_attach (struct usb_serial *serial) +static int garmin_attach(struct usb_serial *serial) { int status = 0; struct usb_serial_port *port = serial->port[0]; - struct garmin_data * garmin_data_p = NULL; + struct garmin_data *garmin_data_p = NULL; dbg("%s", __func__); @@ -1542,7 +1523,7 @@ static int garmin_attach (struct usb_serial *serial) init_timer(&garmin_data_p->timer); spin_lock_init(&garmin_data_p->lock); INIT_LIST_HEAD(&garmin_data_p->pktlist); - //garmin_data_p->timer.expires = jiffies + session_timeout; + /* garmin_data_p->timer.expires = jiffies + session_timeout; */ garmin_data_p->timer.data = (unsigned long)garmin_data_p; garmin_data_p->timer.function = timeout_handler; garmin_data_p->port = port; @@ -1556,16 +1537,16 @@ static int garmin_attach (struct usb_serial *serial) } -static void garmin_shutdown (struct usb_serial *serial) +static void garmin_shutdown(struct usb_serial *serial) { struct usb_serial_port *port = serial->port[0]; - struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); + struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); dbg("%s", __func__); - usb_kill_urb (port->interrupt_in_urb); + usb_kill_urb(port->interrupt_in_urb); del_timer_sync(&garmin_data_p->timer); - kfree (garmin_data_p); + kfree(garmin_data_p); usb_set_serial_port_data(port, NULL); } @@ -1588,7 +1569,6 @@ static struct usb_serial_driver garmin_device = { .shutdown = garmin_shutdown, .write = garmin_write, .write_room = garmin_write_room, - .chars_in_buffer = garmin_chars_in_buffer, .write_bulk_callback = garmin_write_bulk_callback, .read_bulk_callback = garmin_read_bulk_callback, .read_int_callback = garmin_read_int_callback, @@ -1596,7 +1576,7 @@ static struct usb_serial_driver garmin_device = { -static int __init garmin_init (void) +static int __init garmin_init(void) { int retval; @@ -1616,10 +1596,10 @@ failed_garmin_register: } -static void __exit garmin_exit (void) +static void __exit garmin_exit(void) { - usb_deregister (&garmin_driver); - usb_serial_deregister (&garmin_device); + usb_deregister(&garmin_driver); + usb_serial_deregister(&garmin_device); } @@ -1628,8 +1608,8 @@ static void __exit garmin_exit (void) module_init(garmin_init); module_exit(garmin_exit); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); module_param(debug, bool, S_IWUSR | S_IRUGO); diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 537f12a027c2..fe84c88ec20c 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -18,7 +18,7 @@ #include <linux/moduleparam.h> #include <linux/usb.h> #include <linux/usb/serial.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> static int debug; @@ -81,7 +81,7 @@ static int generic_probe(struct usb_interface *interface, } #endif -int usb_serial_generic_register (int _debug) +int usb_serial_generic_register(int _debug) { int retval = 0; @@ -89,10 +89,11 @@ int usb_serial_generic_register (int _debug) #ifdef CONFIG_USB_SERIAL_GENERIC generic_device_ids[0].idVendor = vendor; generic_device_ids[0].idProduct = product; - generic_device_ids[0].match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT; + generic_device_ids[0].match_flags = + USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT; /* register our generic driver with ourselves */ - retval = usb_serial_register (&usb_serial_generic_device); + retval = usb_serial_register(&usb_serial_generic_device); if (retval) goto exit; retval = usb_register(&generic_driver); @@ -103,16 +104,17 @@ exit: return retval; } -void usb_serial_generic_deregister (void) +void usb_serial_generic_deregister(void) { #ifdef CONFIG_USB_SERIAL_GENERIC /* remove our generic driver */ usb_deregister(&generic_driver); - usb_serial_deregister (&usb_serial_generic_device); + usb_serial_deregister(&usb_serial_generic_device); #endif } -int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp) +int usb_serial_generic_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; int result = 0; @@ -120,11 +122,11 @@ int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp) dbg("%s - port %d", __func__, port->number); - /* force low_latency on so that our tty_push actually forces the data through, - otherwise it is scheduled, and with high data rates (like with OHCI) data - can get lost. */ - if (port->tty) - port->tty->low_latency = 1; + /* force low_latency on so that our tty_push actually forces the data + through, otherwise it is scheduled, and with high data rates (like + with OHCI) data can get lost. */ + if (tty) + tty->low_latency = 1; /* clear the throttle flags */ spin_lock_irqsave(&port->lock, flags); @@ -135,8 +137,9 @@ int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp) /* 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, - usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), + usb_fill_bulk_urb(port->read_urb, serial->dev, + usb_rcvbulkpipe(serial->dev, + port->bulk_in_endpointAddress), port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, ((serial->type->read_bulk_callback) ? @@ -145,14 +148,16 @@ int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp) port); result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result) - dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __func__, result); + dev_err(&port->dev, + "%s - failed resubmitting read urb, error %d\n", + __func__, result); } return result; } EXPORT_SYMBOL_GPL(usb_serial_generic_open); -static void generic_cleanup (struct usb_serial_port *port) +static void generic_cleanup(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; @@ -182,7 +187,7 @@ int usb_serial_generic_resume(struct usb_serial *serial) #endif for (i = 0; i < serial->num_ports; i++) { port = serial->port[i]; - if (port->open_count && port->read_urb) { + if (port->port.count && port->read_urb) { r = usb_submit_urb(port->read_urb, GFP_NOIO); if (r < 0) c++; @@ -192,13 +197,15 @@ int usb_serial_generic_resume(struct usb_serial *serial) return c ? -EIO : 0; } -void usb_serial_generic_close (struct usb_serial_port *port, struct file * filp) +void usb_serial_generic_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { dbg("%s - port %d", __func__, port->number); - generic_cleanup (port); + generic_cleanup(port); } -int usb_serial_generic_write(struct usb_serial_port *port, const unsigned char *buf, int count) +int usb_serial_generic_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; int result; @@ -208,7 +215,7 @@ int usb_serial_generic_write(struct usb_serial_port *port, const unsigned char * if (count == 0) { dbg("%s - write request of 0 bytes", __func__); - return (0); + return 0; } /* only do something if we have a bulk out endpoint */ @@ -223,27 +230,32 @@ int usb_serial_generic_write(struct usb_serial_port *port, const unsigned char * port->write_urb_busy = 1; spin_unlock_irqrestore(&port->lock, flags); - count = (count > port->bulk_out_size) ? port->bulk_out_size : count; + count = (count > port->bulk_out_size) ? + port->bulk_out_size : count; - memcpy (port->write_urb->transfer_buffer, buf, count); + memcpy(port->write_urb->transfer_buffer, buf, count); data = port->write_urb->transfer_buffer; usb_serial_debug_data(debug, &port->dev, __func__, count, data); /* set up our urb */ - usb_fill_bulk_urb (port->write_urb, serial->dev, - usb_sndbulkpipe (serial->dev, - port->bulk_out_endpointAddress), + usb_fill_bulk_urb(port->write_urb, serial->dev, + usb_sndbulkpipe(serial->dev, + port->bulk_out_endpointAddress), port->write_urb->transfer_buffer, count, - ((serial->type->write_bulk_callback) ? + ((serial->type->write_bulk_callback) ? serial->type->write_bulk_callback : - usb_serial_generic_write_bulk_callback), port); + usb_serial_generic_write_bulk_callback), + port); /* send the data out the bulk port */ port->write_urb_busy = 1; result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result) { - dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __func__, result); - /* don't have to grab the lock here, as we will retry if != 0 */ + dev_err(&port->dev, + "%s - failed submitting write urb, error %d\n", + __func__, result); + /* don't have to grab the lock here, as we will + retry if != 0 */ port->write_urb_busy = 0; } else result = count; @@ -255,8 +267,9 @@ int usb_serial_generic_write(struct usb_serial_port *port, const unsigned char * return 0; } -int usb_serial_generic_write_room (struct usb_serial_port *port) +int usb_serial_generic_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; int room = 0; @@ -272,8 +285,9 @@ int usb_serial_generic_write_room (struct usb_serial_port *port) return room; } -int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port) +int usb_serial_generic_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; int chars = 0; @@ -286,7 +300,7 @@ int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port) } dbg("%s - returns %d", __func__, chars); - return (chars); + return chars; } @@ -297,24 +311,26 @@ static void resubmit_read_urb(struct usb_serial_port *port, gfp_t mem_flags) int result; /* Continue reading from device */ - usb_fill_bulk_urb (urb, serial->dev, - usb_rcvbulkpipe (serial->dev, - port->bulk_in_endpointAddress), + usb_fill_bulk_urb(urb, serial->dev, + usb_rcvbulkpipe(serial->dev, + port->bulk_in_endpointAddress), urb->transfer_buffer, urb->transfer_buffer_length, - ((serial->type->read_bulk_callback) ? - serial->type->read_bulk_callback : + ((serial->type->read_bulk_callback) ? + serial->type->read_bulk_callback : usb_serial_generic_read_bulk_callback), port); result = usb_submit_urb(urb, mem_flags); if (result) - dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __func__, result); + dev_err(&port->dev, + "%s - failed resubmitting read urb, error %d\n", + __func__, result); } /* Push data to tty layer and resubmit the bulk read URB */ -static void flush_and_resubmit_read_urb (struct usb_serial_port *port) +static void flush_and_resubmit_read_urb(struct usb_serial_port *port) { struct urb *urb = port->read_urb; - struct tty_struct *tty = port->tty; + struct tty_struct *tty = port->port.tty; int room; /* Push data to tty */ @@ -329,7 +345,7 @@ static void flush_and_resubmit_read_urb (struct usb_serial_port *port) resubmit_read_urb(port, GFP_ATOMIC); } -void usb_serial_generic_read_bulk_callback (struct urb *urb) +void usb_serial_generic_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; unsigned char *data = urb->transfer_buffer; @@ -344,20 +360,21 @@ void usb_serial_generic_read_bulk_callback (struct urb *urb) return; } - usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, data); /* Throttle the device if requested by tty */ spin_lock_irqsave(&port->lock, flags); - if (!(port->throttled = port->throttle_req)) { + port->throttled = port->throttle_req; + if (!port->throttled) { spin_unlock_irqrestore(&port->lock, flags); flush_and_resubmit_read_urb(port); - } else { + } else spin_unlock_irqrestore(&port->lock, flags); - } } EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); -void usb_serial_generic_write_bulk_callback (struct urb *urb) +void usb_serial_generic_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; int status = urb->status; @@ -374,8 +391,9 @@ 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) +void usb_serial_generic_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; unsigned long flags; dbg("%s - port %d", __func__, port->number); @@ -387,8 +405,9 @@ void usb_serial_generic_throttle (struct usb_serial_port *port) spin_unlock_irqrestore(&port->lock, flags); } -void usb_serial_generic_unthrottle (struct usb_serial_port *port) +void usb_serial_generic_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int was_throttled; unsigned long flags; @@ -406,15 +425,14 @@ void usb_serial_generic_unthrottle (struct usb_serial_port *port) } } -void usb_serial_generic_shutdown (struct usb_serial *serial) +void usb_serial_generic_shutdown(struct usb_serial *serial) { int i; dbg("%s", __func__); /* stop reads and writes on all ports */ - for (i=0; i < serial->num_ports; ++i) { + for (i = 0; i < serial->num_ports; ++i) generic_cleanup(serial->port[i]); - } } diff --git a/drivers/usb/serial/hp4x.c b/drivers/usb/serial/hp4x.c index 75b88b356ebc..ab905869e959 100644 --- a/drivers/usb/serial/hp4x.c +++ b/drivers/usb/serial/hp4x.c @@ -9,7 +9,8 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * See Documentation/usb/usb-serial.txt for more information on using this driver + * See Documentation/usb/usb-serial.txt for more information on using this + * driver */ #include <linux/kernel.h> diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 2fd449bcfa35..bfa508ddb0fe 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -44,7 +44,7 @@ #include <linux/wait.h> #include <linux/firmware.h> #include <linux/ihex.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include "io_edgeport.h" @@ -66,16 +66,16 @@ /* receive port state */ enum RXSTATE { - EXPECT_HDR1 = 0, /* Expect header byte 1 */ - EXPECT_HDR2 = 1, /* Expect header byte 2 */ - EXPECT_DATA = 2, /* Expect 'RxBytesRemaining' data */ - EXPECT_HDR3 = 3, /* Expect header byte 3 (for status hdrs only) */ + EXPECT_HDR1 = 0, /* Expect header byte 1 */ + EXPECT_HDR2 = 1, /* Expect header byte 2 */ + EXPECT_DATA = 2, /* Expect 'RxBytesRemaining' data */ + EXPECT_HDR3 = 3, /* Expect header byte 3 (for status hdrs only) */ }; -/* Transmit Fifo - * This Transmit queue is an extension of the edgeport Rx buffer. - * The maximum amount of data buffered in both the edgeport +/* Transmit Fifo + * This Transmit queue is an extension of the edgeport Rx buffer. + * The maximum amount of data buffered in both the edgeport * Rx buffer (maxTxCredits) and this buffer will never exceed maxTxCredits. */ struct TxFifo { @@ -132,12 +132,12 @@ struct edgeport_serial { int is_epic; /* flag if EPiC device or not */ __u8 interrupt_in_endpoint; /* the interrupt endpoint handle */ - unsigned char * interrupt_in_buffer; /* the buffer we use for the interrupt endpoint */ - struct urb * interrupt_read_urb; /* our interrupt urb */ + unsigned char *interrupt_in_buffer; /* the buffer we use for the interrupt endpoint */ + struct urb *interrupt_read_urb; /* our interrupt urb */ __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 */ + unsigned char *bulk_in_buffer; /* the buffer we use for the bulk in endpoint */ + struct urb *read_urb; /* our bulk read urb */ bool read_in_progress; spinlock_t es_lock; @@ -162,16 +162,17 @@ struct divisor_table_entry { __u16 Divisor; }; -// -// Define table of divisors for Rev A EdgePort/4 hardware -// These assume a 3.6864MHz crystal, the standard /16, and -// MCR.7 = 0. -// +/* + * Define table of divisors for Rev A EdgePort/4 hardware + * These assume a 3.6864MHz crystal, the standard /16, and + * MCR.7 = 0. + */ + static const struct divisor_table_entry divisor_table[] = { - { 50, 4608}, - { 75, 3072}, - { 110, 2095}, /* 2094.545455 => 230450 => .0217 % over */ - { 134, 1713}, /* 1713.011152 => 230398.5 => .00065% under */ + { 50, 4608}, + { 75, 3072}, + { 110, 2095}, /* 2094.545455 => 230450 => .0217 % over */ + { 134, 1713}, /* 1713.011152 => 230398.5 => .00065% under */ { 150, 1536}, { 300, 768}, { 600, 384}, @@ -194,64 +195,86 @@ static int debug; static int low_latency = 1; /* tty low latency flag, on by default */ -static atomic_t CmdUrbs; /* Number of outstanding Command Write Urbs */ +static atomic_t CmdUrbs; /* Number of outstanding Command Write Urbs */ /* local function prototypes */ /* function prototypes for all URB callbacks */ -static void edge_interrupt_callback (struct urb *urb); -static void edge_bulk_in_callback (struct urb *urb); -static void edge_bulk_out_data_callback (struct urb *urb); -static void edge_bulk_out_cmd_callback (struct urb *urb); +static void edge_interrupt_callback(struct urb *urb); +static void edge_bulk_in_callback(struct urb *urb); +static void edge_bulk_out_data_callback(struct urb *urb); +static void edge_bulk_out_cmd_callback(struct urb *urb); /* function prototypes for the usbserial callbacks */ -static int edge_open (struct usb_serial_port *port, struct file *filp); -static void edge_close (struct usb_serial_port *port, struct file *filp); -static int edge_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static int edge_write_room (struct usb_serial_port *port); -static int edge_chars_in_buffer (struct usb_serial_port *port); -static void edge_throttle (struct usb_serial_port *port); -static void edge_unthrottle (struct usb_serial_port *port); -static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old_termios); -static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg); -static void edge_break (struct usb_serial_port *port, int break_state); -static int edge_tiocmget (struct usb_serial_port *port, struct file *file); -static int edge_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); -static int edge_startup (struct usb_serial *serial); -static void edge_shutdown (struct usb_serial *serial); - +static int edge_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); +static void edge_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); +static int edge_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); +static int edge_write_room(struct tty_struct *tty); +static int edge_chars_in_buffer(struct tty_struct *tty); +static void edge_throttle(struct tty_struct *tty); +static void edge_unthrottle(struct tty_struct *tty); +static void edge_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, + struct ktermios *old_termios); +static int edge_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg); +static void edge_break(struct tty_struct *tty, int break_state); +static int edge_tiocmget(struct tty_struct *tty, struct file *file); +static int edge_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear); +static int edge_startup(struct usb_serial *serial); +static void edge_shutdown(struct usb_serial *serial); #include "io_tables.h" /* all of the devices that this driver supports */ /* function prototypes for all of our local functions */ -static void process_rcvd_data (struct edgeport_serial *edge_serial, unsigned char *buffer, __u16 bufferLength); -static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2, __u8 byte3); -static void edge_tty_recv (struct device *dev, struct tty_struct *tty, unsigned char *data, int length); -static void handle_new_msr (struct edgeport_port *edge_port, __u8 newMsr); -static void handle_new_lsr (struct edgeport_port *edge_port, __u8 lsrData, __u8 lsr, __u8 data); -static int send_iosp_ext_cmd (struct edgeport_port *edge_port, __u8 command, __u8 param); -static int calc_baud_rate_divisor (int baud_rate, int *divisor); -static int send_cmd_write_baud_rate (struct edgeport_port *edge_port, int baudRate); -static void change_port_settings (struct edgeport_port *edge_port, struct ktermios *old_termios); -static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 regNum, __u8 regValue); -static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer, int writeLength); -static void send_more_port_data (struct edgeport_serial *edge_serial, struct edgeport_port *edge_port); - -static int sram_write (struct usb_serial *serial, __u16 extAddr, __u16 addr, __u16 length, const __u8 *data); -static int rom_read (struct usb_serial *serial, __u16 extAddr, __u16 addr, __u16 length, __u8 *data); -static int rom_write (struct usb_serial *serial, __u16 extAddr, __u16 addr, __u16 length, const __u8 *data); -static void get_manufacturing_desc (struct edgeport_serial *edge_serial); -static void get_boot_desc (struct edgeport_serial *edge_serial); -static void load_application_firmware (struct edgeport_serial *edge_serial); - -static void unicode_to_ascii(char *string, int buflen, __le16 *unicode, int unicode_size); - - -// ************************************************************************ -// ************************************************************************ -// ************************************************************************ -// ************************************************************************ + +static void process_rcvd_data(struct edgeport_serial *edge_serial, + unsigned char *buffer, __u16 bufferLength); +static void process_rcvd_status(struct edgeport_serial *edge_serial, + __u8 byte2, __u8 byte3); +static void edge_tty_recv(struct device *dev, struct tty_struct *tty, + unsigned char *data, int length); +static void handle_new_msr(struct edgeport_port *edge_port, __u8 newMsr); +static void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData, + __u8 lsr, __u8 data); +static int send_iosp_ext_cmd(struct edgeport_port *edge_port, __u8 command, + __u8 param); +static int calc_baud_rate_divisor(int baud_rate, int *divisor); +static int send_cmd_write_baud_rate(struct edgeport_port *edge_port, + int baudRate); +static void change_port_settings(struct tty_struct *tty, + struct edgeport_port *edge_port, + struct ktermios *old_termios); +static int send_cmd_write_uart_register(struct edgeport_port *edge_port, + __u8 regNum, __u8 regValue); +static int write_cmd_usb(struct edgeport_port *edge_port, + unsigned char *buffer, int writeLength); +static void send_more_port_data(struct edgeport_serial *edge_serial, + struct edgeport_port *edge_port); + +static int sram_write(struct usb_serial *serial, __u16 extAddr, __u16 addr, + __u16 length, const __u8 *data); +static int rom_read(struct usb_serial *serial, __u16 extAddr, __u16 addr, + __u16 length, __u8 *data); +static int rom_write(struct usb_serial *serial, __u16 extAddr, __u16 addr, + __u16 length, const __u8 *data); +static void get_manufacturing_desc(struct edgeport_serial *edge_serial); +static void get_boot_desc(struct edgeport_serial *edge_serial); +static void load_application_firmware(struct edgeport_serial *edge_serial); + +static void unicode_to_ascii(char *string, int buflen, + __le16 *unicode, int unicode_size); + + +/* ************************************************************************ */ +/* ************************************************************************ */ +/* ************************************************************************ */ +/* ************************************************************************ */ /************************************************************************ * * @@ -261,7 +284,7 @@ static void unicode_to_ascii(char *string, int buflen, __le16 *unicode, int unic * embedded in this driver * * * ************************************************************************/ -static void update_edgeport_E2PROM (struct edgeport_serial *edge_serial) +static void update_edgeport_E2PROM(struct edgeport_serial *edge_serial) { __u32 BootCurVer; __u32 BootNewVer; @@ -275,16 +298,14 @@ static void update_edgeport_E2PROM (struct edgeport_serial *edge_serial) int response; switch (edge_serial->product_info.iDownloadFile) { - case EDGE_DOWNLOAD_FILE_I930: - fw_name = "edgeport/boot.fw"; - break; - - case EDGE_DOWNLOAD_FILE_80251: - fw_name = "edgeport/boot2.fw"; - break; - - default: - return; + case EDGE_DOWNLOAD_FILE_I930: + fw_name = "edgeport/boot.fw"; + break; + case EDGE_DOWNLOAD_FILE_80251: + fw_name = "edgeport/boot2.fw"; + break; + default: + return; } response = request_ihex_firmware(&fw, fw_name, @@ -300,7 +321,7 @@ static void update_edgeport_E2PROM (struct edgeport_serial *edge_serial) BootMinorVersion = rec->data[1]; BootBuildNumber = (rec->data[2] << 8) | rec->data[3]; - // Check Boot Image Version + /* Check Boot Image Version */ BootCurVer = (edge_serial->boot_descriptor.MajorVersion << 24) + (edge_serial->boot_descriptor.MinorVersion << 16) + le16_to_cpu(edge_serial->boot_descriptor.BuildNumber); @@ -352,29 +373,29 @@ static void update_edgeport_E2PROM (struct edgeport_serial *edge_serial) * Get string descriptor from device * * * ************************************************************************/ -static int get_string (struct usb_device *dev, int Id, char *string, int buflen) +static int get_string(struct usb_device *dev, int Id, char *string, int buflen) { struct usb_string_descriptor StringDesc; struct usb_string_descriptor *pStringDesc; - dbg("%s - USB String ID = %d", __func__, Id ); + dbg("%s - USB String ID = %d", __func__, Id); - if (!usb_get_descriptor(dev, USB_DT_STRING, Id, &StringDesc, sizeof(StringDesc))) { + if (!usb_get_descriptor(dev, USB_DT_STRING, Id, + &StringDesc, sizeof(StringDesc))) return 0; - } - pStringDesc = kmalloc (StringDesc.bLength, GFP_KERNEL); - - if (!pStringDesc) { + pStringDesc = kmalloc(StringDesc.bLength, GFP_KERNEL); + if (!pStringDesc) return 0; - } - if (!usb_get_descriptor(dev, USB_DT_STRING, Id, pStringDesc, StringDesc.bLength )) { + if (!usb_get_descriptor(dev, USB_DT_STRING, Id, + pStringDesc, StringDesc.bLength)) { kfree(pStringDesc); return 0; } - unicode_to_ascii(string, buflen, pStringDesc->wData, pStringDesc->bLength/2); + unicode_to_ascii(string, buflen, + pStringDesc->wData, pStringDesc->bLength/2); kfree(pStringDesc); dbg("%s - USB String %s", __func__, string); @@ -388,24 +409,24 @@ static int get_string (struct usb_device *dev, int Id, char *string, int buflen) * Get string descriptor from device * ************************************************************************/ -static int get_string_desc (struct usb_device *dev, int Id, struct usb_string_descriptor **pRetDesc) +static int get_string_desc(struct usb_device *dev, int Id, + struct usb_string_descriptor **pRetDesc) { struct usb_string_descriptor StringDesc; struct usb_string_descriptor *pStringDesc; - dbg("%s - USB String ID = %d", __func__, Id ); + dbg("%s - USB String ID = %d", __func__, Id); - if (!usb_get_descriptor(dev, USB_DT_STRING, Id, &StringDesc, sizeof(StringDesc))) { + if (!usb_get_descriptor(dev, USB_DT_STRING, Id, &StringDesc, + sizeof(StringDesc))) return 0; - } - pStringDesc = kmalloc (StringDesc.bLength, GFP_KERNEL); - - if (!pStringDesc) { + pStringDesc = kmalloc(StringDesc.bLength, GFP_KERNEL); + if (!pStringDesc) return -1; - } - if (!usb_get_descriptor(dev, USB_DT_STRING, Id, pStringDesc, StringDesc.bLength )) { + if (!usb_get_descriptor(dev, USB_DT_STRING, Id, pStringDesc, + StringDesc.bLength)) { kfree(pStringDesc); return -1; } @@ -417,25 +438,30 @@ static int get_string_desc (struct usb_device *dev, int Id, struct usb_string_de static void dump_product_info(struct edgeport_product_info *product_info) { - // Dump Product Info structure + /* Dump Product Info structure */ dbg("**Product Information:"); - dbg(" ProductId %x", product_info->ProductId ); - dbg(" NumPorts %d", product_info->NumPorts ); - dbg(" ProdInfoVer %d", product_info->ProdInfoVer ); + dbg(" ProductId %x", product_info->ProductId); + dbg(" NumPorts %d", product_info->NumPorts); + dbg(" ProdInfoVer %d", product_info->ProdInfoVer); dbg(" IsServer %d", product_info->IsServer); - dbg(" IsRS232 %d", product_info->IsRS232 ); - dbg(" IsRS422 %d", product_info->IsRS422 ); - dbg(" IsRS485 %d", product_info->IsRS485 ); - dbg(" RomSize %d", product_info->RomSize ); - dbg(" RamSize %d", product_info->RamSize ); - dbg(" CpuRev %x", product_info->CpuRev ); + dbg(" IsRS232 %d", product_info->IsRS232); + dbg(" IsRS422 %d", product_info->IsRS422); + dbg(" IsRS485 %d", product_info->IsRS485); + dbg(" RomSize %d", product_info->RomSize); + dbg(" RamSize %d", product_info->RamSize); + dbg(" CpuRev %x", product_info->CpuRev); dbg(" BoardRev %x", product_info->BoardRev); dbg(" BootMajorVersion %d.%d.%d", product_info->BootMajorVersion, product_info->BootMinorVersion, le16_to_cpu(product_info->BootBuildNumber)); - dbg(" ManufactureDescDate %d/%d/%d", product_info->ManufactureDescDate[0], - product_info->ManufactureDescDate[1], - product_info->ManufactureDescDate[2]+1900); + dbg(" FirmwareMajorVersion %d.%d.%d", + product_info->FirmwareMajorVersion, + product_info->FirmwareMinorVersion, + le16_to_cpu(product_info->FirmwareBuildNumber)); + dbg(" ManufactureDescDate %d/%d/%d", + product_info->ManufactureDescDate[0], + product_info->ManufactureDescDate[1], + product_info->ManufactureDescDate[2]+1900); dbg(" iDownloadFile 0x%x", product_info->iDownloadFile); dbg(" EpicVer %d", product_info->EpicVer); } @@ -444,55 +470,60 @@ static void get_product_info(struct edgeport_serial *edge_serial) { struct edgeport_product_info *product_info = &edge_serial->product_info; - memset (product_info, 0, sizeof(struct edgeport_product_info)); - - product_info->ProductId = (__u16)(le16_to_cpu(edge_serial->serial->dev->descriptor.idProduct) & ~ION_DEVICE_ID_80251_NETCHIP); - product_info->NumPorts = edge_serial->manuf_descriptor.NumPorts; - product_info->ProdInfoVer = 0; - - product_info->RomSize = edge_serial->manuf_descriptor.RomSize; - product_info->RamSize = edge_serial->manuf_descriptor.RamSize; - product_info->CpuRev = edge_serial->manuf_descriptor.CpuRev; - product_info->BoardRev = edge_serial->manuf_descriptor.BoardRev; - - product_info->BootMajorVersion = edge_serial->boot_descriptor.MajorVersion; - product_info->BootMinorVersion = edge_serial->boot_descriptor.MinorVersion; - product_info->BootBuildNumber = edge_serial->boot_descriptor.BuildNumber; - - memcpy(product_info->ManufactureDescDate, edge_serial->manuf_descriptor.DescDate, sizeof(edge_serial->manuf_descriptor.DescDate)); - - // check if this is 2nd generation hardware - if (le16_to_cpu(edge_serial->serial->dev->descriptor.idProduct) & ION_DEVICE_ID_80251_NETCHIP) { - product_info->iDownloadFile = EDGE_DOWNLOAD_FILE_80251; - } else { - product_info->iDownloadFile = EDGE_DOWNLOAD_FILE_I930; - } - - // Determine Product type and set appropriate flags + memset(product_info, 0, sizeof(struct edgeport_product_info)); + + product_info->ProductId = (__u16)(le16_to_cpu(edge_serial->serial->dev->descriptor.idProduct) & ~ION_DEVICE_ID_80251_NETCHIP); + product_info->NumPorts = edge_serial->manuf_descriptor.NumPorts; + product_info->ProdInfoVer = 0; + + product_info->RomSize = edge_serial->manuf_descriptor.RomSize; + product_info->RamSize = edge_serial->manuf_descriptor.RamSize; + product_info->CpuRev = edge_serial->manuf_descriptor.CpuRev; + product_info->BoardRev = edge_serial->manuf_descriptor.BoardRev; + + product_info->BootMajorVersion = + edge_serial->boot_descriptor.MajorVersion; + product_info->BootMinorVersion = + edge_serial->boot_descriptor.MinorVersion; + product_info->BootBuildNumber = + edge_serial->boot_descriptor.BuildNumber; + + memcpy(product_info->ManufactureDescDate, + edge_serial->manuf_descriptor.DescDate, + sizeof(edge_serial->manuf_descriptor.DescDate)); + + /* check if this is 2nd generation hardware */ + if (le16_to_cpu(edge_serial->serial->dev->descriptor.idProduct) + & ION_DEVICE_ID_80251_NETCHIP) + product_info->iDownloadFile = EDGE_DOWNLOAD_FILE_80251; + else + product_info->iDownloadFile = EDGE_DOWNLOAD_FILE_I930; + + /* Determine Product type and set appropriate flags */ switch (DEVICE_ID_FROM_USB_PRODUCT_ID(product_info->ProductId)) { - case ION_DEVICE_ID_EDGEPORT_COMPATIBLE: - case ION_DEVICE_ID_EDGEPORT_4T: - case ION_DEVICE_ID_EDGEPORT_4: - case ION_DEVICE_ID_EDGEPORT_2: - case ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU: - case ION_DEVICE_ID_EDGEPORT_8: - case ION_DEVICE_ID_EDGEPORT_421: - case ION_DEVICE_ID_EDGEPORT_21: - case ION_DEVICE_ID_EDGEPORT_2_DIN: - case ION_DEVICE_ID_EDGEPORT_4_DIN: - case ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU: - product_info->IsRS232 = 1; - break; + case ION_DEVICE_ID_EDGEPORT_COMPATIBLE: + case ION_DEVICE_ID_EDGEPORT_4T: + case ION_DEVICE_ID_EDGEPORT_4: + case ION_DEVICE_ID_EDGEPORT_2: + case ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU: + case ION_DEVICE_ID_EDGEPORT_8: + case ION_DEVICE_ID_EDGEPORT_421: + case ION_DEVICE_ID_EDGEPORT_21: + case ION_DEVICE_ID_EDGEPORT_2_DIN: + case ION_DEVICE_ID_EDGEPORT_4_DIN: + case ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU: + product_info->IsRS232 = 1; + break; - case ION_DEVICE_ID_EDGEPORT_2I: // Edgeport/2 RS422/RS485 - product_info->IsRS422 = 1; - product_info->IsRS485 = 1; - break; + case ION_DEVICE_ID_EDGEPORT_2I: /* Edgeport/2 RS422/RS485 */ + product_info->IsRS422 = 1; + product_info->IsRS485 = 1; + break; - case ION_DEVICE_ID_EDGEPORT_8I: // Edgeport/4 RS422 - case ION_DEVICE_ID_EDGEPORT_4I: // Edgeport/4 RS422 - product_info->IsRS422 = 1; - break; + case ION_DEVICE_ID_EDGEPORT_8I: /* Edgeport/4 RS422 */ + case ION_DEVICE_ID_EDGEPORT_4I: /* Edgeport/4 RS422 */ + product_info->IsRS422 = 1; + break; } dump_product_info(product_info); @@ -520,32 +551,32 @@ static int get_epic_descriptor(struct edgeport_serial *ep) ep->is_epic = 1; memset(product_info, 0, sizeof(struct edgeport_product_info)); - product_info->NumPorts = epic->NumPorts; - product_info->ProdInfoVer = 0; - product_info->FirmwareMajorVersion = epic->MajorVersion; - product_info->FirmwareMinorVersion = epic->MinorVersion; - product_info->FirmwareBuildNumber = epic->BuildNumber; - product_info->iDownloadFile = epic->iDownloadFile; - product_info->EpicVer = epic->EpicVer; - product_info->Epic = epic->Supports; - product_info->ProductId = ION_DEVICE_ID_EDGEPORT_COMPATIBLE; + product_info->NumPorts = epic->NumPorts; + product_info->ProdInfoVer = 0; + product_info->FirmwareMajorVersion = epic->MajorVersion; + product_info->FirmwareMinorVersion = epic->MinorVersion; + product_info->FirmwareBuildNumber = epic->BuildNumber; + product_info->iDownloadFile = epic->iDownloadFile; + product_info->EpicVer = epic->EpicVer; + product_info->Epic = epic->Supports; + product_info->ProductId = ION_DEVICE_ID_EDGEPORT_COMPATIBLE; dump_product_info(product_info); bits = &ep->epic_descriptor.Supports; dbg("**EPIC descriptor:"); dbg(" VendEnableSuspend: %s", bits->VendEnableSuspend ? "TRUE": "FALSE"); - dbg(" IOSPOpen : %s", bits->IOSPOpen ? "TRUE": "FALSE" ); - dbg(" IOSPClose : %s", bits->IOSPClose ? "TRUE": "FALSE" ); - dbg(" IOSPChase : %s", bits->IOSPChase ? "TRUE": "FALSE" ); - dbg(" IOSPSetRxFlow : %s", bits->IOSPSetRxFlow ? "TRUE": "FALSE" ); - dbg(" IOSPSetTxFlow : %s", bits->IOSPSetTxFlow ? "TRUE": "FALSE" ); - dbg(" IOSPSetXChar : %s", bits->IOSPSetXChar ? "TRUE": "FALSE" ); - dbg(" IOSPRxCheck : %s", bits->IOSPRxCheck ? "TRUE": "FALSE" ); - dbg(" IOSPSetClrBreak : %s", bits->IOSPSetClrBreak ? "TRUE": "FALSE" ); - dbg(" IOSPWriteMCR : %s", bits->IOSPWriteMCR ? "TRUE": "FALSE" ); - dbg(" IOSPWriteLCR : %s", bits->IOSPWriteLCR ? "TRUE": "FALSE" ); - dbg(" IOSPSetBaudRate : %s", bits->IOSPSetBaudRate ? "TRUE": "FALSE" ); - dbg(" TrueEdgeport : %s", bits->TrueEdgeport ? "TRUE": "FALSE" ); + dbg(" IOSPOpen : %s", bits->IOSPOpen ? "TRUE": "FALSE"); + dbg(" IOSPClose : %s", bits->IOSPClose ? "TRUE": "FALSE"); + dbg(" IOSPChase : %s", bits->IOSPChase ? "TRUE": "FALSE"); + dbg(" IOSPSetRxFlow : %s", bits->IOSPSetRxFlow ? "TRUE": "FALSE"); + dbg(" IOSPSetTxFlow : %s", bits->IOSPSetTxFlow ? "TRUE": "FALSE"); + dbg(" IOSPSetXChar : %s", bits->IOSPSetXChar ? "TRUE": "FALSE"); + dbg(" IOSPRxCheck : %s", bits->IOSPRxCheck ? "TRUE": "FALSE"); + dbg(" IOSPSetClrBreak : %s", bits->IOSPSetClrBreak ? "TRUE": "FALSE"); + dbg(" IOSPWriteMCR : %s", bits->IOSPWriteMCR ? "TRUE": "FALSE"); + dbg(" IOSPWriteLCR : %s", bits->IOSPWriteLCR ? "TRUE": "FALSE"); + dbg(" IOSPSetBaudRate : %s", bits->IOSPSetBaudRate ? "TRUE": "FALSE"); + dbg(" TrueEdgeport : %s", bits->TrueEdgeport ? "TRUE": "FALSE"); } return result; @@ -561,10 +592,10 @@ static int get_epic_descriptor(struct edgeport_serial *ep) /***************************************************************************** * edge_interrupt_callback - * this is the callback function for when we have received data on the + * this is the callback function for when we have received data on the * interrupt endpoint. *****************************************************************************/ -static void edge_interrupt_callback (struct urb *urb) +static void edge_interrupt_callback(struct urb *urb) { struct edgeport_serial *edge_serial = urb->context; struct edgeport_port *edge_port; @@ -589,17 +620,17 @@ static void edge_interrupt_callback (struct urb *urb) case -ESHUTDOWN: /* this urb is terminated, clean up */ dbg("%s - urb shutting down with status: %d", - __func__, status); + __func__, status); return; default: - dbg("%s - nonzero urb status received: %d", - __func__, status); + dbg("%s - nonzero urb status received: %d", __func__, status); goto exit; } - // process this interrupt-read even if there are no ports open + /* process this interrupt-read even if there are no ports open */ if (length) { - usb_serial_debug_data(debug, &edge_serial->serial->dev->dev, __func__, length, data); + usb_serial_debug_data(debug, &edge_serial->serial->dev->dev, + __func__, length, data); if (length > 1) { bytes_avail = data[0] | (data[1] << 8); @@ -613,7 +644,8 @@ static void edge_interrupt_callback (struct urb *urb) dbg("%s - posting a read", __func__); edge_serial->read_in_progress = true; - /* we have pending bytes on the bulk in pipe, send a request */ + /* 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) { @@ -627,7 +659,8 @@ static void edge_interrupt_callback (struct urb *urb) /* grab the txcredits for the ports if available */ position = 2; portNumber = 0; - while ((position < length) && (portNumber < edge_serial->serial->num_ports)) { + while ((position < length) && + (portNumber < edge_serial->serial->num_ports)) { txCredits = data[position] | (data[position+1] << 8); if (txCredits) { port = edge_serial->serial->port[portNumber]; @@ -636,14 +669,19 @@ static void edge_interrupt_callback (struct urb *urb) spin_lock(&edge_port->ep_lock); edge_port->txCredits += txCredits; spin_unlock(&edge_port->ep_lock); - dbg("%s - txcredits for port%d = %d", __func__, portNumber, edge_port->txCredits); - - /* tell the tty driver that something has changed */ - if (edge_port->port->tty) - tty_wakeup(edge_port->port->tty); - - // Since we have more credit, check if more data can be sent - send_more_port_data(edge_serial, edge_port); + dbg("%s - txcredits for port%d = %d", + __func__, portNumber, + edge_port->txCredits); + + /* tell the tty driver that something + has changed */ + if (edge_port->port->port.tty) + tty_wakeup(edge_port->port->port.tty); + + /* Since we have more credit, check + if more data can be sent */ + send_more_port_data(edge_serial, + edge_port); } } position += 2; @@ -652,19 +690,20 @@ static void edge_interrupt_callback (struct urb *urb) } exit: - result = usb_submit_urb (urb, GFP_ATOMIC); - if (result) { - dev_err(&urb->dev->dev, "%s - Error %d submitting control urb\n", __func__, result); - } + result = usb_submit_urb(urb, GFP_ATOMIC); + if (result) + dev_err(&urb->dev->dev, + "%s - Error %d submitting control urb\n", + __func__, result); } /***************************************************************************** * edge_bulk_in_callback - * this is the callback function for when we have received data on the + * this is the callback function for when we have received data on the * bulk in endpoint. *****************************************************************************/ -static void edge_bulk_in_callback (struct urb *urb) +static void edge_bulk_in_callback(struct urb *urb) { struct edgeport_serial *edge_serial = urb->context; unsigned char *data = urb->transfer_buffer; @@ -689,16 +728,18 @@ static void edge_bulk_in_callback (struct urb *urb) raw_data_length = urb->actual_length; - usb_serial_debug_data(debug, &edge_serial->serial->dev->dev, __func__, raw_data_length, data); + usb_serial_debug_data(debug, &edge_serial->serial->dev->dev, + __func__, raw_data_length, data); spin_lock(&edge_serial->es_lock); /* decrement our rxBytes available by the number that we just got */ edge_serial->rxBytesAvail -= raw_data_length; - dbg("%s - Received = %d, rxBytesAvail %d", __func__, raw_data_length, edge_serial->rxBytesAvail); + dbg("%s - Received = %d, rxBytesAvail %d", __func__, + raw_data_length, edge_serial->rxBytesAvail); - process_rcvd_data (edge_serial, data, urb->actual_length); + process_rcvd_data(edge_serial, data, urb->actual_length); /* check to see if there's any more data for us to read */ if (edge_serial->rxBytesAvail > 0) { @@ -721,10 +762,10 @@ static void edge_bulk_in_callback (struct urb *urb) /***************************************************************************** * edge_bulk_out_data_callback - * this is the callback function for when we have finished sending serial data - * on the bulk out endpoint. + * this is the callback function for when we have finished sending + * serial data on the bulk out endpoint. *****************************************************************************/ -static void edge_bulk_out_data_callback (struct urb *urb) +static void edge_bulk_out_data_callback(struct urb *urb) { struct edgeport_port *edge_port = urb->context; struct tty_struct *tty; @@ -737,27 +778,29 @@ static void edge_bulk_out_data_callback (struct urb *urb) __func__, status); } - tty = edge_port->port->tty; + tty = edge_port->port->port.tty; if (tty && edge_port->open) { - /* let the tty driver wakeup if it has a special write_wakeup function */ + /* let the tty driver wakeup if it has a special + write_wakeup function */ tty_wakeup(tty); } - // Release the Write URB + /* Release the Write URB */ 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); + /* 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); } /***************************************************************************** * BulkOutCmdCallback - * this is the callback function for when we have finished sending a command - * on the bulk out endpoint. + * this is the callback function for when we have finished sending a + * command on the bulk out endpoint. *****************************************************************************/ -static void edge_bulk_out_cmd_callback (struct urb *urb) +static void edge_bulk_out_cmd_callback(struct urb *urb) { struct edgeport_port *edge_port = urb->context; struct tty_struct *tty; @@ -766,22 +809,24 @@ static void edge_bulk_out_cmd_callback (struct urb *urb) dbg("%s", __func__); atomic_dec(&CmdUrbs); - dbg("%s - FREE URB %p (outstanding %d)", __func__, urb, atomic_read(&CmdUrbs)); + dbg("%s - FREE URB %p (outstanding %d)", __func__, + urb, atomic_read(&CmdUrbs)); /* clean up the transfer buffer */ kfree(urb->transfer_buffer); /* Free the command urb */ - usb_free_urb (urb); + usb_free_urb(urb); if (status) { - dbg("%s - nonzero write bulk status received: %d", __func__, status); + dbg("%s - nonzero write bulk status received: %d", + __func__, status); return; } /* Get pointer to tty */ - tty = edge_port->port->tty; + tty = edge_port->port->port.tty; /* tell the tty driver that something has changed */ if (tty && edge_port->open) @@ -803,7 +848,8 @@ static void edge_bulk_out_cmd_callback (struct urb *urb) * If successful, we return 0 * Otherwise we return a negative error number. *****************************************************************************/ -static int edge_open (struct usb_serial_port *port, struct file * filp) +static int edge_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct usb_serial *serial; @@ -815,55 +861,62 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) if (edge_port == NULL) return -ENODEV; - if (port->tty) - port->tty->low_latency = low_latency; + if (tty) + tty->low_latency = low_latency; - /* see if we've set up our endpoint info yet (can't set it up in edge_startup - as the structures were not set up at that time.) */ + /* see if we've set up our endpoint info yet (can't set it up + in edge_startup as the structures were not set up at that time.) */ serial = port->serial; edge_serial = usb_get_serial_data(serial); - if (edge_serial == NULL) { + if (edge_serial == NULL) return -ENODEV; - } if (edge_serial->interrupt_in_buffer == NULL) { struct usb_serial_port *port0 = serial->port[0]; - + /* not set up yet, so do it now */ - edge_serial->interrupt_in_buffer = port0->interrupt_in_buffer; - edge_serial->interrupt_in_endpoint = port0->interrupt_in_endpointAddress; + edge_serial->interrupt_in_buffer = + port0->interrupt_in_buffer; + edge_serial->interrupt_in_endpoint = + port0->interrupt_in_endpointAddress; edge_serial->interrupt_read_urb = port0->interrupt_in_urb; edge_serial->bulk_in_buffer = port0->bulk_in_buffer; - edge_serial->bulk_in_endpoint = port0->bulk_in_endpointAddress; + edge_serial->bulk_in_endpoint = + port0->bulk_in_endpointAddress; edge_serial->read_urb = port0->read_urb; - edge_serial->bulk_out_endpoint = port0->bulk_out_endpointAddress; - + edge_serial->bulk_out_endpoint = + port0->bulk_out_endpointAddress; + /* set up our interrupt urb */ usb_fill_int_urb(edge_serial->interrupt_read_urb, - serial->dev, - usb_rcvintpipe(serial->dev, - port0->interrupt_in_endpointAddress), - port0->interrupt_in_buffer, - edge_serial->interrupt_read_urb->transfer_buffer_length, - edge_interrupt_callback, edge_serial, - edge_serial->interrupt_read_urb->interval); - + serial->dev, + usb_rcvintpipe(serial->dev, + port0->interrupt_in_endpointAddress), + port0->interrupt_in_buffer, + edge_serial->interrupt_read_urb->transfer_buffer_length, + edge_interrupt_callback, edge_serial, + edge_serial->interrupt_read_urb->interval); + /* set up our bulk in urb */ usb_fill_bulk_urb(edge_serial->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, - port0->bulk_in_endpointAddress), - port0->bulk_in_buffer, - edge_serial->read_urb->transfer_buffer_length, - edge_bulk_in_callback, edge_serial); + usb_rcvbulkpipe(serial->dev, + port0->bulk_in_endpointAddress), + port0->bulk_in_buffer, + edge_serial->read_urb->transfer_buffer_length, + edge_bulk_in_callback, edge_serial); edge_serial->read_in_progress = false; /* start interrupt read for this edgeport - * this interrupt will continue as long as the edgeport is connected */ - response = usb_submit_urb (edge_serial->interrupt_read_urb, GFP_KERNEL); + * this interrupt will continue as long + * as the edgeport is connected */ + response = usb_submit_urb(edge_serial->interrupt_read_urb, + GFP_KERNEL); if (response) { - dev_err(&port->dev, "%s - Error %d submitting control urb\n", __func__, response); + dev_err(&port->dev, + "%s - Error %d submitting control urb\n", + __func__, response); } } - + /* initialize our wait queues */ init_waitqueue_head(&edge_port->wait_open); init_waitqueue_head(&edge_port->wait_chase); @@ -871,26 +924,29 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) init_waitqueue_head(&edge_port->wait_command); /* initialize our icount structure */ - memset (&(edge_port->icount), 0x00, sizeof(edge_port->icount)); + memset(&(edge_port->icount), 0x00, sizeof(edge_port->icount)); /* 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->txCredits = 0; /* Can't send any data yet */ + /* Must always set this bit to enable ints! */ + edge_port->shadowMCR = MCR_MASTER_IE; edge_port->chaseResponsePending = false; /* send a open port command */ edge_port->openPending = true; edge_port->open = false; - response = send_iosp_ext_cmd (edge_port, IOSP_CMD_OPEN_PORT, 0); + 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", __func__); + dev_err(&port->dev, "%s - error sending open port command\n", + __func__); 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, OPEN_TIMEOUT); + wait_event_timeout(edge_port->wait_open, !edge_port->openPending, + OPEN_TIMEOUT); if (!edge_port->open) { /* open timed out */ @@ -904,25 +960,26 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) edge_port->txfifo.tail = 0; edge_port->txfifo.count = 0; edge_port->txfifo.size = edge_port->maxTxCredits; - edge_port->txfifo.fifo = kmalloc (edge_port->maxTxCredits, GFP_KERNEL); + edge_port->txfifo.fifo = kmalloc(edge_port->maxTxCredits, GFP_KERNEL); if (!edge_port->txfifo.fifo) { dbg("%s - no memory", __func__); - edge_close (port, filp); + edge_close(tty, port, filp); return -ENOMEM; } /* Allocate a URB for the write */ - edge_port->write_urb = usb_alloc_urb (0, GFP_KERNEL); + edge_port->write_urb = usb_alloc_urb(0, GFP_KERNEL); edge_port->write_in_progress = false; if (!edge_port->write_urb) { dbg("%s - no memory", __func__); - edge_close (port, filp); + edge_close(tty, port, filp); return -ENOMEM; } - dbg("%s(%d) - Initialize TX fifo to %d bytes", __func__, port->number, edge_port->maxTxCredits); + dbg("%s(%d) - Initialize TX fifo to %d bytes", + __func__, port->number, edge_port->maxTxCredits); dbg("%s exited", __func__); @@ -948,27 +1005,28 @@ static void block_until_chase_response(struct edgeport_port *edge_port) int loop = 10; while (1) { - // Save Last credits + /* Save Last credits */ lastCredits = edge_port->txCredits; - // Did we get our Chase response + /* Did we get our Chase response */ if (!edge_port->chaseResponsePending) { dbg("%s - Got Chase Response", __func__); - // did we get all of our credit back? - if (edge_port->txCredits == edge_port->maxTxCredits ) { + /* did we get all of our credit back? */ + if (edge_port->txCredits == edge_port->maxTxCredits) { dbg("%s - Got all credits", __func__); return; } } - // Block the thread for a while - prepare_to_wait(&edge_port->wait_chase, &wait, TASK_UNINTERRUPTIBLE); + /* Block the thread for a while */ + prepare_to_wait(&edge_port->wait_chase, &wait, + TASK_UNINTERRUPTIBLE); schedule_timeout(timeout); finish_wait(&edge_port->wait_chase, &wait); if (lastCredits == edge_port->txCredits) { - // No activity.. count down. + /* No activity.. count down. */ loop--; if (loop == 0) { edge_port->chaseResponsePending = false; @@ -976,8 +1034,9 @@ static void block_until_chase_response(struct edgeport_port *edge_port) return; } } else { - // Reset timeout value back to 10 seconds - dbg("%s - Last %d, Current %d", __func__, lastCredits, edge_port->txCredits); + /* Reset timeout value back to 10 seconds */ + dbg("%s - Last %d, Current %d", __func__, + lastCredits, edge_port->txCredits); loop = 10; } } @@ -994,7 +1053,7 @@ static void block_until_chase_response(struct edgeport_port *edge_port) * 3. A timeout of 3 seconds without activity has expired * ************************************************************************/ -static void block_until_tx_empty (struct edgeport_port *edge_port) +static void block_until_tx_empty(struct edgeport_port *edge_port) { DEFINE_WAIT(wait); struct TxFifo *fifo = &edge_port->txfifo; @@ -1003,31 +1062,32 @@ static void block_until_tx_empty (struct edgeport_port *edge_port) int loop = 30; while (1) { - // Save Last count + /* Save Last count */ lastCount = fifo->count; - // Is the Edgeport Buffer empty? + /* Is the Edgeport Buffer empty? */ if (lastCount == 0) { dbg("%s - TX Buffer Empty", __func__); return; } - // Block the thread for a while - prepare_to_wait (&edge_port->wait_chase, &wait, TASK_UNINTERRUPTIBLE); + /* Block the thread for a while */ + prepare_to_wait(&edge_port->wait_chase, &wait, + TASK_UNINTERRUPTIBLE); schedule_timeout(timeout); finish_wait(&edge_port->wait_chase, &wait); dbg("%s wait", __func__); if (lastCount == fifo->count) { - // No activity.. count down. + /* No activity.. count down. */ loop--; if (loop == 0) { dbg("%s - TIMEOUT", __func__); return; } } else { - // Reset timeout value back to seconds + /* Reset timeout value back to seconds */ loop = 30; } } @@ -1038,20 +1098,21 @@ static void block_until_tx_empty (struct edgeport_port *edge_port) * edge_close * this function is called by the tty driver when a port is closed *****************************************************************************/ -static void edge_close (struct usb_serial_port *port, struct file * filp) +static void edge_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct edgeport_serial *edge_serial; struct edgeport_port *edge_port; int status; dbg("%s - port %d", __func__, port->number); - + edge_serial = usb_get_serial_data(port->serial); edge_port = usb_get_serial_port_data(port); - if ((edge_serial == NULL) || (edge_port == NULL)) + if (edge_serial == NULL || edge_port == NULL) return; - - // block until tx is empty + + /* block until tx is empty */ block_until_tx_empty(edge_port); edge_port->closePending = true; @@ -1063,13 +1124,12 @@ static void edge_close (struct usb_serial_port *port, struct file * filp) edge_port->chaseResponsePending = true; dbg("%s - Sending IOSP_CMD_CHASE_PORT", __func__); - status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0); - if (status == 0) { - // block until chase finished + status = send_iosp_ext_cmd(edge_port, IOSP_CMD_CHASE_PORT, 0); + if (status == 0) + /* block until chase finished */ block_until_chase_response(edge_port); - } else { + else edge_port->chaseResponsePending = false; - } } if ((!edge_serial->is_epic) || @@ -1077,10 +1137,10 @@ static void edge_close (struct usb_serial_port *port, struct file * filp) (edge_serial->epic_descriptor.Supports.IOSPClose))) { /* close the port */ dbg("%s - Sending IOSP_CMD_CLOSE_PORT", __func__); - send_iosp_ext_cmd (edge_port, IOSP_CMD_CLOSE_PORT, 0); + send_iosp_ext_cmd(edge_port, IOSP_CMD_CLOSE_PORT, 0); } - //port->close = true; + /* port->close = true; */ edge_port->closePending = false; edge_port->open = false; edge_port->openPending = false; @@ -1088,7 +1148,8 @@ static void edge_close (struct usb_serial_port *port, struct file * filp) usb_kill_urb(edge_port->write_urb); if (edge_port->write_urb) { - /* if this urb had a transfer buffer already (old transfer) free it */ + /* if this urb had a transfer buffer already + (old transfer) free it */ kfree(edge_port->write_urb->transfer_buffer); usb_free_urb(edge_port->write_urb); edge_port->write_urb = NULL; @@ -1097,16 +1158,17 @@ static void edge_close (struct usb_serial_port *port, struct file * filp) edge_port->txfifo.fifo = NULL; dbg("%s exited", __func__); -} +} /***************************************************************************** * SerialWrite - * this function is called by the tty driver when data should be written to - * the port. - * If successful, we return the number of bytes written, otherwise we return - * a negative error number. + * this function is called by the tty driver when data should be written + * to the port. + * If successful, we return the number of bytes written, otherwise we + * return a negative error number. *****************************************************************************/ -static int edge_write (struct usb_serial_port *port, const unsigned char *data, int count) +static int edge_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *data, int count) { struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct TxFifo *fifo; @@ -1121,66 +1183,76 @@ static int edge_write (struct usb_serial_port *port, const unsigned char *data, if (edge_port == NULL) return -ENODEV; - // get a pointer to the Tx fifo + /* get a pointer to the Tx fifo */ fifo = &edge_port->txfifo; spin_lock_irqsave(&edge_port->ep_lock, flags); - // calculate number of bytes to put in fifo - copySize = min ((unsigned int)count, (edge_port->txCredits - fifo->count)); + /* calculate number of bytes to put in fifo */ + copySize = min((unsigned int)count, + (edge_port->txCredits - fifo->count)); - dbg("%s(%d) of %d byte(s) Fifo room %d -- will copy %d bytes", __func__, - port->number, count, edge_port->txCredits - fifo->count, copySize); + dbg("%s(%d) of %d byte(s) Fifo room %d -- will copy %d bytes", + __func__, port->number, count, + edge_port->txCredits - fifo->count, copySize); - /* catch writes of 0 bytes which the tty driver likes to give us, and when txCredits is empty */ + /* catch writes of 0 bytes which the tty driver likes to give us, + and when txCredits is empty */ if (copySize == 0) { dbg("%s - copySize = Zero", __func__); goto finish_write; } - // queue the data - // since we can never overflow the buffer we do not have to check for full condition - - // the copy is done is two parts -- first fill to the end of the buffer - // then copy the reset from the start of the buffer - + /* queue the data + * since we can never overflow the buffer we do not have to check for a + * full condition + * + * the copy is done is two parts -- first fill to the end of the buffer + * then copy the reset from the start of the buffer + */ bytesleft = fifo->size - fifo->head; - firsthalf = min (bytesleft, copySize); - dbg("%s - copy %d bytes of %d into fifo ", __func__, firsthalf, bytesleft); + firsthalf = min(bytesleft, copySize); + dbg("%s - copy %d bytes of %d into fifo ", __func__, + firsthalf, bytesleft); /* now copy our data */ memcpy(&fifo->fifo[fifo->head], data, firsthalf); - usb_serial_debug_data(debug, &port->dev, __func__, firsthalf, &fifo->fifo[fifo->head]); + usb_serial_debug_data(debug, &port->dev, __func__, + firsthalf, &fifo->fifo[fifo->head]); - // update the index and size + /* update the index and size */ fifo->head += firsthalf; fifo->count += firsthalf; - // wrap the index - if (fifo->head == fifo->size) { + /* wrap the index */ + if (fifo->head == fifo->size) fifo->head = 0; - } secondhalf = copySize-firsthalf; if (secondhalf) { dbg("%s - copy rest of data %d", __func__, secondhalf); memcpy(&fifo->fifo[fifo->head], &data[firsthalf], secondhalf); - usb_serial_debug_data(debug, &port->dev, __func__, secondhalf, &fifo->fifo[fifo->head]); - // update the index and size + usb_serial_debug_data(debug, &port->dev, __func__, + secondhalf, &fifo->fifo[fifo->head]); + /* update the index and size */ fifo->count += secondhalf; fifo->head += secondhalf; - // No need to check for wrap since we can not get to end of fifo in this part + /* No need to check for wrap since we can not get to end of + * the fifo in this part + */ } finish_write: spin_unlock_irqrestore(&edge_port->ep_lock, flags); - send_more_port_data((struct edgeport_serial *)usb_get_serial_data(port->serial), edge_port); + send_more_port_data((struct edgeport_serial *) + usb_get_serial_data(port->serial), edge_port); - dbg("%s wrote %d byte(s) TxCredits %d, Fifo %d", __func__, copySize, edge_port->txCredits, fifo->count); + dbg("%s wrote %d byte(s) TxCredits %d, Fifo %d", __func__, + copySize, edge_port->txCredits, fifo->count); - return copySize; + return copySize; } @@ -1197,7 +1269,8 @@ finish_write: * can transmit more. * ************************************************************************/ -static void send_more_port_data(struct edgeport_serial *edge_serial, struct edgeport_port *edge_port) +static void send_more_port_data(struct edgeport_serial *edge_serial, + struct edgeport_port *edge_port) { struct TxFifo *fifo = &edge_port->txfifo; struct urb *urb; @@ -1216,67 +1289,78 @@ static void send_more_port_data(struct edgeport_serial *edge_serial, struct edge if (edge_port->write_in_progress || !edge_port->open || (fifo->count == 0)) { - dbg("%s(%d) EXIT - fifo %d, PendingWrite = %d", __func__, edge_port->port->number, fifo->count, edge_port->write_in_progress); + dbg("%s(%d) EXIT - fifo %d, PendingWrite = %d", + __func__, edge_port->port->number, + fifo->count, edge_port->write_in_progress); goto exit_send; } - // since the amount of data in the fifo will always fit into the - // edgeport buffer we do not need to check the write length - - // Do we have enough credits for this port to make it worthwhile - // to bother queueing a write. If it's too small, say a few bytes, - // it's better to wait for more credits so we can do a larger - // write. - if (edge_port->txCredits < EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(edge_port->maxTxCredits,EDGE_FW_BULK_MAX_PACKET_SIZE)) { - dbg("%s(%d) Not enough credit - fifo %d TxCredit %d", __func__, edge_port->port->number, fifo->count, edge_port->txCredits ); + /* since the amount of data in the fifo will always fit into the + * edgeport buffer we do not need to check the write length + * + * Do we have enough credits for this port to make it worthwhile + * to bother queueing a write. If it's too small, say a few bytes, + * it's better to wait for more credits so we can do a larger write. + */ + if (edge_port->txCredits < EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(edge_port->maxTxCredits, EDGE_FW_BULK_MAX_PACKET_SIZE)) { + dbg("%s(%d) Not enough credit - fifo %d TxCredit %d", + __func__, edge_port->port->number, fifo->count, + edge_port->txCredits); goto exit_send; } - // lock this write + /* lock this write */ edge_port->write_in_progress = true; - // get a pointer to the write_urb + /* get a pointer to the write_urb */ urb = edge_port->write_urb; /* make sure transfer buffer is freed */ kfree(urb->transfer_buffer); urb->transfer_buffer = NULL; - /* build the data header for the buffer and port that we are about to send out */ + /* build the data header for the buffer and port that we are about + to send out */ count = fifo->count; - buffer = kmalloc (count+2, GFP_ATOMIC); + buffer = kmalloc(count+2, GFP_ATOMIC); if (buffer == NULL) { - dev_err(&edge_port->port->dev, "%s - no more kernel memory...\n", __func__); + dev_err(&edge_port->port->dev, + "%s - no more kernel memory...\n", __func__); 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); - buffer[1] = IOSP_BUILD_DATA_HDR2 (edge_port->port->number - edge_port->port->serial->minor, count); + buffer[0] = IOSP_BUILD_DATA_HDR1(edge_port->port->number + - edge_port->port->serial->minor, count); + buffer[1] = IOSP_BUILD_DATA_HDR2(edge_port->port->number + - edge_port->port->serial->minor, count); /* now copy our data */ bytesleft = fifo->size - fifo->tail; - firsthalf = min (bytesleft, count); + firsthalf = min(bytesleft, count); memcpy(&buffer[2], &fifo->fifo[fifo->tail], firsthalf); fifo->tail += firsthalf; fifo->count -= firsthalf; - if (fifo->tail == fifo->size) { + if (fifo->tail == fifo->size) fifo->tail = 0; - } secondhalf = count-firsthalf; if (secondhalf) { - memcpy(&buffer[2+firsthalf], &fifo->fifo[fifo->tail], secondhalf); + memcpy(&buffer[2+firsthalf], &fifo->fifo[fifo->tail], + secondhalf); fifo->tail += secondhalf; fifo->count -= secondhalf; } if (count) - usb_serial_debug_data(debug, &edge_port->port->dev, __func__, count, &buffer[2]); + usb_serial_debug_data(debug, &edge_port->port->dev, + __func__, count, &buffer[2]); /* fill up the urb with all of our data and submit it */ - usb_fill_bulk_urb (urb, edge_serial->serial->dev, - usb_sndbulkpipe(edge_serial->serial->dev, edge_serial->bulk_out_endpoint), - buffer, count+2, edge_bulk_out_data_callback, edge_port); + usb_fill_bulk_urb(urb, edge_serial->serial->dev, + usb_sndbulkpipe(edge_serial->serial->dev, + edge_serial->bulk_out_endpoint), + buffer, count+2, + edge_bulk_out_data_callback, edge_port); /* decrement the number of credits we have by the number we just sent */ edge_port->txCredits -= count; @@ -1286,14 +1370,17 @@ static void send_more_port_data(struct edgeport_serial *edge_serial, struct edge status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { /* something went wrong */ - dev_err(&edge_port->port->dev, "%s - usb_submit_urb(write bulk) failed, status = %d, data lost\n", __func__, status); + dev_err(&edge_port->port->dev, + "%s - usb_submit_urb(write bulk) failed, status = %d, data lost\n", + __func__, status); edge_port->write_in_progress = false; /* revert the credits as something bad happened. */ edge_port->txCredits += count; edge_port->icount.tx -= count; } - dbg("%s wrote %d byte(s) TxCredit %d, Fifo %d", __func__, count, edge_port->txCredits, fifo->count); + dbg("%s wrote %d byte(s) TxCredit %d, Fifo %d", + __func__, count, edge_port->txCredits, fifo->count); exit_send: spin_unlock_irqrestore(&edge_port->ep_lock, flags); @@ -1302,14 +1389,14 @@ exit_send: /***************************************************************************** * edge_write_room - * this function is called by the tty driver when it wants to know how many - * bytes of data we can accept for a specific port. - * If successful, we return the amount of room that we have for this port - * (the txCredits), - * Otherwise we return a negative error number. + * this function is called by the tty driver when it wants to know how + * many bytes of data we can accept for a specific port. If successful, + * we return the amount of room that we have for this port (the txCredits) + * otherwise we return a negative error number. *****************************************************************************/ -static int edge_write_room (struct usb_serial_port *port) +static int edge_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); int room; unsigned long flags; @@ -1317,18 +1404,18 @@ static int edge_write_room (struct usb_serial_port *port) dbg("%s", __func__); if (edge_port == NULL) - return -ENODEV; + return 0; if (edge_port->closePending) - return -ENODEV; + return 0; dbg("%s - port %d", __func__, port->number); if (!edge_port->open) { dbg("%s - port not opened", __func__); - return -EINVAL; + return 0; } - // total of both buffers is still txCredit + /* total of both buffers is still txCredit */ spin_lock_irqsave(&edge_port->ep_lock, flags); room = edge_port->txCredits - edge_port->txfifo.count; spin_unlock_irqrestore(&edge_port->ep_lock, flags); @@ -1340,15 +1427,16 @@ static int edge_write_room (struct usb_serial_port *port) /***************************************************************************** * edge_chars_in_buffer - * this function is called by the tty driver when it wants to know how many - * bytes of data we currently have outstanding in the port (data that has - * been written, but hasn't made it out the port yet) - * If successful, we return the number of bytes left to be written in the - * system, + * this function is called by the tty driver when it wants to know how + * many bytes of data we currently have outstanding in the port (data that + * has been written, but hasn't made it out the port yet) + * If successful, we return the number of bytes left to be written in the + * system, * Otherwise we return a negative error number. *****************************************************************************/ -static int edge_chars_in_buffer (struct usb_serial_port *port) +static int edge_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); int num_chars; unsigned long flags; @@ -1356,20 +1444,22 @@ static int edge_chars_in_buffer (struct usb_serial_port *port) dbg("%s", __func__); if (edge_port == NULL) - return -ENODEV; + return 0; if (edge_port->closePending) - return -ENODEV; + return 0; if (!edge_port->open) { dbg("%s - port not opened", __func__); - return -EINVAL; + return 0; } spin_lock_irqsave(&edge_port->ep_lock, flags); - num_chars = edge_port->maxTxCredits - edge_port->txCredits + edge_port->txfifo.count; + num_chars = edge_port->maxTxCredits - edge_port->txCredits + + edge_port->txfifo.count; spin_unlock_irqrestore(&edge_port->ep_lock, flags); if (num_chars) { - dbg("%s(port %d) - returns %d", __func__, port->number, num_chars); + dbg("%s(port %d) - returns %d", __func__, + port->number, num_chars); } return num_chars; @@ -1381,10 +1471,10 @@ static int edge_chars_in_buffer (struct usb_serial_port *port) * this function is called by the tty driver when it wants to stop the data * being read from the port. *****************************************************************************/ -static void edge_throttle (struct usb_serial_port *port) +static void edge_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty; int status; dbg("%s - port %d", __func__, port->number); @@ -1397,28 +1487,21 @@ static void edge_throttle (struct usb_serial_port *port) return; } - tty = port->tty; - if (!tty) { - dbg ("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the stop character */ if (I_IXOFF(tty)) { unsigned char stop_char = STOP_CHAR(tty); - status = edge_write (port, &stop_char, 1); - if (status <= 0) { + status = edge_write(tty, port, &stop_char, 1); + if (status <= 0) return; - } } /* if we are implementing RTS/CTS, toggle that line */ if (tty->termios->c_cflag & CRTSCTS) { edge_port->shadowMCR &= ~MCR_RTS; - status = send_cmd_write_uart_register(edge_port, MCR, edge_port->shadowMCR); - if (status != 0) { + status = send_cmd_write_uart_register(edge_port, MCR, + edge_port->shadowMCR); + if (status != 0) return; - } } return; @@ -1427,13 +1510,13 @@ static void edge_throttle (struct usb_serial_port *port) /***************************************************************************** * edge_unthrottle - * this function is called by the tty driver when it wants to resume the data - * being read from the port (called after SerialThrottle is called) + * this function is called by the tty driver when it wants to resume the + * data being read from the port (called after SerialThrottle is called) *****************************************************************************/ -static void edge_unthrottle (struct usb_serial_port *port) +static void edge_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty; int status; dbg("%s - port %d", __func__, port->number); @@ -1446,43 +1529,31 @@ static void edge_unthrottle (struct usb_serial_port *port) return; } - tty = port->tty; - if (!tty) { - dbg ("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the start character */ if (I_IXOFF(tty)) { unsigned char start_char = START_CHAR(tty); - status = edge_write (port, &start_char, 1); - if (status <= 0) { + status = edge_write(tty, port, &start_char, 1); + if (status <= 0) return; - } } - /* if we are implementing RTS/CTS, toggle that line */ if (tty->termios->c_cflag & CRTSCTS) { edge_port->shadowMCR |= MCR_RTS; - status = send_cmd_write_uart_register(edge_port, MCR, edge_port->shadowMCR); - if (status != 0) { - return; - } + send_cmd_write_uart_register(edge_port, MCR, + edge_port->shadowMCR); } - - return; } /***************************************************************************** * SerialSetTermios - * this function is called by the tty driver when it wants to change the termios structure + * this function is called by the tty driver when it wants to change + * the termios structure *****************************************************************************/ -static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) +static void edge_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { - /* FIXME: This function appears unused ?? */ struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; unsigned int cflag; cflag = tty->termios->c_cflag; @@ -1502,9 +1573,7 @@ static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old } /* change the port settings to the new ones specified */ - change_port_settings (edge_port, old_termios); - - return; + change_port_settings(tty, edge_port, old_termios); } @@ -1516,9 +1585,10 @@ static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old * release the bus after transmitting. This must be done when * the transmit shift register is empty, not be done when the * transmit holding register is empty. This functionality - * allows an RS485 driver to be written in user space. + * allows an RS485 driver to be written in user space. *****************************************************************************/ -static int get_lsr_info(struct edgeport_port *edge_port, unsigned int __user *value) +static int get_lsr_info(struct edgeport_port *edge_port, + unsigned int __user *value) { unsigned int result = 0; unsigned long flags; @@ -1536,25 +1606,10 @@ static int get_lsr_info(struct edgeport_port *edge_port, unsigned int __user *va return 0; } -static int get_number_bytes_avail(struct edgeport_port *edge_port, unsigned int __user *value) -{ - unsigned int result = 0; - struct tty_struct *tty = edge_port->port->tty; - - if (!tty) - return -ENOIOCTLCMD; - - result = tty->read_cnt; - - dbg("%s(%d) = %d", __func__, edge_port->port->number, result); - if (copy_to_user(value, &result, sizeof(int))) - return -EFAULT; - //return 0; - return -ENOIOCTLCMD; -} - -static int edge_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear) +static int edge_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); unsigned int mcr; @@ -1582,8 +1637,9 @@ static int edge_tiocmset (struct usb_serial_port *port, struct file *file, unsig return 0; } -static int edge_tiocmget(struct usb_serial_port *port, struct file *file) +static int edge_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); unsigned int result = 0; unsigned int msr; @@ -1606,7 +1662,8 @@ static int edge_tiocmget(struct usb_serial_port *port, struct file *file) return result; } -static int get_serial_info(struct edgeport_port *edge_port, struct serial_struct __user *retinfo) +static int get_serial_info(struct edgeport_port *edge_port, + struct serial_struct __user *retinfo) { struct serial_struct tmp; @@ -1624,9 +1681,6 @@ static int get_serial_info(struct edgeport_port *edge_port, struct serial_struct tmp.baud_base = 9600; tmp.close_delay = 5*HZ; tmp.closing_wait = 30*HZ; -// tmp.custom_divisor = state->custom_divisor; -// tmp.hub6 = state->hub6; -// tmp.io_type = state->io_type; if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; @@ -1639,8 +1693,10 @@ static int get_serial_info(struct edgeport_port *edge_port, struct serial_struct * SerialIoctl * this function handles any ioctl calls to the driver *****************************************************************************/ -static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg) +static int edge_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; DEFINE_WAIT(wait); struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct async_icount cnow; @@ -1650,71 +1706,61 @@ static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd); switch (cmd) { - // return number of bytes available - case TIOCINQ: - dbg("%s (%d) TIOCINQ", __func__, port->number); - return get_number_bytes_avail(edge_port, (unsigned int __user *) arg); - break; - - case TIOCSERGETLSR: - dbg("%s (%d) TIOCSERGETLSR", __func__, port->number); - return get_lsr_info(edge_port, (unsigned int __user *) arg); - return 0; - - case TIOCGSERIAL: - dbg("%s (%d) TIOCGSERIAL", __func__, port->number); - return get_serial_info(edge_port, (struct serial_struct __user *) arg); - - case TIOCSSERIAL: - dbg("%s (%d) TIOCSSERIAL", __func__, port->number); - break; - - case TIOCMIWAIT: - dbg("%s (%d) TIOCMIWAIT", __func__, port->number); - cprev = edge_port->icount; - while (1) { - prepare_to_wait(&edge_port->delta_msr_wait, &wait, TASK_INTERRUPTIBLE); - schedule(); - finish_wait(&edge_port->delta_msr_wait, &wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - cnow = edge_port->icount; - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) - return -EIO; /* no change => error */ - 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)) ) { - return 0; - } - cprev = cnow; + case TIOCSERGETLSR: + dbg("%s (%d) TIOCSERGETLSR", __func__, port->number); + return get_lsr_info(edge_port, (unsigned int __user *) arg); + + case TIOCGSERIAL: + dbg("%s (%d) TIOCGSERIAL", __func__, port->number); + return get_serial_info(edge_port, (struct serial_struct __user *) arg); + + case TIOCMIWAIT: + dbg("%s (%d) TIOCMIWAIT", __func__, port->number); + cprev = edge_port->icount; + while (1) { + prepare_to_wait(&edge_port->delta_msr_wait, + &wait, TASK_INTERRUPTIBLE); + schedule(); + finish_wait(&edge_port->delta_msr_wait, &wait); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + cnow = edge_port->icount; + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return -EIO; /* no change => error */ + 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))) { + return 0; } - /* NOTREACHED */ - break; + cprev = cnow; + } + /* NOTREACHED */ + break; - case TIOCGICOUNT: - cnow = edge_port->icount; - memset(&icount, 0, sizeof(icount)); - icount.cts = cnow.cts; - icount.dsr = cnow.dsr; - icount.rng = cnow.rng; - icount.dcd = cnow.dcd; - icount.rx = cnow.rx; - icount.tx = cnow.tx; - icount.frame = cnow.frame; - icount.overrun = cnow.overrun; - icount.parity = cnow.parity; - icount.brk = cnow.brk; - icount.buf_overrun = cnow.buf_overrun; - - dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d", __func__, port->number, icount.rx, icount.tx ); - if (copy_to_user((void __user *)arg, &icount, sizeof(icount))) - return -EFAULT; - return 0; + case TIOCGICOUNT: + cnow = edge_port->icount; + memset(&icount, 0, sizeof(icount)); + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d", + __func__, port->number, icount.rx, icount.tx); + if (copy_to_user((void __user *)arg, &icount, sizeof(icount))) + return -EFAULT; + return 0; } - return -ENOIOCTLCMD; } @@ -1723,8 +1769,9 @@ static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned * SerialBreak * this function sends a break to the port *****************************************************************************/ -static void edge_break (struct usb_serial_port *port, int break_state) +static void edge_break(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct edgeport_serial *edge_serial = usb_get_serial_data(port->serial); int status; @@ -1736,9 +1783,9 @@ static void edge_break (struct usb_serial_port *port, int break_state) edge_port->chaseResponsePending = true; dbg("%s - Sending IOSP_CMD_CHASE_PORT", __func__); - status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0); + status = send_iosp_ext_cmd(edge_port, IOSP_CMD_CHASE_PORT, 0); if (status == 0) { - // block until chase finished + /* block until chase finished */ block_until_chase_response(edge_port); } else { edge_port->chaseResponsePending = false; @@ -1750,14 +1797,16 @@ static void edge_break (struct usb_serial_port *port, int break_state) (edge_serial->epic_descriptor.Supports.IOSPSetClrBreak))) { if (break_state == -1) { dbg("%s - Sending IOSP_CMD_SET_BREAK", __func__); - status = send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_BREAK, 0); + status = send_iosp_ext_cmd(edge_port, + IOSP_CMD_SET_BREAK, 0); } else { dbg("%s - Sending IOSP_CMD_CLEAR_BREAK", __func__); - status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CLEAR_BREAK, 0); - } - if (status) { - dbg("%s - error sending break set/clear command.", __func__); + status = send_iosp_ext_cmd(edge_port, + IOSP_CMD_CLEAR_BREAK, 0); } + if (status) + dbg("%s - error sending break set/clear command.", + __func__); } return; @@ -1768,7 +1817,8 @@ static void edge_break (struct usb_serial_port *port, int break_state) * process_rcvd_data * this function handles the data received on the bulk in pipe. *****************************************************************************/ -static void process_rcvd_data (struct edgeport_serial *edge_serial, unsigned char * buffer, __u16 bufferLength) +static void process_rcvd_data(struct edgeport_serial *edge_serial, + unsigned char *buffer, __u16 bufferLength) { struct usb_serial_port *port; struct edgeport_port *edge_port; @@ -1789,105 +1839,123 @@ static void process_rcvd_data (struct edgeport_serial *edge_serial, unsigned cha lastBufferLength = bufferLength; switch (edge_serial->rxState) { - case EXPECT_HDR1: - edge_serial->rxHeader1 = *buffer; - ++buffer; - --bufferLength; + case EXPECT_HDR1: + edge_serial->rxHeader1 = *buffer; + ++buffer; + --bufferLength; - if (bufferLength == 0) { - edge_serial->rxState = EXPECT_HDR2; + if (bufferLength == 0) { + edge_serial->rxState = EXPECT_HDR2; + break; + } + /* otherwise, drop on through */ + case EXPECT_HDR2: + edge_serial->rxHeader2 = *buffer; + ++buffer; + --bufferLength; + + dbg("%s - Hdr1=%02X Hdr2=%02X", __func__, + edge_serial->rxHeader1, edge_serial->rxHeader2); + /* Process depending on whether this header is + * data or status */ + + if (IS_CMD_STAT_HDR(edge_serial->rxHeader1)) { + /* Decode this status header and go to + * EXPECT_HDR1 (if we can process the status + * with only 2 bytes), or go to EXPECT_HDR3 to + * get the third byte. */ + edge_serial->rxPort = + IOSP_GET_HDR_PORT(edge_serial->rxHeader1); + edge_serial->rxStatusCode = + IOSP_GET_STATUS_CODE( + edge_serial->rxHeader1); + + if (!IOSP_STATUS_IS_2BYTE( + edge_serial->rxStatusCode)) { + /* This status needs additional bytes. + * Save what we have and then wait for + * more data. + */ + edge_serial->rxStatusParam + = edge_serial->rxHeader2; + edge_serial->rxState = EXPECT_HDR3; break; } - /* otherwise, drop on through */ - - case EXPECT_HDR2: - edge_serial->rxHeader2 = *buffer; - ++buffer; - --bufferLength; - - dbg("%s - Hdr1=%02X Hdr2=%02X", __func__, edge_serial->rxHeader1, edge_serial->rxHeader2); - - // Process depending on whether this header is - // data or status - - if (IS_CMD_STAT_HDR(edge_serial->rxHeader1)) { - // Decode this status header and goto EXPECT_HDR1 (if we - // can process the status with only 2 bytes), or goto - // EXPECT_HDR3 to get the third byte. - - edge_serial->rxPort = IOSP_GET_HDR_PORT(edge_serial->rxHeader1); - edge_serial->rxStatusCode = IOSP_GET_STATUS_CODE(edge_serial->rxHeader1); - - if (!IOSP_STATUS_IS_2BYTE(edge_serial->rxStatusCode)) { - // This status needs additional bytes. Save what we have - // and then wait for more data. - edge_serial->rxStatusParam = edge_serial->rxHeader2; - - edge_serial->rxState = EXPECT_HDR3; - break; - } + /* We have all the header bytes, process the + status now */ + process_rcvd_status(edge_serial, + edge_serial->rxHeader2, 0); + edge_serial->rxState = EXPECT_HDR1; + break; + } else { + edge_serial->rxPort = + IOSP_GET_HDR_PORT(edge_serial->rxHeader1); + edge_serial->rxBytesRemaining = + IOSP_GET_HDR_DATA_LEN( + edge_serial->rxHeader1, + edge_serial->rxHeader2); + dbg("%s - Data for Port %u Len %u", + __func__, + edge_serial->rxPort, + edge_serial->rxBytesRemaining); + + /* ASSERT(DevExt->RxPort < DevExt->NumPorts); + * ASSERT(DevExt->RxBytesRemaining < + * IOSP_MAX_DATA_LENGTH); + */ - // We have all the header bytes, process the status now - process_rcvd_status (edge_serial, edge_serial->rxHeader2, 0); - edge_serial->rxState = EXPECT_HDR1; + if (bufferLength == 0) { + edge_serial->rxState = EXPECT_DATA; break; - } else { - edge_serial->rxPort = IOSP_GET_HDR_PORT(edge_serial->rxHeader1); - edge_serial->rxBytesRemaining = IOSP_GET_HDR_DATA_LEN(edge_serial->rxHeader1, edge_serial->rxHeader2); - - dbg("%s - Data for Port %u Len %u", __func__, edge_serial->rxPort, edge_serial->rxBytesRemaining); - - //ASSERT( DevExt->RxPort < DevExt->NumPorts ); - //ASSERT( DevExt->RxBytesRemaining < IOSP_MAX_DATA_LENGTH ); - - if (bufferLength == 0 ) { - edge_serial->rxState = EXPECT_DATA; - break; - } - // Else, drop through } + /* Else, drop through */ + } + case EXPECT_DATA: /* Expect data */ + if (bufferLength < edge_serial->rxBytesRemaining) { + rxLen = bufferLength; + /* Expect data to start next buffer */ + edge_serial->rxState = EXPECT_DATA; + } else { + /* BufLen >= RxBytesRemaining */ + rxLen = edge_serial->rxBytesRemaining; + /* Start another header next time */ + edge_serial->rxState = EXPECT_HDR1; + } - case EXPECT_DATA: // Expect data - - if (bufferLength < edge_serial->rxBytesRemaining) { - rxLen = bufferLength; - edge_serial->rxState = EXPECT_DATA; // Expect data to start next buffer - } else { - // BufLen >= RxBytesRemaining - rxLen = edge_serial->rxBytesRemaining; - edge_serial->rxState = EXPECT_HDR1; // Start another header next time - } + bufferLength -= rxLen; + edge_serial->rxBytesRemaining -= rxLen; - bufferLength -= rxLen; - edge_serial->rxBytesRemaining -= rxLen; - - /* spit this data back into the tty driver if this port is open */ - if (rxLen) { - port = edge_serial->serial->port[edge_serial->rxPort]; - edge_port = usb_get_serial_port_data(port); - if (edge_port->open) { - tty = edge_port->port->tty; - if (tty) { - dbg("%s - Sending %d bytes to TTY for port %d", __func__, rxLen, edge_serial->rxPort); - edge_tty_recv(&edge_serial->serial->dev->dev, tty, buffer, rxLen); - } - edge_port->icount.rx += rxLen; + /* spit this data back into the tty driver if this + port is open */ + if (rxLen) { + port = edge_serial->serial->port[ + edge_serial->rxPort]; + edge_port = usb_get_serial_port_data(port); + if (edge_port->open) { + tty = edge_port->port->port.tty; + if (tty) { + dbg("%s - Sending %d bytes to TTY for port %d", + __func__, rxLen, edge_serial->rxPort); + edge_tty_recv(&edge_serial->serial->dev->dev, tty, buffer, rxLen); } - buffer += rxLen; + edge_port->icount.rx += rxLen; } + buffer += rxLen; + } + break; - break; - - case EXPECT_HDR3: // Expect 3rd byte of status header - edge_serial->rxHeader3 = *buffer; - ++buffer; - --bufferLength; - - // We have all the header bytes, process the status now - process_rcvd_status (edge_serial, edge_serial->rxStatusParam, edge_serial->rxHeader3); - edge_serial->rxState = EXPECT_HDR1; - break; - + case EXPECT_HDR3: /* Expect 3rd byte of status header */ + edge_serial->rxHeader3 = *buffer; + ++buffer; + --bufferLength; + + /* We have all the header bytes, process the + status now */ + process_rcvd_status(edge_serial, + edge_serial->rxStatusParam, + edge_serial->rxHeader3); + edge_serial->rxState = EXPECT_HDR1; + break; } } } @@ -1895,9 +1963,11 @@ static void process_rcvd_data (struct edgeport_serial *edge_serial, unsigned cha /***************************************************************************** * process_rcvd_status - * this function handles the any status messages received on the bulk in pipe. + * this function handles the any status messages received on the + * bulk in pipe. *****************************************************************************/ -static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2, __u8 byte3) +static void process_rcvd_status(struct edgeport_serial *edge_serial, + __u8 byte2, __u8 byte3) { struct usb_serial_port *port; struct edgeport_port *edge_port; @@ -1907,7 +1977,9 @@ static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2 port = edge_serial->serial->port[edge_serial->rxPort]; edge_port = usb_get_serial_port_data(port); if (edge_port == NULL) { - dev_err(&edge_serial->serial->dev->dev, "%s - edge_port == NULL for port %d\n", __func__, edge_serial->rxPort); + dev_err(&edge_serial->serial->dev->dev, + "%s - edge_port == NULL for port %d\n", + __func__, edge_serial->rxPort); return; } @@ -1915,22 +1987,28 @@ static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2 if (code == IOSP_EXT_STATUS) { switch (byte2) { - case IOSP_EXT_STATUS_CHASE_RSP: - // we want to do EXT status regardless of port open/closed - dbg("%s - Port %u EXT CHASE_RSP Data = %02x", __func__, edge_serial->rxPort, byte3 ); - // Currently, the only EXT_STATUS is Chase, so process here instead of one more call - // to one more subroutine. If/when more EXT_STATUS, there'll be more work to do. - // 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; - wake_up (&edge_port->wait_chase); - return; + case IOSP_EXT_STATUS_CHASE_RSP: + /* we want to do EXT status regardless of port + * open/closed */ + dbg("%s - Port %u EXT CHASE_RSP Data = %02x", + __func__, edge_serial->rxPort, byte3); + /* Currently, the only EXT_STATUS is Chase, so process + * here instead of one more call to one more subroutine + * If/when more EXT_STATUS, there'll be more work to do + * 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; + wake_up(&edge_port->wait_chase); + return; - case IOSP_EXT_STATUS_RX_CHECK_RSP: - dbg("%s ========== Port %u CHECK_RSP Sequence = %02x =============\n", __func__, edge_serial->rxPort, byte3 ); - //Port->RxCheckRsp = true; - return; + case IOSP_EXT_STATUS_RX_CHECK_RSP: + dbg("%s ========== Port %u CHECK_RSP Sequence = %02x =============\n", __func__, edge_serial->rxPort, byte3); + /* Port->RxCheckRsp = true; */ + return; } } @@ -1938,11 +2016,14 @@ static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2 edge_port->txCredits = GET_TX_BUFFER_SIZE(byte3); edge_port->maxTxCredits = edge_port->txCredits; dbg("%s - Port %u Open Response Inital MSR = %02x TxBufferSize = %d", __func__, edge_serial->rxPort, byte2, edge_port->txCredits); - handle_new_msr (edge_port, byte2); + handle_new_msr(edge_port, byte2); - /* send the current line settings to the port so we are in sync with any further termios calls */ - if (edge_port->port->tty) - change_port_settings (edge_port, edge_port->port->tty->termios); + /* send the current line settings to the port so we are + in sync with any further termios calls */ + /* FIXME: locking on tty */ + if (edge_port->port->port.tty) + change_port_settings(edge_port->port->port.tty, + edge_port, edge_port->port->port.tty->termios); /* we have completed the open */ edge_port->openPending = false; @@ -1951,45 +2032,49 @@ static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2 return; } - // 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 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) return; - } switch (code) { - // Not currently sent by Edgeport - case IOSP_STATUS_LSR: - dbg("%s - Port %u LSR Status = %02x", __func__, edge_serial->rxPort, byte2); - handle_new_lsr(edge_port, false, byte2, 0); - break; + /* Not currently sent by Edgeport */ + case IOSP_STATUS_LSR: + dbg("%s - Port %u LSR Status = %02x", + __func__, edge_serial->rxPort, byte2); + handle_new_lsr(edge_port, false, byte2, 0); + break; - case IOSP_STATUS_LSR_DATA: - dbg("%s - Port %u LSR Status = %02x, Data = %02x", __func__, edge_serial->rxPort, byte2, byte3); - // byte2 is LSR Register - // byte3 is broken data byte - handle_new_lsr(edge_port, true, byte2, byte3); - break; - // - // case IOSP_EXT_4_STATUS: - // dbg("%s - Port %u LSR Status = %02x Data = %02x", __func__, edge_serial->rxPort, byte2, byte3); - // break; - // - case IOSP_STATUS_MSR: - dbg("%s - Port %u MSR Status = %02x", __func__, edge_serial->rxPort, byte2); - - // Process this new modem status and generate appropriate - // events, etc, based on the new status. This routine - // also saves the MSR in Port->ShadowMsr. - handle_new_msr(edge_port, byte2); - break; + case IOSP_STATUS_LSR_DATA: + dbg("%s - Port %u LSR Status = %02x, Data = %02x", + __func__, edge_serial->rxPort, byte2, byte3); + /* byte2 is LSR Register */ + /* byte3 is broken data byte */ + handle_new_lsr(edge_port, true, byte2, byte3); + break; + /* + * case IOSP_EXT_4_STATUS: + * dbg("%s - Port %u LSR Status = %02x Data = %02x", + * __func__, edge_serial->rxPort, byte2, byte3); + * break; + */ + case IOSP_STATUS_MSR: + dbg("%s - Port %u MSR Status = %02x", + __func__, edge_serial->rxPort, byte2); + /* + * Process this new modem status and generate appropriate + * events, etc, based on the new status. This routine + * also saves the MSR in Port->ShadowMsr. + */ + handle_new_msr(edge_port, byte2); + break; - default: - dbg("%s - Unrecognized IOSP status code %u\n", __func__, code); - break; + default: + dbg("%s - Unrecognized IOSP status code %u\n", __func__, code); + break; } - return; } @@ -1998,7 +2083,8 @@ static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2 * edge_tty_recv * this function passes data on to the tty flip buffer *****************************************************************************/ -static void edge_tty_recv(struct device *dev, struct tty_struct *tty, unsigned char *data, int length) +static void edge_tty_recv(struct device *dev, struct tty_struct *tty, + unsigned char *data, int length) { int cnt; @@ -2007,7 +2093,7 @@ static void edge_tty_recv(struct device *dev, struct tty_struct *tty, unsigned c if (cnt < length) { dev_err(dev, "%s - dropping data, %d bytes lost\n", __func__, length - cnt); - if(cnt == 0) + if (cnt == 0) break; } tty_insert_flip_string(tty, data, cnt); @@ -2029,22 +2115,19 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 newMsr) dbg("%s %02x", __func__, newMsr); - if (newMsr & (EDGEPORT_MSR_DELTA_CTS | EDGEPORT_MSR_DELTA_DSR | EDGEPORT_MSR_DELTA_RI | EDGEPORT_MSR_DELTA_CD)) { + if (newMsr & (EDGEPORT_MSR_DELTA_CTS | EDGEPORT_MSR_DELTA_DSR | + EDGEPORT_MSR_DELTA_RI | EDGEPORT_MSR_DELTA_CD)) { icount = &edge_port->icount; /* update input line counters */ - if (newMsr & EDGEPORT_MSR_DELTA_CTS) { + if (newMsr & EDGEPORT_MSR_DELTA_CTS) icount->cts++; - } - if (newMsr & EDGEPORT_MSR_DELTA_DSR) { + if (newMsr & EDGEPORT_MSR_DELTA_DSR) icount->dsr++; - } - if (newMsr & EDGEPORT_MSR_DELTA_CD) { + if (newMsr & EDGEPORT_MSR_DELTA_CD) icount->dcd++; - } - if (newMsr & EDGEPORT_MSR_DELTA_RI) { + if (newMsr & EDGEPORT_MSR_DELTA_RI) icount->rng++; - } wake_up_interruptible(&edge_port->delta_msr_wait); } @@ -2059,42 +2142,41 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 newMsr) * handle_new_lsr * this function handles any change to the lsr register for a port. *****************************************************************************/ -static void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData, __u8 lsr, __u8 data) +static void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData, + __u8 lsr, __u8 data) { - __u8 newLsr = (__u8)(lsr & (__u8)(LSR_OVER_ERR | LSR_PAR_ERR | LSR_FRM_ERR | LSR_BREAK)); - struct async_icount *icount; + __u8 newLsr = (__u8) (lsr & (__u8) + (LSR_OVER_ERR | LSR_PAR_ERR | LSR_FRM_ERR | LSR_BREAK)); + struct async_icount *icount; dbg("%s - %02x", __func__, newLsr); edge_port->shadowLSR = lsr; if (newLsr & LSR_BREAK) { - // - // Parity and Framing errors only count if they - // occur exclusive of a break being - // received. - // + /* + * Parity and Framing errors only count if they + * occur exclusive of a break being + * received. + */ newLsr &= (__u8)(LSR_OVER_ERR | LSR_BREAK); } /* Place LSR data byte into Rx buffer */ - if (lsrData && edge_port->port->tty) - edge_tty_recv(&edge_port->port->dev, edge_port->port->tty, &data, 1); + if (lsrData && edge_port->port->port.tty) + edge_tty_recv(&edge_port->port->dev, + edge_port->port->port.tty, &data, 1); /* update input line counters */ icount = &edge_port->icount; - if (newLsr & LSR_BREAK) { + if (newLsr & LSR_BREAK) icount->brk++; - } - if (newLsr & LSR_OVER_ERR) { + if (newLsr & LSR_OVER_ERR) icount->overrun++; - } - if (newLsr & LSR_PAR_ERR) { + if (newLsr & LSR_PAR_ERR) icount->parity++; - } - if (newLsr & LSR_FRM_ERR) { + if (newLsr & LSR_FRM_ERR) icount->frame++; - } return; } @@ -2102,12 +2184,13 @@ static void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData, __u8 l /**************************************************************************** * sram_write - * writes a number of bytes to the Edgeport device's sram starting at the + * writes a number of bytes to the Edgeport device's sram starting at the * given address. * If successful returns the number of bytes written, otherwise it returns * a negative error number of the problem. ****************************************************************************/ -static int sram_write(struct usb_serial *serial, __u16 extAddr, __u16 addr, __u16 length, const __u8 *data) +static int sram_write(struct usb_serial *serial, __u16 extAddr, __u16 addr, + __u16 length, const __u8 *data) { int result; __u16 current_length; @@ -2115,32 +2198,37 @@ static int sram_write(struct usb_serial *serial, __u16 extAddr, __u16 addr, __u1 dbg("%s - %x, %x, %d", __func__, extAddr, addr, length); - transfer_buffer = kmalloc (64, GFP_KERNEL); + transfer_buffer = kmalloc(64, GFP_KERNEL); if (!transfer_buffer) { - dev_err(&serial->dev->dev, "%s - kmalloc(%d) failed.\n", __func__, 64); + dev_err(&serial->dev->dev, "%s - kmalloc(%d) failed.\n", + __func__, 64); return -ENOMEM; } /* need to split these writes up into 64 byte chunks */ result = 0; while (length > 0) { - if (length > 64) { + if (length > 64) current_length = 64; - } else { + else current_length = length; - } -// dbg("%s - writing %x, %x, %d", __func__, extAddr, addr, current_length); - memcpy (transfer_buffer, data, current_length); - result = usb_control_msg (serial->dev, usb_sndctrlpipe(serial->dev, 0), USB_REQUEST_ION_WRITE_RAM, - 0x40, addr, extAddr, transfer_buffer, current_length, 300); + +/* dbg("%s - writing %x, %x, %d", __func__, + extAddr, addr, current_length); */ + memcpy(transfer_buffer, data, current_length); + result = usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + USB_REQUEST_ION_WRITE_RAM, + 0x40, addr, extAddr, transfer_buffer, + current_length, 300); if (result < 0) break; length -= current_length; addr += current_length; data += current_length; - } + } - kfree (transfer_buffer); + kfree(transfer_buffer); return result; } @@ -2152,40 +2240,45 @@ static int sram_write(struct usb_serial *serial, __u16 extAddr, __u16 addr, __u1 * If successful returns the number of bytes written, otherwise it returns * a negative error number of the problem. ****************************************************************************/ -static int rom_write(struct usb_serial *serial, __u16 extAddr, __u16 addr, __u16 length, const __u8 *data) +static int rom_write(struct usb_serial *serial, __u16 extAddr, __u16 addr, + __u16 length, const __u8 *data) { int result; __u16 current_length; unsigned char *transfer_buffer; -// dbg("%s - %x, %x, %d", __func__, extAddr, addr, length); +/* dbg("%s - %x, %x, %d", __func__, extAddr, addr, length); */ - transfer_buffer = kmalloc (64, GFP_KERNEL); + transfer_buffer = kmalloc(64, GFP_KERNEL); if (!transfer_buffer) { - dev_err(&serial->dev->dev, "%s - kmalloc(%d) failed.\n", __func__, 64); + dev_err(&serial->dev->dev, "%s - kmalloc(%d) failed.\n", + __func__, 64); return -ENOMEM; } /* need to split these writes up into 64 byte chunks */ result = 0; while (length > 0) { - if (length > 64) { + if (length > 64) current_length = 64; - } else { + else current_length = length; - } -// dbg("%s - writing %x, %x, %d", __func__, extAddr, addr, current_length); - memcpy (transfer_buffer, data, current_length); - result = usb_control_msg (serial->dev, usb_sndctrlpipe(serial->dev, 0), USB_REQUEST_ION_WRITE_ROM, - 0x40, addr, extAddr, transfer_buffer, current_length, 300); +/* dbg("%s - writing %x, %x, %d", __func__, + extAddr, addr, current_length); */ + memcpy(transfer_buffer, data, current_length); + result = usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + USB_REQUEST_ION_WRITE_ROM, 0x40, + addr, extAddr, + transfer_buffer, current_length, 300); if (result < 0) break; length -= current_length; addr += current_length; data += current_length; - } + } - kfree (transfer_buffer); + kfree(transfer_buffer); return result; } @@ -2197,7 +2290,8 @@ static int rom_write(struct usb_serial *serial, __u16 extAddr, __u16 addr, __u16 * If successful returns the number of bytes read, otherwise it returns * a negative error number of the problem. ****************************************************************************/ -static int rom_read (struct usb_serial *serial, __u16 extAddr, __u16 addr, __u16 length, __u8 *data) +static int rom_read(struct usb_serial *serial, __u16 extAddr, + __u16 addr, __u16 length, __u8 *data) { int result; __u16 current_length; @@ -2205,32 +2299,36 @@ static int rom_read (struct usb_serial *serial, __u16 extAddr, __u16 addr, __u16 dbg("%s - %x, %x, %d", __func__, extAddr, addr, length); - transfer_buffer = kmalloc (64, GFP_KERNEL); + transfer_buffer = kmalloc(64, GFP_KERNEL); if (!transfer_buffer) { - dev_err(&serial->dev->dev, "%s - kmalloc(%d) failed.\n", __func__, 64); + dev_err(&serial->dev->dev, + "%s - kmalloc(%d) failed.\n", __func__, 64); return -ENOMEM; } /* need to split these reads up into 64 byte chunks */ result = 0; while (length > 0) { - if (length > 64) { + if (length > 64) current_length = 64; - } else { + else current_length = length; - } -// dbg("%s - %x, %x, %d", __func__, extAddr, addr, current_length); - result = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), USB_REQUEST_ION_READ_ROM, - 0xC0, addr, extAddr, transfer_buffer, current_length, 300); +/* dbg("%s - %x, %x, %d", __func__, + extAddr, addr, current_length); */ + result = usb_control_msg(serial->dev, + usb_rcvctrlpipe(serial->dev, 0), + USB_REQUEST_ION_READ_ROM, + 0xC0, addr, extAddr, transfer_buffer, + current_length, 300); if (result < 0) break; - memcpy (data, transfer_buffer, current_length); + memcpy(data, transfer_buffer, current_length); length -= current_length; addr += current_length; data += current_length; - } + } - kfree (transfer_buffer); + kfree(transfer_buffer); return result; } @@ -2239,7 +2337,8 @@ static int rom_read (struct usb_serial *serial, __u16 extAddr, __u16 addr, __u16 * send_iosp_ext_cmd * Is used to send a IOSP message to the Edgeport device ****************************************************************************/ -static int send_iosp_ext_cmd (struct edgeport_port *edge_port, __u8 command, __u8 param) +static int send_iosp_ext_cmd(struct edgeport_port *edge_port, + __u8 command, __u8 param) { unsigned char *buffer; unsigned char *currentCommand; @@ -2248,19 +2347,20 @@ static int send_iosp_ext_cmd (struct edgeport_port *edge_port, __u8 command, __u dbg("%s - %d, %d", __func__, command, param); - buffer = kmalloc (10, GFP_ATOMIC); + buffer = kmalloc(10, GFP_ATOMIC); if (!buffer) { - dev_err(&edge_port->port->dev, "%s - kmalloc(%d) failed.\n", __func__, 10); + dev_err(&edge_port->port->dev, + "%s - kmalloc(%d) failed.\n", __func__, 10); return -ENOMEM; } currentCommand = buffer; - MAKE_CMD_EXT_CMD (¤tCommand, &length, - edge_port->port->number - edge_port->port->serial->minor, - command, param); + MAKE_CMD_EXT_CMD(¤tCommand, &length, + edge_port->port->number - edge_port->port->serial->minor, + command, param); - status = write_cmd_usb (edge_port, buffer, length); + status = write_cmd_usb(edge_port, buffer, length); if (status) { /* something bad happened, let's free up the memory */ kfree(buffer); @@ -2274,43 +2374,50 @@ static int send_iosp_ext_cmd (struct edgeport_port *edge_port, __u8 command, __u * write_cmd_usb * this function writes the given buffer out to the bulk write endpoint. *****************************************************************************/ -static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer, int length) +static int write_cmd_usb(struct edgeport_port *edge_port, + unsigned char *buffer, int length) { - struct edgeport_serial *edge_serial = usb_get_serial_data(edge_port->port->serial); + struct edgeport_serial *edge_serial = + usb_get_serial_data(edge_port->port->serial); int status = 0; struct urb *urb; int timeout; - usb_serial_debug_data(debug, &edge_port->port->dev, __func__, length, buffer); + usb_serial_debug_data(debug, &edge_port->port->dev, + __func__, length, buffer); /* Allocate our next urb */ - urb = usb_alloc_urb (0, GFP_ATOMIC); + urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) return -ENOMEM; atomic_inc(&CmdUrbs); - dbg("%s - ALLOCATE URB %p (outstanding %d)", __func__, urb, atomic_read(&CmdUrbs)); + dbg("%s - ALLOCATE URB %p (outstanding %d)", + __func__, 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); + 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; status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { /* something went wrong */ - dev_err(&edge_port->port->dev, "%s - usb_submit_urb(write command) failed, status = %d\n", __func__, status); + dev_err(&edge_port->port->dev, + "%s - usb_submit_urb(write command) failed, status = %d\n", + __func__, status); usb_kill_urb(urb); usb_free_urb(urb); atomic_dec(&CmdUrbs); return status; } - // wait for command to finish + /* wait for command to finish */ timeout = COMMAND_TIMEOUT; #if 0 - wait_event (&edge_port->wait_command, !edge_port->commandPending); + wait_event(&edge_port->wait_command, !edge_port->commandPending); if (edge_port->commandPending) { /* command timed out */ @@ -2327,15 +2434,18 @@ static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer * this function sends the proper command to change the baud rate of the * specified port. *****************************************************************************/ -static int send_cmd_write_baud_rate (struct edgeport_port *edge_port, int baudRate) +static int send_cmd_write_baud_rate(struct edgeport_port *edge_port, + int baudRate) { - struct edgeport_serial *edge_serial = usb_get_serial_data(edge_port->port->serial); + struct edgeport_serial *edge_serial = + usb_get_serial_data(edge_port->port->serial); unsigned char *cmdBuffer; unsigned char *currCmd; int cmdLen = 0; int divisor; int status; - unsigned char number = edge_port->port->number - edge_port->port->serial->minor; + unsigned char number = + edge_port->port->number - edge_port->port->serial->minor; if (edge_serial->is_epic && !edge_serial->epic_descriptor.Supports.IOSPSetBaudRate) { @@ -2344,36 +2454,40 @@ static int send_cmd_write_baud_rate (struct edgeport_port *edge_port, int baudRa return 0; } - dbg("%s - port = %d, baud = %d", __func__, edge_port->port->number, baudRate); + dbg("%s - port = %d, baud = %d", __func__, + edge_port->port->number, baudRate); - status = calc_baud_rate_divisor (baudRate, &divisor); + status = calc_baud_rate_divisor(baudRate, &divisor); if (status) { - dev_err(&edge_port->port->dev, "%s - bad baud rate\n", __func__); + dev_err(&edge_port->port->dev, "%s - bad baud rate\n", + __func__); return status; } - // Alloc memory for the string of commands. - cmdBuffer = kmalloc (0x100, GFP_ATOMIC); + /* Alloc memory for the string of commands. */ + cmdBuffer = kmalloc(0x100, GFP_ATOMIC); if (!cmdBuffer) { - dev_err(&edge_port->port->dev, "%s - kmalloc(%d) failed.\n", __func__, 0x100); + dev_err(&edge_port->port->dev, + "%s - kmalloc(%d) failed.\n", __func__, 0x100); return -ENOMEM; } currCmd = cmdBuffer; - // Enable access to divisor latch - MAKE_CMD_WRITE_REG( &currCmd, &cmdLen, number, LCR, LCR_DL_ENABLE ); + /* Enable access to divisor latch */ + MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, LCR, LCR_DL_ENABLE); - // Write the divisor itself - MAKE_CMD_WRITE_REG( &currCmd, &cmdLen, number, DLL, LOW8 (divisor) ); - MAKE_CMD_WRITE_REG( &currCmd, &cmdLen, number, DLM, HIGH8(divisor) ); + /* Write the divisor itself */ + MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, DLL, LOW8(divisor)); + MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, DLM, HIGH8(divisor)); - // Restore original value to disable access to divisor latch - MAKE_CMD_WRITE_REG( &currCmd, &cmdLen, number, LCR, edge_port->shadowLCR); + /* Restore original value to disable access to divisor latch */ + MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, LCR, + edge_port->shadowLCR); - status = write_cmd_usb(edge_port, cmdBuffer, cmdLen ); + status = write_cmd_usb(edge_port, cmdBuffer, cmdLen); if (status) { /* something bad happened, let's free up the memory */ - kfree (cmdBuffer); + kfree(cmdBuffer); } return status; @@ -2385,7 +2499,7 @@ static int send_cmd_write_baud_rate (struct edgeport_port *edge_port, int baudRa * this function calculates the proper baud rate divisor for the specified * baud rate. *****************************************************************************/ -static int calc_baud_rate_divisor (int baudrate, int *divisor) +static int calc_baud_rate_divisor(int baudrate, int *divisor) { int i; __u16 custom; @@ -2394,17 +2508,17 @@ static int calc_baud_rate_divisor (int baudrate, int *divisor) dbg("%s - %d", __func__, baudrate); for (i = 0; i < ARRAY_SIZE(divisor_table); i++) { - if ( divisor_table[i].BaudRate == baudrate ) { + if (divisor_table[i].BaudRate == baudrate) { *divisor = divisor_table[i].Divisor; return 0; } } - // We have tried all of the standard baud rates - // lets try to calculate the divisor for this baud rate - // Make sure the baud rate is reasonable + /* We have tried all of the standard baud rates + * lets try to calculate the divisor for this baud rate + * Make sure the baud rate is reasonable */ if (baudrate > 50 && baudrate < 230400) { - // get divisor + /* get divisor */ custom = (__u16)((230400L + baudrate/2) / baudrate); *divisor = custom; @@ -2419,17 +2533,20 @@ static int calc_baud_rate_divisor (int baudrate, int *divisor) /***************************************************************************** * send_cmd_write_uart_register - * this function builds up a uart register message and sends to to the device. + * this function builds up a uart register message and sends to to the device. *****************************************************************************/ -static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 regNum, __u8 regValue) +static int send_cmd_write_uart_register(struct edgeport_port *edge_port, + __u8 regNum, __u8 regValue) { - struct edgeport_serial *edge_serial = usb_get_serial_data(edge_port->port->serial); + struct edgeport_serial *edge_serial = + usb_get_serial_data(edge_port->port->serial); unsigned char *cmdBuffer; unsigned char *currCmd; unsigned long cmdLen = 0; int status; - dbg("%s - write to %s register 0x%02x", (regNum == MCR) ? "MCR" : "LCR", __func__, regValue); + dbg("%s - write to %s register 0x%02x", + (regNum == MCR) ? "MCR" : "LCR", __func__, regValue); if (edge_serial->is_epic && !edge_serial->epic_descriptor.Supports.IOSPWriteMCR && @@ -2441,27 +2558,26 @@ static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 r if (edge_serial->is_epic && !edge_serial->epic_descriptor.Supports.IOSPWriteLCR && regNum == LCR) { - dbg ("SendCmdWriteUartReg - Not writing to LCR Register"); + dbg("SendCmdWriteUartReg - Not writing to LCR Register"); return 0; } - // Alloc memory for the string of commands. - cmdBuffer = kmalloc (0x10, GFP_ATOMIC); - if (cmdBuffer == NULL ) { + /* Alloc memory for the string of commands. */ + cmdBuffer = kmalloc(0x10, GFP_ATOMIC); + if (cmdBuffer == NULL) return -ENOMEM; - } currCmd = cmdBuffer; - // Build a cmd in the buffer to write the given register - MAKE_CMD_WRITE_REG (&currCmd, &cmdLen, - edge_port->port->number - edge_port->port->serial->minor, - regNum, regValue); + /* Build a cmd in the buffer to write the given register */ + MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, + edge_port->port->number - edge_port->port->serial->minor, + regNum, regValue); status = write_cmd_usb(edge_port, cmdBuffer, cmdLen); if (status) { /* something bad happened, let's free up the memory */ - kfree (cmdBuffer); + kfree(cmdBuffer); } return status; @@ -2470,16 +2586,15 @@ static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 r /***************************************************************************** * change_port_settings - * This routine is called to set the UART on the device to match the specified - * new settings. + * This routine is called to set the UART on the device to match the + * specified new settings. *****************************************************************************/ -#ifndef CMSPAR -#define CMSPAR 0 -#endif -static void change_port_settings (struct edgeport_port *edge_port, struct ktermios *old_termios) + +static void change_port_settings(struct tty_struct *tty, + struct edgeport_port *edge_port, struct ktermios *old_termios) { - struct edgeport_serial *edge_serial = usb_get_serial_data(edge_port->port->serial); - struct tty_struct *tty; + struct edgeport_serial *edge_serial = + usb_get_serial_data(edge_port->port->serial); int baud; unsigned cflag; __u8 mask = 0xff; @@ -2498,21 +2613,26 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi return; } - tty = edge_port->port->tty; - if ((!tty) || - (!tty->termios)) { - dbg("%s - no tty structures", __func__); - return; - } - cflag = tty->termios->c_cflag; switch (cflag & CSIZE) { - case CS5: lData = LCR_BITS_5; mask = 0x1f; dbg("%s - data bits = 5", __func__); break; - case CS6: lData = LCR_BITS_6; mask = 0x3f; dbg("%s - data bits = 6", __func__); break; - case CS7: lData = LCR_BITS_7; mask = 0x7f; dbg("%s - data bits = 7", __func__); break; - default: - case CS8: lData = LCR_BITS_8; dbg("%s - data bits = 8", __func__); break; + case CS5: + lData = LCR_BITS_5; mask = 0x1f; + dbg("%s - data bits = 5", __func__); + break; + case CS6: + lData = LCR_BITS_6; mask = 0x3f; + dbg("%s - data bits = 6", __func__); + break; + case CS7: + lData = LCR_BITS_7; mask = 0x7f; + dbg("%s - data bits = 7", __func__); + break; + default: + case CS8: + lData = LCR_BITS_8; + dbg("%s - data bits = 8", __func__); + break; } lParity = LCR_PAR_NONE; @@ -2554,7 +2674,8 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi dbg("%s - RTS/CTS is disabled", __func__); } - /* if we are implementing XON/XOFF, set the start and stop character in the device */ + /* if we are implementing XON/XOFF, set the start and stop character + in the device */ if (I_IXOFF(tty) || I_IXON(tty)) { unsigned char stop_char = STOP_CHAR(tty); unsigned char start_char = START_CHAR(tty); @@ -2562,14 +2683,17 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi if ((!edge_serial->is_epic) || ((edge_serial->is_epic) && (edge_serial->epic_descriptor.Supports.IOSPSetXChar))) { - send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_XON_CHAR, start_char); - send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_XOFF_CHAR, stop_char); + send_iosp_ext_cmd(edge_port, + IOSP_CMD_SET_XON_CHAR, start_char); + send_iosp_ext_cmd(edge_port, + IOSP_CMD_SET_XOFF_CHAR, stop_char); } /* if we are implementing INBOUND XON/XOFF */ if (I_IXOFF(tty)) { rxFlow |= IOSP_RX_FLOW_XON_XOFF; - dbg("%s - INBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x", __func__, start_char, stop_char); + dbg("%s - INBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x", + __func__, start_char, stop_char); } else { dbg("%s - INBOUND XON/XOFF is disabled", __func__); } @@ -2577,7 +2701,8 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi /* if we are implementing OUTBOUND XON/XOFF */ if (I_IXON(tty)) { txFlow |= IOSP_TX_FLOW_XON_XOFF; - dbg("%s - OUTBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x", __func__, start_char, stop_char); + dbg("%s - OUTBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x", + __func__, start_char, stop_char); } else { dbg("%s - OUTBOUND XON/XOFF is disabled", __func__); } @@ -2600,20 +2725,20 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi edge_port->validDataMask = mask; /* Send the updated LCR value to the EdgePort */ - status = send_cmd_write_uart_register(edge_port, LCR, edge_port->shadowLCR); - if (status != 0) { + status = send_cmd_write_uart_register(edge_port, LCR, + edge_port->shadowLCR); + if (status != 0) return; - } /* set up the MCR register and send it to the EdgePort */ edge_port->shadowMCR = MCR_MASTER_IE; - if (cflag & CBAUD) { + if (cflag & CBAUD) edge_port->shadowMCR |= (MCR_DTR | MCR_RTS); - } - status = send_cmd_write_uart_register(edge_port, MCR, edge_port->shadowMCR); - if (status != 0) { + + status = send_cmd_write_uart_register(edge_port, MCR, + edge_port->shadowMCR); + if (status != 0) return; - } /* Determine divisor based on baud rate */ baud = tty_get_baud_rate(tty); @@ -2623,7 +2748,7 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi } dbg("%s - baud rate = %d", __func__, baud); - status = send_cmd_write_baud_rate (edge_port, baud); + status = send_cmd_write_baud_rate(edge_port, baud); if (status == -1) { /* Speed change was not possible - put back the old speed */ baud = tty_termios_baud_rate(old_termios); @@ -2640,7 +2765,8 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi * ASCII range, but it's only for debugging... * NOTE: expects the unicode in LE format ****************************************************************************/ -static void unicode_to_ascii(char *string, int buflen, __le16 *unicode, int unicode_size) +static void unicode_to_ascii(char *string, int buflen, + __le16 *unicode, int unicode_size) { int i; @@ -2659,75 +2785,99 @@ static void unicode_to_ascii(char *string, int buflen, __le16 *unicode, int unic /**************************************************************************** * get_manufacturing_desc - * reads in the manufacturing descriptor and stores it into the serial + * reads in the manufacturing descriptor and stores it into the serial * structure. ****************************************************************************/ -static void get_manufacturing_desc (struct edgeport_serial *edge_serial) +static void get_manufacturing_desc(struct edgeport_serial *edge_serial) { int response; dbg("getting manufacturer descriptor"); - response = rom_read (edge_serial->serial, (EDGE_MANUF_DESC_ADDR & 0xffff0000) >> 16, - (__u16)(EDGE_MANUF_DESC_ADDR & 0x0000ffff), EDGE_MANUF_DESC_LEN, - (__u8 *)(&edge_serial->manuf_descriptor)); + response = rom_read(edge_serial->serial, + (EDGE_MANUF_DESC_ADDR & 0xffff0000) >> 16, + (__u16)(EDGE_MANUF_DESC_ADDR & 0x0000ffff), + EDGE_MANUF_DESC_LEN, + (__u8 *)(&edge_serial->manuf_descriptor)); - if (response < 1) { - dev_err(&edge_serial->serial->dev->dev, "error in getting manufacturer descriptor\n"); - } else { + if (response < 1) + dev_err(&edge_serial->serial->dev->dev, + "error in getting manufacturer descriptor\n"); + else { char string[30]; dbg("**Manufacturer Descriptor"); - dbg(" RomSize: %dK", edge_serial->manuf_descriptor.RomSize); - dbg(" RamSize: %dK", edge_serial->manuf_descriptor.RamSize); - dbg(" CpuRev: %d", edge_serial->manuf_descriptor.CpuRev); - dbg(" BoardRev: %d", edge_serial->manuf_descriptor.BoardRev); - dbg(" NumPorts: %d", edge_serial->manuf_descriptor.NumPorts); - dbg(" DescDate: %d/%d/%d", edge_serial->manuf_descriptor.DescDate[0], edge_serial->manuf_descriptor.DescDate[1], edge_serial->manuf_descriptor.DescDate[2]+1900); + dbg(" RomSize: %dK", + edge_serial->manuf_descriptor.RomSize); + dbg(" RamSize: %dK", + edge_serial->manuf_descriptor.RamSize); + dbg(" CpuRev: %d", + edge_serial->manuf_descriptor.CpuRev); + dbg(" BoardRev: %d", + edge_serial->manuf_descriptor.BoardRev); + dbg(" NumPorts: %d", + edge_serial->manuf_descriptor.NumPorts); + dbg(" DescDate: %d/%d/%d", + edge_serial->manuf_descriptor.DescDate[0], + edge_serial->manuf_descriptor.DescDate[1], + edge_serial->manuf_descriptor.DescDate[2]+1900); unicode_to_ascii(string, sizeof(string), - edge_serial->manuf_descriptor.SerialNumber, - edge_serial->manuf_descriptor.SerNumLength/2); + edge_serial->manuf_descriptor.SerialNumber, + edge_serial->manuf_descriptor.SerNumLength/2); dbg(" SerialNumber: %s", string); unicode_to_ascii(string, sizeof(string), - edge_serial->manuf_descriptor.AssemblyNumber, - edge_serial->manuf_descriptor.AssemblyNumLength/2); + edge_serial->manuf_descriptor.AssemblyNumber, + edge_serial->manuf_descriptor.AssemblyNumLength/2); dbg(" AssemblyNumber: %s", string); unicode_to_ascii(string, sizeof(string), edge_serial->manuf_descriptor.OemAssyNumber, edge_serial->manuf_descriptor.OemAssyNumLength/2); dbg(" OemAssyNumber: %s", string); - dbg(" UartType: %d", edge_serial->manuf_descriptor.UartType); - dbg(" IonPid: %d", edge_serial->manuf_descriptor.IonPid); - dbg(" IonConfig: %d", edge_serial->manuf_descriptor.IonConfig); + dbg(" UartType: %d", + edge_serial->manuf_descriptor.UartType); + dbg(" IonPid: %d", + edge_serial->manuf_descriptor.IonPid); + dbg(" IonConfig: %d", + edge_serial->manuf_descriptor.IonConfig); } } /**************************************************************************** * get_boot_desc - * reads in the bootloader descriptor and stores it into the serial + * reads in the bootloader descriptor and stores it into the serial * structure. ****************************************************************************/ -static void get_boot_desc (struct edgeport_serial *edge_serial) +static void get_boot_desc(struct edgeport_serial *edge_serial) { int response; dbg("getting boot descriptor"); - response = rom_read (edge_serial->serial, (EDGE_BOOT_DESC_ADDR & 0xffff0000) >> 16, - (__u16)(EDGE_BOOT_DESC_ADDR & 0x0000ffff), EDGE_BOOT_DESC_LEN, - (__u8 *)(&edge_serial->boot_descriptor)); + response = rom_read(edge_serial->serial, + (EDGE_BOOT_DESC_ADDR & 0xffff0000) >> 16, + (__u16)(EDGE_BOOT_DESC_ADDR & 0x0000ffff), + EDGE_BOOT_DESC_LEN, + (__u8 *)(&edge_serial->boot_descriptor)); - if (response < 1) { - dev_err(&edge_serial->serial->dev->dev, "error in getting boot descriptor\n"); - } else { + if (response < 1) + dev_err(&edge_serial->serial->dev->dev, + "error in getting boot descriptor\n"); + else { dbg("**Boot Descriptor:"); - dbg(" BootCodeLength: %d", le16_to_cpu(edge_serial->boot_descriptor.BootCodeLength)); - dbg(" MajorVersion: %d", edge_serial->boot_descriptor.MajorVersion); - dbg(" MinorVersion: %d", edge_serial->boot_descriptor.MinorVersion); - dbg(" BuildNumber: %d", le16_to_cpu(edge_serial->boot_descriptor.BuildNumber)); - dbg(" Capabilities: 0x%x", le16_to_cpu(edge_serial->boot_descriptor.Capabilities)); - dbg(" UConfig0: %d", edge_serial->boot_descriptor.UConfig0); - dbg(" UConfig1: %d", edge_serial->boot_descriptor.UConfig1); + dbg(" BootCodeLength: %d", + le16_to_cpu(edge_serial->boot_descriptor.BootCodeLength)); + dbg(" MajorVersion: %d", + edge_serial->boot_descriptor.MajorVersion); + dbg(" MinorVersion: %d", + edge_serial->boot_descriptor.MinorVersion); + dbg(" BuildNumber: %d", + le16_to_cpu(edge_serial->boot_descriptor.BuildNumber)); + dbg(" Capabilities: 0x%x", + le16_to_cpu(edge_serial->boot_descriptor.Capabilities)); + dbg(" UConfig0: %d", + edge_serial->boot_descriptor.UConfig0); + dbg(" UConfig1: %d", + edge_serial->boot_descriptor.UConfig1); } } @@ -2736,7 +2886,7 @@ static void get_boot_desc (struct edgeport_serial *edge_serial) * load_application_firmware * This is called to load the application firmware to the device ****************************************************************************/ -static void load_application_firmware (struct edgeport_serial *edge_serial) +static void load_application_firmware(struct edgeport_serial *edge_serial) { const struct ihex_binrec *rec; const struct firmware *fw; @@ -2813,7 +2963,7 @@ static void load_application_firmware (struct edgeport_serial *edge_serial) /**************************************************************************** * edge_startup ****************************************************************************/ -static int edge_startup (struct usb_serial *serial) +static int edge_startup(struct usb_serial *serial) { struct edgeport_serial *edge_serial; struct edgeport_port *edge_port; @@ -2855,10 +3005,10 @@ static int edge_startup (struct usb_serial *serial) sizeof(struct edge_compatibility_bits)); /* get the manufacturing descriptor for this device */ - get_manufacturing_desc (edge_serial); + get_manufacturing_desc(edge_serial); /* get the boot descriptor */ - get_boot_desc (edge_serial); + get_boot_desc(edge_serial); get_product_info(edge_serial); } @@ -2879,41 +3029,43 @@ static int edge_startup (struct usb_serial *serial) /* If not an EPiC device */ if (!edge_serial->is_epic) { /* now load the application firmware into this device */ - load_application_firmware (edge_serial); + load_application_firmware(edge_serial); dbg("%s - time 2 %ld", __func__, jiffies); /* Check current Edgeport EEPROM and update if necessary */ - update_edgeport_E2PROM (edge_serial); + update_edgeport_E2PROM(edge_serial); dbg("%s - time 3 %ld", __func__, jiffies); /* set the configuration to use #1 */ -// dbg("set_configuration 1"); -// usb_set_configuration (dev, 1); +/* dbg("set_configuration 1"); */ +/* usb_set_configuration (dev, 1); */ } dbg(" FirmwareMajorVersion %d.%d.%d", edge_serial->product_info.FirmwareMajorVersion, edge_serial->product_info.FirmwareMinorVersion, le16_to_cpu(edge_serial->product_info.FirmwareBuildNumber)); - /* we set up the pointers to the endpoints in the edge_open function, + /* we set up the pointers to the endpoints in the edge_open function, * as the structures aren't created yet. */ /* set up our port private structures */ for (i = 0; i < serial->num_ports; ++i) { - edge_port = kmalloc (sizeof(struct edgeport_port), GFP_KERNEL); + edge_port = kmalloc(sizeof(struct edgeport_port), GFP_KERNEL); if (edge_port == NULL) { - dev_err(&serial->dev->dev, "%s - Out of memory\n", __func__); + dev_err(&serial->dev->dev, "%s - Out of memory\n", + __func__); for (j = 0; j < i; ++j) { - kfree (usb_get_serial_port_data(serial->port[j])); - usb_set_serial_port_data(serial->port[j], NULL); + kfree(usb_get_serial_port_data(serial->port[j])); + usb_set_serial_port_data(serial->port[j], + NULL); } usb_set_serial_data(serial, NULL); kfree(edge_serial); return -ENOMEM; } - memset (edge_port, 0, sizeof(struct edgeport_port)); + memset(edge_port, 0, sizeof(struct edgeport_port)); spin_lock_init(&edge_port->ep_lock); edge_port->port = serial->port[i]; usb_set_serial_port_data(serial->port[i], edge_port); @@ -2922,14 +3074,16 @@ static int edge_startup (struct usb_serial *serial) response = 0; 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. */ + /* 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; - for (i = 0; i < serial->interface->altsetting[0].desc.bNumEndpoints; ++i) { + 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; + endpoint = &serial->interface->altsetting[0]. + endpoint[i].desc; buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); if (!interrupt_in_found && (usb_endpoint_is_int_in(endpoint))) { @@ -2937,58 +3091,67 @@ static int edge_startup (struct usb_serial *serial) dbg("found interrupt in"); /* not set up yet, so do it now */ - edge_serial->interrupt_read_urb = usb_alloc_urb(0, GFP_KERNEL); + edge_serial->interrupt_read_urb = + usb_alloc_urb(0, GFP_KERNEL); if (!edge_serial->interrupt_read_urb) { err("out of memory"); return -ENOMEM; } - edge_serial->interrupt_in_buffer = kmalloc(buffer_size, GFP_KERNEL); + edge_serial->interrupt_in_buffer = + kmalloc(buffer_size, GFP_KERNEL); if (!edge_serial->interrupt_in_buffer) { err("out of memory"); usb_free_urb(edge_serial->interrupt_read_urb); return -ENOMEM; } - edge_serial->interrupt_in_endpoint = endpoint->bEndpointAddress; + edge_serial->interrupt_in_endpoint = + endpoint->bEndpointAddress; /* set up our interrupt urb */ - usb_fill_int_urb(edge_serial->interrupt_read_urb, - dev, - usb_rcvintpipe(dev, endpoint->bEndpointAddress), - edge_serial->interrupt_in_buffer, - buffer_size, - edge_interrupt_callback, - edge_serial, - endpoint->bInterval); + usb_fill_int_urb( + edge_serial->interrupt_read_urb, + dev, + usb_rcvintpipe(dev, + endpoint->bEndpointAddress), + edge_serial->interrupt_in_buffer, + buffer_size, + edge_interrupt_callback, + edge_serial, + endpoint->bInterval); interrupt_in_found = true; } if (!bulk_in_found && - (usb_endpoint_is_bulk_in(endpoint))) { + (usb_endpoint_is_bulk_in(endpoint))) { /* we found a bulk in endpoint */ dbg("found bulk in"); /* not set up yet, so do it now */ - edge_serial->read_urb = usb_alloc_urb(0, GFP_KERNEL); + edge_serial->read_urb = + usb_alloc_urb(0, GFP_KERNEL); if (!edge_serial->read_urb) { err("out of memory"); return -ENOMEM; } - edge_serial->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); + edge_serial->bulk_in_buffer = + kmalloc(buffer_size, GFP_KERNEL); if (!edge_serial->bulk_in_buffer) { - err ("out of memory"); + err("out of memory"); usb_free_urb(edge_serial->read_urb); return -ENOMEM; } - edge_serial->bulk_in_endpoint = endpoint->bEndpointAddress; + edge_serial->bulk_in_endpoint = + endpoint->bEndpointAddress; /* set up our bulk in urb */ usb_fill_bulk_urb(edge_serial->read_urb, dev, - usb_rcvbulkpipe(dev, endpoint->bEndpointAddress), - edge_serial->bulk_in_buffer, - le16_to_cpu(endpoint->wMaxPacketSize), - edge_bulk_in_callback, - edge_serial); + usb_rcvbulkpipe(dev, + endpoint->bEndpointAddress), + edge_serial->bulk_in_buffer, + le16_to_cpu(endpoint->wMaxPacketSize), + edge_bulk_in_callback, + edge_serial); bulk_in_found = true; } @@ -2996,21 +3159,24 @@ static int edge_startup (struct usb_serial *serial) (usb_endpoint_is_bulk_out(endpoint))) { /* we found a bulk out endpoint */ dbg("found bulk out"); - edge_serial->bulk_out_endpoint = endpoint->bEndpointAddress; + edge_serial->bulk_out_endpoint = + endpoint->bEndpointAddress; bulk_out_found = true; } } if (!interrupt_in_found || !bulk_in_found || !bulk_out_found) { - err ("Error - the proper endpoints were not found!"); + err("Error - the proper endpoints were not found!"); return -ENODEV; } /* start interrupt read for this edgeport this interrupt will * continue as long as the edgeport is connected */ - response = usb_submit_urb(edge_serial->interrupt_read_urb, GFP_KERNEL); + response = usb_submit_urb(edge_serial->interrupt_read_urb, + GFP_KERNEL); if (response) - err("%s - Error %d submitting control urb", __func__, response); + err("%s - Error %d submitting control urb", + __func__, response); } return response; } @@ -3020,7 +3186,7 @@ static int edge_startup (struct usb_serial *serial) * edge_shutdown * This function is called whenever the device is removed from the usb bus. ****************************************************************************/ -static void edge_shutdown (struct usb_serial *serial) +static void edge_shutdown(struct usb_serial *serial) { struct edgeport_serial *edge_serial = usb_get_serial_data(serial); int i; @@ -3028,8 +3194,8 @@ static void edge_shutdown (struct usb_serial *serial) dbg("%s", __func__); /* stop reads and writes on all ports */ - for (i=0; i < serial->num_ports; ++i) { - kfree (usb_get_serial_port_data(serial->port[i])); + for (i = 0; i < serial->num_ports; ++i) { + kfree(usb_get_serial_port_data(serial->port[i])); usb_set_serial_port_data(serial->port[i], NULL); } /* free up our endpoint stuff */ @@ -3069,7 +3235,7 @@ static int __init edgeport_init(void) if (retval) goto failed_epic_device_register; retval = usb_register(&io_driver); - if (retval) + if (retval) goto failed_usb_register; atomic_set(&CmdUrbs, 0); info(DRIVER_DESC " " DRIVER_VERSION); @@ -3094,19 +3260,19 @@ failed_2port_device_register: ****************************************************************************/ static void __exit edgeport_exit (void) { - usb_deregister (&io_driver); - usb_serial_deregister (&edgeport_2port_device); - usb_serial_deregister (&edgeport_4port_device); - usb_serial_deregister (&edgeport_8port_device); - usb_serial_deregister (&epic_device); + usb_deregister(&io_driver); + usb_serial_deregister(&edgeport_2port_device); + usb_serial_deregister(&edgeport_4port_device); + usb_serial_deregister(&edgeport_8port_device); + usb_serial_deregister(&epic_device); } module_init(edgeport_init); module_exit(edgeport_exit); /* Module information */ -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_FIRMWARE("edgeport/boot.fw"); MODULE_FIRMWARE("edgeport/boot2.fw"); diff --git a/drivers/usb/serial/io_tables.h b/drivers/usb/serial/io_tables.h index 2ec85893f27a..7eb9d67b81b6 100644 --- a/drivers/usb/serial/io_tables.h +++ b/drivers/usb/serial/io_tables.h @@ -8,7 +8,7 @@ * 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 IO_TABLES_H @@ -90,10 +90,10 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) }, { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) }, { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) }, - { } /* Terminating entry */ + { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table_combined); +MODULE_DEVICE_TABLE(usb, id_table_combined); static struct usb_driver io_driver = { .name = "io_edgeport", diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 61daea3f7b2d..cb4c54316cf5 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -18,8 +18,8 @@ * * Version history: * - * July 11, 2002 Removed 4 port device structure since all TI UMP - * chips have only 2 ports + * July 11, 2002 Removed 4 port device structure since all TI UMP + * chips have only 2 ports * David Iacovelli (davidi@ionetworks.com) * */ @@ -38,7 +38,7 @@ #include <linux/serial.h> #include <linux/ioctl.h> #include <linux/firmware.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> @@ -57,18 +57,19 @@ struct edgeport_uart_buf_desc { - __u32 count; // Number of bytes currently in buffer + __u32 count; /* Number of bytes currently in buffer */ }; /* different hardware types */ #define HARDWARE_TYPE_930 0 #define HARDWARE_TYPE_TIUMP 1 -// IOCTL_PRIVATE_TI_GET_MODE Definitions -#define TI_MODE_CONFIGURING 0 // Device has not entered start device -#define TI_MODE_BOOT 1 // Staying in boot mode -#define TI_MODE_DOWNLOAD 2 // Made it to download mode -#define TI_MODE_TRANSITIONING 3 // Currently in boot mode but transitioning to download mode +/* IOCTL_PRIVATE_TI_GET_MODE Definitions */ +#define TI_MODE_CONFIGURING 0 /* Device has not entered start device */ +#define TI_MODE_BOOT 1 /* Staying in boot mode */ +#define TI_MODE_DOWNLOAD 2 /* Made it to download mode */ +#define TI_MODE_TRANSITIONING 3 /* Currently in boot mode but + transitioning to download mode */ /* read urb state */ #define EDGE_READ_URB_RUNNING 0 @@ -82,10 +83,9 @@ struct edgeport_uart_buf_desc { /* Product information read from the Edgeport */ -struct product_info -{ - int TiMode; // Current TI Mode - __u8 hardware_type; // Type of hardware +struct product_info { + int TiMode; /* Current TI Mode */ + __u8 hardware_type; /* Type of hardware */ } __attribute__((packed)); /* circular buffer */ @@ -116,7 +116,7 @@ struct edgeport_port { happen */ struct edgeport_serial *edge_serial; struct usb_serial_port *port; - __u8 bUartMode; /* Port type, 0: RS232, etc. */ + __u8 bUartMode; /* Port type, 0: RS232, etc. */ spinlock_t ep_lock; int ep_read_urb_state; int ep_write_urb_in_use; @@ -125,8 +125,9 @@ struct edgeport_port { struct edgeport_serial { struct product_info product_info; - u8 TI_I2C_Type; // Type of I2C in UMP - u8 TiReadI2C; // Set to TRUE if we have read the I2c in Boot Mode + u8 TI_I2C_Type; /* Type of I2C in UMP */ + u8 TiReadI2C; /* Set to TRUE if we have read the + I2c in Boot Mode */ struct mutex es_lock; int num_ports_open; struct usb_serial *serial; @@ -214,7 +215,7 @@ static struct usb_device_id id_table_combined [] = { { } }; -MODULE_DEVICE_TABLE (usb, id_table_combined); +MODULE_DEVICE_TABLE(usb, id_table_combined); static struct usb_driver io_driver = { .name = "io_ti", @@ -231,20 +232,20 @@ static unsigned short OperationalBuildNumber; static int debug; -static int TIStayInBootMode = 0; static int low_latency = EDGE_LOW_LATENCY; static int closing_wait = EDGE_CLOSING_WAIT; -static int ignore_cpu_rev = 0; -static int default_uart_mode = 0; /* RS232 */ - +static int ignore_cpu_rev; +static int default_uart_mode; /* RS232 */ -static void edge_tty_recv(struct device *dev, struct tty_struct *tty, unsigned char *data, int length); +static void edge_tty_recv(struct device *dev, struct tty_struct *tty, + unsigned char *data, int length); static void stop_read(struct edgeport_port *edge_port); static int restart_read(struct edgeport_port *edge_port); -static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old_termios); -static void edge_send(struct usb_serial_port *port); +static void edge_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios); +static void edge_send(struct tty_struct *tty); /* sysfs attributes */ static int edge_create_sysfs_attrs(struct usb_serial_port *port); @@ -262,87 +263,57 @@ static unsigned int edge_buf_get(struct edge_buf *eb, char *buf, unsigned int count); -static int TIReadVendorRequestSync (struct usb_device *dev, - __u8 request, - __u16 value, - __u16 index, - u8 *data, - int size) +static int ti_vread_sync(struct usb_device *dev, __u8 request, + __u16 value, __u16 index, u8 *data, int size) { int status; - status = usb_control_msg (dev, - usb_rcvctrlpipe(dev, 0), - request, - (USB_TYPE_VENDOR | - USB_RECIP_DEVICE | - USB_DIR_IN), - value, - index, - data, - size, - 1000); + status = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request, + (USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN), + value, index, data, size, 1000); if (status < 0) return status; if (status != size) { - dbg ("%s - wanted to write %d, but only wrote %d", - __func__, size, status); + dbg("%s - wanted to write %d, but only wrote %d", + __func__, size, status); return -ECOMM; } return 0; } -static int TISendVendorRequestSync (struct usb_device *dev, - __u8 request, - __u16 value, - __u16 index, - u8 *data, - int size) +static int ti_vsend_sync(struct usb_device *dev, __u8 request, + __u16 value, __u16 index, u8 *data, int size) { int status; - status = usb_control_msg (dev, - usb_sndctrlpipe(dev, 0), - request, - (USB_TYPE_VENDOR | - USB_RECIP_DEVICE | - USB_DIR_OUT), - value, - index, - data, - size, - 1000); + status = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request, + (USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT), + value, index, data, size, 1000); if (status < 0) return status; if (status != size) { - dbg ("%s - wanted to write %d, but only wrote %d", + dbg("%s - wanted to write %d, but only wrote %d", __func__, size, status); return -ECOMM; } return 0; } -static int TIWriteCommandSync (struct usb_device *dev, __u8 command, +static int send_cmd(struct usb_device *dev, __u8 command, __u8 moduleid, __u16 value, u8 *data, int size) { - return TISendVendorRequestSync (dev, - command, // Request - value, // wValue - moduleid, // wIndex - data, // TransferBuffer - size); // TransferBufferLength - + return ti_vsend_sync(dev, command, value, moduleid, data, size); } /* clear tx/rx buffers and fifo in TI UMP */ -static int TIPurgeDataSync (struct usb_serial_port *port, __u16 mask) +static int purge_port(struct usb_serial_port *port, __u16 mask) { int port_number = port->number - port->serial->minor; - dbg ("%s - port %d, mask %x", __func__, port_number, mask); + dbg("%s - port %d, mask %x", __func__, port_number, mask); - return TIWriteCommandSync (port->serial->dev, + return send_cmd(port->serial->dev, UMPC_PURGE_PORT, (__u8)(UMPM_UART1_PORT + port_number), mask, @@ -351,92 +322,87 @@ static int TIPurgeDataSync (struct usb_serial_port *port, __u16 mask) } /** - * TIReadDownloadMemory - Read edgeport memory from TI chip + * read_download_mem - Read edgeport memory from TI chip * @dev: usb device pointer * @start_address: Device CPU address at which to read * @length: Length of above data * @address_type: Can read both XDATA and I2C * @buffer: pointer to input data buffer */ -static int TIReadDownloadMemory(struct usb_device *dev, int start_address, +static int read_download_mem(struct usb_device *dev, int start_address, int length, __u8 address_type, __u8 *buffer) { int status = 0; __u8 read_length; __be16 be_start_address; - - dbg ("%s - @ %x for %d", __func__, start_address, length); + + dbg("%s - @ %x for %d", __func__, start_address, length); /* Read in blocks of 64 bytes * (TI firmware can't handle more than 64 byte reads) */ while (length) { if (length > 64) - read_length= 64; + read_length = 64; else read_length = (__u8)length; if (read_length > 1) { - dbg ("%s - @ %x for %d", __func__, + dbg("%s - @ %x for %d", __func__, start_address, read_length); } - be_start_address = cpu_to_be16 (start_address); - status = TIReadVendorRequestSync (dev, - UMPC_MEMORY_READ, // Request - (__u16)address_type, // wValue (Address type) - (__force __u16)be_start_address, // wIndex (Address to read) - buffer, // TransferBuffer - read_length); // TransferBufferLength + be_start_address = cpu_to_be16(start_address); + status = ti_vread_sync(dev, UMPC_MEMORY_READ, + (__u16)address_type, + (__force __u16)be_start_address, + buffer, read_length); if (status) { - dbg ("%s - ERROR %x", __func__, status); + dbg("%s - ERROR %x", __func__, status); return status; } - if (read_length > 1) { + if (read_length > 1) usb_serial_debug_data(debug, &dev->dev, __func__, read_length, buffer); - } /* Update pointers/length */ start_address += read_length; buffer += read_length; length -= read_length; } - + return status; } -static int TIReadRam (struct usb_device *dev, int start_address, int length, __u8 *buffer) +static int read_ram(struct usb_device *dev, int start_address, + int length, __u8 *buffer) { - return TIReadDownloadMemory (dev, - start_address, - length, - DTK_ADDR_SPACE_XDATA, - buffer); + return read_download_mem(dev, start_address, length, + DTK_ADDR_SPACE_XDATA, buffer); } /* Read edgeport memory to a given block */ -static int TIReadBootMemory (struct edgeport_serial *serial, int start_address, int length, __u8 * buffer) +static int read_boot_mem(struct edgeport_serial *serial, + int start_address, int length, __u8 *buffer) { int status = 0; int i; - for (i=0; i< length; i++) { - status = TIReadVendorRequestSync (serial->serial->dev, - UMPC_MEMORY_READ, // Request - serial->TI_I2C_Type, // wValue (Address type) - (__u16)(start_address+i), // wIndex - &buffer[i], // TransferBuffer - 0x01); // TransferBufferLength + for (i = 0; i < length; i++) { + status = ti_vread_sync(serial->serial->dev, + UMPC_MEMORY_READ, serial->TI_I2C_Type, + (__u16)(start_address+i), &buffer[i], 0x01); if (status) { - dbg ("%s - ERROR %x", __func__, status); + dbg("%s - ERROR %x", __func__, status); return status; } } - dbg ("%s - start_address = %x, length = %d", __func__, start_address, length); - usb_serial_debug_data(debug, &serial->serial->dev->dev, __func__, length, buffer); + dbg("%s - start_address = %x, length = %d", + __func__, start_address, length); + usb_serial_debug_data(debug, &serial->serial->dev->dev, + __func__, length, buffer); serial->TiReadI2C = 1; @@ -444,7 +410,8 @@ static int TIReadBootMemory (struct edgeport_serial *serial, int start_address, } /* Write given block to TI EPROM memory */ -static int TIWriteBootMemory (struct edgeport_serial *serial, int start_address, int length, __u8 *buffer) +static int write_boot_mem(struct edgeport_serial *serial, + int start_address, int length, __u8 *buffer) { int status = 0; int i; @@ -452,57 +419,58 @@ static int TIWriteBootMemory (struct edgeport_serial *serial, int start_address, /* Must do a read before write */ if (!serial->TiReadI2C) { - status = TIReadBootMemory(serial, 0, 1, &temp); + status = read_boot_mem(serial, 0, 1, &temp); if (status) return status; } - for (i=0; i < length; ++i) { - status = TISendVendorRequestSync (serial->serial->dev, - UMPC_MEMORY_WRITE, // Request - buffer[i], // wValue - (__u16)(i+start_address), // wIndex - NULL, // TransferBuffer - 0); // TransferBufferLength + for (i = 0; i < length; ++i) { + status = ti_vsend_sync(serial->serial->dev, + UMPC_MEMORY_WRITE, buffer[i], + (__u16)(i + start_address), NULL, 0); if (status) return status; } - dbg ("%s - start_sddr = %x, length = %d", __func__, start_address, length); - usb_serial_debug_data(debug, &serial->serial->dev->dev, __func__, length, buffer); + dbg("%s - start_sddr = %x, length = %d", + __func__, start_address, length); + usb_serial_debug_data(debug, &serial->serial->dev->dev, + __func__, length, buffer); return status; } /* Write edgeport I2C memory to TI chip */ -static int TIWriteDownloadI2C (struct edgeport_serial *serial, int start_address, int length, __u8 address_type, __u8 *buffer) +static int write_i2c_mem(struct edgeport_serial *serial, + int start_address, int length, __u8 address_type, __u8 *buffer) { int status = 0; int write_length; __be16 be_start_address; /* We can only send a maximum of 1 aligned byte page at a time */ - + /* calulate the number of bytes left in the first page */ - write_length = EPROM_PAGE_SIZE - (start_address & (EPROM_PAGE_SIZE - 1)); + write_length = EPROM_PAGE_SIZE - + (start_address & (EPROM_PAGE_SIZE - 1)); if (write_length > length) write_length = length; - dbg ("%s - BytesInFirstPage Addr = %x, length = %d", __func__, start_address, write_length); - usb_serial_debug_data(debug, &serial->serial->dev->dev, __func__, write_length, buffer); + dbg("%s - BytesInFirstPage Addr = %x, length = %d", + __func__, start_address, write_length); + usb_serial_debug_data(debug, &serial->serial->dev->dev, + __func__, write_length, buffer); /* Write first page */ - be_start_address = cpu_to_be16 (start_address); - status = TISendVendorRequestSync (serial->serial->dev, - UMPC_MEMORY_WRITE, // Request - (__u16)address_type, // wValue - (__force __u16)be_start_address, // wIndex - buffer, // TransferBuffer - write_length); + be_start_address = cpu_to_be16(start_address); + status = ti_vsend_sync(serial->serial->dev, + UMPC_MEMORY_WRITE, (__u16)address_type, + (__force __u16)be_start_address, + buffer, write_length); if (status) { - dbg ("%s - ERROR %d", __func__, status); + dbg("%s - ERROR %d", __func__, status); return status; } @@ -510,29 +478,31 @@ static int TIWriteDownloadI2C (struct edgeport_serial *serial, int start_address start_address += write_length; buffer += write_length; - /* We should be aligned now -- can write max page size bytes at a time */ + /* We should be aligned now -- can write + max page size bytes at a time */ while (length) { if (length > EPROM_PAGE_SIZE) write_length = EPROM_PAGE_SIZE; else write_length = length; - dbg ("%s - Page Write Addr = %x, length = %d", __func__, start_address, write_length); - usb_serial_debug_data(debug, &serial->serial->dev->dev, __func__, write_length, buffer); + dbg("%s - Page Write Addr = %x, length = %d", + __func__, start_address, write_length); + usb_serial_debug_data(debug, &serial->serial->dev->dev, + __func__, write_length, buffer); /* Write next page */ - be_start_address = cpu_to_be16 (start_address); - status = TISendVendorRequestSync (serial->serial->dev, - UMPC_MEMORY_WRITE, // Request - (__u16)address_type, // wValue - (__force __u16)be_start_address, // wIndex - buffer, // TransferBuffer - write_length); // TransferBufferLength + be_start_address = cpu_to_be16(start_address); + status = ti_vsend_sync(serial->serial->dev, UMPC_MEMORY_WRITE, + (__u16)address_type, + (__force __u16)be_start_address, + buffer, write_length); if (status) { - dev_err (&serial->serial->dev->dev, "%s - ERROR %d\n", __func__, status); + dev_err(&serial->serial->dev->dev, "%s - ERROR %d\n", + __func__, status); return status; } - + length -= write_length; start_address += write_length; buffer += write_length; @@ -541,25 +511,25 @@ static int TIWriteDownloadI2C (struct edgeport_serial *serial, int start_address } /* Examine the UMP DMA registers and LSR - * + * * Check the MSBit of the X and Y DMA byte count registers. * A zero in this bit indicates that the TX DMA buffers are empty * then check the TX Empty bit in the UART. */ -static int TIIsTxActive (struct edgeport_port *port) +static int tx_active(struct edgeport_port *port) { int status; struct out_endpoint_desc_block *oedb; __u8 *lsr; int bytes_left = 0; - oedb = kmalloc (sizeof (* oedb), GFP_KERNEL); + oedb = kmalloc(sizeof(*oedb), GFP_KERNEL); if (!oedb) { - dev_err (&port->port->dev, "%s - out of memory\n", __func__); + dev_err(&port->port->dev, "%s - out of memory\n", __func__); return -ENOMEM; } - lsr = kmalloc (1, GFP_KERNEL); /* Sigh, that's right, just one byte, + lsr = kmalloc(1, GFP_KERNEL); /* Sigh, that's right, just one byte, as not all platforms can do DMA from stack */ if (!lsr) { @@ -567,51 +537,47 @@ static int TIIsTxActive (struct edgeport_port *port) return -ENOMEM; } /* Read the DMA Count Registers */ - status = TIReadRam (port->port->serial->dev, - port->dma_address, - sizeof( *oedb), - (void *)oedb); - + status = read_ram(port->port->serial->dev, port->dma_address, + sizeof(*oedb), (void *)oedb); if (status) goto exit_is_tx_active; - dbg ("%s - XByteCount 0x%X", __func__, oedb->XByteCount); + dbg("%s - XByteCount 0x%X", __func__, oedb->XByteCount); /* and the LSR */ - status = TIReadRam (port->port->serial->dev, - port->uart_base + UMPMEM_OFFS_UART_LSR, - 1, - lsr); + status = read_ram(port->port->serial->dev, + port->uart_base + UMPMEM_OFFS_UART_LSR, 1, lsr); if (status) goto exit_is_tx_active; - dbg ("%s - LSR = 0x%X", __func__, *lsr); - + dbg("%s - LSR = 0x%X", __func__, *lsr); + /* If either buffer has data or we are transmitting then return TRUE */ - if ((oedb->XByteCount & 0x80 ) != 0 ) + if ((oedb->XByteCount & 0x80) != 0) bytes_left += 64; - if ((*lsr & UMP_UART_LSR_TX_MASK ) == 0 ) + if ((*lsr & UMP_UART_LSR_TX_MASK) == 0) bytes_left += 1; /* We return Not Active if we get any kind of error */ exit_is_tx_active: - dbg ("%s - return %d", __func__, bytes_left ); + dbg("%s - return %d", __func__, bytes_left); kfree(lsr); kfree(oedb); return bytes_left; } -static void TIChasePort(struct edgeport_port *port, unsigned long timeout, int flush) +static void chase_port(struct edgeport_port *port, unsigned long timeout, + int flush) { int baud_rate; - struct tty_struct *tty = port->port->tty; + struct tty_struct *tty = port->port->port.tty; wait_queue_t wait; unsigned long flags; if (!timeout) - timeout = (HZ*EDGE_CLOSING_WAIT)/100; + timeout = (HZ * EDGE_CLOSING_WAIT)/100; /* wait for data to drain from the buffer */ spin_lock_irqsave(&port->ep_lock, flags); @@ -621,7 +587,8 @@ static void TIChasePort(struct edgeport_port *port, unsigned long timeout, int f set_current_state(TASK_INTERRUPTIBLE); if (edge_buf_data_avail(port->ep_out_buf) == 0 || timeout == 0 || signal_pending(current) - || !usb_get_intfdata(port->port->serial->interface)) /* disconnect */ + || !usb_get_intfdata(port->port->serial->interface)) + /* disconnect */ break; spin_unlock_irqrestore(&port->ep_lock, flags); timeout = schedule_timeout(timeout); @@ -636,8 +603,9 @@ static void TIChasePort(struct edgeport_port *port, unsigned long timeout, int f /* wait for data to drain from the device */ timeout += jiffies; while ((long)(jiffies - timeout) < 0 && !signal_pending(current) - && usb_get_intfdata(port->port->serial->interface)) { /* not disconnected */ - if (!TIIsTxActive(port)) + && usb_get_intfdata(port->port->serial->interface)) { + /* not disconnected */ + if (!tx_active(port)) break; msleep(10); } @@ -647,72 +615,72 @@ static void TIChasePort(struct edgeport_port *port, unsigned long timeout, int f return; /* wait one more character time, based on baud rate */ - /* (TIIsTxActive doesn't seem to wait for the last byte) */ - if ((baud_rate=port->baud_rate) == 0) + /* (tx_active doesn't seem to wait for the last byte) */ + baud_rate = port->baud_rate; + if (baud_rate == 0) baud_rate = 50; msleep(max(1, DIV_ROUND_UP(10000, baud_rate))); } -static int TIChooseConfiguration (struct usb_device *dev) +static int choose_config(struct usb_device *dev) { - // There may be multiple configurations on this device, in which case - // we would need to read and parse all of them to find out which one - // we want. However, we just support one config at this point, - // configuration # 1, which is Config Descriptor 0. + /* + * There may be multiple configurations on this device, in which case + * we would need to read and parse all of them to find out which one + * we want. However, we just support one config at this point, + * configuration # 1, which is Config Descriptor 0. + */ - dbg ("%s - Number of Interfaces = %d", __func__, dev->config->desc.bNumInterfaces); - dbg ("%s - MAX Power = %d", __func__, dev->config->desc.bMaxPower*2); + dbg("%s - Number of Interfaces = %d", + __func__, dev->config->desc.bNumInterfaces); + dbg("%s - MAX Power = %d", + __func__, dev->config->desc.bMaxPower * 2); if (dev->config->desc.bNumInterfaces != 1) { - dev_err (&dev->dev, "%s - bNumInterfaces is not 1, ERROR!\n", __func__); + dev_err(&dev->dev, "%s - bNumInterfaces is not 1, ERROR!\n", + __func__); return -ENODEV; } return 0; } -static int TIReadRom (struct edgeport_serial *serial, int start_address, int length, __u8 *buffer) +static int read_rom(struct edgeport_serial *serial, + int start_address, int length, __u8 *buffer) { int status; if (serial->product_info.TiMode == TI_MODE_DOWNLOAD) { - status = TIReadDownloadMemory (serial->serial->dev, + status = read_download_mem(serial->serial->dev, start_address, length, serial->TI_I2C_Type, buffer); } else { - status = TIReadBootMemory (serial, - start_address, - length, - buffer); + status = read_boot_mem(serial, start_address, length, + buffer); } - return status; } -static int TIWriteRom (struct edgeport_serial *serial, int start_address, int length, __u8 *buffer) +static int write_rom(struct edgeport_serial *serial, int start_address, + int length, __u8 *buffer) { if (serial->product_info.TiMode == TI_MODE_BOOT) - return TIWriteBootMemory (serial, - start_address, - length, - buffer); + return write_boot_mem(serial, start_address, length, + buffer); if (serial->product_info.TiMode == TI_MODE_DOWNLOAD) - return TIWriteDownloadI2C (serial, - start_address, - length, - serial->TI_I2C_Type, - buffer); - + return write_i2c_mem(serial, start_address, length, + serial->TI_I2C_Type, buffer); return -EINVAL; } /* Read a descriptor header from I2C based on type */ -static int TIGetDescriptorAddress (struct edgeport_serial *serial, int desc_type, struct ti_i2c_desc *rom_desc) +static int get_descriptor_addr(struct edgeport_serial *serial, + int desc_type, struct ti_i2c_desc *rom_desc) { int start_address; int status; @@ -720,41 +688,42 @@ static int TIGetDescriptorAddress (struct edgeport_serial *serial, int desc_type /* Search for requested descriptor in I2C */ start_address = 2; do { - status = TIReadRom (serial, + status = read_rom(serial, start_address, sizeof(struct ti_i2c_desc), - (__u8 *)rom_desc ); + (__u8 *)rom_desc); if (status) return 0; if (rom_desc->Type == desc_type) return start_address; - start_address = start_address + sizeof(struct ti_i2c_desc) + rom_desc->Size; + start_address = start_address + sizeof(struct ti_i2c_desc) + + rom_desc->Size; } while ((start_address < TI_MAX_I2C_SIZE) && rom_desc->Type); - + return 0; } /* Validate descriptor checksum */ -static int ValidChecksum(struct ti_i2c_desc *rom_desc, __u8 *buffer) +static int valid_csum(struct ti_i2c_desc *rom_desc, __u8 *buffer) { __u16 i; __u8 cs = 0; - for (i=0; i < rom_desc->Size; i++) { + for (i = 0; i < rom_desc->Size; i++) cs = (__u8)(cs + buffer[i]); - } + if (cs != rom_desc->CheckSum) { - dbg ("%s - Mismatch %x - %x", __func__, rom_desc->CheckSum, cs); + dbg("%s - Mismatch %x - %x", __func__, rom_desc->CheckSum, cs); return -EINVAL; } return 0; } /* Make sure that the I2C image is good */ -static int TiValidateI2cImage (struct edgeport_serial *serial) +static int check_i2c_image(struct edgeport_serial *serial) { struct device *dev = &serial->serial->dev->dev; int status = 0; @@ -763,120 +732,124 @@ static int TiValidateI2cImage (struct edgeport_serial *serial) __u8 *buffer; __u16 ttype; - rom_desc = kmalloc (sizeof (*rom_desc), GFP_KERNEL); + rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL); if (!rom_desc) { - dev_err (dev, "%s - out of memory\n", __func__); + dev_err(dev, "%s - out of memory\n", __func__); return -ENOMEM; } - buffer = kmalloc (TI_MAX_I2C_SIZE, GFP_KERNEL); + buffer = kmalloc(TI_MAX_I2C_SIZE, GFP_KERNEL); if (!buffer) { - dev_err (dev, "%s - out of memory when allocating buffer\n", __func__); - kfree (rom_desc); + dev_err(dev, "%s - out of memory when allocating buffer\n", + __func__); + kfree(rom_desc); return -ENOMEM; } - // Read the first byte (Signature0) must be 0x52 or 0x10 - status = TIReadRom (serial, 0, 1, buffer); + /* Read the first byte (Signature0) must be 0x52 or 0x10 */ + status = read_rom(serial, 0, 1, buffer); if (status) - goto ExitTiValidateI2cImage; + goto out; if (*buffer != UMP5152 && *buffer != UMP3410) { - dev_err (dev, "%s - invalid buffer signature\n", __func__); + dev_err(dev, "%s - invalid buffer signature\n", __func__); status = -ENODEV; - goto ExitTiValidateI2cImage; + goto out; } do { - // Validate the I2C - status = TIReadRom (serial, + /* Validate the I2C */ + status = read_rom(serial, start_address, sizeof(struct ti_i2c_desc), (__u8 *)rom_desc); if (status) break; - if ((start_address + sizeof(struct ti_i2c_desc) + rom_desc->Size) > TI_MAX_I2C_SIZE) { + if ((start_address + sizeof(struct ti_i2c_desc) + + rom_desc->Size) > TI_MAX_I2C_SIZE) { status = -ENODEV; - dbg ("%s - structure too big, erroring out.", __func__); + dbg("%s - structure too big, erroring out.", __func__); break; } - dbg ("%s Type = 0x%x", __func__, rom_desc->Type); + dbg("%s Type = 0x%x", __func__, rom_desc->Type); - // Skip type 2 record + /* Skip type 2 record */ ttype = rom_desc->Type & 0x0f; - if ( ttype != I2C_DESC_TYPE_FIRMWARE_BASIC - && ttype != I2C_DESC_TYPE_FIRMWARE_AUTO ) { - // Read the descriptor data - status = TIReadRom(serial, - start_address+sizeof(struct ti_i2c_desc), - rom_desc->Size, - buffer); + if (ttype != I2C_DESC_TYPE_FIRMWARE_BASIC + && ttype != I2C_DESC_TYPE_FIRMWARE_AUTO) { + /* Read the descriptor data */ + status = read_rom(serial, start_address + + sizeof(struct ti_i2c_desc), + rom_desc->Size, buffer); if (status) break; - status = ValidChecksum(rom_desc, buffer); + status = valid_csum(rom_desc, buffer); if (status) break; } - start_address = start_address + sizeof(struct ti_i2c_desc) + rom_desc->Size; + start_address = start_address + sizeof(struct ti_i2c_desc) + + rom_desc->Size; - } while ((rom_desc->Type != I2C_DESC_TYPE_ION) && (start_address < TI_MAX_I2C_SIZE)); + } while ((rom_desc->Type != I2C_DESC_TYPE_ION) && + (start_address < TI_MAX_I2C_SIZE)); - if ((rom_desc->Type != I2C_DESC_TYPE_ION) || (start_address > TI_MAX_I2C_SIZE)) + if ((rom_desc->Type != I2C_DESC_TYPE_ION) || + (start_address > TI_MAX_I2C_SIZE)) status = -ENODEV; -ExitTiValidateI2cImage: - kfree (buffer); - kfree (rom_desc); +out: + kfree(buffer); + kfree(rom_desc); return status; } -static int TIReadManufDescriptor (struct edgeport_serial *serial, __u8 *buffer) +static int get_manuf_info(struct edgeport_serial *serial, __u8 *buffer) { int status; int start_address; struct ti_i2c_desc *rom_desc; struct edge_ti_manuf_descriptor *desc; - rom_desc = kmalloc (sizeof (*rom_desc), GFP_KERNEL); + rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL); if (!rom_desc) { - dev_err (&serial->serial->dev->dev, "%s - out of memory\n", __func__); + dev_err(&serial->serial->dev->dev, "%s - out of memory\n", + __func__); return -ENOMEM; } - start_address = TIGetDescriptorAddress (serial, I2C_DESC_TYPE_ION, rom_desc); + start_address = get_descriptor_addr(serial, I2C_DESC_TYPE_ION, + rom_desc); if (!start_address) { - dbg ("%s - Edge Descriptor not found in I2C", __func__); + dbg("%s - Edge Descriptor not found in I2C", __func__); status = -ENODEV; goto exit; } - // Read the descriptor data - status = TIReadRom (serial, - start_address+sizeof(struct ti_i2c_desc), - rom_desc->Size, - buffer); + /* Read the descriptor data */ + status = read_rom(serial, start_address+sizeof(struct ti_i2c_desc), + rom_desc->Size, buffer); if (status) goto exit; - - status = ValidChecksum(rom_desc, buffer); - + + status = valid_csum(rom_desc, buffer); + desc = (struct edge_ti_manuf_descriptor *)buffer; - dbg ( "%s - IonConfig 0x%x", __func__, desc->IonConfig ); - dbg ( "%s - Version %d", __func__, desc->Version ); - dbg ( "%s - Cpu/Board 0x%x", __func__, desc->CpuRev_BoardRev ); - dbg ( "%s - NumPorts %d", __func__, desc->NumPorts ); - dbg ( "%s - NumVirtualPorts %d", __func__, desc->NumVirtualPorts ); - dbg ( "%s - TotalPorts %d", __func__, desc->TotalPorts ); + dbg("%s - IonConfig 0x%x", __func__, desc->IonConfig); + dbg("%s - Version %d", __func__, desc->Version); + dbg("%s - Cpu/Board 0x%x", __func__, desc->CpuRev_BoardRev); + dbg("%s - NumPorts %d", __func__, desc->NumPorts); + dbg("%s - NumVirtualPorts %d", __func__, desc->NumVirtualPorts); + dbg("%s - TotalPorts %d", __func__, desc->TotalPorts); exit: - kfree (rom_desc); + kfree(rom_desc); return status; } /* Build firmware header used for firmware update */ -static int BuildI2CFirmwareHeader (__u8 *header, struct device *dev) +static int build_i2c_fw_hdr(__u8 *header, struct device *dev) { __u8 *buffer; int buffer_size; @@ -889,24 +862,28 @@ static int BuildI2CFirmwareHeader (__u8 *header, struct device *dev) const struct firmware *fw; const char *fw_name = "edgeport/down3.bin"; - // In order to update the I2C firmware we must change the type 2 record to type 0xF2. - // This will force the UMP to come up in Boot Mode. Then while in boot mode, the driver - // will download the latest firmware (padded to 15.5k) into the UMP ram. - // And finally when the device comes back up in download mode the driver will cause - // the new firmware to be copied from the UMP Ram to I2C and the firmware will update - // the record type from 0xf2 to 0x02. - - // Allocate a 15.5k buffer + 2 bytes for version number (Firmware Record) - buffer_size = (((1024 * 16) - 512 )+ sizeof(struct ti_i2c_firmware_rec)); - - buffer = kmalloc (buffer_size, GFP_KERNEL); + /* In order to update the I2C firmware we must change the type 2 record + * to type 0xF2. This will force the UMP to come up in Boot Mode. + * Then while in boot mode, the driver will download the latest + * firmware (padded to 15.5k) into the UMP ram. And finally when the + * device comes back up in download mode the driver will cause the new + * firmware to be copied from the UMP Ram to I2C and the firmware will + * update the record type from 0xf2 to 0x02. + */ + + /* Allocate a 15.5k buffer + 2 bytes for version number + * (Firmware Record) */ + buffer_size = (((1024 * 16) - 512 ) + + sizeof(struct ti_i2c_firmware_rec)); + + buffer = kmalloc(buffer_size, GFP_KERNEL); if (!buffer) { - dev_err (dev, "%s - out of memory\n", __func__); + dev_err(dev, "%s - out of memory\n", __func__); return -ENOMEM; } - + // Set entire image of 0xffs - memset (buffer, 0xff, buffer_size); + memset(buffer, 0xff, buffer_size); err = request_firmware(&fw, fw_name, dev); if (err) { @@ -921,16 +898,16 @@ static int BuildI2CFirmwareHeader (__u8 *header, struct device *dev) OperationalMinorVersion = fw->data[1]; OperationalBuildNumber = fw->data[2] | (fw->data[3] << 8); - // Copy version number into firmware record + /* Copy version number into firmware record */ firmware_rec = (struct ti_i2c_firmware_rec *)buffer; firmware_rec->Ver_Major = OperationalMajorVersion; firmware_rec->Ver_Minor = OperationalMinorVersion; - // Pointer to fw_down memory image + /* Pointer to fw_down memory image */ img_header = (struct ti_i2c_image_header *)&fw->data[4]; - memcpy (buffer + sizeof(struct ti_i2c_firmware_rec), + memcpy(buffer + sizeof(struct ti_i2c_firmware_rec), &fw->data[4 + sizeof(struct ti_i2c_image_header)], le16_to_cpu(img_header->Length)); @@ -940,12 +917,12 @@ static int BuildI2CFirmwareHeader (__u8 *header, struct device *dev) cs = (__u8)(cs + buffer[i]); } - kfree (buffer); + kfree(buffer); - // Build new header + /* Build new header */ i2c_header = (struct ti_i2c_desc *)header; firmware_rec = (struct ti_i2c_firmware_rec*)i2c_header->Data; - + i2c_header->Type = I2C_DESC_TYPE_FIRMWARE_BLANK; i2c_header->Size = (__u16)buffer_size; i2c_header->CheckSum = cs; @@ -956,103 +933,100 @@ static int BuildI2CFirmwareHeader (__u8 *header, struct device *dev) } /* Try to figure out what type of I2c we have */ -static int TIGetI2cTypeInBootMode (struct edgeport_serial *serial) +static int i2c_type_bootmode(struct edgeport_serial *serial) { int status; __u8 data; - - // Try to read type 2 - status = TIReadVendorRequestSync (serial->serial->dev, - UMPC_MEMORY_READ, // Request - DTK_ADDR_SPACE_I2C_TYPE_II, // wValue (Address type) - 0, // wIndex - &data, // TransferBuffer - 0x01); // TransferBufferLength + + /* Try to read type 2 */ + status = ti_vread_sync(serial->serial->dev, UMPC_MEMORY_READ, + DTK_ADDR_SPACE_I2C_TYPE_II, 0, &data, 0x01); if (status) - dbg ("%s - read 2 status error = %d", __func__, status); + dbg("%s - read 2 status error = %d", __func__, status); else - dbg ("%s - read 2 data = 0x%x", __func__, data); + dbg("%s - read 2 data = 0x%x", __func__, data); if ((!status) && (data == UMP5152 || data == UMP3410)) { - dbg ("%s - ROM_TYPE_II", __func__); + dbg("%s - ROM_TYPE_II", __func__); serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II; return 0; } - // Try to read type 3 - status = TIReadVendorRequestSync (serial->serial->dev, - UMPC_MEMORY_READ, // Request - DTK_ADDR_SPACE_I2C_TYPE_III, // wValue (Address type) - 0, // wIndex - &data, // TransferBuffer - 0x01); // TransferBufferLength + /* Try to read type 3 */ + status = ti_vread_sync(serial->serial->dev, UMPC_MEMORY_READ, + DTK_ADDR_SPACE_I2C_TYPE_III, 0, &data, 0x01); if (status) - dbg ("%s - read 3 status error = %d", __func__, status); + dbg("%s - read 3 status error = %d", __func__, status); else - dbg ("%s - read 2 data = 0x%x", __func__, data); + dbg("%s - read 2 data = 0x%x", __func__, data); if ((!status) && (data == UMP5152 || data == UMP3410)) { - dbg ("%s - ROM_TYPE_III", __func__); + dbg("%s - ROM_TYPE_III", __func__); serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_III; return 0; } - dbg ("%s - Unknown", __func__); + dbg("%s - Unknown", __func__); serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II; return -ENODEV; } -static int TISendBulkTransferSync (struct usb_serial *serial, void *buffer, int length, int *num_sent) +static int bulk_xfer(struct usb_serial *serial, void *buffer, + int length, int *num_sent) { int status; - status = usb_bulk_msg (serial->dev, - usb_sndbulkpipe(serial->dev, - serial->port[0]->bulk_out_endpointAddress), - buffer, - length, - num_sent, - 1000); + status = usb_bulk_msg(serial->dev, + usb_sndbulkpipe(serial->dev, + serial->port[0]->bulk_out_endpointAddress), + buffer, length, num_sent, 1000); return status; } /* Download given firmware image to the device (IN BOOT MODE) */ -static int TIDownloadCodeImage (struct edgeport_serial *serial, __u8 *image, int image_length) +static int download_code(struct edgeport_serial *serial, __u8 *image, + int image_length) { int status = 0; int pos; int transfer; int done; - // Transfer firmware image + /* Transfer firmware image */ for (pos = 0; pos < image_length; ) { - // Read the next buffer from file + /* Read the next buffer from file */ transfer = image_length - pos; if (transfer > EDGE_FW_BULK_MAX_PACKET_SIZE) transfer = EDGE_FW_BULK_MAX_PACKET_SIZE; - // Transfer data - status = TISendBulkTransferSync (serial->serial, &image[pos], transfer, &done); + /* Transfer data */ + status = bulk_xfer(serial->serial, &image[pos], + transfer, &done); if (status) break; - // Advance buffer pointer + /* Advance buffer pointer */ pos += done; } return status; } -// FIXME!!! -static int TIConfigureBootDevice (struct usb_device *dev) +/* FIXME!!! */ +static int config_boot_dev(struct usb_device *dev) { return 0; } +static int ti_cpu_rev(struct edge_ti_manuf_descriptor *desc) +{ + return TI_GET_CPU_REVISION(desc->CpuRev_BoardRev); +} + /** * DownloadTIFirmware - Download run-time operating firmware to the TI5052 - * + * * This routine downloads the main operating code into the TI5052, using the * boot code already burned into E2PROM or ROM. */ -static int TIDownloadFirmware (struct edgeport_serial *serial) +static int download_fw(struct edgeport_serial *serial) { struct device *dev = &serial->serial->dev->dev; int status = 0; @@ -1071,22 +1045,25 @@ static int TIDownloadFirmware (struct edgeport_serial *serial) /* Default to type 2 i2c */ serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II; - status = TIChooseConfiguration (serial->serial->dev); + status = choose_config(serial->serial->dev); if (status) return status; interface = &serial->serial->interface->cur_altsetting->desc; if (!interface) { - dev_err (dev, "%s - no interface set, error!\n", __func__); + dev_err(dev, "%s - no interface set, error!\n", __func__); return -ENODEV; } - // Setup initial mode -- the default mode 0 is TI_MODE_CONFIGURING - // if we have more than one endpoint we are definitely in download mode + /* + * Setup initial mode -- the default mode 0 is TI_MODE_CONFIGURING + * if we have more than one endpoint we are definitely in download + * mode + */ if (interface->bNumEndpoints > 1) serial->product_info.TiMode = TI_MODE_DOWNLOAD; else - // Otherwise we will remain in configuring mode + /* Otherwise we will remain in configuring mode */ serial->product_info.TiMode = TI_MODE_CONFIGURING; /********************************************************************/ @@ -1097,256 +1074,273 @@ static int TIDownloadFirmware (struct edgeport_serial *serial) dbg("%s - RUNNING IN DOWNLOAD MODE", __func__); - status = TiValidateI2cImage (serial); + status = check_i2c_image(serial); if (status) { dbg("%s - DOWNLOAD MODE -- BAD I2C", __func__); return status; } - + /* Validate Hardware version number * Read Manufacturing Descriptor from TI Based Edgeport */ - ti_manuf_desc = kmalloc (sizeof (*ti_manuf_desc), GFP_KERNEL); + ti_manuf_desc = kmalloc(sizeof(*ti_manuf_desc), GFP_KERNEL); if (!ti_manuf_desc) { - dev_err (dev, "%s - out of memory.\n", __func__); + dev_err(dev, "%s - out of memory.\n", __func__); return -ENOMEM; } - status = TIReadManufDescriptor (serial, (__u8 *)ti_manuf_desc); + status = get_manuf_info(serial, (__u8 *)ti_manuf_desc); if (status) { - kfree (ti_manuf_desc); + kfree(ti_manuf_desc); return status; } - // Check version number of ION descriptor - if (!ignore_cpu_rev && TI_GET_CPU_REVISION(ti_manuf_desc->CpuRev_BoardRev) < 2) { - dbg ( "%s - Wrong CPU Rev %d (Must be 2)", __func__, - TI_GET_CPU_REVISION(ti_manuf_desc->CpuRev_BoardRev)); - kfree (ti_manuf_desc); - return -EINVAL; - } + /* Check version number of ION descriptor */ + if (!ignore_cpu_rev && ti_cpu_rev(ti_manuf_desc) < 2) { + dbg("%s - Wrong CPU Rev %d (Must be 2)", + __func__, ti_cpu_rev(ti_manuf_desc)); + kfree(ti_manuf_desc); + return -EINVAL; + } - rom_desc = kmalloc (sizeof (*rom_desc), GFP_KERNEL); + rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL); if (!rom_desc) { - dev_err (dev, "%s - out of memory.\n", __func__); - kfree (ti_manuf_desc); + dev_err(dev, "%s - out of memory.\n", __func__); + kfree(ti_manuf_desc); return -ENOMEM; } - // Search for type 2 record (firmware record) - if ((start_address = TIGetDescriptorAddress (serial, I2C_DESC_TYPE_FIRMWARE_BASIC, rom_desc)) != 0) { + /* Search for type 2 record (firmware record) */ + start_address = get_descriptor_addr(serial, + I2C_DESC_TYPE_FIRMWARE_BASIC, rom_desc); + if (start_address != 0) { struct ti_i2c_firmware_rec *firmware_version; __u8 record; - dbg ("%s - Found Type FIRMWARE (Type 2) record", __func__); + dbg("%s - Found Type FIRMWARE (Type 2) record", + __func__); - firmware_version = kmalloc (sizeof (*firmware_version), GFP_KERNEL); + firmware_version = kmalloc(sizeof(*firmware_version), + GFP_KERNEL); if (!firmware_version) { - dev_err (dev, "%s - out of memory.\n", __func__); - kfree (rom_desc); - kfree (ti_manuf_desc); + dev_err(dev, "%s - out of memory.\n", __func__); + kfree(rom_desc); + kfree(ti_manuf_desc); return -ENOMEM; } - // Validate version number - // Read the descriptor data - status = TIReadRom (serial, - start_address+sizeof(struct ti_i2c_desc), + /* Validate version number + * Read the descriptor data + */ + status = read_rom(serial, start_address + + sizeof(struct ti_i2c_desc), sizeof(struct ti_i2c_firmware_rec), (__u8 *)firmware_version); if (status) { - kfree (firmware_version); - kfree (rom_desc); - kfree (ti_manuf_desc); + kfree(firmware_version); + kfree(rom_desc); + kfree(ti_manuf_desc); return status; } - // Check version number of download with current version in I2c - download_cur_ver = (firmware_version->Ver_Major << 8) + + /* Check version number of download with current + version in I2c */ + download_cur_ver = (firmware_version->Ver_Major << 8) + (firmware_version->Ver_Minor); download_new_ver = (OperationalMajorVersion << 8) + (OperationalMinorVersion); - dbg ("%s - >>>Firmware Versions Device %d.%d Driver %d.%d", - __func__, - firmware_version->Ver_Major, - firmware_version->Ver_Minor, - OperationalMajorVersion, - OperationalMinorVersion); + dbg("%s - >> FW Versions Device %d.%d Driver %d.%d", + __func__, + firmware_version->Ver_Major, + firmware_version->Ver_Minor, + OperationalMajorVersion, + OperationalMinorVersion); - // Check if we have an old version in the I2C and update if necessary + /* Check if we have an old version in the I2C and + update if necessary */ if (download_cur_ver != download_new_ver) { - dbg ("%s - Update I2C Download from %d.%d to %d.%d", - __func__, - firmware_version->Ver_Major, - firmware_version->Ver_Minor, - OperationalMajorVersion, - OperationalMinorVersion); - - // In order to update the I2C firmware we must change the type 2 record to type 0xF2. - // This will force the UMP to come up in Boot Mode. Then while in boot mode, the driver - // will download the latest firmware (padded to 15.5k) into the UMP ram. - // And finally when the device comes back up in download mode the driver will cause - // the new firmware to be copied from the UMP Ram to I2C and the firmware will update - // the record type from 0xf2 to 0x02. - + dbg("%s - Update I2C dld from %d.%d to %d.%d", + __func__, + firmware_version->Ver_Major, + firmware_version->Ver_Minor, + OperationalMajorVersion, + OperationalMinorVersion); + + /* In order to update the I2C firmware we must + * change the type 2 record to type 0xF2. This + * will force the UMP to come up in Boot Mode. + * Then while in boot mode, the driver will + * download the latest firmware (padded to + * 15.5k) into the UMP ram. Finally when the + * device comes back up in download mode the + * driver will cause the new firmware to be + * copied from the UMP Ram to I2C and the + * firmware will update the record type from + * 0xf2 to 0x02. + */ record = I2C_DESC_TYPE_FIRMWARE_BLANK; - // Change the I2C Firmware record type to 0xf2 to trigger an update - status = TIWriteRom (serial, - start_address, - sizeof(record), - &record); + /* Change the I2C Firmware record type to + 0xf2 to trigger an update */ + status = write_rom(serial, start_address, + sizeof(record), &record); if (status) { - kfree (firmware_version); - kfree (rom_desc); - kfree (ti_manuf_desc); + kfree(firmware_version); + kfree(rom_desc); + kfree(ti_manuf_desc); return status; } - // verify the write -- must do this in order for write to - // complete before we do the hardware reset - status = TIReadRom (serial, + /* verify the write -- must do this in order + * for write to complete before we do the + * hardware reset + */ + status = read_rom(serial, start_address, sizeof(record), &record); - if (status) { - kfree (firmware_version); - kfree (rom_desc); - kfree (ti_manuf_desc); + kfree(firmware_version); + kfree(rom_desc); + kfree(ti_manuf_desc); return status; } if (record != I2C_DESC_TYPE_FIRMWARE_BLANK) { - dev_err (dev, "%s - error resetting device\n", __func__); - kfree (firmware_version); - kfree (rom_desc); - kfree (ti_manuf_desc); + dev_err(dev, + "%s - error resetting device\n", + __func__); + kfree(firmware_version); + kfree(rom_desc); + kfree(ti_manuf_desc); return -ENODEV; } - dbg ("%s - HARDWARE RESET", __func__); + dbg("%s - HARDWARE RESET", __func__); - // Reset UMP -- Back to BOOT MODE - status = TISendVendorRequestSync (serial->serial->dev, - UMPC_HARDWARE_RESET, // Request - 0, // wValue - 0, // wIndex - NULL, // TransferBuffer - 0); // TransferBufferLength + /* Reset UMP -- Back to BOOT MODE */ + status = ti_vsend_sync(serial->serial->dev, + UMPC_HARDWARE_RESET, + 0, 0, NULL, 0); - dbg ( "%s - HARDWARE RESET return %d", __func__, status); + dbg("%s - HARDWARE RESET return %d", + __func__, status); /* return an error on purpose. */ - kfree (firmware_version); - kfree (rom_desc); - kfree (ti_manuf_desc); + kfree(firmware_version); + kfree(rom_desc); + kfree(ti_manuf_desc); return -ENODEV; } - kfree (firmware_version); + kfree(firmware_version); } - // Search for type 0xF2 record (firmware blank record) - else if ((start_address = TIGetDescriptorAddress (serial, I2C_DESC_TYPE_FIRMWARE_BLANK, rom_desc)) != 0) { - #define HEADER_SIZE (sizeof(struct ti_i2c_desc) + sizeof(struct ti_i2c_firmware_rec)) + /* Search for type 0xF2 record (firmware blank record) */ + else if ((start_address = get_descriptor_addr(serial, I2C_DESC_TYPE_FIRMWARE_BLANK, rom_desc)) != 0) { +#define HEADER_SIZE (sizeof(struct ti_i2c_desc) + \ + sizeof(struct ti_i2c_firmware_rec)) __u8 *header; __u8 *vheader; - header = kmalloc (HEADER_SIZE, GFP_KERNEL); + header = kmalloc(HEADER_SIZE, GFP_KERNEL); if (!header) { - dev_err (dev, "%s - out of memory.\n", __func__); - kfree (rom_desc); - kfree (ti_manuf_desc); + dev_err(dev, "%s - out of memory.\n", __func__); + kfree(rom_desc); + kfree(ti_manuf_desc); return -ENOMEM; } - - vheader = kmalloc (HEADER_SIZE, GFP_KERNEL); + + vheader = kmalloc(HEADER_SIZE, GFP_KERNEL); if (!vheader) { - dev_err (dev, "%s - out of memory.\n", __func__); - kfree (header); - kfree (rom_desc); - kfree (ti_manuf_desc); + dev_err(dev, "%s - out of memory.\n", __func__); + kfree(header); + kfree(rom_desc); + kfree(ti_manuf_desc); return -ENOMEM; } - - dbg ("%s - Found Type BLANK FIRMWARE (Type F2) record", __func__); - - // In order to update the I2C firmware we must change the type 2 record to type 0xF2. - // This will force the UMP to come up in Boot Mode. Then while in boot mode, the driver - // will download the latest firmware (padded to 15.5k) into the UMP ram. - // And finally when the device comes back up in download mode the driver will cause - // the new firmware to be copied from the UMP Ram to I2C and the firmware will update - // the record type from 0xf2 to 0x02. - status = BuildI2CFirmwareHeader(header, dev); + + dbg("%s - Found Type BLANK FIRMWARE (Type F2) record", + __func__); + + /* + * In order to update the I2C firmware we must change + * the type 2 record to type 0xF2. This will force the + * UMP to come up in Boot Mode. Then while in boot + * mode, the driver will download the latest firmware + * (padded to 15.5k) into the UMP ram. Finally when the + * device comes back up in download mode the driver + * will cause the new firmware to be copied from the + * UMP Ram to I2C and the firmware will update the + * record type from 0xf2 to 0x02. + */ + status = build_i2c_fw_hdr(header, dev); if (status) { - kfree (vheader); - kfree (header); - kfree (rom_desc); - kfree (ti_manuf_desc); + kfree(vheader); + kfree(header); + kfree(rom_desc); + kfree(ti_manuf_desc); return status; } - // Update I2C with type 0xf2 record with correct size and checksum - status = TIWriteRom (serial, + /* Update I2C with type 0xf2 record with correct + size and checksum */ + status = write_rom(serial, start_address, HEADER_SIZE, header); if (status) { - kfree (vheader); - kfree (header); - kfree (rom_desc); - kfree (ti_manuf_desc); + kfree(vheader); + kfree(header); + kfree(rom_desc); + kfree(ti_manuf_desc); return status; } - // verify the write -- must do this in order for write to - // complete before we do the hardware reset - status = TIReadRom (serial, - start_address, - HEADER_SIZE, - vheader); + /* verify the write -- must do this in order for + write to complete before we do the hardware reset */ + status = read_rom(serial, start_address, + HEADER_SIZE, vheader); if (status) { - dbg ("%s - can't read header back", __func__); - kfree (vheader); - kfree (header); - kfree (rom_desc); - kfree (ti_manuf_desc); + dbg("%s - can't read header back", __func__); + kfree(vheader); + kfree(header); + kfree(rom_desc); + kfree(ti_manuf_desc); return status; } if (memcmp(vheader, header, HEADER_SIZE)) { - dbg ("%s - write download record failed", __func__); - kfree (vheader); - kfree (header); - kfree (rom_desc); - kfree (ti_manuf_desc); + dbg("%s - write download record failed", + __func__); + kfree(vheader); + kfree(header); + kfree(rom_desc); + kfree(ti_manuf_desc); return status; } - kfree (vheader); - kfree (header); + kfree(vheader); + kfree(header); - dbg ("%s - Start firmware update", __func__); + dbg("%s - Start firmware update", __func__); - // Tell firmware to copy download image into I2C - status = TISendVendorRequestSync (serial->serial->dev, - UMPC_COPY_DNLD_TO_I2C, // Request - 0, // wValue - 0, // wIndex - NULL, // TransferBuffer - 0); // TransferBufferLength + /* Tell firmware to copy download image into I2C */ + status = ti_vsend_sync(serial->serial->dev, + UMPC_COPY_DNLD_TO_I2C, 0, 0, NULL, 0); - dbg ("%s - Update complete 0x%x", __func__, status); + dbg("%s - Update complete 0x%x", __func__, status); if (status) { - dev_err (dev, "%s - UMPC_COPY_DNLD_TO_I2C failed\n", __func__); - kfree (rom_desc); - kfree (ti_manuf_desc); + dev_err(dev, + "%s - UMPC_COPY_DNLD_TO_I2C failed\n", + __func__); + kfree(rom_desc); + kfree(ti_manuf_desc); return status; } } // The device is running the download code - kfree (rom_desc); - kfree (ti_manuf_desc); + kfree(rom_desc); + kfree(ti_manuf_desc); return 0; } @@ -1355,32 +1349,26 @@ static int TIDownloadFirmware (struct edgeport_serial *serial) /********************************************************************/ dbg("%s - RUNNING IN BOOT MODE", __func__); - // Configure the TI device so we can use the BULK pipes for download - status = TIConfigureBootDevice (serial->serial->dev); + /* Configure the TI device so we can use the BULK pipes for download */ + status = config_boot_dev(serial->serial->dev); if (status) return status; - if (le16_to_cpu(serial->serial->dev->descriptor.idVendor) != USB_VENDOR_ID_ION) { - dbg ("%s - VID = 0x%x", __func__, + if (le16_to_cpu(serial->serial->dev->descriptor.idVendor) + != USB_VENDOR_ID_ION) { + dbg("%s - VID = 0x%x", __func__, le16_to_cpu(serial->serial->dev->descriptor.idVendor)); serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II; - goto StayInBootMode; + goto stayinbootmode; } - // We have an ION device (I2c Must be programmed) - // Determine I2C image type - if (TIGetI2cTypeInBootMode(serial)) { - goto StayInBootMode; - } + /* We have an ION device (I2c Must be programmed) + Determine I2C image type */ + if (i2c_type_bootmode(serial)) + goto stayinbootmode; - // Registry variable set? - if (TIStayInBootMode) { - dbg ("%s - TIStayInBootMode", __func__); - goto StayInBootMode; - } - - // Check for ION Vendor ID and that the I2C is valid - if (!TiValidateI2cImage(serial)) { + /* Check for ION Vendor ID and that the I2C is valid */ + if (!check_i2c_image(serial)) { struct ti_i2c_image_header *header; int i; __u8 cs = 0; @@ -1393,49 +1381,52 @@ static int TIDownloadFirmware (struct edgeport_serial *serial) /* Validate Hardware version number * Read Manufacturing Descriptor from TI Based Edgeport */ - ti_manuf_desc = kmalloc (sizeof (*ti_manuf_desc), GFP_KERNEL); + ti_manuf_desc = kmalloc(sizeof(*ti_manuf_desc), GFP_KERNEL); if (!ti_manuf_desc) { - dev_err (dev, "%s - out of memory.\n", __func__); + dev_err(dev, "%s - out of memory.\n", __func__); return -ENOMEM; } - status = TIReadManufDescriptor (serial, (__u8 *)ti_manuf_desc); + status = get_manuf_info(serial, (__u8 *)ti_manuf_desc); if (status) { - kfree (ti_manuf_desc); - goto StayInBootMode; + kfree(ti_manuf_desc); + goto stayinbootmode; } - // Check for version 2 - if (!ignore_cpu_rev && TI_GET_CPU_REVISION(ti_manuf_desc->CpuRev_BoardRev) < 2) { - dbg ("%s - Wrong CPU Rev %d (Must be 2)", __func__, - TI_GET_CPU_REVISION(ti_manuf_desc->CpuRev_BoardRev)); - kfree (ti_manuf_desc); - goto StayInBootMode; + /* Check for version 2 */ + if (!ignore_cpu_rev && ti_cpu_rev(ti_manuf_desc) < 2) { + dbg("%s - Wrong CPU Rev %d (Must be 2)", + __func__, ti_cpu_rev(ti_manuf_desc)); + kfree(ti_manuf_desc); + goto stayinbootmode; } - kfree (ti_manuf_desc); + kfree(ti_manuf_desc); - // In order to update the I2C firmware we must change the type 2 record to type 0xF2. - // This will force the UMP to come up in Boot Mode. Then while in boot mode, the driver - // will download the latest firmware (padded to 15.5k) into the UMP ram. - // And finally when the device comes back up in download mode the driver will cause - // the new firmware to be copied from the UMP Ram to I2C and the firmware will update - // the record type from 0xf2 to 0x02. - /* + * In order to update the I2C firmware we must change the type + * 2 record to type 0xF2. This will force the UMP to come up + * in Boot Mode. Then while in boot mode, the driver will + * download the latest firmware (padded to 15.5k) into the + * UMP ram. Finally when the device comes back up in download + * mode the driver will cause the new firmware to be copied + * from the UMP Ram to I2C and the firmware will update the + * record type from 0xf2 to 0x02. + * * Do we really have to copy the whole firmware image, * or could we do this in place! */ - // Allocate a 15.5k buffer + 3 byte header - buffer_size = (((1024 * 16) - 512) + sizeof(struct ti_i2c_image_header)); - buffer = kmalloc (buffer_size, GFP_KERNEL); + /* Allocate a 15.5k buffer + 3 byte header */ + buffer_size = (((1024 * 16) - 512) + + sizeof(struct ti_i2c_image_header)); + buffer = kmalloc(buffer_size, GFP_KERNEL); if (!buffer) { - dev_err (dev, "%s - out of memory\n", __func__); + dev_err(dev, "%s - out of memory\n", __func__); return -ENOMEM; } - - // Initialize the buffer to 0xff (pad the buffer) - memset (buffer, 0xff, buffer_size); + + /* Initialize the buffer to 0xff (pad the buffer) */ + memset(buffer, 0xff, buffer_size); err = request_firmware(&fw, fw_name, dev); if (err) { @@ -1447,38 +1438,43 @@ static int TIDownloadFirmware (struct edgeport_serial *serial) memcpy(buffer, &fw->data[4], fw->size - 4); release_firmware(fw); - for(i = sizeof(struct ti_i2c_image_header); i < buffer_size; i++) { + for (i = sizeof(struct ti_i2c_image_header); + i < buffer_size; i++) { cs = (__u8)(cs + buffer[i]); } - + header = (struct ti_i2c_image_header *)buffer; - - // update length and checksum after padding - header->Length = cpu_to_le16((__u16)(buffer_size - sizeof(struct ti_i2c_image_header))); + + /* update length and checksum after padding */ + header->Length = cpu_to_le16((__u16)(buffer_size - + sizeof(struct ti_i2c_image_header))); header->CheckSum = cs; - // Download the operational code - dbg ("%s - Downloading operational code image (TI UMP)", __func__); - status = TIDownloadCodeImage (serial, buffer, buffer_size); + /* Download the operational code */ + dbg("%s - Downloading operational code image (TI UMP)", + __func__); + status = download_code(serial, buffer, buffer_size); - kfree (buffer); + kfree(buffer); if (status) { - dbg ("%s - Error downloading operational code image", __func__); + dbg("%s - Error downloading operational code image", + __func__); return status; } - // Device will reboot + /* Device will reboot */ serial->product_info.TiMode = TI_MODE_TRANSITIONING; - dbg ("%s - Download successful -- Device rebooting...", __func__); + dbg("%s - Download successful -- Device rebooting...", + __func__); /* return an error on purpose */ return -ENODEV; } -StayInBootMode: - // Eprom is invalid or blank stay in boot mode +stayinbootmode: + /* Eprom is invalid or blank stay in boot mode */ dbg("%s - STAYING IN BOOT MODE", __func__); serial->product_info.TiMode = TI_MODE_BOOT; @@ -1486,156 +1482,33 @@ StayInBootMode: } -static int TISetDtr (struct edgeport_port *port) -{ - int port_number = port->port->number - port->port->serial->minor; - - dbg ("%s", __func__); - port->shadow_mcr |= MCR_DTR; - - return TIWriteCommandSync (port->port->serial->dev, - UMPC_SET_CLR_DTR, - (__u8)(UMPM_UART1_PORT + port_number), - 1, /* set */ - NULL, - 0); -} - -static int TIClearDtr (struct edgeport_port *port) -{ - int port_number = port->port->number - port->port->serial->minor; - - dbg ("%s", __func__); - port->shadow_mcr &= ~MCR_DTR; - - return TIWriteCommandSync (port->port->serial->dev, - UMPC_SET_CLR_DTR, - (__u8)(UMPM_UART1_PORT + port_number), - 0, /* clear */ - NULL, - 0); -} - -static int TISetRts (struct edgeport_port *port) -{ - int port_number = port->port->number - port->port->serial->minor; - - dbg ("%s", __func__); - port->shadow_mcr |= MCR_RTS; - - return TIWriteCommandSync (port->port->serial->dev, - UMPC_SET_CLR_RTS, - (__u8)(UMPM_UART1_PORT + port_number), - 1, /* set */ - NULL, - 0); -} - -static int TIClearRts (struct edgeport_port *port) -{ - int port_number = port->port->number - port->port->serial->minor; - - dbg ("%s", __func__); - port->shadow_mcr &= ~MCR_RTS; - - return TIWriteCommandSync (port->port->serial->dev, - UMPC_SET_CLR_RTS, - (__u8)(UMPM_UART1_PORT + port_number), - 0, /* clear */ - NULL, - 0); -} - -static int TISetLoopBack (struct edgeport_port *port) -{ - int port_number = port->port->number - port->port->serial->minor; - - dbg ("%s", __func__); - - return TIWriteCommandSync (port->port->serial->dev, - UMPC_SET_CLR_LOOPBACK, - (__u8)(UMPM_UART1_PORT + port_number), - 1, /* set */ - NULL, - 0); -} - -static int TIClearLoopBack (struct edgeport_port *port) -{ - int port_number = port->port->number - port->port->serial->minor; - - dbg ("%s", __func__); - - return TIWriteCommandSync (port->port->serial->dev, - UMPC_SET_CLR_LOOPBACK, - (__u8)(UMPM_UART1_PORT + port_number), - 0, /* clear */ - NULL, - 0); -} - -static int TISetBreak (struct edgeport_port *port) +static int ti_do_config(struct edgeport_port *port, int feature, int on) { int port_number = port->port->number - port->port->serial->minor; - - dbg ("%s", __func__); - - return TIWriteCommandSync (port->port->serial->dev, - UMPC_SET_CLR_BREAK, - (__u8)(UMPM_UART1_PORT + port_number), - 1, /* set */ - NULL, - 0); + on = !!on; /* 1 or 0 not bitmask */ + return send_cmd(port->port->serial->dev, + feature, (__u8)(UMPM_UART1_PORT + port_number), + on, NULL, 0); } -static int TIClearBreak (struct edgeport_port *port) -{ - int port_number = port->port->number - port->port->serial->minor; - - dbg ("%s", __func__); - return TIWriteCommandSync (port->port->serial->dev, - UMPC_SET_CLR_BREAK, - (__u8)(UMPM_UART1_PORT + port_number), - 0, /* clear */ - NULL, - 0); -} - -static int TIRestoreMCR (struct edgeport_port *port, __u8 mcr) +static int restore_mcr(struct edgeport_port *port, __u8 mcr) { int status = 0; - dbg ("%s - %x", __func__, mcr); - - if (mcr & MCR_DTR) - status = TISetDtr (port); - else - status = TIClearDtr (port); + dbg("%s - %x", __func__, mcr); + status = ti_do_config(port, UMPC_SET_CLR_DTR, mcr & MCR_DTR); if (status) return status; - - if (mcr & MCR_RTS) - status = TISetRts (port); - else - status = TIClearRts (port); - + status = ti_do_config(port, UMPC_SET_CLR_RTS, mcr & MCR_RTS); if (status) return status; - - if (mcr & MCR_LOOPBACK) - status = TISetLoopBack (port); - else - status = TIClearLoopBack (port); - - return status; + return ti_do_config(port, UMPC_SET_CLR_LOOPBACK, mcr & MCR_LOOPBACK); } - - /* Convert TI LSR to standard UART flags */ -static __u8 MapLineStatus (__u8 ti_lsr) +static __u8 map_line_status(__u8 ti_lsr) { __u8 lsr = 0; @@ -1647,22 +1520,23 @@ static __u8 MapLineStatus (__u8 ti_lsr) MAP_FLAG(UMP_UART_LSR_PE_MASK, LSR_PAR_ERR) /* parity error */ MAP_FLAG(UMP_UART_LSR_FE_MASK, LSR_FRM_ERR) /* framing error */ MAP_FLAG(UMP_UART_LSR_BR_MASK, LSR_BREAK) /* break detected */ - MAP_FLAG(UMP_UART_LSR_RX_MASK, LSR_RX_AVAIL) /* receive data available */ - MAP_FLAG(UMP_UART_LSR_TX_MASK, LSR_TX_EMPTY) /* transmit holding register empty */ + MAP_FLAG(UMP_UART_LSR_RX_MASK, LSR_RX_AVAIL) /* rx data available */ + MAP_FLAG(UMP_UART_LSR_TX_MASK, LSR_TX_EMPTY) /* tx hold reg empty */ #undef MAP_FLAG return lsr; } -static void handle_new_msr (struct edgeport_port *edge_port, __u8 msr) +static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr) { struct async_icount *icount; struct tty_struct *tty; - dbg ("%s - %02x", __func__, msr); + dbg("%s - %02x", __func__, msr); - if (msr & (EDGEPORT_MSR_DELTA_CTS | EDGEPORT_MSR_DELTA_DSR | EDGEPORT_MSR_DELTA_RI | EDGEPORT_MSR_DELTA_CD)) { + if (msr & (EDGEPORT_MSR_DELTA_CTS | EDGEPORT_MSR_DELTA_DSR | + EDGEPORT_MSR_DELTA_RI | EDGEPORT_MSR_DELTA_CD)) { icount = &edge_port->icount; /* update input line counters */ @@ -1674,13 +1548,13 @@ static void handle_new_msr (struct edgeport_port *edge_port, __u8 msr) icount->dcd++; if (msr & EDGEPORT_MSR_DELTA_RI) icount->rng++; - wake_up_interruptible (&edge_port->delta_msr_wait); + wake_up_interruptible(&edge_port->delta_msr_wait); } /* Save the new modem status */ edge_port->shadow_msr = msr & 0xf0; - tty = edge_port->port->tty; + tty = edge_port->port->port.tty; /* handle CTS flow control */ if (tty && C_CRTSCTS(tty)) { if (msr & EDGEPORT_MSR_CTS) { @@ -1694,26 +1568,27 @@ static void handle_new_msr (struct edgeport_port *edge_port, __u8 msr) return; } -static void handle_new_lsr (struct edgeport_port *edge_port, int lsr_data, __u8 lsr, __u8 data) +static void handle_new_lsr(struct edgeport_port *edge_port, int lsr_data, + __u8 lsr, __u8 data) { struct async_icount *icount; - __u8 new_lsr = (__u8)(lsr & (__u8)(LSR_OVER_ERR | LSR_PAR_ERR | LSR_FRM_ERR | LSR_BREAK)); + __u8 new_lsr = (__u8)(lsr & (__u8)(LSR_OVER_ERR | LSR_PAR_ERR | + LSR_FRM_ERR | LSR_BREAK)); - dbg ("%s - %02x", __func__, new_lsr); + dbg("%s - %02x", __func__, new_lsr); edge_port->shadow_lsr = lsr; - if (new_lsr & LSR_BREAK) { + if (new_lsr & LSR_BREAK) /* * Parity and Framing errors only count if they * occur exclusive of a break being received. */ new_lsr &= (__u8)(LSR_OVER_ERR | LSR_BREAK); - } /* Place LSR data byte into Rx buffer */ - if (lsr_data && edge_port->port->tty) - edge_tty_recv(&edge_port->port->dev, edge_port->port->tty, &data, 1); + if (lsr_data && edge_port->port->port.tty) + edge_tty_recv(&edge_port->port->dev, edge_port->port->port.tty, &data, 1); /* update input line counters */ icount = &edge_port->icount; @@ -1728,7 +1603,7 @@ static void handle_new_lsr (struct edgeport_port *edge_port, int lsr_data, __u8 } -static void edge_interrupt_callback (struct urb *urb) +static void edge_interrupt_callback(struct urb *urb) { struct edgeport_serial *edge_serial = urb->context; struct usb_serial_port *port; @@ -1762,66 +1637,71 @@ static void edge_interrupt_callback (struct urb *urb) } if (!length) { - dbg ("%s - no data in urb", __func__); + dbg("%s - no data in urb", __func__); goto exit; } - - usb_serial_debug_data(debug, &edge_serial->serial->dev->dev, __func__, length, data); - + + usb_serial_debug_data(debug, &edge_serial->serial->dev->dev, + __func__, length, data); + if (length != 2) { - dbg ("%s - expecting packet of size 2, got %d", __func__, length); + dbg("%s - expecting packet of size 2, got %d", + __func__, length); goto exit; } - port_number = TIUMP_GET_PORT_FROM_CODE (data[0]); - function = TIUMP_GET_FUNC_FROM_CODE (data[0]); - dbg ("%s - port_number %d, function %d, info 0x%x", + port_number = TIUMP_GET_PORT_FROM_CODE(data[0]); + function = TIUMP_GET_FUNC_FROM_CODE(data[0]); + dbg("%s - port_number %d, function %d, info 0x%x", __func__, port_number, function, data[1]); port = edge_serial->serial->port[port_number]; edge_port = usb_get_serial_port_data(port); if (!edge_port) { - dbg ("%s - edge_port not found", __func__); + dbg("%s - edge_port not found", __func__); return; } switch (function) { case TIUMP_INTERRUPT_CODE_LSR: - lsr = MapLineStatus(data[1]); + lsr = map_line_status(data[1]); if (lsr & UMP_UART_LSR_DATA_MASK) { - /* Save the LSR event for bulk read completion routine */ - dbg ("%s - LSR Event Port %u LSR Status = %02x", + /* Save the LSR event for bulk read + completion routine */ + dbg("%s - LSR Event Port %u LSR Status = %02x", __func__, port_number, lsr); edge_port->lsr_event = 1; edge_port->lsr_mask = lsr; } else { - dbg ("%s - ===== Port %d LSR Status = %02x ======", + dbg("%s - ===== Port %d LSR Status = %02x ======", __func__, port_number, lsr); - handle_new_lsr (edge_port, 0, lsr, 0); + handle_new_lsr(edge_port, 0, lsr, 0); } break; - case TIUMP_INTERRUPT_CODE_MSR: // MSR + case TIUMP_INTERRUPT_CODE_MSR: /* MSR */ /* Copy MSR from UMP */ msr = data[1]; - dbg ("%s - ===== Port %u MSR Status = %02x ======\n", + dbg("%s - ===== Port %u MSR Status = %02x ======\n", __func__, port_number, msr); - handle_new_msr (edge_port, msr); + handle_new_msr(edge_port, msr); break; default: - dev_err (&urb->dev->dev, "%s - Unknown Interrupt code from UMP %x\n", - __func__, data[1]); + dev_err(&urb->dev->dev, + "%s - Unknown Interrupt code from UMP %x\n", + __func__, data[1]); break; - + } exit: - retval = usb_submit_urb (urb, GFP_ATOMIC); + retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) - dev_err (&urb->dev->dev, "%s - usb_submit_urb failed with result %d\n", + dev_err(&urb->dev->dev, + "%s - usb_submit_urb failed with result %d\n", __func__, retval); } -static void edge_bulk_in_callback (struct urb *urb) +static void edge_bulk_in_callback(struct urb *urb) { struct edgeport_port *edge_port = urb->context; unsigned char *data = urb->transfer_buffer; @@ -1844,15 +1724,16 @@ static void edge_bulk_in_callback (struct urb *urb) __func__, status); return; default: - dev_err (&urb->dev->dev,"%s - nonzero read bulk status received: %d\n", - __func__, status); + dev_err(&urb->dev->dev, + "%s - nonzero read bulk status received: %d\n", + __func__, status); } if (status == -EPIPE) goto exit; if (status) { - dev_err(&urb->dev->dev,"%s - stopping read!\n", __func__); + dev_err(&urb->dev->dev, "%s - stopping read!\n", __func__); return; } @@ -1860,23 +1741,24 @@ static void edge_bulk_in_callback (struct urb *urb) if (edge_port->lsr_event) { edge_port->lsr_event = 0; - dbg ("%s ===== Port %u LSR Status = %02x, Data = %02x ======", + dbg("%s ===== Port %u LSR Status = %02x, Data = %02x ======", __func__, port_number, edge_port->lsr_mask, *data); - handle_new_lsr (edge_port, 1, edge_port->lsr_mask, *data); + handle_new_lsr(edge_port, 1, edge_port->lsr_mask, *data); /* Adjust buffer length/pointer */ --urb->actual_length; ++data; } - tty = edge_port->port->tty; + tty = edge_port->port->port.tty; if (tty && urb->actual_length) { - usb_serial_debug_data(debug, &edge_port->port->dev, __func__, urb->actual_length, data); - - if (edge_port->close_pending) { - dbg ("%s - close is pending, dropping data on the floor.", __func__); - } else { - edge_tty_recv(&edge_port->port->dev, tty, data, urb->actual_length); - } + usb_serial_debug_data(debug, &edge_port->port->dev, + __func__, urb->actual_length, data); + if (edge_port->close_pending) + dbg("%s - close pending, dropping data on the floor", + __func__); + else + edge_tty_recv(&edge_port->port->dev, tty, data, + urb->actual_length); edge_port->icount.rx += urb->actual_length; } @@ -1891,37 +1773,31 @@ exit: } spin_unlock(&edge_port->ep_lock); if (retval) - dev_err (&urb->dev->dev, "%s - usb_submit_urb failed with result %d\n", + dev_err(&urb->dev->dev, + "%s - usb_submit_urb failed with result %d\n", __func__, retval); } -static void edge_tty_recv(struct device *dev, struct tty_struct *tty, unsigned char *data, int length) +static void edge_tty_recv(struct device *dev, struct tty_struct *tty, + unsigned char *data, int length) { - int cnt; - - do { - cnt = tty_buffer_request_room(tty, length); - if (cnt < length) { - dev_err(dev, "%s - dropping data, %d bytes lost\n", - __func__, length - cnt); - if(cnt == 0) - break; - } - tty_insert_flip_string(tty, data, cnt); - data += cnt; - length -= cnt; - } while (length > 0); + int queued; + tty_buffer_request_room(tty, length); + queued = tty_insert_flip_string(tty, data, length); + if (queued < length) + dev_err(dev, "%s - dropping data, %d bytes lost\n", + __func__, length - queued); tty_flip_buffer_push(tty); } -static void edge_bulk_out_callback (struct urb *urb) +static void edge_bulk_out_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct edgeport_port *edge_port = usb_get_serial_port_data(port); int status = urb->status; - dbg ("%s - port %d", __func__, port->number); + dbg("%s - port %d", __func__, port->number); edge_port->ep_write_urb_in_use = 0; @@ -1942,10 +1818,11 @@ static void edge_bulk_out_callback (struct urb *urb) } /* send any buffered data */ - edge_send(port); + edge_send(port->port.tty); } -static int edge_open (struct usb_serial_port *port, struct file * filp) +static int edge_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct edgeport_serial *edge_serial; @@ -1961,122 +1838,125 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) if (edge_port == NULL) return -ENODEV; - port->tty->low_latency = low_latency; + if (tty) + tty->low_latency = low_latency; port_number = port->number - port->serial->minor; switch (port_number) { - case 0: - edge_port->uart_base = UMPMEM_BASE_UART1; - edge_port->dma_address = UMPD_OEDB1_ADDRESS; - break; - case 1: - edge_port->uart_base = UMPMEM_BASE_UART2; - edge_port->dma_address = UMPD_OEDB2_ADDRESS; - break; - default: - dev_err (&port->dev, "Unknown port number!!!\n"); - return -ENODEV; + case 0: + edge_port->uart_base = UMPMEM_BASE_UART1; + edge_port->dma_address = UMPD_OEDB1_ADDRESS; + break; + case 1: + edge_port->uart_base = UMPMEM_BASE_UART2; + edge_port->dma_address = UMPD_OEDB2_ADDRESS; + break; + default: + dev_err(&port->dev, "Unknown port number!!!\n"); + return -ENODEV; } - dbg ("%s - port_number = %d, uart_base = %04x, dma_address = %04x", - __func__, port_number, edge_port->uart_base, edge_port->dma_address); + dbg("%s - port_number = %d, uart_base = %04x, dma_address = %04x", + __func__, port_number, edge_port->uart_base, + edge_port->dma_address); dev = port->serial->dev; - memset (&(edge_port->icount), 0x00, sizeof(edge_port->icount)); - init_waitqueue_head (&edge_port->delta_msr_wait); + memset(&(edge_port->icount), 0x00, sizeof(edge_port->icount)); + init_waitqueue_head(&edge_port->delta_msr_wait); /* turn off loopback */ - status = TIClearLoopBack (edge_port); + status = ti_do_config(edge_port, UMPC_SET_CLR_LOOPBACK, 0); if (status) { - dev_err(&port->dev,"%s - cannot send clear loopback command, %d\n", + dev_err(&port->dev, + "%s - cannot send clear loopback command, %d\n", __func__, status); return status; } - + /* set up the port settings */ - edge_set_termios (port, port->tty->termios); + if (tty) + edge_set_termios(tty, port, port->port.tty->termios); /* open up the port */ /* milliseconds to timeout for DMA transfer */ transaction_timeout = 2; - edge_port->ump_read_timeout = max (20, ((transaction_timeout * 3) / 2) ); + edge_port->ump_read_timeout = + max(20, ((transaction_timeout * 3) / 2)); - // milliseconds to timeout for DMA transfer - open_settings = (u8)(UMP_DMA_MODE_CONTINOUS | - UMP_PIPE_TRANS_TIMEOUT_ENA | + /* milliseconds to timeout for DMA transfer */ + open_settings = (u8)(UMP_DMA_MODE_CONTINOUS | + UMP_PIPE_TRANS_TIMEOUT_ENA | (transaction_timeout << 2)); - dbg ("%s - Sending UMPC_OPEN_PORT", __func__); + dbg("%s - Sending UMPC_OPEN_PORT", __func__); /* Tell TI to open and start the port */ - status = TIWriteCommandSync (dev, - UMPC_OPEN_PORT, - (u8)(UMPM_UART1_PORT + port_number), - open_settings, - NULL, - 0); + status = send_cmd(dev, UMPC_OPEN_PORT, + (u8)(UMPM_UART1_PORT + port_number), open_settings, NULL, 0); if (status) { - dev_err(&port->dev,"%s - cannot send open command, %d\n", __func__, status); + dev_err(&port->dev, "%s - cannot send open command, %d\n", + __func__, status); return status; } /* Start the DMA? */ - status = TIWriteCommandSync (dev, - UMPC_START_PORT, - (u8)(UMPM_UART1_PORT + port_number), - 0, - NULL, - 0); + status = send_cmd(dev, UMPC_START_PORT, + (u8)(UMPM_UART1_PORT + port_number), 0, NULL, 0); if (status) { - dev_err(&port->dev,"%s - cannot send start DMA command, %d\n", __func__, status); + dev_err(&port->dev, "%s - cannot send start DMA command, %d\n", + __func__, status); return status; } /* Clear TX and RX buffers in UMP */ - status = TIPurgeDataSync (port, UMP_PORT_DIR_OUT | UMP_PORT_DIR_IN); + status = purge_port(port, UMP_PORT_DIR_OUT | UMP_PORT_DIR_IN); if (status) { - dev_err(&port->dev,"%s - cannot send clear buffers command, %d\n", __func__, status); + dev_err(&port->dev, + "%s - cannot send clear buffers command, %d\n", + __func__, status); return status; } /* Read Initial MSR */ - status = TIReadVendorRequestSync (dev, - UMPC_READ_MSR, // Request - 0, // wValue - (__u16)(UMPM_UART1_PORT + port_number), // wIndex (Address) - &edge_port->shadow_msr, // TransferBuffer - 1); // TransferBufferLength + status = ti_vread_sync(dev, UMPC_READ_MSR, 0, + (__u16)(UMPM_UART1_PORT + port_number), + &edge_port->shadow_msr, 1); if (status) { - dev_err(&port->dev,"%s - cannot send read MSR command, %d\n", __func__, status); + dev_err(&port->dev, "%s - cannot send read MSR command, %d\n", + __func__, status); return status; } - dbg ("ShadowMSR 0x%X", edge_port->shadow_msr); - + dbg("ShadowMSR 0x%X", edge_port->shadow_msr); + /* Set Initial MCR */ edge_port->shadow_mcr = MCR_RTS | MCR_DTR; - dbg ("ShadowMCR 0x%X", edge_port->shadow_mcr); + dbg("ShadowMCR 0x%X", edge_port->shadow_mcr); edge_serial = edge_port->edge_serial; if (mutex_lock_interruptible(&edge_serial->es_lock)) return -ERESTARTSYS; if (edge_serial->num_ports_open == 0) { - /* we are the first port to be opened, let's post the interrupt urb */ + /* we are the first port to open, post the interrupt urb */ urb = edge_serial->serial->port[0]->interrupt_in_urb; if (!urb) { - dev_err (&port->dev, "%s - no interrupt urb present, exiting\n", __func__); + dev_err(&port->dev, + "%s - no interrupt urb present, exiting\n", + __func__); status = -EINVAL; goto release_es_lock; } urb->complete = edge_interrupt_callback; urb->context = edge_serial; urb->dev = dev; - status = usb_submit_urb (urb, GFP_KERNEL); + status = usb_submit_urb(urb, GFP_KERNEL); if (status) { - dev_err (&port->dev, "%s - usb_submit_urb failed with value %d\n", __func__, status); + dev_err(&port->dev, + "%s - usb_submit_urb failed with value %d\n", + __func__, status); goto release_es_lock; } } @@ -2085,13 +1965,14 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) * reset the data toggle on the bulk endpoints to work around bug in * host controllers where things get out of sync some times */ - usb_clear_halt (dev, port->write_urb->pipe); - usb_clear_halt (dev, port->read_urb->pipe); + usb_clear_halt(dev, port->write_urb->pipe); + usb_clear_halt(dev, port->read_urb->pipe); /* start up our bulk read urb */ urb = port->read_urb; if (!urb) { - dev_err (&port->dev, "%s - no read urb present, exiting\n", __func__); + dev_err(&port->dev, "%s - no read urb present, exiting\n", + __func__); status = -EINVAL; goto unlink_int_urb; } @@ -2099,9 +1980,11 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) urb->complete = edge_bulk_in_callback; urb->context = edge_port; urb->dev = dev; - status = usb_submit_urb (urb, GFP_KERNEL); + status = usb_submit_urb(urb, GFP_KERNEL); if (status) { - dev_err (&port->dev, "%s - read bulk usb_submit_urb failed with value %d\n", __func__, status); + dev_err(&port->dev, + "%s - read bulk usb_submit_urb failed with value %d\n", + __func__, status); goto unlink_int_urb; } @@ -2119,7 +2002,8 @@ release_es_lock: return status; } -static void edge_close (struct usb_serial_port *port, struct file *filp) +static void edge_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct edgeport_serial *edge_serial; struct edgeport_port *edge_port; @@ -2127,18 +2011,18 @@ static void edge_close (struct usb_serial_port *port, struct file *filp) int status; dbg("%s - port %d", __func__, port->number); - + edge_serial = usb_get_serial_data(port->serial); edge_port = usb_get_serial_port_data(port); - if ((edge_serial == NULL) || (edge_port == NULL)) + if (edge_serial == NULL || edge_port == NULL) return; - - /* The bulkreadcompletion routine will check + + /* The bulkreadcompletion routine will check * this flag and dump add read data */ edge_port->close_pending = 1; /* chase the port close and flush */ - TIChasePort (edge_port, (HZ*closing_wait)/100, 1); + chase_port(edge_port, (HZ * closing_wait) / 100, 1); usb_kill_urb(port->read_urb); usb_kill_urb(port->write_urb); @@ -2148,7 +2032,7 @@ static void edge_close (struct usb_serial_port *port, struct file *filp) * send a close port command to it */ dbg("%s - send umpc_close_port", __func__); port_number = port->number - port->serial->minor; - status = TIWriteCommandSync (port->serial->dev, + status = send_cmd(port->serial->dev, UMPC_CLOSE_PORT, (__u8)(UMPM_UART1_PORT + port_number), 0, @@ -2167,7 +2051,8 @@ static void edge_close (struct usb_serial_port *port, struct file *filp) dbg("%s - exited", __func__); } -static int edge_write (struct usb_serial_port *port, const unsigned char *data, int count) +static int edge_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *data, int count) { struct edgeport_port *edge_port = usb_get_serial_port_data(port); unsigned long flags; @@ -2188,16 +2073,16 @@ static int edge_write (struct usb_serial_port *port, const unsigned char *data, count = edge_buf_put(edge_port->ep_out_buf, data, count); spin_unlock_irqrestore(&edge_port->ep_lock, flags); - edge_send(port); + edge_send(tty); return count; } -static void edge_send(struct usb_serial_port *port) +static void edge_send(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int count, result; struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; unsigned long flags; @@ -2223,11 +2108,12 @@ static void edge_send(struct usb_serial_port *port) spin_unlock_irqrestore(&edge_port->ep_lock, flags); - usb_serial_debug_data(debug, &port->dev, __func__, count, port->write_urb->transfer_buffer); + usb_serial_debug_data(debug, &port->dev, __func__, count, + port->write_urb->transfer_buffer); /* set up our urb */ - usb_fill_bulk_urb (port->write_urb, port->serial->dev, - usb_sndbulkpipe (port->serial->dev, + usb_fill_bulk_urb(port->write_urb, port->serial->dev, + usb_sndbulkpipe(port->serial->dev, port->bulk_out_endpointAddress), port->write_urb->transfer_buffer, count, edge_bulk_out_callback, @@ -2236,23 +2122,23 @@ static void edge_send(struct usb_serial_port *port) /* send the data out the bulk port */ result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result) { - dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __func__, result); + dev_err(&port->dev, + "%s - failed submitting write urb, error %d\n", + __func__, result); edge_port->ep_write_urb_in_use = 0; - // TODO: reschedule edge_send - } else { + /* TODO: reschedule edge_send */ + } else edge_port->icount.tx += count; - } /* wakeup any process waiting for writes to complete */ /* there is now more room in the buffer for new writes */ - if (tty) { - /* let the tty driver wakeup if it has a special write_wakeup function */ + if (tty) tty_wakeup(tty); - } } -static int edge_write_room (struct usb_serial_port *port) +static int edge_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); int room = 0; unsigned long flags; @@ -2260,9 +2146,9 @@ static int edge_write_room (struct usb_serial_port *port) dbg("%s - port %d", __func__, port->number); if (edge_port == NULL) - return -ENODEV; + return 0; if (edge_port->close_pending == 1) - return -ENODEV; + return 0; spin_lock_irqsave(&edge_port->ep_lock, flags); room = edge_buf_space_avail(edge_port->ep_out_buf); @@ -2272,8 +2158,9 @@ static int edge_write_room (struct usb_serial_port *port) return room; } -static int edge_chars_in_buffer (struct usb_serial_port *port) +static int edge_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; @@ -2281,22 +2168,22 @@ static int edge_chars_in_buffer (struct usb_serial_port *port) dbg("%s - port %d", __func__, port->number); if (edge_port == NULL) - return -ENODEV; + return 0; if (edge_port->close_pending == 1) - return -ENODEV; + return 0; spin_lock_irqsave(&edge_port->ep_lock, flags); chars = edge_buf_data_avail(edge_port->ep_out_buf); spin_unlock_irqrestore(&edge_port->ep_lock, flags); - dbg ("%s - returns %d", __func__, chars); + dbg("%s - returns %d", __func__, chars); return chars; } -static void edge_throttle (struct usb_serial_port *port) +static void edge_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty; int status; dbg("%s - port %d", __func__, port->number); @@ -2304,16 +2191,10 @@ static void edge_throttle (struct usb_serial_port *port) if (edge_port == NULL) return; - tty = port->tty; - if (!tty) { - dbg ("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the stop character */ if (I_IXOFF(tty)) { unsigned char stop_char = STOP_CHAR(tty); - status = edge_write (port, &stop_char, 1); + status = edge_write(tty, port, &stop_char, 1); if (status <= 0) { dev_err(&port->dev, "%s - failed to write stop character, %d\n", __func__, status); } @@ -2326,10 +2207,10 @@ static void edge_throttle (struct usb_serial_port *port) } -static void edge_unthrottle (struct usb_serial_port *port) +static void edge_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty; int status; dbg("%s - port %d", __func__, port->number); @@ -2337,27 +2218,22 @@ static void edge_unthrottle (struct usb_serial_port *port) if (edge_port == NULL) return; - tty = port->tty; - if (!tty) { - dbg ("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the start character */ if (I_IXOFF(tty)) { unsigned char start_char = START_CHAR(tty); - status = edge_write (port, &start_char, 1); + status = edge_write(tty, port, &start_char, 1); if (status <= 0) { dev_err(&port->dev, "%s - failed to write start character, %d\n", __func__, status); } } - /* if we are implementing RTS/CTS, restart reads */ /* are the Edgeport will assert the RTS line */ if (C_CRTSCTS(tty)) { status = restart_read(edge_port); if (status) - dev_err(&port->dev, "%s - read bulk usb_submit_urb failed with value %d\n", __func__, status); + dev_err(&port->dev, + "%s - read bulk usb_submit_urb failed: %d\n", + __func__, status); } } @@ -2398,22 +2274,23 @@ static int restart_read(struct edgeport_port *edge_port) return status; } -static void change_port_settings (struct edgeport_port *edge_port, struct ktermios *old_termios) +static void change_port_settings(struct tty_struct *tty, + struct edgeport_port *edge_port, struct ktermios *old_termios) { struct ump_uart_config *config; - struct tty_struct *tty; int baud; unsigned cflag; int status; - int port_number = edge_port->port->number - edge_port->port->serial->minor; + int port_number = edge_port->port->number - + edge_port->port->serial->minor; dbg("%s - port %d", __func__, edge_port->port->number); - tty = edge_port->port->tty; - config = kmalloc (sizeof (*config), GFP_KERNEL); if (!config) { - dev_err (&edge_port->port->dev, "%s - out of memory\n", __func__); + *tty->termios = *old_termios; + dev_err(&edge_port->port->dev, "%s - out of memory\n", + __func__); return; } @@ -2427,22 +2304,22 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi config->bUartMode = (__u8)(edge_port->bUartMode); switch (cflag & CSIZE) { - case CS5: - config->bDataBits = UMP_UART_CHAR5BITS; - dbg ("%s - data bits = 5", __func__); - break; - case CS6: - config->bDataBits = UMP_UART_CHAR6BITS; - dbg ("%s - data bits = 6", __func__); - break; - case CS7: - config->bDataBits = UMP_UART_CHAR7BITS; - dbg ("%s - data bits = 7", __func__); - break; - default: - case CS8: - config->bDataBits = UMP_UART_CHAR8BITS; - dbg ("%s - data bits = 8", __func__); + case CS5: + config->bDataBits = UMP_UART_CHAR5BITS; + dbg("%s - data bits = 5", __func__); + break; + case CS6: + config->bDataBits = UMP_UART_CHAR6BITS; + dbg("%s - data bits = 6", __func__); + break; + case CS7: + config->bDataBits = UMP_UART_CHAR7BITS; + dbg("%s - data bits = 7", __func__); + break; + default: + case CS8: + config->bDataBits = UMP_UART_CHAR8BITS; + dbg("%s - data bits = 8", __func__); break; } @@ -2457,7 +2334,7 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi dbg("%s - parity = even", __func__); } } else { - config->bParity = UMP_UART_NOPARITY; + config->bParity = UMP_UART_NOPARITY; dbg("%s - parity = none", __func__); } @@ -2480,29 +2357,26 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi restart_read(edge_port); } - /* if we are implementing XON/XOFF, set the start and stop character in the device */ - if (I_IXOFF(tty) || I_IXON(tty)) { - config->cXon = START_CHAR(tty); - config->cXoff = STOP_CHAR(tty); + /* if we are implementing XON/XOFF, set the start and stop + character in the device */ + config->cXon = START_CHAR(tty); + config->cXoff = STOP_CHAR(tty); - /* if we are implementing INBOUND XON/XOFF */ - if (I_IXOFF(tty)) { - config->wFlags |= UMP_MASK_UART_FLAGS_IN_X; - dbg ("%s - INBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x", - __func__, config->cXon, config->cXoff); - } else { - dbg ("%s - INBOUND XON/XOFF is disabled", __func__); - } + /* if we are implementing INBOUND XON/XOFF */ + if (I_IXOFF(tty)) { + config->wFlags |= UMP_MASK_UART_FLAGS_IN_X; + dbg("%s - INBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x", + __func__, config->cXon, config->cXoff); + } else + dbg("%s - INBOUND XON/XOFF is disabled", __func__); - /* if we are implementing OUTBOUND XON/XOFF */ - if (I_IXON(tty)) { - config->wFlags |= UMP_MASK_UART_FLAGS_OUT_X; - dbg ("%s - OUTBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x", - __func__, config->cXon, config->cXoff); - } else { - dbg ("%s - OUTBOUND XON/XOFF is disabled", __func__); - } - } + /* if we are implementing OUTBOUND XON/XOFF */ + if (I_IXON(tty)) { + config->wFlags |= UMP_MASK_UART_FLAGS_OUT_X; + dbg("%s - OUTBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x", + __func__, config->cXon, config->cXoff); + } else + dbg("%s - OUTBOUND XON/XOFF is disabled", __func__); tty->termios->c_cflag &= ~CMSPAR; @@ -2519,41 +2393,36 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi /* FIXME: Recompute actual baud from divisor here */ - dbg ("%s - baud rate = %d, wBaudRate = %d", __func__, baud, config->wBaudRate); + dbg("%s - baud rate = %d, wBaudRate = %d", __func__, baud, + config->wBaudRate); - dbg ("wBaudRate: %d", (int)(461550L / config->wBaudRate)); - dbg ("wFlags: 0x%x", config->wFlags); - dbg ("bDataBits: %d", config->bDataBits); - dbg ("bParity: %d", config->bParity); - dbg ("bStopBits: %d", config->bStopBits); - dbg ("cXon: %d", config->cXon); - dbg ("cXoff: %d", config->cXoff); - dbg ("bUartMode: %d", config->bUartMode); + dbg("wBaudRate: %d", (int)(461550L / config->wBaudRate)); + dbg("wFlags: 0x%x", config->wFlags); + dbg("bDataBits: %d", config->bDataBits); + dbg("bParity: %d", config->bParity); + dbg("bStopBits: %d", config->bStopBits); + dbg("cXon: %d", config->cXon); + dbg("cXoff: %d", config->cXoff); + dbg("bUartMode: %d", config->bUartMode); /* move the word values into big endian mode */ - cpu_to_be16s (&config->wFlags); - cpu_to_be16s (&config->wBaudRate); + cpu_to_be16s(&config->wFlags); + cpu_to_be16s(&config->wBaudRate); - status = TIWriteCommandSync (edge_port->port->serial->dev, - UMPC_SET_CONFIG, + status = send_cmd(edge_port->port->serial->dev, UMPC_SET_CONFIG, (__u8)(UMPM_UART1_PORT + port_number), - 0, - (__u8 *)config, - sizeof(*config)); - if (status) { - dbg ("%s - error %d when trying to write config to device", + 0, (__u8 *)config, sizeof(*config)); + if (status) + dbg("%s - error %d when trying to write config to device", __func__, status); - } - - kfree (config); - + kfree(config); return; } -static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) +static void edge_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; unsigned int cflag; cflag = tty->termios->c_cflag; @@ -2562,20 +2431,19 @@ static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old tty->termios->c_cflag, tty->termios->c_iflag); dbg("%s - old clfag %08x old iflag %08x", __func__, old_termios->c_cflag, old_termios->c_iflag); - dbg("%s - port %d", __func__, port->number); if (edge_port == NULL) return; - /* change the port settings to the new ones specified */ - change_port_settings (edge_port, old_termios); - + change_port_settings(tty, edge_port, old_termios); return; } -static int edge_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear) +static int edge_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); unsigned int mcr; unsigned long flags; @@ -2601,13 +2469,13 @@ static int edge_tiocmset (struct usb_serial_port *port, struct file *file, unsig edge_port->shadow_mcr = mcr; spin_unlock_irqrestore(&edge_port->ep_lock, flags); - TIRestoreMCR (edge_port, mcr); - + restore_mcr(edge_port, mcr); return 0; } -static int edge_tiocmget(struct usb_serial_port *port, struct file *file) +static int edge_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); unsigned int result = 0; unsigned int msr; @@ -2634,7 +2502,8 @@ static int edge_tiocmget(struct usb_serial_port *port, struct file *file) return result; } -static int get_serial_info (struct edgeport_port *edge_port, struct serial_struct __user *retinfo) +static int get_serial_info(struct edgeport_port *edge_port, + struct serial_struct __user *retinfo) { struct serial_struct tmp; @@ -2652,18 +2521,16 @@ static int get_serial_info (struct edgeport_port *edge_port, struct serial_struc tmp.baud_base = 9600; tmp.close_delay = 5*HZ; tmp.closing_wait = closing_wait; -// tmp.custom_divisor = state->custom_divisor; -// tmp.hub6 = state->hub6; -// tmp.io_type = state->io_type; - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; return 0; } -static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg) +static int edge_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct async_icount cnow; struct async_icount cprev; @@ -2671,81 +2538,64 @@ static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd); switch (cmd) { - case TIOCINQ: - dbg("%s - (%d) TIOCINQ", __func__, port->number); -// return get_number_bytes_avail(edge_port, (unsigned int *) arg); - break; - - case TIOCSERGETLSR: - dbg("%s - (%d) TIOCSERGETLSR", __func__, port->number); -// return get_lsr_info(edge_port, (unsigned int *) arg); - break; - - case TIOCGSERIAL: - dbg("%s - (%d) TIOCGSERIAL", __func__, port->number); - return get_serial_info(edge_port, (struct serial_struct __user *) arg); - break; - - case TIOCSSERIAL: - dbg("%s - (%d) TIOCSSERIAL", __func__, port->number); - break; - - case TIOCMIWAIT: - dbg("%s - (%d) TIOCMIWAIT", __func__, port->number); - cprev = edge_port->icount; - while (1) { - interruptible_sleep_on(&edge_port->delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - cnow = edge_port->icount; - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) - return -EIO; /* no change => error */ - 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)) ) { - return 0; - } - cprev = cnow; + case TIOCGSERIAL: + dbg("%s - (%d) TIOCGSERIAL", __func__, port->number); + return get_serial_info(edge_port, + (struct serial_struct __user *) arg); + case TIOCMIWAIT: + dbg("%s - (%d) TIOCMIWAIT", __func__, port->number); + cprev = edge_port->icount; + while (1) { + interruptible_sleep_on(&edge_port->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + cnow = edge_port->icount; + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return -EIO; /* no change => error */ + 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))) { + return 0; } - /* not reached */ - break; - - case TIOCGICOUNT: - dbg ("%s - (%d) TIOCGICOUNT RX=%d, TX=%d", __func__, - port->number, edge_port->icount.rx, edge_port->icount.tx); - if (copy_to_user((void __user *)arg, &edge_port->icount, sizeof(edge_port->icount))) - return -EFAULT; - return 0; + cprev = cnow; + } + /* not reached */ + break; + case TIOCGICOUNT: + dbg("%s - (%d) TIOCGICOUNT RX=%d, TX=%d", __func__, + port->number, edge_port->icount.rx, edge_port->icount.tx); + if (copy_to_user((void __user *)arg, &edge_port->icount, + sizeof(edge_port->icount))) + return -EFAULT; + return 0; } - return -ENOIOCTLCMD; } -static void edge_break (struct usb_serial_port *port, int break_state) +static void edge_break(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); int status; + int bv = 0; /* Off */ - dbg ("%s - state = %d", __func__, break_state); + dbg("%s - state = %d", __func__, break_state); /* chase the port close */ - TIChasePort (edge_port, 0, 0); + chase_port(edge_port, 0, 0); - if (break_state == -1) { - status = TISetBreak (edge_port); - } else { - status = TIClearBreak (edge_port); - } - if (status) { - dbg ("%s - error %d sending break set/clear command.", + if (break_state == -1) + bv = 1; /* On */ + status = ti_do_config(edge_port, UMPC_SET_CLR_BREAK, bv); + if (status) + dbg("%s - error %d sending break set/clear command.", __func__, status); - } } -static int edge_startup (struct usb_serial *serial) +static int edge_startup(struct usb_serial *serial) { struct edgeport_serial *edge_serial; struct edgeport_port *edge_port; @@ -2765,9 +2615,9 @@ static int edge_startup (struct usb_serial *serial) edge_serial->serial = serial; usb_set_serial_data(serial, edge_serial); - status = TIDownloadFirmware (edge_serial); + status = download_fw(edge_serial); if (status) { - kfree (edge_serial); + kfree(edge_serial); return status; } @@ -2775,13 +2625,15 @@ static int edge_startup (struct usb_serial *serial) for (i = 0; i < serial->num_ports; ++i) { edge_port = kzalloc(sizeof(struct edgeport_port), GFP_KERNEL); if (edge_port == NULL) { - dev_err(&serial->dev->dev, "%s - Out of memory\n", __func__); + dev_err(&serial->dev->dev, "%s - Out of memory\n", + __func__); goto cleanup; } spin_lock_init(&edge_port->ep_lock); edge_port->ep_out_buf = edge_buf_alloc(EDGE_OUT_BUF_SIZE); if (edge_port->ep_out_buf == NULL) { - dev_err(&serial->dev->dev, "%s - Out of memory\n", __func__); + dev_err(&serial->dev->dev, "%s - Out of memory\n", + __func__); kfree(edge_port); goto cleanup; } @@ -2790,27 +2642,27 @@ static int edge_startup (struct usb_serial *serial) usb_set_serial_port_data(serial->port[i], edge_port); edge_port->bUartMode = default_uart_mode; } - + return 0; cleanup: - for (--i; i>=0; --i) { + for (--i; i >= 0; --i) { edge_port = usb_get_serial_port_data(serial->port[i]); edge_buf_free(edge_port->ep_out_buf); kfree(edge_port); usb_set_serial_port_data(serial->port[i], NULL); } - kfree (edge_serial); + kfree(edge_serial); usb_set_serial_data(serial, NULL); return -ENOMEM; } -static void edge_shutdown (struct usb_serial *serial) +static void edge_shutdown(struct usb_serial *serial) { int i; struct edgeport_port *edge_port; - dbg ("%s", __func__); + dbg("%s", __func__); for (i = 0; i < serial->num_ports; ++i) { edge_port = usb_get_serial_port_data(serial->port[i]); @@ -2852,7 +2704,8 @@ static ssize_t store_uart_mode(struct device *dev, return count; } -static DEVICE_ATTR(uart_mode, S_IWUSR | S_IRUGO, show_uart_mode, store_uart_mode); +static DEVICE_ATTR(uart_mode, S_IWUSR | S_IRUGO, show_uart_mode, + store_uart_mode); static int edge_create_sysfs_attrs(struct usb_serial_port *port) { @@ -2922,9 +2775,9 @@ static void edge_buf_free(struct edge_buf *eb) static void edge_buf_clear(struct edge_buf *eb) { - if (eb != NULL) - eb->buf_get = eb->buf_put; - /* equivalent to a get of all data available */ + if (eb != NULL) + eb->buf_get = eb->buf_put; + /* equivalent to a get of all data available */ } @@ -2937,10 +2790,9 @@ static void edge_buf_clear(struct edge_buf *eb) static unsigned int edge_buf_data_avail(struct edge_buf *eb) { - if (eb != NULL) - return ((eb->buf_size + eb->buf_put - eb->buf_get) % eb->buf_size); - else + if (eb == NULL) return 0; + return ((eb->buf_size + eb->buf_put - eb->buf_get) % eb->buf_size); } @@ -2953,10 +2805,9 @@ static unsigned int edge_buf_data_avail(struct edge_buf *eb) static unsigned int edge_buf_space_avail(struct edge_buf *eb) { - if (eb != NULL) - return ((eb->buf_size + eb->buf_get - eb->buf_put - 1) % eb->buf_size); - else + if (eb == NULL) return 0; + return ((eb->buf_size + eb->buf_get - eb->buf_put - 1) % eb->buf_size); } @@ -3113,7 +2964,7 @@ static int __init edgeport_init(void) if (retval) goto failed_2port_device_register; retval = usb_register(&io_driver); - if (retval) + if (retval) goto failed_usb_register; info(DRIVER_DESC " " DRIVER_VERSION); return 0; @@ -3125,11 +2976,11 @@ failed_1port_device_register: return retval; } -static void __exit edgeport_exit (void) +static void __exit edgeport_exit(void) { - usb_deregister (&io_driver); - usb_serial_deregister (&edgeport_1port_device); - usb_serial_deregister (&edgeport_2port_device); + usb_deregister(&io_driver); + usb_serial_deregister(&edgeport_1port_device); + usb_serial_deregister(&edgeport_2port_device); } module_init(edgeport_init); @@ -3151,8 +3002,8 @@ module_param(closing_wait, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(closing_wait, "Maximum wait for data to drain, in .01 secs"); module_param(ignore_cpu_rev, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(ignore_cpu_rev, "Ignore the cpu revision when connecting to a device"); +MODULE_PARM_DESC(ignore_cpu_rev, + "Ignore the cpu revision when connecting to a device"); module_param(default_uart_mode, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(default_uart_mode, "Default uart_mode, 0=RS232, ..."); - diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index d9fb3768a2d7..cd9a2e138c8b 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -53,7 +53,7 @@ #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include "ipaq.h" @@ -74,19 +74,21 @@ static int connect_retries = KP_RETRIES; static int initial_wait; /* Function prototypes for an ipaq */ -static int ipaq_open (struct usb_serial_port *port, struct file *filp); -static void ipaq_close (struct usb_serial_port *port, struct file *filp); -static int ipaq_startup (struct usb_serial *serial); -static void ipaq_shutdown (struct usb_serial *serial); -static int ipaq_write(struct usb_serial_port *port, const unsigned char *buf, - int count); -static int ipaq_write_bulk(struct usb_serial_port *port, const unsigned char *buf, - int count); +static int ipaq_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void ipaq_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static int ipaq_startup(struct usb_serial *serial); +static void ipaq_shutdown(struct usb_serial *serial); +static int ipaq_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); +static int ipaq_write_bulk(struct usb_serial_port *port, + const unsigned char *buf, int count); static void ipaq_write_gather(struct usb_serial_port *port); -static void ipaq_read_bulk_callback (struct urb *urb); +static void ipaq_read_bulk_callback(struct urb *urb); static void ipaq_write_bulk_callback(struct urb *urb); -static int ipaq_write_room(struct usb_serial_port *port); -static int ipaq_chars_in_buffer(struct usb_serial_port *port); +static int ipaq_write_room(struct tty_struct *tty); +static int ipaq_chars_in_buffer(struct tty_struct *tty); static void ipaq_destroy_lists(struct usb_serial_port *port); @@ -550,7 +552,7 @@ static struct usb_device_id ipaq_id_table [] = { { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, ipaq_id_table); +MODULE_DEVICE_TABLE(usb, ipaq_id_table); static struct usb_driver ipaq_driver = { .name = "ipaq", @@ -591,7 +593,8 @@ static spinlock_t write_list_lock; static int bytes_in; static int bytes_out; -static int ipaq_open(struct usb_serial_port *port, struct file *filp) +static int ipaq_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; struct ipaq_private *priv; @@ -617,9 +620,9 @@ static int ipaq_open(struct usb_serial_port *port, struct file *filp) for (i = 0; i < URBDATA_QUEUE_MAX / PACKET_SIZE; i++) { pkt = kmalloc(sizeof(struct ipaq_packet), GFP_KERNEL); - if (pkt == NULL) { + if (pkt == NULL) goto enomem; - } + pkt->data = kmalloc(PACKET_SIZE, GFP_KERNEL); if (pkt->data == NULL) { kfree(pkt); @@ -637,23 +640,28 @@ static int ipaq_open(struct usb_serial_port *port, struct file *filp) * discipline instead of queueing. */ - port->tty->low_latency = 1; - port->tty->raw = 1; - port->tty->real_raw = 1; - + if (tty) { + tty->low_latency = 1; + /* FIXME: These two are bogus */ + tty->raw = 1; + tty->real_raw = 1; + } /* * Lose the small buffers usbserial provides. Make larger ones. */ kfree(port->bulk_in_buffer); kfree(port->bulk_out_buffer); + /* make sure the generic serial code knows */ + port->bulk_out_buffer = NULL; + port->bulk_in_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL); - if (port->bulk_in_buffer == NULL) { - port->bulk_out_buffer = NULL; /* prevent double free */ + if (port->bulk_in_buffer == NULL) goto enomem; - } + port->bulk_out_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL); if (port->bulk_out_buffer == NULL) { + /* the buffer is useless, free it */ kfree(port->bulk_in_buffer); port->bulk_in_buffer = NULL; goto enomem; @@ -661,8 +669,9 @@ static int ipaq_open(struct usb_serial_port *port, struct file *filp) port->read_urb->transfer_buffer = port->bulk_in_buffer; port->write_urb->transfer_buffer = port->bulk_out_buffer; port->read_urb->transfer_buffer_length = URBDATA_SIZE; - port->bulk_out_size = port->write_urb->transfer_buffer_length = URBDATA_SIZE; - + port->bulk_out_size = port->write_urb->transfer_buffer_length + = URBDATA_SIZE; + msleep(1000*initial_wait); /* @@ -691,13 +700,15 @@ static int ipaq_open(struct usb_serial_port *port, struct file *filp) /* Start reading from the device */ usb_fill_bulk_urb(port->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, - ipaq_read_bulk_callback, port); + usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, + port->read_urb->transfer_buffer_length, + ipaq_read_bulk_callback, port); result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result) { - err("%s - failed submitting read urb, error %d", __func__, result); + err("%s - failed submitting read urb, error %d", + __func__, result); goto error; } @@ -713,12 +724,13 @@ error: } -static void ipaq_close(struct usb_serial_port *port, struct file *filp) +static void ipaq_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct ipaq_private *priv = usb_get_serial_port_data(port); dbg("%s - port %d", __func__, port->number); - + /* * shut down bulk read and write */ @@ -728,7 +740,8 @@ static void ipaq_close(struct usb_serial_port *port, struct file *filp) kfree(priv); usb_set_serial_port_data(port, NULL); - /* Uncomment the following line if you want to see some statistics in your syslog */ + /* Uncomment the following line if you want to see some statistics + * in your syslog */ /* info ("Bytes In = %d Bytes Out = %d", bytes_in, bytes_out); */ } @@ -748,9 +761,10 @@ static void ipaq_read_bulk_callback(struct urb *urb) return; } - usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, data); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -759,18 +773,20 @@ static void ipaq_read_bulk_callback(struct urb *urb) } /* Continue trying to always read */ - usb_fill_bulk_urb(port->read_urb, port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, - ipaq_read_bulk_callback, port); + usb_fill_bulk_urb(port->read_urb, port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, + port->read_urb->transfer_buffer_length, + ipaq_read_bulk_callback, port); result = usb_submit_urb(port->read_urb, GFP_ATOMIC); if (result) - err("%s - failed resubmitting read urb, error %d", __func__, result); + err("%s - failed resubmitting read urb, error %d", + __func__, result); return; } -static int ipaq_write(struct usb_serial_port *port, const unsigned char *buf, - int count) +static int ipaq_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { const unsigned char *current_position = buf; int bytes_sent = 0; @@ -780,9 +796,8 @@ static int ipaq_write(struct usb_serial_port *port, const unsigned char *buf, while (count > 0) { transfer_size = min(count, PACKET_SIZE); - if (ipaq_write_bulk(port, current_position, transfer_size)) { + if (ipaq_write_bulk(port, current_position, transfer_size)) break; - } current_position += transfer_size; bytes_sent += transfer_size; count -= transfer_size; @@ -790,10 +805,10 @@ static int ipaq_write(struct usb_serial_port *port, const unsigned char *buf, } return bytes_sent; -} +} -static int ipaq_write_bulk(struct usb_serial_port *port, const unsigned char *buf, - int count) +static int ipaq_write_bulk(struct usb_serial_port *port, + const unsigned char *buf, int count) { struct ipaq_private *priv = usb_get_serial_port_data(port); struct ipaq_packet *pkt = NULL; @@ -830,9 +845,9 @@ static int ipaq_write_bulk(struct usb_serial_port *port, const unsigned char *bu ipaq_write_gather(port); spin_unlock_irqrestore(&write_list_lock, flags); result = usb_submit_urb(port->write_urb, GFP_ATOMIC); - if (result) { - err("%s - failed submitting write urb, error %d", __func__, result); - } + if (result) + err("%s - failed submitting write urb, error %d", + __func__, result); } else { spin_unlock_irqrestore(&write_list_lock, flags); } @@ -859,16 +874,15 @@ static void ipaq_write_gather(struct usb_serial_port *port) list_move(&pkt->list, &priv->freelist); priv->free_len += PACKET_SIZE; } - if (room == 0) { + if (room == 0) break; - } } count = URBDATA_SIZE - room; - usb_fill_bulk_urb(port->write_urb, serial->dev, - usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress), - port->write_urb->transfer_buffer, count, ipaq_write_bulk_callback, - port); + usb_fill_bulk_urb(port->write_urb, serial->dev, + usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress), + port->write_urb->transfer_buffer, count, + ipaq_write_bulk_callback, port); return; } @@ -893,9 +907,9 @@ static void ipaq_write_bulk_callback(struct urb *urb) ipaq_write_gather(port); spin_unlock_irqrestore(&write_list_lock, flags); result = usb_submit_urb(port->write_urb, GFP_ATOMIC); - if (result) { - err("%s - failed submitting write urb, error %d", __func__, result); - } + if (result) + err("%s - failed submitting write urb, error %d", + __func__, result); } else { priv->active = 0; spin_unlock_irqrestore(&write_list_lock, flags); @@ -904,16 +918,18 @@ static void ipaq_write_bulk_callback(struct urb *urb) usb_serial_port_softint(port); } -static int ipaq_write_room(struct usb_serial_port *port) +static int ipaq_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ipaq_private *priv = usb_get_serial_port_data(port); dbg("%s - freelen %d", __func__, priv->free_len); return priv->free_len; } -static int ipaq_chars_in_buffer(struct usb_serial_port *port) +static int ipaq_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ipaq_private *priv = usb_get_serial_port_data(port); dbg("%s - queuelen %d", __func__, priv->queue_len); @@ -944,7 +960,7 @@ static int ipaq_startup(struct usb_serial *serial) serial->dev->actconfig->desc.bConfigurationValue); return -ENODEV; } - return usb_reset_configuration (serial->dev); + return usb_reset_configuration(serial->dev); } static void ipaq_shutdown(struct usb_serial *serial) @@ -957,7 +973,7 @@ static int __init ipaq_init(void) int retval; spin_lock_init(&write_list_lock); retval = usb_serial_register(&ipaq_device); - if (retval) + if (retval) goto failed_usb_serial_register; info(DRIVER_DESC " " DRIVER_VERSION); if (vendor) { @@ -967,7 +983,7 @@ static int __init ipaq_init(void) retval = usb_register(&ipaq_driver); if (retval) goto failed_usb_register; - + return 0; failed_usb_register: usb_serial_deregister(&ipaq_device); @@ -986,8 +1002,8 @@ static void __exit ipaq_exit(void) module_init(ipaq_init); module_exit(ipaq_exit); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); @@ -1000,7 +1016,9 @@ module_param(product, ushort, 0); MODULE_PARM_DESC(product, "User specified USB idProduct"); module_param(connect_retries, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(connect_retries, "Maximum number of connect retries (one second each)"); +MODULE_PARM_DESC(connect_retries, + "Maximum number of connect retries (one second each)"); module_param(initial_wait, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(initial_wait, "Time to wait before attempting a connection (in seconds)"); +MODULE_PARM_DESC(initial_wait, + "Time to wait before attempting a connection (in seconds)"); diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index bc85ca5c1c37..a842025b9b57 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -10,27 +10,27 @@ * (at your option) any later version. * * All information about the device was acquired using SnoopyPro - * on MSFT's O/S, and examing the MSFT drivers' debug output + * on MSFT's O/S, and examing the MSFT drivers' debug output * (insanely left _on_ in the enduser version) * * It was written out of frustration with the IPWireless USB modem * supplied by Axity3G/Sentech South Africa not supporting * Linux whatsoever. * - * Nobody provided any proprietary information that was not already + * Nobody provided any proprietary information that was not already * available for this device. - * - * The modem adheres to the "3GPP TS 27.007 AT command set for 3G - * User Equipment (UE)" standard, available from + * + * The modem adheres to the "3GPP TS 27.007 AT command set for 3G + * User Equipment (UE)" standard, available from * http://www.3gpp.org/ftp/Specs/html-info/27007.htm * * The code was only tested the IPWireless handheld modem distributed * in South Africa by Sentech. - * + * * It may work for Woosh Inc in .nz too, as it appears they use the * same kit. * - * There is still some work to be done in terms of handling + * There is still some work to be done in terms of handling * DCD, DTR, RTS, CTS which are currently faked. * It's good enough for PPP at this point. It's based off all kinds of * code found in usb/serial and usb/class @@ -47,7 +47,7 @@ #include <linux/spinlock.h> #include <linux/usb.h> #include <linux/usb/serial.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> /* * Version Information @@ -64,7 +64,7 @@ /* Message sizes */ #define EVENT_BUFFER_SIZE 0xFF -#define CHAR2INT16(c1,c0) (((u32)((c1) & 0xff) << 8) + (u32)((c0) & 0xff)) +#define CHAR2INT16(c1, c0) (((u32)((c1) & 0xff) << 8) + (u32)((c0) & 0xff)) #define NUM_BULK_URBS 24 #define NUM_CONTROL_URBS 16 @@ -94,33 +94,34 @@ enum { /* data bits */ #define ipw_dtb_7 0x700 -#define ipw_dtb_8 0x810 // ok so the define is misleading, I know, but forces 8,n,1 - // I mean, is there a point to any other setting these days? :) +#define ipw_dtb_8 0x810 /* ok so the define is misleading, I know, but forces 8,n,1 */ + /* I mean, is there a point to any other setting these days? :) */ /* usb control request types : */ -#define IPW_SIO_RXCTL 0x00 // control bulk rx channel transmissions, value=1/0 (on/off) -#define IPW_SIO_SET_BAUD 0x01 // set baud, value=requested ipw_sio_bxxxx -#define IPW_SIO_SET_LINE 0x03 // set databits, parity. value=ipw_dtb_x -#define IPW_SIO_SET_PIN 0x03 // set/clear dtr/rts value=ipw_pin_xxx -#define IPW_SIO_POLL 0x08 // get serial port status byte, call with value=0 -#define IPW_SIO_INIT 0x11 // initializes ? value=0 (appears as first thing todo on open) -#define IPW_SIO_PURGE 0x12 // purge all transmissions?, call with value=numchar_to_purge -#define IPW_SIO_HANDFLOW 0x13 // set xon/xoff limits value=0, and a buffer of 0x10 bytes -#define IPW_SIO_SETCHARS 0x13 // set the flowcontrol special chars, value=0, buf=6 bytes, - // last 2 bytes contain flowcontrol chars e.g. 00 00 00 00 11 13 +#define IPW_SIO_RXCTL 0x00 /* control bulk rx channel transmissions, value=1/0 (on/off) */ +#define IPW_SIO_SET_BAUD 0x01 /* set baud, value=requested ipw_sio_bxxxx */ +#define IPW_SIO_SET_LINE 0x03 /* set databits, parity. value=ipw_dtb_x */ +#define IPW_SIO_SET_PIN 0x03 /* set/clear dtr/rts value=ipw_pin_xxx */ +#define IPW_SIO_POLL 0x08 /* get serial port status byte, call with value=0 */ +#define IPW_SIO_INIT 0x11 /* initializes ? value=0 (appears as first thing todo on open) */ +#define IPW_SIO_PURGE 0x12 /* purge all transmissions?, call with value=numchar_to_purge */ +#define IPW_SIO_HANDFLOW 0x13 /* set xon/xoff limits value=0, and a buffer of 0x10 bytes */ +#define IPW_SIO_SETCHARS 0x13 /* set the flowcontrol special chars, value=0, buf=6 bytes, */ + /* last 2 bytes contain flowcontrol chars e.g. 00 00 00 00 11 13 */ /* values used for request IPW_SIO_SET_PIN */ #define IPW_PIN_SETDTR 0x101 #define IPW_PIN_SETRTS 0x202 #define IPW_PIN_CLRDTR 0x100 -#define IPW_PIN_CLRRTS 0x200 // unconfirmed +#define IPW_PIN_CLRRTS 0x200 /* unconfirmed */ /* values used for request IPW_SIO_RXCTL */ #define IPW_RXBULK_ON 1 #define IPW_RXBULK_OFF 0 /* various 16 byte hardcoded transferbuffers used by flow control */ -#define IPW_BYTES_FLOWINIT { 0x01, 0, 0, 0, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } +#define IPW_BYTES_FLOWINIT { 0x01, 0, 0, 0, 0x40, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0 } /* Interpretation of modem status lines */ /* These need sorting out by individually connecting pins and checking @@ -132,17 +133,6 @@ enum { #define IPW_CTS ((1<<5) | (1<<4)) #define IPW_WANTS_TO_SEND 0x30 -//#define IPW_DTR /* Data Terminal Ready */ -//#define IPW_CTS /* Clear To Send */ -//#define IPW_CD /* Carrier Detect */ -//#define IPW_DSR /* Data Set Ready */ -//#define IPW_RxD /* Receive pin */ - -//#define IPW_LE -//#define IPW_RTS -//#define IPW_ST -//#define IPW_SR -//#define IPW_RI /* Ring Indicator */ static struct usb_device_id usb_ipw_ids[] = { { USB_DEVICE(IPW_VID, IPW_PID) }, @@ -177,9 +167,10 @@ static void ipw_read_bulk_callback(struct urb *urb) return; } - usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, data); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -187,19 +178,22 @@ static void ipw_read_bulk_callback(struct urb *urb) } /* Continue trying to always read */ - usb_fill_bulk_urb (port->read_urb, port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, + usb_fill_bulk_urb(port->read_urb, port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - ipw_read_bulk_callback, port); + port->read_urb->transfer_buffer, + port->read_urb->transfer_buffer_length, + ipw_read_bulk_callback, port); result = usb_submit_urb(port->read_urb, GFP_ATOMIC); if (result) - dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __func__, result); + dev_err(&port->dev, + "%s - failed resubmitting read urb, error %d\n", + __func__, result); return; } -static int ipw_open(struct usb_serial_port *port, struct file *filp) +static int ipw_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_device *dev = port->serial->dev; u8 buf_flow_static[16] = IPW_BYTES_FLOWINIT; @@ -212,29 +206,33 @@ static int ipw_open(struct usb_serial_port *port, struct file *filp) if (!buf_flow_init) return -ENOMEM; - if (port->tty) - port->tty->low_latency = 1; - - /* --1: Tell the modem to initialize (we think) From sniffs this is always the - * first thing that gets sent to the modem during opening of the device */ - dbg("%s: Sending SIO_INIT (we guess)",__func__); - result = usb_control_msg(dev, usb_sndctrlpipe(dev,0), - IPW_SIO_INIT, - USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, - 0, - 0, /* index */ - NULL, - 0, - 100000); + if (tty) + tty->low_latency = 1; + + /* --1: Tell the modem to initialize (we think) From sniffs this is + * always the first thing that gets sent to the modem during + * opening of the device */ + dbg("%s: Sending SIO_INIT (we guess)", __func__); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + IPW_SIO_INIT, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + 0, + 0, /* index */ + NULL, + 0, + 100000); if (result < 0) - dev_err(&port->dev, "Init of modem failed (error = %d)\n", result); + dev_err(&port->dev, + "Init of modem failed (error = %d)\n", result); /* reset the bulk pipes */ - usb_clear_halt(dev, usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress)); - usb_clear_halt(dev, usb_sndbulkpipe(dev, port->bulk_out_endpointAddress)); + usb_clear_halt(dev, + usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress)); + usb_clear_halt(dev, + usb_sndbulkpipe(dev, port->bulk_out_endpointAddress)); - /*--2: Start reading from the device */ - dbg("%s: setting up bulk read callback",__func__); + /*--2: Start reading from the device */ + dbg("%s: setting up bulk read callback", __func__); usb_fill_bulk_urb(port->read_urb, dev, usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress), port->bulk_in_buffer, @@ -242,66 +240,72 @@ static int ipw_open(struct usb_serial_port *port, struct file *filp) ipw_read_bulk_callback, port); result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result < 0) - dbg("%s - usb_submit_urb(read bulk) failed with status %d", __func__, result); + dbg("%s - usb_submit_urb(read bulk) failed with status %d", + __func__, result); /*--3: Tell the modem to open the floodgates on the rx bulk channel */ - dbg("%s:asking modem for RxRead (RXBULK_ON)",__func__); + dbg("%s:asking modem for RxRead (RXBULK_ON)", __func__); result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - IPW_SIO_RXCTL, - USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, - IPW_RXBULK_ON, - 0, /* index */ - NULL, - 0, - 100000); - if (result < 0) - dev_err(&port->dev, "Enabling bulk RxRead failed (error = %d)\n", result); + IPW_SIO_RXCTL, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + IPW_RXBULK_ON, + 0, /* index */ + NULL, + 0, + 100000); + if (result < 0) + dev_err(&port->dev, + "Enabling bulk RxRead failed (error = %d)\n", result); /*--4: setup the initial flowcontrol */ - dbg("%s:setting init flowcontrol (%s)",__func__,buf_flow_init); + dbg("%s:setting init flowcontrol (%s)", __func__, buf_flow_init); result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - IPW_SIO_HANDFLOW, - USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, - 0, - 0, - buf_flow_init, - 0x10, - 200000); + IPW_SIO_HANDFLOW, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + 0, + 0, + buf_flow_init, + 0x10, + 200000); if (result < 0) - dev_err(&port->dev, "initial flowcontrol failed (error = %d)\n", result); + dev_err(&port->dev, + "initial flowcontrol failed (error = %d)\n", result); /*--5: raise the dtr */ - dbg("%s:raising dtr",__func__); + dbg("%s:raising dtr", __func__); result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - IPW_SIO_SET_PIN, - USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, - IPW_PIN_SETDTR, - 0, - NULL, - 0, - 200000); + IPW_SIO_SET_PIN, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + IPW_PIN_SETDTR, + 0, + NULL, + 0, + 200000); if (result < 0) - dev_err(&port->dev, "setting dtr failed (error = %d)\n", result); + dev_err(&port->dev, + "setting dtr failed (error = %d)\n", result); /*--6: raise the rts */ - dbg("%s:raising rts",__func__); + dbg("%s:raising rts", __func__); result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - IPW_SIO_SET_PIN, - USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, - IPW_PIN_SETRTS, - 0, - NULL, - 0, - 200000); + IPW_SIO_SET_PIN, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + IPW_PIN_SETRTS, + 0, + NULL, + 0, + 200000); if (result < 0) - dev_err(&port->dev, "setting dtr failed (error = %d)\n", result); - + dev_err(&port->dev, + "setting dtr failed (error = %d)\n", result); + kfree(buf_flow_init); return 0; } -static void ipw_close(struct usb_serial_port *port, struct file * filp) +static void ipw_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_device *dev = port->serial->dev; int result; @@ -312,56 +316,62 @@ static void ipw_close(struct usb_serial_port *port, struct file * filp) } /*--1: drop the dtr */ - dbg("%s:dropping dtr",__func__); + dbg("%s:dropping dtr", __func__); result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - IPW_SIO_SET_PIN, - USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, - IPW_PIN_CLRDTR, - 0, - NULL, - 0, - 200000); + IPW_SIO_SET_PIN, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + IPW_PIN_CLRDTR, + 0, + NULL, + 0, + 200000); if (result < 0) - dev_err(&port->dev, "dropping dtr failed (error = %d)\n", result); + dev_err(&port->dev, "dropping dtr failed (error = %d)\n", + result); /*--2: drop the rts */ - dbg("%s:dropping rts",__func__); + dbg("%s:dropping rts", __func__); result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - IPW_SIO_SET_PIN, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, - IPW_PIN_CLRRTS, - 0, - NULL, - 0, - 200000); + IPW_SIO_SET_PIN, USB_TYPE_VENDOR | + USB_RECIP_INTERFACE | USB_DIR_OUT, + IPW_PIN_CLRRTS, + 0, + NULL, + 0, + 200000); if (result < 0) - dev_err(&port->dev, "dropping rts failed (error = %d)\n", result); + dev_err(&port->dev, + "dropping rts failed (error = %d)\n", result); /*--3: purge */ - dbg("%s:sending purge",__func__); + dbg("%s:sending purge", __func__); result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - IPW_SIO_PURGE, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, - 0x03, - 0, - NULL, - 0, - 200000); + IPW_SIO_PURGE, USB_TYPE_VENDOR | + USB_RECIP_INTERFACE | USB_DIR_OUT, + 0x03, + 0, + NULL, + 0, + 200000); if (result < 0) dev_err(&port->dev, "purge failed (error = %d)\n", result); - /* send RXBULK_off (tell modem to stop transmitting bulk data on rx chan) */ + /* send RXBULK_off (tell modem to stop transmitting bulk data on + rx chan) */ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - IPW_SIO_RXCTL, - USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, - IPW_RXBULK_OFF, - 0, /* index */ - NULL, - 0, - 100000); + IPW_SIO_RXCTL, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + IPW_RXBULK_OFF, + 0, /* index */ + NULL, + 0, + 100000); if (result < 0) - dev_err(&port->dev, "Disabling bulk RxRead failed (error = %d)\n", result); + dev_err(&port->dev, + "Disabling bulk RxRead failed (error = %d)\n", result); /* shutdown any in-flight urbs that we know about */ usb_kill_urb(port->read_urb); @@ -384,13 +394,14 @@ static void ipw_write_bulk_callback(struct urb *urb) usb_serial_port_softint(port); } -static int ipw_write(struct usb_serial_port *port, const unsigned char *buf, int count) +static int ipw_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { struct usb_device *dev = port->serial->dev; int ret; dbg("%s: TOP: count=%d, in_interrupt=%ld", __func__, - count, in_interrupt() ); + count, in_interrupt()); if (count == 0) { dbg("%s - write request of 0 bytes", __func__); @@ -421,13 +432,14 @@ static int ipw_write(struct usb_serial_port *port, const unsigned char *buf, int ret = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (ret != 0) { port->write_urb_busy = 0; - dbg("%s - usb_submit_urb(write bulk) failed with error = %d", __func__, ret); + dbg("%s - usb_submit_urb(write bulk) failed with error = %d", + __func__, ret); return ret; } dbg("%s returning %d", __func__, count); return count; -} +} static int ipw_probe(struct usb_serial_port *port) { @@ -486,8 +498,8 @@ module_init(usb_ipw_init); module_exit(usb_ipw_exit); /* Module information */ -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 004d57385a75..e59155c6607d 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -19,7 +19,12 @@ * was written by Roman Weissgaerber <weissg@vienna.at>, Dag Brattli * <dag@brattli.net>, and Jean Tourrilhes <jt@hpl.hp.com> * - * See Documentation/usb/usb-serial.txt for more information on using this driver + * See Documentation/usb/usb-serial.txt for more information on using this + * driver + * + * 2008_Jun_02 Felipe Balbi <me@felipebalbi.com> + * Introduced common header to be used also in USB Gadget Framework. + * Still needs some other style fixes. * * 2007_Jun_21 Alan Cox <alan@redhat.com> * Minimal cleanups for some of the driver problens and tty layer abuse. @@ -59,9 +64,10 @@ #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> +#include <linux/usb/irda.h> /* * Version Information @@ -70,100 +76,77 @@ #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>" #define DRIVER_DESC "USB IR Dongle driver" -/* USB IrDA class spec information */ -#define USB_CLASS_IRDA 0x02 -#define USB_DT_IRDA 0x21 -#define IU_REQ_GET_CLASS_DESC 0x06 -#define SPEED_2400 0x01 -#define SPEED_9600 0x02 -#define SPEED_19200 0x03 -#define SPEED_38400 0x04 -#define SPEED_57600 0x05 -#define SPEED_115200 0x06 -#define SPEED_576000 0x07 -#define SPEED_1152000 0x08 -#define SPEED_4000000 0x09 - -struct irda_class_desc { - u8 bLength; - u8 bDescriptorType; - u16 bcdSpecRevision; - u8 bmDataSize; - u8 bmWindowSize; - u8 bmMinTurnaroundTime; - u16 wBaudRate; - u8 bmAdditionalBOFs; - u8 bIrdaRateSniff; - u8 bMaxUnicastList; -} __attribute__ ((packed)); - static int debug; /* if overridden by the user, then use their value for the size of the read and * write urbs */ static int buffer_size; + /* if overridden by the user, then use the specified number of XBOFs */ static int xbof = -1; static int ir_startup (struct usb_serial *serial); -static int ir_open (struct usb_serial_port *port, struct file *filep); -static void ir_close (struct usb_serial_port *port, struct file *filep); -static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int count); +static int ir_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filep); +static void ir_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filep); +static int ir_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); static void ir_write_bulk_callback (struct urb *urb); static void ir_read_bulk_callback (struct urb *urb); -static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_termios); +static void ir_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios); /* Not that this lot means you can only have one per system */ -static u8 ir_baud = 0; -static u8 ir_xbof = 0; -static u8 ir_add_bof = 0; +static u8 ir_baud; +static u8 ir_xbof; +static u8 ir_add_bof; -static struct usb_device_id id_table [] = { +static struct usb_device_id ir_id_table[] = { { USB_DEVICE(0x050f, 0x0180) }, /* KC Technology, KC-180 */ { USB_DEVICE(0x08e9, 0x0100) }, /* XTNDAccess */ { USB_DEVICE(0x09c4, 0x0011) }, /* ACTiSys ACT-IR2000U */ - { USB_INTERFACE_INFO (USB_CLASS_APP_SPEC, USB_CLASS_IRDA, 0) }, + { USB_INTERFACE_INFO(USB_CLASS_APP_SPEC, USB_SUBCLASS_IRDA, 0) }, { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, ir_id_table); static struct usb_driver ir_driver = { - .name = "ir-usb", - .probe = usb_serial_probe, - .disconnect = usb_serial_disconnect, - .id_table = id_table, - .no_dynamic_id = 1, + .name = "ir-usb", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = ir_id_table, + .no_dynamic_id = 1, }; - static struct usb_serial_driver ir_device = { - .driver = { - .owner = THIS_MODULE, - .name = "ir-usb", + .driver = { + .owner = THIS_MODULE, + .name = "ir-usb", }, - .description = "IR Dongle", - .usb_driver = &ir_driver, - .id_table = id_table, - .num_ports = 1, - .set_termios = ir_set_termios, - .attach = ir_startup, - .open = ir_open, - .close = ir_close, - .write = ir_write, - .write_bulk_callback = ir_write_bulk_callback, - .read_bulk_callback = ir_read_bulk_callback, + .description = "IR Dongle", + .usb_driver = &ir_driver, + .id_table = ir_id_table, + .num_ports = 1, + .set_termios = ir_set_termios, + .attach = ir_startup, + .open = ir_open, + .close = ir_close, + .write = ir_write, + .write_bulk_callback = ir_write_bulk_callback, + .read_bulk_callback = ir_read_bulk_callback, }; -static inline void irda_usb_dump_class_desc(struct irda_class_desc *desc) +static inline void irda_usb_dump_class_desc(struct usb_irda_cs_descriptor *desc) { dbg("bLength=%x", desc->bLength); dbg("bDescriptorType=%x", desc->bDescriptorType); - dbg("bcdSpecRevision=%x", desc->bcdSpecRevision); + dbg("bcdSpecRevision=%x", __le16_to_cpu(desc->bcdSpecRevision)); dbg("bmDataSize=%x", desc->bmDataSize); dbg("bmWindowSize=%x", desc->bmWindowSize); dbg("bmMinTurnaroundTime=%d", desc->bmMinTurnaroundTime); - dbg("wBaudRate=%x", desc->wBaudRate); + dbg("wBaudRate=%x", __le16_to_cpu(desc->wBaudRate)); dbg("bmAdditionalBOFs=%x", desc->bmAdditionalBOFs); dbg("bIrdaRateSniff=%x", desc->bIrdaRateSniff); dbg("bMaxUnicastList=%x", desc->bMaxUnicastList); @@ -181,35 +164,37 @@ static inline void irda_usb_dump_class_desc(struct irda_class_desc *desc) * * Based on the same function in drivers/net/irda/irda-usb.c */ -static struct irda_class_desc *irda_usb_find_class_desc(struct usb_device *dev, unsigned int ifnum) +static struct usb_irda_cs_descriptor * +irda_usb_find_class_desc(struct usb_device *dev, unsigned int ifnum) { - struct irda_class_desc *desc; + struct usb_irda_cs_descriptor *desc; int ret; - - desc = kzalloc(sizeof (struct irda_class_desc), GFP_KERNEL); - if (desc == NULL) + + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) return NULL; - - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev,0), - IU_REQ_GET_CLASS_DESC, + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_CS_IRDA_GET_CLASS_DESC, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, ifnum, desc, sizeof(*desc), 1000); - + dbg("%s - ret=%d", __func__, ret); if (ret < sizeof(*desc)) { dbg("%s - class descriptor read %s (%d)", __func__, - (ret<0) ? "failed" : "too short", + (ret < 0) ? "failed" : "too short", ret); goto error; } - if (desc->bDescriptorType != USB_DT_IRDA) { + if (desc->bDescriptorType != USB_DT_CS_IRDA) { dbg("%s - bad class descriptor type", __func__); goto error; } - + irda_usb_dump_class_desc(desc); return desc; + error: kfree(desc); return NULL; @@ -219,64 +204,101 @@ error: static u8 ir_xbof_change(u8 xbof) { u8 result; + /* reference irda-usb.c */ - switch(xbof) { - case 48: result = 0x10; break; - case 28: - case 24: result = 0x20; break; - default: - case 12: result = 0x30; break; - case 5: - case 6: result = 0x40; break; - case 3: result = 0x50; break; - case 2: result = 0x60; break; - case 1: result = 0x70; break; - case 0: result = 0x80; break; + switch (xbof) { + case 48: + result = 0x10; + break; + case 28: + case 24: + result = 0x20; + break; + default: + case 12: + result = 0x30; + break; + case 5: + case 6: + result = 0x40; + break; + case 3: + result = 0x50; + break; + case 2: + result = 0x60; + break; + case 1: + result = 0x70; + break; + case 0: + result = 0x80; + break; } + return(result); } -static int ir_startup (struct usb_serial *serial) +static int ir_startup(struct usb_serial *serial) { - struct irda_class_desc *irda_desc; + struct usb_irda_cs_descriptor *irda_desc; - irda_desc = irda_usb_find_class_desc (serial->dev, 0); - if (irda_desc == NULL) { - dev_err (&serial->dev->dev, "IRDA class descriptor not found, device not bound\n"); + irda_desc = irda_usb_find_class_desc(serial->dev, 0); + if (!irda_desc) { + dev_err(&serial->dev->dev, + "IRDA class descriptor not found, device not bound\n"); return -ENODEV; } - dbg ("%s - Baud rates supported:%s%s%s%s%s%s%s%s%s", + dbg("%s - Baud rates supported:%s%s%s%s%s%s%s%s%s", __func__, - (irda_desc->wBaudRate & 0x0001) ? " 2400" : "", - (irda_desc->wBaudRate & 0x0002) ? " 9600" : "", - (irda_desc->wBaudRate & 0x0004) ? " 19200" : "", - (irda_desc->wBaudRate & 0x0008) ? " 38400" : "", - (irda_desc->wBaudRate & 0x0010) ? " 57600" : "", - (irda_desc->wBaudRate & 0x0020) ? " 115200" : "", - (irda_desc->wBaudRate & 0x0040) ? " 576000" : "", - (irda_desc->wBaudRate & 0x0080) ? " 1152000" : "", - (irda_desc->wBaudRate & 0x0100) ? " 4000000" : ""); - - switch( irda_desc->bmAdditionalBOFs ) { - case 0x01: ir_add_bof = 48; break; - case 0x02: ir_add_bof = 24; break; - case 0x04: ir_add_bof = 12; break; - case 0x08: ir_add_bof = 6; break; - case 0x10: ir_add_bof = 3; break; - case 0x20: ir_add_bof = 2; break; - case 0x40: ir_add_bof = 1; break; - case 0x80: ir_add_bof = 0; break; - default:; + (irda_desc->wBaudRate & USB_IRDA_BR_2400) ? " 2400" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_9600) ? " 9600" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_19200) ? " 19200" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_38400) ? " 38400" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_57600) ? " 57600" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_115200) ? " 115200" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_576000) ? " 576000" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_1152000) ? " 1152000" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_4000000) ? " 4000000" : ""); + + switch (irda_desc->bmAdditionalBOFs) { + case USB_IRDA_AB_48: + ir_add_bof = 48; + break; + case USB_IRDA_AB_24: + ir_add_bof = 24; + break; + case USB_IRDA_AB_12: + ir_add_bof = 12; + break; + case USB_IRDA_AB_6: + ir_add_bof = 6; + break; + case USB_IRDA_AB_3: + ir_add_bof = 3; + break; + case USB_IRDA_AB_2: + ir_add_bof = 2; + break; + case USB_IRDA_AB_1: + ir_add_bof = 1; + break; + case USB_IRDA_AB_0: + ir_add_bof = 0; + break; + default: + break; } - kfree (irda_desc); + kfree(irda_desc); - return 0; + return 0; } -static int ir_open (struct usb_serial_port *port, struct file *filp) +static int ir_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { char *buffer; int result = 0; @@ -285,51 +307,56 @@ static int ir_open (struct usb_serial_port *port, struct file *filp) if (buffer_size) { /* override the default buffer sizes */ - buffer = kmalloc (buffer_size, GFP_KERNEL); + buffer = kmalloc(buffer_size, GFP_KERNEL); if (!buffer) { - dev_err (&port->dev, "%s - out of memory.\n", __func__); + dev_err(&port->dev, "%s - out of memory.\n", __func__); return -ENOMEM; } - kfree (port->read_urb->transfer_buffer); + kfree(port->read_urb->transfer_buffer); port->read_urb->transfer_buffer = buffer; port->read_urb->transfer_buffer_length = buffer_size; - buffer = kmalloc (buffer_size, GFP_KERNEL); + buffer = kmalloc(buffer_size, GFP_KERNEL); if (!buffer) { - dev_err (&port->dev, "%s - out of memory.\n", __func__); + dev_err(&port->dev, "%s - out of memory.\n", __func__); return -ENOMEM; } - kfree (port->write_urb->transfer_buffer); + kfree(port->write_urb->transfer_buffer); port->write_urb->transfer_buffer = buffer; port->write_urb->transfer_buffer_length = buffer_size; port->bulk_out_size = buffer_size; } /* Start reading from the device */ - usb_fill_bulk_urb ( + usb_fill_bulk_urb( port->read_urb, - port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), + port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress), port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, ir_read_bulk_callback, port); result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result) - dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __func__, result); + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", + __func__, result); return result; } -static void ir_close (struct usb_serial_port *port, struct file * filp) +static void ir_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { dbg("%s - port %d", __func__, port->number); - + /* shutdown our bulk read */ usb_kill_urb(port->read_urb); } -static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int count) +static int ir_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { unsigned char *transfer_buffer; int result; @@ -337,11 +364,6 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int dbg("%s - port = %d, count = %d", __func__, port->number, count); - if (!port->tty) { - dev_err (&port->dev, "%s - no tty???\n", __func__); - return 0; - } - if (count == 0) return 0; @@ -359,7 +381,7 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int /* * The first byte of the packet we send to the device contains an - * inband header which indicates an additional number of BOFs and + * inbound header which indicates an additional number of BOFs and * a baud rate change. * * See section 5.4.2.2 of the USB IrDA spec. @@ -367,9 +389,9 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int *transfer_buffer = ir_xbof | ir_baud; ++transfer_buffer; - memcpy (transfer_buffer, buf, transfer_size); + memcpy(transfer_buffer, buf, transfer_size); - usb_fill_bulk_urb ( + usb_fill_bulk_urb( port->write_urb, port->serial->dev, usb_sndbulkpipe(port->serial->dev, @@ -381,17 +403,19 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int port->write_urb->transfer_flags = URB_ZERO_PACKET; - result = usb_submit_urb (port->write_urb, GFP_ATOMIC); + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result) { port->write_urb_busy = 0; - dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __func__, result); + dev_err(&port->dev, + "%s - failed submitting write urb, error %d\n", + __func__, result); } else result = transfer_size; return result; } -static void ir_write_bulk_callback (struct urb *urb) +static void ir_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; int status = urb->status; @@ -405,7 +429,7 @@ static void ir_write_bulk_callback (struct urb *urb) return; } - usb_serial_debug_data ( + usb_serial_debug_data( debug, &port->dev, __func__, @@ -415,7 +439,7 @@ static void ir_write_bulk_callback (struct urb *urb) usb_serial_port_softint(port); } -static void ir_read_bulk_callback (struct urb *urb) +static void ir_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct tty_struct *tty; @@ -425,74 +449,61 @@ static void ir_read_bulk_callback (struct urb *urb) dbg("%s - port %d", __func__, port->number); - if (!port->open_count) { + if (!port->port.count) { dbg("%s - port closed.", __func__); return; } switch (status) { - case 0: /* Successful */ - - /* - * The first byte of the packet we get from the device - * contains a busy indicator and baud rate change. - * See section 5.4.1.2 of the USB IrDA spec. - */ - if ((*data & 0x0f) > 0) - ir_baud = *data & 0x0f; - - usb_serial_debug_data ( - debug, - &port->dev, - __func__, - urb->actual_length, - data); - - tty = port->tty; - - if (tty_buffer_request_room(tty, urb->actual_length - 1)) { - tty_insert_flip_string(tty, data+1, urb->actual_length - 1); - tty_flip_buffer_push(tty); - } - - /* - * No break here. - * We want to resubmit the urb so we can read - * again. - */ + case 0: /* Successful */ + /* + * The first byte of the packet we get from the device + * contains a busy indicator and baud rate change. + * See section 5.4.1.2 of the USB IrDA spec. + */ + if ((*data & 0x0f) > 0) + ir_baud = *data & 0x0f; + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, data); + tty = port->port.tty; + if (tty_buffer_request_room(tty, urb->actual_length - 1)) { + tty_insert_flip_string(tty, data+1, urb->actual_length - 1); + tty_flip_buffer_push(tty); + } - case -EPROTO: /* taking inspiration from pl2303.c */ + /* + * No break here. + * We want to resubmit the urb so we can read + * again. + */ + case -EPROTO: /* taking inspiration from pl2303.c */ /* Continue trying to always read */ - usb_fill_bulk_urb ( - port->read_urb, - port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - ir_read_bulk_callback, - port); - - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", - __func__, result); - - break ; - - default: - dbg("%s - nonzero read bulk status received: %d", - __func__, - status); + usb_fill_bulk_urb( + port->read_urb, + port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, + port->read_urb->transfer_buffer_length, + ir_read_bulk_callback, + port); + + result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + if (result) + dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", + __func__, result); break ; - + default: + dbg("%s - nonzero read bulk status received: %d", + __func__, status); + break ; } - return; } -static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) +static void ir_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { unsigned char *transfer_buffer; int result; @@ -501,7 +512,7 @@ static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_t dbg("%s - port %d", __func__, port->number); - baud = tty_get_baud_rate(port->tty); + baud = tty_get_baud_rate(tty); /* * FIXME, we should compare the baud request against the @@ -510,19 +521,36 @@ static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_t */ switch (baud) { - case 2400: ir_baud = SPEED_2400; break; - case 9600: ir_baud = SPEED_9600; break; - case 19200: ir_baud = SPEED_19200; break; - case 38400: ir_baud = SPEED_38400; break; - case 57600: ir_baud = SPEED_57600; break; - case 115200: ir_baud = SPEED_115200; break; - case 576000: ir_baud = SPEED_576000; break; - case 1152000: ir_baud = SPEED_1152000; break; - case 4000000: ir_baud = SPEED_4000000; break; - break; - default: - ir_baud = SPEED_9600; - baud = 9600; + case 2400: + ir_baud = USB_IRDA_BR_2400; + break; + case 9600: + ir_baud = USB_IRDA_BR_9600; + break; + case 19200: + ir_baud = USB_IRDA_BR_19200; + break; + case 38400: + ir_baud = USB_IRDA_BR_38400; + break; + case 57600: + ir_baud = USB_IRDA_BR_57600; + break; + case 115200: + ir_baud = USB_IRDA_BR_115200; + break; + case 576000: + ir_baud = USB_IRDA_BR_576000; + break; + case 1152000: + ir_baud = USB_IRDA_BR_1152000; + break; + case 4000000: + ir_baud = USB_IRDA_BR_4000000; + break; + default: + ir_baud = USB_IRDA_BR_9600; + baud = 9600; } if (xbof == -1) @@ -538,10 +566,11 @@ static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_t transfer_buffer = port->write_urb->transfer_buffer; *transfer_buffer = ir_xbof | ir_baud; - usb_fill_bulk_urb ( + usb_fill_bulk_urb( port->write_urb, port->serial->dev, - usb_sndbulkpipe(port->serial->dev, port->bulk_out_endpointAddress), + usb_sndbulkpipe(port->serial->dev, + port->bulk_out_endpointAddress), port->write_urb->transfer_buffer, 1, ir_write_bulk_callback, @@ -549,38 +578,44 @@ static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_t port->write_urb->transfer_flags = URB_ZERO_PACKET; - result = usb_submit_urb (port->write_urb, GFP_KERNEL); + result = usb_submit_urb(port->write_urb, GFP_KERNEL); if (result) - dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __func__, result); + dev_err(&port->dev, + "%s - failed submitting write urb, error %d\n", + __func__, result); /* Only speed changes are supported */ - tty_termios_copy_hw(port->tty->termios, old_termios); - tty_encode_baud_rate(port->tty, baud, baud); + tty_termios_copy_hw(tty->termios, old_termios); + tty_encode_baud_rate(tty, baud, baud); } - -static int __init ir_init (void) +static int __init ir_init(void) { int retval; + retval = usb_serial_register(&ir_device); if (retval) goto failed_usb_serial_register; + retval = usb_register(&ir_driver); - if (retval) + if (retval) goto failed_usb_register; + info(DRIVER_DESC " " DRIVER_VERSION); + return 0; + failed_usb_register: usb_serial_deregister(&ir_device); + failed_usb_serial_register: return retval; } - -static void __exit ir_exit (void) +static void __exit ir_exit(void) { - usb_deregister (&ir_driver); - usb_serial_deregister (&ir_device); + usb_deregister(&ir_driver); + usb_serial_deregister(&ir_device); } diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index a01e987c7d32..ddff37fa6339 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -144,9 +144,10 @@ static void iuu_shutdown(struct usb_serial *serial) } } -static int iuu_tiocmset(struct usb_serial_port *port, struct file *file, +static int iuu_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct iuu_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -171,8 +172,9 @@ static int iuu_tiocmset(struct usb_serial_port *port, struct file *file, * When no card , the reader respond with TIOCM_CD * This is known as CD autodetect mechanism */ -static int iuu_tiocmget(struct usb_serial_port *port, struct file *file) +static int iuu_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct iuu_private *priv = usb_get_serial_port_data(port); unsigned long flags; int rc; @@ -316,11 +318,10 @@ static int bulk_immediate(struct usb_serial_port *port, u8 *buf, u8 count) port->bulk_out_endpointAddress), buf, count, &actual, HZ * 1); - if (status != IUU_OPERATION_OK) { + if (status != IUU_OPERATION_OK) dbg("%s - error = %2x", __func__, status); - } else { + else dbg("%s - write OK !", __func__); - } return status; } @@ -340,12 +341,10 @@ static int read_immediate(struct usb_serial_port *port, u8 *buf, u8 count) port->bulk_in_endpointAddress), buf, count, &actual, HZ * 1); - if (status != IUU_OPERATION_OK) { + if (status != IUU_OPERATION_OK) dbg("%s - error = %2x", __func__, status); - } else { + else dbg("%s - read OK !", __func__); - } - return status; } @@ -630,7 +629,7 @@ static void read_buf_callback(struct urb *urb) } dbg("%s - %i chars to write", __func__, urb->actual_length); - tty = port->tty; + tty = port->port.tty; if (data == NULL) dbg("%s - data is NULL !!!", __func__); if (tty && urb->actual_length && data) { @@ -752,11 +751,10 @@ static void iuu_uart_read_callback(struct urb *urb) /* if nothing to write call again rxcmd */ dbg("%s - rxcmd recall", __func__); iuu_led_activity_off(urb); - return; } -static int iuu_uart_write(struct usb_serial_port *port, const u8 *buf, - int count) +static int iuu_uart_write(struct tty_struct *tty, struct usb_serial_port *port, + const u8 *buf, int count) { struct iuu_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -769,14 +767,14 @@ static int iuu_uart_write(struct usb_serial_port *port, const u8 *buf, if (priv->writelen > 0) { /* buffer already filled but not commited */ spin_unlock_irqrestore(&priv->lock, flags); - return (0); + return 0; } /* fill the buffer */ memcpy(priv->writebuf, buf, count); priv->writelen = count; spin_unlock_irqrestore(&priv->lock, flags); - return (count); + return count; } static void read_rxcmd_callback(struct urb *urb) @@ -948,7 +946,8 @@ static int set_control_lines(struct usb_device *dev, u8 value) return 0; } -static void iuu_close(struct usb_serial_port *port, struct file *filp) +static void iuu_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { /* iuu_led (port,255,0,0,0); */ struct usb_serial *serial; @@ -964,8 +963,8 @@ static void iuu_close(struct usb_serial_port *port, struct file *filp) iuu_uart_off(port); if (serial->dev) { - if (port->tty) { - c_cflag = port->tty->termios->c_cflag; + if (tty) { + c_cflag = tty->termios->c_cflag; if (c_cflag & HUPCL) { /* drop DTR and RTS */ priv = usb_get_serial_port_data(port); @@ -989,7 +988,8 @@ static void iuu_close(struct usb_serial_port *port, struct file *filp) } } -static int iuu_open(struct usb_serial_port *port, struct file *filp) +static int iuu_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; u8 *buf; @@ -1036,15 +1036,17 @@ static int iuu_open(struct usb_serial_port *port, struct file *filp) /* set the termios structure */ spin_lock_irqsave(&priv->lock, flags); - if (!priv->termios_initialized) { - *(port->tty->termios) = tty_std_termios; - port->tty->termios->c_cflag = CLOCAL | CREAD | CS8 | B9600 - | TIOCM_CTS | CSTOPB | PARENB; - port->tty->termios->c_lflag = 0; - port->tty->termios->c_oflag = 0; - port->tty->termios->c_iflag = 0; + if (tty && !priv->termios_initialized) { + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = CLOCAL | CREAD | CS8 | B9600 + | TIOCM_CTS | CSTOPB | PARENB; + tty->termios->c_ispeed = 9600; + tty->termios->c_ospeed = 9600; + tty->termios->c_lflag = 0; + tty->termios->c_oflag = 0; + tty->termios->c_iflag = 0; priv->termios_initialized = 1; - port->tty->low_latency = 1; + tty->low_latency = 1; priv->poll = 0; } spin_unlock_irqrestore(&priv->lock, flags); @@ -1148,7 +1150,7 @@ static int iuu_open(struct usb_serial_port *port, struct file *filp) if (result) { dev_err(&port->dev, "%s - failed submitting read urb," " error %d\n", __func__, result); - iuu_close(port, NULL); + iuu_close(tty, port, NULL); return -EPROTO; } else { dbg("%s - rxcmd OK", __func__); diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 11e439b90eac..704716f6f6d3 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -1,29 +1,29 @@ /* Keyspan USB to Serial Converter driver - + (C) Copyright (C) 2000-2001 Hugh Blemings <hugh@blemings.org> (C) Copyright (C) 2002 Greg Kroah-Hartman <greg@kroah.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. See http://misc.nu/hugh/keyspan.html for more information. - + Code in this driver inspired by and in a number of places taken from Brian Warner's original Keyspan-PDA driver. This driver has been put together with the support of Innosys, Inc. and Keyspan, Inc the manufacturers of the Keyspan USB-serial products. Thanks Guys :) - + Thanks to Paulus for miscellaneous tidy ups, some largish chunks of much nicer and/or completely new code and (perhaps most uniquely) having the patience to sit down and explain why and where he'd changed - stuff. - - Tip 'o the hat to IBM (and previously Linuxcare :) for supporting + stuff. + + Tip 'o the hat to IBM (and previously Linuxcare :) for supporting staff in their work on open source projects. Change History @@ -70,21 +70,21 @@ Thu May 31 11:56:42 PDT 2001 gkh switched from using spinlock to a semaphore - + (04/08/2001) gb Identify version on module load. - + (11/01/2000) Adam J. Richter usb_device_id table support. - + Tue Oct 10 23:15:33 EST 2000 Hugh Merged Paul's changes with my USA-49W mods. Work in progress still... - + Wed Jul 19 14:00:42 EST 2000 gkh Added module_init and module_exit functions to handle the fact that this driver is a loadable module now. - + Tue Jul 18 16:14:52 EST 2000 Hugh Basic character input/output for USA-19 now mostly works, fixed at 9600 baud for the moment. @@ -107,7 +107,7 @@ #include <linux/spinlock.h> #include <linux/firmware.h> #include <linux/ihex.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include "keyspan.h" @@ -132,15 +132,15 @@ struct keyspan_serial_private { struct urb *instat_urb; char instat_buf[INSTAT_BUFLEN]; - /* added to support 49wg, where data from all 4 ports comes in on 1 EP */ - /* and high-speed supported */ + /* added to support 49wg, where data from all 4 ports comes in + on 1 EP and high-speed supported */ struct urb *indat_urb; char indat_buf[INDAT49W_BUFLEN]; /* XXX this one probably will need a lock */ struct urb *glocont_urb; char glocont_buf[GLOCONT_BUFLEN]; - char ctrl_buf[8]; // for EP0 control message + char ctrl_buf[8]; /* for EP0 control message */ }; struct keyspan_port_private { @@ -186,19 +186,19 @@ struct keyspan_port_private { int resend_cont; /* need to resend control packet */ }; - /* Include Keyspan message headers. All current Keyspan Adapters make use of one of five message formats which are referred - to as USA-26, USA-28, USA-49, USA-90, USA-67 by Keyspan and within this driver. */ + to as USA-26, USA-28, USA-49, USA-90, USA-67 by Keyspan and + within this driver. */ #include "keyspan_usa26msg.h" #include "keyspan_usa28msg.h" #include "keyspan_usa49msg.h" #include "keyspan_usa90msg.h" #include "keyspan_usa67msg.h" - + /* Functions used by new usb-serial code. */ -static int __init keyspan_init (void) +static int __init keyspan_init(void) { int retval; retval = usb_serial_register(&keyspan_pre_device); @@ -214,7 +214,7 @@ static int __init keyspan_init (void) if (retval) goto failed_4port_device_register; retval = usb_register(&keyspan_driver); - if (retval) + if (retval) goto failed_usb_register; info(DRIVER_VERSION ":" DRIVER_DESC); @@ -232,35 +232,24 @@ failed_pre_device_register: return retval; } -static void __exit keyspan_exit (void) +static void __exit keyspan_exit(void) { - usb_deregister (&keyspan_driver); - usb_serial_deregister (&keyspan_pre_device); - usb_serial_deregister (&keyspan_1port_device); - usb_serial_deregister (&keyspan_2port_device); - usb_serial_deregister (&keyspan_4port_device); + usb_deregister(&keyspan_driver); + usb_serial_deregister(&keyspan_pre_device); + usb_serial_deregister(&keyspan_1port_device); + usb_serial_deregister(&keyspan_2port_device); + usb_serial_deregister(&keyspan_4port_device); } module_init(keyspan_init); module_exit(keyspan_exit); -static void keyspan_rx_throttle (struct usb_serial_port *port) -{ - dbg("%s - port %d", __func__, port->number); -} - - -static void keyspan_rx_unthrottle (struct usb_serial_port *port) -{ - dbg("%s - port %d", __func__, port->number); -} - - -static void keyspan_break_ctl (struct usb_serial_port *port, int break_state) +static void keyspan_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct keyspan_port_private *p_priv; - dbg("%s", __func__); + dbg("%s", __func__); p_priv = usb_get_serial_port_data(port); @@ -273,14 +262,13 @@ static void keyspan_break_ctl (struct usb_serial_port *port, int break_state) } -static void keyspan_set_termios (struct usb_serial_port *port, - struct ktermios *old_termios) +static void keyspan_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { int baud_rate, device_port; struct keyspan_port_private *p_priv; const struct keyspan_device_details *d_details; unsigned int cflag; - struct tty_struct *tty = port->tty; dbg("%s", __func__); @@ -292,7 +280,7 @@ static void keyspan_set_termios (struct usb_serial_port *port, /* Baud rate calculation takes baud rate as an integer so other rates can be generated if desired. */ baud_rate = tty_get_baud_rate(tty); - /* If no match or invalid, don't change */ + /* If no match or invalid, don't change */ if (d_details->calculate_baud_rate(baud_rate, d_details->baudclk, NULL, NULL, NULL, device_port) == KEYSPAN_BAUD_RATE_OK) { /* FIXME - more to do here to ensure rate changes cleanly */ @@ -312,35 +300,32 @@ static void keyspan_set_termios (struct usb_serial_port *port, keyspan_send_setup(port, 0); } -static int keyspan_tiocmget(struct usb_serial_port *port, struct file *file) +static int keyspan_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; + struct keyspan_port_private *p_priv = usb_get_serial_port_data(port); unsigned int value; - struct keyspan_port_private *p_priv; - p_priv = usb_get_serial_port_data(port); - value = ((p_priv->rts_state) ? TIOCM_RTS : 0) | ((p_priv->dtr_state) ? TIOCM_DTR : 0) | ((p_priv->cts_state) ? TIOCM_CTS : 0) | ((p_priv->dsr_state) ? TIOCM_DSR : 0) | ((p_priv->dcd_state) ? TIOCM_CAR : 0) | - ((p_priv->ri_state) ? TIOCM_RNG : 0); + ((p_priv->ri_state) ? TIOCM_RNG : 0); return value; } -static int keyspan_tiocmset(struct usb_serial_port *port, struct file *file, +static int keyspan_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { - struct keyspan_port_private *p_priv; + struct usb_serial_port *port = tty->driver_data; + struct keyspan_port_private *p_priv = usb_get_serial_port_data(port); - p_priv = usb_get_serial_port_data(port); - if (set & TIOCM_RTS) p_priv->rts_state = 1; if (set & TIOCM_DTR) p_priv->dtr_state = 1; - if (clear & TIOCM_RTS) p_priv->rts_state = 0; if (clear & TIOCM_DTR) @@ -349,35 +334,29 @@ static int keyspan_tiocmset(struct usb_serial_port *port, struct file *file, return 0; } -static int keyspan_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return -ENOIOCTLCMD; -} - - /* Write function is similar for the four protocols used - with only a minor change for usa90 (usa19hs) required */ -static int keyspan_write(struct usb_serial_port *port, - const unsigned char *buf, int count) +/* Write function is similar for the four protocols used + with only a minor change for usa90 (usa19hs) required */ +static int keyspan_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count) { struct keyspan_port_private *p_priv; const struct keyspan_device_details *d_details; int flip; int left, todo; struct urb *this_urb; - int err, maxDataLen, dataOffset; + int err, maxDataLen, dataOffset; p_priv = usb_get_serial_port_data(port); d_details = p_priv->device_details; if (d_details->msg_format == msg_usa90) { - maxDataLen = 64; + maxDataLen = 64; dataOffset = 0; } else { maxDataLen = 63; dataOffset = 1; } - + dbg("%s - for port %d (%d chars), flip=%d", __func__, port->number, count, p_priv->out_flip); @@ -387,37 +366,40 @@ static int keyspan_write(struct usb_serial_port *port, todo = maxDataLen; flip = p_priv->out_flip; - + /* Check we have a valid urb/endpoint before we use it... */ - if ((this_urb = p_priv->out_urbs[flip]) == NULL) { + this_urb = p_priv->out_urbs[flip]; + if (this_urb == NULL) { /* no bulk out, so return 0 bytes written */ dbg("%s - no output urb :(", __func__); return count; } - dbg("%s - endpoint %d flip %d", __func__, usb_pipeendpoint(this_urb->pipe), flip); + dbg("%s - endpoint %d flip %d", + __func__, usb_pipeendpoint(this_urb->pipe), flip); if (this_urb->status == -EINPROGRESS) { - if (time_before(jiffies, p_priv->tx_start_time[flip] + 10 * HZ)) + if (time_before(jiffies, + p_priv->tx_start_time[flip] + 10 * HZ)) break; usb_unlink_urb(this_urb); break; } - /* First byte in buffer is "last flag" (except for usa19hx) - unused so - for now so set to zero */ + /* First byte in buffer is "last flag" (except for usa19hx) + - unused so for now so set to zero */ ((char *)this_urb->transfer_buffer)[0] = 0; - memcpy (this_urb->transfer_buffer + dataOffset, buf, todo); + memcpy(this_urb->transfer_buffer + dataOffset, buf, todo); buf += todo; /* send the data out the bulk port */ this_urb->transfer_buffer_length = todo + dataOffset; this_urb->dev = port->serial->dev; - if ((err = usb_submit_urb(this_urb, GFP_ATOMIC)) != 0) { + err = usb_submit_urb(this_urb, GFP_ATOMIC); + if (err != 0) dbg("usb_submit_urb(write bulk) failed (%d)", err); - } p_priv->tx_start_time[flip] = jiffies; /* Flip for next time if usa26 or usa28 interface @@ -437,7 +419,7 @@ static void usa26_indat_callback(struct urb *urb) unsigned char *data = urb->transfer_buffer; int status = urb->status; - dbg ("%s", __func__); + dbg("%s", __func__); endpoint = usb_pipeendpoint(urb->pipe); @@ -448,17 +430,18 @@ static void usa26_indat_callback(struct urb *urb) } port = urb->context; - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { /* 0x80 bit is error flag */ if ((data[0] & 0x80) == 0) { - /* no errors on individual bytes, only possible overrun err*/ + /* no errors on individual bytes, only + possible overrun err */ if (data[0] & RXERROR_OVERRUN) - err = TTY_OVERRUN; - else err = 0; - for (i = 1; i < urb->actual_length ; ++i) { + err = TTY_OVERRUN; + else + err = 0; + for (i = 1; i < urb->actual_length ; ++i) tty_insert_flip_char(tty, data[i], err); - } } else { /* some bytes had errors, every byte has status */ dbg("%s - RX error!!!!", __func__); @@ -476,17 +459,19 @@ static void usa26_indat_callback(struct urb *urb) } tty_flip_buffer_push(tty); } - - /* Resubmit urb so we continue receiving */ + + /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; - if (port->open_count) - if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { - dbg("%s - resubmit read urb failed. (%d)", __func__, err); - } + if (port->port.count) { + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err != 0) + dbg("%s - resubmit read urb failed. (%d)", + __func__, err); + } return; } - /* Outdat handling is common for all devices */ +/* Outdat handling is common for all devices */ static void usa2x_outdat_callback(struct urb *urb) { struct usb_serial_port *port; @@ -494,16 +479,16 @@ static void usa2x_outdat_callback(struct urb *urb) port = urb->context; p_priv = usb_get_serial_port_data(port); - dbg ("%s - urb %d", __func__, urb == p_priv->out_urbs[1]); + dbg("%s - urb %d", __func__, urb == p_priv->out_urbs[1]); - if (port->open_count) + if (port->port.count) usb_serial_port_softint(port); } static void usa26_inack_callback(struct urb *urb) { - dbg ("%s", __func__); - + dbg("%s", __func__); + } static void usa26_outcont_callback(struct urb *urb) @@ -515,8 +500,9 @@ static void usa26_outcont_callback(struct urb *urb) p_priv = usb_get_serial_port_data(port); if (p_priv->resend_cont) { - dbg ("%s - sending setup", __func__); - keyspan_usa26_send_setup(port->serial, port, p_priv->resend_cont - 1); + dbg("%s - sending setup", __func__); + keyspan_usa26_send_setup(port->serial, port, + p_priv->resend_cont - 1); } } @@ -552,14 +538,14 @@ static void usa26_instat_callback(struct urb *urb) /* Now do something useful with the data */ - /* Check port number from message and retrieve private data */ + /* Check port number from message and retrieve private data */ if (msg->port >= serial->num_ports) { - dbg ("%s - Unexpected port number %d", __func__, msg->port); + dbg("%s - Unexpected port number %d", __func__, msg->port); goto exit; } port = serial->port[msg->port]; p_priv = usb_get_serial_port_data(port); - + /* Update handshaking pin state information */ old_dcd_state = p_priv->dcd_state; p_priv->cts_state = ((msg->hskia_cts) ? 1 : 0); @@ -567,39 +553,38 @@ static void usa26_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state != p_priv->dcd_state) { if (old_dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); /* else */ /* wake_up_interruptible(&p_priv->open_wait); */ } - + /* Resubmit urb so we continue receiving */ urb->dev = serial->dev; - if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err != 0) dbg("%s - resubmit read urb failed. (%d)", __func__, err); - } exit: ; } static void usa26_glocont_callback(struct urb *urb) { - dbg ("%s", __func__); - + dbg("%s", __func__); } static void usa28_indat_callback(struct urb *urb) { - int i, err; + int err; struct usb_serial_port *port; struct tty_struct *tty; unsigned char *data; struct keyspan_port_private *p_priv; int status = urb->status; - dbg ("%s", __func__); + dbg("%s", __func__); port = urb->context; p_priv = usb_get_serial_port_data(port); @@ -619,20 +604,20 @@ static void usa28_indat_callback(struct urb *urb) p_priv = usb_get_serial_port_data(port); data = urb->transfer_buffer; - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { - for (i = 0; i < urb->actual_length ; ++i) { - tty_insert_flip_char(tty, data[i], 0); - } + tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; - if (port->open_count) - if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { - dbg("%s - resubmit read urb failed. (%d)", __func__, err); - } + if (port->port.count) { + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err != 0) + dbg("%s - resubmit read urb failed. (%d)", + __func__, err); + } p_priv->in_flip ^= 1; urb = p_priv->in_urbs[p_priv->in_flip]; @@ -641,7 +626,7 @@ static void usa28_indat_callback(struct urb *urb) static void usa28_inack_callback(struct urb *urb) { - dbg ("%s", __func__); + dbg("%s", __func__); } static void usa28_outcont_callback(struct urb *urb) @@ -653,8 +638,9 @@ static void usa28_outcont_callback(struct urb *urb) p_priv = usb_get_serial_port_data(port); if (p_priv->resend_cont) { - dbg ("%s - sending setup", __func__); - keyspan_usa28_send_setup(port->serial, port, p_priv->resend_cont - 1); + dbg("%s - sending setup", __func__); + keyspan_usa28_send_setup(port->serial, port, + p_priv->resend_cont - 1); } } @@ -684,19 +670,18 @@ static void usa28_instat_callback(struct urb *urb) /*dbg("%s %x %x %x %x %x %x %x %x %x %x %x %x", __func__ data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11]);*/ - - /* Now do something useful with the data */ - msg = (struct keyspan_usa28_portStatusMessage *)data; + /* Now do something useful with the data */ + msg = (struct keyspan_usa28_portStatusMessage *)data; - /* Check port number from message and retrieve private data */ + /* Check port number from message and retrieve private data */ if (msg->port >= serial->num_ports) { - dbg ("%s - Unexpected port number %d", __func__, msg->port); + dbg("%s - Unexpected port number %d", __func__, msg->port); goto exit; } port = serial->port[msg->port]; p_priv = usb_get_serial_port_data(port); - + /* Update handshaking pin state information */ old_dcd_state = p_priv->dcd_state; p_priv->cts_state = ((msg->cts) ? 1 : 0); @@ -704,25 +689,25 @@ static void usa28_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state != p_priv->dcd_state) { if (old_dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); /* else */ /* wake_up_interruptible(&p_priv->open_wait); */ } /* Resubmit urb so we continue receiving */ urb->dev = serial->dev; - if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err != 0) dbg("%s - resubmit read urb failed. (%d)", __func__, err); - } exit: ; } static void usa28_glocont_callback(struct urb *urb) { - dbg ("%s", __func__); + dbg("%s", __func__); } @@ -733,7 +718,7 @@ static void usa49_glocont_callback(struct urb *urb) struct keyspan_port_private *p_priv; int i; - dbg ("%s", __func__); + dbg("%s", __func__); serial = urb->context; for (i = 0; i < serial->num_ports; ++i) { @@ -741,8 +726,9 @@ static void usa49_glocont_callback(struct urb *urb) p_priv = usb_get_serial_port_data(port); if (p_priv->resend_cont) { - dbg ("%s - sending setup", __func__); - keyspan_usa49_send_setup(serial, port, p_priv->resend_cont - 1); + dbg("%s - sending setup", __func__); + keyspan_usa49_send_setup(serial, port, + p_priv->resend_cont - 1); break; } } @@ -761,7 +747,7 @@ static void usa49_instat_callback(struct urb *urb) int old_dcd_state; int status = urb->status; - dbg ("%s", __func__); + dbg("%s", __func__); serial = urb->context; @@ -770,7 +756,8 @@ static void usa49_instat_callback(struct urb *urb) return; } - if (urb->actual_length != sizeof(struct keyspan_usa49_portStatusMessage)) { + if (urb->actual_length != + sizeof(struct keyspan_usa49_portStatusMessage)) { dbg("%s - bad length %d", __func__, urb->actual_length); goto exit; } @@ -778,18 +765,19 @@ static void usa49_instat_callback(struct urb *urb) /*dbg(" %x %x %x %x %x %x %x %x %x %x %x", __func__, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10]);*/ - - /* Now do something useful with the data */ + + /* Now do something useful with the data */ msg = (struct keyspan_usa49_portStatusMessage *)data; - /* Check port number from message and retrieve private data */ + /* Check port number from message and retrieve private data */ if (msg->portNumber >= serial->num_ports) { - dbg ("%s - Unexpected port number %d", __func__, msg->portNumber); + dbg("%s - Unexpected port number %d", + __func__, msg->portNumber); goto exit; } port = serial->port[msg->portNumber]; p_priv = usb_get_serial_port_data(port); - + /* Update handshaking pin state information */ old_dcd_state = p_priv->dcd_state; p_priv->cts_state = ((msg->cts) ? 1 : 0); @@ -797,26 +785,26 @@ static void usa49_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state != p_priv->dcd_state) { if (old_dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); /* else */ /* wake_up_interruptible(&p_priv->open_wait); */ } - /* Resubmit urb so we continue receiving */ + /* Resubmit urb so we continue receiving */ urb->dev = serial->dev; - if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err != 0) dbg("%s - resubmit read urb failed. (%d)", __func__, err); - } exit: ; } static void usa49_inack_callback(struct urb *urb) { - dbg ("%s", __func__); + dbg("%s", __func__); } static void usa49_indat_callback(struct urb *urb) @@ -828,7 +816,7 @@ static void usa49_indat_callback(struct urb *urb) unsigned char *data = urb->transfer_buffer; int status = urb->status; - dbg ("%s", __func__); + dbg("%s", __func__); endpoint = usb_pipeendpoint(urb->pipe); @@ -839,14 +827,13 @@ static void usa49_indat_callback(struct urb *urb) } port = urb->context; - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { /* 0x80 bit is error flag */ if ((data[0] & 0x80) == 0) { /* no error on any byte */ - for (i = 1; i < urb->actual_length ; ++i) { - tty_insert_flip_char(tty, data[i], 0); - } + tty_insert_flip_string(tty, data + 1, + urb->actual_length - 1); } else { /* some bytes had errors, every byte has status */ for (i = 0; i + 1 < urb->actual_length; i += 2) { @@ -863,13 +850,15 @@ static void usa49_indat_callback(struct urb *urb) } tty_flip_buffer_push(tty); } - - /* Resubmit urb so we continue receiving */ + + /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; - if (port->open_count) - if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { - dbg("%s - resubmit read urb failed. (%d)", __func__, err); - } + if (port->port.count) { + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err != 0) + dbg("%s - resubmit read urb failed. (%d)", + __func__, err); + } } static void usa49wg_indat_callback(struct urb *urb) @@ -881,7 +870,7 @@ static void usa49wg_indat_callback(struct urb *urb) unsigned char *data = urb->transfer_buffer; int status = urb->status; - dbg ("%s", __func__); + dbg("%s", __func__); serial = urb->context; @@ -899,12 +888,12 @@ static void usa49wg_indat_callback(struct urb *urb) /* Check port number from message*/ if (data[i] >= serial->num_ports) { - dbg ("%s - Unexpected port number %d", + dbg("%s - Unexpected port number %d", __func__, data[i]); return; } port = serial->port[data[i++]]; - tty = port->tty; + tty = port->port.tty; len = data[i++]; /* 0x80 bit is error flag */ @@ -912,7 +901,7 @@ static void usa49wg_indat_callback(struct urb *urb) /* no error on any byte */ i++; for (x = 1; x < len ; ++x) - if (port->open_count) + if (port->port.count) tty_insert_flip_char(tty, data[i++], 0); else @@ -930,13 +919,13 @@ static void usa49wg_indat_callback(struct urb *urb) if (stat & RXERROR_PARITY) flag |= TTY_PARITY; /* XXX should handle break (0x10) */ - if (port->open_count) + if (port->port.count) tty_insert_flip_char(tty, data[i+1], flag); i += 2; } } - if (port->open_count) + if (port->port.count) tty_flip_buffer_push(tty); } } @@ -952,7 +941,7 @@ static void usa49wg_indat_callback(struct urb *urb) /* not used, usa-49 doesn't have per-port control endpoints */ static void usa49_outcont_callback(struct urb *urb) { - dbg ("%s", __func__); + dbg("%s", __func__); } static void usa90_indat_callback(struct urb *urb) @@ -965,7 +954,7 @@ static void usa90_indat_callback(struct urb *urb) unsigned char *data = urb->transfer_buffer; int status = urb->status; - dbg ("%s", __func__); + dbg("%s", __func__); endpoint = usb_pipeendpoint(urb->pipe); @@ -978,29 +967,26 @@ static void usa90_indat_callback(struct urb *urb) port = urb->context; p_priv = usb_get_serial_port_data(port); - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { - /* if current mode is DMA, looks like usa28 format - otherwise looks like usa26 data format */ + otherwise looks like usa26 data format */ - if (p_priv->baud > 57600) { - for (i = 0; i < urb->actual_length ; ++i) - tty_insert_flip_char(tty, data[i], 0); - } + if (p_priv->baud > 57600) + tty_insert_flip_string(tty, data, urb->actual_length); else { - /* 0x80 bit is error flag */ if ((data[0] & 0x80) == 0) { - /* no errors on individual bytes, only possible overrun err*/ + /* no errors on individual bytes, only + possible overrun err*/ if (data[0] & RXERROR_OVERRUN) - err = TTY_OVERRUN; - else err = 0; - for (i = 1; i < urb->actual_length ; ++i) - tty_insert_flip_char(tty, data[i], err); - - } - else { + err = TTY_OVERRUN; + else + err = 0; + for (i = 1; i < urb->actual_length ; ++i) + tty_insert_flip_char(tty, data[i], + err); + } else { /* some bytes had errors, every byte has status */ dbg("%s - RX error!!!!", __func__); for (i = 0; i + 1 < urb->actual_length; i += 2) { @@ -1012,19 +998,22 @@ static void usa90_indat_callback(struct urb *urb) if (stat & RXERROR_PARITY) flag |= TTY_PARITY; /* XXX should handle break (0x10) */ - tty_insert_flip_char(tty, data[i+1], flag); + tty_insert_flip_char(tty, data[i+1], + flag); } } } tty_flip_buffer_push(tty); } - + /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; - if (port->open_count) - if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { - dbg("%s - resubmit read urb failed. (%d)", __func__, err); - } + if (port->port.count) { + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err != 0) + dbg("%s - resubmit read urb failed. (%d)", + __func__, err); + } return; } @@ -1056,7 +1045,7 @@ static void usa90_instat_callback(struct urb *urb) port = serial->port[0]; p_priv = usb_get_serial_port_data(port); - + /* Update handshaking pin state information */ old_dcd_state = p_priv->dcd_state; p_priv->cts_state = ((msg->cts) ? 1 : 0); @@ -1064,19 +1053,19 @@ static void usa90_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state != p_priv->dcd_state) { if (old_dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); /* else */ /* wake_up_interruptible(&p_priv->open_wait); */ } - + /* Resubmit urb so we continue receiving */ urb->dev = serial->dev; - if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err != 0) dbg("%s - resubmit read urb failed. (%d)", __func__, err); - } exit: ; } @@ -1090,8 +1079,9 @@ static void usa90_outcont_callback(struct urb *urb) p_priv = usb_get_serial_port_data(port); if (p_priv->resend_cont) { - dbg ("%s - sending setup", __func__); - keyspan_usa90_send_setup(port->serial, port, p_priv->resend_cont - 1); + dbg("%s - sending setup", __func__); + keyspan_usa90_send_setup(port->serial, port, + p_priv->resend_cont - 1); } } @@ -1107,7 +1097,7 @@ static void usa67_instat_callback(struct urb *urb) int old_dcd_state; int status = urb->status; - dbg ("%s", __func__); + dbg("%s", __func__); serial = urb->context; @@ -1116,7 +1106,8 @@ static void usa67_instat_callback(struct urb *urb) return; } - if (urb->actual_length != sizeof(struct keyspan_usa67_portStatusMessage)) { + if (urb->actual_length != + sizeof(struct keyspan_usa67_portStatusMessage)) { dbg("%s - bad length %d", __func__, urb->actual_length); return; } @@ -1127,7 +1118,7 @@ static void usa67_instat_callback(struct urb *urb) /* Check port number from message and retrieve private data */ if (msg->port >= serial->num_ports) { - dbg ("%s - Unexpected port number %d", __func__, msg->port); + dbg("%s - Unexpected port number %d", __func__, msg->port); return; } @@ -1139,10 +1130,10 @@ static void usa67_instat_callback(struct urb *urb) p_priv->cts_state = ((msg->hskia_cts) ? 1 : 0); p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state != p_priv->dcd_state) { if (old_dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); /* else */ /* wake_up_interruptible(&p_priv->open_wait); */ } @@ -1161,7 +1152,7 @@ static void usa67_glocont_callback(struct urb *urb) struct keyspan_port_private *p_priv; int i; - dbg ("%s", __func__); + dbg("%s", __func__); serial = urb->context; for (i = 0; i < serial->num_ports; ++i) { @@ -1169,7 +1160,7 @@ static void usa67_glocont_callback(struct urb *urb) p_priv = usb_get_serial_port_data(port); if (p_priv->resend_cont) { - dbg ("%s - sending setup", __func__); + dbg("%s - sending setup", __func__); keyspan_usa67_send_setup(serial, port, p_priv->resend_cont - 1); break; @@ -1177,8 +1168,9 @@ static void usa67_glocont_callback(struct urb *urb) } } -static int keyspan_write_room (struct usb_serial_port *port) +static int keyspan_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct keyspan_port_private *p_priv; const struct keyspan_device_details *d_details; int flip; @@ -1191,32 +1183,30 @@ static int keyspan_write_room (struct usb_serial_port *port) /* FIXME: locking */ if (d_details->msg_format == msg_usa90) - data_len = 64; + data_len = 64; else data_len = 63; flip = p_priv->out_flip; /* Check both endpoints to see if any are available. */ - if ((this_urb = p_priv->out_urbs[flip]) != NULL) { + this_urb = p_priv->out_urbs[flip]; + if (this_urb != NULL) { if (this_urb->status != -EINPROGRESS) - return (data_len); - flip = (flip + 1) & d_details->outdat_endp_flip; - if ((this_urb = p_priv->out_urbs[flip]) != NULL) + return data_len; + flip = (flip + 1) & d_details->outdat_endp_flip; + this_urb = p_priv->out_urbs[flip]; + if (this_urb != NULL) { if (this_urb->status != -EINPROGRESS) - return (data_len); + return data_len; + } } return 0; } -static int keyspan_chars_in_buffer (struct usb_serial_port *port) -{ - return 0; -} - - -static int keyspan_open (struct usb_serial_port *port, struct file *filp) +static int keyspan_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct keyspan_port_private *p_priv; struct keyspan_serial_private *s_priv; @@ -1225,7 +1215,7 @@ static int keyspan_open (struct usb_serial_port *port, struct file *filp) int i, err; int baud_rate, device_port; struct urb *urb; - unsigned int cflag; + unsigned int cflag = 0; s_priv = usb_get_serial_data(serial); p_priv = usb_get_serial_port_data(port); @@ -1247,50 +1237,53 @@ static int keyspan_open (struct usb_serial_port *port, struct file *filp) /* Reset low level data toggle and start reading from endpoints */ for (i = 0; i < 2; i++) { - if ((urb = p_priv->in_urbs[i]) == NULL) + urb = p_priv->in_urbs[i]; + if (urb == NULL) continue; urb->dev = serial->dev; - /* make sure endpoint data toggle is synchronized with the device */ - + /* make sure endpoint data toggle is synchronized + with the device */ usb_clear_halt(urb->dev, urb->pipe); - - if ((err = usb_submit_urb(urb, GFP_KERNEL)) != 0) { - dbg("%s - submit urb %d failed (%d)", __func__, i, err); - } + err = usb_submit_urb(urb, GFP_KERNEL); + if (err != 0) + dbg("%s - submit urb %d failed (%d)", + __func__, i, err); } /* Reset low level data toggle on out endpoints */ for (i = 0; i < 2; i++) { - if ((urb = p_priv->out_urbs[i]) == NULL) + urb = p_priv->out_urbs[i]; + if (urb == NULL) continue; urb->dev = serial->dev; - /* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 0); */ + /* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe), 0); */ } /* get the terminal config for the setup message now so we don't * need to send 2 of them */ - cflag = port->tty->termios->c_cflag; device_port = port->number - port->serial->minor; - - /* Baud rate calculation takes baud rate as an integer - so other rates can be generated if desired. */ - baud_rate = tty_get_baud_rate(port->tty); - /* If no match or invalid, leave as default */ - if (baud_rate >= 0 - && d_details->calculate_baud_rate(baud_rate, d_details->baudclk, - NULL, NULL, NULL, device_port) == KEYSPAN_BAUD_RATE_OK) { - p_priv->baud = baud_rate; + if (tty) { + cflag = tty->termios->c_cflag; + /* Baud rate calculation takes baud rate as an integer + so other rates can be generated if desired. */ + baud_rate = tty_get_baud_rate(tty); + /* If no match or invalid, leave as default */ + if (baud_rate >= 0 + && d_details->calculate_baud_rate(baud_rate, d_details->baudclk, + NULL, NULL, NULL, device_port) == KEYSPAN_BAUD_RATE_OK) { + p_priv->baud = baud_rate; + } } - /* set CTS/RTS handshake etc. */ p_priv->cflag = cflag; p_priv->flow_control = (cflag & CRTSCTS)? flow_cts: flow_none; keyspan_send_setup(port, 1); - //mdelay(100); - //keyspan_set_termios(port, NULL); + /* mdelay(100); */ + /* keyspan_set_termios(port, NULL); */ return 0; } @@ -1301,7 +1294,8 @@ static inline void stop_urb(struct urb *urb) usb_kill_urb(urb); } -static void keyspan_close(struct usb_serial_port *port, struct file *filp) +static void keyspan_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int i; struct usb_serial *serial = port->serial; @@ -1311,15 +1305,15 @@ static void keyspan_close(struct usb_serial_port *port, struct file *filp) dbg("%s", __func__); s_priv = usb_get_serial_data(serial); p_priv = usb_get_serial_port_data(port); - + p_priv->rts_state = 0; p_priv->dtr_state = 0; - + if (serial->dev) { keyspan_send_setup(port, 2); /* pilot-xfer seems to work best with this delay */ mdelay(100); - // keyspan_set_termios(port, NULL); + /* keyspan_set_termios(port, NULL); */ } /*while (p_priv->outcont_urb->status == -EINPROGRESS) { @@ -1338,11 +1332,11 @@ static void keyspan_close(struct usb_serial_port *port, struct file *filp) stop_urb(p_priv->out_urbs[i]); } } - port->tty = NULL; + port->port.tty = NULL; } - /* download the firmware to a pre-renumeration device */ -static int keyspan_fake_startup (struct usb_serial *serial) +/* download the firmware to a pre-renumeration device */ +static int keyspan_fake_startup(struct usb_serial *serial) { int response; const struct ihex_binrec *record; @@ -1352,10 +1346,11 @@ static int keyspan_fake_startup (struct usb_serial *serial) dbg("Keyspan startup version %04x product %04x", le16_to_cpu(serial->dev->descriptor.bcdDevice), le16_to_cpu(serial->dev->descriptor.idProduct)); - - if ((le16_to_cpu(serial->dev->descriptor.bcdDevice) & 0x8000) != 0x8000) { + + if ((le16_to_cpu(serial->dev->descriptor.bcdDevice) & 0x8000) + != 0x8000) { dbg("Firmware already loaded. Quitting."); - return(1); + return 1; } /* Select firmware image on the basis of idProduct */ @@ -1379,11 +1374,11 @@ static int keyspan_fake_startup (struct usb_serial *serial) case keyspan_usa19_pre_product_id: fw_name = "keyspan/usa19.fw"; break; - + case keyspan_usa19qi_pre_product_id: fw_name = "keyspan/usa19qi.fw"; break; - + case keyspan_mpr_pre_product_id: fw_name = "keyspan/mpr.fw"; break; @@ -1391,15 +1386,15 @@ static int keyspan_fake_startup (struct usb_serial *serial) case keyspan_usa19qw_pre_product_id: fw_name = "keyspan/usa19qw.fw"; break; - + case keyspan_usa18x_pre_product_id: fw_name = "keyspan/usa18x.fw"; break; - + case keyspan_usa19w_pre_product_id: fw_name = "keyspan/usa19w.fw"; break; - + case keyspan_usa49w_pre_product_id: fw_name = "keyspan/usa49w.fw"; break; @@ -1431,8 +1426,7 @@ static int keyspan_fake_startup (struct usb_serial *serial) (unsigned char *)record->data, be16_to_cpu(record->len), 0xa0); if (response < 0) { - dev_err(&serial->dev->dev, "ezusb_writememory failed for Keyspan" - "firmware (%d %04X %p %d)\n", + dev_err(&serial->dev->dev, "ezusb_writememory failed for Keyspan firmware (%d %04X %p %d)\n", response, be32_to_cpu(record->addr), record->data, be16_to_cpu(record->len)); break; @@ -1445,7 +1439,7 @@ static int keyspan_fake_startup (struct usb_serial *serial) response = ezusb_set_reset(serial, 0); /* we don't want this device to have a driver assigned to it. */ - return (1); + return 1; } /* Helper functions used by keyspan_setup_urbs */ @@ -1467,7 +1461,7 @@ static struct usb_endpoint_descriptor const *find_ep(struct usb_serial const *se return NULL; } -static struct urb *keyspan_setup_urb (struct usb_serial *serial, int endpoint, +static struct urb *keyspan_setup_urb(struct usb_serial *serial, int endpoint, int dir, void *ctx, char *buf, int len, void (*callback)(struct urb *)) { @@ -1478,10 +1472,10 @@ static struct urb *keyspan_setup_urb (struct usb_serial *serial, int endpoint, if (endpoint == -1) return NULL; /* endpoint not needed */ - dbg ("%s - alloc for endpoint %d.", __func__, endpoint); + dbg("%s - alloc for endpoint %d.", __func__, endpoint); urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */ if (urb == NULL) { - dbg ("%s - alloc for endpoint %d failed.", __func__, endpoint); + dbg("%s - alloc for endpoint %d failed.", __func__, endpoint); return NULL; } @@ -1554,7 +1548,7 @@ static struct callbacks { }, { /* msg_usa90 callbacks */ .instat_callback = usa90_instat_callback, - .glocont_callback = usa28_glocont_callback, + .glocont_callback = usa28_glocont_callback, .indat_callback = usa90_indat_callback, .outdat_callback = usa2x_outdat_callback, .inack_callback = usa28_inack_callback, @@ -1582,16 +1576,16 @@ static void keyspan_setup_urbs(struct usb_serial *serial) struct callbacks *cback; int endp; - dbg ("%s", __func__); + dbg("%s", __func__); s_priv = usb_get_serial_data(serial); d_details = s_priv->device_details; - /* Setup values for the various callback routines */ + /* Setup values for the various callback routines */ cback = &keyspan_callbacks[d_details->msg_format]; - /* Allocate and set up urbs for each one that is in use, - starting with instat endpoints */ + /* Allocate and set up urbs for each one that is in use, + starting with instat endpoints */ s_priv->instat_urb = keyspan_setup_urb (serial, d_details->instat_endpoint, USB_DIR_IN, serial, s_priv->instat_buf, INSTAT_BUFLEN, @@ -1607,8 +1601,8 @@ static void keyspan_setup_urbs(struct usb_serial *serial) serial, s_priv->glocont_buf, GLOCONT_BUFLEN, cback->glocont_callback); - /* Setup endpoints for each port specific thing */ - for (i = 0; i < d_details->num_ports; i ++) { + /* Setup endpoints for each port specific thing */ + for (i = 0; i < d_details->num_ports; i++) { port = serial->port[i]; p_priv = usb_get_serial_port_data(port); @@ -1644,8 +1638,7 @@ static void keyspan_setup_urbs(struct usb_serial *serial) (serial, d_details->outcont_endpoints[i], USB_DIR_OUT, port, p_priv->outcont_buffer, 64, cback->outcont_callback); - } - + } } /* usa19 function doesn't require prescaler */ @@ -1653,46 +1646,39 @@ static int keyspan_usa19_calc_baud(u32 baud_rate, u32 baudclk, u8 *rate_hi, u8 *rate_low, u8 *prescaler, int portnum) { u32 b16, /* baud rate times 16 (actual rate used internally) */ - div, /* divisor */ + div, /* divisor */ cnt; /* inverse of divisor (programmed into 8051) */ - - dbg ("%s - %d.", __func__, baud_rate); - - /* prevent divide by zero... */ - if( (b16 = (baud_rate * 16L)) == 0) { - return (KEYSPAN_INVALID_BAUD_RATE); - } - /* Any "standard" rate over 57k6 is marginal on the USA-19 - as we run out of divisor resolution. */ - if (baud_rate > 57600) { - return (KEYSPAN_INVALID_BAUD_RATE); - } - - /* calculate the divisor and the counter (its inverse) */ - if( (div = (baudclk / b16)) == 0) { - return (KEYSPAN_INVALID_BAUD_RATE); - } - else { + dbg("%s - %d.", __func__, baud_rate); + + /* prevent divide by zero... */ + b16 = baud_rate * 16L; + if (b16 == 0) + return KEYSPAN_INVALID_BAUD_RATE; + /* Any "standard" rate over 57k6 is marginal on the USA-19 + as we run out of divisor resolution. */ + if (baud_rate > 57600) + return KEYSPAN_INVALID_BAUD_RATE; + + /* calculate the divisor and the counter (its inverse) */ + div = baudclk / b16; + if (div == 0) + return KEYSPAN_INVALID_BAUD_RATE; + else cnt = 0 - div; - } - if(div > 0xffff) { - return (KEYSPAN_INVALID_BAUD_RATE); - } + if (div > 0xffff) + return KEYSPAN_INVALID_BAUD_RATE; - /* return the counter values if non-null */ - if (rate_low) { + /* return the counter values if non-null */ + if (rate_low) *rate_low = (u8) (cnt & 0xff); - } - if (rate_hi) { + if (rate_hi) *rate_hi = (u8) ((cnt >> 8) & 0xff); - } - if (rate_low && rate_hi) { - dbg ("%s - %d %02x %02x.", __func__, baud_rate, *rate_hi, *rate_low); - } - - return (KEYSPAN_BAUD_RATE_OK); + if (rate_low && rate_hi) + dbg("%s - %d %02x %02x.", + __func__, baud_rate, *rate_hi, *rate_low); + return KEYSPAN_BAUD_RATE_OK; } /* usa19hs function doesn't require prescaler */ @@ -1700,34 +1686,35 @@ static int keyspan_usa19hs_calc_baud(u32 baud_rate, u32 baudclk, u8 *rate_hi, u8 *rate_low, u8 *prescaler, int portnum) { u32 b16, /* baud rate times 16 (actual rate used internally) */ - div; /* divisor */ - - dbg ("%s - %d.", __func__, baud_rate); + div; /* divisor */ - /* prevent divide by zero... */ - if( (b16 = (baud_rate * 16L)) == 0) - return (KEYSPAN_INVALID_BAUD_RATE); - + dbg("%s - %d.", __func__, baud_rate); + /* prevent divide by zero... */ + b16 = baud_rate * 16L; + if (b16 == 0) + return KEYSPAN_INVALID_BAUD_RATE; - /* calculate the divisor */ - if( (div = (baudclk / b16)) == 0) - return (KEYSPAN_INVALID_BAUD_RATE); + /* calculate the divisor */ + div = baudclk / b16; + if (div == 0) + return KEYSPAN_INVALID_BAUD_RATE; - if(div > 0xffff) - return (KEYSPAN_INVALID_BAUD_RATE); + if (div > 0xffff) + return KEYSPAN_INVALID_BAUD_RATE; - /* return the counter values if non-null */ - if (rate_low) + /* return the counter values if non-null */ + if (rate_low) *rate_low = (u8) (div & 0xff); - - if (rate_hi) + + if (rate_hi) *rate_hi = (u8) ((div >> 8) & 0xff); - - if (rate_low && rate_hi) - dbg ("%s - %d %02x %02x.", __func__, baud_rate, *rate_hi, *rate_low); - - return (KEYSPAN_BAUD_RATE_OK); + + if (rate_low && rate_hi) + dbg("%s - %d %02x %02x.", + __func__, baud_rate, *rate_hi, *rate_low); + + return KEYSPAN_BAUD_RATE_OK; } static int keyspan_usa19w_calc_baud(u32 baud_rate, u32 baudclk, u8 *rate_hi, @@ -1735,64 +1722,61 @@ static int keyspan_usa19w_calc_baud(u32 baud_rate, u32 baudclk, u8 *rate_hi, { u32 b16, /* baud rate times 16 (actual rate used internally) */ clk, /* clock with 13/8 prescaler */ - div, /* divisor using 13/8 prescaler */ + div, /* divisor using 13/8 prescaler */ res, /* resulting baud rate using 13/8 prescaler */ diff, /* error using 13/8 prescaler */ smallest_diff; u8 best_prescaler; int i; - dbg ("%s - %d.", __func__, baud_rate); + dbg("%s - %d.", __func__, baud_rate); - /* prevent divide by zero */ - if( (b16 = baud_rate * 16L) == 0) { - return (KEYSPAN_INVALID_BAUD_RATE); - } + /* prevent divide by zero */ + b16 = baud_rate * 16L; + if (b16 == 0) + return KEYSPAN_INVALID_BAUD_RATE; - /* Calculate prescaler by trying them all and looking - for best fit */ - - /* start with largest possible difference */ + /* Calculate prescaler by trying them all and looking + for best fit */ + + /* start with largest possible difference */ smallest_diff = 0xffffffff; /* 0 is an invalid prescaler, used as a flag */ best_prescaler = 0; - for(i = 8; i <= 0xff; ++i) { + for (i = 8; i <= 0xff; ++i) { clk = (baudclk * 8) / (u32) i; - - if( (div = clk / b16) == 0) { + + div = clk / b16; + if (div == 0) continue; - } res = clk / div; - diff= (res > b16) ? (res-b16) : (b16-res); + diff = (res > b16) ? (res-b16) : (b16-res); - if(diff < smallest_diff) { + if (diff < smallest_diff) { best_prescaler = i; smallest_diff = diff; } } - if(best_prescaler == 0) { - return (KEYSPAN_INVALID_BAUD_RATE); - } + if (best_prescaler == 0) + return KEYSPAN_INVALID_BAUD_RATE; clk = (baudclk * 8) / (u32) best_prescaler; div = clk / b16; - /* return the divisor and prescaler if non-null */ - if (rate_low) { + /* return the divisor and prescaler if non-null */ + if (rate_low) *rate_low = (u8) (div & 0xff); - } - if (rate_hi) { + if (rate_hi) *rate_hi = (u8) ((div >> 8) & 0xff); - } if (prescaler) { *prescaler = best_prescaler; /* dbg("%s - %d %d", __func__, *prescaler, div); */ } - return (KEYSPAN_BAUD_RATE_OK); + return KEYSPAN_BAUD_RATE_OK; } /* USA-28 supports different maximum baud rates on each port */ @@ -1800,57 +1784,51 @@ static int keyspan_usa28_calc_baud(u32 baud_rate, u32 baudclk, u8 *rate_hi, u8 *rate_low, u8 *prescaler, int portnum) { u32 b16, /* baud rate times 16 (actual rate used internally) */ - div, /* divisor */ + div, /* divisor */ cnt; /* inverse of divisor (programmed into 8051) */ - dbg ("%s - %d.", __func__, baud_rate); + dbg("%s - %d.", __func__, baud_rate); /* prevent divide by zero */ - if ((b16 = baud_rate * 16L) == 0) - return (KEYSPAN_INVALID_BAUD_RATE); - - /* calculate the divisor and the counter (its inverse) */ - if ((div = (KEYSPAN_USA28_BAUDCLK / b16)) == 0) { - return (KEYSPAN_INVALID_BAUD_RATE); - } - else { + b16 = baud_rate * 16L; + if (b16 == 0) + return KEYSPAN_INVALID_BAUD_RATE; + + /* calculate the divisor and the counter (its inverse) */ + div = KEYSPAN_USA28_BAUDCLK / b16; + if (div == 0) + return KEYSPAN_INVALID_BAUD_RATE; + else cnt = 0 - div; - } - /* check for out of range, based on portnum, - and return result */ - if(portnum == 0) { - if(div > 0xffff) - return (KEYSPAN_INVALID_BAUD_RATE); - } - else { - if(portnum == 1) { - if(div > 0xff) { - return (KEYSPAN_INVALID_BAUD_RATE); - } - } - else { - return (KEYSPAN_INVALID_BAUD_RATE); - } + /* check for out of range, based on portnum, + and return result */ + if (portnum == 0) { + if (div > 0xffff) + return KEYSPAN_INVALID_BAUD_RATE; + } else { + if (portnum == 1) { + if (div > 0xff) + return KEYSPAN_INVALID_BAUD_RATE; + } else + return KEYSPAN_INVALID_BAUD_RATE; } /* return the counter values if not NULL (port 1 will ignore retHi) */ - if (rate_low) { + if (rate_low) *rate_low = (u8) (cnt & 0xff); - } - if (rate_hi) { + if (rate_hi) *rate_hi = (u8) ((cnt >> 8) & 0xff); - } - dbg ("%s - %d OK.", __func__, baud_rate); - return (KEYSPAN_BAUD_RATE_OK); + dbg("%s - %d OK.", __func__, baud_rate); + return KEYSPAN_BAUD_RATE_OK; } static int keyspan_usa26_send_setup(struct usb_serial *serial, struct usb_serial_port *port, int reset_port) { - struct keyspan_usa26_portControlMessage msg; + struct keyspan_usa26_portControlMessage msg; struct keyspan_serial_private *s_priv; struct keyspan_port_private *p_priv; const struct keyspan_device_details *d_details; @@ -1858,7 +1836,7 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial, struct urb *this_urb; int device_port, err; - dbg ("%s reset=%d", __func__, reset_port); + dbg("%s reset=%d", __func__, reset_port); s_priv = usb_get_serial_data(serial); p_priv = usb_get_serial_port_data(port); @@ -1881,22 +1859,22 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial, if ((reset_port + 1) > p_priv->resend_cont) p_priv->resend_cont = reset_port + 1; if (this_urb->status == -EINPROGRESS) { - /* dbg ("%s - already writing", __func__); */ + /* dbg("%s - already writing", __func__); */ mdelay(5); - return(-1); + return -1; } - memset(&msg, 0, sizeof (struct keyspan_usa26_portControlMessage)); - - /* Only set baud rate if it's changed */ + memset(&msg, 0, sizeof(struct keyspan_usa26_portControlMessage)); + + /* Only set baud rate if it's changed */ if (p_priv->old_baud != p_priv->baud) { p_priv->old_baud = p_priv->baud; msg.setClocking = 0xff; if (d_details->calculate_baud_rate (p_priv->baud, d_details->baudclk, &msg.baudHi, - &msg.baudLo, &msg.prescaler, device_port) == KEYSPAN_INVALID_BAUD_RATE ) { - dbg("%s - Invalid baud rate %d requested, using 9600.", __func__, - p_priv->baud); + &msg.baudLo, &msg.prescaler, device_port) == KEYSPAN_INVALID_BAUD_RATE) { + dbg("%s - Invalid baud rate %d requested, using 9600.", + __func__, p_priv->baud); msg.baudLo = 0; msg.baudHi = 125; /* Values for 9600 baud */ msg.prescaler = 10; @@ -1922,7 +1900,7 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial, if (p_priv->cflag & PARENB) { /* note USA_PARITY_NONE == 0 */ msg.lcr |= (p_priv->cflag & PARODD)? - USA_PARITY_ODD: USA_PARITY_EVEN; + USA_PARITY_ODD : USA_PARITY_EVEN; } msg.setLcr = 0xff; @@ -1963,7 +1941,7 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial, /* Sending intermediate configs */ else { - msg._txOn = (! p_priv->break_on); + msg._txOn = (!p_priv->break_on); msg._txOff = 0; msg.txFlush = 0; msg.txBreak = (p_priv->break_on); @@ -1975,23 +1953,23 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial, msg.resetDataToggle = 0x0; } - /* Do handshaking outputs */ + /* Do handshaking outputs */ msg.setTxTriState_setRts = 0xff; msg.txTriState_rts = p_priv->rts_state; msg.setHskoa_setDtr = 0xff; msg.hskoa_dtr = p_priv->dtr_state; - + p_priv->resend_cont = 0; - memcpy (this_urb->transfer_buffer, &msg, sizeof(msg)); - + memcpy(this_urb->transfer_buffer, &msg, sizeof(msg)); + /* send the data out the device on control endpoint */ this_urb->transfer_buffer_length = sizeof(msg); this_urb->dev = serial->dev; - if ((err = usb_submit_urb(this_urb, GFP_ATOMIC)) != 0) { + err = usb_submit_urb(this_urb, GFP_ATOMIC); + if (err != 0) dbg("%s - usb_submit_urb(setup) failed (%d)", __func__, err); - } #if 0 else { dbg("%s - usb_submit_urb(%d) OK %d bytes (end %d)", __func__ @@ -2007,14 +1985,14 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial, struct usb_serial_port *port, int reset_port) { - struct keyspan_usa28_portControlMessage msg; + struct keyspan_usa28_portControlMessage msg; struct keyspan_serial_private *s_priv; struct keyspan_port_private *p_priv; const struct keyspan_device_details *d_details; struct urb *this_urb; int device_port, err; - dbg ("%s", __func__); + dbg("%s", __func__); s_priv = usb_get_serial_data(serial); p_priv = usb_get_serial_port_data(port); @@ -2022,7 +2000,8 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial, device_port = port->number - port->serial->minor; /* only do something if we have a bulk out endpoint */ - if ((this_urb = p_priv->outcont_urb) == NULL) { + this_urb = p_priv->outcont_urb; + if (this_urb == NULL) { dbg("%s - oops no urb.", __func__); return -1; } @@ -2032,17 +2011,18 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial, if ((reset_port + 1) > p_priv->resend_cont) p_priv->resend_cont = reset_port + 1; if (this_urb->status == -EINPROGRESS) { - dbg ("%s already writing", __func__); + dbg("%s already writing", __func__); mdelay(5); - return(-1); + return -1; } - memset(&msg, 0, sizeof (struct keyspan_usa28_portControlMessage)); + memset(&msg, 0, sizeof(struct keyspan_usa28_portControlMessage)); msg.setBaudRate = 1; if (d_details->calculate_baud_rate(p_priv->baud, d_details->baudclk, - &msg.baudHi, &msg.baudLo, NULL, device_port) == KEYSPAN_INVALID_BAUD_RATE ) { - dbg("%s - Invalid baud rate requested %d.", __func__, p_priv->baud); + &msg.baudHi, &msg.baudLo, NULL, device_port) == KEYSPAN_INVALID_BAUD_RATE) { + dbg("%s - Invalid baud rate requested %d.", + __func__, p_priv->baud); msg.baudLo = 0xff; msg.baudHi = 0xb2; /* Values for 9600 baud */ } @@ -2053,7 +2033,7 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial, msg.ctsFlowControl = (p_priv->flow_control == flow_cts); msg.xonFlowControl = 0; - /* Do handshaking outputs, DTR is inverted relative to RTS */ + /* Do handshaking outputs, DTR is inverted relative to RTS */ msg.rts = p_priv->rts_state; msg.dtr = p_priv->dtr_state; @@ -2095,7 +2075,7 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial, } /* Sending intermediate configs */ else { - msg._txOn = (! p_priv->break_on); + msg._txOn = (!p_priv->break_on); msg._txOff = 0; msg.txFlush = 0; msg.txForceXoff = 0; @@ -2109,15 +2089,15 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial, } p_priv->resend_cont = 0; - memcpy (this_urb->transfer_buffer, &msg, sizeof(msg)); + memcpy(this_urb->transfer_buffer, &msg, sizeof(msg)); /* send the data out the device on control endpoint */ this_urb->transfer_buffer_length = sizeof(msg); this_urb->dev = serial->dev; - if ((err = usb_submit_urb(this_urb, GFP_ATOMIC)) != 0) { + err = usb_submit_urb(this_urb, GFP_ATOMIC); + if (err != 0) dbg("%s - usb_submit_urb(setup) failed", __func__); - } #if 0 else { dbg("%s - usb_submit_urb(setup) OK %d bytes", __func__, @@ -2140,7 +2120,7 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial, struct urb *this_urb; int err, device_port; - dbg ("%s", __func__); + dbg("%s", __func__); s_priv = usb_get_serial_data(serial); p_priv = usb_get_serial_port_data(port); @@ -2151,7 +2131,9 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial, /* Work out which port within the device is being setup */ device_port = port->number - port->serial->minor; - dbg("%s - endpoint %d port %d (%d)",__func__, usb_pipeendpoint(this_urb->pipe), port->number, device_port); + dbg("%s - endpoint %d port %d (%d)", + __func__, usb_pipeendpoint(this_urb->pipe), + port->number, device_port); /* Make sure we have an urb then send the message */ if (this_urb == NULL) { @@ -2165,30 +2147,30 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial, p_priv->resend_cont = reset_port + 1; if (this_urb->status == -EINPROGRESS) { - /* dbg ("%s - already writing", __func__); */ + /* dbg("%s - already writing", __func__); */ mdelay(5); - return(-1); + return -1; } - memset(&msg, 0, sizeof (struct keyspan_usa49_portControlMessage)); + memset(&msg, 0, sizeof(struct keyspan_usa49_portControlMessage)); /*msg.portNumber = port->number;*/ msg.portNumber = device_port; - - /* Only set baud rate if it's changed */ + + /* Only set baud rate if it's changed */ if (p_priv->old_baud != p_priv->baud) { p_priv->old_baud = p_priv->baud; msg.setClocking = 0xff; if (d_details->calculate_baud_rate (p_priv->baud, d_details->baudclk, &msg.baudHi, - &msg.baudLo, &msg.prescaler, device_port) == KEYSPAN_INVALID_BAUD_RATE ) { - dbg("%s - Invalid baud rate %d requested, using 9600.", __func__, - p_priv->baud); + &msg.baudLo, &msg.prescaler, device_port) == KEYSPAN_INVALID_BAUD_RATE) { + dbg("%s - Invalid baud rate %d requested, using 9600.", + __func__, p_priv->baud); msg.baudLo = 0; msg.baudHi = 125; /* Values for 9600 baud */ msg.prescaler = 10; } - //msg.setPrescaler = 0xff; + /* msg.setPrescaler = 0xff; */ } msg.lcr = (p_priv->cflag & CSTOPB)? STOPBITS_678_2: STOPBITS_5678_1; @@ -2209,19 +2191,19 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial, if (p_priv->cflag & PARENB) { /* note USA_PARITY_NONE == 0 */ msg.lcr |= (p_priv->cflag & PARODD)? - USA_PARITY_ODD: USA_PARITY_EVEN; + USA_PARITY_ODD : USA_PARITY_EVEN; } msg.setLcr = 0xff; msg.ctsFlowControl = (p_priv->flow_control == flow_cts); msg.xonFlowControl = 0; msg.setFlowControl = 0xff; - + msg.forwardingLength = 16; msg.xonChar = 17; msg.xoffChar = 19; - /* Opening port */ + /* Opening port */ if (reset_port == 1) { msg._txOn = 1; msg._txOff = 0; @@ -2253,7 +2235,7 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial, } /* Sending intermediate configs */ else { - msg._txOn = (! p_priv->break_on); + msg._txOn = (!p_priv->break_on); msg._txOff = 0; msg.txFlush = 0; msg.txBreak = (p_priv->break_on); @@ -2267,16 +2249,17 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial, msg.disablePort = 0; } - /* Do handshaking outputs */ + /* Do handshaking outputs */ msg.setRts = 0xff; msg.rts = p_priv->rts_state; msg.setDtr = 0xff; msg.dtr = p_priv->dtr_state; - + p_priv->resend_cont = 0; - /* if the device is a 49wg, we send control message on usb control EP 0 */ + /* if the device is a 49wg, we send control message on usb + control EP 0 */ if (d_details->product_id == keyspan_usa49wg_product_id) { dr = (void *)(s_priv->ctrl_buf); @@ -2286,23 +2269,24 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial, dr->wIndex = 0; dr->wLength = cpu_to_le16(sizeof(msg)); - memcpy (s_priv->glocont_buf, &msg, sizeof(msg)); + memcpy(s_priv->glocont_buf, &msg, sizeof(msg)); - usb_fill_control_urb(this_urb, serial->dev, usb_sndctrlpipe(serial->dev, 0), - (unsigned char *)dr, s_priv->glocont_buf, sizeof(msg), - usa49_glocont_callback, serial); + usb_fill_control_urb(this_urb, serial->dev, + usb_sndctrlpipe(serial->dev, 0), + (unsigned char *)dr, s_priv->glocont_buf, + sizeof(msg), usa49_glocont_callback, serial); } else { memcpy(this_urb->transfer_buffer, &msg, sizeof(msg)); - + /* send the data out the device on control endpoint */ this_urb->transfer_buffer_length = sizeof(msg); this_urb->dev = serial->dev; } - if ((err = usb_submit_urb(this_urb, GFP_ATOMIC)) != 0) { + err = usb_submit_urb(this_urb, GFP_ATOMIC); + if (err != 0) dbg("%s - usb_submit_urb(setup) failed (%d)", __func__, err); - } #if 0 else { dbg("%s - usb_submit_urb(%d) OK %d bytes (end %d)", __func__, @@ -2318,7 +2302,7 @@ static int keyspan_usa90_send_setup(struct usb_serial *serial, struct usb_serial_port *port, int reset_port) { - struct keyspan_usa90_portControlMessage msg; + struct keyspan_usa90_portControlMessage msg; struct keyspan_serial_private *s_priv; struct keyspan_port_private *p_priv; const struct keyspan_device_details *d_details; @@ -2326,14 +2310,15 @@ static int keyspan_usa90_send_setup(struct usb_serial *serial, int err; u8 prescaler; - dbg ("%s", __func__); + dbg("%s", __func__); s_priv = usb_get_serial_data(serial); p_priv = usb_get_serial_port_data(port); d_details = s_priv->device_details; /* only do something if we have a bulk out endpoint */ - if ((this_urb = p_priv->outcont_urb) == NULL) { + this_urb = p_priv->outcont_urb; + if (this_urb == NULL) { dbg("%s - oops no urb.", __func__); return -1; } @@ -2343,24 +2328,24 @@ static int keyspan_usa90_send_setup(struct usb_serial *serial, if ((reset_port + 1) > p_priv->resend_cont) p_priv->resend_cont = reset_port + 1; if (this_urb->status == -EINPROGRESS) { - dbg ("%s already writing", __func__); + dbg("%s already writing", __func__); mdelay(5); - return(-1); + return -1; } - memset(&msg, 0, sizeof (struct keyspan_usa90_portControlMessage)); + memset(&msg, 0, sizeof(struct keyspan_usa90_portControlMessage)); - /* Only set baud rate if it's changed */ + /* Only set baud rate if it's changed */ if (p_priv->old_baud != p_priv->baud) { p_priv->old_baud = p_priv->baud; msg.setClocking = 0x01; if (d_details->calculate_baud_rate (p_priv->baud, d_details->baudclk, &msg.baudHi, - &msg.baudLo, &prescaler, 0) == KEYSPAN_INVALID_BAUD_RATE ) { - dbg("%s - Invalid baud rate %d requested, using 9600.", __func__, - p_priv->baud); + &msg.baudLo, &prescaler, 0) == KEYSPAN_INVALID_BAUD_RATE) { + dbg("%s - Invalid baud rate %d requested, using 9600.", + __func__, p_priv->baud); p_priv->baud = 9600; - d_details->calculate_baud_rate (p_priv->baud, d_details->baudclk, + d_details->calculate_baud_rate(p_priv->baud, d_details->baudclk, &msg.baudHi, &msg.baudLo, &prescaler, 0); } msg.setRxMode = 1; @@ -2368,13 +2353,10 @@ static int keyspan_usa90_send_setup(struct usb_serial *serial, } /* modes must always be correctly specified */ - if (p_priv->baud > 57600) - { + if (p_priv->baud > 57600) { msg.rxMode = RXMODE_DMA; msg.txMode = TXMODE_DMA; - } - else - { + } else { msg.rxMode = RXMODE_BYHAND; msg.txMode = TXMODE_BYHAND; } @@ -2397,7 +2379,7 @@ static int keyspan_usa90_send_setup(struct usb_serial *serial, if (p_priv->cflag & PARENB) { /* note USA_PARITY_NONE == 0 */ msg.lcr |= (p_priv->cflag & PARODD)? - USA_PARITY_ODD: USA_PARITY_EVEN; + USA_PARITY_ODD : USA_PARITY_EVEN; } if (p_priv->old_cflag != p_priv->cflag) { p_priv->old_cflag = p_priv->cflag; @@ -2408,47 +2390,46 @@ static int keyspan_usa90_send_setup(struct usb_serial *serial, msg.txFlowControl = TXFLOW_CTS; msg.setTxFlowControl = 0x01; msg.setRxFlowControl = 0x01; - + msg.rxForwardingLength = 16; - msg.rxForwardingTimeout = 16; + msg.rxForwardingTimeout = 16; msg.txAckSetting = 0; msg.xonChar = 17; msg.xoffChar = 19; - /* Opening port */ + /* Opening port */ if (reset_port == 1) { msg.portEnabled = 1; msg.rxFlush = 1; msg.txBreak = (p_priv->break_on); } /* Closing port */ - else if (reset_port == 2) { + else if (reset_port == 2) msg.portEnabled = 0; - } /* Sending intermediate configs */ else { - if (port->open_count) + if (port->port.count) msg.portEnabled = 1; msg.txBreak = (p_priv->break_on); } - /* Do handshaking outputs */ + /* Do handshaking outputs */ msg.setRts = 0x01; msg.rts = p_priv->rts_state; msg.setDtr = 0x01; msg.dtr = p_priv->dtr_state; - + p_priv->resend_cont = 0; - memcpy (this_urb->transfer_buffer, &msg, sizeof(msg)); - + memcpy(this_urb->transfer_buffer, &msg, sizeof(msg)); + /* send the data out the device on control endpoint */ this_urb->transfer_buffer_length = sizeof(msg); this_urb->dev = serial->dev; - if ((err = usb_submit_urb(this_urb, GFP_ATOMIC)) != 0) { + err = usb_submit_urb(this_urb, GFP_ATOMIC); + if (err != 0) dbg("%s - usb_submit_urb(setup) failed (%d)", __func__, err); - } return 0; } @@ -2463,7 +2444,7 @@ static int keyspan_usa67_send_setup(struct usb_serial *serial, struct urb *this_urb; int err, device_port; - dbg ("%s", __func__); + dbg("%s", __func__); s_priv = usb_get_serial_data(serial); p_priv = usb_get_serial_port_data(port); @@ -2486,9 +2467,9 @@ static int keyspan_usa67_send_setup(struct usb_serial *serial, if ((reset_port + 1) > p_priv->resend_cont) p_priv->resend_cont = reset_port + 1; if (this_urb->status == -EINPROGRESS) { - /* dbg ("%s - already writing", __func__); */ + /* dbg("%s - already writing", __func__); */ mdelay(5); - return(-1); + return -1; } memset(&msg, 0, sizeof(struct keyspan_usa67_portControlMessage)); @@ -2501,9 +2482,9 @@ static int keyspan_usa67_send_setup(struct usb_serial *serial, msg.setClocking = 0xff; if (d_details->calculate_baud_rate (p_priv->baud, d_details->baudclk, &msg.baudHi, - &msg.baudLo, &msg.prescaler, device_port) == KEYSPAN_INVALID_BAUD_RATE ) { - dbg("%s - Invalid baud rate %d requested, using 9600.", __func__, - p_priv->baud); + &msg.baudLo, &msg.prescaler, device_port) == KEYSPAN_INVALID_BAUD_RATE) { + dbg("%s - Invalid baud rate %d requested, using 9600.", + __func__, p_priv->baud); msg.baudLo = 0; msg.baudHi = 125; /* Values for 9600 baud */ msg.prescaler = 10; @@ -2529,7 +2510,7 @@ static int keyspan_usa67_send_setup(struct usb_serial *serial, if (p_priv->cflag & PARENB) { /* note USA_PARITY_NONE == 0 */ msg.lcr |= (p_priv->cflag & PARODD)? - USA_PARITY_ODD: USA_PARITY_EVEN; + USA_PARITY_ODD : USA_PARITY_EVEN; } msg.setLcr = 0xff; @@ -2566,7 +2547,7 @@ static int keyspan_usa67_send_setup(struct usb_serial *serial, msg.resetDataToggle = 0; } else { /* Sending intermediate configs */ - msg._txOn = (! p_priv->break_on); + msg._txOn = (!p_priv->break_on); msg._txOff = 0; msg.txFlush = 0; msg.txBreak = (p_priv->break_on); @@ -2606,7 +2587,7 @@ static void keyspan_send_setup(struct usb_serial_port *port, int reset_port) struct keyspan_serial_private *s_priv; const struct keyspan_device_details *d_details; - dbg ("%s", __func__); + dbg("%s", __func__); s_priv = usb_get_serial_data(serial); d_details = s_priv->device_details; @@ -2633,7 +2614,7 @@ static void keyspan_send_setup(struct usb_serial_port *port, int reset_port) /* Gets called by the "real" driver (ie once firmware is loaded and renumeration has taken place. */ -static int keyspan_startup (struct usb_serial *serial) +static int keyspan_startup(struct usb_serial *serial) { int i, err; struct usb_serial_port *port; @@ -2644,17 +2625,20 @@ static int keyspan_startup (struct usb_serial *serial) dbg("%s", __func__); for (i = 0; (d_details = keyspan_devices[i]) != NULL; ++i) - if (d_details->product_id == le16_to_cpu(serial->dev->descriptor.idProduct)) + if (d_details->product_id == + le16_to_cpu(serial->dev->descriptor.idProduct)) break; if (d_details == NULL) { - dev_err(&serial->dev->dev, "%s - unknown product id %x\n", __func__, le16_to_cpu(serial->dev->descriptor.idProduct)); + dev_err(&serial->dev->dev, "%s - unknown product id %x\n", + __func__, le16_to_cpu(serial->dev->descriptor.idProduct)); return 1; } /* Setup private data for serial driver */ s_priv = kzalloc(sizeof(struct keyspan_serial_private), GFP_KERNEL); if (!s_priv) { - dbg("%s - kmalloc for keyspan_serial_private failed.", __func__); + dbg("%s - kmalloc for keyspan_serial_private failed.", + __func__); return -ENOMEM; } @@ -2664,10 +2648,11 @@ static int keyspan_startup (struct usb_serial *serial) /* Now setup per port private data */ for (i = 0; i < serial->num_ports; i++) { port = serial->port[i]; - p_priv = kzalloc(sizeof(struct keyspan_port_private), GFP_KERNEL); + p_priv = kzalloc(sizeof(struct keyspan_port_private), + GFP_KERNEL); if (!p_priv) { dbg("%s - kmalloc for keyspan_port_private (%d) failed!.", __func__, i); - return (1); + return 1; } p_priv->device_details = d_details; usb_set_serial_port_data(port, p_priv); @@ -2689,11 +2674,11 @@ static int keyspan_startup (struct usb_serial *serial) dbg("%s - submit indat urb failed %d", __func__, err); } - + return 0; } -static void keyspan_shutdown (struct usb_serial *serial) +static void keyspan_shutdown(struct usb_serial *serial) { int i, j; struct usb_serial_port *port; @@ -2745,8 +2730,8 @@ static void keyspan_shutdown (struct usb_serial *serial) } } -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_FIRMWARE("keyspan/usa28.fw"); diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h index b52fb657a244..38b4582e0734 100644 --- a/drivers/usb/serial/keyspan.h +++ b/drivers/usb/serial/keyspan.h @@ -35,17 +35,18 @@ /* Function prototypes for Keyspan serial converter */ -static int keyspan_open (struct usb_serial_port *port, +static int keyspan_open (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); -static void keyspan_close (struct usb_serial_port *port, +static void keyspan_close (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); static int keyspan_startup (struct usb_serial *serial); static void keyspan_shutdown (struct usb_serial *serial); -static void keyspan_rx_throttle (struct usb_serial_port *port); -static void keyspan_rx_unthrottle (struct usb_serial_port *port); -static int keyspan_write_room (struct usb_serial_port *port); +static int keyspan_write_room (struct tty_struct *tty); -static int keyspan_write (struct usb_serial_port *port, +static int keyspan_write (struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count); @@ -53,18 +54,14 @@ static void keyspan_send_setup (struct usb_serial_port *port, int reset_port); -static int keyspan_chars_in_buffer (struct usb_serial_port *port); -static int keyspan_ioctl (struct usb_serial_port *port, - struct file *file, - unsigned int cmd, - unsigned long arg); -static void keyspan_set_termios (struct usb_serial_port *port, +static void keyspan_set_termios (struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); -static void keyspan_break_ctl (struct usb_serial_port *port, +static void keyspan_break_ctl (struct tty_struct *tty, int break_state); -static int keyspan_tiocmget (struct usb_serial_port *port, +static int keyspan_tiocmget (struct tty_struct *tty, struct file *file); -static int keyspan_tiocmset (struct usb_serial_port *port, +static int keyspan_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int keyspan_fake_startup (struct usb_serial *serial); @@ -138,7 +135,8 @@ static int keyspan_usa67_send_setup (struct usb_serial *serial, /* Product IDs post-renumeration. Note that the 28x and 28xb have the same id's post-renumeration but behave identically - so it's not an issue. */ + so it's not an issue. As such, the 28xb is not listed in any + of the device tables. */ #define keyspan_usa18x_product_id 0x0112 #define keyspan_usa19_product_id 0x0107 #define keyspan_usa19qi_product_id 0x010c @@ -482,7 +480,6 @@ static struct usb_device_id keyspan_ids_combined[] = { { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_product_id) }, { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_product_id) }, { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_product_id) }, - { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_product_id) }, { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xg_product_id) }, { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_product_id)}, { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_product_id)}, @@ -532,7 +529,6 @@ static struct usb_device_id keyspan_2port_ids[] = { { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_product_id) }, { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_product_id) }, { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_product_id) }, - { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_product_id) }, { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xg_product_id) }, { } /* Terminating entry */ }; @@ -568,10 +564,6 @@ static struct usb_serial_driver keyspan_1port_device = { .close = keyspan_close, .write = keyspan_write, .write_room = keyspan_write_room, - .chars_in_buffer = keyspan_chars_in_buffer, - .throttle = keyspan_rx_throttle, - .unthrottle = keyspan_rx_unthrottle, - .ioctl = keyspan_ioctl, .set_termios = keyspan_set_termios, .break_ctl = keyspan_break_ctl, .tiocmget = keyspan_tiocmget, @@ -592,10 +584,6 @@ static struct usb_serial_driver keyspan_2port_device = { .close = keyspan_close, .write = keyspan_write, .write_room = keyspan_write_room, - .chars_in_buffer = keyspan_chars_in_buffer, - .throttle = keyspan_rx_throttle, - .unthrottle = keyspan_rx_unthrottle, - .ioctl = keyspan_ioctl, .set_termios = keyspan_set_termios, .break_ctl = keyspan_break_ctl, .tiocmget = keyspan_tiocmget, @@ -616,10 +604,6 @@ static struct usb_serial_driver keyspan_4port_device = { .close = keyspan_close, .write = keyspan_write, .write_room = keyspan_write_room, - .chars_in_buffer = keyspan_chars_in_buffer, - .throttle = keyspan_rx_throttle, - .unthrottle = keyspan_rx_unthrottle, - .ioctl = keyspan_ioctl, .set_termios = keyspan_set_termios, .break_ctl = keyspan_break_ctl, .tiocmget = keyspan_tiocmget, diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 644a1eaaa376..040040a267d9 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -10,8 +10,9 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * See Documentation/usb/usb-serial.txt for more information on using this driver - * + * See Documentation/usb/usb-serial.txt for more information on using this + * driver + * * (09/07/2001) gkh * cleaned up the Xircom support. Added ids for Entregra device which is * the same as the Xircom device. Enabled the code to be compiled for @@ -21,23 +22,24 @@ * support for Xircom PGSDB9 * * (05/31/2001) gkh - * switched from using spinlock to a semaphore, which fixes lots of problems. + * switched from using spinlock to a semaphore, which fixes lots of + * problems. * * (04/08/2001) gb * Identify version on module load. - * + * * (11/01/2000) Adam J. Richter * usb_device_id table support - * + * * (10/05/2000) gkh * Fixed bug with urb->dev not being set properly, now that the usb * core needs it. - * + * * (08/28/2000) gkh * Added locks for SMP safeness. - * Fixed MOD_INC and MOD_DEC logic and the ability to open a port more + * Fixed MOD_INC and MOD_DEC logic and the ability to open a port more * than once. - * + * * (07/20/2000) borchers * - keyspan_pda_write no longer sleeps if it is called on interrupt time; * PPP and the line discipline with stty echo on can call write on @@ -55,14 +57,14 @@ * than done directly from the callback to avoid the race in write_chan * - keyspan_pda_chars_in_buffer also indicates its buffer is full if the * urb status is -EINPROGRESS, meaning it cannot write at the moment - * + * * (07/19/2000) gkh * Added module_init and module_exit functions to handle the fact that this * driver is a loadable module now. * * (03/26/2000) gkh * Split driver up into device specific pieces. - * + * */ @@ -78,7 +80,7 @@ #include <linux/workqueue.h> #include <linux/firmware.h> #include <linux/ihex.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> @@ -135,7 +137,7 @@ static struct usb_device_id id_table_combined [] = { { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table_combined); +MODULE_DEVICE_TABLE(usb, id_table_combined); static struct usb_driver keyspan_pda_driver = { .name = "keyspan_pda", @@ -159,9 +161,9 @@ static struct usb_device_id id_table_fake [] = { #ifdef XIRCOM static struct usb_device_id id_table_fake_xircom [] = { - { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID) }, - { USB_DEVICE(ENTREGRA_VENDOR_ID, ENTREGRA_FAKE_ID) }, - { } + { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID) }, + { USB_DEVICE(ENTREGRA_VENDOR_ID, ENTREGRA_FAKE_ID) }, + { } }; #endif @@ -171,7 +173,7 @@ static void keyspan_pda_wakeup_write(struct work_struct *work) container_of(work, struct keyspan_pda_private, wakeup_work); struct usb_serial_port *port = priv->port; - tty_wakeup(port->tty); + tty_wakeup(port->port.tty); } static void keyspan_pda_request_unthrottle(struct work_struct *work) @@ -184,7 +186,7 @@ static void keyspan_pda_request_unthrottle(struct work_struct *work) dbg(" request_unthrottle"); /* ask the device to tell us when the tx buffer becomes sufficiently empty */ - result = usb_control_msg(serial->dev, + result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 7, /* request_unthrottle */ USB_TYPE_VENDOR | USB_RECIP_INTERFACE @@ -195,17 +197,16 @@ static void keyspan_pda_request_unthrottle(struct work_struct *work) 0, 2000); if (result < 0) - dbg("%s - error %d from usb_control_msg", + dbg("%s - error %d from usb_control_msg", __func__, result); } -static void keyspan_pda_rx_interrupt (struct urb *urb) +static void keyspan_pda_rx_interrupt(struct urb *urb) { struct usb_serial_port *port = urb->context; - struct tty_struct *tty = port->tty; + struct tty_struct *tty = port->port.tty; unsigned char *data = urb->transfer_buffer; - int i; int retval; int status = urb->status; struct keyspan_pda_private *priv; @@ -228,14 +229,13 @@ static void keyspan_pda_rx_interrupt (struct urb *urb) goto exit; } - /* see if the message is data or a status interrupt */ + /* see if the message is data or a status interrupt */ switch (data[0]) { case 0: /* rest of message is rx data */ if (urb->actual_length) { - for (i = 1; i < urb->actual_length ; ++i) { - tty_insert_flip_char(tty, data[i], 0); - } + tty_insert_flip_string(tty, data + 1, + urb->actual_length - 1); tty_flip_buffer_push(tty); } break; @@ -259,14 +259,14 @@ static void keyspan_pda_rx_interrupt (struct urb *urb) } exit: - retval = usb_submit_urb (urb, GFP_ATOMIC); + retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) - err ("%s - usb_submit_urb failed with result %d", + err("%s - usb_submit_urb failed with result %d", __func__, retval); } -static void keyspan_pda_rx_throttle (struct usb_serial_port *port) +static void keyspan_pda_rx_throttle(struct tty_struct *tty) { /* stop receiving characters. We just turn off the URB request, and let chars pile up in the device. If we're doing hardware @@ -274,14 +274,15 @@ static void keyspan_pda_rx_throttle (struct usb_serial_port *port) fills up. If we're doing XON/XOFF, this would be a good time to send an XOFF, although it might make sense to foist that off upon the device too. */ - + struct usb_serial_port *port = tty->driver_data; dbg("keyspan_pda_rx_throttle port %d", port->number); usb_kill_urb(port->interrupt_in_urb); } -static void keyspan_pda_rx_unthrottle (struct usb_serial_port *port) +static void keyspan_pda_rx_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; /* just restart the receive interrupt URB */ dbg("keyspan_pda_rx_unthrottle port %d", port->number); port->interrupt_in_urb->dev = port->serial->dev; @@ -291,32 +292,52 @@ static void keyspan_pda_rx_unthrottle (struct usb_serial_port *port) } -static speed_t keyspan_pda_setbaud (struct usb_serial *serial, speed_t baud) +static speed_t keyspan_pda_setbaud(struct usb_serial *serial, speed_t baud) { int rc; int bindex; - switch(baud) { - case 110: bindex = 0; break; - case 300: bindex = 1; break; - case 1200: bindex = 2; break; - case 2400: bindex = 3; break; - case 4800: bindex = 4; break; - case 9600: bindex = 5; break; - case 19200: bindex = 6; break; - case 38400: bindex = 7; break; - case 57600: bindex = 8; break; - case 115200: bindex = 9; break; - default: - bindex = 5; /* Default to 9600 */ - baud = 9600; + switch (baud) { + case 110: + bindex = 0; + break; + case 300: + bindex = 1; + break; + case 1200: + bindex = 2; + break; + case 2400: + bindex = 3; + break; + case 4800: + bindex = 4; + break; + case 9600: + bindex = 5; + break; + case 19200: + bindex = 6; + break; + case 38400: + bindex = 7; + break; + case 57600: + bindex = 8; + break; + case 115200: + bindex = 9; + break; + default: + bindex = 5; /* Default to 9600 */ + baud = 9600; } /* rather than figure out how to sleep while waiting for this to complete, I just use the "legacy" API. */ rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 0, /* set baud */ - USB_TYPE_VENDOR + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, /* type */ bindex, /* value */ @@ -330,8 +351,9 @@ static speed_t keyspan_pda_setbaud (struct usb_serial *serial, speed_t baud) } -static void keyspan_pda_break_ctl (struct usb_serial_port *port, int break_state) +static void keyspan_pda_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; int value; int result; @@ -341,11 +363,11 @@ static void keyspan_pda_break_ctl (struct usb_serial_port *port, int break_state else value = 0; /* clear break */ result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - 4, /* set break */ - USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, - value, 0, NULL, 0, 2000); + 4, /* set break */ + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + value, 0, NULL, 0, 2000); if (result < 0) - dbg("%s - error %d from usb_control_msg", + dbg("%s - error %d from usb_control_msg", __func__, result); /* there is something funky about this.. the TCSBRK that 'cu' performs ought to translate into a break_ctl(-1),break_ctl(0) pair HZ/4 @@ -354,8 +376,8 @@ static void keyspan_pda_break_ctl (struct usb_serial_port *port, int break_state } -static void keyspan_pda_set_termios (struct usb_serial_port *port, - struct ktermios *old_termios) +static void keyspan_pda_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; speed_t speed; @@ -380,7 +402,7 @@ static void keyspan_pda_set_termios (struct usb_serial_port *port, For now, just do baud. */ - speed = tty_get_baud_rate(port->tty); + speed = tty_get_baud_rate(tty); speed = keyspan_pda_setbaud(serial, speed); if (speed == 0) { @@ -390,8 +412,8 @@ static void keyspan_pda_set_termios (struct usb_serial_port *port, } /* Only speed can change so copy the old h/w parameters then encode the new speed */ - tty_termios_copy_hw(port->tty->termios, old_termios); - tty_encode_baud_rate(port->tty, speed, speed); + tty_termios_copy_hw(tty->termios, old_termios); + tty_encode_baud_rate(tty, speed, speed); } @@ -408,7 +430,7 @@ static int keyspan_pda_get_modem_info(struct usb_serial *serial, 3, /* get pins */ USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_IN, 0, 0, &data, 1, 2000); - if (rc > 0) + if (rc >= 0) *value = data; return rc; } @@ -425,8 +447,9 @@ static int keyspan_pda_set_modem_info(struct usb_serial *serial, return rc; } -static int keyspan_pda_tiocmget(struct usb_serial_port *port, struct file *file) +static int keyspan_pda_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; int rc; unsigned char status; @@ -445,9 +468,10 @@ static int keyspan_pda_tiocmget(struct usb_serial_port *port, struct file *file) return value; } -static int keyspan_pda_tiocmset(struct usb_serial_port *port, struct file *file, +static int keyspan_pda_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; int rc; unsigned char status; @@ -469,23 +493,8 @@ static int keyspan_pda_tiocmset(struct usb_serial_port *port, struct file *file, return rc; } -static int keyspan_pda_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case TIOCMIWAIT: - /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ - /* TODO */ - case TIOCGICOUNT: - /* return count of modemline transitions */ - return 0; /* TODO */ - } - - return -ENOIOCTLCMD; -} - -static int keyspan_pda_write(struct usb_serial_port *port, - const unsigned char *buf, int count) +static int keyspan_pda_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; int request_unthrottle = 0; @@ -501,10 +510,10 @@ static int keyspan_pda_write(struct usb_serial_port *port, select() or poll() too) until we receive that unthrottle interrupt. Block if we can't write anything at all, otherwise write as much as we can. */ - dbg("keyspan_pda_write(%d)",count); + dbg("keyspan_pda_write(%d)", count); if (count == 0) { dbg(" write request of 0 bytes"); - return (0); + return 0; } /* we might block because of: @@ -531,7 +540,7 @@ static int keyspan_pda_write(struct usb_serial_port *port, scheduler time, since usb_control_msg() sleeps. */ if (count > priv->tx_room && !in_interrupt()) { unsigned char room; - rc = usb_control_msg(serial->dev, + rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), 6, /* write_room */ USB_TYPE_VENDOR | USB_RECIP_INTERFACE @@ -562,7 +571,7 @@ static int keyspan_pda_write(struct usb_serial_port *port, if (count) { /* now transfer data */ - memcpy (port->write_urb->transfer_buffer, buf, count); + memcpy(port->write_urb->transfer_buffer, buf, count); /* send the data out the bulk port */ port->write_urb->transfer_buffer_length = count; @@ -574,8 +583,7 @@ static int keyspan_pda_write(struct usb_serial_port *port, dbg(" usb_submit_urb(write bulk) failed"); goto exit; } - } - else { + } else { /* There wasn't any room left, so we are throttled until the buffer empties a bit */ request_unthrottle = 1; @@ -594,7 +602,7 @@ exit: } -static void keyspan_pda_write_bulk_callback (struct urb *urb) +static void keyspan_pda_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct keyspan_pda_private *priv; @@ -607,22 +615,21 @@ static void keyspan_pda_write_bulk_callback (struct urb *urb) } -static int keyspan_pda_write_room (struct usb_serial_port *port) +static int keyspan_pda_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct keyspan_pda_private *priv; - priv = usb_get_serial_port_data(port); - /* used by n_tty.c for processing of tabs and such. Giving it our conservative guess is probably good enough, but needs testing by running a console through the device. */ - - return (priv->tx_room); + return priv->tx_room; } -static int keyspan_pda_chars_in_buffer (struct usb_serial_port *port) +static int keyspan_pda_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct keyspan_pda_private *priv; unsigned long flags; int ret = 0; @@ -640,7 +647,8 @@ static int keyspan_pda_chars_in_buffer (struct usb_serial_port *port) } -static int keyspan_pda_open (struct usb_serial_port *port, struct file *filp) +static int keyspan_pda_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; unsigned char room; @@ -672,8 +680,8 @@ static int keyspan_pda_open (struct usb_serial_port *port, struct file *filp) /* the normal serial device seems to always turn on DTR and RTS here, so do the same */ - if (port->tty->termios->c_cflag & CBAUD) - keyspan_pda_set_modem_info(serial, (1<<7) | (1<<2) ); + if (tty && (tty->termios->c_cflag & CBAUD)) + keyspan_pda_set_modem_info(serial, (1<<7) | (1<<2)); else keyspan_pda_set_modem_info(serial, 0); @@ -690,13 +698,15 @@ error: } -static void keyspan_pda_close(struct usb_serial_port *port, struct file *filp) +static void keyspan_pda_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; if (serial->dev) { - /* the normal serial device seems to always shut off DTR and RTS now */ - if (port->tty->termios->c_cflag & HUPCL) + /* the normal serial device seems to always shut + off DTR and RTS now */ + if (tty->termios->c_cflag & HUPCL) keyspan_pda_set_modem_info(serial, 0); /* shutdown our bulk reads and writes */ @@ -707,7 +717,7 @@ static void keyspan_pda_close(struct usb_serial_port *port, struct file *filp) /* download the firmware to a "fake" device (pre-renumeration) */ -static int keyspan_pda_fake_startup (struct usb_serial *serial) +static int keyspan_pda_fake_startup(struct usb_serial *serial) { int response; const char *fw_name; @@ -756,10 +766,10 @@ static int keyspan_pda_fake_startup (struct usb_serial *serial) response = ezusb_set_reset(serial, 0); /* we want this device to fail to have a driver assigned to it. */ - return (1); + return 1; } -static int keyspan_pda_startup (struct usb_serial *serial) +static int keyspan_pda_startup(struct usb_serial *serial) { struct keyspan_pda_private *priv; @@ -769,20 +779,20 @@ static int keyspan_pda_startup (struct usb_serial *serial) priv = kmalloc(sizeof(struct keyspan_pda_private), GFP_KERNEL); if (!priv) - return (1); /* error */ + return 1; /* error */ usb_set_serial_port_data(serial->port[0], priv); init_waitqueue_head(&serial->port[0]->write_wait); INIT_WORK(&priv->wakeup_work, keyspan_pda_wakeup_write); INIT_WORK(&priv->unthrottle_work, keyspan_pda_request_unthrottle); priv->serial = serial; priv->port = serial->port[0]; - return (0); + return 0; } -static void keyspan_pda_shutdown (struct usb_serial *serial) +static void keyspan_pda_shutdown(struct usb_serial *serial) { dbg("%s", __func__); - + kfree(usb_get_serial_port_data(serial->port[0])); } @@ -832,7 +842,6 @@ static struct usb_serial_driver keyspan_pda_device = { .chars_in_buffer = keyspan_pda_chars_in_buffer, .throttle = keyspan_pda_rx_throttle, .unthrottle = keyspan_pda_rx_unthrottle, - .ioctl = keyspan_pda_ioctl, .set_termios = keyspan_pda_set_termios, .break_ctl = keyspan_pda_break_ctl, .tiocmget = keyspan_pda_tiocmget, @@ -842,7 +851,7 @@ static struct usb_serial_driver keyspan_pda_device = { }; -static int __init keyspan_pda_init (void) +static int __init keyspan_pda_init(void) { int retval; retval = usb_serial_register(&keyspan_pda_device); @@ -863,7 +872,7 @@ static int __init keyspan_pda_init (void) goto failed_usb_register; info(DRIVER_DESC " " DRIVER_VERSION); return 0; -failed_usb_register: +failed_usb_register: #ifdef XIRCOM usb_serial_deregister(&xircom_pgs_fake_device); failed_xircom_register: @@ -880,15 +889,15 @@ failed_pda_register: } -static void __exit keyspan_pda_exit (void) +static void __exit keyspan_pda_exit(void) { - usb_deregister (&keyspan_pda_driver); - usb_serial_deregister (&keyspan_pda_device); + usb_deregister(&keyspan_pda_driver); + usb_serial_deregister(&keyspan_pda_device); #ifdef KEYSPAN - usb_serial_deregister (&keyspan_pda_fake_device); + usb_serial_deregister(&keyspan_pda_fake_device); #endif #ifdef XIRCOM - usb_serial_deregister (&xircom_pgs_fake_device); + usb_serial_deregister(&xircom_pgs_fake_device); #endif } @@ -896,8 +905,8 @@ static void __exit keyspan_pda_exit (void) module_init(keyspan_pda_init); module_exit(keyspan_pda_exit); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index f328948d74e3..b84dddc71124 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -15,12 +15,12 @@ * Neither Palm, nor their contractor (MCCI) or their supplier (KLSI) provided * information that was not already available. * - * It seems that KLSI bought some silicon-design information from ScanLogic, + * It seems that KLSI bought some silicon-design information from ScanLogic, * whose SL11R processor is at the core of the KL5KUSB chipset from KLSI. * KLSI has firmware available for their devices; it is probable that the * firmware differs from that used by KLSI in their products. If you have an - * original KLSI device and can provide some information on it, I would be - * most interested in adding support for it here. If you have any information + * original KLSI device and can provide some information on it, I would be + * most interested in adding support for it here. If you have any information * on the protocol used (or find errors in my reverse-engineered stuff), please * let me know. * @@ -40,7 +40,7 @@ * 0.2 - TIOCMGET works, so autopilot(1) can be used! * 0.1 - can be used to to pilot-xfer -p /dev/ttyUSB0 -l * - * The driver skeleton is mainly based on mct_u232.c and various other + * The driver skeleton is mainly based on mct_u232.c and various other * pieces of code shamelessly copied from the drivers/usb/serial/ directory. */ @@ -53,7 +53,7 @@ #include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <asm/unaligned.h> #include <linux/usb.h> #include <linux/usb/serial.h> @@ -72,33 +72,25 @@ static int debug; /* * Function prototypes */ -static int klsi_105_startup (struct usb_serial *serial); -static void klsi_105_shutdown (struct usb_serial *serial); -static int klsi_105_open (struct usb_serial_port *port, - struct file *filp); -static void klsi_105_close (struct usb_serial_port *port, - struct file *filp); -static int klsi_105_write (struct usb_serial_port *port, - const unsigned char *buf, - int count); -static void klsi_105_write_bulk_callback (struct urb *urb); -static int klsi_105_chars_in_buffer (struct usb_serial_port *port); -static int klsi_105_write_room (struct usb_serial_port *port); - -static void klsi_105_read_bulk_callback (struct urb *urb); -static void klsi_105_set_termios (struct usb_serial_port *port, - struct ktermios *old); -static void klsi_105_throttle (struct usb_serial_port *port); -static void klsi_105_unthrottle (struct usb_serial_port *port); -/* -static void klsi_105_break_ctl (struct usb_serial_port *port, - int break_state ); - */ -static int klsi_105_tiocmget (struct usb_serial_port *port, - struct file *file); -static int klsi_105_tiocmset (struct usb_serial_port *port, - struct file *file, unsigned int set, - unsigned int clear); +static int klsi_105_startup(struct usb_serial *serial); +static void klsi_105_shutdown(struct usb_serial *serial); +static int klsi_105_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void klsi_105_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static int klsi_105_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count); +static void klsi_105_write_bulk_callback(struct urb *urb); +static int klsi_105_chars_in_buffer(struct tty_struct *tty); +static int klsi_105_write_room(struct tty_struct *tty); +static void klsi_105_read_bulk_callback(struct urb *urb); +static void klsi_105_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); +static void klsi_105_throttle(struct tty_struct *tty); +static void klsi_105_unthrottle(struct tty_struct *tty); +static int klsi_105_tiocmget(struct tty_struct *tty, struct file *file); +static int klsi_105_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear); /* * All of the device info needed for the KLSI converters. @@ -109,7 +101,7 @@ static struct usb_device_id id_table [] = { { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, id_table); static struct usb_driver kl5kusb105d_driver = { .name = "kl5kusb105d", @@ -134,7 +126,7 @@ static struct usb_serial_driver kl5kusb105d_device = { .write_bulk_callback = klsi_105_write_bulk_callback, .chars_in_buffer = klsi_105_chars_in_buffer, .write_room = klsi_105_write_room, - .read_bulk_callback =klsi_105_read_bulk_callback, + .read_bulk_callback = klsi_105_read_bulk_callback, .set_termios = klsi_105_set_termios, /*.break_ctl = klsi_105_break_ctl,*/ .tiocmget = klsi_105_tiocmget, @@ -161,7 +153,7 @@ struct klsi_105_private { struct ktermios termios; unsigned long line_state; /* modem line settings */ /* write pool */ - struct urb * write_urb_pool[NUM_URBS]; + struct urb *write_urb_pool[NUM_URBS]; spinlock_t lock; unsigned long bytes_in; unsigned long bytes_out; @@ -180,15 +172,15 @@ static int klsi_105_chg_port_settings(struct usb_serial_port *port, { int rc; - rc = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - KL5KUSB105A_SIO_SET_DATA, - USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_INTERFACE, - 0, /* value */ - 0, /* index */ - settings, - sizeof(struct klsi_105_port_settings), - KLSI_TIMEOUT); + rc = usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + KL5KUSB105A_SIO_SET_DATA, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_INTERFACE, + 0, /* value */ + 0, /* index */ + settings, + sizeof(struct klsi_105_port_settings), + KLSI_TIMEOUT); if (rc < 0) err("Change port settings failed (error = %d)", rc); info("%s - %d byte block, baudrate %x, databits %d, u1 %d, u2 %d", @@ -196,7 +188,7 @@ static int klsi_105_chg_port_settings(struct usb_serial_port *port, settings->pktlen, settings->baudrate, settings->databits, settings->unknown1, settings->unknown2); - return rc; + return rc; } /* klsi_105_chg_port_settings */ /* translate a 16-bit status value from the device to linux's TIO bits */ @@ -210,9 +202,9 @@ static unsigned long klsi_105_status2linestate(const __u16 status) return res; } -/* +/* * Read line control via vendor command and return result through - * *line_state_p + * *line_state_p */ /* It seems that the status buffer has always only 2 bytes length */ #define KLSI_STATUSBUF_LEN 2 @@ -220,14 +212,14 @@ static int klsi_105_get_line_state(struct usb_serial_port *port, unsigned long *line_state_p) { int rc; - __u8 status_buf[KLSI_STATUSBUF_LEN] = { -1,-1}; + __u8 status_buf[KLSI_STATUSBUF_LEN] = { -1, -1}; __u16 status; info("%s - sending SIO Poll request", __func__); - rc = usb_control_msg(port->serial->dev, + rc = usb_control_msg(port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0), KL5KUSB105A_SIO_POLL, - USB_TYPE_VENDOR | USB_DIR_IN, + USB_TYPE_VENDOR | USB_DIR_IN, 0, /* value */ 0, /* index */ status_buf, KLSI_STATUSBUF_LEN, @@ -236,15 +228,14 @@ 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 = le16_to_cpu(get_unaligned((__le16 *)status_buf)); + status = get_unaligned_le16(status_buf); info("%s - read status %x %x", __func__, status_buf[0], status_buf[1]); *line_state_p = klsi_105_status2linestate(status); } - - return rc; + return rc; } @@ -252,7 +243,7 @@ static int klsi_105_get_line_state(struct usb_serial_port *port, * Driver's tty interface functions */ -static int klsi_105_startup (struct usb_serial *serial) +static int klsi_105_startup(struct usb_serial *serial) { struct klsi_105_private *priv; int i, j; @@ -262,7 +253,7 @@ static int klsi_105_startup (struct usb_serial *serial) */ /* allocate the private data structure */ - for (i=0; i<serial->num_ports; i++) { + for (i = 0; i < serial->num_ports; i++) { priv = kmalloc(sizeof(struct klsi_105_private), GFP_KERNEL); if (!priv) { @@ -283,9 +274,9 @@ static int klsi_105_startup (struct usb_serial *serial) priv->bytes_out = 0; usb_set_serial_port_data(serial->port[i], priv); - spin_lock_init (&priv->lock); - for (j=0; j<NUM_URBS; j++) { - struct urb* urb = usb_alloc_urb(0, GFP_KERNEL); + spin_lock_init(&priv->lock); + for (j = 0; j < NUM_URBS; j++) { + struct urb *urb = usb_alloc_urb(0, GFP_KERNEL); priv->write_urb_pool[j] = urb; if (urb == NULL) { @@ -293,10 +284,11 @@ static int klsi_105_startup (struct usb_serial *serial) goto err_cleanup; } - urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, - GFP_KERNEL); + urb->transfer_buffer = + kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); if (!urb->transfer_buffer) { - err("%s - out of memory for urb buffers.", __func__); + err("%s - out of memory for urb buffers.", + __func__); goto err_cleanup; } } @@ -304,13 +296,13 @@ static int klsi_105_startup (struct usb_serial *serial) /* priv->termios is left uninitalized until port opening */ init_waitqueue_head(&serial->port[i]->write_wait); } - + return 0; err_cleanup: for (; i >= 0; i--) { priv = usb_get_serial_port_data(serial->port[i]); - for (j=0; j < NUM_URBS; j++) { + 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]); @@ -322,22 +314,23 @@ err_cleanup: } /* klsi_105_startup */ -static void klsi_105_shutdown (struct usb_serial *serial) +static void klsi_105_shutdown(struct usb_serial *serial) { int i; - + dbg("%s", __func__); /* stop reads and writes on all ports */ - for (i=0; i < serial->num_ports; ++i) { - struct klsi_105_private *priv = usb_get_serial_port_data(serial->port[i]); + for (i = 0; i < serial->num_ports; ++i) { + struct klsi_105_private *priv = + usb_get_serial_port_data(serial->port[i]); unsigned long flags; if (priv) { /* kill our write urb pool */ int j; struct urb **write_urbs = priv->write_urb_pool; - spin_lock_irqsave(&priv->lock,flags); + spin_lock_irqsave(&priv->lock, flags); for (j = 0; j < NUM_URBS; j++) { if (write_urbs[j]) { @@ -349,19 +342,18 @@ static void klsi_105_shutdown (struct usb_serial *serial) * oopses. */ /* usb_kill_urb(write_urbs[j]); */ kfree(write_urbs[j]->transfer_buffer); - usb_free_urb (write_urbs[j]); + usb_free_urb(write_urbs[j]); } } - - spin_unlock_irqrestore (&priv->lock, flags); - + spin_unlock_irqrestore(&priv->lock, flags); kfree(priv); usb_set_serial_port_data(serial->port[i], NULL); } } } /* klsi_105_shutdown */ -static int klsi_105_open (struct usb_serial_port *port, struct file *filp) +static int klsi_105_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct klsi_105_private *priv = usb_get_serial_port_data(port); int retval = 0; @@ -375,11 +367,11 @@ static int klsi_105_open (struct usb_serial_port *port, struct file *filp) /* force low_latency on so that our tty_push actually forces * the data through - * port->tty->low_latency = 1; */ + * tty->low_latency = 1; */ /* Do a defined restart: * Set up sane default baud rate and send the 'READ_ON' - * vendor command. + * vendor command. * FIXME: set modem line control (how?) * Then read the modem line control and store values in * priv->line_state. @@ -390,24 +382,24 @@ static int klsi_105_open (struct usb_serial_port *port, struct file *filp) cfg.unknown1 = 0; cfg.unknown2 = 1; klsi_105_chg_port_settings(port, &cfg); - + /* set up termios structure */ - spin_lock_irqsave (&priv->lock, flags); - priv->termios.c_iflag = port->tty->termios->c_iflag; - priv->termios.c_oflag = port->tty->termios->c_oflag; - priv->termios.c_cflag = port->tty->termios->c_cflag; - priv->termios.c_lflag = port->tty->termios->c_lflag; - for (i=0; i<NCCS; i++) - priv->termios.c_cc[i] = port->tty->termios->c_cc[i]; + spin_lock_irqsave(&priv->lock, flags); + priv->termios.c_iflag = tty->termios->c_iflag; + priv->termios.c_oflag = tty->termios->c_oflag; + priv->termios.c_cflag = tty->termios->c_cflag; + priv->termios.c_lflag = tty->termios->c_lflag; + for (i = 0; i < NCCS; i++) + priv->termios.c_cc[i] = tty->termios->c_cc[i]; priv->cfg.pktlen = cfg.pktlen; priv->cfg.baudrate = cfg.baudrate; priv->cfg.databits = cfg.databits; priv->cfg.unknown1 = cfg.unknown1; priv->cfg.unknown2 = cfg.unknown2; - spin_unlock_irqrestore (&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); /* READ_ON and urb submission */ - usb_fill_bulk_urb(port->read_urb, port->serial->dev, + usb_fill_bulk_urb(port->read_urb, port->serial->dev, usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), port->read_urb->transfer_buffer, @@ -423,7 +415,7 @@ static int klsi_105_open (struct usb_serial_port *port, struct file *filp) } rc = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev,0), + usb_sndctrlpipe(port->serial->dev, 0), KL5KUSB105A_SIO_CONFIGURE, USB_TYPE_VENDOR|USB_DIR_OUT|USB_RECIP_INTERFACE, KL5KUSB105A_SIO_CONFIGURE_READ_ON, @@ -434,14 +426,14 @@ static int klsi_105_open (struct usb_serial_port *port, struct file *filp) if (rc < 0) { err("Enabling read failed (error = %d)", rc); retval = rc; - } else + } else dbg("%s - enabled reading", __func__); rc = klsi_105_get_line_state(port, &line_state); if (rc >= 0) { - spin_lock_irqsave (&priv->lock, flags); + spin_lock_irqsave(&priv->lock, flags); priv->line_state = line_state; - spin_unlock_irqrestore (&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); dbg("%s - read line state 0x%lx", __func__, line_state); retval = 0; } else @@ -452,7 +444,8 @@ exit: } /* klsi_105_open */ -static void klsi_105_close (struct usb_serial_port *port, struct file *filp) +static void klsi_105_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct klsi_105_private *priv = usb_get_serial_port_data(port); int rc; @@ -462,14 +455,14 @@ static void klsi_105_close (struct usb_serial_port *port, struct file *filp) mutex_lock(&port->serial->disc_mutex); if (!port->serial->disconnected) { /* send READ_OFF */ - rc = usb_control_msg (port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - KL5KUSB105A_SIO_CONFIGURE, - USB_TYPE_VENDOR | USB_DIR_OUT, - KL5KUSB105A_SIO_CONFIGURE_READ_OFF, - 0, /* index */ - NULL, 0, - KLSI_TIMEOUT); + rc = usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + KL5KUSB105A_SIO_CONFIGURE, + USB_TYPE_VENDOR | USB_DIR_OUT, + KL5KUSB105A_SIO_CONFIGURE_READ_OFF, + 0, /* index */ + NULL, 0, + KLSI_TIMEOUT); if (rc < 0) err("Disabling read failed (error = %d)", rc); } @@ -482,23 +475,24 @@ static void klsi_105_close (struct usb_serial_port *port, struct file *filp) /* FIXME */ /* wgg - do I need this? I think so. */ usb_kill_urb(port->interrupt_in_urb); - info("kl5kusb105 port stats: %ld bytes in, %ld bytes out", priv->bytes_in, priv->bytes_out); + info("kl5kusb105 port stats: %ld bytes in, %ld bytes out", + priv->bytes_in, priv->bytes_out); } /* klsi_105_close */ /* We need to write a complete 64-byte data block and encode the - * number actually sent in the first double-byte, LSB-order. That + * number actually sent in the first double-byte, LSB-order. That * leaves at most 62 bytes of payload. */ #define KLSI_105_DATA_OFFSET 2 /* in the bulk urb data block */ -static int klsi_105_write (struct usb_serial_port *port, - const unsigned char *buf, int count) +static int klsi_105_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count) { struct klsi_105_private *priv = usb_get_serial_port_data(port); int result, size; - int bytes_sent=0; + int bytes_sent = 0; dbg("%s - port %d", __func__, port->number); @@ -507,34 +501,37 @@ static int klsi_105_write (struct usb_serial_port *port, struct urb *urb = NULL; unsigned long flags; int i; - /* since the pool is per-port we might not need the spin lock !? */ - spin_lock_irqsave (&priv->lock, flags); - for (i=0; i<NUM_URBS; i++) { + /* since the pool is per-port we might not need + the spin lock !? */ + spin_lock_irqsave(&priv->lock, flags); + for (i = 0; i < NUM_URBS; i++) { if (priv->write_urb_pool[i]->status != -EINPROGRESS) { urb = priv->write_urb_pool[i]; dbg("%s - using pool URB %d", __func__, i); break; } } - spin_unlock_irqrestore (&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); - if (urb==NULL) { + if (urb == NULL) { dbg("%s - no more free urbs", __func__); goto exit; } if (urb->transfer_buffer == NULL) { - urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_ATOMIC); + urb->transfer_buffer = + kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_ATOMIC); if (urb->transfer_buffer == NULL) { err("%s - no more kernel memory...", __func__); goto exit; } } - size = min (count, port->bulk_out_size - KLSI_105_DATA_OFFSET); - size = min (size, URB_TRANSFER_BUFFER_SIZE - KLSI_105_DATA_OFFSET); + size = min(count, port->bulk_out_size - KLSI_105_DATA_OFFSET); + size = min(size, URB_TRANSFER_BUFFER_SIZE - + KLSI_105_DATA_OFFSET); - memcpy (urb->transfer_buffer + KLSI_105_DATA_OFFSET, buf, size); + memcpy(urb->transfer_buffer + KLSI_105_DATA_OFFSET, buf, size); /* write payload size into transfer buffer */ ((__u8 *)urb->transfer_buffer)[0] = (__u8) (size & 0xFF); @@ -552,7 +549,8 @@ static int klsi_105_write (struct usb_serial_port *port, /* send the data out the bulk port */ result = usb_submit_urb(urb, GFP_ATOMIC); if (result) { - err("%s - failed submitting write urb, error %d", __func__, result); + err("%s - failed submitting write urb, error %d", + __func__, result); goto exit; } buf += size; @@ -561,12 +559,12 @@ static int klsi_105_write (struct usb_serial_port *port, } exit: /* lockless, but it's for debug info only... */ - priv->bytes_out+=bytes_sent; + priv->bytes_out += bytes_sent; return bytes_sent; /* that's how much we wrote */ } /* klsi_105_write */ -static void klsi_105_write_bulk_callback ( struct urb *urb) +static void klsi_105_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; int status = urb->status; @@ -584,50 +582,50 @@ static void klsi_105_write_bulk_callback ( struct urb *urb) /* return number of characters currently in the writing process */ -static int klsi_105_chars_in_buffer (struct usb_serial_port *port) +static int klsi_105_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int chars = 0; int i; unsigned long flags; struct klsi_105_private *priv = usb_get_serial_port_data(port); - spin_lock_irqsave (&priv->lock, flags); + spin_lock_irqsave(&priv->lock, flags); for (i = 0; i < NUM_URBS; ++i) { - if (priv->write_urb_pool[i]->status == -EINPROGRESS) { + if (priv->write_urb_pool[i]->status == -EINPROGRESS) chars += URB_TRANSFER_BUFFER_SIZE; - } } - spin_unlock_irqrestore (&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); dbg("%s - returns %d", __func__, chars); - return (chars); + return chars; } -static int klsi_105_write_room (struct usb_serial_port *port) +static int klsi_105_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; unsigned long flags; int i; int room = 0; struct klsi_105_private *priv = usb_get_serial_port_data(port); - spin_lock_irqsave (&priv->lock, flags); + spin_lock_irqsave(&priv->lock, flags); for (i = 0; i < NUM_URBS; ++i) { - if (priv->write_urb_pool[i]->status != -EINPROGRESS) { + if (priv->write_urb_pool[i]->status != -EINPROGRESS) room += URB_TRANSFER_BUFFER_SIZE; - } } - spin_unlock_irqrestore (&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); dbg("%s - returns %d", __func__, room); - return (room); + return room; } -static void klsi_105_read_bulk_callback (struct urb *urb) +static void klsi_105_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct klsi_105_private *priv = usb_get_serial_port_data(port); @@ -660,13 +658,13 @@ static void klsi_105_read_bulk_callback (struct urb *urb) } else { int bytes_sent = ((__u8 *) data)[0] + ((unsigned int) ((__u8 *) data)[1] << 8); - tty = port->tty; + tty = port->port.tty; /* we should immediately resubmit the URB, before attempting * to pass the data on to the tty layer. But that needs locking * against re-entry an then mixed-up data because of * intermixed tty_flip_buffer_push()s * FIXME - */ + */ usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); @@ -686,7 +684,7 @@ static void klsi_105_read_bulk_callback (struct urb *urb) priv->bytes_in += bytes_sent; } /* Continue trying to always read */ - usb_fill_bulk_urb(port->read_urb, port->serial->dev, + usb_fill_bulk_urb(port->read_urb, port->serial->dev, usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), port->read_urb->transfer_buffer, @@ -695,15 +693,16 @@ static void klsi_105_read_bulk_callback (struct urb *urb) port); rc = usb_submit_urb(port->read_urb, GFP_ATOMIC); if (rc) - err("%s - failed resubmitting read urb, error %d", __func__, rc); + err("%s - failed resubmitting read urb, error %d", + __func__, rc); } /* klsi_105_read_bulk_callback */ -static void klsi_105_set_termios (struct usb_serial_port *port, - struct ktermios *old_termios) +static void klsi_105_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, + struct ktermios *old_termios) { struct klsi_105_private *priv = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; unsigned int iflag = tty->termios->c_iflag; unsigned int old_iflag = old_termios->c_iflag; unsigned int cflag = tty->termios->c_cflag; @@ -711,65 +710,63 @@ static void klsi_105_set_termios (struct usb_serial_port *port, struct klsi_105_port_settings cfg; unsigned long flags; speed_t baud; - + /* lock while we are modifying the settings */ - spin_lock_irqsave (&priv->lock, flags); - + spin_lock_irqsave(&priv->lock, flags); + /* * Update baud rate */ baud = tty_get_baud_rate(tty); - if( (cflag & CBAUD) != (old_cflag & CBAUD) ) { - /* reassert DTR and (maybe) RTS on transition from B0 */ - if( (old_cflag & CBAUD) == B0 ) { + if ((cflag & CBAUD) != (old_cflag & CBAUD)) { + /* reassert DTR and (maybe) RTS on transition from B0 */ + if ((old_cflag & CBAUD) == B0) { dbg("%s: baud was B0", __func__); #if 0 priv->control_state |= TIOCM_DTR; /* don't set RTS if using hardware flow control */ - if (!(old_cflag & CRTSCTS)) { + if (!(old_cflag & CRTSCTS)) priv->control_state |= TIOCM_RTS; - } mct_u232_set_modem_ctrl(serial, priv->control_state); #endif } } - switch(baud) { - case 0: /* handled below */ - break; - case 1200: - priv->cfg.baudrate = kl5kusb105a_sio_b1200; - break; - case 2400: - priv->cfg.baudrate = kl5kusb105a_sio_b2400; - break; - case 4800: - priv->cfg.baudrate = kl5kusb105a_sio_b4800; - break; - case 9600: - priv->cfg.baudrate = kl5kusb105a_sio_b9600; - break; - case 19200: - priv->cfg.baudrate = kl5kusb105a_sio_b19200; - break; - case 38400: - priv->cfg.baudrate = kl5kusb105a_sio_b38400; - break; - case 57600: - priv->cfg.baudrate = kl5kusb105a_sio_b57600; - break; - case 115200: - priv->cfg.baudrate = kl5kusb105a_sio_b115200; - break; - default: - dbg("KLSI USB->Serial converter:" - " unsupported baudrate request, using default" - " of 9600"); + switch (baud) { + case 0: /* handled below */ + break; + case 1200: + priv->cfg.baudrate = kl5kusb105a_sio_b1200; + break; + case 2400: + priv->cfg.baudrate = kl5kusb105a_sio_b2400; + break; + case 4800: + priv->cfg.baudrate = kl5kusb105a_sio_b4800; + break; + case 9600: + priv->cfg.baudrate = kl5kusb105a_sio_b9600; + break; + case 19200: + priv->cfg.baudrate = kl5kusb105a_sio_b19200; + break; + case 38400: + priv->cfg.baudrate = kl5kusb105a_sio_b38400; + break; + case 57600: + priv->cfg.baudrate = kl5kusb105a_sio_b57600; + break; + case 115200: + priv->cfg.baudrate = kl5kusb105a_sio_b115200; + break; + default: + dbg("KLSI USB->Serial converter:" + " unsupported baudrate request, using default of 9600"); priv->cfg.baudrate = kl5kusb105a_sio_b9600; - baud = 9600; - break; + baud = 9600; + break; } - if ((cflag & CBAUD) == B0 ) { + if ((cflag & CBAUD) == B0) { dbg("%s: baud is B0", __func__); /* Drop RTS and DTR */ /* maybe this should be simulated by sending read @@ -778,7 +775,7 @@ static void klsi_105_set_termios (struct usb_serial_port *port, ; #if 0 priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); - mct_u232_set_modem_ctrl(serial, priv->control_state); + mct_u232_set_modem_ctrl(serial, priv->control_state); #endif } tty_encode_baud_rate(tty, baud, baud); @@ -788,11 +785,11 @@ static void klsi_105_set_termios (struct usb_serial_port *port, switch (cflag & CSIZE) { case CS5: dbg("%s - 5 bits/byte not supported", __func__); - spin_unlock_irqrestore (&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); return ; case CS6: dbg("%s - 6 bits/byte not supported", __func__); - spin_unlock_irqrestore (&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); return ; case CS7: priv->cfg.databits = kl5kusb105a_dtb_7; @@ -811,8 +808,7 @@ static void klsi_105_set_termios (struct usb_serial_port *port, * Update line control register (LCR) */ if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD)) - || (cflag & CSTOPB) != (old_cflag & CSTOPB) ) { - + || (cflag & CSTOPB) != (old_cflag & CSTOPB)) { /* Not currently supported */ tty->termios->c_cflag &= ~(PARENB|PARODD|CSTOPB); #if 0 @@ -833,20 +829,18 @@ static void klsi_105_set_termios (struct usb_serial_port *port, #endif ; } - /* * Set flow control: well, I do not really now how to handle DTR/RTS. * Just do what we have seen with SniffUSB on Win98. */ - if( (iflag & IXOFF) != (old_iflag & IXOFF) + if ((iflag & IXOFF) != (old_iflag & IXOFF) || (iflag & IXON) != (old_iflag & IXON) - || (cflag & CRTSCTS) != (old_cflag & CRTSCTS) ) { - + || (cflag & CRTSCTS) != (old_cflag & CRTSCTS)) { /* Not currently supported */ tty->termios->c_cflag &= ~CRTSCTS; /* Drop DTR/RTS if no flow control otherwise assert */ #if 0 - if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS) ) + if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS)) priv->control_state |= TIOCM_DTR | TIOCM_RTS; else priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); @@ -854,19 +848,21 @@ static void klsi_105_set_termios (struct usb_serial_port *port, #endif ; } - memcpy (&cfg, &priv->cfg, sizeof(cfg)); - spin_unlock_irqrestore (&priv->lock, flags); - + memcpy(&cfg, &priv->cfg, sizeof(cfg)); + spin_unlock_irqrestore(&priv->lock, flags); + /* now commit changes to device */ klsi_105_chg_port_settings(port, &cfg); } /* klsi_105_set_termios */ #if 0 -static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state ) +static void mct_u232_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; - struct mct_u232_private *priv = (struct mct_u232_private *)port->private; + struct mct_u232_private *priv = + (struct mct_u232_private *)port->private; unsigned char lcr = priv->last_lcr; dbg("%sstate=%d", __func__, break_state); @@ -878,8 +874,9 @@ static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state ) } /* mct_u232_break_ctl */ #endif -static int klsi_105_tiocmget (struct usb_serial_port *port, struct file *file) +static int klsi_105_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct klsi_105_private *priv = usb_get_serial_port_data(port); unsigned long flags; int rc; @@ -893,18 +890,18 @@ static int klsi_105_tiocmget (struct usb_serial_port *port, struct file *file) return rc; } - spin_lock_irqsave (&priv->lock, flags); + spin_lock_irqsave(&priv->lock, flags); priv->line_state = line_state; - spin_unlock_irqrestore (&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); dbg("%s - read line state 0x%lx", __func__, line_state); return (int)line_state; } -static int klsi_105_tiocmset (struct usb_serial_port *port, struct file *file, - unsigned int set, unsigned int clear) +static int klsi_105_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) { int retval = -EINVAL; - + dbg("%s", __func__); /* if this ever gets implemented, it should be done something like this: @@ -929,14 +926,16 @@ static int klsi_105_tiocmset (struct usb_serial_port *port, struct file *file, return retval; } -static void klsi_105_throttle (struct usb_serial_port *port) +static void klsi_105_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); usb_kill_urb(port->read_urb); } -static void klsi_105_unthrottle (struct usb_serial_port *port) +static void klsi_105_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int result; dbg("%s - port %d", __func__, port->number); @@ -950,7 +949,7 @@ static void klsi_105_unthrottle (struct usb_serial_port *port) -static int __init klsi_105_init (void) +static int __init klsi_105_init(void) { int retval; retval = usb_serial_register(&kl5kusb105d_device); @@ -969,19 +968,19 @@ failed_usb_serial_register: } -static void __exit klsi_105_exit (void) +static void __exit klsi_105_exit(void) { - usb_deregister (&kl5kusb105d_driver); - usb_serial_deregister (&kl5kusb105d_device); + usb_deregister(&kl5kusb105d_driver); + usb_serial_deregister(&kl5kusb105d_device); } -module_init (klsi_105_init); -module_exit (klsi_105_exit); +module_init(klsi_105_init); +module_exit(klsi_105_exit); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_LICENSE("GPL"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 693f00da7c03..deba28ec77e8 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -1,7 +1,7 @@ /* * KOBIL USB Smart Card Terminal Driver * - * Copyright (C) 2002 KOBIL Systems GmbH + * Copyright (C) 2002 KOBIL Systems GmbH * Author: Thomas Wahrenbruch * * Contact: linuxusb@kobil.de @@ -20,7 +20,7 @@ * * Supported readers: USB TWIN, KAAN Standard Plus and SecOVID Reader Plus * (Adapter K), B1 Professional and KAAN Professional (Adapter B) - * + * * (21/05/2004) tw * Fix bug with P'n'P readers * @@ -44,7 +44,7 @@ #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/ioctl.h> @@ -68,21 +68,24 @@ static int debug; /* Function prototypes */ -static int kobil_startup (struct usb_serial *serial); -static void kobil_shutdown (struct usb_serial *serial); -static int kobil_open (struct usb_serial_port *port, struct file *filp); -static void kobil_close (struct usb_serial_port *port, struct file *filp); -static int kobil_write (struct usb_serial_port *port, +static int kobil_startup(struct usb_serial *serial); +static void kobil_shutdown(struct usb_serial *serial); +static int kobil_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void kobil_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); +static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); -static int kobil_write_room(struct usb_serial_port *port); -static int kobil_ioctl(struct usb_serial_port *port, struct file *file, +static int kobil_write_room(struct tty_struct *tty); +static int kobil_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); -static int kobil_tiocmget(struct usb_serial_port *port, struct file *file); -static int kobil_tiocmset(struct usb_serial_port *port, struct file *file, +static int kobil_tiocmget(struct tty_struct *tty, struct file *file); +static int kobil_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); -static void kobil_read_int_callback( struct urb *urb ); -static void kobil_write_callback( struct urb *purb ); -static void kobil_set_termios(struct usb_serial_port *port, struct ktermios *old); +static void kobil_read_int_callback(struct urb *urb); +static void kobil_write_callback(struct urb *purb); +static void kobil_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); static struct usb_device_id id_table [] = { @@ -94,7 +97,7 @@ static struct usb_device_id id_table [] = { }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, id_table); static struct usb_driver kobil_driver = { .name = "kobil", @@ -131,14 +134,14 @@ static struct usb_serial_driver kobil_device = { struct kobil_private { int write_int_endpoint_address; int read_int_endpoint_address; - unsigned char buf[KOBIL_BUF_LENGTH]; // buffer for the APDU to send - int filled; // index of the last char in buf - int cur_pos; // index of the next char to send in buf + unsigned char buf[KOBIL_BUF_LENGTH]; /* buffer for the APDU to send */ + int filled; /* index of the last char in buf */ + int cur_pos; /* index of the next char to send in buf */ __u16 device_type; }; -static int kobil_startup (struct usb_serial *serial) +static int kobil_startup(struct usb_serial *serial) { int i; struct kobil_private *priv; @@ -149,20 +152,20 @@ static int kobil_startup (struct usb_serial *serial) struct usb_host_endpoint *endpoint; priv = kmalloc(sizeof(struct kobil_private), GFP_KERNEL); - if (!priv){ + if (!priv) return -ENOMEM; - } priv->filled = 0; priv->cur_pos = 0; priv->device_type = le16_to_cpu(serial->dev->descriptor.idProduct); - switch (priv->device_type){ + switch (priv->device_type) { case KOBIL_ADAPTER_B_PRODUCT_ID: printk(KERN_DEBUG "KOBIL B1 PRO / KAAN PRO detected\n"); break; case KOBIL_ADAPTER_K_PRODUCT_ID: - printk(KERN_DEBUG "KOBIL KAAN Standard Plus / SecOVID Reader Plus detected\n"); + printk(KERN_DEBUG + "KOBIL KAAN Standard Plus / SecOVID Reader Plus detected\n"); break; case KOBIL_USBTWIN_PRODUCT_ID: printk(KERN_DEBUG "KOBIL USBTWIN detected\n"); @@ -173,44 +176,48 @@ static int kobil_startup (struct usb_serial *serial) } usb_set_serial_port_data(serial->port[0], priv); - // search for the necessary endpoints + /* search for the necessary endpoints */ pdev = serial->dev; - actconfig = pdev->actconfig; - interface = actconfig->interface[0]; + actconfig = pdev->actconfig; + interface = actconfig->interface[0]; altsetting = interface->cur_altsetting; - endpoint = altsetting->endpoint; - - for (i = 0; i < altsetting->desc.bNumEndpoints; i++) { + endpoint = altsetting->endpoint; + + for (i = 0; i < altsetting->desc.bNumEndpoints; i++) { endpoint = &altsetting->endpoint[i]; if (usb_endpoint_is_int_out(&endpoint->desc)) { - dbg("%s Found interrupt out endpoint. Address: %d", __func__, endpoint->desc.bEndpointAddress); - priv->write_int_endpoint_address = endpoint->desc.bEndpointAddress; - } + dbg("%s Found interrupt out endpoint. Address: %d", + __func__, endpoint->desc.bEndpointAddress); + priv->write_int_endpoint_address = + endpoint->desc.bEndpointAddress; + } if (usb_endpoint_is_int_in(&endpoint->desc)) { - dbg("%s Found interrupt in endpoint. Address: %d", __func__, endpoint->desc.bEndpointAddress); - priv->read_int_endpoint_address = endpoint->desc.bEndpointAddress; - } + dbg("%s Found interrupt in endpoint. Address: %d", + __func__, endpoint->desc.bEndpointAddress); + priv->read_int_endpoint_address = + endpoint->desc.bEndpointAddress; + } } return 0; } -static void kobil_shutdown (struct usb_serial *serial) +static void kobil_shutdown(struct usb_serial *serial) { int i; dbg("%s - port %d", __func__, serial->port[0]->number); - for (i=0; i < serial->num_ports; ++i) { - while (serial->port[i]->open_count > 0) { - kobil_close (serial->port[i], NULL); - } + for (i = 0; i < serial->num_ports; ++i) { + while (serial->port[i]->port.count > 0) + kobil_close(NULL, serial->port[i], NULL); kfree(usb_get_serial_port_data(serial->port[i])); usb_set_serial_port_data(serial->port[i], NULL); } } -static int kobil_open (struct usb_serial_port *port, struct file *filp) +static int kobil_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int result = 0; struct kobil_private *priv; @@ -221,7 +228,7 @@ static int kobil_open (struct usb_serial_port *port, struct file *filp) dbg("%s - port %d", __func__, port->number); priv = usb_get_serial_port_data(port); - // someone sets the dev to 0 if the close method has been called + /* someone sets the dev to 0 if the close method has been called */ port->interrupt_in_urb->dev = port->serial->dev; @@ -229,100 +236,115 @@ static int kobil_open (struct usb_serial_port *port, struct file *filp) * the data through, otherwise it is scheduled, and with high * data rates (like with OHCI) data can get lost. */ - port->tty->low_latency = 1; - - // without this, every push_tty_char is echoed :-( - port->tty->termios->c_lflag = 0; - port->tty->termios->c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN | XCASE); - port->tty->termios->c_iflag = IGNBRK | IGNPAR | IXOFF; - port->tty->termios->c_oflag &= ~ONLCR; // do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) - - // allocate memory for transfer buffer + if (tty) { + tty->low_latency = 1; + + /* Default to echo off and other sane device settings */ + tty->termios->c_lflag = 0; + tty->termios->c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN | + XCASE); + tty->termios->c_iflag = IGNBRK | IGNPAR | IXOFF; + /* do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) */ + tty->termios->c_oflag &= ~ONLCR; + } + /* allocate memory for transfer buffer */ transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL); - if (! transfer_buffer) { + if (!transfer_buffer) return -ENOMEM; - } - - // allocate write_urb - if (!port->write_urb) { - dbg("%s - port %d Allocating port->write_urb", __func__, port->number); - port->write_urb = usb_alloc_urb(0, GFP_KERNEL); + + /* allocate write_urb */ + if (!port->write_urb) { + dbg("%s - port %d Allocating port->write_urb", + __func__, port->number); + port->write_urb = usb_alloc_urb(0, GFP_KERNEL); if (!port->write_urb) { - dbg("%s - port %d usb_alloc_urb failed", __func__, port->number); + dbg("%s - port %d usb_alloc_urb failed", + __func__, port->number); kfree(transfer_buffer); return -ENOMEM; } } - // allocate memory for write_urb transfer buffer - port->write_urb->transfer_buffer = kmalloc(write_urb_transfer_buffer_length, GFP_KERNEL); - if (! port->write_urb->transfer_buffer) { + /* allocate memory for write_urb transfer buffer */ + port->write_urb->transfer_buffer = + kmalloc(write_urb_transfer_buffer_length, GFP_KERNEL); + if (!port->write_urb->transfer_buffer) { kfree(transfer_buffer); usb_free_urb(port->write_urb); port->write_urb = NULL; return -ENOMEM; - } - - // get hardware version - result = usb_control_msg( port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0 ), - SUSBCRequest_GetMisc, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, - SUSBCR_MSC_GetHWVersion, - 0, - transfer_buffer, - transfer_buffer_length, - KOBIL_TIMEOUT + } + + /* get hardware version */ + result = usb_control_msg(port->serial->dev, + usb_rcvctrlpipe(port->serial->dev, 0), + SUSBCRequest_GetMisc, + USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, + SUSBCR_MSC_GetHWVersion, + 0, + transfer_buffer, + transfer_buffer_length, + KOBIL_TIMEOUT + ); + dbg("%s - port %d Send get_HW_version URB returns: %i", + __func__, port->number, result); + dbg("Harware version: %i.%i.%i", + transfer_buffer[0], transfer_buffer[1], transfer_buffer[2]); + + /* get firmware version */ + result = usb_control_msg(port->serial->dev, + usb_rcvctrlpipe(port->serial->dev, 0), + SUSBCRequest_GetMisc, + USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, + SUSBCR_MSC_GetFWVersion, + 0, + transfer_buffer, + transfer_buffer_length, + KOBIL_TIMEOUT + ); + dbg("%s - port %d Send get_FW_version URB returns: %i", + __func__, port->number, result); + dbg("Firmware version: %i.%i.%i", + transfer_buffer[0], transfer_buffer[1], transfer_buffer[2]); + + if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || + priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) { + /* Setting Baudrate, Parity and Stopbits */ + result = usb_control_msg(port->serial->dev, + usb_rcvctrlpipe(port->serial->dev, 0), + SUSBCRequest_SetBaudRateParityAndStopBits, + USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, + SUSBCR_SBR_9600 | SUSBCR_SPASB_EvenParity | + SUSBCR_SPASB_1StopBit, + 0, + transfer_buffer, + 0, + KOBIL_TIMEOUT ); - dbg("%s - port %d Send get_HW_version URB returns: %i", __func__, port->number, result); - dbg("Harware version: %i.%i.%i", transfer_buffer[0], transfer_buffer[1], transfer_buffer[2] ); - - // get firmware version - result = usb_control_msg( port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0 ), - SUSBCRequest_GetMisc, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, - SUSBCR_MSC_GetFWVersion, - 0, - transfer_buffer, - transfer_buffer_length, - KOBIL_TIMEOUT + dbg("%s - port %d Send set_baudrate URB returns: %i", + __func__, port->number, result); + + /* reset all queues */ + result = usb_control_msg(port->serial->dev, + usb_rcvctrlpipe(port->serial->dev, 0), + SUSBCRequest_Misc, + USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, + SUSBCR_MSC_ResetAllQueues, + 0, + transfer_buffer, + 0, + KOBIL_TIMEOUT ); - dbg("%s - port %d Send get_FW_version URB returns: %i", __func__, port->number, result); - dbg("Firmware version: %i.%i.%i", transfer_buffer[0], transfer_buffer[1], transfer_buffer[2] ); - - if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) { - // Setting Baudrate, Parity and Stopbits - result = usb_control_msg( port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0 ), - SUSBCRequest_SetBaudRateParityAndStopBits, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - SUSBCR_SBR_9600 | SUSBCR_SPASB_EvenParity | SUSBCR_SPASB_1StopBit, - 0, - transfer_buffer, - 0, - KOBIL_TIMEOUT - ); - dbg("%s - port %d Send set_baudrate URB returns: %i", __func__, port->number, result); - - // reset all queues - result = usb_control_msg( port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0 ), - SUSBCRequest_Misc, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - SUSBCR_MSC_ResetAllQueues, - 0, - transfer_buffer, - 0, - KOBIL_TIMEOUT - ); - dbg("%s - port %d Send reset_all_queues URB returns: %i", __func__, port->number, result); + dbg("%s - port %d Send reset_all_queues URB returns: %i", + __func__, port->number, result); } - if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || + if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || + priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { - // start reading (Adapter B 'cause PNP string) - result = usb_submit_urb( port->interrupt_in_urb, GFP_ATOMIC ); - dbg("%s - port %d Send read URB returns: %i", __func__, port->number, result); + /* start reading (Adapter B 'cause PNP string) */ + result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); + dbg("%s - port %d Send read URB returns: %i", + __func__, port->number, result); } kfree(transfer_buffer); @@ -330,13 +352,14 @@ static int kobil_open (struct usb_serial_port *port, struct file *filp) } -static void kobil_close (struct usb_serial_port *port, struct file *filp) +static void kobil_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { dbg("%s - port %d", __func__, port->number); if (port->write_urb) { usb_kill_urb(port->write_urb); - usb_free_urb( port->write_urb ); + usb_free_urb(port->write_urb); port->write_urb = NULL; } usb_kill_urb(port->interrupt_in_urb); @@ -350,7 +373,7 @@ static void kobil_read_int_callback(struct urb *urb) struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; int status = urb->status; -// char *dbg_data; +/* char *dbg_data; */ dbg("%s - port %d", __func__, port->number); @@ -360,51 +383,53 @@ static void kobil_read_int_callback(struct urb *urb) return; } - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { - // BEGIN DEBUG + /* BEGIN DEBUG */ /* - dbg_data = kzalloc((3 * purb->actual_length + 10) * sizeof(char), GFP_KERNEL); + dbg_data = kzalloc((3 * purb->actual_length + 10) + * sizeof(char), GFP_KERNEL); if (! dbg_data) { - return; + return; } - for (i = 0; i < purb->actual_length; i++) { - sprintf(dbg_data +3*i, "%02X ", data[i]); + for (i = 0; i < purb->actual_length; i++) { + sprintf(dbg_data +3*i, "%02X ", data[i]); } - dbg(" <-- %s", dbg_data ); + dbg(" <-- %s", dbg_data); kfree(dbg_data); */ - // END DEBUG + /* END DEBUG */ tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } - - // someone sets the dev to 0 if the close method has been called + /* someone sets the dev to 0 if the close method has been called */ port->interrupt_in_urb->dev = port->serial->dev; result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); - dbg("%s - port %d Send read URB returns: %i", __func__, port->number, result); + dbg("%s - port %d Send read URB returns: %i", + __func__, port->number, result); } -static void kobil_write_callback( struct urb *purb ) +static void kobil_write_callback(struct urb *purb) { } -static int kobil_write (struct usb_serial_port *port, +static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { int length = 0; int result = 0; int todo = 0; - struct kobil_private * priv; + struct kobil_private *priv; if (count == 0) { - dbg("%s - port %d write request of 0 bytes", __func__, port->number); + dbg("%s - port %d write request of 0 bytes", + __func__, port->number); return 0; } @@ -415,106 +440,113 @@ static int kobil_write (struct usb_serial_port *port, return -ENOMEM; } - // Copy data to buffer - memcpy (priv->buf + priv->filled, buf, count); - - usb_serial_debug_data(debug, &port->dev, __func__, count, priv->buf + priv->filled); - + /* Copy data to buffer */ + memcpy(priv->buf + priv->filled, buf, count); + usb_serial_debug_data(debug, &port->dev, __func__, count, + priv->buf + priv->filled); priv->filled = priv->filled + count; - - // only send complete block. TWIN, KAAN SIM and adapter K use the same protocol. - if ( ((priv->device_type != KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 2) && (priv->filled >= (priv->buf[1] + 3))) || - ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 3) && (priv->filled >= (priv->buf[2] + 4))) ) { - - // stop reading (except TWIN and KAAN SIM) - if ( (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) || (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) ) + /* only send complete block. TWIN, KAAN SIM and adapter K + use the same protocol. */ + if (((priv->device_type != KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 2) && (priv->filled >= (priv->buf[1] + 3))) || + ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 3) && (priv->filled >= (priv->buf[2] + 4)))) { + /* stop reading (except TWIN and KAAN SIM) */ + if ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) + || (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID)) usb_kill_urb(port->interrupt_in_urb); todo = priv->filled - priv->cur_pos; - while(todo > 0) { - // max 8 byte in one urb (endpoint size) + while (todo > 0) { + /* max 8 byte in one urb (endpoint size) */ length = (todo < 8) ? todo : 8; - // copy data to transfer buffer - memcpy(port->write_urb->transfer_buffer, priv->buf + priv->cur_pos, length ); - usb_fill_int_urb( port->write_urb, - port->serial->dev, - usb_sndintpipe(port->serial->dev, priv->write_int_endpoint_address), - port->write_urb->transfer_buffer, - length, - kobil_write_callback, - port, - 8 - ); + /* copy data to transfer buffer */ + memcpy(port->write_urb->transfer_buffer, + priv->buf + priv->cur_pos, length); + usb_fill_int_urb(port->write_urb, + port->serial->dev, + usb_sndintpipe(port->serial->dev, + priv->write_int_endpoint_address), + port->write_urb->transfer_buffer, + length, + kobil_write_callback, + port, + 8 + ); priv->cur_pos = priv->cur_pos + length; - result = usb_submit_urb( port->write_urb, GFP_NOIO ); - dbg("%s - port %d Send write URB returns: %i", __func__, port->number, result); + result = usb_submit_urb(port->write_urb, GFP_NOIO); + dbg("%s - port %d Send write URB returns: %i", + __func__, port->number, result); todo = priv->filled - priv->cur_pos; - if (todo > 0) { + if (todo > 0) msleep(24); - } + } - } // end while - priv->filled = 0; priv->cur_pos = 0; - // someone sets the dev to 0 if the close method has been called + /* someone sets the dev to 0 if the close method + has been called */ port->interrupt_in_urb->dev = port->serial->dev; - - // start reading (except TWIN and KAAN SIM) - if ( (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) || (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) ) { - // someone sets the dev to 0 if the close method has been called + + /* start reading (except TWIN and KAAN SIM) */ + if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || + priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) { + /* someone sets the dev to 0 if the close method has + been called */ port->interrupt_in_urb->dev = port->serial->dev; - - result = usb_submit_urb( port->interrupt_in_urb, GFP_NOIO ); - dbg("%s - port %d Send read URB returns: %i", __func__, port->number, result); + + result = usb_submit_urb(port->interrupt_in_urb, + GFP_NOIO); + dbg("%s - port %d Send read URB returns: %i", + __func__, port->number, result); } } return count; } -static int kobil_write_room (struct usb_serial_port *port) +static int kobil_write_room(struct tty_struct *tty) { - //dbg("%s - port %d", __func__, port->number); + /* dbg("%s - port %d", __func__, port->number); */ + /* FIXME */ return 8; } -static int kobil_tiocmget(struct usb_serial_port *port, struct file *file) +static int kobil_tiocmget(struct tty_struct *tty, struct file *file) { - struct kobil_private * priv; + struct usb_serial_port *port = tty->driver_data; + struct kobil_private *priv; int result; unsigned char *transfer_buffer; int transfer_buffer_length = 8; priv = usb_get_serial_port_data(port); - if ((priv->device_type == KOBIL_USBTWIN_PRODUCT_ID) || (priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID)) { - // This device doesn't support ioctl calls + if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID + || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { + /* This device doesn't support ioctl calls */ return -EINVAL; } - // allocate memory for transfer buffer + /* allocate memory for transfer buffer */ transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL); - if (!transfer_buffer) { + if (!transfer_buffer) return -ENOMEM; - } - result = usb_control_msg( port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0 ), - SUSBCRequest_GetStatusLineState, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, - 0, - 0, - transfer_buffer, - transfer_buffer_length, - KOBIL_TIMEOUT); - - dbg("%s - port %d Send get_status_line_state URB returns: %i. Statusline: %02x", + result = usb_control_msg(port->serial->dev, + usb_rcvctrlpipe(port->serial->dev, 0), + SUSBCRequest_GetStatusLineState, + USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, + 0, + 0, + transfer_buffer, + transfer_buffer_length, + KOBIL_TIMEOUT); + + dbg("%s - port %d Send get_status_line_state URB returns: %i. Statusline: %02x", __func__, port->number, result, transfer_buffer[0]); result = 0; @@ -524,10 +556,11 @@ static int kobil_tiocmget(struct usb_serial_port *port, struct file *file) return result; } -static int kobil_tiocmset(struct usb_serial_port *port, struct file *file, +static int kobil_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { - struct kobil_private * priv; + struct usb_serial_port *port = tty->driver_data; + struct kobil_private *priv; int result; int dtr = 0; int rts = 0; @@ -536,16 +569,16 @@ static int kobil_tiocmset(struct usb_serial_port *port, struct file *file, /* FIXME: locking ? */ priv = usb_get_serial_port_data(port); - if ((priv->device_type == KOBIL_USBTWIN_PRODUCT_ID) || (priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID)) { - // This device doesn't support ioctl calls + if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID + || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { + /* This device doesn't support ioctl calls */ return -EINVAL; } - // allocate memory for transfer buffer + /* allocate memory for transfer buffer */ transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL); - if (! transfer_buffer) { + if (!transfer_buffer) return -ENOMEM; - } if (set & TIOCM_RTS) rts = 1; @@ -558,66 +591,77 @@ static int kobil_tiocmset(struct usb_serial_port *port, struct file *file, if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) { if (dtr != 0) - dbg("%s - port %d Setting DTR", __func__, port->number); + dbg("%s - port %d Setting DTR", + __func__, port->number); else - dbg("%s - port %d Clearing DTR", __func__, port->number); - result = usb_control_msg( port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0 ), - SUSBCRequest_SetStatusLinesOrQueues, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - ((dtr != 0) ? SUSBCR_SSL_SETDTR : SUSBCR_SSL_CLRDTR), - 0, - transfer_buffer, - 0, - KOBIL_TIMEOUT); + dbg("%s - port %d Clearing DTR", + __func__, port->number); + result = usb_control_msg(port->serial->dev, + usb_rcvctrlpipe(port->serial->dev, 0), + SUSBCRequest_SetStatusLinesOrQueues, + USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, + ((dtr != 0) ? SUSBCR_SSL_SETDTR : SUSBCR_SSL_CLRDTR), + 0, + transfer_buffer, + 0, + KOBIL_TIMEOUT); } else { if (rts != 0) - dbg("%s - port %d Setting RTS", __func__, port->number); + dbg("%s - port %d Setting RTS", + __func__, port->number); else - dbg("%s - port %d Clearing RTS", __func__, port->number); - result = usb_control_msg( port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0 ), - SUSBCRequest_SetStatusLinesOrQueues, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - ((rts != 0) ? SUSBCR_SSL_SETRTS : SUSBCR_SSL_CLRRTS), - 0, - transfer_buffer, - 0, - KOBIL_TIMEOUT); + dbg("%s - port %d Clearing RTS", + __func__, port->number); + result = usb_control_msg(port->serial->dev, + usb_rcvctrlpipe(port->serial->dev, 0), + SUSBCRequest_SetStatusLinesOrQueues, + USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, + ((rts != 0) ? SUSBCR_SSL_SETRTS : SUSBCR_SSL_CLRRTS), + 0, + transfer_buffer, + 0, + KOBIL_TIMEOUT); } - dbg("%s - port %d Send set_status_line URB returns: %i", __func__, port->number, result); + dbg("%s - port %d Send set_status_line URB returns: %i", + __func__, port->number, result); kfree(transfer_buffer); return (result < 0) ? result : 0; } -static void kobil_set_termios(struct usb_serial_port *port, struct ktermios *old) +static void kobil_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old) { - struct kobil_private * priv; + struct kobil_private *priv; int result; unsigned short urb_val = 0; - int c_cflag = port->tty->termios->c_cflag; + int c_cflag = tty->termios->c_cflag; speed_t speed; - void * settings; + void *settings; priv = usb_get_serial_port_data(port); - if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) - // This device doesn't support ioctl calls + if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || + priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { + /* This device doesn't support ioctl calls */ + *tty->termios = *old; return; + } - switch (speed = tty_get_baud_rate(port->tty)) { - case 1200: - urb_val = SUSBCR_SBR_1200; - break; - default: - speed = 9600; - case 9600: - urb_val = SUSBCR_SBR_9600; - break; + speed = tty_get_baud_rate(tty); + switch (speed) { + case 1200: + urb_val = SUSBCR_SBR_1200; + break; + default: + speed = 9600; + case 9600: + urb_val = SUSBCR_SBR_9600; + break; } - urb_val |= (c_cflag & CSTOPB) ? SUSBCR_SPASB_2StopBits : SUSBCR_SPASB_1StopBit; + urb_val |= (c_cflag & CSTOPB) ? SUSBCR_SPASB_2StopBits : + SUSBCR_SPASB_1StopBit; settings = kzalloc(50, GFP_KERNEL); - if (! settings) + if (!settings) return; sprintf(settings, "%d ", speed); @@ -634,66 +678,69 @@ static void kobil_set_termios(struct usb_serial_port *port, struct ktermios *old urb_val |= SUSBCR_SPASB_NoParity; strcat(settings, "No Parity"); } - port->tty->termios->c_cflag &= ~CMSPAR; - tty_encode_baud_rate(port->tty, speed, speed); - - result = usb_control_msg( port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0 ), - SUSBCRequest_SetBaudRateParityAndStopBits, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - urb_val, - 0, - settings, - 0, - KOBIL_TIMEOUT + tty->termios->c_cflag &= ~CMSPAR; + tty_encode_baud_rate(tty, speed, speed); + + result = usb_control_msg(port->serial->dev, + usb_rcvctrlpipe(port->serial->dev, 0), + SUSBCRequest_SetBaudRateParityAndStopBits, + USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, + urb_val, + 0, + settings, + 0, + KOBIL_TIMEOUT ); kfree(settings); } -static int kobil_ioctl(struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) +static int kobil_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) { - struct kobil_private * priv = usb_get_serial_port_data(port); + struct usb_serial_port *port = tty->driver_data; + struct kobil_private *priv = usb_get_serial_port_data(port); unsigned char *transfer_buffer; int transfer_buffer_length = 8; int result; - if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) - // This device doesn't support ioctl calls - return 0; + if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || + priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) + /* This device doesn't support ioctl calls */ + return -ENOIOCTLCMD; switch (cmd) { - case TCFLSH: // 0x540B + case TCFLSH: transfer_buffer = kmalloc(transfer_buffer_length, GFP_KERNEL); - if (! transfer_buffer) - return -ENOBUFS; - - result = usb_control_msg( port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0 ), - SUSBCRequest_Misc, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - SUSBCR_MSC_ResetAllQueues, - 0, - NULL,//transfer_buffer, - 0, - KOBIL_TIMEOUT + if (!transfer_buffer) + return -ENOBUFS; + + result = usb_control_msg(port->serial->dev, + usb_rcvctrlpipe(port->serial->dev, 0), + SUSBCRequest_Misc, + USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, + SUSBCR_MSC_ResetAllQueues, + 0, + NULL, /* transfer_buffer, */ + 0, + KOBIL_TIMEOUT ); - + dbg("%s - port %d Send reset_all_queues (FLUSH) URB returns: %i", __func__, port->number, result); kfree(transfer_buffer); - return (result < 0) ? -EFAULT : 0; + return (result < 0) ? -EIO: 0; default: return -ENOIOCTLCMD; } } -static int __init kobil_init (void) +static int __init kobil_init(void) { int retval; retval = usb_serial_register(&kobil_device); if (retval) goto failed_usb_serial_register; retval = usb_register(&kobil_driver); - if (retval) + if (retval) goto failed_usb_register; info(DRIVER_VERSION " " DRIVER_AUTHOR); @@ -707,18 +754,18 @@ failed_usb_serial_register: } -static void __exit kobil_exit (void) +static void __exit kobil_exit(void) { - usb_deregister (&kobil_driver); - usb_serial_deregister (&kobil_device); + usb_deregister(&kobil_driver); + usb_serial_deregister(&kobil_device); } module_init(kobil_init); module_exit(kobil_exit); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_LICENSE( "GPL" ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Debug enabled or not"); diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 5fc2cef30e39..0ded8bd6ec85 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -33,10 +33,11 @@ * - Fixed an endianess problem with the baudrate selection for PowerPC. * * 06-Dec-2001 Martin Hamilton <martinh@gnu.org> - * Added support for the Belkin F5U109 DB9 adaptor + * - Added support for the Belkin F5U109 DB9 adaptor * * 30-May-2001 Greg Kroah-Hartman - * switched from using spinlock to a semaphore, which fixes lots of problems. + * - switched from using spinlock to a semaphore, which fixes lots of + * problems. * * 04-May-2001 Stelian Pop * - Set the maximum bulk output size for Sitecom U232-P25 model to 16 bytes @@ -49,7 +50,7 @@ * 08-Apr-2001 gb * - Identify version on module load. * - * 06-Jan-2001 Cornel Ciocirlan + * 06-Jan-2001 Cornel Ciocirlan * - Added support for Sitecom U232-P25 model (Product Id 0x0230) * - Added support for D-Link DU-H3SP USB BAY (Product Id 0x0200) * @@ -59,8 +60,8 @@ * (lots of things will change if/when the usb-serial core changes to * handle these issues. * - * 27-Nov-2000 Wolfgang Grandegger - * A version for kernel 2.4.0-test10 released to the Linux community + * 27-Nov-2000 Wolfgang Grandegge + * A version for kernel 2.4.0-test10 released to the Linux community * (via linux-usb-devel). */ @@ -73,7 +74,7 @@ #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include "mct_u232.h" @@ -90,28 +91,21 @@ static int debug; /* * Function prototypes */ -static int mct_u232_startup (struct usb_serial *serial); -static void mct_u232_shutdown (struct usb_serial *serial); -static int mct_u232_open (struct usb_serial_port *port, - struct file *filp); -static void mct_u232_close (struct usb_serial_port *port, - struct file *filp); -static void mct_u232_read_int_callback (struct urb *urb); -static void mct_u232_set_termios (struct usb_serial_port *port, - struct ktermios * old); -static int mct_u232_ioctl (struct usb_serial_port *port, - struct file * file, - unsigned int cmd, - unsigned long arg); -static void mct_u232_break_ctl (struct usb_serial_port *port, - int break_state ); -static int mct_u232_tiocmget (struct usb_serial_port *port, - struct file *file); -static int mct_u232_tiocmset (struct usb_serial_port *port, - struct file *file, unsigned int set, - unsigned int clear); -static void mct_u232_throttle (struct usb_serial_port *port); -static void mct_u232_unthrottle (struct usb_serial_port *port); +static int mct_u232_startup(struct usb_serial *serial); +static void mct_u232_shutdown(struct usb_serial *serial); +static int mct_u232_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void mct_u232_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void mct_u232_read_int_callback(struct urb *urb); +static void mct_u232_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); +static void mct_u232_break_ctl(struct tty_struct *tty, int break_state); +static int mct_u232_tiocmget(struct tty_struct *tty, struct file *file); +static int mct_u232_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear); +static void mct_u232_throttle(struct tty_struct *tty); +static void mct_u232_unthrottle(struct tty_struct *tty); /* @@ -125,7 +119,7 @@ static struct usb_device_id id_table_combined [] = { { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table_combined); +MODULE_DEVICE_TABLE(usb, id_table_combined); static struct usb_driver mct_u232_driver = { .name = "mct_u232", @@ -149,7 +143,6 @@ static struct usb_serial_driver mct_u232_device = { .throttle = mct_u232_throttle, .unthrottle = mct_u232_unthrottle, .read_int_callback = mct_u232_read_int_callback, - .ioctl = mct_u232_ioctl, .set_termios = mct_u232_set_termios, .break_ctl = mct_u232_break_ctl, .tiocmget = mct_u232_tiocmget, @@ -180,23 +173,34 @@ struct mct_u232_private { * Later day 2.6.0-test kernels have new baud rates like B230400 which * we do not know how to support. We ignore them for the moment. */ -static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value, speed_t *result) +static int mct_u232_calculate_baud_rate(struct usb_serial *serial, + speed_t value, speed_t *result) { *result = value; if (le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_SITECOM_PID - || le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_BELKIN_F5U109_PID) { + || le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_BELKIN_F5U109_PID) { switch (value) { - case 300: return 0x01; - case 600: return 0x02; /* this one not tested */ - case 1200: return 0x03; - case 2400: return 0x04; - case 4800: return 0x06; - case 9600: return 0x08; - case 19200: return 0x09; - case 38400: return 0x0a; - case 57600: return 0x0b; - case 115200: return 0x0c; + case 300: + return 0x01; + case 600: + return 0x02; /* this one not tested */ + case 1200: + return 0x03; + case 2400: + return 0x04; + case 4800: + return 0x06; + case 9600: + return 0x08; + case 19200: + return 0x09; + case 38400: + return 0x0a; + case 57600: + return 0x0b; + case 115200: + return 0x0c; default: *result = 9600; return 0x08; @@ -224,26 +228,27 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value } } -static int mct_u232_set_baud_rate(struct usb_serial *serial, struct usb_serial_port *port, - speed_t value) +static int mct_u232_set_baud_rate(struct tty_struct *tty, + struct usb_serial *serial, struct usb_serial_port *port, speed_t value) { __le32 divisor; - int rc; - unsigned char zero_byte = 0; - unsigned char cts_enable_byte = 0; - speed_t speed; - - divisor = cpu_to_le32(mct_u232_calculate_baud_rate(serial, value, &speed)); - - rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - MCT_U232_SET_BAUD_RATE_REQUEST, - MCT_U232_SET_REQUEST_TYPE, - 0, 0, &divisor, MCT_U232_SET_BAUD_RATE_SIZE, - WDR_TIMEOUT); + int rc; + unsigned char zero_byte = 0; + unsigned char cts_enable_byte = 0; + speed_t speed; + + divisor = cpu_to_le32(mct_u232_calculate_baud_rate(serial, value, + &speed)); + + rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + MCT_U232_SET_BAUD_RATE_REQUEST, + MCT_U232_SET_REQUEST_TYPE, + 0, 0, &divisor, MCT_U232_SET_BAUD_RATE_SIZE, + WDR_TIMEOUT); if (rc < 0) /*FIXME: What value speed results */ err("Set BAUD RATE %d failed (error = %d)", value, rc); else - tty_encode_baud_rate(port->tty, speed, speed); + tty_encode_baud_rate(tty, speed, speed); dbg("set_baud_rate: value: 0x%x, divisor: 0x%x", value, divisor); /* Mimic the MCT-supplied Windows driver (version 1.21P.0104), which @@ -258,55 +263,55 @@ static int mct_u232_set_baud_rate(struct usb_serial *serial, struct usb_serial_p whether data will be transmitted to a device which is not asserting the 'CTS' signal. If the second message's data byte is zero, data will be transmitted even if 'CTS' is not asserted (i.e. no hardware - flow control). if the second message's data byte is nonzero (a value - of 1 is used by this driver), data will not be transmitted to a device - which is not asserting 'CTS'. + flow control). if the second message's data byte is nonzero (a + value of 1 is used by this driver), data will not be transmitted to + a device which is not asserting 'CTS'. */ rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - MCT_U232_SET_UNKNOWN1_REQUEST, - MCT_U232_SET_REQUEST_TYPE, - 0, 0, &zero_byte, MCT_U232_SET_UNKNOWN1_SIZE, - WDR_TIMEOUT); + MCT_U232_SET_UNKNOWN1_REQUEST, + MCT_U232_SET_REQUEST_TYPE, + 0, 0, &zero_byte, MCT_U232_SET_UNKNOWN1_SIZE, + WDR_TIMEOUT); if (rc < 0) - err("Sending USB device request code %d failed (error = %d)", + err("Sending USB device request code %d failed (error = %d)", MCT_U232_SET_UNKNOWN1_REQUEST, rc); - if (port && C_CRTSCTS(port->tty)) { + if (port && C_CRTSCTS(tty)) cts_enable_byte = 1; - } - dbg("set_baud_rate: send second control message, data = %02X", cts_enable_byte); + dbg("set_baud_rate: send second control message, data = %02X", + cts_enable_byte); rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - MCT_U232_SET_CTS_REQUEST, - MCT_U232_SET_REQUEST_TYPE, - 0, 0, &cts_enable_byte, MCT_U232_SET_CTS_SIZE, - WDR_TIMEOUT); + MCT_U232_SET_CTS_REQUEST, + MCT_U232_SET_REQUEST_TYPE, + 0, 0, &cts_enable_byte, MCT_U232_SET_CTS_SIZE, + WDR_TIMEOUT); if (rc < 0) - err("Sending USB device request code %d failed (error = %d)", - MCT_U232_SET_CTS_REQUEST, rc); + err("Sending USB device request code %d failed (error = %d)", + MCT_U232_SET_CTS_REQUEST, rc); - return rc; + return rc; } /* mct_u232_set_baud_rate */ static int mct_u232_set_line_ctrl(struct usb_serial *serial, unsigned char lcr) { - int rc; - rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - MCT_U232_SET_LINE_CTRL_REQUEST, - MCT_U232_SET_REQUEST_TYPE, - 0, 0, &lcr, MCT_U232_SET_LINE_CTRL_SIZE, - WDR_TIMEOUT); + int rc; + rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + MCT_U232_SET_LINE_CTRL_REQUEST, + MCT_U232_SET_REQUEST_TYPE, + 0, 0, &lcr, MCT_U232_SET_LINE_CTRL_SIZE, + WDR_TIMEOUT); if (rc < 0) err("Set LINE CTRL 0x%x failed (error = %d)", lcr, rc); dbg("set_line_ctrl: 0x%x", lcr); - return rc; + return rc; } /* mct_u232_set_line_ctrl */ static int mct_u232_set_modem_ctrl(struct usb_serial *serial, unsigned int control_state) { - int rc; + int rc; unsigned char mcr = MCT_U232_MCR_NONE; if (control_state & TIOCM_DTR) @@ -314,37 +319,39 @@ static int mct_u232_set_modem_ctrl(struct usb_serial *serial, if (control_state & TIOCM_RTS) mcr |= MCT_U232_MCR_RTS; - rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - MCT_U232_SET_MODEM_CTRL_REQUEST, - MCT_U232_SET_REQUEST_TYPE, - 0, 0, &mcr, MCT_U232_SET_MODEM_CTRL_SIZE, - WDR_TIMEOUT); + rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + MCT_U232_SET_MODEM_CTRL_REQUEST, + MCT_U232_SET_REQUEST_TYPE, + 0, 0, &mcr, MCT_U232_SET_MODEM_CTRL_SIZE, + WDR_TIMEOUT); if (rc < 0) err("Set MODEM CTRL 0x%x failed (error = %d)", mcr, rc); dbg("set_modem_ctrl: state=0x%x ==> mcr=0x%x", control_state, mcr); - return rc; + return rc; } /* mct_u232_set_modem_ctrl */ -static int mct_u232_get_modem_stat(struct usb_serial *serial, unsigned char *msr) +static int mct_u232_get_modem_stat(struct usb_serial *serial, + unsigned char *msr) { - int rc; - rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), - MCT_U232_GET_MODEM_STAT_REQUEST, - MCT_U232_GET_REQUEST_TYPE, - 0, 0, msr, MCT_U232_GET_MODEM_STAT_SIZE, - WDR_TIMEOUT); + int rc; + rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + MCT_U232_GET_MODEM_STAT_REQUEST, + MCT_U232_GET_REQUEST_TYPE, + 0, 0, msr, MCT_U232_GET_MODEM_STAT_SIZE, + WDR_TIMEOUT); if (rc < 0) { err("Get MODEM STATus failed (error = %d)", rc); *msr = 0; } dbg("get_modem_stat: 0x%x", *msr); - return rc; + return rc; } /* mct_u232_get_modem_stat */ -static void mct_u232_msr_to_state(unsigned int *control_state, unsigned char msr) +static void mct_u232_msr_to_state(unsigned int *control_state, + unsigned char msr) { - /* Translate Control Line states */ + /* Translate Control Line states */ if (msr & MCT_U232_MSR_DSR) *control_state |= TIOCM_DSR; else @@ -361,14 +368,14 @@ static void mct_u232_msr_to_state(unsigned int *control_state, unsigned char msr *control_state |= TIOCM_CD; else *control_state &= ~TIOCM_CD; - dbg("msr_to_state: msr=0x%x ==> state=0x%x", msr, *control_state); + dbg("msr_to_state: msr=0x%x ==> state=0x%x", msr, *control_state); } /* mct_u232_msr_to_state */ /* * Driver's tty interface functions */ -static int mct_u232_startup (struct usb_serial *serial) +static int mct_u232_startup(struct usb_serial *serial) { struct mct_u232_private *priv; struct usb_serial_port *port, *rport; @@ -390,18 +397,18 @@ static int mct_u232_startup (struct usb_serial *serial) rport->interrupt_in_urb = NULL; port->read_urb->context = port; - return (0); + return 0; } /* mct_u232_startup */ -static void mct_u232_shutdown (struct usb_serial *serial) +static void mct_u232_shutdown(struct usb_serial *serial) { struct mct_u232_private *priv; int i; - + dbg("%s", __func__); - for (i=0; i < serial->num_ports; ++i) { + for (i = 0; i < serial->num_ports; ++i) { /* My special items, the standard routines free my urbs */ priv = usb_get_serial_port_data(serial->port[i]); if (priv) { @@ -411,7 +418,8 @@ static void mct_u232_shutdown (struct usb_serial *serial) } } /* mct_u232_shutdown */ -static int mct_u232_open (struct usb_serial_port *port, struct file *filp) +static int mct_u232_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); @@ -428,21 +436,22 @@ static int mct_u232_open (struct usb_serial_port *port, struct file *filp) * it seems to be able to accept only 16 bytes (and that's what * SniffUSB says too...) */ - if (le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_SITECOM_PID) + if (le16_to_cpu(serial->dev->descriptor.idProduct) + == MCT_U232_SITECOM_PID) port->bulk_out_size = 16; - /* Do a defined restart: the normal serial device seems to + /* Do a defined restart: the normal serial device seems to * always turn on DTR and RTS here, so do the same. I'm not * sure if this is really necessary. But it should not harm * either. */ spin_lock_irqsave(&priv->lock, flags); - if (port->tty->termios->c_cflag & CBAUD) + if (tty && (tty->termios->c_cflag & CBAUD)) priv->control_state = TIOCM_DTR | TIOCM_RTS; else priv->control_state = 0; - - priv->last_lcr = (MCT_U232_DATA_BITS_8 | + + priv->last_lcr = (MCT_U232_DATA_BITS_8 | MCT_U232_PARITY_NONE | MCT_U232_STOP_BITS_1); control_state = priv->control_state; @@ -481,15 +490,16 @@ error: } /* mct_u232_open */ -static void mct_u232_close (struct usb_serial_port *port, struct file *filp) +static void mct_u232_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { unsigned int c_cflag; unsigned int control_state; struct mct_u232_private *priv = usb_get_serial_port_data(port); dbg("%s port %d", __func__, port->number); - if (port->tty) { - c_cflag = port->tty->termios->c_cflag; + if (tty) { + c_cflag = tty->termios->c_cflag; mutex_lock(&port->serial->disc_mutex); if (c_cflag & HUPCL && !port->serial->disconnected) { /* drop DTR and RTS */ @@ -512,7 +522,7 @@ static void mct_u232_close (struct usb_serial_port *port, struct file *filp) } /* mct_u232_close */ -static void mct_u232_read_int_callback (struct urb *urb) +static void mct_u232_read_int_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct mct_u232_private *priv = usb_get_serial_port_data(port); @@ -545,36 +555,34 @@ static void mct_u232_read_int_callback (struct urb *urb) return; } - dbg("%s - port %d", __func__, port->number); - usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); + dbg("%s - port %d", __func__, port->number); + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, data); /* * Work-a-round: handle the 'usual' bulk-in pipe here */ if (urb->transfer_buffer_length > 2) { - int i; - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { - for (i = 0; i < urb->actual_length ; ++i) { - tty_insert_flip_char(tty, data[i], 0); - } + tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } goto exit; } - + /* * The interrupt-in pipe signals exceptional conditions (modem line * signal changes and errors). data[0] holds MSR, data[1] holds LSR. */ spin_lock_irqsave(&priv->lock, flags); priv->last_msr = data[MCT_U232_MSR_INDEX]; - + /* Record Control Line states */ mct_u232_msr_to_state(&priv->control_state, priv->last_msr); #if 0 - /* Not yet handled. See belin_sa.c for further information */ + /* Not yet handled. See belkin_sa.c for further information */ /* Now to report any errors */ priv->last_lsr = data[MCT_U232_LSR_INDEX]; /* @@ -583,7 +591,7 @@ static void mct_u232_read_int_callback (struct urb *urb) * to look in to this before committing any code. */ if (priv->last_lsr & MCT_U232_LSR_ERR) { - tty = port->tty; + tty = port->port.tty; /* Overrun Error */ if (priv->last_lsr & MCT_U232_LSR_OE) { } @@ -600,18 +608,19 @@ static void mct_u232_read_int_callback (struct urb *urb) #endif spin_unlock_irqrestore(&priv->lock, flags); exit: - retval = usb_submit_urb (urb, GFP_ATOMIC); + retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) - err ("%s - usb_submit_urb failed with result %d", + err("%s - usb_submit_urb failed with result %d", __func__, retval); } /* mct_u232_read_int_callback */ -static void mct_u232_set_termios (struct usb_serial_port *port, - struct ktermios *old_termios) +static void mct_u232_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, + struct ktermios *old_termios) { struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); - struct ktermios *termios = port->tty->termios; + struct ktermios *termios = tty->termios; unsigned int cflag = termios->c_cflag; unsigned int old_cflag = old_termios->c_cflag; unsigned long flags; @@ -631,20 +640,20 @@ static void mct_u232_set_termios (struct usb_serial_port *port, * Premature optimization is the root of all evil. */ - /* reassert DTR and RTS on transition from B0 */ + /* reassert DTR and RTS on transition from B0 */ if ((old_cflag & CBAUD) == B0) { dbg("%s: baud was B0", __func__); control_state |= TIOCM_DTR | TIOCM_RTS; mct_u232_set_modem_ctrl(serial, control_state); } - mct_u232_set_baud_rate(serial, port, tty_get_baud_rate(port->tty)); + mct_u232_set_baud_rate(tty, serial, port, tty_get_baud_rate(tty)); - if ((cflag & CBAUD) == B0 ) { + if ((cflag & CBAUD) == B0) { dbg("%s: baud is B0", __func__); /* Drop RTS and DTR */ control_state &= ~(TIOCM_DTR | TIOCM_RTS); - mct_u232_set_modem_ctrl(serial, control_state); + mct_u232_set_modem_ctrl(serial, control_state); } /* @@ -689,8 +698,9 @@ static void mct_u232_set_termios (struct usb_serial_port *port, spin_unlock_irqrestore(&priv->lock, flags); } /* mct_u232_set_termios */ -static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state ) +static void mct_u232_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); unsigned char lcr; @@ -709,12 +719,13 @@ static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state ) } /* mct_u232_break_ctl */ -static int mct_u232_tiocmget (struct usb_serial_port *port, struct file *file) +static int mct_u232_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct mct_u232_private *priv = usb_get_serial_port_data(port); unsigned int control_state; unsigned long flags; - + dbg("%s", __func__); spin_lock_irqsave(&priv->lock, flags); @@ -724,14 +735,15 @@ static int mct_u232_tiocmget (struct usb_serial_port *port, struct file *file) return control_state; } -static int mct_u232_tiocmset (struct usb_serial_port *port, struct file *file, +static int mct_u232_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); unsigned int control_state; unsigned long flags; - + dbg("%s", __func__); spin_lock_irqsave(&priv->lock, flags); @@ -751,77 +763,50 @@ static int mct_u232_tiocmset (struct usb_serial_port *port, struct file *file, return mct_u232_set_modem_ctrl(serial, control_state); } -static int mct_u232_ioctl (struct usb_serial_port *port, struct file * file, - unsigned int cmd, unsigned long arg) -{ - dbg("%scmd=0x%x", __func__, cmd); - - /* Based on code from acm.c and others */ - switch (cmd) { - case TIOCMIWAIT: - /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ - /* TODO */ - return( 0 ); - - case TIOCGICOUNT: - /* return count of modemline transitions */ - /* TODO */ - return 0; - - default: - dbg("%s: arg not supported - 0x%04x", __func__,cmd); - return(-ENOIOCTLCMD); - break; - } - return 0; -} /* mct_u232_ioctl */ - -static void mct_u232_throttle (struct usb_serial_port *port) +static void mct_u232_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct mct_u232_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int control_state; - struct tty_struct *tty; - tty = port->tty; dbg("%s - port %d", __func__, port->number); spin_lock_irqsave(&priv->lock, flags); priv->rx_flags |= THROTTLED; if (C_CRTSCTS(tty)) { - priv->control_state &= ~TIOCM_RTS; - control_state = priv->control_state; - spin_unlock_irqrestore(&priv->lock, flags); - (void) mct_u232_set_modem_ctrl(port->serial, control_state); + priv->control_state &= ~TIOCM_RTS; + control_state = priv->control_state; + spin_unlock_irqrestore(&priv->lock, flags); + (void) mct_u232_set_modem_ctrl(port->serial, control_state); } else { - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); } } -static void mct_u232_unthrottle (struct usb_serial_port *port) +static void mct_u232_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct mct_u232_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int control_state; - struct tty_struct *tty; dbg("%s - port %d", __func__, port->number); - tty = port->tty; spin_lock_irqsave(&priv->lock, flags); if ((priv->rx_flags & THROTTLED) && C_CRTSCTS(tty)) { - priv->rx_flags &= ~THROTTLED; - priv->control_state |= TIOCM_RTS; - control_state = priv->control_state; - spin_unlock_irqrestore(&priv->lock, flags); - (void) mct_u232_set_modem_ctrl(port->serial, control_state); + priv->rx_flags &= ~THROTTLED; + priv->control_state |= TIOCM_RTS; + control_state = priv->control_state; + spin_unlock_irqrestore(&priv->lock, flags); + (void) mct_u232_set_modem_ctrl(port->serial, control_state); } else { - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); } } -static int __init mct_u232_init (void) +static int __init mct_u232_init(void) { int retval; retval = usb_serial_register(&mct_u232_device); @@ -839,18 +824,17 @@ failed_usb_serial_register: } -static void __exit mct_u232_exit (void) +static void __exit mct_u232_exit(void) { - usb_deregister (&mct_u232_driver); - usb_serial_deregister (&mct_u232_device); + usb_deregister(&mct_u232_driver); + usb_serial_deregister(&mct_u232_device); } - -module_init (mct_u232_init); +module_init(mct_u232_init); module_exit(mct_u232_exit); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 50f1fe263338..7c4917d77c0a 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -33,7 +33,7 @@ #include <linux/serial_reg.h> #include <linux/usb.h> #include <linux/usb/serial.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> /* @@ -64,8 +64,7 @@ #define URB_TRANSFER_BUFFER_SIZE 32 /* URB Size */ /* This structure holds all of the local port information */ -struct moschip_port -{ +struct moschip_port { __u8 shadowLCR; /* last LCR value received */ __u8 shadowMCR; /* last MCR value received */ __u8 shadowMSR; /* last MSR value received */ @@ -76,8 +75,7 @@ struct moschip_port }; /* This structure holds all of the individual serial device information */ -struct moschip_serial -{ +struct moschip_serial { int interrupt_started; }; @@ -88,7 +86,7 @@ static int debug; #define MOSCHIP_DEVICE_ID_7715 0x7715 static struct usb_device_id moschip_port_id_table [] = { - { USB_DEVICE(USB_VENDOR_ID_MOSCHIP,MOSCHIP_DEVICE_ID_7720) }, + { USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7720) }, { } /* terminating entry */ }; MODULE_DEVICE_TABLE(usb, moschip_port_id_table); @@ -108,7 +106,7 @@ static void mos7720_interrupt_callback(struct urb *urb) __u8 sp1; __u8 sp2; - dbg("%s"," : Entering\n"); + dbg("%s", " : Entering\n"); switch (status) { case 0: @@ -208,7 +206,7 @@ static void mos7720_bulk_in_callback(struct urb *urb) mos7720_port = urb->context; if (!mos7720_port) { - dbg("%s","NULL mos7720_port pointer \n"); + dbg("%s", "NULL mos7720_port pointer \n"); return ; } @@ -218,7 +216,7 @@ static void mos7720_bulk_in_callback(struct urb *urb) data = urb->transfer_buffer; - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -264,7 +262,7 @@ static void mos7720_bulk_out_data_callback(struct urb *urb) dbg("Entering ........."); - tty = mos7720_port->port->tty; + tty = mos7720_port->port->port.tty; if (tty && mos7720_port->open) tty_wakeup(tty); @@ -284,17 +282,16 @@ static int send_mos_cmd(struct usb_serial *serial, __u8 request, __u16 value, __u16 size = 0x0000; if (value < MOS_MAX_PORT) { - if (product == MOSCHIP_DEVICE_ID_7715) { + if (product == MOSCHIP_DEVICE_ID_7715) value = value*0x100+0x100; - } else { + else value = value*0x100+0x200; - } } else { value = 0x0000; if ((product == MOSCHIP_DEVICE_ID_7715) && (index != 0x08)) { dbg("serial->product== MOSCHIP_DEVICE_ID_7715"); - //index = 0x01 ; + /* index = 0x01 ; */ } } @@ -308,19 +305,20 @@ static int send_mos_cmd(struct usb_serial *serial, __u8 request, __u16 value, request = (__u8)MOS_READ; requesttype = (__u8)0xC0; size = 0x01; - pipe = usb_rcvctrlpipe(serial->dev,0); + pipe = usb_rcvctrlpipe(serial->dev, 0); } status = usb_control_msg(serial->dev, pipe, request, requesttype, value, index, data, size, MOS_WDR_TIMEOUT); if (status < 0) - dbg("Command Write failed Value %x index %x\n",value,index); + dbg("Command Write failed Value %x index %x\n", value, index); return status; } -static int mos7720_open(struct usb_serial_port *port, struct file * filp) +static int mos7720_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial; struct usb_serial_port *port0; @@ -351,7 +349,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_KERNEL); + urb = usb_alloc_urb(0, GFP_KERNEL); mos7720_port->write_urb_pool[j] = urb; if (urb == NULL) { @@ -385,7 +383,7 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp) */ port_number = port->number - port->serial->minor; send_mos_cmd(port->serial, MOS_READ, port_number, UART_LSR, &data); - dbg("SS::%p LSR:%x\n",mos7720_port, data); + dbg("SS::%p LSR:%x\n", mos7720_port, data); dbg("Check:Sending Command .........."); @@ -402,10 +400,10 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp) data = 0xCF; send_mos_cmd(serial, MOS_WRITE, port_number, 0x02, &data); data = 0x03; - mos7720_port->shadowLCR = data; + mos7720_port->shadowLCR = data; send_mos_cmd(serial, MOS_WRITE, port_number, 0x03, &data); data = 0x0b; - mos7720_port->shadowMCR = data; + mos7720_port->shadowMCR = data; send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); data = 0x0b; send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); @@ -420,7 +418,8 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp) data = 0x03; send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, port_number + 1, &data); data = 0x00; - send_mos_cmd(port->serial, MOS_WRITE, MOS_MAX_PORT, port_number + 1, &data); + send_mos_cmd(port->serial, MOS_WRITE, MOS_MAX_PORT, + port_number + 1, &data); */ data = 0x00; send_mos_cmd(serial, MOS_READ, MOS_MAX_PORT, 0x08, &data); @@ -429,28 +428,26 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp) send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, 0x08, &data); data = 0x83; - mos7720_port->shadowLCR = data; + mos7720_port->shadowLCR = data; send_mos_cmd(serial, MOS_WRITE, port_number, 0x03, &data); data = 0x0c; send_mos_cmd(serial, MOS_WRITE, port_number, 0x00, &data); data = 0x00; send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data); data = 0x03; - mos7720_port->shadowLCR = data; + mos7720_port->shadowLCR = data; send_mos_cmd(serial, MOS_WRITE, port_number, 0x03, &data); data = 0x0c; send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data); data = 0x0c; send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data); -//Matrix - /* force low_latency on so that our tty_push actually forces * * the data through,otherwise it is scheduled, and with * * high data rates (like with OHCI) data can get lost. */ - if (port->tty) - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; /* see if we've set up our endpoint info yet * * (can't set it up in mos7720_startup as the * @@ -465,15 +462,15 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp) /* set up our interrupt urb */ usb_fill_int_urb(port0->interrupt_in_urb, serial->dev, - usb_rcvintpipe(serial->dev, - port->interrupt_in_endpointAddress), - port0->interrupt_in_buffer, - port0->interrupt_in_urb->transfer_buffer_length, - mos7720_interrupt_callback, mos7720_port, - port0->interrupt_in_urb->interval); + usb_rcvintpipe(serial->dev, + port->interrupt_in_endpointAddress), + port0->interrupt_in_buffer, + port0->interrupt_in_urb->transfer_buffer_length, + mos7720_interrupt_callback, mos7720_port, + port0->interrupt_in_urb->interval); /* start interrupt read for this mos7720 this interrupt * - * will continue as long as the mos7720 is connected */ + * will continue as long as the mos7720 is connected */ dbg("Submit URB over !!!"); response = usb_submit_urb(port0->interrupt_in_urb, GFP_KERNEL); if (response) @@ -485,14 +482,14 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp) /* set up our bulk in urb */ usb_fill_bulk_urb(port->read_urb, serial->dev, usb_rcvbulkpipe(serial->dev, - port->bulk_in_endpointAddress), + port->bulk_in_endpointAddress), port->bulk_in_buffer, port->read_urb->transfer_buffer_length, mos7720_bulk_in_callback, mos7720_port); response = usb_submit_urb(port->read_urb, GFP_KERNEL); if (response) - dev_err(&port->dev, - "%s - Error %d submitting read urb\n", __func__, response); + dev_err(&port->dev, "%s - Error %d submitting read urb\n", + __func__, response); /* initialize our icount structure */ memset(&(mos7720_port->icount), 0x00, sizeof(mos7720_port->icount)); @@ -515,8 +512,9 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp) * system, * Otherwise we return a negative error number. */ -static int mos7720_chars_in_buffer(struct usb_serial_port *port) +static int mos7720_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int i; int chars = 0; struct moschip_port *mos7720_port; @@ -530,14 +528,16 @@ 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] && 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", __func__, chars); return chars; } -static void mos7720_close(struct usb_serial_port *port, struct file *filp) +static void mos7720_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial; struct moschip_port *mos7720_port; @@ -575,12 +575,12 @@ static void mos7720_close(struct usb_serial_port *port, struct file *filp) * been disconnected */ if (!serial->disconnected) { data = 0x00; - send_mos_cmd(serial, MOS_WRITE, port->number - port->serial->minor, - 0x04, &data); + send_mos_cmd(serial, MOS_WRITE, + port->number - port->serial->minor, 0x04, &data); data = 0x00; - send_mos_cmd(serial, MOS_WRITE, port->number - port->serial->minor, - 0x01, &data); + send_mos_cmd(serial, MOS_WRITE, + port->number - port->serial->minor, 0x01, &data); } mutex_unlock(&serial->disc_mutex); mos7720_port->open = 0; @@ -588,9 +588,10 @@ static void mos7720_close(struct usb_serial_port *port, struct file *filp) dbg("Leaving %s", __func__); } -static void mos7720_break(struct usb_serial_port *port, int break_state) +static void mos7720_break(struct tty_struct *tty, int break_state) { - unsigned char data; + struct usb_serial_port *port = tty->driver_data; + unsigned char data; struct usb_serial *serial; struct moschip_port *mos7720_port; @@ -621,8 +622,9 @@ static void mos7720_break(struct usb_serial_port *port, int break_state) * If successful, we return the amount of room that we have for this port * Otherwise we return a negative error number. */ -static int mos7720_write_room(struct usb_serial_port *port) +static int mos7720_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7720_port; int room = 0; int i; @@ -637,7 +639,8 @@ static int mos7720_write_room(struct usb_serial_port *port) /* FIXME: Locking */ for (i = 0; i < NUM_URBS; ++i) { - if (mos7720_port->write_urb_pool[i] && 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; } @@ -645,8 +648,8 @@ static int mos7720_write_room(struct usb_serial_port *port) return room; } -static int mos7720_write(struct usb_serial_port *port, - const unsigned char *data, int count) +static int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *data, int count) { int status; int i; @@ -672,9 +675,10 @@ 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] && 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); + dbg("URB:%d", i); break; } } @@ -692,7 +696,7 @@ static int mos7720_write(struct usb_serial_port *port, goto exit; } } - transfer_size = min (count, URB_TRANSFER_BUFFER_SIZE); + transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE); memcpy(urb->transfer_buffer, current_position, transfer_size); usb_serial_debug_data(debug, &port->dev, __func__, transfer_size, @@ -701,12 +705,12 @@ static int mos7720_write(struct usb_serial_port *port, /* fill urb with data and submit */ usb_fill_bulk_urb(urb, serial->dev, usb_sndbulkpipe(serial->dev, - port->bulk_out_endpointAddress), + port->bulk_out_endpointAddress), urb->transfer_buffer, transfer_size, mos7720_bulk_out_data_callback, mos7720_port); /* send it down the pipe */ - status = usb_submit_urb(urb,GFP_ATOMIC); + status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { err("%s - usb_submit_urb(write bulk) failed with status = %d", __func__, status); @@ -719,10 +723,10 @@ exit: return bytes_sent; } -static void mos7720_throttle(struct usb_serial_port *port) +static void mos7720_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7720_port; - struct tty_struct *tty; int status; dbg("%s- port %d\n", __func__, port->number); @@ -739,16 +743,10 @@ static void mos7720_throttle(struct usb_serial_port *port) dbg("%s: Entering ..........", __func__); - tty = port->tty; - if (!tty) { - dbg("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the stop character */ if (I_IXOFF(tty)) { unsigned char stop_char = STOP_CHAR(tty); - status = mos7720_write(port, &stop_char, 1); + status = mos7720_write(tty, port, &stop_char, 1); if (status <= 0) return; } @@ -764,11 +762,11 @@ static void mos7720_throttle(struct usb_serial_port *port) } } -static void mos7720_unthrottle(struct usb_serial_port *port) +static void mos7720_unthrottle(struct tty_struct *tty) { - struct tty_struct *tty; - int status; + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7720_port = usb_get_serial_port_data(port); + int status; if (mos7720_port == NULL) return; @@ -780,16 +778,10 @@ static void mos7720_unthrottle(struct usb_serial_port *port) dbg("%s: Entering ..........", __func__); - tty = port->tty; - if (!tty) { - dbg("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the start character */ if (I_IXOFF(tty)) { unsigned char start_char = START_CHAR(tty); - status = mos7720_write(port, &start_char, 1); + status = mos7720_write(tty, port, &start_char, 1); if (status <= 0) return; } @@ -819,9 +811,9 @@ static int set_higher_rates(struct moschip_port *mos7720_port, port = mos7720_port->port; serial = port->serial; - /*********************************************** - * Init Sequence for higher rates - ***********************************************/ + /*********************************************** + * Init Sequence for higher rates + ***********************************************/ dbg("Sending Setting Commands .........."); port_number = port->number - port->serial->minor; @@ -832,7 +824,7 @@ static int set_higher_rates(struct moschip_port *mos7720_port, data = 0x0CF; send_mos_cmd(serial, MOS_WRITE, port->number, 0x02, &data); data = 0x00b; - mos7720_port->shadowMCR = data; + mos7720_port->shadowMCR = data; send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); data = 0x00b; send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); @@ -843,12 +835,12 @@ static int set_higher_rates(struct moschip_port *mos7720_port, send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, 0x08, &data); - /*********************************************** - * Set for higher rates * - ***********************************************/ + /*********************************************** + * Set for higher rates * + ***********************************************/ data = baud * 0x10; - send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, port_number + 1,&data); + send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, port_number + 1, &data); data = 0x003; send_mos_cmd(serial, MOS_READ, MOS_MAX_PORT, 0x08, &data); @@ -856,34 +848,33 @@ static int set_higher_rates(struct moschip_port *mos7720_port, send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, 0x08, &data); data = 0x02b; - mos7720_port->shadowMCR = data; + mos7720_port->shadowMCR = data; send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); data = 0x02b; send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); - /*********************************************** - * Set DLL/DLM - ***********************************************/ + /*********************************************** + * Set DLL/DLM + ***********************************************/ data = mos7720_port->shadowLCR | UART_LCR_DLAB; - mos7720_port->shadowLCR = data; + mos7720_port->shadowLCR = data; send_mos_cmd(serial, MOS_WRITE, port_number, 0x03, &data); data = 0x001; /* DLL */ - send_mos_cmd(serial, MOS_WRITE, port_number, 0x00, &data); + send_mos_cmd(serial, MOS_WRITE, port_number, 0x00, &data); data = 0x000; /* DLM */ - send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data); + send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data); data = mos7720_port->shadowLCR & ~UART_LCR_DLAB; - mos7720_port->shadowLCR = data; + mos7720_port->shadowLCR = data; send_mos_cmd(serial, MOS_WRITE, port_number, 0x03, &data); return 0; } /* baud rate information */ -struct divisor_table_entry -{ +struct divisor_table_entry { __u32 baudrate; __u16 divisor; }; @@ -932,8 +923,8 @@ static int calc_baud_rate_divisor(int baudrate, int *divisor) } } - /* After trying for all the standard baud rates * - * Try calculating the divisor for this baud rate */ + /* After trying for all the standard baud rates * + * Try calculating the divisor for this baud rate */ if (baudrate > 75 && baudrate < 230400) { /* get the divisor */ custom = (__u16)(230400L / baudrate); @@ -945,7 +936,7 @@ static int calc_baud_rate_divisor(int baudrate, int *divisor) custom++; *divisor = custom; - dbg("Baud %d = %d",baudrate, custom); + dbg("Baud %d = %d", baudrate, custom); return 0; } @@ -979,29 +970,29 @@ static int send_cmd_write_baud_rate(struct moschip_port *mos7720_port, number = port->number - port->serial->minor; dbg("%s - port = %d, baud = %d", __func__, port->number, baudrate); - /* Calculate the Divisor */ + /* Calculate the Divisor */ status = calc_baud_rate_divisor(baudrate, &divisor); if (status) { err("%s - bad baud rate", __func__); return status; } - /* Enable access to divisor latch */ - data = mos7720_port->shadowLCR | UART_LCR_DLAB; - mos7720_port->shadowLCR = data; - send_mos_cmd(serial, MOS_WRITE, number, UART_LCR, &data); + /* Enable access to divisor latch */ + data = mos7720_port->shadowLCR | UART_LCR_DLAB; + mos7720_port->shadowLCR = data; + send_mos_cmd(serial, MOS_WRITE, number, UART_LCR, &data); /* Write the divisor */ data = ((unsigned char)(divisor & 0xff)); - send_mos_cmd(serial, MOS_WRITE, number, 0x00, &data); + send_mos_cmd(serial, MOS_WRITE, number, 0x00, &data); data = ((unsigned char)((divisor & 0xff00) >> 8)); - send_mos_cmd(serial, MOS_WRITE, number, 0x01, &data); + send_mos_cmd(serial, MOS_WRITE, number, 0x01, &data); - /* Disable access to divisor latch */ - data = mos7720_port->shadowLCR & ~UART_LCR_DLAB; - mos7720_port->shadowLCR = data; - send_mos_cmd(serial, MOS_WRITE, number, 0x03, &data); + /* Disable access to divisor latch */ + data = mos7720_port->shadowLCR & ~UART_LCR_DLAB; + mos7720_port->shadowLCR = data; + send_mos_cmd(serial, MOS_WRITE, number, 0x03, &data); return status; } @@ -1011,12 +1002,12 @@ static int send_cmd_write_baud_rate(struct moschip_port *mos7720_port, * This routine is called to set the UART on the device to match * the specified new settings. */ -static void change_port_settings(struct moschip_port *mos7720_port, +static void change_port_settings(struct tty_struct *tty, + struct moschip_port *mos7720_port, struct ktermios *old_termios) { struct usb_serial_port *port; struct usb_serial *serial; - struct tty_struct *tty; int baud; unsigned cflag; unsigned iflag; @@ -1042,8 +1033,6 @@ static void change_port_settings(struct moschip_port *mos7720_port, return; } - tty = mos7720_port->port->tty; - dbg("%s: Entering ..........", __func__); lData = UART_LCR_WLEN8; @@ -1106,29 +1095,31 @@ static void change_port_settings(struct moschip_port *mos7720_port, #define LCR_PAR_MASK 0x38 /* Mask for parity field */ /* Update the LCR with the correct value */ - mos7720_port->shadowLCR &= ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK); + mos7720_port->shadowLCR &= + ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK); mos7720_port->shadowLCR |= (lData | lParity | lStop); /* Disable Interrupts */ data = 0x00; - send_mos_cmd(serial,MOS_WRITE,port->number - port->serial->minor, UART_IER, &data); + send_mos_cmd(serial, MOS_WRITE, port->number - port->serial->minor, + UART_IER, &data); data = 0x00; - send_mos_cmd(serial, MOS_WRITE, port_number, UART_FCR, &data); + send_mos_cmd(serial, MOS_WRITE, port_number, UART_FCR, &data); data = 0xcf; - send_mos_cmd(serial, MOS_WRITE, port_number, UART_FCR, &data); + send_mos_cmd(serial, MOS_WRITE, port_number, UART_FCR, &data); /* Send the updated LCR value to the mos7720 */ data = mos7720_port->shadowLCR; - send_mos_cmd(serial, MOS_WRITE, port_number, UART_LCR, &data); + send_mos_cmd(serial, MOS_WRITE, port_number, UART_LCR, &data); - data = 0x00b; - mos7720_port->shadowMCR = data; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); - data = 0x00b; - send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); + data = 0x00b; + mos7720_port->shadowMCR = data; + send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); + data = 0x00b; + send_mos_cmd(serial, MOS_WRITE, port_number, 0x04, &data); /* set up the MCR register and send it to the mos7720 */ mos7720_port->shadowMCR = UART_MCR_OUT2; @@ -1137,9 +1128,8 @@ static void change_port_settings(struct moschip_port *mos7720_port, if (cflag & CRTSCTS) { mos7720_port->shadowMCR |= (UART_MCR_XONANY); - - /* To set hardware flow control to the specified * - * serial port, in SP1/2_CONTROL_REG */ + /* To set hardware flow control to the specified * + * serial port, in SP1/2_CONTROL_REG */ if (port->number) { data = 0x001; send_mos_cmd(serial, MOS_WRITE, MOS_MAX_PORT, @@ -1198,14 +1188,13 @@ static void change_port_settings(struct moschip_port *mos7720_port, * this function is called by the tty driver when it wants to change the * termios structure. */ -static void mos7720_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void mos7720_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { int status; unsigned int cflag; struct usb_serial *serial; struct moschip_port *mos7720_port; - struct tty_struct *tty; serial = port->serial; @@ -1214,15 +1203,12 @@ static void mos7720_set_termios(struct usb_serial_port *port, if (mos7720_port == NULL) return; - tty = port->tty; - - if (!mos7720_port->open) { dbg("%s - port not opened", __func__); return; } - dbg("%s\n","setting termios - ASPIRE"); + dbg("%s\n", "setting termios - ASPIRE"); cflag = tty->termios->c_cflag; @@ -1237,14 +1223,14 @@ static void mos7720_set_termios(struct usb_serial_port *port, dbg("%s - port %d", __func__, port->number); /* change the port settings to the new ones specified */ - change_port_settings(mos7720_port, old_termios); + change_port_settings(tty, mos7720_port, old_termios); - if(!port->read_urb) { - dbg("%s","URB KILLED !!!!!\n"); + if (!port->read_urb) { + dbg("%s", "URB KILLED !!!!!\n"); return; } - if(port->read_urb->status != -EINPROGRESS) { + if (port->read_urb->status != -EINPROGRESS) { port->read_urb->dev = serial->dev; status = usb_submit_urb(port->read_urb, GFP_ATOMIC); if (status) @@ -1264,13 +1250,13 @@ static void mos7720_set_termios(struct usb_serial_port *port, * transmit holding register is empty. This functionality * allows an RS485 driver to be written in user space. */ -static int get_lsr_info(struct moschip_port *mos7720_port, - unsigned int __user *value) +static int get_lsr_info(struct tty_struct *tty, + struct moschip_port *mos7720_port, unsigned int __user *value) { int count; unsigned int result = 0; - count = mos7720_chars_in_buffer(mos7720_port->port); + count = mos7720_chars_in_buffer(tty); if (count == 0) { dbg("%s -- Empty", __func__); result = TIOCSER_TEMT; @@ -1290,7 +1276,7 @@ static int get_number_bytes_avail(struct moschip_port *mos7720_port, unsigned int __user *value) { unsigned int result = 0; - struct tty_struct *tty = mos7720_port->port->tty; + struct tty_struct *tty = mos7720_port->port->port.tty; if (!tty) return -ENOIOCTLCMD; @@ -1316,7 +1302,7 @@ static int set_modem_info(struct moschip_port *mos7720_port, unsigned int cmd, if (mos7720_port == NULL) return -1; - port = (struct usb_serial_port*)mos7720_port->port; + port = (struct usb_serial_port *)mos7720_port->port; mcr = mos7720_port->shadowMCR; if (copy_from_user(&arg, value, sizeof(int))) @@ -1397,7 +1383,7 @@ static int get_serial_info(struct moschip_port *mos7720_port, tmp.port = mos7720_port->port->number; tmp.irq = 0; tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; - tmp.xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE; + tmp.xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE; tmp.baud_base = 9600; tmp.close_delay = 5*HZ; tmp.closing_wait = 30*HZ; @@ -1407,9 +1393,10 @@ static int get_serial_info(struct moschip_port *mos7720_port, return 0; } -static int mos7720_ioctl(struct usb_serial_port *port, struct file *file, +static int mos7720_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7720_port; struct async_icount cnow; struct async_icount cprev; @@ -1431,14 +1418,16 @@ static int mos7720_ioctl(struct usb_serial_port *port, struct file *file, case TIOCSERGETLSR: dbg("%s (%d) TIOCSERGETLSR", __func__, port->number); - return get_lsr_info(mos7720_port, (unsigned int __user *)arg); + return get_lsr_info(tty, mos7720_port, + (unsigned int __user *)arg); return 0; + /* FIXME: These should be using the mode methods */ case TIOCMBIS: case TIOCMBIC: case TIOCMSET: - dbg("%s (%d) TIOCMSET/TIOCMBIC/TIOCMSET", __func__, - port->number); + dbg("%s (%d) TIOCMSET/TIOCMBIC/TIOCMSET", + __func__, port->number); return set_modem_info(mos7720_port, cmd, (unsigned int __user *)arg); @@ -1452,10 +1441,6 @@ static int mos7720_ioctl(struct usb_serial_port *port, struct file *file, return get_serial_info(mos7720_port, (struct serial_struct __user *)arg); - case TIOCSSERIAL: - dbg("%s (%d) TIOCSSERIAL", __func__, port->number); - break; - case TIOCMIWAIT: dbg("%s (%d) TIOCMIWAIT", __func__, port->number); cprev = mos7720_port->icount; @@ -1469,7 +1454,7 @@ static int mos7720_ioctl(struct usb_serial_port *port, struct file *file, 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)) ) { + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { return 0; } cprev = cnow; @@ -1492,7 +1477,7 @@ static int mos7720_ioctl(struct usb_serial_port *port, struct file *file, icount.buf_overrun = cnow.buf_overrun; dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d", __func__, - port->number, icount.rx, icount.tx ); + port->number, icount.rx, icount.tx); if (copy_to_user((void __user *)arg, &icount, sizeof(icount))) return -EFAULT; return 0; @@ -1543,7 +1528,8 @@ static int mos7720_startup(struct usb_serial *serial) /* Initialize all port interrupt end point to port 0 int * endpoint. Our device has only one interrupt endpoint * comman to all ports */ - serial->port[i]->interrupt_in_endpointAddress = serial->port[0]->interrupt_in_endpointAddress; + serial->port[i]->interrupt_in_endpointAddress = + serial->port[0]->interrupt_in_endpointAddress; mos7720_port->port = serial->port[i]; usb_set_serial_port_data(serial->port[i], mos7720_port); @@ -1555,13 +1541,15 @@ static int mos7720_startup(struct usb_serial *serial) /* setting configuration feature to one */ usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - (__u8)0x03, 0x00,0x01,0x00, NULL, 0x00, 5*HZ); + (__u8)0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5*HZ); - send_mos_cmd(serial,MOS_READ,0x00, UART_LSR, &data); // LSR For Port 1 - dbg("LSR:%x",data); + /* LSR For Port 1 */ + send_mos_cmd(serial, MOS_READ, 0x00, UART_LSR, &data); + dbg("LSR:%x", data); - send_mos_cmd(serial,MOS_READ,0x01, UART_LSR, &data); // LSR For Port 2 - dbg("LSR:%x",data); + /* LSR For Port 2 */ + send_mos_cmd(serial, MOS_READ, 0x01, UART_LSR, &data); + dbg("LSR:%x", data); return 0; } @@ -1571,7 +1559,7 @@ static void mos7720_shutdown(struct usb_serial *serial) int i; /* free private structure allocated for serial port */ - for (i=0; i < serial->num_ports; ++i) { + for (i = 0; i < serial->num_ports; ++i) { kfree(usb_get_serial_port_data(serial->port[i])); usb_set_serial_port_data(serial->port[i], NULL); } @@ -1651,8 +1639,8 @@ module_init(moschip7720_init); module_exit(moschip7720_exit); /* Module information */ -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 78f2f6db494d..09d82062b973 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -33,7 +33,7 @@ #include <linux/serial.h> #include <linux/usb.h> #include <linux/usb/serial.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> /* * Version Information @@ -82,8 +82,8 @@ * Defines used for sending commands to port */ -#define WAIT_FOR_EVER (HZ * 0 ) /* timeout urb is wait for ever */ -#define MOS_WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */ +#define WAIT_FOR_EVER (HZ * 0) /* timeout urb is wait for ever */ +#define MOS_WDR_TIMEOUT (HZ * 5) /* default urb timeout */ #define MOS_PORT1 0x0200 #define MOS_PORT2 0x0300 @@ -102,8 +102,8 @@ #define MAX_NAME_LEN 64 -#define ZLP_REG1 0x3A //Zero_Flag_Reg1 58 -#define ZLP_REG5 0x3E //Zero_Flag_Reg5 62 +#define ZLP_REG1 0x3A /* Zero_Flag_Reg1 58 */ +#define ZLP_REG5 0x3E /* Zero_Flag_Reg5 62 */ /* For higher baud Rates use TIOCEXBAUD */ #define TIOCEXBAUD 0x5462 @@ -142,7 +142,7 @@ #define MOS_MSR_DELTA_RI 0x40 #define MOS_MSR_DELTA_CD 0x80 -// Serial Port register Address +/* Serial Port register Address */ #define INTERRUPT_ENABLE_REGISTER ((__u16)(0x01)) #define FIFO_CONTROL_REGISTER ((__u16)(0x02)) #define LINE_CONTROL_REGISTER ((__u16)(0x03)) @@ -201,11 +201,11 @@ struct moschip_port { struct async_icount icount; struct usb_serial_port *port; /* loop back to the owner of this object */ - /*Offsets */ + /* Offsets */ __u8 SpRegOffset; __u8 ControlRegOffset; __u8 DcrRegOffset; - //for processing control URBS in interrupt context + /* for processing control URBS in interrupt context */ struct urb *control_urb; struct usb_ctrlrequest *dr; char *ctrl_buf; @@ -244,7 +244,7 @@ static int mos7840_set_reg_sync(struct usb_serial_port *port, __u16 reg, */ static int mos7840_get_reg_sync(struct usb_serial_port *port, __u16 reg, - __u16 * val) + __u16 *val) { struct usb_device *dev = port->serial->dev; int ret = 0; @@ -269,16 +269,15 @@ 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 + /* For the UART control registers, the application number need + to be Or'ed */ if (port->serial->num_ports == 4) { - val |= - (((__u16) port->number - (__u16) (port->serial->minor)) + - 1) << 8; + val |= (((__u16) port->number - + (__u16) (port->serial->minor)) + 1) << 8; dbg("mos7840_set_uart_reg application number is %x\n", val); } else { if (((__u16) port->number - (__u16) (port->serial->minor)) == 0) { - val |= - (((__u16) port->number - + val |= (((__u16) port->number - (__u16) (port->serial->minor)) + 1) << 8; dbg("mos7840_set_uart_reg application number is %x\n", val); @@ -302,14 +301,15 @@ static int mos7840_set_uart_reg(struct usb_serial_port *port, __u16 reg, * by passing usb_rcvctrlpipe function as parameter. */ static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg, - __u16 * val) + __u16 *val) { struct usb_device *dev = port->serial->dev; int ret = 0; __u16 Wval; - //dbg("application number is %4x \n",(((__u16)port->number - (__u16)(port->serial->minor))+1)<<8); - /*Wval is same as application number */ + /* dbg("application number is %4x \n", + (((__u16)port->number - (__u16)(port->serial->minor))+1)<<8); */ + /* Wval is same as application number */ if (port->serial->num_ports == 4) { Wval = (((__u16) port->number - (__u16) (port->serial->minor)) + @@ -317,14 +317,12 @@ static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg, dbg("mos7840_get_uart_reg application number is %x\n", Wval); } else { if (((__u16) port->number - (__u16) (port->serial->minor)) == 0) { - Wval = - (((__u16) port->number - + Wval = (((__u16) port->number - (__u16) (port->serial->minor)) + 1) << 8; dbg("mos7840_get_uart_reg application number is %x\n", Wval); } else { - Wval = - (((__u16) port->number - + Wval = (((__u16) port->number - (__u16) (port->serial->minor)) + 2) << 8; dbg("mos7840_get_uart_reg application number is %x\n", Wval); @@ -406,11 +404,11 @@ static void mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr) dbg("%s - %02x", __func__, new_lsr); if (new_lsr & SERIAL_LSR_BI) { - // - // Parity and Framing errors only count if they - // occur exclusive of a break being - // received. - // + /* + * Parity and Framing errors only count if they + * occur exclusive of a break being + * received. + */ new_lsr &= (__u8) (SERIAL_LSR_OE | SERIAL_LSR_BI); } @@ -492,7 +490,7 @@ exit: } static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg, - __u16 * val) + __u16 *val) { struct usb_device *dev = mcs->port->serial->dev; struct usb_ctrlrequest *dr = mcs->dr; @@ -501,7 +499,7 @@ static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg, dr->bRequestType = MCS_RD_RTYPE; dr->bRequest = MCS_RDREQ; - dr->wValue = cpu_to_le16(Wval); //0; + dr->wValue = cpu_to_le16(Wval); /* 0 */ dr->wIndex = cpu_to_le16(reg); dr->wLength = cpu_to_le16(2); @@ -607,7 +605,8 @@ static void mos7840_interrupt_callback(struct urb *urb) } } } - if (!(rv < 0)) /* the completion handler for the control urb will resubmit */ + if (!(rv < 0)) + /* the completion handler for the control urb will resubmit */ return; exit: result = usb_submit_urb(urb, GFP_ATOMIC); @@ -656,8 +655,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; } @@ -710,7 +709,7 @@ static void mos7840_bulk_in_callback(struct urb *urb) dbg("%s", "Entering ........... \n"); if (urb->actual_length) { - tty = mos7840_port->port->tty; + tty = mos7840_port->port->port.tty; if (tty) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -741,8 +740,8 @@ static void mos7840_bulk_in_callback(struct urb *urb) /***************************************************************************** * mos7840_bulk_out_data_callback - * this is the callback function for when we have finished sending serial data - * on the bulk out endpoint. + * this is the callback function for when we have finished sending + * serial data on the bulk out endpoint. *****************************************************************************/ static void mos7840_bulk_out_data_callback(struct urb *urb) @@ -774,7 +773,7 @@ static void mos7840_bulk_out_data_callback(struct urb *urb) dbg("%s \n", "Entering ........."); - tty = mos7840_port->port->tty; + tty = mos7840_port->port->port.tty; if (tty && mos7840_port->open) tty_wakeup(tty); @@ -804,7 +803,8 @@ static int mos7840_serial_probe(struct usb_serial *serial, * Otherwise we return a negative error number. *****************************************************************************/ -static int mos7840_open(struct usb_serial_port *port, struct file *filp) +static int mos7840_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int response; int j; @@ -847,7 +847,8 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp) continue; } - 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; @@ -868,9 +869,8 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp) * 0x08 : SP1/2 Control Reg *****************************************************************************/ -//NEED to check the following Block + /* NEED to check the following Block */ - status = 0; Data = 0x0; status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data); if (status < 0) { @@ -890,36 +890,35 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp) dbg("writing Spreg failed\n"); return -1; } -//End of block to be checked + /* End of block to be checked */ - status = 0; Data = 0x0; - status = - mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, &Data); + status = mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, + &Data); if (status < 0) { dbg("Reading Controlreg failed\n"); return -1; } - Data |= 0x08; //Driver done bit - Data |= 0x20; //rx_disable - status = mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data); + Data |= 0x08; /* Driver done bit */ + Data |= 0x20; /* rx_disable */ + status = mos7840_set_reg_sync(port, + mos7840_port->ControlRegOffset, Data); if (status < 0) { dbg("writing Controlreg failed\n"); return -1; } - //do register settings here - // Set all regs to the device default values. - //////////////////////////////////// - // First Disable all interrupts. - //////////////////////////////////// - + /* do register settings here */ + /* Set all regs to the device default values. */ + /*********************************** + * First Disable all interrupts. + ***********************************/ Data = 0x00; status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); if (status < 0) { dbg("disableing interrupts failed\n"); return -1; } - // Set FIFO_CONTROL_REGISTER to the default value + /* Set FIFO_CONTROL_REGISTER to the default value */ Data = 0x00; status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data); if (status < 0) { @@ -946,90 +945,73 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp) status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data); mos7840_port->shadowLCR = Data; - Data |= SERIAL_LCR_DLAB; //data latch enable in LCR 0x80 + Data |= SERIAL_LCR_DLAB; /* data latch enable in LCR 0x80 */ status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); Data = 0x0c; - status = 0; status = mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data); Data = 0x0; - status = 0; status = mos7840_set_uart_reg(port, DIVISOR_LATCH_MSB, Data); Data = 0x00; - status = 0; status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data); Data = Data & ~SERIAL_LCR_DLAB; - status = 0; status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); mos7840_port->shadowLCR = Data; - //clearing Bulkin and Bulkout Fifo + /* clearing Bulkin and Bulkout Fifo */ Data = 0x0; - status = 0; status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data); Data = Data | 0x0c; - status = 0; status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data); Data = Data & ~0x0c; - status = 0; status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data); - //Finally enable all interrupts - Data = 0x0; + /* Finally enable all interrupts */ Data = 0x0c; - status = 0; status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); - //clearing rx_disable + /* clearing rx_disable */ Data = 0x0; - status = 0; - status = - mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, &Data); + status = mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, + &Data); Data = Data & ~0x20; - status = 0; - status = - mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data); + status = mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, + Data); - // rx_negate + /* rx_negate */ Data = 0x0; - status = 0; - status = - mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, &Data); + status = mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, + &Data); Data = Data | 0x10; - status = 0; - status = - mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data); + status = mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, + Data); /* force low_latency on so that our tty_push actually forces * * the data through,otherwise it is scheduled, and with * * high data rates (like with OHCI) data can get lost. */ + if (tty) + tty->low_latency = 1; - if (port->tty) - port->tty->low_latency = 1; -/* 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.) */ + /* 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 (port0->open_ports == 1) { if (serial->port[0]->interrupt_in_buffer == NULL) { - /* set up interrupt urb */ - usb_fill_int_urb(serial->port[0]->interrupt_in_urb, - serial->dev, - usb_rcvintpipe(serial->dev, - serial->port[0]-> - interrupt_in_endpointAddress), - serial->port[0]->interrupt_in_buffer, - serial->port[0]->interrupt_in_urb-> - transfer_buffer_length, - mos7840_interrupt_callback, - serial, - serial->port[0]->interrupt_in_urb-> - interval); + serial->dev, + usb_rcvintpipe(serial->dev, + serial->port[0]->interrupt_in_endpointAddress), + serial->port[0]->interrupt_in_buffer, + serial->port[0]->interrupt_in_urb-> + transfer_buffer_length, + mos7840_interrupt_callback, + serial, + serial->port[0]->interrupt_in_urb->interval); /* start interrupt read for mos7840 * * will continue as long as mos7840 is connected */ @@ -1084,14 +1066,16 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp) memset(&(mos7840_port->icount), 0x00, sizeof(mos7840_port->icount)); /* initialize our port settings */ - mos7840_port->shadowMCR = MCR_MASTER_IE; /* Must set to enable ints! */ + /* Must set to enable ints! */ + mos7840_port->shadowMCR = MCR_MASTER_IE; /* send a open port command */ mos7840_port->open = 1; - //mos7840_change_port_settings(mos7840_port,old_termios); + /* mos7840_change_port_settings(mos7840_port,old_termios); */ mos7840_port->icount.tx = 0; mos7840_port->icount.rx = 0; - dbg("\n\nusb_serial serial:%p mos7840_port:%p\n usb_serial_port port:%p\n\n", serial, mos7840_port, port); + dbg("\n\nusb_serial serial:%p mos7840_port:%p\n usb_serial_port port:%p\n\n", + serial, mos7840_port, port); return 0; @@ -1104,11 +1088,12 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp) * been written, but hasn't made it out the port yet) * If successful, we return the number of bytes left to be written in the * system, - * Otherwise we return a negative error number. + * Otherwise we return zero. *****************************************************************************/ -static int mos7840_chars_in_buffer(struct usb_serial_port *port) +static int mos7840_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int i; int chars = 0; unsigned long flags; @@ -1118,22 +1103,20 @@ static int mos7840_chars_in_buffer(struct usb_serial_port *port) if (mos7840_port_paranoia_check(port, __func__)) { dbg("%s", "Invalid port \n"); - return -1; + return 0; } mos7840_port = mos7840_get_port_private(port); if (mos7840_port == NULL) { dbg("%s \n", "mos7840_break:leaving ..........."); - return -1; + return 0; } - spin_lock_irqsave(&mos7840_port->pool_lock,flags); - for (i = 0; i < NUM_URBS; ++i) { - if (mos7840_port->busy[i]) { + spin_lock_irqsave(&mos7840_port->pool_lock, flags); + for (i = 0; i < NUM_URBS; ++i) + if (mos7840_port->busy[i]) chars += URB_TRANSFER_BUFFER_SIZE; - } - } - spin_unlock_irqrestore(&mos7840_port->pool_lock,flags); + spin_unlock_irqrestore(&mos7840_port->pool_lock, flags); dbg("%s - returns %d", __func__, chars); return chars; @@ -1149,7 +1132,8 @@ static int mos7840_chars_in_buffer(struct usb_serial_port *port) * 3. A timeout of 3 seconds without activity has expired * ************************************************************************/ -static void mos7840_block_until_tx_empty(struct moschip_port *mos7840_port) +static void mos7840_block_until_tx_empty(struct tty_struct *tty, + struct moschip_port *mos7840_port) { int timeout = HZ / 10; int wait = 30; @@ -1157,12 +1141,11 @@ static void mos7840_block_until_tx_empty(struct moschip_port *mos7840_port) while (1) { - count = mos7840_chars_in_buffer(mos7840_port->port); + count = mos7840_chars_in_buffer(tty); /* Check for Buffer status */ - if (count <= 0) { + if (count <= 0) return; - } /* Block the thread for a while */ interruptible_sleep_on_timeout(&mos7840_port->wait_chase, @@ -1185,7 +1168,8 @@ static void mos7840_block_until_tx_empty(struct moschip_port *mos7840_port) * this function is called by the tty driver when a port is closed *****************************************************************************/ -static void mos7840_close(struct usb_serial_port *port, struct file *filp) +static void mos7840_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial; struct moschip_port *mos7840_port; @@ -1226,32 +1210,28 @@ static void mos7840_close(struct usb_serial_port *port, struct file *filp) } } - if (serial->dev) { + if (serial->dev) /* flush and block until tx is empty */ - mos7840_block_until_tx_empty(mos7840_port); - } + mos7840_block_until_tx_empty(tty, mos7840_port); /* While closing port, shutdown all bulk read, write * * and interrupt read if they exists */ if (serial->dev) { - if (mos7840_port->write_urb) { dbg("%s", "Shutdown bulk write\n"); usb_kill_urb(mos7840_port->write_urb); } - if (mos7840_port->read_urb) { dbg("%s", "Shutdown bulk read\n"); usb_kill_urb(mos7840_port->read_urb); } if ((&mos7840_port->control_urb)) { dbg("%s", "Shutdown control read\n"); - // usb_kill_urb (mos7840_port->control_urb); - + /*/ usb_kill_urb (mos7840_port->control_urb); */ } } -// if(mos7840_port->ctrl_buf != NULL) -// kfree(mos7840_port->ctrl_buf); +/* if(mos7840_port->ctrl_buf != NULL) */ +/* kfree(mos7840_port->ctrl_buf); */ port0->open_ports--; dbg("mos7840_num_open_ports in close%d:in port%d\n", port0->open_ports, port->number); @@ -1264,10 +1244,8 @@ static void mos7840_close(struct usb_serial_port *port, struct file *filp) if (mos7840_port->write_urb) { /* if this urb had a transfer buffer already (old tx) free it */ - - if (mos7840_port->write_urb->transfer_buffer != NULL) { + if (mos7840_port->write_urb->transfer_buffer != NULL) kfree(mos7840_port->write_urb->transfer_buffer); - } usb_free_urb(mos7840_port->write_urb); } @@ -1293,20 +1271,19 @@ static void mos7840_close(struct usb_serial_port *port, struct file *filp) * ************************************************************************/ -static void mos7840_block_until_chase_response(struct moschip_port - *mos7840_port) +static void mos7840_block_until_chase_response(struct tty_struct *tty, + struct moschip_port *mos7840_port) { int timeout = 1 * HZ; int wait = 10; int count; while (1) { - count = mos7840_chars_in_buffer(mos7840_port->port); + count = mos7840_chars_in_buffer(tty); /* Check for Buffer status */ - if (count <= 0) { + if (count <= 0) return; - } /* Block the thread for a while */ interruptible_sleep_on_timeout(&mos7840_port->wait_chase, @@ -1328,8 +1305,9 @@ static void mos7840_block_until_chase_response(struct moschip_port * mos7840_break * this function sends a break to the port *****************************************************************************/ -static void mos7840_break(struct usb_serial_port *port, int break_state) +static void mos7840_break(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; unsigned char data; struct usb_serial *serial; struct moschip_port *mos7840_port; @@ -1350,21 +1328,17 @@ static void mos7840_break(struct usb_serial_port *port, int break_state) mos7840_port = mos7840_get_port_private(port); - if (mos7840_port == NULL) { + if (mos7840_port == NULL) return; - } - - if (serial->dev) { + if (serial->dev) /* flush and block until tx is empty */ - mos7840_block_until_chase_response(mos7840_port); - } + mos7840_block_until_chase_response(tty, mos7840_port); - if (break_state == -1) { + if (break_state == -1) data = mos7840_port->shadowLCR | LCR_SET_BREAK; - } else { + else data = mos7840_port->shadowLCR & ~LCR_SET_BREAK; - } mos7840_port->shadowLCR = data; dbg("mcs7840_break mos7840_port->shadowLCR is %x\n", @@ -1383,8 +1357,9 @@ static void mos7840_break(struct usb_serial_port *port, int break_state) * Otherwise we return a negative error number. *****************************************************************************/ -static int mos7840_write_room(struct usb_serial_port *port) +static int mos7840_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int i; int room = 0; unsigned long flags; @@ -1406,9 +1381,8 @@ static int mos7840_write_room(struct usb_serial_port *port) spin_lock_irqsave(&mos7840_port->pool_lock, flags); for (i = 0; i < NUM_URBS; ++i) { - if (!mos7840_port->busy[i]) { + if (!mos7840_port->busy[i]) room += URB_TRANSFER_BUFFER_SIZE; - } } spin_unlock_irqrestore(&mos7840_port->pool_lock, flags); @@ -1426,7 +1400,7 @@ static int mos7840_write_room(struct usb_serial_port *port) * return a negative error number. *****************************************************************************/ -static int mos7840_write(struct usb_serial_port *port, +static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *data, int count) { int status; @@ -1438,45 +1412,41 @@ static int mos7840_write(struct usb_serial_port *port, struct moschip_port *mos7840_port; struct usb_serial *serial; struct urb *urb; - //__u16 Data; + /* __u16 Data; */ const unsigned char *current_position = data; unsigned char *data1; dbg("%s \n", "entering ..........."); - //dbg("mos7840_write: mos7840_port->shadowLCR is %x\n",mos7840_port->shadowLCR); + /* dbg("mos7840_write: mos7840_port->shadowLCR is %x\n", + mos7840_port->shadowLCR); */ #ifdef NOTMOS7840 Data = 0x00; - status = 0; status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data); mos7840_port->shadowLCR = Data; dbg("mos7840_write: LINE_CONTROL_REGISTER is %x\n", Data); dbg("mos7840_write: mos7840_port->shadowLCR is %x\n", mos7840_port->shadowLCR); - //Data = 0x03; - //status = mos7840_set_uart_reg(port,LINE_CONTROL_REGISTER,Data); - //mos7840_port->shadowLCR=Data;//Need to add later + /* Data = 0x03; */ + /* status = mos7840_set_uart_reg(port,LINE_CONTROL_REGISTER,Data); */ + /* mos7840_port->shadowLCR=Data;//Need to add later */ - Data |= SERIAL_LCR_DLAB; //data latch enable in LCR 0x80 - status = 0; + Data |= SERIAL_LCR_DLAB; /* data latch enable in LCR 0x80 */ status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); - //Data = 0x0c; - //status = mos7840_set_uart_reg(port,DIVISOR_LATCH_LSB,Data); + /* Data = 0x0c; */ + /* status = mos7840_set_uart_reg(port,DIVISOR_LATCH_LSB,Data); */ Data = 0x00; - status = 0; status = mos7840_get_uart_reg(port, DIVISOR_LATCH_LSB, &Data); dbg("mos7840_write:DLL value is %x\n", Data); Data = 0x0; - status = 0; status = mos7840_get_uart_reg(port, DIVISOR_LATCH_MSB, &Data); dbg("mos7840_write:DLM value is %x\n", Data); Data = Data & ~SERIAL_LCR_DLAB; dbg("mos7840_write: mos7840_port->shadowLCR is %x\n", mos7840_port->shadowLCR); - status = 0; status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); #endif @@ -1555,8 +1525,7 @@ static int mos7840_write(struct usb_serial_port *port, mos7840_port->icount.tx += transfer_size; smp_wmb(); dbg("mos7840_port->icount.tx is %d:\n", mos7840_port->icount.tx); - exit: - +exit: return bytes_sent; } @@ -1567,10 +1536,10 @@ static int mos7840_write(struct usb_serial_port *port, * being read from the port. *****************************************************************************/ -static void mos7840_throttle(struct usb_serial_port *port) +static void mos7840_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7840_port; - struct tty_struct *tty; int status; if (mos7840_port_paranoia_check(port, __func__)) { @@ -1592,32 +1561,20 @@ static void mos7840_throttle(struct usb_serial_port *port) dbg("%s", "Entering .......... \n"); - tty = port->tty; - if (!tty) { - dbg("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the stop character */ if (I_IXOFF(tty)) { unsigned char stop_char = STOP_CHAR(tty); - status = mos7840_write(port, &stop_char, 1); - if (status <= 0) { + status = mos7840_write(tty, port, &stop_char, 1); + if (status <= 0) return; - } } - /* if we are implementing RTS/CTS, toggle that line */ if (tty->termios->c_cflag & CRTSCTS) { mos7840_port->shadowMCR &= ~MCR_RTS; - status = 0; - status = - mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, + status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, mos7840_port->shadowMCR); - - if (status < 0) { + if (status < 0) return; - } } return; @@ -1625,12 +1582,13 @@ static void mos7840_throttle(struct usb_serial_port *port) /***************************************************************************** * mos7840_unthrottle - * this function is called by the tty driver when it wants to resume the data - * being read from the port (called after SerialThrottle is called) + * this function is called by the tty driver when it wants to resume + * the data being read from the port (called after mos7840_throttle is + * called) *****************************************************************************/ -static void mos7840_unthrottle(struct usb_serial_port *port) +static void mos7840_unthrottle(struct tty_struct *tty) { - struct tty_struct *tty; + struct usb_serial_port *port = tty->driver_data; int status; struct moschip_port *mos7840_port = mos7840_get_port_private(port); @@ -1649,43 +1607,32 @@ static void mos7840_unthrottle(struct usb_serial_port *port) dbg("%s", "Entering .......... \n"); - tty = port->tty; - if (!tty) { - dbg("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the start character */ if (I_IXOFF(tty)) { unsigned char start_char = START_CHAR(tty); - status = mos7840_write(port, &start_char, 1); - if (status <= 0) { + status = mos7840_write(tty, port, &start_char, 1); + if (status <= 0) return; - } } /* if we are implementing RTS/CTS, toggle that line */ if (tty->termios->c_cflag & CRTSCTS) { mos7840_port->shadowMCR |= MCR_RTS; - status = 0; - status = - mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, + status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, mos7840_port->shadowMCR); - if (status < 0) { + if (status < 0) return; - } } - - return; } -static int mos7840_tiocmget(struct usb_serial_port *port, struct file *file) +static int mos7840_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7840_port; unsigned int result; __u16 msr; __u16 mcr; - int status = 0; + int status; mos7840_port = mos7840_get_port_private(port); dbg("%s - port %d", __func__, port->number); @@ -1708,9 +1655,10 @@ static int mos7840_tiocmget(struct usb_serial_port *port, struct file *file) return result; } -static int mos7840_tiocmset(struct usb_serial_port *port, struct file *file, +static int mos7840_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7840_port; unsigned int mcr; int status; @@ -1755,7 +1703,7 @@ static int mos7840_tiocmset(struct usb_serial_port *port, struct file *file, * baud rate. *****************************************************************************/ static int mos7840_calc_baud_rate_divisor(int baudRate, int *divisor, - __u16 * clk_sel_val) + __u16 *clk_sel_val) { dbg("%s - %d", __func__, baudRate); @@ -1807,9 +1755,8 @@ static int mos7840_calc_baud_rate_divisor(int baudRate, int *divisor, /* Check for round off */ round1 = (__u16) (2304000L / baudrate); round = (__u16) (round1 - (custom * 10)); - if (round > 4) { + if (round > 4) custom++; - } *divisor = custom; dbg(" Baud %d = %d\n", baudrate, custom); @@ -1857,16 +1804,15 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port, dbg("%s - port = %d, baud = %d", __func__, mos7840_port->port->number, baudRate); - //reset clk_uart_sel in spregOffset + /* reset clk_uart_sel in spregOffset */ if (baudRate > 115200) { #ifdef HW_flow_control - //NOTE: need to see the pther register to modify - //setting h/w flow control bit to 1; - status = 0; + /* NOTE: need to see the pther register to modify */ + /* setting h/w flow control bit to 1 */ Data = 0x2b; mos7840_port->shadowMCR = Data; - status = - mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); + status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, + Data); if (status < 0) { dbg("Writing spreg failed in set_serial_baud\n"); return -1; @@ -1875,12 +1821,11 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port, } else { #ifdef HW_flow_control - //setting h/w flow control bit to 0; - status = 0; + / *setting h/w flow control bit to 0 */ Data = 0xb; mos7840_port->shadowMCR = Data; - status = - mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); + status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, + Data); if (status < 0) { dbg("Writing spreg failed in set_serial_baud\n"); return -1; @@ -1889,25 +1834,20 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port, } - if (1) //baudRate <= 115200) - { + if (1) { /* baudRate <= 115200) */ clk_sel_val = 0x0; Data = 0x0; - status = 0; - status = - mos7840_calc_baud_rate_divisor(baudRate, &divisor, + status = mos7840_calc_baud_rate_divisor(baudRate, &divisor, &clk_sel_val); - status = - mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, - &Data); + status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, + &Data); if (status < 0) { dbg("reading spreg failed in set_serial_baud\n"); return -1; } Data = (Data & 0x8f) | clk_sel_val; - status = 0; - status = - mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data); + status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, + Data); if (status < 0) { dbg("Writing spreg failed in set_serial_baud\n"); return -1; @@ -1939,7 +1879,6 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port, mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); } - return status; } @@ -1949,10 +1888,9 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port, * the specified new settings. *****************************************************************************/ -static void mos7840_change_port_settings(struct moschip_port *mos7840_port, - struct ktermios *old_termios) +static void mos7840_change_port_settings(struct tty_struct *tty, + struct moschip_port *mos7840_port, struct ktermios *old_termios) { - struct tty_struct *tty; int baud; unsigned cflag; unsigned iflag; @@ -1988,8 +1926,6 @@ static void mos7840_change_port_settings(struct moschip_port *mos7840_port, return; } - tty = mos7840_port->port->tty; - dbg("%s", "Entering .......... \n"); lData = LCR_BITS_8; @@ -2033,9 +1969,8 @@ static void mos7840_change_port_settings(struct moschip_port *mos7840_port, dbg("%s - parity = none", __func__); } - if (cflag & CMSPAR) { + if (cflag & CMSPAR) lParity = lParity | 0x20; - } /* Change the Stop bit */ if (cflag & CSTOPB) { @@ -2077,16 +2012,13 @@ static void mos7840_change_port_settings(struct moschip_port *mos7840_port, /* set up the MCR register and send it to the mos7840 */ mos7840_port->shadowMCR = MCR_MASTER_IE; - if (cflag & CBAUD) { + if (cflag & CBAUD) mos7840_port->shadowMCR |= (MCR_DTR | MCR_RTS); - } - if (cflag & CRTSCTS) { + if (cflag & CRTSCTS) mos7840_port->shadowMCR |= (MCR_XON_ANY); - - } else { + else mos7840_port->shadowMCR &= ~(MCR_XON_ANY); - } Data = mos7840_port->shadowMCR; mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); @@ -2131,14 +2063,14 @@ static void mos7840_change_port_settings(struct moschip_port *mos7840_port, * the termios structure *****************************************************************************/ -static void mos7840_set_termios(struct usb_serial_port *port, +static void mos7840_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { int status; unsigned int cflag; struct usb_serial *serial; struct moschip_port *mos7840_port; - struct tty_struct *tty; dbg("mos7840_set_termios: START\n"); if (mos7840_port_paranoia_check(port, __func__)) { dbg("%s", "Invalid port \n"); @@ -2157,8 +2089,6 @@ static void mos7840_set_termios(struct usb_serial_port *port, if (mos7840_port == NULL) return; - tty = port->tty; - if (!mos7840_port->open) { dbg("%s - port not opened", __func__); return; @@ -2176,7 +2106,7 @@ static void mos7840_set_termios(struct usb_serial_port *port, /* change the port settings to the new ones specified */ - mos7840_change_port_settings(mos7840_port, old_termios); + mos7840_change_port_settings(tty, mos7840_port, old_termios); if (!mos7840_port->read_urb) { dbg("%s", "URB KILLED !!!!!\n"); @@ -2205,13 +2135,13 @@ static void mos7840_set_termios(struct usb_serial_port *port, * allows an RS485 driver to be written in user space. *****************************************************************************/ -static int mos7840_get_lsr_info(struct moschip_port *mos7840_port, +static int mos7840_get_lsr_info(struct tty_struct *tty, unsigned int __user *value) { int count; unsigned int result = 0; - count = mos7840_chars_in_buffer(mos7840_port->port); + count = mos7840_chars_in_buffer(tty); if (count == 0) { dbg("%s -- Empty", __func__); result = TIOCSER_TEMT; @@ -2227,6 +2157,8 @@ static int mos7840_get_lsr_info(struct moschip_port *mos7840_port, * function to set modem info *****************************************************************************/ +/* FIXME: Should be using the model control hooks */ + static int mos7840_set_modem_info(struct moschip_port *mos7840_port, unsigned int cmd, unsigned int __user *value) { @@ -2282,7 +2214,6 @@ static int mos7840_set_modem_info(struct moschip_port *mos7840_port, mos7840_port->shadowMCR = mcr; Data = mos7840_port->shadowMCR; - status = 0; status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); if (status < 0) { dbg("setting MODEM_CONTROL_REGISTER Failed\n"); @@ -2303,10 +2234,8 @@ static int mos7840_get_modem_info(struct moschip_port *mos7840_port, unsigned int result = 0; __u16 msr; unsigned int mcr = mos7840_port->shadowMCR; - int status = 0; - status = - mos7840_get_uart_reg(mos7840_port->port, MODEM_STATUS_REGISTER, - &msr); + mos7840_get_uart_reg(mos7840_port->port, + MODEM_STATUS_REGISTER, &msr); result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) /* 0x002 */ |((mcr & MCR_RTS) ? TIOCM_RTS : 0) /* 0x004 */ |((msr & MOS7840_MSR_CTS) ? TIOCM_CTS : 0) /* 0x020 */ @@ -2359,12 +2288,12 @@ static int mos7840_get_serial_info(struct moschip_port *mos7840_port, * this function handles any ioctl calls to the driver *****************************************************************************/ -static int mos7840_ioctl(struct usb_serial_port *port, struct file *file, +static int mos7840_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; void __user *argp = (void __user *)arg; struct moschip_port *mos7840_port; - struct tty_struct *tty; struct async_icount cnow; struct async_icount cprev; @@ -2381,8 +2310,6 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file, if (mos7840_port == NULL) return -1; - tty = mos7840_port->port->tty; - dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd); switch (cmd) { @@ -2390,9 +2317,10 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file, case TIOCSERGETLSR: dbg("%s (%d) TIOCSERGETLSR", __func__, port->number); - return mos7840_get_lsr_info(mos7840_port, argp); + return mos7840_get_lsr_info(tty, argp); return 0; + /* FIXME: use the modem hooks and remove this */ case TIOCMBIS: case TIOCMBIC: case TIOCMSET: @@ -2418,7 +2346,7 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file, dbg("%s (%d) TIOCMIWAIT", __func__, port->number); cprev = mos7840_port->icount; while (1) { - //interruptible_sleep_on(&mos7840_port->delta_msr_wait); + /* interruptible_sleep_on(&mos7840_port->delta_msr_wait); */ mos7840_port->delta_msr_cond = 0; wait_event_interruptible(mos7840_port->delta_msr_wait, (mos7840_port-> @@ -2463,13 +2391,9 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file, if (copy_to_user(argp, &icount, sizeof(icount))) return -EFAULT; return 0; - - case TIOCEXBAUD: - return 0; default: break; } - return -ENOIOCTLCMD; } @@ -2527,8 +2451,9 @@ static int mos7840_startup(struct usb_serial *serial) goto error; } - /* Initialize all port interrupt end point to port 0 int endpoint * - * Our device has only one interrupt end point comman to all port */ + /* Initialize all port interrupt end point to port 0 int + * endpoint. Our device has only one interrupt end point + * common to all port */ mos7840_port->port = serial->port[i]; mos7840_set_port_private(serial->port[i], mos7840_port); @@ -2564,27 +2489,23 @@ static int mos7840_startup(struct usb_serial *serial) mos7840_port->DcrRegOffset = 0x1c; } mos7840_dump_serial_port(mos7840_port); - mos7840_set_port_private(serial->port[i], mos7840_port); - //enable rx_disable bit in control register - - status = - mos7840_get_reg_sync(serial->port[i], - mos7840_port->ControlRegOffset, &Data); + /* enable rx_disable bit in control register */ + status = mos7840_get_reg_sync(serial->port[i], + mos7840_port->ControlRegOffset, &Data); if (status < 0) { dbg("Reading ControlReg failed status-0x%x\n", status); break; } else dbg("ControlReg Reading success val is %x, status%d\n", Data, status); - Data |= 0x08; //setting driver done bit - Data |= 0x04; //sp1_bit to have cts change reflect in modem status reg + Data |= 0x08; /* setting driver done bit */ + Data |= 0x04; /* sp1_bit to have cts change reflect in + modem status reg */ - //Data |= 0x20; //rx_disable bit - status = 0; - status = - mos7840_set_reg_sync(serial->port[i], + /* Data |= 0x20; //rx_disable bit */ + status = mos7840_set_reg_sync(serial->port[i], mos7840_port->ControlRegOffset, Data); if (status < 0) { dbg("Writing ControlReg failed(rx_disable) status-0x%x\n", status); @@ -2593,13 +2514,11 @@ static int mos7840_startup(struct usb_serial *serial) dbg("ControlReg Writing success(rx_disable) status%d\n", status); - //Write default values in DCR (i.e 0x01 in DCR0, 0x05 in DCR2 and 0x24 in DCR3 + /* Write default values in DCR (i.e 0x01 in DCR0, 0x05 in DCR2 + and 0x24 in DCR3 */ Data = 0x01; - status = 0; - status = - mos7840_set_reg_sync(serial->port[i], - (__u16) (mos7840_port->DcrRegOffset + - 0), Data); + status = mos7840_set_reg_sync(serial->port[i], + (__u16) (mos7840_port->DcrRegOffset + 0), Data); if (status < 0) { dbg("Writing DCR0 failed status-0x%x\n", status); break; @@ -2607,11 +2526,8 @@ static int mos7840_startup(struct usb_serial *serial) dbg("DCR0 Writing success status%d\n", status); Data = 0x05; - status = 0; - status = - mos7840_set_reg_sync(serial->port[i], - (__u16) (mos7840_port->DcrRegOffset + - 1), Data); + status = mos7840_set_reg_sync(serial->port[i], + (__u16) (mos7840_port->DcrRegOffset + 1), Data); if (status < 0) { dbg("Writing DCR1 failed status-0x%x\n", status); break; @@ -2619,22 +2535,17 @@ static int mos7840_startup(struct usb_serial *serial) dbg("DCR1 Writing success status%d\n", status); Data = 0x24; - status = 0; - status = - mos7840_set_reg_sync(serial->port[i], - (__u16) (mos7840_port->DcrRegOffset + - 2), Data); + status = mos7840_set_reg_sync(serial->port[i], + (__u16) (mos7840_port->DcrRegOffset + 2), Data); if (status < 0) { dbg("Writing DCR2 failed status-0x%x\n", status); break; } else dbg("DCR2 Writing success status%d\n", status); - // write values in clkstart0x0 and clkmulti 0x20 + /* write values in clkstart0x0 and clkmulti 0x20 */ Data = 0x0; - status = 0; - status = - mos7840_set_reg_sync(serial->port[i], + status = mos7840_set_reg_sync(serial->port[i], CLK_START_VALUE_REGISTER, Data); if (status < 0) { dbg("Writing CLK_START_VALUE_REGISTER failed status-0x%x\n", status); @@ -2643,9 +2554,8 @@ static int mos7840_startup(struct usb_serial *serial) dbg("CLK_START_VALUE_REGISTER Writing success status%d\n", status); Data = 0x20; - status = - mos7840_set_reg_sync(serial->port[i], CLK_MULTI_REGISTER, - Data); + 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); @@ -2654,11 +2564,10 @@ static int mos7840_startup(struct usb_serial *serial) dbg("CLK_MULTI_REGISTER Writing success status%d\n", status); - //write value 0x0 to scratchpad register + /* write value 0x0 to scratchpad register */ Data = 0x00; - status = - mos7840_set_uart_reg(serial->port[i], SCRATCH_PAD_REGISTER, - Data); + status = mos7840_set_uart_reg(serial->port[i], + SCRATCH_PAD_REGISTER, Data); if (status < 0) { dbg("Writing SCRATCH_PAD_REGISTER failed status-0x%x\n", status); @@ -2667,21 +2576,17 @@ static int mos7840_startup(struct usb_serial *serial) dbg("SCRATCH_PAD_REGISTER Writing success status%d\n", status); - //Zero Length flag register + /* Zero Length flag register */ if ((mos7840_port->port_num != 1) && (serial->num_ports == 2)) { Data = 0xff; - status = 0; status = mos7840_set_reg_sync(serial->port[i], - (__u16) (ZLP_REG1 + - ((__u16) - mos7840_port-> - port_num)), - Data); + (__u16) (ZLP_REG1 + + ((__u16)mos7840_port->port_num)), Data); dbg("ZLIP offset%x\n", (__u16) (ZLP_REG1 + - ((__u16) mos7840_port->port_num))); + ((__u16) mos7840_port->port_num))); if (status < 0) { dbg("Writing ZLP_REG%d failed status-0x%x\n", i + 2, status); @@ -2691,13 +2596,9 @@ static int mos7840_startup(struct usb_serial *serial) i + 2, status); } else { Data = 0xff; - status = 0; status = mos7840_set_reg_sync(serial->port[i], - (__u16) (ZLP_REG1 + - ((__u16) - mos7840_port-> - port_num) - - 0x1), Data); + (__u16) (ZLP_REG1 + + ((__u16)mos7840_port->port_num) - 0x1), Data); dbg("ZLIP offset%x\n", (__u16) (ZLP_REG1 + ((__u16) mos7840_port->port_num) - 0x1)); @@ -2712,14 +2613,16 @@ static int mos7840_startup(struct usb_serial *serial) } 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) { + 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 + /* Zero Length flag enable */ Data = 0x0f; status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, Data); if (status < 0) { @@ -2762,7 +2665,7 @@ static void mos7840_shutdown(struct usb_serial *serial) return; } - /* check for the ports to be closed,close the ports and disconnect */ + /* check for the ports to be closed,close the ports and disconnect */ /* free private structure allocated for serial port * * stop reads and writes on all ports */ @@ -2843,20 +2746,12 @@ static int __init moschip7840_init(void) /* Register with the usb */ retval = usb_register(&io_driver); - - if (retval) - goto failed_usb_register; - if (retval == 0) { dbg("%s\n", "Leaving..."); return 0; } - - failed_usb_register: usb_serial_deregister(&moschip7840_4port_device); - - failed_port_device_register: - +failed_port_device_register: return retval; } diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c index 43c8894353bf..d6736531a0fa 100644 --- a/drivers/usb/serial/navman.c +++ b/drivers/usb/serial/navman.c @@ -64,7 +64,7 @@ static void navman_read_int_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -79,7 +79,8 @@ exit: __func__, result); } -static int navman_open(struct usb_serial_port *port, struct file *filp) +static int navman_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int result = 0; @@ -96,14 +97,15 @@ static int navman_open(struct usb_serial_port *port, struct file *filp) return result; } -static void navman_close(struct usb_serial_port *port, struct file *filp) +static void navman_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { dbg("%s - port %d", __func__, port->number); usb_kill_urb(port->interrupt_in_urb); } -static int navman_write(struct usb_serial_port *port, +static int navman_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { dbg("%s - port %d", __func__, port->number); diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 7b7422f49478..ae8e227f3db2 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -5,26 +5,28 @@ * modify it under the terms of the GNU General Public License version * 2 as published by the Free Software Foundation. * - * See Documentation/usb/usb-serial.txt for more information on using this driver + * See Documentation/usb/usb-serial.txt for more information on using this + * driver * * Please report both successes and troubles to the author at omninet@kroah.com - * + * * (05/30/2001) gkh - * switched from using spinlock to a semaphore, which fixes lots of problems. + * switched from using spinlock to a semaphore, which fixes lots of + * problems. * * (04/08/2001) gb * Identify version on module load. * * (11/01/2000) Adam J. Richter * usb_device_id table support - * + * * (10/05/2000) gkh * Fixed bug with urb->dev not being set properly, now that the usb * core needs it. - * + * * (08/28/2000) gkh * Added locks for SMP safeness. - * Fixed MOD_INC and MOD_DEC logic and the ability to open a port more + * Fixed MOD_INC and MOD_DEC logic and the ability to open a port more * than once. * Fixed potential race in omninet_write_bulk_callback * @@ -43,7 +45,7 @@ #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> @@ -58,25 +60,29 @@ static int debug; #define ZYXEL_VENDOR_ID 0x0586 #define ZYXEL_OMNINET_ID 0x1000 -#define BT_IGNITIONPRO_ID 0x2000 /* This one seems to be a re-branded ZyXEL device */ +/* This one seems to be a re-branded ZyXEL device */ +#define BT_IGNITIONPRO_ID 0x2000 /* function prototypes */ -static int omninet_open (struct usb_serial_port *port, struct file *filp); -static void omninet_close (struct usb_serial_port *port, struct file *filp); -static void omninet_read_bulk_callback (struct urb *urb); -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 [] = { +static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); +static void omninet_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); +static void omninet_read_bulk_callback(struct urb *urb); +static void omninet_write_bulk_callback(struct urb *urb); +static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); +static int omninet_write_room(struct tty_struct *tty); +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) }, { USB_DEVICE(ZYXEL_VENDOR_ID, BT_IGNITIONPRO_ID) }, { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, id_table); static struct usb_driver omninet_driver = { .name = "omninet", @@ -130,34 +136,34 @@ static struct usb_serial_driver zyxel_omninet_device = { * */ -struct omninet_header -{ +struct omninet_header { __u8 oh_seq; __u8 oh_len; __u8 oh_xxx; __u8 oh_pad; }; -struct omninet_data -{ - __u8 od_outseq; // Sequence number for bulk_out URBs +struct omninet_data { + __u8 od_outseq; /* Sequence number for bulk_out URBs */ }; -static int omninet_attach (struct usb_serial *serial) +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.", __func__, sizeof(struct omninet_data)); + od = kmalloc(sizeof(struct omninet_data), GFP_KERNEL); + if (!od) { + err("%s- kmalloc(%Zd) failed.", + __func__, 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) +static int omninet_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; struct usb_serial_port *wport; @@ -166,22 +172,24 @@ static int omninet_open (struct usb_serial_port *port, struct file *filp) dbg("%s - port %d", __func__, port->number); wport = serial->port[1]; - wport->tty = port->tty; + wport->port.tty = tty; /* FIXME */ /* Start reading from the device */ - usb_fill_bulk_urb(port->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, - omninet_read_bulk_callback, port); + usb_fill_bulk_urb(port->read_urb, serial->dev, + usb_rcvbulkpipe(serial->dev, + port->bulk_in_endpointAddress), + 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) { - err("%s - failed submitting read urb, error %d", __func__, result); - } - + if (result) + err("%s - failed submitting read urb, error %d", + __func__, result); return result; } -static void omninet_close (struct usb_serial_port *port, struct file * filp) +static void omninet_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { dbg("%s - port %d", __func__, port->number); usb_kill_urb(port->read_urb); @@ -192,14 +200,14 @@ static void omninet_close (struct usb_serial_port *port, struct file * filp) #define OMNINET_HEADERLEN sizeof(struct omninet_header) #define OMNINET_BULKOUTSIZE (64 - OMNINET_HEADERLEN) -static void omninet_read_bulk_callback (struct urb *urb) +static void omninet_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; unsigned char *data = urb->transfer_buffer; struct omninet_header *header = (struct omninet_header *) &data[0]; int status = urb->status; - int i; int result; + int i; dbg("%s - port %d", __func__, port->number); @@ -209,42 +217,46 @@ static void omninet_read_bulk_callback (struct urb *urb) return; } - if ((debug) && (header->oh_xxx != 0x30)) { + if (debug && header->oh_xxx != 0x30) { if (urb->actual_length) { - printk (KERN_DEBUG __FILE__ ": omninet_read %d: ", header->oh_len); - for (i = 0; i < (header->oh_len + OMNINET_HEADERLEN); i++) { - printk ("%.2x ", data[i]); - } - printk ("\n"); + printk(KERN_DEBUG __FILE__ + ": omninet_read %d: ", header->oh_len); + for (i = 0; i < (header->oh_len + + OMNINET_HEADERLEN); i++) + printk("%.2x ", data[i]); + printk("\n"); } } if (urb->actual_length && header->oh_len) { - for (i = 0; i < header->oh_len; i++) { - tty_insert_flip_char(port->tty, data[OMNINET_DATAOFFSET + i], 0); - } - tty_flip_buffer_push(port->tty); + tty_insert_flip_string(port->port.tty, + data + OMNINET_DATAOFFSET, header->oh_len); + tty_flip_buffer_push(port->port.tty); } /* Continue trying to always read */ - usb_fill_bulk_urb(urb, port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), - urb->transfer_buffer, urb->transfer_buffer_length, - omninet_read_bulk_callback, port); + usb_fill_bulk_urb(urb, port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress), + urb->transfer_buffer, urb->transfer_buffer_length, + omninet_read_bulk_callback, port); result = usb_submit_urb(urb, GFP_ATOMIC); if (result) - err("%s - failed resubmitting read urb, error %d", __func__, result); + err("%s - failed resubmitting read urb, error %d", + __func__, result); return; } -static int omninet_write (struct usb_serial_port *port, const unsigned char *buf, int count) +static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { - struct usb_serial *serial = port->serial; - struct usb_serial_port *wport = serial->port[1]; + struct usb_serial *serial = port->serial; + struct usb_serial_port *wport = serial->port[1]; - struct omninet_data *od = usb_get_serial_port_data(port); - struct omninet_header *header = (struct omninet_header *) wport->write_urb->transfer_buffer; + struct omninet_data *od = usb_get_serial_port_data(port); + struct omninet_header *header = (struct omninet_header *) + wport->write_urb->transfer_buffer; int result; @@ -252,7 +264,7 @@ static int omninet_write (struct usb_serial_port *port, const unsigned char *buf if (count == 0) { dbg("%s - write request of 0 bytes", __func__); - return (0); + return 0; } spin_lock_bh(&wport->lock); @@ -266,9 +278,11 @@ static int omninet_write (struct usb_serial_port *port, const unsigned char *buf count = (count > OMNINET_BULKOUTSIZE) ? OMNINET_BULKOUTSIZE : count; - memcpy (wport->write_urb->transfer_buffer + OMNINET_DATAOFFSET, buf, count); + memcpy(wport->write_urb->transfer_buffer + OMNINET_DATAOFFSET, + buf, count); - usb_serial_debug_data(debug, &port->dev, __func__, count, wport->write_urb->transfer_buffer); + usb_serial_debug_data(debug, &port->dev, __func__, count, + wport->write_urb->transfer_buffer); header->oh_seq = od->od_outseq++; header->oh_len = count; @@ -282,7 +296,8 @@ static int omninet_write (struct usb_serial_port *port, const unsigned char *buf result = usb_submit_urb(wport->write_urb, GFP_ATOMIC); if (result) { wport->write_urb_busy = 0; - err("%s - failed submitting write urb, error %d", __func__, result); + err("%s - failed submitting write urb, error %d", + __func__, result); } else result = count; @@ -290,8 +305,9 @@ static int omninet_write (struct usb_serial_port *port, const unsigned char *buf } -static int omninet_write_room (struct usb_serial_port *port) +static int omninet_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; struct usb_serial_port *wport = serial->port[1]; @@ -303,12 +319,13 @@ static int omninet_write_room (struct usb_serial_port *port) dbg("%s - returns %d", __func__, room); - return (room); + return room; } -static void omninet_write_bulk_callback (struct urb *urb) +static void omninet_write_bulk_callback(struct urb *urb) { -/* struct omninet_header *header = (struct omninet_header *) urb->transfer_buffer; */ +/* struct omninet_header *header = (struct omninet_header *) + urb->transfer_buffer; */ struct usb_serial_port *port = urb->context; int status = urb->status; @@ -325,18 +342,18 @@ static void omninet_write_bulk_callback (struct urb *urb) } -static void omninet_shutdown (struct usb_serial *serial) +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", __func__); + dbg("%s", __func__); usb_kill_urb(wport->write_urb); kfree(usb_get_serial_port_data(port)); } -static int __init omninet_init (void) +static int __init omninet_init(void) { int retval; retval = usb_serial_register(&zyxel_omninet_device); @@ -354,18 +371,18 @@ failed_usb_serial_register: } -static void __exit omninet_exit (void) +static void __exit omninet_exit(void) { - usb_deregister (&omninet_driver); - usb_serial_deregister (&zyxel_omninet_device); + usb_deregister(&omninet_driver); + usb_serial_deregister(&zyxel_omninet_device); } module_init(omninet_init); module_exit(omninet_exit); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index a73420dd052a..e4eca95f2b0f 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -43,29 +43,25 @@ #include <linux/usb/serial.h> /* Function prototypes */ -static int option_open(struct usb_serial_port *port, struct file *filp); -static void option_close(struct usb_serial_port *port, struct file *filp); +static int option_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); +static void option_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); static int option_startup(struct usb_serial *serial); static void option_shutdown(struct usb_serial *serial); -static void option_rx_throttle(struct usb_serial_port *port); -static void option_rx_unthrottle(struct usb_serial_port *port); -static int option_write_room(struct usb_serial_port *port); +static int option_write_room(struct tty_struct *tty); static void option_instat_callback(struct urb *urb); -static int option_write(struct usb_serial_port *port, +static int option_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); - -static int option_chars_in_buffer(struct usb_serial_port *port); -static int option_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg); -static void option_set_termios(struct usb_serial_port *port, - struct ktermios *old); -static void option_break_ctl(struct usb_serial_port *port, int break_state); -static int option_tiocmget(struct usb_serial_port *port, struct file *file); -static int option_tiocmset(struct usb_serial_port *port, struct file *file, +static int option_chars_in_buffer(struct tty_struct *tty); +static void option_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); +static int option_tiocmget(struct tty_struct *tty, struct file *file); +static int option_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); -static int option_send_setup(struct usb_serial_port *port); +static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *port); /* Vendor and product IDs */ #define OPTION_VENDOR_ID 0x0AF0 @@ -173,6 +169,7 @@ static int option_send_setup(struct usb_serial_port *port); #define DELL_VENDOR_ID 0x413C #define KYOCERA_VENDOR_ID 0x0c88 +#define KYOCERA_PRODUCT_KPC650 0x17da #define KYOCERA_PRODUCT_KPC680 0x180a #define ANYDATA_VENDOR_ID 0x16d5 @@ -305,6 +302,7 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(ONDA_VENDOR_ID, ONDA_PRODUCT_ET502HS) }, { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) }, { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) }, + { USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC650) }, { USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC680) }, { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */ @@ -340,11 +338,7 @@ static struct usb_serial_driver option_1port_device = { .write = option_write, .write_room = option_write_room, .chars_in_buffer = option_chars_in_buffer, - .throttle = option_rx_throttle, - .unthrottle = option_rx_unthrottle, - .ioctl = option_ioctl, .set_termios = option_set_termios, - .break_ctl = option_break_ctl, .tiocmget = option_tiocmget, .tiocmset = option_tiocmset, .attach = option_startup, @@ -401,47 +395,32 @@ static int __init option_init(void) return 0; failed_driver_register: - usb_serial_deregister (&option_1port_device); + usb_serial_deregister(&option_1port_device); failed_1port_device_register: return retval; } static void __exit option_exit(void) { - usb_deregister (&option_driver); - usb_serial_deregister (&option_1port_device); + usb_deregister(&option_driver); + usb_serial_deregister(&option_1port_device); } module_init(option_init); module_exit(option_exit); -static void option_rx_throttle(struct usb_serial_port *port) -{ - dbg("%s", __func__); -} - -static void option_rx_unthrottle(struct usb_serial_port *port) -{ - dbg("%s", __func__); -} - -static void option_break_ctl(struct usb_serial_port *port, int break_state) -{ - /* Unfortunately, I don't know how to send a break */ - dbg("%s", __func__); -} - -static void option_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void option_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { dbg("%s", __func__); /* Doesn't support option setting */ - tty_termios_copy_hw(port->tty->termios, old_termios); - option_send_setup(port); + tty_termios_copy_hw(tty->termios, old_termios); + option_send_setup(tty, port); } -static int option_tiocmget(struct usb_serial_port *port, struct file *file) +static int option_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; unsigned int value; struct option_port_private *portdata; @@ -457,9 +436,10 @@ static int option_tiocmget(struct usb_serial_port *port, struct file *file) return value; } -static int option_tiocmset(struct usb_serial_port *port, struct file *file, +static int option_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct option_port_private *portdata; portdata = usb_get_serial_port_data(port); @@ -474,17 +454,11 @@ static int option_tiocmset(struct usb_serial_port *port, struct file *file, portdata->rts_state = 0; if (clear & TIOCM_DTR) portdata->dtr_state = 0; - return option_send_setup(port); -} - -static int option_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return -ENOIOCTLCMD; + return option_send_setup(tty, port); } /* Write */ -static int option_write(struct usb_serial_port *port, +static int option_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { struct option_port_private *portdata; @@ -499,7 +473,7 @@ static int option_write(struct usb_serial_port *port, i = 0; left = count; - for (i=0; left > 0 && i < N_OUT_URB; i++) { + for (i = 0; left > 0 && i < N_OUT_URB; i++) { todo = left; if (todo > OUT_BUFLEN) todo = OUT_BUFLEN; @@ -520,7 +494,7 @@ static int option_write(struct usb_serial_port *port, usb_pipeendpoint(this_urb->pipe), i); /* send the data */ - memcpy (this_urb->transfer_buffer, buf, todo); + memcpy(this_urb->transfer_buffer, buf, todo); this_urb->transfer_buffer_length = todo; this_urb->dev = port->serial->dev; @@ -560,7 +534,7 @@ static void option_indat_callback(struct urb *urb) dbg("%s: nonzero status: %d on endpoint %02x.", __func__, status, endpoint); } else { - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -570,7 +544,7 @@ static void option_indat_callback(struct urb *urb) } /* Resubmit urb so we continue receiving */ - if (port->open_count && status != -ESHUTDOWN) { + if (port->port.count && status != -ESHUTDOWN) { err = usb_submit_urb(urb, GFP_ATOMIC); if (err) printk(KERN_ERR "%s: resubmit read urb failed. " @@ -611,7 +585,7 @@ static void option_instat_callback(struct urb *urb) struct usb_serial *serial = port->serial; dbg("%s", __func__); - dbg("%s: urb %p port %p has data %p", __func__,urb,port,portdata); + dbg("%s: urb %p port %p has data %p", __func__, urb, port, portdata); if (status == 0) { struct usb_ctrlrequest *req_pkt = @@ -636,12 +610,12 @@ static void option_instat_callback(struct urb *urb) portdata->dsr_state = ((signals & 0x02) ? 1 : 0); portdata->ri_state = ((signals & 0x08) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) && + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state && !portdata->dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); } else { dbg("%s: type %x req %x", __func__, - req_pkt->bRequestType,req_pkt->bRequest); + req_pkt->bRequestType, req_pkt->bRequest); } } else dbg("%s: error %d", __func__, status); @@ -656,8 +630,9 @@ static void option_instat_callback(struct urb *urb) } } -static int option_write_room(struct usb_serial_port *port) +static int option_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct option_port_private *portdata; int i; int data_len = 0; @@ -666,7 +641,7 @@ static int option_write_room(struct usb_serial_port *port) portdata = usb_get_serial_port_data(port); - for (i=0; i < N_OUT_URB; i++) { + for (i = 0; i < N_OUT_URB; i++) { this_urb = portdata->out_urbs[i]; if (this_urb && !test_bit(i, &portdata->out_busy)) data_len += OUT_BUFLEN; @@ -676,8 +651,9 @@ static int option_write_room(struct usb_serial_port *port) return data_len; } -static int option_chars_in_buffer(struct usb_serial_port *port) +static int option_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct option_port_private *portdata; int i; int data_len = 0; @@ -685,7 +661,7 @@ static int option_chars_in_buffer(struct usb_serial_port *port) portdata = usb_get_serial_port_data(port); - for (i=0; i < N_OUT_URB; i++) { + for (i = 0; i < N_OUT_URB; i++) { this_urb = portdata->out_urbs[i]; /* FIXME: This locking is insufficient as this_urb may go unused during the test */ @@ -696,7 +672,8 @@ static int option_chars_in_buffer(struct usb_serial_port *port) return data_len; } -static int option_open(struct usb_serial_port *port, struct file *filp) +static int option_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct option_port_private *portdata; struct usb_serial *serial = port->serial; @@ -714,7 +691,7 @@ static int option_open(struct usb_serial_port *port, struct file *filp) /* Reset low level data toggle and start reading from endpoints */ for (i = 0; i < N_IN_URB; i++) { urb = portdata->in_urbs[i]; - if (! urb) + if (!urb) continue; if (urb->dev != serial->dev) { dbg("%s: dev %p != %p", __func__, @@ -739,21 +716,23 @@ static int option_open(struct usb_serial_port *port, struct file *filp) /* Reset low level data toggle on out endpoints */ for (i = 0; i < N_OUT_URB; i++) { urb = portdata->out_urbs[i]; - if (! urb) + if (!urb) continue; urb->dev = serial->dev; /* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 0); */ } - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; - option_send_setup(port); + option_send_setup(tty, port); - return (0); + return 0; } -static void option_close(struct usb_serial_port *port, struct file *filp) +static void option_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int i; struct usb_serial *serial = port->serial; @@ -768,7 +747,7 @@ static void option_close(struct usb_serial_port *port, struct file *filp) if (serial->dev) { mutex_lock(&serial->disc_mutex); if (!serial->disconnected) - option_send_setup(port); + option_send_setup(tty, port); mutex_unlock(&serial->disc_mutex); /* Stop reading/writing urbs */ @@ -777,7 +756,7 @@ static void option_close(struct usb_serial_port *port, struct file *filp) for (i = 0; i < N_OUT_URB; i++) usb_kill_urb(portdata->out_urbs[i]); } - port->tty = NULL; + port->port.tty = NULL; /* FIXME */ } /* Helper functions used by option_setup_urbs */ @@ -807,7 +786,7 @@ static struct urb *option_setup_urb(struct usb_serial *serial, int endpoint, /* Setup urbs */ static void option_setup_urbs(struct usb_serial *serial) { - int i,j; + int i, j; struct usb_serial_port *port; struct option_port_private *portdata; @@ -817,18 +796,22 @@ static void option_setup_urbs(struct usb_serial *serial) port = serial->port[i]; portdata = usb_get_serial_port_data(port); - /* Do indat endpoints first */ + /* Do indat endpoints first */ for (j = 0; j < N_IN_URB; ++j) { - portdata->in_urbs[j] = option_setup_urb (serial, - port->bulk_in_endpointAddress, USB_DIR_IN, port, - portdata->in_buffer[j], IN_BUFLEN, option_indat_callback); + portdata->in_urbs[j] = option_setup_urb(serial, + port->bulk_in_endpointAddress, + USB_DIR_IN, port, + portdata->in_buffer[j], + IN_BUFLEN, option_indat_callback); } /* outdat endpoints */ for (j = 0; j < N_OUT_URB; ++j) { - portdata->out_urbs[j] = option_setup_urb (serial, - port->bulk_out_endpointAddress, USB_DIR_OUT, port, - portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback); + portdata->out_urbs[j] = option_setup_urb(serial, + port->bulk_out_endpointAddress, + USB_DIR_OUT, port, + portdata->out_buffer[j], + OUT_BUFLEN, option_outdat_callback); } } } @@ -839,7 +822,8 @@ static void option_setup_urbs(struct usb_serial *serial) * This is exactly the same as SET_CONTROL_LINE_STATE from the PSTN * CDC. */ -static int option_send_setup(struct usb_serial_port *port) +static int option_send_setup(struct tty_struct *tty, + struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct option_port_private *portdata; @@ -848,7 +832,7 @@ static int option_send_setup(struct usb_serial_port *port) portdata = usb_get_serial_port_data(port); - if (port->tty) { + if (tty) { int val = 0; if (portdata->dtr_state) val |= 0x01; @@ -856,10 +840,9 @@ static int option_send_setup(struct usb_serial_port *port) val |= 0x02; return usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev, 0), - 0x22,0x21,val,ifNum,NULL,0,USB_CTRL_SET_TIMEOUT); + usb_rcvctrlpipe(serial->dev, 0), + 0x22, 0x21, val, ifNum, NULL, 0, USB_CTRL_SET_TIMEOUT); } - return 0; } @@ -879,7 +862,7 @@ static int option_startup(struct usb_serial *serial) if (!portdata) { dbg("%s: kmalloc for option_port_private (%d) failed!.", __func__, i); - return (1); + return 1; } for (j = 0; j < N_IN_URB; j++) { @@ -898,17 +881,15 @@ static int option_startup(struct usb_serial *serial) usb_set_serial_port_data(port, portdata); - if (! port->interrupt_in_urb) + if (!port->interrupt_in_urb) continue; err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (err) dbg("%s: submit irq_in urb failed %d", __func__, err); } - option_setup_urbs(serial); - - return (0); + return 0; bail_out_error2: for (j = 0; j < N_OUT_URB; j++) @@ -947,7 +928,8 @@ static void option_shutdown(struct usb_serial *serial) for (j = 0; j < N_IN_URB; j++) { if (portdata->in_urbs[j]) { usb_free_urb(portdata->in_urbs[j]); - free_page((unsigned long)portdata->in_buffer[j]); + free_page((unsigned long) + portdata->in_buffer[j]); portdata->in_urbs[j] = NULL; } } diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index a9625c180dc3..81db5715ee25 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -25,7 +25,8 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. * - * See Documentation/usb/usb-serial.txt for more information on using this driver + * See Documentation/usb/usb-serial.txt for more information on using this + * driver * * TODO: * - implement correct flushing for ioctls and oti6858_close() @@ -49,7 +50,7 @@ #include <linux/spinlock.h> #include <linux/usb.h> #include <linux/usb/serial.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include "oti6858.h" #define OTI6858_DESCRIPTION \ @@ -135,27 +136,28 @@ struct oti6858_control_pkt { #define OTI6858_CTRL_PKT_SIZE sizeof(struct oti6858_control_pkt) #define OTI6858_CTRL_EQUALS_PENDING(a, priv) \ - ( ((a)->divisor == (priv)->pending_setup.divisor) \ + (((a)->divisor == (priv)->pending_setup.divisor) \ && ((a)->control == (priv)->pending_setup.control) \ - && ((a)->frame_fmt == (priv)->pending_setup.frame_fmt) ) + && ((a)->frame_fmt == (priv)->pending_setup.frame_fmt)) /* function prototypes */ -static int oti6858_open(struct usb_serial_port *port, struct file *filp); -static void oti6858_close(struct usb_serial_port *port, struct file *filp); -static void oti6858_set_termios(struct usb_serial_port *port, - struct ktermios *old); -static int oti6858_ioctl(struct usb_serial_port *port, struct file *file, +static int oti6858_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void oti6858_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void oti6858_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); +static int oti6858_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); static void oti6858_read_int_callback(struct urb *urb); static void oti6858_read_bulk_callback(struct urb *urb); static void oti6858_write_bulk_callback(struct urb *urb); -static int oti6858_write(struct usb_serial_port *port, +static int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); -static int oti6858_write_room(struct usb_serial_port *port); -static void oti6858_break_ctl(struct usb_serial_port *port, int break_state); -static int oti6858_chars_in_buffer(struct usb_serial_port *port); -static int oti6858_tiocmget(struct usb_serial_port *port, struct file *file); -static int oti6858_tiocmset(struct usb_serial_port *port, struct file *file, +static int oti6858_write_room(struct tty_struct *tty); +static int oti6858_chars_in_buffer(struct tty_struct *tty); +static int oti6858_tiocmget(struct tty_struct *tty, struct file *file); +static int oti6858_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int oti6858_startup(struct usb_serial *serial); static void oti6858_shutdown(struct usb_serial *serial); @@ -184,7 +186,6 @@ static struct usb_serial_driver oti6858_device = { .close = oti6858_close, .write = oti6858_write, .ioctl = oti6858_ioctl, - .break_ctl = oti6858_break_ctl, .set_termios = oti6858_set_termios, .tiocmget = oti6858_tiocmget, .tiocmset = oti6858_tiocmset, @@ -220,7 +221,7 @@ struct oti6858_private { struct delayed_work delayed_setup_work; wait_queue_head_t intr_wait; - struct usb_serial_port *port; /* USB port with which associated */ + struct usb_serial_port *port; /* USB port with which associated */ }; #undef dbg @@ -229,7 +230,8 @@ struct oti6858_private { static void setup_line(struct work_struct *work) { - struct oti6858_private *priv = container_of(work, struct oti6858_private, delayed_setup_work.work); + struct oti6858_private *priv = container_of(work, + struct oti6858_private, delayed_setup_work.work); struct usb_serial_port *port = priv->port; struct oti6858_control_pkt *new_setup; unsigned long flags; @@ -237,10 +239,12 @@ static void setup_line(struct work_struct *work) dbg("%s(port = %d)", __func__, port->number); - if ((new_setup = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL)) == NULL) { + new_setup = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL); + if (new_setup == NULL) { dev_err(&port->dev, "%s(): out of memory!\n", __func__); /* we will try again */ - schedule_delayed_work(&priv->delayed_setup_work, msecs_to_jiffies(2)); + schedule_delayed_work(&priv->delayed_setup_work, + msecs_to_jiffies(2)); return; } @@ -256,7 +260,8 @@ static void setup_line(struct work_struct *work) dev_err(&port->dev, "%s(): error reading status\n", __func__); kfree(new_setup); /* we will try again */ - schedule_delayed_work(&priv->delayed_setup_work, msecs_to_jiffies(2)); + schedule_delayed_work(&priv->delayed_setup_work, + msecs_to_jiffies(2)); return; } @@ -297,7 +302,8 @@ static void setup_line(struct work_struct *work) void send_data(struct work_struct *work) { - struct oti6858_private *priv = container_of(work, struct oti6858_private, delayed_write_work.work); + struct oti6858_private *priv = container_of(work, + struct oti6858_private, delayed_write_work.work); struct usb_serial_port *port = priv->port; int count = 0, result; unsigned long flags; @@ -308,7 +314,8 @@ void send_data(struct work_struct *work) spin_lock_irqsave(&priv->lock, flags); if (priv->flags.write_urb_in_use) { spin_unlock_irqrestore(&priv->lock, flags); - schedule_delayed_work(&priv->delayed_write_work, msecs_to_jiffies(2)); + schedule_delayed_work(&priv->delayed_write_work, + msecs_to_jiffies(2)); return; } priv->flags.write_urb_in_use = 1; @@ -359,8 +366,8 @@ void send_data(struct work_struct *work) static int oti6858_startup(struct usb_serial *serial) { - struct usb_serial_port *port = serial->port[0]; - struct oti6858_private *priv; + struct usb_serial_port *port = serial->port[0]; + struct oti6858_private *priv; int i; for (i = 0; i < serial->num_ports; ++i) { @@ -375,8 +382,8 @@ static int oti6858_startup(struct usb_serial *serial) spin_lock_init(&priv->lock); init_waitqueue_head(&priv->intr_wait); -// INIT_WORK(&priv->setup_work, setup_line, serial->port[i]); -// INIT_WORK(&priv->write_work, send_data, serial->port[i]); +/* INIT_WORK(&priv->setup_work, setup_line, serial->port[i]); */ +/* INIT_WORK(&priv->write_work, send_data, serial->port[i]); */ priv->port = port; INIT_DELAYED_WORK(&priv->delayed_setup_work, setup_line); INIT_DELAYED_WORK(&priv->delayed_write_work, send_data); @@ -395,7 +402,7 @@ static int oti6858_startup(struct usb_serial *serial) return -ENOMEM; } -static int oti6858_write(struct usb_serial_port *port, +static int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { struct oti6858_private *priv = usb_get_serial_port_data(port); @@ -413,8 +420,9 @@ static int oti6858_write(struct usb_serial_port *port, return count; } -static int oti6858_write_room(struct usb_serial_port *port) +static int oti6858_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct oti6858_private *priv = usb_get_serial_port_data(port); int room = 0; unsigned long flags; @@ -428,8 +436,9 @@ static int oti6858_write_room(struct usb_serial_port *port) return room; } -static int oti6858_chars_in_buffer(struct usb_serial_port *port) +static int oti6858_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct oti6858_private *priv = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; @@ -443,8 +452,8 @@ static int oti6858_chars_in_buffer(struct usb_serial_port *port) return chars; } -static void oti6858_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void oti6858_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct oti6858_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -455,22 +464,22 @@ static void oti6858_set_termios(struct usb_serial_port *port, dbg("%s(port = %d)", __func__, port->number); - if (!port->tty || !port->tty->termios) { + if (!tty) { dbg("%s(): no tty structures", __func__); return; } spin_lock_irqsave(&priv->lock, flags); if (!priv->flags.termios_initialized) { - *(port->tty->termios) = tty_std_termios; - port->tty->termios->c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL; + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL; + tty->termios->c_ispeed = 38400; + tty->termios->c_ospeed = 38400; priv->flags.termios_initialized = 1; - port->tty->termios->c_ispeed = 38400; - port->tty->termios->c_ospeed = 38400; } spin_unlock_irqrestore(&priv->lock, flags); - cflag = port->tty->termios->c_cflag; + cflag = tty->termios->c_cflag; spin_lock_irqsave(&priv->lock, flags); divisor = priv->pending_setup.divisor; @@ -480,19 +489,19 @@ static void oti6858_set_termios(struct usb_serial_port *port, frame_fmt &= ~FMT_DATA_BITS_MASK; switch (cflag & CSIZE) { - case CS5: - frame_fmt |= FMT_DATA_BITS_5; - break; - case CS6: - frame_fmt |= FMT_DATA_BITS_6; - break; - case CS7: - frame_fmt |= FMT_DATA_BITS_7; - break; - default: - case CS8: - frame_fmt |= FMT_DATA_BITS_8; - break; + case CS5: + frame_fmt |= FMT_DATA_BITS_5; + break; + case CS6: + frame_fmt |= FMT_DATA_BITS_6; + break; + case CS7: + frame_fmt |= FMT_DATA_BITS_7; + break; + default: + case CS8: + frame_fmt |= FMT_DATA_BITS_8; + break; } /* manufacturer claims that this device can work with baud rates @@ -500,7 +509,7 @@ static void oti6858_set_termios(struct usb_serial_port *port, * guarantee that any other baud rate will work (especially * the higher ones) */ - br = tty_get_baud_rate(port->tty); + br = tty_get_baud_rate(tty); if (br == 0) { divisor = 0; } else { @@ -511,23 +520,21 @@ static void oti6858_set_termios(struct usb_serial_port *port, new_divisor = (96000000 + 8 * br) / (16 * br); real_br = 96000000 / (16 * new_divisor); divisor = cpu_to_le16(new_divisor); - tty_encode_baud_rate(port->tty, real_br, real_br); + tty_encode_baud_rate(tty, real_br, real_br); } frame_fmt &= ~FMT_STOP_BITS_MASK; - if ((cflag & CSTOPB) != 0) { + if ((cflag & CSTOPB) != 0) frame_fmt |= FMT_STOP_BITS_2; - } else { + else frame_fmt |= FMT_STOP_BITS_1; - } frame_fmt &= ~FMT_PARITY_MASK; if ((cflag & PARENB) != 0) { - if ((cflag & PARODD) != 0) { + if ((cflag & PARODD) != 0) frame_fmt |= FMT_PARITY_ODD; - } else { + else frame_fmt |= FMT_PARITY_EVEN; - } } else { frame_fmt |= FMT_PARITY_NONE; } @@ -564,7 +571,8 @@ static void oti6858_set_termios(struct usb_serial_port *port, spin_unlock_irqrestore(&priv->lock, flags); } -static int oti6858_open(struct usb_serial_port *port, struct file *filp) +static int oti6858_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct oti6858_private *priv = usb_get_serial_port_data(port); struct ktermios tmp_termios; @@ -578,10 +586,11 @@ static int oti6858_open(struct usb_serial_port *port, struct file *filp) usb_clear_halt(serial->dev, port->write_urb->pipe); usb_clear_halt(serial->dev, port->read_urb->pipe); - if (port->open_count != 1) + if (port->port.count != 1) return 0; - if ((buf = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL)) == NULL) { + buf = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL); + if (buf == NULL) { dev_err(&port->dev, "%s(): out of memory!\n", __func__); return -ENOMEM; } @@ -617,18 +626,19 @@ static int oti6858_open(struct usb_serial_port *port, struct file *filp) if (result != 0) { dev_err(&port->dev, "%s(): usb_submit_urb() failed" " with error %d\n", __func__, result); - oti6858_close(port, NULL); + oti6858_close(tty, port, NULL); return -EPROTO; } /* setup termios */ - if (port->tty) - oti6858_set_termios(port, &tmp_termios); + if (tty) + oti6858_set_termios(tty, port, &tmp_termios); return 0; } -static void oti6858_close(struct usb_serial_port *port, struct file *filp) +static void oti6858_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct oti6858_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -641,7 +651,7 @@ static void oti6858_close(struct usb_serial_port *port, struct file *filp) spin_lock_irqsave(&priv->lock, flags); timeout = 30 * HZ; /* PL2303_CLOSING_WAIT */ init_waitqueue_entry(&wait, current); - add_wait_queue(&port->tty->write_wait, &wait); + add_wait_queue(&tty->write_wait, &wait); dbg("%s(): entering wait loop", __func__); for (;;) { set_current_state(TASK_INTERRUPTIBLE); @@ -654,7 +664,7 @@ static void oti6858_close(struct usb_serial_port *port, struct file *filp) spin_lock_irqsave(&priv->lock, flags); } set_current_state(TASK_RUNNING); - remove_wait_queue(&port->tty->write_wait, &wait); + remove_wait_queue(&tty->write_wait, &wait); dbg("%s(): after wait loop", __func__); /* clear out any remaining data in the buffer */ @@ -669,7 +679,7 @@ static void oti6858_close(struct usb_serial_port *port, struct file *filp) /* data is in the buffer to compute a delay */ /* that is not unnecessarily long) */ /* FIXME - bps = tty_get_baud_rate(port->tty); + bps = tty_get_baud_rate(tty); if (bps > 1200) timeout = max((HZ*2560)/bps,HZ/10); else @@ -690,7 +700,7 @@ static void oti6858_close(struct usb_serial_port *port, struct file *filp) usb_kill_urb(port->interrupt_in_urb); /* - if (port->tty && (port->tty->termios->c_cflag) & HUPCL) { + if (tty && (tty->termios->c_cflag) & HUPCL) { // drop DTR and RTS spin_lock_irqsave(&priv->lock, flags); priv->pending_setup.control &= ~CONTROL_MASK; @@ -699,9 +709,10 @@ static void oti6858_close(struct usb_serial_port *port, struct file *filp) */ } -static int oti6858_tiocmset(struct usb_serial_port *port, struct file *file, +static int oti6858_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct oti6858_private *priv = usb_get_serial_port_data(port); unsigned long flags; u8 control; @@ -724,16 +735,16 @@ static int oti6858_tiocmset(struct usb_serial_port *port, struct file *file, if ((clear & TIOCM_DTR) != 0) control &= ~CONTROL_DTR_HIGH; - if (control != priv->pending_setup.control) { + if (control != priv->pending_setup.control) priv->pending_setup.control = control; - } - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); return 0; } -static int oti6858_tiocmget(struct usb_serial_port *port, struct file *file) +static int oti6858_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct oti6858_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned pin_state; @@ -779,7 +790,8 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) spin_unlock_irqrestore(&priv->lock, flags); while (1) { - wait_event_interruptible(priv->intr_wait, priv->status.pin_state != prev); + wait_event_interruptible(priv->intr_wait, + priv->status.pin_state != prev); if (signal_pending(current)) return -ERESTARTSYS; @@ -789,12 +801,11 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) changed = prev ^ status; /* FIXME: check if this is correct (active high/low) */ - if ( ((arg & TIOCM_RNG) && (changed & PIN_RI)) || - ((arg & TIOCM_DSR) && (changed & PIN_DSR)) || - ((arg & TIOCM_CD) && (changed & PIN_DCD)) || - ((arg & TIOCM_CTS) && (changed & PIN_CTS))) { - return 0; - } + if (((arg & TIOCM_RNG) && (changed & PIN_RI)) || + ((arg & TIOCM_DSR) && (changed & PIN_DSR)) || + ((arg & TIOCM_CD) && (changed & PIN_DCD)) || + ((arg & TIOCM_CTS) && (changed & PIN_CTS))) + return 0; prev = status; } @@ -802,56 +813,25 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) return 0; } -static int oti6858_ioctl(struct usb_serial_port *port, struct file *file, +static int oti6858_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { - void __user *user_arg = (void __user *) arg; - unsigned int x; + struct usb_serial_port *port = tty->driver_data; dbg("%s(port = %d, cmd = 0x%04x, arg = 0x%08lx)", __func__, port->number, cmd, arg); switch (cmd) { - case TIOCMBIS: - if (copy_from_user(&x, user_arg, sizeof(x))) - return -EFAULT; - return oti6858_tiocmset(port, NULL, x, 0); - - case TIOCMBIC: - if (copy_from_user(&x, user_arg, sizeof(x))) - return -EFAULT; - return oti6858_tiocmset(port, NULL, 0, x); - - case TIOCMIWAIT: - dbg("%s(): TIOCMIWAIT", __func__); - return wait_modem_info(port, arg); - - default: - dbg("%s(): 0x%04x not supported", __func__, cmd); - break; + case TIOCMIWAIT: + dbg("%s(): TIOCMIWAIT", __func__); + return wait_modem_info(port, arg); + default: + dbg("%s(): 0x%04x not supported", __func__, cmd); + break; } - return -ENOIOCTLCMD; } -static void oti6858_break_ctl(struct usb_serial_port *port, int break_state) -{ - int state; - - dbg("%s(port = %d)", __func__, port->number); - - state = (break_state == 0) ? 0 : 1; - dbg("%s(): turning break %s", __func__, state ? "on" : "off"); - - /* FIXME */ -/* - result = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0), - BREAK_REQUEST, BREAK_REQUEST_TYPE, state, - 0, NULL, 0, 100); - if (result != 0) - dbg("%s(): error sending break", __func__); - */ -} static void oti6858_shutdown(struct usb_serial *serial) { @@ -964,7 +944,7 @@ static void oti6858_read_int_callback(struct urb *urb) spin_lock_irqsave(&priv->lock, flags); if (priv->flags.write_urb_in_use == 0 && oti6858_buf_data_avail(priv->buf) != 0) { - schedule_delayed_work(&priv->delayed_write_work,0); + schedule_delayed_work(&priv->delayed_write_work, 0); resubmit = 0; } spin_unlock_irqrestore(&priv->lock, flags); @@ -973,7 +953,7 @@ static void oti6858_read_int_callback(struct urb *urb) if (resubmit) { int result; -// dbg("%s(): submitting interrupt urb", __func__); +/* dbg("%s(): submitting interrupt urb", __func__); */ urb->dev = port->serial->dev; result = usb_submit_urb(urb, GFP_ATOMIC); if (result != 0) { @@ -1002,14 +982,16 @@ static void oti6858_read_bulk_callback(struct urb *urb) spin_unlock_irqrestore(&priv->lock, flags); if (status != 0) { - if (!port->open_count) { + if (!port->port.count) { dbg("%s(): port is closed, exiting", __func__); return; } /* if (status == -EPROTO) { - // PL2303 mysteriously fails with -EPROTO reschedule the read - dbg("%s - caught -EPROTO, resubmitting the urb", __func__); + * PL2303 mysteriously fails with -EPROTO reschedule + the read * + dbg("%s - caught -EPROTO, resubmitting the urb", + __func__); result = usb_submit_urb(urb, GFP_ATOMIC); if (result) dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __func__, result); @@ -1020,14 +1002,14 @@ static void oti6858_read_bulk_callback(struct urb *urb) return; } - tty = port->tty; + tty = port->port.tty; if (tty != NULL && urb->actual_length > 0) { tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } - // schedule the interrupt urb if we are still open */ - if (port->open_count != 0) { + /* schedule the interrupt urb if we are still open */ + if (port->port.count != 0) { port->interrupt_in_urb->dev = port->serial->dev; result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); if (result != 0) { @@ -1078,7 +1060,7 @@ static void oti6858_write_bulk_callback(struct urb *urb) priv->flags.write_urb_in_use = 0; - // schedule the interrupt urb if we are still open */ + /* schedule the interrupt urb if we are still open */ port->interrupt_in_urb->dev = port->serial->dev; dbg("%s(): submitting interrupt urb", __func__); result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); @@ -1153,7 +1135,7 @@ static unsigned int oti6858_buf_data_avail(struct oti6858_buf *pb) { if (pb == NULL) return 0; - return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size); + return (pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size; } /* @@ -1166,7 +1148,7 @@ static unsigned int oti6858_buf_space_avail(struct oti6858_buf *pb) { if (pb == NULL) return 0; - return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size); + return (pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size; } /* @@ -1253,13 +1235,12 @@ static int __init oti6858_init(void) { int retval; - if ((retval = usb_serial_register(&oti6858_device)) == 0) { - if ((retval = usb_register(&oti6858_driver)) != 0) + retval = usb_serial_register(&oti6858_device); + if (retval == 0) { + retval = usb_register(&oti6858_driver); + if (retval) usb_serial_deregister(&oti6858_device); - else - return 0; } - return retval; } diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 2a0dd1b50dc4..2c9c446ad625 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -10,7 +10,8 @@ * modify it under the terms of the GNU General Public License version * 2 as published by the Free Software Foundation. * - * See Documentation/usb/usb-serial.txt for more information on using this driver + * See Documentation/usb/usb-serial.txt for more information on using this + * driver * */ @@ -25,7 +26,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/spinlock.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include "pl2303.h" @@ -116,7 +117,7 @@ static struct usb_driver pl2303_driver = { #define CONTROL_RTS 0x02 #define BREAK_REQUEST_TYPE 0x21 -#define BREAK_REQUEST 0x23 +#define BREAK_REQUEST 0x23 #define BREAK_ON 0xffff #define BREAK_OFF 0x0000 @@ -222,7 +223,7 @@ static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb) if (pb == NULL) return 0; - return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size); + return (pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size; } /* @@ -236,7 +237,7 @@ static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb) if (pb == NULL) return 0; - return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size); + return (pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size; } /* @@ -395,7 +396,7 @@ static int pl2303_startup(struct usb_serial *serial) cleanup: kfree(buf); - for (--i; i>=0; --i) { + for (--i; i >= 0; --i) { priv = usb_get_serial_port_data(serial->port[i]); pl2303_buf_free(priv->buf); kfree(priv); @@ -407,7 +408,7 @@ cleanup: static int set_control_lines(struct usb_device *dev, u8 value) { int retval; - + retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE, value, 0, NULL, 0, 100); @@ -452,14 +453,14 @@ static void pl2303_send(struct usb_serial_port *port) dev_err(&port->dev, "%s - failed submitting write urb," " error %d\n", __func__, result); priv->write_urb_in_use = 0; - // TODO: reschedule pl2303_send + /* TODO: reschedule pl2303_send */ } usb_serial_port_softint(port); } -static int pl2303_write(struct usb_serial_port *port, const unsigned char *buf, - int count) +static int pl2303_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -478,8 +479,9 @@ static int pl2303_write(struct usb_serial_port *port, const unsigned char *buf, return count; } -static int pl2303_write_room(struct usb_serial_port *port) +static int pl2303_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct pl2303_private *priv = usb_get_serial_port_data(port); int room = 0; unsigned long flags; @@ -494,8 +496,9 @@ static int pl2303_write_room(struct usb_serial_port *port) return room; } -static int pl2303_chars_in_buffer(struct usb_serial_port *port) +static int pl2303_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct pl2303_private *priv = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; @@ -510,8 +513,8 @@ static int pl2303_chars_in_buffer(struct usb_serial_port *port) return chars; } -static void pl2303_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void pl2303_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; struct pl2303_private *priv = usb_get_serial_port_data(port); @@ -526,11 +529,10 @@ static void pl2303_set_termios(struct usb_serial_port *port, spin_lock_irqsave(&priv->lock, flags); if (!priv->termios_initialized) { - *(port->tty->termios) = tty_std_termios; - port->tty->termios->c_cflag = B9600 | CS8 | CREAD | - HUPCL | CLOCAL; - port->tty->termios->c_ispeed = 9600; - port->tty->termios->c_ospeed = 9600; + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + tty->termios->c_ispeed = 9600; + tty->termios->c_ospeed = 9600; priv->termios_initialized = 1; } spin_unlock_irqrestore(&priv->lock, flags); @@ -539,16 +541,16 @@ static void pl2303_set_termios(struct usb_serial_port *port, serial settings even to the same values as before. Thus we actually need to filter in this specific case */ - if (!tty_termios_hw_change(port->tty->termios, old_termios)) + if (!tty_termios_hw_change(tty->termios, old_termios)) return; - cflag = port->tty->termios->c_cflag; + cflag = tty->termios->c_cflag; buf = kzalloc(7, GFP_KERNEL); if (!buf) { dev_err(&port->dev, "%s - out of memory.\n", __func__); /* Report back no change occurred */ - *port->tty->termios = *old_termios; + *tty->termios = *old_termios; return; } @@ -560,16 +562,24 @@ static void pl2303_set_termios(struct usb_serial_port *port, if (cflag & CSIZE) { switch (cflag & CSIZE) { - case CS5: buf[6] = 5; break; - case CS6: buf[6] = 6; break; - case CS7: buf[6] = 7; break; - default: - case CS8: buf[6] = 8; break; + case CS5: + buf[6] = 5; + break; + case CS6: + buf[6] = 6; + break; + case CS7: + buf[6] = 7; + break; + default: + case CS8: + buf[6] = 8; + break; } dbg("%s - data bits = %d", __func__, buf[6]); } - baud = tty_get_baud_rate(port->tty);; + baud = tty_get_baud_rate(tty); dbg("%s - baud = %d", __func__, baud); if (baud) { buf[0] = baud & 0xff; @@ -646,12 +656,13 @@ static void pl2303_set_termios(struct usb_serial_port *port, /* FIXME: Need to read back resulting baud rate */ if (baud) - tty_encode_baud_rate(port->tty, baud, baud); + tty_encode_baud_rate(tty, baud, baud); kfree(buf); } -static void pl2303_close(struct usb_serial_port *port, struct file *filp) +static void pl2303_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -666,7 +677,7 @@ static void pl2303_close(struct usb_serial_port *port, struct file *filp) spin_lock_irqsave(&priv->lock, flags); timeout = PL2303_CLOSING_WAIT; init_waitqueue_entry(&wait, current); - add_wait_queue(&port->tty->write_wait, &wait); + add_wait_queue(&tty->write_wait, &wait); for (;;) { set_current_state(TASK_INTERRUPTIBLE); if (pl2303_buf_data_avail(priv->buf) == 0 || @@ -678,7 +689,7 @@ static void pl2303_close(struct usb_serial_port *port, struct file *filp) spin_lock_irqsave(&priv->lock, flags); } set_current_state(TASK_RUNNING); - remove_wait_queue(&port->tty->write_wait, &wait); + remove_wait_queue(&tty->write_wait, &wait); /* clear out any remaining data in the buffer */ pl2303_buf_clear(priv->buf); spin_unlock_irqrestore(&priv->lock, flags); @@ -690,9 +701,9 @@ static void pl2303_close(struct usb_serial_port *port, struct file *filp) /* for lower rates we should really know how much */ /* data is in the buffer to compute a delay */ /* that is not unnecessarily long) */ - bps = tty_get_baud_rate(port->tty); + bps = tty_get_baud_rate(tty); if (bps > 1200) - timeout = max((HZ*2560)/bps,HZ/10); + timeout = max((HZ*2560)/bps, HZ/10); else timeout = 2*HZ; schedule_timeout_interruptible(timeout); @@ -703,8 +714,8 @@ static void pl2303_close(struct usb_serial_port *port, struct file *filp) usb_kill_urb(port->read_urb); usb_kill_urb(port->interrupt_in_urb); - if (port->tty) { - c_cflag = port->tty->termios->c_cflag; + if (tty) { + c_cflag = tty->termios->c_cflag; if (c_cflag & HUPCL) { /* drop DTR and RTS */ spin_lock_irqsave(&priv->lock, flags); @@ -715,7 +726,8 @@ static void pl2303_close(struct usb_serial_port *port, struct file *filp) } } -static int pl2303_open(struct usb_serial_port *port, struct file *filp) +static int pl2303_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct ktermios tmp_termios; struct usb_serial *serial = port->serial; @@ -734,11 +746,10 @@ static int pl2303_open(struct usb_serial_port *port, struct file *filp) } /* Setup termios */ - if (port->tty) { - pl2303_set_termios(port, &tmp_termios); - } + if (tty) + pl2303_set_termios(tty, port, &tmp_termios); - //FIXME: need to assert RTS and DTR if CRTSCTS off + /* FIXME: need to assert RTS and DTR if CRTSCTS off */ dbg("%s - submitting read urb", __func__); port->read_urb->dev = serial->dev; @@ -746,7 +757,7 @@ static int pl2303_open(struct usb_serial_port *port, struct file *filp) if (result) { dev_err(&port->dev, "%s - failed submitting read urb," " error %d\n", __func__, result); - pl2303_close(port, NULL); + pl2303_close(tty, port, NULL); return -EPROTO; } @@ -756,15 +767,16 @@ static int pl2303_open(struct usb_serial_port *port, struct file *filp) if (result) { dev_err(&port->dev, "%s - failed submitting interrupt urb," " error %d\n", __func__, result); - pl2303_close(port, NULL); + pl2303_close(tty, port, NULL); return -EPROTO; } return 0; } -static int pl2303_tiocmset(struct usb_serial_port *port, struct file *file, +static int pl2303_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; u8 control; @@ -787,8 +799,9 @@ static int pl2303_tiocmset(struct usb_serial_port *port, struct file *file, return set_control_lines(port->serial->dev, control); } -static int pl2303_tiocmget(struct usb_serial_port *port, struct file *file) +static int pl2303_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int mcr; @@ -839,12 +852,12 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) status = priv->line_status; spin_unlock_irqrestore(&priv->lock, flags); - changed=prevstatus^status; + changed = prevstatus ^ status; if (((arg & TIOCM_RNG) && (changed & UART_RING)) || ((arg & TIOCM_DSR) && (changed & UART_DSR)) || ((arg & TIOCM_CD) && (changed & UART_DCD)) || - ((arg & TIOCM_CTS) && (changed & UART_CTS)) ) { + ((arg & TIOCM_CTS) && (changed & UART_CTS))) { return 0; } prevstatus = status; @@ -853,26 +866,26 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) return 0; } -static int pl2303_ioctl(struct usb_serial_port *port, struct file *file, +static int pl2303_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; dbg("%s (%d) cmd = 0x%04x", __func__, port->number, cmd); switch (cmd) { - case TIOCMIWAIT: - dbg("%s (%d) TIOCMIWAIT", __func__, port->number); - return wait_modem_info(port, arg); - - default: - dbg("%s not supported = 0x%04x", __func__, cmd); - break; + case TIOCMIWAIT: + dbg("%s (%d) TIOCMIWAIT", __func__, port->number); + return wait_modem_info(port, arg); + default: + dbg("%s not supported = 0x%04x", __func__, cmd); + break; } - return -ENOIOCTLCMD; } -static void pl2303_break_ctl(struct usb_serial_port *port, int break_state) +static void pl2303_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; u16 state; int result; @@ -883,7 +896,8 @@ static void pl2303_break_ctl(struct usb_serial_port *port, int break_state) state = BREAK_OFF; else state = BREAK_ON; - dbg("%s - turning break %s", __func__, state==BREAK_OFF ? "off" : "on"); + dbg("%s - turning break %s", __func__, + state == BREAK_OFF ? "off" : "on"); result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), BREAK_REQUEST, BREAK_REQUEST_TYPE, state, @@ -937,7 +951,7 @@ static void pl2303_update_line_status(struct usb_serial_port *port, if (actual_length < length) return; - /* Save off the uart status for others to look at */ + /* Save off the uart status for others to look at */ spin_lock_irqsave(&priv->lock, flags); priv->line_status = data[status_idx]; spin_unlock_irqrestore(&priv->lock, flags); @@ -1001,7 +1015,7 @@ static void pl2303_read_bulk_callback(struct urb *urb) if (status) { dbg("%s - urb status = %d", __func__, status); - if (!port->open_count) { + if (!port->port.count) { dbg("%s - port is closed, exiting.", __func__); return; } @@ -1036,7 +1050,7 @@ static void pl2303_read_bulk_callback(struct urb *urb) /* break takes precedence over parity, */ /* which takes precedence over framing errors */ - if (line_status & UART_BREAK_ERROR ) + if (line_status & UART_BREAK_ERROR) tty_flag = TTY_BREAK; else if (line_status & UART_PARITY_ERROR) tty_flag = TTY_PARITY; @@ -1044,7 +1058,7 @@ static void pl2303_read_bulk_callback(struct urb *urb) tty_flag = TTY_FRAME; dbg("%s - tty_flag = %d", __func__, tty_flag); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length + 1); /* overrun is special, not associated with a char */ @@ -1056,7 +1070,7 @@ static void pl2303_read_bulk_callback(struct urb *urb) } /* Schedule the next read _if_ we are still open */ - if (port->open_count) { + if (port->port.count) { urb->dev = port->serial->dev; result = usb_submit_urb(urb, GFP_ATOMIC); if (result) diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index 94bddf06ea4f..def52d07a4ea 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -13,24 +13,25 @@ * Stuart Lynne <sl@lineo.com>, Tom Rushworth <tbr@lineo.com> */ -/* - * The encapsultaion is designed to overcome difficulties with some USB hardware. +/* + * The encapsultaion is designed to overcome difficulties with some USB + * hardware. * * While the USB protocol has a CRC over the data while in transit, i.e. while - * being carried over the bus, there is no end to end protection. If the hardware - * has any problems getting the data into or out of the USB transmit and receive - * FIFO's then data can be lost. + * being carried over the bus, there is no end to end protection. If the + * hardware has any problems getting the data into or out of the USB transmit + * and receive FIFO's then data can be lost. * - * This protocol adds a two byte trailer to each USB packet to specify the number - * of bytes of valid data and a 10 bit CRC that will allow the receiver to verify - * that the entire USB packet was received without error. + * This protocol adds a two byte trailer to each USB packet to specify the + * number of bytes of valid data and a 10 bit CRC that will allow the receiver + * to verify that the entire USB packet was received without error. * - * Because in this case the sender and receiver are the class and function drivers - * there is now end to end protection. + * Because in this case the sender and receiver are the class and function + * drivers there is now end to end protection. * - * There is an additional option that can be used to force all transmitted packets - * to be padded to the maximum packet size. This provides a work around for some - * devices which have problems with small USB packets. + * There is an additional option that can be used to force all transmitted + * packets to be padded to the maximum packet size. This provides a work + * around for some devices which have problems with small USB packets. * * Assuming a packetsize of N: * @@ -44,11 +45,12 @@ * | Data Length | 10 bit CRC | * + 7 . 6 . 5 . 4 . 3 . 2 . 1 . 0 | 7 . 6 . 5 . 4 . 3 . 2 . 1 . 0 + * - * The 10 bit CRC is computed across the sent data, followed by the trailer with - * the length set and the CRC set to zero. The CRC is then OR'd into the trailer. + * The 10 bit CRC is computed across the sent data, followed by the trailer + * with the length set and the CRC set to zero. The CRC is then OR'd into + * the trailer. * - * When received a 10 bit CRC is computed over the entire frame including the trailer - * and should be equal to zero. + * When received a 10 bit CRC is computed over the entire frame including + * the trailer and should be equal to zero. * * Two module parameters are used to control the encapsulation, if both are * turned of the module works as a simple serial device with NO @@ -69,7 +71,7 @@ #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> @@ -86,12 +88,12 @@ static int padded = CONFIG_USB_SERIAL_SAFE_PADDED; #define DRIVER_AUTHOR "sl@lineo.com, tbr@lineo.com" #define DRIVER_DESC "USB Safe Encapsulated Serial" -MODULE_AUTHOR (DRIVER_AUTHOR); -MODULE_DESCRIPTION (DRIVER_DESC); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -static __u16 vendor; // no default -static __u16 product; // no default +static __u16 vendor; /* no default */ +static __u16 product; /* no default */ module_param(vendor, ushort, 0); MODULE_PARM_DESC(vendor, "User specified USB idVendor (required)"); module_param(product, ushort, 0); @@ -122,30 +124,31 @@ MODULE_PARM_DESC(padded, "Pad to full wMaxPacketSize On/Off"); #define LINEO_SAFESERIAL_CRC_PADDED 0x02 -#define MY_USB_DEVICE(vend,prod,dc,ic,isc) \ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_CLASS | \ - USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS, \ - .idVendor = (vend), \ - .idProduct = (prod),\ - .bDeviceClass = (dc),\ - .bInterfaceClass = (ic), \ - .bInterfaceSubClass = (isc), +#define MY_USB_DEVICE(vend, prod, dc, ic, isc) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_DEV_CLASS | \ + USB_DEVICE_ID_MATCH_INT_CLASS | \ + USB_DEVICE_ID_MATCH_INT_SUBCLASS, \ + .idVendor = (vend), \ + .idProduct = (prod),\ + .bDeviceClass = (dc),\ + .bInterfaceClass = (ic), \ + .bInterfaceSubClass = (isc), static struct usb_device_id id_table[] = { - {MY_USB_DEVICE (0x49f, 0xffff, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, // Itsy - {MY_USB_DEVICE (0x3f0, 0x2101, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, // Calypso - {MY_USB_DEVICE (0x4dd, 0x8001, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, // Iris - {MY_USB_DEVICE (0x4dd, 0x8002, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, // Collie - {MY_USB_DEVICE (0x4dd, 0x8003, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, // Collie - {MY_USB_DEVICE (0x4dd, 0x8004, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, // Collie - {MY_USB_DEVICE (0x5f9, 0xffff, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, // Sharp tmp - // extra null entry for module - // vendor/produc parameters - {MY_USB_DEVICE (0, 0, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, - {} // terminating entry + {MY_USB_DEVICE(0x49f, 0xffff, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Itsy */ + {MY_USB_DEVICE(0x3f0, 0x2101, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Calypso */ + {MY_USB_DEVICE(0x4dd, 0x8001, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Iris */ + {MY_USB_DEVICE(0x4dd, 0x8002, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Collie */ + {MY_USB_DEVICE(0x4dd, 0x8003, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Collie */ + {MY_USB_DEVICE(0x4dd, 0x8004, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Collie */ + {MY_USB_DEVICE(0x5f9, 0xffff, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Sharp tmp */ + /* extra null entry for module vendor/produc parameters */ + {MY_USB_DEVICE(0, 0, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, + {} /* terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, id_table); static struct usb_driver safe_driver = { .name = "safe_serial", @@ -156,29 +159,45 @@ static struct usb_driver safe_driver = { }; static const __u16 crc10_table[256] = { - 0x000, 0x233, 0x255, 0x066, 0x299, 0x0aa, 0x0cc, 0x2ff, 0x301, 0x132, 0x154, 0x367, 0x198, 0x3ab, 0x3cd, 0x1fe, - 0x031, 0x202, 0x264, 0x057, 0x2a8, 0x09b, 0x0fd, 0x2ce, 0x330, 0x103, 0x165, 0x356, 0x1a9, 0x39a, 0x3fc, 0x1cf, - 0x062, 0x251, 0x237, 0x004, 0x2fb, 0x0c8, 0x0ae, 0x29d, 0x363, 0x150, 0x136, 0x305, 0x1fa, 0x3c9, 0x3af, 0x19c, - 0x053, 0x260, 0x206, 0x035, 0x2ca, 0x0f9, 0x09f, 0x2ac, 0x352, 0x161, 0x107, 0x334, 0x1cb, 0x3f8, 0x39e, 0x1ad, - 0x0c4, 0x2f7, 0x291, 0x0a2, 0x25d, 0x06e, 0x008, 0x23b, 0x3c5, 0x1f6, 0x190, 0x3a3, 0x15c, 0x36f, 0x309, 0x13a, - 0x0f5, 0x2c6, 0x2a0, 0x093, 0x26c, 0x05f, 0x039, 0x20a, 0x3f4, 0x1c7, 0x1a1, 0x392, 0x16d, 0x35e, 0x338, 0x10b, - 0x0a6, 0x295, 0x2f3, 0x0c0, 0x23f, 0x00c, 0x06a, 0x259, 0x3a7, 0x194, 0x1f2, 0x3c1, 0x13e, 0x30d, 0x36b, 0x158, - 0x097, 0x2a4, 0x2c2, 0x0f1, 0x20e, 0x03d, 0x05b, 0x268, 0x396, 0x1a5, 0x1c3, 0x3f0, 0x10f, 0x33c, 0x35a, 0x169, - 0x188, 0x3bb, 0x3dd, 0x1ee, 0x311, 0x122, 0x144, 0x377, 0x289, 0x0ba, 0x0dc, 0x2ef, 0x010, 0x223, 0x245, 0x076, - 0x1b9, 0x38a, 0x3ec, 0x1df, 0x320, 0x113, 0x175, 0x346, 0x2b8, 0x08b, 0x0ed, 0x2de, 0x021, 0x212, 0x274, 0x047, - 0x1ea, 0x3d9, 0x3bf, 0x18c, 0x373, 0x140, 0x126, 0x315, 0x2eb, 0x0d8, 0x0be, 0x28d, 0x072, 0x241, 0x227, 0x014, - 0x1db, 0x3e8, 0x38e, 0x1bd, 0x342, 0x171, 0x117, 0x324, 0x2da, 0x0e9, 0x08f, 0x2bc, 0x043, 0x270, 0x216, 0x025, - 0x14c, 0x37f, 0x319, 0x12a, 0x3d5, 0x1e6, 0x180, 0x3b3, 0x24d, 0x07e, 0x018, 0x22b, 0x0d4, 0x2e7, 0x281, 0x0b2, - 0x17d, 0x34e, 0x328, 0x11b, 0x3e4, 0x1d7, 0x1b1, 0x382, 0x27c, 0x04f, 0x029, 0x21a, 0x0e5, 0x2d6, 0x2b0, 0x083, - 0x12e, 0x31d, 0x37b, 0x148, 0x3b7, 0x184, 0x1e2, 0x3d1, 0x22f, 0x01c, 0x07a, 0x249, 0x0b6, 0x285, 0x2e3, 0x0d0, - 0x11f, 0x32c, 0x34a, 0x179, 0x386, 0x1b5, 0x1d3, 0x3e0, 0x21e, 0x02d, 0x04b, 0x278, 0x087, 0x2b4, 0x2d2, 0x0e1, + 0x000, 0x233, 0x255, 0x066, 0x299, 0x0aa, 0x0cc, 0x2ff, + 0x301, 0x132, 0x154, 0x367, 0x198, 0x3ab, 0x3cd, 0x1fe, + 0x031, 0x202, 0x264, 0x057, 0x2a8, 0x09b, 0x0fd, 0x2ce, + 0x330, 0x103, 0x165, 0x356, 0x1a9, 0x39a, 0x3fc, 0x1cf, + 0x062, 0x251, 0x237, 0x004, 0x2fb, 0x0c8, 0x0ae, 0x29d, + 0x363, 0x150, 0x136, 0x305, 0x1fa, 0x3c9, 0x3af, 0x19c, + 0x053, 0x260, 0x206, 0x035, 0x2ca, 0x0f9, 0x09f, 0x2ac, + 0x352, 0x161, 0x107, 0x334, 0x1cb, 0x3f8, 0x39e, 0x1ad, + 0x0c4, 0x2f7, 0x291, 0x0a2, 0x25d, 0x06e, 0x008, 0x23b, + 0x3c5, 0x1f6, 0x190, 0x3a3, 0x15c, 0x36f, 0x309, 0x13a, + 0x0f5, 0x2c6, 0x2a0, 0x093, 0x26c, 0x05f, 0x039, 0x20a, + 0x3f4, 0x1c7, 0x1a1, 0x392, 0x16d, 0x35e, 0x338, 0x10b, + 0x0a6, 0x295, 0x2f3, 0x0c0, 0x23f, 0x00c, 0x06a, 0x259, + 0x3a7, 0x194, 0x1f2, 0x3c1, 0x13e, 0x30d, 0x36b, 0x158, + 0x097, 0x2a4, 0x2c2, 0x0f1, 0x20e, 0x03d, 0x05b, 0x268, + 0x396, 0x1a5, 0x1c3, 0x3f0, 0x10f, 0x33c, 0x35a, 0x169, + 0x188, 0x3bb, 0x3dd, 0x1ee, 0x311, 0x122, 0x144, 0x377, + 0x289, 0x0ba, 0x0dc, 0x2ef, 0x010, 0x223, 0x245, 0x076, + 0x1b9, 0x38a, 0x3ec, 0x1df, 0x320, 0x113, 0x175, 0x346, + 0x2b8, 0x08b, 0x0ed, 0x2de, 0x021, 0x212, 0x274, 0x047, + 0x1ea, 0x3d9, 0x3bf, 0x18c, 0x373, 0x140, 0x126, 0x315, + 0x2eb, 0x0d8, 0x0be, 0x28d, 0x072, 0x241, 0x227, 0x014, + 0x1db, 0x3e8, 0x38e, 0x1bd, 0x342, 0x171, 0x117, 0x324, + 0x2da, 0x0e9, 0x08f, 0x2bc, 0x043, 0x270, 0x216, 0x025, + 0x14c, 0x37f, 0x319, 0x12a, 0x3d5, 0x1e6, 0x180, 0x3b3, + 0x24d, 0x07e, 0x018, 0x22b, 0x0d4, 0x2e7, 0x281, 0x0b2, + 0x17d, 0x34e, 0x328, 0x11b, 0x3e4, 0x1d7, 0x1b1, 0x382, + 0x27c, 0x04f, 0x029, 0x21a, 0x0e5, 0x2d6, 0x2b0, 0x083, + 0x12e, 0x31d, 0x37b, 0x148, 0x3b7, 0x184, 0x1e2, 0x3d1, + 0x22f, 0x01c, 0x07a, 0x249, 0x0b6, 0x285, 0x2e3, 0x0d0, + 0x11f, 0x32c, 0x34a, 0x179, 0x386, 0x1b5, 0x1d3, 0x3e0, + 0x21e, 0x02d, 0x04b, 0x278, 0x087, 0x2b4, 0x2d2, 0x0e1, }; -#define CRC10_INITFCS 0x000 // Initial FCS value -#define CRC10_GOODFCS 0x000 // Good final FCS value -#define CRC10_FCS(fcs, c) ( (((fcs) << 8) & 0x3ff) ^ crc10_table[((fcs) >> 2) & 0xff] ^ (c)) +#define CRC10_INITFCS 0x000 /* Initial FCS value */ +#define CRC10_GOODFCS 0x000 /* Good final FCS value */ +#define CRC10_FCS(fcs, c) ((((fcs) << 8) & 0x3ff) ^ crc10_table[((fcs) >> 2) & 0xff] ^ (c)) -/** +/** * fcs_compute10 - memcpy and calculate 10 bit CRC across buffer * @sp: pointer to buffer * @len: number of bytes @@ -187,13 +206,13 @@ static const __u16 crc10_table[256] = { * Perform a memcpy and calculate fcs using ppp 10bit CRC algorithm. Return * new 10 bit FCS. */ -static __u16 __inline__ fcs_compute10 (unsigned char *sp, int len, __u16 fcs) +static __u16 __inline__ fcs_compute10(unsigned char *sp, int len, __u16 fcs) { - for (; len-- > 0; fcs = CRC10_FCS (fcs, *sp++)); + for (; len-- > 0; fcs = CRC10_FCS(fcs, *sp++)); return fcs; } -static void safe_read_bulk_callback (struct urb *urb) +static void safe_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; unsigned char *data = urb->transfer_buffer; @@ -201,7 +220,7 @@ static void safe_read_bulk_callback (struct urb *urb) int result; int status = urb->status; - dbg ("%s", __func__); + dbg("%s", __func__); if (status) { dbg("%s - nonzero read bulk status received: %d", @@ -209,76 +228,82 @@ static void safe_read_bulk_callback (struct urb *urb) return; } - dbg ("safe_read_bulk_callback length: %d", port->read_urb->actual_length); + dbg("safe_read_bulk_callback length: %d", + port->read_urb->actual_length); #ifdef ECHO_RCV { int i; unsigned char *cp = port->read_urb->transfer_buffer; for (i = 0; i < port->read_urb->actual_length; i++) { - if ((i % 32) == 0) { - printk ("\nru[%02x] ", i); - } - printk ("%02x ", *cp++); + if ((i % 32) == 0) + printk("\nru[%02x] ", i); + printk("%02x ", *cp++); } - printk ("\n"); + printk("\n"); } #endif if (safe) { __u16 fcs; - if (!(fcs = fcs_compute10 (data, length, CRC10_INITFCS))) { + fcs = fcs_compute10(data, length, CRC10_INITFCS); + if (!fcs) { int actual_length = data[length - 2] >> 2; if (actual_length <= (length - 2)) { - info ("%s - actual: %d", __func__, actual_length); - tty_insert_flip_string(port->tty, data, actual_length); - tty_flip_buffer_push (port->tty); + info("%s - actual: %d", __func__, + actual_length); + tty_insert_flip_string(port->port.tty, + data, actual_length); + tty_flip_buffer_push(port->port.tty); } else { - err ("%s - inconsistent lengths %d:%d", __func__, - actual_length, length); + err("%s - inconsistent lengths %d:%d", + __func__, actual_length, length); } } else { - err ("%s - bad CRC %x", __func__, fcs); + err("%s - bad CRC %x", __func__, fcs); } } else { - tty_insert_flip_string(port->tty, data, length); - tty_flip_buffer_push (port->tty); + tty_insert_flip_string(port->port.tty, data, length); + tty_flip_buffer_push(port->port.tty); } /* Continue trying to always read */ - usb_fill_bulk_urb (urb, port->serial->dev, - usb_rcvbulkpipe (port->serial->dev, port->bulk_in_endpointAddress), - urb->transfer_buffer, urb->transfer_buffer_length, - safe_read_bulk_callback, port); - - if ((result = usb_submit_urb (urb, GFP_ATOMIC))) { - err ("%s - failed resubmitting read urb, error %d", __func__, result); + usb_fill_bulk_urb(urb, port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress), + urb->transfer_buffer, urb->transfer_buffer_length, + safe_read_bulk_callback, port); + + result = usb_submit_urb(urb, GFP_ATOMIC); + if (result) + err("%s - failed resubmitting read urb, error %d", + __func__, result); /* FIXME: Need a mechanism to retry later if this happens */ - } } -static int safe_write (struct usb_serial_port *port, const unsigned char *buf, int count) +static int safe_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { unsigned char *data; int result; int i; int packet_length; - dbg ("safe_write port: %p %d urb: %p count: %d", port, port->number, port->write_urb, - count); + dbg("safe_write port: %p %d urb: %p count: %d", + port, port->number, port->write_urb, count); if (!port->write_urb) { - dbg ("%s - write urb NULL", __func__); + dbg("%s - write urb NULL", __func__); return 0; } - dbg ("safe_write write_urb: %d transfer_buffer_length", + dbg("safe_write write_urb: %d transfer_buffer_length", port->write_urb->transfer_buffer_length); if (!port->write_urb->transfer_buffer_length) { - dbg ("%s - write urb transfer_buffer_length zero", __func__); + dbg("%s - write urb transfer_buffer_length zero", __func__); return 0; } if (count == 0) { - dbg ("%s - write request of 0 bytes", __func__); + dbg("%s - write request of 0 bytes", __func__); return 0; } spin_lock_bh(&port->lock); @@ -290,85 +315,85 @@ static int safe_write (struct usb_serial_port *port, const unsigned char *buf, i port->write_urb_busy = 1; spin_unlock_bh(&port->lock); - packet_length = port->bulk_out_size; // get max packetsize + packet_length = port->bulk_out_size; /* get max packetsize */ - i = packet_length - (safe ? 2 : 0); // get bytes to send + i = packet_length - (safe ? 2 : 0); /* get bytes to send */ count = (count > i) ? i : count; - // get the data into the transfer buffer + /* get the data into the transfer buffer */ data = port->write_urb->transfer_buffer; - memset (data, '0', packet_length); + memset(data, '0', packet_length); - memcpy (data, buf, count); + memcpy(data, buf, count); if (safe) { __u16 fcs; - // pad if necessary - if (!padded) { + /* pad if necessary */ + if (!padded) packet_length = count + 2; - } - // set count + /* set count */ data[packet_length - 2] = count << 2; data[packet_length - 1] = 0; - // compute fcs and insert into trailer - fcs = fcs_compute10 (data, packet_length, CRC10_INITFCS); + /* compute fcs and insert into trailer */ + fcs = fcs_compute10(data, packet_length, CRC10_INITFCS); data[packet_length - 2] |= fcs >> 8; data[packet_length - 1] |= fcs & 0xff; - // set length to send + /* set length to send */ port->write_urb->transfer_buffer_length = packet_length; } else { port->write_urb->transfer_buffer_length = count; } - usb_serial_debug_data(debug, &port->dev, __func__, count, port->write_urb->transfer_buffer); + usb_serial_debug_data(debug, &port->dev, __func__, count, + port->write_urb->transfer_buffer); #ifdef ECHO_TX { int i; unsigned char *cp = port->write_urb->transfer_buffer; for (i = 0; i < port->write_urb->transfer_buffer_length; i++) { - if ((i % 32) == 0) { - printk ("\nsu[%02x] ", i); - } - printk ("%02x ", *cp++); + if ((i % 32) == 0) + printk("\nsu[%02x] ", i); + printk("%02x ", *cp++); } - printk ("\n"); + printk("\n"); } #endif port->write_urb->dev = port->serial->dev; - if ((result = usb_submit_urb (port->write_urb, GFP_KERNEL))) { + result = usb_submit_urb(port->write_urb, GFP_KERNEL); + if (result) { port->write_urb_busy = 0; - err ("%s - failed submitting write urb, error %d", __func__, result); + err("%s - failed submitting write urb, error %d", + __func__, result); return 0; } - dbg ("%s urb: %p submitted", __func__, port->write_urb); + dbg("%s urb: %p submitted", __func__, port->write_urb); - return (count); + return count; } -static int safe_write_room (struct usb_serial_port *port) +static int safe_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int room = 0; /* Default: no room */ unsigned long flags; - dbg ("%s", __func__); + dbg("%s", __func__); spin_lock_irqsave(&port->lock, flags); if (port->write_urb_busy) room = port->bulk_out_size - (safe ? 2 : 0); spin_unlock_irqrestore(&port->lock, flags); - if (room) { - dbg ("safe_write_room returns %d", room); - } - + if (room) + dbg("safe_write_room returns %d", room); return room; } -static int safe_startup (struct usb_serial *serial) +static int safe_startup(struct usb_serial *serial) { switch (serial->interface->cur_altsetting->desc.bInterfaceProtocol) { case LINEO_SAFESERIAL_CRC: @@ -396,17 +421,18 @@ static struct usb_serial_driver safe_device = { .attach = safe_startup, }; -static int __init safe_init (void) +static int __init safe_init(void) { int i, retval; - info (DRIVER_VERSION " " DRIVER_AUTHOR); - info (DRIVER_DESC); - info ("vendor: %x product: %x safe: %d padded: %d\n", vendor, product, safe, padded); + info(DRIVER_VERSION " " DRIVER_AUTHOR); + info(DRIVER_DESC); + info("vendor: %x product: %x safe: %d padded: %d\n", + vendor, product, safe, padded); - // if we have vendor / product parameters patch them into id list + /* if we have vendor / product parameters patch them into id list */ if (vendor || product) { - info ("vendor: %x product: %x\n", vendor, product); + info("vendor: %x product: %x\n", vendor, product); for (i = 0; i < ARRAY_SIZE(id_table); i++) { if (!id_table[i].idVendor && !id_table[i].idProduct) { @@ -431,11 +457,11 @@ failed_usb_serial_register: return retval; } -static void __exit safe_exit (void) +static void __exit safe_exit(void) { - usb_deregister (&safe_driver); - usb_serial_deregister (&safe_device); + usb_deregister(&safe_driver); + usb_serial_deregister(&safe_device); } -module_init (safe_init); -module_exit (safe_exit); +module_init(safe_init); +module_exit(safe_exit); diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 29074c1ba22b..2f6f1523ec56 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -250,7 +250,8 @@ struct sierra_port_private { int ri_state; }; -static int sierra_send_setup(struct usb_serial_port *port) +static int sierra_send_setup(struct tty_struct *tty, + struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct sierra_port_private *portdata; @@ -260,7 +261,7 @@ static int sierra_send_setup(struct usb_serial_port *port) portdata = usb_get_serial_port_data(port); - if (port->tty) { + if (tty) { int val = 0; if (portdata->dtr_state) val |= 0x01; @@ -284,32 +285,17 @@ static int sierra_send_setup(struct usb_serial_port *port) return 0; } -static void sierra_rx_throttle(struct usb_serial_port *port) +static void sierra_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { dbg("%s", __func__); + tty_termios_copy_hw(tty->termios, old_termios); + sierra_send_setup(tty, port); } -static void sierra_rx_unthrottle(struct usb_serial_port *port) -{ - dbg("%s", __func__); -} - -static void sierra_break_ctl(struct usb_serial_port *port, int break_state) -{ - /* Unfortunately, I don't know how to send a break */ - dbg("%s", __func__); -} - -static void sierra_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) -{ - dbg("%s", __func__); - tty_termios_copy_hw(port->tty->termios, old_termios); - sierra_send_setup(port); -} - -static int sierra_tiocmget(struct usb_serial_port *port, struct file *file) +static int sierra_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; unsigned int value; struct sierra_port_private *portdata; @@ -325,9 +311,10 @@ static int sierra_tiocmget(struct usb_serial_port *port, struct file *file) return value; } -static int sierra_tiocmset(struct usb_serial_port *port, struct file *file, +static int sierra_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct sierra_port_private *portdata; portdata = usb_get_serial_port_data(port); @@ -341,13 +328,7 @@ static int sierra_tiocmset(struct usb_serial_port *port, struct file *file, portdata->rts_state = 0; if (clear & TIOCM_DTR) portdata->dtr_state = 0; - return sierra_send_setup(port); -} - -static int sierra_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return -ENOIOCTLCMD; + return sierra_send_setup(tty, port); } static void sierra_outdat_callback(struct urb *urb) @@ -374,8 +355,8 @@ static void sierra_outdat_callback(struct urb *urb) } /* Write */ -static int sierra_write(struct usb_serial_port *port, - const unsigned char *buf, int count) +static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { struct sierra_port_private *portdata = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; @@ -463,7 +444,7 @@ static void sierra_indat_callback(struct urb *urb) dbg("%s: nonzero status: %d on endpoint %02x.", __func__, status, endpoint); } else { - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -473,7 +454,7 @@ static void sierra_indat_callback(struct urb *urb) } /* Resubmit urb so we continue receiving */ - if (port->open_count && status != -ESHUTDOWN) { + if (port->port.count && status != -ESHUTDOWN) { err = usb_submit_urb(urb, GFP_ATOMIC); if (err) dev_err(&port->dev, "resubmit read urb failed." @@ -517,9 +498,9 @@ static void sierra_instat_callback(struct urb *urb) portdata->dsr_state = ((signals & 0x02) ? 1 : 0); portdata->ri_state = ((signals & 0x08) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) && + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state && !portdata->dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); } else { dbg("%s: type %x req %x", __func__, req_pkt->bRequestType, req_pkt->bRequest); @@ -537,8 +518,9 @@ static void sierra_instat_callback(struct urb *urb) } } -static int sierra_write_room(struct usb_serial_port *port) +static int sierra_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct sierra_port_private *portdata = usb_get_serial_port_data(port); unsigned long flags; @@ -557,22 +539,8 @@ static int sierra_write_room(struct usb_serial_port *port) return 2048; } -static int sierra_chars_in_buffer(struct usb_serial_port *port) -{ - dbg("%s - port %d", __func__, port->number); - - /* - * We can't really account for how much data we - * have sent out, but hasn't made it through to the - * device as we can't see the backend here, so just - * tell the tty layer that everything is flushed. - * - * FIXME: should walk the outstanding urbs info - */ - return 0; -} - -static int sierra_open(struct usb_serial_port *port, struct file *filp) +static int sierra_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct sierra_port_private *portdata; struct usb_serial *serial = port->serial; @@ -612,9 +580,10 @@ static int sierra_open(struct usb_serial_port *port, struct file *filp) } } - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; - sierra_send_setup(port); + sierra_send_setup(tty, port); /* start up the interrupt endpoint if we have one */ if (port->interrupt_in_urb) { @@ -626,7 +595,8 @@ static int sierra_open(struct usb_serial_port *port, struct file *filp) return 0; } -static void sierra_close(struct usb_serial_port *port, struct file *filp) +static void sierra_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int i; struct usb_serial *serial = port->serial; @@ -641,7 +611,7 @@ static void sierra_close(struct usb_serial_port *port, struct file *filp) if (serial->dev) { mutex_lock(&serial->disc_mutex); if (!serial->disconnected) - sierra_send_setup(port); + sierra_send_setup(tty, port); mutex_unlock(&serial->disc_mutex); /* Stop reading/writing urbs */ @@ -651,7 +621,7 @@ static void sierra_close(struct usb_serial_port *port, struct file *filp) usb_kill_urb(port->interrupt_in_urb); - port->tty = NULL; + port->port.tty = NULL; /* FIXME */ } static int sierra_startup(struct usb_serial *serial) @@ -754,12 +724,7 @@ static struct usb_serial_driver sierra_device = { .close = sierra_close, .write = sierra_write, .write_room = sierra_write_room, - .chars_in_buffer = sierra_chars_in_buffer, - .throttle = sierra_rx_throttle, - .unthrottle = sierra_rx_unthrottle, - .ioctl = sierra_ioctl, .set_termios = sierra_set_termios, - .break_ctl = sierra_break_ctl, .tiocmget = sierra_tiocmget, .tiocmset = sierra_tiocmset, .attach = sierra_startup, @@ -792,7 +757,7 @@ failed_device_register: static void __exit sierra_exit(void) { - usb_deregister (&sierra_driver); + usb_deregister(&sierra_driver); usb_serial_deregister(&sierra_device); } diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 55b2570b8b8b..283cf6b36b2c 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -208,7 +208,7 @@ static inline unsigned int ringbuf_avail_data(struct ringbuf *pb) { if (pb == NULL) return 0; - return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size); + return (pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size; } /* get the number of space in the pipo */ @@ -216,7 +216,7 @@ static inline unsigned int ringbuf_avail_space(struct ringbuf *pb) { if (pb == NULL) return 0; - return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size); + return (pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size; } /* put count data into pipo */ @@ -448,7 +448,8 @@ static void spcp8x5_set_workMode(struct usb_device *dev, u16 value, /* close the serial port. We should wait for data sending to device 1st and * then kill all urb. */ -static void spcp8x5_close(struct usb_serial_port *port, struct file *filp) +static void spcp8x5_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct spcp8x5_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -464,7 +465,7 @@ static void spcp8x5_close(struct usb_serial_port *port, struct file *filp) spin_lock_irqsave(&priv->lock, flags); timeout = SPCP8x5_CLOSING_WAIT; init_waitqueue_entry(&wait, current); - add_wait_queue(&port->tty->write_wait, &wait); + add_wait_queue(&tty->write_wait, &wait); for (;;) { set_current_state(TASK_INTERRUPTIBLE); if (ringbuf_avail_data(priv->buf) == 0 || @@ -475,7 +476,7 @@ static void spcp8x5_close(struct usb_serial_port *port, struct file *filp) spin_lock_irqsave(&priv->lock, flags); } set_current_state(TASK_RUNNING); - remove_wait_queue(&port->tty->write_wait, &wait); + remove_wait_queue(&tty->write_wait, &wait); /* clear out any remaining data in the buffer */ clear_ringbuf(priv->buf); @@ -486,7 +487,7 @@ static void spcp8x5_close(struct usb_serial_port *port, struct file *filp) * flow control for data rates of 1200 bps or more, for lower rates we * should really know how much data is in the buffer to compute a delay * that is not unnecessarily long) */ - bps = tty_get_baud_rate(port->tty); + bps = tty_get_baud_rate(tty); if (bps > 1200) timeout = max((HZ*2560) / bps, HZ/10); else @@ -495,8 +496,8 @@ static void spcp8x5_close(struct usb_serial_port *port, struct file *filp) schedule_timeout(timeout); /* clear control lines */ - if (port->tty) { - c_cflag = port->tty->termios->c_cflag; + if (tty) { + c_cflag = tty->termios->c_cflag; if (c_cflag & HUPCL) { spin_lock_irqsave(&priv->lock, flags); priv->line_control = 0; @@ -518,14 +519,14 @@ static void spcp8x5_close(struct usb_serial_port *port, struct file *filp) } /* set the serial param for transfer. we should check if we really need to - * transfer. then if be set flow contorl we should do this too. */ -static void spcp8x5_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) + * transfer. if we set flow control we should do this too. */ +static void spcp8x5_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; struct spcp8x5_private *priv = usb_get_serial_port_data(port); unsigned long flags; - unsigned int cflag = port->tty->termios->c_cflag; + unsigned int cflag = tty->termios->c_cflag; unsigned int old_cflag = old_termios->c_cflag; unsigned short uartdata; unsigned char buf[2] = {0, 0}; @@ -533,21 +534,19 @@ static void spcp8x5_set_termios(struct usb_serial_port *port, int i; u8 control; - if ((!port->tty) || (!port->tty->termios)) - return; - /* for the 1st time call this function */ spin_lock_irqsave(&priv->lock, flags); if (!priv->termios_initialized) { - *(port->tty->termios) = tty_std_termios; - port->tty->termios->c_cflag = B115200 | CS8 | CREAD | - HUPCL | CLOCAL; + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; + tty->termios->c_ispeed = 115200; + tty->termios->c_ospeed = 115200; priv->termios_initialized = 1; } spin_unlock_irqrestore(&priv->lock, flags); /* check that they really want us to change something */ - if (!tty_termios_hw_change(port->tty->termios, old_termios)) + if (!tty_termios_hw_change(tty->termios, old_termios)) return; /* set DTR/RTS active */ @@ -567,7 +566,7 @@ static void spcp8x5_set_termios(struct usb_serial_port *port, } /* Set Baud Rate */ - baud = tty_get_baud_rate(port->tty);; + baud = tty_get_baud_rate(tty);; switch (baud) { case 300: buf[0] = 0x00; break; case 600: buf[0] = 0x01; break; @@ -643,7 +642,8 @@ static void spcp8x5_set_termios(struct usb_serial_port *port, /* open the serial port. do some usb system call. set termios and get the line * status of the device. then submit the read urb */ -static int spcp8x5_open(struct usb_serial_port *port, struct file *filp) +static int spcp8x5_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct ktermios tmp_termios; struct usb_serial *serial = port->serial; @@ -665,7 +665,7 @@ static int spcp8x5_open(struct usb_serial_port *port, struct file *filp) return ret; spin_lock_irqsave(&priv->lock, flags); - if (port->tty->termios->c_cflag & CBAUD) + if (tty && (tty->termios->c_cflag & CBAUD)) priv->line_control = MCR_DTR | MCR_RTS; else priv->line_control = 0; @@ -674,8 +674,8 @@ static int spcp8x5_open(struct usb_serial_port *port, struct file *filp) spcp8x5_set_ctrlLine(serial->dev, priv->line_control , priv->type); /* Setup termios */ - if (port->tty) - spcp8x5_set_termios(port, &tmp_termios); + if (tty) + spcp8x5_set_termios(tty, port, &tmp_termios); spcp8x5_get_msr(serial->dev, &status, priv->type); @@ -690,7 +690,7 @@ static int spcp8x5_open(struct usb_serial_port *port, struct file *filp) port->read_urb->dev = serial->dev; ret = usb_submit_urb(port->read_urb, GFP_KERNEL); if (ret) { - spcp8x5_close(port, NULL); + spcp8x5_close(tty, port, NULL); return -EPROTO; } return 0; @@ -717,7 +717,7 @@ static void spcp8x5_read_bulk_callback(struct urb *urb) /* check the urb status */ if (urb->status) { - if (!port->open_count) + if (!port->port.count) return; if (urb->status == -EPROTO) { /* spcp8x5 mysteriously fails with -EPROTO */ @@ -755,7 +755,7 @@ static void spcp8x5_read_bulk_callback(struct urb *urb) tty_flag = TTY_FRAME; dev_dbg(&port->dev, "tty_flag = %d\n", tty_flag); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length + 1); /* overrun is special, not associated with a char */ @@ -767,7 +767,7 @@ static void spcp8x5_read_bulk_callback(struct urb *urb) } /* Schedule the next read _if_ we are still open */ - if (port->open_count) { + if (port->port.count) { urb->dev = port->serial->dev; result = usb_submit_urb(urb , GFP_ATOMIC); if (result) @@ -866,7 +866,7 @@ static void spcp8x5_write_bulk_callback(struct urb *urb) } /* write data to ring buffer. and then start the write transfer */ -static int spcp8x5_write(struct usb_serial_port *port, +static int spcp8x5_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { struct spcp8x5_private *priv = usb_get_serial_port_data(port); @@ -925,9 +925,10 @@ static int spcp8x5_wait_modem_info(struct usb_serial_port *port, return 0; } -static int spcp8x5_ioctl(struct usb_serial_port *port, struct file *file, +static int spcp8x5_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; dbg("%s (%d) cmd = 0x%04x", __func__, port->number, cmd); switch (cmd) { @@ -943,9 +944,10 @@ static int spcp8x5_ioctl(struct usb_serial_port *port, struct file *file, return -ENOIOCTLCMD; } -static int spcp8x5_tiocmset(struct usb_serial_port *port, struct file *file, +static int spcp8x5_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct spcp8x5_private *priv = usb_get_serial_port_data(port); unsigned long flags; u8 control; @@ -965,8 +967,9 @@ static int spcp8x5_tiocmset(struct usb_serial_port *port, struct file *file, return spcp8x5_set_ctrlLine(port->serial->dev, control , priv->type); } -static int spcp8x5_tiocmget(struct usb_serial_port *port, struct file *file) +static int spcp8x5_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct spcp8x5_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int mcr; @@ -989,8 +992,9 @@ static int spcp8x5_tiocmget(struct usb_serial_port *port, struct file *file) } /* get the avail space room in ring buffer */ -static int spcp8x5_write_room(struct usb_serial_port *port) +static int spcp8x5_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct spcp8x5_private *priv = usb_get_serial_port_data(port); int room = 0; unsigned long flags; @@ -1003,8 +1007,9 @@ static int spcp8x5_write_room(struct usb_serial_port *port) } /* get the number of avail data in write ring buffer */ -static int spcp8x5_chars_in_buffer(struct usb_serial_port *port) +static int spcp8x5_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct spcp8x5_private *priv = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index a26a629dfc4f..e39c779e4160 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -16,7 +16,7 @@ * For questions or problems with this driver, contact Texas Instruments * technical support, or Al Borchers <alborchers@steinerpoint.com>, or * Peter Berger <pberger@brimson.com>. - * + * * This driver needs this hotplug script in /etc/hotplug/usb/ti_usb_3410_5052 * or in /etc/hotplug.d/usb/ti_usb_3410_5052.hotplug to set the device * configuration. @@ -70,6 +70,7 @@ #include <linux/kernel.h> #include <linux/errno.h> +#include <linux/firmware.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> @@ -81,7 +82,7 @@ #include <linux/serial.h> #include <linux/circ_buf.h> #include <linux/mutex.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/firmware.h> @@ -149,21 +150,24 @@ struct ti_device { static int ti_startup(struct usb_serial *serial); static void ti_shutdown(struct usb_serial *serial); -static int ti_open(struct usb_serial_port *port, struct file *file); -static void ti_close(struct usb_serial_port *port, struct file *file); -static int ti_write(struct usb_serial_port *port, const unsigned char *data, - int count); -static int ti_write_room(struct usb_serial_port *port); -static int ti_chars_in_buffer(struct usb_serial_port *port); -static void ti_throttle(struct usb_serial_port *port); -static void ti_unthrottle(struct usb_serial_port *port); -static int ti_ioctl(struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg); -static void ti_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios); -static int ti_tiocmget(struct usb_serial_port *port, struct file *file); -static int ti_tiocmset(struct usb_serial_port *port, struct file *file, - unsigned int set, unsigned int clear); -static void ti_break(struct usb_serial_port *port, int break_state); +static int ti_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *file); +static void ti_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *file); +static int ti_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *data, int count); +static int ti_write_room(struct tty_struct *tty); +static int ti_chars_in_buffer(struct tty_struct *tty); +static void ti_throttle(struct tty_struct *tty); +static void ti_unthrottle(struct tty_struct *tty); +static int ti_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg); +static void ti_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios); +static int ti_tiocmget(struct tty_struct *tty, struct file *file); +static int ti_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear); +static void ti_break(struct tty_struct *tty, int break_state); static void ti_interrupt_callback(struct urb *urb); static void ti_bulk_in_callback(struct urb *urb); static void ti_bulk_out_callback(struct urb *urb); @@ -192,8 +196,7 @@ static int ti_command_in_sync(struct ti_device *tdev, __u8 command, static int ti_write_byte(struct ti_device *tdev, unsigned long addr, __u8 mask, __u8 byte); -static int ti_download_firmware(struct ti_device *tdev, char *fw_name); - +static int ti_download_firmware(struct ti_device *tdev, int type); /* circular buffer */ static struct circ_buf *ti_buf_alloc(void); @@ -325,19 +328,25 @@ module_param(debug, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Enable debugging, 0=no, 1=yes"); module_param(low_latency, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(low_latency, "TTY low_latency flag, 0=off, 1=on, default is off"); +MODULE_PARM_DESC(low_latency, + "TTY low_latency flag, 0=off, 1=on, default is off"); module_param(closing_wait, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(closing_wait, "Maximum wait for data to drain in close, in .01 secs, default is 4000"); +MODULE_PARM_DESC(closing_wait, + "Maximum wait for data to drain in close, in .01 secs, default is 4000"); module_param_array(vendor_3410, ushort, &vendor_3410_count, S_IRUGO); -MODULE_PARM_DESC(vendor_3410, "Vendor ids for 3410 based devices, 1-5 short integers"); +MODULE_PARM_DESC(vendor_3410, + "Vendor ids for 3410 based devices, 1-5 short integers"); module_param_array(product_3410, ushort, &product_3410_count, S_IRUGO); -MODULE_PARM_DESC(product_3410, "Product ids for 3410 based devices, 1-5 short integers"); +MODULE_PARM_DESC(product_3410, + "Product ids for 3410 based devices, 1-5 short integers"); module_param_array(vendor_5052, ushort, &vendor_5052_count, S_IRUGO); -MODULE_PARM_DESC(vendor_5052, "Vendor ids for 5052 based devices, 1-5 short integers"); +MODULE_PARM_DESC(vendor_5052, + "Vendor ids for 5052 based devices, 1-5 short integers"); module_param_array(product_5052, ushort, &product_5052_count, S_IRUGO); -MODULE_PARM_DESC(product_5052, "Product ids for 5052 based devices, 1-5 short integers"); +MODULE_PARM_DESC(product_5052, + "Product ids for 5052 based devices, 1-5 short integers"); MODULE_DEVICE_TABLE(usb, ti_id_table_combined); @@ -346,18 +355,18 @@ MODULE_DEVICE_TABLE(usb, ti_id_table_combined); static int __init ti_init(void) { - int i,j; + int i, j; int ret; /* insert extra vendor and product ids */ j = ARRAY_SIZE(ti_id_table_3410) - TI_EXTRA_VID_PID_COUNT - 1; - for (i=0; i<min(vendor_3410_count,product_3410_count); i++,j++) { + for (i = 0; i < min(vendor_3410_count, product_3410_count); i++, j++) { ti_id_table_3410[j].idVendor = vendor_3410[i]; ti_id_table_3410[j].idProduct = product_3410[i]; ti_id_table_3410[j].match_flags = USB_DEVICE_ID_MATCH_DEVICE; } j = ARRAY_SIZE(ti_id_table_5052) - TI_EXTRA_VID_PID_COUNT - 1; - for (i=0; i<min(vendor_5052_count,product_5052_count); i++,j++) { + for (i = 0; i < min(vendor_5052_count, product_5052_count); i++, j++) { ti_id_table_5052[j].idVendor = vendor_5052[i]; ti_id_table_5052[j].idProduct = product_5052[i]; ti_id_table_5052[j].match_flags = USB_DEVICE_ID_MATCH_DEVICE; @@ -426,15 +435,15 @@ static int ti_startup(struct usb_serial *serial) /* determine device type */ if (usb_match_id(serial->interface, ti_id_table_3410)) tdev->td_is_3410 = 1; - dbg("%s - device type is %s", __func__, tdev->td_is_3410 ? "3410" : "5052"); + dbg("%s - device type is %s", __func__, + tdev->td_is_3410 ? "3410" : "5052"); /* if we have only 1 configuration, download firmware */ if (dev->descriptor.bNumConfigurations == 1) { - if (tdev->td_is_3410) - status = ti_download_firmware(tdev, "ti_3410.fw"); + status = ti_download_firmware(tdev, 3410); else - status = ti_download_firmware(tdev, "ti_5052.fw"); + status = ti_download_firmware(tdev, 5052); if (status) goto free_tdev; @@ -446,7 +455,7 @@ static int ti_startup(struct usb_serial *serial) status = -ENODEV; goto free_tdev; - } + } /* the second configuration must be set (in sysfs by hotplug script) */ if (dev->actconfig->desc.bConfigurationValue == TI_BOOT_CONFIG) { @@ -463,7 +472,8 @@ static int ti_startup(struct usb_serial *serial) goto free_tports; } spin_lock_init(&tport->tp_lock); - tport->tp_uart_base_addr = (i == 0 ? TI_UART1_BASE_ADDR : TI_UART2_BASE_ADDR); + tport->tp_uart_base_addr = (i == 0 ? + TI_UART1_BASE_ADDR : TI_UART2_BASE_ADDR); tport->tp_flags = low_latency ? ASYNC_LOW_LATENCY : 0; tport->tp_closing_wait = closing_wait; init_waitqueue_head(&tport->tp_msr_wait); @@ -480,11 +490,11 @@ static int ti_startup(struct usb_serial *serial) usb_set_serial_port_data(serial->port[i], tport); tport->tp_uart_mode = 0; /* default is RS232 */ } - + return 0; free_tports: - for (--i; i>=0; --i) { + for (--i; i >= 0; --i) { tport = usb_get_serial_port_data(serial->port[i]); ti_buf_free(tport->tp_write_buf); kfree(tport); @@ -505,7 +515,7 @@ static void ti_shutdown(struct usb_serial *serial) dbg("%s", __func__); - for (i=0; i < serial->num_ports; ++i) { + for (i = 0; i < serial->num_ports; ++i) { tport = usb_get_serial_port_data(serial->port[i]); if (tport) { ti_buf_free(tport->tp_write_buf); @@ -519,7 +529,8 @@ static void ti_shutdown(struct usb_serial *serial) } -static int ti_open(struct usb_serial_port *port, struct file *file) +static int ti_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *file) { struct ti_port *tport = usb_get_serial_port_data(port); struct ti_device *tdev; @@ -527,8 +538,8 @@ static int ti_open(struct usb_serial_port *port, struct file *file) struct urb *urb; int port_number; int status; - __u16 open_settings = (__u8)(TI_PIPE_MODE_CONTINOUS | - TI_PIPE_TIMEOUT_ENABLE | + __u16 open_settings = (__u8)(TI_PIPE_MODE_CONTINOUS | + TI_PIPE_TIMEOUT_ENABLE | (TI_TRANSFER_TIMEOUT << 2)); dbg("%s - port %d", __func__, port->number); @@ -543,9 +554,9 @@ static int ti_open(struct usb_serial_port *port, struct file *file) if (mutex_lock_interruptible(&tdev->td_open_close_lock)) return -ERESTARTSYS; - if (port->tty) - port->tty->low_latency = - (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0; + if (tty) + tty->low_latency = + (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0; port_number = port->number - port->serial->minor; @@ -559,7 +570,8 @@ static int ti_open(struct usb_serial_port *port, struct file *file) dbg("%s - start interrupt in urb", __func__); urb = tdev->td_serial->port[0]->interrupt_in_urb; if (!urb) { - dev_err(&port->dev, "%s - no interrupt urb\n", __func__); + dev_err(&port->dev, "%s - no interrupt urb\n", + __func__); status = -EINVAL; goto release_lock; } @@ -568,18 +580,22 @@ static int ti_open(struct usb_serial_port *port, struct file *file) urb->dev = dev; status = usb_submit_urb(urb, GFP_KERNEL); if (status) { - dev_err(&port->dev, "%s - submit interrupt urb failed, %d\n", __func__, status); + dev_err(&port->dev, + "%s - submit interrupt urb failed, %d\n", + __func__, status); goto release_lock; } } - ti_set_termios(port, port->tty->termios); + if (tty) + ti_set_termios(tty, port, tty->termios); dbg("%s - sending TI_OPEN_PORT", __func__); status = ti_command_out_sync(tdev, TI_OPEN_PORT, (__u8)(TI_UART1_PORT + port_number), open_settings, NULL, 0); if (status) { - dev_err(&port->dev, "%s - cannot send open command, %d\n", __func__, status); + dev_err(&port->dev, "%s - cannot send open command, %d\n", + __func__, status); goto unlink_int_urb; } @@ -587,7 +603,8 @@ static int ti_open(struct usb_serial_port *port, struct file *file) status = ti_command_out_sync(tdev, TI_START_PORT, (__u8)(TI_UART1_PORT + port_number), 0, NULL, 0); if (status) { - dev_err(&port->dev, "%s - cannot send start command, %d\n", __func__, status); + dev_err(&port->dev, "%s - cannot send start command, %d\n", + __func__, status); goto unlink_int_urb; } @@ -595,13 +612,15 @@ static int ti_open(struct usb_serial_port *port, struct file *file) status = ti_command_out_sync(tdev, TI_PURGE_PORT, (__u8)(TI_UART1_PORT + port_number), TI_PURGE_INPUT, NULL, 0); if (status) { - dev_err(&port->dev, "%s - cannot clear input buffers, %d\n", __func__, status); + dev_err(&port->dev, "%s - cannot clear input buffers, %d\n", + __func__, status); goto unlink_int_urb; } status = ti_command_out_sync(tdev, TI_PURGE_PORT, (__u8)(TI_UART1_PORT + port_number), TI_PURGE_OUTPUT, NULL, 0); if (status) { - dev_err(&port->dev, "%s - cannot clear output buffers, %d\n", __func__, status); + dev_err(&port->dev, "%s - cannot clear output buffers, %d\n", + __func__, status); goto unlink_int_urb; } @@ -610,13 +629,15 @@ static int ti_open(struct usb_serial_port *port, struct file *file) usb_clear_halt(dev, port->write_urb->pipe); usb_clear_halt(dev, port->read_urb->pipe); - ti_set_termios(port, port->tty->termios); + if (tty) + ti_set_termios(tty, port, tty->termios); dbg("%s - sending TI_OPEN_PORT (2)", __func__); status = ti_command_out_sync(tdev, TI_OPEN_PORT, (__u8)(TI_UART1_PORT + port_number), open_settings, NULL, 0); if (status) { - dev_err(&port->dev, "%s - cannot send open command (2), %d\n", __func__, status); + dev_err(&port->dev, "%s - cannot send open command (2), %d\n", + __func__, status); goto unlink_int_urb; } @@ -624,7 +645,8 @@ static int ti_open(struct usb_serial_port *port, struct file *file) status = ti_command_out_sync(tdev, TI_START_PORT, (__u8)(TI_UART1_PORT + port_number), 0, NULL, 0); if (status) { - dev_err(&port->dev, "%s - cannot send start command (2), %d\n", __func__, status); + dev_err(&port->dev, "%s - cannot send start command (2), %d\n", + __func__, status); goto unlink_int_urb; } @@ -642,7 +664,8 @@ static int ti_open(struct usb_serial_port *port, struct file *file) urb->dev = dev; status = usb_submit_urb(urb, GFP_KERNEL); if (status) { - dev_err(&port->dev, "%s - submit read urb failed, %d\n", __func__, status); + dev_err(&port->dev, "%s - submit read urb failed, %d\n", + __func__, status); goto unlink_int_urb; } @@ -661,7 +684,8 @@ release_lock: } -static void ti_close(struct usb_serial_port *port, struct file *file) +static void ti_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *file) { struct ti_device *tdev; struct ti_port *tport; @@ -670,7 +694,7 @@ static void ti_close(struct usb_serial_port *port, struct file *file) int do_unlock; dbg("%s - port %d", __func__, port->number); - + tdev = usb_get_serial_data(port->serial); tport = usb_get_serial_port_data(port); if (tdev == NULL || tport == NULL) @@ -690,7 +714,9 @@ static void ti_close(struct usb_serial_port *port, struct file *file) status = ti_command_out_sync(tdev, TI_CLOSE_PORT, (__u8)(TI_UART1_PORT + port_number), 0, NULL, 0); if (status) - dev_err(&port->dev, "%s - cannot send close port command, %d\n" , __func__, status); + dev_err(&port->dev, + "%s - cannot send close port command, %d\n" + , __func__, status); /* if mutex_lock is interrupted, continue anyway */ do_unlock = !mutex_lock_interruptible(&tdev->td_open_close_lock); @@ -707,8 +733,8 @@ static void ti_close(struct usb_serial_port *port, struct file *file) } -static int ti_write(struct usb_serial_port *port, const unsigned char *data, - int count) +static int ti_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *data, int count) { struct ti_port *tport = usb_get_serial_port_data(port); unsigned long flags; @@ -733,8 +759,9 @@ static int ti_write(struct usb_serial_port *port, const unsigned char *data, } -static int ti_write_room(struct usb_serial_port *port) +static int ti_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); int room = 0; unsigned long flags; @@ -743,7 +770,7 @@ static int ti_write_room(struct usb_serial_port *port) if (tport == NULL) return -ENODEV; - + spin_lock_irqsave(&tport->tp_lock, flags); room = ti_buf_space_avail(tport->tp_write_buf); spin_unlock_irqrestore(&tport->tp_lock, flags); @@ -753,8 +780,9 @@ static int ti_write_room(struct usb_serial_port *port) } -static int ti_chars_in_buffer(struct usb_serial_port *port) +static int ti_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; @@ -773,32 +801,26 @@ static int ti_chars_in_buffer(struct usb_serial_port *port) } -static void ti_throttle(struct usb_serial_port *port) +static void ti_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); - struct tty_struct *tty; dbg("%s - port %d", __func__, port->number); if (tport == NULL) return; - tty = port->tty; - if (!tty) { - dbg("%s - no tty", __func__); - return; - } - if (I_IXOFF(tty) || C_CRTSCTS(tty)) ti_stop_read(tport, tty); } -static void ti_unthrottle(struct usb_serial_port *port) +static void ti_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); - struct tty_struct *tty; int status; dbg("%s - port %d", __func__, port->number); @@ -806,23 +828,19 @@ static void ti_unthrottle(struct usb_serial_port *port) if (tport == NULL) return; - tty = port->tty; - if (!tty) { - dbg("%s - no tty", __func__); - return; - } - if (I_IXOFF(tty) || C_CRTSCTS(tty)) { status = ti_restart_read(tport, tty); if (status) - dev_err(&port->dev, "%s - cannot restart read, %d\n", __func__, status); + dev_err(&port->dev, "%s - cannot restart read, %d\n", + __func__, status); } } -static int ti_ioctl(struct usb_serial_port *port, struct file *file, +static int ti_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); struct async_icount cnow; struct async_icount cprev; @@ -833,55 +851,52 @@ static int ti_ioctl(struct usb_serial_port *port, struct file *file, return -ENODEV; switch (cmd) { - case TIOCGSERIAL: - dbg("%s - (%d) TIOCGSERIAL", __func__, port->number); - return ti_get_serial_info(tport, (struct serial_struct __user *)arg); - break; - - case TIOCSSERIAL: - dbg("%s - (%d) TIOCSSERIAL", __func__, port->number); - return ti_set_serial_info(tport, (struct serial_struct __user *)arg); - break; - - case TIOCMIWAIT: - dbg("%s - (%d) TIOCMIWAIT", __func__, port->number); - cprev = tport->tp_icount; - while (1) { - interruptible_sleep_on(&tport->tp_msr_wait); - if (signal_pending(current)) - return -ERESTARTSYS; - cnow = tport->tp_icount; - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) - return -EIO; /* no change => error */ - 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)) ) { - return 0; - } - cprev = cnow; - } - break; - - case TIOCGICOUNT: - dbg("%s - (%d) TIOCGICOUNT RX=%d, TX=%d", __func__, port->number, tport->tp_icount.rx, tport->tp_icount.tx); - if (copy_to_user((void __user *)arg, &tport->tp_icount, sizeof(tport->tp_icount))) - return -EFAULT; - return 0; + case TIOCGSERIAL: + dbg("%s - (%d) TIOCGSERIAL", __func__, port->number); + return ti_get_serial_info(tport, + (struct serial_struct __user *)arg); + case TIOCSSERIAL: + dbg("%s - (%d) TIOCSSERIAL", __func__, port->number); + return ti_set_serial_info(tport, + (struct serial_struct __user *)arg); + case TIOCMIWAIT: + dbg("%s - (%d) TIOCMIWAIT", __func__, port->number); + cprev = tport->tp_icount; + while (1) { + interruptible_sleep_on(&tport->tp_msr_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + cnow = tport->tp_icount; + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return -EIO; /* no change => error */ + 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))) + return 0; + cprev = cnow; + } + break; + case TIOCGICOUNT: + dbg("%s - (%d) TIOCGICOUNT RX=%d, TX=%d", + __func__, port->number, + tport->tp_icount.rx, tport->tp_icount.tx); + if (copy_to_user((void __user *)arg, &tport->tp_icount, + sizeof(tport->tp_icount))) + return -EFAULT; + return 0; } - return -ENOIOCTLCMD; } -static void ti_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void ti_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct ti_port *tport = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; struct ti_uart_config *config; - tcflag_t cflag,iflag; + tcflag_t cflag, iflag; int baud; int status; int port_number = port->number - port->serial->minor; @@ -893,7 +908,8 @@ static void ti_set_termios(struct usb_serial_port *port, iflag = tty->termios->c_iflag; dbg("%s - cflag %08x, iflag %08x", __func__, cflag, iflag); - dbg("%s - old clfag %08x, old iflag %08x", __func__, old_termios->c_cflag, old_termios->c_iflag); + dbg("%s - old clfag %08x, old iflag %08x", __func__, + old_termios->c_cflag, old_termios->c_iflag); if (tport == NULL) return; @@ -912,19 +928,19 @@ static void ti_set_termios(struct usb_serial_port *port, config->bUartMode = (__u8)(tport->tp_uart_mode); switch (cflag & CSIZE) { - case CS5: - config->bDataBits = TI_UART_5_DATA_BITS; - break; - case CS6: - config->bDataBits = TI_UART_6_DATA_BITS; - break; - case CS7: - config->bDataBits = TI_UART_7_DATA_BITS; - break; - default: - case CS8: - config->bDataBits = TI_UART_8_DATA_BITS; - break; + case CS5: + config->bDataBits = TI_UART_5_DATA_BITS; + break; + case CS6: + config->bDataBits = TI_UART_6_DATA_BITS; + break; + case CS7: + config->bDataBits = TI_UART_7_DATA_BITS; + break; + default: + case CS8: + config->bDataBits = TI_UART_8_DATA_BITS; + break; } /* CMSPAR isn't supported by this driver */ @@ -940,7 +956,7 @@ static void ti_set_termios(struct usb_serial_port *port, } } else { config->wFlags &= ~TI_UART_ENABLE_PARITY_CHECKING; - config->bParity = TI_UART_NO_PARITY; + config->bParity = TI_UART_NO_PARITY; } if (cflag & CSTOPB) @@ -993,7 +1009,8 @@ static void ti_set_termios(struct usb_serial_port *port, (__u8)(TI_UART1_PORT + port_number), 0, (__u8 *)config, sizeof(*config)); if (status) - dev_err(&port->dev, "%s - cannot set config on port %d, %d\n", __func__, port_number, status); + dev_err(&port->dev, "%s - cannot set config on port %d, %d\n", + __func__, port_number, status); /* SET_CONFIG asserts RTS and DTR, reset them correctly */ mcr = tport->tp_shadow_mcr; @@ -1002,14 +1019,17 @@ static void ti_set_termios(struct usb_serial_port *port, mcr &= ~(TI_MCR_DTR | TI_MCR_RTS); status = ti_set_mcr(tport, mcr); if (status) - dev_err(&port->dev, "%s - cannot set modem control on port %d, %d\n", __func__, port_number, status); + dev_err(&port->dev, + "%s - cannot set modem control on port %d, %d\n", + __func__, port_number, status); kfree(config); } -static int ti_tiocmget(struct usb_serial_port *port, struct file *file) +static int ti_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); unsigned int result; unsigned int msr; @@ -1040,9 +1060,10 @@ static int ti_tiocmget(struct usb_serial_port *port, struct file *file) } -static int ti_tiocmset(struct usb_serial_port *port, struct file *file, +static int ti_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); unsigned int mcr; unsigned long flags; @@ -1074,8 +1095,9 @@ static int ti_tiocmset(struct usb_serial_port *port, struct file *file, } -static void ti_break(struct usb_serial_port *port, int break_state) +static void ti_break(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); int status; @@ -1141,10 +1163,12 @@ static void ti_interrupt_callback(struct urb *urb) port_number = TI_GET_PORT_FROM_CODE(data[0]); function = TI_GET_FUNC_FROM_CODE(data[0]); - dbg("%s - port_number %d, function %d, data 0x%02X", __func__, port_number, function, data[1]); + dbg("%s - port_number %d, function %d, data 0x%02X", + __func__, port_number, function, data[1]); if (port_number >= serial->num_ports) { - dev_err(dev, "%s - bad port number, %d\n", __func__, port_number); + dev_err(dev, "%s - bad port number, %d\n", + __func__, port_number); goto exit; } @@ -1156,7 +1180,8 @@ static void ti_interrupt_callback(struct urb *urb) switch (function) { case TI_CODE_DATA_ERROR: - dev_err(dev, "%s - DATA ERROR, port %d, data 0x%02X\n", __func__, port_number, data[1]); + dev_err(dev, "%s - DATA ERROR, port %d, data 0x%02X\n", + __func__, port_number, data[1]); break; case TI_CODE_MODEM_STATUS: @@ -1166,7 +1191,8 @@ static void ti_interrupt_callback(struct urb *urb) break; default: - dev_err(dev, "%s - unknown interrupt code, 0x%02X\n", __func__, data[1]); + dev_err(dev, "%s - unknown interrupt code, 0x%02X\n", + __func__, data[1]); break; } @@ -1200,7 +1226,7 @@ static void ti_bulk_in_callback(struct urb *urb) return; default: dev_err(dev, "%s - nonzero urb status, %d\n", - __func__, status ); + __func__, status); tport->tp_tdev->td_urb_error = 1; wake_up_interruptible(&tport->tp_write_wait); } @@ -1213,15 +1239,16 @@ static void ti_bulk_in_callback(struct urb *urb) return; } - if (port->tty && urb->actual_length) { + if (port->port.tty && urb->actual_length) { usb_serial_debug_data(debug, dev, __func__, urb->actual_length, urb->transfer_buffer); if (!tport->tp_is_open) dbg("%s - port closed, dropping data", __func__); else - ti_recv(&urb->dev->dev, port->tty, urb->transfer_buffer, - urb->actual_length); + ti_recv(&urb->dev->dev, port->port.tty, + urb->transfer_buffer, + urb->actual_length); spin_lock(&tport->tp_lock); tport->tp_icount.rx += urb->actual_length; @@ -1285,8 +1312,9 @@ static void ti_recv(struct device *dev, struct tty_struct *tty, do { cnt = tty_buffer_request_room(tty, length); if (cnt < length) { - dev_err(dev, "%s - dropping data, %d bytes lost\n", __func__, length - cnt); - if(cnt == 0) + dev_err(dev, "%s - dropping data, %d bytes lost\n", + __func__, length - cnt); + if (cnt == 0) break; } tty_insert_flip_string(tty, data, cnt); @@ -1302,7 +1330,7 @@ static void ti_send(struct ti_port *tport) { int count, result; struct usb_serial_port *port = tport->tp_port; - struct tty_struct *tty = port->tty; + struct tty_struct *tty = port->port.tty; /* FIXME */ unsigned long flags; @@ -1328,7 +1356,8 @@ static void ti_send(struct ti_port *tport) spin_unlock_irqrestore(&tport->tp_lock, flags); - usb_serial_debug_data(debug, &port->dev, __func__, count, port->write_urb->transfer_buffer); + usb_serial_debug_data(debug, &port->dev, __func__, count, + port->write_urb->transfer_buffer); usb_fill_bulk_urb(port->write_urb, port->serial->dev, usb_sndbulkpipe(port->serial->dev, @@ -1338,8 +1367,9 @@ static void ti_send(struct ti_port *tport) result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result) { - dev_err(&port->dev, "%s - submit write urb failed, %d\n", __func__, result); - tport->tp_write_urb_in_use = 0; + dev_err(&port->dev, "%s - submit write urb failed, %d\n", + __func__, result); + tport->tp_write_urb_in_use = 0; /* TODO: reschedule ti_send */ } else { spin_lock_irqsave(&tport->tp_lock, flags); @@ -1374,7 +1404,7 @@ static int ti_set_mcr(struct ti_port *tport, unsigned int mcr) static int ti_get_lsr(struct ti_port *tport) { - int size,status; + int size, status; struct ti_device *tdev = tport->tp_tdev; struct usb_serial_port *port = tport->tp_port; int port_number = port->number - port->serial->minor; @@ -1392,7 +1422,9 @@ static int ti_get_lsr(struct ti_port *tport) status = ti_command_in_sync(tdev, TI_GET_PORT_STATUS, (__u8)(TI_UART1_PORT+port_number), 0, (__u8 *)data, size); if (status) { - dev_err(&port->dev, "%s - get port status command failed, %d\n", __func__, status); + dev_err(&port->dev, + "%s - get port status command failed, %d\n", + __func__, status); goto free_data; } @@ -1442,8 +1474,9 @@ static int ti_set_serial_info(struct ti_port *tport, return -EFAULT; tport->tp_flags = new_serial.flags & TI_SET_SERIAL_FLAGS; - if (port->tty) - port->tty->low_latency = + /* FIXME */ + if (port->port.tty) + port->port.tty->low_latency = (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0; tport->tp_closing_wait = new_serial.closing_wait; @@ -1477,7 +1510,7 @@ static void ti_handle_new_msr(struct ti_port *tport, __u8 msr) tport->tp_msr = msr & TI_MSR_MASK; /* handle CTS flow control */ - tty = tport->tp_port->tty; + tty = tport->tp_port->port.tty; if (tty && C_CRTSCTS(tty)) { if (msr & TI_MSR_CTS) { tty->hw_stopped = 0; @@ -1627,7 +1660,8 @@ static int ti_write_byte(struct ti_device *tdev, unsigned long addr, struct ti_write_data_bytes *data; struct device *dev = &tdev->td_serial->dev->dev; - dbg("%s - addr 0x%08lX, mask 0x%02X, byte 0x%02X", __func__, addr, mask, byte); + dbg("%s - addr 0x%08lX, mask 0x%02X, byte 0x%02X", + __func__, addr, mask, byte); size = sizeof(struct ti_write_data_bytes) + 2; data = kmalloc(size, GFP_KERNEL); @@ -1655,67 +1689,68 @@ static int ti_write_byte(struct ti_device *tdev, unsigned long addr, return status; } - -static int ti_download_firmware(struct ti_device *tdev, - char *fw_name) +static int ti_do_download(struct usb_device *dev, int pipe, + u8 *buffer, int size) { - const struct firmware *fw; - int status = 0; - int buffer_size; int pos; - int len; + u8 cs = 0; int done; - __u8 cs = 0; - __u8 *buffer; - struct usb_device *dev = tdev->td_serial->dev; struct ti_firmware_header *header; - unsigned int pipe = usb_sndbulkpipe(dev, - tdev->td_serial->port[0]->bulk_out_endpointAddress); - - buffer_size = TI_FIRMWARE_BUF_SIZE + sizeof(struct ti_firmware_header); - - if (request_firmware(&fw, fw_name, &dev->dev)) { - dev_err(&dev->dev, "%s - failed to load firmware \"%s\"\n", - __func__, fw_name); - return -ENOENT; - } - if (fw->size > buffer_size) { - dev_err(&dev->dev, "%s - firmware \"%s\" is too large\n", - __func__, fw_name); - release_firmware(fw); - return -EINVAL; - } - - buffer = kmalloc(buffer_size, GFP_KERNEL); - if (!buffer) { - dev_err(&dev->dev, "%s - out of memory\n", __func__); - release_firmware(fw); - return -ENOMEM; - } - - memcpy(buffer, fw->data, fw->size); - memset(buffer+fw->size, 0xff, buffer_size-fw->size); + int status; + int len; - for(pos = sizeof(struct ti_firmware_header); pos < buffer_size; pos++) + for (pos = sizeof(struct ti_firmware_header); pos < size; pos++) cs = (__u8)(cs + buffer[pos]); header = (struct ti_firmware_header *)buffer; - header->wLength = cpu_to_le16((__u16)(buffer_size - sizeof(struct ti_firmware_header))); + header->wLength = cpu_to_le16((__u16)(size + - sizeof(struct ti_firmware_header))); header->bCheckSum = cs; dbg("%s - downloading firmware", __func__); - for (pos = 0; pos < buffer_size; pos += done) { - len = min(buffer_size - pos, TI_DOWNLOAD_MAX_PACKET_SIZE); - status = usb_bulk_msg(dev, pipe, buffer+pos, len, &done, 1000); + for (pos = 0; pos < size; pos += done) { + len = min(size - pos, TI_DOWNLOAD_MAX_PACKET_SIZE); + status = usb_bulk_msg(dev, pipe, buffer + pos, len, + &done, 1000); if (status) break; } + return status; +} - kfree(buffer); - release_firmware(fw); +static int ti_download_firmware(struct ti_device *tdev, int type) +{ + int status = -ENOMEM; + int buffer_size; + __u8 *buffer; + struct usb_device *dev = tdev->td_serial->dev; + unsigned int pipe = usb_sndbulkpipe(dev, + tdev->td_serial->port[0]->bulk_out_endpointAddress); + const struct firmware *fw_p; + char buf[32]; + sprintf(buf, "ti_usb-%d.bin", type); + if (request_firmware(&fw_p, buf, &dev->dev)) { + dev_err(&dev->dev, "%s - firmware not found\n", __func__); + return -ENOENT; + } + if (fw_p->size > TI_FIRMWARE_BUF_SIZE) { + dev_err(&dev->dev, "%s - firmware too large\n", __func__); + return -ENOENT; + } + + buffer_size = TI_FIRMWARE_BUF_SIZE + sizeof(struct ti_firmware_header); + buffer = kmalloc(buffer_size, GFP_KERNEL); + if (buffer) { + memcpy(buffer, fw_p->data, fw_p->size); + memset(buffer + fw_p->size, 0xff, buffer_size - fw_p->size); + ti_do_download(dev, pipe, buffer, fw_p->size); + kfree(buffer); + } + release_firmware(fw_p); if (status) { - dev_err(&dev->dev, "%s - error downloading firmware, %d\n", __func__, status); + dev_err(&dev->dev, "%s - error downloading firmware, %d\n", + __func__, status); return status; } @@ -1787,7 +1822,7 @@ static void ti_buf_clear(struct circ_buf *cb) static int ti_buf_data_avail(struct circ_buf *cb) { - return CIRC_CNT(cb->head,cb->tail,TI_WRITE_BUF_SIZE); + return CIRC_CNT(cb->head, cb->tail, TI_WRITE_BUF_SIZE); } @@ -1800,7 +1835,7 @@ static int ti_buf_data_avail(struct circ_buf *cb) static int ti_buf_space_avail(struct circ_buf *cb) { - return CIRC_SPACE(cb->head,cb->tail,TI_WRITE_BUF_SIZE); + return CIRC_SPACE(cb->head, cb->tail, TI_WRITE_BUF_SIZE); } diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 0cb0d77dc429..8c2d531eedea 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -12,7 +12,8 @@ * This driver was originally based on the ACM driver by Armin Fuerst (which was * based on a driver by Brad Keryan) * - * See Documentation/usb/usb-serial.txt for more information on using this driver + * See Documentation/usb/usb-serial.txt for more information on using this + * driver * */ @@ -28,7 +29,7 @@ #include <linux/spinlock.h> #include <linux/mutex.h> #include <linux/list.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include "pl2303.h" @@ -59,7 +60,8 @@ static struct usb_driver usb_serial_driver = { */ static int debug; -static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; /* initially all NULL */ +/* initially all NULL */ +static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; static DEFINE_MUTEX(table_lock); static LIST_HEAD(usb_serial_driver_list); @@ -76,7 +78,8 @@ struct usb_serial *usb_serial_get_by_index(unsigned index) return serial; } -static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_ports, unsigned int *minor) +static struct usb_serial *get_free_serial(struct usb_serial *serial, + int num_ports, unsigned int *minor) { unsigned int i, j; int good_spot; @@ -122,9 +125,8 @@ static void return_serial(struct usb_serial *serial) if (serial == NULL) return; - for (i = 0; i < serial->num_ports; ++i) { + for (i = 0; i < serial->num_ports; ++i) serial_table[serial->minor + i] = NULL; - } } static void destroy_serial(struct kref *kref) @@ -143,7 +145,7 @@ static void destroy_serial(struct kref *kref) return_serial(serial); for (i = 0; i < serial->num_ports; ++i) - serial->port[i]->open_count = 0; + serial->port[i]->port.count = 0; /* the ports are cleaned up and released in port_release() */ for (i = 0; i < serial->num_ports; ++i) @@ -156,7 +158,8 @@ static void destroy_serial(struct kref *kref) * not get cleaned up in port_release() as it was never registered with * the driver core */ if (serial->num_ports < serial->num_port_pointers) { - for (i = serial->num_ports; i < serial->num_port_pointers; ++i) { + for (i = serial->num_ports; + i < serial->num_port_pointers; ++i) { port = serial->port[i]; if (!port) continue; @@ -167,7 +170,7 @@ static void destroy_serial(struct kref *kref) usb_put_dev(serial->dev); /* free up any memory that we allocated */ - kfree (serial); + kfree(serial); } void usb_serial_put(struct usb_serial *serial) @@ -180,13 +183,13 @@ void usb_serial_put(struct usb_serial *serial) /***************************************************************************** * Driver tty interface functions *****************************************************************************/ -static int serial_open (struct tty_struct *tty, struct file * filp) +static int serial_open (struct tty_struct *tty, struct file *filp) { struct usb_serial *serial; struct usb_serial_port *port; unsigned int portNumber; int retval; - + dbg("%s", __func__); /* get the serial object associated with this tty pointer */ @@ -207,15 +210,15 @@ static int serial_open (struct tty_struct *tty, struct file * filp) retval = -ERESTARTSYS; goto bailout_kref_put; } - - ++port->open_count; + + ++port->port.count; /* set up our port structure making the tty driver * remember our port object, and us it */ tty->driver_data = port; - port->tty = tty; + port->port.tty = tty; - if (port->open_count == 1) { + if (port->port.count == 1) { /* lock this module before we call it * this may fail, which means we must bail out, @@ -228,9 +231,9 @@ static int serial_open (struct tty_struct *tty, struct file * filp) retval = usb_autopm_get_interface(serial->interface); if (retval) goto bailout_module_put; - /* only call the device specific open if this + /* only call the device specific open if this * is the first time the port is opened */ - retval = serial->type->open(port, filp); + retval = serial->type->open(tty, port, filp); if (retval) goto bailout_interface_put; } @@ -243,16 +246,16 @@ bailout_interface_put: bailout_module_put: module_put(serial->type->driver.owner); bailout_mutex_unlock: - port->open_count = 0; + port->port.count = 0; tty->driver_data = NULL; - port->tty = NULL; + port->port.tty = NULL; mutex_unlock(&port->mutex); bailout_kref_put: usb_serial_put(serial); return retval; } -static void serial_close(struct tty_struct *tty, struct file * filp) +static void serial_close(struct tty_struct *tty, struct file *filp) { struct usb_serial_port *port = tty->driver_data; @@ -263,27 +266,30 @@ static void serial_close(struct tty_struct *tty, struct file * filp) mutex_lock(&port->mutex); - if (port->open_count == 0) { + if (port->port.count == 0) { mutex_unlock(&port->mutex); return; } - --port->open_count; - if (port->open_count == 0) - /* only call the device specific close if this + --port->port.count; + if (port->port.count == 0) + /* only call the device specific close if this * port is being closed by the last owner */ - port->serial->type->close(port, filp); + port->serial->type->close(tty, port, filp); - if (port->open_count == (port->console? 1 : 0)) { - if (port->tty) { - if (port->tty->driver_data) - port->tty->driver_data = NULL; - port->tty = NULL; + if (port->port.count == (port->console? 1 : 0)) { + if (port->port.tty) { + if (port->port.tty->driver_data) + port->port.tty->driver_data = NULL; + port->port.tty = NULL; } } - if (port->open_count == 0) { - usb_autopm_put_interface(port->serial->interface); + if (port->port.count == 0) { + mutex_lock(&port->serial->disc_mutex); + if (!port->serial->disconnected) + usb_autopm_put_interface(port->serial->interface); + mutex_unlock(&port->serial->disc_mutex); module_put(port->serial->type->driver.owner); } @@ -291,7 +297,8 @@ static void serial_close(struct tty_struct *tty, struct file * filp) usb_serial_put(port->serial); } -static int serial_write (struct tty_struct * tty, const unsigned char *buf, int count) +static int serial_write(struct tty_struct *tty, const unsigned char *buf, + int count) { struct usb_serial_port *port = tty->driver_data; int retval = -ENODEV; @@ -301,107 +308,112 @@ static int serial_write (struct tty_struct * tty, const unsigned char *buf, int dbg("%s - port %d, %d byte(s)", __func__, port->number, count); - /* open_count is managed under the mutex lock for the tty so cannot - drop to zero until after the last close completes */ - WARN_ON(!port->open_count); + /* count is managed under the mutex lock for the tty so cannot + drop to zero until after the last close completes */ + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function */ - retval = port->serial->type->write(port, buf, count); + retval = port->serial->type->write(tty, port, buf, count); exit: return retval; } -static int serial_write_room (struct tty_struct *tty) +static int serial_write_room(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function */ - return port->serial->type->write_room(port); + return port->serial->type->write_room(tty); } -static int serial_chars_in_buffer (struct tty_struct *tty) +static int serial_chars_in_buffer(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; dbg("%s = port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function */ - return port->serial->type->chars_in_buffer(port); + return port->serial->type->chars_in_buffer(tty); } -static void serial_throttle (struct tty_struct * tty) +static void serial_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function */ if (port->serial->type->throttle) - port->serial->type->throttle(port); + port->serial->type->throttle(tty); } -static void serial_unthrottle (struct tty_struct * tty) +static void serial_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function */ if (port->serial->type->unthrottle) - port->serial->type->unthrottle(port); + port->serial->type->unthrottle(tty); } -static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) +static int serial_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; int retval = -ENODEV; dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); - /* pass on to the driver specific version of this function if it is available */ + /* pass on to the driver specific version of this function + if it is available */ if (port->serial->type->ioctl) { lock_kernel(); - retval = port->serial->type->ioctl(port, file, cmd, arg); + retval = port->serial->type->ioctl(tty, file, cmd, arg); unlock_kernel(); - } - else + } else retval = -ENOIOCTLCMD; return retval; } -static void serial_set_termios (struct tty_struct *tty, struct ktermios * old) +static void serial_set_termios(struct tty_struct *tty, struct ktermios *old) { struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); - /* pass on to the driver specific version of this function if it is available */ + WARN_ON(!port->port.count); + /* pass on to the driver specific version of this function + if it is available */ if (port->serial->type->set_termios) - port->serial->type->set_termios(port, old); + port->serial->type->set_termios(tty, port, old); else tty_termios_copy_hw(tty->termios, old); } -static void serial_break (struct tty_struct *tty, int break_state) +static int serial_break(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); - /* pass on to the driver specific version of this function if it is available */ + WARN_ON(!port->port.count); + /* pass on to the driver specific version of this function + if it is available */ if (port->serial->type->break_ctl) { lock_kernel(); - port->serial->type->break_ctl(port, break_state); + port->serial->type->break_ctl(tty, break_state); unlock_kernel(); } + return 0; } -static int serial_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) +static int serial_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) { struct usb_serial *serial; int length = 0; @@ -410,26 +422,29 @@ static int serial_read_proc (char *page, char **start, off_t off, int count, int char tmp[40]; dbg("%s", __func__); - length += sprintf (page, "usbserinfo:1.0 driver:2.0\n"); + length += sprintf(page, "usbserinfo:1.0 driver:2.0\n"); for (i = 0; i < SERIAL_TTY_MINORS && length < PAGE_SIZE; ++i) { serial = usb_serial_get_by_index(i); if (serial == NULL) continue; - length += sprintf (page+length, "%d:", i); + length += sprintf(page+length, "%d:", i); if (serial->type->driver.owner) - length += sprintf (page+length, " module:%s", module_name(serial->type->driver.owner)); - length += sprintf (page+length, " name:\"%s\"", serial->type->description); - length += sprintf (page+length, " vendor:%04x product:%04x", - le16_to_cpu(serial->dev->descriptor.idVendor), - le16_to_cpu(serial->dev->descriptor.idProduct)); - length += sprintf (page+length, " num_ports:%d", serial->num_ports); - length += sprintf (page+length, " port:%d", i - serial->minor + 1); - + length += sprintf(page+length, " module:%s", + module_name(serial->type->driver.owner)); + length += sprintf(page+length, " name:\"%s\"", + serial->type->description); + length += sprintf(page+length, " vendor:%04x product:%04x", + le16_to_cpu(serial->dev->descriptor.idVendor), + le16_to_cpu(serial->dev->descriptor.idProduct)); + length += sprintf(page+length, " num_ports:%d", + serial->num_ports); + length += sprintf(page+length, " port:%d", + i - serial->minor + 1); usb_make_path(serial->dev, tmp, sizeof(tmp)); - length += sprintf (page+length, " path:%s", tmp); - - length += sprintf (page+length, "\n"); + length += sprintf(page+length, " path:%s", tmp); + + length += sprintf(page+length, "\n"); if ((length + begin) > (off + count)) { usb_serial_put(serial); goto done; @@ -445,31 +460,31 @@ done: if (off >= (length + begin)) return 0; *start = page + (off-begin); - return ((count < begin+length-off) ? count : begin+length-off); + return (count < begin+length-off) ? count : begin+length-off; } -static int serial_tiocmget (struct tty_struct *tty, struct file *file) +static int serial_tiocmget(struct tty_struct *tty, struct file *file) { struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); if (port->serial->type->tiocmget) - return port->serial->type->tiocmget(port, file); + return port->serial->type->tiocmget(tty, file); return -EINVAL; } -static int serial_tiocmset (struct tty_struct *tty, struct file *file, +static int serial_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); if (port->serial->type->tiocmset) - return port->serial->type->tiocmset(port, file, set, clear); + return port->serial->type->tiocmset(tty, file, set, clear); return -EINVAL; } @@ -482,6 +497,7 @@ void usb_serial_port_softint(struct usb_serial_port *port) { schedule_work(&port->work); } +EXPORT_SYMBOL_GPL(usb_serial_port_softint); static void usb_serial_port_work(struct work_struct *work) { @@ -490,11 +506,11 @@ static void usb_serial_port_work(struct work_struct *work) struct tty_struct *tty; dbg("%s - port %d", __func__, port->number); - + if (!port) return; - tty = port->tty; + tty = port->port.tty; if (!tty) return; @@ -505,7 +521,7 @@ static void port_release(struct device *dev) { struct usb_serial_port *port = to_usb_serial_port(dev); - dbg ("%s - %s", __func__, dev->bus_id); + dbg ("%s - %s", __func__, dev_name(dev)); port_free(port); } @@ -543,9 +559,9 @@ static void port_free(struct usb_serial_port *port) kfree(port); } -static struct usb_serial * create_serial (struct usb_device *dev, - struct usb_interface *interface, - struct usb_serial_driver *driver) +static struct usb_serial *create_serial(struct usb_device *dev, + struct usb_interface *interface, + struct usb_serial_driver *driver) { struct usb_serial *serial; @@ -564,7 +580,7 @@ static struct usb_serial * create_serial (struct usb_device *dev, } static const struct usb_device_id *match_dynamic_id(struct usb_interface *intf, - struct usb_serial_driver *drv) + struct usb_serial_driver *drv) { struct usb_dynid *dynid; @@ -596,7 +612,8 @@ exit: return id; } -static struct usb_serial_driver *search_serial_device(struct usb_interface *iface) +static struct usb_serial_driver *search_serial_device( + struct usb_interface *iface) { const struct usb_device_id *id; struct usb_serial_driver *drv; @@ -614,7 +631,7 @@ static struct usb_serial_driver *search_serial_device(struct usb_interface *ifac int usb_serial_probe(struct usb_interface *interface, const struct usb_device_id *id) { - struct usb_device *dev = interface_to_usbdev (interface); + struct usb_device *dev = interface_to_usbdev(interface); struct usb_serial *serial = NULL; struct usb_serial_port *port; struct usb_host_interface *iface_desc; @@ -625,7 +642,7 @@ int usb_serial_probe(struct usb_interface *interface, struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS]; struct usb_serial_driver *type = NULL; int retval; - int minor; + unsigned int minor; int buffer_size; int i; int num_interrupt_in = 0; @@ -643,7 +660,7 @@ int usb_serial_probe(struct usb_interface *interface, return -ENODEV; } - serial = create_serial (dev, interface, type); + serial = create_serial(dev, interface, type); if (!serial) { unlock_kernel(); dev_err(&interface->dev, "%s - out of memory\n", __func__); @@ -656,8 +673,9 @@ int usb_serial_probe(struct usb_interface *interface, if (!try_module_get(type->driver.owner)) { unlock_kernel(); - dev_err(&interface->dev, "module get failed, exiting\n"); - kfree (serial); + dev_err(&interface->dev, + "module get failed, exiting\n"); + kfree(serial); return -EIO; } @@ -667,8 +685,8 @@ int usb_serial_probe(struct usb_interface *interface, if (retval) { unlock_kernel(); - dbg ("sub driver rejected device"); - kfree (serial); + dbg("sub driver rejected device"); + kfree(serial); return retval; } } @@ -709,7 +727,7 @@ int usb_serial_probe(struct usb_interface *interface, } #if defined(CONFIG_USB_SERIAL_PL2303) || defined(CONFIG_USB_SERIAL_PL2303_MODULE) - /* BEGIN HORRIBLE HACK FOR PL2303 */ + /* BEGIN HORRIBLE HACK FOR PL2303 */ /* this is needed due to the looney way its endpoints are set up */ if (((le16_to_cpu(dev->descriptor.idVendor) == PL2303_VENDOR_ID) && (le16_to_cpu(dev->descriptor.idProduct) == PL2303_PRODUCT_ID)) || @@ -738,7 +756,7 @@ int usb_serial_probe(struct usb_interface *interface, if (num_bulk_in == 0 || num_bulk_out == 0) { unlock_kernel(); dev_info(&interface->dev, "PL-2303 hack: descriptors matched but endpoints did not\n"); - kfree (serial); + kfree(serial); return -ENODEV; } } @@ -750,8 +768,9 @@ int usb_serial_probe(struct usb_interface *interface, num_ports = num_bulk_out; if (num_ports == 0) { unlock_kernel(); - dev_err(&interface->dev, "Generic device with no bulk out, not allowed.\n"); - kfree (serial); + dev_err(&interface->dev, + "Generic device with no bulk out, not allowed.\n"); + kfree(serial); return -EIO; } } @@ -761,11 +780,12 @@ int usb_serial_probe(struct usb_interface *interface, if (type->calc_num_ports) { if (!try_module_get(type->driver.owner)) { unlock_kernel(); - dev_err(&interface->dev, "module get failed, exiting\n"); - kfree (serial); + dev_err(&interface->dev, + "module get failed, exiting\n"); + kfree(serial); return -EIO; } - num_ports = type->calc_num_ports (serial); + num_ports = type->calc_num_ports(serial); module_put(type->driver.owner); } if (!num_ports) @@ -783,7 +803,8 @@ int usb_serial_probe(struct usb_interface *interface, type->description); /* create our ports, we need as many as the max endpoints */ - /* we don't use num_ports here cauz some devices have more endpoint pairs than ports */ + /* we don't use num_ports here because some devices have more + endpoint pairs than ports */ max_endpoints = max(num_bulk_in, num_bulk_out); max_endpoints = max(max_endpoints, num_interrupt_in); max_endpoints = max(max_endpoints, num_interrupt_out); @@ -791,7 +812,8 @@ int usb_serial_probe(struct usb_interface *interface, serial->num_port_pointers = max_endpoints; unlock_kernel(); - dbg("%s - setting up %d port structures for this device", __func__, max_endpoints); + dbg("%s - setting up %d port structures for this device", + __func__, max_endpoints); for (i = 0; i < max_endpoints; ++i) { port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL); if (!port) @@ -807,7 +829,7 @@ int usb_serial_probe(struct usb_interface *interface, for (i = 0; i < num_bulk_in; ++i) { endpoint = bulk_in_endpoint[i]; port = serial->port[i]; - port->read_urb = usb_alloc_urb (0, GFP_KERNEL); + port->read_urb = usb_alloc_urb(0, GFP_KERNEL); if (!port->read_urb) { dev_err(&interface->dev, "No free urbs available\n"); goto probe_error; @@ -815,17 +837,17 @@ int usb_serial_probe(struct usb_interface *interface, buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); port->bulk_in_size = buffer_size; port->bulk_in_endpointAddress = endpoint->bEndpointAddress; - port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL); + port->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); if (!port->bulk_in_buffer) { - dev_err(&interface->dev, "Couldn't allocate bulk_in_buffer\n"); + dev_err(&interface->dev, + "Couldn't allocate bulk_in_buffer\n"); goto probe_error; } - usb_fill_bulk_urb (port->read_urb, dev, - usb_rcvbulkpipe (dev, - endpoint->bEndpointAddress), - port->bulk_in_buffer, buffer_size, - serial->type->read_bulk_callback, - port); + usb_fill_bulk_urb(port->read_urb, dev, + usb_rcvbulkpipe(dev, + endpoint->bEndpointAddress), + port->bulk_in_buffer, buffer_size, + serial->type->read_bulk_callback, port); } for (i = 0; i < num_bulk_out; ++i) { @@ -839,17 +861,17 @@ int usb_serial_probe(struct usb_interface *interface, buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); port->bulk_out_size = buffer_size; port->bulk_out_endpointAddress = endpoint->bEndpointAddress; - port->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL); + port->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL); if (!port->bulk_out_buffer) { - dev_err(&interface->dev, "Couldn't allocate bulk_out_buffer\n"); + dev_err(&interface->dev, + "Couldn't allocate bulk_out_buffer\n"); goto probe_error; } - usb_fill_bulk_urb (port->write_urb, dev, - usb_sndbulkpipe (dev, - endpoint->bEndpointAddress), - port->bulk_out_buffer, buffer_size, - serial->type->write_bulk_callback, - port); + usb_fill_bulk_urb(port->write_urb, dev, + usb_sndbulkpipe(dev, + endpoint->bEndpointAddress), + port->bulk_out_buffer, buffer_size, + serial->type->write_bulk_callback, port); } if (serial->type->read_int_callback) { @@ -858,73 +880,82 @@ int usb_serial_probe(struct usb_interface *interface, port = serial->port[i]; port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); if (!port->interrupt_in_urb) { - dev_err(&interface->dev, "No free urbs available\n"); + dev_err(&interface->dev, + "No free urbs available\n"); goto probe_error; } buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); - port->interrupt_in_endpointAddress = endpoint->bEndpointAddress; - port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL); + port->interrupt_in_endpointAddress = + endpoint->bEndpointAddress; + port->interrupt_in_buffer = kmalloc(buffer_size, + GFP_KERNEL); if (!port->interrupt_in_buffer) { - dev_err(&interface->dev, "Couldn't allocate interrupt_in_buffer\n"); + dev_err(&interface->dev, + "Couldn't allocate interrupt_in_buffer\n"); goto probe_error; } - usb_fill_int_urb (port->interrupt_in_urb, dev, - usb_rcvintpipe (dev, - endpoint->bEndpointAddress), - port->interrupt_in_buffer, buffer_size, - serial->type->read_int_callback, port, - endpoint->bInterval); + usb_fill_int_urb(port->interrupt_in_urb, dev, + usb_rcvintpipe(dev, + endpoint->bEndpointAddress), + port->interrupt_in_buffer, buffer_size, + serial->type->read_int_callback, port, + endpoint->bInterval); } } else if (num_interrupt_in) { dbg("the device claims to support interrupt in transfers, but read_int_callback is not defined"); } - + if (serial->type->write_int_callback) { for (i = 0; i < num_interrupt_out; ++i) { endpoint = interrupt_out_endpoint[i]; port = serial->port[i]; port->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL); if (!port->interrupt_out_urb) { - dev_err(&interface->dev, "No free urbs available\n"); + dev_err(&interface->dev, + "No free urbs available\n"); goto probe_error; } buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); port->interrupt_out_size = buffer_size; - port->interrupt_out_endpointAddress = endpoint->bEndpointAddress; - port->interrupt_out_buffer = kmalloc (buffer_size, GFP_KERNEL); + port->interrupt_out_endpointAddress = + endpoint->bEndpointAddress; + port->interrupt_out_buffer = kmalloc(buffer_size, + GFP_KERNEL); if (!port->interrupt_out_buffer) { - dev_err(&interface->dev, "Couldn't allocate interrupt_out_buffer\n"); + dev_err(&interface->dev, + "Couldn't allocate interrupt_out_buffer\n"); goto probe_error; } - usb_fill_int_urb (port->interrupt_out_urb, dev, - usb_sndintpipe (dev, - endpoint->bEndpointAddress), - port->interrupt_out_buffer, buffer_size, - serial->type->write_int_callback, port, - endpoint->bInterval); + usb_fill_int_urb(port->interrupt_out_urb, dev, + usb_sndintpipe(dev, + endpoint->bEndpointAddress), + port->interrupt_out_buffer, buffer_size, + serial->type->write_int_callback, port, + endpoint->bInterval); } } else if (num_interrupt_out) { dbg("the device claims to support interrupt out transfers, but write_int_callback is not defined"); } - + /* if this device type has an attach function, call it */ if (type->attach) { if (!try_module_get(type->driver.owner)) { - dev_err(&interface->dev, "module get failed, exiting\n"); + dev_err(&interface->dev, + "module get failed, exiting\n"); goto probe_error; } - retval = type->attach (serial); + retval = type->attach(serial); module_put(type->driver.owner); if (retval < 0) goto probe_error; if (retval > 0) { - /* quietly accept this device, but don't bind to a serial port - * as it's about to disappear */ + /* quietly accept this device, but don't bind to a + serial port as it's about to disappear */ goto exit; } } - if (get_free_serial (serial, num_ports, &minor) == NULL) { + if (get_free_serial(serial, num_ports, &minor) == NULL) { dev_err(&interface->dev, "No more free serial devices\n"); goto probe_error; } @@ -938,19 +969,19 @@ int usb_serial_probe(struct usb_interface *interface, port->dev.bus = &usb_serial_bus_type; port->dev.release = &port_release; - snprintf (&port->dev.bus_id[0], sizeof(port->dev.bus_id), "ttyUSB%d", port->number); - dbg ("%s - registering %s", __func__, port->dev.bus_id); + dev_set_name(&port->dev, "ttyUSB%d", port->number); + dbg ("%s - registering %s", __func__, dev_name(&port->dev)); retval = device_register(&port->dev); if (retval) dev_err(&port->dev, "Error registering port device, " "continuing\n"); } - usb_serial_console_init (debug, minor); + usb_serial_console_init(debug, minor); exit: /* success */ - usb_set_intfdata (interface, serial); + usb_set_intfdata(interface, serial); return 0; probe_error: @@ -986,29 +1017,30 @@ probe_error: /* free up any memory that we allocated */ for (i = 0; i < serial->num_port_pointers; ++i) kfree(serial->port[i]); - kfree (serial); + kfree(serial); return -EIO; } +EXPORT_SYMBOL_GPL(usb_serial_probe); void usb_serial_disconnect(struct usb_interface *interface) { int i; - struct usb_serial *serial = usb_get_intfdata (interface); + struct usb_serial *serial = usb_get_intfdata(interface); struct device *dev = &interface->dev; struct usb_serial_port *port; usb_serial_console_disconnect(serial); - dbg ("%s", __func__); + dbg("%s", __func__); mutex_lock(&serial->disc_mutex); - usb_set_intfdata (interface, NULL); + usb_set_intfdata(interface, NULL); /* must set a flag, to signal subdrivers */ serial->disconnected = 1; for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; if (port) { - if (port->tty) - tty_hangup(port->tty); + if (port->port.tty) + tty_hangup(port->port.tty); kill_traffic(port); } } @@ -1018,6 +1050,7 @@ void usb_serial_disconnect(struct usb_interface *interface) usb_serial_put(serial); dev_info(dev, "device disconnected\n"); } +EXPORT_SYMBOL_GPL(usb_serial_disconnect); int usb_serial_suspend(struct usb_interface *intf, pm_message_t message) { @@ -1076,9 +1109,8 @@ static int __init usb_serial_init(void) return -ENOMEM; /* Initialize our global data */ - for (i = 0; i < SERIAL_TTY_MINORS; ++i) { + for (i = 0; i < SERIAL_TTY_MINORS; ++i) serial_table[i] = NULL; - } result = bus_register(&usb_serial_bus_type); if (result) { @@ -1093,9 +1125,11 @@ static int __init usb_serial_init(void) usb_serial_tty_driver->minor_start = 0; usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL; - usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV; usb_serial_tty_driver->init_termios = tty_std_termios; - usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD + | HUPCL | CLOCAL; usb_serial_tty_driver->init_termios.c_ispeed = 9600; usb_serial_tty_driver->init_termios.c_ospeed = 9600; tty_set_operations(usb_serial_tty_driver, &serial_ops); @@ -1133,7 +1167,7 @@ exit_reg_driver: bus_unregister(&usb_serial_bus_type); exit_bus: - err ("%s - returning with error %d", __func__, result); + err("%s - returning with error %d", __func__, result); put_tty_driver(usb_serial_tty_driver); return result; } @@ -1160,7 +1194,7 @@ module_exit(usb_serial_exit); if (!type->function) { \ type->function = usb_serial_generic_##function; \ dbg("Had to override the " #function \ - " usb serial operation with the generic one.");\ + " usb serial operation with the generic one.");\ } \ } while (0) @@ -1177,8 +1211,9 @@ static void fixup_generic(struct usb_serial_driver *device) set_to_generic_if_null(device, resume); } -int usb_serial_register(struct usb_serial_driver *driver) /* must be called with BKL held */ +int usb_serial_register(struct usb_serial_driver *driver) { + /* must be called with BKL held */ int retval; fixup_generic(driver); @@ -1191,37 +1226,30 @@ int usb_serial_register(struct usb_serial_driver *driver) /* must be called with retval = usb_serial_bus_register(driver); if (retval) { - err("problem %d when registering driver %s", retval, driver->description); + err("problem %d when registering driver %s", + retval, driver->description); list_del(&driver->driver_list); - } - else - info("USB Serial support registered for %s", driver->description); + } else + info("USB Serial support registered for %s", + driver->description); return retval; } +EXPORT_SYMBOL_GPL(usb_serial_register); -void usb_serial_deregister(struct usb_serial_driver *device) /* must be called with BKL held */ +void usb_serial_deregister(struct usb_serial_driver *device) { + /* must be called with BKL held */ info("USB Serial deregistering driver %s", device->description); list_del(&device->driver_list); usb_serial_bus_deregister(device); } - - - -/* If the usb-serial core is built into the core, the usb-serial drivers - need these symbols to load properly as modules. */ -EXPORT_SYMBOL_GPL(usb_serial_register); EXPORT_SYMBOL_GPL(usb_serial_deregister); -EXPORT_SYMBOL_GPL(usb_serial_probe); -EXPORT_SYMBOL_GPL(usb_serial_disconnect); -EXPORT_SYMBOL_GPL(usb_serial_port_softint); - /* Module information */ -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); diff --git a/drivers/usb/serial/usb_debug.c b/drivers/usb/serial/usb_debug.c index f9fc926b56d8..fc5d9952b03b 100644 --- a/drivers/usb/serial/usb_debug.c +++ b/drivers/usb/serial/usb_debug.c @@ -15,6 +15,8 @@ #include <linux/usb.h> #include <linux/usb/serial.h> +#define USB_DEBUG_MAX_PACKET_SIZE 8 + static struct usb_device_id id_table [] = { { USB_DEVICE(0x0525, 0x127a) }, { }, @@ -29,6 +31,13 @@ static struct usb_driver debug_driver = { .no_dynamic_id = 1, }; +int usb_debug_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) +{ + port->bulk_out_size = USB_DEBUG_MAX_PACKET_SIZE; + return usb_serial_generic_open(tty, port, filp); +} + static struct usb_serial_driver debug_device = { .driver = { .owner = THIS_MODULE, @@ -36,6 +45,7 @@ static struct usb_serial_driver debug_device = { }, .id_table = id_table, .num_ports = 1, + .open = usb_debug_open, }; static int __init debug_init(void) diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 5fc20122145f..cf8924f9a2cc 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -9,7 +9,8 @@ * modify it under the terms of the GNU General Public License version * 2 as published by the Free Software Foundation. * - * See Documentation/usb/usb-serial.txt for more information on using this driver + * See Documentation/usb/usb-serial.txt for more information on using this + * driver * */ @@ -23,7 +24,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/spinlock.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include "visor.h" @@ -35,25 +36,29 @@ #define DRIVER_DESC "USB HandSpring Visor / Palm OS driver" /* function prototypes for a handspring visor */ -static int visor_open (struct usb_serial_port *port, struct file *filp); -static void visor_close (struct usb_serial_port *port, struct file *filp); -static int visor_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static int visor_write_room (struct usb_serial_port *port); -static int visor_chars_in_buffer (struct usb_serial_port *port); -static void visor_throttle (struct usb_serial_port *port); -static void visor_unthrottle (struct usb_serial_port *port); -static int visor_probe (struct usb_serial *serial, const struct usb_device_id *id); +static int visor_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); +static void visor_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); +static int visor_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); +static int visor_write_room(struct tty_struct *tty); +static void visor_throttle(struct tty_struct *tty); +static void visor_unthrottle(struct tty_struct *tty); +static int visor_probe(struct usb_serial *serial, + const struct usb_device_id *id); static int visor_calc_num_ports(struct usb_serial *serial); -static void visor_shutdown (struct usb_serial *serial); -static int visor_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); -static void visor_write_bulk_callback (struct urb *urb); -static void visor_read_bulk_callback (struct urb *urb); -static void visor_read_int_callback (struct urb *urb); -static int clie_3_5_startup (struct usb_serial *serial); -static int treo_attach (struct usb_serial *serial); -static int clie_5_attach (struct usb_serial *serial); -static int palm_os_3_probe (struct usb_serial *serial, const struct usb_device_id *id); -static int palm_os_4_probe (struct usb_serial *serial, const struct usb_device_id *id); +static void visor_shutdown(struct usb_serial *serial); +static void visor_write_bulk_callback(struct urb *urb); +static void visor_read_bulk_callback(struct urb *urb); +static void visor_read_int_callback(struct urb *urb); +static int clie_3_5_startup(struct usb_serial *serial); +static int treo_attach(struct usb_serial *serial); +static int clie_5_attach(struct usb_serial *serial); +static int palm_os_3_probe(struct usb_serial *serial, + const struct usb_device_id *id); +static int palm_os_4_probe(struct usb_serial *serial, + const struct usb_device_id *id); /* Parameters that may be passed into the module. */ static int debug; @@ -105,13 +110,13 @@ static struct usb_device_id id_table [] = { .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(ACER_VENDOR_ID, ACER_S10_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, - { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID), + { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, - { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SPH_I500_ID), + { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SPH_I500_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(TAPWAVE_VENDOR_ID, TAPWAVE_ZODIAC_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, - { USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_IQUE_3600_ID), + { USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_IQUE_3600_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(ACEECA_VENDOR_ID, ACEECA_MEZ1000_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, @@ -170,7 +175,7 @@ static struct usb_device_id id_table_combined [] = { { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table_combined); +MODULE_DEVICE_TABLE(usb, id_table_combined); static struct usb_driver visor_driver = { .name = "visor", @@ -180,7 +185,8 @@ static struct usb_driver visor_driver = { .no_dynamic_id = 1, }; -/* All of the device info needed for the Handspring Visor, and Palm 4.0 devices */ +/* All of the device info needed for the Handspring Visor, + and Palm 4.0 devices */ static struct usb_serial_driver handspring_device = { .driver = { .owner = THIS_MODULE, @@ -198,10 +204,8 @@ static struct usb_serial_driver handspring_device = { .probe = visor_probe, .calc_num_ports = visor_calc_num_ports, .shutdown = visor_shutdown, - .ioctl = visor_ioctl, .write = visor_write, .write_room = visor_write_room, - .chars_in_buffer = visor_chars_in_buffer, .write_bulk_callback = visor_write_bulk_callback, .read_bulk_callback = visor_read_bulk_callback, .read_int_callback = visor_read_int_callback, @@ -225,10 +229,8 @@ static struct usb_serial_driver clie_5_device = { .probe = visor_probe, .calc_num_ports = visor_calc_num_ports, .shutdown = visor_shutdown, - .ioctl = visor_ioctl, .write = visor_write, .write_room = visor_write_room, - .chars_in_buffer = visor_chars_in_buffer, .write_bulk_callback = visor_write_bulk_callback, .read_bulk_callback = visor_read_bulk_callback, .read_int_callback = visor_read_int_callback, @@ -249,10 +251,8 @@ static struct usb_serial_driver clie_3_5_device = { .throttle = visor_throttle, .unthrottle = visor_unthrottle, .attach = clie_3_5_startup, - .ioctl = visor_ioctl, .write = visor_write, .write_room = visor_write_room, - .chars_in_buffer = visor_chars_in_buffer, .write_bulk_callback = visor_write_bulk_callback, .read_bulk_callback = visor_read_bulk_callback, }; @@ -274,7 +274,8 @@ static int stats; /****************************************************************************** * Handspring Visor specific driver functions ******************************************************************************/ -static int visor_open (struct usb_serial_port *port, struct file *filp) +static int visor_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { struct usb_serial *serial = port->serial; struct visor_private *priv = usb_get_serial_port_data(port); @@ -300,42 +301,45 @@ static int visor_open (struct usb_serial_port *port, struct file *filp) * through, otherwise it is scheduled, and with high data rates (like * with OHCI) data can get lost. */ - if (port->tty) - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; /* Start reading from the device */ - usb_fill_bulk_urb (port->read_urb, serial->dev, - usb_rcvbulkpipe (serial->dev, + usb_fill_bulk_urb(port->read_urb, serial->dev, + usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, visor_read_bulk_callback, port); result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result) { - dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", - __func__, result); + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", + __func__, result); goto exit; } - + if (port->interrupt_in_urb) { dbg("%s - adding interrupt input for treo", __func__); result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (result) - dev_err(&port->dev, "%s - failed submitting interrupt urb, error %d\n", - __func__, result); + dev_err(&port->dev, + "%s - failed submitting interrupt urb, error %d\n", + __func__, result); } -exit: +exit: return result; } -static void visor_close (struct usb_serial_port *port, struct file * filp) +static void visor_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct visor_private *priv = usb_get_serial_port_data(port); unsigned char *transfer_buffer; dbg("%s - port %d", __func__, port->number); - + /* shutdown our urbs */ usb_kill_urb(port->read_urb); usb_kill_urb(port->interrupt_in_urb); @@ -343,14 +347,14 @@ static void visor_close (struct usb_serial_port *port, struct file * filp) mutex_lock(&port->serial->disc_mutex); if (!port->serial->disconnected) { /* Try to send shutdown message, unless the device is gone */ - transfer_buffer = kmalloc (0x12, GFP_KERNEL); + transfer_buffer = kmalloc(0x12, GFP_KERNEL); if (transfer_buffer) { - usb_control_msg (port->serial->dev, + usb_control_msg(port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0), VISOR_CLOSE_NOTIFICATION, 0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300); - kfree (transfer_buffer); + kfree(transfer_buffer); } } mutex_unlock(&port->serial->disc_mutex); @@ -361,7 +365,8 @@ static void visor_close (struct usb_serial_port *port, struct file * filp) } -static int visor_write (struct usb_serial_port *port, const unsigned char *buf, int count) +static int visor_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { struct visor_private *priv = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; @@ -381,7 +386,7 @@ static int visor_write (struct usb_serial_port *port, const unsigned char *buf, priv->outstanding_urbs++; spin_unlock_irqrestore(&priv->lock, flags); - buffer = kmalloc (count, GFP_ATOMIC); + buffer = kmalloc(count, GFP_ATOMIC); if (!buffer) { dev_err(&port->dev, "out of memory\n"); count = -ENOMEM; @@ -395,21 +400,22 @@ static int visor_write (struct usb_serial_port *port, const unsigned char *buf, goto error_no_urb; } - memcpy (buffer, buf, count); + memcpy(buffer, buf, count); usb_serial_debug_data(debug, &port->dev, __func__, count, buffer); - usb_fill_bulk_urb (urb, serial->dev, - usb_sndbulkpipe (serial->dev, + usb_fill_bulk_urb(urb, serial->dev, + usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress), - buffer, count, + buffer, count, visor_write_bulk_callback, port); /* send it down the pipe */ status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { - dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed with status = %d\n", - __func__, status); + dev_err(&port->dev, + "%s - usb_submit_urb(write bulk) failed with status = %d\n", + __func__, status); count = status; goto error; } else { @@ -435,8 +441,9 @@ error_no_buffer: } -static int visor_write_room (struct usb_serial_port *port) +static int visor_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct visor_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -460,23 +467,7 @@ static int visor_write_room (struct usb_serial_port *port) } -static int visor_chars_in_buffer (struct usb_serial_port *port) -{ - dbg("%s - port %d", __func__, port->number); - - /* - * We can't really account for how much data we - * have sent out, but hasn't made it through to the - * device, so just tell the tty layer that everything - * is flushed. - * - * FIXME: Should walk outstanding_urbs - */ - return 0; -} - - -static void visor_write_bulk_callback (struct urb *urb) +static void visor_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct visor_private *priv = usb_get_serial_port_data(port); @@ -484,7 +475,7 @@ static void visor_write_bulk_callback (struct urb *urb) unsigned long flags; /* free up the transfer buffer, as usb_free_urb() does not do this */ - kfree (urb->transfer_buffer); + kfree(urb->transfer_buffer); dbg("%s - port %d", __func__, port->number); @@ -500,7 +491,7 @@ static void visor_write_bulk_callback (struct urb *urb) } -static void visor_read_bulk_callback (struct urb *urb) +static void visor_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct visor_private *priv = usb_get_serial_port_data(port); @@ -518,11 +509,13 @@ static void visor_read_bulk_callback (struct urb *urb) return; } - usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, data); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { - available_room = tty_buffer_request_room(tty, urb->actual_length); + available_room = tty_buffer_request_room(tty, + urb->actual_length); if (available_room) { tty_insert_flip_string(tty, data, available_room); tty_flip_buffer_push(tty); @@ -536,22 +529,23 @@ static void visor_read_bulk_callback (struct urb *urb) /* Continue trying to always read if we should */ if (!priv->throttled) { - usb_fill_bulk_urb (port->read_urb, port->serial->dev, + usb_fill_bulk_urb(port->read_urb, port->serial->dev, usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), + port->bulk_in_endpointAddress), port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, visor_read_bulk_callback, port); result = usb_submit_urb(port->read_urb, GFP_ATOMIC); if (result) - dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __func__, result); - } else { + dev_err(&port->dev, + "%s - failed resubmitting read urb, error %d\n", + __func__, result); + } else priv->actually_throttled = 1; - } spin_unlock(&priv->lock); } -static void visor_read_int_callback (struct urb *urb) +static void visor_read_int_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; int status = urb->status; @@ -585,14 +579,16 @@ static void visor_read_int_callback (struct urb *urb) urb->actual_length, urb->transfer_buffer); exit: - result = usb_submit_urb (urb, GFP_ATOMIC); + result = usb_submit_urb(urb, GFP_ATOMIC); if (result) - dev_err(&urb->dev->dev, "%s - Error %d submitting interrupt urb\n", - __func__, result); + dev_err(&urb->dev->dev, + "%s - Error %d submitting interrupt urb\n", + __func__, result); } -static void visor_throttle (struct usb_serial_port *port) +static void visor_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct visor_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -603,8 +599,9 @@ static void visor_throttle (struct usb_serial_port *port) } -static void visor_unthrottle (struct usb_serial_port *port) +static void visor_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct visor_private *priv = usb_get_serial_port_data(port); unsigned long flags; int result; @@ -618,10 +615,13 @@ static void visor_unthrottle (struct usb_serial_port *port) port->read_urb->dev = port->serial->dev; result = usb_submit_urb(port->read_urb, GFP_ATOMIC); if (result) - dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __func__, result); + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", + __func__, result); } -static int palm_os_3_probe (struct usb_serial *serial, const struct usb_device_id *id) +static int palm_os_3_probe(struct usb_serial *serial, + const struct usb_device_id *id) { struct device *dev = &serial->dev->dev; struct visor_connection_info *connection_info; @@ -633,7 +633,7 @@ static int palm_os_3_probe (struct usb_serial *serial, const struct usb_device_i dbg("%s", __func__); - transfer_buffer = kmalloc (sizeof (*connection_info), GFP_KERNEL); + transfer_buffer = kmalloc(sizeof(*connection_info), GFP_KERNEL); if (!transfer_buffer) { dev_err(dev, "%s - kmalloc(%Zd) failed.\n", __func__, sizeof(*connection_info)); @@ -641,7 +641,7 @@ static int palm_os_3_probe (struct usb_serial *serial, const struct usb_device_i } /* send a get connection info request */ - retval = usb_control_msg (serial->dev, + retval = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_GET_CONNECTION_INFORMATION, 0xc2, 0x0000, 0x0000, transfer_buffer, @@ -653,29 +653,31 @@ static int palm_os_3_probe (struct usb_serial *serial, const struct usb_device_i } if (retval == sizeof(*connection_info)) { - connection_info = (struct visor_connection_info *)transfer_buffer; + connection_info = (struct visor_connection_info *) + transfer_buffer; num_ports = le16_to_cpu(connection_info->num_ports); for (i = 0; i < num_ports; ++i) { - switch (connection_info->connections[i].port_function_id) { - case VISOR_FUNCTION_GENERIC: - string = "Generic"; - break; - case VISOR_FUNCTION_DEBUGGER: - string = "Debugger"; - break; - case VISOR_FUNCTION_HOTSYNC: - string = "HotSync"; - break; - case VISOR_FUNCTION_CONSOLE: - string = "Console"; - break; - case VISOR_FUNCTION_REMOTE_FILE_SYS: - string = "Remote File System"; - break; - default: - string = "unknown"; - break; + switch ( + connection_info->connections[i].port_function_id) { + case VISOR_FUNCTION_GENERIC: + string = "Generic"; + break; + case VISOR_FUNCTION_DEBUGGER: + string = "Debugger"; + break; + case VISOR_FUNCTION_HOTSYNC: + string = "HotSync"; + break; + case VISOR_FUNCTION_CONSOLE: + string = "Console"; + break; + case VISOR_FUNCTION_REMOTE_FILE_SYS: + string = "Remote File System"; + break; + default: + string = "unknown"; + break; } dev_info(dev, "%s: port %d, is for %s use\n", serial->type->description, @@ -686,11 +688,11 @@ static int palm_os_3_probe (struct usb_serial *serial, const struct usb_device_i * Handle devices that report invalid stuff here. */ if (num_ports == 0 || num_ports > 2) { - dev_warn (dev, "%s: No valid connect info available\n", + dev_warn(dev, "%s: No valid connect info available\n", serial->type->description); num_ports = 2; } - + dev_info(dev, "%s: Number of ports: %d\n", serial->type->description, num_ports); @@ -700,8 +702,9 @@ static int palm_os_3_probe (struct usb_serial *serial, const struct usb_device_i */ usb_set_serial_data(serial, (void *)(long)num_ports); - /* ask for the number of bytes available, but ignore the response as it is broken */ - retval = usb_control_msg (serial->dev, + /* ask for the number of bytes available, but ignore the + response as it is broken */ + retval = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_REQUEST_BYTES_AVAILABLE, 0xc2, 0x0000, 0x0005, transfer_buffer, @@ -712,12 +715,13 @@ static int palm_os_3_probe (struct usb_serial *serial, const struct usb_device_i retval = 0; exit: - kfree (transfer_buffer); + kfree(transfer_buffer); return retval; } -static int palm_os_4_probe (struct usb_serial *serial, const struct usb_device_id *id) +static int palm_os_4_probe(struct usb_serial *serial, + const struct usb_device_id *id) { struct device *dev = &serial->dev->dev; struct palm_ext_connection_info *connection_info; @@ -726,18 +730,18 @@ static int palm_os_4_probe (struct usb_serial *serial, const struct usb_device_i dbg("%s", __func__); - transfer_buffer = kmalloc (sizeof (*connection_info), GFP_KERNEL); + transfer_buffer = kmalloc(sizeof(*connection_info), GFP_KERNEL); if (!transfer_buffer) { dev_err(dev, "%s - kmalloc(%Zd) failed.\n", __func__, sizeof(*connection_info)); return -ENOMEM; } - retval = usb_control_msg (serial->dev, - usb_rcvctrlpipe(serial->dev, 0), + retval = usb_control_msg(serial->dev, + usb_rcvctrlpipe(serial->dev, 0), PALM_GET_EXT_CONNECTION_INFORMATION, 0xc2, 0x0000, 0x0000, transfer_buffer, - sizeof (*connection_info), 300); + sizeof(*connection_info), 300); if (retval < 0) dev_err(dev, "%s - error %d getting connection info\n", __func__, retval); @@ -745,15 +749,17 @@ static int palm_os_4_probe (struct usb_serial *serial, const struct usb_device_i usb_serial_debug_data(debug, &serial->dev->dev, __func__, retval, transfer_buffer); - kfree (transfer_buffer); + kfree(transfer_buffer); return 0; } -static int visor_probe (struct usb_serial *serial, const struct usb_device_id *id) +static int visor_probe(struct usb_serial *serial, + const struct usb_device_id *id) { int retval = 0; - int (*startup) (struct usb_serial *serial, const struct usb_device_id *id); + int (*startup)(struct usb_serial *serial, + const struct usb_device_id *id); dbg("%s", __func__); @@ -771,7 +777,7 @@ static int visor_probe (struct usb_serial *serial, const struct usb_device_id *i return retval; } -static int visor_calc_num_ports (struct usb_serial *serial) +static int visor_calc_num_ports(struct usb_serial *serial) { int num_ports = (int)(long)(usb_get_serial_data(serial)); @@ -788,7 +794,7 @@ static int generic_startup(struct usb_serial *serial) int i; for (i = 0; i < serial->num_ports; ++i) { - priv = kzalloc (sizeof(*priv), GFP_KERNEL); + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { while (i-- != 0) { priv = usb_get_serial_port_data(ports[i]); @@ -803,7 +809,7 @@ static int generic_startup(struct usb_serial *serial) return 0; } -static int clie_3_5_startup (struct usb_serial *serial) +static int clie_3_5_startup(struct usb_serial *serial) { struct device *dev = &serial->dev->dev; int result; @@ -816,62 +822,72 @@ static int clie_3_5_startup (struct usb_serial *serial) */ /* get the config number */ - result = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), + result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), USB_REQ_GET_CONFIGURATION, USB_DIR_IN, 0, 0, &data, 1, 3000); if (result < 0) { - dev_err(dev, "%s: get config number failed: %d\n", __func__, result); + dev_err(dev, "%s: get config number failed: %d\n", + __func__, result); return result; } if (result != 1) { - dev_err(dev, "%s: get config number bad return length: %d\n", __func__, result); + dev_err(dev, "%s: get config number bad return length: %d\n", + __func__, result); return -EIO; } /* get the interface number */ - result = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), - USB_REQ_GET_INTERFACE, + result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + USB_REQ_GET_INTERFACE, USB_DIR_IN | USB_RECIP_INTERFACE, 0, 0, &data, 1, 3000); if (result < 0) { - dev_err(dev, "%s: get interface number failed: %d\n", __func__, result); + dev_err(dev, "%s: get interface number failed: %d\n", + __func__, result); return result; } if (result != 1) { - dev_err(dev, "%s: get interface number bad return length: %d\n", __func__, result); + dev_err(dev, + "%s: get interface number bad return length: %d\n", + __func__, result); return -EIO; } return generic_startup(serial); } - -static int treo_attach (struct usb_serial *serial) + +static int treo_attach(struct usb_serial *serial) { struct usb_serial_port *swap_port; /* Only do this endpoint hack for the Handspring devices with * interrupt in endpoints, which for now are the Treo devices. */ - if (!((le16_to_cpu(serial->dev->descriptor.idVendor) == HANDSPRING_VENDOR_ID) || - (le16_to_cpu(serial->dev->descriptor.idVendor) == KYOCERA_VENDOR_ID)) || - (serial->num_interrupt_in == 0)) + if (!((le16_to_cpu(serial->dev->descriptor.idVendor) + == HANDSPRING_VENDOR_ID) || + (le16_to_cpu(serial->dev->descriptor.idVendor) + == KYOCERA_VENDOR_ID)) || + (serial->num_interrupt_in == 0)) goto generic_startup; dbg("%s", __func__); /* - * It appears that Treos and Kyoceras want to use the - * 1st bulk in endpoint to communicate with the 2nd bulk out endpoint, - * so let's swap the 1st and 2nd bulk in and interrupt endpoints. - * Note that swapping the bulk out endpoints would break lots of + * It appears that Treos and Kyoceras want to use the + * 1st bulk in endpoint to communicate with the 2nd bulk out endpoint, + * so let's swap the 1st and 2nd bulk in and interrupt endpoints. + * Note that swapping the bulk out endpoints would break lots of * apps that want to communicate on the second port. */ #define COPY_PORT(dest, src) \ - dest->read_urb = src->read_urb; \ - dest->bulk_in_endpointAddress = src->bulk_in_endpointAddress; \ - dest->bulk_in_buffer = src->bulk_in_buffer; \ - dest->interrupt_in_urb = src->interrupt_in_urb; \ - dest->interrupt_in_endpointAddress = src->interrupt_in_endpointAddress; \ - dest->interrupt_in_buffer = src->interrupt_in_buffer; + do { \ + dest->read_urb = src->read_urb; \ + dest->bulk_in_endpointAddress = src->bulk_in_endpointAddress;\ + dest->bulk_in_buffer = src->bulk_in_buffer; \ + dest->interrupt_in_urb = src->interrupt_in_urb; \ + dest->interrupt_in_endpointAddress = \ + src->interrupt_in_endpointAddress;\ + dest->interrupt_in_buffer = src->interrupt_in_buffer; \ + } while (0); swap_port = kmalloc(sizeof(*swap_port), GFP_KERNEL); if (!swap_port) @@ -885,28 +901,30 @@ generic_startup: return generic_startup(serial); } -static int clie_5_attach (struct usb_serial *serial) +static int clie_5_attach(struct usb_serial *serial) { dbg("%s", __func__); - /* TH55 registers 2 ports. - Communication in from the UX50/TH55 uses bulk_in_endpointAddress from port 0 - Communication out to the UX50/TH55 uses bulk_out_endpointAddress from port 1 - + /* TH55 registers 2 ports. + Communication in from the UX50/TH55 uses bulk_in_endpointAddress + from port 0. Communication out to the UX50/TH55 uses + bulk_out_endpointAddress from port 1 + Lets do a quick and dirty mapping */ - + /* some sanity check */ if (serial->num_ports < 2) return -1; - + /* port 0 now uses the modified endpoint Address */ - serial->port[0]->bulk_out_endpointAddress = serial->port[1]->bulk_out_endpointAddress; + serial->port[0]->bulk_out_endpointAddress = + serial->port[1]->bulk_out_endpointAddress; return generic_startup(serial); } -static void visor_shutdown (struct usb_serial *serial) +static void visor_shutdown(struct usb_serial *serial) { struct visor_private *priv; int i; @@ -922,37 +940,35 @@ static void visor_shutdown (struct usb_serial *serial) } } -static int visor_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) -{ - dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd); - - return -ENOIOCTLCMD; -} - -static int __init visor_init (void) +static int __init visor_init(void) { int i, retval; /* Only if parameters were passed to us */ - if ((vendor>0) && (product>0)) { - struct usb_device_id usb_dev_temp[]= - {{USB_DEVICE(vendor, product), - .driver_info = (kernel_ulong_t)&palm_os_4_probe }}; + if (vendor > 0 && product > 0) { + struct usb_device_id usb_dev_temp[] = { + { + USB_DEVICE(vendor, product), + .driver_info = + (kernel_ulong_t) &palm_os_4_probe + } + }; /* Find the last entry in id_table */ - for (i=0; ; i++) { - if (id_table[i].idVendor==0) { + for (i = 0;; i++) { + if (id_table[i].idVendor == 0) { id_table[i] = usb_dev_temp[0]; break; } } /* Find the last entry in id_table_combined */ - for (i=0; ; i++) { - if (id_table_combined[i].idVendor==0) { + for (i = 0;; i++) { + if (id_table_combined[i].idVendor == 0) { id_table_combined[i] = usb_dev_temp[0]; break; } } - info("Untested USB device specified at time of module insertion"); + info( + "Untested USB device specified at time of module insertion"); info("Warning: This is not guaranteed to work"); info("Using a newer kernel is preferred to this method"); info("Adding Palm OS protocol 4.x support for unknown device: 0x%x/0x%x", @@ -968,7 +984,7 @@ static int __init visor_init (void) if (retval) goto failed_clie_5_register; retval = usb_register(&visor_driver); - if (retval) + if (retval) goto failed_usb_register; info(DRIVER_DESC); @@ -986,18 +1002,18 @@ failed_handspring_register: static void __exit visor_exit (void) { - usb_deregister (&visor_driver); - usb_serial_deregister (&handspring_device); - usb_serial_deregister (&clie_3_5_device); - usb_serial_deregister (&clie_5_device); + usb_deregister(&visor_driver); + usb_serial_deregister(&handspring_device); + usb_serial_deregister(&clie_3_5_device); + usb_serial_deregister(&clie_5_device); } module_init(visor_init); module_exit(visor_exit); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 665aa77a917b..3a9d14384a43 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -12,29 +12,31 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * See Documentation/usb/usb-serial.txt for more information on using this driver + * See Documentation/usb/usb-serial.txt for more information on using this + * driver * * (10/09/2002) Stuart MacDonald (stuartm@connecttech.com) * Upgrade to full working driver * * (05/30/2001) gkh - * switched from using spinlock to a semaphore, which fixes lots of problems. + * switched from using spinlock to a semaphore, which fixes lots of + * problems. * * (04/08/2001) gb * Identify version on module load. - * + * * 2001_Mar_19 gkh - * Fixed MOD_INC and MOD_DEC logic, the ability to open a port more + * Fixed MOD_INC and MOD_DEC logic, the ability to open a port more * than once, and the got the proper usb_device_id table entries so * the driver works again. * * (11/01/2000) Adam J. Richter * usb_device_id table support - * + * * (10/05/2000) gkh * Fixed bug with urb->dev not being set properly, now that the usb * core needs it. - * + * * (10/03/2000) smd * firmware is improved to guard against crap sent to device * firmware now replies CMD_FAILURE on bad things @@ -52,9 +54,9 @@ * Fixed bug with port->minor that was found by Al Borchers * * (07/04/2000) gkh - * Added support for port settings. Baud rate can now be changed. Line signals - * are not transferred to and from the tty layer yet, but things seem to be - * working well now. + * Added support for port settings. Baud rate can now be changed. Line + * signals are not transferred to and from the tty layer yet, but things + * seem to be working well now. * * (05/04/2000) gkh * First cut at open and close commands. Data can flow through the ports at @@ -62,7 +64,7 @@ * * (03/26/2000) gkh * Split driver up into device specific pieces. - * + * */ #include <linux/kernel.h> @@ -75,7 +77,7 @@ #include <linux/module.h> #include <linux/spinlock.h> #include <linux/mutex.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <asm/termbits.h> #include <linux/usb.h> #include <linux/serial_reg.h> @@ -125,7 +127,7 @@ static struct usb_device_id id_table_combined [] = { { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table_combined); +MODULE_DEVICE_TABLE(usb, id_table_combined); static struct usb_driver whiteheat_driver = { .name = "whiteheat", @@ -136,26 +138,34 @@ static struct usb_driver whiteheat_driver = { }; /* function prototypes for the Connect Tech WhiteHEAT prerenumeration device */ -static int whiteheat_firmware_download (struct usb_serial *serial, const struct usb_device_id *id); -static int whiteheat_firmware_attach (struct usb_serial *serial); +static int whiteheat_firmware_download(struct usb_serial *serial, + const struct usb_device_id *id); +static int whiteheat_firmware_attach(struct usb_serial *serial); /* function prototypes for the Connect Tech WhiteHEAT serial converter */ -static int whiteheat_attach (struct usb_serial *serial); -static void whiteheat_shutdown (struct usb_serial *serial); -static int whiteheat_open (struct usb_serial_port *port, struct file *filp); -static void whiteheat_close (struct usb_serial_port *port, struct file *filp); -static int whiteheat_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static int whiteheat_write_room (struct usb_serial_port *port); -static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); -static void whiteheat_set_termios (struct usb_serial_port *port, struct ktermios * old); -static int whiteheat_tiocmget (struct usb_serial_port *port, struct file *file); -static int whiteheat_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); -static void whiteheat_break_ctl (struct usb_serial_port *port, int break_state); -static int whiteheat_chars_in_buffer (struct usb_serial_port *port); -static void whiteheat_throttle (struct usb_serial_port *port); -static void whiteheat_unthrottle (struct usb_serial_port *port); -static void whiteheat_read_callback (struct urb *urb); -static void whiteheat_write_callback (struct urb *urb); +static int whiteheat_attach(struct usb_serial *serial); +static void whiteheat_shutdown(struct usb_serial *serial); +static int whiteheat_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void whiteheat_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static int whiteheat_write(struct tty_struct *tty, + struct usb_serial_port *port, + const unsigned char *buf, int count); +static int whiteheat_write_room(struct tty_struct *tty); +static int whiteheat_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg); +static void whiteheat_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); +static int whiteheat_tiocmget(struct tty_struct *tty, struct file *file); +static int whiteheat_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear); +static void whiteheat_break_ctl(struct tty_struct *tty, int break_state); +static int whiteheat_chars_in_buffer(struct tty_struct *tty); +static void whiteheat_throttle(struct tty_struct *tty); +static void whiteheat_unthrottle(struct tty_struct *tty); +static void whiteheat_read_callback(struct urb *urb); +static void whiteheat_write_callback(struct urb *urb); static struct usb_serial_driver whiteheat_fake_device = { .driver = { @@ -202,7 +212,9 @@ struct whiteheat_command_private { struct mutex mutex; __u8 port_running; __u8 command_finished; - wait_queue_head_t wait_command; /* for handling sleeping while waiting for a command to finish */ + wait_queue_head_t wait_command; /* for handling sleeping whilst + waiting for a command to + finish */ __u8 result_buffer[64]; }; @@ -239,14 +251,16 @@ static void command_port_write_callback(struct urb *urb); static void command_port_read_callback(struct urb *urb); static int start_port_read(struct usb_serial_port *port); -static struct whiteheat_urb_wrap *urb_to_wrap(struct urb *urb, struct list_head *head); +static struct whiteheat_urb_wrap *urb_to_wrap(struct urb *urb, + struct list_head *head); static struct list_head *list_first(struct list_head *head); static void rx_data_softint(struct work_struct *work); -static int firm_send_command(struct usb_serial_port *port, __u8 command, __u8 *data, __u8 datasize); +static int firm_send_command(struct usb_serial_port *port, __u8 command, + __u8 *data, __u8 datasize); static int firm_open(struct usb_serial_port *port); static int firm_close(struct usb_serial_port *port); -static int firm_setup_port(struct usb_serial_port *port); +static int firm_setup_port(struct tty_struct *tty); static int firm_set_rts(struct usb_serial_port *port, __u8 onoff); static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff); static int firm_set_break(struct usb_serial_port *port, __u8 onoff); @@ -278,7 +292,8 @@ static int firm_report_tx_done(struct usb_serial_port *port); - device renumerated itself and comes up as new device id with all firmware download completed. */ -static int whiteheat_firmware_download (struct usb_serial *serial, const struct usb_device_id *id) +static int whiteheat_firmware_download(struct usb_serial *serial, + const struct usb_device_id *id) { int response, ret = -ENOENT; const struct firmware *loader_fw = NULL, *firmware_fw = NULL; @@ -313,7 +328,7 @@ static int whiteheat_firmware_download (struct usb_serial *serial, const struct record = ihex_next_binrec(record); } - response = ezusb_set_reset (serial, 0); + response = ezusb_set_reset(serial, 0); record = (const struct ihex_binrec *)firmware_fw->data; while (record && be32_to_cpu(record->addr) < 0x1b40) @@ -330,8 +345,8 @@ static int whiteheat_firmware_download (struct usb_serial *serial, const struct } ++record; } - - response = ezusb_set_reset (serial, 1); + + response = ezusb_set_reset(serial, 1); record = (const struct ihex_binrec *)firmware_fw->data; while (record && be32_to_cpu(record->addr) < 0x1b40) { @@ -355,7 +370,7 @@ static int whiteheat_firmware_download (struct usb_serial *serial, const struct } -static int whiteheat_firmware_attach (struct usb_serial *serial) +static int whiteheat_firmware_attach(struct usb_serial *serial) { /* We want this device to fail to have a driver assigned to it */ return 1; @@ -365,7 +380,7 @@ static int whiteheat_firmware_attach (struct usb_serial *serial) /***************************************************************************** * Connect Tech's White Heat serial driver functions *****************************************************************************/ -static int whiteheat_attach (struct usb_serial *serial) +static int whiteheat_attach(struct usb_serial *serial) { struct usb_serial_port *command_port; struct whiteheat_command_private *command_info; @@ -386,43 +401,52 @@ static int whiteheat_attach (struct usb_serial *serial) command_port = serial->port[COMMAND_PORT]; - pipe = usb_sndbulkpipe (serial->dev, command_port->bulk_out_endpointAddress); + pipe = usb_sndbulkpipe(serial->dev, + command_port->bulk_out_endpointAddress); command = kmalloc(2, GFP_KERNEL); if (!command) goto no_command_buffer; command[0] = WHITEHEAT_GET_HW_INFO; command[1] = 0; - + result = kmalloc(sizeof(*hw_info) + 1, GFP_KERNEL); if (!result) goto no_result_buffer; /* * When the module is reloaded the firmware is still there and * the endpoints are still in the usb core unchanged. This is the - * unlinking bug in disguise. Same for the call below. - */ + * unlinking bug in disguise. Same for the call below. + */ usb_clear_halt(serial->dev, pipe); - ret = usb_bulk_msg (serial->dev, pipe, command, 2, &alen, COMMAND_TIMEOUT_MS); + ret = usb_bulk_msg(serial->dev, pipe, command, 2, + &alen, COMMAND_TIMEOUT_MS); if (ret) { - err("%s: Couldn't send command [%d]", serial->type->description, ret); + err("%s: Couldn't send command [%d]", + serial->type->description, ret); goto no_firmware; } else if (alen != 2) { - err("%s: Send command incomplete [%d]", serial->type->description, alen); + err("%s: Send command incomplete [%d]", + serial->type->description, alen); goto no_firmware; } - pipe = usb_rcvbulkpipe (serial->dev, command_port->bulk_in_endpointAddress); + pipe = usb_rcvbulkpipe(serial->dev, + command_port->bulk_in_endpointAddress); /* See the comment on the usb_clear_halt() above */ usb_clear_halt(serial->dev, pipe); - ret = usb_bulk_msg (serial->dev, pipe, result, sizeof(*hw_info) + 1, &alen, COMMAND_TIMEOUT_MS); + ret = usb_bulk_msg(serial->dev, pipe, result, + sizeof(*hw_info) + 1, &alen, COMMAND_TIMEOUT_MS); if (ret) { - err("%s: Couldn't get results [%d]", serial->type->description, ret); + err("%s: Couldn't get results [%d]", + serial->type->description, ret); goto no_firmware; } else if (alen != sizeof(*hw_info) + 1) { - err("%s: Get results incomplete [%d]", serial->type->description, alen); + err("%s: Get results incomplete [%d]", + serial->type->description, alen); goto no_firmware; } else if (result[0] != command[0]) { - err("%s: Command failed [%d]", serial->type->description, result[0]); + err("%s: Command failed [%d]", + serial->type->description, result[0]); goto no_firmware; } @@ -436,7 +460,8 @@ static int whiteheat_attach (struct usb_serial *serial) info = kmalloc(sizeof(struct whiteheat_private), GFP_KERNEL); if (info == NULL) { - err("%s: Out of memory for port structures\n", serial->type->description); + err("%s: Out of memory for port structures\n", + serial->type->description); goto no_private; } @@ -506,9 +531,11 @@ static int whiteheat_attach (struct usb_serial *serial) usb_set_serial_port_data(port, info); } - command_info = kmalloc(sizeof(struct whiteheat_command_private), GFP_KERNEL); + command_info = kmalloc(sizeof(struct whiteheat_command_private), + GFP_KERNEL); if (command_info == NULL) { - err("%s: Out of memory for port structures\n", serial->type->description); + err("%s: Out of memory for port structures\n", + serial->type->description); goto no_command_private; } @@ -525,9 +552,12 @@ static int whiteheat_attach (struct usb_serial *serial) no_firmware: /* Firmware likely not running */ - err("%s: Unable to retrieve firmware version, try replugging\n", serial->type->description); - err("%s: If the firmware is not running (status led not blinking)\n", serial->type->description); - err("%s: please contact support@connecttech.com\n", serial->type->description); + err("%s: Unable to retrieve firmware version, try replugging\n", + serial->type->description); + err("%s: If the firmware is not running (status led not blinking)\n", + serial->type->description); + err("%s: please contact support@connecttech.com\n", + serial->type->description); kfree(result); return -ENODEV; @@ -570,7 +600,7 @@ no_command_buffer: } -static void whiteheat_shutdown (struct usb_serial *serial) +static void whiteheat_shutdown(struct usb_serial *serial) { struct usb_serial_port *command_port; struct usb_serial_port *port; @@ -585,7 +615,7 @@ static void whiteheat_shutdown (struct usb_serial *serial) /* free up our private data for our command port */ command_port = serial->port[COMMAND_PORT]; - kfree (usb_get_serial_port_data(command_port)); + kfree(usb_get_serial_port_data(command_port)); for (i = 0; i < serial->num_ports; i++) { port = serial->port[i]; @@ -612,11 +642,10 @@ static void whiteheat_shutdown (struct usb_serial *serial) return; } - -static int whiteheat_open (struct usb_serial_port *port, struct file *filp) +static int whiteheat_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int retval = 0; - struct ktermios old_term; dbg("%s - port %d", __func__, port->number); @@ -624,7 +653,8 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp) if (retval) goto exit; - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; /* send an open port command */ retval = firm_open(port); @@ -640,9 +670,8 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp) goto exit; } - old_term.c_cflag = ~port->tty->termios->c_cflag; - old_term.c_iflag = ~port->tty->termios->c_iflag; - whiteheat_set_termios(port, &old_term); + if (tty) + firm_setup_port(tty); /* Work around HCD bugs */ usb_clear_halt(port->serial->dev, port->read_urb->pipe); @@ -651,7 +680,8 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp) /* Start reading from the device */ retval = start_port_read(port); if (retval) { - err("%s - failed submitting read urb, error %d", __func__, retval); + err("%s - failed submitting read urb, error %d", + __func__, retval); firm_close(port); stop_command_port(port->serial); goto exit; @@ -663,7 +693,8 @@ exit: } -static void whiteheat_close(struct usb_serial_port *port, struct file * filp) +static void whiteheat_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct whiteheat_private *info = usb_get_serial_port_data(port); struct whiteheat_urb_wrap *wrap; @@ -681,7 +712,7 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) } mutex_unlock(&port->serial->disc_mutex); - port->tty->closing = 1; + tty->closing = 1; /* * Not currently in use; tty_wait_until_sent() calls @@ -689,12 +720,12 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) * acquisition. This should be fixed at some point. Greg's been * notified. if ((filp->f_flags & (O_NDELAY | O_NONBLOCK)) == 0) { - tty_wait_until_sent(port->tty, CLOSING_DELAY); + tty_wait_until_sent(tty, CLOSING_DELAY); } */ - tty_driver_flush_buffer(port->tty); - tty_ldisc_flush(port->tty); + tty_driver_flush_buffer(tty); + tty_ldisc_flush(tty); firm_report_tx_done(port); @@ -728,11 +759,12 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) stop_command_port(port->serial); - port->tty->closing = 0; + tty->closing = 0; } -static int whiteheat_write(struct usb_serial_port *port, const unsigned char *buf, int count) +static int whiteheat_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; struct whiteheat_private *info = usb_get_serial_port_data(port); @@ -763,16 +795,19 @@ static int whiteheat_write(struct usb_serial_port *port, const unsigned char *bu wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); urb = wrap->urb; - bytes = (count > port->bulk_out_size) ? port->bulk_out_size : count; - memcpy (urb->transfer_buffer, buf + sent, bytes); + bytes = (count > port->bulk_out_size) ? + port->bulk_out_size : count; + memcpy(urb->transfer_buffer, buf + sent, bytes); - usb_serial_debug_data(debug, &port->dev, __func__, bytes, urb->transfer_buffer); + usb_serial_debug_data(debug, &port->dev, + __func__, bytes, urb->transfer_buffer); urb->dev = serial->dev; urb->transfer_buffer_length = bytes; result = usb_submit_urb(urb, GFP_ATOMIC); if (result) { - err("%s - failed submitting write urb, error %d", __func__, result); + err("%s - failed submitting write urb, error %d", + __func__, result); sent = result; spin_lock_irqsave(&info->lock, flags); list_add(tmp, &info->tx_urbs_free); @@ -790,16 +825,16 @@ static int whiteheat_write(struct usb_serial_port *port, const unsigned char *bu return sent; } - -static int whiteheat_write_room(struct usb_serial_port *port) +static int whiteheat_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); struct list_head *tmp; int room = 0; unsigned long flags; dbg("%s - port %d", __func__, port->number); - + spin_lock_irqsave(&info->lock, flags); list_for_each(tmp, &info->tx_urbs_free) room++; @@ -810,9 +845,9 @@ static int whiteheat_write_room(struct usb_serial_port *port) return (room); } - -static int whiteheat_tiocmget (struct usb_serial_port *port, struct file *file) +static int whiteheat_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); unsigned int modem_signals = 0; @@ -827,10 +862,10 @@ static int whiteheat_tiocmget (struct usb_serial_port *port, struct file *file) return modem_signals; } - -static int whiteheat_tiocmset (struct usb_serial_port *port, struct file *file, +static int whiteheat_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); dbg("%s - port %d", __func__, port->number); @@ -851,65 +886,55 @@ static int whiteheat_tiocmset (struct usb_serial_port *port, struct file *file, } -static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) +static int whiteheat_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct serial_struct serstruct; void __user *user_arg = (void __user *)arg; dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd); switch (cmd) { - case TIOCGSERIAL: - memset(&serstruct, 0, sizeof(serstruct)); - serstruct.type = PORT_16654; - serstruct.line = port->serial->minor; - serstruct.port = port->number; - serstruct.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; - serstruct.xmit_fifo_size = port->bulk_out_size; - serstruct.custom_divisor = 0; - serstruct.baud_base = 460800; - serstruct.close_delay = CLOSING_DELAY; - serstruct.closing_wait = CLOSING_DELAY; - - if (copy_to_user(user_arg, &serstruct, sizeof(serstruct))) - return -EFAULT; - - break; - - case TIOCSSERIAL: - if (copy_from_user(&serstruct, user_arg, sizeof(serstruct))) - return -EFAULT; - - /* - * For now this is ignored. dip sets the ASYNC_[V]HI flags - * but this isn't used by us at all. Maybe someone somewhere - * will need the custom_divisor setting. - */ - - break; - - default: - break; + case TIOCGSERIAL: + memset(&serstruct, 0, sizeof(serstruct)); + serstruct.type = PORT_16654; + serstruct.line = port->serial->minor; + serstruct.port = port->number; + serstruct.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; + serstruct.xmit_fifo_size = port->bulk_out_size; + serstruct.custom_divisor = 0; + serstruct.baud_base = 460800; + serstruct.close_delay = CLOSING_DELAY; + serstruct.closing_wait = CLOSING_DELAY; + + if (copy_to_user(user_arg, &serstruct, sizeof(serstruct))) + return -EFAULT; + break; + default: + break; } return -ENOIOCTLCMD; } -static void whiteheat_set_termios(struct usb_serial_port *port, struct ktermios *old_termios) +static void whiteheat_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { - dbg("%s -port %d", __func__, port->number); - firm_setup_port(port); + firm_setup_port(tty); } - -static void whiteheat_break_ctl(struct usb_serial_port *port, int break_state) { +static void whiteheat_break_ctl(struct tty_struct *tty, int break_state) +{ + struct usb_serial_port *port = tty->driver_data; firm_set_break(port, break_state); } -static int whiteheat_chars_in_buffer(struct usb_serial_port *port) +static int whiteheat_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); struct list_head *tmp; struct whiteheat_urb_wrap *wrap; @@ -925,13 +950,14 @@ static int whiteheat_chars_in_buffer(struct usb_serial_port *port) } spin_unlock_irqrestore(&info->lock, flags); - dbg ("%s - returns %d", __func__, chars); + dbg("%s - returns %d", __func__, chars); return chars; } -static void whiteheat_throttle (struct usb_serial_port *port) +static void whiteheat_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); unsigned long flags; @@ -945,8 +971,9 @@ static void whiteheat_throttle (struct usb_serial_port *port) } -static void whiteheat_unthrottle (struct usb_serial_port *port) +static void whiteheat_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); int actually_throttled; unsigned long flags; @@ -993,7 +1020,7 @@ static void command_port_read_callback(struct urb *urb) command_info = usb_get_serial_port_data(command_port); if (!command_info) { - dbg ("%s - command_info is NULL, exiting.", __func__); + dbg("%s - command_info is NULL, exiting.", __func__); return; } if (status) { @@ -1004,7 +1031,8 @@ static void command_port_read_callback(struct urb *urb) return; } - usb_serial_debug_data(debug, &command_port->dev, __func__, urb->actual_length, data); + usb_serial_debug_data(debug, &command_port->dev, + __func__, urb->actual_length, data); if (data[0] == WHITEHEAT_CMD_COMPLETE) { command_info->command_finished = WHITEHEAT_CMD_COMPLETE; @@ -1013,21 +1041,23 @@ static void command_port_read_callback(struct urb *urb) command_info->command_finished = WHITEHEAT_CMD_FAILURE; wake_up(&command_info->wait_command); } else if (data[0] == WHITEHEAT_EVENT) { - /* These are unsolicited reports from the firmware, hence no waiting command to wakeup */ + /* These are unsolicited reports from the firmware, hence no + waiting command to wakeup */ dbg("%s - event received", __func__); } else if (data[0] == WHITEHEAT_GET_DTR_RTS) { - memcpy(command_info->result_buffer, &data[1], urb->actual_length - 1); + memcpy(command_info->result_buffer, &data[1], + urb->actual_length - 1); command_info->command_finished = WHITEHEAT_CMD_COMPLETE; wake_up(&command_info->wait_command); - } else { + } else dbg("%s - bad reply from firmware", __func__); - } - + /* Continue trying to always read */ command_port->read_urb->dev = command_port->serial->dev; result = usb_submit_urb(command_port->read_urb, GFP_ATOMIC); if (result) - dbg("%s - failed resubmitting read urb, error %d", __func__, result); + dbg("%s - failed resubmitting read urb, error %d", + __func__, result); } @@ -1060,7 +1090,8 @@ static void whiteheat_read_callback(struct urb *urb) return; } - usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); + usb_serial_debug_data(debug, &port->dev, + __func__, urb->actual_length, data); spin_lock(&info->lock); list_add_tail(&wrap->list, &info->rx_urb_q); @@ -1107,7 +1138,8 @@ static void whiteheat_write_callback(struct urb *urb) /***************************************************************************** * Connect Tech's White Heat firmware interface *****************************************************************************/ -static int firm_send_command(struct usb_serial_port *port, __u8 command, __u8 *data, __u8 datasize) +static int firm_send_command(struct usb_serial_port *port, __u8 command, + __u8 *data, __u8 datasize) { struct usb_serial_port *command_port; struct whiteheat_command_private *command_info; @@ -1122,13 +1154,13 @@ static int firm_send_command(struct usb_serial_port *port, __u8 command, __u8 *d command_info = usb_get_serial_port_data(command_port); mutex_lock(&command_info->mutex); command_info->command_finished = false; - + transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer; transfer_buffer[0] = command; - memcpy (&transfer_buffer[1], data, datasize); + memcpy(&transfer_buffer[1], data, datasize); command_port->write_urb->transfer_buffer_length = datasize + 1; command_port->write_urb->dev = port->serial->dev; - retval = usb_submit_urb (command_port->write_urb, GFP_NOIO); + retval = usb_submit_urb(command_port->write_urb, GFP_NOIO); if (retval) { dbg("%s - submit urb failed", __func__); goto exit; @@ -1155,51 +1187,57 @@ static int firm_send_command(struct usb_serial_port *port, __u8 command, __u8 *d if (command_info->command_finished == WHITEHEAT_CMD_COMPLETE) { dbg("%s - command completed.", __func__); switch (command) { - case WHITEHEAT_GET_DTR_RTS: - info = usb_get_serial_port_data(port); - memcpy(&info->mcr, command_info->result_buffer, sizeof(struct whiteheat_dr_info)); + case WHITEHEAT_GET_DTR_RTS: + info = usb_get_serial_port_data(port); + memcpy(&info->mcr, command_info->result_buffer, + sizeof(struct whiteheat_dr_info)); break; } } - exit: mutex_unlock(&command_info->mutex); return retval; } -static int firm_open(struct usb_serial_port *port) { +static int firm_open(struct usb_serial_port *port) +{ struct whiteheat_simple open_command; open_command.port = port->number - port->serial->minor + 1; - return firm_send_command(port, WHITEHEAT_OPEN, (__u8 *)&open_command, sizeof(open_command)); + return firm_send_command(port, WHITEHEAT_OPEN, + (__u8 *)&open_command, sizeof(open_command)); } -static int firm_close(struct usb_serial_port *port) { +static int firm_close(struct usb_serial_port *port) +{ struct whiteheat_simple close_command; close_command.port = port->number - port->serial->minor + 1; - return firm_send_command(port, WHITEHEAT_CLOSE, (__u8 *)&close_command, sizeof(close_command)); + return firm_send_command(port, WHITEHEAT_CLOSE, + (__u8 *)&close_command, sizeof(close_command)); } -static int firm_setup_port(struct usb_serial_port *port) { +static int firm_setup_port(struct tty_struct *tty) +{ + struct usb_serial_port *port = tty->driver_data; struct whiteheat_port_settings port_settings; - unsigned int cflag = port->tty->termios->c_cflag; + unsigned int cflag = tty->termios->c_cflag; port_settings.port = port->number + 1; /* get the byte size */ switch (cflag & CSIZE) { - case CS5: port_settings.bits = 5; break; - case CS6: port_settings.bits = 6; break; - case CS7: port_settings.bits = 7; break; - default: - case CS8: port_settings.bits = 8; break; + case CS5: port_settings.bits = 5; break; + case CS6: port_settings.bits = 6; break; + case CS7: port_settings.bits = 7; break; + default: + case CS8: port_settings.bits = 8; break; } dbg("%s - data bits = %d", __func__, port_settings.bits); - + /* determine the parity */ if (cflag & PARENB) if (cflag & CMSPAR) @@ -1225,7 +1263,8 @@ static int firm_setup_port(struct usb_serial_port *port) { /* figure out the flow control settings */ if (cflag & CRTSCTS) - port_settings.hflow = (WHITEHEAT_HFLOW_CTS | WHITEHEAT_HFLOW_RTS); + port_settings.hflow = (WHITEHEAT_HFLOW_CTS | + WHITEHEAT_HFLOW_RTS); else port_settings.hflow = WHITEHEAT_HFLOW_NONE; dbg("%s - hardware flow control = %s %s %s %s", __func__, @@ -1233,81 +1272,95 @@ static int firm_setup_port(struct usb_serial_port *port) { (port_settings.hflow & WHITEHEAT_HFLOW_RTS) ? "RTS" : "", (port_settings.hflow & WHITEHEAT_HFLOW_DSR) ? "DSR" : "", (port_settings.hflow & WHITEHEAT_HFLOW_DTR) ? "DTR" : ""); - + /* determine software flow control */ - if (I_IXOFF(port->tty)) + if (I_IXOFF(tty)) port_settings.sflow = WHITEHEAT_SFLOW_RXTX; else port_settings.sflow = WHITEHEAT_SFLOW_NONE; dbg("%s - software flow control = %c", __func__, port_settings.sflow); - - port_settings.xon = START_CHAR(port->tty); - port_settings.xoff = STOP_CHAR(port->tty); - dbg("%s - XON = %2x, XOFF = %2x", __func__, port_settings.xon, port_settings.xoff); + + port_settings.xon = START_CHAR(tty); + port_settings.xoff = STOP_CHAR(tty); + dbg("%s - XON = %2x, XOFF = %2x", + __func__, port_settings.xon, port_settings.xoff); /* get the baud rate wanted */ - port_settings.baud = tty_get_baud_rate(port->tty); + port_settings.baud = tty_get_baud_rate(tty); dbg("%s - baud rate = %d", __func__, port_settings.baud); /* fixme: should set validated settings */ - tty_encode_baud_rate(port->tty, port_settings.baud, port_settings.baud); + tty_encode_baud_rate(tty, port_settings.baud, port_settings.baud); /* handle any settings that aren't specified in the tty structure */ port_settings.lloop = 0; - + /* now send the message to the device */ - return firm_send_command(port, WHITEHEAT_SETUP_PORT, (__u8 *)&port_settings, sizeof(port_settings)); + return firm_send_command(port, WHITEHEAT_SETUP_PORT, + (__u8 *)&port_settings, sizeof(port_settings)); } -static int firm_set_rts(struct usb_serial_port *port, __u8 onoff) { +static int firm_set_rts(struct usb_serial_port *port, __u8 onoff) +{ struct whiteheat_set_rdb rts_command; rts_command.port = port->number - port->serial->minor + 1; rts_command.state = onoff; - return firm_send_command(port, WHITEHEAT_SET_RTS, (__u8 *)&rts_command, sizeof(rts_command)); + return firm_send_command(port, WHITEHEAT_SET_RTS, + (__u8 *)&rts_command, sizeof(rts_command)); } -static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff) { +static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff) +{ struct whiteheat_set_rdb dtr_command; dtr_command.port = port->number - port->serial->minor + 1; dtr_command.state = onoff; - return firm_send_command(port, WHITEHEAT_SET_RTS, (__u8 *)&dtr_command, sizeof(dtr_command)); + return firm_send_command(port, WHITEHEAT_SET_DTR, + (__u8 *)&dtr_command, sizeof(dtr_command)); } -static int firm_set_break(struct usb_serial_port *port, __u8 onoff) { +static int firm_set_break(struct usb_serial_port *port, __u8 onoff) +{ struct whiteheat_set_rdb break_command; break_command.port = port->number - port->serial->minor + 1; break_command.state = onoff; - return firm_send_command(port, WHITEHEAT_SET_RTS, (__u8 *)&break_command, sizeof(break_command)); + return firm_send_command(port, WHITEHEAT_SET_BREAK, + (__u8 *)&break_command, sizeof(break_command)); } -static int firm_purge(struct usb_serial_port *port, __u8 rxtx) { +static int firm_purge(struct usb_serial_port *port, __u8 rxtx) +{ struct whiteheat_purge purge_command; purge_command.port = port->number - port->serial->minor + 1; purge_command.what = rxtx; - return firm_send_command(port, WHITEHEAT_PURGE, (__u8 *)&purge_command, sizeof(purge_command)); + return firm_send_command(port, WHITEHEAT_PURGE, + (__u8 *)&purge_command, sizeof(purge_command)); } -static int firm_get_dtr_rts(struct usb_serial_port *port) { +static int firm_get_dtr_rts(struct usb_serial_port *port) +{ struct whiteheat_simple get_dr_command; get_dr_command.port = port->number - port->serial->minor + 1; - return firm_send_command(port, WHITEHEAT_GET_DTR_RTS, (__u8 *)&get_dr_command, sizeof(get_dr_command)); + return firm_send_command(port, WHITEHEAT_GET_DTR_RTS, + (__u8 *)&get_dr_command, sizeof(get_dr_command)); } -static int firm_report_tx_done(struct usb_serial_port *port) { +static int firm_report_tx_done(struct usb_serial_port *port) +{ struct whiteheat_simple close_command; close_command.port = port->number - port->serial->minor + 1; - return firm_send_command(port, WHITEHEAT_REPORT_TX_DONE, (__u8 *)&close_command, sizeof(close_command)); + return firm_send_command(port, WHITEHEAT_REPORT_TX_DONE, + (__u8 *)&close_command, sizeof(close_command)); } @@ -1319,7 +1372,7 @@ static int start_command_port(struct usb_serial *serial) struct usb_serial_port *command_port; struct whiteheat_command_private *command_info; int retval = 0; - + command_port = serial->port[COMMAND_PORT]; command_info = usb_get_serial_port_data(command_port); mutex_lock(&command_info->mutex); @@ -1330,7 +1383,8 @@ static int start_command_port(struct usb_serial *serial) command_port->read_urb->dev = serial->dev; retval = usb_submit_urb(command_port->read_urb, GFP_KERNEL); if (retval) { - err("%s - failed submitting read urb, error %d", __func__, retval); + err("%s - failed submitting read urb, error %d", + __func__, retval); goto exit; } } @@ -1400,7 +1454,8 @@ static int start_port_read(struct usb_serial_port *port) } -static struct whiteheat_urb_wrap *urb_to_wrap(struct urb* urb, struct list_head *head) +static struct whiteheat_urb_wrap *urb_to_wrap(struct urb *urb, + struct list_head *head) { struct whiteheat_urb_wrap *wrap; struct list_head *tmp; @@ -1426,7 +1481,7 @@ static void rx_data_softint(struct work_struct *work) struct whiteheat_private *info = container_of(work, struct whiteheat_private, rx_work); struct usb_serial_port *port = info->port; - struct tty_struct *tty = port->tty; + struct tty_struct *tty = port->port.tty; struct whiteheat_urb_wrap *wrap; struct urb *urb; unsigned long flags; @@ -1449,7 +1504,8 @@ static void rx_data_softint(struct work_struct *work) urb = wrap->urb; if (tty && urb->actual_length) { - int len = tty_buffer_request_room(tty, urb->actual_length); + int len = tty_buffer_request_room(tty, + urb->actual_length); /* This stuff can go away now I suspect */ if (unlikely(len < urb->actual_length)) { spin_lock_irqsave(&info->lock, flags); @@ -1466,7 +1522,8 @@ static void rx_data_softint(struct work_struct *work) urb->dev = port->serial->dev; result = usb_submit_urb(urb, GFP_ATOMIC); if (result) { - err("%s - failed resubmitting read urb, error %d", __func__, result); + err("%s - failed resubmitting read urb, error %d", + __func__, result); spin_lock_irqsave(&info->lock, flags); list_add(tmp, &info->rx_urbs_free); continue; @@ -1485,7 +1542,7 @@ static void rx_data_softint(struct work_struct *work) /***************************************************************************** * Connect Tech's White Heat module functions *****************************************************************************/ -static int __init whiteheat_init (void) +static int __init whiteheat_init(void) { int retval; retval = usb_serial_register(&whiteheat_fake_device); @@ -1508,19 +1565,19 @@ failed_fake_register: } -static void __exit whiteheat_exit (void) +static void __exit whiteheat_exit(void) { - usb_deregister (&whiteheat_driver); - usb_serial_deregister (&whiteheat_fake_device); - usb_serial_deregister (&whiteheat_device); + usb_deregister(&whiteheat_driver); + usb_serial_deregister(&whiteheat_fake_device); + usb_serial_deregister(&whiteheat_device); } module_init(whiteheat_init); module_exit(whiteheat_exit); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_FIRMWARE("whiteheat.fw"); diff --git a/drivers/usb/serial/whiteheat.h b/drivers/usb/serial/whiteheat.h index f16079705664..38065df4d2d8 100644 --- a/drivers/usb/serial/whiteheat.h +++ b/drivers/usb/serial/whiteheat.h @@ -2,7 +2,7 @@ * USB ConnectTech WhiteHEAT driver * * Copyright (C) 2002 - * Connect Tech Inc. + * Connect Tech Inc. * * Copyright (C) 1999, 2000 * Greg Kroah-Hartman (greg@kroah.com) @@ -12,7 +12,8 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * See Documentation/usb/usb-serial.txt for more information on using this driver + * See Documentation/usb/usb-serial.txt for more information on using this + * driver * */ @@ -30,13 +31,16 @@ #define WHITEHEAT_DUMP 7 /* dump memory */ #define WHITEHEAT_STATUS 8 /* get status */ #define WHITEHEAT_PURGE 9 /* clear the UART fifos */ -#define WHITEHEAT_GET_DTR_RTS 10 /* get the state of DTR and RTS for a port */ -#define WHITEHEAT_GET_HW_INFO 11 /* get EEPROM info and hardware ID */ +#define WHITEHEAT_GET_DTR_RTS 10 /* get the state of DTR and RTS + for a port */ +#define WHITEHEAT_GET_HW_INFO 11 /* get EEPROM info and + hardware ID */ #define WHITEHEAT_REPORT_TX_DONE 12 /* get the next TX done */ #define WHITEHEAT_EVENT 13 /* unsolicited status events */ -#define WHITEHEAT_ECHO 14 /* send data to the indicated IN endpoint */ -#define WHITEHEAT_DO_TEST 15 /* perform the specified test */ -#define WHITEHEAT_CMD_COMPLETE 16 /* reply for certain commands */ +#define WHITEHEAT_ECHO 14 /* send data to the indicated + IN endpoint */ +#define WHITEHEAT_DO_TEST 15 /* perform specified test */ +#define WHITEHEAT_CMD_COMPLETE 16 /* reply for some commands */ #define WHITEHEAT_CMD_FAILURE 17 /* reply for failed commands */ @@ -67,20 +71,28 @@ struct whiteheat_simple { #define WHITEHEAT_PAR_MARK '1' /* mark (force 1) parity */ #define WHITEHEAT_SFLOW_NONE 'n' /* no software flow control */ -#define WHITEHEAT_SFLOW_RX 'r' /* XOFF/ON is sent when RX fills/empties */ -#define WHITEHEAT_SFLOW_TX 't' /* when received XOFF/ON will stop/start TX */ +#define WHITEHEAT_SFLOW_RX 'r' /* XOFF/ON is sent when RX + fills/empties */ +#define WHITEHEAT_SFLOW_TX 't' /* when received XOFF/ON will + stop/start TX */ #define WHITEHEAT_SFLOW_RXTX 'b' /* both SFLOW_RX and SFLOW_TX */ #define WHITEHEAT_HFLOW_NONE 0x00 /* no hardware flow control */ -#define WHITEHEAT_HFLOW_RTS_TOGGLE 0x01 /* RTS is on during transmit, off otherwise */ -#define WHITEHEAT_HFLOW_DTR 0x02 /* DTR is off/on when RX fills/empties */ -#define WHITEHEAT_HFLOW_CTS 0x08 /* when received CTS off/on will stop/start TX */ -#define WHITEHEAT_HFLOW_DSR 0x10 /* when received DSR off/on will stop/start TX */ -#define WHITEHEAT_HFLOW_RTS 0x80 /* RTS is off/on when RX fills/empties */ +#define WHITEHEAT_HFLOW_RTS_TOGGLE 0x01 /* RTS is on during transmit, + off otherwise */ +#define WHITEHEAT_HFLOW_DTR 0x02 /* DTR is off/on when RX + fills/empties */ +#define WHITEHEAT_HFLOW_CTS 0x08 /* when received CTS off/on + will stop/start TX */ +#define WHITEHEAT_HFLOW_DSR 0x10 /* when received DSR off/on + will stop/start TX */ +#define WHITEHEAT_HFLOW_RTS 0x80 /* RTS is off/on when RX + fills/empties */ struct whiteheat_port_settings { __u8 port; /* port number (1 to N) */ - __u32 baud; /* any value 7 - 460800, firmware calculates best fit; arrives little endian */ + __u32 baud; /* any value 7 - 460800, firmware calculates + best fit; arrives little endian */ __u8 bits; /* 5, 6, 7, or 8 */ __u8 stop; /* 1 or 2, default 1 (2 = 1.5 if bits = 5) */ __u8 parity; /* see WHITEHEAT_PAR_* above */ @@ -167,12 +179,14 @@ struct whiteheat_echo { */ #define WHITEHEAT_TEST_UART_RW 0x01 /* read/write uart registers */ #define WHITEHEAT_TEST_UART_INTR 0x02 /* uart interrupt */ -#define WHITEHEAT_TEST_SETUP_CONT 0x03 /* setup for PORT_CONT/PORT_DISCONT */ +#define WHITEHEAT_TEST_SETUP_CONT 0x03 /* setup for + PORT_CONT/PORT_DISCONT */ #define WHITEHEAT_TEST_PORT_CONT 0x04 /* port connect */ #define WHITEHEAT_TEST_PORT_DISCONT 0x05 /* port disconnect */ #define WHITEHEAT_TEST_UART_CLK_START 0x06 /* uart clock test start */ #define WHITEHEAT_TEST_UART_CLK_STOP 0x07 /* uart clock test stop */ -#define WHITEHEAT_TEST_MODEM_FT 0x08 /* modem signals, requires a loopback cable/connector */ +#define WHITEHEAT_TEST_MODEM_FT 0x08 /* modem signals, requires a + loopback cable/connector */ #define WHITEHEAT_TEST_ERASE_EEPROM 0x09 /* erase eeprom */ #define WHITEHEAT_TEST_READ_EEPROM 0x0a /* read eeprom */ #define WHITEHEAT_TEST_PROGRAM_EEPROM 0x0b /* program eeprom */ @@ -198,19 +212,27 @@ struct whiteheat_test { #define WHITEHEAT_EVENT_CONNECT 0x08 /* connect field is valid */ #define WHITEHEAT_FLOW_NONE 0x00 /* no flow control active */ -#define WHITEHEAT_FLOW_HARD_OUT 0x01 /* TX is stopped by CTS (waiting for CTS to go on) */ -#define WHITEHEAT_FLOW_HARD_IN 0x02 /* remote TX is stopped by RTS */ -#define WHITEHEAT_FLOW_SOFT_OUT 0x04 /* TX is stopped by XOFF received (waiting for XON) */ -#define WHITEHEAT_FLOW_SOFT_IN 0x08 /* remote TX is stopped by XOFF transmitted */ +#define WHITEHEAT_FLOW_HARD_OUT 0x01 /* TX is stopped by CTS + (waiting for CTS to go on) */ +#define WHITEHEAT_FLOW_HARD_IN 0x02 /* remote TX is stopped + by RTS */ +#define WHITEHEAT_FLOW_SOFT_OUT 0x04 /* TX is stopped by XOFF + received (waiting for XON) */ +#define WHITEHEAT_FLOW_SOFT_IN 0x08 /* remote TX is stopped by XOFF + transmitted */ #define WHITEHEAT_FLOW_TX_DONE 0x80 /* TX has completed */ struct whiteheat_status_info { __u8 port; /* port number (1 to N) */ - __u8 event; /* indicates what the current event is, see WHITEHEAT_EVENT_* above */ - __u8 modem; /* modem signal status (copy of uart's MSR register) */ + __u8 event; /* indicates what the current event is, + see WHITEHEAT_EVENT_* above */ + __u8 modem; /* modem signal status (copy of uart's + MSR register) */ __u8 error; /* line status (copy of uart's LSR register) */ - __u8 flow; /* flow control state, see WHITEHEAT_FLOW_* above */ - __u8 connect; /* 0 means not connected, non-zero means connected */ + __u8 flow; /* flow control state, see WHITEHEAT_FLOW_* + above */ + __u8 connect; /* 0 means not connected, non-zero means + connected */ }; @@ -256,7 +278,8 @@ struct whiteheat_hw_info { struct whiteheat_event_info { __u8 port; /* port number (1 to N) */ __u8 event; /* see whiteheat_status_info.event */ - __u8 info; /* see whiteheat_status_info.modem, .error, .flow, .connect */ + __u8 info; /* see whiteheat_status_info.modem, .error, + .flow, .connect */ }; @@ -269,7 +292,8 @@ struct whiteheat_event_info { struct whiteheat_test_info { __u8 port; /* port number (1 to N) */ - __u8 test; /* indicates which test this is a response for, see WHITEHEAT_DO_TEST above */ + __u8 test; /* indicates which test this is a response for, + see WHITEHEAT_DO_TEST above */ __u8 status; /* see WHITEHEAT_TEST_* above */ __u8 results[32]; /* test-dependent results */ }; diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c index 579e9f52053a..17f1ae232919 100644 --- a/drivers/usb/storage/datafab.c +++ b/drivers/usb/storage/datafab.c @@ -1,7 +1,5 @@ /* Driver for Datafab USB Compact Flash reader * - * $Id: datafab.c,v 1.7 2002/02/25 00:40:13 mdharm Exp $ - * * datafab driver v0.1: * * First release diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c index 01e430654a13..a2b5526c9fa0 100644 --- a/drivers/usb/storage/debug.c +++ b/drivers/usb/storage/debug.c @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Debugging Functions Source Code File * - * $Id: debug.c,v 1.9 2002/04/22 03:39:43 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h index 77e244a8c376..dbb985d52423 100644 --- a/drivers/usb/storage/debug.h +++ b/drivers/usb/storage/debug.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Debugging Functions Header File * - * $Id: debug.h,v 1.6 2001/01/12 23:51:04 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/dpcm.c b/drivers/usb/storage/dpcm.c index 9a410b5a6e5b..939923471af4 100644 --- a/drivers/usb/storage/dpcm.c +++ b/drivers/usb/storage/dpcm.c @@ -1,7 +1,5 @@ /* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader * - * $Id: dpcm.c,v 1.4 2001/06/11 02:54:25 mdharm Exp $ - * * DPCM driver v0.1: * * First release diff --git a/drivers/usb/storage/dpcm.h b/drivers/usb/storage/dpcm.h index 81b464cfcc1e..e7b7b0f120d7 100644 --- a/drivers/usb/storage/dpcm.h +++ b/drivers/usb/storage/dpcm.h @@ -1,7 +1,5 @@ /* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader * - * $Id: dpcm.h,v 1.2 2000/08/25 00:13:51 mdharm Exp $ - * * DPCM driver v0.1: * * First release diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c index f5a4e8d6a3b1..7a4d45677227 100644 --- a/drivers/usb/storage/freecom.c +++ b/drivers/usb/storage/freecom.c @@ -1,7 +1,5 @@ /* Driver for Freecom USB/IDE adaptor * - * $Id: freecom.c,v 1.22 2002/04/22 03:39:43 mdharm Exp $ - * * Freecom v0.1: * * First release diff --git a/drivers/usb/storage/freecom.h b/drivers/usb/storage/freecom.h index 1b012d62d0a8..20d0fe6ba0c8 100644 --- a/drivers/usb/storage/freecom.h +++ b/drivers/usb/storage/freecom.h @@ -1,7 +1,5 @@ /* Driver for Freecom USB/IDE adaptor * - * $Id: freecom.h,v 1.4 2000/08/29 14:49:15 dlbrown Exp $ - * * Freecom v0.1: * * First release diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c index 187dd1e01093..4995bb595aef 100644 --- a/drivers/usb/storage/initializers.c +++ b/drivers/usb/storage/initializers.c @@ -1,7 +1,5 @@ /* Special Initializers for certain USB Mass Storage devices * - * $Id: initializers.c,v 1.2 2000/09/06 22:35:57 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h index ad3ffd4236c2..529327fbb06b 100644 --- a/drivers/usb/storage/initializers.h +++ b/drivers/usb/storage/initializers.h @@ -1,7 +1,5 @@ /* Header file for Special Initializers for certain USB Mass Storage devices * - * $Id: initializers.h,v 1.1 2000/08/29 23:07:02 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index 3addcd8f827b..383abf2516a5 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -1,7 +1,5 @@ /* Transport & Protocol Driver for In-System Design, Inc. ISD200 ASIC * - * $Id: isd200.c,v 1.16 2002/04/22 03:39:43 mdharm Exp $ - * * Current development and maintenance: * (C) 2001-2002 Björn Stenberg (bjorn@haxx.se) * @@ -586,7 +584,7 @@ static void isd200_invoke_transport( struct us_data *us, /* if the command gets aborted by the higher layers, we need to * short-circuit all other processing */ - if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { + if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { US_DEBUGP("-- command was aborted\n"); goto Handle_Abort; } @@ -633,7 +631,7 @@ static void isd200_invoke_transport( struct us_data *us, if (need_auto_sense) { result = isd200_read_regs(us); - if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { + if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { US_DEBUGP("-- auto-sense aborted\n"); goto Handle_Abort; } @@ -663,7 +661,7 @@ static void isd200_invoke_transport( struct us_data *us, srb->result = DID_ABORT << 16; /* permit the reset transfer to take place */ - clear_bit(US_FLIDX_ABORTING, &us->flags); + clear_bit(US_FLIDX_ABORTING, &us->dflags); /* Need reset here */ } diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c index 61097cbb1585..df67f13c9e73 100644 --- a/drivers/usb/storage/jumpshot.c +++ b/drivers/usb/storage/jumpshot.c @@ -1,7 +1,5 @@ /* Driver for Lexar "Jumpshot" Compact Flash reader * - * $Id: jumpshot.c,v 1.7 2002/02/25 00:40:13 mdharm Exp $ - * * jumpshot driver v0.1: * * First release diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c index b9b8ede61fb3..3b3357e20ea7 100644 --- a/drivers/usb/storage/protocol.c +++ b/drivers/usb/storage/protocol.c @@ -1,7 +1,5 @@ /* Driver for USB Mass Storage compliant devices * - * $Id: protocol.c,v 1.14 2002/04/22 03:39:43 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/protocol.h b/drivers/usb/storage/protocol.h index 8737a36891ca..487056ffb516 100644 --- a/drivers/usb/storage/protocol.h +++ b/drivers/usb/storage/protocol.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Protocol Functions Header File * - * $Id: protocol.h,v 1.4 2001/02/13 07:10:03 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 3fcde9f0fa5f..09779f6a8179 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * SCSI layer glue code * - * $Id: scsiglue.c,v 1.26 2002/04/22 03:39:43 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * @@ -73,7 +71,6 @@ static const char* host_info(struct Scsi_Host *host) static int slave_alloc (struct scsi_device *sdev) { struct us_data *us = host_to_us(sdev->host); - struct usb_host_endpoint *bulk_in_ep; /* * Set the INQUIRY transfer length to 36. We don't use any of @@ -82,16 +79,22 @@ static int slave_alloc (struct scsi_device *sdev) */ sdev->inquiry_len = 36; - /* Scatter-gather buffers (all but the last) must have a length - * divisible by the bulk maxpacket size. Otherwise a data packet - * would end up being short, causing a premature end to the data - * transfer. We'll use the maxpacket value of the bulk-IN pipe - * to set the SCSI device queue's DMA alignment mask. + /* USB has unusual DMA-alignment requirements: Although the + * starting address of each scatter-gather element doesn't matter, + * the length of each element except the last must be divisible + * by the Bulk maxpacket value. There's currently no way to + * express this by block-layer constraints, so we'll cop out + * and simply require addresses to be aligned at 512-byte + * boundaries. This is okay since most block I/O involves + * hardware sectors that are multiples of 512 bytes in length, + * and since host controllers up through USB 2.0 have maxpacket + * values no larger than 512. + * + * But it doesn't suffice for Wireless USB, where Bulk maxpacket + * values can be as large as 2048. To make that work properly + * will require changes to the block layer. */ - bulk_in_ep = us->pusb_dev->ep_in[usb_pipeendpoint(us->recv_bulk_pipe)]; - blk_queue_update_dma_alignment(sdev->request_queue, - le16_to_cpu(bulk_in_ep->desc.wMaxPacketSize) - 1); - /* wMaxPacketSize must be a power of 2 */ + blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1)); /* * The UFI spec treates the Peripheral Qualifier bits in an @@ -116,10 +119,10 @@ static int slave_configure(struct scsi_device *sdev) * while others have trouble with more than 64K. At this time we * are limiting both to 32K (64 sectores). */ - if (us->flags & (US_FL_MAX_SECTORS_64 | US_FL_MAX_SECTORS_MIN)) { + if (us->fflags & (US_FL_MAX_SECTORS_64 | US_FL_MAX_SECTORS_MIN)) { unsigned int max_sectors = 64; - if (us->flags & US_FL_MAX_SECTORS_MIN) + if (us->fflags & US_FL_MAX_SECTORS_MIN) max_sectors = PAGE_CACHE_SIZE >> 9; if (sdev->request_queue->max_sectors > max_sectors) blk_queue_max_sectors(sdev->request_queue, @@ -148,7 +151,7 @@ static int slave_configure(struct scsi_device *sdev) * majority of devices work fine, but a few still can't * handle it. The sd driver will simply assume those * devices are write-enabled. */ - if (us->flags & US_FL_NO_WP_DETECT) + if (us->fflags & US_FL_NO_WP_DETECT) sdev->skip_ms_page_3f = 1; /* A number of devices have problems with MODE SENSE for @@ -158,13 +161,13 @@ static int slave_configure(struct scsi_device *sdev) /* Some disks return the total number of blocks in response * to READ CAPACITY rather than the highest block number. * If this device makes that mistake, tell the sd driver. */ - if (us->flags & US_FL_FIX_CAPACITY) + if (us->fflags & 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) + if (us->fflags & US_FL_CAPACITY_HEURISTICS) sdev->guess_capacity = 1; /* Some devices report a SCSI revision level above 2 but are @@ -213,7 +216,7 @@ static int slave_configure(struct scsi_device *sdev) /* Some devices choke when they receive a PREVENT-ALLOW MEDIUM * REMOVAL command, so suppress those commands. */ - if (us->flags & US_FL_NOT_LOCKABLE) + if (us->fflags & US_FL_NOT_LOCKABLE) sdev->lockable = 0; /* this is to satisfy the compiler, tho I don't think the @@ -238,7 +241,7 @@ static int queuecommand(struct scsi_cmnd *srb, } /* fail the command if we are disconnecting */ - if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { + if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) { US_DEBUGP("Fail command during disconnect\n"); srb->result = DID_NO_CONNECT << 16; done(srb); @@ -248,7 +251,7 @@ static int queuecommand(struct scsi_cmnd *srb, /* enqueue the command and wake up the control thread */ srb->scsi_done = done; us->srb = srb; - up(&(us->sema)); + complete(&us->cmnd_ready); return 0; } @@ -280,9 +283,9 @@ static int command_abort(struct scsi_cmnd *srb) * with the reset). Note that we must retain the host lock while * calling usb_stor_stop_transport(); otherwise it might interfere * with an auto-reset that begins as soon as we release the lock. */ - set_bit(US_FLIDX_TIMED_OUT, &us->flags); - if (!test_bit(US_FLIDX_RESETTING, &us->flags)) { - set_bit(US_FLIDX_ABORTING, &us->flags); + set_bit(US_FLIDX_TIMED_OUT, &us->dflags); + if (!test_bit(US_FLIDX_RESETTING, &us->dflags)) { + set_bit(US_FLIDX_ABORTING, &us->dflags); usb_stor_stop_transport(us); } scsi_unlock(us_to_host(us)); @@ -329,7 +332,7 @@ void usb_stor_report_device_reset(struct us_data *us) struct Scsi_Host *host = us_to_host(us); scsi_report_device_reset(host, 0, 0); - if (us->flags & US_FL_SCM_MULT_TARG) { + if (us->fflags & US_FL_SCM_MULT_TARG) { for (i = 1; i < host->max_id; ++i) scsi_report_device_reset(host, 0, i); } @@ -400,7 +403,7 @@ static int proc_info (struct Scsi_Host *host, char *buffer, pos += sprintf(pos, " Quirks:"); #define US_FLAG(name, value) \ - if (us->flags & value) pos += sprintf(pos, " " #name); + if (us->fflags & value) pos += sprintf(pos, " " #name); US_DO_ALL_FLAGS #undef US_FLAG diff --git a/drivers/usb/storage/scsiglue.h b/drivers/usb/storage/scsiglue.h index 737e4fa6045f..ffa1cca93d2c 100644 --- a/drivers/usb/storage/scsiglue.h +++ b/drivers/usb/storage/scsiglue.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * SCSI Connecting Glue Header File * - * $Id: scsiglue.h,v 1.4 2000/08/25 00:13:51 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index 8972b17da843..c5a54b872c24 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -1,6 +1,5 @@ /* Driver for SanDisk SDDR-09 SmartMedia reader * - * $Id: sddr09.c,v 1.24 2002/04/22 03:39:43 mdharm Exp $ * (c) 2000, 2001 Robert Baruch (autophile@starband.net) * (c) 2002 Andries Brouwer (aeb@cwi.nl) * Developed with the assistance of: diff --git a/drivers/usb/storage/sddr09.h b/drivers/usb/storage/sddr09.h index c03089a9ec38..e50033ad7b19 100644 --- a/drivers/usb/storage/sddr09.h +++ b/drivers/usb/storage/sddr09.h @@ -1,8 +1,6 @@ /* Driver for SanDisk SDDR-09 SmartMedia reader * Header File * - * $Id: sddr09.h,v 1.5 2000/08/25 00:13:51 mdharm Exp $ - * * Current development and maintenance by: * (c) 2000 Robert Baruch (autophile@dol.net) * (c) 2002 Andries Brouwer (aeb@cwi.nl) diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c index 6d14327c921d..0d8df7577899 100644 --- a/drivers/usb/storage/sddr55.c +++ b/drivers/usb/storage/sddr55.c @@ -1,7 +1,5 @@ /* Driver for SanDisk SDDR-55 SmartMedia reader * - * $Id:$ - * * SDDR55 driver v0.1: * * First release diff --git a/drivers/usb/storage/sddr55.h b/drivers/usb/storage/sddr55.h index d6bd32f6c9f3..a815a0470c84 100644 --- a/drivers/usb/storage/sddr55.h +++ b/drivers/usb/storage/sddr55.h @@ -1,8 +1,6 @@ /* Driver for SanDisk SDDR-55 SmartMedia reader * Header File * - * $Id:$ - * * Current development and maintenance by: * (c) 2002 Simon Munton * diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c index 570c1250f6f3..ae6d64810d2a 100644 --- a/drivers/usb/storage/shuttle_usbat.c +++ b/drivers/usb/storage/shuttle_usbat.c @@ -1,7 +1,5 @@ /* Driver for SCM Microsystems (a.k.a. Shuttle) USB-ATAPI cable * - * $Id: shuttle_usbat.c,v 1.17 2002/04/22 03:39:43 mdharm Exp $ - * * Current development and maintenance by: * (c) 2000, 2001 Robert Baruch (autophile@starband.net) * (c) 2004, 2005 Daniel Drake <dsd@gentoo.org> diff --git a/drivers/usb/storage/shuttle_usbat.h b/drivers/usb/storage/shuttle_usbat.h index 3ddf143a1dec..d8bfc43e9044 100644 --- a/drivers/usb/storage/shuttle_usbat.h +++ b/drivers/usb/storage/shuttle_usbat.h @@ -1,8 +1,6 @@ /* Driver for SCM Microsystems USB-ATAPI cable * Header File * - * $Id: shuttle_usbat.h,v 1.5 2000/09/17 14:44:52 groovyjava Exp $ - * * Current development and maintenance by: * (c) 2000 Robert Baruch (autophile@dol.net) * (c) 2004, 2005 Daniel Drake <dsd@gentoo.org> diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 6610d2dd1e7f..fcbbfdb7b2b0 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1,7 +1,5 @@ /* Driver for USB Mass Storage compliant devices * - * $Id: transport.c,v 1.47 2002/04/22 03:39:43 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * @@ -75,14 +73,14 @@ * by a separate code path.) * * The abort function (usb_storage_command_abort() in scsiglue.c) first - * sets the machine state and the ABORTING bit in us->flags to prevent + * sets the machine state and the ABORTING bit in us->dflags to prevent * new URBs from being submitted. It then calls usb_stor_stop_transport() - * below, which atomically tests-and-clears the URB_ACTIVE bit in us->flags + * below, which atomically tests-and-clears the URB_ACTIVE bit in us->dflags * to see if the current_urb needs to be stopped. Likewise, the SG_ACTIVE * bit is tested to see if the current_sg scatter-gather request needs to be * stopped. The timeout callback routine does much the same thing. * - * When a disconnect occurs, the DISCONNECTING bit in us->flags is set to + * When a disconnect occurs, the DISCONNECTING bit in us->dflags is set to * prevent new URBs from being submitted, and usb_stor_stop_transport() is * called to stop any ongoing requests. * @@ -127,8 +125,8 @@ static int usb_stor_msg_common(struct us_data *us, int timeout) long timeleft; int status; - /* don't submit URBs during abort/disconnect processing */ - if (us->flags & ABORTING_OR_DISCONNECTING) + /* don't submit URBs during abort processing */ + if (test_bit(US_FLIDX_ABORTING, &us->dflags)) return -EIO; /* set up data structures for the wakeup system */ @@ -159,13 +157,13 @@ static int usb_stor_msg_common(struct us_data *us, int timeout) /* since the URB has been submitted successfully, it's now okay * to cancel it */ - set_bit(US_FLIDX_URB_ACTIVE, &us->flags); + set_bit(US_FLIDX_URB_ACTIVE, &us->dflags); - /* did an abort/disconnect occur during the submission? */ - if (us->flags & ABORTING_OR_DISCONNECTING) { + /* did an abort occur during the submission? */ + if (test_bit(US_FLIDX_ABORTING, &us->dflags)) { /* cancel the URB, if it hasn't been cancelled already */ - if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->flags)) { + if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags)) { US_DEBUGP("-- cancelling URB\n"); usb_unlink_urb(us->current_urb); } @@ -175,7 +173,7 @@ static int usb_stor_msg_common(struct us_data *us, int timeout) timeleft = wait_for_completion_interruptible_timeout( &urb_done, timeout ? : MAX_SCHEDULE_TIMEOUT); - clear_bit(US_FLIDX_URB_ACTIVE, &us->flags); + clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags); if (timeleft <= 0) { US_DEBUGP("%s -- cancelling URB\n", @@ -419,8 +417,8 @@ static int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe, { int result; - /* don't submit s-g requests during abort/disconnect processing */ - if (us->flags & ABORTING_OR_DISCONNECTING) + /* don't submit s-g requests during abort processing */ + if (test_bit(US_FLIDX_ABORTING, &us->dflags)) return USB_STOR_XFER_ERROR; /* initialize the scatter-gather request block */ @@ -435,13 +433,13 @@ static int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe, /* since the block has been initialized successfully, it's now * okay to cancel it */ - set_bit(US_FLIDX_SG_ACTIVE, &us->flags); + set_bit(US_FLIDX_SG_ACTIVE, &us->dflags); - /* did an abort/disconnect occur during the submission? */ - if (us->flags & ABORTING_OR_DISCONNECTING) { + /* did an abort occur during the submission? */ + if (test_bit(US_FLIDX_ABORTING, &us->dflags)) { /* cancel the request, if it hasn't been cancelled already */ - if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->flags)) { + if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->dflags)) { US_DEBUGP("-- cancelling sg request\n"); usb_sg_cancel(&us->current_sg); } @@ -449,7 +447,7 @@ static int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe, /* wait for the completion of the transfer */ usb_sg_wait(&us->current_sg); - clear_bit(US_FLIDX_SG_ACTIVE, &us->flags); + clear_bit(US_FLIDX_SG_ACTIVE, &us->dflags); result = us->current_sg.status; if (act_len) @@ -530,7 +528,7 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) /* if the command gets aborted by the higher layers, we need to * short-circuit all other processing */ - if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { + if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { US_DEBUGP("-- command was aborted\n"); srb->result = DID_ABORT << 16; goto Handle_Errors; @@ -616,7 +614,7 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) /* let's clean up right away */ scsi_eh_restore_cmnd(srb, &ses); - if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { + if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { US_DEBUGP("-- auto-sense aborted\n"); srb->result = DID_ABORT << 16; goto Handle_Errors; @@ -629,7 +627,7 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) * auto-sense is perfectly valid */ srb->result = DID_ERROR << 16; - if (!(us->flags & US_FL_SCM_MULT_TARG)) + if (!(us->fflags & US_FL_SCM_MULT_TARG)) goto Handle_Errors; return; } @@ -679,8 +677,8 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) /* Set the RESETTING bit, and clear the ABORTING bit so that * the reset may proceed. */ scsi_lock(us_to_host(us)); - set_bit(US_FLIDX_RESETTING, &us->flags); - clear_bit(US_FLIDX_ABORTING, &us->flags); + set_bit(US_FLIDX_RESETTING, &us->dflags); + clear_bit(US_FLIDX_ABORTING, &us->dflags); scsi_unlock(us_to_host(us)); /* We must release the device lock because the pre_reset routine @@ -695,7 +693,7 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) scsi_unlock(us_to_host(us)); us->transport_reset(us); } - clear_bit(US_FLIDX_RESETTING, &us->flags); + clear_bit(US_FLIDX_RESETTING, &us->dflags); } /* Stop the current URB transfer */ @@ -707,13 +705,13 @@ void usb_stor_stop_transport(struct us_data *us) * let's wake it up. The test_and_clear_bit() call * guarantees that if a URB has just been submitted, * it won't be cancelled more than once. */ - if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->flags)) { + if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags)) { US_DEBUGP("-- cancelling URB\n"); usb_unlink_urb(us->current_urb); } /* If we are waiting for a scatter-gather operation, cancel it. */ - if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->flags)) { + if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->dflags)) { US_DEBUGP("-- cancelling sg request\n"); usb_sg_cancel(&us->current_sg); } @@ -914,7 +912,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) unsigned int cbwlen = US_BULK_CB_WRAP_LEN; /* Take care of BULK32 devices; set extra byte to 0 */ - if ( unlikely(us->flags & US_FL_BULK32)) { + if (unlikely(us->fflags & US_FL_BULK32)) { cbwlen = 32; us->iobuf[31] = 0; } @@ -925,7 +923,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) bcb->Flags = srb->sc_data_direction == DMA_FROM_DEVICE ? 1 << 7 : 0; bcb->Tag = ++us->tag; bcb->Lun = srb->device->lun; - if (us->flags & US_FL_SCM_MULT_TARG) + if (us->fflags & US_FL_SCM_MULT_TARG) bcb->Lun |= srb->device->id << 4; bcb->Length = srb->cmd_len; @@ -951,7 +949,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) /* Some USB-IDE converter chips need a 100us delay between the * command phase and the data phase. Some devices need a little * more than that, probably because of clock rate inaccuracies. */ - if (unlikely(us->flags & US_FL_GO_SLOW)) + if (unlikely(us->fflags & US_FL_GO_SLOW)) udelay(125); if (transfer_length) { @@ -1010,7 +1008,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) US_DEBUGP("Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n", le32_to_cpu(bcs->Signature), bcs->Tag, residue, bcs->Status); - if (!(bcs->Tag == us->tag || (us->flags & US_FL_BULK_IGNORE_TAG)) || + if (!(bcs->Tag == us->tag || (us->fflags & US_FL_BULK_IGNORE_TAG)) || bcs->Status > US_BULK_STAT_PHASE) { US_DEBUGP("Bulk logical error\n"); return USB_STOR_TRANSPORT_ERROR; @@ -1035,7 +1033,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) /* try to compute the actual residue, based on how much data * was really transferred and what the device tells us */ if (residue) { - if (!(us->flags & US_FL_IGNORE_RESIDUE)) { + if (!(us->fflags & US_FL_IGNORE_RESIDUE)) { residue = min(residue, transfer_length); scsi_set_resid(srb, max(scsi_get_resid(srb), (int) residue)); @@ -1090,7 +1088,7 @@ static int usb_stor_reset_common(struct us_data *us, int result; int result2; - if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { + if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) { US_DEBUGP("No reset during disconnect\n"); return -EIO; } @@ -1103,12 +1101,12 @@ static int usb_stor_reset_common(struct us_data *us, return result; } - /* Give the device some time to recover from the reset, - * but don't delay disconnect processing. */ - wait_event_interruptible_timeout(us->delay_wait, - test_bit(US_FLIDX_DISCONNECTING, &us->flags), - HZ*6); - if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { + /* Give the device some time to recover from the reset, + * but don't delay disconnect processing. */ + wait_event_interruptible_timeout(us->delay_wait, + test_bit(US_FLIDX_DISCONNECTING, &us->dflags), + HZ*6); + if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) { US_DEBUGP("Reset interrupted by disconnect\n"); return -EIO; } @@ -1170,13 +1168,12 @@ int usb_stor_port_reset(struct us_data *us) US_DEBUGP("unable to lock device for reset: %d\n", result); else { /* Were we disconnected while waiting for the lock? */ - if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { + if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) { result = -EIO; US_DEBUGP("No reset during disconnect\n"); } else { - result = usb_reset_composite_device( - us->pusb_dev, us->pusb_intf); - US_DEBUGP("usb_reset_composite_device returns %d\n", + result = usb_reset_device(us->pusb_dev); + US_DEBUGP("usb_reset_device returns %d\n", result); } if (rc_lock) diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h index ada7c2f43f84..e70b88182f0e 100644 --- a/drivers/usb/storage/transport.h +++ b/drivers/usb/storage/transport.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Transport Functions Header File * - * $Id: transport.h,v 1.18 2002/04/21 02:57:59 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 39a7c11795c4..7ae69f55aa96 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Unusual Devices File * - * $Id: unusual_devs.h,v 1.32 2002/02/25 02:41:24 mdharm Exp $ - * * Current development and maintenance by: * (c) 2000-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * @@ -1234,6 +1232,17 @@ UNUSUAL_DEV( 0x0851, 0x1543, 0x0200, 0x0200, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE), +/* Andrew Lunn <andrew@lunn.ch> + * PanDigital Digital Picture Frame. Does not like ALLOW_MEDIUM_REMOVAL + * on LUN 4. + * Note: Vend:Prod clash with "Ltd Maxell WS30 Slim Digital Camera" +*/ +UNUSUAL_DEV( 0x0851, 0x1543, 0x0200, 0x0200, + "PanDigital", + "Photo Frame", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_NOT_LOCKABLE), + /* Submitted by Jan De Luyck <lkml@kcore.org> */ UNUSUAL_DEV( 0x08bd, 0x1100, 0x0000, 0x0000, "CITIZEN", diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index e268aacb773a..bfea851be985 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -1,7 +1,5 @@ /* Driver for USB Mass Storage compliant devices * - * $Id: usb.c,v 1.75 2002/04/22 03:39:43 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999-2003 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * @@ -312,26 +310,27 @@ static int usb_stor_control_thread(void * __us) for(;;) { US_DEBUGP("*** thread sleeping.\n"); - if(down_interruptible(&us->sema)) + if (wait_for_completion_interruptible(&us->cmnd_ready)) break; - + US_DEBUGP("*** thread awakened.\n"); /* lock the device pointers */ mutex_lock(&(us->dev_mutex)); - /* if the device has disconnected, we are free to exit */ - if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { - US_DEBUGP("-- exiting\n"); + /* lock access to the state */ + scsi_lock(host); + + /* When we are called with no command pending, we're done */ + if (us->srb == NULL) { + scsi_unlock(host); mutex_unlock(&us->dev_mutex); + US_DEBUGP("-- exiting\n"); break; } - /* lock access to the state */ - scsi_lock(host); - /* has the command timed out *already* ? */ - if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { + if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { us->srb->result = DID_ABORT << 16; goto SkipForAbort; } @@ -350,7 +349,7 @@ static int usb_stor_control_thread(void * __us) * the maximum known LUN */ else if (us->srb->device->id && - !(us->flags & US_FL_SCM_MULT_TARG)) { + !(us->fflags & US_FL_SCM_MULT_TARG)) { US_DEBUGP("Bad target number (%d:%d)\n", us->srb->device->id, us->srb->device->lun); us->srb->result = DID_BAD_TARGET << 16; @@ -365,7 +364,7 @@ static int usb_stor_control_thread(void * __us) /* Handle those devices which need us to fake * their inquiry data */ else if ((us->srb->cmnd[0] == INQUIRY) && - (us->flags & US_FL_FIX_INQUIRY)) { + (us->fflags & US_FL_FIX_INQUIRY)) { unsigned char data_ptr[36] = { 0x00, 0x80, 0x02, 0x02, 0x1F, 0x00, 0x00, 0x00}; @@ -384,12 +383,8 @@ static int usb_stor_control_thread(void * __us) /* lock access to the state */ scsi_lock(host); - /* did the command already complete because of a disconnect? */ - if (!us->srb) - ; /* nothing to do */ - /* indicate that the command is done */ - else if (us->srb->result != DID_ABORT << 16) { + if (us->srb->result != DID_ABORT << 16) { US_DEBUGP("scsi cmd done, result=0x%x\n", us->srb->result); us->srb->scsi_done(us->srb); @@ -403,12 +398,12 @@ SkipForAbort: * the TIMED_OUT flag, not srb->result == DID_ABORT, because * the timeout might have occurred after the command had * already completed with a different result code. */ - if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { + if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { complete(&(us->notify)); /* Allow USB transfers to resume */ - clear_bit(US_FLIDX_ABORTING, &us->flags); - clear_bit(US_FLIDX_TIMED_OUT, &us->flags); + clear_bit(US_FLIDX_ABORTING, &us->dflags); + clear_bit(US_FLIDX_TIMED_OUT, &us->dflags); } /* finished working on this command */ @@ -500,9 +495,9 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id) us->protocol = (unusual_dev->useTransport == US_PR_DEVICE) ? idesc->bInterfaceProtocol : unusual_dev->useTransport; - us->flags = USB_US_ORIG_FLAGS(id->driver_info); + us->fflags = USB_US_ORIG_FLAGS(id->driver_info); - if (us->flags & US_FL_IGNORE_DEVICE) { + if (us->fflags & US_FL_IGNORE_DEVICE) { printk(KERN_INFO USB_STORAGE "device ignored\n"); return -ENODEV; } @@ -512,7 +507,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id) * disable it if we're in full-speed */ if (dev->speed != USB_SPEED_HIGH) - us->flags &= ~US_FL_GO_SLOW; + us->fflags &= ~US_FL_GO_SLOW; /* Log a message if a non-generic unusual_dev entry contains an * unnecessary subclass or protocol override. This may stimulate @@ -533,7 +528,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id) if (unusual_dev->useTransport != US_PR_DEVICE && us->protocol == idesc->bInterfaceProtocol) msg += 2; - if (msg >= 0 && !(us->flags & US_FL_NEED_OVERRIDE)) + if (msg >= 0 && !(us->fflags & US_FL_NEED_OVERRIDE)) printk(KERN_NOTICE USB_STORAGE "This device " "(%04x,%04x,%04x S %02x P %02x)" " has %s in unusual_devs.h (kernel" @@ -663,7 +658,7 @@ static int get_transport(struct us_data *us) US_DEBUGP("Transport: %s\n", us->transport_name); /* fix for single-lun devices */ - if (us->flags & US_FL_SINGLE_LUN) + if (us->fflags & US_FL_SINGLE_LUN) us->max_lun = 0; return 0; } @@ -820,12 +815,11 @@ static void usb_stor_release_resources(struct us_data *us) US_DEBUGP("-- %s\n", __func__); /* Tell the control thread to exit. The SCSI host must - * already have been removed so it won't try to queue - * any more commands. + * already have been removed and the DISCONNECTING flag set + * so that we won't accept any more commands. */ US_DEBUGP("-- sending exit command to thread\n"); - set_bit(US_FLIDX_DISCONNECTING, &us->flags); - up(&us->sema); + complete(&us->cmnd_ready); if (us->ctl_thread) kthread_stop(us->ctl_thread); @@ -859,39 +853,36 @@ static void dissociate_dev(struct us_data *us) usb_set_intfdata(us->pusb_intf, NULL); } -/* First stage of disconnect processing: stop all commands and remove - * the host */ +/* First stage of disconnect processing: stop SCSI scanning, + * remove the host, and stop accepting new commands + */ static void quiesce_and_remove_host(struct us_data *us) { struct Scsi_Host *host = us_to_host(us); - /* Prevent new USB transfers, stop the current command, and - * interrupt a SCSI-scan or device-reset delay */ - scsi_lock(host); - set_bit(US_FLIDX_DISCONNECTING, &us->flags); - scsi_unlock(host); - usb_stor_stop_transport(us); - wake_up(&us->delay_wait); + /* If the device is really gone, cut short reset delays */ + if (us->pusb_dev->state == USB_STATE_NOTATTACHED) + set_bit(US_FLIDX_DISCONNECTING, &us->dflags); - /* queuecommand won't accept any new commands and the control - * thread won't execute a previously-queued command. If there - * is such a command pending, complete it with an error. */ - mutex_lock(&us->dev_mutex); - if (us->srb) { - us->srb->result = DID_NO_CONNECT << 16; - scsi_lock(host); - us->srb->scsi_done(us->srb); - us->srb = NULL; - complete(&us->notify); /* in case of an abort */ - scsi_unlock(host); - } - mutex_unlock(&us->dev_mutex); + /* Prevent SCSI-scanning (if it hasn't started yet) + * and wait for the SCSI-scanning thread to stop. + */ + set_bit(US_FLIDX_DONT_SCAN, &us->dflags); + wake_up(&us->delay_wait); + wait_for_completion(&us->scanning_done); - /* Now we own no commands so it's safe to remove the SCSI host */ + /* Removing the host will perform an orderly shutdown: caches + * synchronized, disks spun down, etc. + */ scsi_remove_host(host); - /* Wait for the SCSI-scanning thread to stop */ - wait_for_completion(&us->scanning_done); + /* Prevent any new commands from being accepted and cut short + * reset delays. + */ + scsi_lock(host); + set_bit(US_FLIDX_DISCONNECTING, &us->dflags); + scsi_unlock(host); + wake_up(&us->delay_wait); } /* Second stage of disconnect processing: deallocate all resources */ @@ -919,16 +910,16 @@ static int usb_stor_scan_thread(void * __us) printk(KERN_DEBUG "usb-storage: waiting for device " "to settle before scanning\n"); wait_event_freezable_timeout(us->delay_wait, - test_bit(US_FLIDX_DISCONNECTING, &us->flags), + test_bit(US_FLIDX_DONT_SCAN, &us->dflags), delay_use * HZ); } /* If the device is still connected, perform the scanning */ - if (!test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { + if (!test_bit(US_FLIDX_DONT_SCAN, &us->dflags)) { /* For bulk-only devices, determine the max LUN value */ if (us->protocol == US_PR_BULK && - !(us->flags & US_FL_SINGLE_LUN)) { + !(us->fflags & US_FL_SINGLE_LUN)) { mutex_lock(&us->dev_mutex); us->max_lun = usb_stor_Bulk_max_lun(us); mutex_unlock(&us->dev_mutex); @@ -975,7 +966,7 @@ static int storage_probe(struct usb_interface *intf, us = host_to_us(host); memset(us, 0, sizeof(struct us_data)); mutex_init(&(us->dev_mutex)); - init_MUTEX_LOCKED(&(us->sema)); + init_completion(&us->cmnd_ready); init_completion(&(us->notify)); init_waitqueue_head(&us->delay_wait); init_completion(&us->scanning_done); @@ -1023,6 +1014,7 @@ static int storage_probe(struct usb_interface *intf, if (IS_ERR(th)) { printk(KERN_WARNING USB_STORAGE "Unable to start the device-scanning thread\n"); + complete(&us->scanning_done); quiesce_and_remove_host(us); result = PTR_ERR(th); goto BadDevice; @@ -1065,6 +1057,7 @@ static struct usb_driver usb_storage_driver = { .pre_reset = storage_pre_reset, .post_reset = storage_post_reset, .id_table = storage_usb_ids, + .soft_unbind = 1, }; static int __init usb_stor_init(void) diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 8d87503e2560..a4ad73bd832d 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Main Header File * - * $Id: usb.h,v 1.21 2002/04/21 02:57:59 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * @@ -67,16 +65,14 @@ struct us_unusual_dev { }; -/* Dynamic flag definitions: used in set_bit() etc. */ -#define US_FLIDX_URB_ACTIVE 18 /* 0x00040000 current_urb is in use */ -#define US_FLIDX_SG_ACTIVE 19 /* 0x00080000 current_sg is in use */ -#define US_FLIDX_ABORTING 20 /* 0x00100000 abort is in progress */ -#define US_FLIDX_DISCONNECTING 21 /* 0x00200000 disconnect in progress */ -#define ABORTING_OR_DISCONNECTING ((1UL << US_FLIDX_ABORTING) | \ - (1UL << US_FLIDX_DISCONNECTING)) -#define US_FLIDX_RESETTING 22 /* 0x00400000 device reset in progress */ -#define US_FLIDX_TIMED_OUT 23 /* 0x00800000 SCSI midlayer timed out */ - +/* Dynamic bitflag definitions (us->dflags): used in set_bit() etc. */ +#define US_FLIDX_URB_ACTIVE 0 /* current_urb is in use */ +#define US_FLIDX_SG_ACTIVE 1 /* current_sg is in use */ +#define US_FLIDX_ABORTING 2 /* abort is in progress */ +#define US_FLIDX_DISCONNECTING 3 /* disconnect in progress */ +#define US_FLIDX_RESETTING 4 /* device reset in progress */ +#define US_FLIDX_TIMED_OUT 5 /* SCSI midlayer timed out */ +#define US_FLIDX_DONT_SCAN 6 /* don't scan (disconnect) */ #define USB_STOR_STRING_LEN 32 @@ -109,7 +105,8 @@ struct us_data { struct usb_device *pusb_dev; /* this usb_device */ struct usb_interface *pusb_intf; /* this interface */ struct us_unusual_dev *unusual_dev; /* device-filter entry */ - unsigned long flags; /* from filter initially */ + unsigned long fflags; /* fixed flags from filter */ + unsigned long dflags; /* dynamic atomic bitflags */ unsigned int send_bulk_pipe; /* cached pipe values */ unsigned int recv_bulk_pipe; unsigned int send_ctrl_pipe; @@ -147,7 +144,7 @@ struct us_data { struct task_struct *ctl_thread; /* the control thread */ /* mutual exclusion and synchronization structures */ - struct semaphore sema; /* to sleep thread on */ + struct completion cmnd_ready; /* to sleep thread on */ struct completion notify; /* thread begin/end */ wait_queue_head_t delay_wait; /* wait during scan, reset */ struct completion scanning_done; /* wait for scan thread */ diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 9b887ef64ff1..70d135e0cc47 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1658,6 +1658,32 @@ config FB_PM3 similar boards, 3DLabs Permedia3 Create!, Appian Jeronimo 2000 and maybe other boards. +config FB_CARMINE + tristate "Fujitsu carmine frame buffer support" + depends on FB && PCI + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This is the frame buffer device driver for the Fujitsu Carmine chip. + The driver provides two independent frame buffer devices. + +choice + depends on FB_CARMINE + prompt "DRAM timing" + default FB_CARMINE_DRAM_EVAL + +config FB_CARMINE_DRAM_EVAL + bool "Eval board timings" + help + Use timings which work on the eval card. + +config CARMINE_DRAM_CUSTOM + bool "Custom board timings" + help + Use custom board timings. +endchoice + config FB_AU1100 bool "Au1100 LCD Driver" depends on (FB = y) && MIPS && SOC_AU1100 @@ -1840,6 +1866,16 @@ config FB_W100 If unsure, say N. +config FB_SH_MOBILE_LCDC + tristate "SuperH Mobile LCDC framebuffer support" + depends on FB && SUPERH + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + default m + ---help--- + Frame buffer driver for the on-chip SH-Mobile LCD controller. + config FB_S3C2410 tristate "S3C2410 LCD framebuffer support" depends on FB && ARCH_S3C2410 @@ -1951,6 +1987,23 @@ config FB_AM200EPD This enables support for the Metronome display controller used on the E-Ink AM-200 EPD devkit. +config FB_COBALT + tristate "Cobalt server LCD frame buffer support" + depends on FB && MIPS_COBALT + +config FB_SH7760 + bool "SH7760/SH7763 LCDC support" + depends on FB && (CPU_SUBTYPE_SH7760 || CPU_SUBTYPE_SH7763) + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + Support for the SH7760/SH7763 integrated (D)STN/TFT LCD Controller. + Supports display resolutions up to 1024x1024 pixel, grayscale and + color operation, with depths ranging from 1 bpp to 8 bpp monochrome + and 8, 15 or 16 bpp color; 90 degrees clockwise display rotation for + panels <= 320 pixel horizontal resolution. + config FB_VIRTUAL tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)" depends on FB diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 04bca35403ff..0ebc1bfd2514 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -106,17 +106,22 @@ obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o obj-$(CONFIG_FB_MAXINE) += maxinefb.o obj-$(CONFIG_FB_METRONOME) += metronomefb.o obj-$(CONFIG_FB_S1D13XXX) += s1d13xxxfb.o +obj-$(CONFIG_FB_SH7760) += sh7760fb.o obj-$(CONFIG_FB_IMX) += imxfb.o obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o obj-$(CONFIG_FB_FSL_DIU) += fsl-diu-fb.o +obj-$(CONFIG_FB_COBALT) += cobalt_lcdfb.o 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 obj-$(CONFIG_FB_XILINX) += xilinxfb.o +obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o +obj-$(CONFIG_FB_SH7343VOU) += sh7343_voufb.o obj-$(CONFIG_FB_OMAP) += omap/ obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o +obj-$(CONFIG_FB_CARMINE) += carminefb.o # Platform or fallback drivers go here obj-$(CONFIG_FB_UVESA) += uvesafb.o diff --git a/drivers/video/acornfb.c b/drivers/video/acornfb.c index eedb8285e32f..017233d0c481 100644 --- a/drivers/video/acornfb.c +++ b/drivers/video/acornfb.c @@ -23,6 +23,7 @@ #include <linux/string.h> #include <linux/ctype.h> #include <linux/slab.h> +#include <linux/mm.h> #include <linux/init.h> #include <linux/fb.h> #include <linux/platform_device.h> diff --git a/drivers/video/am200epd.c b/drivers/video/am200epd.c index 51e26c1f5e8b..32dd85126931 100644 --- a/drivers/video/am200epd.c +++ b/drivers/video/am200epd.c @@ -221,7 +221,7 @@ static int am200_setup_irq(struct fb_info *info) return retval; } - return set_irq_type(IRQ_GPIO(RDY_GPIO_PIN), IRQT_FALLING); + return set_irq_type(IRQ_GPIO(RDY_GPIO_PIN), IRQ_TYPE_EDGE_FALLING); } static void am200_set_rst(struct metronomefb_par *par, int state) diff --git a/drivers/video/amifb.c b/drivers/video/amifb.c index 45c154ade9ca..b8e9a8682f2d 100644 --- a/drivers/video/amifb.c +++ b/drivers/video/amifb.c @@ -1136,7 +1136,6 @@ static int amifb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg * Interface to the low level console driver */ -int amifb_init(void); static void amifb_deinit(void); /* @@ -2048,13 +2047,16 @@ static void amifb_copyarea(struct fb_info *info, width = x2 - dx; height = y2 - dy; + if (area->sx + dx < area->dx || area->sy + dy < area->dy) + return; + /* update sx,sy */ sx = area->sx + (dx - area->dx); sy = area->sy + (dy - area->dy); /* the source must be completely inside the virtual screen */ - if (sx < 0 || sy < 0 || (sx + width) > info->var.xres_virtual || - (sy + height) > info->var.yres_virtual) + if (sx + width > info->var.xres_virtual || + sy + height > info->var.yres_virtual) return; if (dy > sy || (dy == sy && dx > sx)) { @@ -2245,7 +2247,7 @@ static inline void chipfree(void) * Initialisation */ -int __init amifb_init(void) +static int __init amifb_init(void) { int tag, i, err = 0; u_long chipptr; @@ -3790,16 +3792,14 @@ static void ami_rebuild_copper(void) } } - -module_init(amifb_init); - -#ifdef MODULE -MODULE_LICENSE("GPL"); - -void cleanup_module(void) +static void __exit amifb_exit(void) { unregister_framebuffer(&fb_info); amifb_deinit(); amifb_video_off(); } -#endif /* MODULE */ + +module_init(amifb_init); +module_exit(amifb_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/video/atafb.c b/drivers/video/atafb.c index fa55d356b535..77eb8b34fbfa 100644 --- a/drivers/video/atafb.c +++ b/drivers/video/atafb.c @@ -2593,13 +2593,16 @@ static void atafb_copyarea(struct fb_info *info, const struct fb_copyarea *area) width = x2 - dx; height = y2 - dy; + if (area->sx + dx < area->dx || area->sy + dy < area->dy) + return; + /* update sx,sy */ sx = area->sx + (dx - area->dx); sy = area->sy + (dy - area->dy); /* the source must be completely inside the virtual screen */ - if (sx < 0 || sy < 0 || (sx + width) > info->var.xres_virtual || - (sy + height) > info->var.yres_virtual) + if (sx + width > info->var.xres_virtual || + sy + height > info->var.yres_virtual) return; if (dy > sy || (dy == sy && dx > sx)) { diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index b004036d4087..5b3a15dffb5f 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -256,6 +256,20 @@ static int atmel_lcdfb_alloc_video_memory(struct atmel_lcdfb_info *sinfo) return 0; } +static const struct fb_videomode *atmel_lcdfb_choose_mode(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct fb_videomode varfbmode; + const struct fb_videomode *fbmode = NULL; + + fb_var_to_videomode(&varfbmode, var); + fbmode = fb_find_nearest_mode(&varfbmode, &info->modelist); + if (fbmode) + fb_videomode_to_var(var, fbmode); + return fbmode; +} + + /** * atmel_lcdfb_check_var - Validates a var passed in. * @var: frame buffer variable screen structure @@ -289,6 +303,15 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; dev_dbg(dev, "%s:\n", __func__); + + if (!(var->pixclock && var->bits_per_pixel)) { + /* choose a suitable mode if possible */ + if (!atmel_lcdfb_choose_mode(var, info)) { + dev_err(dev, "needed value not specified\n"); + return -EINVAL; + } + } + dev_dbg(dev, " resolution: %ux%u\n", var->xres, var->yres); dev_dbg(dev, " pixclk: %lu KHz\n", PICOS2KHZ(var->pixclock)); dev_dbg(dev, " bpp: %u\n", var->bits_per_pixel); @@ -299,6 +322,13 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, return -EINVAL; } + /* Do not allow to have real resoulution larger than virtual */ + if (var->xres > var->xres_virtual) + var->xres_virtual = var->xres; + + if (var->yres > var->yres_virtual) + var->yres_virtual = var->yres; + /* Force same alignment for each line */ var->xres = (var->xres + 3) & ~3UL; var->xres_virtual = (var->xres_virtual + 3) & ~3UL; @@ -379,6 +409,35 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, return 0; } +/* + * LCD reset sequence + */ +static void atmel_lcdfb_reset(struct atmel_lcdfb_info *sinfo) +{ + might_sleep(); + + /* LCD power off */ + lcdc_writel(sinfo, ATMEL_LCDC_PWRCON, sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET); + + /* wait for the LCDC core to become idle */ + while (lcdc_readl(sinfo, ATMEL_LCDC_PWRCON) & ATMEL_LCDC_BUSY) + msleep(10); + + /* DMA disable */ + lcdc_writel(sinfo, ATMEL_LCDC_DMACON, 0); + + /* wait for DMA engine to become idle */ + while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY) + msleep(10); + + /* LCD power on */ + lcdc_writel(sinfo, ATMEL_LCDC_PWRCON, + (sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET) | ATMEL_LCDC_PWR); + + /* DMA enable */ + lcdc_writel(sinfo, ATMEL_LCDC_DMACON, sinfo->default_dmacon); +} + /** * atmel_lcdfb_set_par - Alters the hardware state. * @info: frame buffer structure that represents a single frame buffer @@ -401,6 +460,8 @@ static int atmel_lcdfb_set_par(struct fb_info *info) unsigned long clk_value_khz; unsigned long bits_per_line; + might_sleep(); + dev_dbg(info->device, "%s:\n", __func__); dev_dbg(info->device, " * resolution: %ux%u (%ux%u virtual)\n", info->var.xres, info->var.yres, @@ -511,6 +572,8 @@ static int atmel_lcdfb_set_par(struct fb_info *info) /* Disable all interrupts */ lcdc_writel(sinfo, ATMEL_LCDC_IDR, ~0UL); + /* Enable FIFO & DMA errors */ + lcdc_writel(sinfo, ATMEL_LCDC_IER, ATMEL_LCDC_UFLWI | ATMEL_LCDC_OWRI | ATMEL_LCDC_MERI); /* ...wait for DMA engine to become idle... */ while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY) @@ -645,10 +708,26 @@ static irqreturn_t atmel_lcdfb_interrupt(int irq, void *dev_id) u32 status; status = lcdc_readl(sinfo, ATMEL_LCDC_ISR); - lcdc_writel(sinfo, ATMEL_LCDC_IDR, status); + if (status & ATMEL_LCDC_UFLWI) { + dev_warn(info->device, "FIFO underflow %#x\n", status); + /* reset DMA and FIFO to avoid screen shifting */ + schedule_work(&sinfo->task); + } + lcdc_writel(sinfo, ATMEL_LCDC_ICR, status); return IRQ_HANDLED; } +/* + * LCD controller task (to reset the LCD) + */ +static void atmel_lcdfb_task(struct work_struct *work) +{ + struct atmel_lcdfb_info *sinfo = + container_of(work, struct atmel_lcdfb_info, task); + + atmel_lcdfb_reset(sinfo); +} + static int __init atmel_lcdfb_init_fbinfo(struct atmel_lcdfb_info *sinfo) { struct fb_info *info = sinfo->info; @@ -691,6 +770,7 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) struct fb_info *info; struct atmel_lcdfb_info *sinfo; struct atmel_lcdfb_info *pdata_sinfo; + struct fb_videomode fbmode; struct resource *regs = NULL; struct resource *map = NULL; int ret; @@ -824,6 +904,10 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) goto unmap_mmio; } + /* Some operations on the LCDC might sleep and + * require a preemptible task context */ + INIT_WORK(&sinfo->task, atmel_lcdfb_task); + ret = atmel_lcdfb_init_fbinfo(sinfo); if (ret < 0) { dev_err(dev, "init fbinfo failed: %d\n", ret); @@ -853,6 +937,10 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) goto free_cmap; } + /* add selected videomode to modelist */ + fb_var_to_videomode(&fbmode, &info->var); + fb_add_videomode(&fbmode, &info->modelist); + /* Power up the LCDC screen */ if (sinfo->atmel_lcdfb_power_control) sinfo->atmel_lcdfb_power_control(1); @@ -866,6 +954,7 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) free_cmap: fb_dealloc_cmap(&info->cmap); unregister_irqs: + cancel_work_sync(&sinfo->task); free_irq(sinfo->irq_base, info); unmap_mmio: exit_backlight(sinfo); @@ -903,6 +992,7 @@ static int __exit atmel_lcdfb_remove(struct platform_device *pdev) if (!sinfo) return 0; + cancel_work_sync(&sinfo->task); exit_backlight(sinfo); if (sinfo->atmel_lcdfb_power_control) sinfo->atmel_lcdfb_power_control(0); diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c index 24ee96c4e9e9..243ea4ab20c8 100644 --- a/drivers/video/aty/aty128fb.c +++ b/drivers/video/aty/aty128fb.c @@ -1339,10 +1339,8 @@ static int aty128_var_to_pll(u32 period_in_ps, struct aty128_pll *pll, if (vclk * 12 < c.ppll_min) vclk = c.ppll_min/12; - pll->post_divider = -1; - /* now, find an acceptable divider */ - for (i = 0; i < sizeof(post_dividers); i++) { + for (i = 0; i < ARRAY_SIZE(post_dividers); i++) { output_freq = post_dividers[i] * vclk; if (output_freq >= c.ppll_min && output_freq <= c.ppll_max) { pll->post_divider = post_dividers[i]; @@ -1350,7 +1348,7 @@ static int aty128_var_to_pll(u32 period_in_ps, struct aty128_pll *pll, } } - if (pll->post_divider < 0) + if (i == ARRAY_SIZE(post_dividers)) return -EINVAL; /* calculate feedback divider */ @@ -1872,7 +1870,7 @@ static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_i struct fb_info *info = pci_get_drvdata(pdev); struct aty128fb_par *par = info->par; struct fb_var_screeninfo var; - char video_card[DEVICE_NAME_SIZE]; + char video_card[50]; u8 chip_rev; u32 dac; diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index bd4ac0bafecb..620ba8120368 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -424,7 +424,6 @@ static struct { #endif /* CONFIG_FB_ATY_CT */ }; -/* can not fail */ static int __devinit correct_chipset(struct atyfb_par *par) { u8 rev; @@ -437,6 +436,9 @@ static int __devinit correct_chipset(struct atyfb_par *par) if (par->pci_id == aty_chips[i].pci_id) break; + if (i < 0) + return -ENODEV; + name = aty_chips[i].name; par->pll_limits.pll_max = aty_chips[i].pll; par->pll_limits.mclk = aty_chips[i].mclk; @@ -2229,6 +2231,7 @@ static int __devinit aty_init(struct fb_info *info) const char *ramname = NULL, *xtal; int gtb_memsize, has_var = 0; struct fb_var_screeninfo var; + int ret; init_waitqueue_head(&par->vblank.wait); spin_lock_init(&par->int_lock); @@ -2610,7 +2613,8 @@ static int __devinit aty_init(struct fb_info *info) var.yres_virtual = var.yres; } - if (atyfb_check_var(&var, info)) { + ret = atyfb_check_var(&var, info); + if (ret) { PRINTKE("can't set default video mode\n"); goto aty_init_exit; } @@ -2621,10 +2625,12 @@ static int __devinit aty_init(struct fb_info *info) #endif /* CONFIG_FB_ATY_CT */ info->var = var; - if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret < 0) goto aty_init_exit; - if (register_framebuffer(info) < 0) { + ret = register_framebuffer(info); + if (ret < 0) { fb_dealloc_cmap(&info->cmap); goto aty_init_exit; } @@ -2650,7 +2656,7 @@ aty_init_exit: par->mtrr_aper = -1; } #endif - return -1; + return ret; } static void aty_resume_chip(struct fb_info *info) @@ -2709,8 +2715,7 @@ static int atyfb_blank(int blank, struct fb_info *info) if (par->lock_blank || par->asleep) return 0; -#ifdef CONFIG_FB_ATY_BACKLIGHT -#elif defined(CONFIG_FB_ATY_GENERIC_LCD) +#ifdef CONFIG_FB_ATY_GENERIC_LCD if (par->lcd_table && blank > FB_BLANK_NORMAL && (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) { u32 pm = aty_ld_lcd(POWER_MANAGEMENT, par); @@ -2739,8 +2744,7 @@ static int atyfb_blank(int blank, struct fb_info *info) } aty_st_le32(CRTC_GEN_CNTL, gen_cntl, par); -#ifdef CONFIG_FB_ATY_BACKLIGHT -#elif defined(CONFIG_FB_ATY_GENERIC_LCD) +#ifdef CONFIG_FB_ATY_GENERIC_LCD if (par->lcd_table && blank <= FB_BLANK_NORMAL && (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) { u32 pm = aty_ld_lcd(POWER_MANAGEMENT, par); @@ -3331,7 +3335,7 @@ static int __devinit init_from_bios(struct atyfb_par *par) PRINTKE("no BIOS frequency table found, use parameters\n"); ret = -ENXIO; } - iounmap((void* __iomem )bios_base); + iounmap((void __iomem *)bios_base); return ret; } @@ -3418,14 +3422,7 @@ static int __devinit atyfb_pci_probe(struct pci_dev *pdev, const struct pci_devi struct fb_info *info; struct resource *rp; struct atyfb_par *par; - int i, rc = -ENOMEM; - - for (i = ARRAY_SIZE(aty_chips) - 1; i >= 0; i--) - if (pdev->device == aty_chips[i].pci_id) - break; - - if (i < 0) - return -ENODEV; + int rc = -ENOMEM; /* Enable device in PCI config */ if (pci_enable_device(pdev)) { @@ -3456,7 +3453,7 @@ static int __devinit atyfb_pci_probe(struct pci_dev *pdev, const struct pci_devi par = info->par; info->fix = atyfb_fix; info->device = &pdev->dev; - par->pci_id = aty_chips[i].pci_id; + par->pci_id = pdev->device; par->res_start = res_start; par->res_size = res_size; par->irq = pdev->irq; @@ -3474,7 +3471,8 @@ static int __devinit atyfb_pci_probe(struct pci_dev *pdev, const struct pci_devi pci_set_drvdata(pdev, info); /* Init chip & register framebuffer */ - if (aty_init(info)) + rc = aty_init(info); + if (rc) goto err_release_io; #ifdef __sparc__ @@ -3655,18 +3653,62 @@ static void __devexit atyfb_pci_remove(struct pci_dev *pdev) atyfb_remove(info); } -/* - * This driver uses its own matching table. That will be more difficult - * to fix, so for now, we just match against any ATI ID and let the - * probe() function find out what's up. That also mean we don't have - * a module ID table though. - */ static struct pci_device_id atyfb_pci_tbl[] = { - { PCI_VENDOR_ID_ATI, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, - PCI_BASE_CLASS_DISPLAY << 16, 0xff0000, 0 }, - { 0, } +#ifdef CONFIG_FB_ATY_GX + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GX) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64CX) }, +#endif /* CONFIG_FB_ATY_GX */ + +#ifdef CONFIG_FB_ATY_CT + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64CT) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64ET) }, + + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LT) }, + + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64VT) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GT) }, + + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64VU) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GU) }, + + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LG) }, + + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64VV) }, + + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GV) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GW) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GY) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GZ) }, + + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GB) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GD) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GI) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GP) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GQ) }, + + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LB) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LD) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LI) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LP) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LQ) }, + + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GM) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GN) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GO) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GL) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GR) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GS) }, + + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LM) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LN) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LR) }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LS) }, +#endif /* CONFIG_FB_ATY_CT */ + { } }; +MODULE_DEVICE_TABLE(pci, atyfb_pci_tbl); + static struct pci_driver atyfb_driver = { .name = "atyfb", .id_table = atyfb_pci_tbl, diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c index 400e9264e456..652273e9f5f9 100644 --- a/drivers/video/aty/radeon_base.c +++ b/drivers/video/aty/radeon_base.c @@ -2098,15 +2098,7 @@ static void radeon_identify_vram(struct radeonfb_info *rinfo) static ssize_t radeon_show_one_edid(char *buf, loff_t off, size_t count, const u8 *edid) { - if (off > EDID_LENGTH) - return 0; - - if (off + count > EDID_LENGTH) - count = EDID_LENGTH - off; - - memcpy(buf, edid + off, count); - - return count; + return memory_read_from_buffer(buf, count, &off, edid, EDID_LENGTH); } @@ -2161,6 +2153,7 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev, struct radeonfb_info *rinfo; int ret; unsigned char c1, c2; + int err = 0; pr_debug("radeonfb_pci_register BEGIN\n"); @@ -2340,9 +2333,14 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev, /* Register some sysfs stuff (should be done better) */ if (rinfo->mon1_EDID) - sysfs_create_bin_file(&rinfo->pdev->dev.kobj, &edid1_attr); + err |= sysfs_create_bin_file(&rinfo->pdev->dev.kobj, + &edid1_attr); if (rinfo->mon2_EDID) - sysfs_create_bin_file(&rinfo->pdev->dev.kobj, &edid2_attr); + err |= sysfs_create_bin_file(&rinfo->pdev->dev.kobj, + &edid2_attr); + if (err) + pr_warning("%s() Creating sysfs files failed, continuing\n", + __func__); /* save current mode regs before we switch into the new one * so we can restore this upon __exit diff --git a/drivers/video/aty/radeonfb.h b/drivers/video/aty/radeonfb.h index c347e38cd0b0..ccbfffd12805 100644 --- a/drivers/video/aty/radeonfb.h +++ b/drivers/video/aty/radeonfb.h @@ -289,7 +289,7 @@ struct radeonfb_info { struct radeon_regs state; struct radeon_regs init_state; - char name[DEVICE_NAME_SIZE]; + char name[50]; unsigned long mmio_base_phys; unsigned long fb_base_phys; diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 30bf7f2f1635..452b770d8cc9 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -36,6 +36,30 @@ config LCD_LTV350QV The LTV350QV panel is present on all ATSTK1000 boards. +config LCD_ILI9320 + tristate + depends on LCD_CLASS_DEVICE && BACKLIGHT_LCD_SUPPORT + default n + help + If you have a panel based on the ILI9320 controller chip + then say y to include a power driver for it. + +config LCD_VGG2432A4 + tristate "VGG2432A4 LCM device support" + depends on BACKLIGHT_LCD_SUPPORT && LCD_CLASS_DEVICE && SPI_MASTER + select LCD_ILI9320 + default n + help + If you have a VGG2432A4 panel based on the ILI9320 controller chip + then say y to include a power driver for it. + +config LCD_PLATFORM + tristate "Platform LCD controls" + depends on LCD_CLASS_DEVICE + help + This driver provides a platform-device registered LCD power + control interface. + # # Backlight # @@ -63,6 +87,18 @@ config BACKLIGHT_ATMEL_LCDC If in doubt, it's safe to enable this option; it doesn't kick in unless the board's description says it's wired that way. +config BACKLIGHT_ATMEL_PWM + tristate "Atmel PWM backlight control" + depends on BACKLIGHT_CLASS_DEVICE && ATMEL_PWM + default n + help + Say Y here if you want to use the PWM peripheral in Atmel AT91 and + AVR32 devices. This driver will need additional platform data to know + which PWM instance to use and how to configure it. + + To compile this driver as a module, choose M here: the module will be + called atmel-pwm-bl. + config BACKLIGHT_CORGI tristate "Generic (aka Sharp Corgi) Backlight Driver" depends on BACKLIGHT_CLASS_DEVICE @@ -119,3 +155,12 @@ config BACKLIGHT_PWM help If you have a LCD backlight adjustable by PWM, say Y to enable this driver. + +config BACKLIGHT_MBP_NVIDIA + tristate "MacBook Pro Nvidia Backlight Driver" + depends on BACKLIGHT_CLASS_DEVICE && X86 + default n + help + If you have an Apple Macbook Pro with Nvidia graphics hardware say Y + to enable a driver for its backlight + diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index b51a7cd12500..b405aace803f 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -1,9 +1,13 @@ # Backlight & LCD drivers obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o -obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o +obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o +obj-$(CONFIG_LCD_ILI9320) += ili9320.o +obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o +obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o +obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o obj-$(CONFIG_BACKLIGHT_CORGI) += corgi_bl.o obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o @@ -11,3 +15,5 @@ obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o +obj-$(CONFIG_BACKLIGHT_MBP_NVIDIA) += mbp_nvidia_bl.o + diff --git a/drivers/video/backlight/atmel-pwm-bl.c b/drivers/video/backlight/atmel-pwm-bl.c new file mode 100644 index 000000000000..505c0823a105 --- /dev/null +++ b/drivers/video/backlight/atmel-pwm-bl.c @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2008 Atmel Corporation + * + * Backlight driver using Atmel PWM peripheral. + * + * 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/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/fb.h> +#include <linux/clk.h> +#include <linux/gpio.h> +#include <linux/backlight.h> +#include <linux/atmel_pwm.h> +#include <linux/atmel-pwm-bl.h> + +struct atmel_pwm_bl { + const struct atmel_pwm_bl_platform_data *pdata; + struct backlight_device *bldev; + struct platform_device *pdev; + struct pwm_channel pwmc; + int gpio_on; +}; + +static int atmel_pwm_bl_set_intensity(struct backlight_device *bd) +{ + struct atmel_pwm_bl *pwmbl = bl_get_data(bd); + int intensity = bd->props.brightness; + int pwm_duty; + + if (bd->props.power != FB_BLANK_UNBLANK) + intensity = 0; + if (bd->props.fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + + if (pwmbl->pdata->pwm_active_low) + pwm_duty = pwmbl->pdata->pwm_duty_min + intensity; + else + pwm_duty = pwmbl->pdata->pwm_duty_max - intensity; + + if (pwm_duty > pwmbl->pdata->pwm_duty_max) + pwm_duty = pwmbl->pdata->pwm_duty_max; + if (pwm_duty < pwmbl->pdata->pwm_duty_min) + pwm_duty = pwmbl->pdata->pwm_duty_min; + + if (!intensity) { + if (pwmbl->gpio_on != -1) { + gpio_set_value(pwmbl->gpio_on, + 0 ^ pwmbl->pdata->on_active_low); + } + pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty); + pwm_channel_disable(&pwmbl->pwmc); + } else { + pwm_channel_enable(&pwmbl->pwmc); + pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty); + if (pwmbl->gpio_on != -1) { + gpio_set_value(pwmbl->gpio_on, + 1 ^ pwmbl->pdata->on_active_low); + } + } + + return 0; +} + +static int atmel_pwm_bl_get_intensity(struct backlight_device *bd) +{ + struct atmel_pwm_bl *pwmbl = bl_get_data(bd); + u8 intensity; + + if (pwmbl->pdata->pwm_active_low) { + intensity = pwm_channel_readl(&pwmbl->pwmc, PWM_CDTY) - + pwmbl->pdata->pwm_duty_min; + } else { + intensity = pwmbl->pdata->pwm_duty_max - + pwm_channel_readl(&pwmbl->pwmc, PWM_CDTY); + } + + return intensity; +} + +static int atmel_pwm_bl_init_pwm(struct atmel_pwm_bl *pwmbl) +{ + unsigned long pwm_rate = pwmbl->pwmc.mck; + unsigned long prescale = DIV_ROUND_UP(pwm_rate, + (pwmbl->pdata->pwm_frequency * + pwmbl->pdata->pwm_compare_max)) - 1; + + /* + * Prescale must be power of two and maximum 0xf in size because of + * hardware limit. PWM speed will be: + * PWM module clock speed / (2 ^ prescale). + */ + prescale = fls(prescale); + if (prescale > 0xf) + prescale = 0xf; + + pwm_channel_writel(&pwmbl->pwmc, PWM_CMR, prescale); + pwm_channel_writel(&pwmbl->pwmc, PWM_CDTY, + pwmbl->pdata->pwm_duty_min + + pwmbl->bldev->props.brightness); + pwm_channel_writel(&pwmbl->pwmc, PWM_CPRD, + pwmbl->pdata->pwm_compare_max); + + dev_info(&pwmbl->pdev->dev, "Atmel PWM backlight driver " + "(%lu Hz)\n", pwmbl->pwmc.mck / + pwmbl->pdata->pwm_compare_max / + (1 << prescale)); + + return pwm_channel_enable(&pwmbl->pwmc); +} + +static struct backlight_ops atmel_pwm_bl_ops = { + .get_brightness = atmel_pwm_bl_get_intensity, + .update_status = atmel_pwm_bl_set_intensity, +}; + +static int atmel_pwm_bl_probe(struct platform_device *pdev) +{ + const struct atmel_pwm_bl_platform_data *pdata; + struct backlight_device *bldev; + struct atmel_pwm_bl *pwmbl; + int retval; + + pwmbl = kzalloc(sizeof(struct atmel_pwm_bl), GFP_KERNEL); + if (!pwmbl) + return -ENOMEM; + + pwmbl->pdev = pdev; + + pdata = pdev->dev.platform_data; + if (!pdata) { + retval = -ENODEV; + goto err_free_mem; + } + + if (pdata->pwm_compare_max < pdata->pwm_duty_max || + pdata->pwm_duty_min > pdata->pwm_duty_max || + pdata->pwm_frequency == 0) { + retval = -EINVAL; + goto err_free_mem; + } + + pwmbl->pdata = pdata; + pwmbl->gpio_on = pdata->gpio_on; + + retval = pwm_channel_alloc(pdata->pwm_channel, &pwmbl->pwmc); + if (retval) + goto err_free_mem; + + if (pwmbl->gpio_on != -1) { + retval = gpio_request(pwmbl->gpio_on, "gpio_atmel_pwm_bl"); + if (retval) { + pwmbl->gpio_on = -1; + goto err_free_pwm; + } + + /* Turn display off by defatult. */ + retval = gpio_direction_output(pwmbl->gpio_on, + 0 ^ pdata->on_active_low); + if (retval) + goto err_free_gpio; + } + + bldev = backlight_device_register("atmel-pwm-bl", + &pdev->dev, pwmbl, &atmel_pwm_bl_ops); + if (IS_ERR(bldev)) { + retval = PTR_ERR(bldev); + goto err_free_gpio; + } + + pwmbl->bldev = bldev; + + platform_set_drvdata(pdev, pwmbl); + + /* Power up the backlight by default at middle intesity. */ + bldev->props.power = FB_BLANK_UNBLANK; + bldev->props.max_brightness = pdata->pwm_duty_max - pdata->pwm_duty_min; + bldev->props.brightness = bldev->props.max_brightness / 2; + + retval = atmel_pwm_bl_init_pwm(pwmbl); + if (retval) + goto err_free_bl_dev; + + atmel_pwm_bl_set_intensity(bldev); + + return 0; + +err_free_bl_dev: + platform_set_drvdata(pdev, NULL); + backlight_device_unregister(bldev); +err_free_gpio: + if (pwmbl->gpio_on != -1) + gpio_free(pwmbl->gpio_on); +err_free_pwm: + pwm_channel_free(&pwmbl->pwmc); +err_free_mem: + kfree(pwmbl); + return retval; +} + +static int __exit atmel_pwm_bl_remove(struct platform_device *pdev) +{ + struct atmel_pwm_bl *pwmbl = platform_get_drvdata(pdev); + + if (pwmbl->gpio_on != -1) { + gpio_set_value(pwmbl->gpio_on, 0); + gpio_free(pwmbl->gpio_on); + } + pwm_channel_disable(&pwmbl->pwmc); + pwm_channel_free(&pwmbl->pwmc); + backlight_device_unregister(pwmbl->bldev); + platform_set_drvdata(pdev, NULL); + kfree(pwmbl); + + return 0; +} + +static struct platform_driver atmel_pwm_bl_driver = { + .driver = { + .name = "atmel-pwm-bl", + }, + /* REVISIT add suspend() and resume() */ + .remove = __exit_p(atmel_pwm_bl_remove), +}; + +static int __init atmel_pwm_bl_init(void) +{ + return platform_driver_probe(&atmel_pwm_bl_driver, atmel_pwm_bl_probe); +} +module_init(atmel_pwm_bl_init); + +static void __exit atmel_pwm_bl_exit(void) +{ + platform_driver_unregister(&atmel_pwm_bl_driver); +} +module_exit(atmel_pwm_bl_exit); + +MODULE_AUTHOR("Hans-Christian egtvedt <hans-christian.egtvedt@atmel.com>"); +MODULE_DESCRIPTION("Atmel PWM backlight driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index 39394757679c..fab0bc874b58 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -191,6 +191,7 @@ static struct device_attribute bl_device_attributes[] = { * backlight_device class. * @name: the name of the new object(must be the same as the name of the * respective framebuffer device). + * @parent: a pointer to the parent device * @devdata: an optional pointer to be stored for private driver use. The * methods may retrieve it by using bl_get_data(bd). * @ops: the backlight operations structure. diff --git a/drivers/video/backlight/ili9320.c b/drivers/video/backlight/ili9320.c new file mode 100644 index 000000000000..ba89b41b639c --- /dev/null +++ b/drivers/video/backlight/ili9320.c @@ -0,0 +1,330 @@ +/* drivers/video/backlight/ili9320.c + * + * ILI9320 LCD controller driver core. + * + * Copyright 2007 Simtec Electronics + * http://armlinux.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. +*/ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/lcd.h> +#include <linux/module.h> + +#include <linux/spi/spi.h> + +#include <video/ili9320.h> + +#include "ili9320.h" + + +static inline int ili9320_write_spi(struct ili9320 *ili, + unsigned int reg, + unsigned int value) +{ + struct ili9320_spi *spi = &ili->access.spi; + unsigned char *addr = spi->buffer_addr; + unsigned char *data = spi->buffer_data; + + /* spi message consits of: + * first byte: ID and operation + */ + + addr[0] = spi->id | ILI9320_SPI_INDEX | ILI9320_SPI_WRITE; + addr[1] = reg >> 8; + addr[2] = reg; + + /* second message is the data to transfer */ + + data[0] = spi->id | ILI9320_SPI_DATA | ILI9320_SPI_WRITE; + data[1] = value >> 8; + data[2] = value; + + return spi_sync(spi->dev, &spi->message); +} + +int ili9320_write(struct ili9320 *ili, unsigned int reg, unsigned int value) +{ + dev_dbg(ili->dev, "write: reg=%02x, val=%04x\n", reg, value); + return ili->write(ili, reg, value); +} + +EXPORT_SYMBOL_GPL(ili9320_write); + +int ili9320_write_regs(struct ili9320 *ili, + struct ili9320_reg *values, + int nr_values) +{ + int index; + int ret; + + for (index = 0; index < nr_values; index++, values++) { + ret = ili9320_write(ili, values->address, values->value); + if (ret != 0) + return ret; + } + + return 0; +} + +EXPORT_SYMBOL_GPL(ili9320_write_regs); + +static void ili9320_reset(struct ili9320 *lcd) +{ + struct ili9320_platdata *cfg = lcd->platdata; + + cfg->reset(1); + mdelay(50); + + cfg->reset(0); + mdelay(50); + + cfg->reset(1); + mdelay(100); +} + +static inline int ili9320_init_chip(struct ili9320 *lcd) +{ + int ret; + + ili9320_reset(lcd); + + ret = lcd->client->init(lcd, lcd->platdata); + if (ret != 0) { + dev_err(lcd->dev, "failed to initialise display\n"); + return ret; + } + + lcd->initialised = 1; + return 0; +} + +static inline int ili9320_power_on(struct ili9320 *lcd) +{ + if (!lcd->initialised) + ili9320_init_chip(lcd); + + lcd->display1 |= (ILI9320_DISPLAY1_D(3) | ILI9320_DISPLAY1_BASEE); + ili9320_write(lcd, ILI9320_DISPLAY1, lcd->display1); + + return 0; +} + +static inline int ili9320_power_off(struct ili9320 *lcd) +{ + lcd->display1 &= ~(ILI9320_DISPLAY1_D(3) | ILI9320_DISPLAY1_BASEE); + ili9320_write(lcd, ILI9320_DISPLAY1, lcd->display1); + + return 0; +} + +#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) + +static int ili9320_power(struct ili9320 *lcd, int power) +{ + int ret = 0; + + dev_dbg(lcd->dev, "power %d => %d\n", lcd->power, power); + + if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) + ret = ili9320_power_on(lcd); + else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) + ret = ili9320_power_off(lcd); + + if (ret == 0) + lcd->power = power; + else + dev_warn(lcd->dev, "failed to set power mode %d\n", power); + + return ret; +} + +static inline struct ili9320 *to_our_lcd(struct lcd_device *lcd) +{ + return lcd_get_data(lcd); +} + +static int ili9320_set_power(struct lcd_device *ld, int power) +{ + struct ili9320 *lcd = to_our_lcd(ld); + + return ili9320_power(lcd, power); +} + +static int ili9320_get_power(struct lcd_device *ld) +{ + struct ili9320 *lcd = to_our_lcd(ld); + + return lcd->power; +} + +static struct lcd_ops ili9320_ops = { + .get_power = ili9320_get_power, + .set_power = ili9320_set_power, +}; + +static void __devinit ili9320_setup_spi(struct ili9320 *ili, + struct spi_device *dev) +{ + struct ili9320_spi *spi = &ili->access.spi; + + ili->write = ili9320_write_spi; + spi->dev = dev; + + /* fill the two messages we are going to use to send the data + * with, the first the address followed by the data. The datasheet + * says they should be done as two distinct cycles of the SPI CS line. + */ + + spi->xfer[0].tx_buf = spi->buffer_addr; + spi->xfer[1].tx_buf = spi->buffer_data; + spi->xfer[0].len = 3; + spi->xfer[1].len = 3; + spi->xfer[0].bits_per_word = 8; + spi->xfer[1].bits_per_word = 8; + spi->xfer[0].cs_change = 1; + + spi_message_init(&spi->message); + spi_message_add_tail(&spi->xfer[0], &spi->message); + spi_message_add_tail(&spi->xfer[1], &spi->message); +} + +int __devinit ili9320_probe_spi(struct spi_device *spi, + struct ili9320_client *client) +{ + struct ili9320_platdata *cfg = spi->dev.platform_data; + struct device *dev = &spi->dev; + struct ili9320 *ili; + struct lcd_device *lcd; + int ret = 0; + + /* verify we where given some information */ + + if (cfg == NULL) { + dev_err(dev, "no platform data supplied\n"); + return -EINVAL; + } + + if (cfg->hsize <= 0 || cfg->vsize <= 0 || cfg->reset == NULL) { + dev_err(dev, "invalid platform data supplied\n"); + return -EINVAL; + } + + /* allocate and initialse our state */ + + ili = kzalloc(sizeof(struct ili9320), GFP_KERNEL); + if (ili == NULL) { + dev_err(dev, "no memory for device\n"); + return -ENOMEM; + } + + ili->access.spi.id = ILI9320_SPI_IDCODE | ILI9320_SPI_ID(1); + + ili->dev = dev; + ili->client = client; + ili->power = FB_BLANK_POWERDOWN; + ili->platdata = cfg; + + dev_set_drvdata(&spi->dev, ili); + + ili9320_setup_spi(ili, spi); + + lcd = lcd_device_register("ili9320", dev, ili, &ili9320_ops); + if (IS_ERR(lcd)) { + dev_err(dev, "failed to register lcd device\n"); + ret = PTR_ERR(lcd); + goto err_free; + } + + ili->lcd = lcd; + + dev_info(dev, "initialising %s\n", client->name); + + ret = ili9320_power(ili, FB_BLANK_UNBLANK); + if (ret != 0) { + dev_err(dev, "failed to set lcd power state\n"); + goto err_unregister; + } + + return 0; + + err_unregister: + lcd_device_unregister(lcd); + + err_free: + kfree(ili); + + return ret; +} + +EXPORT_SYMBOL_GPL(ili9320_probe_spi); + +int __devexit ili9320_remove(struct ili9320 *ili) +{ + ili9320_power(ili, FB_BLANK_POWERDOWN); + + lcd_device_unregister(ili->lcd); + kfree(ili); + + return 0; +} + +EXPORT_SYMBOL_GPL(ili9320_remove); + +#ifdef CONFIG_PM +int ili9320_suspend(struct ili9320 *lcd, pm_message_t state) +{ + int ret; + + dev_dbg(lcd->dev, "%s: event %d\n", __func__, state.event); + + if (state.event == PM_EVENT_SUSPEND) { + ret = ili9320_power(lcd, FB_BLANK_POWERDOWN); + + if (lcd->platdata->suspend == ILI9320_SUSPEND_DEEP) { + ili9320_write(lcd, ILI9320_POWER1, lcd->power1 | + ILI9320_POWER1_SLP | + ILI9320_POWER1_DSTB); + lcd->initialised = 0; + } + + return ret; + } + + return 0; +} + +EXPORT_SYMBOL_GPL(ili9320_suspend); + +int ili9320_resume(struct ili9320 *lcd) +{ + dev_info(lcd->dev, "resuming from power state %d\n", lcd->power); + + if (lcd->platdata->suspend == ILI9320_SUSPEND_DEEP) { + ili9320_write(lcd, ILI9320_POWER1, 0x00); + } + + return ili9320_power(lcd, FB_BLANK_UNBLANK); +} + +EXPORT_SYMBOL_GPL(ili9320_resume); +#endif + +/* Power down all displays on reboot, poweroff or halt */ +void ili9320_shutdown(struct ili9320 *lcd) +{ + ili9320_power(lcd, FB_BLANK_POWERDOWN); +} + +EXPORT_SYMBOL_GPL(ili9320_shutdown); + +MODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>"); +MODULE_DESCRIPTION("ILI9320 LCD Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/backlight/ili9320.h b/drivers/video/backlight/ili9320.h new file mode 100644 index 000000000000..e388eca7cac5 --- /dev/null +++ b/drivers/video/backlight/ili9320.h @@ -0,0 +1,80 @@ +/* drivers/video/backlight/ili9320.h + * + * ILI9320 LCD controller driver core. + * + * Copyright 2007 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * http://armlinux.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. +*/ + +/* Holder for register and value pairs. */ +struct ili9320_reg { + unsigned short address; + unsigned short value; +}; + +struct ili9320; + +struct ili9320_client { + const char *name; + int (*init)(struct ili9320 *ili, struct ili9320_platdata *cfg); + +}; +/* Device attached via an SPI bus. */ +struct ili9320_spi { + struct spi_device *dev; + struct spi_message message; + struct spi_transfer xfer[2]; + + unsigned char id; + unsigned char buffer_addr[4]; + unsigned char buffer_data[4]; +}; + +/* ILI9320 device state. */ +struct ili9320 { + union { + struct ili9320_spi spi; /* SPI attachged device. */ + } access; /* Register access method. */ + + struct device *dev; + struct lcd_device *lcd; /* LCD device we created. */ + struct ili9320_client *client; + struct ili9320_platdata *platdata; + + int power; /* current power state. */ + int initialised; + + unsigned short display1; + unsigned short power1; + + int (*write)(struct ili9320 *ili, unsigned int reg, unsigned int val); +}; + + +/* ILI9320 register access routines */ + +extern int ili9320_write(struct ili9320 *ili, + unsigned int reg, unsigned int value); + +extern int ili9320_write_regs(struct ili9320 *ili, + struct ili9320_reg *values, + int nr_values); + +/* Device probe */ + +extern int ili9320_probe_spi(struct spi_device *spi, + struct ili9320_client *cli); + +extern int ili9320_remove(struct ili9320 *lcd); +extern void ili9320_shutdown(struct ili9320 *lcd); + +/* PM */ + +extern int ili9320_suspend(struct ili9320 *lcd, pm_message_t state); +extern int ili9320_resume(struct ili9320 *lcd); diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index 299fd318dd45..b15b2b84a6f7 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -33,7 +33,7 @@ static int fb_notifier_callback(struct notifier_block *self, ld = container_of(self, struct lcd_device, fb_notif); mutex_lock(&ld->ops_lock); if (ld->ops) - if (!ld->ops->check_fb || ld->ops->check_fb(evdata->info)) + if (!ld->ops->check_fb || ld->ops->check_fb(ld, evdata->info)) ld->ops->set_power(ld, *(int *)evdata->data); mutex_unlock(&ld->ops_lock); return 0; diff --git a/drivers/video/backlight/mbp_nvidia_bl.c b/drivers/video/backlight/mbp_nvidia_bl.c new file mode 100644 index 000000000000..385cba40ea87 --- /dev/null +++ b/drivers/video/backlight/mbp_nvidia_bl.c @@ -0,0 +1,116 @@ +/* + * Backlight Driver for Nvidia 8600 in Macbook Pro + * + * Copyright (c) Red Hat <mjg@redhat.com> + * Based on code from Pommed: + * Copyright (C) 2006 Nicolas Boichat <nicolas @boichat.ch> + * Copyright (C) 2006 Felipe Alfaro Solana <felipe_alfaro @linuxmail.org> + * Copyright (C) 2007 Julien BLACHE <jb@jblache.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This driver triggers SMIs which cause the firmware to change the + * backlight brightness. This is icky in many ways, but it's impractical to + * get at the firmware code in order to figure out what it's actually doing. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/backlight.h> +#include <linux/err.h> +#include <linux/dmi.h> +#include <linux/io.h> + +static struct backlight_device *mbp_backlight_device; + +static struct dmi_system_id __initdata mbp_device_table[] = { + { + .ident = "3,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,1"), + }, + }, + { + .ident = "3,2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,2"), + }, + }, + { + .ident = "4,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4,1"), + }, + }, + { } +}; + +static int mbp_send_intensity(struct backlight_device *bd) +{ + int intensity = bd->props.brightness; + + outb(0x04 | (intensity << 4), 0xb3); + outb(0xbf, 0xb2); + + return 0; +} + +static int mbp_get_intensity(struct backlight_device *bd) +{ + outb(0x03, 0xb3); + outb(0xbf, 0xb2); + return inb(0xb3) >> 4; +} + +static struct backlight_ops mbp_ops = { + .get_brightness = mbp_get_intensity, + .update_status = mbp_send_intensity, +}; + +static int __init mbp_init(void) +{ + if (!dmi_check_system(mbp_device_table)) + return -ENODEV; + + if (!request_region(0xb2, 2, "Macbook Pro backlight")) + return -ENXIO; + + mbp_backlight_device = backlight_device_register("mbp_backlight", + NULL, NULL, + &mbp_ops); + if (IS_ERR(mbp_backlight_device)) { + release_region(0xb2, 2); + return PTR_ERR(mbp_backlight_device); + } + + mbp_backlight_device->props.max_brightness = 15; + mbp_backlight_device->props.brightness = + mbp_get_intensity(mbp_backlight_device); + backlight_update_status(mbp_backlight_device); + + return 0; +} + +static void __exit mbp_exit(void) +{ + backlight_device_unregister(mbp_backlight_device); + + release_region(0xb2, 2); +} + +module_init(mbp_init); +module_exit(mbp_exit); + +MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); +MODULE_DESCRIPTION("Nvidia-based Macbook Pro Backlight Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("svnAppleInc.:pnMacBookPro3,1"); +MODULE_ALIAS("svnAppleInc.:pnMacBookPro3,2"); +MODULE_ALIAS("svnAppleInc.:pnMacBookPro4,1"); diff --git a/drivers/video/backlight/platform_lcd.c b/drivers/video/backlight/platform_lcd.c new file mode 100644 index 000000000000..72d44dbfce82 --- /dev/null +++ b/drivers/video/backlight/platform_lcd.c @@ -0,0 +1,172 @@ +/* drivers/video/backlight/platform_lcd.c + * + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Generic platform-device LCD power control interface. + * + * 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/platform_device.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/lcd.h> + +#include <video/platform_lcd.h> + +struct platform_lcd { + struct device *us; + struct lcd_device *lcd; + struct plat_lcd_data *pdata; + + unsigned int power; + unsigned int suspended : 1; +}; + +static inline struct platform_lcd *to_our_lcd(struct lcd_device *lcd) +{ + return lcd_get_data(lcd); +} + +static int platform_lcd_get_power(struct lcd_device *lcd) +{ + struct platform_lcd *plcd = to_our_lcd(lcd); + + return plcd->power; +} + +static int platform_lcd_set_power(struct lcd_device *lcd, int power) +{ + struct platform_lcd *plcd = to_our_lcd(lcd); + int lcd_power = 1; + + if (power == FB_BLANK_POWERDOWN || plcd->suspended) + lcd_power = 0; + + plcd->pdata->set_power(plcd->pdata, lcd_power); + plcd->power = power; + + return 0; +} + +static int platform_lcd_match(struct lcd_device *lcd, struct fb_info *info) +{ + struct platform_lcd *plcd = to_our_lcd(lcd); + struct plat_lcd_data *pdata = plcd->pdata; + + if (pdata->match_fb) + return pdata->match_fb(pdata, info); + + return plcd->us->parent == info->device; +} + +static struct lcd_ops platform_lcd_ops = { + .get_power = platform_lcd_get_power, + .set_power = platform_lcd_set_power, + .check_fb = platform_lcd_match, +}; + +static int __devinit platform_lcd_probe(struct platform_device *pdev) +{ + struct plat_lcd_data *pdata; + struct platform_lcd *plcd; + struct device *dev = &pdev->dev; + int err; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(dev, "no platform data supplied\n"); + return -EINVAL; + } + + plcd = kzalloc(sizeof(struct platform_lcd), GFP_KERNEL); + if (!plcd) { + dev_err(dev, "no memory for state\n"); + return -ENOMEM; + } + + plcd->us = dev; + plcd->pdata = pdata; + plcd->lcd = lcd_device_register("platform-lcd", dev, + plcd, &platform_lcd_ops); + if (IS_ERR(plcd->lcd)) { + dev_err(dev, "cannot register lcd device\n"); + err = PTR_ERR(plcd->lcd); + goto err_mem; + } + + platform_set_drvdata(pdev, plcd); + return 0; + + err_mem: + kfree(plcd); + return err; +} + +static int __devexit platform_lcd_remove(struct platform_device *pdev) +{ + struct platform_lcd *plcd = platform_get_drvdata(pdev); + + lcd_device_unregister(plcd->lcd); + kfree(plcd); + + return 0; +} + +#ifdef CONFIG_PM +static int platform_lcd_suspend(struct platform_device *pdev, pm_message_t st) +{ + struct platform_lcd *plcd = platform_get_drvdata(pdev); + + plcd->suspended = 1; + platform_lcd_set_power(plcd->lcd, plcd->power); + + return 0; +} + +static int platform_lcd_resume(struct platform_device *pdev) +{ + struct platform_lcd *plcd = platform_get_drvdata(pdev); + + plcd->suspended = 0; + platform_lcd_set_power(plcd->lcd, plcd->power); + + return 0; +} +#else +#define platform_lcd_suspend NULL +#define platform_lcd_resume NULL +#endif + +static struct platform_driver platform_lcd_driver = { + .driver = { + .name = "platform-lcd", + .owner = THIS_MODULE, + }, + .probe = platform_lcd_probe, + .remove = __devexit_p(platform_lcd_remove), + .suspend = platform_lcd_suspend, + .resume = platform_lcd_resume, +}; + +static int __init platform_lcd_init(void) +{ + return platform_driver_register(&platform_lcd_driver); +} + +static void __exit platform_lcd_cleanup(void) +{ + platform_driver_unregister(&platform_lcd_driver); +} + +module_init(platform_lcd_init); +module_exit(platform_lcd_cleanup); + +MODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:platform-lcd"); diff --git a/drivers/video/backlight/vgg2432a4.c b/drivers/video/backlight/vgg2432a4.c new file mode 100644 index 000000000000..593c7687d54a --- /dev/null +++ b/drivers/video/backlight/vgg2432a4.c @@ -0,0 +1,284 @@ +/* drivers/video/backlight/vgg2432a4.c + * + * VGG2432A4 (ILI9320) LCD controller driver. + * + * Copyright 2007 Simtec Electronics + * http://armlinux.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. +*/ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/lcd.h> +#include <linux/module.h> + +#include <linux/spi/spi.h> + +#include <video/ili9320.h> + +#include "ili9320.h" + +/* Device initialisation sequences */ + +static struct ili9320_reg vgg_init1[] = { + { + .address = ILI9320_POWER1, + .value = ILI9320_POWER1_AP(0) | ILI9320_POWER1_BT(0), + }, { + .address = ILI9320_POWER2, + .value = (ILI9320_POWER2_VC(7) | + ILI9320_POWER2_DC0(0) | ILI9320_POWER2_DC1(0)), + }, { + .address = ILI9320_POWER3, + .value = ILI9320_POWER3_VRH(0), + }, { + .address = ILI9320_POWER4, + .value = ILI9320_POWER4_VREOUT(0), + }, +}; + +static struct ili9320_reg vgg_init2[] = { + { + .address = ILI9320_POWER1, + .value = (ILI9320_POWER1_AP(3) | ILI9320_POWER1_APE | + ILI9320_POWER1_BT(7) | ILI9320_POWER1_SAP), + }, { + .address = ILI9320_POWER2, + .value = ILI9320_POWER2_VC(7) | ILI9320_POWER2_DC0(3), + } +}; + +static struct ili9320_reg vgg_gamma[] = { + { + .address = ILI9320_GAMMA1, + .value = 0x0000, + }, { + .address = ILI9320_GAMMA2, + .value = 0x0505, + }, { + .address = ILI9320_GAMMA3, + .value = 0x0004, + }, { + .address = ILI9320_GAMMA4, + .value = 0x0006, + }, { + .address = ILI9320_GAMMA5, + .value = 0x0707, + }, { + .address = ILI9320_GAMMA6, + .value = 0x0105, + }, { + .address = ILI9320_GAMMA7, + .value = 0x0002, + }, { + .address = ILI9320_GAMMA8, + .value = 0x0707, + }, { + .address = ILI9320_GAMMA9, + .value = 0x0704, + }, { + .address = ILI9320_GAMMA10, + .value = 0x807, + } + +}; + +static struct ili9320_reg vgg_init0[] = { + [0] = { + /* set direction and scan mode gate */ + .address = ILI9320_DRIVER, + .value = ILI9320_DRIVER_SS, + }, { + .address = ILI9320_DRIVEWAVE, + .value = (ILI9320_DRIVEWAVE_MUSTSET | + ILI9320_DRIVEWAVE_EOR | ILI9320_DRIVEWAVE_BC), + }, { + .address = ILI9320_ENTRYMODE, + .value = ILI9320_ENTRYMODE_ID(3) | ILI9320_ENTRYMODE_BGR, + }, { + .address = ILI9320_RESIZING, + .value = 0x0, + }, +}; + + +static int vgg2432a4_lcd_init(struct ili9320 *lcd, + struct ili9320_platdata *cfg) +{ + unsigned int addr; + int ret; + + /* Set VCore before anything else (VGG243237-6UFLWA) */ + ret = ili9320_write(lcd, 0x00e5, 0x8000); + if (ret) + goto err_initial; + + /* Start the oscillator up before we can do anything else. */ + ret = ili9320_write(lcd, ILI9320_OSCILATION, ILI9320_OSCILATION_OSC); + if (ret) + goto err_initial; + + /* must wait at-lesat 10ms after starting */ + mdelay(15); + + ret = ili9320_write_regs(lcd, vgg_init0, ARRAY_SIZE(vgg_init0)); + if (ret != 0) + goto err_initial; + + ili9320_write(lcd, ILI9320_DISPLAY2, cfg->display2); + ili9320_write(lcd, ILI9320_DISPLAY3, cfg->display3); + ili9320_write(lcd, ILI9320_DISPLAY4, cfg->display4); + + ili9320_write(lcd, ILI9320_RGB_IF1, cfg->rgb_if1); + ili9320_write(lcd, ILI9320_FRAMEMAKER, 0x0); + ili9320_write(lcd, ILI9320_RGB_IF2, ILI9320_RGBIF2_DPL); + + ret = ili9320_write_regs(lcd, vgg_init1, ARRAY_SIZE(vgg_init1)); + if (ret != 0) + goto err_vgg; + + mdelay(300); + + ret = ili9320_write_regs(lcd, vgg_init2, ARRAY_SIZE(vgg_init2)); + if (ret != 0) + goto err_vgg2; + + mdelay(100); + + ili9320_write(lcd, ILI9320_POWER3, 0x13c); + + mdelay(100); + + ili9320_write(lcd, ILI9320_POWER4, 0x1c00); + ili9320_write(lcd, ILI9320_POWER7, 0x000e); + + mdelay(100); + + ili9320_write(lcd, ILI9320_GRAM_HORIZ_ADDR, 0x00); + ili9320_write(lcd, ILI9320_GRAM_VERT_ADD, 0x00); + + ret = ili9320_write_regs(lcd, vgg_gamma, ARRAY_SIZE(vgg_gamma)); + if (ret != 0) + goto err_vgg3; + + ili9320_write(lcd, ILI9320_HORIZ_START, 0x0); + ili9320_write(lcd, ILI9320_HORIZ_END, cfg->hsize - 1); + ili9320_write(lcd, ILI9320_VERT_START, 0x0); + ili9320_write(lcd, ILI9320_VERT_END, cfg->vsize - 1); + + ili9320_write(lcd, ILI9320_DRIVER2, + ILI9320_DRIVER2_NL(((cfg->vsize - 240) / 8) + 0x1D)); + + ili9320_write(lcd, ILI9320_BASE_IMAGE, 0x1); + ili9320_write(lcd, ILI9320_VERT_SCROLL, 0x00); + + for (addr = ILI9320_PARTIAL1_POSITION; addr <= ILI9320_PARTIAL2_END; + addr++) { + ili9320_write(lcd, addr, 0x0); + } + + ili9320_write(lcd, ILI9320_INTERFACE1, 0x10); + ili9320_write(lcd, ILI9320_INTERFACE2, cfg->interface2); + ili9320_write(lcd, ILI9320_INTERFACE3, cfg->interface3); + ili9320_write(lcd, ILI9320_INTERFACE4, cfg->interface4); + ili9320_write(lcd, ILI9320_INTERFACE5, cfg->interface5); + ili9320_write(lcd, ILI9320_INTERFACE6, cfg->interface6); + + lcd->display1 = (ILI9320_DISPLAY1_D(3) | ILI9320_DISPLAY1_DTE | + ILI9320_DISPLAY1_GON | ILI9320_DISPLAY1_BASEE | + 0x40); + + ili9320_write(lcd, ILI9320_DISPLAY1, lcd->display1); + + return 0; + + err_vgg3: + err_vgg2: + err_vgg: + err_initial: + return ret; +} + +#ifdef CONFIG_PM +static int vgg2432a4_suspend(struct spi_device *spi, pm_message_t state) +{ + return ili9320_suspend(dev_get_drvdata(&spi->dev), state); +} + +static int vgg2432a4_resume(struct spi_device *spi) +{ + return ili9320_resume(dev_get_drvdata(&spi->dev)); +} +#else +#define vgg2432a4_suspend NULL +#define vgg2432a4_resume NULL +#endif + +static struct ili9320_client vgg2432a4_client = { + .name = "VGG2432A4", + .init = vgg2432a4_lcd_init, +}; + +/* Device probe */ + +static int __devinit vgg2432a4_probe(struct spi_device *spi) +{ + int ret; + + ret = ili9320_probe_spi(spi, &vgg2432a4_client); + if (ret != 0) { + dev_err(&spi->dev, "failed to initialise ili9320\n"); + return ret; + } + + return 0; +} + +static int __devexit vgg2432a4_remove(struct spi_device *spi) +{ + return ili9320_remove(dev_get_drvdata(&spi->dev)); +} + +static void vgg2432a4_shutdown(struct spi_device *spi) +{ + ili9320_shutdown(dev_get_drvdata(&spi->dev)); +} + +static struct spi_driver vgg2432a4_driver = { + .driver = { + .name = "VGG2432A4", + .owner = THIS_MODULE, + }, + .probe = vgg2432a4_probe, + .remove = __devexit_p(vgg2432a4_remove), + .shutdown = vgg2432a4_shutdown, + .suspend = vgg2432a4_suspend, + .resume = vgg2432a4_resume, +}; + +/* Device driver initialisation */ + +static int __init vgg2432a4_init(void) +{ + return spi_register_driver(&vgg2432a4_driver); +} + +static void __exit vgg2432a4_exit(void) +{ + spi_unregister_driver(&vgg2432a4_driver); +} + +module_init(vgg2432a4_init); +module_exit(vgg2432a4_exit); + +MODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>"); +MODULE_DESCRIPTION("VGG2432A4 LCD Driver"); +MODULE_LICENSE("GPL v2"); + + diff --git a/drivers/video/bf54x-lq043fb.c b/drivers/video/bf54x-lq043fb.c index 49834a67a623..940467aed13f 100644 --- a/drivers/video/bf54x-lq043fb.c +++ b/drivers/video/bf54x-lq043fb.c @@ -478,7 +478,7 @@ static int bfin_lcd_set_contrast(struct lcd_device *dev, int contrast) return 0; } -static int bfin_lcd_check_fb(struct fb_info *fi) +static int bfin_lcd_check_fb(struct lcd_device *dev, struct fb_info *fi) { if (!fi || (fi == &bfin_bf54x_fb)) return 1; diff --git a/drivers/video/bfin-t350mcqb-fb.c b/drivers/video/bfin-t350mcqb-fb.c index 135d6dd7e672..7d1b819e501c 100644 --- a/drivers/video/bfin-t350mcqb-fb.c +++ b/drivers/video/bfin-t350mcqb-fb.c @@ -396,7 +396,7 @@ static int bfin_lcd_set_contrast(struct lcd_device *dev, int contrast) return 0; } -static int bfin_lcd_check_fb(struct fb_info *fi) +static int bfin_lcd_check_fb(struct lcd_device *dev, struct fb_info *fi) { if (!fi || (fi == &bfin_t350mcqb_fb)) return 1; diff --git a/drivers/video/carminefb.c b/drivers/video/carminefb.c new file mode 100644 index 000000000000..e15bb447440a --- /dev/null +++ b/drivers/video/carminefb.c @@ -0,0 +1,790 @@ +/* + * Frame buffer driver for the Carmine GPU. + * + * The driver configures the GPU as follows + * - FB0 is display 0 with unique memory area + * - FB1 is display 1 with unique memory area + * - both display use 32 bit colors + */ +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/fb.h> +#include <linux/interrupt.h> +#include <linux/pci.h> + +#include "carminefb.h" +#include "carminefb_regs.h" + +#if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN) +#error "The endianness of the target host has not been defined." +#endif + +/* + * The initial video mode can be supplied via two different ways: + * - as a string that is passed to fb_find_mode() (module option fb_mode_str) + * - as an integer that picks the video mode from carmine_modedb[] (module + * option fb_mode) + * + * If nothing is used than the initial video mode will be the + * CARMINEFB_DEFAULT_VIDEO_MODE member of the carmine_modedb[]. + */ +#define CARMINEFB_DEFAULT_VIDEO_MODE 1 + +static unsigned int fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE; +module_param(fb_mode, uint, 444); +MODULE_PARM_DESC(fb_mode, "Initial video mode as integer."); + +static char *fb_mode_str; +module_param(fb_mode_str, charp, 444); +MODULE_PARM_DESC(fb_mode_str, "Initial video mode in characters."); + +/* + * Carminefb displays: + * 0b000 None + * 0b001 Display 0 + * 0b010 Display 1 + */ +static int fb_displays = CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1; +module_param(fb_displays, int, 444); +MODULE_PARM_DESC(fb_displays, "Bit mode, which displays are used"); + +struct carmine_hw { + void __iomem *v_regs; + void __iomem *screen_mem; + struct fb_info *fb[MAX_DISPLAY]; +}; + +struct carmine_resolution { + u32 htp; + u32 hsp; + u32 hsw; + u32 hdp; + u32 vtr; + u32 vsp; + u32 vsw; + u32 vdp; + u32 disp_mode; +}; + +struct carmine_fb { + void __iomem *display_reg; + void __iomem *screen_base; + u32 smem_offset; + u32 cur_mode; + u32 new_mode; + struct carmine_resolution *res; + u32 pseudo_palette[16]; +}; + +static struct fb_fix_screeninfo carminefb_fix __devinitdata = { + .id = "Carmine", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .accel = FB_ACCEL_NONE, +}; + +static const struct fb_videomode carmine_modedb[] = { + { + .name = "640x480", + .xres = 640, + .yres = 480, + }, { + .name = "800x600", + .xres = 800, + .yres = 600, + }, +}; + +static struct carmine_resolution car_modes[] = { + { + /* 640x480 */ + .htp = 800, + .hsp = 672, + .hsw = 96, + .hdp = 640, + .vtr = 525, + .vsp = 490, + .vsw = 2, + .vdp = 480, + .disp_mode = 0x1400, + }, + { + /* 800x600 */ + .htp = 1060, + .hsp = 864, + .hsw = 72, + .hdp = 800, + .vtr = 628, + .vsp = 601, + .vsw = 2, + .vdp = 600, + .disp_mode = 0x0d00, + } +}; + +static int carmine_find_mode(const struct fb_var_screeninfo *var) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(car_modes); i++) + if (car_modes[i].hdp == var->xres && + car_modes[i].vdp == var->yres) + return i; + return -EINVAL; +} + +static void c_set_disp_reg(const struct carmine_fb *par, + u32 offset, u32 val) +{ + writel(val, par->display_reg + offset); +} + +static u32 c_get_disp_reg(const struct carmine_fb *par, + u32 offset) +{ + return readl(par->display_reg + offset); +} + +static void c_set_hw_reg(const struct carmine_hw *hw, + u32 offset, u32 val) +{ + writel(val, hw->v_regs + offset); +} + +static u32 c_get_hw_reg(const struct carmine_hw *hw, + u32 offset) +{ + return readl(hw->v_regs + offset); +} + +static int carmine_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, struct fb_info *info) +{ + if (regno >= 16) + return 1; + + red >>= 8; + green >>= 8; + blue >>= 8; + transp >>= 8; + + ((u32 *)info->pseudo_palette)[regno] = be32_to_cpu(transp << 24 | + red << 0 | green << 8 | blue << 16); + return 0; +} + +static int carmine_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + int ret; + + ret = carmine_find_mode(var); + if (ret < 0) + return ret; + + if (var->grayscale || var->rotate || var->nonstd) + return -EINVAL; + + var->xres_virtual = var->xres; + var->yres_virtual = var->yres; + + var->bits_per_pixel = 32; + +#ifdef __BIG_ENDIAN + var->transp.offset = 24; + var->red.offset = 0; + var->green.offset = 8; + var->blue.offset = 16; +#else + var->transp.offset = 24; + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; +#endif + + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 8; + + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + var->transp.msb_right = 0; + return 0; +} + +static void carmine_init_display_param(struct carmine_fb *par) +{ + u32 width; + u32 height; + u32 param; + u32 window_size; + u32 soffset = par->smem_offset; + + c_set_disp_reg(par, CARMINE_DISP_REG_C_TRANS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_MLMR_TRANS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_CURSOR_MODE, + CARMINE_CURSOR0_PRIORITY_MASK | + CARMINE_CURSOR1_PRIORITY_MASK | + CARMINE_CURSOR_CUTZ_MASK); + + /* Set default cursor position */ + c_set_disp_reg(par, CARMINE_DISP_REG_CUR1_POS, 0 << 16 | 0); + c_set_disp_reg(par, CARMINE_DISP_REG_CUR2_POS, 0 << 16 | 0); + + /* Set default display mode */ + c_set_disp_reg(par, CARMINE_DISP_REG_L0_EXT_MODE, CARMINE_WINDOW_MODE | + CARMINE_EXT_CMODE_DIRECT24_RGBA); + c_set_disp_reg(par, CARMINE_DISP_REG_L1_EXT_MODE, + CARMINE_EXT_CMODE_DIRECT24_RGBA); + c_set_disp_reg(par, CARMINE_DISP_REG_L2_EXT_MODE, CARMINE_EXTEND_MODE | + CARMINE_EXT_CMODE_DIRECT24_RGBA); + c_set_disp_reg(par, CARMINE_DISP_REG_L3_EXT_MODE, CARMINE_EXTEND_MODE | + CARMINE_EXT_CMODE_DIRECT24_RGBA); + c_set_disp_reg(par, CARMINE_DISP_REG_L4_EXT_MODE, CARMINE_EXTEND_MODE | + CARMINE_EXT_CMODE_DIRECT24_RGBA); + c_set_disp_reg(par, CARMINE_DISP_REG_L5_EXT_MODE, CARMINE_EXTEND_MODE | + CARMINE_EXT_CMODE_DIRECT24_RGBA); + c_set_disp_reg(par, CARMINE_DISP_REG_L6_EXT_MODE, CARMINE_EXTEND_MODE | + CARMINE_EXT_CMODE_DIRECT24_RGBA); + c_set_disp_reg(par, CARMINE_DISP_REG_L7_EXT_MODE, CARMINE_EXTEND_MODE | + CARMINE_EXT_CMODE_DIRECT24_RGBA); + + /* Set default frame size to layer mode register */ + width = par->res->hdp * 4 / CARMINE_DISP_WIDTH_UNIT; + width = width << CARMINE_DISP_WIDTH_SHIFT; + + height = par->res->vdp - 1; + param = width | height; + + c_set_disp_reg(par, CARMINE_DISP_REG_L0_MODE_W_H, param); + c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIDTH, width); + c_set_disp_reg(par, CARMINE_DISP_REG_L2_MODE_W_H, param); + c_set_disp_reg(par, CARMINE_DISP_REG_L3_MODE_W_H, param); + c_set_disp_reg(par, CARMINE_DISP_REG_L4_MODE_W_H, param); + c_set_disp_reg(par, CARMINE_DISP_REG_L5_MODE_W_H, param); + c_set_disp_reg(par, CARMINE_DISP_REG_L6_MODE_W_H, param); + c_set_disp_reg(par, CARMINE_DISP_REG_L7_MODE_W_H, param); + + /* Set default pos and size */ + window_size = (par->res->vdp - 1) << CARMINE_DISP_WIN_H_SHIFT; + window_size |= par->res->hdp; + + c_set_disp_reg(par, CARMINE_DISP_REG_L0_WIN_POS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L0_WIN_SIZE, window_size); + c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIN_POS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIN_SIZE, window_size); + c_set_disp_reg(par, CARMINE_DISP_REG_L2_WIN_POS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L2_WIN_SIZE, window_size); + c_set_disp_reg(par, CARMINE_DISP_REG_L3_WIN_POS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L3_WIN_SIZE, window_size); + c_set_disp_reg(par, CARMINE_DISP_REG_L4_WIN_POS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L4_WIN_SIZE, window_size); + c_set_disp_reg(par, CARMINE_DISP_REG_L5_WIN_POS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L5_WIN_SIZE, window_size); + c_set_disp_reg(par, CARMINE_DISP_REG_L6_WIN_POS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L6_WIN_SIZE, window_size); + c_set_disp_reg(par, CARMINE_DISP_REG_L7_WIN_POS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L7_WIN_SIZE, window_size); + + /* Set default origin address */ + c_set_disp_reg(par, CARMINE_DISP_REG_L0_ORG_ADR, soffset); + c_set_disp_reg(par, CARMINE_DISP_REG_L1_ORG_ADR, soffset); + c_set_disp_reg(par, CARMINE_DISP_REG_L2_ORG_ADR1, soffset); + c_set_disp_reg(par, CARMINE_DISP_REG_L3_ORG_ADR1, soffset); + c_set_disp_reg(par, CARMINE_DISP_REG_L4_ORG_ADR1, soffset); + c_set_disp_reg(par, CARMINE_DISP_REG_L5_ORG_ADR1, soffset); + c_set_disp_reg(par, CARMINE_DISP_REG_L6_ORG_ADR1, soffset); + c_set_disp_reg(par, CARMINE_DISP_REG_L7_ORG_ADR1, soffset); + + /* Set default display address */ + c_set_disp_reg(par, CARMINE_DISP_REG_L0_DISP_ADR, soffset); + c_set_disp_reg(par, CARMINE_DISP_REG_L2_DISP_ADR1, soffset); + c_set_disp_reg(par, CARMINE_DISP_REG_L3_DISP_ADR1, soffset); + c_set_disp_reg(par, CARMINE_DISP_REG_L4_DISP_ADR1, soffset); + c_set_disp_reg(par, CARMINE_DISP_REG_L5_DISP_ADR1, soffset); + c_set_disp_reg(par, CARMINE_DISP_REG_L6_DISP_ADR0, soffset); + c_set_disp_reg(par, CARMINE_DISP_REG_L7_DISP_ADR0, soffset); + + /* Set default display position */ + c_set_disp_reg(par, CARMINE_DISP_REG_L0_DISP_POS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L2_DISP_POS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L3_DISP_POS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L4_DISP_POS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L5_DISP_POS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L6_DISP_POS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L7_DISP_POS, 0); + + /* Set default blend mode */ + c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L0, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L1, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L2, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L3, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L4, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L5, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L6, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L7, 0); + + /* default transparency mode */ + c_set_disp_reg(par, CARMINE_DISP_REG_L0_TRANS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L1_TRANS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L2_TRANS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L3_TRANS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L4_TRANS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L5_TRANS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L6_TRANS, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L7_TRANS, 0); + + /* Set default read skip parameter */ + c_set_disp_reg(par, CARMINE_DISP_REG_L0RM, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L2RM, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L3RM, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L4RM, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L5RM, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L6RM, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L7RM, 0); + + c_set_disp_reg(par, CARMINE_DISP_REG_L0PX, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L2PX, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L3PX, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L4PX, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L5PX, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L6PX, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L7PX, 0); + + c_set_disp_reg(par, CARMINE_DISP_REG_L0PY, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L2PY, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L3PY, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L4PY, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L5PY, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L6PY, 0); + c_set_disp_reg(par, CARMINE_DISP_REG_L7PY, 0); +} + +static void set_display_parameters(struct carmine_fb *par) +{ + u32 mode; + u32 hdp, vdp, htp, hsp, hsw, vtr, vsp, vsw; + + /* + * display timing. Parameters are decreased by one because hardware + * spec is 0 to (n - 1) + * */ + hdp = par->res->hdp - 1; + vdp = par->res->vdp - 1; + htp = par->res->htp - 1; + hsp = par->res->hsp - 1; + hsw = par->res->hsw - 1; + vtr = par->res->vtr - 1; + vsp = par->res->vsp - 1; + vsw = par->res->vsw - 1; + + c_set_disp_reg(par, CARMINE_DISP_REG_H_TOTAL, + htp << CARMINE_DISP_HTP_SHIFT); + c_set_disp_reg(par, CARMINE_DISP_REG_H_PERIOD, + (hdp << CARMINE_DISP_HDB_SHIFT) | hdp); + c_set_disp_reg(par, CARMINE_DISP_REG_V_H_W_H_POS, + (vsw << CARMINE_DISP_VSW_SHIFT) | + (hsw << CARMINE_DISP_HSW_SHIFT) | + (hsp)); + c_set_disp_reg(par, CARMINE_DISP_REG_V_TOTAL, + vtr << CARMINE_DISP_VTR_SHIFT); + c_set_disp_reg(par, CARMINE_DISP_REG_V_PERIOD_POS, + (vdp << CARMINE_DISP_VDP_SHIFT) | vsp); + + /* clock */ + mode = c_get_disp_reg(par, CARMINE_DISP_REG_DCM1); + mode = (mode & ~CARMINE_DISP_DCM_MASK) | + (par->res->disp_mode & CARMINE_DISP_DCM_MASK); + /* enable video output and layer 0 */ + mode |= CARMINE_DEN | CARMINE_L0E; + c_set_disp_reg(par, CARMINE_DISP_REG_DCM1, mode); +} + +static int carmine_set_par(struct fb_info *info) +{ + struct carmine_fb *par = info->par; + int ret; + + ret = carmine_find_mode(&info->var); + if (ret < 0) + return ret; + + par->new_mode = ret; + if (par->cur_mode != par->new_mode) { + + par->cur_mode = par->new_mode; + par->res = &car_modes[par->new_mode]; + + carmine_init_display_param(par); + set_display_parameters(par); + } + + info->fix.line_length = info->var.xres * info->var.bits_per_pixel / 8; + return 0; +} + +static int init_hardware(struct carmine_hw *hw) +{ + u32 flags; + u32 loops; + u32 ret; + + /* Initalize Carmine */ + /* Sets internal clock */ + c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, + CARMINE_DFLT_IP_CLOCK_ENABLE); + + /* Video signal output is turned off */ + c_set_hw_reg(hw, CARMINE_DISP0_REG + CARMINE_DISP_REG_DCM1, 0); + c_set_hw_reg(hw, CARMINE_DISP1_REG + CARMINE_DISP_REG_DCM1, 0); + + /* Software reset */ + c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_SOFTWARE_RESET, 1); + c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_SOFTWARE_RESET, 0); + + /* I/O mode settings */ + flags = CARMINE_DFLT_IP_DCTL_IO_CONT1 << 16 | + CARMINE_DFLT_IP_DCTL_IO_CONT0; + c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_IOCONT1_IOCONT0, + flags); + + /* DRAM initial sequence */ + flags = CARMINE_DFLT_IP_DCTL_MODE << 16 | CARMINE_DFLT_IP_DCTL_ADD; + c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_MODE_ADD, + flags); + + flags = CARMINE_DFLT_IP_DCTL_SET_TIME1 << 16 | + CARMINE_DFLT_IP_DCTL_EMODE; + c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_SETTIME1_EMODE, + flags); + + flags = CARMINE_DFLT_IP_DCTL_REFRESH << 16 | + CARMINE_DFLT_IP_DCTL_SET_TIME2; + c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_REFRESH_SETTIME2, + flags); + + flags = CARMINE_DFLT_IP_DCTL_RESERVE2 << 16 | + CARMINE_DFLT_IP_DCTL_FIFO_DEPTH; + c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV2_RSV1, flags); + + flags = CARMINE_DFLT_IP_DCTL_DDRIF2 << 16 | CARMINE_DFLT_IP_DCTL_DDRIF1; + c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_DDRIF2_DDRIF1, + flags); + + flags = CARMINE_DFLT_IP_DCTL_RESERVE0 << 16 | + CARMINE_DFLT_IP_DCTL_STATES; + c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV0_STATES, + flags); + + /* Executes DLL reset */ + if (CARMINE_DCTL_DLL_RESET) { + for (loops = 0; loops < CARMINE_DCTL_INIT_WAIT_LIMIT; loops++) { + + ret = c_get_hw_reg(hw, CARMINE_DCTL_REG + + CARMINE_DCTL_REG_RSV0_STATES); + ret &= CARMINE_DCTL_REG_STATES_MASK; + if (!ret) + break; + + mdelay(CARMINE_DCTL_INIT_WAIT_INTERVAL); + } + + if (loops >= CARMINE_DCTL_INIT_WAIT_LIMIT) { + printk(KERN_ERR "DRAM init failed\n"); + return -EIO; + } + } + + flags = CARMINE_DFLT_IP_DCTL_MODE_AFT_RST << 16 | + CARMINE_DFLT_IP_DCTL_ADD; + c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_MODE_ADD, flags); + + flags = CARMINE_DFLT_IP_DCTL_RESERVE0 << 16 | + CARMINE_DFLT_IP_DCTL_STATES_AFT_RST; + c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV0_STATES, + flags); + + /* Initialize the write back register */ + c_set_hw_reg(hw, CARMINE_WB_REG + CARMINE_WB_REG_WBM, + CARMINE_WB_REG_WBM_DEFAULT); + + /* Initialize the Kottos registers */ + c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_VRINTM, 0); + c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_VRERRM, 0); + + /* Set DC offsets */ + c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_PX, 0); + c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_PY, 0); + c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_LX, 0); + c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_LY, 0); + c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_TX, 0); + c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_TY, 0); + return 0; +} + +static struct fb_ops carminefb_ops = { + .owner = THIS_MODULE, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + + .fb_check_var = carmine_check_var, + .fb_set_par = carmine_set_par, + .fb_setcolreg = carmine_setcolreg, +}; + +static int alloc_carmine_fb(void __iomem *regs, void __iomem *smem_base, + int smem_offset, struct device *device, struct fb_info **rinfo) +{ + int ret; + struct fb_info *info; + struct carmine_fb *par; + + info = framebuffer_alloc(sizeof *par, device); + if (!info) + return -ENOMEM; + + par = info->par; + par->display_reg = regs; + par->smem_offset = smem_offset; + + info->screen_base = smem_base + smem_offset; + info->screen_size = CARMINE_DISPLAY_MEM; + info->fbops = &carminefb_ops; + + info->fix = carminefb_fix; + info->pseudo_palette = par->pseudo_palette; + info->flags = FBINFO_DEFAULT; + + ret = fb_alloc_cmap(&info->cmap, 256, 1); + if (ret < 0) + goto err_free_fb; + + if (fb_mode > ARRAY_SIZE(carmine_modedb)) + fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE; + + par->cur_mode = par->new_mode = ~0; + + ret = fb_find_mode(&info->var, info, fb_mode_str, carmine_modedb, + ARRAY_SIZE(carmine_modedb), + &carmine_modedb[fb_mode], 32); + if (!ret || ret == 4) { + ret = -EINVAL; + goto err_dealloc_cmap; + } + + fb_videomode_to_modelist(carmine_modedb, ARRAY_SIZE(carmine_modedb), + &info->modelist); + + ret = register_framebuffer(info); + if (ret < 0) + goto err_dealloc_cmap; + + printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, + info->fix.id); + + *rinfo = info; + return 0; + +err_dealloc_cmap: + fb_dealloc_cmap(&info->cmap); +err_free_fb: + framebuffer_release(info); + return ret; +} + +static void cleanup_fb_device(struct fb_info *info) +{ + if (info) { + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); + } +} + +static int __devinit carminefb_probe(struct pci_dev *dev, + const struct pci_device_id *ent) +{ + struct carmine_hw *hw; + struct device *device = &dev->dev; + struct fb_info *info; + int ret; + + ret = pci_enable_device(dev); + if (ret) + return ret; + + ret = -ENOMEM; + hw = kzalloc(sizeof *hw, GFP_KERNEL); + if (!hw) + goto err_enable_pci; + + carminefb_fix.mmio_start = pci_resource_start(dev, CARMINE_CONFIG_BAR); + carminefb_fix.mmio_len = pci_resource_len(dev, CARMINE_CONFIG_BAR); + + if (!request_mem_region(carminefb_fix.mmio_start, + carminefb_fix.mmio_len, + "carminefb regbase")) { + printk(KERN_ERR "carminefb: Can't reserve regbase.\n"); + ret = -EBUSY; + goto err_free_hw; + } + hw->v_regs = ioremap_nocache(carminefb_fix.mmio_start, + carminefb_fix.mmio_len); + if (!hw->v_regs) { + printk(KERN_ERR "carminefb: Can't remap %s register.\n", + carminefb_fix.id); + goto err_free_reg_mmio; + } + + carminefb_fix.smem_start = pci_resource_start(dev, CARMINE_MEMORY_BAR); + carminefb_fix.smem_len = pci_resource_len(dev, CARMINE_MEMORY_BAR); + + /* The memory area tends to be very large (256 MiB). Remap only what + * is required for that largest resolution to avoid remaps at run + * time + */ + if (carminefb_fix.smem_len > CARMINE_TOTAL_DIPLAY_MEM) + carminefb_fix.smem_len = CARMINE_TOTAL_DIPLAY_MEM; + + else if (carminefb_fix.smem_len < CARMINE_TOTAL_DIPLAY_MEM) { + printk(KERN_ERR "carminefb: Memory bar is only %d bytes, %d " + "are required.", carminefb_fix.smem_len, + CARMINE_TOTAL_DIPLAY_MEM); + goto err_free_reg_mmio; + } + + if (!request_mem_region(carminefb_fix.smem_start, + carminefb_fix.smem_len, "carminefb smem")) { + printk(KERN_ERR "carminefb: Can't reserve smem.\n"); + goto err_unmap_vregs; + } + + hw->screen_mem = ioremap_nocache(carminefb_fix.smem_start, + carminefb_fix.smem_len); + if (!hw->screen_mem) { + printk(KERN_ERR "carmine: Can't ioremap smem area.\n"); + release_mem_region(carminefb_fix.smem_start, + carminefb_fix.smem_len); + goto err_reg_smem; + } + + ret = init_hardware(hw); + if (ret) + goto err_unmap_screen; + + info = NULL; + if (fb_displays & CARMINE_USE_DISPLAY0) { + ret = alloc_carmine_fb(hw->v_regs + CARMINE_DISP0_REG, + hw->screen_mem, CARMINE_DISPLAY_MEM * 0, + device, &info); + if (ret) + goto err_deinit_hw; + } + + hw->fb[0] = info; + + info = NULL; + if (fb_displays & CARMINE_USE_DISPLAY1) { + ret = alloc_carmine_fb(hw->v_regs + CARMINE_DISP1_REG, + hw->screen_mem, CARMINE_DISPLAY_MEM * 1, + device, &info); + if (ret) + goto err_cleanup_fb0; + } + + hw->fb[1] = info; + info = NULL; + + pci_set_drvdata(dev, hw); + return 0; + +err_cleanup_fb0: + cleanup_fb_device(hw->fb[0]); +err_deinit_hw: + /* disable clock, etc */ + c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 0); +err_unmap_screen: + iounmap(hw->screen_mem); +err_reg_smem: + release_mem_region(carminefb_fix.mmio_start, carminefb_fix.mmio_len); +err_unmap_vregs: + iounmap(hw->v_regs); +err_free_reg_mmio: + release_mem_region(carminefb_fix.mmio_start, carminefb_fix.mmio_len); +err_free_hw: + kfree(hw); +err_enable_pci: + pci_disable_device(dev); + return ret; +} + +static void __devexit carminefb_remove(struct pci_dev *dev) +{ + struct carmine_hw *hw = pci_get_drvdata(dev); + struct fb_fix_screeninfo fix; + int i; + + /* in case we use only fb1 and not fb1 */ + if (hw->fb[0]) + fix = hw->fb[0]->fix; + else + fix = hw->fb[1]->fix; + + /* deactivate display(s) and switch clocks */ + c_set_hw_reg(hw, CARMINE_DISP0_REG + CARMINE_DISP_REG_DCM1, 0); + c_set_hw_reg(hw, CARMINE_DISP1_REG + CARMINE_DISP_REG_DCM1, 0); + c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 0); + + for (i = 0; i < MAX_DISPLAY; i++) + cleanup_fb_device(hw->fb[i]); + + iounmap(hw->screen_mem); + release_mem_region(fix.smem_start, fix.smem_len); + iounmap(hw->v_regs); + release_mem_region(fix.mmio_start, fix.mmio_len); + + pci_set_drvdata(dev, NULL); + pci_disable_device(dev); + kfree(hw); +} + +#define PCI_VENDOR_ID_FUJITU_LIMITED 0x10cf +static struct pci_device_id carmine_devices[] __devinitdata = { +{ + PCI_DEVICE(PCI_VENDOR_ID_FUJITU_LIMITED, 0x202b)}, + {0, 0, 0, 0, 0, 0, 0} +}; + +MODULE_DEVICE_TABLE(pci, carmine_devices); + +static struct pci_driver carmine_pci_driver = { + .name = "carminefb", + .id_table = carmine_devices, + .probe = carminefb_probe, + .remove = __devexit_p(carminefb_remove), +}; + +static int __init carminefb_init(void) +{ + if (!(fb_displays & + (CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1))) { + printk(KERN_ERR "If you disable both displays than you don't " + "need the driver at all\n"); + return -EINVAL; + } + return pci_register_driver(&carmine_pci_driver); +} +module_init(carminefb_init); + +static void __exit carminefb_cleanup(void) +{ + pci_unregister_driver(&carmine_pci_driver); +} +module_exit(carminefb_cleanup); + +MODULE_AUTHOR("Sebastian Siewior <bigeasy@linutronix.de>"); +MODULE_DESCRIPTION("Framebuffer driver for Fujitsu Carmine based devices"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/carminefb.h b/drivers/video/carminefb.h new file mode 100644 index 000000000000..05306de0c6b6 --- /dev/null +++ b/drivers/video/carminefb.h @@ -0,0 +1,64 @@ +#ifndef CARMINE_CARMINE_H +#define CARMINE_CARMINE_H + +#define CARMINE_MEMORY_BAR 2 +#define CARMINE_CONFIG_BAR 3 + +#define MAX_DISPLAY 2 +#define CARMINE_DISPLAY_MEM (800 * 600 * 4) +#define CARMINE_TOTAL_DIPLAY_MEM (CARMINE_DISPLAY_MEM * MAX_DISPLAY) + +#define CARMINE_USE_DISPLAY0 (1 << 0) +#define CARMINE_USE_DISPLAY1 (1 << 1) + +/* + * This values work on the eval card. Custom boards may use different timings, + * here an example :) + */ + +/* DRAM initialization values */ +#ifdef CONFIG_FB_CARMINE_DRAM_EVAL + +#define CARMINE_DFLT_IP_CLOCK_ENABLE (0x03ff) +#define CARMINE_DFLT_IP_DCTL_ADD (0x05c3) +#define CARMINE_DFLT_IP_DCTL_MODE (0x0121) +#define CARMINE_DFLT_IP_DCTL_EMODE (0x8000) +#define CARMINE_DFLT_IP_DCTL_SET_TIME1 (0x4749) +#define CARMINE_DFLT_IP_DCTL_SET_TIME2 (0x2a22) +#define CARMINE_DFLT_IP_DCTL_REFRESH (0x0042) +#define CARMINE_DFLT_IP_DCTL_STATES (0x0003) +#define CARMINE_DFLT_IP_DCTL_RESERVE0 (0x0020) +#define CARMINE_DFLT_IP_DCTL_FIFO_DEPTH (0x000f) +#define CARMINE_DFLT_IP_DCTL_RESERVE2 (0x0000) +#define CARMINE_DFLT_IP_DCTL_DDRIF1 (0x6646) +#define CARMINE_DFLT_IP_DCTL_DDRIF2 (0x0055) +#define CARMINE_DFLT_IP_DCTL_MODE_AFT_RST (0x0021) +#define CARMINE_DFLT_IP_DCTL_STATES_AFT_RST (0x0002) +#define CARMINE_DFLT_IP_DCTL_IO_CONT0 (0x0555) +#define CARMINE_DFLT_IP_DCTL_IO_CONT1 (0x0555) +#define CARMINE_DCTL_DLL_RESET (1) +#endif + +#ifdef CONFIG_CARMINE_DRAM_CUSTOM + +#define CARMINE_DFLT_IP_CLOCK_ENABLE (0x03ff) +#define CARMINE_DFLT_IP_DCTL_ADD (0x03b2) +#define CARMINE_DFLT_IP_DCTL_MODE (0x0161) +#define CARMINE_DFLT_IP_DCTL_EMODE (0x8000) +#define CARMINE_DFLT_IP_DCTL_SET_TIME1 (0x2628) +#define CARMINE_DFLT_IP_DCTL_SET_TIME2 (0x1a09) +#define CARMINE_DFLT_IP_DCTL_REFRESH (0x00fe) +#define CARMINE_DFLT_IP_DCTL_STATES (0x0003) +#define CARMINE_DFLT_IP_DCTL_RESERVE0 (0x0020) +#define CARMINE_DFLT_IP_DCTL_FIFO_DEPTH (0x000f) +#define CARMINE_DFLT_IP_DCTL_RESERVE2 (0x0000) +#define CARMINE_DFLT_IP_DCTL_DDRIF1 (0x0646) +#define CARMINE_DFLT_IP_DCTL_DDRIF2 (0x55aa) +#define CARMINE_DFLT_IP_DCTL_MODE_AFT_RST (0x0061) +#define CARMINE_DFLT_IP_DCTL_STATES_AFT_RST (0x0002) +#define CARMINE_DFLT_IP_DCTL_IO_CONT0 (0x0555) +#define CARMINE_DFLT_IP_DCTL_IO_CONT1 (0x0555) +#define CARMINE_DCTL_DLL_RESET (1) +#endif + +#endif diff --git a/drivers/video/carminefb_regs.h b/drivers/video/carminefb_regs.h new file mode 100644 index 000000000000..045215600b73 --- /dev/null +++ b/drivers/video/carminefb_regs.h @@ -0,0 +1,159 @@ +#ifndef _CARMINEFB_REGS_H +#define _CARMINEFB_REGS_H + +#define CARMINE_OVERLAY_EXT_MODE (0x00000002) +#define CARMINE_GRAPH_REG (0x00000000) +#define CARMINE_DISP0_REG (0x00100000) +#define CARMINE_DISP1_REG (0x00140000) +#define CARMINE_WB_REG (0x00180000) +#define CARMINE_DCTL_REG (0x00300000) +#define CARMINE_CTL_REG (0x00400000) +#define CARMINE_WINDOW_MODE (0x00000001) +#define CARMINE_EXTEND_MODE (CARMINE_WINDOW_MODE | \ + CARMINE_OVERLAY_EXT_MODE) +#define CARMINE_L0E (1 << 16) +#define CARMINE_L2E (1 << 18) +#define CARMINE_DEN (1 << 31) + +#define CARMINE_EXT_CMODE_DIRECT24_RGBA (0xC0000000) +#define CARMINE_DCTL_REG_MODE_ADD (0x00) +#define CARMINE_DCTL_REG_SETTIME1_EMODE (0x04) +#define CARMINE_DCTL_REG_REFRESH_SETTIME2 (0x08) +#define CARMINE_DCTL_REG_RSV0_STATES (0x0C) +#define CARMINE_DCTL_REG_RSV2_RSV1 (0x10) +#define CARMINE_DCTL_REG_DDRIF2_DDRIF1 (0x14) +#define CARMINE_DCTL_REG_IOCONT1_IOCONT0 (0x24) +#define CARMINE_DCTL_REG_STATES_MASK (0x000F) +#define CARMINE_DCTL_INIT_WAIT_INTERVAL (1) +#define CARMINE_DCTL_INIT_WAIT_LIMIT (5000) +#define CARMINE_WB_REG_WBM_DEFAULT (0x0001c020) +#define CARMINE_DISP_REG_L0RM (0x1880) +#define CARMINE_DISP_REG_L0PX (0x1884) +#define CARMINE_DISP_REG_L0PY (0x1888) +#define CARMINE_DISP_REG_L2RM (0x18A0) +#define CARMINE_DISP_REG_L2PX (0x18A4) +#define CARMINE_DISP_REG_L2PY (0x18A8) +#define CARMINE_DISP_REG_L3RM (0x18B0) +#define CARMINE_DISP_REG_L3PX (0x18B4) +#define CARMINE_DISP_REG_L3PY (0x18B8) +#define CARMINE_DISP_REG_L4RM (0x18C0) +#define CARMINE_DISP_REG_L4PX (0x18C4) +#define CARMINE_DISP_REG_L4PY (0x18C8) +#define CARMINE_DISP_REG_L5RM (0x18D0) +#define CARMINE_DISP_REG_L5PX (0x18D4) +#define CARMINE_DISP_REG_L5PY (0x18D8) +#define CARMINE_DISP_REG_L6RM (0x1924) +#define CARMINE_DISP_REG_L6PX (0x1928) +#define CARMINE_DISP_REG_L6PY (0x192C) +#define CARMINE_DISP_REG_L7RM (0x1964) +#define CARMINE_DISP_REG_L7PX (0x1968) +#define CARMINE_DISP_REG_L7PY (0x196C) +#define CARMINE_WB_REG_WBM (0x0004) +#define CARMINE_DISP_HTP_SHIFT (16) +#define CARMINE_DISP_HDB_SHIFT (16) +#define CARMINE_DISP_HSW_SHIFT (16) +#define CARMINE_DISP_VSW_SHIFT (24) +#define CARMINE_DISP_VTR_SHIFT (16) +#define CARMINE_DISP_VDP_SHIFT (16) +#define CARMINE_CURSOR_CUTZ_MASK (0x00000100) +#define CARMINE_CURSOR0_PRIORITY_MASK (0x00010000) +#define CARMINE_CURSOR1_PRIORITY_MASK (0x00020000) +#define CARMINE_DISP_WIDTH_SHIFT (16) +#define CARMINE_DISP_WIN_H_SHIFT (16) +#define CARMINE_DISP_REG_H_TOTAL (0x0004) +#define CARMINE_DISP_REG_H_PERIOD (0x0008) +#define CARMINE_DISP_REG_V_H_W_H_POS (0x000C) +#define CARMINE_DISP_REG_V_TOTAL (0x0010) +#define CARMINE_DISP_REG_V_PERIOD_POS (0x0014) +#define CARMINE_DISP_REG_L0_MODE_W_H (0x0020) +#define CARMINE_DISP_REG_L0_ORG_ADR (0x0024) +#define CARMINE_DISP_REG_L0_DISP_ADR (0x0028) +#define CARMINE_DISP_REG_L0_DISP_POS (0x002C) +#define CARMINE_DISP_REG_L1_WIDTH (0x0030) +#define CARMINE_DISP_REG_L1_ORG_ADR (0x0034) +#define CARMINE_DISP_REG_L2_MODE_W_H (0x0040) +#define CARMINE_DISP_REG_L2_ORG_ADR1 (0x0044) +#define CARMINE_DISP_REG_L2_DISP_ADR1 (0x0048) +#define CARMINE_DISP_REG_L2_DISP_POS (0x0054) +#define CARMINE_DISP_REG_L3_MODE_W_H (0x0058) +#define CARMINE_DISP_REG_L3_ORG_ADR1 (0x005C) +#define CARMINE_DISP_REG_L3_DISP_ADR1 (0x0060) +#define CARMINE_DISP_REG_L3_DISP_POS (0x006C) +#define CARMINE_DISP_REG_L4_MODE_W_H (0x0070) +#define CARMINE_DISP_REG_L4_ORG_ADR1 (0x0074) +#define CARMINE_DISP_REG_L4_DISP_ADR1 (0x0078) +#define CARMINE_DISP_REG_L4_DISP_POS (0x0084) +#define CARMINE_DISP_REG_L5_MODE_W_H (0x0088) +#define CARMINE_DISP_REG_L5_ORG_ADR1 (0x008C) +#define CARMINE_DISP_REG_L5_DISP_ADR1 (0x0090) +#define CARMINE_DISP_REG_L5_DISP_POS (0x009C) +#define CARMINE_DISP_REG_CURSOR_MODE (0x00A0) +#define CARMINE_DISP_REG_CUR1_POS (0x00A8) +#define CARMINE_DISP_REG_CUR2_POS (0x00B0) +#define CARMINE_DISP_REG_C_TRANS (0x00BC) +#define CARMINE_DISP_REG_MLMR_TRANS (0x00C0) +#define CARMINE_DISP_REG_L0_EXT_MODE (0x0110) +#define CARMINE_DISP_REG_L0_WIN_POS (0x0114) +#define CARMINE_DISP_REG_L0_WIN_SIZE (0x0118) +#define CARMINE_DISP_REG_L1_EXT_MODE (0x0120) +#define CARMINE_DISP_REG_L1_WIN_POS (0x0124) +#define CARMINE_DISP_REG_L1_WIN_SIZE (0x0128) +#define CARMINE_DISP_REG_L2_EXT_MODE (0x0130) +#define CARMINE_DISP_REG_L2_WIN_POS (0x0134) +#define CARMINE_DISP_REG_L2_WIN_SIZE (0x0138) +#define CARMINE_DISP_REG_L3_EXT_MODE (0x0140) +#define CARMINE_DISP_REG_L3_WIN_POS (0x0144) +#define CARMINE_DISP_REG_L3_WIN_SIZE (0x0148) +#define CARMINE_DISP_REG_L4_EXT_MODE (0x0150) +#define CARMINE_DISP_REG_L4_WIN_POS (0x0154) +#define CARMINE_DISP_REG_L4_WIN_SIZE (0x0158) +#define CARMINE_DISP_REG_L5_EXT_MODE (0x0160) +#define CARMINE_DISP_REG_L5_WIN_POS (0x0164) +#define CARMINE_DISP_REG_L5_WIN_SIZE (0x0168) +#define CARMINE_DISP_REG_L6_EXT_MODE (0x1918) +#define CARMINE_DISP_REG_L6_WIN_POS (0x191c) +#define CARMINE_DISP_REG_L6_WIN_SIZE (0x1920) +#define CARMINE_DISP_REG_L7_EXT_MODE (0x1958) +#define CARMINE_DISP_REG_L7_WIN_POS (0x195c) +#define CARMINE_DISP_REG_L7_WIN_SIZE (0x1960) +#define CARMINE_DISP_REG_BLEND_MODE_L0 (0x00B4) +#define CARMINE_DISP_REG_BLEND_MODE_L1 (0x0188) +#define CARMINE_DISP_REG_BLEND_MODE_L2 (0x018C) +#define CARMINE_DISP_REG_BLEND_MODE_L3 (0x0190) +#define CARMINE_DISP_REG_BLEND_MODE_L4 (0x0194) +#define CARMINE_DISP_REG_BLEND_MODE_L5 (0x0198) +#define CARMINE_DISP_REG_BLEND_MODE_L6 (0x1990) +#define CARMINE_DISP_REG_BLEND_MODE_L7 (0x1994) +#define CARMINE_DISP_REG_L0_TRANS (0x01A0) +#define CARMINE_DISP_REG_L1_TRANS (0x01A4) +#define CARMINE_DISP_REG_L2_TRANS (0x01A8) +#define CARMINE_DISP_REG_L3_TRANS (0x01AC) +#define CARMINE_DISP_REG_L4_TRANS (0x01B0) +#define CARMINE_DISP_REG_L5_TRANS (0x01B4) +#define CARMINE_DISP_REG_L6_TRANS (0x1998) +#define CARMINE_DISP_REG_L7_TRANS (0x199c) +#define CARMINE_EXTEND_MODE_MASK (0x00000003) +#define CARMINE_DISP_DCM_MASK (0x0000FFFF) +#define CARMINE_DISP_REG_DCM1 (0x0100) +#define CARMINE_DISP_WIDTH_UNIT (64) +#define CARMINE_DISP_REG_L6_MODE_W_H (0x1900) +#define CARMINE_DISP_REG_L6_ORG_ADR1 (0x1904) +#define CARMINE_DISP_REG_L6_DISP_ADR0 (0x1908) +#define CARMINE_DISP_REG_L6_DISP_POS (0x1914) +#define CARMINE_DISP_REG_L7_MODE_W_H (0x1940) +#define CARMINE_DISP_REG_L7_ORG_ADR1 (0x1944) +#define CARMINE_DISP_REG_L7_DISP_ADR0 (0x1948) +#define CARMINE_DISP_REG_L7_DISP_POS (0x1954) +#define CARMINE_CTL_REG_CLOCK_ENABLE (0x000C) +#define CARMINE_CTL_REG_SOFTWARE_RESET (0x0010) +#define CARMINE_CTL_REG_IST_MASK_ALL (0x07FFFFFF) +#define CARMINE_GRAPH_REG_VRINTM (0x00028064) +#define CARMINE_GRAPH_REG_VRERRM (0x0002806C) +#define CARMINE_GRAPH_REG_DC_OFFSET_PX (0x0004005C) +#define CARMINE_GRAPH_REG_DC_OFFSET_PY (0x00040060) +#define CARMINE_GRAPH_REG_DC_OFFSET_LX (0x00040064) +#define CARMINE_GRAPH_REG_DC_OFFSET_LY (0x00040068) +#define CARMINE_GRAPH_REG_DC_OFFSET_TX (0x0004006C) +#define CARMINE_GRAPH_REG_DC_OFFSET_TY (0x00040070) + +#endif diff --git a/drivers/video/cobalt_lcdfb.c b/drivers/video/cobalt_lcdfb.c new file mode 100644 index 000000000000..7bad24ed04ef --- /dev/null +++ b/drivers/video/cobalt_lcdfb.c @@ -0,0 +1,371 @@ +/* + * Cobalt server LCD frame buffer driver. + * + * Copyright (C) 2008 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> + * + * 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/delay.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/uaccess.h> +#include <linux/platform_device.h> + +/* + * Cursor position address + * \X 0 1 2 ... 14 15 + * Y+----+----+----+---+----+----+ + * 0|0x00|0x01|0x02|...|0x0e|0x0f| + * +----+----+----+---+----+----+ + * 1|0x40|0x41|0x42|...|0x4e|0x4f| + * +----+----+----+---+----+----+ + */ +#define LCD_DATA_REG_OFFSET 0x10 +#define LCD_XRES_MAX 16 +#define LCD_YRES_MAX 2 +#define LCD_CHARS_MAX 32 + +#define LCD_CLEAR 0x01 +#define LCD_CURSOR_MOVE_HOME 0x02 +#define LCD_RESET 0x06 +#define LCD_OFF 0x08 +#define LCD_CURSOR_OFF 0x0c +#define LCD_CURSOR_BLINK_OFF 0x0e +#define LCD_CURSOR_ON 0x0f +#define LCD_ON LCD_CURSOR_ON +#define LCD_CURSOR_MOVE_LEFT 0x10 +#define LCD_CURSOR_MOVE_RIGHT 0x14 +#define LCD_DISPLAY_LEFT 0x18 +#define LCD_DISPLAY_RIGHT 0x1c +#define LCD_PRERESET 0x3f /* execute 4 times continuously */ +#define LCD_BUSY 0x80 + +#define LCD_GRAPHIC_MODE 0x40 +#define LCD_TEXT_MODE 0x80 +#define LCD_CUR_POS_MASK 0x7f + +#define LCD_CUR_POS(x) ((x) & LCD_CUR_POS_MASK) +#define LCD_TEXT_POS(x) ((x) | LCD_TEXT_MODE) + +static inline void lcd_write_control(struct fb_info *info, u8 control) +{ + writel((u32)control << 24, info->screen_base); +} + +static inline u8 lcd_read_control(struct fb_info *info) +{ + return readl(info->screen_base) >> 24; +} + +static inline void lcd_write_data(struct fb_info *info, u8 data) +{ + writel((u32)data << 24, info->screen_base + LCD_DATA_REG_OFFSET); +} + +static inline u8 lcd_read_data(struct fb_info *info) +{ + return readl(info->screen_base + LCD_DATA_REG_OFFSET) >> 24; +} + +static int lcd_busy_wait(struct fb_info *info) +{ + u8 val = 0; + int timeout = 10, retval = 0; + + do { + val = lcd_read_control(info); + val &= LCD_BUSY; + if (val != LCD_BUSY) + break; + + if (msleep_interruptible(1)) + return -EINTR; + + timeout--; + } while (timeout); + + if (val == LCD_BUSY) + retval = -EBUSY; + + return retval; +} + +static void lcd_clear(struct fb_info *info) +{ + int i; + + for (i = 0; i < 4; i++) { + udelay(150); + + lcd_write_control(info, LCD_PRERESET); + } + + udelay(150); + + lcd_write_control(info, LCD_CLEAR); + + udelay(150); + + lcd_write_control(info, LCD_RESET); +} + +static struct fb_fix_screeninfo cobalt_lcdfb_fix __initdata = { + .id = "cobalt-lcd", + .type = FB_TYPE_TEXT, + .type_aux = FB_AUX_TEXT_MDA, + .visual = FB_VISUAL_MONO01, + .line_length = LCD_XRES_MAX, + .accel = FB_ACCEL_NONE, +}; + +static ssize_t cobalt_lcdfb_read(struct fb_info *info, char __user *buf, + size_t count, loff_t *ppos) +{ + char src[LCD_CHARS_MAX]; + unsigned long pos; + int len, retval = 0; + + pos = *ppos; + if (pos >= LCD_CHARS_MAX || count == 0) + return 0; + + if (count > LCD_CHARS_MAX) + count = LCD_CHARS_MAX; + + if (pos + count > LCD_CHARS_MAX) + count = LCD_CHARS_MAX - pos; + + for (len = 0; len < count; len++) { + retval = lcd_busy_wait(info); + if (retval < 0) + break; + + lcd_write_control(info, LCD_TEXT_POS(pos)); + + retval = lcd_busy_wait(info); + if (retval < 0) + break; + + src[len] = lcd_read_data(info); + if (pos == 0x0f) + pos = 0x40; + else + pos++; + } + + if (retval < 0 && signal_pending(current)) + return -ERESTARTSYS; + + if (copy_to_user(buf, src, len)) + return -EFAULT; + + *ppos += len; + + return len; +} + +static ssize_t cobalt_lcdfb_write(struct fb_info *info, const char __user *buf, + size_t count, loff_t *ppos) +{ + char dst[LCD_CHARS_MAX]; + unsigned long pos; + int len, retval = 0; + + pos = *ppos; + if (pos >= LCD_CHARS_MAX || count == 0) + return 0; + + if (count > LCD_CHARS_MAX) + count = LCD_CHARS_MAX; + + if (pos + count > LCD_CHARS_MAX) + count = LCD_CHARS_MAX - pos; + + if (copy_from_user(dst, buf, count)) + return -EFAULT; + + for (len = 0; len < count; len++) { + retval = lcd_busy_wait(info); + if (retval < 0) + break; + + lcd_write_control(info, LCD_TEXT_POS(pos)); + + retval = lcd_busy_wait(info); + if (retval < 0) + break; + + lcd_write_data(info, dst[len]); + if (pos == 0x0f) + pos = 0x40; + else + pos++; + } + + if (retval < 0 && signal_pending(current)) + return -ERESTARTSYS; + + *ppos += len; + + return len; +} + +static int cobalt_lcdfb_blank(int blank_mode, struct fb_info *info) +{ + int retval; + + retval = lcd_busy_wait(info); + if (retval < 0) + return retval; + + switch (blank_mode) { + case FB_BLANK_UNBLANK: + lcd_write_control(info, LCD_ON); + break; + default: + lcd_write_control(info, LCD_OFF); + break; + } + + return 0; +} + +static int cobalt_lcdfb_cursor(struct fb_info *info, struct fb_cursor *cursor) +{ + u32 x, y; + int retval; + + switch (cursor->set) { + case FB_CUR_SETPOS: + x = cursor->image.dx; + y = cursor->image.dy; + if (x >= LCD_XRES_MAX || y >= LCD_YRES_MAX) + return -EINVAL; + + retval = lcd_busy_wait(info); + if (retval < 0) + return retval; + + lcd_write_control(info, + LCD_TEXT_POS(info->fix.line_length * y + x)); + break; + default: + return -EINVAL; + } + + retval = lcd_busy_wait(info); + if (retval < 0) + return retval; + + if (cursor->enable) + lcd_write_control(info, LCD_CURSOR_ON); + else + lcd_write_control(info, LCD_CURSOR_OFF); + + return 0; +} + +static struct fb_ops cobalt_lcd_fbops = { + .owner = THIS_MODULE, + .fb_read = cobalt_lcdfb_read, + .fb_write = cobalt_lcdfb_write, + .fb_blank = cobalt_lcdfb_blank, + .fb_cursor = cobalt_lcdfb_cursor, +}; + +static int __init cobalt_lcdfb_probe(struct platform_device *dev) +{ + struct fb_info *info; + struct resource *res; + int retval; + + info = framebuffer_alloc(0, &dev->dev); + if (!info) + return -ENOMEM; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) { + framebuffer_release(info); + return -EBUSY; + } + + info->screen_size = res->end - res->start + 1; + info->screen_base = ioremap(res->start, info->screen_size); + info->fbops = &cobalt_lcd_fbops; + info->fix = cobalt_lcdfb_fix; + info->fix.smem_start = res->start; + info->fix.smem_len = info->screen_size; + info->pseudo_palette = NULL; + info->par = NULL; + info->flags = FBINFO_DEFAULT; + + retval = register_framebuffer(info); + if (retval < 0) { + iounmap(info->screen_base); + framebuffer_release(info); + return retval; + } + + platform_set_drvdata(dev, info); + + lcd_clear(info); + + printk(KERN_INFO "fb%d: Cobalt server LCD frame buffer device\n", + info->node); + + return 0; +} + +static int __devexit cobalt_lcdfb_remove(struct platform_device *dev) +{ + struct fb_info *info; + + info = platform_get_drvdata(dev); + if (info) { + iounmap(info->screen_base); + unregister_framebuffer(info); + framebuffer_release(info); + } + + return 0; +} + +static struct platform_driver cobalt_lcdfb_driver = { + .probe = cobalt_lcdfb_probe, + .remove = __devexit_p(cobalt_lcdfb_remove), + .driver = { + .name = "cobalt-lcd", + .owner = THIS_MODULE, + }, +}; + +static int __init cobalt_lcdfb_init(void) +{ + return platform_driver_register(&cobalt_lcdfb_driver); +} + +static void __exit cobalt_lcdfb_exit(void) +{ + platform_driver_unregister(&cobalt_lcdfb_driver); +} + +module_init(cobalt_lcdfb_init); +module_exit(cobalt_lcdfb_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Yoichi Yuasa"); +MODULE_DESCRIPTION("Cobalt server LCD frame buffer driver"); diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 97aff8db10bf..3ccfa76d9b2a 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -107,9 +107,7 @@ static struct display fb_display[MAX_NR_CONSOLES]; static signed char con2fb_map[MAX_NR_CONSOLES]; static signed char con2fb_map_boot[MAX_NR_CONSOLES]; -#ifndef MODULE -static int logo_height; -#endif + static int logo_lines; /* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO enums. */ @@ -607,6 +605,7 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, struct fbcon_ops *ops = info->fbcon_par; int cnt, erase = vc->vc_video_erase_char, step; unsigned short *save = NULL, *r, *q; + int logo_height; if (info->flags & FBINFO_MODULE) { logo_shown = FBCON_LOGO_DONTSHOW; @@ -3586,7 +3585,8 @@ static int __init fb_console_init(void) acquire_console_sem(); fb_register_client(&fbcon_event_notifier); - fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), "fbcon"); + fbcon_device = device_create_drvdata(fb_class, NULL, MKDEV(0, 0), + NULL, "fbcon"); if (IS_ERR(fbcon_device)) { printk(KERN_WARNING "Unable to create device " diff --git a/drivers/video/console/fbcon.h b/drivers/video/console/fbcon.h index 0135e0395456..de1b1365279b 100644 --- a/drivers/video/console/fbcon.h +++ b/drivers/video/console/fbcon.h @@ -92,7 +92,7 @@ struct fbcon_ops { #define attr_fgcol(fgshift,s) \ (((s) >> (fgshift)) & 0x0f) #define attr_bgcol(bgshift,s) \ - (((s) >> (bgshift)) & 0x0f) + (((s) >> (bgshift)) & 0x07) /* Monochrome */ #define attr_bold(s) \ @@ -146,10 +146,8 @@ static inline int attr_col_ec(int shift, struct vc_data *vc, return is_fg ? fg : bg; } -#define attr_bgcol_ec(bgshift,vc,info) \ - attr_col_ec(bgshift,vc,info,0); -#define attr_fgcol_ec(fgshift,vc,info) \ - attr_col_ec(fgshift,vc,info,1); +#define attr_bgcol_ec(bgshift, vc, info) attr_col_ec(bgshift, vc, info, 0) +#define attr_fgcol_ec(fgshift, vc, info) attr_col_ec(fgshift, vc, info, 1) /* Font */ #define REFCOUNT(fd) (((int *)(fd))[-1]) diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c index 38a296bbdfc9..9901064199bd 100644 --- a/drivers/video/console/mdacon.c +++ b/drivers/video/console/mdacon.c @@ -71,13 +71,15 @@ static char *mda_type_name; /* console information */ -static int mda_first_vc = 1; +static int mda_first_vc = 13; static int mda_last_vc = 16; static struct vc_data *mda_display_fg = NULL; module_param(mda_first_vc, int, 0); +MODULE_PARM_DESC(mda_first_vc, "First virtual console. Default: 13"); module_param(mda_last_vc, int, 0); +MODULE_PARM_DESC(mda_last_vc, "Last virtual console. Default: 16"); /* MDA register values */ diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c index a11cc2fdd4cd..4055dbdd1b42 100644 --- a/drivers/video/console/sticon.c +++ b/drivers/video/console/sticon.c @@ -370,7 +370,7 @@ static const struct consw sti_con = { -int __init sticonsole_init(void) +static int __init sticonsole_init(void) { /* already initialized ? */ if (sticon_sti) diff --git a/drivers/video/console/sticore.c b/drivers/video/console/sticore.c index e9ab657f0bb7..d7822af0e00a 100644 --- a/drivers/video/console/sticore.c +++ b/drivers/video/console/sticore.c @@ -29,7 +29,7 @@ #define STI_DRIVERVERSION "Version 0.9a" -struct sti_struct *default_sti __read_mostly; +static struct sti_struct *default_sti __read_mostly; /* number of STI ROMS found and their ptrs to each struct */ static int num_sti_roms __read_mostly; @@ -68,8 +68,7 @@ static const struct sti_init_flags default_init_flags = { .init_cmap_tx = 1, }; -int -sti_init_graph(struct sti_struct *sti) +static int sti_init_graph(struct sti_struct *sti) { struct sti_init_inptr_ext inptr_ext = { 0, }; struct sti_init_inptr inptr = { @@ -100,8 +99,7 @@ static const struct sti_conf_flags default_conf_flags = { .wait = STI_WAIT, }; -void -sti_inq_conf(struct sti_struct *sti) +static void sti_inq_conf(struct sti_struct *sti) { struct sti_conf_inptr inptr = { 0, }; unsigned long flags; @@ -237,8 +235,8 @@ static void sti_flush(unsigned long start, unsigned long end) flush_icache_range(start, end); } -void __devinit -sti_rom_copy(unsigned long base, unsigned long count, void *dest) +static void __devinit sti_rom_copy(unsigned long base, unsigned long count, + void *dest) { unsigned long dest_start = (unsigned long) dest; @@ -478,8 +476,8 @@ sti_init_glob_cfg(struct sti_struct *sti, } #ifdef CONFIG_FB -struct sti_cooked_font * __devinit -sti_select_fbfont(struct sti_cooked_rom *cooked_rom, const char *fbfont_name) +static struct sti_cooked_font __devinit +*sti_select_fbfont(struct sti_cooked_rom *cooked_rom, const char *fbfont_name) { const struct font_desc *fbfont; unsigned int size, bpc; @@ -534,16 +532,16 @@ sti_select_fbfont(struct sti_cooked_rom *cooked_rom, const char *fbfont_name) return cooked_font; } #else -struct sti_cooked_font * __devinit -sti_select_fbfont(struct sti_cooked_rom *cooked_rom, const char *fbfont_name) +static struct sti_cooked_font __devinit +*sti_select_fbfont(struct sti_cooked_rom *cooked_rom, const char *fbfont_name) { return NULL; } #endif -struct sti_cooked_font * __devinit -sti_select_font(struct sti_cooked_rom *rom, - int (*search_font_fnc) (struct sti_cooked_rom *,int,int) ) +static struct sti_cooked_font __devinit +*sti_select_font(struct sti_cooked_rom *rom, + int (*search_font_fnc)(struct sti_cooked_rom *, int, int)) { struct sti_cooked_font *font; int i; @@ -707,8 +705,7 @@ sti_get_bmode_rom (unsigned long address) return raw; } -struct sti_rom * __devinit -sti_get_wmode_rom (unsigned long address) +static struct sti_rom __devinit *sti_get_wmode_rom(unsigned long address) { struct sti_rom *raw; unsigned long size; @@ -723,8 +720,8 @@ sti_get_wmode_rom (unsigned long address) return raw; } -int __devinit -sti_read_rom(int wordmode, struct sti_struct *sti, unsigned long address) +static int __devinit sti_read_rom(int wordmode, struct sti_struct *sti, + unsigned long address) { struct sti_cooked_rom *cooked; struct sti_rom *raw = NULL; diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 33ebdb198daf..6b487801eeae 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -848,9 +848,8 @@ int fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var) { struct fb_fix_screeninfo *fix = &info->fix; - int xoffset = var->xoffset; - int yoffset = var->yoffset; - int err = 0, yres = info->var.yres; + unsigned int yres = info->var.yres; + int err = 0; if (var->yoffset > 0) { if (var->vmode & FB_VMODE_YWRAP) { @@ -866,8 +865,8 @@ fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var) (var->xoffset % fix->xpanstep))) err = -EINVAL; - if (err || !info->fbops->fb_pan_display || xoffset < 0 || - yoffset < 0 || var->yoffset + yres > info->var.yres_virtual || + if (err || !info->fbops->fb_pan_display || + var->yoffset + yres > info->var.yres_virtual || var->xoffset + info->var.xres > info->var.xres_virtual) return -EINVAL; @@ -1439,8 +1438,9 @@ register_framebuffer(struct fb_info *fb_info) break; fb_info->node = i; - fb_info->dev = device_create(fb_class, fb_info->device, - MKDEV(FB_MAJOR, i), "fb%d", i); + fb_info->dev = device_create_drvdata(fb_class, fb_info->device, + MKDEV(FB_MAJOR, i), NULL, + "fb%d", i); if (IS_ERR(fb_info->dev)) { /* Not fatal */ printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev)); diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c index 052e18058498..6a0aa180c266 100644 --- a/drivers/video/fbmon.c +++ b/drivers/video/fbmon.c @@ -879,7 +879,7 @@ int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var) if (edid_is_timing_block(block)) { var->xres = var->xres_virtual = H_ACTIVE; var->yres = var->yres_virtual = V_ACTIVE; - var->height = var->width = -1; + var->height = var->width = 0; var->right_margin = H_SYNC_OFFSET; var->left_margin = (H_ACTIVE + H_BLANKING) - (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH); diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index 09d7e22c6fef..9cd36c223d33 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -279,58 +279,42 @@ static struct diu_hw dr = { static struct diu_pool pool; -/* To allocate memory for framebuffer. First try __get_free_pages(). If it - * fails, try rh_alloc. The reason is __get_free_pages() cannot allocate - * very large memory (more than 4MB). We don't want to allocate all memory - * in rheap since small memory allocation/deallocation will fragment the - * rheap and make the furture large allocation fail. +/** + * fsl_diu_alloc - allocate memory for the DIU + * @size: number of bytes to allocate + * @param: returned physical address of memory + * + * This function allocates a physically-contiguous block of memory. */ - -static void *fsl_diu_alloc(unsigned long size, phys_addr_t *phys) +static void *fsl_diu_alloc(size_t size, phys_addr_t *phys) { void *virt; - pr_debug("size=%lu\n", size); + pr_debug("size=%zu\n", size); - virt = (void *)__get_free_pages(GFP_DMA | __GFP_ZERO, get_order(size)); + virt = alloc_pages_exact(size, GFP_DMA | __GFP_ZERO); if (virt) { *phys = virt_to_phys(virt); - pr_debug("virt %p, phys=%llx\n", virt, (uint64_t) *phys); - return virt; - } - if (!diu_ops.diu_mem) { - printk(KERN_INFO "%s: no diu_mem." - " To reserve more memory, put 'diufb=15M' " - "in the command line\n", __func__); - return NULL; - } - - virt = (void *)rh_alloc(&diu_ops.diu_rh_info, size, "DIU"); - if (virt) { - *phys = virt_to_bus(virt); - memset(virt, 0, size); + pr_debug("virt=%p phys=%llx\n", virt, + (unsigned long long)*phys); } - pr_debug("rh virt=%p phys=%llx\n", virt, (unsigned long long)*phys); - return virt; } -static void fsl_diu_free(void *p, unsigned long size) +/** + * fsl_diu_free - release DIU memory + * @virt: pointer returned by fsl_diu_alloc() + * @size: number of bytes allocated by fsl_diu_alloc() + * + * This function releases memory allocated by fsl_diu_alloc(). + */ +static void fsl_diu_free(void *virt, size_t size) { - pr_debug("p=%p size=%lu\n", p, size); + pr_debug("virt=%p size=%zu\n", virt, size); - if (!p) - return; - - if ((p >= diu_ops.diu_mem) && - (p < (diu_ops.diu_mem + diu_ops.diu_size))) { - pr_debug("rh\n"); - rh_free(&diu_ops.diu_rh_info, (unsigned long) p); - } else { - pr_debug("dma\n"); - free_pages((unsigned long)p, get_order(size)); - } + if (virt && size) + free_pages_exact(virt, size); } static int fsl_diu_enable_panel(struct fb_info *info) diff --git a/drivers/video/geode/lxfb.h b/drivers/video/geode/lxfb.h index 3b9416f4ee20..6a51448fd3f7 100644 --- a/drivers/video/geode/lxfb.h +++ b/drivers/video/geode/lxfb.h @@ -51,8 +51,6 @@ static inline unsigned int lx_get_pitch(unsigned int xres, int bpp) } void lx_set_mode(struct fb_info *); -void lx_get_gamma(struct fb_info *, unsigned int *, int); -void lx_set_gamma(struct fb_info *, unsigned int *, int); unsigned int lx_framebuffer_size(void); int lx_blank_display(struct fb_info *, int); void lx_set_palette_reg(struct fb_info *, unsigned int, unsigned int, diff --git a/drivers/video/geode/lxfb_ops.c b/drivers/video/geode/lxfb_ops.c index aaef9165ec9b..b1cd49c99356 100644 --- a/drivers/video/geode/lxfb_ops.c +++ b/drivers/video/geode/lxfb_ops.c @@ -517,25 +517,25 @@ void lx_set_palette_reg(struct fb_info *info, unsigned regno, int lx_blank_display(struct fb_info *info, int blank_mode) { struct lxfb_par *par = info->par; - u32 dcfg, fp_pm; - int blank, hsync, vsync, crt; + u32 dcfg, misc, fp_pm; + int blank, hsync, vsync; /* CRT power saving modes. */ switch (blank_mode) { case FB_BLANK_UNBLANK: - blank = 0; hsync = 1; vsync = 1; crt = 1; + blank = 0; hsync = 1; vsync = 1; break; case FB_BLANK_NORMAL: - blank = 1; hsync = 1; vsync = 1; crt = 1; + blank = 1; hsync = 1; vsync = 1; break; case FB_BLANK_VSYNC_SUSPEND: - blank = 1; hsync = 1; vsync = 0; crt = 1; + blank = 1; hsync = 1; vsync = 0; break; case FB_BLANK_HSYNC_SUSPEND: - blank = 1; hsync = 0; vsync = 1; crt = 1; + blank = 1; hsync = 0; vsync = 1; break; case FB_BLANK_POWERDOWN: - blank = 1; hsync = 0; vsync = 0; crt = 0; + blank = 1; hsync = 0; vsync = 0; break; default: return -EINVAL; @@ -545,15 +545,23 @@ int lx_blank_display(struct fb_info *info, int blank_mode) dcfg &= ~(VP_DCFG_DAC_BL_EN | VP_DCFG_HSYNC_EN | VP_DCFG_VSYNC_EN | VP_DCFG_CRT_EN); if (!blank) - dcfg |= VP_DCFG_DAC_BL_EN; + dcfg |= VP_DCFG_DAC_BL_EN | VP_DCFG_CRT_EN; if (hsync) dcfg |= VP_DCFG_HSYNC_EN; if (vsync) dcfg |= VP_DCFG_VSYNC_EN; - if (crt) - dcfg |= VP_DCFG_CRT_EN; + write_vp(par, VP_DCFG, dcfg); + misc = read_vp(par, VP_MISC); + + if (vsync && hsync) + misc &= ~VP_MISC_DACPWRDN; + else + misc |= VP_MISC_DACPWRDN; + + write_vp(par, VP_MISC, misc); + /* Power on/off flat panel */ if (par->output & OUTPUT_PANEL) { diff --git a/drivers/video/hgafb.c b/drivers/video/hgafb.c index c18880d9db1f..0129c044f6d6 100644 --- a/drivers/video/hgafb.c +++ b/drivers/video/hgafb.c @@ -551,7 +551,7 @@ static struct fb_ops hgafb_ops = { * Initialization */ -static int __init hgafb_probe(struct device *device) +static int __init hgafb_probe(struct platform_device *pdev) { struct fb_info *info; @@ -565,7 +565,7 @@ static int __init hgafb_probe(struct device *device) printk(KERN_INFO "hgafb: %s with %ldK of memory detected.\n", hga_type_name, hga_vram_len/1024); - info = framebuffer_alloc(0, NULL); + info = framebuffer_alloc(0, &pdev->dev); if (!info) { iounmap(hga_vram); return -ENOMEM; @@ -593,13 +593,13 @@ static int __init hgafb_probe(struct device *device) printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, info->fix.id); - dev_set_drvdata(device, info); + platform_set_drvdata(pdev, info); return 0; } -static int hgafb_remove(struct device *device) +static int hgafb_remove(struct platform_device *pdev) { - struct fb_info *info = dev_get_drvdata(device); + struct fb_info *info = platform_get_drvdata(pdev); hga_txt_mode(); hga_clear_screen(); @@ -620,16 +620,15 @@ static int hgafb_remove(struct device *device) return 0; } -static struct device_driver hgafb_driver = { - .name = "hgafb", - .bus = &platform_bus_type, +static struct platform_driver hgafb_driver = { .probe = hgafb_probe, .remove = hgafb_remove, + .driver = { + .name = "hgafb", + }, }; -static struct platform_device hgafb_device = { - .name = "hgafb", -}; +static struct platform_device *hgafb_device; static int __init hgafb_init(void) { @@ -638,12 +637,15 @@ static int __init hgafb_init(void) if (fb_get_options("hgafb", NULL)) return -ENODEV; - ret = driver_register(&hgafb_driver); + ret = platform_driver_register(&hgafb_driver); if (!ret) { - ret = platform_device_register(&hgafb_device); - if (ret) - driver_unregister(&hgafb_driver); + hgafb_device = platform_device_register_simple("hgafb", 0, NULL, 0); + + if (IS_ERR(hgafb_device)) { + platform_driver_unregister(&hgafb_driver); + ret = PTR_ERR(hgafb_device); + } } return ret; @@ -651,8 +653,8 @@ static int __init hgafb_init(void) static void __exit hgafb_exit(void) { - platform_device_unregister(&hgafb_device); - driver_unregister(&hgafb_driver); + platform_device_unregister(hgafb_device); + platform_driver_unregister(&hgafb_driver); } /* ------------------------------------------------------------------------- diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c index 94e4d3ac1a05..0c5a475c1cae 100644 --- a/drivers/video/imxfb.c +++ b/drivers/video/imxfb.c @@ -24,6 +24,7 @@ #include <linux/string.h> #include <linux/interrupt.h> #include <linux/slab.h> +#include <linux/mm.h> #include <linux/fb.h> #include <linux/delay.h> #include <linux/init.h> diff --git a/drivers/video/macfb.c b/drivers/video/macfb.c index aa8c714d6245..b790ddff76f9 100644 --- a/drivers/video/macfb.c +++ b/drivers/video/macfb.c @@ -596,7 +596,7 @@ static struct fb_ops macfb_ops = { .fb_imageblit = cfb_imageblit, }; -void __init macfb_setup(char *options) +static void __init macfb_setup(char *options) { char *this_opt; diff --git a/drivers/video/neofb.c b/drivers/video/neofb.c index 5246b0402d76..25172b2a2a94 100644 --- a/drivers/video/neofb.c +++ b/drivers/video/neofb.c @@ -201,7 +201,6 @@ static int neoFindMode(int xres, int yres, int depth) * * Determine the closest clock frequency to the one requested. */ -#define REF_FREQ 0xe517 /* 14.31818 in 20.12 fixed point */ #define MAX_N 127 #define MAX_D 31 #define MAX_F 1 @@ -211,27 +210,24 @@ static void neoCalcVCLK(const struct fb_info *info, { int n, d, f; int n_best = 0, d_best = 0, f_best = 0; - long f_best_diff = (0x7ffff << 12); /* 20.12 */ - long f_target = (freq << 12) / 1000; /* 20.12 */ + long f_best_diff = 0x7ffff; for (f = 0; f <= MAX_F; f++) - for (n = 0; n <= MAX_N; n++) - for (d = 0; d <= MAX_D; d++) { - long f_out; /* 20.12 */ - long f_diff; /* 20.12 */ - - f_out = - ((((n + 1) << 12) / ((d + - 1) * - (1 << f))) >> 12) - * REF_FREQ; - f_diff = abs(f_out - f_target); - if (f_diff < f_best_diff) { + for (d = 0; d <= MAX_D; d++) + for (n = 0; n <= MAX_N; n++) { + long f_out; + long f_diff; + + f_out = ((14318 * (n + 1)) / (d + 1)) >> f; + f_diff = abs(f_out - freq); + if (f_diff <= f_best_diff) { f_best_diff = f_diff; n_best = n; d_best = d; f_best = f; } + if (f_out > freq) + break; } if (info->fix.accel == FB_ACCEL_NEOMAGIC_NM2200 || @@ -248,11 +244,11 @@ static void neoCalcVCLK(const struct fb_info *info, par->VCLK3Denominator = d_best; #ifdef NEOFB_DEBUG - printk("neoVCLK: f:%d NumLow=%d NumHi=%d Den=%d Df=%d\n", - f_target >> 12, + printk(KERN_DEBUG "neoVCLK: f:%ld NumLow=%d NumHi=%d Den=%d Df=%ld\n", + freq, par->VCLK3NumeratorLow, par->VCLK3NumeratorHigh, - par->VCLK3Denominator, f_best_diff >> 12); + par->VCLK3Denominator, f_best_diff); #endif } @@ -263,15 +259,20 @@ static void neoCalcVCLK(const struct fb_info *info, */ static int vgaHWInit(const struct fb_var_screeninfo *var, - const struct fb_info *info, - struct neofb_par *par, struct xtimings *timings) + struct neofb_par *par) { + int hsync_end = var->xres + var->right_margin + var->hsync_len; + int htotal = (hsync_end + var->left_margin) >> 3; + int vsync_start = var->yres + var->lower_margin; + int vsync_end = vsync_start + var->vsync_len; + int vtotal = vsync_end + var->upper_margin; + par->MiscOutReg = 0x23; - if (!(timings->sync & FB_SYNC_HOR_HIGH_ACT)) + if (!(var->sync & FB_SYNC_HOR_HIGH_ACT)) par->MiscOutReg |= 0x40; - if (!(timings->sync & FB_SYNC_VERT_HIGH_ACT)) + if (!(var->sync & FB_SYNC_VERT_HIGH_ACT)) par->MiscOutReg |= 0x80; /* @@ -286,25 +287,25 @@ static int vgaHWInit(const struct fb_var_screeninfo *var, /* * CRTC Controller */ - par->CRTC[0] = (timings->HTotal >> 3) - 5; - par->CRTC[1] = (timings->HDisplay >> 3) - 1; - par->CRTC[2] = (timings->HDisplay >> 3) - 1; - par->CRTC[3] = (((timings->HTotal >> 3) - 1) & 0x1F) | 0x80; - par->CRTC[4] = (timings->HSyncStart >> 3); - par->CRTC[5] = ((((timings->HTotal >> 3) - 1) & 0x20) << 2) - | (((timings->HSyncEnd >> 3)) & 0x1F); - par->CRTC[6] = (timings->VTotal - 2) & 0xFF; - par->CRTC[7] = (((timings->VTotal - 2) & 0x100) >> 8) - | (((timings->VDisplay - 1) & 0x100) >> 7) - | ((timings->VSyncStart & 0x100) >> 6) - | (((timings->VDisplay - 1) & 0x100) >> 5) - | 0x10 | (((timings->VTotal - 2) & 0x200) >> 4) - | (((timings->VDisplay - 1) & 0x200) >> 3) - | ((timings->VSyncStart & 0x200) >> 2); + par->CRTC[0] = htotal - 5; + par->CRTC[1] = (var->xres >> 3) - 1; + par->CRTC[2] = (var->xres >> 3) - 1; + par->CRTC[3] = ((htotal - 1) & 0x1F) | 0x80; + par->CRTC[4] = ((var->xres + var->right_margin) >> 3); + par->CRTC[5] = (((htotal - 1) & 0x20) << 2) + | (((hsync_end >> 3)) & 0x1F); + par->CRTC[6] = (vtotal - 2) & 0xFF; + par->CRTC[7] = (((vtotal - 2) & 0x100) >> 8) + | (((var->yres - 1) & 0x100) >> 7) + | ((vsync_start & 0x100) >> 6) + | (((var->yres - 1) & 0x100) >> 5) + | 0x10 | (((vtotal - 2) & 0x200) >> 4) + | (((var->yres - 1) & 0x200) >> 3) + | ((vsync_start & 0x200) >> 2); par->CRTC[8] = 0x00; - par->CRTC[9] = (((timings->VDisplay - 1) & 0x200) >> 4) | 0x40; + par->CRTC[9] = (((var->yres - 1) & 0x200) >> 4) | 0x40; - if (timings->dblscan) + if (var->vmode & FB_VMODE_DOUBLE) par->CRTC[9] |= 0x80; par->CRTC[10] = 0x00; @@ -313,13 +314,13 @@ static int vgaHWInit(const struct fb_var_screeninfo *var, par->CRTC[13] = 0x00; par->CRTC[14] = 0x00; par->CRTC[15] = 0x00; - par->CRTC[16] = timings->VSyncStart & 0xFF; - par->CRTC[17] = (timings->VSyncEnd & 0x0F) | 0x20; - par->CRTC[18] = (timings->VDisplay - 1) & 0xFF; + par->CRTC[16] = vsync_start & 0xFF; + par->CRTC[17] = (vsync_end & 0x0F) | 0x20; + par->CRTC[18] = (var->yres - 1) & 0xFF; par->CRTC[19] = var->xres_virtual >> 4; par->CRTC[20] = 0x00; - par->CRTC[21] = (timings->VDisplay - 1) & 0xFF; - par->CRTC[22] = (timings->VTotal - 1) & 0xFF; + par->CRTC[21] = (var->yres - 1) & 0xFF; + par->CRTC[22] = (vtotal - 1) & 0xFF; par->CRTC[23] = 0xC3; par->CRTC[24] = 0xFF; @@ -483,7 +484,8 @@ static inline int neo2200_sync(struct fb_info *info) { struct neofb_par *par = info->par; - while (readl(&par->neo2200->bltStat) & 1); + while (readl(&par->neo2200->bltStat) & 1) + cpu_relax(); return 0; } @@ -591,34 +593,14 @@ static int neofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct neofb_par *par = info->par; - unsigned int pixclock = var->pixclock; - struct xtimings timings; int memlen, vramlen; int mode_ok = 0; DBG("neofb_check_var"); - if (!pixclock) - pixclock = 10000; /* 10ns = 100MHz */ - timings.pixclock = 1000000000 / pixclock; - if (timings.pixclock < 1) - timings.pixclock = 1; - - if (timings.pixclock > par->maxClock) + if (PICOS2KHZ(var->pixclock) > par->maxClock) return -EINVAL; - timings.dblscan = var->vmode & FB_VMODE_DOUBLE; - timings.interlaced = var->vmode & FB_VMODE_INTERLACED; - timings.HDisplay = var->xres; - timings.HSyncStart = timings.HDisplay + var->right_margin; - timings.HSyncEnd = timings.HSyncStart + var->hsync_len; - timings.HTotal = timings.HSyncEnd + var->left_margin; - timings.VDisplay = var->yres; - timings.VSyncStart = timings.VDisplay + var->lower_margin; - timings.VSyncEnd = timings.VSyncStart + var->vsync_len; - timings.VTotal = timings.VSyncEnd + var->upper_margin; - timings.sync = var->sync; - /* Is the mode larger than the LCD panel? */ if (par->internal_display && ((var->xres > par->NeoPanelWidth) || @@ -759,11 +741,11 @@ neofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) static int neofb_set_par(struct fb_info *info) { struct neofb_par *par = info->par; - struct xtimings timings; unsigned char temp; int i, clock_hi = 0; int lcd_stretch; int hoffset, voffset; + int vsync_start, vtotal; DBG("neofb_set_par"); @@ -771,28 +753,15 @@ static int neofb_set_par(struct fb_info *info) vgaHWProtect(1); /* Blank the screen */ - timings.dblscan = info->var.vmode & FB_VMODE_DOUBLE; - timings.interlaced = info->var.vmode & FB_VMODE_INTERLACED; - timings.HDisplay = info->var.xres; - timings.HSyncStart = timings.HDisplay + info->var.right_margin; - timings.HSyncEnd = timings.HSyncStart + info->var.hsync_len; - timings.HTotal = timings.HSyncEnd + info->var.left_margin; - timings.VDisplay = info->var.yres; - timings.VSyncStart = timings.VDisplay + info->var.lower_margin; - timings.VSyncEnd = timings.VSyncStart + info->var.vsync_len; - timings.VTotal = timings.VSyncEnd + info->var.upper_margin; - timings.sync = info->var.sync; - timings.pixclock = PICOS2KHZ(info->var.pixclock); - - if (timings.pixclock < 1) - timings.pixclock = 1; + vsync_start = info->var.yres + info->var.lower_margin; + vtotal = vsync_start + info->var.vsync_len + info->var.upper_margin; /* * This will allocate the datastructure and initialize all of the * generic VGA registers. */ - if (vgaHWInit(&info->var, info, par, &timings)) + if (vgaHWInit(&info->var, par)) return -EINVAL; /* @@ -831,10 +800,10 @@ static int neofb_set_par(struct fb_info *info) par->ExtCRTDispAddr = 0x10; /* Vertical Extension */ - par->VerticalExt = (((timings.VTotal - 2) & 0x400) >> 10) - | (((timings.VDisplay - 1) & 0x400) >> 9) - | (((timings.VSyncStart) & 0x400) >> 8) - | (((timings.VSyncStart) & 0x400) >> 7); + par->VerticalExt = (((vtotal - 2) & 0x400) >> 10) + | (((info->var.yres - 1) & 0x400) >> 9) + | (((vsync_start) & 0x400) >> 8) + | (((vsync_start) & 0x400) >> 7); /* Fast write bursts on unless disabled. */ if (par->pci_burst) @@ -995,7 +964,7 @@ static int neofb_set_par(struct fb_info *info) * Calculate the VCLK that most closely matches the requested dot * clock. */ - neoCalcVCLK(info, par, timings.pixclock); + neoCalcVCLK(info, par, PICOS2KHZ(info->var.pixclock)); /* Since we program the clocks ourselves, always use VCLK3. */ par->MiscOutReg |= 0x0C; @@ -1927,9 +1896,6 @@ static int __devinit neo_init_hw(struct fb_info *info) int maxClock = 65000; int CursorMem = 1024; int CursorOff = 0x100; - int linearSize = 1024; - int maxWidth = 1024; - int maxHeight = 1024; DBG("neo_init_hw"); @@ -1948,81 +1914,52 @@ static int __devinit neo_init_hw(struct fb_info *info) case FB_ACCEL_NEOMAGIC_NM2070: videoRam = 896; maxClock = 65000; - CursorMem = 2048; - CursorOff = 0x100; - linearSize = 1024; - maxWidth = 1024; - maxHeight = 1024; break; case FB_ACCEL_NEOMAGIC_NM2090: case FB_ACCEL_NEOMAGIC_NM2093: - videoRam = 1152; - maxClock = 80000; - CursorMem = 2048; - CursorOff = 0x100; - linearSize = 2048; - maxWidth = 1024; - maxHeight = 1024; - break; case FB_ACCEL_NEOMAGIC_NM2097: videoRam = 1152; maxClock = 80000; - CursorMem = 1024; - CursorOff = 0x100; - linearSize = 2048; - maxWidth = 1024; - maxHeight = 1024; break; case FB_ACCEL_NEOMAGIC_NM2160: videoRam = 2048; maxClock = 90000; - CursorMem = 1024; - CursorOff = 0x100; - linearSize = 2048; - maxWidth = 1024; - maxHeight = 1024; break; case FB_ACCEL_NEOMAGIC_NM2200: videoRam = 2560; maxClock = 110000; - CursorMem = 1024; - CursorOff = 0x1000; - linearSize = 4096; - maxWidth = 1280; - maxHeight = 1024; /* ???? */ - - par->neo2200 = (Neo2200 __iomem *) par->mmio_vbase; break; case FB_ACCEL_NEOMAGIC_NM2230: videoRam = 3008; maxClock = 110000; - CursorMem = 1024; - CursorOff = 0x1000; - linearSize = 4096; - maxWidth = 1280; - maxHeight = 1024; /* ???? */ - - par->neo2200 = (Neo2200 __iomem *) par->mmio_vbase; break; case FB_ACCEL_NEOMAGIC_NM2360: videoRam = 4096; maxClock = 110000; - CursorMem = 1024; - CursorOff = 0x1000; - linearSize = 4096; - maxWidth = 1280; - maxHeight = 1024; /* ???? */ - - par->neo2200 = (Neo2200 __iomem *) par->mmio_vbase; break; case FB_ACCEL_NEOMAGIC_NM2380: videoRam = 6144; maxClock = 110000; + break; + } + switch (info->fix.accel) { + case FB_ACCEL_NEOMAGIC_NM2070: + case FB_ACCEL_NEOMAGIC_NM2090: + case FB_ACCEL_NEOMAGIC_NM2093: + CursorMem = 2048; + CursorOff = 0x100; + break; + case FB_ACCEL_NEOMAGIC_NM2097: + case FB_ACCEL_NEOMAGIC_NM2160: + CursorMem = 1024; + CursorOff = 0x100; + break; + case FB_ACCEL_NEOMAGIC_NM2200: + case FB_ACCEL_NEOMAGIC_NM2230: + case FB_ACCEL_NEOMAGIC_NM2360: + case FB_ACCEL_NEOMAGIC_NM2380: CursorMem = 1024; CursorOff = 0x1000; - linearSize = 8192; - maxWidth = 1280; - maxHeight = 1024; /* ???? */ par->neo2200 = (Neo2200 __iomem *) par->mmio_vbase; break; @@ -2036,7 +1973,7 @@ static int __devinit neo_init_hw(struct fb_info *info) */ par->maxClock = maxClock; par->cursorOff = CursorOff; - return ((videoRam * 1024)); + return videoRam * 1024; } diff --git a/drivers/video/offb.c b/drivers/video/offb.c index d7b3dcc0dc43..e1d9eeb1aeaf 100644 --- a/drivers/video/offb.c +++ b/drivers/video/offb.c @@ -47,6 +47,7 @@ enum { cmap_M3B, /* ATI Rage Mobility M3 Head B */ cmap_radeon, /* ATI Radeon */ cmap_gxt2000, /* IBM GXT2000 */ + cmap_avivo, /* ATI R5xx */ }; struct offb_par { @@ -58,26 +59,36 @@ struct offb_par { struct offb_par default_par; - /* - * Interface used by the world - */ - -static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp, struct fb_info *info); -static int offb_blank(int blank, struct fb_info *info); - #ifdef CONFIG_PPC32 extern boot_infos_t *boot_infos; #endif -static struct fb_ops offb_ops = { - .owner = THIS_MODULE, - .fb_setcolreg = offb_setcolreg, - .fb_blank = offb_blank, - .fb_fillrect = cfb_fillrect, - .fb_copyarea = cfb_copyarea, - .fb_imageblit = cfb_imageblit, -}; +/* Definitions used by the Avivo palette hack */ +#define AVIVO_DC_LUT_RW_SELECT 0x6480 +#define AVIVO_DC_LUT_RW_MODE 0x6484 +#define AVIVO_DC_LUT_RW_INDEX 0x6488 +#define AVIVO_DC_LUT_SEQ_COLOR 0x648c +#define AVIVO_DC_LUT_PWL_DATA 0x6490 +#define AVIVO_DC_LUT_30_COLOR 0x6494 +#define AVIVO_DC_LUT_READ_PIPE_SELECT 0x6498 +#define AVIVO_DC_LUT_WRITE_EN_MASK 0x649c +#define AVIVO_DC_LUT_AUTOFILL 0x64a0 + +#define AVIVO_DC_LUTA_CONTROL 0x64c0 +#define AVIVO_DC_LUTA_BLACK_OFFSET_BLUE 0x64c4 +#define AVIVO_DC_LUTA_BLACK_OFFSET_GREEN 0x64c8 +#define AVIVO_DC_LUTA_BLACK_OFFSET_RED 0x64cc +#define AVIVO_DC_LUTA_WHITE_OFFSET_BLUE 0x64d0 +#define AVIVO_DC_LUTA_WHITE_OFFSET_GREEN 0x64d4 +#define AVIVO_DC_LUTA_WHITE_OFFSET_RED 0x64d8 + +#define AVIVO_DC_LUTB_CONTROL 0x6cc0 +#define AVIVO_DC_LUTB_BLACK_OFFSET_BLUE 0x6cc4 +#define AVIVO_DC_LUTB_BLACK_OFFSET_GREEN 0x6cc8 +#define AVIVO_DC_LUTB_BLACK_OFFSET_RED 0x6ccc +#define AVIVO_DC_LUTB_WHITE_OFFSET_BLUE 0x6cd0 +#define AVIVO_DC_LUTB_WHITE_OFFSET_GREEN 0x6cd4 +#define AVIVO_DC_LUTB_WHITE_OFFSET_RED 0x6cd8 /* * Set a single color register. The values supplied are already @@ -160,6 +171,17 @@ static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, out_le32(((unsigned __iomem *) par->cmap_adr) + regno, (red << 16 | green << 8 | blue)); break; + case cmap_avivo: + /* Write to both LUTs for now */ + writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); + writeb(regno, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX); + writel(((red) << 22) | ((green) << 12) | ((blue) << 2), + par->cmap_adr + AVIVO_DC_LUT_30_COLOR); + writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); + writeb(regno, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX); + writel(((red) << 22) | ((green) << 12) | ((blue) << 2), + par->cmap_adr + AVIVO_DC_LUT_30_COLOR); + break; } return 0; @@ -216,12 +238,59 @@ static int offb_blank(int blank, struct fb_info *info) out_le32(((unsigned __iomem *) par->cmap_adr) + i, 0); break; + case cmap_avivo: + writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); + writeb(i, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX); + writel(0, par->cmap_adr + AVIVO_DC_LUT_30_COLOR); + writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); + writeb(i, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX); + writel(0, par->cmap_adr + AVIVO_DC_LUT_30_COLOR); + break; } } else fb_set_cmap(&info->cmap, info); return 0; } +static int offb_set_par(struct fb_info *info) +{ + struct offb_par *par = (struct offb_par *) info->par; + + /* On avivo, initialize palette control */ + if (par->cmap_type == cmap_avivo) { + writel(0, par->cmap_adr + AVIVO_DC_LUTA_CONTROL); + writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_BLUE); + writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_GREEN); + writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_RED); + writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_BLUE); + writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_GREEN); + writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_RED); + writel(0, par->cmap_adr + AVIVO_DC_LUTB_CONTROL); + writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_BLUE); + writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_GREEN); + writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_RED); + writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_BLUE); + writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_GREEN); + writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_RED); + writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); + writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_MODE); + writel(0x0000003f, par->cmap_adr + AVIVO_DC_LUT_WRITE_EN_MASK); + writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); + writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_MODE); + writel(0x0000003f, par->cmap_adr + AVIVO_DC_LUT_WRITE_EN_MASK); + } + return 0; +} + +static struct fb_ops offb_ops = { + .owner = THIS_MODULE, + .fb_setcolreg = offb_setcolreg, + .fb_set_par = offb_set_par, + .fb_blank = offb_blank, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; static void __iomem *offb_map_reg(struct device_node *np, int index, unsigned long offset, unsigned long size) @@ -245,6 +314,59 @@ static void __iomem *offb_map_reg(struct device_node *np, int index, return ioremap(taddr + offset, size); } +static void offb_init_palette_hacks(struct fb_info *info, struct device_node *dp, + const char *name, unsigned long address) +{ + struct offb_par *par = (struct offb_par *) info->par; + + if (dp && !strncmp(name, "ATY,Rage128", 11)) { + par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); + if (par->cmap_adr) + par->cmap_type = cmap_r128; + } else if (dp && (!strncmp(name, "ATY,RageM3pA", 12) + || !strncmp(name, "ATY,RageM3p12A", 14))) { + par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); + if (par->cmap_adr) + par->cmap_type = cmap_M3A; + } else if (dp && !strncmp(name, "ATY,RageM3pB", 12)) { + par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); + if (par->cmap_adr) + par->cmap_type = cmap_M3B; + } else if (dp && !strncmp(name, "ATY,Rage6", 9)) { + par->cmap_adr = offb_map_reg(dp, 1, 0, 0x1fff); + if (par->cmap_adr) + par->cmap_type = cmap_radeon; + } else if (!strncmp(name, "ATY,", 4)) { + unsigned long base = address & 0xff000000UL; + par->cmap_adr = + ioremap(base + 0x7ff000, 0x1000) + 0xcc0; + par->cmap_data = par->cmap_adr + 1; + par->cmap_type = cmap_m64; + } else if (dp && (of_device_is_compatible(dp, "pci1014,b7") || + of_device_is_compatible(dp, "pci1014,21c"))) { + par->cmap_adr = offb_map_reg(dp, 0, 0x6000, 0x1000); + if (par->cmap_adr) + par->cmap_type = cmap_gxt2000; + } else if (dp && !strncmp(name, "vga,Display-", 12)) { + /* Look for AVIVO initialized by SLOF */ + struct device_node *pciparent = of_get_parent(dp); + const u32 *vid, *did; + vid = of_get_property(pciparent, "vendor-id", NULL); + did = of_get_property(pciparent, "device-id", NULL); + /* This will match most R5xx */ + if (vid && did && *vid == 0x1002 && + ((*did >= 0x7100 && *did < 0x7800) || + (*did >= 0x9400))) { + par->cmap_adr = offb_map_reg(pciparent, 2, 0, 0x10000); + if (par->cmap_adr) + par->cmap_type = cmap_avivo; + } + of_node_put(pciparent); + } + info->fix.visual = (par->cmap_type != cmap_unknown) ? + FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_STATIC_PSEUDOCOLOR; +} + static void __init offb_init_fb(const char *name, const char *full_name, int width, int height, int depth, int pitch, unsigned long address, @@ -283,6 +405,7 @@ static void __init offb_init_fb(const char *name, const char *full_name, fix = &info->fix; var = &info->var; + info->par = par; strcpy(fix->id, "OFfb "); strncat(fix->id, name, sizeof(fix->id) - sizeof("OFfb ")); @@ -298,39 +421,9 @@ static void __init offb_init_fb(const char *name, const char *full_name, fix->type_aux = 0; par->cmap_type = cmap_unknown; - if (depth == 8) { - if (dp && !strncmp(name, "ATY,Rage128", 11)) { - par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); - if (par->cmap_adr) - par->cmap_type = cmap_r128; - } else if (dp && (!strncmp(name, "ATY,RageM3pA", 12) - || !strncmp(name, "ATY,RageM3p12A", 14))) { - par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); - if (par->cmap_adr) - par->cmap_type = cmap_M3A; - } else if (dp && !strncmp(name, "ATY,RageM3pB", 12)) { - par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); - if (par->cmap_adr) - par->cmap_type = cmap_M3B; - } else if (dp && !strncmp(name, "ATY,Rage6", 9)) { - par->cmap_adr = offb_map_reg(dp, 1, 0, 0x1fff); - if (par->cmap_adr) - par->cmap_type = cmap_radeon; - } else if (!strncmp(name, "ATY,", 4)) { - unsigned long base = address & 0xff000000UL; - par->cmap_adr = - ioremap(base + 0x7ff000, 0x1000) + 0xcc0; - par->cmap_data = par->cmap_adr + 1; - par->cmap_type = cmap_m64; - } else if (dp && (of_device_is_compatible(dp, "pci1014,b7") || - of_device_is_compatible(dp, "pci1014,21c"))) { - par->cmap_adr = offb_map_reg(dp, 0, 0x6000, 0x1000); - if (par->cmap_adr) - par->cmap_type = cmap_gxt2000; - } - fix->visual = (par->cmap_type != cmap_unknown) ? - FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_STATIC_PSEUDOCOLOR; - } else + if (depth == 8) + offb_init_palette_hacks(info, dp, name, address); + else fix->visual = FB_VISUAL_TRUECOLOR; var->xoffset = var->yoffset = 0; @@ -395,7 +488,6 @@ static void __init offb_init_fb(const char *name, const char *full_name, info->fbops = &offb_ops; info->screen_base = ioremap(address, fix->smem_len); - info->par = par; info->pseudo_palette = (void *) (info + 1); info->flags = FBINFO_DEFAULT | foreign_endian; diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c index ab32ceb06178..ab77c51fe9d6 100644 --- a/drivers/video/omap/dispc.c +++ b/drivers/video/omap/dispc.c @@ -20,6 +20,7 @@ */ #include <linux/kernel.h> #include <linux/dma-mapping.h> +#include <linux/mm.h> #include <linux/vmalloc.h> #include <linux/clk.h> #include <linux/io.h> diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c index 14d0f7a11145..f85af5c4fa68 100644 --- a/drivers/video/omap/omapfb_main.c +++ b/drivers/video/omap/omapfb_main.c @@ -25,6 +25,7 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <linux/platform_device.h> +#include <linux/mm.h> #include <linux/uaccess.h> #include <asm/mach-types.h> diff --git a/drivers/video/omap/sossi.c b/drivers/video/omap/sossi.c index 81dbcf53cf0e..fafd0f26b90f 100644 --- a/drivers/video/omap/sossi.c +++ b/drivers/video/omap/sossi.c @@ -646,7 +646,7 @@ static int sossi_init(struct omapfb_device *fbdev) sossi_write_reg(SOSSI_INIT1_REG, l); if ((r = request_irq(INT_1610_SoSSI_MATCH, sossi_match_irq, - IRQT_FALLING, + IRQ_TYPE_EDGE_FALLING, "sossi_match", sossi.fbdev->dev)) < 0) { dev_err(sossi.fbdev->dev, "can't get SoSSI match IRQ\n"); goto err; diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c index dc3af1c78c56..4b5d80771904 100644 --- a/drivers/video/ps3fb.c +++ b/drivers/video/ps3fb.c @@ -1297,6 +1297,7 @@ static int ps3fb_shutdown(struct ps3_system_bus_device *dev) static struct ps3_system_bus_driver ps3fb_driver = { .match_id = PS3_MATCH_ID_GRAPHICS, + .match_sub_id = PS3_MATCH_SUB_ID_FB, .core.name = DEVICE_NAME, .core.owner = THIS_MODULE, .probe = ps3fb_probe, diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index d0746261c957..69de2fed6c58 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -30,6 +30,7 @@ #include <linux/string.h> #include <linux/interrupt.h> #include <linux/slab.h> +#include <linux/mm.h> #include <linux/fb.h> #include <linux/delay.h> #include <linux/init.h> @@ -40,6 +41,7 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/completion.h> +#include <linux/mutex.h> #include <linux/kthread.h> #include <linux/freezer.h> @@ -227,6 +229,22 @@ static int pxafb_bpp_to_lccr3(struct fb_var_screeninfo *var) case 4: ret = LCCR3_4BPP; break; case 8: ret = LCCR3_8BPP; break; case 16: ret = LCCR3_16BPP; break; + case 24: + switch (var->red.length + var->green.length + + var->blue.length + var->transp.length) { + case 18: ret = LCCR3_18BPP_P | LCCR3_PDFOR_3; break; + case 19: ret = LCCR3_19BPP_P; break; + } + break; + case 32: + switch (var->red.length + var->green.length + + var->blue.length + var->transp.length) { + case 18: ret = LCCR3_18BPP | LCCR3_PDFOR_3; break; + case 19: ret = LCCR3_19BPP; break; + case 24: ret = LCCR3_24BPP | LCCR3_PDFOR_3; break; + case 25: ret = LCCR3_25BPP; break; + } + break; } return ret; } @@ -345,6 +363,41 @@ static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) var->green.offset = 5; var->green.length = 6; var->blue.offset = 0; var->blue.length = 5; var->transp.offset = var->transp.length = 0; + } else if (var->bits_per_pixel > 16) { + struct pxafb_mode_info *mode; + + mode = pxafb_getmode(inf, var); + if (!mode) + return -EINVAL; + + switch (mode->depth) { + case 18: /* RGB666 */ + var->transp.offset = var->transp.length = 0; + var->red.offset = 12; var->red.length = 6; + var->green.offset = 6; var->green.length = 6; + var->blue.offset = 0; var->blue.length = 6; + break; + case 19: /* RGBT666 */ + var->transp.offset = 18; var->transp.length = 1; + var->red.offset = 12; var->red.length = 6; + var->green.offset = 6; var->green.length = 6; + var->blue.offset = 0; var->blue.length = 6; + break; + case 24: /* RGB888 */ + var->transp.offset = var->transp.length = 0; + var->red.offset = 16; var->red.length = 8; + var->green.offset = 8; var->green.length = 8; + var->blue.offset = 0; var->blue.length = 8; + break; + case 25: /* RGBT888 */ + var->transp.offset = 24; var->transp.length = 1; + var->red.offset = 16; var->red.length = 8; + var->green.offset = 8; var->green.length = 8; + var->blue.offset = 0; var->blue.length = 8; + break; + default: + return -EINVAL; + } } else { var->red.offset = var->green.offset = 0; var->blue.offset = var->transp.offset = 0; @@ -376,7 +429,7 @@ static int pxafb_set_par(struct fb_info *info) struct pxafb_info *fbi = (struct pxafb_info *)info; struct fb_var_screeninfo *var = &info->var; - if (var->bits_per_pixel == 16) + if (var->bits_per_pixel >= 16) fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR; else if (!fbi->cmap_static) fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; @@ -391,7 +444,7 @@ static int pxafb_set_par(struct fb_info *info) fbi->fb.fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; - if (var->bits_per_pixel == 16) + if (var->bits_per_pixel >= 16) fbi->palette_size = 0; else fbi->palette_size = var->bits_per_pixel == 1 ? @@ -404,7 +457,7 @@ static int pxafb_set_par(struct fb_info *info) */ pxafb_set_truecolor(fbi->fb.fix.visual == FB_VISUAL_TRUECOLOR); - if (fbi->fb.var.bits_per_pixel == 16) + if (fbi->fb.var.bits_per_pixel >= 16) fb_dealloc_cmap(&fbi->fb.cmap); else fb_alloc_cmap(&fbi->fb.cmap, 1<<fbi->fb.var.bits_per_pixel, 0); @@ -831,6 +884,8 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var, case 4: case 8: case 16: + case 24: + case 32: break; default: printk(KERN_ERR "%s: invalid bit depth %d\n", @@ -968,6 +1023,11 @@ static void pxafb_setup_gpio(struct pxafb_info *fbi) for (gpio = 58; ldd_bits; gpio++, ldd_bits--) pxa_gpio_mode(gpio | GPIO_ALT_FN_2_OUT); + /* 18 bit interface */ + if (fbi->fb.var.bits_per_pixel > 16) { + pxa_gpio_mode(86 | GPIO_ALT_FN_2_OUT); + pxa_gpio_mode(87 | GPIO_ALT_FN_2_OUT); + } pxa_gpio_mode(GPIO74_LCD_FCLK_MD); pxa_gpio_mode(GPIO75_LCD_LCLK_MD); pxa_gpio_mode(GPIO76_LCD_PCLK_MD); @@ -1058,7 +1118,7 @@ static void set_ctrlr_state(struct pxafb_info *fbi, u_int state) { u_int old_state; - down(&fbi->ctrlr_sem); + mutex_lock(&fbi->ctrlr_lock); old_state = fbi->state; @@ -1146,7 +1206,7 @@ static void set_ctrlr_state(struct pxafb_info *fbi, u_int state) } break; } - up(&fbi->ctrlr_sem); + mutex_unlock(&fbi->ctrlr_lock); } /* @@ -1276,7 +1336,7 @@ static int __devinit pxafb_map_video_memory(struct pxafb_info *fbi) fbi->dma_buff_phys = fbi->map_dma; fbi->palette_cpu = (u16 *) fbi->dma_buff->palette; - pr_debug("pxafb: palette_mem_size = 0x%08lx\n", fbi->palette_size*sizeof(u16)); + pr_debug("pxafb: palette_mem_size = 0x%08x\n", fbi->palette_size*sizeof(u16)); #ifdef CONFIG_FB_PXA_SMARTPANEL fbi->smart_cmds = (uint16_t *) fbi->dma_buff->cmd_buff; @@ -1399,7 +1459,7 @@ static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev) init_waitqueue_head(&fbi->ctrlr_wait); INIT_WORK(&fbi->task, pxafb_task); - init_MUTEX(&fbi->ctrlr_sem); + mutex_init(&fbi->ctrlr_lock); init_completion(&fbi->disable_done); #ifdef CONFIG_FB_PXA_SMARTPANEL init_completion(&fbi->command_done); diff --git a/drivers/video/pxafb.h b/drivers/video/pxafb.h index 8238dc826429..31541b86f13d 100644 --- a/drivers/video/pxafb.h +++ b/drivers/video/pxafb.h @@ -106,7 +106,7 @@ struct pxafb_info { volatile u_char state; volatile u_char task_state; - struct semaphore ctrlr_sem; + struct mutex ctrlr_lock; wait_queue_head_t ctrlr_wait; struct work_struct task; diff --git a/drivers/video/sa1100fb.c b/drivers/video/sa1100fb.c index ab2b2110478b..78bcdbc3f484 100644 --- a/drivers/video/sa1100fb.c +++ b/drivers/video/sa1100fb.c @@ -167,6 +167,7 @@ #include <linux/string.h> #include <linux/interrupt.h> #include <linux/slab.h> +#include <linux/mm.h> #include <linux/fb.h> #include <linux/delay.h> #include <linux/init.h> @@ -174,6 +175,7 @@ #include <linux/cpufreq.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> +#include <linux/mutex.h> #include <asm/hardware.h> #include <asm/io.h> @@ -1107,7 +1109,7 @@ static void set_ctrlr_state(struct sa1100fb_info *fbi, u_int state) { u_int old_state; - down(&fbi->ctrlr_sem); + mutex_lock(&fbi->ctrlr_lock); old_state = fbi->state; @@ -1192,7 +1194,7 @@ static void set_ctrlr_state(struct sa1100fb_info *fbi, u_int state) } break; } - up(&fbi->ctrlr_sem); + mutex_unlock(&fbi->ctrlr_lock); } /* @@ -1444,7 +1446,7 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev) init_waitqueue_head(&fbi->ctrlr_wait); INIT_WORK(&fbi->task, sa1100fb_task); - init_MUTEX(&fbi->ctrlr_sem); + mutex_init(&fbi->ctrlr_lock); return fbi; } diff --git a/drivers/video/sa1100fb.h b/drivers/video/sa1100fb.h index f465b27ed860..86831db9a042 100644 --- a/drivers/video/sa1100fb.h +++ b/drivers/video/sa1100fb.h @@ -100,7 +100,7 @@ struct sa1100fb_info { volatile u_char state; volatile u_char task_state; - struct semaphore ctrlr_sem; + struct mutex ctrlr_lock; wait_queue_head_t ctrlr_wait; struct work_struct task; diff --git a/drivers/video/sh7760fb.c b/drivers/video/sh7760fb.c new file mode 100644 index 000000000000..8d0212da4514 --- /dev/null +++ b/drivers/video/sh7760fb.c @@ -0,0 +1,659 @@ +/* + * SH7760/SH7763 LCDC Framebuffer driver. + * + * (c) 2006-2008 MSC Vertriebsges.m.b.H., + * Manuel Lauss <mano@roarinelk.homelinux.net> + * (c) 2008 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com> + * + * 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. + * + * PLEASE HAVE A LOOK AT Documentation/fb/sh7760fb.txt! + * + * Thanks to Siegfried Schaefer <s.schaefer at schaefer-edv.de> + * for his original source and testing! + */ + +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/fb.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <asm/sh7760fb.h> + +struct sh7760fb_par { + void __iomem *base; + int irq; + + struct sh7760fb_platdata *pd; /* display information */ + + dma_addr_t fbdma; /* physical address */ + + int rot; /* rotation enabled? */ + + u32 pseudo_palette[16]; + + struct platform_device *dev; + struct resource *ioarea; + struct completion vsync; /* vsync irq event */ +}; + +static irqreturn_t sh7760fb_irq(int irq, void *data) +{ + struct completion *c = data; + + complete(c); + + return IRQ_HANDLED; +} + +static void sh7760fb_wait_vsync(struct fb_info *info) +{ + struct sh7760fb_par *par = info->par; + + if (par->pd->novsync) + return; + + iowrite16(ioread16(par->base + LDINTR) & ~VINT_CHECK, + par->base + LDINTR); + + if (par->irq < 0) { + /* poll for vert. retrace: status bit is sticky */ + while (!(ioread16(par->base + LDINTR) & VINT_CHECK)) + cpu_relax(); + } else { + /* a "wait_for_irq_event(par->irq)" would be extremely nice */ + init_completion(&par->vsync); + enable_irq(par->irq); + wait_for_completion(&par->vsync); + disable_irq_nosync(par->irq); + } +} + +/* wait_for_lps - wait until power supply has reached a certain state. */ +static int wait_for_lps(struct sh7760fb_par *par, int val) +{ + int i = 100; + while (--i && ((ioread16(par->base + LDPMMR) & 3) != val)) + msleep(1); + + if (i <= 0) + return -ETIMEDOUT; + + return 0; +} + +/* en/disable the LCDC */ +static int sh7760fb_blank(int blank, struct fb_info *info) +{ + struct sh7760fb_par *par = info->par; + struct sh7760fb_platdata *pd = par->pd; + unsigned short cntr = ioread16(par->base + LDCNTR); + unsigned short intr = ioread16(par->base + LDINTR); + int lps; + + if (blank == FB_BLANK_UNBLANK) { + intr |= VINT_START; + cntr = LDCNTR_DON2 | LDCNTR_DON; + lps = 3; + } else { + intr &= ~VINT_START; + cntr = LDCNTR_DON2; + lps = 0; + } + + if (pd->blank) + pd->blank(blank); + + iowrite16(intr, par->base + LDINTR); + iowrite16(cntr, par->base + LDCNTR); + + return wait_for_lps(par, lps); +} + +/* set color registers */ +static int sh7760fb_setcmap(struct fb_cmap *cmap, struct fb_info *info) +{ + struct sh7760fb_par *par = info->par; + u32 s = cmap->start; + u32 l = cmap->len; + u16 *r = cmap->red; + u16 *g = cmap->green; + u16 *b = cmap->blue; + u32 col, tmo; + int ret; + + ret = 0; + + sh7760fb_wait_vsync(info); + + /* request palette access */ + iowrite16(LDPALCR_PALEN, par->base + LDPALCR); + + /* poll for access grant */ + tmo = 100; + while (!(ioread16(par->base + LDPALCR) & LDPALCR_PALS) && (--tmo)) + cpu_relax(); + + if (!tmo) { + ret = 1; + dev_dbg(info->dev, "no palette access!\n"); + goto out; + } + + while (l && (s < 256)) { + col = ((*r) & 0xff) << 16; + col |= ((*g) & 0xff) << 8; + col |= ((*b) & 0xff); + col &= SH7760FB_PALETTE_MASK; + iowrite32(col, par->base + LDPR(s)); + + if (s < 16) + ((u32 *) (info->pseudo_palette))[s] = s; + + s++; + l--; + r++; + g++; + b++; + } +out: + iowrite16(0, par->base + LDPALCR); + return ret; +} + +static void encode_fix(struct fb_fix_screeninfo *fix, struct fb_info *info, + unsigned long stride) +{ + memset(fix, 0, sizeof(struct fb_fix_screeninfo)); + strcpy(fix->id, "sh7760-lcdc"); + + fix->smem_start = (unsigned long)info->screen_base; + fix->smem_len = info->screen_size; + + fix->line_length = stride; +} + +static int sh7760fb_get_color_info(struct device *dev, + u16 lddfr, int *bpp, int *gray) +{ + int lbpp, lgray; + + lgray = lbpp = 0; + + switch (lddfr & LDDFR_COLOR_MASK) { + case LDDFR_1BPP_MONO: + lgray = 1; + lbpp = 1; + break; + case LDDFR_2BPP_MONO: + lgray = 1; + lbpp = 2; + break; + case LDDFR_4BPP_MONO: + lgray = 1; + case LDDFR_4BPP: + lbpp = 4; + break; + case LDDFR_6BPP_MONO: + lgray = 1; + case LDDFR_8BPP: + lbpp = 8; + break; + case LDDFR_16BPP_RGB555: + case LDDFR_16BPP_RGB565: + lbpp = 16; + lgray = 0; + break; + default: + dev_dbg(dev, "unsupported LDDFR bit depth.\n"); + return -EINVAL; + } + + if (bpp) + *bpp = lbpp; + if (gray) + *gray = lgray; + + return 0; +} + +static int sh7760fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct fb_fix_screeninfo *fix = &info->fix; + struct sh7760fb_par *par = info->par; + int ret, bpp; + + /* get color info from register value */ + ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, NULL); + if (ret) + return ret; + + var->bits_per_pixel = bpp; + + if ((var->grayscale) && (var->bits_per_pixel == 1)) + fix->visual = FB_VISUAL_MONO10; + else if (var->bits_per_pixel >= 15) + fix->visual = FB_VISUAL_TRUECOLOR; + else + fix->visual = FB_VISUAL_PSEUDOCOLOR; + + /* TODO: add some more validation here */ + return 0; +} + +/* + * sh7760fb_set_par - set videomode. + * + * NOTE: The rotation, grayscale and DSTN codepaths are + * totally untested! + */ +static int sh7760fb_set_par(struct fb_info *info) +{ + struct sh7760fb_par *par = info->par; + struct fb_videomode *vm = par->pd->def_mode; + unsigned long sbase, dstn_off, ldsarl, stride; + unsigned short hsynp, hsynw, htcn, hdcn; + unsigned short vsynp, vsynw, vtln, vdln; + unsigned short lddfr, ldmtr; + int ret, bpp, gray; + + par->rot = par->pd->rotate; + + /* rotate only works with xres <= 320 */ + if (par->rot && (vm->xres > 320)) { + dev_dbg(info->dev, "rotation disabled due to display size\n"); + par->rot = 0; + } + + /* calculate LCDC reg vals from display parameters */ + hsynp = vm->right_margin + vm->xres; + hsynw = vm->hsync_len; + htcn = vm->left_margin + hsynp + hsynw; + hdcn = vm->xres; + vsynp = vm->lower_margin + vm->yres; + vsynw = vm->vsync_len; + vtln = vm->upper_margin + vsynp + vsynw; + vdln = vm->yres; + + /* get color info from register value */ + ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, &gray); + if (ret) + return ret; + + dev_dbg(info->dev, "%dx%d %dbpp %s (orientation %s)\n", hdcn, + vdln, bpp, gray ? "grayscale" : "color", + par->rot ? "rotated" : "normal"); + +#ifdef CONFIG_CPU_LITTLE_ENDIAN + lddfr = par->pd->lddfr | (1 << 8); +#else + lddfr = par->pd->lddfr & ~(1 << 8); +#endif + + ldmtr = par->pd->ldmtr; + + if (!(vm->sync & FB_SYNC_HOR_HIGH_ACT)) + ldmtr |= LDMTR_CL1POL; + if (!(vm->sync & FB_SYNC_VERT_HIGH_ACT)) + ldmtr |= LDMTR_FLMPOL; + + /* shut down LCDC before changing display parameters */ + sh7760fb_blank(FB_BLANK_POWERDOWN, info); + + iowrite16(par->pd->ldickr, par->base + LDICKR); /* pixclock */ + iowrite16(ldmtr, par->base + LDMTR); /* polarities */ + iowrite16(lddfr, par->base + LDDFR); /* color/depth */ + iowrite16((par->rot ? 1 << 13 : 0), par->base + LDSMR); /* rotate */ + iowrite16(par->pd->ldpmmr, par->base + LDPMMR); /* Power Management */ + iowrite16(par->pd->ldpspr, par->base + LDPSPR); /* Power Supply Ctrl */ + + /* display resolution */ + iowrite16(((htcn >> 3) - 1) | (((hdcn >> 3) - 1) << 8), + par->base + LDHCNR); + iowrite16(vdln - 1, par->base + LDVDLNR); + iowrite16(vtln - 1, par->base + LDVTLNR); + /* h/v sync signals */ + iowrite16((vsynp - 1) | ((vsynw - 1) << 12), par->base + LDVSYNR); + iowrite16(((hsynp >> 3) - 1) | (((hsynw >> 3) - 1) << 12), + par->base + LDHSYNR); + /* AC modulation sig */ + iowrite16(par->pd->ldaclnr, par->base + LDACLNR); + + stride = (par->rot) ? vtln : hdcn; + if (!gray) + stride *= (bpp + 7) >> 3; + else { + if (bpp == 1) + stride >>= 3; + else if (bpp == 2) + stride >>= 2; + else if (bpp == 4) + stride >>= 1; + /* 6 bpp == 8 bpp */ + } + + /* if rotated, stride must be power of 2 */ + if (par->rot) { + unsigned long bit = 1 << 31; + while (bit) { + if (stride & bit) + break; + bit >>= 1; + } + if (stride & ~bit) + stride = bit << 1; /* not P-o-2, round up */ + } + iowrite16(stride, par->base + LDLAOR); + + /* set display mem start address */ + sbase = (unsigned long)par->fbdma; + if (par->rot) + sbase += (hdcn - 1) * stride; + + iowrite32(sbase, par->base + LDSARU); + + /* + * for DSTN need to set address for lower half. + * I (mlau) don't know which address to set it to, + * so I guessed at (stride * yres/2). + */ + if (((ldmtr & 0x003f) >= LDMTR_DSTN_MONO_8) && + ((ldmtr & 0x003f) <= LDMTR_DSTN_COLOR_16)) { + + dev_dbg(info->dev, " ***** DSTN untested! *****\n"); + + dstn_off = stride; + if (par->rot) + dstn_off *= hdcn >> 1; + else + dstn_off *= vdln >> 1; + + ldsarl = sbase + dstn_off; + } else + ldsarl = 0; + + iowrite32(ldsarl, par->base + LDSARL); /* mem for lower half of DSTN */ + + encode_fix(&info->fix, info, stride); + sh7760fb_check_var(&info->var, info); + + sh7760fb_blank(FB_BLANK_UNBLANK, info); /* panel on! */ + + dev_dbg(info->dev, "hdcn : %6d htcn : %6d\n", hdcn, htcn); + dev_dbg(info->dev, "hsynw : %6d hsynp : %6d\n", hsynw, hsynp); + dev_dbg(info->dev, "vdln : %6d vtln : %6d\n", vdln, vtln); + dev_dbg(info->dev, "vsynw : %6d vsynp : %6d\n", vsynw, vsynp); + dev_dbg(info->dev, "clksrc: %6d clkdiv: %6d\n", + (par->pd->ldickr >> 12) & 3, par->pd->ldickr & 0x1f); + dev_dbg(info->dev, "ldpmmr: 0x%04x ldpspr: 0x%04x\n", par->pd->ldpmmr, + par->pd->ldpspr); + dev_dbg(info->dev, "ldmtr : 0x%04x lddfr : 0x%04x\n", ldmtr, lddfr); + dev_dbg(info->dev, "ldlaor: %ld\n", stride); + dev_dbg(info->dev, "ldsaru: 0x%08lx ldsarl: 0x%08lx\n", sbase, ldsarl); + + return 0; +} + +static struct fb_ops sh7760fb_ops = { + .owner = THIS_MODULE, + .fb_blank = sh7760fb_blank, + .fb_check_var = sh7760fb_check_var, + .fb_setcmap = sh7760fb_setcmap, + .fb_set_par = sh7760fb_set_par, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +static void sh7760fb_free_mem(struct fb_info *info) +{ + struct sh7760fb_par *par = info->par; + + if (!info->screen_base) + return; + + dma_free_coherent(info->dev, info->screen_size, + info->screen_base, par->fbdma); + + par->fbdma = 0; + info->screen_base = NULL; + info->screen_size = 0; +} + +/* allocate the framebuffer memory. This memory must be in Area3, + * (dictated by the DMA engine) and contiguous, at a 512 byte boundary. + */ +static int sh7760fb_alloc_mem(struct fb_info *info) +{ + struct sh7760fb_par *par = info->par; + void *fbmem; + unsigned long vram; + int ret, bpp; + + if (info->screen_base) + return 0; + + /* get color info from register value */ + ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, NULL); + if (ret) { + printk(KERN_ERR "colinfo\n"); + return ret; + } + + /* min VRAM: xres_min = 16, yres_min = 1, bpp = 1: 2byte -> 1 page + max VRAM: xres_max = 1024, yres_max = 1024, bpp = 16: 2MB */ + + vram = info->var.xres * info->var.yres; + if (info->var.grayscale) { + if (bpp == 1) + vram >>= 3; + else if (bpp == 2) + vram >>= 2; + else if (bpp == 4) + vram >>= 1; + } else if (bpp > 8) + vram *= 2; + if ((vram < 1) || (vram > 1024 * 2048)) { + dev_dbg(info->dev, "too much VRAM required. Check settings\n"); + return -ENODEV; + } + + if (vram < PAGE_SIZE) + vram = PAGE_SIZE; + + fbmem = dma_alloc_coherent(info->dev, vram, &par->fbdma, GFP_KERNEL); + + if (!fbmem) + return -ENOMEM; + + if ((par->fbdma & SH7760FB_DMA_MASK) != SH7760FB_DMA_MASK) { + sh7760fb_free_mem(info); + dev_err(info->dev, "kernel gave me memory at 0x%08lx, which is" + "unusable for the LCDC\n", (unsigned long)par->fbdma); + return -ENOMEM; + } + + info->screen_base = fbmem; + info->screen_size = vram; + + return 0; +} + +static int __devinit sh7760fb_probe(struct platform_device *pdev) +{ + struct fb_info *info; + struct resource *res; + struct sh7760fb_par *par; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(res == NULL)) { + dev_err(&pdev->dev, "invalid resource\n"); + return -EINVAL; + } + + info = framebuffer_alloc(sizeof(struct sh7760fb_par), &pdev->dev); + if (!info) + return -ENOMEM; + + par = info->par; + par->dev = pdev; + + par->pd = pdev->dev.platform_data; + if (!par->pd) { + dev_dbg(info->dev, "no display setup data!\n"); + ret = -ENODEV; + goto out_fb; + } + + par->ioarea = request_mem_region(res->start, + (res->end - res->start), pdev->name); + if (!par->ioarea) { + dev_err(&pdev->dev, "mmio area busy\n"); + ret = -EBUSY; + goto out_fb; + } + + par->base = ioremap_nocache(res->start, res->end - res->start + 1); + if (!par->base) { + dev_err(&pdev->dev, "cannot remap\n"); + ret = -ENODEV; + goto out_res; + } + + iowrite16(0, par->base + LDINTR); /* disable vsync irq */ + par->irq = platform_get_irq(pdev, 0); + if (par->irq >= 0) { + ret = request_irq(par->irq, sh7760fb_irq, 0, + "sh7760-lcdc", &par->vsync); + if (ret) { + dev_err(&pdev->dev, "cannot grab IRQ\n"); + par->irq = -ENXIO; + } else + disable_irq_nosync(par->irq); + } + + fb_videomode_to_var(&info->var, par->pd->def_mode); + + ret = sh7760fb_alloc_mem(info); + if (ret) { + dev_dbg(info->dev, "framebuffer memory allocation failed!\n"); + goto out_unmap; + } + + info->pseudo_palette = par->pseudo_palette; + + /* fixup color register bitpositions. These are fixed by hardware */ + info->var.red.offset = 11; + info->var.red.length = 5; + info->var.red.msb_right = 0; + + info->var.green.offset = 5; + info->var.green.length = 6; + info->var.green.msb_right = 0; + + info->var.blue.offset = 0; + info->var.blue.length = 5; + info->var.blue.msb_right = 0; + + info->var.transp.offset = 0; + info->var.transp.length = 0; + info->var.transp.msb_right = 0; + + /* set the DON2 bit now, before cmap allocation, as it will randomize + * palette memory. + */ + iowrite16(LDCNTR_DON2, par->base + LDCNTR); + info->fbops = &sh7760fb_ops; + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret) { + dev_dbg(info->dev, "Unable to allocate cmap memory\n"); + goto out_mem; + } + + ret = register_framebuffer(info); + if (ret < 0) { + dev_dbg(info->dev, "cannot register fb!\n"); + goto out_cmap; + } + platform_set_drvdata(pdev, info); + + printk(KERN_INFO "%s: memory at phys 0x%08lx-0x%08lx, size %ld KiB\n", + pdev->name, + (unsigned long)par->fbdma, + (unsigned long)(par->fbdma + info->screen_size - 1), + info->screen_size >> 10); + + return 0; + +out_cmap: + sh7760fb_blank(FB_BLANK_POWERDOWN, info); + fb_dealloc_cmap(&info->cmap); +out_mem: + sh7760fb_free_mem(info); +out_unmap: + if (par->irq >= 0) + free_irq(par->irq, &par->vsync); + iounmap(par->base); +out_res: + release_resource(par->ioarea); + kfree(par->ioarea); +out_fb: + framebuffer_release(info); + return ret; +} + +static int __devexit sh7760fb_remove(struct platform_device *dev) +{ + struct fb_info *info = platform_get_drvdata(dev); + struct sh7760fb_par *par = info->par; + + sh7760fb_blank(FB_BLANK_POWERDOWN, info); + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); + sh7760fb_free_mem(info); + if (par->irq >= 0) + free_irq(par->irq, par); + iounmap(par->base); + release_resource(par->ioarea); + kfree(par->ioarea); + framebuffer_release(info); + platform_set_drvdata(dev, NULL); + + return 0; +} + +static struct platform_driver sh7760_lcdc_driver = { + .driver = { + .name = "sh7760-lcdc", + .owner = THIS_MODULE, + }, + .probe = sh7760fb_probe, + .remove = __devexit_p(sh7760fb_remove), +}; + +static int __init sh7760fb_init(void) +{ + return platform_driver_register(&sh7760_lcdc_driver); +} + +static void __exit sh7760fb_exit(void) +{ + platform_driver_unregister(&sh7760_lcdc_driver); +} + +module_init(sh7760fb_init); +module_exit(sh7760fb_exit); + +MODULE_AUTHOR("Nobuhiro Iwamatsu, Manuel Lauss"); +MODULE_DESCRIPTION("FBdev for SH7760/63 integrated LCD Controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c new file mode 100644 index 000000000000..f6ef6cca73cd --- /dev/null +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -0,0 +1,725 @@ +/* + * SuperH Mobile LCDC Framebuffer + * + * Copyright (c) 2008 Magnus Damm + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/fb.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <asm/sh_mobile_lcdc.h> + +#define PALETTE_NR 16 + +struct sh_mobile_lcdc_priv; +struct sh_mobile_lcdc_chan { + struct sh_mobile_lcdc_priv *lcdc; + unsigned long *reg_offs; + unsigned long ldmt1r_value; + unsigned long enabled; /* ME and SE in LDCNT2R */ + struct sh_mobile_lcdc_chan_cfg cfg; + u32 pseudo_palette[PALETTE_NR]; + struct fb_info info; + dma_addr_t dma_handle; +}; + +struct sh_mobile_lcdc_priv { + void __iomem *base; + struct clk *clk; + unsigned long lddckr; + struct sh_mobile_lcdc_chan ch[2]; +}; + +/* shared registers */ +#define _LDDCKR 0x410 +#define _LDDCKSTPR 0x414 +#define _LDINTR 0x468 +#define _LDSR 0x46c +#define _LDCNT1R 0x470 +#define _LDCNT2R 0x474 +#define _LDDDSR 0x47c +#define _LDDWD0R 0x800 +#define _LDDRDR 0x840 +#define _LDDWAR 0x900 +#define _LDDRAR 0x904 + +/* per-channel registers */ +enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, + LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR }; + +static unsigned long lcdc_offs_mainlcd[] = { + [LDDCKPAT1R] = 0x400, + [LDDCKPAT2R] = 0x404, + [LDMT1R] = 0x418, + [LDMT2R] = 0x41c, + [LDMT3R] = 0x420, + [LDDFR] = 0x424, + [LDSM1R] = 0x428, + [LDSA1R] = 0x430, + [LDMLSR] = 0x438, + [LDHCNR] = 0x448, + [LDHSYNR] = 0x44c, + [LDVLNR] = 0x450, + [LDVSYNR] = 0x454, + [LDPMR] = 0x460, +}; + +static unsigned long lcdc_offs_sublcd[] = { + [LDDCKPAT1R] = 0x408, + [LDDCKPAT2R] = 0x40c, + [LDMT1R] = 0x600, + [LDMT2R] = 0x604, + [LDMT3R] = 0x608, + [LDDFR] = 0x60c, + [LDSM1R] = 0x610, + [LDSA1R] = 0x618, + [LDMLSR] = 0x620, + [LDHCNR] = 0x624, + [LDHSYNR] = 0x628, + [LDVLNR] = 0x62c, + [LDVSYNR] = 0x630, + [LDPMR] = 0x63c, +}; + +#define START_LCDC 0x00000001 +#define LCDC_RESET 0x00000100 +#define DISPLAY_BEU 0x00000008 +#define LCDC_ENABLE 0x00000001 + +static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan, + int reg_nr, unsigned long data) +{ + iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr]); +} + +static unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan, + int reg_nr) +{ + return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]); +} + +static void lcdc_write(struct sh_mobile_lcdc_priv *priv, + unsigned long reg_offs, unsigned long data) +{ + iowrite32(data, priv->base + reg_offs); +} + +static unsigned long lcdc_read(struct sh_mobile_lcdc_priv *priv, + unsigned long reg_offs) +{ + return ioread32(priv->base + reg_offs); +} + +static void lcdc_wait_bit(struct sh_mobile_lcdc_priv *priv, + unsigned long reg_offs, + unsigned long mask, unsigned long until) +{ + while ((lcdc_read(priv, reg_offs) & mask) != until) + cpu_relax(); +} + +static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan) +{ + return chan->cfg.chan == LCDC_CHAN_SUBLCD; +} + +static void lcdc_sys_write_index(void *handle, unsigned long data) +{ + struct sh_mobile_lcdc_chan *ch = handle; + + lcdc_write(ch->lcdc, _LDDWD0R, data | 0x10000000); + lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); + lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); +} + +static void lcdc_sys_write_data(void *handle, unsigned long data) +{ + struct sh_mobile_lcdc_chan *ch = handle; + + lcdc_write(ch->lcdc, _LDDWD0R, data | 0x11000000); + lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); + lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); +} + +static unsigned long lcdc_sys_read_data(void *handle) +{ + struct sh_mobile_lcdc_chan *ch = handle; + + lcdc_write(ch->lcdc, _LDDRDR, 0x01000000); + lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); + lcdc_write(ch->lcdc, _LDDRAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); + udelay(1); + + return lcdc_read(ch->lcdc, _LDDRDR) & 0xffff; +} + +struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = { + lcdc_sys_write_index, + lcdc_sys_write_data, + lcdc_sys_read_data, +}; + +static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv, + int start) +{ + unsigned long tmp = lcdc_read(priv, _LDCNT2R); + int k; + + /* start or stop the lcdc */ + if (start) + lcdc_write(priv, _LDCNT2R, tmp | START_LCDC); + else + lcdc_write(priv, _LDCNT2R, tmp & ~START_LCDC); + + /* wait until power is applied/stopped on all channels */ + for (k = 0; k < ARRAY_SIZE(priv->ch); k++) + if (lcdc_read(priv, _LDCNT2R) & priv->ch[k].enabled) + while (1) { + tmp = lcdc_read_chan(&priv->ch[k], LDPMR) & 3; + if (start && tmp == 3) + break; + if (!start && tmp == 0) + break; + cpu_relax(); + } + + if (!start) + lcdc_write(priv, _LDDCKSTPR, 1); /* stop dotclock */ +} + +static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) +{ + struct sh_mobile_lcdc_chan *ch; + struct fb_videomode *lcd_cfg; + struct sh_mobile_lcdc_board_cfg *board_cfg; + unsigned long tmp; + int k, m; + int ret = 0; + + /* reset */ + lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET); + lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0); + + /* enable LCDC channels */ + tmp = lcdc_read(priv, _LDCNT2R); + tmp |= priv->ch[0].enabled; + tmp |= priv->ch[1].enabled; + lcdc_write(priv, _LDCNT2R, tmp); + + /* read data from external memory, avoid using the BEU for now */ + lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU); + + /* stop the lcdc first */ + sh_mobile_lcdc_start_stop(priv, 0); + + /* configure clocks */ + tmp = priv->lddckr; + for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { + ch = &priv->ch[k]; + + if (!priv->ch[k].enabled) + continue; + + m = ch->cfg.clock_divider; + if (!m) + continue; + + if (m == 1) + m = 1 << 6; + tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0); + + lcdc_write_chan(ch, LDDCKPAT1R, 0x00000000); + lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1); + } + + lcdc_write(priv, _LDDCKR, tmp); + + /* start dotclock again */ + lcdc_write(priv, _LDDCKSTPR, 0); + lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0); + + /* interrupts are disabled */ + lcdc_write(priv, _LDINTR, 0); + + for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { + ch = &priv->ch[k]; + lcd_cfg = &ch->cfg.lcd_cfg; + + if (!ch->enabled) + continue; + + tmp = ch->ldmt1r_value; + tmp |= (lcd_cfg->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28; + tmp |= (lcd_cfg->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27; + lcdc_write_chan(ch, LDMT1R, tmp); + + /* setup SYS bus */ + lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r); + lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r); + + /* horizontal configuration */ + tmp = lcd_cfg->xres + lcd_cfg->hsync_len; + tmp += lcd_cfg->left_margin; + tmp += lcd_cfg->right_margin; + tmp /= 8; /* HTCN */ + tmp |= (lcd_cfg->xres / 8) << 16; /* HDCN */ + lcdc_write_chan(ch, LDHCNR, tmp); + + tmp = lcd_cfg->xres; + tmp += lcd_cfg->right_margin; + tmp /= 8; /* HSYNP */ + tmp |= (lcd_cfg->hsync_len / 8) << 16; /* HSYNW */ + lcdc_write_chan(ch, LDHSYNR, tmp); + + /* power supply */ + lcdc_write_chan(ch, LDPMR, 0); + + /* vertical configuration */ + tmp = lcd_cfg->yres + lcd_cfg->vsync_len; + tmp += lcd_cfg->upper_margin; + tmp += lcd_cfg->lower_margin; /* VTLN */ + tmp |= lcd_cfg->yres << 16; /* VDLN */ + lcdc_write_chan(ch, LDVLNR, tmp); + + tmp = lcd_cfg->yres; + tmp += lcd_cfg->lower_margin; /* VSYNP */ + tmp |= lcd_cfg->vsync_len << 16; /* VSYNW */ + lcdc_write_chan(ch, LDVSYNR, tmp); + + board_cfg = &ch->cfg.board_cfg; + if (board_cfg->setup_sys) + ret = board_cfg->setup_sys(board_cfg->board_data, ch, + &sh_mobile_lcdc_sys_bus_ops); + if (ret) + return ret; + } + + /* --- display_lcdc_data() --- */ + lcdc_write(priv, _LDINTR, 0x00000f00); + + /* word and long word swap */ + lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6); + + for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { + ch = &priv->ch[k]; + + if (!priv->ch[k].enabled) + continue; + + /* set bpp format in PKF[4:0] */ + tmp = lcdc_read_chan(ch, LDDFR); + tmp &= ~(0x0001001f); + tmp |= (priv->ch[k].info.var.bits_per_pixel == 16) ? 3 : 0; + lcdc_write_chan(ch, LDDFR, tmp); + + /* point out our frame buffer */ + lcdc_write_chan(ch, LDSA1R, ch->info.fix.smem_start); + + /* set line size */ + lcdc_write_chan(ch, LDMLSR, ch->info.fix.line_length); + + /* continuous read mode */ + lcdc_write_chan(ch, LDSM1R, 0); + } + + /* display output */ + lcdc_write(priv, _LDCNT1R, LCDC_ENABLE); + + /* start the lcdc */ + sh_mobile_lcdc_start_stop(priv, 1); + + /* tell the board code to enable the panel */ + for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { + ch = &priv->ch[k]; + board_cfg = &ch->cfg.board_cfg; + if (board_cfg->display_on) + board_cfg->display_on(board_cfg->board_data); + } + + return 0; +} + +static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) +{ + struct sh_mobile_lcdc_chan *ch; + struct sh_mobile_lcdc_board_cfg *board_cfg; + int k; + + /* tell the board code to disable the panel */ + for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { + ch = &priv->ch[k]; + board_cfg = &ch->cfg.board_cfg; + if (board_cfg->display_off) + board_cfg->display_off(board_cfg->board_data); + } + + /* stop the lcdc */ + sh_mobile_lcdc_start_stop(priv, 0); +} + +static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch) +{ + int ifm, miftyp; + + switch (ch->cfg.interface_type) { + case RGB8: ifm = 0; miftyp = 0; break; + case RGB9: ifm = 0; miftyp = 4; break; + case RGB12A: ifm = 0; miftyp = 5; break; + case RGB12B: ifm = 0; miftyp = 6; break; + case RGB16: ifm = 0; miftyp = 7; break; + case RGB18: ifm = 0; miftyp = 10; break; + case RGB24: ifm = 0; miftyp = 11; break; + case SYS8A: ifm = 1; miftyp = 0; break; + case SYS8B: ifm = 1; miftyp = 1; break; + case SYS8C: ifm = 1; miftyp = 2; break; + case SYS8D: ifm = 1; miftyp = 3; break; + case SYS9: ifm = 1; miftyp = 4; break; + case SYS12: ifm = 1; miftyp = 5; break; + case SYS16A: ifm = 1; miftyp = 7; break; + case SYS16B: ifm = 1; miftyp = 8; break; + case SYS16C: ifm = 1; miftyp = 9; break; + case SYS18: ifm = 1; miftyp = 10; break; + case SYS24: ifm = 1; miftyp = 11; break; + default: goto bad; + } + + /* SUBLCD only supports SYS interface */ + if (lcdc_chan_is_sublcd(ch)) { + if (ifm == 0) + goto bad; + else + ifm = 0; + } + + ch->ldmt1r_value = (ifm << 12) | miftyp; + return 0; + bad: + return -EINVAL; +} + +static int sh_mobile_lcdc_setup_clocks(struct device *dev, int clock_source, + struct sh_mobile_lcdc_priv *priv) +{ + char *str; + int icksel; + + switch (clock_source) { + case LCDC_CLK_BUS: str = "bus_clk"; icksel = 0; break; + case LCDC_CLK_PERIPHERAL: str = "peripheral_clk"; icksel = 1; break; + case LCDC_CLK_EXTERNAL: str = NULL; icksel = 2; break; + default: + return -EINVAL; + } + + priv->lddckr = icksel << 16; + + if (str) { + priv->clk = clk_get(dev, str); + if (IS_ERR(priv->clk)) { + dev_err(dev, "cannot get clock %s\n", str); + return PTR_ERR(priv->clk); + } + + clk_enable(priv->clk); + } + + return 0; +} + +static int sh_mobile_lcdc_setcolreg(u_int regno, + u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + u32 *palette = info->pseudo_palette; + + if (regno >= PALETTE_NR) + return -EINVAL; + + /* only FB_VISUAL_TRUECOLOR supported */ + + red >>= 16 - info->var.red.length; + green >>= 16 - info->var.green.length; + blue >>= 16 - info->var.blue.length; + transp >>= 16 - info->var.transp.length; + + palette[regno] = (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset) | + (transp << info->var.transp.offset); + + return 0; +} + +static struct fb_fix_screeninfo sh_mobile_lcdc_fix = { + .id = "SH Mobile LCDC", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .accel = FB_ACCEL_NONE, +}; + +static struct fb_ops sh_mobile_lcdc_ops = { + .fb_setcolreg = sh_mobile_lcdc_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) +{ + switch (bpp) { + case 16: /* PKF[4:0] = 00011 - RGB 565 */ + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + var->transp.offset = 0; + var->transp.length = 0; + break; + + case 32: /* PKF[4:0] = 00000 - RGB 888 + * sh7722 pdf says 00RRGGBB but reality is GGBB00RR + * this may be because LDDDSR has word swap enabled.. + */ + var->red.offset = 0; + var->red.length = 8; + var->green.offset = 24; + var->green.length = 8; + var->blue.offset = 16; + var->blue.length = 8; + var->transp.offset = 0; + var->transp.length = 0; + break; + default: + return -EINVAL; + } + var->bits_per_pixel = bpp; + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + var->transp.msb_right = 0; + return 0; +} + +static int sh_mobile_lcdc_remove(struct platform_device *pdev); + +static int __init sh_mobile_lcdc_probe(struct platform_device *pdev) +{ + struct fb_info *info; + struct sh_mobile_lcdc_priv *priv; + struct sh_mobile_lcdc_info *pdata; + struct sh_mobile_lcdc_chan_cfg *cfg; + struct resource *res; + int error; + void *buf; + int i, j; + + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "no platform data defined\n"); + error = -EINVAL; + goto err0; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "cannot find IO resource\n"); + error = -ENOENT; + goto err0; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "cannot allocate device data\n"); + error = -ENOMEM; + goto err0; + } + + platform_set_drvdata(pdev, priv); + pdata = pdev->dev.platform_data; + + j = 0; + for (i = 0; i < ARRAY_SIZE(pdata->ch); i++) { + priv->ch[j].lcdc = priv; + memcpy(&priv->ch[j].cfg, &pdata->ch[i], sizeof(pdata->ch[i])); + + error = sh_mobile_lcdc_check_interface(&priv->ch[i]); + if (error) { + dev_err(&pdev->dev, "unsupported interface type\n"); + goto err1; + } + + switch (pdata->ch[i].chan) { + case LCDC_CHAN_MAINLCD: + priv->ch[j].enabled = 1 << 1; + priv->ch[j].reg_offs = lcdc_offs_mainlcd; + j++; + break; + case LCDC_CHAN_SUBLCD: + priv->ch[j].enabled = 1 << 2; + priv->ch[j].reg_offs = lcdc_offs_sublcd; + j++; + break; + } + } + + if (!j) { + dev_err(&pdev->dev, "no channels defined\n"); + error = -EINVAL; + goto err1; + } + + error = sh_mobile_lcdc_setup_clocks(&pdev->dev, + pdata->clock_source, priv); + if (error) { + dev_err(&pdev->dev, "unable to setup clocks\n"); + goto err1; + } + + priv->lddckr = pdata->lddckr; + priv->base = ioremap_nocache(res->start, (res->end - res->start) + 1); + + for (i = 0; i < j; i++) { + info = &priv->ch[i].info; + cfg = &priv->ch[i].cfg; + + info->fbops = &sh_mobile_lcdc_ops; + info->var.xres = info->var.xres_virtual = cfg->lcd_cfg.xres; + info->var.yres = info->var.yres_virtual = cfg->lcd_cfg.yres; + info->var.activate = FB_ACTIVATE_NOW; + error = sh_mobile_lcdc_set_bpp(&info->var, cfg->bpp); + if (error) + break; + + info->fix = sh_mobile_lcdc_fix; + info->fix.line_length = cfg->lcd_cfg.xres * (cfg->bpp / 8); + info->fix.smem_len = info->fix.line_length * cfg->lcd_cfg.yres; + + buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len, + &priv->ch[i].dma_handle, GFP_KERNEL); + if (!buf) { + dev_err(&pdev->dev, "unable to allocate buffer\n"); + error = -ENOMEM; + break; + } + + info->pseudo_palette = &priv->ch[i].pseudo_palette; + info->flags = FBINFO_FLAG_DEFAULT; + + error = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0); + if (error < 0) { + dev_err(&pdev->dev, "unable to allocate cmap\n"); + dma_free_coherent(&pdev->dev, info->fix.smem_len, + buf, priv->ch[i].dma_handle); + break; + } + + memset(buf, 0, info->fix.smem_len); + info->fix.smem_start = priv->ch[i].dma_handle; + info->screen_base = buf; + info->device = &pdev->dev; + } + + if (error) + goto err1; + + error = sh_mobile_lcdc_start(priv); + if (error) { + dev_err(&pdev->dev, "unable to start hardware\n"); + goto err1; + } + + for (i = 0; i < j; i++) { + error = register_framebuffer(&priv->ch[i].info); + if (error < 0) + goto err1; + } + + for (i = 0; i < j; i++) { + info = &priv->ch[i].info; + dev_info(info->dev, + "registered %s/%s as %dx%d %dbpp.\n", + pdev->name, + (priv->ch[i].cfg.chan == LCDC_CHAN_MAINLCD) ? + "mainlcd" : "sublcd", + (int) priv->ch[i].cfg.lcd_cfg.xres, + (int) priv->ch[i].cfg.lcd_cfg.yres, + priv->ch[i].cfg.bpp); + } + + return 0; + err1: + sh_mobile_lcdc_remove(pdev); + err0: + return error; +} + +static int sh_mobile_lcdc_remove(struct platform_device *pdev) +{ + struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); + struct fb_info *info; + int i; + + for (i = 0; i < ARRAY_SIZE(priv->ch); i++) + if (priv->ch[i].info.dev) + unregister_framebuffer(&priv->ch[i].info); + + sh_mobile_lcdc_stop(priv); + + for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { + info = &priv->ch[i].info; + + if (!info->device) + continue; + + dma_free_coherent(&pdev->dev, info->fix.smem_len, + info->screen_base, priv->ch[i].dma_handle); + fb_dealloc_cmap(&info->cmap); + } + + if (priv->clk) { + clk_disable(priv->clk); + clk_put(priv->clk); + } + + if (priv->base) + iounmap(priv->base); + + kfree(priv); + return 0; +} + +static struct platform_driver sh_mobile_lcdc_driver = { + .driver = { + .name = "sh_mobile_lcdc_fb", + .owner = THIS_MODULE, + }, + .probe = sh_mobile_lcdc_probe, + .remove = sh_mobile_lcdc_remove, +}; + +static int __init sh_mobile_lcdc_init(void) +{ + return platform_driver_register(&sh_mobile_lcdc_driver); +} + +static void __exit sh_mobile_lcdc_exit(void) +{ + platform_driver_unregister(&sh_mobile_lcdc_driver); +} + +module_init(sh_mobile_lcdc_init); +module_exit(sh_mobile_lcdc_exit); + +MODULE_DESCRIPTION("SuperH Mobile LCDC Framebuffer driver"); +MODULE_AUTHOR("Magnus Damm <damm@opensource.se>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/sis/init.h b/drivers/video/sis/init.h index f40a680df86f..b96005c39c67 100644 --- a/drivers/video/sis/init.h +++ b/drivers/video/sis/init.h @@ -73,7 +73,6 @@ #ifdef SIS_CP #undef SIS_CP #endif -#include <linux/version.h> #include <linux/types.h> #include <asm/io.h> #include <linux/fb.h> diff --git a/drivers/video/sis/init301.h b/drivers/video/sis/init301.h index 7708e1e1d99e..51d99222375d 100644 --- a/drivers/video/sis/init301.h +++ b/drivers/video/sis/init301.h @@ -67,7 +67,6 @@ #ifdef SIS_CP #undef SIS_CP #endif -#include <linux/version.h> #include <linux/types.h> #include <asm/io.h> #include <linux/fb.h> diff --git a/drivers/video/sis/initextlfb.c b/drivers/video/sis/initextlfb.c index 47a33501549d..99c04a4855d1 100644 --- a/drivers/video/sis/initextlfb.c +++ b/drivers/video/sis/initextlfb.c @@ -30,7 +30,6 @@ #include "vgatypes.h" #include "vstruct.h" -#include <linux/version.h> #include <linux/types.h> #include <linux/fb.h> diff --git a/drivers/video/sis/osdef.h b/drivers/video/sis/osdef.h index c1492782cb18..6ff8f988a1a7 100644 --- a/drivers/video/sis/osdef.h +++ b/drivers/video/sis/osdef.h @@ -87,7 +87,6 @@ /**********************************************************************/ #ifdef SIS_LINUX_KERNEL -#include <linux/version.h> #ifdef CONFIG_FB_SIS_300 #define SIS300 diff --git a/drivers/video/sis/sis.h b/drivers/video/sis/sis.h index a14e82211037..7c5710e3fb56 100644 --- a/drivers/video/sis/sis.h +++ b/drivers/video/sis/sis.h @@ -24,8 +24,6 @@ #ifndef _SIS_H_ #define _SIS_H_ -#include <linux/version.h> - #include "osdef.h" #include <video/sisfb.h> @@ -42,16 +40,6 @@ #define SIS_NEW_CONFIG_COMPAT #endif /* CONFIG_COMPAT */ -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,8) -#define SIS_IOTYPE1 void __iomem -#define SIS_IOTYPE2 __iomem -#define SISINITSTATIC static -#else -#define SIS_IOTYPE1 unsigned char -#define SIS_IOTYPE2 -#define SISINITSTATIC -#endif - #undef SISFBDEBUG #ifdef SISFBDEBUG @@ -505,8 +493,8 @@ struct sis_video_info { unsigned long UMAsize, LFBsize; - SIS_IOTYPE1 *video_vbase; - SIS_IOTYPE1 *mmio_vbase; + void __iomem *video_vbase; + void __iomem *mmio_vbase; unsigned char *bios_abase; @@ -533,8 +521,8 @@ struct sis_video_info { int sisfb_nocrt2rate; u32 heapstart; /* offset */ - SIS_IOTYPE1 *sisfb_heap_start; /* address */ - SIS_IOTYPE1 *sisfb_heap_end; /* address */ + void __iomem *sisfb_heap_start; /* address */ + void __iomem *sisfb_heap_end; /* address */ u32 sisfb_heap_size; int havenoheap; @@ -612,7 +600,7 @@ struct sis_video_info { u8 detectedpdca; u8 detectedlcda; - SIS_IOTYPE1 *hwcursor_vbase; + void __iomem *hwcursor_vbase; int chronteltype; int tvxpos, tvypos; diff --git a/drivers/video/sis/sis_accel.c b/drivers/video/sis/sis_accel.c index 7addf91d2fea..ceb434c95c0d 100644 --- a/drivers/video/sis/sis_accel.c +++ b/drivers/video/sis/sis_accel.c @@ -28,7 +28,6 @@ * for more information and updates) */ -#include <linux/version.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/fb.h> diff --git a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c index b9343844cd1f..346d6458cf76 100644 --- a/drivers/video/sis/sis_main.c +++ b/drivers/video/sis/sis_main.c @@ -33,7 +33,6 @@ * */ -#include <linux/version.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -41,13 +40,7 @@ #include <linux/errno.h> #include <linux/string.h> #include <linux/mm.h> - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) -#include <linux/tty.h> -#else #include <linux/screen_info.h> -#endif - #include <linux/slab.h> #include <linux/fb.h> #include <linux/selection.h> @@ -1167,11 +1160,7 @@ sisfb_set_mode(struct sis_video_info *ivideo, int clrscrn) unsigned short modeno = ivideo->mode_no; /* >=2.6.12's fbcon clears the screen anyway */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12) - if(!clrscrn) modeno |= 0x80; -#else modeno |= 0x80; -#endif outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD); @@ -1436,11 +1425,8 @@ sisfb_set_par(struct fb_info *info) if((err = sisfb_do_set_var(&info->var, 1, info))) return err; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) - sisfb_get_fix(&info->fix, info->currcon, info); -#else sisfb_get_fix(&info->fix, -1, info); -#endif + return 0; } @@ -1676,14 +1662,8 @@ sisfb_blank(int blank, struct fb_info *info) /* ----------- FBDev related routines for all series ---------- */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) static int sisfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) -#else -static int sisfb_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg, - struct fb_info *info) -#endif { struct sis_video_info *ivideo = (struct sis_video_info *)info->par; struct sis_memreq sismemreq; @@ -3986,8 +3966,7 @@ sisfb_handle_command(struct sis_video_info *ivideo, struct sisfb_cmd *sisfb_comm } #ifndef MODULE -SISINITSTATIC int __init -sisfb_setup(char *options) +static int __init sisfb_setup(char *options) { char *this_opt; @@ -4086,9 +4065,9 @@ sisfb_setup(char *options) #endif static int __devinit -sisfb_check_rom(SIS_IOTYPE1 *rom_base, struct sis_video_info *ivideo) +sisfb_check_rom(void __iomem *rom_base, struct sis_video_info *ivideo) { - SIS_IOTYPE1 *rom; + void __iomem *rom; int romptr; if((readb(rom_base) != 0x55) || (readb(rom_base + 1) != 0xaa)) @@ -4117,10 +4096,9 @@ static unsigned char * __devinit sisfb_find_rom(struct pci_dev *pdev) { struct sis_video_info *ivideo = pci_get_drvdata(pdev); - SIS_IOTYPE1 *rom_base; + void __iomem *rom_base; unsigned char *myrombase = NULL; u32 temp; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11) size_t romsize; /* First, try the official pci ROM functions (except @@ -4151,7 +4129,6 @@ sisfb_find_rom(struct pci_dev *pdev) } if(myrombase) return myrombase; -#endif /* Otherwise do it the conventional way. */ @@ -4225,7 +4202,7 @@ sisfb_post_map_vram(struct sis_video_info *ivideo, unsigned int *mapsize, static int __devinit sisfb_post_300_buswidth(struct sis_video_info *ivideo) { - SIS_IOTYPE1 *FBAddress = ivideo->video_vbase; + void __iomem *FBAddress = ivideo->video_vbase; unsigned short temp; unsigned char reg; int i, j; @@ -4273,7 +4250,7 @@ sisfb_post_300_rwtest(struct sis_video_info *ivideo, int iteration, int buswidth int PseudoRankCapacity, int PseudoAdrPinCount, unsigned int mapsize) { - SIS_IOTYPE1 *FBAddr = ivideo->video_vbase; + void __iomem *FBAddr = ivideo->video_vbase; unsigned short sr14; unsigned int k, RankCapacity, PageCapacity, BankNumHigh, BankNumMid; unsigned int PhysicalAdrOtherPage, PhysicalAdrHigh, PhysicalAdrHalfPage; @@ -5829,7 +5806,7 @@ sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ivideo->engineok = 0; ivideo->sisfb_was_boot_device = 0; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)) + if(pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW) { if(ivideo->sisvga_enabled) ivideo->sisfb_was_boot_device = 1; @@ -5840,7 +5817,6 @@ sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) "as the primary VGA device\n"); } } -#endif ivideo->sisfb_parm_mem = sisfb_parm_mem; ivideo->sisfb_accel = sisfb_accel; @@ -6010,7 +5986,7 @@ sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ivideo->modeprechange = reg & 0x7f; } else if(ivideo->sisvga_enabled) { #if defined(__i386__) || defined(__x86_64__) - unsigned char SIS_IOTYPE2 *tt = ioremap(0x400, 0x100); + unsigned char __iomem *tt = ioremap(0x400, 0x100); if(tt) { ivideo->modeprechange = readb(tt + 0x49); iounmap(tt); @@ -6503,7 +6479,7 @@ static struct pci_driver sisfb_driver = { .remove = __devexit_p(sisfb_remove) }; -SISINITSTATIC int __init sisfb_init(void) +static int __init sisfb_init(void) { #ifndef MODULE char *options = NULL; diff --git a/drivers/video/sis/sis_main.h b/drivers/video/sis/sis_main.h index 3e3b7fa05d6c..9540e977270e 100644 --- a/drivers/video/sis/sis_main.h +++ b/drivers/video/sis/sis_main.h @@ -665,11 +665,11 @@ static struct _customttable { /* Interface used by the world */ #ifndef MODULE -SISINITSTATIC int sisfb_setup(char *options); +static int sisfb_setup(char *options); #endif /* Interface to the low level console driver */ -SISINITSTATIC int sisfb_init(void); +static int sisfb_init(void); /* fbdev routines */ static int sisfb_get_fix(struct fb_fix_screeninfo *fix, int con, diff --git a/drivers/video/sis/vgatypes.h b/drivers/video/sis/vgatypes.h index b532fbd2b04c..81a22eaabfde 100644 --- a/drivers/video/sis/vgatypes.h +++ b/drivers/video/sis/vgatypes.h @@ -53,10 +53,6 @@ #ifndef _VGATYPES_H_ #define _VGATYPES_H_ -#ifdef SIS_LINUX_KERNEL -#include <linux/version.h> -#endif - #define SISIOMEMTYPE #ifdef SIS_LINUX_KERNEL diff --git a/drivers/video/skeletonfb.c b/drivers/video/skeletonfb.c index 62321458f71a..df5336561d13 100644 --- a/drivers/video/skeletonfb.c +++ b/drivers/video/skeletonfb.c @@ -675,13 +675,13 @@ static struct fb_ops xxxfb_ops = { * Initialization */ -/* static int __init xxfb_probe (struct device *device) -- for platform devs */ +/* static int __init xxfb_probe (struct platform_device *pdev) -- for platform devs */ static int __devinit xxxfb_probe(struct pci_dev *dev, const struct pci_device_id *ent) { struct fb_info *info; struct xxx_par *par; - struct device* device = &dev->dev; /* for pci drivers */ + struct device *device = &dev->dev; /* or &pdev->dev */ int cmap_len, retval; /* @@ -824,18 +824,18 @@ static int __devinit xxxfb_probe(struct pci_dev *dev, return -EINVAL; printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, info->fix.id); - pci_set_drvdata(dev, info); /* or dev_set_drvdata(device, info) */ + pci_set_drvdata(dev, info); /* or platform_set_drvdata(pdev, info) */ return 0; } /* * Cleanup */ -/* static void __devexit xxxfb_remove(struct device *device) */ +/* static void __devexit xxxfb_remove(struct platform_device *pdev) */ static void __devexit xxxfb_remove(struct pci_dev *dev) { struct fb_info *info = pci_get_drvdata(dev); - /* or dev_get_drvdata(device); */ + /* or platform_get_drvdata(pdev); */ if (info) { unregister_framebuffer(info); @@ -961,18 +961,17 @@ static int xxxfb_resume(struct platform_dev *dev) #define xxxfb_resume NULL #endif /* CONFIG_PM */ -static struct device_driver xxxfb_driver = { - .name = "xxxfb", - .bus = &platform_bus_type, +static struct platform_device_driver xxxfb_driver = { .probe = xxxfb_probe, .remove = xxxfb_remove, .suspend = xxxfb_suspend, /* optional but recommended */ .resume = xxxfb_resume, /* optional but recommended */ + .driver = { + .name = "xxxfb", + }, }; -static struct platform_device xxxfb_device = { - .name = "xxxfb", -}; +static struct platform_device *xxxfb_device; #ifndef MODULE /* @@ -1002,12 +1001,16 @@ static int __init xxxfb_init(void) return -ENODEV; xxxfb_setup(option); #endif - ret = driver_register(&xxxfb_driver); + ret = platform_driver_register(&xxxfb_driver); if (!ret) { - ret = platform_device_register(&xxxfb_device); - if (ret) - driver_unregister(&xxxfb_driver); + xxxfb_device = platform_device_register_simple("xxxfb", 0, + NULL, 0); + + if (IS_ERR(xxxfb_device)) { + platform_driver_unregister(&xxxfb_driver); + ret = PTR_ERR(xxxfb_device); + } } return ret; @@ -1015,8 +1018,8 @@ static int __init xxxfb_init(void) static void __exit xxxfb_exit(void) { - platform_device_unregister(&xxxfb_device); - driver_unregister(&xxxfb_driver); + platform_device_unregister(xxxfb_device); + platform_driver_unregister(&xxxfb_driver); } #endif /* CONFIG_PCI */ diff --git a/drivers/video/sm501fb.c b/drivers/video/sm501fb.c index 15d4a768b1f6..f94ae84a58cd 100644 --- a/drivers/video/sm501fb.c +++ b/drivers/video/sm501fb.c @@ -48,10 +48,15 @@ enum sm501_controller { HEAD_PANEL = 1, }; -/* SM501 memory address */ +/* SM501 memory address. + * + * This structure is used to track memory usage within the SM501 framebuffer + * allocation. The sm_addr field is stored as an offset as it is often used + * against both the physical and mapped addresses. + */ struct sm501_mem { unsigned long size; - unsigned long sm_addr; + unsigned long sm_addr; /* offset from base of sm501 fb. */ void __iomem *k_addr; }; @@ -142,31 +147,68 @@ static inline void sm501fb_sync_regs(struct sm501fb_info *info) static int sm501_alloc_mem(struct sm501fb_info *inf, struct sm501_mem *mem, unsigned int why, size_t size) { - unsigned int ptr = 0; + struct sm501fb_par *par; + struct fb_info *fbi; + unsigned int ptr; + unsigned int end; switch (why) { case SM501_MEMF_CURSOR: ptr = inf->fbmem_len - size; - inf->fbmem_len = ptr; + inf->fbmem_len = ptr; /* adjust available memory. */ break; case SM501_MEMF_PANEL: ptr = inf->fbmem_len - size; - if (ptr < inf->fb[0]->fix.smem_len) + fbi = inf->fb[HEAD_CRT]; + + /* round down, some programs such as directfb do not draw + * 0,0 correctly unless the start is aligned to a page start. + */ + + if (ptr > 0) + ptr &= ~(PAGE_SIZE - 1); + + if (fbi && ptr < fbi->fix.smem_len) + return -ENOMEM; + + if (ptr < 0) return -ENOMEM; break; case SM501_MEMF_CRT: ptr = 0; + + /* check to see if we have panel memory allocated + * which would put an limit on available memory. */ + + fbi = inf->fb[HEAD_PANEL]; + if (fbi) { + par = fbi->par; + end = par->screen.k_addr ? par->screen.sm_addr : inf->fbmem_len; + } else + end = inf->fbmem_len; + + if ((ptr + size) > end) + return -ENOMEM; + break; case SM501_MEMF_ACCEL: - ptr = inf->fb[0]->fix.smem_len; + fbi = inf->fb[HEAD_CRT]; + ptr = fbi ? fbi->fix.smem_len : 0; + + fbi = inf->fb[HEAD_PANEL]; + if (fbi) { + par = fbi->par; + end = par->screen.sm_addr; + } else + end = inf->fbmem_len; - if ((ptr + size) > - (inf->fb[1]->fix.smem_start - inf->fbmem_res->start)) + if ((ptr + size) > end) return -ENOMEM; + break; default: @@ -663,15 +705,25 @@ static void sm501fb_panel_power(struct sm501fb_info *fbi, int to) sm501fb_sync_regs(fbi); mdelay(10); + /* VBIASEN */ + if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) { - control |= SM501_DC_PANEL_CONTROL_BIAS; /* VBIASEN */ + if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN) + control &= ~SM501_DC_PANEL_CONTROL_BIAS; + else + control |= SM501_DC_PANEL_CONTROL_BIAS; + writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); } if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) { - control |= SM501_DC_PANEL_CONTROL_FPEN; + if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN) + control &= ~SM501_DC_PANEL_CONTROL_FPEN; + else + control |= SM501_DC_PANEL_CONTROL_FPEN; + writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); @@ -679,14 +731,22 @@ static void sm501fb_panel_power(struct sm501fb_info *fbi, int to) } else if (!to && (control & SM501_DC_PANEL_CONTROL_VDD) != 0) { /* disable panel power */ if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) { - control &= ~SM501_DC_PANEL_CONTROL_FPEN; + if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN) + control |= SM501_DC_PANEL_CONTROL_FPEN; + else + control &= ~SM501_DC_PANEL_CONTROL_FPEN; + writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); } if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) { - control &= ~SM501_DC_PANEL_CONTROL_BIAS; + if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN) + control |= SM501_DC_PANEL_CONTROL_BIAS; + else + control &= ~SM501_DC_PANEL_CONTROL_BIAS; + writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); @@ -1210,39 +1270,6 @@ static struct fb_ops sm501fb_ops_pnl = { .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 @@ -1250,10 +1277,16 @@ static struct sm501fb_info *sm501fb_info_alloc(struct fb_info *fbinfo_crt, 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; + struct sm501fb_par *par; + struct sm501fb_info *info; int ret; + if (fbi == NULL) + return 0; + + par = fbi->par; + info = par->info; + par->cursor_regs = info->regs + reg_base; ret = sm501_alloc_mem(info, &par->cursor, SM501_MEMF_CURSOR, 1024); @@ -1281,13 +1314,10 @@ static int sm501fb_start(struct sm501fb_info *info, struct platform_device *pdev) { struct resource *res; - struct device *dev; + struct device *dev = &pdev->dev; int k; 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 */ @@ -1390,11 +1420,6 @@ static void sm501fb_stop(struct sm501fb_info *info) 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) @@ -1539,36 +1564,93 @@ static struct sm501_platdata_fb sm501fb_def_pdata = { static char driver_name_crt[] = "sm501fb-crt"; static char driver_name_pnl[] = "sm501fb-panel"; -static int __init sm501fb_probe(struct platform_device *pdev) +static int __devinit sm501fb_probe_one(struct sm501fb_info *info, + enum sm501_controller head) { - struct sm501fb_info *info; - struct device *dev = &pdev->dev; - struct fb_info *fbinfo_crt; - struct fb_info *fbinfo_pnl; - int ret; + unsigned char *name = (head == HEAD_CRT) ? "crt" : "panel"; + struct sm501_platdata_fbsub *pd; + struct sm501fb_par *par; + struct fb_info *fbi; - /* allocate our framebuffers */ + pd = (head == HEAD_CRT) ? info->pdata->fb_crt : info->pdata->fb_pnl; + + /* Do not initialise if we've not been given any platform data */ + if (pd == NULL) { + dev_info(info->dev, "no data for fb %s (disabled)\n", name); + return 0; + } - fbinfo_crt = framebuffer_alloc(sizeof(struct sm501fb_par), dev); - if (fbinfo_crt == NULL) { - dev_err(dev, "cannot allocate crt framebuffer\n"); + fbi = framebuffer_alloc(sizeof(struct sm501fb_par), info->dev); + if (fbi == NULL) { + dev_err(info->dev, "cannot allocate %s framebuffer\n", name); 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; + par = fbi->par; + par->info = info; + par->head = head; + fbi->pseudo_palette = &par->pseudo_palette; + + info->fb[head] = fbi; + + return 0; +} + +/* Free up anything allocated by sm501fb_init_fb */ + +static void sm501_free_init_fb(struct sm501fb_info *info, + enum sm501_controller head) +{ + struct fb_info *fbi = info->fb[head]; + + fb_dealloc_cmap(&fbi->cmap); +} + +static int __devinit sm501fb_start_one(struct sm501fb_info *info, + enum sm501_controller head, + const char *drvname) +{ + struct fb_info *fbi = info->fb[head]; + int ret; + + if (!fbi) + return 0; + + ret = sm501fb_init_fb(info->fb[head], head, drvname); + if (ret) { + dev_err(info->dev, "cannot initialise fb %s\n", drvname); + return ret; + } + + ret = register_framebuffer(info->fb[head]); + if (ret) { + dev_err(info->dev, "failed to register fb %s\n", drvname); + sm501_free_init_fb(info, head); + return ret; } - info = sm501fb_info_alloc(fbinfo_crt, fbinfo_pnl); - if (info == NULL) { - dev_err(dev, "cannot allocate par\n"); - ret = -ENOMEM; - goto sm501fb_alloc_fail; + dev_info(info->dev, "fb%d: %s frame buffer\n", fbi->node, fbi->fix.id); + + return 0; +} + +static int __devinit sm501fb_probe(struct platform_device *pdev) +{ + struct sm501fb_info *info; + struct device *dev = &pdev->dev; + int ret; + + /* allocate our framebuffers */ + + info = kzalloc(sizeof(struct sm501fb_info), GFP_KERNEL); + if (!info) { + dev_err(dev, "failed to allocate state\n"); + return -ENOMEM; } + info->dev = dev = &pdev->dev; + platform_set_drvdata(pdev, info); + if (dev->parent->platform_data) { struct sm501_platdata *pd = dev->parent->platform_data; info->pdata = pd->fb; @@ -1579,90 +1661,88 @@ static int __init sm501fb_probe(struct platform_device *pdev) info->pdata = &sm501fb_def_pdata; } - /* start the framebuffers */ + /* probe for the presence of each panel */ - ret = sm501fb_start(info, pdev); - if (ret) { - dev_err(dev, "cannot initialise SM501\n"); - goto sm501fb_start_fail; + ret = sm501fb_probe_one(info, HEAD_CRT); + if (ret < 0) { + dev_err(dev, "failed to probe CRT\n"); + goto err_alloc; } - /* CRT framebuffer setup */ + ret = sm501fb_probe_one(info, HEAD_PANEL); + if (ret < 0) { + dev_err(dev, "failed to probe PANEL\n"); + goto err_probed_crt; + } - 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; + if (info->fb[HEAD_PANEL] == NULL && + info->fb[HEAD_CRT] == NULL) { + dev_err(dev, "no framebuffers found\n"); + goto err_alloc; } - /* Panel framebuffer setup */ + /* get the resources for both of the framebuffers */ - ret = sm501fb_init_fb(fbinfo_pnl, HEAD_PANEL, driver_name_pnl); + ret = sm501fb_start(info, pdev); if (ret) { - dev_err(dev, "cannot initialise Panel fb\n"); - goto sm501fb_start_fail; + dev_err(dev, "cannot initialise SM501\n"); + goto err_probed_panel; } - /* 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 = sm501fb_start_one(info, HEAD_CRT, driver_name_crt); + if (ret) { + dev_err(dev, "failed to start CRT\n"); + goto err_started; } - ret = register_framebuffer(fbinfo_pnl); - if (ret < 0) { - dev_err(dev, "failed to register panel fb (%d)\n", ret); - goto register_pnl_fail; + ret = sm501fb_start_one(info, HEAD_PANEL, driver_name_pnl); + if (ret) { + dev_err(dev, "failed to start Panel\n"); + goto err_started_crt; } - 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; + goto err_started_panel; ret = device_create_file(dev, &dev_attr_fbregs_pnl); if (ret) - goto fbregs_pnl_fail; + goto err_attached_crtsrc_file; ret = device_create_file(dev, &dev_attr_fbregs_crt); if (ret) - goto fbregs_crt_fail; + goto err_attached_pnlregs_file; /* we registered, return ok */ return 0; - fbregs_crt_fail: +err_attached_pnlregs_file: device_remove_file(dev, &dev_attr_fbregs_pnl); - fbregs_pnl_fail: +err_attached_crtsrc_file: device_remove_file(dev, &dev_attr_crt_src); - crtsrc_fail: - unregister_framebuffer(fbinfo_pnl); +err_started_panel: + unregister_framebuffer(info->fb[HEAD_PANEL]); + sm501_free_init_fb(info, HEAD_PANEL); - register_pnl_fail: - unregister_framebuffer(fbinfo_crt); +err_started_crt: + unregister_framebuffer(info->fb[HEAD_CRT]); + sm501_free_init_fb(info, HEAD_CRT); - register_crt_fail: +err_started: sm501fb_stop(info); - sm501fb_start_fail: - sm501fb_info_release(info); +err_probed_panel: + framebuffer_release(info->fb[HEAD_PANEL]); - sm501fb_alloc_fail: - framebuffer_release(fbinfo_pnl); +err_probed_crt: + framebuffer_release(info->fb[HEAD_CRT]); - fbinfo_crt_alloc_fail: - framebuffer_release(fbinfo_crt); +err_alloc: + kfree(info); return ret; } @@ -1681,11 +1761,14 @@ static int sm501fb_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_fbregs_pnl); device_remove_file(&pdev->dev, &dev_attr_crt_src); + sm501_free_init_fb(info, HEAD_CRT); + sm501_free_init_fb(info, HEAD_PANEL); + unregister_framebuffer(fbinfo_crt); unregister_framebuffer(fbinfo_pnl); sm501fb_stop(info); - sm501fb_info_release(info); + kfree(info); framebuffer_release(fbinfo_pnl); framebuffer_release(fbinfo_crt); diff --git a/drivers/video/sticore.h b/drivers/video/sticore.h index 1a9a60c74be3..7fe5be4bc70e 100644 --- a/drivers/video/sticore.h +++ b/drivers/video/sticore.h @@ -352,8 +352,6 @@ struct sti_struct *sti_get_rom(unsigned int index); /* 0: default sti */ /* functions to call the STI ROM directly */ -int sti_init_graph(struct sti_struct *sti); -void sti_inq_conf(struct sti_struct *sti); void sti_putc(struct sti_struct *sti, int c, int y, int x); void sti_set(struct sti_struct *sti, int src_y, int src_x, int height, int width, u8 color); diff --git a/drivers/video/stifb.c b/drivers/video/stifb.c index 598d35eff935..166481402412 100644 --- a/drivers/video/stifb.c +++ b/drivers/video/stifb.c @@ -1078,8 +1078,7 @@ static struct fb_ops stifb_ops = { * Initialization */ -int __init -stifb_init_fb(struct sti_struct *sti, int bpp_pref) +static int __init stifb_init_fb(struct sti_struct *sti, int bpp_pref) { struct fb_fix_screeninfo *fix; struct fb_var_screeninfo *var; @@ -1315,8 +1314,7 @@ static int stifb_disabled __initdata; int __init stifb_setup(char *options); -int __init -stifb_init(void) +static int __init stifb_init(void) { struct sti_struct *sti; struct sti_struct *def_sti; diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c index ea9f19d25597..77aafcfae037 100644 --- a/drivers/video/tdfxfb.c +++ b/drivers/video/tdfxfb.c @@ -836,16 +836,12 @@ static int tdfxfb_pan_display(struct fb_var_screeninfo *var, struct tdfx_par *par = info->par; u32 addr = var->yoffset * info->fix.line_length; - if (nopan || var->xoffset || (var->yoffset > var->yres_virtual)) - return -EINVAL; - if ((var->yoffset + var->yres > var->yres_virtual && nowrap)) + if (nopan || var->xoffset) return -EINVAL; banshee_make_room(par, 1); tdfx_outl(par, VIDDESKSTART, addr); - info->var.xoffset = var->xoffset; - info->var.yoffset = var->yoffset; return 0; } @@ -1426,6 +1422,8 @@ MODULE_LICENSE("GPL"); module_param(hwcursor, int, 0644); MODULE_PARM_DESC(hwcursor, "Enable hardware cursor " "(1=enable, 0=disable, default=1)"); +module_param(mode_option, charp, 0); +MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'"); #ifdef CONFIG_MTRR module_param(nomtrr, bool, 0); MODULE_PARM_DESC(nomtrr, "Disable MTRR support (default: enabled)"); diff --git a/drivers/video/tridentfb.c b/drivers/video/tridentfb.c index beefab2992c0..479b2e79ad68 100644 --- a/drivers/video/tridentfb.c +++ b/drivers/video/tridentfb.c @@ -1,5 +1,5 @@ /* - * Frame buffer driver for Trident Blade and Image series + * Frame buffer driver for Trident TGUI, Blade and Image series * * Copyright 2001, 2002 - Jani Monoses <jani@iv.ro> * @@ -13,7 +13,6 @@ * code, suggestions * TODO: * timing value tweaking so it looks good on every monitor in every mode - * TGUI acceleration */ #include <linux/module.h> @@ -22,25 +21,26 @@ #include <linux/pci.h> #include <linux/delay.h> +#include <video/vga.h> #include <video/trident.h> -#define VERSION "0.7.8-NEWAPI" - struct tridentfb_par { void __iomem *io_virt; /* iospace virtual memory address */ + u32 pseudo_pal[16]; + int chip_id; + int flatpanel; + void (*init_accel) (struct tridentfb_par *, int, int); + void (*wait_engine) (struct tridentfb_par *); + void (*fill_rect) + (struct tridentfb_par *par, u32, u32, u32, u32, u32, u32); + void (*copy_rect) + (struct tridentfb_par *par, u32, u32, u32, u32, u32, u32); + void (*image_blit) + (struct tridentfb_par *par, const char*, + u32, u32, u32, u32, u32, u32); + unsigned char eng_oper; /* engine operation... */ }; -static unsigned char eng_oper; /* engine operation... */ -static struct fb_ops tridentfb_ops; - -static struct tridentfb_par default_par; - -/* FIXME:kmalloc these 3 instead */ -static struct fb_info fb_info; -static u32 pseudo_pal[16]; - -static struct fb_var_screeninfo default_var; - static struct fb_fix_screeninfo tridentfb_fix = { .id = "Trident", .type = FB_TYPE_PACKED_PIXELS, @@ -49,27 +49,22 @@ static struct fb_fix_screeninfo tridentfb_fix = { .accel = FB_ACCEL_NONE, }; -static int chip_id; - -static int defaultaccel; -static int displaytype; - /* defaults which are normally overriden by user values */ /* video mode */ -static char *mode_option __devinitdata = "640x480"; -static int bpp = 8; +static char *mode_option __devinitdata = "640x480-8@60"; +static int bpp __devinitdata = 8; -static int noaccel; +static int noaccel __devinitdata; static int center; static int stretch; -static int fp; -static int crt; +static int fp __devinitdata; +static int crt __devinitdata; -static int memsize; -static int memdiff; +static int memsize __devinitdata; +static int memdiff __devinitdata; static int nativex; module_param(mode_option, charp, 0); @@ -84,25 +79,53 @@ module_param(memsize, int, 0); module_param(memdiff, int, 0); module_param(nativex, int, 0); module_param(fp, int, 0); +MODULE_PARM_DESC(fp, "Define if flatpanel is connected"); module_param(crt, int, 0); +MODULE_PARM_DESC(crt, "Define if CRT is connected"); + +static inline int is_oldclock(int id) +{ + return (id == TGUI9440) || + (id == TGUI9660) || + (id == CYBER9320); +} + +static inline int is_oldprotect(int id) +{ + return is_oldclock(id) || + (id == PROVIDIA9685) || + (id == CYBER9382) || + (id == CYBER9385); +} + +static inline int is_blade(int id) +{ + return (id == BLADE3D) || + (id == CYBERBLADEE4) || + (id == CYBERBLADEi7) || + (id == CYBERBLADEi7D) || + (id == CYBERBLADEi1) || + (id == CYBERBLADEi1D) || + (id == CYBERBLADEAi1) || + (id == CYBERBLADEAi1D); +} -static int chip3D; -static int chipcyber; +static inline int is_xp(int id) +{ + return (id == CYBERBLADEXPAi1) || + (id == CYBERBLADEXPm8) || + (id == CYBERBLADEXPm16); +} -static int is3Dchip(int id) +static inline int is3Dchip(int id) { - return ((id == BLADE3D) || (id == CYBERBLADEE4) || - (id == CYBERBLADEi7) || (id == CYBERBLADEi7D) || + return is_blade(id) || is_xp(id) || (id == CYBER9397) || (id == CYBER9397DVD) || (id == CYBER9520) || (id == CYBER9525DVD) || - (id == IMAGE975) || (id == IMAGE985) || - (id == CYBERBLADEi1) || (id == CYBERBLADEi1D) || - (id == CYBERBLADEAi1) || (id == CYBERBLADEAi1D) || - (id == CYBERBLADEXPm8) || (id == CYBERBLADEXPm16) || - (id == CYBERBLADEXPAi1)); + (id == IMAGE975) || (id == IMAGE985); } -static int iscyber(int id) +static inline int iscyber(int id) { switch (id) { case CYBER9388: @@ -122,12 +145,7 @@ static int iscyber(int id) return 1; case CYBER9320: - case TGUI9660: - case IMAGE975: - case IMAGE985: - case BLADE3D: case CYBERBLADEi7: /* VIA MPV4 integrated version */ - default: /* case CYBERBLDAEXPm8: Strange */ /* case CYBERBLDAEXPm16: Strange */ @@ -135,147 +153,110 @@ static int iscyber(int id) } } -#define CRT 0x3D0 /* CRTC registers offset for color display */ - -#ifndef TRIDENT_MMIO - #define TRIDENT_MMIO 1 -#endif - -#if TRIDENT_MMIO - #define t_outb(val, reg) writeb(val,((struct tridentfb_par *)(fb_info.par))->io_virt + reg) - #define t_inb(reg) readb(((struct tridentfb_par*)(fb_info.par))->io_virt + reg) -#else - #define t_outb(val, reg) outb(val, reg) - #define t_inb(reg) inb(reg) -#endif +static inline void t_outb(struct tridentfb_par *p, u8 val, u16 reg) +{ + fb_writeb(val, p->io_virt + reg); +} +static inline u8 t_inb(struct tridentfb_par *p, u16 reg) +{ + return fb_readb(p->io_virt + reg); +} -static struct accel_switch { - void (*init_accel) (int, int); - void (*wait_engine) (void); - void (*fill_rect) (u32, u32, u32, u32, u32, u32); - void (*copy_rect) (u32, u32, u32, u32, u32, u32); -} *acc; +static inline void writemmr(struct tridentfb_par *par, u16 r, u32 v) +{ + fb_writel(v, par->io_virt + r); +} -#define writemmr(r, v) writel(v, ((struct tridentfb_par *)fb_info.par)->io_virt + r) -#define readmmr(r) readl(((struct tridentfb_par *)fb_info.par)->io_virt + r) +static inline u32 readmmr(struct tridentfb_par *par, u16 r) +{ + return fb_readl(par->io_virt + r); +} /* * Blade specific acceleration. */ #define point(x, y) ((y) << 16 | (x)) -#define STA 0x2120 -#define CMD 0x2144 -#define ROP 0x2148 -#define CLR 0x2160 -#define SR1 0x2100 -#define SR2 0x2104 -#define DR1 0x2108 -#define DR2 0x210C - -#define ROP_S 0xCC - -static void blade_init_accel(int pitch, int bpp) + +static void blade_init_accel(struct tridentfb_par *par, int pitch, int bpp) { int v1 = (pitch >> 3) << 20; - int tmp = 0, v2; - switch (bpp) { - case 8: - tmp = 0; - break; - case 15: - tmp = 5; - break; - case 16: - tmp = 1; - break; - case 24: - case 32: - tmp = 2; - break; - } - v2 = v1 | (tmp << 29); - writemmr(0x21C0, v2); - writemmr(0x21C4, v2); - writemmr(0x21B8, v2); - writemmr(0x21BC, v2); - writemmr(0x21D0, v1); - writemmr(0x21D4, v1); - writemmr(0x21C8, v1); - writemmr(0x21CC, v1); - writemmr(0x216C, 0); + int tmp = bpp == 24 ? 2 : (bpp >> 4); + int v2 = v1 | (tmp << 29); + + writemmr(par, 0x21C0, v2); + writemmr(par, 0x21C4, v2); + writemmr(par, 0x21B8, v2); + writemmr(par, 0x21BC, v2); + writemmr(par, 0x21D0, v1); + writemmr(par, 0x21D4, v1); + writemmr(par, 0x21C8, v1); + writemmr(par, 0x21CC, v1); + writemmr(par, 0x216C, 0); } -static void blade_wait_engine(void) +static void blade_wait_engine(struct tridentfb_par *par) { - while (readmmr(STA) & 0xFA800000) ; + while (readmmr(par, STATUS) & 0xFA800000) + cpu_relax(); } -static void blade_fill_rect(u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop) +static void blade_fill_rect(struct tridentfb_par *par, + u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop) { - writemmr(CLR, c); - writemmr(ROP, rop ? 0x66 : ROP_S); - writemmr(CMD, 0x20000000 | 1 << 19 | 1 << 4 | 2 << 2); + writemmr(par, COLOR, c); + writemmr(par, ROP, rop ? ROP_X : ROP_S); + writemmr(par, CMD, 0x20000000 | 1 << 19 | 1 << 4 | 2 << 2); - writemmr(DR1, point(x, y)); - writemmr(DR2, point(x + w - 1, y + h - 1)); + writemmr(par, DST1, point(x, y)); + writemmr(par, DST2, point(x + w - 1, y + h - 1)); } -static void blade_copy_rect(u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h) +static void blade_image_blit(struct tridentfb_par *par, const char *data, + u32 x, u32 y, u32 w, u32 h, u32 c, u32 b) +{ + unsigned size = ((w + 31) >> 5) * h; + + writemmr(par, COLOR, c); + writemmr(par, BGCOLOR, b); + writemmr(par, CMD, 0xa0000000 | 3 << 19); + + writemmr(par, DST1, point(x, y)); + writemmr(par, DST2, point(x + w - 1, y + h - 1)); + + memcpy(par->io_virt + 0x10000, data, 4 * size); +} + +static void blade_copy_rect(struct tridentfb_par *par, + u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h) { - u32 s1, s2, d1, d2; int direction = 2; - s1 = point(x1, y1); - s2 = point(x1 + w - 1, y1 + h - 1); - d1 = point(x2, y2); - d2 = point(x2 + w - 1, y2 + h - 1); + u32 s1 = point(x1, y1); + u32 s2 = point(x1 + w - 1, y1 + h - 1); + u32 d1 = point(x2, y2); + u32 d2 = point(x2 + w - 1, y2 + h - 1); if ((y1 > y2) || ((y1 == y2) && (x1 > x2))) direction = 0; - writemmr(ROP, ROP_S); - writemmr(CMD, 0xE0000000 | 1 << 19 | 1 << 4 | 1 << 2 | direction); + writemmr(par, ROP, ROP_S); + writemmr(par, CMD, 0xE0000000 | 1 << 19 | 1 << 4 | 1 << 2 | direction); - writemmr(SR1, direction ? s2 : s1); - writemmr(SR2, direction ? s1 : s2); - writemmr(DR1, direction ? d2 : d1); - writemmr(DR2, direction ? d1 : d2); + writemmr(par, SRC1, direction ? s2 : s1); + writemmr(par, SRC2, direction ? s1 : s2); + writemmr(par, DST1, direction ? d2 : d1); + writemmr(par, DST2, direction ? d1 : d2); } -static struct accel_switch accel_blade = { - blade_init_accel, - blade_wait_engine, - blade_fill_rect, - blade_copy_rect, -}; - /* * BladeXP specific acceleration functions */ -#define ROP_P 0xF0 -#define masked_point(x, y) ((y & 0xffff)<<16|(x & 0xffff)) - -static void xp_init_accel(int pitch, int bpp) +static void xp_init_accel(struct tridentfb_par *par, int pitch, int bpp) { - int tmp = 0, v1; - unsigned char x = 0; - - switch (bpp) { - case 8: - x = 0; - break; - case 16: - x = 1; - break; - case 24: - x = 3; - break; - case 32: - x = 2; - break; - } + unsigned char x = bpp == 24 ? 3 : (bpp >> 4); + int v1 = pitch << (bpp == 24 ? 20 : (18 + x)); switch (pitch << (bpp >> 3)) { case 8192: @@ -293,42 +274,21 @@ static void xp_init_accel(int pitch, int bpp) break; } - t_outb(x, 0x2125); - - eng_oper = x | 0x40; - - switch (bpp) { - case 8: - tmp = 18; - break; - case 15: - case 16: - tmp = 19; - break; - case 24: - case 32: - tmp = 20; - break; - } + t_outb(par, x, 0x2125); - v1 = pitch << tmp; + par->eng_oper = x | 0x40; - writemmr(0x2154, v1); - writemmr(0x2150, v1); - t_outb(3, 0x2126); + writemmr(par, 0x2154, v1); + writemmr(par, 0x2150, v1); + t_outb(par, 3, 0x2126); } -static void xp_wait_engine(void) +static void xp_wait_engine(struct tridentfb_par *par) { - int busy; - int count, timeout; - - count = 0; - timeout = 0; - for (;;) { - busy = t_inb(STA) & 0x80; - if (busy != 0x80) - return; + int count = 0; + int timeout = 0; + + while (t_inb(par, STATUS) & 0x80) { count++; if (count == 10000000) { /* Timeout */ @@ -336,30 +296,31 @@ static void xp_wait_engine(void) timeout++; if (timeout == 8) { /* Reset engine */ - t_outb(0x00, 0x2120); + t_outb(par, 0x00, STATUS); return; } } + cpu_relax(); } } -static void xp_fill_rect(u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop) +static void xp_fill_rect(struct tridentfb_par *par, + u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop) { - writemmr(0x2127, ROP_P); - writemmr(0x2158, c); - writemmr(0x2128, 0x4000); - writemmr(0x2140, masked_point(h, w)); - writemmr(0x2138, masked_point(y, x)); - t_outb(0x01, 0x2124); - t_outb(eng_oper, 0x2125); + writemmr(par, 0x2127, ROP_P); + writemmr(par, 0x2158, c); + writemmr(par, DRAWFL, 0x4000); + writemmr(par, OLDDIM, point(h, w)); + writemmr(par, OLDDST, point(y, x)); + t_outb(par, 0x01, OLDCMD); + t_outb(par, par->eng_oper, 0x2125); } -static void xp_copy_rect(u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h) +static void xp_copy_rect(struct tridentfb_par *par, + u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h) { - int direction; u32 x1_tmp, x2_tmp, y1_tmp, y2_tmp; - - direction = 0x0004; + int direction = 0x0004; if ((x1 < x2) && (y1 == y2)) { direction |= 0x0200; @@ -379,103 +340,152 @@ static void xp_copy_rect(u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h) y2_tmp = y2; } - writemmr(0x2128, direction); - t_outb(ROP_S, 0x2127); - writemmr(0x213C, masked_point(y1_tmp, x1_tmp)); - writemmr(0x2138, masked_point(y2_tmp, x2_tmp)); - writemmr(0x2140, masked_point(h, w)); - t_outb(0x01, 0x2124); + writemmr(par, DRAWFL, direction); + t_outb(par, ROP_S, 0x2127); + writemmr(par, OLDSRC, point(y1_tmp, x1_tmp)); + writemmr(par, OLDDST, point(y2_tmp, x2_tmp)); + writemmr(par, OLDDIM, point(h, w)); + t_outb(par, 0x01, OLDCMD); } -static struct accel_switch accel_xp = { - xp_init_accel, - xp_wait_engine, - xp_fill_rect, - xp_copy_rect, -}; - /* * Image specific acceleration functions */ -static void image_init_accel(int pitch, int bpp) +static void image_init_accel(struct tridentfb_par *par, int pitch, int bpp) { - int tmp = 0; - switch (bpp) { - case 8: - tmp = 0; - break; - case 15: - tmp = 5; - break; - case 16: - tmp = 1; - break; - case 24: - case 32: - tmp = 2; - break; - } - writemmr(0x2120, 0xF0000000); - writemmr(0x2120, 0x40000000 | tmp); - writemmr(0x2120, 0x80000000); - writemmr(0x2144, 0x00000000); - writemmr(0x2148, 0x00000000); - writemmr(0x2150, 0x00000000); - writemmr(0x2154, 0x00000000); - writemmr(0x2120, 0x60000000 | (pitch << 16) | pitch); - writemmr(0x216C, 0x00000000); - writemmr(0x2170, 0x00000000); - writemmr(0x217C, 0x00000000); - writemmr(0x2120, 0x10000000); - writemmr(0x2130, (2047 << 16) | 2047); + int tmp = bpp == 24 ? 2: (bpp >> 4); + + writemmr(par, 0x2120, 0xF0000000); + writemmr(par, 0x2120, 0x40000000 | tmp); + writemmr(par, 0x2120, 0x80000000); + writemmr(par, 0x2144, 0x00000000); + writemmr(par, 0x2148, 0x00000000); + writemmr(par, 0x2150, 0x00000000); + writemmr(par, 0x2154, 0x00000000); + writemmr(par, 0x2120, 0x60000000 | (pitch << 16) | pitch); + writemmr(par, 0x216C, 0x00000000); + writemmr(par, 0x2170, 0x00000000); + writemmr(par, 0x217C, 0x00000000); + writemmr(par, 0x2120, 0x10000000); + writemmr(par, 0x2130, (2047 << 16) | 2047); } -static void image_wait_engine(void) +static void image_wait_engine(struct tridentfb_par *par) { - while (readmmr(0x2164) & 0xF0000000) ; + while (readmmr(par, 0x2164) & 0xF0000000) + cpu_relax(); } -static void image_fill_rect(u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop) +static void image_fill_rect(struct tridentfb_par *par, + u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop) { - writemmr(0x2120, 0x80000000); - writemmr(0x2120, 0x90000000 | ROP_S); + writemmr(par, 0x2120, 0x80000000); + writemmr(par, 0x2120, 0x90000000 | ROP_S); - writemmr(0x2144, c); + writemmr(par, 0x2144, c); - writemmr(DR1, point(x, y)); - writemmr(DR2, point(x + w - 1, y + h - 1)); + writemmr(par, DST1, point(x, y)); + writemmr(par, DST2, point(x + w - 1, y + h - 1)); - writemmr(0x2124, 0x80000000 | 3 << 22 | 1 << 10 | 1 << 9); + writemmr(par, 0x2124, 0x80000000 | 3 << 22 | 1 << 10 | 1 << 9); } -static void image_copy_rect(u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h) +static void image_copy_rect(struct tridentfb_par *par, + u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h) { - u32 s1, s2, d1, d2; - int direction = 2; - s1 = point(x1, y1); - s2 = point(x1 + w - 1, y1 + h - 1); - d1 = point(x2, y2); - d2 = point(x2 + w - 1, y2 + h - 1); + int direction = 0x4; + u32 s1 = point(x1, y1); + u32 s2 = point(x1 + w - 1, y1 + h - 1); + u32 d1 = point(x2, y2); + u32 d2 = point(x2 + w - 1, y2 + h - 1); if ((y1 > y2) || ((y1 == y2) && (x1 > x2))) direction = 0; - writemmr(0x2120, 0x80000000); - writemmr(0x2120, 0x90000000 | ROP_S); + writemmr(par, 0x2120, 0x80000000); + writemmr(par, 0x2120, 0x90000000 | ROP_S); - writemmr(SR1, direction ? s2 : s1); - writemmr(SR2, direction ? s1 : s2); - writemmr(DR1, direction ? d2 : d1); - writemmr(DR2, direction ? d1 : d2); - writemmr(0x2124, 0x80000000 | 1 << 22 | 1 << 10 | 1 << 7 | direction); + writemmr(par, SRC1, direction ? s2 : s1); + writemmr(par, SRC2, direction ? s1 : s2); + writemmr(par, DST1, direction ? d2 : d1); + writemmr(par, DST2, direction ? d1 : d2); + writemmr(par, 0x2124, + 0x80000000 | 1 << 22 | 1 << 10 | 1 << 7 | direction); } -static struct accel_switch accel_image = { - image_init_accel, - image_wait_engine, - image_fill_rect, - image_copy_rect, -}; +/* + * TGUI 9440/96XX acceleration + */ + +static void tgui_init_accel(struct tridentfb_par *par, int pitch, int bpp) +{ + unsigned char x = bpp == 24 ? 3 : (bpp >> 4); + + /* disable clipping */ + writemmr(par, 0x2148, 0); + writemmr(par, 0x214C, point(4095, 2047)); + + switch ((pitch * bpp) / 8) { + case 8192: + case 512: + x |= 0x00; + break; + case 1024: + x |= 0x04; + break; + case 2048: + x |= 0x08; + break; + case 4096: + x |= 0x0C; + break; + } + + fb_writew(x, par->io_virt + 0x2122); +} + +static void tgui_fill_rect(struct tridentfb_par *par, + u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop) +{ + t_outb(par, ROP_P, 0x2127); + writemmr(par, OLDCLR, c); + writemmr(par, DRAWFL, 0x4020); + writemmr(par, OLDDIM, point(w - 1, h - 1)); + writemmr(par, OLDDST, point(x, y)); + t_outb(par, 1, OLDCMD); +} + +static void tgui_copy_rect(struct tridentfb_par *par, + u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h) +{ + int flags = 0; + u16 x1_tmp, x2_tmp, y1_tmp, y2_tmp; + + if ((x1 < x2) && (y1 == y2)) { + flags |= 0x0200; + x1_tmp = x1 + w - 1; + x2_tmp = x2 + w - 1; + } else { + x1_tmp = x1; + x2_tmp = x2; + } + + if (y1 < y2) { + flags |= 0x0100; + y1_tmp = y1 + h - 1; + y2_tmp = y2 + h - 1; + } else { + y1_tmp = y1; + y2_tmp = y2; + } + + writemmr(par, DRAWFL, 0x4 | flags); + t_outb(par, ROP_S, 0x2127); + writemmr(par, OLDSRC, point(x1_tmp, y1_tmp)); + writemmr(par, OLDDST, point(x2_tmp, y2_tmp)); + writemmr(par, OLDDIM, point(w - 1, h - 1)); + t_outb(par, 1, OLDCMD); +} /* * Accel functions called by the upper layers @@ -484,129 +494,162 @@ static struct accel_switch accel_image = { static void tridentfb_fillrect(struct fb_info *info, const struct fb_fillrect *fr) { - int bpp = info->var.bits_per_pixel; - int col = 0; + struct tridentfb_par *par = info->par; + int col; - switch (bpp) { - default: - case 8: - col |= fr->color; + if (info->flags & FBINFO_HWACCEL_DISABLED) { + cfb_fillrect(info, fr); + return; + } + if (info->var.bits_per_pixel == 8) { + col = fr->color; col |= col << 8; col |= col << 16; - break; - case 16: + } else col = ((u32 *)(info->pseudo_palette))[fr->color]; - break; - case 32: - col = ((u32 *)(info->pseudo_palette))[fr->color]; - break; + + par->wait_engine(par); + par->fill_rect(par, fr->dx, fr->dy, fr->width, + fr->height, col, fr->rop); +} + +static void tridentfb_imageblit(struct fb_info *info, + const struct fb_image *img) +{ + struct tridentfb_par *par = info->par; + int col, bgcol; + + if ((info->flags & FBINFO_HWACCEL_DISABLED) || img->depth != 1) { + cfb_imageblit(info, img); + return; + } + if (info->var.bits_per_pixel == 8) { + col = img->fg_color; + col |= col << 8; + col |= col << 16; + bgcol = img->bg_color; + bgcol |= bgcol << 8; + bgcol |= bgcol << 16; + } else { + col = ((u32 *)(info->pseudo_palette))[img->fg_color]; + bgcol = ((u32 *)(info->pseudo_palette))[img->bg_color]; } - acc->fill_rect(fr->dx, fr->dy, fr->width, fr->height, col, fr->rop); - acc->wait_engine(); + par->wait_engine(par); + if (par->image_blit) + par->image_blit(par, img->data, img->dx, img->dy, + img->width, img->height, col, bgcol); + else + cfb_imageblit(info, img); } + static void tridentfb_copyarea(struct fb_info *info, const struct fb_copyarea *ca) { - acc->copy_rect(ca->sx, ca->sy, ca->dx, ca->dy, ca->width, ca->height); - acc->wait_engine(); + struct tridentfb_par *par = info->par; + + if (info->flags & FBINFO_HWACCEL_DISABLED) { + cfb_copyarea(info, ca); + return; + } + par->wait_engine(par); + par->copy_rect(par, ca->sx, ca->sy, ca->dx, ca->dy, + ca->width, ca->height); +} + +static int tridentfb_sync(struct fb_info *info) +{ + struct tridentfb_par *par = info->par; + + if (!(info->flags & FBINFO_HWACCEL_DISABLED)) + par->wait_engine(par); + return 0; } -#else /* !CONFIG_FB_TRIDENT_ACCEL */ +#else #define tridentfb_fillrect cfb_fillrect #define tridentfb_copyarea cfb_copyarea +#define tridentfb_imageblit cfb_imageblit #endif /* CONFIG_FB_TRIDENT_ACCEL */ - /* * Hardware access functions */ -static inline unsigned char read3X4(int reg) +static inline unsigned char read3X4(struct tridentfb_par *par, int reg) { - struct tridentfb_par *par = (struct tridentfb_par *)fb_info.par; - writeb(reg, par->io_virt + CRT + 4); - return readb(par->io_virt + CRT + 5); + return vga_mm_rcrt(par->io_virt, reg); } -static inline void write3X4(int reg, unsigned char val) +static inline void write3X4(struct tridentfb_par *par, int reg, + unsigned char val) { - struct tridentfb_par *par = (struct tridentfb_par *)fb_info.par; - writeb(reg, par->io_virt + CRT + 4); - writeb(val, par->io_virt + CRT + 5); + vga_mm_wcrt(par->io_virt, reg, val); } -static inline unsigned char read3C4(int reg) +static inline unsigned char read3CE(struct tridentfb_par *par, + unsigned char reg) { - t_outb(reg, 0x3C4); - return t_inb(0x3C5); + return vga_mm_rgfx(par->io_virt, reg); } -static inline void write3C4(int reg, unsigned char val) +static inline void writeAttr(struct tridentfb_par *par, int reg, + unsigned char val) { - t_outb(reg, 0x3C4); - t_outb(val, 0x3C5); + fb_readb(par->io_virt + VGA_IS1_RC); /* flip-flop to index */ + vga_mm_wattr(par->io_virt, reg, val); } -static inline unsigned char read3CE(int reg) +static inline void write3CE(struct tridentfb_par *par, int reg, + unsigned char val) { - t_outb(reg, 0x3CE); - return t_inb(0x3CF); + vga_mm_wgfx(par->io_virt, reg, val); } -static inline void writeAttr(int reg, unsigned char val) -{ - readb(((struct tridentfb_par *)fb_info.par)->io_virt + CRT + 0x0A); /* flip-flop to index */ - t_outb(reg, 0x3C0); - t_outb(val, 0x3C0); -} - -static inline void write3CE(int reg, unsigned char val) -{ - t_outb(reg, 0x3CE); - t_outb(val, 0x3CF); -} - -static void enable_mmio(void) +static void enable_mmio(struct tridentfb_par *par) { /* Goto New Mode */ - outb(0x0B, 0x3C4); - inb(0x3C5); + vga_io_rseq(0x0B); /* Unprotect registers */ - outb(NewMode1, 0x3C4); - outb(0x80, 0x3C5); + vga_io_wseq(NewMode1, 0x80); + if (!is_oldprotect(par->chip_id)) + vga_io_wseq(Protection, 0x92); /* Enable MMIO */ outb(PCIReg, 0x3D4); outb(inb(0x3D5) | 0x01, 0x3D5); } -static void disable_mmio(void) +static void disable_mmio(struct tridentfb_par *par) { /* Goto New Mode */ - t_outb(0x0B, 0x3C4); - t_inb(0x3C5); + vga_mm_rseq(par->io_virt, 0x0B); /* Unprotect registers */ - t_outb(NewMode1, 0x3C4); - t_outb(0x80, 0x3C5); + vga_mm_wseq(par->io_virt, NewMode1, 0x80); + if (!is_oldprotect(par->chip_id)) + vga_mm_wseq(par->io_virt, Protection, 0x92); /* Disable MMIO */ - t_outb(PCIReg, 0x3D4); - t_outb(t_inb(0x3D5) & ~0x01, 0x3D5); + t_outb(par, PCIReg, 0x3D4); + t_outb(par, t_inb(par, 0x3D5) & ~0x01, 0x3D5); } -#define crtc_unlock() write3X4(CRTVSyncEnd, read3X4(CRTVSyncEnd) & 0x7F) +static inline void crtc_unlock(struct tridentfb_par *par) +{ + write3X4(par, VGA_CRTC_V_SYNC_END, + read3X4(par, VGA_CRTC_V_SYNC_END) & 0x7F); +} /* Return flat panel's maximum x resolution */ -static int __devinit get_nativex(void) +static int __devinit get_nativex(struct tridentfb_par *par) { int x, y, tmp; if (nativex) return nativex; - tmp = (read3CE(VertStretch) >> 4) & 3; + tmp = (read3CE(par, VertStretch) >> 4) & 3; switch (tmp) { case 0: @@ -632,77 +675,92 @@ static int __devinit get_nativex(void) } /* Set pitch */ -static void set_lwidth(int width) +static inline void set_lwidth(struct tridentfb_par *par, int width) { - write3X4(Offset, width & 0xFF); - write3X4(AddColReg, - (read3X4(AddColReg) & 0xCF) | ((width & 0x300) >> 4)); + write3X4(par, VGA_CRTC_OFFSET, width & 0xFF); + write3X4(par, AddColReg, + (read3X4(par, AddColReg) & 0xCF) | ((width & 0x300) >> 4)); } /* For resolutions smaller than FP resolution stretch */ -static void screen_stretch(void) +static void screen_stretch(struct tridentfb_par *par) { - if (chip_id != CYBERBLADEXPAi1) - write3CE(BiosReg, 0); + if (par->chip_id != CYBERBLADEXPAi1) + write3CE(par, BiosReg, 0); else - write3CE(BiosReg, 8); - write3CE(VertStretch, (read3CE(VertStretch) & 0x7C) | 1); - write3CE(HorStretch, (read3CE(HorStretch) & 0x7C) | 1); + write3CE(par, BiosReg, 8); + write3CE(par, VertStretch, (read3CE(par, VertStretch) & 0x7C) | 1); + write3CE(par, HorStretch, (read3CE(par, HorStretch) & 0x7C) | 1); } /* For resolutions smaller than FP resolution center */ -static void screen_center(void) +static inline void screen_center(struct tridentfb_par *par) { - write3CE(VertStretch, (read3CE(VertStretch) & 0x7C) | 0x80); - write3CE(HorStretch, (read3CE(HorStretch) & 0x7C) | 0x80); + write3CE(par, VertStretch, (read3CE(par, VertStretch) & 0x7C) | 0x80); + write3CE(par, HorStretch, (read3CE(par, HorStretch) & 0x7C) | 0x80); } /* Address of first shown pixel in display memory */ -static void set_screen_start(int base) +static void set_screen_start(struct tridentfb_par *par, int base) { - write3X4(StartAddrLow, base & 0xFF); - write3X4(StartAddrHigh, (base & 0xFF00) >> 8); - write3X4(CRTCModuleTest, - (read3X4(CRTCModuleTest) & 0xDF) | ((base & 0x10000) >> 11)); - write3X4(CRTHiOrd, - (read3X4(CRTHiOrd) & 0xF8) | ((base & 0xE0000) >> 17)); + u8 tmp; + write3X4(par, VGA_CRTC_START_LO, base & 0xFF); + write3X4(par, VGA_CRTC_START_HI, (base & 0xFF00) >> 8); + tmp = read3X4(par, CRTCModuleTest) & 0xDF; + write3X4(par, CRTCModuleTest, tmp | ((base & 0x10000) >> 11)); + tmp = read3X4(par, CRTHiOrd) & 0xF8; + write3X4(par, CRTHiOrd, tmp | ((base & 0xE0000) >> 17)); } /* Set dotclock frequency */ -static void set_vclk(unsigned long freq) +static void set_vclk(struct tridentfb_par *par, unsigned long freq) { int m, n, k; - unsigned long f, fi, d, di; - unsigned char lo = 0, hi = 0; + unsigned long fi, d, di; + unsigned char best_m = 0, best_n = 0, best_k = 0; + unsigned char hi, lo; + unsigned char shift = !is_oldclock(par->chip_id) ? 2 : 1; d = 20000; - for (k = 2; k >= 0; k--) - for (m = 0; m < 63; m++) - for (n = 0; n < 128; n++) { + for (k = shift; k >= 0; k--) + for (m = 1; m < 32; m++) { + n = ((m + 2) << shift) - 8; + for (n = (n < 0 ? 0 : n); n < 122; n++) { fi = ((14318l * (n + 8)) / (m + 2)) >> k; - if ((di = abs(fi - freq)) < d) { + di = abs(fi - freq); + if (di < d || (di == d && k == best_k)) { d = di; - f = fi; - lo = n; - hi = (k << 6) | m; + best_n = n; + best_m = m; + best_k = k; } if (fi > freq) break; } - if (chip3D) { - write3C4(ClockHigh, hi); - write3C4(ClockLow, lo); + } + + if (is_oldclock(par->chip_id)) { + lo = best_n | (best_m << 7); + hi = (best_m >> 1) | (best_k << 4); } else { - outb(lo, 0x43C8); - outb(hi, 0x43C9); + lo = best_n; + hi = best_m | (best_k << 6); + } + + if (is3Dchip(par->chip_id)) { + vga_mm_wseq(par->io_virt, ClockHigh, hi); + vga_mm_wseq(par->io_virt, ClockLow, lo); + } else { + t_outb(par, lo, 0x43C8); + t_outb(par, hi, 0x43C9); } debug("VCLK = %X %X\n", hi, lo); } /* Set number of lines for flat panels*/ -static void set_number_of_lines(int lines) +static void set_number_of_lines(struct tridentfb_par *par, int lines) { - int tmp = read3CE(CyberEnhance) & 0x8F; + int tmp = read3CE(par, CyberEnhance) & 0x8F; if (lines > 1024) tmp |= 0x50; else if (lines > 768) @@ -711,24 +769,24 @@ static void set_number_of_lines(int lines) tmp |= 0x20; else if (lines > 480) tmp |= 0x10; - write3CE(CyberEnhance, tmp); + write3CE(par, CyberEnhance, tmp); } /* * If we see that FP is active we assume we have one. - * Otherwise we have a CRT display.User can override. + * Otherwise we have a CRT display. User can override. */ -static unsigned int __devinit get_displaytype(void) +static int __devinit is_flatpanel(struct tridentfb_par *par) { if (fp) - return DISPLAY_FP; - if (crt || !chipcyber) - return DISPLAY_CRT; - return (read3CE(FPConfig) & 0x10) ? DISPLAY_FP : DISPLAY_CRT; + return 1; + if (crt || !iscyber(par->chip_id)) + return 0; + return (read3CE(par, FPConfig) & 0x10) ? 1 : 0; } /* Try detecting the video memory size */ -static unsigned int __devinit get_memsize(void) +static unsigned int __devinit get_memsize(struct tridentfb_par *par) { unsigned char tmp, tmp2; unsigned int k; @@ -737,12 +795,12 @@ static unsigned int __devinit get_memsize(void) if (memsize) k = memsize * Kb; else - switch (chip_id) { + switch (par->chip_id) { case CYBER9525DVD: k = 2560 * Kb; break; default: - tmp = read3X4(SPR) & 0x0F; + tmp = read3X4(par, SPR) & 0x0F; switch (tmp) { case 0x01: @@ -774,7 +832,7 @@ static unsigned int __devinit get_memsize(void) break; case 0x0E: /* XP */ - tmp2 = read3C4(0xC1); + tmp2 = vga_mm_rseq(par->io_virt, 0xC1); switch (tmp2) { case 0x00: k = 20 * Mb; @@ -812,26 +870,67 @@ static unsigned int __devinit get_memsize(void) static int tridentfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { + struct tridentfb_par *par = info->par; int bpp = var->bits_per_pixel; + int line_length; + int ramdac = 230000; /* 230MHz for most 3D chips */ debug("enter\n"); /* check color depth */ if (bpp == 24) bpp = var->bits_per_pixel = 32; + if (bpp != 8 && bpp != 16 && bpp != 32) + return -EINVAL; + if (par->chip_id == TGUI9440 && bpp == 32) + return -EINVAL; /* check whether resolution fits on panel and in memory */ - if (flatpanel && nativex && var->xres > nativex) + if (par->flatpanel && nativex && var->xres > nativex) + return -EINVAL; + /* various resolution checks */ + var->xres = (var->xres + 7) & ~0x7; + if (var->xres > var->xres_virtual) + var->xres_virtual = var->xres; + if (var->yres > var->yres_virtual) + var->yres_virtual = var->yres; + if (var->xres_virtual > 4095 || var->yres > 2048) return -EINVAL; - if (var->xres * var->yres_virtual * bpp / 8 > info->fix.smem_len) + /* prevent from position overflow for acceleration */ + if (var->yres_virtual > 0xffff) + return -EINVAL; + line_length = var->xres_virtual * bpp / 8; + + if (!is3Dchip(par->chip_id) && + !(info->flags & FBINFO_HWACCEL_DISABLED)) { + /* acceleration requires line length to be power of 2 */ + if (line_length <= 512) + var->xres_virtual = 512 * 8 / bpp; + else if (line_length <= 1024) + var->xres_virtual = 1024 * 8 / bpp; + else if (line_length <= 2048) + var->xres_virtual = 2048 * 8 / bpp; + else if (line_length <= 4096) + var->xres_virtual = 4096 * 8 / bpp; + else if (line_length <= 8192) + var->xres_virtual = 8192 * 8 / bpp; + else + return -EINVAL; + + line_length = var->xres_virtual * bpp / 8; + } + + /* datasheet specifies how to set panning only up to 4 MB */ + if (line_length * (var->yres_virtual - var->yres) > (4 << 20)) + var->yres_virtual = ((4 << 20) / line_length) + var->yres; + + if (line_length * var->yres_virtual > info->fix.smem_len) return -EINVAL; switch (bpp) { case 8: var->red.offset = 0; - var->green.offset = 0; - var->blue.offset = 0; - var->red.length = 6; - var->green.length = 6; - var->blue.length = 6; + var->red.length = 8; + var->green = var->red; + var->blue = var->red; break; case 16: var->red.offset = 11; @@ -852,6 +951,33 @@ static int tridentfb_check_var(struct fb_var_screeninfo *var, default: return -EINVAL; } + + if (is_xp(par->chip_id)) + ramdac = 350000; + + switch (par->chip_id) { + case TGUI9440: + ramdac = (bpp >= 16) ? 45000 : 90000; + break; + case CYBER9320: + case TGUI9660: + ramdac = 135000; + break; + case PROVIDIA9685: + case CYBER9388: + case CYBER9382: + case CYBER9385: + ramdac = 170000; + break; + } + + /* The clock is doubled for 32 bpp */ + if (bpp == 32) + ramdac /= 2; + + if (PICOS2KHZ(var->pixclock) > ramdac) + return -EINVAL; + debug("exit\n"); return 0; @@ -862,25 +988,31 @@ static int tridentfb_check_var(struct fb_var_screeninfo *var, static int tridentfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { + struct tridentfb_par *par = info->par; unsigned int offset; debug("enter\n"); - offset = (var->xoffset + (var->yoffset * var->xres)) + offset = (var->xoffset + (var->yoffset * var->xres_virtual)) * var->bits_per_pixel / 32; - info->var.xoffset = var->xoffset; - info->var.yoffset = var->yoffset; - set_screen_start(offset); + set_screen_start(par, offset); debug("exit\n"); return 0; } -#define shadowmode_on() write3CE(CyberControl, read3CE(CyberControl) | 0x81) -#define shadowmode_off() write3CE(CyberControl, read3CE(CyberControl) & 0x7E) +static inline void shadowmode_on(struct tridentfb_par *par) +{ + write3CE(par, CyberControl, read3CE(par, CyberControl) | 0x81); +} + +static inline void shadowmode_off(struct tridentfb_par *par) +{ + write3CE(par, CyberControl, read3CE(par, CyberControl) & 0x7E); +} /* Set the hardware to the requested video mode */ static int tridentfb_set_par(struct fb_info *info) { - struct tridentfb_par *par = (struct tridentfb_par *)(info->par); + struct tridentfb_par *par = info->par; u32 htotal, hdispend, hsyncstart, hsyncend, hblankstart, hblankend; u32 vtotal, vdispend, vsyncstart, vsyncend, vblankstart, vblankend; struct fb_var_screeninfo *var = &info->var; @@ -891,58 +1023,73 @@ static int tridentfb_set_par(struct fb_info *info) debug("enter\n"); hdispend = var->xres / 8 - 1; hsyncstart = (var->xres + var->right_margin) / 8; - hsyncend = var->hsync_len / 8; - htotal = - (var->xres + var->left_margin + var->right_margin + - var->hsync_len) / 8 - 10; + hsyncend = (var->xres + var->right_margin + var->hsync_len) / 8; + htotal = (var->xres + var->left_margin + var->right_margin + + var->hsync_len) / 8 - 5; hblankstart = hdispend + 1; - hblankend = htotal + 5; + hblankend = htotal + 3; vdispend = var->yres - 1; vsyncstart = var->yres + var->lower_margin; - vsyncend = var->vsync_len; - vtotal = var->upper_margin + vsyncstart + vsyncend - 2; - vblankstart = var->yres; - vblankend = vtotal + 2; + vsyncend = vsyncstart + var->vsync_len; + vtotal = var->upper_margin + vsyncend - 2; + vblankstart = vdispend + 1; + vblankend = vtotal; + + if (info->var.vmode & FB_VMODE_INTERLACED) { + vtotal /= 2; + vdispend /= 2; + vsyncstart /= 2; + vsyncend /= 2; + vblankstart /= 2; + vblankend /= 2; + } - crtc_unlock(); - write3CE(CyberControl, 8); + enable_mmio(par); + crtc_unlock(par); + write3CE(par, CyberControl, 8); + tmp = 0xEB; + if (var->sync & FB_SYNC_HOR_HIGH_ACT) + tmp &= ~0x40; + if (var->sync & FB_SYNC_VERT_HIGH_ACT) + tmp &= ~0x80; - if (flatpanel && var->xres < nativex) { + if (par->flatpanel && var->xres < nativex) { /* * on flat panels with native size larger * than requested resolution decide whether * we stretch or center */ - t_outb(0xEB, 0x3C2); + t_outb(par, tmp | 0xC0, VGA_MIS_W); - shadowmode_on(); + shadowmode_on(par); if (center) - screen_center(); + screen_center(par); else if (stretch) - screen_stretch(); + screen_stretch(par); } else { - t_outb(0x2B, 0x3C2); - write3CE(CyberControl, 8); + t_outb(par, tmp, VGA_MIS_W); + write3CE(par, CyberControl, 8); } /* vertical timing values */ - write3X4(CRTVTotal, vtotal & 0xFF); - write3X4(CRTVDispEnd, vdispend & 0xFF); - write3X4(CRTVSyncStart, vsyncstart & 0xFF); - write3X4(CRTVSyncEnd, (vsyncend & 0x0F)); - write3X4(CRTVBlankStart, vblankstart & 0xFF); - write3X4(CRTVBlankEnd, 0 /* p->vblankend & 0xFF */ ); + write3X4(par, VGA_CRTC_V_TOTAL, vtotal & 0xFF); + write3X4(par, VGA_CRTC_V_DISP_END, vdispend & 0xFF); + write3X4(par, VGA_CRTC_V_SYNC_START, vsyncstart & 0xFF); + write3X4(par, VGA_CRTC_V_SYNC_END, (vsyncend & 0x0F)); + write3X4(par, VGA_CRTC_V_BLANK_START, vblankstart & 0xFF); + write3X4(par, VGA_CRTC_V_BLANK_END, vblankend & 0xFF); /* horizontal timing values */ - write3X4(CRTHTotal, htotal & 0xFF); - write3X4(CRTHDispEnd, hdispend & 0xFF); - write3X4(CRTHSyncStart, hsyncstart & 0xFF); - write3X4(CRTHSyncEnd, (hsyncend & 0x1F) | ((hblankend & 0x20) << 2)); - write3X4(CRTHBlankStart, hblankstart & 0xFF); - write3X4(CRTHBlankEnd, 0 /* (p->hblankend & 0x1F) */ ); + write3X4(par, VGA_CRTC_H_TOTAL, htotal & 0xFF); + write3X4(par, VGA_CRTC_H_DISP, hdispend & 0xFF); + write3X4(par, VGA_CRTC_H_SYNC_START, hsyncstart & 0xFF); + write3X4(par, VGA_CRTC_H_SYNC_END, + (hsyncend & 0x1F) | ((hblankend & 0x20) << 2)); + write3X4(par, VGA_CRTC_H_BLANK_START, hblankstart & 0xFF); + write3X4(par, VGA_CRTC_H_BLANK_END, hblankend & 0x1F); /* higher bits of vertical timing values */ tmp = 0x10; @@ -954,39 +1101,43 @@ static int tridentfb_set_par(struct fb_info *info) if (vtotal & 0x200) tmp |= 0x20; if (vdispend & 0x200) tmp |= 0x40; if (vsyncstart & 0x200) tmp |= 0x80; - write3X4(CRTOverflow, tmp); + write3X4(par, VGA_CRTC_OVERFLOW, tmp); - tmp = read3X4(CRTHiOrd) | 0x08; /* line compare bit 10 */ + tmp = read3X4(par, CRTHiOrd) & 0x07; + tmp |= 0x08; /* line compare bit 10 */ if (vtotal & 0x400) tmp |= 0x80; if (vblankstart & 0x400) tmp |= 0x40; if (vsyncstart & 0x400) tmp |= 0x20; if (vdispend & 0x400) tmp |= 0x10; - write3X4(CRTHiOrd, tmp); + write3X4(par, CRTHiOrd, tmp); - tmp = 0; - if (htotal & 0x800) tmp |= 0x800 >> 11; - if (hblankstart & 0x800) tmp |= 0x800 >> 7; - write3X4(HorizOverflow, tmp); + tmp = (htotal >> 8) & 0x01; + tmp |= (hdispend >> 7) & 0x02; + tmp |= (hsyncstart >> 5) & 0x08; + tmp |= (hblankstart >> 4) & 0x10; + write3X4(par, HorizOverflow, tmp); tmp = 0x40; if (vblankstart & 0x200) tmp |= 0x20; //FIXME if (info->var.vmode & FB_VMODE_DOUBLE) tmp |= 0x80; /* double scan for 200 line modes */ - write3X4(CRTMaxScanLine, tmp); + write3X4(par, VGA_CRTC_MAX_SCAN, tmp); - write3X4(CRTLineCompare, 0xFF); - write3X4(CRTPRowScan, 0); - write3X4(CRTModeControl, 0xC3); + write3X4(par, VGA_CRTC_LINE_COMPARE, 0xFF); + write3X4(par, VGA_CRTC_PRESET_ROW, 0); + write3X4(par, VGA_CRTC_MODE, 0xC3); - write3X4(LinearAddReg, 0x20); /* enable linear addressing */ + write3X4(par, LinearAddReg, 0x20); /* enable linear addressing */ tmp = (info->var.vmode & FB_VMODE_INTERLACED) ? 0x84 : 0x80; - write3X4(CRTCModuleTest, tmp); /* enable access extended memory */ - - write3X4(GraphEngReg, 0x80); /* enable GE for text acceleration */ + /* enable access extended memory */ + write3X4(par, CRTCModuleTest, tmp); + tmp = read3CE(par, MiscIntContReg) & ~0x4; + if (info->var.vmode & FB_VMODE_INTERLACED) + tmp |= 0x4; + write3CE(par, MiscIntContReg, tmp); -#ifdef CONFIG_FB_TRIDENT_ACCEL - acc->init_accel(info->var.xres, bpp); -#endif + /* enable GE for text acceleration */ + write3X4(par, GraphEngReg, 0x80); switch (bpp) { case 8: @@ -1003,57 +1154,59 @@ static int tridentfb_set_par(struct fb_info *info) break; } - write3X4(PixelBusReg, tmp); + write3X4(par, PixelBusReg, tmp); - tmp = 0x10; - if (chipcyber) + tmp = read3X4(par, DRAMControl); + if (!is_oldprotect(par->chip_id)) + tmp |= 0x10; + if (iscyber(par->chip_id)) tmp |= 0x20; - write3X4(DRAMControl, tmp); /* both IO, linear enable */ - - write3X4(InterfaceSel, read3X4(InterfaceSel) | 0x40); - write3X4(Performance, 0x92); - write3X4(PCIReg, 0x07); /* MMIO & PCI read and write burst enable */ + write3X4(par, DRAMControl, tmp); /* both IO, linear enable */ + + write3X4(par, InterfaceSel, read3X4(par, InterfaceSel) | 0x40); + if (!is_xp(par->chip_id)) + write3X4(par, Performance, read3X4(par, Performance) | 0x10); + /* MMIO & PCI read and write burst enable */ + if (par->chip_id != TGUI9440 && par->chip_id != IMAGE975) + write3X4(par, PCIReg, read3X4(par, PCIReg) | 0x06); + + vga_mm_wseq(par->io_virt, 0, 3); + vga_mm_wseq(par->io_virt, 1, 1); /* set char clock 8 dots wide */ + /* enable 4 maps because needed in chain4 mode */ + vga_mm_wseq(par->io_virt, 2, 0x0F); + vga_mm_wseq(par->io_virt, 3, 0); + vga_mm_wseq(par->io_virt, 4, 0x0E); /* memory mode enable bitmaps ?? */ /* convert from picoseconds to kHz */ vclk = PICOS2KHZ(info->var.pixclock); - if (bpp == 32) + + /* divide clock by 2 if 32bpp chain4 mode display and CPU path */ + tmp = read3CE(par, MiscExtFunc) & 0xF0; + if (bpp == 32 || (par->chip_id == TGUI9440 && bpp == 16)) { + tmp |= 8; vclk *= 2; - set_vclk(vclk); - - write3C4(0, 3); - write3C4(1, 1); /* set char clock 8 dots wide */ - write3C4(2, 0x0F); /* enable 4 maps because needed in chain4 mode */ - write3C4(3, 0); - write3C4(4, 0x0E); /* memory mode enable bitmaps ?? */ - - write3CE(MiscExtFunc, (bpp == 32) ? 0x1A : 0x12); /* divide clock by 2 if 32bpp */ - /* chain4 mode display and CPU path */ - write3CE(0x5, 0x40); /* no CGA compat, allow 256 col */ - write3CE(0x6, 0x05); /* graphics mode */ - write3CE(0x7, 0x0F); /* planes? */ - - if (chip_id == CYBERBLADEXPAi1) { - /* This fixes snow-effect in 32 bpp */ - write3X4(CRTHSyncStart, 0x84); } + set_vclk(par, vclk); + write3CE(par, MiscExtFunc, tmp | 0x12); + write3CE(par, 0x5, 0x40); /* no CGA compat, allow 256 col */ + write3CE(par, 0x6, 0x05); /* graphics mode */ + write3CE(par, 0x7, 0x0F); /* planes? */ - writeAttr(0x10, 0x41); /* graphics mode and support 256 color modes */ - writeAttr(0x12, 0x0F); /* planes */ - writeAttr(0x13, 0); /* horizontal pel panning */ + /* graphics mode and support 256 color modes */ + writeAttr(par, 0x10, 0x41); + writeAttr(par, 0x12, 0x0F); /* planes */ + writeAttr(par, 0x13, 0); /* horizontal pel panning */ /* colors */ for (tmp = 0; tmp < 0x10; tmp++) - writeAttr(tmp, tmp); - readb(par->io_virt + CRT + 0x0A); /* flip-flop to index */ - t_outb(0x20, 0x3C0); /* enable attr */ + writeAttr(par, tmp, tmp); + fb_readb(par->io_virt + VGA_IS1_RC); /* flip-flop to index */ + t_outb(par, 0x20, VGA_ATT_W); /* enable attr */ switch (bpp) { case 8: tmp = 0; break; - case 15: - tmp = 0x10; - break; case 16: tmp = 0x30; break; @@ -1063,19 +1216,23 @@ static int tridentfb_set_par(struct fb_info *info) break; } - t_inb(0x3C8); - t_inb(0x3C6); - t_inb(0x3C6); - t_inb(0x3C6); - t_inb(0x3C6); - t_outb(tmp, 0x3C6); - t_inb(0x3C8); - - if (flatpanel) - set_number_of_lines(info->var.yres); - set_lwidth(info->var.xres * bpp / (4 * 16)); + t_inb(par, VGA_PEL_IW); + t_inb(par, VGA_PEL_MSK); + t_inb(par, VGA_PEL_MSK); + t_inb(par, VGA_PEL_MSK); + t_inb(par, VGA_PEL_MSK); + t_outb(par, tmp, VGA_PEL_MSK); + t_inb(par, VGA_PEL_IW); + + if (par->flatpanel) + set_number_of_lines(par, info->var.yres); + info->fix.line_length = info->var.xres_virtual * bpp / 8; + set_lwidth(par, info->fix.line_length / 8); + + if (!(info->flags & FBINFO_HWACCEL_DISABLED)) + par->init_accel(par, info->var.xres_virtual, bpp); + info->fix.visual = (bpp == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; - info->fix.line_length = info->var.xres * (bpp >> 3); info->cmap.len = (bpp == 8) ? 256 : 16; debug("exit\n"); return 0; @@ -1087,17 +1244,18 @@ static int tridentfb_setcolreg(unsigned regno, unsigned red, unsigned green, struct fb_info *info) { int bpp = info->var.bits_per_pixel; + struct tridentfb_par *par = info->par; if (regno >= info->cmap.len) return 1; if (bpp == 8) { - t_outb(0xFF, 0x3C6); - t_outb(regno, 0x3C8); + t_outb(par, 0xFF, VGA_PEL_MSK); + t_outb(par, regno, VGA_PEL_IW); - t_outb(red >> 10, 0x3C9); - t_outb(green >> 10, 0x3C9); - t_outb(blue >> 10, 0x3C9); + t_outb(par, red >> 10, VGA_PEL_D); + t_outb(par, green >> 10, VGA_PEL_D); + t_outb(par, blue >> 10, VGA_PEL_D); } else if (regno < 16) { if (bpp == 16) { /* RGB 565 */ @@ -1108,28 +1266,28 @@ static int tridentfb_setcolreg(unsigned regno, unsigned red, unsigned green, col |= col << 16; ((u32 *)(info->pseudo_palette))[regno] = col; } else if (bpp == 32) /* ARGB 8888 */ - ((u32*)info->pseudo_palette)[regno] = + ((u32 *)info->pseudo_palette)[regno] = ((transp & 0xFF00) << 16) | ((red & 0xFF00) << 8) | ((green & 0xFF00)) | ((blue & 0xFF00) >> 8); } -/* debug("exit\n"); */ return 0; } -/* Try blanking the screen.For flat panels it does nothing */ +/* Try blanking the screen. For flat panels it does nothing */ static int tridentfb_blank(int blank_mode, struct fb_info *info) { unsigned char PMCont, DPMSCont; + struct tridentfb_par *par = info->par; debug("enter\n"); - if (flatpanel) + if (par->flatpanel) return 0; - t_outb(0x04, 0x83C8); /* Read DPMS Control */ - PMCont = t_inb(0x83C6) & 0xFC; - DPMSCont = read3CE(PowerStatus) & 0xFC; + t_outb(par, 0x04, 0x83C8); /* Read DPMS Control */ + PMCont = t_inb(par, 0x83C6) & 0xFC; + DPMSCont = read3CE(par, PowerStatus) & 0xFC; switch (blank_mode) { case FB_BLANK_UNBLANK: /* Screen: On, HSync: On, VSync: On */ @@ -1155,9 +1313,9 @@ static int tridentfb_blank(int blank_mode, struct fb_info *info) break; } - write3CE(PowerStatus, DPMSCont); - t_outb(4, 0x83C8); - t_outb(PMCont, 0x83C6); + write3CE(par, PowerStatus, DPMSCont); + t_outb(par, 4, 0x83C8); + t_outb(par, PMCont, 0x83C6); debug("exit\n"); @@ -1174,33 +1332,46 @@ static struct fb_ops tridentfb_ops = { .fb_set_par = tridentfb_set_par, .fb_fillrect = tridentfb_fillrect, .fb_copyarea = tridentfb_copyarea, - .fb_imageblit = cfb_imageblit, + .fb_imageblit = tridentfb_imageblit, +#ifdef CONFIG_FB_TRIDENT_ACCEL + .fb_sync = tridentfb_sync, +#endif }; -static int __devinit trident_pci_probe(struct pci_dev * dev, - const struct pci_device_id * id) +static int __devinit trident_pci_probe(struct pci_dev *dev, + const struct pci_device_id *id) { int err; unsigned char revision; + struct fb_info *info; + struct tridentfb_par *default_par; + int chip3D; + int chip_id; err = pci_enable_device(dev); if (err) return err; - chip_id = id->device; + info = framebuffer_alloc(sizeof(struct tridentfb_par), &dev->dev); + if (!info) + return -ENOMEM; + default_par = info->par; - if (chip_id == CYBERBLADEi1) - output("*** Please do use cyblafb, Cyberblade/i1 support " - "will soon be removed from tridentfb!\n"); + chip_id = id->device; +#ifndef CONFIG_FB_TRIDENT_ACCEL + noaccel = 1; +#endif /* If PCI id is 0x9660 then further detect chip type */ if (chip_id == TGUI9660) { - outb(RevisionID, 0x3C4); - revision = inb(0x3C5); + revision = vga_io_rseq(RevisionID); switch (revision) { + case 0x21: + chip_id = PROVIDIA9685; + break; case 0x22: case 0x23: chip_id = CYBER9397; @@ -1229,123 +1400,170 @@ static int __devinit trident_pci_probe(struct pci_dev * dev, } chip3D = is3Dchip(chip_id); - chipcyber = iscyber(chip_id); if (is_xp(chip_id)) { - acc = &accel_xp; + default_par->init_accel = xp_init_accel; + default_par->wait_engine = xp_wait_engine; + default_par->fill_rect = xp_fill_rect; + default_par->copy_rect = xp_copy_rect; + tridentfb_fix.accel = FB_ACCEL_TRIDENT_BLADEXP; } else if (is_blade(chip_id)) { - acc = &accel_blade; - } else { - acc = &accel_image; + default_par->init_accel = blade_init_accel; + default_par->wait_engine = blade_wait_engine; + default_par->fill_rect = blade_fill_rect; + default_par->copy_rect = blade_copy_rect; + default_par->image_blit = blade_image_blit; + tridentfb_fix.accel = FB_ACCEL_TRIDENT_BLADE3D; + } else if (chip3D) { /* 3DImage family left */ + default_par->init_accel = image_init_accel; + default_par->wait_engine = image_wait_engine; + default_par->fill_rect = image_fill_rect; + default_par->copy_rect = image_copy_rect; + tridentfb_fix.accel = FB_ACCEL_TRIDENT_3DIMAGE; + } else { /* TGUI 9440/96XX family */ + default_par->init_accel = tgui_init_accel; + default_par->wait_engine = xp_wait_engine; + default_par->fill_rect = tgui_fill_rect; + default_par->copy_rect = tgui_copy_rect; + tridentfb_fix.accel = FB_ACCEL_TRIDENT_TGUI; } - /* acceleration is on by default for 3D chips */ - defaultaccel = chip3D && !noaccel; - - fb_info.par = &default_par; + default_par->chip_id = chip_id; /* setup MMIO region */ tridentfb_fix.mmio_start = pci_resource_start(dev, 1); - tridentfb_fix.mmio_len = chip3D ? 0x20000 : 0x10000; + tridentfb_fix.mmio_len = pci_resource_len(dev, 1); - if (!request_mem_region(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len, "tridentfb")) { + if (!request_mem_region(tridentfb_fix.mmio_start, + tridentfb_fix.mmio_len, "tridentfb")) { debug("request_region failed!\n"); + framebuffer_release(info); return -1; } - default_par.io_virt = ioremap_nocache(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len); + default_par->io_virt = ioremap_nocache(tridentfb_fix.mmio_start, + tridentfb_fix.mmio_len); - if (!default_par.io_virt) { + if (!default_par->io_virt) { debug("ioremap failed\n"); err = -1; goto out_unmap1; } - enable_mmio(); + enable_mmio(default_par); /* setup framebuffer memory */ tridentfb_fix.smem_start = pci_resource_start(dev, 0); - tridentfb_fix.smem_len = get_memsize(); + tridentfb_fix.smem_len = get_memsize(default_par); - if (!request_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len, "tridentfb")) { + if (!request_mem_region(tridentfb_fix.smem_start, + tridentfb_fix.smem_len, "tridentfb")) { debug("request_mem_region failed!\n"); - disable_mmio(); + disable_mmio(info->par); err = -1; goto out_unmap1; } - fb_info.screen_base = ioremap_nocache(tridentfb_fix.smem_start, - tridentfb_fix.smem_len); + info->screen_base = ioremap_nocache(tridentfb_fix.smem_start, + tridentfb_fix.smem_len); - if (!fb_info.screen_base) { + if (!info->screen_base) { debug("ioremap failed\n"); err = -1; goto out_unmap2; } - output("%s board found\n", pci_name(dev)); - displaytype = get_displaytype(); + default_par->flatpanel = is_flatpanel(default_par); - if (flatpanel) - nativex = get_nativex(); + if (default_par->flatpanel) + nativex = get_nativex(default_par); - fb_info.fix = tridentfb_fix; - fb_info.fbops = &tridentfb_ops; + info->fix = tridentfb_fix; + info->fbops = &tridentfb_ops; + info->pseudo_palette = default_par->pseudo_pal; + info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; + if (!noaccel && default_par->init_accel) { + info->flags &= ~FBINFO_HWACCEL_DISABLED; + info->flags |= FBINFO_HWACCEL_COPYAREA; + info->flags |= FBINFO_HWACCEL_FILLRECT; + } else + info->flags |= FBINFO_HWACCEL_DISABLED; - fb_info.flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; -#ifdef CONFIG_FB_TRIDENT_ACCEL - fb_info.flags |= FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT; -#endif - fb_info.pseudo_palette = pseudo_pal; + info->pixmap.addr = kmalloc(4096, GFP_KERNEL); + if (!info->pixmap.addr) { + err = -ENOMEM; + goto out_unmap2; + } + + info->pixmap.size = 4096; + info->pixmap.buf_align = 4; + info->pixmap.scan_align = 1; + info->pixmap.access_align = 32; + info->pixmap.flags = FB_PIXMAP_SYSTEM; - if (!fb_find_mode(&default_var, &fb_info, + if (default_par->image_blit) { + info->flags |= FBINFO_HWACCEL_IMAGEBLIT; + info->pixmap.scan_align = 4; + } + + if (noaccel) { + printk(KERN_DEBUG "disabling acceleration\n"); + info->flags |= FBINFO_HWACCEL_DISABLED; + info->pixmap.scan_align = 1; + } + + if (!fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, bpp)) { err = -EINVAL; goto out_unmap2; } - err = fb_alloc_cmap(&fb_info.cmap, 256, 0); + err = fb_alloc_cmap(&info->cmap, 256, 0); if (err < 0) goto out_unmap2; - if (defaultaccel && acc) - default_var.accel_flags |= FB_ACCELF_TEXT; - else - default_var.accel_flags &= ~FB_ACCELF_TEXT; - default_var.activate |= FB_ACTIVATE_NOW; - fb_info.var = default_var; - fb_info.device = &dev->dev; - if (register_framebuffer(&fb_info) < 0) { - printk(KERN_ERR "tridentfb: could not register Trident framebuffer\n"); - fb_dealloc_cmap(&fb_info.cmap); + info->var.activate |= FB_ACTIVATE_NOW; + info->device = &dev->dev; + if (register_framebuffer(info) < 0) { + printk(KERN_ERR "tridentfb: could not register framebuffer\n"); + fb_dealloc_cmap(&info->cmap); err = -EINVAL; goto out_unmap2; } output("fb%d: %s frame buffer device %dx%d-%dbpp\n", - fb_info.node, fb_info.fix.id, default_var.xres, - default_var.yres, default_var.bits_per_pixel); + info->node, info->fix.id, info->var.xres, + info->var.yres, info->var.bits_per_pixel); + + pci_set_drvdata(dev, info); return 0; out_unmap2: - if (fb_info.screen_base) - iounmap(fb_info.screen_base); + kfree(info->pixmap.addr); + if (info->screen_base) + iounmap(info->screen_base); release_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len); - disable_mmio(); + disable_mmio(info->par); out_unmap1: - if (default_par.io_virt) - iounmap(default_par.io_virt); + if (default_par->io_virt) + iounmap(default_par->io_virt); release_mem_region(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len); + framebuffer_release(info); return err; } static void __devexit trident_pci_remove(struct pci_dev *dev) { - struct tridentfb_par *par = (struct tridentfb_par*)fb_info.par; - unregister_framebuffer(&fb_info); + struct fb_info *info = pci_get_drvdata(dev); + struct tridentfb_par *par = info->par; + + unregister_framebuffer(info); iounmap(par->io_virt); - iounmap(fb_info.screen_base); + iounmap(info->screen_base); release_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len); release_mem_region(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len); + pci_set_drvdata(dev, NULL); + kfree(info->pixmap.addr); + framebuffer_release(info); } /* List of boards that we are trying to support */ @@ -1358,6 +1576,7 @@ static struct pci_device_id trident_devices[] = { {PCI_VENDOR_ID_TRIDENT, CYBERBLADEAi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {PCI_VENDOR_ID_TRIDENT, CYBERBLADEAi1D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {PCI_VENDOR_ID_TRIDENT, CYBERBLADEE4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_TRIDENT, TGUI9440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {PCI_VENDOR_ID_TRIDENT, TGUI9660, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {PCI_VENDOR_ID_TRIDENT, IMAGE975, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {PCI_VENDOR_ID_TRIDENT, IMAGE985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, @@ -1399,9 +1618,9 @@ static int __init tridentfb_setup(char *options) if (!strncmp(opt, "noaccel", 7)) noaccel = 1; else if (!strncmp(opt, "fp", 2)) - displaytype = DISPLAY_FP; + fp = 1; else if (!strncmp(opt, "crt", 3)) - displaytype = DISPLAY_CRT; + fp = 0; else if (!strncmp(opt, "bpp=", 4)) bpp = simple_strtoul(opt + 4, NULL, 0); else if (!strncmp(opt, "center", 6)) @@ -1430,7 +1649,6 @@ static int __init tridentfb_init(void) return -ENODEV; tridentfb_setup(option); #endif - output("Trident framebuffer %s initializing\n", VERSION); return pci_register_driver(&tridentfb_pci_driver); } diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c index cdbb56edb6cb..50744229c7a9 100644 --- a/drivers/video/uvesafb.c +++ b/drivers/video/uvesafb.c @@ -2054,8 +2054,8 @@ MODULE_PARM_DESC(maxhf, module_param(maxvf, ushort, 0); MODULE_PARM_DESC(maxvf, "Maximum vertical frequency [Hz], overrides EDID data"); -module_param_named(mode, mode_option, charp, 0); -MODULE_PARM_DESC(mode, +module_param(mode_option, charp, 0); +MODULE_PARM_DESC(mode_option, "Specify initial video mode as \"<xres>x<yres>[-<bpp>][@<refresh>]\""); module_param(vbemode, ushort, 0); MODULE_PARM_DESC(vbemode, diff --git a/drivers/video/vfb.c b/drivers/video/vfb.c index 072638a9528a..93fe08d6c78f 100644 --- a/drivers/video/vfb.c +++ b/drivers/video/vfb.c @@ -443,19 +443,29 @@ static int vfb_mmap(struct fb_info *info, } #ifndef MODULE +/* + * The virtual framebuffer driver is only enabled if explicitly + * requested by passing 'video=vfb:' (or any actual options). + */ static int __init vfb_setup(char *options) { char *this_opt; + vfb_enable = 0; + + if (!options) + return 1; + vfb_enable = 1; - if (!options || !*options) + if (!*options) return 1; while ((this_opt = strsep(&options, ",")) != NULL) { if (!*this_opt) continue; - if (!strncmp(this_opt, "disable", 7)) + /* Test disable for backwards compatibility */ + if (!strcmp(this_opt, "disable")) vfb_enable = 0; } return 1; diff --git a/drivers/video/vga16fb.c b/drivers/video/vga16fb.c index 9b3c5923365e..e31bca8a0cb2 100644 --- a/drivers/video/vga16fb.c +++ b/drivers/video/vga16fb.c @@ -26,18 +26,6 @@ #include <asm/io.h> #include <video/vga.h> -#define GRAPHICS_ADDR_REG VGA_GFX_I /* Graphics address register. */ -#define GRAPHICS_DATA_REG VGA_GFX_D /* Graphics data register. */ - -#define SET_RESET_INDEX VGA_GFX_SR_VALUE /* Set/Reset Register index. */ -#define ENABLE_SET_RESET_INDEX VGA_GFX_SR_ENABLE /* Enable Set/Reset Register index. */ -#define DATA_ROTATE_INDEX VGA_GFX_DATA_ROTATE /* Data Rotate Register index. */ -#define GRAPHICS_MODE_INDEX VGA_GFX_MODE /* Graphics Mode Register index. */ -#define BIT_MASK_INDEX VGA_GFX_BIT_MASK /* Bit Mask Register index. */ - -#define dac_reg (VGA_PEL_IW) -#define dac_val (VGA_PEL_D) - #define VGA_FB_PHYS 0xA0000 #define VGA_FB_PHYS_LEN 65536 @@ -108,7 +96,7 @@ static struct fb_fix_screeninfo vga16fb_fix __initdata = { .visual = FB_VISUAL_PSEUDOCOLOR, .xpanstep = 8, .ypanstep = 1, - .line_length = 640/8, + .line_length = 640 / 8, .accel = FB_ACCEL_NONE }; @@ -135,23 +123,22 @@ static inline int setmode(int mode) { int oldmode; - vga_io_w(GRAPHICS_ADDR_REG, GRAPHICS_MODE_INDEX); - oldmode = vga_io_r(GRAPHICS_DATA_REG); - vga_io_w(GRAPHICS_DATA_REG, mode); + oldmode = vga_io_rgfx(VGA_GFX_MODE); + vga_io_w(VGA_GFX_D, mode); return oldmode; } /* Select the Bit Mask Register and return its value. */ static inline int selectmask(void) { - return vga_io_rgfx(BIT_MASK_INDEX); + return vga_io_rgfx(VGA_GFX_BIT_MASK); } /* Set the value of the Bit Mask Register. It must already have been selected with selectmask(). */ static inline void setmask(int mask) { - vga_io_w(GRAPHICS_DATA_REG, mask); + vga_io_w(VGA_GFX_D, mask); } /* Set the Data Rotate Register and return its old value. @@ -161,9 +148,8 @@ static inline int setop(int op) { int oldop; - vga_io_w(GRAPHICS_ADDR_REG, DATA_ROTATE_INDEX); - oldop = vga_io_r(GRAPHICS_DATA_REG); - vga_io_w(GRAPHICS_DATA_REG, op); + oldop = vga_io_rgfx(VGA_GFX_DATA_ROTATE); + vga_io_w(VGA_GFX_D, op); return oldop; } @@ -173,9 +159,8 @@ static inline int setsr(int sr) { int oldsr; - vga_io_w(GRAPHICS_ADDR_REG, ENABLE_SET_RESET_INDEX); - oldsr = vga_io_r(GRAPHICS_DATA_REG); - vga_io_w(GRAPHICS_DATA_REG, sr); + oldsr = vga_io_rgfx(VGA_GFX_SR_ENABLE); + vga_io_w(VGA_GFX_D, sr); return oldsr; } @@ -184,22 +169,21 @@ static inline int setcolor(int color) { int oldcolor; - vga_io_w(GRAPHICS_ADDR_REG, SET_RESET_INDEX); - oldcolor = vga_io_r(GRAPHICS_DATA_REG); - vga_io_w(GRAPHICS_DATA_REG, color); + oldcolor = vga_io_rgfx(VGA_GFX_SR_VALUE); + vga_io_w(VGA_GFX_D, color); return oldcolor; } /* Return the value in the Graphics Address Register. */ static inline int getindex(void) { - return vga_io_r(GRAPHICS_ADDR_REG); + return vga_io_r(VGA_GFX_I); } /* Set the value in the Graphics Address Register. */ static inline void setindex(int index) { - vga_io_w(GRAPHICS_ADDR_REG, index); + vga_io_w(VGA_GFX_I, index); } static void vga16fb_pan_var(struct fb_info *info, @@ -672,10 +656,10 @@ static void ega16_setpalette(int regno, unsigned red, unsigned green, unsigned b static void vga16_setpalette(int regno, unsigned red, unsigned green, unsigned blue) { - outb(regno, dac_reg); - outb(red >> 10, dac_val); - outb(green >> 10, dac_val); - outb(blue >> 10, dac_val); + outb(regno, VGA_PEL_IW); + outb(red >> 10, VGA_PEL_D); + outb(green >> 10, VGA_PEL_D); + outb(blue >> 10, VGA_PEL_D); } static int vga16fb_setcolreg(unsigned regno, unsigned red, unsigned green, @@ -719,28 +703,15 @@ static int vga16fb_pan_display(struct fb_var_screeninfo *var, blanking code was originally by Huang shi chao, and modified by Christoph Rimek (chrimek@toppoint.de) and todd j. derr (tjd@barefoot.org) for Linux. */ -#define attrib_port VGA_ATC_IW -#define seq_port_reg VGA_SEQ_I -#define seq_port_val VGA_SEQ_D -#define gr_port_reg VGA_GFX_I -#define gr_port_val VGA_GFX_D -#define video_misc_rd VGA_MIS_R -#define video_misc_wr VGA_MIS_W -#define vga_video_port_reg VGA_CRT_IC -#define vga_video_port_val VGA_CRT_DC static void vga_vesa_blank(struct vga16fb_par *par, int mode) { - unsigned char SeqCtrlIndex; - unsigned char CrtCtrlIndex; + unsigned char SeqCtrlIndex = vga_io_r(VGA_SEQ_I); + unsigned char CrtCtrlIndex = vga_io_r(VGA_CRT_IC); - //cli(); - SeqCtrlIndex = vga_io_r(seq_port_reg); - CrtCtrlIndex = vga_io_r(vga_video_port_reg); - /* save original values of VGA controller registers */ if(!par->vesa_blanked) { - par->vga_state.CrtMiscIO = vga_io_r(video_misc_rd); + par->vga_state.CrtMiscIO = vga_io_r(VGA_MIS_R); //sti(); par->vga_state.HorizontalTotal = vga_io_rcrt(0x00); /* HorizontalTotal */ @@ -756,12 +727,11 @@ static void vga_vesa_blank(struct vga16fb_par *par, int mode) /* assure that video is enabled */ /* "0x20" is VIDEO_ENABLE_bit in register 01 of sequencer */ - //cli(); vga_io_wseq(0x01, par->vga_state.ClockingMode | 0x20); /* test for vertical retrace in process.... */ if ((par->vga_state.CrtMiscIO & 0x80) == 0x80) - vga_io_w(video_misc_wr, par->vga_state.CrtMiscIO & 0xef); + vga_io_w(VGA_MIS_W, par->vga_state.CrtMiscIO & 0xef); /* * Set <End of vertical retrace> to minimum (0) and @@ -769,12 +739,10 @@ static void vga_vesa_blank(struct vga16fb_par *par, int mode) * Result: turn off vertical sync (VSync) pulse. */ if (mode & FB_BLANK_VSYNC_SUSPEND) { - outb_p(0x10,vga_video_port_reg); /* StartVertRetrace */ - outb_p(0xff,vga_video_port_val); /* maximum value */ - outb_p(0x11,vga_video_port_reg); /* EndVertRetrace */ - outb_p(0x40,vga_video_port_val); /* minimum (bits 0..3) */ - outb_p(0x07,vga_video_port_reg); /* Overflow */ - outb_p(par->vga_state.Overflow | 0x84,vga_video_port_val); /* bits 9,10 of vert. retrace */ + vga_io_wcrt(VGA_CRTC_V_SYNC_START, 0xff); + vga_io_wcrt(VGA_CRTC_V_SYNC_END, 0x40); + /* bits 9,10 of vert. retrace */ + vga_io_wcrt(VGA_CRTC_OVERFLOW, par->vga_state.Overflow | 0x84); } if (mode & FB_BLANK_HSYNC_SUSPEND) { @@ -783,29 +751,22 @@ static void vga_vesa_blank(struct vga16fb_par *par, int mode) * <Start of horizontal Retrace> to maximum * Result: turn off horizontal sync (HSync) pulse. */ - outb_p(0x04,vga_video_port_reg); /* StartHorizRetrace */ - outb_p(0xff,vga_video_port_val); /* maximum */ - outb_p(0x05,vga_video_port_reg); /* EndHorizRetrace */ - outb_p(0x00,vga_video_port_val); /* minimum (0) */ + vga_io_wcrt(VGA_CRTC_H_SYNC_START, 0xff); + vga_io_wcrt(VGA_CRTC_H_SYNC_END, 0x00); } /* restore both index registers */ - outb_p(SeqCtrlIndex,seq_port_reg); - outb_p(CrtCtrlIndex,vga_video_port_reg); - //sti(); + outb_p(SeqCtrlIndex, VGA_SEQ_I); + outb_p(CrtCtrlIndex, VGA_CRT_IC); } static void vga_vesa_unblank(struct vga16fb_par *par) { - unsigned char SeqCtrlIndex; - unsigned char CrtCtrlIndex; + unsigned char SeqCtrlIndex = vga_io_r(VGA_SEQ_I); + unsigned char CrtCtrlIndex = vga_io_r(VGA_CRT_IC); - //cli(); - SeqCtrlIndex = vga_io_r(seq_port_reg); - CrtCtrlIndex = vga_io_r(vga_video_port_reg); - /* restore original values of VGA controller registers */ - vga_io_w(video_misc_wr, par->vga_state.CrtMiscIO); + vga_io_w(VGA_MIS_W, par->vga_state.CrtMiscIO); /* HorizontalTotal */ vga_io_wcrt(0x00, par->vga_state.HorizontalTotal); @@ -827,9 +788,8 @@ static void vga_vesa_unblank(struct vga16fb_par *par) vga_io_wseq(0x01, par->vga_state.ClockingMode); /* restore index/control registers */ - vga_io_w(seq_port_reg, SeqCtrlIndex); - vga_io_w(vga_video_port_reg, CrtCtrlIndex); - //sti(); + vga_io_w(VGA_SEQ_I, SeqCtrlIndex); + vga_io_w(VGA_CRT_IC, CrtCtrlIndex); } static void vga_pal_blank(void) @@ -837,10 +797,10 @@ static void vga_pal_blank(void) int i; for (i=0; i<16; i++) { - outb_p (i, dac_reg) ; - outb_p (0, dac_val) ; - outb_p (0, dac_val) ; - outb_p (0, dac_val) ; + outb_p(i, VGA_PEL_IW); + outb_p(0, VGA_PEL_D); + outb_p(0, VGA_PEL_D); + outb_p(0, VGA_PEL_D); } } @@ -1087,12 +1047,15 @@ static void vga16fb_copyarea(struct fb_info *info, const struct fb_copyarea *are width = x2 - dx; height = y2 - dy; + if (sx + dx < old_dx || sy + dy < old_dy) + return; + /* update sx1,sy1 */ sx += (dx - old_dx); sy += (dy - old_dy); /* the source must be completely inside the virtual screen */ - if (sx < 0 || sy < 0 || (sx + width) > vxres || (sy + height) > vyres) + if (sx + width > vxres || sy + height > vyres) return; switch (info->fix.type) { @@ -1482,6 +1445,7 @@ static void __exit vga16fb_exit(void) platform_driver_unregister(&vga16fb_driver); } +MODULE_DESCRIPTION("Legacy VGA framebuffer device driver"); MODULE_LICENSE("GPL"); module_init(vga16fb_init); module_exit(vga16fb_exit); diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index 7084e7e146c0..5b78fd0aff0a 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -71,13 +71,6 @@ static int virtio_uevent(struct device *_dv, struct kobj_uevent_env *env) dev->id.device, dev->id.vendor); } -static struct bus_type virtio_bus = { - .name = "virtio", - .match = virtio_dev_match, - .dev_attrs = virtio_dev_attrs, - .uevent = virtio_uevent, -}; - static void add_status(struct virtio_device *dev, unsigned status) { dev->config->set_status(dev, dev->config->get_status(dev) | status); @@ -120,12 +113,16 @@ static int virtio_dev_probe(struct device *_d) set_bit(f, dev->features); } + /* Transport features always preserved to pass to finalize_features. */ + for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++) + if (device_features & (1 << i)) + set_bit(i, dev->features); + err = drv->probe(dev); if (err) add_status(dev, VIRTIO_CONFIG_S_FAILED); else { - /* They should never have set feature bits beyond 32 */ - dev->config->set_features(dev, dev->features[0]); + dev->config->finalize_features(dev); add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); } return err; @@ -147,13 +144,20 @@ static int virtio_dev_remove(struct device *_d) return 0; } +static struct bus_type virtio_bus = { + .name = "virtio", + .match = virtio_dev_match, + .dev_attrs = virtio_dev_attrs, + .uevent = virtio_uevent, + .probe = virtio_dev_probe, + .remove = virtio_dev_remove, +}; + int register_virtio_driver(struct virtio_driver *driver) { /* Catch this early. */ BUG_ON(driver->feature_table_size && !driver->feature_table); driver->driver.bus = &virtio_bus; - driver->driver.probe = virtio_dev_probe; - driver->driver.remove = virtio_dev_remove; return driver_register(&driver->driver); } EXPORT_SYMBOL_GPL(register_virtio_driver); diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index eae7236310e4..c7dc37c7cce9 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -94,12 +94,17 @@ static u32 vp_get_features(struct virtio_device *vdev) return ioread32(vp_dev->ioaddr + VIRTIO_PCI_HOST_FEATURES); } -/* virtio config->set_features() implementation */ -static void vp_set_features(struct virtio_device *vdev, u32 features) +/* virtio config->finalize_features() implementation */ +static void vp_finalize_features(struct virtio_device *vdev) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); - iowrite32(features, vp_dev->ioaddr + VIRTIO_PCI_GUEST_FEATURES); + /* Give virtio_ring a chance to accept features. */ + vring_transport_features(vdev); + + /* We only support 32 feature bits. */ + BUILD_BUG_ON(ARRAY_SIZE(vdev->features) != 1); + iowrite32(vdev->features[0], vp_dev->ioaddr+VIRTIO_PCI_GUEST_FEATURES); } /* virtio config->get() implementation */ @@ -297,7 +302,7 @@ static struct virtio_config_ops virtio_pci_config_ops = { .find_vq = vp_find_vq, .del_vq = vp_del_vq, .get_features = vp_get_features, - .set_features = vp_set_features, + .finalize_features = vp_finalize_features, }; /* the PCI probing function */ diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 72bf8bc09014..6eb5303fed11 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -18,6 +18,7 @@ */ #include <linux/virtio.h> #include <linux/virtio_ring.h> +#include <linux/virtio_config.h> #include <linux/device.h> #ifdef DEBUG @@ -87,8 +88,11 @@ static int vring_add_buf(struct virtqueue *_vq, if (vq->num_free < out + in) { pr_debug("Can't add buf len %i - avail = %i\n", out + in, vq->num_free); - /* We notify *even if* VRING_USED_F_NO_NOTIFY is set here. */ - vq->notify(&vq->vq); + /* FIXME: for historical reasons, we force a notify here if + * there are outgoing parts to the buffer. Presumably the + * host should service the ring ASAP. */ + if (out) + vq->notify(&vq->vq); END_USE(vq); return -ENOSPC; } @@ -320,4 +324,19 @@ void vring_del_virtqueue(struct virtqueue *vq) } EXPORT_SYMBOL_GPL(vring_del_virtqueue); +/* Manipulates transport-specific feature bits. */ +void vring_transport_features(struct virtio_device *vdev) +{ + unsigned int i; + + for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++) { + switch (i) { + default: + /* We don't understand this bit. */ + clear_bit(i, vdev->features); + } + } +} +EXPORT_SYMBOL_GPL(vring_transport_features); + MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index ccb78f66c2b6..48399e134c0d 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -788,8 +788,6 @@ config WATCHDOG_RIO machines. The watchdog timeout period is normally one minute but can be changed with a boot-time parameter. -# V850 Architecture - # XTENSA Architecture # diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 25b352b664d9..edd305a64e63 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -119,8 +119,6 @@ obj-$(CONFIG_SH_WDT) += shwdt.o # SPARC64 Architecture -# V850 Architecture - # XTENSA Architecture # Architecture Independant diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 591bc29b55f5..d4427cb86979 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -610,6 +610,7 @@ static ssize_t show_target_kb(struct sys_device *dev, char *buf) } static ssize_t store_target_kb(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { diff --git a/drivers/xen/events.c b/drivers/xen/events.c index 332dd63750a0..0e0c28574af8 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -734,6 +734,33 @@ static void restore_cpu_ipis(unsigned int cpu) } } +/* Clear an irq's pending state, in preparation for polling on it */ +void xen_clear_irq_pending(int irq) +{ + int evtchn = evtchn_from_irq(irq); + + if (VALID_EVTCHN(evtchn)) + clear_evtchn(evtchn); +} + +/* Poll waiting for an irq to become pending. In the usual case, the + irq will be disabled so it won't deliver an interrupt. */ +void xen_poll_irq(int irq) +{ + evtchn_port_t evtchn = evtchn_from_irq(irq); + + if (VALID_EVTCHN(evtchn)) { + struct sched_poll poll; + + poll.nr_ports = 1; + poll.timeout = 0; + poll.ports = &evtchn; + + if (HYPERVISOR_sched_op(SCHEDOP_poll, &poll) != 0) + BUG(); + } +} + void xen_irq_resume(void) { unsigned int cpu, irq, evtchn; diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index 5b546e365f00..a5bc91ae6ff6 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -63,11 +63,12 @@ static int xen_suspend(void *data) gnttab_resume(); xen_mm_unpin_all(); - device_power_up(); + device_power_up(PMSG_RESUME); if (!*cancelled) { xen_irq_resume(); xen_console_resume(); + xen_timer_resume(); } return 0; @@ -107,12 +108,13 @@ static void do_suspend(void) goto out; } - if (!cancelled) + if (!cancelled) { + xen_arch_resume(); xenbus_resume(); - else + } else xenbus_suspend_cancel(); - device_resume(); + device_resume(PMSG_RESUME); /* Make sure timer events get retriggered on all CPUs */ clock_was_set(); diff --git a/drivers/zorro/zorro-sysfs.c b/drivers/zorro/zorro-sysfs.c index 3da712cc7708..5290552d2ef7 100644 --- a/drivers/zorro/zorro-sysfs.c +++ b/drivers/zorro/zorro-sysfs.c @@ -15,7 +15,6 @@ #include <linux/zorro.h> #include <linux/stat.h> #include <linux/string.h> -#include <linux/fs.h> #include "zorro.h" |