| /***************************************************************** | 
 |  * | 
 |  * Filename:		donauboe.c | 
 |  * Version: 		2.17 | 
 |  * Description:   Driver for the Toshiba OBOE (or type-O or 701) | 
 |  *                FIR Chipset, also supports the DONAUOBOE (type-DO | 
 |  *                or d01) FIR chipset which as far as I know is | 
 |  *                register compatible. | 
 |  * Documentation: http://libxg.free.fr/irda/lib-irda.html | 
 |  * Status:        Experimental. | 
 |  * Author:        James McKenzie <james@fishsoup.dhs.org> | 
 |  * Created at:    Sat May 8  12:35:27 1999 | 
 |  * Modified:      Paul Bristow <paul.bristow@technologist.com> | 
 |  * Modified:      Mon Nov 11 19:10:05 1999 | 
 |  * Modified:      James McKenzie <james@fishsoup.dhs.org> | 
 |  * Modified:      Thu Mar 16 12:49:00 2000 (Substantial rewrite) | 
 |  * Modified:      Sat Apr 29 00:23:03 2000 (Added DONAUOBOE support) | 
 |  * Modified:      Wed May 24 23:45:02 2000 (Fixed chipio_t structure) | 
 |  * Modified: 2.13 Christian Gennerat <christian.gennerat@polytechnique.org> | 
 |  * Modified: 2.13 dim jan 07 21:57:39 2001 (tested with kernel 2.4 & irnet/ppp) | 
 |  * Modified: 2.14 Christian Gennerat <christian.gennerat@polytechnique.org> | 
 |  * Modified: 2.14 lun fev 05 17:55:59 2001 (adapted to patch-2.4.1-pre8-irda1) | 
 |  * Modified: 2.15 Martin Lucina <mato@kotelna.sk> | 
 |  * Modified: 2.15 Fri Jun 21 20:40:59 2002 (sync with 2.4.18, substantial fixes) | 
 |  * Modified: 2.16 Martin Lucina <mato@kotelna.sk> | 
 |  * Modified: 2.16 Sat Jun 22 18:54:29 2002 (fix freeregion, default to verbose) | 
 |  * Modified: 2.17 Christian Gennerat <christian.gennerat@polytechnique.org> | 
 |  * Modified: 2.17 jeu sep 12 08:50:20 2002 (save_flags();cli(); replaced by spinlocks) | 
 |  * Modified: 2.18 Christian Gennerat <christian.gennerat@polytechnique.org> | 
 |  * Modified: 2.18 ven jan 10 03:14:16 2003 Change probe default options | 
 |  * | 
 |  *     Copyright (c) 1999 James McKenzie, 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. | 
 |  * | 
 |  *     Neither James McKenzie nor Cambridge University admit liability nor | 
 |  *     provide warranty for any of this software. This material is | 
 |  *     provided "AS-IS" and at no charge. | 
 |  * | 
 |  *     Applicable Models : Libretto 100/110CT and many more. | 
 |  *     Toshiba refers to this chip as the type-O IR port, | 
 |  *     or the type-DO IR port. | 
 |  * | 
 |  ********************************************************************/ | 
 |  | 
 | /* Look at toshoboe.h (currently in include/net/irda) for details of */ | 
 | /* Where to get documentation on the chip         */ | 
 |  | 
 |  | 
 | static char *rcsid = | 
 |   "$Id: donauboe.c V2.18 ven jan 10 03:14:16 2003$"; | 
 |  | 
 | /* See below for a description of the logic in this driver */ | 
 |  | 
 | /* User servicable parts */ | 
 | /* USE_PROBE Create the code which probes the chip and does a few tests */ | 
 | /* do_probe module parameter Enable this code */ | 
 | /* Probe code is very useful for understanding how the hardware works */ | 
 | /* Use it with various combinations of TT_LEN, RX_LEN */ | 
 | /* Strongly recomended, disable if the probe fails on your machine */ | 
 | /* and send me <james@fishsoup.dhs.org> the output of dmesg */ | 
 | #define USE_PROBE 1 | 
 | #undef  USE_PROBE | 
 |  | 
 | /* Trace Transmit ring, interrupts, Receive ring or not ? */ | 
 | #define PROBE_VERBOSE 1 | 
 |  | 
 | /* Debug option, examine sent and received raw data */ | 
 | /* Irdadump is better, but does not see all packets. enable it if you want. */ | 
 | #undef DUMP_PACKETS | 
 |  | 
 | /* MIR mode has not been tested. Some behaviour is different */ | 
 | /* Seems to work against an Ericsson R520 for me. -Martin */ | 
 | #define USE_MIR | 
 |  | 
 | /* Schedule back to back hardware transmits wherever possible, otherwise */ | 
 | /* we need an interrupt for every frame, unset if oboe works for a bit and */ | 
 | /* then hangs */ | 
 | #define OPTIMIZE_TX | 
 |  | 
 | /* Set the number of slots in the rings */ | 
 | /* If you get rx/tx fifo overflows at high bitrates, you can try increasing */ | 
 | /* these */ | 
 |  | 
 | #define RING_SIZE (OBOE_RING_SIZE_RX8 | OBOE_RING_SIZE_TX8) | 
 | #define TX_SLOTS    8 | 
 | #define RX_SLOTS    8 | 
 |  | 
 |  | 
 | /* Less user servicable parts below here */ | 
 |  | 
 | /* Test, Transmit and receive buffer sizes, adjust at your peril */ | 
 | /* remarks: nfs usually needs 1k blocks */ | 
 | /* remarks: in SIR mode, CRC is received, -> RX_LEN=TX_LEN+2 */ | 
 | /* remarks: test accepts large blocks. Standard is 0x80 */ | 
 | /* When TT_LEN > RX_LEN (SIR mode) data is stored in successive slots. */ | 
 | /* When 3 or more slots are needed for each test packet, */ | 
 | /* data received in the first slots is overwritten, even */ | 
 | /* if OBOE_CTL_RX_HW_OWNS is not set, without any error! */ | 
 | #define TT_LEN      0x80 | 
 | #define TX_LEN      0xc00 | 
 | #define RX_LEN      0xc04 | 
 | /* Real transmitted length (SIR mode) is about 14+(2%*TX_LEN) more */ | 
 | /* long than user-defined length (see async_wrap_skb) and is less then 4K */ | 
 | /* Real received length is (max RX_LEN) differs from user-defined */ | 
 | /* length only b the CRC (2 or 4 bytes) */ | 
 | #define BUF_SAFETY  0x7a | 
 | #define RX_BUF_SZ   (RX_LEN) | 
 | #define TX_BUF_SZ   (TX_LEN+BUF_SAFETY) | 
 |  | 
 |  | 
 | /* Logic of the netdev part of this driver                             */ | 
 |  | 
 | /* The RX ring is filled with buffers, when a packet arrives           */ | 
 | /* it is DMA'd into the buffer which is marked used and RxDone called  */ | 
 | /* RxDone forms an skb (and checks the CRC if in SIR mode) and ships   */ | 
 | /* the packet off upstairs */ | 
 |  | 
 | /* The transmitter on the oboe chip can work in one of two modes       */ | 
 | /* for each ring->tx[] the transmitter can either                      */ | 
 | /* a) transmit the packet, leave the trasmitter enabled and proceed to */ | 
 | /*    the next ring                                                    */ | 
 | /* OR                                                                  */ | 
 | /* b) transmit the packet, switch off the transmitter and issue TxDone */ | 
 |  | 
 | /* All packets are entered into the ring in mode b), if the ring was   */ | 
 | /* empty the transmitter is started.                                   */ | 
 |  | 
 | /* If OPTIMIZE_TX is defined then in TxDone if the ring contains       */ | 
 | /* more than one packet, all but the last are set to mode a) [HOWEVER  */ | 
 | /* the hardware may not notice this, this is why we start in mode b) ] */ | 
 | /* then restart the transmitter                                        */ | 
 |  | 
 | /* If OPTIMIZE_TX is not defined then we just restart the transmitter  */ | 
 | /* if the ring isn't empty */ | 
 |  | 
 | /* Speed changes are delayed until the TxRing is empty                 */ | 
 | /* mtt is handled by generating packets with bad CRCs, before the data */ | 
 |  | 
 | /* TODO: */ | 
 | /* check the mtt works ok      */ | 
 | /* finish the watchdog         */ | 
 |  | 
 | /* No user servicable parts below here */ | 
 |  | 
 | #include <linux/module.h> | 
 |  | 
 | #include <linux/kernel.h> | 
 | #include <linux/types.h> | 
 | #include <linux/skbuff.h> | 
 | #include <linux/netdevice.h> | 
 | #include <linux/ioport.h> | 
 | #include <linux/delay.h> | 
 | #include <linux/slab.h> | 
 | #include <linux/init.h> | 
 | #include <linux/pci.h> | 
 | #include <linux/rtnetlink.h> | 
 |  | 
 | #include <asm/system.h> | 
 | #include <asm/io.h> | 
 |  | 
 | #include <net/irda/wrapper.h> | 
 | #include <net/irda/irda.h> | 
 | //#include <net/irda/irmod.h> | 
 | //#include <net/irda/irlap_frame.h> | 
 | #include <net/irda/irda_device.h> | 
 | #include <net/irda/crc.h> | 
 |  | 
 | #include "donauboe.h" | 
 |  | 
 | #define INB(port)       inb_p(port) | 
 | #define OUTB(val,port)  outb_p(val,port) | 
 | #define OUTBP(val,port) outb_p(val,port) | 
 |  | 
 | #define PROMPT  OUTB(OBOE_PROMPT_BIT,OBOE_PROMPT); | 
 |  | 
 | #if PROBE_VERBOSE | 
 | #define PROBE_DEBUG(args...) (printk (args)) | 
 | #else | 
 | #define PROBE_DEBUG(args...) ; | 
 | #endif | 
 |  | 
 | /* Set the DMA to be byte at a time */ | 
 | #define CONFIG0H_DMA_OFF OBOE_CONFIG0H_RCVANY | 
 | #define CONFIG0H_DMA_ON_NORX CONFIG0H_DMA_OFF| OBOE_CONFIG0H_ENDMAC | 
 | #define CONFIG0H_DMA_ON CONFIG0H_DMA_ON_NORX | OBOE_CONFIG0H_ENRX | 
 |  | 
 | static struct pci_device_id toshoboe_pci_tbl[] = { | 
 | 	{ PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_FIR701, PCI_ANY_ID, PCI_ANY_ID, }, | 
 | 	{ PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_FIRD01, PCI_ANY_ID, PCI_ANY_ID, }, | 
 | 	{ }			/* Terminating entry */ | 
 | }; | 
 | MODULE_DEVICE_TABLE(pci, toshoboe_pci_tbl); | 
 |  | 
 | #define DRIVER_NAME "toshoboe" | 
 | static char *driver_name = DRIVER_NAME; | 
 |  | 
 | static int max_baud = 4000000; | 
 | #ifdef USE_PROBE | 
 | static int do_probe = 0; | 
 | #endif | 
 |  | 
 |  | 
 | /**********************************************************************/ | 
 | static int | 
 | toshoboe_checkfcs (unsigned char *buf, int len) | 
 | { | 
 |   int i; | 
 |   union | 
 |   { | 
 |     __u16 value; | 
 |     __u8 bytes[2]; | 
 |   } | 
 |   fcs; | 
 |  | 
 |   fcs.value = INIT_FCS; | 
 |  | 
 |   for (i = 0; i < len; ++i) | 
 |     fcs.value = irda_fcs (fcs.value, *(buf++)); | 
 |  | 
 |   return (fcs.value == GOOD_FCS); | 
 | } | 
 |  | 
 | /***********************************************************************/ | 
 | /* Generic chip handling code */ | 
 | #ifdef DUMP_PACKETS | 
 | static unsigned char dump[50]; | 
 | static void | 
 | _dumpbufs (unsigned char *data, int len, char tete) | 
 | { | 
 | int i,j; | 
 | char head=tete; | 
 | for (i=0;i<len;i+=16) { | 
 |     for (j=0;j<16 && i+j<len;j++) { sprintf(&dump[3*j],"%02x.",data[i+j]); } | 
 |     dump [3*j]=0; | 
 |     IRDA_DEBUG (2, "%c%s\n",head , dump); | 
 |     head='+'; | 
 |     } | 
 | } | 
 | #endif | 
 |  | 
 | #ifdef USE_PROBE | 
 | /* Dump the registers */ | 
 | static void | 
 | toshoboe_dumpregs (struct toshoboe_cb *self) | 
 | { | 
 |   __u32 ringbase; | 
 |  | 
 |   IRDA_DEBUG (4, "%s()\n", __FUNCTION__); | 
 |  | 
 |   ringbase = INB (OBOE_RING_BASE0) << 10; | 
 |   ringbase |= INB (OBOE_RING_BASE1) << 18; | 
 |   ringbase |= INB (OBOE_RING_BASE2) << 26; | 
 |  | 
 |   printk (KERN_ERR DRIVER_NAME ": Register dump:\n"); | 
 |   printk (KERN_ERR "Interrupts: Tx:%d Rx:%d TxUnder:%d RxOver:%d Sip:%d\n", | 
 |           self->int_tx, self->int_rx, self->int_txunder, self->int_rxover, | 
 |           self->int_sip); | 
 |   printk (KERN_ERR "RX %02x TX %02x RingBase %08x\n", | 
 |           INB (OBOE_RXSLOT), INB (OBOE_TXSLOT), ringbase); | 
 |   printk (KERN_ERR "RING_SIZE %02x IER %02x ISR %02x\n", | 
 |           INB (OBOE_RING_SIZE), INB (OBOE_IER), INB (OBOE_ISR)); | 
 |   printk (KERN_ERR "CONFIG1 %02x STATUS %02x\n", | 
 |           INB (OBOE_CONFIG1), INB (OBOE_STATUS)); | 
 |   printk (KERN_ERR "CONFIG0 %02x%02x ENABLE %02x%02x\n", | 
 |           INB (OBOE_CONFIG0H), INB (OBOE_CONFIG0L), | 
 |           INB (OBOE_ENABLEH), INB (OBOE_ENABLEL)); | 
 |   printk (KERN_ERR "NEW_PCONFIG %02x%02x CURR_PCONFIG %02x%02x\n", | 
 |           INB (OBOE_NEW_PCONFIGH), INB (OBOE_NEW_PCONFIGL), | 
 |           INB (OBOE_CURR_PCONFIGH), INB (OBOE_CURR_PCONFIGL)); | 
 |   printk (KERN_ERR "MAXLEN %02x%02x RXCOUNT %02x%02x\n", | 
 |           INB (OBOE_MAXLENH), INB (OBOE_MAXLENL), | 
 |           INB (OBOE_RXCOUNTL), INB (OBOE_RXCOUNTH)); | 
 |  | 
 |   if (self->ring) | 
 |     { | 
 |       int i; | 
 |       ringbase = virt_to_bus (self->ring); | 
 |       printk (KERN_ERR "Ring at %08x:\n", ringbase); | 
 |       printk (KERN_ERR "RX:"); | 
 |       for (i = 0; i < RX_SLOTS; ++i) | 
 |         printk (" (%d,%02x)",self->ring->rx[i].len,self->ring->rx[i].control); | 
 |       printk ("\n"); | 
 |       printk (KERN_ERR "TX:"); | 
 |       for (i = 0; i < RX_SLOTS; ++i) | 
 |         printk (" (%d,%02x)",self->ring->tx[i].len,self->ring->tx[i].control); | 
 |       printk ("\n"); | 
 |     } | 
 | } | 
 | #endif | 
 |  | 
 | /*Don't let the chip look at memory */ | 
 | static void | 
 | toshoboe_disablebm (struct toshoboe_cb *self) | 
 | { | 
 |   __u8 command; | 
 |   IRDA_DEBUG (4, "%s()\n", __FUNCTION__); | 
 |  | 
 |   pci_read_config_byte (self->pdev, PCI_COMMAND, &command); | 
 |   command &= ~PCI_COMMAND_MASTER; | 
 |   pci_write_config_byte (self->pdev, PCI_COMMAND, command); | 
 |  | 
 | } | 
 |  | 
 | /* Shutdown the chip and point the taskfile reg somewhere else */ | 
 | static void | 
 | toshoboe_stopchip (struct toshoboe_cb *self) | 
 | { | 
 |   IRDA_DEBUG (4, "%s()\n", __FUNCTION__); | 
 |  | 
 |   /*Disable interrupts */ | 
 |   OUTB (0x0, OBOE_IER); | 
 |   /*Disable DMA, Disable Rx, Disable Tx */ | 
 |   OUTB (CONFIG0H_DMA_OFF, OBOE_CONFIG0H); | 
 |   /*Disable SIR MIR FIR, Tx and Rx */ | 
 |   OUTB (0x00, OBOE_ENABLEH); | 
 |   /*Point the ring somewhere safe */ | 
 |   OUTB (0x3f, OBOE_RING_BASE2); | 
 |   OUTB (0xff, OBOE_RING_BASE1); | 
 |   OUTB (0xff, OBOE_RING_BASE0); | 
 |  | 
 |   OUTB (RX_LEN >> 8, OBOE_MAXLENH); | 
 |   OUTB (RX_LEN & 0xff, OBOE_MAXLENL); | 
 |  | 
 |   /*Acknoledge any pending interrupts */ | 
 |   OUTB (0xff, OBOE_ISR); | 
 |  | 
 |   /*Why */ | 
 |   OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); | 
 |  | 
 |   /*switch it off */ | 
 |   OUTB (OBOE_CONFIG1_OFF, OBOE_CONFIG1); | 
 |  | 
 |   toshoboe_disablebm (self); | 
 | } | 
 |  | 
 | /* Transmitter initialization */ | 
 | static void | 
 | toshoboe_start_DMA (struct toshoboe_cb *self, int opts) | 
 | { | 
 |   OUTB (0x0, OBOE_ENABLEH); | 
 |   OUTB (CONFIG0H_DMA_ON | opts,  OBOE_CONFIG0H); | 
 |   OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); | 
 |   PROMPT; | 
 | } | 
 |  | 
 | /*Set the baud rate */ | 
 | static void | 
 | toshoboe_setbaud (struct toshoboe_cb *self) | 
 | { | 
 |   __u16 pconfig = 0; | 
 |   __u8 config0l = 0; | 
 |  | 
 |   IRDA_DEBUG (2, "%s(%d/%d)\n", __FUNCTION__, self->speed, self->io.speed); | 
 |  | 
 |   switch (self->speed) | 
 |     { | 
 |     case 2400: | 
 |     case 4800: | 
 |     case 9600: | 
 |     case 19200: | 
 |     case 38400: | 
 |     case 57600: | 
 |     case 115200: | 
 | #ifdef USE_MIR | 
 |     case 1152000: | 
 | #endif | 
 |     case 4000000: | 
 |       break; | 
 |     default: | 
 |  | 
 |       printk (KERN_ERR DRIVER_NAME ": switch to unsupported baudrate %d\n", | 
 |               self->speed); | 
 |       return; | 
 |     } | 
 |  | 
 |   switch (self->speed) | 
 |     { | 
 |       /* For SIR the preamble is done by adding XBOFs */ | 
 |       /* to the packet */ | 
 |       /* set to filtered SIR mode, filter looks for BOF and EOF */ | 
 |     case 2400: | 
 |       pconfig |= 47 << OBOE_PCONFIG_BAUDSHIFT; | 
 |       pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; | 
 |       break; | 
 |     case 4800: | 
 |       pconfig |= 23 << OBOE_PCONFIG_BAUDSHIFT; | 
 |       pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; | 
 |       break; | 
 |     case 9600: | 
 |       pconfig |= 11 << OBOE_PCONFIG_BAUDSHIFT; | 
 |       pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; | 
 |       break; | 
 |     case 19200: | 
 |       pconfig |= 5 << OBOE_PCONFIG_BAUDSHIFT; | 
 |       pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; | 
 |       break; | 
 |     case 38400: | 
 |       pconfig |= 2 << OBOE_PCONFIG_BAUDSHIFT; | 
 |       pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; | 
 |       break; | 
 |     case 57600: | 
 |       pconfig |= 1 << OBOE_PCONFIG_BAUDSHIFT; | 
 |       pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; | 
 |       break; | 
 |     case 115200: | 
 |       pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT; | 
 |       pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; | 
 |       break; | 
 |     default: | 
 |       /*Set to packet based reception */ | 
 |       OUTB (RX_LEN >> 8, OBOE_MAXLENH); | 
 |       OUTB (RX_LEN & 0xff, OBOE_MAXLENL); | 
 |       break; | 
 |     } | 
 |  | 
 |   switch (self->speed) | 
 |     { | 
 |     case 2400: | 
 |     case 4800: | 
 |     case 9600: | 
 |     case 19200: | 
 |     case 38400: | 
 |     case 57600: | 
 |     case 115200: | 
 |       config0l = OBOE_CONFIG0L_ENSIR; | 
 |       if (self->async) | 
 |         { | 
 |           /*Set to character based reception */ | 
 |           /*System will lock if MAXLEN=0 */ | 
 |           /*so have to be careful */ | 
 |           OUTB (0x01, OBOE_MAXLENH); | 
 |           OUTB (0x01, OBOE_MAXLENL); | 
 |           OUTB (0x00, OBOE_MAXLENH); | 
 |         } | 
 |       else | 
 |         { | 
 |           /*Set to packet based reception */ | 
 |           config0l |= OBOE_CONFIG0L_ENSIRF; | 
 |           OUTB (RX_LEN >> 8, OBOE_MAXLENH); | 
 |           OUTB (RX_LEN & 0xff, OBOE_MAXLENL); | 
 |         } | 
 |       break; | 
 |  | 
 | #ifdef USE_MIR | 
 |       /* MIR mode */ | 
 |       /* Set for 16 bit CRC and enable MIR */ | 
 |       /* Preamble now handled by the chip */ | 
 |     case 1152000: | 
 |       pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT; | 
 |       pconfig |= 8 << OBOE_PCONFIG_WIDTHSHIFT; | 
 |       pconfig |= 1 << OBOE_PCONFIG_PREAMBLESHIFT; | 
 |       config0l = OBOE_CONFIG0L_CRC16 | OBOE_CONFIG0L_ENMIR; | 
 |       break; | 
 | #endif | 
 |       /* FIR mode */ | 
 |       /* Set for 32 bit CRC and enable FIR */ | 
 |       /* Preamble handled by the chip */ | 
 |     case 4000000: | 
 |       pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT; | 
 |       /* Documentation says 14, but toshiba use 15 in their drivers */ | 
 |       pconfig |= 15 << OBOE_PCONFIG_PREAMBLESHIFT; | 
 |       config0l = OBOE_CONFIG0L_ENFIR; | 
 |       break; | 
 |     } | 
 |  | 
 |   /* Copy into new PHY config buffer */ | 
 |   OUTBP (pconfig >> 8, OBOE_NEW_PCONFIGH); | 
 |   OUTB (pconfig & 0xff, OBOE_NEW_PCONFIGL); | 
 |   OUTB (config0l, OBOE_CONFIG0L); | 
 |  | 
 |   /* Now make OBOE copy from new PHY to current PHY */ | 
 |   OUTB (0x0, OBOE_ENABLEH); | 
 |   OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); | 
 |   PROMPT; | 
 |  | 
 |   /* speed change executed */ | 
 |   self->new_speed = 0; | 
 |   self->io.speed = self->speed; | 
 | } | 
 |  | 
 | /*Let the chip look at memory */ | 
 | static void | 
 | toshoboe_enablebm (struct toshoboe_cb *self) | 
 | { | 
 |   IRDA_DEBUG (4, "%s()\n", __FUNCTION__); | 
 |   pci_set_master (self->pdev); | 
 | } | 
 |  | 
 | /*setup the ring */ | 
 | static void | 
 | toshoboe_initring (struct toshoboe_cb *self) | 
 | { | 
 |   int i; | 
 |  | 
 |   IRDA_DEBUG (4, "%s()\n", __FUNCTION__); | 
 |  | 
 |   for (i = 0; i < TX_SLOTS; ++i) | 
 |     { | 
 |       self->ring->tx[i].len = 0; | 
 |       self->ring->tx[i].control = 0x00; | 
 |       self->ring->tx[i].address = virt_to_bus (self->tx_bufs[i]); | 
 |     } | 
 |  | 
 |   for (i = 0; i < RX_SLOTS; ++i) | 
 |     { | 
 |       self->ring->rx[i].len = RX_LEN; | 
 |       self->ring->rx[i].len = 0; | 
 |       self->ring->rx[i].address = virt_to_bus (self->rx_bufs[i]); | 
 |       self->ring->rx[i].control = OBOE_CTL_RX_HW_OWNS; | 
 |     } | 
 | } | 
 |  | 
 | static void | 
 | toshoboe_resetptrs (struct toshoboe_cb *self) | 
 | { | 
 |   /* Can reset pointers by twidling DMA */ | 
 |   OUTB (0x0, OBOE_ENABLEH); | 
 |   OUTBP (CONFIG0H_DMA_OFF, OBOE_CONFIG0H); | 
 |   OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); | 
 |  | 
 |   self->rxs = inb_p (OBOE_RXSLOT) & OBOE_SLOT_MASK; | 
 |   self->txs = inb_p (OBOE_TXSLOT) & OBOE_SLOT_MASK; | 
 | } | 
 |  | 
 | /* Called in locked state */ | 
 | static void | 
 | toshoboe_initptrs (struct toshoboe_cb *self) | 
 | { | 
 |  | 
 |   /* spin_lock_irqsave(self->spinlock, flags); */ | 
 |   /* save_flags (flags); */ | 
 |  | 
 |   /* Can reset pointers by twidling DMA */ | 
 |   toshoboe_resetptrs (self); | 
 |  | 
 |   OUTB (0x0, OBOE_ENABLEH); | 
 |   OUTB (CONFIG0H_DMA_ON, OBOE_CONFIG0H); | 
 |   OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); | 
 |  | 
 |   self->txpending = 0; | 
 |  | 
 |   /* spin_unlock_irqrestore(self->spinlock, flags); */ | 
 |   /* restore_flags (flags); */ | 
 | } | 
 |  | 
 | /* Wake the chip up and get it looking at the rings */ | 
 | /* Called in locked state */ | 
 | static void | 
 | toshoboe_startchip (struct toshoboe_cb *self) | 
 | { | 
 |   __u32 physaddr; | 
 |  | 
 |   IRDA_DEBUG (4, "%s()\n", __FUNCTION__); | 
 |  | 
 |   toshoboe_initring (self); | 
 |   toshoboe_enablebm (self); | 
 |   OUTBP (OBOE_CONFIG1_RESET, OBOE_CONFIG1); | 
 |   OUTBP (OBOE_CONFIG1_ON, OBOE_CONFIG1); | 
 |  | 
 |   /* Stop the clocks */ | 
 |   OUTB (0, OBOE_ENABLEH); | 
 |  | 
 |   /*Set size of rings */ | 
 |   OUTB (RING_SIZE, OBOE_RING_SIZE); | 
 |  | 
 |   /*Acknoledge any pending interrupts */ | 
 |   OUTB (0xff, OBOE_ISR); | 
 |  | 
 |   /*Enable ints */ | 
 |   OUTB (OBOE_INT_TXDONE  | OBOE_INT_RXDONE | | 
 |         OBOE_INT_TXUNDER | OBOE_INT_RXOVER | OBOE_INT_SIP , OBOE_IER); | 
 |  | 
 |   /*Acknoledge any pending interrupts */ | 
 |   OUTB (0xff, OBOE_ISR); | 
 |  | 
 |   /*Set the maximum packet length to 0xfff (4095) */ | 
 |   OUTB (RX_LEN >> 8, OBOE_MAXLENH); | 
 |   OUTB (RX_LEN & 0xff, OBOE_MAXLENL); | 
 |  | 
 |   /*Shutdown DMA */ | 
 |   OUTB (CONFIG0H_DMA_OFF, OBOE_CONFIG0H); | 
 |  | 
 |   /*Find out where the rings live */ | 
 |   physaddr = virt_to_bus (self->ring); | 
 |  | 
 |   IRDA_ASSERT ((physaddr & 0x3ff) == 0, | 
 | 	       printk (KERN_ERR DRIVER_NAME "ring not correctly aligned\n"); | 
 | 	       return;); | 
 |  | 
 |   OUTB ((physaddr >> 10) & 0xff, OBOE_RING_BASE0); | 
 |   OUTB ((physaddr >> 18) & 0xff, OBOE_RING_BASE1); | 
 |   OUTB ((physaddr >> 26) & 0x3f, OBOE_RING_BASE2); | 
 |  | 
 |   /*Enable DMA controler in byte mode and RX */ | 
 |   OUTB (CONFIG0H_DMA_ON, OBOE_CONFIG0H); | 
 |  | 
 |   /* Start up the clocks */ | 
 |   OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); | 
 |  | 
 |   /*set to sensible speed */ | 
 |   self->speed = 9600; | 
 |   toshoboe_setbaud (self); | 
 |   toshoboe_initptrs (self); | 
 | } | 
 |  | 
 | static void | 
 | toshoboe_isntstuck (struct toshoboe_cb *self) | 
 | { | 
 | } | 
 |  | 
 | static void | 
 | toshoboe_checkstuck (struct toshoboe_cb *self) | 
 | { | 
 |   unsigned long flags; | 
 |  | 
 |   if (0) | 
 |     { | 
 |       spin_lock_irqsave(&self->spinlock, flags); | 
 |  | 
 |       /* This will reset the chip completely */ | 
 |       printk (KERN_ERR DRIVER_NAME ": Resetting chip\n"); | 
 |  | 
 |       toshoboe_stopchip (self); | 
 |       toshoboe_startchip (self); | 
 |       spin_unlock_irqrestore(&self->spinlock, flags); | 
 |     } | 
 | } | 
 |  | 
 | /*Generate packet of about mtt us long */ | 
 | static int | 
 | toshoboe_makemttpacket (struct toshoboe_cb *self, void *buf, int mtt) | 
 | { | 
 |   int xbofs; | 
 |  | 
 |   xbofs = ((int) (mtt/100)) * (int) (self->speed); | 
 |   xbofs=xbofs/80000; /*Eight bits per byte, and mtt is in us*/ | 
 |   xbofs++; | 
 |  | 
 |   IRDA_DEBUG (2, DRIVER_NAME | 
 |       ": generated mtt of %d bytes for %d us at %d baud\n" | 
 | 	  , xbofs,mtt,self->speed); | 
 |  | 
 |   if (xbofs > TX_LEN) | 
 |     { | 
 |       printk (KERN_ERR DRIVER_NAME ": wanted %d bytes MTT but TX_LEN is %d\n", | 
 |               xbofs, TX_LEN); | 
 |       xbofs = TX_LEN; | 
 |     } | 
 |  | 
 |   /*xbofs will do for SIR, MIR and FIR,SIR mode doesn't generate a checksum anyway */ | 
 |   memset (buf, XBOF, xbofs); | 
 |  | 
 |   return xbofs; | 
 | } | 
 |  | 
 | static int toshoboe_invalid_dev(int irq) | 
 | { | 
 |   printk (KERN_WARNING DRIVER_NAME ": irq %d for unknown device.\n", irq); | 
 |   return 1; | 
 | } | 
 |  | 
 | #ifdef USE_PROBE | 
 | /***********************************************************************/ | 
 | /* Probe code */ | 
 |  | 
 | static void | 
 | toshoboe_dumptx (struct toshoboe_cb *self) | 
 | { | 
 |   int i; | 
 |   PROBE_DEBUG(KERN_WARNING "TX:"); | 
 |   for (i = 0; i < RX_SLOTS; ++i) | 
 |     PROBE_DEBUG(" (%d,%02x)",self->ring->tx[i].len,self->ring->tx[i].control); | 
 |   PROBE_DEBUG(" [%d]\n",self->speed); | 
 | } | 
 |  | 
 | static void | 
 | toshoboe_dumprx (struct toshoboe_cb *self, int score) | 
 | { | 
 |   int i; | 
 |   PROBE_DEBUG(" %d\nRX:",score); | 
 |   for (i = 0; i < RX_SLOTS; ++i) | 
 |     PROBE_DEBUG(" (%d,%02x)",self->ring->rx[i].len,self->ring->rx[i].control); | 
 |   PROBE_DEBUG("\n"); | 
 | } | 
 |  | 
 | static inline int | 
 | stuff_byte (__u8 byte, __u8 * buf) | 
 | { | 
 |   switch (byte) | 
 |     { | 
 |     case BOF:                  /* FALLTHROUGH */ | 
 |     case EOF:                  /* FALLTHROUGH */ | 
 |     case CE: | 
 |       /* Insert transparently coded */ | 
 |       buf[0] = CE;              /* Send link escape */ | 
 |       buf[1] = byte ^ IRDA_TRANS; /* Complement bit 5 */ | 
 |       return 2; | 
 |       /* break; */ | 
 |     default: | 
 |       /* Non-special value, no transparency required */ | 
 |       buf[0] = byte; | 
 |       return 1; | 
 |       /* break; */ | 
 |     } | 
 | } | 
 |  | 
 | static irqreturn_t | 
 | toshoboe_probeinterrupt (int irq, void *dev_id, struct pt_regs *regs) | 
 | { | 
 |   struct toshoboe_cb *self = (struct toshoboe_cb *) dev_id; | 
 |   __u8 irqstat; | 
 |  | 
 |   if (self == NULL && toshoboe_invalid_dev(irq)) | 
 |     return IRQ_NONE; | 
 |  | 
 |   irqstat = INB (OBOE_ISR); | 
 |  | 
 | /* was it us */ | 
 |   if (!(irqstat & OBOE_INT_MASK)) | 
 |     return IRQ_NONE; | 
 |  | 
 | /* Ack all the interrupts */ | 
 |   OUTB (irqstat, OBOE_ISR); | 
 |  | 
 |   if (irqstat & OBOE_INT_TXDONE) | 
 |     { | 
 |       int txp; | 
 |  | 
 |       self->int_tx++; | 
 |       PROBE_DEBUG("T"); | 
 |  | 
 |       txp = INB (OBOE_TXSLOT) & OBOE_SLOT_MASK; | 
 |       if (self->ring->tx[txp].control & OBOE_CTL_TX_HW_OWNS) | 
 |         { | 
 |           self->int_tx+=100; | 
 |           PROBE_DEBUG("S"); | 
 |           toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); | 
 |         } | 
 |     } | 
 |  | 
 |   if (irqstat & OBOE_INT_RXDONE) { | 
 |     self->int_rx++; | 
 |     PROBE_DEBUG("R"); } | 
 |   if (irqstat & OBOE_INT_TXUNDER) { | 
 |     self->int_txunder++; | 
 |     PROBE_DEBUG("U"); } | 
 |   if (irqstat & OBOE_INT_RXOVER) { | 
 |     self->int_rxover++; | 
 |     PROBE_DEBUG("O"); } | 
 |   if (irqstat & OBOE_INT_SIP) { | 
 |     self->int_sip++; | 
 |     PROBE_DEBUG("I"); } | 
 |   return IRQ_HANDLED; | 
 | } | 
 |  | 
 | static int | 
 | toshoboe_maketestpacket (unsigned char *buf, int badcrc, int fir) | 
 | { | 
 |   int i; | 
 |   int len = 0; | 
 |   union | 
 |   { | 
 |     __u16 value; | 
 |     __u8 bytes[2]; | 
 |   } | 
 |   fcs; | 
 |  | 
 |   if (fir) | 
 |     { | 
 |       memset (buf, 0, TT_LEN); | 
 |       return (TT_LEN); | 
 |     } | 
 |  | 
 |   fcs.value = INIT_FCS; | 
 |  | 
 |   memset (buf, XBOF, 10); | 
 |   len += 10; | 
 |   buf[len++] = BOF; | 
 |  | 
 |   for (i = 0; i < TT_LEN; ++i) | 
 |     { | 
 |       len += stuff_byte (i, buf + len); | 
 |       fcs.value = irda_fcs (fcs.value, i); | 
 |     } | 
 |  | 
 |   len += stuff_byte (fcs.bytes[0] ^ badcrc, buf + len); | 
 |   len += stuff_byte (fcs.bytes[1] ^ badcrc, buf + len); | 
 |   buf[len++] = EOF; | 
 |   len++; | 
 |   return len; | 
 | } | 
 |  | 
 | static int | 
 | toshoboe_probefail (struct toshoboe_cb *self, char *msg) | 
 | { | 
 |   printk (KERN_ERR DRIVER_NAME "probe(%d) failed %s\n",self-> speed, msg); | 
 |   toshoboe_dumpregs (self); | 
 |   toshoboe_stopchip (self); | 
 |   free_irq (self->io.irq, (void *) self); | 
 |   return 0; | 
 | } | 
 |  | 
 | static int | 
 | toshoboe_numvalidrcvs (struct toshoboe_cb *self) | 
 | { | 
 |   int i, ret = 0; | 
 |   for (i = 0; i < RX_SLOTS; ++i) | 
 |     if ((self->ring->rx[i].control & 0xe0) == 0) | 
 |       ret++; | 
 |  | 
 |   return ret; | 
 | } | 
 |  | 
 | static int | 
 | toshoboe_numrcvs (struct toshoboe_cb *self) | 
 | { | 
 |   int i, ret = 0; | 
 |   for (i = 0; i < RX_SLOTS; ++i) | 
 |     if (!(self->ring->rx[i].control & OBOE_CTL_RX_HW_OWNS)) | 
 |       ret++; | 
 |  | 
 |   return ret; | 
 | } | 
 |  | 
 | static int | 
 | toshoboe_probe (struct toshoboe_cb *self) | 
 | { | 
 |   int i, j, n; | 
 | #ifdef USE_MIR | 
 |   int bauds[] = { 9600, 115200, 4000000, 1152000 }; | 
 | #else | 
 |   int bauds[] = { 9600, 115200, 4000000 }; | 
 | #endif | 
 |   unsigned long flags; | 
 |  | 
 |   IRDA_DEBUG (4, "%s()\n", __FUNCTION__); | 
 |  | 
 |   if (request_irq (self->io.irq, toshoboe_probeinterrupt, | 
 |                    self->io.irqflags, "toshoboe", (void *) self)) | 
 |     { | 
 |       printk (KERN_ERR DRIVER_NAME ": probe failed to allocate irq %d\n", | 
 |               self->io.irq); | 
 |       return 0; | 
 |     } | 
 |  | 
 |   /* test 1: SIR filter and back to back */ | 
 |  | 
 |   for (j = 0; j < (sizeof (bauds) / sizeof (int)); ++j) | 
 |     { | 
 |       int fir = (j > 1); | 
 |       toshoboe_stopchip (self); | 
 |  | 
 |  | 
 |       spin_lock_irqsave(&self->spinlock, flags); | 
 |       /*Address is already setup */ | 
 |       toshoboe_startchip (self); | 
 |       self->int_rx = self->int_tx = 0; | 
 |       self->speed = bauds[j]; | 
 |       toshoboe_setbaud (self); | 
 |       toshoboe_initptrs (self); | 
 |       spin_unlock_irqrestore(&self->spinlock, flags); | 
 |  | 
 |       self->ring->tx[self->txs].control = | 
 | /*   (FIR only) OBOE_CTL_TX_SIP needed for switching to next slot */ | 
 | /*    MIR: all received data is stored in one slot */ | 
 |         (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX | 
 |               : OBOE_CTL_TX_HW_OWNS ; | 
 |       self->ring->tx[self->txs].len = | 
 |         toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); | 
 |       self->txs++; | 
 |       self->txs %= TX_SLOTS; | 
 |  | 
 |       self->ring->tx[self->txs].control = | 
 |         (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_SIP | 
 |               : OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ; | 
 |       self->ring->tx[self->txs].len = | 
 |         toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); | 
 |       self->txs++; | 
 |       self->txs %= TX_SLOTS; | 
 |  | 
 |       self->ring->tx[self->txs].control = | 
 |         (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX | 
 |               : OBOE_CTL_TX_HW_OWNS ; | 
 |       self->ring->tx[self->txs].len = | 
 |         toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); | 
 |       self->txs++; | 
 |       self->txs %= TX_SLOTS; | 
 |  | 
 |       self->ring->tx[self->txs].control = | 
 |         (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX | 
 |               | OBOE_CTL_TX_SIP     | OBOE_CTL_TX_BAD_CRC | 
 |               : OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ; | 
 |       self->ring->tx[self->txs].len = | 
 |         toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); | 
 |       self->txs++; | 
 |       self->txs %= TX_SLOTS; | 
 |  | 
 |       toshoboe_dumptx (self); | 
 |       /* Turn on TX and RX and loopback */ | 
 |       toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); | 
 |  | 
 |       i = 0; | 
 |       n = fir ? 1 : 4; | 
 |       while (toshoboe_numvalidrcvs (self) != n) | 
 |         { | 
 |           if (i > 4800) | 
 |               return toshoboe_probefail (self, "filter test"); | 
 |           udelay ((9600*(TT_LEN+16))/self->speed); | 
 |           i++; | 
 |         } | 
 |  | 
 |       n = fir ? 203 : 102; | 
 |       while ((toshoboe_numrcvs(self) != self->int_rx) || (self->int_tx != n)) | 
 |         { | 
 |           if (i > 4800) | 
 |               return toshoboe_probefail (self, "interrupt test"); | 
 |           udelay ((9600*(TT_LEN+16))/self->speed); | 
 |           i++; | 
 |         } | 
 |      toshoboe_dumprx (self,i); | 
 |  | 
 |      } | 
 |  | 
 |   /* test 2: SIR in char at a time */ | 
 |  | 
 |   toshoboe_stopchip (self); | 
 |   self->int_rx = self->int_tx = 0; | 
 |  | 
 |   spin_lock_irqsave(&self->spinlock, flags); | 
 |   toshoboe_startchip (self); | 
 |   spin_unlock_irqrestore(&self->spinlock, flags); | 
 |  | 
 |   self->async = 1; | 
 |   self->speed = 115200; | 
 |   toshoboe_setbaud (self); | 
 |   self->ring->tx[self->txs].control = | 
 |     OBOE_CTL_TX_RTCENTX | OBOE_CTL_TX_HW_OWNS; | 
 |   self->ring->tx[self->txs].len = 4; | 
 |  | 
 |   ((unsigned char *) self->tx_bufs[self->txs])[0] = 'f'; | 
 |   ((unsigned char *) self->tx_bufs[self->txs])[1] = 'i'; | 
 |   ((unsigned char *) self->tx_bufs[self->txs])[2] = 's'; | 
 |   ((unsigned char *) self->tx_bufs[self->txs])[3] = 'h'; | 
 |   toshoboe_dumptx (self); | 
 |   toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); | 
 |  | 
 |   i = 0; | 
 |   while (toshoboe_numvalidrcvs (self) != 4) | 
 |     { | 
 |       if (i > 100) | 
 |           return toshoboe_probefail (self, "Async test"); | 
 |       udelay (100); | 
 |       i++; | 
 |     } | 
 |  | 
 |   while ((toshoboe_numrcvs (self) != self->int_rx) || (self->int_tx != 1)) | 
 |     { | 
 |       if (i > 100) | 
 |           return toshoboe_probefail (self, "Async interrupt test"); | 
 |       udelay (100); | 
 |       i++; | 
 |     } | 
 |   toshoboe_dumprx (self,i); | 
 |  | 
 |   self->async = 0; | 
 |   self->speed = 9600; | 
 |   toshoboe_setbaud (self); | 
 |   toshoboe_stopchip (self); | 
 |  | 
 |   free_irq (self->io.irq, (void *) self); | 
 |  | 
 |   printk (KERN_WARNING DRIVER_NAME ": Self test passed ok\n"); | 
 |  | 
 |   return 1; | 
 | } | 
 | #endif | 
 |  | 
 | /******************************************************************/ | 
 | /* Netdev style code */ | 
 |  | 
 | /* Transmit something */ | 
 | static int | 
 | toshoboe_hard_xmit (struct sk_buff *skb, struct net_device *dev) | 
 | { | 
 |   struct toshoboe_cb *self; | 
 |   __s32 speed; | 
 |   int mtt, len, ctl; | 
 |   unsigned long flags; | 
 |   struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb; | 
 |  | 
 |   self = (struct toshoboe_cb *) dev->priv; | 
 |  | 
 |   IRDA_ASSERT (self != NULL, return 0; ); | 
 |  | 
 |   IRDA_DEBUG (1, "%s.tx:%x(%x)%x\n", __FUNCTION__ | 
 |       ,skb->len,self->txpending,INB (OBOE_ENABLEH)); | 
 |   if (!cb->magic) { | 
 |       IRDA_DEBUG (2, "%s.Not IrLAP:%x\n", __FUNCTION__, cb->magic); | 
 | #ifdef DUMP_PACKETS | 
 |       _dumpbufs(skb->data,skb->len,'>'); | 
 | #endif | 
 |     } | 
 |  | 
 |   /* change speed pending, wait for its execution */ | 
 |   if (self->new_speed) | 
 |       return -EBUSY; | 
 |  | 
 |   /* device stopped (apm) wait for restart */ | 
 |   if (self->stopped) | 
 |       return -EBUSY; | 
 |  | 
 |   toshoboe_checkstuck (self); | 
 |  | 
 |   dev->trans_start = jiffies; | 
 |  | 
 |  /* Check if we need to change the speed */ | 
 |   /* But not now. Wait after transmission if mtt not required */ | 
 |   speed=irda_get_next_speed(skb); | 
 |   if ((speed != self->io.speed) && (speed != -1)) | 
 |     { | 
 |       spin_lock_irqsave(&self->spinlock, flags); | 
 |  | 
 |       if (self->txpending || skb->len) | 
 |         { | 
 |           self->new_speed = speed; | 
 |           IRDA_DEBUG (1, "%s: Queued TxDone scheduled speed change %d\n" , | 
 | 		      __FUNCTION__, speed); | 
 |           /* if no data, that's all! */ | 
 |           if (!skb->len) | 
 |             { | 
 | 	      spin_unlock_irqrestore(&self->spinlock, flags); | 
 |               dev_kfree_skb (skb); | 
 |               return 0; | 
 |             } | 
 |           /* True packet, go on, but */ | 
 |           /* do not accept anything before change speed execution */ | 
 |           netif_stop_queue(dev); | 
 |           /* ready to process TxDone interrupt */ | 
 | 	  spin_unlock_irqrestore(&self->spinlock, flags); | 
 |         } | 
 |       else | 
 |         { | 
 |           /* idle and no data, change speed now */ | 
 |           self->speed = speed; | 
 |           toshoboe_setbaud (self); | 
 | 	  spin_unlock_irqrestore(&self->spinlock, flags); | 
 |           dev_kfree_skb (skb); | 
 |           return 0; | 
 |         } | 
 |  | 
 |     } | 
 |  | 
 |   if ((mtt = irda_get_mtt(skb))) | 
 |     { | 
 |       /* This is fair since the queue should be empty anyway */ | 
 |       spin_lock_irqsave(&self->spinlock, flags); | 
 |  | 
 |       if (self->txpending) | 
 |         { | 
 | 	  spin_unlock_irqrestore(&self->spinlock, flags); | 
 |           return -EBUSY; | 
 |         } | 
 |  | 
 |       /* If in SIR mode we need to generate a string of XBOFs */ | 
 |       /* In MIR and FIR we need to generate a string of data */ | 
 |       /* which we will add a wrong checksum to */ | 
 |  | 
 |       mtt = toshoboe_makemttpacket (self, self->tx_bufs[self->txs], mtt); | 
 |       IRDA_DEBUG (1, "%s.mtt:%x(%x)%d\n", __FUNCTION__ | 
 |           ,skb->len,mtt,self->txpending); | 
 |       if (mtt) | 
 |         { | 
 |           self->ring->tx[self->txs].len = mtt & 0xfff; | 
 |  | 
 |           ctl = OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX; | 
 |           if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_FIRON) | 
 |             { | 
 |               ctl |= OBOE_CTL_TX_BAD_CRC | OBOE_CTL_TX_SIP ; | 
 |             } | 
 | #ifdef USE_MIR | 
 |           else if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_MIRON) | 
 |             { | 
 |               ctl |= OBOE_CTL_TX_BAD_CRC; | 
 |             } | 
 | #endif | 
 |           self->ring->tx[self->txs].control = ctl; | 
 |  | 
 |           OUTB (0x0, OBOE_ENABLEH); | 
 |           /* It is only a timer. Do not send mtt packet outside! */ | 
 |           toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); | 
 |  | 
 |           self->txpending++; | 
 |  | 
 |           self->txs++; | 
 |           self->txs %= TX_SLOTS; | 
 |  | 
 |         } | 
 |       else | 
 |         { | 
 |           printk(KERN_ERR DRIVER_NAME ": problem with mtt packet - ignored\n"); | 
 |         } | 
 |       spin_unlock_irqrestore(&self->spinlock, flags); | 
 |     } | 
 |  | 
 | #ifdef DUMP_PACKETS | 
 | dumpbufs(skb->data,skb->len,'>'); | 
 | #endif | 
 |  | 
 |   spin_lock_irqsave(&self->spinlock, flags); | 
 |  | 
 |   if (self->ring->tx[self->txs].control & OBOE_CTL_TX_HW_OWNS) | 
 |     { | 
 |       IRDA_DEBUG (0, "%s.ful:%x(%x)%x\n", __FUNCTION__ | 
 |           ,skb->len, self->ring->tx[self->txs].control, self->txpending); | 
 |       toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX); | 
 |       spin_unlock_irqrestore(&self->spinlock, flags); | 
 |       return -EBUSY; | 
 |     } | 
 |  | 
 |   if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_SIRON) | 
 |     { | 
 |       len = async_wrap_skb (skb, self->tx_bufs[self->txs], TX_BUF_SZ); | 
 |     } | 
 |   else | 
 |     { | 
 |       len = skb->len; | 
 |       memcpy (self->tx_bufs[self->txs], skb->data, len); | 
 |     } | 
 |   self->ring->tx[self->txs].len = len & 0x0fff; | 
 |  | 
 |   /*Sometimes the HW doesn't see us assert RTCENTX in the interrupt code */ | 
 |   /*later this plays safe, we garuntee the last packet to be transmitted */ | 
 |   /*has RTCENTX set */ | 
 |  | 
 |   ctl = OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX; | 
 |   if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_FIRON) | 
 |     { | 
 |       ctl |= OBOE_CTL_TX_SIP ; | 
 |     } | 
 |   self->ring->tx[self->txs].control = ctl; | 
 |  | 
 |   /* If transmitter is idle start in one-shot mode */ | 
 |  | 
 |   if (!self->txpending) | 
 |       toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX); | 
 |  | 
 |   self->txpending++; | 
 |  | 
 |   self->txs++; | 
 |   self->txs %= TX_SLOTS; | 
 |  | 
 |   spin_unlock_irqrestore(&self->spinlock, flags); | 
 |   dev_kfree_skb (skb); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | /*interrupt handler */ | 
 | static irqreturn_t | 
 | toshoboe_interrupt (int irq, void *dev_id, struct pt_regs *regs) | 
 | { | 
 |   struct toshoboe_cb *self = (struct toshoboe_cb *) dev_id; | 
 |   __u8 irqstat; | 
 |   struct sk_buff *skb = NULL; | 
 |  | 
 |   if (self == NULL && toshoboe_invalid_dev(irq)) | 
 |     return IRQ_NONE; | 
 |  | 
 |   irqstat = INB (OBOE_ISR); | 
 |  | 
 | /* was it us */ | 
 |   if (!(irqstat & OBOE_INT_MASK)) | 
 |       return IRQ_NONE; | 
 |  | 
 | /* Ack all the interrupts */ | 
 |   OUTB (irqstat, OBOE_ISR); | 
 |  | 
 |   toshoboe_isntstuck (self); | 
 |  | 
 | /* Txdone */ | 
 |   if (irqstat & OBOE_INT_TXDONE) | 
 |     { | 
 |       int txp, txpc; | 
 |       int i; | 
 |  | 
 |       txp = self->txpending; | 
 |       self->txpending = 0; | 
 |  | 
 |       for (i = 0; i < TX_SLOTS; ++i) | 
 |         { | 
 |           if (self->ring->tx[i].control & OBOE_CTL_TX_HW_OWNS) | 
 |               self->txpending++; | 
 |         } | 
 |       IRDA_DEBUG (1, "%s.txd(%x)%x/%x\n", __FUNCTION__ | 
 |           ,irqstat,txp,self->txpending); | 
 |  | 
 |       txp = INB (OBOE_TXSLOT) & OBOE_SLOT_MASK; | 
 |  | 
 |       /* Got anything queued ? start it together */ | 
 |       if (self->ring->tx[txp].control & OBOE_CTL_TX_HW_OWNS) | 
 |         { | 
 |           txpc = txp; | 
 | #ifdef OPTIMIZE_TX | 
 |           while (self->ring->tx[txpc].control & OBOE_CTL_TX_HW_OWNS) | 
 |             { | 
 |               txp = txpc; | 
 |               txpc++; | 
 |               txpc %= TX_SLOTS; | 
 |               self->stats.tx_packets++; | 
 |               if (self->ring->tx[txpc].control & OBOE_CTL_TX_HW_OWNS) | 
 |                   self->ring->tx[txp].control &= ~OBOE_CTL_TX_RTCENTX; | 
 |             } | 
 |           self->stats.tx_packets--; | 
 | #else | 
 |           self->stats.tx_packets++; | 
 | #endif | 
 |           toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX); | 
 |         } | 
 |  | 
 |       if ((!self->txpending) && (self->new_speed)) | 
 |         { | 
 |           self->speed = self->new_speed; | 
 |           IRDA_DEBUG (1, "%s: Executed TxDone scheduled speed change %d\n", | 
 | 		      __FUNCTION__, self->speed); | 
 |           toshoboe_setbaud (self); | 
 |         } | 
 |  | 
 |       /* Tell network layer that we want more frames */ | 
 |       if (!self->new_speed) | 
 |           netif_wake_queue(self->netdev); | 
 |     } | 
 |  | 
 |   if (irqstat & OBOE_INT_RXDONE) | 
 |     { | 
 |       while (!(self->ring->rx[self->rxs].control & OBOE_CTL_RX_HW_OWNS)) | 
 |         { | 
 |           int len = self->ring->rx[self->rxs].len; | 
 |           skb = NULL; | 
 |           IRDA_DEBUG (3, "%s.rcv:%x(%x)\n", __FUNCTION__ | 
 | 		      ,len,self->ring->rx[self->rxs].control); | 
 |  | 
 | #ifdef DUMP_PACKETS | 
 | dumpbufs(self->rx_bufs[self->rxs],len,'<'); | 
 | #endif | 
 |  | 
 |           if (self->ring->rx[self->rxs].control == 0) | 
 |             { | 
 |               __u8 enable = INB (OBOE_ENABLEH); | 
 |  | 
 |               /* In SIR mode we need to check the CRC as this */ | 
 |               /* hasn't been done by the hardware */ | 
 |               if (enable & OBOE_ENABLEH_SIRON) | 
 |                 { | 
 |                   if (!toshoboe_checkfcs (self->rx_bufs[self->rxs], len)) | 
 |                       len = 0; | 
 |                   /*Trim off the CRC */ | 
 |                   if (len > 1) | 
 |                       len -= 2; | 
 |                   else | 
 |                       len = 0; | 
 |                   IRDA_DEBUG (1, "%s.SIR:%x(%x)\n", __FUNCTION__, len,enable); | 
 |                 } | 
 |  | 
 | #ifdef USE_MIR | 
 |               else if (enable & OBOE_ENABLEH_MIRON) | 
 |                 { | 
 |                   if (len > 1) | 
 |                       len -= 2; | 
 |                   else | 
 |                       len = 0; | 
 |                   IRDA_DEBUG (2, "%s.MIR:%x(%x)\n", __FUNCTION__, len,enable); | 
 |                 } | 
 | #endif | 
 |               else if (enable & OBOE_ENABLEH_FIRON) | 
 |                 { | 
 |                   if (len > 3) | 
 |                       len -= 4;   /*FIXME: check this */ | 
 |                   else | 
 |                       len = 0; | 
 |                   IRDA_DEBUG (1, "%s.FIR:%x(%x)\n", __FUNCTION__, len,enable); | 
 |                 } | 
 |               else | 
 |                   IRDA_DEBUG (0, "%s.?IR:%x(%x)\n", __FUNCTION__, len,enable); | 
 |  | 
 |               if (len) | 
 |                 { | 
 |                   skb = dev_alloc_skb (len + 1); | 
 |                   if (skb) | 
 |                     { | 
 |                       skb_reserve (skb, 1); | 
 |  | 
 |                       skb_put (skb, len); | 
 |                       memcpy (skb->data, self->rx_bufs[self->rxs], len); | 
 |  | 
 |                       self->stats.rx_packets++; | 
 |                       skb->dev = self->netdev; | 
 |                       skb->mac.raw = skb->data; | 
 |                       skb->protocol = htons (ETH_P_IRDA); | 
 |                     } | 
 |                   else | 
 |                     { | 
 |                       printk (KERN_INFO | 
 |                               "%s(), memory squeeze, dropping frame.\n", | 
 | 			      __FUNCTION__); | 
 |                     } | 
 |                 } | 
 |             } | 
 |           else | 
 |             { | 
 |             /* TODO: =========================================== */ | 
 |             /*  if OBOE_CTL_RX_LENGTH, our buffers are too small */ | 
 |             /* (MIR or FIR) data is lost. */ | 
 |             /* (SIR) data is splitted in several slots. */ | 
 |             /* we have to join all the received buffers received */ | 
 |             /*in a large buffer before checking CRC. */ | 
 |             IRDA_DEBUG (0, "%s.err:%x(%x)\n", __FUNCTION__ | 
 |                 ,len,self->ring->rx[self->rxs].control); | 
 |             } | 
 |  | 
 |           self->ring->rx[self->rxs].len = 0x0; | 
 |           self->ring->rx[self->rxs].control = OBOE_CTL_RX_HW_OWNS; | 
 |  | 
 |           self->rxs++; | 
 |           self->rxs %= RX_SLOTS; | 
 |  | 
 |           if (skb) | 
 |               netif_rx (skb); | 
 |  | 
 |         } | 
 |     } | 
 |  | 
 |   if (irqstat & OBOE_INT_TXUNDER) | 
 |     { | 
 |       printk (KERN_WARNING DRIVER_NAME ": tx fifo underflow\n"); | 
 |     } | 
 |   if (irqstat & OBOE_INT_RXOVER) | 
 |     { | 
 |       printk (KERN_WARNING DRIVER_NAME ": rx fifo overflow\n"); | 
 |     } | 
 | /* This must be useful for something... */ | 
 |   if (irqstat & OBOE_INT_SIP) | 
 |     { | 
 |       self->int_sip++; | 
 |       IRDA_DEBUG (1, "%s.sip:%x(%x)%x\n", __FUNCTION__ | 
 | 	      ,self->int_sip,irqstat,self->txpending); | 
 |     } | 
 |   return IRQ_HANDLED; | 
 | } | 
 |  | 
 |  | 
 | static int | 
 | toshoboe_net_open (struct net_device *dev) | 
 | { | 
 |   struct toshoboe_cb *self; | 
 |   unsigned long flags; | 
 |  | 
 |   IRDA_DEBUG (4, "%s()\n", __FUNCTION__); | 
 |  | 
 |   IRDA_ASSERT (dev != NULL, return -1; ); | 
 |   self = (struct toshoboe_cb *) dev->priv; | 
 |  | 
 |   IRDA_ASSERT (self != NULL, return 0; ); | 
 |  | 
 |   if (self->async) | 
 |     return -EBUSY; | 
 |  | 
 |   if (self->stopped) | 
 |     return 0; | 
 |  | 
 |   if (request_irq (self->io.irq, toshoboe_interrupt, | 
 |                    SA_SHIRQ | SA_INTERRUPT, dev->name, (void *) self)) | 
 |     { | 
 |       return -EAGAIN; | 
 |     } | 
 |  | 
 |   spin_lock_irqsave(&self->spinlock, flags); | 
 |   toshoboe_startchip (self); | 
 |   spin_unlock_irqrestore(&self->spinlock, flags); | 
 |  | 
 |   /* Ready to play! */ | 
 |   netif_start_queue(dev); | 
 |  | 
 |   /* | 
 |    * Open new IrLAP layer instance, now that everything should be | 
 |    * initialized properly | 
 |    */ | 
 |   self->irlap = irlap_open (dev, &self->qos, driver_name); | 
 |  | 
 |   self->irdad = 1; | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | static int | 
 | toshoboe_net_close (struct net_device *dev) | 
 | { | 
 |   struct toshoboe_cb *self; | 
 |  | 
 |   IRDA_DEBUG (4, "%s()\n", __FUNCTION__); | 
 |  | 
 |   IRDA_ASSERT (dev != NULL, return -1; ); | 
 |   self = (struct toshoboe_cb *) dev->priv; | 
 |  | 
 |   /* Stop device */ | 
 |   netif_stop_queue(dev); | 
 |  | 
 |   /* Stop and remove instance of IrLAP */ | 
 |   if (self->irlap) | 
 |     irlap_close (self->irlap); | 
 |   self->irlap = NULL; | 
 |  | 
 |   self->irdad = 0; | 
 |  | 
 |   free_irq (self->io.irq, (void *) self); | 
 |  | 
 |   if (!self->stopped) | 
 |     { | 
 |       toshoboe_stopchip (self); | 
 |     } | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | /* | 
 |  * Function toshoboe_net_ioctl (dev, rq, cmd) | 
 |  * | 
 |  *    Process IOCTL commands for this device | 
 |  * | 
 |  */ | 
 | static int | 
 | toshoboe_net_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) | 
 | { | 
 |   struct if_irda_req *irq = (struct if_irda_req *) rq; | 
 |   struct toshoboe_cb *self; | 
 |   unsigned long flags; | 
 |   int ret = 0; | 
 |  | 
 |   IRDA_ASSERT (dev != NULL, return -1; ); | 
 |  | 
 |   self = dev->priv; | 
 |  | 
 |   IRDA_ASSERT (self != NULL, return -1; ); | 
 |  | 
 |   IRDA_DEBUG (5, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__, dev->name, cmd); | 
 |  | 
 |   /* Disable interrupts & save flags */ | 
 |   spin_lock_irqsave(&self->spinlock, flags); | 
 |  | 
 |   switch (cmd) | 
 |     { | 
 |     case SIOCSBANDWIDTH:       /* Set bandwidth */ | 
 |       /* This function will also be used by IrLAP to change the | 
 |        * speed, so we still must allow for speed change within | 
 |        * interrupt context. | 
 |        */ | 
 |       IRDA_DEBUG (1, "%s(BANDWIDTH), %s, (%X/%ld\n", __FUNCTION__ | 
 |           ,dev->name, INB (OBOE_STATUS), irq->ifr_baudrate ); | 
 |       if (!in_interrupt () && !capable (CAP_NET_ADMIN)) { | 
 | 	ret = -EPERM; | 
 | 	goto out; | 
 |       } | 
 |  | 
 |       /* self->speed=irq->ifr_baudrate; */ | 
 |       /* toshoboe_setbaud(self); */ | 
 |       /* Just change speed once - inserted by Paul Bristow */ | 
 |       self->new_speed = irq->ifr_baudrate; | 
 |       break; | 
 |     case SIOCSMEDIABUSY:       /* Set media busy */ | 
 |       IRDA_DEBUG (1, "%s(MEDIABUSY), %s, (%X/%x)\n", __FUNCTION__ | 
 |           ,dev->name, INB (OBOE_STATUS), capable (CAP_NET_ADMIN) ); | 
 |       if (!capable (CAP_NET_ADMIN)) { | 
 | 	ret = -EPERM; | 
 | 	goto out; | 
 |       } | 
 |       irda_device_set_media_busy (self->netdev, TRUE); | 
 |       break; | 
 |     case SIOCGRECEIVING:       /* Check if we are receiving right now */ | 
 |       irq->ifr_receiving = (INB (OBOE_STATUS) & OBOE_STATUS_RXBUSY) ? 1 : 0; | 
 |       IRDA_DEBUG (3, "%s(RECEIVING), %s, (%X/%x)\n", __FUNCTION__ | 
 |           ,dev->name, INB (OBOE_STATUS), irq->ifr_receiving ); | 
 |       break; | 
 |     default: | 
 |       IRDA_DEBUG (1, "%s(?), %s, (cmd=0x%X)\n", __FUNCTION__, dev->name, cmd); | 
 |       ret = -EOPNOTSUPP; | 
 |     } | 
 | out: | 
 |   spin_unlock_irqrestore(&self->spinlock, flags); | 
 |   return ret; | 
 |  | 
 | } | 
 |  | 
 | MODULE_DESCRIPTION("Toshiba OBOE IrDA Device Driver"); | 
 | MODULE_AUTHOR("James McKenzie <james@fishsoup.dhs.org>"); | 
 | MODULE_LICENSE("GPL"); | 
 |  | 
 | module_param (max_baud, int, 0); | 
 | MODULE_PARM_DESC(max_baud, "Maximum baud rate"); | 
 |  | 
 | #ifdef USE_PROBE | 
 | module_param (do_probe, bool, 0); | 
 | MODULE_PARM_DESC(do_probe, "Enable/disable chip probing and self-test"); | 
 | #endif | 
 |  | 
 | static void | 
 | toshoboe_close (struct pci_dev *pci_dev) | 
 | { | 
 |   int i; | 
 |   struct toshoboe_cb *self = (struct toshoboe_cb*)pci_get_drvdata(pci_dev); | 
 |  | 
 |   IRDA_DEBUG (4, "%s()\n", __FUNCTION__); | 
 |  | 
 |   IRDA_ASSERT (self != NULL, return; ); | 
 |  | 
 |   if (!self->stopped) | 
 |     { | 
 |       toshoboe_stopchip (self); | 
 |     } | 
 |  | 
 |   release_region (self->io.fir_base, self->io.fir_ext); | 
 |  | 
 |   for (i = 0; i < TX_SLOTS; ++i) | 
 |     { | 
 |       kfree (self->tx_bufs[i]); | 
 |       self->tx_bufs[i] = NULL; | 
 |     } | 
 |  | 
 |   for (i = 0; i < RX_SLOTS; ++i) | 
 |     { | 
 |       kfree (self->rx_bufs[i]); | 
 |       self->rx_bufs[i] = NULL; | 
 |     } | 
 |  | 
 |   unregister_netdev(self->netdev); | 
 |  | 
 |   kfree (self->ringbuf); | 
 |   self->ringbuf = NULL; | 
 |   self->ring = NULL; | 
 |  | 
 |   free_netdev(self->netdev); | 
 | } | 
 |  | 
 | static int | 
 | toshoboe_open (struct pci_dev *pci_dev, const struct pci_device_id *pdid) | 
 | { | 
 |   struct toshoboe_cb *self; | 
 |   struct net_device *dev; | 
 |   int i = 0; | 
 |   int ok = 0; | 
 |   int err; | 
 |  | 
 |   IRDA_DEBUG (4, "%s()\n", __FUNCTION__); | 
 |  | 
 |   if ((err=pci_enable_device(pci_dev))) | 
 |     return err; | 
 |  | 
 |   dev = alloc_irdadev(sizeof (struct toshoboe_cb)); | 
 |   if (dev == NULL) | 
 |     { | 
 |       printk (KERN_ERR DRIVER_NAME ": can't allocate memory for " | 
 |               "IrDA control block\n"); | 
 |       return -ENOMEM; | 
 |     } | 
 |  | 
 |   self = dev->priv; | 
 |   self->netdev = dev; | 
 |   self->pdev = pci_dev; | 
 |   self->base = pci_resource_start(pci_dev,0); | 
 |  | 
 |   self->io.fir_base = self->base; | 
 |   self->io.fir_ext = OBOE_IO_EXTENT; | 
 |   self->io.irq = pci_dev->irq; | 
 |   self->io.irqflags = SA_SHIRQ | SA_INTERRUPT; | 
 |  | 
 |   self->speed = self->io.speed = 9600; | 
 |   self->async = 0; | 
 |  | 
 |   /* Lock the port that we need */ | 
 |   if (NULL==request_region (self->io.fir_base, self->io.fir_ext, driver_name)) | 
 |     { | 
 |       printk (KERN_ERR DRIVER_NAME ": can't get iobase of 0x%03x\n" | 
 | 	      ,self->io.fir_base); | 
 |       err = -EBUSY; | 
 |       goto freeself; | 
 |     } | 
 |  | 
 |   spin_lock_init(&self->spinlock); | 
 |  | 
 |   irda_init_max_qos_capabilies (&self->qos); | 
 |   self->qos.baud_rate.bits = 0; | 
 |  | 
 |   if (max_baud >= 2400) | 
 |     self->qos.baud_rate.bits |= IR_2400; | 
 |   /*if (max_baud>=4800) idev->qos.baud_rate.bits|=IR_4800; */ | 
 |   if (max_baud >= 9600) | 
 |     self->qos.baud_rate.bits |= IR_9600; | 
 |   if (max_baud >= 19200) | 
 |     self->qos.baud_rate.bits |= IR_19200; | 
 |   if (max_baud >= 115200) | 
 |     self->qos.baud_rate.bits |= IR_115200; | 
 | #ifdef USE_MIR | 
 |   if (max_baud >= 1152000) | 
 |     { | 
 |       self->qos.baud_rate.bits |= IR_1152000; | 
 |     } | 
 | #endif | 
 |   if (max_baud >= 4000000) | 
 |     { | 
 |       self->qos.baud_rate.bits |= (IR_4000000 << 8); | 
 |     } | 
 |  | 
 |   /*FIXME: work this out... */ | 
 |   self->qos.min_turn_time.bits = 0xff; | 
 |  | 
 |   irda_qos_bits_to_value (&self->qos); | 
 |  | 
 |   /* Allocate twice the size to guarantee alignment */ | 
 |   self->ringbuf = (void *) kmalloc (OBOE_RING_LEN << 1, GFP_KERNEL); | 
 |   if (!self->ringbuf) | 
 |     { | 
 |       printk (KERN_ERR DRIVER_NAME ": can't allocate DMA buffers\n"); | 
 |       err = -ENOMEM; | 
 |       goto freeregion; | 
 |     } | 
 |  | 
 | #if (BITS_PER_LONG == 64) | 
 | #error broken on 64-bit:  casts pointer to 32-bit, and then back to pointer. | 
 | #endif | 
 |  | 
 |   /*We need to align the taskfile on a taskfile size boundary */ | 
 |   { | 
 |     unsigned long addr; | 
 |  | 
 |     addr = (__u32) self->ringbuf; | 
 |     addr &= ~(OBOE_RING_LEN - 1); | 
 |     addr += OBOE_RING_LEN; | 
 |     self->ring = (struct OboeRing *) addr; | 
 |   } | 
 |  | 
 |   memset (self->ring, 0, OBOE_RING_LEN); | 
 |   self->io.mem_base = (__u32) self->ring; | 
 |  | 
 |   ok = 1; | 
 |   for (i = 0; i < TX_SLOTS; ++i) | 
 |     { | 
 |       self->tx_bufs[i] = kmalloc (TX_BUF_SZ, GFP_KERNEL); | 
 |       if (!self->tx_bufs[i]) | 
 |         ok = 0; | 
 |     } | 
 |  | 
 |   for (i = 0; i < RX_SLOTS; ++i) | 
 |     { | 
 |       self->rx_bufs[i] = kmalloc (RX_BUF_SZ, GFP_KERNEL); | 
 |       if (!self->rx_bufs[i]) | 
 |         ok = 0; | 
 |     } | 
 |  | 
 |   if (!ok) | 
 |     { | 
 |       printk (KERN_ERR DRIVER_NAME ": can't allocate rx/tx buffers\n"); | 
 |       err = -ENOMEM; | 
 |       goto freebufs; | 
 |     } | 
 |  | 
 |  | 
 | #ifdef USE_PROBE | 
 |   if (do_probe) | 
 |     if (!toshoboe_probe (self)) | 
 |       { | 
 |         err = -ENODEV; | 
 |         goto freebufs; | 
 |       } | 
 | #endif | 
 |  | 
 |   SET_MODULE_OWNER(dev); | 
 |   SET_NETDEV_DEV(dev, &pci_dev->dev); | 
 |   dev->hard_start_xmit = toshoboe_hard_xmit; | 
 |   dev->open = toshoboe_net_open; | 
 |   dev->stop = toshoboe_net_close; | 
 |   dev->do_ioctl = toshoboe_net_ioctl; | 
 |  | 
 |   err = register_netdev(dev); | 
 |   if (err) | 
 |     { | 
 |       printk (KERN_ERR DRIVER_NAME ": register_netdev() failed\n"); | 
 |       err = -ENOMEM; | 
 |       goto freebufs; | 
 |     } | 
 |   printk (KERN_INFO "IrDA: Registered device %s\n", dev->name); | 
 |  | 
 |   pci_set_drvdata(pci_dev,self); | 
 |  | 
 |   printk (KERN_INFO DRIVER_NAME ": Using multiple tasks, version %s\n", rcsid); | 
 |  | 
 |   return 0; | 
 |  | 
 | freebufs: | 
 |   for (i = 0; i < TX_SLOTS; ++i) | 
 |     kfree (self->tx_bufs[i]); | 
 |   for (i = 0; i < RX_SLOTS; ++i) | 
 |     kfree (self->rx_bufs[i]); | 
 |   kfree(self->ringbuf); | 
 |  | 
 | freeregion: | 
 |   release_region (self->io.fir_base, self->io.fir_ext); | 
 |  | 
 | freeself: | 
 |   free_netdev(dev); | 
 |  | 
 |   return err; | 
 | } | 
 |  | 
 | static int | 
 | toshoboe_gotosleep (struct pci_dev *pci_dev, pm_message_t crap) | 
 | { | 
 |   struct toshoboe_cb *self = (struct toshoboe_cb*)pci_get_drvdata(pci_dev); | 
 |   unsigned long flags; | 
 |   int i = 10; | 
 |  | 
 |   IRDA_DEBUG (4, "%s()\n", __FUNCTION__); | 
 |  | 
 |   if (!self || self->stopped) | 
 |     return 0; | 
 |  | 
 |   if ((!self->irdad) && (!self->async)) | 
 |     return 0; | 
 |  | 
 | /* Flush all packets */ | 
 |   while ((i--) && (self->txpending)) | 
 |     udelay (10000); | 
 |  | 
 |   spin_lock_irqsave(&self->spinlock, flags); | 
 |  | 
 |   toshoboe_stopchip (self); | 
 |   self->stopped = 1; | 
 |   self->txpending = 0; | 
 |  | 
 |   spin_unlock_irqrestore(&self->spinlock, flags); | 
 |   return 0; | 
 | } | 
 |  | 
 | static int | 
 | toshoboe_wakeup (struct pci_dev *pci_dev) | 
 | { | 
 |   struct toshoboe_cb *self = (struct toshoboe_cb*)pci_get_drvdata(pci_dev); | 
 |   unsigned long flags; | 
 |  | 
 |   IRDA_DEBUG (4, "%s()\n", __FUNCTION__); | 
 |  | 
 |   if (!self || !self->stopped) | 
 |     return 0; | 
 |  | 
 |   if ((!self->irdad) && (!self->async)) | 
 |     return 0; | 
 |  | 
 |   spin_lock_irqsave(&self->spinlock, flags); | 
 |  | 
 |   toshoboe_startchip (self); | 
 |   self->stopped = 0; | 
 |  | 
 |   netif_wake_queue(self->netdev); | 
 |   spin_unlock_irqrestore(&self->spinlock, flags); | 
 |   return 0; | 
 | } | 
 |  | 
 | static struct pci_driver donauboe_pci_driver = { | 
 | 	.name		= "donauboe", | 
 | 	.id_table	= toshoboe_pci_tbl, | 
 | 	.probe		= toshoboe_open, | 
 | 	.remove		= toshoboe_close, | 
 | 	.suspend	= toshoboe_gotosleep, | 
 | 	.resume		= toshoboe_wakeup  | 
 | }; | 
 |  | 
 | static int __init | 
 | donauboe_init (void) | 
 | { | 
 |   return pci_register_driver(&donauboe_pci_driver); | 
 | } | 
 |  | 
 | static void __exit | 
 | donauboe_cleanup (void) | 
 | { | 
 |   pci_unregister_driver(&donauboe_pci_driver); | 
 | } | 
 |  | 
 | module_init(donauboe_init); | 
 | module_exit(donauboe_cleanup); |