diff -urpN linux-2.4.1-pre8/Documentation/Configure.help linux-2.4.1-pre8-irda-patch/Documentation/Configure.help --- linux-2.4.1-pre8/Documentation/Configure.help Mon Jan 22 00:53:05 2001 +++ linux-2.4.1-pre8-irda-patch/Documentation/Configure.help Mon Jan 22 00:56:35 2001 @@ -16848,6 +16848,16 @@ CONFIG_SMC_IRCC_FIR here and read Documentation/modules.txt. The module will be called smc-ircc.o. +ALi M5123 FIR Controller Driver +CONFIG_ALI_FIR + Say Y here if you want to build support for the ALi M5123 FIR + Controller. The ALi M5123 FIR Controller is embedded in ALi + M1543C, M1535, M1535D, M1535+, M1535D Sourth Bridge. This + driver supports SIR, MIR and FIR (4Mbps) speeds. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. The module will be called nsc-ircc.o. + Serial dongle support CONFIG_DONGLE Say Y here if you have an infrared device that connects to your diff -urpN linux-2.4.1-pre8/drivers/net/irda/Config.in linux-2.4.1-pre8-irda-patch/drivers/net/irda/Config.in --- linux-2.4.1-pre8/drivers/net/irda/Config.in Sat Jan 29 04:36:22 2000 +++ linux-2.4.1-pre8-irda-patch/drivers/net/irda/Config.in Mon Jan 22 00:54:52 2001 @@ -11,6 +11,7 @@ dep_tristate 'Winbond W83977AF (IR)' CON dep_tristate 'Toshiba Type-O IR Port' CONFIG_TOSHIBA_FIR $CONFIG_IRDA if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then dep_tristate 'SMC IrCC (Experimental)' CONFIG_SMC_IRCC_FIR $CONFIG_IRDA +dep_tristate 'ALi M5123 FIR (Experimental)' CONFIG_ALI_FIR $CONFIG_IRDA fi comment 'Dongle support' diff -urpN linux-2.4.1-pre8/drivers/net/irda/Makefile linux-2.4.1-pre8-irda-patch/drivers/net/irda/Makefile --- linux-2.4.1-pre8/drivers/net/irda/Makefile Fri Dec 29 23:07:22 2000 +++ linux-2.4.1-pre8-irda-patch/drivers/net/irda/Makefile Mon Jan 22 00:54:52 2001 @@ -16,6 +16,7 @@ obj-$(CONFIG_NSC_FIR) += nsc-ircc.o obj-$(CONFIG_WINBOND_FIR) += w83977af_ir.o obj-$(CONFIG_TOSHIBA_FIR) += toshoboe.o obj-$(CONFIG_SMC_IRCC_FIR) += smc-ircc.o irport.o +obj-$(CONFIG_ALI_FIR) += ali-ircc.o obj-$(CONFIG_ESI_DONGLE) += esi.o obj-$(CONFIG_TEKRAM_DONGLE) += tekram.o obj-$(CONFIG_ACTISYS_DONGLE) += actisys.o diff -urpN linux-2.4.1-pre8/drivers/net/irda/ali-ircc.c linux-2.4.1-pre8-irda-patch/drivers/net/irda/ali-ircc.c --- linux-2.4.1-pre8/drivers/net/irda/ali-ircc.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.1-pre8-irda-patch/drivers/net/irda/ali-ircc.c Mon Jan 22 01:08:29 2001 @@ -0,0 +1,2306 @@ +/********************************************************************* + * + * Filename: ali-ircc.h + * Version: 0.5 + * Description: Driver for the ALI M1535D and M1543C FIR Controller + * Status: Experimental. + * Author: Benjamin Kong + * Created at: 2000/10/16 03:46PM + * Modified at: 2001/1/3 02:55PM + * Modified by: Benjamin Kong + * + * Copyright (c) 2000 Benjamin Kong + * 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. + * + ********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#define CHIP_IO_EXTENT 8 +#define BROKEN_DONGLE_ID + +static char *driver_name = "ali-ircc"; + +/* Module parameters */ +static int qos_mtt_bits = 0x07; /* 1 ms or more */ + +/* Use BIOS settions by default, but user may supply module parameters */ +static unsigned int io[] = { ~0, ~0, ~0, ~0 }; +static unsigned int irq[] = { 0, 0, 0, 0 }; +static unsigned int dma[] = { 0, 0, 0, 0 }; + +static int ali_ircc_probe_43(ali_chip_t *chip, chipio_t *info); +static int ali_ircc_probe_53(ali_chip_t *chip, chipio_t *info); +static int ali_ircc_init_43(ali_chip_t *chip, chipio_t *info); +static int ali_ircc_init_53(ali_chip_t *chip, chipio_t *info); + +/* These are the currently known ALi sourth-bridge chipsets, the only one difference + * is that M1543C doesn't support HP HDSL-3600 + */ +static ali_chip_t chips[] = +{ + { "M1543", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x43, ali_ircc_probe_53, ali_ircc_init_43 }, + { "M1535", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x53, ali_ircc_probe_53, ali_ircc_init_53 }, + { NULL } +}; + +/* Max 4 instances for now */ +static struct ali_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL }; + +/* Dongle Types */ +static char *dongle_types[] = { + "TFDS6000", + "HP HSDL-3600", + "HP HSDL-1100", + "No dongle connected", +}; + +/* Some prototypes */ +static int ali_ircc_open(int i, chipio_t *info); + +#ifdef MODULE +static int ali_ircc_close(struct ali_ircc_cb *self); +#endif /* MODULE */ + +static int ali_ircc_setup(chipio_t *info); +static int ali_ircc_is_receiving(struct ali_ircc_cb *self); +static int ali_ircc_net_init(struct net_device *dev); +static int ali_ircc_net_open(struct net_device *dev); +static int ali_ircc_net_close(struct net_device *dev); +static int ali_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static int ali_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data); +static void ali_ircc_change_speed(struct ali_ircc_cb *self, __u32 baud); +static void ali_ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void ali_ircc_suspend(struct ali_ircc_cb *self); +static void ali_ircc_wakeup(struct ali_ircc_cb *self); +static struct net_device_stats *ali_ircc_net_get_stats(struct net_device *dev); + +/* SIR function */ +static int ali_ircc_sir_hard_xmit(struct sk_buff *skb, struct net_device *dev); +static void ali_ircc_sir_interrupt(int irq, struct ali_ircc_cb *self, struct pt_regs *regs); +static void ali_ircc_sir_receive(struct ali_ircc_cb *self); +static void ali_ircc_sir_write_wakeup(struct ali_ircc_cb *self); +static int ali_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len); +static void ali_ircc_sir_change_speed(struct ali_ircc_cb *priv, __u32 speed); + +/* FIR function */ +static int ali_ircc_fir_hard_xmit(struct sk_buff *skb, struct net_device *dev); +static void ali_ircc_fir_change_speed(struct ali_ircc_cb *priv, __u32 speed); +static void ali_ircc_fir_interrupt(int irq, struct ali_ircc_cb *self, struct pt_regs *regs); +static int ali_ircc_dma_receive(struct ali_ircc_cb *self); +static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self); +static int ali_ircc_dma_xmit_complete(struct ali_ircc_cb *self); +static void ali_ircc_dma_xmit(struct ali_ircc_cb *self); + +/* My Function */ +static int ali_ircc_read_dongle_id (int i, chipio_t *info); +static void ali_ircc_change_dongle_speed(struct ali_ircc_cb *priv, int speed); + +/* ALi chip function */ +static void SIR2FIR(int iobase); +static void FIR2SIR(int iobase); +static void SetCOMInterrupts(struct ali_ircc_cb *self , unsigned char enable); + +/* + * Function ali_ircc_init () + * + * Initialize chip. Find out whay kinds of chips we are dealing with + * and their configuation registers address + */ +int __init ali_ircc_init(void) +{ + ali_chip_t *chip; + chipio_t info; + int ret = -ENODEV; + int cfg, cfg_base; + int reg, revision; + int i = 0; + + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + /* Probe for all the ALi chipsets we know about */ + for (chip= chips; chip->name; chip++, i++) + { + IRDA_DEBUG(2, __FUNCTION__"(), Probing for %s ...\n", chip->name); + + /* Try all config registers for this chip */ + for (cfg=0; cfg<2; cfg++) + { + cfg_base = chip->cfg[cfg]; + if (!cfg_base) + continue; + + memset(&info, 0, sizeof(chipio_t)); + info.cfg_base = cfg_base; + info.fir_base = io[i]; + info.dma = dma[i]; + info.irq = irq[i]; + + + /* Enter Configuration */ + outb(chip->entr1, cfg_base); + outb(chip->entr2, cfg_base); + + /* Select Logical Device 5 Registers (UART2) */ + outb(0x07, cfg_base); + outb(0x05, cfg_base+1); + + /* Read Chip Identification Register */ + outb(chip->cid_index, cfg_base); + reg = inb(cfg_base+1); + + if (reg == chip->cid_value) + { + IRDA_DEBUG(2, __FUNCTION__ + "(), Chip found at 0x%03x\n", cfg_base); + + outb(0x1F, cfg_base); + revision = inb(cfg_base+1); + IRDA_DEBUG(2, __FUNCTION__ + "(), Found %s chip, revision=%d\n", + chip->name, revision); + + /* + * If the user supplies the base address, then + * we init the chip, if not we probe the values + * set by the BIOS + */ + if (io[i] < 2000) + { + chip->init(chip, &info); + } + else + { + chip->probe(chip, &info); + } + + if (ali_ircc_open(i, &info) == 0) + ret = 0; + i++; + } + else + { + IRDA_DEBUG(2, __FUNCTION__ + "(), No %s chip at 0x%03x\n", chip->name, cfg_base); + } + /* Exit configuration */ + outb(0xbb, cfg_base); + } + } + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End -----------------\n"); + return ret; +} + +/* + * Function ali_ircc_cleanup () + * + * Close all configured chips + * + */ +#ifdef MODULE +static void ali_ircc_cleanup(void) +{ + int i; + + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + pm_unregister_all(ali_ircc_pmproc); + + for (i=0; i < 4; i++) { + if (dev_self[i]) + ali_ircc_close(dev_self[i]); + } + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End -----------------\n"); +} +#endif /* MODULE */ + +/* + * Function ali_ircc_open (int i, chipio_t *inf) + * + * Open driver instance + * + */ +static int ali_ircc_open(int i, chipio_t *info) +{ + struct net_device *dev; + struct ali_ircc_cb *self; + struct pm_dev *pmdev; + int dongle_id; + int ret; + int err; + + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + /* Set FIR FIFO and DMA Threshold */ + if ((ali_ircc_setup(info)) == -1) + return -1; + + /* Allocate new instance of the driver */ + self = kmalloc(sizeof(struct ali_ircc_cb), GFP_KERNEL); + if (self == NULL) + { + ERROR(__FUNCTION__ "(), can't allocate memory for control block!\n"); + return -ENOMEM; + } + memset(self, 0, sizeof(struct ali_ircc_cb)); + spin_lock_init(&self->lock); + + /* Need to store self somewhere */ + dev_self[i] = self; + self->index = i; + + /* Initialize IO */ + self->io.cfg_base = info->cfg_base; /* In ali_ircc_probe_53 assign */ + self->io.fir_base = info->fir_base; /* info->sir_base = info->fir_base */ + self->io.sir_base = info->sir_base; /* ALi SIR and FIR use the same address */ + self->io.irq = info->irq; + self->io.fir_ext = CHIP_IO_EXTENT; + self->io.dma = info->dma; + self->io.fifo_size = 16; /* SIR: 16, FIR: 32 Benjamin 2000/11/1 */ + + /* Reserve the ioports that we need */ + ret = check_region(self->io.fir_base, self->io.fir_ext); + if (ret < 0) { + WARNING(__FUNCTION__ "(), can't get iobase of 0x%03x\n", + self->io.fir_base); + dev_self[i] = NULL; + kfree(self); + return -ENODEV; + } + request_region(self->io.fir_base, self->io.fir_ext, driver_name); + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&self->qos); + + /* The only value we must override it the baudrate */ + self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| + IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8); // benjamin 2000/11/8 05:27PM + + self->qos.min_turn_time.bits = qos_mtt_bits; + + irda_qos_bits_to_value(&self->qos); + + self->flags = IFF_FIR|IFF_MIR|IFF_SIR|IFF_DMA|IFF_PIO; // benjamin 2000/11/8 05:27PM + + /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ + self->rx_buff.truesize = 14384; + self->tx_buff.truesize = 14384; + + /* Allocate memory if needed */ + self->rx_buff.head = (__u8 *) kmalloc(self->rx_buff.truesize, + GFP_KERNEL |GFP_DMA); + if (self->rx_buff.head == NULL) + { + kfree(self); + return -ENOMEM; + } + memset(self->rx_buff.head, 0, self->rx_buff.truesize); + + self->tx_buff.head = (__u8 *) kmalloc(self->tx_buff.truesize, + GFP_KERNEL|GFP_DMA); + if (self->tx_buff.head == NULL) { + kfree(self); + kfree(self->rx_buff.head); + return -ENOMEM; + } + memset(self->tx_buff.head, 0, self->tx_buff.truesize); + + self->rx_buff.in_frame = FALSE; + self->rx_buff.state = OUTSIDE_FRAME; + self->tx_buff.data = self->tx_buff.head; + self->rx_buff.data = self->rx_buff.head; + + /* Reset Tx queue info */ + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; + + if (!(dev = dev_alloc("irda%d", &err))) { + ERROR(__FUNCTION__ "(), dev_alloc() failed!\n"); + return -ENOMEM; + } + + dev->priv = (void *) self; + self->netdev = dev; + + /* Override the network functions we need to use */ + dev->init = ali_ircc_net_init; + dev->hard_start_xmit = ali_ircc_sir_hard_xmit; + dev->open = ali_ircc_net_open; + dev->stop = ali_ircc_net_close; + dev->do_ioctl = ali_ircc_net_ioctl; + dev->get_stats = ali_ircc_net_get_stats; + + rtnl_lock(); + err = register_netdevice(dev); + rtnl_unlock(); + if (err) { + ERROR(__FUNCTION__ "(), register_netdev() failed!\n"); + return -1; + } + MESSAGE("IrDA: Registered device %s\n", dev->name); + + /* Check dongle id */ + dongle_id = ali_ircc_read_dongle_id(i, info); + MESSAGE(__FUNCTION__ "(), %s, Found dongle: %s\n", driver_name, dongle_types[dongle_id]); + + self->io.dongle_id = dongle_id; + + pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, ali_ircc_pmproc); + if (pmdev) + pmdev->data = self; + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End -----------------\n"); + + return 0; +} + + +#ifdef MODULE +/* + * Function ali_ircc_close (self) + * + * Close driver instance + * + */ +static int ali_ircc_close(struct ali_ircc_cb *self) +{ + int iobase; + + IRDA_DEBUG(4, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + ASSERT(self != NULL, return -1;); + + iobase = self->io.fir_base; + + /* Remove netdevice */ + if (self->netdev) { + rtnl_lock(); + unregister_netdevice(self->netdev); + rtnl_unlock(); + } + + /* Release the PORT that this driver is using */ + IRDA_DEBUG(4, __FUNCTION__ "(), Releasing Region %03x\n", self->io.fir_base); + release_region(self->io.fir_base, self->io.fir_ext); + + if (self->tx_buff.head) + kfree(self->tx_buff.head); + + if (self->rx_buff.head) + kfree(self->rx_buff.head); + + dev_self[self->index] = NULL; + kfree(self); + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End -----------------\n"); + + return 0; +} +#endif /* MODULE */ + +/* + * Function ali_ircc_init_43 (chip, info) + * + * Initialize the ALi M1543 chip. + */ +static int ali_ircc_init_43(ali_chip_t *chip, chipio_t *info) +{ + /* All controller information like I/O address, DMA channel, IRQ + * are set by BIOS + */ + + return 0; +} + +/* + * Function ali_ircc_init_53 (chip, info) + * + * Initialize the ALi M1535 chip. + */ +static int ali_ircc_init_53(ali_chip_t *chip, chipio_t *info) +{ + /* All controller information like I/O address, DMA channel, IRQ + * are set by BIOS + */ + + return 0; +} + +/* + * Function ali_ircc_probe_43 (chip, info) + * + * Probes for the ALi M1543 + */ +static int ali_ircc_probe_43(ali_chip_t *chip, chipio_t *info) +{ + return 0; +} + +/* + * Function ali_ircc_probe_53 (chip, info) + * + * Probes for the ALi M1535D or M1535 + */ +static int ali_ircc_probe_53(ali_chip_t *chip, chipio_t *info) +{ + int cfg_base = info->cfg_base; + int hi, low, reg; + + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + /* Enter Configuration */ + outb(chip->entr1, cfg_base); + outb(chip->entr2, cfg_base); + + /* Select Logical Device 5 Registers (UART2) */ + outb(0x07, cfg_base); + outb(0x05, cfg_base+1); + + /* Read address control register */ + outb(0x60, cfg_base); + hi = inb(cfg_base+1); + outb(0x61, cfg_base); + low = inb(cfg_base+1); + info->fir_base = (hi<<8) + low; + + info->sir_base = info->fir_base; + + IRDA_DEBUG(2, __FUNCTION__ "(), probing fir_base=0x%03x\n", info->fir_base); + + /* Read IRQ control register */ + outb(0x70, cfg_base); + reg = inb(cfg_base+1); + info->irq = reg & 0x0f; + IRDA_DEBUG(2, __FUNCTION__ "(), probing irq=%d\n", info->irq); + + /* Read DMA channel */ + outb(0x74, cfg_base); + reg = inb(cfg_base+1); + info->dma = reg & 0x07; + + if(info->dma == 0x04) + WARNING(__FUNCTION__ "(), No DMA channel assigned !\n"); + else + IRDA_DEBUG(2, __FUNCTION__ "(), probing dma=%d\n", info->dma); + + /* Read Enabled Status */ + outb(0x30, cfg_base); + reg = inb(cfg_base+1); + info->enabled = (reg & 0x80) && (reg & 0x01); + IRDA_DEBUG(2, __FUNCTION__ "(), probing enabled=%d\n", info->enabled); + + /* Read Power Status */ + outb(0x22, cfg_base); + reg = inb(cfg_base+1); + info->suspended = (reg & 0x20); + IRDA_DEBUG(2, __FUNCTION__ "(), probing suspended=%d\n", info->suspended); + + /* Exit configuration */ + outb(0xbb, cfg_base); + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End -----------------\n"); + + return 0; +} + +/* + * Function ali_ircc_setup (info) + * + * Set FIR FIFO and DMA Threshold + * Returns non-negative on success. + * + */ +static int ali_ircc_setup(chipio_t *info) +{ + unsigned char tmp; + int version; + int iobase = info->fir_base; + + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + /* Switch to FIR space */ + SIR2FIR(iobase); + + /* Master Reset */ + outb(0x40, iobase+FIR_MCR); // benjamin 2000/11/30 11:45AM + + /* Read FIR ID Version Register */ + switch_bank(iobase, BANK3); + version = inb(iobase+FIR_ID_VR); + + /* Should be 0x00 in the M1535/M1535D */ + if(version != 0x00) + { + ERROR("%s, Wrong chip version %02x\n", driver_name, version); + return -1; + } + + // MESSAGE("%s, Found chip at base=0x%03x\n", driver_name, info->cfg_base); + + /* Set FIR FIFO Threshold Register */ + switch_bank(iobase, BANK1); + outb(RX_FIFO_Threshold, iobase+FIR_FIFO_TR); + + /* Set FIR DMA Threshold Register */ + outb(RX_DMA_Threshold, iobase+FIR_DMA_TR); + + /* CRC enable */ + switch_bank(iobase, BANK2); + outb(inb(iobase+FIR_IRDA_CR) | IRDA_CR_CRC, iobase+FIR_IRDA_CR); + + /* NDIS driver set TX Length here BANK2 Alias 3, Alias4*/ + + /* Switch to Bank 0 */ + switch_bank(iobase, BANK0); + + tmp = inb(iobase+FIR_LCR_B); + tmp &=~0x20; // disable SIP + tmp |= 0x80; // these two steps make RX mode + tmp &= 0xbf; + outb(tmp, iobase+FIR_LCR_B); + + /* Disable Interrupt */ + outb(0x00, iobase+FIR_IER); + + + /* Switch to SIR space */ + FIR2SIR(iobase); + + MESSAGE("%s, driver loaded (Benjamin Kong)\n", driver_name); + + /* Enable receive interrupts */ + // outb(UART_IER_RDI, iobase+UART_IER); //benjamin 2000/11/23 01:25PM + // Turn on the interrupts in ali_ircc_net_open + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End ------------------\n"); + + return 0; +} + +/* + * Function ali_ircc_read_dongle_id (int index, info) + * + * Try to read dongle indentification. This procedure needs to be executed + * once after power-on/reset. It also needs to be used whenever you suspect + * that the user may have plugged/unplugged the IrDA Dongle. + */ +static int ali_ircc_read_dongle_id (int i, chipio_t *info) +{ + int dongle_id, reg; + int cfg_base = info->cfg_base; + + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + /* Enter Configuration */ + outb(chips[i].entr1, cfg_base); + outb(chips[i].entr2, cfg_base); + + /* Select Logical Device 5 Registers (UART2) */ + outb(0x07, cfg_base); + outb(0x05, cfg_base+1); + + /* Read Dongle ID */ + outb(0xf0, cfg_base); + reg = inb(cfg_base+1); + dongle_id = ((reg>>6)&0x02) | ((reg>>5)&0x01); + IRDA_DEBUG(2, __FUNCTION__ "(), probing dongle_id=%d, dongle_types=%s\n", + dongle_id, dongle_types[dongle_id]); + + /* Exit configuration */ + outb(0xbb, cfg_base); + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End ------------------\n"); + + return dongle_id; +} + +/* + * Function ali_ircc_interrupt (irq, dev_id, regs) + * + * An interrupt from the chip has arrived. Time to do some work + * + */ +static void ali_ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct ali_ircc_cb *self; + + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + if (!dev) { + WARNING("%s: irq %d for unknown device.\n", driver_name, irq); + return; + } + + self = (struct ali_ircc_cb *) dev->priv; + + spin_lock(&self->lock); + + /* Dispatch interrupt handler for the current speed */ + if (self->io.speed > 115200) + ali_ircc_fir_interrupt(irq, self, regs); + else + ali_ircc_sir_interrupt(irq, self, regs); + + spin_unlock(&self->lock); + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End ------------------\n"); +} +/* + * Function ali_ircc_fir_interrupt(irq, struct ali_ircc_cb *self, regs) + * + * Handle MIR/FIR interrupt + * + */ +static void ali_ircc_fir_interrupt(int irq, struct ali_ircc_cb *self, struct pt_regs *regs) +{ + __u8 eir, OldMessageCount; + int iobase, tmp; + + IRDA_DEBUG(1, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + iobase = self->io.fir_base; + + switch_bank(iobase, BANK0); + self->InterruptID = inb(iobase+FIR_IIR); + self->BusStatus = inb(iobase+FIR_BSR); + + OldMessageCount = (self->LineStatus + 1) & 0x07; + self->LineStatus = inb(iobase+FIR_LSR); + //self->ier = inb(iobase+FIR_IER); 2000/12/1 04:32PM + eir = self->InterruptID & self->ier; /* Mask out the interesting ones */ + + IRDA_DEBUG(1, __FUNCTION__ "(), self->InterruptID = %x\n",self->InterruptID); + IRDA_DEBUG(1, __FUNCTION__ "(), self->LineStatus = %x\n",self->LineStatus); + IRDA_DEBUG(1, __FUNCTION__ "(), self->ier = %x\n",self->ier); + IRDA_DEBUG(1, __FUNCTION__ "(), eir = %x\n",eir); + + /* Disable interrupts */ + SetCOMInterrupts(self, FALSE); + + /* Tx or Rx Interrupt */ + + if (eir & IIR_EOM) + { + if (self->io.direction == IO_XMIT) /* TX */ + { + IRDA_DEBUG(1, __FUNCTION__ "(), ******* IIR_EOM (Tx) *******\n"); + + if(ali_ircc_dma_xmit_complete(self)) + { + if (irda_device_txqueue_empty(self->netdev)) + { + /* Prepare for receive */ + ali_ircc_dma_receive(self); + self->ier = IER_EOM; + } + } + else + { + self->ier = IER_EOM; + } + + } + else /* RX */ + { + IRDA_DEBUG(1, __FUNCTION__ "(), ******* IIR_EOM (Rx) *******\n"); + + if(OldMessageCount > ((self->LineStatus+1) & 0x07)) + { + self->rcvFramesOverflow = TRUE; + IRDA_DEBUG(1, __FUNCTION__ "(), ******* self->rcvFramesOverflow = TRUE ******** \n"); + } + + if (ali_ircc_dma_receive_complete(self)) + { + IRDA_DEBUG(1, __FUNCTION__ "(), ******* receive complete ******** \n"); + + self->ier = IER_EOM; + } + else + { + IRDA_DEBUG(1, __FUNCTION__ "(), ******* Not receive complete ******** \n"); + + self->ier = IER_EOM | IER_TIMER; + } + + } + } + /* Timer Interrupt */ + else if (eir & IIR_TIMER) + { + if(OldMessageCount > ((self->LineStatus+1) & 0x07)) + { + self->rcvFramesOverflow = TRUE; + IRDA_DEBUG(1, __FUNCTION__ "(), ******* self->rcvFramesOverflow = TRUE ******* \n"); + } + /* Disable Timer */ + switch_bank(iobase, BANK1); + tmp = inb(iobase+FIR_CR); + outb( tmp& ~CR_TIMER_EN, iobase+FIR_CR); + + /* Check if this is a Tx timer interrupt */ + if (self->io.direction == IO_XMIT) + { + ali_ircc_dma_xmit(self); + + /* Interrupt on EOM */ + self->ier = IER_EOM; + + } + else /* Rx */ + { + if(ali_ircc_dma_receive_complete(self)) + { + self->ier = IER_EOM; + } + else + { + self->ier = IER_EOM | IER_TIMER; + } + } + } + + /* Restore Interrupt */ + SetCOMInterrupts(self, TRUE); + + IRDA_DEBUG(1, __FUNCTION__ "(), ----------------- End ---------------\n"); +} + +/* + * Function ali_ircc_sir_interrupt (irq, self, eir) + * + * Handle SIR interrupt + * + */ +static void ali_ircc_sir_interrupt(int irq, struct ali_ircc_cb *self, struct pt_regs *regs) +{ + int iobase; + int iir, lsr; + + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + iobase = self->io.sir_base; + + iir = inb(iobase+UART_IIR) & UART_IIR_ID; + if (iir) { + /* Clear interrupt */ + lsr = inb(iobase+UART_LSR); + + IRDA_DEBUG(4, __FUNCTION__ + "(), iir=%02x, lsr=%02x, iobase=%#x\n", + iir, lsr, iobase); + + switch (iir) + { + case UART_IIR_RLSI: + IRDA_DEBUG(2, __FUNCTION__ "(), RLSI\n"); + break; + case UART_IIR_RDI: + /* Receive interrupt */ + ali_ircc_sir_receive(self); + break; + case UART_IIR_THRI: + if (lsr & UART_LSR_THRE) + { + /* Transmitter ready for data */ + ali_ircc_sir_write_wakeup(self); + } + break; + default: + IRDA_DEBUG(0, __FUNCTION__ "(), unhandled IIR=%#x\n", iir); + break; + } + + } + + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End ------------------\n"); +} + + +/* + * Function ali_ircc_sir_receive (self) + * + * Receive one frame from the infrared port + * + */ +static void ali_ircc_sir_receive(struct ali_ircc_cb *self) +{ + int boguscount = 0; + int iobase; + + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start ----------------\n"); + ASSERT(self != NULL, return;); + + iobase = self->io.sir_base; + + /* + * Receive all characters in Rx FIFO, unwrap and unstuff them. + * async_unwrap_char will deliver all found frames + */ + do { + async_unwrap_char(self->netdev, &self->stats, &self->rx_buff, + inb(iobase+UART_RX)); + + /* Make sure we don't stay here to long */ + if (boguscount++ > 32) { + IRDA_DEBUG(2,__FUNCTION__ "(), breaking!\n"); + break; + } + } while (inb(iobase+UART_LSR) & UART_LSR_DR); + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End ------------------\n"); +} + +/* + * Function ali_ircc_sir_write_wakeup (tty) + * + * Called by the driver when there's room for more data. If we have + * more packets to send, we send them here. + * + */ +static void ali_ircc_sir_write_wakeup(struct ali_ircc_cb *self) +{ + int actual = 0; + int iobase; + + ASSERT(self != NULL, return;); + + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + iobase = self->io.sir_base; + + /* Finished with frame? */ + if (self->tx_buff.len > 0) + { + /* Write data left in transmit buffer */ + actual = ali_ircc_sir_write(iobase, self->io.fifo_size, + self->tx_buff.data, self->tx_buff.len); + self->tx_buff.data += actual; + self->tx_buff.len -= actual; + } + else + { + if (self->new_speed) + { + /* We must wait until all data are gone */ + while(!(inb(iobase+UART_LSR) & UART_LSR_TEMT)) + IRDA_DEBUG(1, __FUNCTION__ "(), UART_LSR_THRE\n"); + + IRDA_DEBUG(1, __FUNCTION__ "(), Changing speed! self->new_speed = %d\n", self->new_speed); + ali_ircc_change_speed(self, self->new_speed); + self->new_speed = 0; + + // benjamin 2000/11/10 06:32PM + if (self->io.speed > 115200) + { + IRDA_DEBUG(2, __FUNCTION__ "(), ali_ircc_change_speed from UART_LSR_TEMT \n"); + + self->ier = IER_EOM; + // SetCOMInterrupts(self, TRUE); + return; + } + } + else + { + netif_wake_queue(self->netdev); + } + + self->stats.tx_packets++; + + /* Turn on receive interrupts */ + outb(UART_IER_RDI, iobase+UART_IER); + } + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End ------------------\n"); +} + +static void ali_ircc_change_speed(struct ali_ircc_cb *self, __u32 baud) +{ + struct net_device *dev = self->netdev; + int iobase; + + IRDA_DEBUG(1, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + IRDA_DEBUG(2, __FUNCTION__ "(), setting speed = %d \n", baud); + + iobase = self->io.fir_base; + + SetCOMInterrupts(self, FALSE); // 2000/11/24 11:43AM + + /* Go to MIR, FIR Speed */ + if (baud > 115200) + { + + + ali_ircc_fir_change_speed(self, baud); + + /* Install FIR xmit handler*/ + dev->hard_start_xmit = ali_ircc_fir_hard_xmit; + + /* Enable Interuupt */ + self->ier = IER_EOM; // benjamin 2000/11/20 07:24PM + + /* Be ready for incomming frames */ + ali_ircc_dma_receive(self); // benajmin 2000/11/8 07:46PM not complete + } + /* Go to SIR Speed */ + else + { + ali_ircc_sir_change_speed(self, baud); + + /* Install SIR xmit handler*/ + dev->hard_start_xmit = ali_ircc_sir_hard_xmit; + } + + + SetCOMInterrupts(self, TRUE); // 2000/11/24 11:43AM + + netif_wake_queue(self->netdev); + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End ------------------\n"); +} + +static void ali_ircc_fir_change_speed(struct ali_ircc_cb *priv, __u32 baud) +{ + + int iobase; + struct ali_ircc_cb *self = (struct ali_ircc_cb *) priv; + struct net_device *dev; + + IRDA_DEBUG(1, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + ASSERT(self != NULL, return;); + + dev = self->netdev; + iobase = self->io.fir_base; + + IRDA_DEBUG(1, __FUNCTION__ "(), self->io.speed = %d, change to speed = %d\n",self->io.speed,baud); + + /* Come from SIR speed */ + if(self->io.speed <=115200) + { + SIR2FIR(iobase); + } + + /* Update accounting for new speed */ + self->io.speed = baud; + + // Set Dongle Speed mode + ali_ircc_change_dongle_speed(self, baud); + + IRDA_DEBUG(1, __FUNCTION__ "(), ----------------- End ------------------\n"); +} + +/* + * Function ali_sir_change_speed (self, speed) + * + * Set speed of IrDA port to specified baudrate + * + */ +static void ali_ircc_sir_change_speed(struct ali_ircc_cb *priv, __u32 speed) +{ + struct ali_ircc_cb *self = (struct ali_ircc_cb *) priv; + unsigned long flags; + int iobase; + int fcr; /* FIFO control reg */ + int lcr; /* Line control reg */ + int divisor; + + IRDA_DEBUG(1, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + IRDA_DEBUG(1, __FUNCTION__ "(), Setting speed to: %d\n", speed); + + ASSERT(self != NULL, return;); + + iobase = self->io.sir_base; + + /* Come from MIR or FIR speed */ + if(self->io.speed >115200) + { + // Set Dongle Speed mode first + ali_ircc_change_dongle_speed(self, speed); + + FIR2SIR(iobase); + } + + // Clear Line and Auxiluary status registers 2000/11/24 11:47AM + + inb(iobase+UART_LSR); + inb(iobase+UART_SCR); + + /* Update accounting for new speed */ + self->io.speed = speed; + + spin_lock_irqsave(&self->lock, flags); + + divisor = 115200/speed; + + fcr = UART_FCR_ENABLE_FIFO; + + /* + * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and + * almost 1,7 ms at 19200 bps. At speeds above that we can just forget + * about this timeout since it will always be fast enough. + */ + if (self->io.speed < 38400) + fcr |= UART_FCR_TRIGGER_1; + else + fcr |= UART_FCR_TRIGGER_14; + + /* IrDA ports use 8N1 */ + lcr = UART_LCR_WLEN8; + + outb(UART_LCR_DLAB | lcr, iobase+UART_LCR); /* Set DLAB */ + outb(divisor & 0xff, iobase+UART_DLL); /* Set speed */ + outb(divisor >> 8, iobase+UART_DLM); + outb(lcr, iobase+UART_LCR); /* Set 8N1 */ + outb(fcr, iobase+UART_FCR); /* Enable FIFO's */ + + /* without this, the conection will be broken after come back from FIR speed, + but with this, the SIR connection is harder to established */ + outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase+UART_MCR); + + spin_unlock_irqrestore(&self->lock, flags); + + IRDA_DEBUG(1, __FUNCTION__ "(), ----------------- End ------------------\n"); +} + +static void ali_ircc_change_dongle_speed(struct ali_ircc_cb *priv, int speed) +{ + + struct ali_ircc_cb *self = (struct ali_ircc_cb *) priv; + int iobase,dongle_id; + unsigned long flags; + int tmp = 0; + + IRDA_DEBUG(1, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + iobase = self->io.fir_base; /* or iobase = self->io.sir_base; */ + dongle_id = self->io.dongle_id; + + save_flags(flags); + cli(); + + IRDA_DEBUG(1, __FUNCTION__ "(), Set Speed for %s , Speed = %d\n", dongle_types[dongle_id], speed); + + switch_bank(iobase, BANK2); + tmp = inb(iobase+FIR_IRDA_CR); + + /* IBM type dongle */ + if(dongle_id == 0) + { + if(speed == 4000000) + { + // __ __ + // SD/MODE __| |__ __ + // __ __ + // IRTX __ __| |__ + // T1 T2 T3 T4 T5 + + tmp &= ~IRDA_CR_HDLC; // HDLC=0 + tmp |= IRDA_CR_CRC; // CRC=1 + + switch_bank(iobase, BANK2); + outb(tmp, iobase+FIR_IRDA_CR); + + // T1 -> SD/MODE:0 IRTX:0 + tmp &= ~0x09; + tmp |= 0x02; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // T2 -> SD/MODE:1 IRTX:0 + tmp &= ~0x01; + tmp |= 0x0a; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // T3 -> SD/MODE:1 IRTX:1 + tmp |= 0x0b; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // T4 -> SD/MODE:0 IRTX:1 + tmp &= ~0x08; + tmp |= 0x03; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // T5 -> SD/MODE:0 IRTX:0 + tmp &= ~0x09; + tmp |= 0x02; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // reset -> Normal TX output Signal + outb(tmp & ~0x02, iobase+FIR_IRDA_CR); + } + else /* speed <=1152000 */ + { + // __ + // SD/MODE __| |__ + // + // IRTX ________ + // T1 T2 T3 + + /* MIR 115200, 57600 */ + if (speed==1152000) + { + tmp |= 0xA0; //HDLC=1, 1.152Mbps=1 + } + else + { + tmp &=~0x80; //HDLC 0.576Mbps + tmp |= 0x20; //HDLC=1, + } + + tmp |= IRDA_CR_CRC; // CRC=1 + + switch_bank(iobase, BANK2); + outb(tmp, iobase+FIR_IRDA_CR); + + /* MIR 115200, 57600 */ + + //switch_bank(iobase, BANK2); + // T1 -> SD/MODE:0 IRTX:0 + tmp &= ~0x09; + tmp |= 0x02; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // T2 -> SD/MODE:1 IRTX:0 + tmp &= ~0x01; + tmp |= 0x0a; + outb(tmp, iobase+FIR_IRDA_CR); + + // T3 -> SD/MODE:0 IRTX:0 + tmp &= ~0x09; + tmp |= 0x02; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // reset -> Normal TX output Signal + outb(tmp & ~0x02, iobase+FIR_IRDA_CR); + } + } + else if (dongle_id == 1) /* HP HDSL-3600 */ + { + switch(speed) + { + case 4000000: + tmp &= ~IRDA_CR_HDLC; // HDLC=0 + break; + + case 1152000: + tmp |= 0xA0; // HDLC=1, 1.152Mbps=1 + break; + + case 576000: + tmp &=~0x80; // HDLC 0.576Mbps + tmp |= 0x20; // HDLC=1, + break; + } + + tmp |= IRDA_CR_CRC; // CRC=1 + + switch_bank(iobase, BANK2); + outb(tmp, iobase+FIR_IRDA_CR); + } + else /* HP HDSL-1100 */ + { + if(speed <= 115200) /* SIR */ + { + + tmp &= ~IRDA_CR_FIR_SIN; // HP sin select = 0 + + switch_bank(iobase, BANK2); + outb(tmp, iobase+FIR_IRDA_CR); + } + else /* MIR FIR */ + { + + switch(speed) + { + case 4000000: + tmp &= ~IRDA_CR_HDLC; // HDLC=0 + break; + + case 1152000: + tmp |= 0xA0; // HDLC=1, 1.152Mbps=1 + break; + + case 576000: + tmp &=~0x80; // HDLC 0.576Mbps + tmp |= 0x20; // HDLC=1, + break; + } + + tmp |= IRDA_CR_CRC; // CRC=1 + tmp |= IRDA_CR_FIR_SIN; // HP sin select = 1 + + switch_bank(iobase, BANK2); + outb(tmp, iobase+FIR_IRDA_CR); + } + } + + switch_bank(iobase, BANK0); + + restore_flags(flags); + + IRDA_DEBUG(1, __FUNCTION__ "(), ----------------- End ------------------\n"); +} + +/* + * Function ali_ircc_sir_write (driver) + * + * Fill Tx FIFO with transmit data + * + */ +static int ali_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len) +{ + int actual = 0; + + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + /* Tx FIFO should be empty! */ + if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) { + IRDA_DEBUG(0, __FUNCTION__ "(), failed, fifo not empty!\n"); + return 0; + } + + /* Fill FIFO with current frame */ + while ((fifo_size-- > 0) && (actual < len)) { + /* Transmit next byte */ + outb(buf[actual], iobase+UART_TX); + + actual++; + } + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End ------------------\n"); + return actual; +} + +/* + * Function ali_ircc_net_init (dev) + * + * Initialize network device + * + */ +static int ali_ircc_net_init(struct net_device *dev) +{ + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + /* Setup to be a normal IrDA network device driver */ + irda_device_setup(dev); + + /* Insert overrides below this line! */ + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End ------------------\n"); + + return 0; +} + +/* + * Function ali_ircc_net_open (dev) + * + * Start the device + * + */ +static int ali_ircc_net_open(struct net_device *dev) +{ + struct ali_ircc_cb *self; + int iobase; + + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + ASSERT(dev != NULL, return -1;); + + self = (struct ali_ircc_cb *) dev->priv; + + ASSERT(self != NULL, return 0;); + + iobase = self->io.fir_base; + + /* Request IRQ and install Interrupt Handler */ + if (request_irq(self->io.irq, ali_ircc_interrupt, 0, dev->name, dev)) + { + WARNING("%s, unable to allocate irq=%d\n", driver_name, + self->io.irq); + return -EAGAIN; + } + + /* + * Always allocate the DMA channel after the IRQ, and clean up on + * failure. + */ + if (request_dma(self->io.dma, dev->name)) { + WARNING("%s, unable to allocate dma=%d\n", driver_name, + self->io.dma); + free_irq(self->io.irq, self); + return -EAGAIN; + } + + /* Turn on interrups */ + outb(UART_IER_RLSI | UART_IER_RDI |UART_IER_THRI, iobase+UART_IER); + + /* Ready to play! */ + netif_start_queue(dev); //benjamin by irport + + /* + * Open new IrLAP layer instance, now that everything should be + * initialized properly + */ + self->irlap = irlap_open(dev, &self->qos, "ALI-FIR"); + + MOD_INC_USE_COUNT; + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End ------------------\n"); + + return 0; +} + +/* + * Function ali_ircc_net_close (dev) + * + * Stop the device + * + */ +static int ali_ircc_net_close(struct net_device *dev) +{ + + struct ali_ircc_cb *self; + //int iobase; + + IRDA_DEBUG(4, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + ASSERT(dev != NULL, return -1;); + + self = (struct ali_ircc_cb *) dev->priv; + ASSERT(self != NULL, return 0;); + + /* Stop device */ + netif_stop_queue(dev); + + /* Stop and remove instance of IrLAP */ + if (self->irlap) + irlap_close(self->irlap); + self->irlap = NULL; + + disable_dma(self->io.dma); + + /* Disable interrupts */ + SetCOMInterrupts(self, FALSE); + + free_irq(self->io.irq, dev); + free_dma(self->io.dma); + + MOD_DEC_USE_COUNT; + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End ------------------\n"); + + return 0; +} + +/* + * Function ali_ircc_fir_hard_xmit (skb, dev) + * + * Transmit the frame + * + */ +static int ali_ircc_fir_hard_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ali_ircc_cb *self; + unsigned long flags; + int iobase; + __u32 speed; + int mtt, diff; + + IRDA_DEBUG(1, __FUNCTION__ "(), ---------------- Start -----------------\n"); + + self = (struct ali_ircc_cb *) dev->priv; + iobase = self->io.fir_base; + + netif_stop_queue(dev); + + /* Check if we need to change the speed */ + speed = irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) { + /* Check for empty frame */ + if (!skb->len) { + ali_ircc_change_speed(self, speed); + dev_kfree_skb(skb); + return 0; + } else + self->new_speed = speed; + } + + spin_lock_irqsave(&self->lock, flags); + + /* Register and copy this frame to DMA memory */ + self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail; + self->tx_fifo.queue[self->tx_fifo.free].len = skb->len; + self->tx_fifo.tail += skb->len; + + self->stats.tx_bytes += skb->len; + + memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data, + skb->len); + + self->tx_fifo.len++; + self->tx_fifo.free++; + + /* Start transmit only if there is currently no transmit going on */ + if (self->tx_fifo.len == 1) + { + /* Check if we must wait the min turn time or not */ + mtt = irda_get_mtt(skb); + + if (mtt) + { + /* Check how much time we have used already */ + get_fast_time(&self->now); + + diff = self->now.tv_usec - self->stamp.tv_usec; + /* self->stamp is set from ali_ircc_dma_receive_complete() */ + + IRDA_DEBUG(1, __FUNCTION__ "(), ******* diff = %d ******* \n", diff); + + if (diff < 0) + diff += 1000000; + + /* Check if the mtt is larger than the time we have + * already used by all the protocol processing + */ + if (mtt > diff) + { + mtt -= diff; + + /* + * Use timer if delay larger than 1000 us, and + * use udelay for smaller values which should + * be acceptable + */ + if (mtt > 500) + { + /* Adjust for timer resolution */ + mtt = (mtt+250) / 500; /* 4 discard, 5 get advanced, Let's round off */ + + IRDA_DEBUG(1, __FUNCTION__ "(), ************** mtt = %d ***********\n", mtt); + + /* Setup timer */ + if (mtt == 1) /* 500 us */ + { + switch_bank(iobase, BANK1); + outb(TIMER_IIR_500, iobase+FIR_TIMER_IIR); + } + else if (mtt == 2) /* 1 ms */ + { + switch_bank(iobase, BANK1); + outb(TIMER_IIR_1ms, iobase+FIR_TIMER_IIR); + } + else /* > 2ms -> 4ms */ + { + switch_bank(iobase, BANK1); + outb(TIMER_IIR_2ms, iobase+FIR_TIMER_IIR); + } + + + /* Start timer */ + outb(inb(iobase+FIR_CR) | CR_TIMER_EN, iobase+FIR_CR); + self->io.direction = IO_XMIT; + + /* Enable timer interrupt */ + self->ier = IER_TIMER; + SetCOMInterrupts(self, TRUE); + + /* Timer will take care of the rest */ + goto out; + } + else + udelay(mtt); + } // if (if (mtt > diff) + }// if (mtt) + + /* Enable EOM interrupt */ + self->ier = IER_EOM; + SetCOMInterrupts(self, TRUE); + + /* Transmit frame */ + ali_ircc_dma_xmit(self); + } // if (self->tx_fifo.len == 1) + + out: + + /* Not busy transmitting anymore if window is not full */ + if (self->tx_fifo.free < MAX_TX_WINDOW) + netif_wake_queue(self->netdev); + + /* Restore bank register */ + switch_bank(iobase, BANK0); + + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_skb(skb); + + IRDA_DEBUG(1, __FUNCTION__ "(), ----------------- End ------------------\n"); + return 0; +} + + +static void ali_ircc_dma_xmit(struct ali_ircc_cb *self) +{ + int iobase, tmp; + unsigned char FIFO_OPTI, Hi, Lo; + + + IRDA_DEBUG(1, __FUNCTION__ "(), ---------------- Start -----------------\n"); + + iobase = self->io.fir_base; + + /* FIFO threshold , this method comes from NDIS5 code */ + + if(self->tx_fifo.queue[self->tx_fifo.ptr].len < TX_FIFO_Threshold) + FIFO_OPTI = self->tx_fifo.queue[self->tx_fifo.ptr].len-1; + else + FIFO_OPTI = TX_FIFO_Threshold; + + /* Disable DMA */ + switch_bank(iobase, BANK1); + outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR); + + self->io.direction = IO_XMIT; + + setup_dma(self->io.dma, + self->tx_fifo.queue[self->tx_fifo.ptr].start, + self->tx_fifo.queue[self->tx_fifo.ptr].len, + DMA_TX_MODE); + + /* Reset Tx FIFO */ + switch_bank(iobase, BANK0); + outb(LCR_A_FIFO_RESET, iobase+FIR_LCR_A); + + /* Set Tx FIFO threshold */ + if (self->fifo_opti_buf!=FIFO_OPTI) + { + switch_bank(iobase, BANK1); + outb(FIFO_OPTI, iobase+FIR_FIFO_TR) ; + self->fifo_opti_buf=FIFO_OPTI; + } + + /* Set Tx DMA threshold */ + switch_bank(iobase, BANK1); + outb(TX_DMA_Threshold, iobase+FIR_DMA_TR); + + /* Set max Tx frame size */ + Hi = (self->tx_fifo.queue[self->tx_fifo.ptr].len >> 8) & 0x0f; + Lo = self->tx_fifo.queue[self->tx_fifo.ptr].len & 0xff; + switch_bank(iobase, BANK2); + outb(Hi, iobase+FIR_TX_DSR_HI); + outb(Lo, iobase+FIR_TX_DSR_LO); + + /* Disable SIP , Disable Brick Wall (we don't support in TX mode), Change to TX mode */ + switch_bank(iobase, BANK0); + tmp = inb(iobase+FIR_LCR_B); + tmp &= ~0x20; // Disable SIP + outb(((unsigned char)(tmp & 0x3f) | LCR_B_TX_MODE) & ~LCR_B_BW, iobase+FIR_LCR_B); + IRDA_DEBUG(1, __FUNCTION__ "(), ******* Change to TX mode: FIR_LCR_B = 0x%x ******* \n", inb(iobase+FIR_LCR_B)); + + outb(0, iobase+FIR_LSR); + + /* Enable DMA and Burst Mode */ + switch_bank(iobase, BANK1); + outb(inb(iobase+FIR_CR) | CR_DMA_EN | CR_DMA_BURST, iobase+FIR_CR); + + switch_bank(iobase, BANK0); + + IRDA_DEBUG(1, __FUNCTION__ "(), ----------------- End ------------------\n"); +} + +static int ali_ircc_dma_xmit_complete(struct ali_ircc_cb *self) +{ + int iobase; + int ret = TRUE; + + IRDA_DEBUG(1, __FUNCTION__ "(), ---------------- Start -----------------\n"); + + iobase = self->io.fir_base; + + /* Disable DMA */ + switch_bank(iobase, BANK1); + outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR); + + /* Check for underrun! */ + switch_bank(iobase, BANK0); + if((inb(iobase+FIR_LSR) & LSR_FRAME_ABORT) == LSR_FRAME_ABORT) + + { + ERROR(__FUNCTION__ "(), ********* LSR_FRAME_ABORT *********\n"); + self->stats.tx_errors++; + self->stats.tx_fifo_errors++; + } + else + { + self->stats.tx_packets++; + } + + /* Check if we need to change the speed */ + if (self->new_speed) + { + ali_ircc_change_speed(self, self->new_speed); + self->new_speed = 0; + } + + /* Finished with this frame, so prepare for next */ + self->tx_fifo.ptr++; + self->tx_fifo.len--; + + /* Any frames to be sent back-to-back? */ + if (self->tx_fifo.len) + { + ali_ircc_dma_xmit(self); + + /* Not finished yet! */ + ret = FALSE; + } + else + { /* Reset Tx FIFO info */ + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; + } + + /* Make sure we have room for more frames */ + if (self->tx_fifo.free < MAX_TX_WINDOW) { + /* Not busy transmitting anymore */ + /* Tell the network layer, that we can accept more frames */ + netif_wake_queue(self->netdev); + } + + switch_bank(iobase, BANK0); + + IRDA_DEBUG(1, __FUNCTION__ "(), ----------------- End ------------------\n"); + return ret; +} + +/* + * Function ali_ircc_dma_receive (self) + * + * Get ready for receiving a frame. The device will initiate a DMA + * if it starts to receive a frame. + * + */ +static int ali_ircc_dma_receive(struct ali_ircc_cb *self) +{ + int iobase, tmp; + + IRDA_DEBUG(1, __FUNCTION__ "(), ---------------- Start -----------------\n"); + + iobase = self->io.fir_base; + + /* Reset Tx FIFO info */ + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; + + /* Disable DMA */ + switch_bank(iobase, BANK1); + outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR); + + /* Reset Message Count */ + switch_bank(iobase, BANK0); + outb(0x07, iobase+FIR_LSR); + + self->rcvFramesOverflow = FALSE; + + self->LineStatus = inb(iobase+FIR_LSR) ; + + /* Reset Rx FIFO info */ + self->io.direction = IO_RECV; + self->rx_buff.data = self->rx_buff.head; + + /* Reset Rx FIFO */ + // switch_bank(iobase, BANK0); + outb(LCR_A_FIFO_RESET, iobase+FIR_LCR_A); + + self->st_fifo.len = self->st_fifo.pending_bytes = 0; + self->st_fifo.tail = self->st_fifo.head = 0; + + setup_dma(self->io.dma, self->rx_buff.data, self->rx_buff.truesize, + DMA_RX_MODE); + + /* Set Receive Mode,Brick Wall */ + //switch_bank(iobase, BANK0); + tmp = inb(iobase+FIR_LCR_B); + outb((unsigned char)(tmp &0x3f) | LCR_B_RX_MODE | LCR_B_BW , iobase + FIR_LCR_B); // 2000/12/1 05:16PM + IRDA_DEBUG(1, __FUNCTION__ "(), *** Change To RX mode: FIR_LCR_B = 0x%x *** \n", inb(iobase+FIR_LCR_B)); + + /* Set Rx Threshold */ + switch_bank(iobase, BANK1); + outb(RX_FIFO_Threshold, iobase+FIR_FIFO_TR); + outb(RX_DMA_Threshold, iobase+FIR_DMA_TR); + + /* Enable DMA and Burst Mode */ + // switch_bank(iobase, BANK1); + outb(CR_DMA_EN | CR_DMA_BURST, iobase+FIR_CR); + + switch_bank(iobase, BANK0); + IRDA_DEBUG(1, __FUNCTION__ "(), ----------------- End ------------------\n"); + return 0; +} + +static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self) +{ + struct st_fifo *st_fifo; + struct sk_buff *skb; + __u8 status, MessageCount; + int len, i, iobase, val; + + IRDA_DEBUG(1, __FUNCTION__ "(), ---------------- Start -----------------\n"); + + st_fifo = &self->st_fifo; + iobase = self->io.fir_base; + + switch_bank(iobase, BANK0); + MessageCount = inb(iobase+ FIR_LSR)&0x07; + + if (MessageCount > 0) + IRDA_DEBUG(0, __FUNCTION__ "(), Messsage count = %d,\n", MessageCount); + + for (i=0; i<=MessageCount; i++) + { + /* Bank 0 */ + switch_bank(iobase, BANK0); + status = inb(iobase+FIR_LSR); + + switch_bank(iobase, BANK2); + len = inb(iobase+FIR_RX_DSR_HI) & 0x0f; + len = len << 8; + len |= inb(iobase+FIR_RX_DSR_LO); + + IRDA_DEBUG(1, __FUNCTION__ "(), RX Length = 0x%.2x,\n", len); + IRDA_DEBUG(1, __FUNCTION__ "(), RX Status = 0x%.2x,\n", status); + + if (st_fifo->tail >= MAX_RX_WINDOW) { + IRDA_DEBUG(0, __FUNCTION__ "(), window is full!\n"); + continue; + } + + st_fifo->entries[st_fifo->tail].status = status; + st_fifo->entries[st_fifo->tail].len = len; + st_fifo->pending_bytes += len; + st_fifo->tail++; + st_fifo->len++; + } + + for (i=0; i<=MessageCount; i++) + { + /* Get first entry */ + status = st_fifo->entries[st_fifo->head].status; + len = st_fifo->entries[st_fifo->head].len; + st_fifo->pending_bytes -= len; + st_fifo->head++; + st_fifo->len--; + + /* Check for errors */ + if ((status & 0xd8) || self->rcvFramesOverflow || (len==0)) + { + IRDA_DEBUG(0,__FUNCTION__ "(), ************* RX Errors ************ \n"); + + /* Skip frame */ + self->stats.rx_errors++; + + self->rx_buff.data += len; + + if (status & LSR_FIFO_UR) + { + self->stats.rx_frame_errors++; + IRDA_DEBUG(0,__FUNCTION__ "(), ************* FIFO Errors ************ \n"); + } + if (status & LSR_FRAME_ERROR) + { + self->stats.rx_frame_errors++; + IRDA_DEBUG(0,__FUNCTION__ "(), ************* FRAME Errors ************ \n"); + } + + if (status & LSR_CRC_ERROR) + { + self->stats.rx_crc_errors++; + IRDA_DEBUG(0,__FUNCTION__ "(), ************* CRC Errors ************ \n"); + } + + if(self->rcvFramesOverflow) + { + self->stats.rx_frame_errors++; + IRDA_DEBUG(0,__FUNCTION__ "(), ************* Overran DMA buffer ************ \n"); + } + if(len == 0) + { + self->stats.rx_frame_errors++; + IRDA_DEBUG(0,__FUNCTION__ "(), ********** Receive Frame Size = 0 ********* \n"); + } + } + else + { + + if (st_fifo->pending_bytes < 32) + { + switch_bank(iobase, BANK0); + val = inb(iobase+FIR_BSR); + if ((val& BSR_FIFO_NOT_EMPTY)== 0x80) + { + IRDA_DEBUG(0, __FUNCTION__ "(), ************* BSR_FIFO_NOT_EMPTY ************ \n"); + + /* Put this entry back in fifo */ + st_fifo->head--; + st_fifo->len++; + st_fifo->pending_bytes += len; + st_fifo->entries[st_fifo->head].status = status; + st_fifo->entries[st_fifo->head].len = len; + + /* + * DMA not finished yet, so try again + * later, set timer value, resolution + * 500 us + */ + + switch_bank(iobase, BANK1); + outb(TIMER_IIR_500, iobase+FIR_TIMER_IIR); // 2001/1/2 05:07PM + + /* Enable Timer */ + outb(inb(iobase+FIR_CR) | CR_TIMER_EN, iobase+FIR_CR); + + return FALSE; /* I'll be back! */ + } + } + + /* + * Remember the time we received this frame, so we can + * reduce the min turn time a bit since we will know + * how much time we have used for protocol processing + */ + get_fast_time(&self->stamp); + + skb = dev_alloc_skb(len+1); + if (skb == NULL) + { + WARNING(__FUNCTION__ "(), memory squeeze, " + "dropping frame.\n"); + self->stats.rx_dropped++; + + return FALSE; + } + + /* Make sure IP header gets aligned */ + skb_reserve(skb, 1); + + /* Copy frame without CRC, CRC is removed by hardware*/ + skb_put(skb, len); + memcpy(skb->data, self->rx_buff.data, len); + + /* Move to next frame */ + self->rx_buff.data += len; + self->stats.rx_bytes += len; + self->stats.rx_packets++; + + skb->dev = self->netdev; + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + } + } + + switch_bank(iobase, BANK0); + + IRDA_DEBUG(1, __FUNCTION__ "(), ----------------- End ------------------\n"); + return TRUE; +} + + + +/* + * Function ali_ircc_sir_hard_xmit (skb, dev) + * + * Transmit the frame! + * + */ +static int ali_ircc_sir_hard_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ali_ircc_cb *self; + unsigned long flags; + int iobase; + __u32 speed; + + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + ASSERT(dev != NULL, return 0;); + + self = (struct ali_ircc_cb *) dev->priv; + ASSERT(self != NULL, return 0;); + + iobase = self->io.sir_base; + + netif_stop_queue(dev); + + /* Check if we need to change the speed */ + speed = irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) { + /* Check for empty frame */ + if (!skb->len) { + ali_ircc_change_speed(self, speed); + dev_kfree_skb(skb); + return 0; + } else + self->new_speed = speed; + } + + spin_lock_irqsave(&self->lock, flags); + + /* Init tx buffer */ + self->tx_buff.data = self->tx_buff.head; + + /* Copy skb to tx_buff while wrapping, stuffing and making CRC */ + self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, + self->tx_buff.truesize); + + self->stats.tx_bytes += self->tx_buff.len; + + /* Turn on transmit finished interrupt. Will fire immediately! */ + outb(UART_IER_THRI, iobase+UART_IER); + + spin_unlock_irqrestore(&self->lock, flags); + + dev_kfree_skb(skb); + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End ------------------\n"); + + return 0; +} + + +/* + * Function ali_ircc_net_ioctl (dev, rq, cmd) + * + * Process IOCTL commands for this device + * + */ +static int ali_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct ali_ircc_cb *self; + unsigned long flags; + int ret = 0; + + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + ASSERT(dev != NULL, return -1;); + + self = dev->priv; + + ASSERT(self != NULL, return -1;); + + IRDA_DEBUG(2, __FUNCTION__ "(), %s, (cmd=0x%X)\n", dev->name, cmd); + + /* Disable interrupts & save flags */ + save_flags(flags); + cli(); + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + IRDA_DEBUG(1, __FUNCTION__ "(), SIOCSBANDWIDTH\n"); + /* + * This function will also be used by IrLAP to change the + * speed, so we still must allow for speed change within + * interrupt context. + */ + if (!in_interrupt() && !capable(CAP_NET_ADMIN)) + return -EPERM; + + ali_ircc_change_speed(self, irq->ifr_baudrate); + break; + case SIOCSMEDIABUSY: /* Set media busy */ + IRDA_DEBUG(1, __FUNCTION__ "(), SIOCSMEDIABUSY\n"); + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + irda_device_set_media_busy(self->netdev, TRUE); + break; + case SIOCGRECEIVING: /* Check if we are receiving right now */ + IRDA_DEBUG(2, __FUNCTION__ "(), SIOCGRECEIVING\n"); + irq->ifr_receiving = ali_ircc_is_receiving(self); + break; + default: + ret = -EOPNOTSUPP; + } + + restore_flags(flags); + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End ------------------\n"); + + return ret; +} + +/* + * Function ali_ircc_is_receiving (self) + * + * Return TRUE is we are currently receiving a frame + * + */ +static int ali_ircc_is_receiving(struct ali_ircc_cb *self) +{ + unsigned long flags; + int status = FALSE; + int iobase; + + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start -----------------\n"); + + ASSERT(self != NULL, return FALSE;); + + spin_lock_irqsave(&self->lock, flags); + + if (self->io.speed > 115200) + { + iobase = self->io.fir_base; + + switch_bank(iobase, BANK1); + if((inb(iobase+FIR_FIFO_FR) & 0x3f) != 0) + { + /* We are receiving something */ + IRDA_DEBUG(1, __FUNCTION__ "(), We are receiving something\n"); + status = TRUE; + } + switch_bank(iobase, BANK0); + } + else + { + status = (self->rx_buff.state != OUTSIDE_FRAME); + } + + spin_unlock_irqrestore(&self->lock, flags); + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End ------------------\n"); + + return status; +} + +static struct net_device_stats *ali_ircc_net_get_stats(struct net_device *dev) +{ + struct ali_ircc_cb *self = (struct ali_ircc_cb *) dev->priv; + + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End ------------------\n"); + + return &self->stats; +} + +static void ali_ircc_suspend(struct ali_ircc_cb *self) +{ + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + MESSAGE("%s, Suspending\n", driver_name); + + if (self->io.suspended) + return; + + ali_ircc_net_close(self->netdev); + + self->io.suspended = 1; + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End ------------------\n"); +} + +static void ali_ircc_wakeup(struct ali_ircc_cb *self) +{ + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + if (!self->io.suspended) + return; + + ali_ircc_net_open(self->netdev); + + MESSAGE("%s, Waking up\n", driver_name); + + self->io.suspended = 0; + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End ------------------\n"); +} + +static int ali_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + struct ali_ircc_cb *self = (struct ali_ircc_cb*) dev->data; + + IRDA_DEBUG(2, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + if (self) { + switch (rqst) { + case PM_SUSPEND: + ali_ircc_suspend(self); + break; + case PM_RESUME: + ali_ircc_wakeup(self); + break; + } + } + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End ------------------\n"); + + return 0; +} + + +/* ALi Chip Function */ + +static void SetCOMInterrupts(struct ali_ircc_cb *self , unsigned char enable) +{ + + unsigned char newMask; + + int iobase = self->io.fir_base; /* or sir_base */ + + IRDA_DEBUG(2, __FUNCTION__ "(), -------- Start -------- ( Enable = %d )\n", enable); + + /* Enable the interrupt which we wish to */ + if (enable){ + if (self->io.direction == IO_XMIT) + { + if (self->io.speed > 115200) /* FIR, MIR */ + { + newMask = self->ier; + } + else /* SIR */ + { + newMask = UART_IER_THRI | UART_IER_RDI; + } + } + else { + if (self->io.speed > 115200) /* FIR, MIR */ + { + newMask = self->ier; + } + else /* SIR */ + { + newMask = UART_IER_RDI; + } + } + } + else /* Disable all the interrupts */ + { + newMask = 0x00; + + } + + //SIR and FIR has different registers + if (self->io.speed > 115200) + { + switch_bank(iobase, BANK0); + outb(newMask, iobase+FIR_IER); + } + else + outb(newMask, iobase+UART_IER); + + IRDA_DEBUG(2, __FUNCTION__ "(), ----------------- End ------------------\n"); +} + +static void SIR2FIR(int iobase) +{ + //unsigned char tmp; + unsigned long flags; + + IRDA_DEBUG(1, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + save_flags(flags); + cli(); + + outb(0x28, iobase+UART_MCR); + outb(0x68, iobase+UART_MCR); + outb(0x88, iobase+UART_MCR); + + restore_flags(flags); + + outb(0x60, iobase+FIR_MCR); /* Master Reset */ + outb(0x20, iobase+FIR_MCR); /* Master Interrupt Enable */ + + //tmp = inb(iobase+FIR_LCR_B); /* SIP enable */ + //tmp |= 0x20; + //outb(tmp, iobase+FIR_LCR_B); + + IRDA_DEBUG(1, __FUNCTION__ "(), ----------------- End ------------------\n"); +} + +static void FIR2SIR(int iobase) +{ + unsigned char val; + unsigned long flags; + + IRDA_DEBUG(1, __FUNCTION__ "(), ---------------- Start ----------------\n"); + + save_flags(flags); + cli(); + + outb(0x20, iobase+FIR_MCR); /* IRQ to low */ + outb(0x00, iobase+UART_IER); + + outb(0xA0, iobase+FIR_MCR); /* Don't set master reset */ + outb(0x00, iobase+UART_FCR); + outb(0x07, iobase+UART_FCR); + + val = inb(iobase+UART_RX); + val = inb(iobase+UART_LSR); + val = inb(iobase+UART_MSR); + + restore_flags(flags); + + IRDA_DEBUG(1, __FUNCTION__ "(), ----------------- End ------------------\n"); +} + +#ifdef MODULE +MODULE_AUTHOR("Benjamin Kong "); +MODULE_DESCRIPTION("ALi FIR Controller Driver"); + +MODULE_PARM(io, "1-4i"); +MODULE_PARM_DESC(io, "Base I/O addresses"); +MODULE_PARM(irq, "1-4i"); +MODULE_PARM_DESC(irq, "IRQ lines"); +MODULE_PARM(dma, "1-4i"); +MODULE_PARM_DESC(dma, "DMA channels"); + +int init_module(void) +{ + return ali_ircc_init(); +} + +void cleanup_module(void) +{ + ali_ircc_cleanup(); +} +#endif /* MODULE */ diff -urpN linux-2.4.1-pre8/drivers/net/irda/irport.c linux-2.4.1-pre8-irda-patch/drivers/net/irda/irport.c --- linux-2.4.1-pre8/drivers/net/irda/irport.c Mon Nov 13 05:43:07 2000 +++ linux-2.4.1-pre8-irda-patch/drivers/net/irda/irport.c Mon Jan 22 00:54:52 2001 @@ -616,7 +616,7 @@ int irport_hard_xmit(struct sk_buff *skb struct irport_cb *self; unsigned long flags; int iobase; - __u32 speed; + __s32 speed; ASSERT(dev != NULL, return 0;); @@ -628,12 +628,14 @@ int irport_hard_xmit(struct sk_buff *skb netif_stop_queue(dev); /* Check if we need to change the speed */ - if ((speed = irda_get_speed(skb)) != self->io.speed) { + speed = irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) { /* Check for empty frame */ if (!skb->len) { irda_task_execute(self, __irport_change_speed, irport_change_speed_complete, NULL, (void *) speed); + dev_kfree_skb(skb); return 0; } else self->new_speed = speed; @@ -770,6 +772,7 @@ int irport_net_open(struct net_device *d { struct irport_cb *self; int iobase; + char name[16]; ASSERT(dev != NULL, return -1;); self = (struct irport_cb *) dev->priv; @@ -783,11 +786,14 @@ int irport_net_open(struct net_device *d irport_start(self); + /* Give self a name */ + sprintf(name, "irport@0x%03x", self->io.sir_base); + /* * Open new IrLAP layer instance, now that everything should be * initialized properly */ - self->irlap = irlap_open(dev, &self->qos); + self->irlap = irlap_open(dev, &self->qos, name); /* FIXME: change speed of dongle */ /* Ready to play! */ diff -urpN linux-2.4.1-pre8/drivers/net/irda/irtty.c linux-2.4.1-pre8-irda-patch/drivers/net/irda/irtty.c --- linux-2.4.1-pre8/drivers/net/irda/irtty.c Tue Nov 28 03:07:31 2000 +++ linux-2.4.1-pre8-irda-patch/drivers/net/irda/irtty.c Mon Jan 22 00:54:52 2001 @@ -77,7 +77,7 @@ int __init irtty_init(void) { int status; - irtty = hashbin_new( HB_LOCAL); + irtty = hashbin_new( HB_GLOBAL); /* HB_GLOBAL is good enough */ if ( irtty == NULL) { printk( KERN_WARNING "IrDA: Can't allocate irtty hashbin!\n"); return -ENOMEM; @@ -279,6 +279,11 @@ static void irtty_close(struct tty_struc tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); tty->disc_data = 0; + /* We are not using any dongle anymore! */ + if (self->dongle) + irda_device_dongle_cleanup(self->dongle); + self->dongle = NULL; + /* Remove netdevice */ if (self->netdev) { rtnl_lock(); @@ -286,11 +291,6 @@ static void irtty_close(struct tty_struc rtnl_unlock(); } - /* We are not using any dongle anymore! */ - if (self->dongle) - irda_device_dongle_cleanup(self->dongle); - self->dongle = NULL; - /* Remove speed changing task if any */ if (self->task) irda_task_delete(self->task); @@ -629,7 +629,7 @@ static int irtty_hard_xmit(struct sk_buf { struct irtty_cb *self; int actual = 0; - __u32 speed; + __s32 speed; self = (struct irtty_cb *) dev->priv; ASSERT(self != NULL, return 0;); @@ -638,12 +638,14 @@ static int irtty_hard_xmit(struct sk_buf netif_stop_queue(dev); /* Check if we need to change the speed */ - if ((speed = irda_get_speed(skb)) != self->io.speed) { + speed = irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) { /* Check for empty frame */ if (!skb->len) { irda_task_execute(self, irtty_change_speed, irtty_change_speed_complete, NULL, (void *) speed); + dev_kfree_skb(skb); return 0; } else self->new_speed = speed; @@ -893,6 +895,8 @@ static int irtty_net_init(struct net_dev static int irtty_net_open(struct net_device *dev) { struct irtty_cb *self = (struct irtty_cb *) dev->priv; + struct tty_struct *tty = self->tty; + char name[16]; ASSERT(self != NULL, return -1;); ASSERT(self->magic == IRTTY_MAGIC, return -1;); @@ -905,11 +909,16 @@ static int irtty_net_open(struct net_dev /* Make sure we can receive more data */ irtty_stop_receiver(self, FALSE); + /* Give self a name */ + sprintf(name, "%s%d", tty->driver.name, + MINOR(tty->device) - tty->driver.minor_start + + tty->driver.name_base); + /* * Open new IrLAP layer instance, now that everything should be * initialized properly */ - self->irlap = irlap_open(dev, &self->qos); + self->irlap = irlap_open(dev, &self->qos, name); MOD_INC_USE_COUNT; diff -urpN linux-2.4.1-pre8/drivers/net/irda/nsc-ircc.c linux-2.4.1-pre8-irda-patch/drivers/net/irda/nsc-ircc.c --- linux-2.4.1-pre8/drivers/net/irda/nsc-ircc.c Tue Nov 28 03:07:31 2000 +++ linux-2.4.1-pre8-irda-patch/drivers/net/irda/nsc-ircc.c Mon Jan 22 00:54:53 2001 @@ -1064,7 +1064,7 @@ static int nsc_ircc_hard_xmit_sir(struct struct nsc_ircc_cb *self; unsigned long flags; int iobase; - __u32 speed; + __s32 speed; __u8 bank; self = (struct nsc_ircc_cb *) dev->priv; @@ -1076,10 +1076,12 @@ static int nsc_ircc_hard_xmit_sir(struct netif_stop_queue(dev); /* Check if we need to change the speed */ - if ((speed = irda_get_speed(skb)) != self->io.speed) { + speed = irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) { /* Check for empty frame */ if (!skb->len) { nsc_ircc_change_speed(self, speed); + dev_kfree_skb(skb); return 0; } else self->new_speed = speed; @@ -1116,7 +1118,7 @@ static int nsc_ircc_hard_xmit_fir(struct struct nsc_ircc_cb *self; unsigned long flags; int iobase; - __u32 speed; + __s32 speed; __u8 bank; int mtt, diff; @@ -1126,10 +1128,12 @@ static int nsc_ircc_hard_xmit_fir(struct netif_stop_queue(dev); /* Check if we need to change the speed */ - if ((speed = irda_get_speed(skb)) != self->io.speed) { + speed = irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) { /* Check for empty frame */ if (!skb->len) { nsc_ircc_change_speed(self, speed); + dev_kfree_skb(skb); return 0; } else self->new_speed = speed; @@ -1876,7 +1880,7 @@ static int nsc_ircc_net_open(struct net_ * Open new IrLAP layer instance, now that everything should be * initialized properly */ - self->irlap = irlap_open(dev, &self->qos); + self->irlap = irlap_open(dev, &self->qos, "NSC-FIR"); MOD_INC_USE_COUNT; diff -urpN linux-2.4.1-pre8/drivers/net/irda/smc-ircc.c linux-2.4.1-pre8-irda-patch/drivers/net/irda/smc-ircc.c --- linux-2.4.1-pre8/drivers/net/irda/smc-ircc.c Fri Dec 29 23:07:22 2000 +++ linux-2.4.1-pre8-irda-patch/drivers/net/irda/smc-ircc.c Mon Jan 22 00:54:52 2001 @@ -606,7 +606,7 @@ static int ircc_hard_xmit(struct sk_buff struct irport_cb *irport; struct ircc_cb *self; unsigned long flags; - __u32 speed; + __s32 speed; int iobase; int mtt; @@ -619,10 +619,12 @@ static int ircc_hard_xmit(struct sk_buff netif_stop_queue(dev); /* Check if we need to change the speed after this frame */ - if ((speed = irda_get_speed(skb)) != self->io.speed) { + speed = irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) { /* Check for empty frame */ if (!skb->len) { ircc_change_speed(self, speed); + dev_kfree_skb(skb); return 0; } else self->new_speed = speed; diff -urpN linux-2.4.1-pre8/drivers/net/irda/toshoboe.c linux-2.4.1-pre8-irda-patch/drivers/net/irda/toshoboe.c --- linux-2.4.1-pre8/drivers/net/irda/toshoboe.c Thu Jan 4 21:50:12 2001 +++ linux-2.4.1-pre8-irda-patch/drivers/net/irda/toshoboe.c Mon Jan 22 00:54:52 2001 @@ -263,7 +263,7 @@ static int toshoboe_hard_xmit (struct sk_buff *skb, struct net_device *dev) { struct toshoboe_cb *self; - __u32 speed; + __s32 speed; int mtt, len; self = (struct toshoboe_cb *) dev->priv; @@ -272,10 +272,12 @@ toshoboe_hard_xmit (struct sk_buff *skb, ); /* Check if we need to change the speed */ - if ((speed = irda_get_speed(skb)) != self->io.speed) { + speed = irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) { /* Check for empty frame */ if (!skb->len) { toshoboe_setbaud(self, speed); + dev_kfree_skb(skb); return 0; } else self->new_speed = speed; @@ -537,7 +539,7 @@ toshoboe_net_open (struct net_device *de * Open new IrLAP layer instance, now that everything should be * initialized properly */ - self->irlap = irlap_open(dev, &self->qos); + self->irlap = irlap_open(dev, &self->qos, "Toshiba-FIR"); self->open = 1; @@ -896,7 +898,7 @@ toshoboe_gotosleep (struct toshoboe_cb * /*FIXME: can't sleep here wait one second */ while ((i--) && (self->txpending)) - mdelay (100); + udelay (100); toshoboe_stopchip (self); toshoboe_disablebm (self); diff -urpN linux-2.4.1-pre8/drivers/net/irda/w83977af_ir.c linux-2.4.1-pre8-irda-patch/drivers/net/irda/w83977af_ir.c --- linux-2.4.1-pre8/drivers/net/irda/w83977af_ir.c Sun Nov 12 03:11:23 2000 +++ linux-2.4.1-pre8-irda-patch/drivers/net/irda/w83977af_ir.c Mon Jan 22 00:54:52 2001 @@ -497,7 +497,7 @@ void w83977af_change_speed(struct w83977 int w83977af_hard_xmit(struct sk_buff *skb, struct net_device *dev) { struct w83977af_ir *self; - __u32 speed; + __s32 speed; int iobase; __u8 set; int mtt; @@ -513,10 +513,12 @@ int w83977af_hard_xmit(struct sk_buff *s netif_stop_queue(dev); /* Check if we need to change the speed */ - if ((speed = irda_get_speed(skb)) != self->io.speed) { + speed = irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) { /* Check for empty frame */ if (!skb->len) { w83977af_change_speed(self, speed); + dev_kfree_skb(skb); return 0; } else self->new_speed = speed; @@ -1253,7 +1255,7 @@ static int w83977af_net_open(struct net_ * Open new IrLAP layer instance, now that everything should be * initialized properly */ - self->irlap = irlap_open(dev, &self->qos); + self->irlap = irlap_open(dev, &self->qos, "Winbond-FIR"); MOD_INC_USE_COUNT; diff -urpN linux-2.4.1-pre8/drivers/usb/Config.in linux-2.4.1-pre8-irda-patch/drivers/usb/Config.in --- linux-2.4.1-pre8/drivers/usb/Config.in Tue Nov 28 03:10:35 2000 +++ linux-2.4.1-pre8-irda-patch/drivers/usb/Config.in Mon Jan 22 01:25:04 2001 @@ -27,6 +27,7 @@ comment 'USB Controllers' comment 'USB Device Class drivers' dep_tristate ' USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND + dep_tristate ' IrDA USB support (EXPERIMENTAL)' CONFIG_USB_IRDA $CONFIG_USB $CONFIG_EXPERIMENTAL dep_tristate ' USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB $CONFIG_EXPERIMENTAL dep_tristate ' USB Mass Storage support' CONFIG_USB_STORAGE $CONFIG_USB $CONFIG_SCSI if [ "$CONFIG_USB_STORAGE" != "n" ]; then diff -urpN linux-2.4.1-pre8/drivers/usb/Makefile linux-2.4.1-pre8-irda-patch/drivers/usb/Makefile --- linux-2.4.1-pre8/drivers/usb/Makefile Fri Dec 29 23:07:23 2000 +++ linux-2.4.1-pre8-irda-patch/drivers/usb/Makefile Mon Jan 22 01:25:09 2001 @@ -57,6 +57,7 @@ obj-$(CONFIG_USB_PEGASUS) += pegasus.o obj-$(CONFIG_USB_RIO500) += rio500.o obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_USB_MICROTEK) += microtek.o +obj-$(CONFIG_USB_IRDA) += irda-usb.o obj-$(CONFIG_USB_BLUETOOTH) += bluetooth.o obj-$(CONFIG_USB_NET1080) += net1080.o diff -urpN linux-2.4.1-pre8/drivers/usb/irda-usb.c linux-2.4.1-pre8-irda-patch/drivers/usb/irda-usb.c --- linux-2.4.1-pre8/drivers/usb/irda-usb.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.1-pre8-irda-patch/drivers/usb/irda-usb.c Mon Jan 22 00:55:47 2001 @@ -0,0 +1,1151 @@ +/***************************************************************************** + * + * Filename: irda-usb.c + * Version: 0.1 + * Description: IrDA-USB Driver + * Status: Experimental + * Author: Dag Brattli + * + * Copyright (C) 2000, Roman Weissgaerber + * Copyright (C) 2000, Dag Brattli + * Copyright (C) 2001, Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "irda-usb.h" + +__u32 min_turn_times[] = { 10000, 5000, 1000, 500, 100, 50, 10, 0 }; /* us */ + +static void irda_usb_dump_class_desc(struct irda_class_desc *desc); +static struct irda_class_desc *irda_usb_find_class_desc(struct usb_device *dev, unsigned int ifnum); +static void irda_usb_disconnect(struct usb_device *dev, void *ptr); +static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self); +static int irda_usb_hard_xmit(struct sk_buff *skb, struct net_device *dev); +static int irda_usb_open(struct irda_usb_cb *self); +static int irda_usb_close(struct irda_usb_cb *self); +static void write_bulk_callback(purb_t purb); +static void irda_usb_receive(purb_t purb); +static int irda_usb_net_init(struct net_device *dev); +static int irda_usb_net_open(struct net_device *dev); +static int irda_usb_net_close(struct net_device *dev); +static int irda_usb_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static void irda_usb_net_timeout(struct net_device *dev); +static struct net_device_stats *irda_usb_net_get_stats(struct net_device *dev); + +/* Master instance for each hardware found */ +#define NIRUSB 4 /* Max number of USB-IrDA dongles */ +static struct irda_usb_cb irda_instance[NIRUSB]; + +/* These are the currently known IrDA USB dongles. Add new dongles here */ +struct irda_usb_dongle dongles[] = { + /* idVendor, idProduct, idCapability */ + /* ACTiSYS Corp, ACT-IR2000U FIR-USB Adapter */ + { 0x9c4, 0x011, IUC_SPEED_BUG | IUC_NO_WINDOW }, + /* KC Technology Inc., ?? */ + { 0x50f, 0x180, IUC_DEFAULT }, + /* Extended Systems, Inc., XTNDAccess IrDA USB (ESI-9685) */ + { 0x8e9, 0x100, IUC_SPEED_BUG | IUC_NO_WINDOW }, + { 0, 0, 0 }, /* The end */ +}; + +/* + * This routine is called by the USB subsystem for each new device + * in the system. We need to check if the device is ours, and in + * this case start handling it. + * Note : it might be worth protecting this function by a global + * spinlock... + */ +static void *irda_usb_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) +{ + struct irda_usb_cb *self = NULL; + struct usb_interface_descriptor *interface; + struct usb_endpoint_descriptor *endpoint; + struct irda_class_desc *irda_desc; + struct irda_usb_dongle *dongle; + int class, subclass; + int found; + int capability = IUC_DEFAULT; + int ret; + int ep; + int i; + + IRDA_DEBUG(0, "Vendor: %x, Product: %x\n", dev->descriptor.idVendor, dev->descriptor.idProduct); + + /* Check for all known IrDA-USB dongles */ + found = 0; + for (dongle=dongles;dongle->idVendor;dongle++) { + if ((dev->descriptor.idVendor == dongle->idVendor) && + (dev->descriptor.idProduct == dongle->idProduct)) + { + found = TRUE; + capability = dongle->idCapability; + break; + } + } + if (!found) { + /* Accept all dongles with IrDA-USB class/subclass */ + class = dev->actconfig->interface[ifnum].altsetting[0].bInterfaceClass; + subclass = dev->actconfig->interface[ifnum].altsetting[0].bInterfaceSubClass; + IRDA_DEBUG(0, "Class: %x, Subclass: %x\n", class, subclass); + + if ((class != USB_CLASS_APP_SPEC) || (subclass != USB_CLASS_IRDA)) + return NULL; + } + + MESSAGE("USB IRDA found at address %d\n", dev->devnum); + + /* Try to cleanup all instance that have a pending disconnect + * Instance will be in this state is the disconnect() occurs + * before the net_close(). + * Jean II */ + for (i = 0; i < NIRUSB; i++) { + struct irda_usb_cb *irda = &irda_instance[i]; + if((irda->usbdev != NULL) && + (irda->present == 0) && + (irda->netopen == 0)) { + IRDA_DEBUG(0, __FUNCTION__ "(), found a zombie instance !!!\n"); + irda_usb_disconnect(irda->usbdev, (void *) irda); + } + } + + /* Find an free instance to handle this new device... */ + self = NULL; + for (i = 0; i < NIRUSB; i++) { + if(irda_instance[i].usbdev == NULL) { + self = &irda_instance[i]; + break; + } + } + if(self == NULL) { + IRDA_DEBUG(0, "Too many USB IrDA devices !!! (max = %d)\n", + NIRUSB); + return NULL; + } + + /* Reset the instance */ + self->present = 0; + self->netopen = 0; + +#if 0 /* Is this really necessary? */ + if (usb_set_configuration (dev, dev->config[0].bConfigurationValue) < 0) { + err("set_configuration failed"); + return NULL; + } +#endif +#if 0 /* Is this really necessary? */ + ret = usb_set_interface(dev, ifnum, 0); + IRDA_DEBUG(0, "usb-irda: set interface result %d\n", ret); + switch (ret) { + case USB_ST_NOERROR: /* 0 */ + break; + case USB_ST_STALL: /* -EPIPE = -32 */ + usb_clear_halt(dev, usb_sndctrlpipe(dev, 0)); + IRDA_DEBUG(0, __FUNCTION__ "(), Clearing stall on control interface\n" ); + break; + default: + IRDA_DEBUG(0, __FUNCTION__ "(), Unknown error %d\n", ret); + return NULL; + break; + } +#endif + /* Find our endpoints */ + interface = &dev->actconfig->interface[ifnum].altsetting[0]; + endpoint = interface->endpoint; + ep = endpoint[0].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + if ((endpoint[0].bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) + self->bulk_in_ep = ep; + else + self->bulk_out_ep = ep; + + ep = endpoint[1].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + if ((endpoint[1].bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) + self->bulk_in_ep = ep; + else + self->bulk_out_ep = ep; + + if (self->bulk_out_ep == 0 || self->bulk_in_ep == 0 || + endpoint [0].bmAttributes != USB_ENDPOINT_XFER_BULK || + endpoint [1].bmAttributes != USB_ENDPOINT_XFER_BULK) + { + IRDA_DEBUG(0, __FUNCTION__ "(), Bogus endpoints"); + return NULL; + } + IRDA_DEBUG(0, __FUNCTION__ "(), bulk_in_ep=%d, bulk_out_ep=%d\n", self->bulk_in_ep, self->bulk_out_ep); + + /* Find IrDA class descriptor */ + irda_desc = irda_usb_find_class_desc(dev, ifnum); + if (irda_desc == NULL) + return NULL; + + self->irda_desc = irda_desc; + self->present = 1; + self->netopen = 0; + self->capability = capability; + self->usbdev = dev; + ret = irda_usb_open(self); + if (ret) + return NULL; + + return self; +} + +/* + * Function irda_usb_find_class_desc(dev, ifnum) + * + * Returns instance of IrDA class descriptor, or NULL if not found + * + */ +static struct irda_class_desc *irda_usb_find_class_desc(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_interface_descriptor *interface; + struct irda_class_desc *desc, *ptr; + int ret; + + desc = kmalloc(sizeof (struct irda_class_desc), GFP_KERNEL); + if (desc == NULL) + return NULL; + memset(desc, 0, sizeof(struct irda_class_desc)); + + ret = usb_get_class_descriptor(dev, ifnum, USB_DT_IRDA, 0, (void *) desc, sizeof(struct irda_class_desc)); + IRDA_DEBUG(0, __FUNCTION__ "(), ret=%d\n", ret); + if (ret) { + WARNING("usb-irda: usb_get_class_descriptor failed (0x%x)\n", ret); + } + + /* Check if we found it? */ + if (desc->bDescriptorType == USB_DT_IRDA) + return desc; + + IRDA_DEBUG(0, __FUNCTION__ "(), parsing extra descriptors ...\n"); + + /* Check if the class descriptor is interleaved with standard descriptors */ + interface = &dev->actconfig->interface[ifnum].altsetting[0]; + ret = usb_get_extra_descriptor(interface, USB_DT_IRDA, &ptr); + if (ret) { + kfree(desc); + return NULL; + } + *desc = *ptr; + irda_usb_dump_class_desc(desc); + + return desc; +} + +/* + * Function usb_irda_dump_class_desc(desc) + * + * Prints out the contents of the IrDA class descriptor + * + */ +static void irda_usb_dump_class_desc(struct irda_class_desc *desc) +{ + printk("bLength=%x\n", desc->bLength); + printk("bDescriptorType=%x\n", desc->bDescriptorType); + printk("bcdSpecRevision=%x\n", desc->bcdSpecRevision); + printk("bmDataSize=%x\n", desc->bmDataSize); + printk("bmWindowSize=%x\n", desc->bmWindowSize); + printk("bmMinTurnaroundTime=%d\n", desc->bmMinTurnaroundTime); + printk("wBaudRate=%x\n", desc->wBaudRate); + printk("bmAdditionalBOFs=%x\n", desc->bmAdditionalBOFs); + printk("bIrdaRateSniff=%x\n", desc->bIrdaRateSniff); + printk("bMaxUnicastList=%x\n", desc->bMaxUnicastList); +} + +static void irda_usb_disconnect(struct usb_device *dev, void *ptr) +{ + struct irda_usb_cb *self = (struct irda_usb_cb *) ptr; + int i; + + IRDA_DEBUG(0, __FUNCTION__ "()\n"); + + /* Oups ! We are not there any more */ + self->present = 0; + + /* Hum... Check if networking is still active */ + if (self->netopen) { + /* Accept no more transmissions */ + /*netif_device_detach(self->netdev);*/ + netif_stop_queue(self->netdev); + /* Stop all the receive URBs */ + for (i = 0; i < IU_MAX_URB; i++) + usb_unlink_urb(&(self->rx_urb[i])); + /* Cancel Tx and speed URB */ + usb_unlink_urb(&(self->tx_urb)); + usb_unlink_urb(&(self->speed_urb)); + + IRDA_DEBUG(0, __FUNCTION__ "(), postponing disconnect, network is still active...\n"); + /* better not do anything just yet, usb_irda_cleanup() + * will do whats needed */ + return; + } + + /* Cleanup the device stuff */ + irda_usb_close(self); + /* No longer attached to USB bus */ + self->usbdev = NULL; + IRDA_DEBUG(0, __FUNCTION__ "(), USB IrDA Disconnected\n"); +} + +#if 0 +static void ctrl_callback(urb_t *urb) +{ + IRDA_DEBUG(0, __FUNCTION__ "()\n"); + switch (urb->status) { + case USB_ST_NOERROR: /* 0 */ + break; + case USB_ST_URB_PENDING: /* -EINPROGRESS */ + break; + case USB_ST_URB_KILLED: /* -ENOENT */ + break; + default: + break; + } +} +#endif + +static struct usb_driver irda_driver = { + "irda-usb", + irda_usb_probe, + irda_usb_disconnect, + { NULL, NULL }, + NULL, +}; + +static void irda_usb_init_qos(struct irda_usb_cb *self) +{ + struct irda_class_desc *desc; + + IRDA_DEBUG(0, __FUNCTION__ "()\n"); + + desc = self->irda_desc; + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&self->qos); + + self->qos.baud_rate.bits = desc->wBaudRate; + self->qos.min_turn_time.bits = desc->bmMinTurnaroundTime; + self->qos.additional_bofs.bits = desc->bmAdditionalBOFs; + self->qos.window_size.bits = desc->bmWindowSize; + self->qos.data_size.bits = desc->bmDataSize; + + IRDA_DEBUG(0, __FUNCTION__ "(), dongle says speed=0x%X, size=0x%X, window=0x%X, bofs=0x%X, turn=0x%X\n", self->qos.baud_rate.bits, self->qos.data_size.bits, self->qos.window_size.bits, self->qos.additional_bofs.bits, self->qos.min_turn_time.bits); + + /* Don't always trust what the dongle tell us */ + if(self->capability & IUC_SIR_ONLY) + self->qos.baud_rate.bits &= 0xff; + if(self->capability & IUC_SMALL_PKT) + self->qos.data_size.bits = 0x07; + if(self->capability & IUC_NO_WINDOW) + self->qos.window_size.bits = 0x01; + if(self->capability & IUC_MAX_WINDOW) + self->qos.window_size.bits = 0x7f; + if(self->capability & IUC_MAX_XBOFS) + self->qos.additional_bofs.bits = 0x01; + /* Note : most of those values apply only for the receive path, + * the transmit path will be set differently - Jean II */ + + irda_qos_bits_to_value(&self->qos); + + self->flags |= IFF_SIR; + if (self->qos.baud_rate.value > 115200) + self->flags |= IFF_MIR; + if (self->qos.baud_rate.value > 1152000) + self->flags |= IFF_FIR; +} + +static int irda_usb_open(struct irda_usb_cb *self) +{ + struct net_device *netdev; + int err; + + IRDA_DEBUG(0, __FUNCTION__ "()\n"); + + spin_lock_init(&self->lock); + + irda_usb_init_qos(self); + + /* Allocate the buffer for tansmission */ + /* Specify how much memory we want */ + self->tx_buff.truesize = IRDA_USB_MAX_MTU; + + /* Allocate memory if needed */ + if (self->tx_buff.truesize > 0) { + self->tx_buff.head = (__u8 *) kmalloc(self->tx_buff.truesize, + GFP_KERNEL); + if (self->tx_buff.head == NULL) + return -1; + memset(self->tx_buff.head, 0, self->tx_buff.truesize); + } + self->tx_buff.data = self->tx_buff.head; + + /* Allocate the buffer for speed changes */ + /* Don't reduce the size of this buffer without doing some + * heavy and complete testing. Don't ask why :-( + * Jean II */ + self->speed_buff.truesize = IRDA_USB_SPEED_MTU; + self->speed_buff.head = (__u8 *) kmalloc(self->speed_buff.truesize, + GFP_KERNEL); + if (self->speed_buff.head == NULL) + return -1; + memset(self->speed_buff.head, 0, self->speed_buff.truesize); + self->speed_buff.data = self->speed_buff.head; + + /* Create a network device for us */ + if (!(netdev = dev_alloc("irda%d", &err))) { + ERROR(__FUNCTION__ "(), dev_alloc() failed!\n"); + return -1; + } + self->netdev = netdev; + netdev->priv = (void *) self; + + /* Override the network functions we need to use */ + netdev->init = irda_usb_net_init; + netdev->hard_start_xmit = irda_usb_hard_xmit; + netdev->tx_timeout = irda_usb_net_timeout; + netdev->watchdog_timeo = HZ/10; + netdev->open = irda_usb_net_open; + netdev->stop = irda_usb_net_close; + netdev->get_stats = irda_usb_net_get_stats; + netdev->do_ioctl = irda_usb_net_ioctl; + + rtnl_lock(); + err = register_netdevice(netdev); + rtnl_unlock(); + if (err) { + ERROR(__FUNCTION__ "(), register_netdev() failed!\n"); + return -1; + } + MESSAGE("IrDA: Registered device %s\n", netdev->name); + + return 0; +} + +static int irda_usb_close(struct irda_usb_cb *self) +{ + IRDA_DEBUG(0, __FUNCTION__ "()\n"); + + ASSERT(self != NULL, return -1;); + + /* Remove netdevice */ + if (self->netdev) { + rtnl_lock(); + unregister_netdevice(self->netdev); + self->netdev = NULL; + rtnl_unlock(); + } + if (self->tx_buff.head) { + kfree(self->tx_buff.head); + self->tx_buff.head = NULL; + } + if (self->speed_buff.head) { + kfree(self->speed_buff.head); + self->speed_buff.head = NULL; + } + + return 0; +} + +#if 0 +static int irda_usb_send_cmd(void) +{ + devrequest request; + + +} +#endif + +/* + * Function irda_usb_build_header(self, skb, header) + * + * Builds USB-IrDA outbound header + * + * Important note : the USB-IrDA spec 1.0 say very clearly in chapter 5.4.2.2 + * that the setting of the link speed and xbof number in this outbound header + * should be applied *AFTER* the frame has been sent. + * Unfortunately, some devices are not compliant with that... It seems that + * reading the spec is far too difficult... + * Jean II + */ +static void irda_usb_build_header(struct irda_usb_cb *self, + __u8 *header, + int force) +{ + /* Set the negotiated link speed */ + if (self->new_speed != -1) { + /* Hum... Ugly hack :-( + * Some device are not compliant with the spec and change + * parameters *before* sending the frame. - Jean II + */ + if((self->capability & IUC_SPEED_BUG) && + (!force) && (self->speed != -1)) { + /* No speed and xbofs change here + * (we'll do it later in the write callback) */ + IRDA_DEBUG(0, __FUNCTION__ "(), not changing speed yet\n"); + *header = 0; + return; + } + + IRDA_DEBUG(0, __FUNCTION__ "(), changing speed to %d\n", self->new_speed); + self->speed = self->new_speed; + self->new_speed = -1; + + switch (self->speed) { + case 2400: + *header = SPEED_2400; + break; + default: + case 9600: + *header = SPEED_9600; + break; + case 19200: + *header = SPEED_19200; + break; + case 38400: + *header = SPEED_38400; + break; + case 57600: + *header = SPEED_57600; + break; + case 115200: + *header = SPEED_115200; + break; + case 576000: + *header = SPEED_576000; + break; + case 1152000: + *header = SPEED_1152000; + break; + case 4000000: + *header = SPEED_4000000; + break; + } + } else + /* No change */ + *header = 0; + + /* Set the negotiated additional XBOFS */ + if (self->new_xbofs != -1) { + IRDA_DEBUG(0, __FUNCTION__ "(), changing xbofs to %d\n", self->new_xbofs); + self->xbofs = self->new_xbofs; + self->new_xbofs = -1; + + switch (self->xbofs) { + case 48: + *header |= 0x10; + break; + case 28: + case 24: /* USB spec 1.0 says 24 */ + *header |= 0x20; + break; + default: + case 12: + *header |= 0x30; + break; + case 5: /* Bug in IrLAP spec? (should be 6) */ + case 6: + *header |= 0x40; + break; + case 3: + *header |= 0x50; + break; + case 2: + *header |= 0x60; + break; + case 1: + *header |= 0x70; + break; + case 0: + *header |= 0x80; + break; + } + } +} + +static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self) +{ + unsigned long flags; + __u8 *frame; + purb_t purb; + int ret; + + IRDA_DEBUG(0, __FUNCTION__ "(), speed=%d, xbofs=%d\n", + self->new_speed, self->new_xbofs); + + purb = &self->speed_urb; + if (purb->status != USB_ST_NOERROR) { + WARNING(__FUNCTION__ "(), URB still in use!\n"); + return; + } + + spin_lock_irqsave(&self->lock, flags); + + /* Allocate the fake frame */ + frame = self->speed_buff.head; + + /* Set the new speed and xbofs in this fake frame */ + irda_usb_build_header(self, frame, 1); + + /* Submit the 0 length IrDA frame to trigger new speed settings */ + FILL_BULK_URB(purb, self->usbdev, + usb_sndbulkpipe(self->usbdev, self->bulk_out_ep), + frame, IRDA_USB_MAX_MTU, + write_bulk_callback, self); + purb->transfer_buffer_length = USB_IRDA_HEADER; + //purb->transfer_flags |= USB_ASYNC_UNLINK; + purb->transfer_flags |= USB_QUEUE_BULK; + purb->timeout = MSECS_TO_JIFFIES(100); + + if ((ret = usb_submit_urb(purb))) { + IRDA_DEBUG(0, __FUNCTION__ "(), failed Speed URB\n"); + } + spin_unlock_irqrestore(&self->lock, flags); +} + +static int irda_usb_hard_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct irda_usb_cb *self = netdev->priv; + purb_t purb; + unsigned long flags; + __s32 speed; + __s16 xbofs; + int res, mtt; + + IRDA_DEBUG(2, __FUNCTION__ "()\n"); + + /* Check if the device is still there */ + if((!self) || (!self->present)) { + IRDA_DEBUG(0, __FUNCTION__ "(), Device is gone...\n"); + return 1; /* Failed */ + } + + netif_stop_queue(netdev); + + /* Check if we need to change the number of xbofs */ + xbofs = irda_get_next_xbofs(skb); + if ((xbofs != self->xbofs) && (xbofs != -1)) { + self->new_xbofs = xbofs; + } + + /* Check if we need to change the speed */ + speed = irda_get_next_speed(skb); + if ((speed != self->speed) && (speed != -1)) { + /* Set the desired speed */ + self->new_speed = speed; + + /* Check for empty frame */ + if (!skb->len) { + /* IrLAP send us an empty frame to make us change the + * speed. Changing speed with the USB adapter is in + * fact sending an empty frame to the adapter, so we + * could just let the present function do its job. + * However, we would wait for min turn time, + * do an extra memcpy and increment packet counters... + * Jean II */ + irda_usb_change_speed_xbofs(self); + netdev->trans_start = jiffies; + dev_kfree_skb(skb); + /* Will netif_wake_queue() in callback */ + return 0; + } + } + + purb = &(self->tx_urb); + if (purb->status != USB_ST_NOERROR) { + WARNING(__FUNCTION__ "(), URB still in use!\n"); + return 0; + } + spin_lock_irqsave(&self->lock, flags); + + /* Change setting for next frame */ + irda_usb_build_header(self, self->tx_buff.head, 0); + + /* Copy packet and make room for USB-IrDA header */ + memcpy(self->tx_buff.head+USB_IRDA_HEADER, skb->data, skb->len); + self->tx_buff.len = skb->len+USB_IRDA_HEADER; + + /* Generate min turn time. FIXME: can we do better than this? */ + mtt = irda_get_mtt(skb); + if (mtt) { + udelay(mtt); + } + + FILL_BULK_URB(purb, self->usbdev, + usb_sndbulkpipe(self->usbdev, self->bulk_out_ep), + self->tx_buff.head, IRDA_USB_MAX_MTU, + write_bulk_callback, self); + purb->transfer_buffer_length = self->tx_buff.len; + //purb->transfer_flags |= USB_ASYNC_UNLINK; + purb->transfer_flags |= USB_QUEUE_BULK; + purb->timeout = MSECS_TO_JIFFIES(100); + + if ((res = usb_submit_urb(purb))) { + IRDA_DEBUG(0, __FUNCTION__ "(), failed Tx URB\n"); + self->stats.tx_errors++; + netif_start_queue(netdev); + } else { + self->stats.tx_packets++; + self->stats.tx_bytes += skb->len; + + netdev->trans_start = jiffies; + } + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_skb(skb); + + return 0; +} + +/* + * Note : this function will be called with both tx_urb and speed_urb... + */ +static void write_bulk_callback(purb_t purb) +{ + struct irda_usb_cb *self = purb->context; + + IRDA_DEBUG(2, __FUNCTION__ "()\n"); + + /* We should always have a context */ + if (self == NULL) { + IRDA_DEBUG(0, __FUNCTION__ "(), Bug : self == NULL\n"); + return; + } + + /* urb is now available */ + purb->status = USB_ST_NOERROR; + + /* If the network is closed, stop everything */ + if((!self->netopen) || (!self->present)) { + IRDA_DEBUG(0, __FUNCTION__ "(), Network is gone...\n"); + return; + } + + /* If we need to change the speed or xbofs, do it now */ + if ((self->new_speed != -1) || (self->new_xbofs != -1)) { + IRDA_DEBUG(0, __FUNCTION__ "(), Changing speed now...\n"); + irda_usb_change_speed_xbofs(self); + } else { + /* Otherwise, allow the stack to send more packets */ + netif_wake_queue(self->netdev); + } +} + +static void irda_usb_submit(struct irda_usb_cb *self, struct sk_buff *skb, purb_t purb) +{ + struct irda_skb_cb *cb; + int ret; + + IRDA_DEBUG(2, __FUNCTION__ "()\n"); + + /* Check that we have an urb */ + if (!purb) { + IRDA_DEBUG(0, __FUNCTION__ "(), Bug : purb == NULL\n"); + return; + } + + /* Allocate new skb if it has not been recycled */ + if (!skb) { + skb = dev_alloc_skb(IRDA_USB_MAX_MTU + 1); + if (!skb) { + /* If this ever happen, we are in deep s***. + * Basically, the Rx path will stop... */ + IRDA_DEBUG(0, __FUNCTION__ "(), Failed to allocate Rx skb\n"); + return; + } + } else { + /* Reset recycled skb */ + skb->data = skb->tail = skb->head; + skb->len = 0; + } + /* Make sure IP header get aligned (IrDA header is 5 bytes ) */ + skb_reserve(skb, 1); + + /* Save ourselves */ + cb = (struct irda_skb_cb *) skb->cb; + cb->context = self; + + /* Reinitialize URB */ + FILL_BULK_URB(purb, self->usbdev, + usb_rcvbulkpipe(self->usbdev, self->bulk_in_ep), + skb->data, skb->truesize, + irda_usb_receive, skb); + //purb->transfer_flags |= USB_ASYNC_UNLINK; + purb->transfer_flags |= USB_QUEUE_BULK; + purb->status = USB_ST_NOERROR; + + ret = usb_submit_urb(purb); + if (ret) { + /* If this ever happen, we are in deep s***. + * Basically, the Rx path will stop... */ + IRDA_DEBUG(0, __FUNCTION__ "(), Failed to submit Rx URB %d\n", ret); + } +} + +/* + * Function irda_usb_receive(purb) + * + * Called by the USB subsystem when a frame has been received + * + */ +static void irda_usb_receive(purb_t purb) +{ + struct sk_buff *skb = (struct sk_buff *) purb->context; + struct irda_usb_cb *self; + struct irda_skb_cb *cb; + struct sk_buff *new; + + IRDA_DEBUG(2, __FUNCTION__ "(), len=%d\n", purb->actual_length); + + /* Find ourselves */ + cb = (struct irda_skb_cb *) skb->cb; + self = (struct irda_usb_cb *) cb->context; + + ASSERT(self != NULL, return;); + ASSERT(cb != NULL, return;); + + /* If the network is closed or the device gone, stop everything */ + if ((!self->netopen) || (!self->present)) { + IRDA_DEBUG(0, __FUNCTION__ "(), Network is gone!\n"); + /* Don't re-submit the URB : will stall the Rx path */ + return; + } + + /* Check the status */ + if(purb->status != USB_ST_NOERROR) { + switch (purb->status) { + case USB_ST_CRC: /* -EILSEQ */ + self->stats.rx_errors++; + self->stats.rx_crc_errors++; + break; + default: + WARNING(__FUNCTION__ "(), RX status %d\n", purb->status); + break; + } + goto done; + } + + if (purb->actual_length <= USB_IRDA_HEADER) { + WARNING(__FUNCTION__ "(), empty frame!\n"); + goto done; + } + + /* Fix skb, and remove USB-IrDA header */ + skb_put(skb, purb->actual_length); + skb_pull(skb, USB_IRDA_HEADER); + + /* Don't waste a lot of memory on small IrDA frames */ + if (skb->len < RX_COPY_THRESHOLD) { + new = dev_alloc_skb(skb->len+1); + if (!new) { + self->stats.rx_dropped++; + goto done; + } + + /* Make sure IP header get aligned (IrDA header is 5 bytes) */ + skb_reserve(new, 1); + + /* Copy packet, so we can recycle the original */ + memcpy(skb_put(new, skb->len), skb->data, skb->len); + } else { + /* Deliver the original skb */ + new = skb; + skb = NULL; + } + + self->stats.rx_bytes += new->len; + self->stats.rx_packets++; + + /* Ask the networking layer to queue the packet for the IrDA stack */ + new->dev = self->netdev; + new->mac.raw = new->data; + new->protocol = htons(ETH_P_IRDA); + netif_rx(new); +done: + /* Recycle Rx URB (and possible the skb as well) */ + irda_usb_submit(self, skb, purb); +} + +static int irda_usb_net_init(struct net_device *dev) +{ + IRDA_DEBUG(0, __FUNCTION__ "()\n"); + + /* Set up to be a normal IrDA network device driver */ + irda_device_setup(dev); + + /* Insert overrides below this line! */ + + return 0; +} + +/* + * Function irda_usb_net_open (dev) + * + * Network device is taken up. Usually this is done by "ifconfig irda0 up" + * + * Note : don't mess with self->netopen - Jean II + */ +static int irda_usb_net_open(struct net_device *netdev) +{ + struct irda_usb_cb *self; + char name[8]; + int i; + + IRDA_DEBUG(0, __FUNCTION__ "()\n"); + + ASSERT(netdev != NULL, return -1;); + self = (struct irda_usb_cb *) netdev->priv; + ASSERT(self != NULL, return -1;); + + /* Can only open the device if it's there */ + if(!self->present) { + WARNING(__FUNCTION__ "(), device not present!\n"); + return -1; + } + + /* Initialise default speed and xbofs value + * (IrLAP will change that soon) */ + self->speed = -1; + self->xbofs = -1; + self->new_speed = -1; + self->new_xbofs = -1; + + /* To do *before* submitting Rx urbs and starting net Tx queue + * Jean II */ + self->netopen = 1; + + /* + * Now that everything should be initialized properly, + * Open new IrLAP layer instance to take care of us... + * Note : will send immediately a speed change... + */ + sprintf(name, "usb#%d", self->usbdev->devnum); + self->irlap = irlap_open(netdev, &self->qos, name); + ASSERT(self->irlap != NULL, return -1;); + + /* Allow IrLAP to send data to us */ + netif_start_queue(netdev); + + /* Now that we can pass data to IrLAP, allow the USB layer + * to send us some data... */ + for (i = 0; i < IU_MAX_URB; i++) + irda_usb_submit(self, NULL, &(self->rx_urb[i])); + + /* Ready to play !!! */ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * Function irda_usb_net_close (self) + * + * Network device is taken down. Usually this is done by + * "ifconfig irda0 down" + */ +static int irda_usb_net_close(struct net_device *netdev) +{ + struct irda_usb_cb *self; + int i; + + IRDA_DEBUG(0, __FUNCTION__ "()\n"); + + ASSERT(netdev != NULL, return -1;); + self = (struct irda_usb_cb *) netdev->priv; + ASSERT(self != NULL, return -1;); + + /* Clear this flag *before* unlinking the urbs and *before* + * stopping the network Tx queue - Jean II */ + self->netopen = 0; + + /* Stop network Tx queue */ + netif_stop_queue(netdev); + + /* Deallocate all the Rx path buffers (URBs and skb) */ + for (i = 0; i < IU_MAX_URB; i++) { + purb_t purb = &(self->rx_urb[i]); + struct sk_buff *skb = (struct sk_buff *) purb->context; + /* Cancel the receive command */ + usb_unlink_urb(purb); + /* The skb is ours, free it */ + if(skb) { + dev_kfree_skb(skb); + purb->context = NULL; + } + } + /* Cancel Tx and speed URB */ + usb_unlink_urb(&(self->tx_urb)); + usb_unlink_urb(&(self->speed_urb)); + /* Stop and remove instance of IrLAP */ + if (self->irlap) + irlap_close(self->irlap); + self->irlap = NULL; + + MOD_DEC_USE_COUNT; + + return 0; +} + +static void irda_usb_net_timeout(struct net_device *netdev) +{ + struct irda_usb_cb *self = netdev->priv; + purb_t purb = &(self->tx_urb); + int done = 0; /* If we have made any progress */ + + IRDA_DEBUG(0, __FUNCTION__ "(), Network layer thinks we timed out!\n"); + + if ((!self) || (!self->present)) { + WARNING(__FUNCTION__ "(), device not present!\n"); + return; + } + + WARNING("%s: Tx timed out, urb->status=%d\n", netdev->name, purb->status); + self->stats.tx_errors++; + + /* Check Tx URB */ + purb = &(self->tx_urb); + switch (purb->status) { + case -ECONNABORTED: /* Can't find proper USB_ST_* code */ + purb->status = USB_ST_NOERROR; + netif_wake_queue(self->netdev); + done = 1; + break; + case USB_ST_URB_PENDING: /* -EINPROGRESS == -115 */ + usb_unlink_urb(purb); + done = 1; + break; + default: + break; + } + /* Check speed URB */ + purb = &(self->speed_urb); + switch (purb->status) { + case -ECONNABORTED: /* Can't find proper USB_ST_* code */ + purb->status = USB_ST_NOERROR; + netif_wake_queue(self->netdev); + done = 1; + break; + case USB_ST_URB_PENDING: /* -EINPROGRESS */ + usb_unlink_urb(purb); + done = 1; + break; + default: + break; + } + /* Maybe we need a reset */ + /* if(done == 0) */ +} + +static int irda_usb_is_receiving(struct irda_usb_cb *self) +{ + return 0; /* For now */ +} + +static int irda_usb_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct irda_usb_cb *self; + int ret = 0; + + ASSERT(dev != NULL, return -1;); + self = dev->priv; + ASSERT(self != NULL, return -1;); + + IRDA_DEBUG(2, __FUNCTION__ "(), %s, (cmd=0x%X)\n", dev->name, cmd); + + /* Check if the device is still there */ + if(!self->present) + return -EFAULT; + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + /* Set the desired speed */ + self->new_speed = irq->ifr_baudrate; + irda_usb_change_speed_xbofs(self); + /* Note : will spinlock in above function */ + break; + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + irda_device_set_media_busy(self->netdev, TRUE); + break; + case SIOCGRECEIVING: /* Check if we are receiving right now */ + irq->ifr_receiving = irda_usb_is_receiving(self); + break; + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +static struct net_device_stats *irda_usb_net_get_stats(struct net_device *dev) +{ + struct irda_usb_cb *self = dev->priv; + return &self->stats; +} + +int __init usb_irda_init(void) +{ + if (usb_register(&irda_driver) < 0) + return -1; + + MESSAGE("USB IrDA support registered\n"); + return 0; +} +module_init(usb_irda_init); + +void __exit usb_irda_cleanup(void) +{ + struct irda_usb_cb *irda = NULL; + int i; + + /* Find zombie instances and kill them... */ + for (i = 0; i < NIRUSB; i++) { + irda = &irda_instance[i]; + /* If the Device is zombie */ + if((irda->usbdev != NULL) && (irda->present == 0)) { + IRDA_DEBUG(0, __FUNCTION__ "(), disconnect zombie now !\n"); + irda_usb_disconnect(irda->usbdev, (void *) irda); + } + } + + /* Deregister the driver and remove all pending instances */ + usb_deregister(&irda_driver); +} +module_exit(usb_irda_cleanup); + +MODULE_AUTHOR("Roman Weissgaerber , Dag Brattli and Jean Tourrilhes "); +MODULE_DESCRIPTION("IrDA-USB Dongle Driver"); + + + diff -urpN linux-2.4.1-pre8/drivers/usb/irda-usb.h linux-2.4.1-pre8-irda-patch/drivers/usb/irda-usb.h --- linux-2.4.1-pre8/drivers/usb/irda-usb.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.1-pre8-irda-patch/drivers/usb/irda-usb.h Mon Jan 22 00:55:47 2001 @@ -0,0 +1,125 @@ +/***************************************************************************** + * + * Filename: irda-usb.h + * Version: 0.1 + * Description: IrDA-USB Driver + * Status: Experimental + * Author: Dag Brattli + * + * Copyright (C) 2000, Roman Weissgaerber + * Copyright (C) 2000, Dag Brattli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +#include +#include +#include + +#define RX_COPY_THRESHOLD 200 +#define IRDA_USB_MAX_MTU 2050 +#define IRDA_USB_SPEED_MTU 64 /* Weird, but work like this */ + +/* Maximum number of URB on the Rx and Tx path + * This is the amount of buffers the we keep between the USB harware and the + * IrDA stack. + * Note : the network layer does also queue the packets between us and the + * IrDA stack, and is actually pretty fast and efficient in doing that. + * Therefore, we don't need to have a large number of URBs, and we can + * perfectly live happy with only one. We certainly don't need to keep the + * full IrTTP window around here... + * Having 2 URBs would allow the USB stack to process one URB while we take + * care of the other and then swap the URBs... + * On the other hand, increasing the number of URB will have penalities + * in term of latency and will interact with the link management in IrLAP... + * Jean II */ +#define IU_MAX_URB 1 /* Don't touch !!! */ + +/* Inbound header */ +#define MEDIA_BUSY 0x80 + +#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_usb_dongle { + __u32 idVendor; + __u32 idProduct; + __u32 idCapability; /* Capability of the hardware */ +}; +#define IUC_DEFAULT 0x00 /* Basic device compliant with 1.0 spec */ +#define IUC_SPEED_BUG 0x01 /* Device doesn't set speed after the frame */ +#define IUC_SIR_ONLY 0x02 /* Device doesn't behave at FIR speeds */ +#define IUC_SMALL_PKT 0x04 /* Device doesn't behave with big Rx packets */ +#define IUC_NO_WINDOW 0x08 /* Device doesn't behave with big Rx window */ +#define IUC_MAX_WINDOW 0x10 /* Device underestimate the Rx window */ +#define IUC_MAX_XBOFS 0x20 /* Device need more xbofs than advertised */ + +#define USB_IRDA_HEADER 0x01 +#define USB_CLASS_IRDA 0x02 /* USB_CLASS_APP_SPEC subclass */ +#define USB_DT_IRDA 0x21 + +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)); + +struct irda_usb_cb { + struct irda_class_desc *irda_desc; + struct usb_device *usbdev; /* init: probe_irda */ + unsigned int ifnum; /* Interface number of the USB dev. */ + int netopen; /* Device is active for network */ + int present; /* Device is present on the bus */ + __u32 capability; /* Capability of the hardware */ + char bulk_in_ep, bulk_out_ep; /* Endpoint assignments */ + wait_queue_head_t wait_q; /* for timeouts */ + + struct urb rx_urb[IU_MAX_URB]; /* URBs used to receive data frames */ + struct urb tx_urb; /* URB used to send data frames */ + struct urb speed_urb; /* URB used to send speed commands */ + + struct net_device *netdev; /* Yes! we are some kind of netdev. */ + struct net_device_stats stats; + struct irlap_cb *irlap; /* The link layer we are binded to */ + struct qos_info qos; + iobuff_t tx_buff; /* Transmit buffer */ + iobuff_t rx_buff; /* Receive buffer */ + iobuff_t speed_buff; /* Buffer for speed changes */ + + spinlock_t lock; /* For serializing operations */ + + __u16 xbofs; /* Current xbofs setting */ + __s16 new_xbofs; /* xbofs we need to set */ + __u32 speed; /* Current speed */ + __s32 new_speed; /* speed we need to set */ + __u32 flags; /* Interface flags */ +}; + + diff -urpN linux-2.4.1-pre8/include/net/irda/ali-ircc.h linux-2.4.1-pre8-irda-patch/include/net/irda/ali-ircc.h --- linux-2.4.1-pre8/include/net/irda/ali-ircc.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.1-pre8-irda-patch/include/net/irda/ali-ircc.h Mon Jan 22 01:08:40 2001 @@ -0,0 +1,229 @@ +/********************************************************************* + * + * Filename: ali-ircc.h + * Version: 0.5 + * Description: Driver for the ALI M1535D and M1543C FIR Controller + * Status: Experimental. + * Author: Benjamin Kong + * Created at: 2000/10/16 03:46PM + * Modified at: 2001/1/3 02:56PM + * Modified by: Benjamin Kong + * + * Copyright (c) 2000 Benjamin Kong + * 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. + * + ********************************************************************/ + +#ifndef ALI_IRCC_H +#define ALI_IRCC_H + +#include + +#include +#include +#include + +/* SIR Register */ +/* Usr definition of linux/serial_reg.h */ + +/* FIR Register */ +#define BANK0 0x20 +#define BANK1 0x21 +#define BANK2 0x22 +#define BANK3 0x23 + +#define FIR_MCR 0x07 /* Master Control Register */ + +/* Bank 0 */ +#define FIR_DR 0x00 /* Alias 0, FIR Data Register (R/W) */ +#define FIR_IER 0x01 /* Alias 1, FIR Interrupt Enable Register (R/W) */ +#define FIR_IIR 0x02 /* Alias 2, FIR Interrupt Identification Register (Read only) */ +#define FIR_LCR_A 0x03 /* Alias 3, FIR Line Control Register A (R/W) */ +#define FIR_LCR_B 0x04 /* Alias 4, FIR Line Control Register B (R/W) */ +#define FIR_LSR 0x05 /* Alias 5, FIR Line Status Register (R/W) */ +#define FIR_BSR 0x06 /* Alias 6, FIR Bus Status Register (Read only) */ + + + /* Alias 1 */ + #define IER_FIFO 0x10 /* FIR FIFO Interrupt Enable */ + #define IER_TIMER 0x20 /* Timer Interrupt Enable */ + #define IER_EOM 0x40 /* End of Message Interrupt Enable */ + #define IER_ACT 0x80 /* Active Frame Interrupt Enable */ + + /* Alias 2 */ + #define IIR_FIFO 0x10 /* FIR FIFO Interrupt */ + #define IIR_TIMER 0x20 /* Timer Interrupt */ + #define IIR_EOM 0x40 /* End of Message Interrupt */ + #define IIR_ACT 0x80 /* Active Frame Interrupt */ + + /* Alias 3 */ + #define LCR_A_FIFO_RESET 0x80 /* FIFO Reset */ + + /* Alias 4 */ + #define LCR_B_BW 0x10 /* Brick Wall */ + #define LCR_B_SIP 0x20 /* SIP Enable */ + #define LCR_B_TX_MODE 0x40 /* Transmit Mode */ + #define LCR_B_RX_MODE 0x80 /* Receive Mode */ + + /* Alias 5 */ + #define LSR_FIR_LSA 0x00 /* FIR Line Status Address */ + #define LSR_FRAME_ABORT 0x08 /* Frame Abort */ + #define LSR_CRC_ERROR 0x10 /* CRC Error */ + #define LSR_SIZE_ERROR 0x20 /* Size Error */ + #define LSR_FRAME_ERROR 0x40 /* Frame Error */ + #define LSR_FIFO_UR 0x80 /* FIFO Underrun */ + #define LSR_FIFO_OR 0x80 /* FIFO Overrun */ + + /* Alias 6 */ + #define BSR_FIFO_NOT_EMPTY 0x80 /* FIFO Not Empty */ + +/* Bank 1 */ +#define FIR_CR 0x00 /* Alias 0, FIR Configuration Register (R/W) */ +#define FIR_FIFO_TR 0x01 /* Alias 1, FIR FIFO Threshold Register (R/W) */ +#define FIR_DMA_TR 0x02 /* Alias 2, FIR DMA Threshold Register (R/W) */ +#define FIR_TIMER_IIR 0x03 /* Alias 3, FIR Timer interrupt interval register (W/O) */ +#define FIR_FIFO_FR 0x03 /* Alias 3, FIR FIFO Flag register (R/O) */ +#define FIR_FIFO_RAR 0x04 /* Alias 4, FIR FIFO Read Address register (R/O) */ +#define FIR_FIFO_WAR 0x05 /* Alias 5, FIR FIFO Write Address register (R/O) */ +#define FIR_TR 0x06 /* Alias 6, Test REgister (W/O) */ + + /* Alias 0 */ + #define CR_DMA_EN 0x01 /* DMA Enable */ + #define CR_DMA_BURST 0x02 /* DMA Burst Mode */ + #define CR_TIMER_EN 0x08 /* Timer Enable */ + + /* Alias 3 */ + #define TIMER_IIR_500 0x00 /* 500 us */ + #define TIMER_IIR_1ms 0x01 /* 1 ms */ + #define TIMER_IIR_2ms 0x02 /* 2 ms */ + #define TIMER_IIR_4ms 0x03 /* 4 ms */ + +/* Bank 2 */ +#define FIR_IRDA_CR 0x00 /* Alias 0, IrDA Control Register (R/W) */ +#define FIR_BOF_CR 0x01 /* Alias 1, BOF Count Register (R/W) */ +#define FIR_BW_CR 0x02 /* Alias 2, Brick Wall Count Register (R/W) */ +#define FIR_TX_DSR_HI 0x03 /* Alias 3, TX Data Size Register (high) (R/W) */ +#define FIR_TX_DSR_LO 0x04 /* Alias 4, TX Data Size Register (low) (R/W) */ +#define FIR_RX_DSR_HI 0x05 /* Alias 5, RX Data Size Register (high) (R/W) */ +#define FIR_RX_DSR_LO 0x06 /* Alias 6, RX Data Size Register (low) (R/W) */ + + /* Alias 0 */ + #define IRDA_CR_HDLC1152 0x80 /* 1.152Mbps HDLC Select */ + #define IRDA_CR_CRC 0X40 /* CRC Select. */ + #define IRDA_CR_HDLC 0x20 /* HDLC select. */ + #define IRDA_CR_HP_MODE 0x10 /* HP mode (read only) */ + #define IRDA_CR_SD_ST 0x08 /* SD/MODE State. */ + #define IRDA_CR_FIR_SIN 0x04 /* FIR SIN Select. */ + #define IRDA_CR_ITTX_0 0x02 /* SOUT State. IRTX force to 0 */ + #define IRDA_CR_ITTX_1 0x03 /* SOUT State. IRTX force to 1 */ + +/* Bank 3 */ +#define FIR_ID_VR 0x00 /* Alias 0, FIR ID Version Register (R/O) */ +#define FIR_MODULE_CR 0x01 /* Alias 1, FIR Module Control Register (R/W) */ +#define FIR_IO_BASE_HI 0x02 /* Alias 2, FIR Higher I/O Base Address Register (R/O) */ +#define FIR_IO_BASE_LO 0x03 /* Alias 3, FIR Lower I/O Base Address Register (R/O) */ +#define FIR_IRQ_CR 0x04 /* Alias 4, FIR IRQ Channel Register (R/O) */ +#define FIR_DMA_CR 0x05 /* Alias 5, FIR DMA Channel Register (R/O) */ + +struct ali_chip { + char *name; + int cfg[2]; + unsigned char entr1; + unsigned char entr2; + unsigned char cid_index; + unsigned char cid_value; + int (*probe)(struct ali_chip *chip, chipio_t *info); + int (*init)(struct ali_chip *chip, chipio_t *info); +}; +typedef struct ali_chip ali_chip_t; + + +/* DMA modes needed */ +#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */ +#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */ + +#define MAX_TX_WINDOW 7 +#define MAX_RX_WINDOW 7 + +#define TX_FIFO_Threshold 8 +#define RX_FIFO_Threshold 1 +#define TX_DMA_Threshold 1 +#define RX_DMA_Threshold 1 + +/* For storing entries in the status FIFO */ + +struct st_fifo_entry { + int status; + int len; +}; + +struct st_fifo { + struct st_fifo_entry entries[MAX_RX_WINDOW]; + int pending_bytes; + int head; + int tail; + int len; +}; + +struct frame_cb { + void *start; /* Start of frame in DMA mem */ + int len; /* Lenght of frame in DMA mem */ +}; + +struct tx_fifo { + struct frame_cb queue[MAX_TX_WINDOW]; /* Info about frames in queue */ + int ptr; /* Currently being sent */ + int len; /* Lenght of queue */ + int free; /* Next free slot */ + void *tail; /* Next free start in DMA mem */ +}; + +/* Private data for each instance */ +struct ali_ircc_cb { + + struct st_fifo st_fifo; /* Info about received frames */ + struct tx_fifo tx_fifo; /* Info about frames to be transmitted */ + + struct net_device *netdev; /* Yes! we are some kind of netdevice */ + struct net_device_stats stats; + + struct irlap_cb *irlap; /* The link layer we are binded to */ + struct qos_info qos; /* QoS capabilities for this device */ + + chipio_t io; /* IrDA controller information */ + iobuff_t tx_buff; /* Transmit buffer */ + iobuff_t rx_buff; /* Receive buffer */ + + __u8 ier; /* Interrupt enable register */ + + __u8 InterruptID; /* Interrupt ID */ + __u8 BusStatus; /* Bus Status */ + __u8 LineStatus; /* Line Status */ + + unsigned char rcvFramesOverflow; + + struct timeval stamp; + struct timeval now; + + spinlock_t lock; /* For serializing operations */ + + __u32 flags; /* Interface flags */ + __u32 new_speed; + int index; /* Instance index */ + + unsigned char fifo_opti_buf; + + struct pm_dev *dev; +}; + +static inline void switch_bank(int iobase, int bank) +{ + outb(bank, iobase+FIR_MCR); +} + +#endif /* ALI_IRCC_H */ diff -urpN linux-2.4.1-pre8/include/net/irda/irda.h linux-2.4.1-pre8-irda-patch/include/net/irda/irda.h --- linux-2.4.1-pre8/include/net/irda/irda.h Thu Jan 4 23:51:21 2001 +++ linux-2.4.1-pre8-irda-patch/include/net/irda/irda.h Mon Jan 22 00:54:00 2001 @@ -177,9 +177,10 @@ typedef union { */ struct irda_skb_cb { magic_t magic; /* Be sure that we can trust the information */ - __u32 speed; /* The Speed this frame should be sent with */ + __u32 next_speed; /* The Speed to be set *after* this frame */ __u16 mtt; /* Minimum turn around time */ __u16 xbofs; /* Number of xbofs required, used by SIR mode */ + __u16 next_xbofs; /* Number of xbofs required *after* this frame */ void *context; /* May be used by drivers */ void (*destructor)(struct sk_buff *skb); /* Used for flow control */ __u16 xbofs_delay; /* Number of xbofs used for generating the mtt */ diff -urpN linux-2.4.1-pre8/include/net/irda/irda_device.h linux-2.4.1-pre8-irda-patch/include/net/irda/irda_device.h --- linux-2.4.1-pre8/include/net/irda/irda_device.h Thu Jan 4 23:50:45 2001 +++ linux-2.4.1-pre8-irda-patch/include/net/irda/irda_device.h Mon Jan 22 00:54:00 2001 @@ -218,29 +218,54 @@ extern inline __u16 irda_get_mtt(struct #endif /* - * Function irda_get_speed (skb) + * Function irda_get_next_speed (skb) * - * Extact the speed this frame should be sent out with from the skb + * Extract the speed that should be set *after* this frame from the skb * + * Note : return -1 for user space frames */ -#define irda_get_speed(skb) ( \ +#define irda_get_next_speed(skb) ( \ (((struct irda_skb_cb*) skb->cb)->magic == LAP_MAGIC) ? \ - ((struct irda_skb_cb *)(skb->cb))->speed : 9600 \ + ((struct irda_skb_cb *)(skb->cb))->next_speed : -1 \ ) #if 0 -extern inline __u32 irda_get_speed(struct sk_buff *skb) +extern inline __u32 irda_get_next_speed(struct sk_buff *skb) { __u32 speed; if (((struct irda_skb_cb *)(skb->cb))->magic != LAP_MAGIC) - speed = 9600; + speed = -1; else - speed = ((struct irda_skb_cb *)(skb->cb))->speed; + speed = ((struct irda_skb_cb *)(skb->cb))->next_speed; return speed; } #endif + +/* + * Function irda_get_next_xbofs (skb) + * + * Extract the xbofs that should be set for this frame from the skb + * + * Note : default to 10 for user space frames + */ +#define irda_get_xbofs(skb) ( \ + (((struct irda_skb_cb*) skb->cb)->magic == LAP_MAGIC) ? \ + ((struct irda_skb_cb *)(skb->cb))->xbofs : 10 \ +) + +/* + * Function irda_get_next_xbofs (skb) + * + * Extract the xbofs that should be set *after* this frame from the skb + * + * Note : return -1 for user space frames + */ +#define irda_get_next_xbofs(skb) ( \ + (((struct irda_skb_cb*) skb->cb)->magic == LAP_MAGIC) ? \ + ((struct irda_skb_cb *)(skb->cb))->next_xbofs : -1 \ +) #endif /* IRDA_DEVICE_H */ diff -urpN linux-2.4.1-pre8/include/net/irda/irlap.h linux-2.4.1-pre8-irda-patch/include/net/irda/irlap.h --- linux-2.4.1-pre8/include/net/irda/irlap.h Thu Jan 4 23:50:45 2001 +++ linux-2.4.1-pre8-irda-patch/include/net/irda/irlap.h Mon Jan 22 00:54:00 2001 @@ -93,7 +93,9 @@ struct irlap_cb { irda_queue_t q; /* Must be first */ magic_t magic; + /* Device we are attached to */ struct net_device *netdev; + char hw_name[IFNAMSIZ + 1]; /* Connection state */ volatile IRLAP_STATE state; /* Current state */ @@ -168,7 +170,7 @@ struct irlap_cb { hashbin_t *discovery_log; discovery_t *discovery_cmd; - __u32 speed; + __u32 speed; /* Link speed */ struct qos_info qos_tx; /* QoS requested by peer */ struct qos_info qos_rx; /* QoS requested by self */ @@ -179,6 +181,7 @@ struct irlap_cb { int mtt_required; /* Minumum turnaround time required */ int xbofs_delay; /* Nr of XBOF's used to MTT */ int bofs_count; /* Negotiated extra BOFs */ + int next_bofs; /* Negotiated extra BOFs after next frame */ #ifdef CONFIG_IRDA_COMPRESSION struct irda_compressor compressor; @@ -194,7 +197,8 @@ extern hashbin_t *irlap; int irlap_init(void); void irlap_cleanup(void); -struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos); +struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos, + char * hw_name); void irlap_close(struct irlap_cb *self); void irlap_connect_request(struct irlap_cb *self, __u32 daddr, @@ -237,7 +241,7 @@ void irlap_wait_min_turn_around(struct i void irlap_init_qos_capabilities(struct irlap_cb *, struct qos_info *); void irlap_apply_default_connection_parameters(struct irlap_cb *self); -void irlap_apply_connection_parameters(struct irlap_cb *self); +void irlap_apply_connection_parameters(struct irlap_cb *self, int now); void irlap_set_local_busy(struct irlap_cb *self, int status); #define IRLAP_GET_HEADER_SIZE(self) 2 /* Will be different when we get VFIR */ diff -urpN linux-2.4.1-pre8/include/net/irda/irlap_frame.h linux-2.4.1-pre8-irda-patch/include/net/irda/irlap_frame.h --- linux-2.4.1-pre8/include/net/irda/irlap_frame.h Thu Jan 4 23:50:45 2001 +++ linux-2.4.1-pre8-irda-patch/include/net/irda/irlap_frame.h Mon Jan 22 00:54:00 2001 @@ -110,6 +110,7 @@ struct snrm_frame { __u8 ncaddr; } PACK; +void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb); void irlap_send_discovery_xid_frame(struct irlap_cb *, int S, __u8 s, __u8 command, discovery_t *discovery); void irlap_send_snrm_frame(struct irlap_cb *, struct qos_info *); diff -urpN linux-2.4.1-pre8/include/net/irda/irlmp.h linux-2.4.1-pre8-irda-patch/include/net/irda/irlmp.h --- linux-2.4.1-pre8/include/net/irda/irlmp.h Mon Dec 11 21:59:38 2000 +++ linux-2.4.1-pre8-irda-patch/include/net/irda/irlmp.h Mon Jan 22 00:54:00 2001 @@ -176,7 +176,6 @@ struct irlmp_cb { hashbin_t *services; hashbin_t *cachelog; /* Current discovery log */ - spinlock_t log_lock; /* discovery log spinlock */ int running; @@ -248,7 +247,8 @@ extern int sysctl_discovery_slots; extern int sysctl_discovery; extern struct irlmp_cb *irlmp; -static inline hashbin_t *irlmp_get_cachelog(void) { return irlmp->cachelog; } +/* This function should not exist, it's too dangerous... */ +//static inline hashbin_t *irlmp_get_cachelog(void) { return irlmp->cachelog; } static inline int irlmp_get_lap_tx_queue_len(struct lsap_cb *self) { diff -urpN linux-2.4.1-pre8/include/net/irda/irqueue.h linux-2.4.1-pre8-irda-patch/include/net/irda/irqueue.h --- linux-2.4.1-pre8/include/net/irda/irqueue.h Thu Jan 4 23:50:48 2001 +++ linux-2.4.1-pre8-irda-patch/include/net/irda/irqueue.h Mon Jan 22 00:54:00 2001 @@ -36,12 +36,14 @@ #define NAME_SIZE 32 /* - * Hash types + * Hash types (some flags can be xored) + * See comments in irqueue.c for which one to use... */ -#define HB_NOLOCK 0 -#define HB_GLOBAL 1 -#define HB_LOCAL 2 -#define HB_SORTED 4 +#define HB_NOLOCK 0 /* No concurent access prevention */ +#define HB_GLOBAL 1 /* Prevent concurent write across processors */ +#define HB_LOCAL 2 /* Deprecated (not SMP compatible) */ +#define HB_SORTED 4 /* Not yet supported */ +#define HB_LOCK 8 /* Prevent concurent write with global lock */ /* * Hash defines @@ -79,6 +81,8 @@ typedef struct hashbin_t { irda_queue_t *hb_queue[HASHBIN_SIZE] ALIGN; irda_queue_t* hb_current; + + spinlock_t hb_spinlock; /* To be used by the user */ } hashbin_t; hashbin_t *hashbin_new(int type); diff -urpN linux-2.4.1-pre8/include/net/irda/qos.h linux-2.4.1-pre8-irda-patch/include/net/irda/qos.h --- linux-2.4.1-pre8/include/net/irda/qos.h Thu Jan 4 23:51:20 2001 +++ linux-2.4.1-pre8-irda-patch/include/net/irda/qos.h Mon Jan 22 00:54:00 2001 @@ -100,10 +100,19 @@ void irda_qos_compute_intersection(struc __u32 irlap_max_line_capacity(__u32 speed, __u32 max_turn_time); __u32 irlap_requested_line_capacity(struct qos_info *qos); -__u32 irlap_min_turn_time_in_bytes(__u32 speed, __u32 min_turn_time); int msb_index(__u16 byte); void irda_qos_bits_to_value(struct qos_info *qos); + +/* So simple, how could we not inline those two ? + * Note : one byte is 10 bits if you include start and stop bits + * Jean II */ +#define irlap_min_turn_time_in_bytes(speed, min_turn_time) ( \ + speed * min_turn_time / 10000000 \ +) +#define irlap_xbofs_in_usec(speed, xbofs) ( \ + xbofs * 10000000 / speed \ +) #endif diff -urpN linux-2.4.1-pre8/init/main.c linux-2.4.1-pre8-irda-patch/init/main.c --- linux-2.4.1-pre8/init/main.c Thu Jan 4 05:45:26 2001 +++ linux-2.4.1-pre8-irda-patch/init/main.c Mon Jan 22 00:53:49 2001 @@ -726,6 +726,7 @@ static void __init do_basic_setup(void) filesystem_setup(); #ifdef CONFIG_IRDA + irda_proto_init(); irda_device_init(); /* Must be done after protocol initialization */ #endif #ifdef CONFIG_PCMCIA diff -urpN linux-2.4.1-pre8/net/irda/af_irda.c linux-2.4.1-pre8-irda-patch/net/irda/af_irda.c --- linux-2.4.1-pre8/net/irda/af_irda.c Sun Nov 12 03:11:23 2000 +++ linux-2.4.1-pre8-irda-patch/net/irda/af_irda.c Mon Jan 22 00:54:42 2001 @@ -439,7 +439,7 @@ static void irda_selective_discovery_ind * We were waiting for a node to be discovered, but nothing has come up * so far. Wake up the user and tell him that we failed... */ -static void irda_discovery_timeout(u_long priv) +static void irda_discovery_timeout(u_long priv) { struct irda_sock *self; @@ -776,7 +776,6 @@ static int irda_bind(struct socket *sock struct sock *sk = sock->sk; struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr; struct irda_sock *self; - __u16 hints = 0; int err; IRDA_DEBUG(2, __FUNCTION__ "()\n"); @@ -821,15 +820,6 @@ static int irda_bind(struct socket *sock self->stsap_sel, IAS_KERNEL_ATTR); irias_insert_object(self->ias_obj); -#if 1 /* Will be removed in near future */ - - /* Fill in some default hint bits values */ - if (strncmp(addr->sir_name, "OBEX", 4) == 0) - hints = irlmp_service_to_hint(S_OBEX); - - if (hints) - self->skey = irlmp_register_service(hints); -#endif return 0; } @@ -2321,6 +2311,9 @@ static struct proto_ops SOCKOPS_WRAPPED( SOCKOPS_WRAP(irda_stream, PF_IRDA); SOCKOPS_WRAP(irda_seqpacket, PF_IRDA); SOCKOPS_WRAP(irda_dgram, PF_IRDA); +#ifdef CONFIG_IRDA_ULTRA +SOCKOPS_WRAP(irda_ultra, PF_IRDA); +#endif /* CONFIG_IRDA_ULTRA */ /* * Function irda_device_event (this, event, ptr) diff -urpN linux-2.4.1-pre8/net/irda/discovery.c linux-2.4.1-pre8-irda-patch/net/irda/discovery.c --- linux-2.4.1-pre8/net/irda/discovery.c Sun Nov 12 03:11:23 2000 +++ linux-2.4.1-pre8-irda-patch/net/irda/discovery.c Mon Jan 22 00:54:45 2001 @@ -61,7 +61,7 @@ void irlmp_add_discovery(hashbin_t *cach /* Set time of first discovery if node is new (see below) */ new->first_timestamp = new->timestamp; - spin_lock_irqsave(&irlmp->log_lock, flags); + spin_lock_irqsave(&cachelog->hb_spinlock, flags); /* * Remove all discoveries of devices that has previously been @@ -95,7 +95,7 @@ void irlmp_add_discovery(hashbin_t *cach /* Insert the new and updated version */ hashbin_insert(cachelog, (irda_queue_t *) new, new->daddr, NULL); - spin_unlock_irqrestore(&irlmp->log_lock, flags); + spin_unlock_irqrestore(&cachelog->hb_spinlock, flags); } /* @@ -146,7 +146,7 @@ void irlmp_expire_discoveries(hashbin_t IRDA_DEBUG(4, __FUNCTION__ "()\n"); - spin_lock_irqsave(&irlmp->log_lock, flags); + spin_lock_irqsave(&log->hb_spinlock, flags); discovery = (discovery_t *) hashbin_get_first(log); while (discovery != NULL) { @@ -169,7 +169,7 @@ void irlmp_expire_discoveries(hashbin_t } } - spin_unlock_irqrestore(&irlmp->log_lock, flags); + spin_unlock_irqrestore(&log->hb_spinlock, flags); } /* @@ -230,13 +230,13 @@ struct irda_device_info *irlmp_copy_disc return NULL; /* Save spin lock - spinlock should be discovery specific */ - spin_lock_irqsave(&irlmp->log_lock, flags); + spin_lock_irqsave(&log->hb_spinlock, flags); /* Create the client specific buffer */ n = HASHBIN_GET_SIZE(log); buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC); if (buffer == NULL) { - spin_unlock_irqrestore(&irlmp->log_lock, flags); + spin_unlock_irqrestore(&log->hb_spinlock, flags); return NULL; } @@ -257,7 +257,7 @@ struct irda_device_info *irlmp_copy_disc discovery = (discovery_t *) hashbin_get_next(log); } - spin_unlock_irqrestore(&irlmp->log_lock, flags); + spin_unlock_irqrestore(&log->hb_spinlock, flags); /* Get the actual number of device in the buffer and return */ *pn = i; @@ -276,7 +276,7 @@ __u32 irlmp_find_device(hashbin_t *cache unsigned long flags; discovery_t *d; - spin_lock_irqsave(&irlmp->log_lock, flags); + spin_lock_irqsave(&cachelog->hb_spinlock, flags); /* Look at all discoveries for that link */ d = (discovery_t *) hashbin_get_first(cachelog); @@ -288,13 +288,13 @@ __u32 irlmp_find_device(hashbin_t *cache if (strcmp(name, d->nickname) == 0) { *saddr = d->saddr; - spin_unlock_irqrestore(&irlmp->log_lock, flags); + spin_unlock_irqrestore(&cachelog->hb_spinlock, flags); return d->daddr; } d = (discovery_t *) hashbin_get_next(cachelog); } - spin_unlock_irqrestore(&irlmp->log_lock, flags); + spin_unlock_irqrestore(&cachelog->hb_spinlock, flags); return 0; } @@ -310,7 +310,7 @@ int discovery_proc_read(char *buf, char { discovery_t *discovery; unsigned long flags; - hashbin_t *cachelog = irlmp_get_cachelog(); + hashbin_t *cachelog = irlmp->cachelog; int len = 0; if (!irlmp) @@ -318,7 +318,7 @@ int discovery_proc_read(char *buf, char len = sprintf(buf, "IrLMP: Discovery log:\n\n"); - spin_lock_irqsave(&irlmp->log_lock, flags); + spin_lock_irqsave(&cachelog->hb_spinlock, flags); discovery = (discovery_t *) hashbin_get_first(cachelog); while (( discovery != NULL) && (len < length)) { @@ -362,7 +362,7 @@ int discovery_proc_read(char *buf, char discovery = (discovery_t *) hashbin_get_next(cachelog); } - spin_unlock_irqrestore(&irlmp->log_lock, flags); + spin_unlock_irqrestore(&cachelog->hb_spinlock, flags); return len; } diff -urpN linux-2.4.1-pre8/net/irda/ircomm/ircomm_core.c linux-2.4.1-pre8-irda-patch/net/irda/ircomm/ircomm_core.c --- linux-2.4.1-pre8/net/irda/ircomm/ircomm_core.c Tue Nov 28 03:07:31 2000 +++ linux-2.4.1-pre8-irda-patch/net/irda/ircomm/ircomm_core.c Mon Jan 22 00:54:42 2001 @@ -61,7 +61,7 @@ hashbin_t *ircomm = NULL; int __init ircomm_init(void) { - ircomm = hashbin_new(HB_LOCAL); + ircomm = hashbin_new(HB_LOCK); if (ircomm == NULL) { ERROR(__FUNCTION__ "(), can't allocate hashbin!\n"); return -ENOMEM; diff -urpN linux-2.4.1-pre8/net/irda/ircomm/ircomm_tty.c linux-2.4.1-pre8-irda-patch/net/irda/ircomm/ircomm_tty.c --- linux-2.4.1-pre8/net/irda/ircomm/ircomm_tty.c Tue Nov 28 03:07:31 2000 +++ linux-2.4.1-pre8-irda-patch/net/irda/ircomm/ircomm_tty.c Mon Jan 22 00:54:43 2001 @@ -91,7 +91,7 @@ hashbin_t *ircomm_tty = NULL; */ int __init ircomm_tty_init(void) { - ircomm_tty = hashbin_new(HB_LOCAL); + ircomm_tty = hashbin_new(HB_LOCK); if (ircomm_tty == NULL) { ERROR(__FUNCTION__ "(), can't allocate hashbin!\n"); return -ENOMEM; diff -urpN linux-2.4.1-pre8/net/irda/iriap.c linux-2.4.1-pre8-irda-patch/net/irda/iriap.c --- linux-2.4.1-pre8/net/irda/iriap.c Sun Nov 12 03:11:22 2000 +++ linux-2.4.1-pre8-irda-patch/net/irda/iriap.c Mon Jan 22 00:54:43 2001 @@ -88,12 +88,13 @@ int __init iriap_init(void) __u8 oct_seq[6]; __u16 hints; - /* Allocate master array */ - iriap = hashbin_new(HB_LOCAL); + /* Allocate master array - HB_GLOBAL is plenty good enough */ + iriap = hashbin_new(HB_GLOBAL); if (!iriap) return -ENOMEM; - objects = hashbin_new(HB_LOCAL); + /* Object repository - defined in irias_object.c */ + objects = hashbin_new(HB_LOCK); if (!objects) { WARNING(__FUNCTION__ "(), Can't allocate objects hashbin!\n"); return -ENOMEM; @@ -968,24 +969,22 @@ static char *ias_value_types[] = { "IAS_STRING" }; -int irias_proc_read(char *buf, char **start, off_t offset, int len) +int irias_proc_read(char *buf, char **start, off_t offset, int length) { struct ias_object *obj; struct ias_attrib *attrib; unsigned long flags; + int len = 0; ASSERT( objects != NULL, return 0;); - save_flags( flags); - cli(); + spin_lock_irqsave(&objects->hb_spinlock, flags); - len = 0; - - len += sprintf(buf+len, "LM-IAS Objects:\n"); + len = sprintf(buf+len, "LM-IAS Objects:\n"); /* List all objects */ obj = (struct ias_object *) hashbin_get_first(objects); - while ( obj != NULL) { + while (( obj != NULL) && (len < length)) { ASSERT(obj->magic == IAS_OBJECT_MAGIC, return 0;); len += sprintf(buf+len, "name: %s, ", obj->name); @@ -1030,7 +1029,7 @@ int irias_proc_read(char *buf, char **st } obj = (struct ias_object *) hashbin_get_next(objects); } - restore_flags(flags); + spin_unlock_irqrestore(&objects->hb_spinlock, flags); return len; } diff -urpN linux-2.4.1-pre8/net/irda/irias_object.c linux-2.4.1-pre8-irda-patch/net/irda/irias_object.c --- linux-2.4.1-pre8/net/irda/irias_object.c Mon Jan 1 18:54:07 2001 +++ linux-2.4.1-pre8-irda-patch/net/irda/irias_object.c Mon Jan 22 00:54:43 2001 @@ -34,7 +34,7 @@ hashbin_t *objects = NULL; /* * Used when a missing value needs to be returned */ -struct ias_value missing = { IAS_MISSING, 0, 0, 0}; +struct ias_value missing = { IAS_MISSING, 0, 0, 0, {0}}; /* * Function strdup (str) @@ -84,7 +84,9 @@ struct ias_object *irias_new_object( cha obj->name = strdup( name); obj->id = id; - obj->attribs = hashbin_new(HB_LOCAL); + /* We use the master lock objects->hb_spinlock, so we don't need + * to lock those guys */ + obj->attribs = hashbin_new(HB_NOLOCK); return obj; } @@ -157,13 +159,16 @@ int irias_delete_object(struct ias_objec int irias_delete_attrib(struct ias_object *obj, struct ias_attrib *attrib) { struct ias_attrib *node; + unsigned long flags; ASSERT(obj != NULL, return -1;); ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -1;); ASSERT(attrib != NULL, return -1;); /* Remove atribute from object */ + spin_lock_irqsave(&objects->hb_spinlock, flags); node = hashbin_remove(obj->attribs, 0, attrib->name); + spin_unlock_irqrestore(&objects->hb_spinlock, flags); if (!node) return 0; /* Already removed or non-existent */ @@ -200,9 +205,16 @@ void irias_insert_object(struct ias_obje */ struct ias_object *irias_find_object(char *name) { + unsigned long flags; + struct ias_object *entry; ASSERT(name != NULL, return NULL;); - return hashbin_find(objects, 0, name); + spin_lock_irqsave(&objects->hb_spinlock, flags); + entry = hashbin_find(objects, 0, name); + spin_unlock_irqrestore(&objects->hb_spinlock, flags); + + /* This is unsafe, but what should we do ? - Jean II */ + return(entry); } /* @@ -213,16 +225,20 @@ struct ias_object *irias_find_object(cha */ struct ias_attrib *irias_find_attrib(struct ias_object *obj, char *name) { + unsigned long flags; struct ias_attrib *attrib; ASSERT(obj != NULL, return NULL;); ASSERT(obj->magic == IAS_OBJECT_MAGIC, return NULL;); ASSERT(name != NULL, return NULL;); + spin_lock_irqsave(&objects->hb_spinlock, flags); attrib = hashbin_find(obj->attribs, 0, name); + spin_unlock_irqrestore(&objects->hb_spinlock, flags); if (attrib == NULL) return NULL; + /* This is unsafe, but what should we do ? - Jean II */ return attrib; } @@ -235,6 +251,8 @@ struct ias_attrib *irias_find_attrib(str void irias_add_attrib( struct ias_object *obj, struct ias_attrib *attrib, int owner) { + unsigned long flags; + ASSERT(obj != NULL, return;); ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); @@ -244,7 +262,9 @@ void irias_add_attrib( struct ias_object /* Set if attrib is owned by kernel or user space */ attrib->value->owner = owner; + spin_lock_irqsave(&objects->hb_spinlock, flags); hashbin_insert(obj->attribs, (irda_queue_t *) attrib, 0, attrib->name); + spin_unlock_irqrestore(&objects->hb_spinlock, flags); } /* @@ -258,10 +278,15 @@ int irias_object_change_attribute(char * { struct ias_object *obj; struct ias_attrib *attrib; + unsigned long flags; + + /* Protect ourselves */ + spin_lock_irqsave(&objects->hb_spinlock, flags); /* Find object */ obj = hashbin_find(objects, 0, obj_name); if (obj == NULL) { + spin_unlock_irqrestore(&objects->hb_spinlock, flags); WARNING(__FUNCTION__ "(), Unable to find object: %s\n", obj_name); return -1; @@ -270,12 +295,14 @@ int irias_object_change_attribute(char * /* Find attribute */ attrib = hashbin_find(obj->attribs, 0, attrib_name); if (attrib == NULL) { + spin_unlock_irqrestore(&objects->hb_spinlock, flags); WARNING(__FUNCTION__ "(), Unable to find attribute: %s\n", attrib_name); return -1; } if ( attrib->value->type != new_value->type) { + spin_unlock_irqrestore(&objects->hb_spinlock, flags); IRDA_DEBUG( 0, __FUNCTION__ "(), changing value type not allowed!\n"); return -1; @@ -286,6 +313,9 @@ int irias_object_change_attribute(char * /* Insert new value */ attrib->value = new_value; + + /* Finished */ + spin_unlock_irqrestore(&objects->hb_spinlock, flags); /* Success */ return 0; diff -urpN linux-2.4.1-pre8/net/irda/irlan/irlan_client.c linux-2.4.1-pre8-irda-patch/net/irda/irlan/irlan_client.c --- linux-2.4.1-pre8/net/irda/irlan/irlan_client.c Sun Nov 12 03:11:23 2000 +++ linux-2.4.1-pre8-irda-patch/net/irda/irlan/irlan_client.c Mon Jan 22 00:54:43 2001 @@ -120,8 +120,9 @@ void irlan_client_wakeup(struct irlan_cb return; } - /* Address may have changed! */ + /* Addresses may have changed! */ self->saddr = saddr; + self->daddr = daddr; if (self->disconnect_reason == LM_USER_REQUEST) { IRDA_DEBUG(0, __FUNCTION__ "(), still stopped by user\n"); diff -urpN linux-2.4.1-pre8/net/irda/irlan/irlan_client_event.c linux-2.4.1-pre8-irda-patch/net/irda/irlan/irlan_client_event.c --- linux-2.4.1-pre8/net/irda/irlan/irlan_client_event.c Tue Nov 28 03:07:31 2000 +++ linux-2.4.1-pre8-irda-patch/net/irda/irlan/irlan_client_event.c Mon Jan 22 00:54:44 2001 @@ -109,6 +109,7 @@ static int irlan_client_state_idle(struc irlan_client_get_value_confirm); /* Get some values from peer IAS */ irlan_next_client_state(self, IRLAN_QUERY); + IRDA_DEBUG(0, __FUNCTION__ "(), daddr=%x\n", self->daddr); iriap_getvaluebyclass_request(self->client.iriap, self->saddr, self->daddr, "IrLAN", "IrDA:TinyTP:LsapSel"); @@ -147,6 +148,7 @@ static int irlan_client_state_query(stru self->client.open_retries = 0; + IRDA_DEBUG(0, __FUNCTION__ "(), daddr=%x\n", self->daddr); irttp_connect_request(self->client.tsap_ctrl, self->dtsap_sel_ctrl, self->saddr, self->daddr, NULL, diff -urpN linux-2.4.1-pre8/net/irda/irlan/irlan_common.c linux-2.4.1-pre8-irda-patch/net/irda/irlan/irlan_common.c --- linux-2.4.1-pre8/net/irda/irlan/irlan_common.c Sun Nov 12 03:11:23 2000 +++ linux-2.4.1-pre8-irda-patch/net/irda/irlan/irlan_common.c Mon Jan 22 00:54:44 2001 @@ -123,8 +123,9 @@ int __init irlan_init(void) __u16 hints; IRDA_DEBUG(0, __FUNCTION__ "()\n"); + /* Allocate master structure */ - irlan = hashbin_new(HB_LOCAL); + irlan = hashbin_new(HB_LOCK); if (irlan == NULL) { printk(KERN_WARNING "IrLAN: Can't allocate hashbin!\n"); return -ENOMEM; @@ -157,7 +158,7 @@ int __init irlan_init(void) void irlan_cleanup(void) { - IRDA_DEBUG(4, __FUNCTION__ "()\n"); + IRDA_DEBUG(0, __FUNCTION__ "()\n"); irlmp_unregister_client(ckey); irlmp_unregister_service(skey); @@ -208,7 +209,7 @@ struct irlan_cb *irlan_open(__u32 saddr, { struct irlan_cb *self; - IRDA_DEBUG(2, __FUNCTION__ "()\n"); + IRDA_DEBUG(0, __FUNCTION__ "()\n"); /* * Initialize the irlan structure. @@ -265,7 +266,7 @@ static void __irlan_close(struct irlan_c { struct sk_buff *skb; - IRDA_DEBUG(2, __FUNCTION__ "()\n"); + IRDA_DEBUG(0, __FUNCTION__ "()\n"); ASSERT(self != NULL, return;); ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -1214,6 +1215,5 @@ void cleanup_module(void) /* Free some memory */ irlan_cleanup(); } - #endif /* MODULE */ diff -urpN linux-2.4.1-pre8/net/irda/irlan/irlan_eth.c linux-2.4.1-pre8-irda-patch/net/irda/irlan/irlan_eth.c --- linux-2.4.1-pre8/net/irda/irlan/irlan_eth.c Sun Nov 12 03:11:23 2000 +++ linux-2.4.1-pre8-irda-patch/net/irda/irlan/irlan_eth.c Mon Jan 22 00:54:44 2001 @@ -61,6 +61,7 @@ int irlan_eth_init(struct net_device *de dev->hard_start_xmit = irlan_eth_xmit; dev->get_stats = irlan_eth_get_stats; dev->set_multicast_list = irlan_eth_set_multicast_list; + dev->features |= NETIF_F_DYNALLOC; ether_setup(dev); diff -urpN linux-2.4.1-pre8/net/irda/irlap.c linux-2.4.1-pre8-irda-patch/net/irda/irlap.c --- linux-2.4.1-pre8/net/irda/irlap.c Sun Nov 12 03:11:23 2000 +++ linux-2.4.1-pre8-irda-patch/net/irda/irlap.c Mon Jan 22 00:54:45 2001 @@ -72,14 +72,14 @@ int irlap_proc_read(char *, char **, off int __init irlap_init(void) { /* Allocate master array */ - irlap = hashbin_new(HB_LOCAL); + irlap = hashbin_new(HB_GLOBAL); if (irlap == NULL) { ERROR(__FUNCTION__ "(), can't allocate irlap hashbin!\n"); return -ENOMEM; } #ifdef CONFIG_IRDA_COMPRESSION - irlap_compressors = hashbin_new(HB_LOCAL); + irlap_compressors = hashbin_new(HB_GLOBAL); if (irlap_compressors == NULL) { WARNING(__FUNCTION__ "(), can't allocate compressors hashbin!\n"); @@ -107,7 +107,8 @@ void irlap_cleanup(void) * Initialize IrLAP layer * */ -struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos) +struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos, + char * hw_name) { struct irlap_cb *self; @@ -124,6 +125,13 @@ struct irlap_cb *irlap_open(struct net_d /* Make a binding between the layers */ self->netdev = dev; self->qos_dev = qos; + /* Copy hardware name */ + if(hw_name != NULL) { + strncpy(self->hw_name, hw_name, IFNAMSIZ); + self->hw_name[IFNAMSIZ] = '\0'; + } else { + self->hw_name[0] = '\0'; + } /* FIXME: should we get our own field? */ dev->atalk_ptr = self; @@ -537,8 +545,9 @@ void irlap_discovery_request(struct irla hashbin_delete(self->discovery_log, (FREE_FUNC) kfree); self->discovery_log = NULL; } - - self->discovery_log= hashbin_new(HB_LOCAL); + + /* All operations will occur a predictable time, no need to lock */ + self->discovery_log= hashbin_new(HB_NOLOCK); info.S = discovery->nslots; /* Number of slots */ info.s = 0; /* Current slot */ @@ -859,13 +868,6 @@ void irlap_flush_all_queues(struct irlap /* Free sliding window buffered packets */ while ((skb = skb_dequeue(&self->wx_list)) != NULL) dev_kfree_skb(skb); - -#ifdef CONFIG_IRDA_RECYCLE_RR - if (self->recycle_rr_skb) { - dev_kfree_skb(self->recycle_rr_skb); - self->recycle_rr_skb = NULL; - } -#endif } /* @@ -998,7 +1000,7 @@ void irlap_init_qos_capabilities(struct } /* - * Function irlap_apply_default_connection_parameters (void) + * Function irlap_apply_default_connection_parameters (void, now) * * Use the default connection and transmission parameters * @@ -1010,14 +1012,16 @@ void irlap_apply_default_connection_para ASSERT(self != NULL, return;); ASSERT(self->magic == LAP_MAGIC, return;); + /* xbofs : Default value in NDM */ + self->next_bofs = 12; + self->bofs_count = 12; + + /* NDM Speed is 9600 */ irlap_change_speed(self, 9600, TRUE); /* Set mbusy when going to NDM state */ irda_device_set_media_busy(self->netdev, TRUE); - /* Default value in NDM */ - self->bofs_count = 12; - /* * Generate random connection address for this session, which must * be 7 bits wide and different from 0x00 and 0xfe @@ -1056,23 +1060,31 @@ void irlap_apply_default_connection_para } /* - * Function irlap_apply_connection_parameters (qos) + * Function irlap_apply_connection_parameters (qos, now) * * Initialize IrLAP with the negotiated QoS values * + * If 'now' is false, the speed and xbofs will be changed after the next + * frame is sent. + * If 'now' is true, the speed and xbofs is changed immediately */ -void irlap_apply_connection_parameters(struct irlap_cb *self) +void irlap_apply_connection_parameters(struct irlap_cb *self, int now) { IRDA_DEBUG(4, __FUNCTION__ "()\n"); ASSERT(self != NULL, return;); ASSERT(self->magic == LAP_MAGIC, return;); - irlap_change_speed(self, self->qos_tx.baud_rate.value, FALSE); + /* Set the negociated xbofs value */ + self->next_bofs = self->qos_tx.additional_bofs.value; + if(now) + self->bofs_count = self->next_bofs; + + /* Set the negociated link speed (may need the new xbofs value) */ + irlap_change_speed(self, self->qos_tx.baud_rate.value, now); self->window_size = self->qos_tx.window_size.value; self->window = self->qos_tx.window_size.value; - self->bofs_count = self->qos_tx.additional_bofs.value; /* * Calculate how many bytes it is possible to transmit before the @@ -1173,6 +1185,10 @@ int irlap_proc_read(char *buf, char **st len += sprintf(buf+len, "state: %s\n", irlap_state[self->state]); + len += sprintf(buf+len, " device name: %s, ", + (self->netdev) ? self->netdev->name : "bug"); + len += sprintf(buf+len, "hardware name: %s\n", self->hw_name); + len += sprintf(buf+len, " caddr: %#02x, ", self->caddr); len += sprintf(buf+len, "saddr: %#08x, ", self->saddr); len += sprintf(buf+len, "daddr: %#08x\n", self->daddr); diff -urpN linux-2.4.1-pre8/net/irda/irlap_event.c linux-2.4.1-pre8-irda-patch/net/irda/irlap_event.c --- linux-2.4.1-pre8/net/irda/irlap_event.c Tue Nov 28 03:07:31 2000 +++ linux-2.4.1-pre8-irda-patch/net/irda/irlap_event.c Mon Jan 22 00:54:45 2001 @@ -684,25 +684,26 @@ static int irlap_state_conn(struct irlap irlap_initiate_connection_state(self); -#if 0 /* - * We are allowed to send two frames, but this may increase - * the connect latency, so lets not do it for now. + * Applying the parameters now will make sure we change speed + * *after* we have sent the next frame */ - irlap_send_ua_response_frame(self, &self->qos_rx); -#endif + irlap_apply_connection_parameters(self, FALSE); /* - * Applying the parameters now will make sure we change speed - * after we have sent the next frame + * Sending this frame will force a speed change after it has + * been sent (i.e. the frame will be sent at 9600). */ - irlap_apply_connection_parameters(self); + irlap_send_ua_response_frame(self, &self->qos_rx); +#if 0 /* - * Sending this frame will force a speed change after it has - * been sent + * We are allowed to send two frames, but this may increase + * the connect latency, so lets not do it for now. */ + /* What the hell is this ? - Jean II */ irlap_send_ua_response_frame(self, &self->qos_rx); +#endif /* * The WD-timer could be set to the duration of the P-timer @@ -794,8 +795,9 @@ static int irlap_state_setup(struct irla irlap_qos_negotiate(self, skb); + /* Send UA frame and then change link settings */ + irlap_apply_connection_parameters(self, FALSE); irlap_send_ua_response_frame(self, &self->qos_rx); - irlap_apply_connection_parameters(self); irlap_next_state(self, LAP_NRM_S); irlap_connect_confirm(self, skb); @@ -827,10 +829,19 @@ static int irlap_state_setup(struct irla irlap_qos_negotiate(self, skb); - irlap_apply_connection_parameters(self); + /* Set the new link setting *now* (before the rr frame) */ + irlap_apply_connection_parameters(self, TRUE); self->retry_count = 0; - /* This frame will actually force the speed change */ + /* Wait for turnaround time to give a chance to the other + * device to be ready to receive us. + * Note : the time to switch speed is typically larger + * than the turnaround time, but as we don't have the other + * side speed switch time, that's our best guess... + * Jean II */ + irlap_wait_min_turn_around(self, &self->qos_tx); + + /* This frame will actually be sent at the new speed */ irlap_send_rr_frame(self, CMD_FRAME); irlap_start_final_timer(self, self->final_timeout/2); @@ -991,7 +1002,8 @@ static int irlap_state_pclose(struct irl case RECV_UA_RSP: /* FALLTHROUGH */ case RECV_DM_RSP: del_timer(&self->final_timer); - + + /* Set new link parameters */ irlap_apply_default_connection_parameters(self); /* Always switch state before calling upper layers */ @@ -1944,10 +1956,13 @@ static int irlap_state_nrm_s(struct irla /* Always switch state before calling upper layers */ irlap_next_state(self, LAP_NDM); + /* Send disconnect response */ irlap_wait_min_turn_around(self, &self->qos_tx); irlap_send_ua_response_frame(self, NULL); + del_timer(&self->wd_timer); irlap_flush_all_queues(self); + /* Set default link parameters */ irlap_apply_default_connection_parameters(self); irlap_disconnect_indication(self, LAP_DISC_INDICATION); @@ -2000,10 +2015,13 @@ static int irlap_state_sclose(struct irl case RECV_DISC_CMD: /* Always switch state before calling upper layers */ irlap_next_state(self, LAP_NDM); - + + /* Send disconnect response */ irlap_wait_min_turn_around(self, &self->qos_tx); irlap_send_ua_response_frame(self, NULL); + del_timer(&self->wd_timer); + /* Set default link parameters */ irlap_apply_default_connection_parameters(self); irlap_disconnect_indication(self, LAP_DISC_INDICATION); diff -urpN linux-2.4.1-pre8/net/irda/irlap_frame.c linux-2.4.1-pre8-irda-patch/net/irda/irlap_frame.c --- linux-2.4.1-pre8/net/irda/irlap_frame.c Sun Nov 12 03:11:23 2000 +++ linux-2.4.1-pre8-irda-patch/net/irda/irlap_frame.c Mon Jan 22 00:54:45 2001 @@ -60,7 +60,7 @@ static inline void irlap_insert_info(str */ cb->magic = LAP_MAGIC; cb->mtt = self->mtt_required; - cb->speed = self->speed; + cb->next_speed = self->speed; /* Reset */ self->mtt_required = 0; @@ -70,10 +70,13 @@ static inline void irlap_insert_info(str * force the negotiated minimum turnaround time */ cb->xbofs = self->bofs_count; + cb->next_xbofs = self->next_bofs; cb->xbofs_delay = self->xbofs_delay; /* Reset XBOF's delay (used only for getting min turn time) */ self->xbofs_delay = 0; + /* Put the correct xbofs value for the next packet */ + self->bofs_count = self->next_bofs; } /* diff -urpN linux-2.4.1-pre8/net/irda/irlmp.c linux-2.4.1-pre8-irda-patch/net/irda/irlmp.c --- linux-2.4.1-pre8/net/irda/irlmp.c Sun Nov 12 03:11:23 2000 +++ linux-2.4.1-pre8-irda-patch/net/irda/irlmp.c Mon Jan 22 00:54:45 2001 @@ -82,13 +82,13 @@ int __init irlmp_init(void) memset(irlmp, 0, sizeof(struct irlmp_cb)); irlmp->magic = LMP_MAGIC; - spin_lock_init(&irlmp->log_lock); irlmp->clients = hashbin_new(HB_GLOBAL); irlmp->services = hashbin_new(HB_GLOBAL); irlmp->links = hashbin_new(HB_GLOBAL); irlmp->unconnected_lsaps = hashbin_new(HB_GLOBAL); - irlmp->cachelog = hashbin_new(HB_GLOBAL); + irlmp->cachelog = hashbin_new(HB_NOLOCK); + spin_lock_init(&irlmp->cachelog->hb_spinlock); irlmp->free_lsap_sel = 0x10; /* Reserved 0x00-0x0f */ #ifdef CONFIG_IRDA_CACHE_LAST_LSAP @@ -336,7 +336,6 @@ int irlmp_connect_request(struct lsap_cb struct sk_buff *skb = NULL; struct lap_cb *lap; struct lsap_cb *lsap; - discovery_t *discovery; ASSERT(self != NULL, return -EBADR;); ASSERT(self->magic == LMP_LSAP_MAGIC, return -EBADR;); @@ -377,6 +376,10 @@ int irlmp_connect_request(struct lsap_cb * device with the given daddr */ if (!saddr) { + discovery_t *discovery; + unsigned long flags; + + spin_lock_irqsave(&irlmp->cachelog->hb_spinlock, flags); if (daddr != DEV_ADDR_ANY) discovery = hashbin_find(irlmp->cachelog, daddr, NULL); else { @@ -389,6 +392,7 @@ int irlmp_connect_request(struct lsap_cb saddr = discovery->saddr; daddr = discovery->daddr; } + spin_unlock_irqrestore(&irlmp->cachelog->hb_spinlock, flags); } lap = hashbin_find(irlmp->links, saddr, NULL); if (lap == NULL) { diff -urpN linux-2.4.1-pre8/net/irda/irqueue.c linux-2.4.1-pre8-irda-patch/net/irda/irqueue.c --- linux-2.4.1-pre8/net/irda/irqueue.c Thu Jan 4 22:00:55 2001 +++ linux-2.4.1-pre8-irda-patch/net/irda/irqueue.c Mon Jan 22 00:54:45 2001 @@ -38,6 +38,75 @@ #include #include +/* + * Notes on the concurent access to hashbin and other SMP issues + * ------------------------------------------------------------- + * Hashbins are very often in the IrDA stack a global repository of + * information, and therefore used in a very asynchronous manner following + * various events (driver calls, timers, user calls...). + * Therefore, very often it is highly important to consider the + * management of concurent access to the hashbin and how to guarantee the + * consistency of the operations on it. + * + * If you define your hasbin with the flag HB_GLOBAL, the library + * will use spinlock to protect properly the following operations : + * o hashbin creation with hashbin_new() + * o inserting an element with hashbin_insert() + * o removing an element through hashbin_remove() & hashbin_remove_this() + * The following operation are inadequately protected : + * o hashbin_find() + * All other operation are unprotected, in particular : + * o hashbin_delete() + * o hashbin_get_first()/hashbin_get_next() + * + * In a lot of places in the IrDA stack, hashbin are defined with + * HB_LOCAL. This protects concurent access on the local processor, which + * means not much on a SMP system. + * + * Anyway, there is a fundamental flaw in the way the locking operate, + * which make it most often unusable, even with HB_GLOBAL. I'll explain... + * + * Let's take hashbin_find(). At first glance, the function seems fine, + * spinlock is grabed at the entrance and released when exiting. What's wrong + * with this picture ? + * The function hashbin_find() returns a pointer to the hashbin found, + * so that the user can have a look at the entry after the call to + * hashbin_find(). However, while the user is doing so, he is no longer + * protected from a concurent call to hashbin_remove(). + * In other words, between the call to hashbin_find() and the time + * we process the result, the data may go away !!! Ouch ! + * + * The process of hashbin_get_first()/hashbin_get_next() is in my + * view even more broken in that respect. Those functions use a global + * argument, 'hb_current', so you can't have two concurent GET process + * happening in parallel. In other words, you need to lock access from + * the initial hashbin_get_first() to the very last hashbin_get_next()... + * This means that even on uniprocessor, it's easy to get in troubles... + * + * Therefore, if you intend to "read" the data in the hashbin in + * one way or another (which is very likely), you need to manage the locking + * by yourself and not trust the locking in there. + * I've added a new flag, HB_LOCK, which use a single global lock + * for the function that are self contained. For other functions, the user + * should lock the hasbin himslelf using 'hb_spinlock'... For example on + * how to do it, please check dicovery.c and irnet_irda.c which does the + * right stuff... + * It would probably be possible to use a less heavy handed approach, + * but I'd rather have things safe than fast... + * + * Quick summary : + * o If your hashbin is used in a single thread of execution, i.e. + * everything within a function and its subroutines or with a predetermined + * sequence (state machine, time events), use HB_NOLOCK and be happy. + * o If your hashbin is used in multiple thread of executions, use + * HB_LOCK and lock hashbin_find() and hashbin_get_*() ; or you can lock + * everything yourself. + * o Use HB_GLOBAl only if you know what you are doing. + * o Don't use HB_LOCAL. Anyway, I've deprecated it... + * + * Jean II, 8 January 01 + */ + static irda_queue_t *dequeue_general( irda_queue_t **queue, irda_queue_t* element); static __u32 hash( char* name); @@ -65,11 +134,21 @@ hashbin_t *hashbin_new(int type) memset(hashbin, 0, sizeof(hashbin_t)); hashbin->hb_type = type; hashbin->magic = HB_MAGIC; + hashbin->hb_current = NULL; /* Make sure all spinlock's are unlocked */ - for (i=0;ihb_mutex[i] = SPIN_LOCK_UNLOCKED; - + if ( hashbin->hb_type & HB_GLOBAL ) { + for (i=0;ihb_mutex[ i ]); + } + if ( hashbin->hb_type & HB_LOCK ) { + spin_lock_init(&hashbin->hb_spinlock); + } + if ( hashbin->hb_type & HB_LOCAL ) { + IRDA_DEBUG( 0, __FUNCTION__ + "(), Warning : using an deprecated flag (HB_LOCAL)!\n"); + } + return hashbin; } @@ -115,11 +194,17 @@ int hashbin_clear( hashbin_t* hashbin, F int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func) { irda_queue_t* queue; + unsigned long flags = 0; int i; ASSERT(hashbin != NULL, return -1;); ASSERT(hashbin->magic == HB_MAGIC, return -1;); + /* Synchronize */ + if ( hashbin->hb_type & HB_LOCK ) { + spin_lock_irqsave( &hashbin->hb_spinlock, flags); + } + /* * Free the entries in the hashbin, TODO: use hashbin_clear when * it has been shown to work @@ -134,10 +219,18 @@ int hashbin_delete( hashbin_t* hashbin, } } + /* Cleanup local data */ + hashbin->hb_current = NULL; + hashbin->magic = ~HB_MAGIC; + + /* Release lock */ + if ( hashbin->hb_type & HB_LOCK) { + spin_unlock_irqrestore( &hashbin->hb_spinlock, flags); + } + /* * Free the hashbin structure */ - hashbin->magic = ~HB_MAGIC; kfree(hashbin); return 0; @@ -167,12 +260,12 @@ void hashbin_insert(hashbin_t* hashbin, bin = GET_HASHBIN( hashv ); /* Synchronize */ - if ( hashbin->hb_type & HB_GLOBAL ) { + if ( hashbin->hb_type & HB_LOCK ) { + spin_lock_irqsave( &hashbin->hb_spinlock, flags); + + } else if ( hashbin->hb_type & HB_GLOBAL ) { spin_lock_irqsave( &hashbin->hb_mutex[ bin ], flags); - } else if ( hashbin->hb_type & HB_LOCAL ) { - save_flags(flags); - cli(); } /* Default is no-lock */ /* @@ -195,13 +288,13 @@ void hashbin_insert(hashbin_t* hashbin, hashbin->hb_size++; /* Release lock */ - if ( hashbin->hb_type & HB_GLOBAL) { + if ( hashbin->hb_type & HB_LOCK) { + spin_unlock_irqrestore( &hashbin->hb_spinlock, flags); + } else if ( hashbin->hb_type & HB_GLOBAL) { spin_unlock_irqrestore( &hashbin->hb_mutex[ bin], flags); - } else if ( hashbin->hb_type & HB_LOCAL) { - restore_flags( flags); - } + } /* Default is no-lock */ } /* @@ -229,12 +322,10 @@ void* hashbin_find( hashbin_t* hashbin, bin = GET_HASHBIN( hashv ); /* Synchronize */ + /* No lock here for HB_LOCK, the caller must do it himself */ if ( hashbin->hb_type & HB_GLOBAL ) { spin_lock_irqsave( &hashbin->hb_mutex[ bin ], flags); - } else if ( hashbin->hb_type & HB_LOCAL ) { - save_flags(flags); - cli(); } /* Default is no-lock */ /* @@ -268,10 +359,12 @@ void* hashbin_find( hashbin_t* hashbin, if ( hashbin->hb_type & HB_GLOBAL) { spin_unlock_irqrestore( &hashbin->hb_mutex[ bin], flags); - } else if ( hashbin->hb_type & HB_LOCAL) { - restore_flags( flags); - } + } /* Default is no-lock */ + /* Note : passing entry to the caller is unsafe. The entry is still + * in the hashbin, so another caller can grab it as well. + * Jean II */ + if ( found ) return entry; else @@ -321,12 +414,12 @@ void* hashbin_remove( hashbin_t* hashbin bin = GET_HASHBIN( hashv ); /* Synchronize */ - if ( hashbin->hb_type & HB_GLOBAL ) { + if ( hashbin->hb_type & HB_LOCK ) { + spin_lock_irqsave( &hashbin->hb_spinlock, flags); + + } else if ( hashbin->hb_type & HB_GLOBAL ) { spin_lock_irqsave( &hashbin->hb_mutex[ bin ], flags); - } else if ( hashbin->hb_type & HB_LOCAL ) { - save_flags(flags); - cli(); } /* Default is no-lock */ /* @@ -374,14 +467,18 @@ void* hashbin_remove( hashbin_t* hashbin } /* Release lock */ - if ( hashbin->hb_type & HB_GLOBAL) { + if ( hashbin->hb_type & HB_LOCK) { + spin_unlock_irqrestore( &hashbin->hb_spinlock, flags); + + } else if ( hashbin->hb_type & HB_GLOBAL) { spin_unlock_irqrestore( &hashbin->hb_mutex[ bin], flags); - } else if ( hashbin->hb_type & HB_LOCAL) { - restore_flags( flags); - } + } /* Default is no-lock */ - + /* Note : we can safely pass entry to the caller, because as it is no + * longer in the hashbin, he will be the only owner of it + * Jean II */ + /* Return */ if ( found ) return entry; @@ -391,7 +488,7 @@ void* hashbin_remove( hashbin_t* hashbin } /* - * Function hashbin_remove (hashbin, hashv, name) + * Function hashbin_remove_this (hashbin, entry) * * Remove entry with the given name * @@ -413,6 +510,11 @@ void* hashbin_remove_this( hashbin_t* ha ASSERT( hashbin->magic == HB_MAGIC, return NULL;); ASSERT( entry != NULL, return NULL;); + /* Synchronize */ + if ( hashbin->hb_type & HB_LOCK ) { + spin_lock_irqsave( &hashbin->hb_spinlock, flags); + } + /* Check if valid and not already removed... */ if((entry->q_next == NULL) || (entry->q_prev == NULL)) return NULL; @@ -427,9 +529,6 @@ void* hashbin_remove_this( hashbin_t* ha if ( hashbin->hb_type & HB_GLOBAL ) { spin_lock_irqsave( &hashbin->hb_mutex[ bin ], flags); - } else if ( hashbin->hb_type & HB_LOCAL ) { - save_flags(flags); - cli(); } /* Default is no-lock */ /* @@ -449,12 +548,13 @@ void* hashbin_remove_this( hashbin_t* ha hashbin->hb_current = NULL; /* Release lock */ - if ( hashbin->hb_type & HB_GLOBAL) { + if ( hashbin->hb_type & HB_LOCK) { + spin_unlock_irqrestore( &hashbin->hb_spinlock, flags); + + } else if ( hashbin->hb_type & HB_GLOBAL) { spin_unlock_irqrestore( &hashbin->hb_mutex[ bin], flags); - } else if ( hashbin->hb_type & HB_LOCAL) { - restore_flags( flags); - } + } /* Default is no-lock */ return entry; } diff -urpN linux-2.4.1-pre8/net/irda/irttp.c linux-2.4.1-pre8-irda-patch/net/irda/irttp.c --- linux-2.4.1-pre8/net/irda/irttp.c Sun Nov 12 03:11:23 2000 +++ linux-2.4.1-pre8-irda-patch/net/irda/irttp.c Mon Jan 22 00:54:45 2001 @@ -89,7 +89,7 @@ int __init irttp_init(void) irttp->magic = TTP_MAGIC; - irttp->tsaps = hashbin_new(HB_LOCAL); + irttp->tsaps = hashbin_new(HB_LOCK); if (!irttp->tsaps) { ERROR(__FUNCTION__ "(), can't allocate IrTTP hashbin!\n"); return -ENOMEM; @@ -139,6 +139,15 @@ struct tsap_cb *irttp_open_tsap(__u8 sts ASSERT(irttp != NULL, return NULL;); ASSERT(irttp->magic == TTP_MAGIC, return NULL;); + /* The IrLMP spec (IrLMP 1.1 p10) says that we have the right to + * use only 0x01-0x6F. Of course, we can use LSAP_ANY as well. + * JeanII */ + if((stsap_sel != LSAP_ANY) && + ((stsap_sel < 0x01) || (stsap_sel >= 0x70))) { + IRDA_DEBUG(0, __FUNCTION__ "(), invalid tsap!\n"); + return NULL; + } + self = kmalloc(sizeof(struct tsap_cb), GFP_ATOMIC); if (self == NULL) { IRDA_DEBUG(0, __FUNCTION__ "(), unable to kmalloc!\n"); @@ -1008,30 +1017,43 @@ int irttp_connect_response(struct tsap_c struct tsap_cb *irttp_dup(struct tsap_cb *orig, void *instance) { struct tsap_cb *new; + unsigned long flags; IRDA_DEBUG(1, __FUNCTION__ "()\n"); - if (!hashbin_find(irttp->tsaps, (int) orig, NULL)) { - IRDA_DEBUG(0, __FUNCTION__ "(), unable to find TSAP\n"); - return NULL; - } + /* Allocate a new instance */ new = kmalloc(sizeof(struct tsap_cb), GFP_ATOMIC); if (!new) { IRDA_DEBUG(0, __FUNCTION__ "(), unable to kmalloc\n"); return NULL; } + + /* Protect our access to the old tsap instance */ + spin_lock_irqsave(&irttp->tsaps->hb_spinlock, flags); + + /* Find the old instance */ + if (!hashbin_find(irttp->tsaps, (int) orig, NULL)) { + spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags); + kfree(new); + IRDA_DEBUG(0, __FUNCTION__ "(), unable to find TSAP\n"); + return NULL; + } /* Dup */ memcpy(new, orig, sizeof(struct tsap_cb)); - new->notify.instance = instance; - new->lsap = irlmp_dup(orig->lsap, new); + + /* We don't need the old instance any more */ + spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags); /* Not everything should be copied */ + new->notify.instance = instance; + new->lsap = irlmp_dup(orig->lsap, new); init_timer(&new->todo_timer); skb_queue_head_init(&new->rx_queue); skb_queue_head_init(&new->tx_queue); skb_queue_head_init(&new->rx_fragments); + /* This is locked */ hashbin_insert(irttp->tsaps, (irda_queue_t *) new, (int) new, NULL); return new; @@ -1533,23 +1555,23 @@ static void irttp_start_todo_timer(struc * * Give some info to the /proc file system */ -int irttp_proc_read(char *buf, char **start, off_t offset, int len) +int irttp_proc_read(char *buf, char **start, off_t offset, int length) { struct tsap_cb *self; unsigned long flags; int i = 0; + int len = 0; ASSERT(irttp != NULL, return 0;); - len = 0; - - save_flags(flags); - cli(); + /* Protect our access to the tsap list */ + spin_lock_irqsave(&irttp->tsaps->hb_spinlock, flags); self = (struct tsap_cb *) hashbin_get_first(irttp->tsaps); - while (self != NULL) { - if (!self || self->magic != TTP_TSAP_MAGIC) - return len; + while ((self != NULL) && (len < length)) { + + if ((!self) || (self->magic != TTP_TSAP_MAGIC)) + goto out; len += sprintf(buf+len, "TSAP %d, ", i++); len += sprintf(buf+len, "stsap_sel: %02x, ", @@ -1564,9 +1586,9 @@ int irttp_proc_read(char *buf, char **st self->remote_credit); len += sprintf(buf+len, "send credit: %d\n", self->send_credit); - len += sprintf(buf+len, " tx packets: %d, ", + len += sprintf(buf+len, " tx packets: %ld, ", self->stats.tx_packets); - len += sprintf(buf+len, "rx packets: %d, ", + len += sprintf(buf+len, "rx packets: %ld, ", self->stats.rx_packets); len += sprintf(buf+len, "tx_queue len: %d ", skb_queue_len(&self->tx_queue)); @@ -1590,7 +1612,9 @@ int irttp_proc_read(char *buf, char **st self = (struct tsap_cb *) hashbin_get_next(irttp->tsaps); } - restore_flags(flags); + +out: + spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags); return len; } diff -urpN linux-2.4.1-pre8/net/irda/qos.c linux-2.4.1-pre8-irda-patch/net/irda/qos.c --- linux-2.4.1-pre8/net/irda/qos.c Sun Nov 12 03:11:23 2000 +++ linux-2.4.1-pre8-irda-patch/net/irda/qos.c Mon Jan 22 00:54:45 2001 @@ -70,10 +70,6 @@ static int irlap_param_additional_bofs(v int get); static int irlap_param_min_turn_time(void *instance, irda_param_t *param, int get); -static int value_index(__u32 value, __u32 *array, int size); -static __u32 byte_value(__u8 byte, __u32 *array); -static __u32 index_value(int index, __u32 *array); -static int value_lower_bits(__u32 value, __u32 *array, int size, __u16 *field); __u32 min_turn_times[] = { 10000, 5000, 1000, 500, 100, 50, 10, 0 }; /* us */ __u32 baud_rates[] = { 2400, 9600, 19200, 38400, 57600, 115200, 576000, @@ -130,6 +126,98 @@ static pi_major_info_t pi_major_call_tab static pi_param_info_t irlap_param_info = { pi_major_call_table, 2, 0x7f, 7 }; +/* ---------------------- LOCAL SUBROUTINES ---------------------- */ +/* Note : we start with a bunch of local subroutines. + * As the compiler is "one pass", this is the only way to get them to + * inline properly... + * Jean II + */ +/* + * Function value_index (value, array, size) + * + * Returns the index to the value in the specified array + */ +static inline int value_index(__u32 value, __u32 *array, int size) +{ + int i; + + for (i=0; i < size; i++) + if (array[i] == value) + break; + return i; +} + +/* + * Function index_value (index, array) + * + * Returns value to index in array, easy! + * + */ +static inline __u32 index_value(int index, __u32 *array) +{ + return array[index]; +} + +/* + * Function msb_index (word) + * + * Returns index to most significant bit (MSB) in word + * + */ +int msb_index (__u16 word) +{ + __u16 msb = 0x8000; + int index = 15; /* Current MSB */ + + while (msb) { + if (word & msb) + break; /* Found it! */ + msb >>=1; + index--; + } + return index; +} + +static inline __u32 byte_value(__u8 byte, __u32 *array) +{ + int index; + + ASSERT(array != NULL, return -1;); + + index = msb_index(byte); + + return index_value(index, array); +} + +/* + * Function value_lower_bits (value, array) + * + * Returns a bit field marking all possibility lower than value. + * We may need a "value_higher_bits" in the future... + */ +static inline int value_lower_bits(__u32 value, __u32 *array, int size, __u16 *field) +{ + int i; + __u16 mask = 0x1; + __u16 result = 0x0; + + for (i=0; i < size; i++) { + /* Add the current value to the bit field, shift mask */ + result |= mask; + mask <<= 1; + /* Finished ? */ + if (array[i] >= value) + break; + } + /* Send back a valid index */ + if(i >= size) + i = size - 1; /* Last item */ + *field = result; + return i; +} + +/* -------------------------- MAIN CALLS -------------------------- */ + /* * Function irda_qos_compute_intersection (qos, new) * @@ -594,99 +682,6 @@ __u32 irlap_requested_line_capacity(stru line_capacity); return line_capacity; -} - -__u32 irlap_min_turn_time_in_bytes(__u32 speed, __u32 min_turn_time) -{ - __u32 bytes; - - bytes = speed * min_turn_time / 10000000; - - return bytes; -} - -static __u32 byte_value(__u8 byte, __u32 *array) -{ - int index; - - ASSERT(array != NULL, return -1;); - - index = msb_index(byte); - - return index_value(index, array); -} - -/* - * Function msb_index (word) - * - * Returns index to most significant bit (MSB) in word - * - */ -int msb_index (__u16 word) -{ - __u16 msb = 0x8000; - int index = 15; /* Current MSB */ - - while (msb) { - if (word & msb) - break; /* Found it! */ - msb >>=1; - index--; - } - return index; -} - -/* - * Function value_index (value, array, size) - * - * Returns the index to the value in the specified array - */ -static int value_index(__u32 value, __u32 *array, int size) -{ - int i; - - for (i=0; i < size; i++) - if (array[i] == value) - break; - return i; -} - -/* - * Function index_value (index, array) - * - * Returns value to index in array, easy! - * - */ -static __u32 index_value(int index, __u32 *array) -{ - return array[index]; -} - -/* - * Function value_lower_bits (value, array) - * - * Returns a bit field marking all possibility lower than value. - * We may need a "value_higher_bits" in the future... - */ -static int value_lower_bits(__u32 value, __u32 *array, int size, __u16 *field) -{ - int i; - __u16 mask = 0x1; - __u16 result = 0x0; - - for (i=0; i < size; i++) { - /* Add the current value to the bit field, shift mask */ - result |= mask; - mask <<= 1; - /* Finished ? */ - if (array[i] >= value) - break; - } - /* Send back a valid index */ - if(i >= size) - i = size - 1; /* Last item */ - *field = result; - return i; } void irda_qos_bits_to_value(struct qos_info *qos)