--- /dev/null
+ Notes on Linux's SG driver version 2.1.30
+ -----------------------------------------
+ 990328
+
+Introduction
+============
+These are notes on the Linux SCSI generic packet device driver (sg)
+describing version 2.1.30 . The original driver was written by Lawrence
+Foard and has remained in place with minimal changes since circa 1992.
+Version 2 of this driver remains backward compatible (binary and
+source **) with the original. It adds scatter gather, command queuing,
+per file descriptor sequencing, asynchronous notification and better
+error reporting.
+
+Sg is one of the four "high level" SCSI device drivers along with
+sd, st and sr (disk, tape and CDROM respectively). Sg is more generalized
+(but lower level) than its sibling and tends to be used on SCSI devices
+that don't fit into the already serviced categories. Thus sg is used for
+scanners, cd writers and reading audio cds amongst other things.
+
+The interface and usage of the original sg driver has been documented
+by Heiko Eissfeldt in a HOWTO called SCSI-Programming-HOWTO. My copy
+of the document is version 1.5 dated 7th May 1996. It can found at
+ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO/SCSI-Programming-HOWTO .
+Amongst other things it has a lot of tables from the SCSI-2 standard
+that are very useful for programming this interface.
+
+** It is possible to write applications that perform differently
+depending on whether they are using the original or this version of
+the sg device driver. The author is not aware of any useful applications
+that have problems with version 2 (yet).
+
+
+Architecture
+============
+The SCSI generic packet device driver (sg) is a character based device.
+It is one of the four high level device driver in the SCSI sub-system;
+the others are sd (for direct-access devices - disks), st (for tapes)
+and sr (for data cdroms). The other three devices are block devices.
+
+The unifying layer of the SCSI sub-system in the so-called mid-level.
+Below that are all the drivers for the various adapters supported by
+Linux.
+
+Since sg is a character device it supports the traditional Unix
+system calls of open(), close(), read(), write() and ioctl(). Two other
+related system calls: poll() and fcntl() are added to this list and
+how they interact with the sg device driver is documented later.
+
+An SG device is accessed by write()ing SCSI commands plus any associated
+outgoing data to it; the resulting status codes and any incoming data are
+then obtained by a read() call. The device can be opened O_NONBLOCK
+(non-blocking) and poll() used to monitor its progress. The device may be
+opened O_EXCL which excludes other "sg" users from this device (but not
+"sd", "st" or "sr" users). The buffer given to the write() call is made
+up as follows:
+ - struct sg_header image (see below)
+ - scsi command (6, 10 or 12 bytes long)
+ - data to be written to the device (if any)
+
+The buffer received from the corresponding read() call contains:
+ - struct sg_header image (check status/errors + sense_buffer)
+ - data read back from device (if any)
+
+The given SCSI command has its LUN field overwritten by the LUN value of
+the associated sg device that has been open()ed.
+
+
+sg_header
+=========
+This is the name of the control structure that conveys information
+about the length of data to be read/written by the associated SCSI
+command. It also conveys error and status information from the
+read() call. An instance of this structure is the first thing that
+is placed in the data buffers of both write() and read().
+
+In its original form it looked like this:
+struct sg_header {
+ int pack_len;
+ int reply_len;
+ int pack_id;
+ int result;
+ unsigned int twelve_byte:1;
+ unsigned int other_flags:31;
+ unsigned char sense_buffer[16];
+}; /* this structure is 36 bytes long */
+
+The 'pack_len' is bizzare and ends up having the 'reply_len' put in it
+(perhaps it had a use at some stage).
+
+The 'reply_len' is the length of the data the corresponding read()
+will/should request (including the sg_header).
+
+The 'pack_id' is not acted upon by the sg device driver but is conveyed
+back to the corresponding read() so it can be used for sequencing by an
+application.
+
+The 'result' is also bizzare, turning certain types of host codes it 0 (no
+error), EBUSY or EIO. With better error reporting now available, the
+'result' is best ignored.
+
+The 'twelve_byte' field overrides the internal SCSI command length "guessing"
+algorithm for group 6 and 7 commands (ie when 1st byte >= 0xc0) and forces
+a command lenth of 12 bytes.
+The command length "guessing" algorithm is as follows:
+Group: 0 1 2 3 4 5 6 7
+Length: 6 10 10 12 12 12 10 10
+
+'other_flags' was originally documented as "not used" but some current
+applications assume it has 0 placed in it.
+
+The 'sense_buffer' is the first 16 bytes of SCSI sense buffer that is
+returned when the target returns a SCSI status code of CHECK_CONDITION
+or COMMAND_TERMINATED [or (driver_status & DRIVER_SENSE) is true]. This
+buffer should be at least 18 bytes long and arguably 32 bytes; unfortunately
+this is unlikely to happen in the 2.2.x series of kernels.
+
+The new sg_header offered in this driver is:
+#define SG_MAX_SENSE 16
+struct sg_header
+{
+ int pack_len; /* [o] reply_len (ie useless) ignored as input */
+ int reply_len; /* [i] max length of expected reply (inc. sg_header) */
+ int pack_id; /* [io] id number of packet (use ints >= 0) */
+ int result; /* [o] 0==ok, else (+ve) Unix errno code (e.g. EIO) */
+ unsigned int twelve_byte:1;
+ /* [i] Force 12 byte command length for group 6 & 7 commands */
+ unsigned int target_status:5; /* [o] scsi status from target */
+ unsigned int host_status:8; /* [o] host status (see "DID" codes) */
+ unsigned int driver_status:8; /* [o] driver status+suggestion */
+ unsigned int other_flags:10; /* unused */
+ unsigned char sense_buffer[SG_MAX_SENSE]; /* [o] when target_status is
+ CHECK_CONDITION or COMMAND_TERMINATED this is output. */
+}; /* This structure is 36 bytes long on i386 */
+
+Firstly the new header is binary compatible with the original. This is
+important for keeping existing apps working without recompilation.
+
+Only those elements (or fields) that are new or in some way different
+from the original are documented below.
+
+'pack_id' becomes input to a read() when ioctl(sg_fd, SG_SET_FORCE_PACK_ID,
+&one) is active. A 'pack_id' of -1 is interpreted as fetch the oldest
+waiting packet; any other value will cause the read() to wait (or yield
+EAGAIN) until a packet with that 'pack_id' becomes available. In all cases
+the value of 'pack_id' available after a read() is the value given to that
+variable in the prior, corresponding write().
+
+The 'target_status' field is always output and is the (masked and shifted
+1 bit right) SCSI status code from the target device. The allowable
+values are (found in <scsi/scsi.h>):
+/* N.B. 1 bit offset from usual SCSI status values */
+#define GOOD 0x00
+#define CHECK_CONDITION 0x01
+#define CONDITION_GOOD 0x02
+#define BUSY 0x04
+#define INTERMEDIATE_GOOD 0x08
+#define INTERMEDIATE_C_GOOD 0x0a
+#define RESERVATION_CONFLICT 0x0c
+#define COMMAND_TERMINATED 0x11
+#define QUEUE_FULL 0x14
+When the 'target_status' is CHECK_CONDITION or COMMAND_TERMINATED the
+'sense_buffer' is output. Note that when (driver_status & DRIVER_SENSE)
+is true then the 'sense_buffer' is also output (this seems to occur when
+the scsi ide emulation is used). When the 'sense_buffer' is output the
+SCSI Sense Key can be found at (sense_buffer[2] & 0x0f) .
+
+The 'host_status' field is always output and has the following values
+whose "defines" are not visible outside the kernel (unfortunately):
+#define DID_OK 0x00 /* NO error */
+#define DID_NO_CONNECT 0x01 /* Couldn't connect before timeout period */
+#define DID_BUS_BUSY 0x02 /* BUS stayed busy through time out period */
+#define DID_TIME_OUT 0x03 /* TIMED OUT for other reason */
+#define DID_BAD_TARGET 0x04 /* BAD target. */
+#define DID_ABORT 0x05 /* Told to abort for some other reason */
+#define DID_PARITY 0x06 /* Parity error */
+#define DID_ERROR 0x07 /* Internal error */
+#define DID_RESET 0x08 /* Reset by somebody. */
+#define DID_BAD_INTR 0x09 /* Got an interrupt we weren't expecting. */
+#define DID_PASSTHROUGH 0x0a /* Force command past mid-layer */
+#define DID_SOFT_ERROR 0x0b /* The low level driver just wish a retry */
+
+The 'driver_status' field is always output. When ('driver_status' &
+DRIVER_SENSE) is true the 'sense_buffer' is also output. The following
+values whose "defines" are not visible outside the kernel (unfortunately)
+can occur:
+#define DRIVER_OK 0x00 /* Typically no suggestion */
+#define DRIVER_BUSY 0x01
+#define DRIVER_SOFT 0x02
+#define DRIVER_MEDIA 0x03
+#define DRIVER_ERROR 0x04
+#define DRIVER_INVALID 0x05
+#define DRIVER_TIMEOUT 0x06
+#define DRIVER_HARD 0x07
+#define DRIVER_SENSE 0x08
+/* above status 'or'ed with one of the following suggestions */
+#define SUGGEST_RETRY 0x10
+#define SUGGEST_ABORT 0x20
+#define SUGGEST_REMAP 0x30
+#define SUGGEST_DIE 0x40
+#define SUGGEST_SENSE 0x80
+
+'other_flags' still remains as a 10 bit field, so code that places 0 in it
+will still be happy. It is not used.
+
+
+memory
+======
+Memory is a scarce resource in any computer. Sg needs to reserve memory
+suitable for DMA roughly equal in size to the maximum of the write and
+read data buffers for each packet. This DMA memory is obtained at the time
+of a write() and released when the corresponding read() is called (although
+if memory is tight it may be using the buffer reserved by the open() ).
+
+Linux obtaining memory a challenge for several reasons. The memory pool
+that sg uses is in common with all other device drivers and all user
+processes. In this environment the only way to 99.9% guarantee a driver
+will have memory in Linux is to build it into the kernel (ie not as a
+module) and then reserve it on initialization before user processes get
+a chance. [Of course, another driver initialized before sg could take
+all available memory ...] Another problem is the biggest contiguous
+chunk of memory that can be obtained from the kernel is 32 * PAGE_SIZE
+(which is 128KBytes on i386). As memory gets "splintered" there is a good
+chance that buffers won't be available (my machine has 64 MBytes of RAM
+and has 3 available at the moment).
+
+The original sg driver used the following technique: grab a SG_BIG_BUFF
+sized buffer at driver initialization and use it for all requests greater
+than PAGE_SIZE (4096 bytes on i386). By default SG_BIG_BUFF is set to
+32 KBytes in the origianl driver but many applications suggest that the
+user increases this number. Linux limits the biggest single buffer of
+this type to 32 * PAGE_SIZE (128KBytes on i386). Unfortunately if the
+sg driver is a module then there is a high chance a contiguous block of
+that large size will not be available at module initialization.
+
+The author has found no "silver bullet" solution but uses multiple
+techniques hoping that at least one is able provide memory at the critical
+time. Listed below are some of these techniques:
+ - use scatter gather: then instead of one large buffer needing to
+ be found, multiple smaller buffer can be used
+ - use memory above the 16MByte level: the original driver limited
+ itself to obtaining memory below the 16MByte level (on the i386)
+ due to the shortcomings of DMA on ISA adapters. Yet more and more
+ people use PCI adapters that don't have this problem. So make
+ the decision based on the capabilities of the host adpater
+ associated with the current SCSI device
+ - reserve some memory at open() for emergencies but otherwise
+ fetch and release it on a per packet basis
+ - if the kernel is short of memory then dip into the SCSI DMA
+ pool (maintained by the mid-level driver) to a limited amount
+
+
+
+System Calls
+============
+What follows are descriptions of the characteristics of the standard
+Unix operating system calls when applied to a SCSI generic device
+using this version of the device driver.
+
+open
+----
+The filename should be an 'sg' device such as
+/dev/sg[a-z]
+/dev/sg[0,1,2,...]
+or a symbolic link to one of these. [Devfs has its own sub-directory for
+sg devices.] It seems as though SCSI devices are allocated to sg minor
+numbers in the same order as they appear in 'cat /proc/scsi/scsi'.
+Sg is a "character" based Linux device driver. This means it has an
+open/close/read/write/ioctl type interface.
+
+Flags can be either O_RDONLY or O_RDWR or-ed with either
+O_EXCL waits for other opens on sg device to be closed before
+ proceeding. If O_NONBLOCK is set then yields EBUSY when
+ someone else has the sg device open. The combination of
+ O_RDONLY and O_EXCL is disallowed.
+O_NONBLOCK Sets non-blocking mode. Calls that would otherwise block
+ yield EAGAIN (eg read() ) or EBUSY (eg open() ).
+
+The original version of sg did not allow the O_RDONLY (yielding a EACCES
+error). This version allows it for accessing ioctls (e.g. doing an sg
+device scan with the SG_GET_SCSI_ID ioctl) but write()s will not be
+allowed.
+
+By default, sequencing is per file descriptor in this version of sg. This
+means, for example that 2 processes can independently manipulate the same
+sg device at the same time. This may or may not make sense depending on
+the application: 2 processes (logically) reading from the same direct access
+device (ie a disk) is ok while running 2 instances of cd writing software
+on the same device at the same time probably wouldn't be a good idea. The
+previous version of sg supported only per device sequencing and this can
+still be selected with the SG_SET_MERGE_FD,1 ioctl().
+
+The driver will attempt to reserve SG_SCATTER_SZ bytes (32KBytes in the
+current sg.h) on open() for "emergency" situations. If this is unavailable
+it will halve its request and try again. It gives up if PAGE_SIZE bytes
+(4096 bytes on i386) cannot be obtained so no memory is reserved. In this
+case open() will still return successfully. The actual amount of memory
+reserved can be found with the SG_GET_RESERVED_SIZE ioctl().
+
+Returns a file descriptor if >= 0 , otherwise -1 implies an error.
+
+Error codes (value in 'errno' after -1 returned):
+ENODEV sg not compiled into kernel or the kernel cannot find the
+ sg module (or it can't initialize itself (low memory??))
+ENXIO either scsi sub-system is currently processing some error
+ (eg doing a device reset) or the sg driver/module removed
+ or corrupted
+EBUSY O_NONBLOCK set and some user of this sg device has O_EXCL
+ set while someone is already using this device
+EINTR while waiting for an "exclusive" lock to clear, a signal
+ is received, just try again ...
+ENOMEM An attempt to get memory to store this open's context
+ failed (this was _not_ a request to reserve DMA memory)
+EACCES An attempt to use both O_RDONLY and O_EXCL
+
+
+write
+-----
+Even though sg is a character-based device driver it sends and receives
+packets to/from the associated scsi device. Write() is used to send a
+packet containing 2 mandatory parts and 1 optional part. The mandatory
+parts are:
+ - a control block (an instance of struct sg_header)
+ - a SCSI command (6, 10 or 12 bytes long)
+The optional part is:
+ - outgoing data (eg if a SCSI write command is being sent)
+These should appear as one contiguous string in the buffer given to
+write() in the above order with no pad characters.
+
+If a write() accepts this packet then at some later time the user should
+call a read() to get the result of the SCSI command. The previous sg
+driver enforced a strict write()/read()/write()/read() regime so that a
+second write() would block until first read() was finished. This sg
+driver relaxes that condition and thereby allows command queuing
+(limit is SG_MAX_QUEUE (16) outstanding packets per file descriptor).
+However, for backward compatibility, command queuing is turned off
+by default (#define SG_DEF_COMMAND_Q 0 in sg.h). This can be changed
+via the the SG_SET_COMMAND_Q ioctl() [or by recompiling after changing
+the above define to 1].
+
+In this sg driver a write() should return more or less immediately.
+
+Returns number of bytes written if > 0 , otherwise -1 implies an error.
+
+Error codes (value in 'errno' after -1 returned):
+ENXIO either scsi sub-system is currently processing some error
+ (eg doing a device reset) or the sg driver/module removed
+ or corrupted
+EACCES opened with RD_ONLY flag
+EIO incoming buffer too short. It should be at least (6 +
+ sizeof(struct sg_header))==42 bytes long
+EDOM a) command queuing off: a packet is already queued
+ b) command queuing on: too many packets queued
+ (SG_MAX_QUEUE exceeded)
+EAGAIN SCSI mid-level out of command blocks (rare), try again.
+ This is more likely to happen when queuing commands,
+ so wait a bit (eg usleep(10000) ) before trying again
+ENOMEM can't get memory for DMA. Take evasive action ...
+ (see section on memory)
+
+
+read
+----
+Read() is used to receive a packet containing 1 mandatory part and 1
+optional part. The mandatory part is:
+ - a control block (an instance of struct sg_header)
+The optional part is:
+ - incoming data (eg if a SCSI read command was sent by earlier write() )
+The buffer given to a read() and its corresponding count should be
+sufficient to accommodate this packet to avoid truncation. Truncation has
+occurred if count < sg_header::replylen .
+
+By default, read() will return the oldest packet queued up. If the
+SG_SET_FORCE_PACK_ID,1 ioctl() is active then read() will attempt to
+fetch the packet whose pack_id (given earlier to write()) matches the
+sg_header::pack_id given to this read(). If not available it will either
+wait or yield EAGAIN. As a special case, -1 in sg_header::pack_id given
+to read() will match the oldest packet.
+
+
+Returns number of bytes read if > 0 , otherwise -1 implies an error.
+Unfortunately the return value in the non-error case is simply the
+same as the count argument. It is not the actual number of bytes
+DMA-ed by the SCSI device. This driver is currently unable to provide
+such an underrun indication.
+
+Error codes (value in 'errno' after -1 returned):
+ENXIO either scsi sub-system is currently processing some error
+ (eg doing a device reset) or the sg driver/module removed
+ or corrupted
+EAGAIN either no waiting packet or requested packet is not
+ available while O_NONBLOCK flag was set
+EINTR while waiting for a packet, a signal is received, just
+ try again ...
+EIO if the 'count' given to read() is < sizeof(struct sg_header)
+ and the 'result' element in sg_header is non-zero. Not a
+ recommended error reporting technique
+
+
+close
+-----
+Preferably a close() should be done after all issued write()s have had
+their corresponding read() calls completed. Unfortunately this is not
+always possible. The semantics of close() in Unix are to return more
+or less immediately (ie not wait on any event) so the driver needs to
+arrange to an orderly cleanup of those packets that are still "in
+flight".
+
+A process that has an open file descriptor to an sg device may be aborted
+(eg by a kill signal). In this case, the kernel automatically calls close
+(which is called 'sg_release()' in the version 2 driver) to facilitate
+the cleanup mentioned above.
+
+A problem persists in version 2.1.8 if the sg driver is a module and is
+removed while packets are still "in flight". Hopefully this will be soon
+fixed.
+
+Returns 0 if successful, otherwise -1 implies an error.
+
+Error codes (value in 'errno' after -1 returned):
+ENXIO sg driver/module removed or corrupted
+
+ioctl (sg specific)
+-------------------
+Ken Thompson (or perhaps some other Unix luminary) described ioctl() as
+the "garbage bin of Unix". This driver compounds the situation by adding
+around 18 more commands. These commands either yield state information (10
+of them), change the driver's characteristics (8 of them) or allow direct
+communication with the common SCSI mid-level driver.
+
+Those commands with an appended "+" are new in version 2.
+
+Those commands with an appended "W" are only accessible from file
+descriptors opened with O_RDWR. They will yield EACCES otherwise.
+
+SG_GET_TIMEOUT:
+Ignores its 3rd argument and _returns_ the timeout value (which will be
+>= 0 ). The unit of this timeout is "jiffies" which are currently 10
+millisecond intervals on i386 (less on an alpha). Linux supplies
+a manifest constant HZ which is the number of "jiffies" in 1 second.
+
+SG_SET_TIMEOUT:
+Assumes 3rd argument points to an int containing the new timeout value
+for this file descriptor. The unit is a "jiffy". Packets that are
+already "in flight" will not be effected. The default value is set
+on open() and is SG_DEFAULT_TIMEOUT (defined in sg.h).
+
+SG_EMULATED_HOST:
+Assumes 3rd argument points to an int and outputs a flag indicating
+whether the host (adapter) is connected to a real SCSI bus or is
+emulated one (eg ide-scsi device driver). A value of 1 means emulated
+while 0 is not.
+
+SG_SET_FORCE_LOW_DMA +:
+Assumes 3rd argument points to an int containing 0 or 1. 0 (default)
+means sg decides whether to use memory above 16 Mbyte level (on i386)
+based on the host adapter being used by this SCSI device. Typically
+PCI SCSI adapters will indicate they can DMA to the whole 32 bit address
+space.
+If 1 is given then the host adapter is overridden and only memory below
+the 16MB level is used for DMA. A requirement for this should be
+extremely rare. If the "reserve" buffer allocated on open() is not in
+use then it will be de-allocated and re-allocated under the 16MB level
+(and the latter operation could fail yielding ENOMEM).
+Only the current file descriptor is effected.
+
+SG_GET_LOW_DMA +:
+Assumes 3rd argument points to an int and places 0 or 1 in it. 0
+indicates the whole 32 bit address space is being used for DMA transfers
+on this file descriptor. 1 indicates the memory below the 16MB level
+(on i386) is being used (and this may be the case because the host
+adapters setting has been overridden by SG_SET_FORCE_LOW_DMA,1 .
+
+SG_GET_SCSI_ID +:
+Assumes 3rd argument is pointing to an object of type Sg_scsi_id and
+populates it. That structure contains ints for host_no, channel,
+scsi_id, lun and scsi_type. Most of this information is available from
+other sources (eg SCSI_IOCTL_GET_IDLUN and SCSI_IOCTL_GET_BUS_NUMBER)
+but tends to be awkward to collect.
+
+SG_SET_FORCE_PACK_ID +:
+Assumes 3rd argument is pointing to an int. 0 (default) instructs read()
+to return the oldest (written) packet if multiple packets are
+waiting to be read (when command queuing is being used).
+1 instructs read() to view the sg_header::pack_id as input and return the
+oldest packet matching that pack_id or wait until it arrives (or yield
+EAGAIN if O_NONBLOCK is in force). As a special case the pack_id of -1
+given to read() in the mode will match the oldest packet.
+Only the current file descriptor is effected by this command.
+
+SG_GET_LOW_DMA +:
+Assumes 3rd argument points to an int and places the pack_id of the
+oldest (written) packet in it. If no packet is waiting to be read then
+yields -1.
+
+SG_GET_NUM_WAITING +:
+Assumes 3rd argument points to an int and places the number of packets
+waiting to be read in it.
+
+SG_GET_SG_TABLESIZE +:
+Assumes 3rd argument points to an int and places the maximum number of
+scatter gather elements supported by the host adapter. 0 indicates that
+the adapter does support scatter gather.
+
+SG_SET_RESERVED_SIZE +W:
+This is not currently implemented. It is intended for reserving either a
+large buffer or scatter gather list that will be available until the
+current file descriptor is closed. The requested amount of memory may
+not be available so SG_GET_RESERVED_SIZE should be used after this call
+to see how much was reserved. (EBUSY error possible)
+
+SG_GET_RESERVED_SIZE +:
+Assumes 3rd argument points to an int and places the size in bytes of
+the DMA buffer reserved on open() for emergencies. If this is 0 then it
+is probably not wise to attempt on operation like burning a CD on this
+file descriptor.
+
+SG_SET_MERGE_FD +W:
+Assumes 3rd argument is pointing to an int. 0 (the default) causes all
+subsequent sequencing to be per file descriptor. 1 causes all subsequent
+sequencing to be per device. If this command tries to change the current
+state and the is one or more _other_ file descriptors using this sg
+device then an EBUSY error occurs. Also if this file descriptor was not
+open()ed with the O_RDWR flag then an EACCES error occurs.
+Per device sequencing was the original semantics and allowed, for example
+different processes to "share" the device, one perhaps write()ing with
+the other one read()ing. This command is supplied if anyone needs those
+semantics. Per file descriptor sequencing, perhaps with the usage of
+the O_EXCL flag, seems more sensible.
+
+SG_GET_MERGE_FD +:
+Assumes 3rd argument points to an int and places 0 or 1 in it. 0 implies
+sequencing is per file descriptor. 1 implies sequencing is per device
+(original sg driver's semantics).
+
+SG_SET_COMMAND_Q +:
+Assumes 3rd argument is pointing to an int. 0 (current default, set by
+SG_DEF_COMMAND_Q in sg.h) disables command queuing. Attempts to write()
+a packet while one is already queued will result in a EDOM error.
+1 turns command queuing on.
+Changing the queuing state only effects write()s done after the change.
+Only the current file descriptor is effected by this command.
+
+SG_GET_COMMAND_Q +:
+Assumes 3rd argument points to an int and places 0 or 1 in it. 0 implies
+that command queuing is off on this file descriptor. 1 implies command
+queuing is on.
+
+SG_SET_DEBUG +:
+Assumes 3rd argument is pointing to an int. 0 (default) turns debugging
+off. Values > 0 cause the SCSI sense buffer to be decoded and output
+to the console/log when a SCSI device error occurs. Values > 8 cause
+the current sg device driver's state to be output to the console/log
+(this is a "one off" effect).
+If you need a _lot_ of the SCSI sub-system debug information (mainly from
+the mid-level) then try 'echo "scsi dump 0" > /proc/scsi/scsi' and lots of
+debug will appear in your console/log.
+
+ioctl (in common with sd, st + sr)
+----------------------------------
+The following ioctl()s can be called from any high-level scsi device
+driver (ie sd, st, sr + sg). Access permissions may differ a bit from
+one device to another, the access information given below is specific to
+the sg device driver.
+
+SCSI_IOCTL_GET_IDLUN:
+SCSI_IOCTL_GET_BUS_NUMBER:
+
+SCSI_IOCTL_SEND_COMMAND: W
+If open()ed O_RDONLY yields an EACCESS error. Otherwise is forwarded onto
+the SCSI mid-level driver for processing.
+Don't know much about this one but it looks pretty powerful and
+dangerous. Some comments says it is also deprecated.
+
+<any_command_not matching_above>: W
+If open()ed O_RDONLY yields an EACCESS error. Otherwise is forwarded onto
+the SCSI mid-level driver for processing.
+
+
+poll
+----
+This is a native call in Linux 2.2 but most of its capabilities are available
+through the older select() call. Given a choice poll() should probably be
+used. Typically poll() is used when a sg scsi device is open()ed O_NONBLOCK
+for polling; or alternatively with asynchronous notification using the
+fcntl() system call (below) and the SIGPOLL (aka SIGIO) signal.
+Only if something drastically is wrong (eg file handle gone stale) will
+POLLERR ever be set. POLLPRI, POLLHUP and POLLNVAL are never set.
+POLLIN is set when there is one or more packets waiting to be read.
+When POLLIN is set it implies that a read() will not block (or yield
+EAGAIN in non-blocking mode) but return a packet immediately.
+POLLOUT (aka POLLWRNORM) is set when write() is able to accept a packet
+(ie will _not_ yield an EDOM error). The setting of POLLOUT is effected
+by the SG_SET_COMMAND_Q state: if the state is on then POLLOUT will remain
+set until the number of queued packets reaches SG_MAX_QUEUE, if the
+state is off then POLLOUT is only set when no packets are queued.
+Note that a packet can be queued after write()ing but not available to be
+read(); this typically happens when a SCSI read command is issued while
+the data is being retreaved.
+Poll() is per file descriptor unless SG_SET_MERGE_FD is set in which case
+it is per device.
+
+
+fcntl
+-----
+There are several uses for this system call in association with a sg
+file descriptor. The first pseudo code shows code that is useful for
+scanning the sg devices, taking care not to be caught in a wait for
+an O_EXCL lock by another process, and when the appropriate device is
+found switching to normal blocked io. A working example of this logic
+is in the sg_scan.c utility program.
+
+open("/dev/sga", O_RDONLY | O_NONBLOCK)
+/* check device, EBUSY means some other process has O_EXCL lock on it */
+/* one the device you want is found then ... */
+flags = fcntl(sg_fd, F_GETFL)
+fcntl(sg_fd, F_SETFL, flags & (~ O_NONBLOCK))
+/* for simple apps is is easier to use normal blocked io */
+
+
+Some work has to be done in Linux to set up for asynchronous notification.
+This is a non-blocking mode of operation in which when the driver receives
+data back from a device so that a read() can be done, it sends a SIGPOLL
+(aka SIGIO) signal to the owning process. A working example of this logic
+is in the sg_poll.c test program.
+
+sigemptyset(&sig_set)
+sigaddset(&sig_set, SIGPOLL)
+sigaction(SIGPOLL, &s_action, 0)
+fcntl(sg_fd, F_SETOWN, getpid())
+flags = fcntl(sg_fd, F_GETFL);
+fcntl(sg_fd, F_SETFL, flags | O_ASYNC)
+
+
+Utility and Test Programs
+=========================
W: http://www.torque.net/sg
S: Maintained
+SCSI GENERIC
+L: linux-scsi@vger.rutgers.edu
+M: douglas.gilbert@rbcds.com
+S: Maintained
+
SCSI SUBSYSTEM
L: linux-scsi@vger.rutgers.edu
S: Unmaintained
L: linux-kernel@vger.rutgers.edu
S: Maintained
-USB HUB DRIVER
+USB HUB AND UHCI DRIVERS
P: Johannes Erdfelt
M: jerdfelt@sventech.com
L: linux-usb@peloncho.fis.ucm.es
W: http://roadrunner.swansea.linux.org.uk/v4l.shtml
S: Maintained
-WAN ROUTER AND SANGOMA WANPIPE DRIVERS (X.25, FRAME RELAY, PPP)
-P: Gene Kozin
-M: genek@compuserve.com
+WAN ROUTER & SANGOMA WANPIPE DRIVERS & API (X.25, FRAME RELAY, PPP, CISCO HDLC)
+P: Jaspreet Singh
+M: jaspreet@sangoma.com
M: dm@sangoma.com
W: http://www.sangoma.com
S: Supported
endif
ifeq ($(CONFIG_USB),y)
-DRIVERS := $(DRIVERS) drivers/uusbd/usb.a
+DRIVERS := $(DRIVERS) drivers/usb/usb.a
endif
ifeq ($(CONFIG_I2O),y)
vec = get_sysvec_byname(p+9);
continue;
}
+
+ if (strncmp(p, "cycle=", 6) == 0) {
+ est_cycle_freq = simple_strtol(p+6, NULL, 0);
+ continue;
+ }
}
/* Replace the command line, not that we've killed it with strtok. */
(char*)cpu->serial_no,
systype_name, sysvariation_name, hwrpb->sys_revision,
(char*)hwrpb->ssn,
- hwrpb->cycle_freq ? : est_cycle_freq,
- hwrpb->cycle_freq ? "" : "est.",
+ est_cycle_freq ? : hwrpb->cycle_freq,
+ est_cycle_freq ? "est." : "",
hwrpb->intr_freq / 4096,
(100 * hwrpb->intr_freq / 4096) % 100,
hwrpb->pagesize,
{
void (*irq_handler)(int, void *, struct pt_regs *);
unsigned int year, mon, day, hour, min, sec, cc1, cc2;
- unsigned long cycle_freq;
+ unsigned long cycle_freq, diff, one_percent;
/*
* The Linux interpretation of the CMOS clock register contents:
/* Read cycle counter exactly on falling edge of update flag */
cc1 = rpcc();
- /* If our cycle frequency isn't valid, go another round and give
- a guess at what it should be. */
- cycle_freq = hwrpb->cycle_freq;
- if (cycle_freq == 0) {
- printk("HWRPB cycle frequency bogus. Estimating... ");
-
+ if (!est_cycle_freq) {
+ /* Sometimes the hwrpb->cycle_freq value is bogus.
+ Go another round to check up on it and see. */
do { } while (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP));
do { } while (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP);
cc2 = rpcc();
- est_cycle_freq = cycle_freq = cc2 - cc1;
+ est_cycle_freq = cc2 - cc1;
cc1 = cc2;
+ }
- printk("%lu Hz\n", cycle_freq);
+ /* If the given value is within 1% of what we calculated,
+ accept it. Otherwise, use what we found. */
+ cycle_freq = hwrpb->cycle_freq;
+ one_percent = cycle_freq / 100;
+ diff = cycle_freq - est_cycle_freq;
+ if (diff < 0)
+ diff = -diff;
+ if (diff > one_percent) {
+ cycle_freq = est_cycle_freq;
+ printk("HWRPB cycle frequency bogus. Estimated %lu Hz\n",
+ cycle_freq);
+ }
+ else {
+ est_cycle_freq = 0;
}
/* From John Bowman <bowman@math.ualberta.ca>: allow the values
ret $31, ($28), 1 # .. e1 :
__do_clear_user:
+ ldgp $29,0($27) # we do exceptions -- we need the gp.
and $6, 7, $4 # e0 : find dest misalignment
beq $0, $zerolength # .. e1 :
addq $0, $4, $1 # e0 : bias counter
.globl __copy_user
.ent __copy_user
__copy_user:
+ ldgp $29,0($27) # we do exceptions -- we need the gp.
+ .prologue 1
and $6,7,$3
beq $0,$35
beq $3,$36
.align 3
__strlen_user:
- .prologue 0
+ ldgp $29,0($27) # we do exceptions -- we need the gp.
+ .prologue 1
EX( ldq_u t0, 0(a0) ) # load first quadword (a0 may be misaligned)
lda t1, -1(zero)
.globl __strncpy_from_user
.ent __strncpy_from_user
.frame $30, 0, $26
+ .prologue 1
.align 3
$aligned:
/*** The Function Entry Point ***/
.align 3
__strncpy_from_user:
+ ldgp $29, 0($27) # we do exceptions -- we need the gp.
mov a0, v0 # save the string start
beq a2, $zerolength
source drivers/char/Config.in
+# source drivers/usb/Config.in
+
source fs/Config.in
if [ "$CONFIG_VT" = "y" ]; then
* for buggy PCI BIOS'es :-[).
*/
+extern int skip_ioapic_setup;
+
static void __init pcibios_fixup_devices(void)
{
struct pci_dev *dev;
/*
* Recalculate IRQ numbers if we use the I/O APIC
*/
+ if(!skip_ioapic_setup)
{
int irq;
unsigned char pin;
atakeyb_rep_timer.prev = atakeyb_rep_timer.next = NULL;
add_timer( &atakeyb_rep_timer );
- handle_scancode(rep_scancode);
+ handle_scancode(rep_scancode, 1);
}
atari_enable_irq( IRQ_MFP_ACIA );
add_timer( &atakeyb_rep_timer );
}
- handle_scancode(break_flag | scancode);
+ handle_scancode(scancode, !break_flag);
break;
}
break;
{
case 0x40:
{
- unsigned char scode = (poll.data[1] >> 1) | ((poll.data[1] & 1)?0x80:0);
+ int down = (poll.data[1] & 1) == 0;
+ unsigned char scode = poll.data[1] >> 1;
#if 0
- if (scode & 0x80)
- printk("[%02x]", scode & 0x7f);
+ if (down)
+ printk("[%02x]", scode);
#endif
- handle_scancode(scode);
+ handle_scancode(scode, down);
}
break;
}
extern struct kbd_struct kbd_table[];
extern void adb_bus_init(void);
-extern void handle_scancode(unsigned char);
+extern void handle_scancode(unsigned char, int);
extern void put_queue(int);
/* keyb */
*/
switch (keycode) {
case 0x39:
- handle_scancode(keycode); /* down */
+ handle_scancode(keycode, 1); /* down */
up_flag = 0x80; /* see below ... */
mark_bh(KEYBOARD_BH);
break;
}
}
- handle_scancode(keycode + up_flag);
+ handle_scancode(keycode, !up_flag);
}
static void
SUB_DIRS := block char net misc sound
MOD_SUB_DIRS := $(SUB_DIRS)
ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sbus cdrom isdn pnp \
- macintosh video dio zorro fc4
+ macintosh video dio zorro fc4 usb
ifdef CONFIG_DIO
SUB_DIRS += dio
MOD_SUB_DIRS += macintosh
endif
+ifeq ($(CONFIG_USB),y)
+SUB_DIRS += usb
+MOD_SUB_DIRS += usb
+endif
+
# If CONFIG_SCSI is set, the core of SCSI support will be added to the kernel,
# but some of the low-level things may also be modules.
ifeq ($(CONFIG_SCSI),y)
};
#endif
-int ps2kbd_pretranslate(unsigned char scancode)
-{
- return 1;
-}
-
int ps2kbd_translate(unsigned char scancode, unsigned char *keycode_p, char *uf_p)
{
*uf_p = scancode & 0200;
static void ps2kbd_key(unsigned int keycode, unsigned int up_flag)
{
- handle_scancode(keycode + (up_flag ? 0x80 : 0));
+ handle_scancode(keycode, !up_flag);
}
static inline void ps2kbd_sendbyte(unsigned char val)
#if defined(__BIG_ENDIAN_BITFIELD)
__u8 reserved3 : 2;
- /* Drive can fake writes */
- __u8 test_write : 1;
- __u8 reserved3a : 1;
- /* Drive can write DVD-R discs */
- __u8 dvd_r_write : 1;
/* Drive can write DVD-RAM discs */
__u8 dvd_ram_write : 1;
+ /* Drive can write DVD-R discs */
+ __u8 dvd_r_write : 1;
+ __u8 reserved3a : 1;
+ /* Drive can fake writes */
+ __u8 test_write : 1;
/* Drive can write to CD-R/W (CD-E) discs (orange book, part III) */
__u8 cd_rw_write : 1; /* reserved in 1.2 */
/* Drive supports write to CD-R discs (orange book, part II) */
__u8 cd_r_write : 1; /* reserved in 1.2 */
/* Drive can write to CD-R/W (CD-E) discs (orange book, part III) */
__u8 cd_rw_write : 1; /* reserved in 1.2 */
- /* Drive can write DVD-RAM discs */
- __u8 dvd_ram_write : 1;
- /* Drive can write DVD-R discs */
- __u8 dvd_r_write : 1;
- __u8 reserved3a : 1;
/* Drive can fake writes */
__u8 test_write : 1;
+ __u8 reserved3a : 1;
+ /* Drive can write DVD-R discs */
+ __u8 dvd_r_write : 1;
+ /* Drive can write DVD-RAM discs */
+ __u8 dvd_ram_write : 1;
__u8 reserved3 : 2;
#else
#error "Please fix <asm/byteorder.h>"
#else
#define DEBUG(x)
#endif
+
+static int blksize = 2048;
+static int hsecsize = 2048;
+
\f
/* Drive hardware/firmware characteristics
Identifiers in accordance with Optics Storage documentation */
return -EIO;
}
+ hardsect_size[MAJOR_NR] = &hsecsize;
+ blksize_size[MAJOR_NR] = &blksize;
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
read_ahead[MAJOR_NR] = 4;
request_region(optcd_port, 4, "optcd");
ifndef CONFIG_SUN_KEYBOARD
ifdef CONFIG_VT
-L_OBJS += keyboard.o
+LX_OBJS += keyboard.o
endif
ifneq ($(ARCH),m68k)
L_OBJS += pc_keyb.o defkeymap.o
endif
else
ifdef CONFIG_PCI
-L_OBJS += defkeymap.o keyboard.o
+L_OBJS += defkeymap.o
+LX_OBJS += keyboard.o
endif
endif
amikeyb_rep_timer.expires = jiffies + key_repeat_rate;
amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL;
add_timer(&amikeyb_rep_timer);
- handle_scancode(rep_scancode);
+ handle_scancode(rep_scancode, 1);
restore_flags(flags);
}
if (keycode == AMIKEY_CAPS) {
/* if the key is CAPS, fake a press/release. */
- handle_scancode(AMIKEY_CAPS);
- handle_scancode(BREAK_MASK | AMIKEY_CAPS);
+ handle_scancode(AMIKEY_CAPS, 1);
+ handle_scancode(AMIKEY_CAPS, 0);
} else if (keycode < 0x78) {
/* handle repeat */
if (break_flag) {
amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL;
add_timer(&amikeyb_rep_timer);
}
- handle_scancode(scancode);
+ handle_scancode(scancode, !break_flag);
} else
switch (keycode) {
case 0x78:
}
#endif
+static void init_tea6300(struct i2c_bus *bus)
+{
+ I2CWrite(bus, I2C_TEA6300, TEA6300_VL, 0x35, 1); /* volume left 0dB */
+ I2CWrite(bus, I2C_TEA6300, TEA6300_VR, 0x35, 1); /* volume right 0dB */
+ I2CWrite(bus, I2C_TEA6300, TEA6300_BA, 0x07, 1); /* bass 0dB */
+ I2CWrite(bus, I2C_TEA6300, TEA6300_TR, 0x07, 1); /* treble 0dB */
+ I2CWrite(bus, I2C_TEA6300, TEA6300_FA, 0x0f, 1); /* fader off */
+ I2CWrite(bus, I2C_TEA6300, TEA6300_SW, 0x01, 1); /* mute off input A */
+}
+
static void init_tda8425(struct i2c_bus *bus)
{
I2CWrite(bus, I2C_TDA8425, TDA8425_VL, 0xFC, 1); /* volume left 0dB */
break;
}
+ if (I2CRead(&(btv->i2c), I2C_TEA6300) >=0)
+ {
+ printk(KERN_INFO "bttv%d: fader chip: TEA6300\n",btv->nr);
+ btv->audio_chip = TEA6300;
+ init_tea6300(&(btv->i2c));
+ } else
+ printk(KERN_INFO "bttv%d: NO fader chip: TEA6300\n",btv->nr);
+
printk(KERN_INFO "bttv%d: model: ",btv->nr);
sprintf(btv->video_dev.name,"BT%d",btv->id);
int type; /* card type */
int audio; /* audio mode */
int audio_chip;
+ int fader_chip;
int radio;
u32 *risc_jmp;
#define TDA9850 0x01
#define TDA8425 0x02
#define TDA9840 0x03
+#define TEA6300 0x04
#define I2C_TSA5522 0xc2
#define I2C_TDA9840 0x84
#define I2C_HAUPEE 0xa0
#define I2C_STBEE 0xae
#define I2C_VHX 0xc0
+#define I2C_TEA6300 0x80
#define TDA9840_SW 0x00
#define TDA9840_LVADJ 0x02
#define TDA8425_BA 0x02
#define TDA8425_TR 0x03
#define TDA8425_S1 0x08
-
+
+#define TEA6300_VL 0x00 /* volume control left */
+#define TEA6300_VR 0x01 /* volume control right */
+#define TEA6300_BA 0x02 /* bass control */
+#define TEA6300_TR 0x03 /* treble control */
+#define TEA6300_FA 0x04 /* fader control */
+#define TEA6300_SW 0x05 /* mute and source switch */
#endif
}
else if((scancode & (~BREAK_FLAG)) == DNKEY_CAPS) {
/* printk("handle_scancode: %02x\n",DNKEY_CAPS); */
- handle_scancode(DNKEY_CAPS);
+ handle_scancode(DNKEY_CAPS, 1);
/* printk("handle_scancode: %02x\n",BREAK_FLAG | DNKEY_CAPS); */
- handle_scancode(BREAK_FLAG | DNKEY_CAPS);
+ handle_scancode(DNKEY_CAPS, 0);
}
else if( (scancode == DNKEY_REPEAT) && (prev_scancode < 0x7e) &&
!(prev_scancode==DNKEY_CTRL || prev_scancode==DNKEY_LSHIFT ||
prev_scancode==DNKEY_LALT || prev_scancode==DNKEY_RALT)) {
if(jiffies-lastkeypress > DNKEY_REPEAT_DELAY) {
/* printk("handle_scancode: %02x\n",prev_scancode); */
- handle_scancode(prev_scancode);
+ handle_scancode(prev_scancode, 1);
}
lastscancode=prev_scancode;
}
else {
/* printk("handle_scancode: %02x\n",scancode); */
- handle_scancode(scancode);
+ handle_scancode(scancode & ~BREAK_FLAG, !(scancode & BREAK_FLAG));
lastkeypress=jiffies;
}
}
*/
#include <linux/config.h>
+#include <linux/module.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#define KBD_DEFLOCK 0
#endif
+EXPORT_SYMBOL(handle_scancode);
+
extern void ctrl_alt_del(void);
struct wait_queue * keypress_wait = NULL;
return kbd_getkeycode(scancode);
}
-void handle_scancode(unsigned char scancode)
+void handle_scancode(unsigned char scancode, int down)
{
unsigned char keycode;
- char up_flag; /* 0 or 0200 */
+ char up_flag = down ? 0 : 0200;
char raw_mode;
do_poke_blanked_console = 1;
mark_bh(CONSOLE_BH);
- add_keyboard_randomness(scancode);
+ add_keyboard_randomness(scancode | up_flag);
tty = ttytab? ttytab[fg_console]: NULL;
if (tty && (!tty->driver_data)) {
}
kbd = kbd_table + fg_console;
if ((raw_mode = (kbd->kbdmode == VC_RAW))) {
- put_queue(scancode);
+ put_queue(scancode | up_flag);
/* we do not return yet, because we want to maintain
the key_down array, so that we have the correct
values when finishing RAW mode or when changing VT's */
- }
+ }
- if (!kbd_pretranslate(scancode, raw_mode))
- return;
- /*
+ /*
* Convert scancode to keycode
- */
- up_flag = (scancode & 0200);
- scancode &= 0x7f;
-
+ */
if (!kbd_translate(scancode, &keycode, raw_mode))
return;
if (up_flag) {
rep = 0;
- if(!test_and_clear_bit(keycode, key_down))
+ if(!test_and_clear_bit(keycode, key_down))
up_flag = kbd_unexpected_up(keycode);
} else
- rep = test_and_set_bit(keycode, key_down);
+ rep = test_and_set_bit(keycode, key_down);
#ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */
if (keycode == SYSRQ_KEY) {
if (kbd->kbdmode == VC_MEDIUMRAW) {
/* soon keycodes will require more than one byte */
- put_queue(keycode + up_flag);
+ put_queue(keycode + up_flag);
raw_mode = 1; /* Most key classes will be ignored */
- }
+ }
- /*
+ /*
* Small change in philosophy: earlier we defined repetition by
* rep = keycode == prev_keycode;
* prev_keycode = keycode;
*/
/*
- * Repeat a key only if the input buffers are empty or the
- * characters get echoed locally. This makes key repeat usable
- * with slow applications and under heavy loads.
+ * Repeat a key only if the input buffers are empty or the
+ * characters get echoed locally. This makes key repeat usable
+ * with slow applications and under heavy loads.
*/
if (!rep ||
(vc_kbd_mode(kbd,VC_REPEAT) && tty &&
#if defined(CONFIG_PPC) || defined(CONFIG_MAC)
extern void adbdev_init(void);
#endif
+#ifdef CONFIG_USB_UHCI
+int uhci_init(void);
+#endif
+#ifdef CONFIG_USB_OHCI
+int ohci_init(void);
+#endif
static ssize_t do_write_mem(struct file * file, void *p, unsigned long realp,
const char * buf, size_t count, loff_t *ppos)
if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))
printk("unable to get major %d for memory devs\n", MEM_MAJOR);
rand_initialize();
+#ifdef CONFIG_USB_UHCI
+ uhci_init();
+#endif
+#ifdef CONFIG_USB_OHCI
+ ohci_init();
+#endif
#if defined (CONFIG_FB)
fbmem_init();
#endif
}
}
- if (down_interruptible(&tty->atomic_read))
- return -ERESTARTSYS;
+ if (file->f_flags & O_NONBLOCK) {
+ if (down_trylock(&tty->atomic_read))
+ return -EAGAIN;
+ }
+ else {
+ if (down_interruptible(&tty->atomic_read))
+ return -ERESTARTSYS;
+ }
add_wait_queue(&tty->read_wait, &wait);
set_bit(TTY_DONT_FLIP, &tty->flags);
0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */
};
-static unsigned int prev_scancode = 0; /* remember E0, E1 */
-
int pckbd_setkeycode(unsigned int scancode, unsigned int keycode)
{
if (scancode < SC_LIM || scancode > 255 || keycode > 127)
scancode);
#endif
}
- if (scancode == 0) {
-#ifdef KBD_REPORT_ERR
- printk(KERN_INFO "Keyboard buffer overflow\n");
-#endif
- prev_scancode = 0;
- return 0;
- }
return 1;
}
-int pckbd_pretranslate(unsigned char scancode, char raw_mode)
+int pckbd_translate(unsigned char scancode, unsigned char *keycode,
+ char raw_mode)
{
- if (scancode == 0xff) {
- /* in scancode mode 1, my ESC key generates 0xff */
- /* the calculator keys on a FOCUS 9000 generate 0xff */
-#ifndef KBD_IS_FOCUS_9000
-#ifdef KBD_REPORT_ERR
- if (!raw_mode)
- printk(KERN_DEBUG "Keyboard error\n");
-#endif
-#endif
- prev_scancode = 0;
- return 0;
- }
+ static int prev_scancode = 0;
+ /* special prefix scancodes.. */
if (scancode == 0xe0 || scancode == 0xe1) {
prev_scancode = scancode;
return 0;
- }
- return 1;
-}
+ }
+
+ /* 0xFF is sent by a few keyboards, ignore it. 0x00 is error */
+ if (scancode == 0x00 || scancode == 0xff) {
+ prev_scancode = 0;
+ return 0;
+ }
+
+ scancode &= 0x7f;
-int pckbd_translate(unsigned char scancode, unsigned char *keycode,
- char raw_mode)
-{
if (prev_scancode) {
/*
* usually it will be 0xe0, but a Pause key generates
handle_mouse_event(scancode);
} else {
if (do_acknowledge(scancode))
- handle_scancode(scancode);
+ handle_scancode(scancode, !(scancode & 0x80));
mark_bh(KEYBOARD_BH);
}
* (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz
*
* Fitted to new interface by Alan Cox <alan.cox@linux.org>
+ * Made working and cleaned up functions <mikael.hedin@irf.se>
*
* Notes on the hardware
*
outb(0x08, port);
}
-static inline int fmi_setfreq(struct fmi_device *dev, unsigned long freq)
+static inline int fmi_setfreq(struct fmi_device *dev)
{
int myport = dev->port;
+ unsigned long freq = dev->curfreq;
int i;
outbits(16, RSF16_ENCODE(freq), myport);
v.flags=fmi->flags;
v.mode=VIDEO_MODE_AUTO;
v.signal = fmi_getsigstr(fmi);
- strcpy(v.name, "FM");
if(copy_to_user(arg,&v, sizeof(v)))
return -EFAULT;
return 0;
tmp *= 1000;
if ( tmp<RSF16_MINFREQ || tmp>RSF16_MAXFREQ )
return -EINVAL;
- fmi->curfreq = tmp;
- fmi_setfreq(fmi, fmi->curfreq);
+ /*rounding in steps of 800 to match th freq
+ that will be used */
+ fmi->curfreq = (tmp/800)*800;
+ fmi_setfreq(fmi);
return 0;
}
case VIDIOCGAUDIO:
v.treble=0;
v.flags=( (!fmi->curvol)*VIDEO_AUDIO_MUTE | VIDEO_AUDIO_MUTABLE);
strcpy(v.name, "Radio");
- v.mode=VIDEO_SOUND_MONO;
+ v.mode=VIDEO_SOUND_STEREO;
v.balance=0;
v.step=0; /* No volume, just (un)mute */
if(copy_to_user(arg,&v, sizeof(v)))
v.flags = VIDEO_TUNER_LOW;
v.mode = VIDEO_MODE_AUTO;
v.signal = 0xFFFF; /* We can't get the signal strength */
- strcpy(v.tuner, "FM");
+ strcpy(v.name, "FM");
if (copy_to_user(arg, &v, sizeof(v)))
return -EFAULT;
return 0;
static int softdog_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- int i;
static struct watchdog_info ident=
{
0,
extern struct kbd_struct kbd_table[];
extern struct wait_queue * keypress_wait;
-extern void handle_scancode(unsigned char);
+extern void handle_scancode(unsigned char, int);
static struct adb_ids keyboard_ids;
static struct adb_ids mouse_ids;
return -EINVAL;
}
-int mackbd_pretranslate(unsigned char scancode, char raw_mode)
-{
- return 1;
-}
-
int mackbd_translate(unsigned char keycode, unsigned char *keycodep,
char raw_mode)
{
switch (keycode) {
/*case 0xb9:*/
case 0x39:
- handle_scancode(0x39);
- handle_scancode(0xb9);
+ handle_scancode(0x39, 1);
+ handle_scancode(0x39, 0);
mark_bh(KEYBOARD_BH);
return;
case 0x47:
}
}
- handle_scancode(keycode + up_flag);
+ handle_scancode(keycode, !up_flag);
}
static void
search the MCA slots until it finds a 3c523 with the specified
parameters.
- This driver should support multiple ethernet cards, but I can't test
- that. If someone would I'd greatly appreciate it.
+ This driver does support multiple ethernet cards when used as a module
+ (up to MAX_3C523_CARDS, the default being 4)
This has been tested with both BNC and TP versions, internal and
external transceivers. Haven't tested with the 64K version (that I
update to 1.3.59, incorporated multicast diffs from ni52.c
Feb 15th, 1996
added shared irq support
-
+ Apr 1999
+ added support for multiple cards when used as a module
+ added option to disable multicast as is causes problems
+ Ganesh Sittampalam <ganesh.sittampalam@magdalen.oxford.ac.uk>
+ Stuart Adamson <stuart.adamson@compsoc.net>
+
$Header: /fsys2/home/chrisb/linux-1.3.59-MCA/drivers/net/RCS/3c523.c,v 1.1 1996/02/05 01:53:46 chrisb Exp chrisb $
*/
/*************************************************************************/
#define DEBUG /* debug on */
#define SYSBUSVAL 0 /* 1 = 8 Bit, 0 = 16 bit - 3c523 only does 16 bit */
+#undef ELMC_MULTICAST /* Disable multicast support as it is somewhat seriously broken at the moment */
#define make32(ptr16) (p->memtop + (short) (ptr16) )
#define make24(ptr32) ((char *) (ptr32) - p->base)
static int elmc_close(struct device *dev);
static int elmc_send_packet(struct sk_buff *, struct device *);
static struct net_device_stats *elmc_get_stats(struct device *dev);
+#ifdef ELMC_MULTICAST
static void set_multicast_list(struct device *dev);
+#endif
/* helper-functions */
static int init586(struct device *dev);
while (slot != -1) {
status = mca_read_stored_pos(slot, 2);
+ dev->irq=irq_table[(status & ELMC_STATUS_IRQ_SELECT) >> 6];
+ dev->base_addr=csr_table[(status & ELMC_STATUS_CSR_SELECT) >> 1];
+
/*
If we're trying to match a specified irq or IO address,
we'll reject a match unless it's what we're looking for.
+ Also reject it if the card is already in use.
*/
- if (base_addr || irq) {
- /* we're looking for a card at a particular place */
- if (irq && irq != irq_table[(status & ELMC_STATUS_IRQ_SELECT) >> 6]) {
- slot = mca_find_adapter(ELMC_MCA_ID, slot + 1);
- continue;
- }
- if (base_addr && base_addr != csr_table[(status & ELMC_STATUS_CSR_SELECT) >> 1]) {
- slot = mca_find_adapter(ELMC_MCA_ID, slot + 1);
- continue;
- }
+ if((irq && irq != dev->irq) || (base_addr && base_addr != dev->base_addr)
+ || check_region(dev->base_addr,ELMC_IO_EXTENT)) {
+ slot = mca_find_adapter(ELMC_MCA_ID, slot + 1);
+ continue;
}
/* found what we're looking for... */
break;
/* revision is stored in the first 4 bits of the revision register */
revision = inb(dev->base_addr + ELMC_REVISION) & 0xf;
- /* figure out our irq */
- dev->irq = irq_table[(status & ELMC_STATUS_IRQ_SELECT) >> 6];
-
/* according to docs, we read the interrupt and write it back to
the IRQ select register, since the POST might not configure the IRQ
properly. */
break;
}
- /* Our IO address? */
- dev->base_addr = csr_table[(status & ELMC_STATUS_CSR_SELECT) >> 1];
-
request_region(dev->base_addr, ELMC_IO_EXTENT, "3c523");
dev->priv = (void *) kmalloc(sizeof(struct priv), GFP_KERNEL);
dev->stop = &elmc_close;
dev->get_stats = &elmc_get_stats;
dev->hard_start_xmit = &elmc_send_packet;
+#ifdef ELMC_MULTICAST
dev->set_multicast_list = &set_multicast_list;
+#else
+ dev->set_multicast_list = NULL;
+#endif
ether_setup(dev);
That gets done in elmc_open(). I'm not sure that's such a good idea,
but it works, so I'll go with it. */
+#ifndef ELMC_MULTICAST
+ dev->flags&=~IFF_MULTICAST; /* Multicast doesn't work */
+#endif
+
return 0;
}
* Set MC list ..
*/
+#ifdef ELMC_MULTICAST
static void set_multicast_list(struct device *dev)
{
if (!dev->start) {
startrecv586(dev);
dev->start = 1;
}
+#endif
/*************************************************************************/
#ifdef MODULE
-static char devicename[9] = {0,};
+/* Increase if needed ;) */
+#define MAX_3C523_CARDS 4
+/* I'm not sure where this magic 9 comes from */
+#define NAMELEN 9
-static struct device dev_elmc =
-{
- devicename /*"3c523" */ , 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, elmc_probe
+static char devicenames[NAMELEN * MAX_3C523_CARDS] = {0,};
+
+static struct device dev_elmc[MAX_3C523_CARDS] =
+{
+ {
+ NULL /*"3c523" */ , 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL
+ },
};
-static int irq = 0;
-static int io = 0;
-MODULE_PARM(irq, "i");
-MODULE_PARM(io, "i");
+static int irq[MAX_3C523_CARDS] = {0,};
+static int io[MAX_3C523_CARDS] = {0,};
+MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_3C523_CARDS) "i");
+MODULE_PARM(io, "1-" __MODULE_STRING(MAX_3C523_CARDS) "i");
int init_module(void)
{
- struct device *dev = &dev_elmc;
-
- dev->base_addr = io;
- dev->irq = irq;
- if (register_netdev(dev) != 0) {
- return -EIO;
+ int this_dev,found = 0;
+
+ /* Loop until we either can't find any more cards, or we have MAX_3C523_CARDS */
+ for(this_dev=0; this_dev<MAX_3C523_CARDS; this_dev++)
+ {
+ struct device *dev = &dev_elmc[this_dev];
+ dev->name=devicenames+(NAMELEN*this_dev);
+ dev->irq=irq[this_dev];
+ dev->base_addr=io[this_dev];
+ dev->init=elmc_probe;
+ if(register_netdev(dev)!=0) {
+ if(io[this_dev]==0) break;
+ printk(KERN_WARNING "3c523.c: No 3c523 card found at io=%#x\n",io[this_dev]);
+ } else found++;
}
- return 0;
+
+ if(found==0) {
+ if(io[0]==0) printk(KERN_NOTICE "3c523.c: No 3c523 cards found\n");
+ return -ENXIO;
+ } else return 0;
}
void cleanup_module(void)
{
- struct device *dev = &dev_elmc;
-
- /* shutdown interrupts on the card */
- elmc_id_reset586();
- if (dev->irq != 0) {
- /* this should be done by close, but if we failed to
- initialize properly something may have gotten hosed. */
- free_irq(dev->irq, dev);
- dev->irq = 0;
- }
- if (dev->base_addr != 0) {
- release_region(dev->base_addr, ELMC_IO_EXTENT);
- dev->base_addr = 0;
- }
- irq = 0;
- io = 0;
- unregister_netdev(dev);
+ int this_dev;
+ for(this_dev=0; this_dev<MAX_3C523_CARDS; this_dev++) {
+
+ struct device *dev = &dev_elmc[this_dev];
+ if(dev->priv) {
+ /* shutdown interrupts on the card */
+ elmc_id_reset586();
+ if (dev->irq != 0) {
+ /* this should be done by close, but if we failed to
+ initialize properly something may have gotten hosed. */
+ free_irq(dev->irq, dev);
+ dev->irq = 0;
+ }
+ if (dev->base_addr != 0) {
+ release_region(dev->base_addr, ELMC_IO_EXTENT);
+ dev->base_addr = 0;
+ }
+ irq[this_dev] = 0;
+ io[this_dev] = 0;
+ unregister_netdev(dev);
- mca_set_adapter_procfn(((struct priv *) (dev->priv))->slot,
+ mca_set_adapter_procfn(((struct priv *) (dev->priv))->slot,
NULL, NULL);
- kfree_s(dev->priv, sizeof(struct priv));
- dev->priv = NULL;
+ kfree_s(dev->priv, sizeof(struct priv));
+ dev->priv = NULL;
+ }
+ }
}
#endif /* MODULE */
* Changes by Joel Sloan (jjs@c-me.com) :
* + disable verbose debug messages by default - to enable verbose
* debugging, edit the IBMTR_DEBUG_MESSAGES define below
+ *
+ * Changes by Mike Phillips <phillim@amtrak.com> :
+ * + Added extra #ifdef's to work with new PCMCIA Token Ring Code.
+ * The PCMCIA code now just sets up the card so it can be recognized
+ * by ibmtr_probe. Also checks allocated memory vs. on-board memory
+ * for correct figure to use.
*
* Changes by Tim Hockin (thockin@isunix.it.ilstu.edu) :
* + added spinlocks for SMP sanity (10 March 1999)
#undef NO_AUTODETECT
#undef ENABLE_PAGING
+
#define FALSE 0
#define TRUE (!FALSE)
int ibmtr_probe(struct device *dev);
static int ibmtr_probe1(struct device *dev, int ioaddr);
static unsigned char get_sram_size(struct tok_info *adapt_info);
+#ifdef PCMCIA
+extern unsigned char pcmcia_reality_check(unsigned char gss);
+#endif
static int tok_init_card(struct device *dev);
void tok_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static int trdev_init(struct device *dev);
if (ibmtr_probe1(dev, base_addr))
{
#ifndef MODULE
+#ifndef PCMCIA
tr_freedev(dev);
+#endif
#endif
return -ENODEV;
} else
continue;
if (ibmtr_probe1(dev, ioaddr)) {
#ifndef MODULE
+#ifndef PCMCIA
tr_freedev(dev);
+#endif
#endif
} else
return 0;
unsigned long timeout;
#ifndef MODULE
+#ifndef PCMCIA
dev = init_trdev(dev,0);
+#endif
#endif
/* Query the adapter PIO base port which will return
*/
segment = inb(PIOaddr);
-
+
/*
* Out of range values so we'll assume non-existent IO device
*/
}
/* Now, allocate some of the pl0 buffers for this driver.. */
+
+ /* If called from PCMCIA, ti is already set up, so no need to
+ waste the memory, just use the existing structure */
+
+#ifndef PCMCIA
ti = (struct tok_info *)kmalloc(sizeof(struct tok_info), GFP_KERNEL);
if (ti == NULL)
return -ENOMEM;
memset(ti, 0, sizeof(struct tok_info));
-
+#else
+ ti = dev->priv ;
+#endif
ti->mmio= t_mmio;
ti->readlog_pending = 0;
should fit with out future hope of multiple
adapter support as well /dwm */
+ /* if PCMCIA, then the card is recognized as TR_ISAPNP
+ * and there is no need to set up the interrupt, it is already done. */
+
+#ifndef PCMCIA
switch (cardpresent)
{
case TR_ISA:
irq=10;
if (intr==3)
irq=11;
-
timeout = jiffies + TR_SPIN_INTERVAL;
while(!readb(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN))
if (time_after(jiffies, timeout)) {
kfree_s(ti, sizeof(struct tok_info));
return -ENODEV;
}
+
ti->sram=((__u32)readb(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN)<<12);
ti->global_int_enable=PIOaddr+ADAPTINTREL;
ti->adapter_int_enable=PIOaddr+ADAPTINTREL;
break;
}
+#endif
if (ibmtr_debug_trace & TRC_INIT) { /* just report int */
DPRINTK("irq=%d",irq);
ti->token_release = readb(ti->mmio + AIPEARLYTOKEN);
/* How much shared RAM is on adapter ? */
+#ifdef PCMCIA
+ ti->avail_shared_ram = pcmcia_reality_check(get_sram_size(ti));
+#else
ti->avail_shared_ram = get_sram_size(ti);
-
+#endif
/* We need to set or do a bunch of work here based on previous results.. */
/* Support paging? What sizes?: F=no, E=16k, D=32k, C=16 & 32k */
ti->shared_ram_paging = readb(ti->mmio + AIPSHRAMPAGE);
}
#endif
}
-
/* finish figuring the shared RAM address */
if (cardpresent==TR_ISA) {
static __u32 ram_bndry_mask[]={0xffffe000, 0xffffc000, 0xffff8000, 0xffff0000};
DPRINTK("Using %dK shared RAM\n",ti->mapped_ram_size/2);
#endif
+ /* The PCMCIA has already got the interrupt line and the io port,
+ so no chance of anybody else getting it - MLP */
+
+#ifndef PCMCIA
if (request_irq (dev->irq = irq, &tok_interrupt,0,"ibmtr", dev) != 0) {
DPRINTK("Could not grab irq %d. Halting Token Ring driver.\n",irq);
kfree_s(ti, sizeof(struct tok_info));
return -ENODEV;
}
+
+ /*?? Now, allocate some of the PIO PORTs for this driver.. */
+ request_region(PIOaddr,IBMTR_IO_EXTENT,"ibmtr"); /* record PIOaddr range as busy */
+#endif
- /*?? Now, allocate some of the PIO PORTs for this driver.. */
- request_region(PIOaddr,IBMTR_IO_EXTENT,"ibmtr"); /* record PIOaddr range
- as busy */
#if !TR_NEWFORMAT
DPRINTK("%s",version); /* As we have passed card identification,
let the world know we're here! */
unsigned char avail_sram_code;
static unsigned char size_code[]={ 0,16,32,64,127,128 };
-
/* Adapter gives
'F' -- use RRR bits 3,2
'E' -- 8kb 'D' -- 16kb
dev->change_mtu = ibmtr_change_mtu;
#ifndef MODULE
+#ifndef PCMCIA
tr_setup(dev);
+#endif
#endif
return 0;
}
DPRINTK("PCMCIA card removed.\n");
spin_unlock(&(ti->lock));
dev->interrupt = 0;
- return;
+ return;
}
/* Check ISRP EVEN too. */
__u32 encoded_addr;
__u32 hw_encoded_addr;
struct tok_info *ti;
-
ti=(struct tok_info *) dev->priv;
ti->do_tok_int=NOT_FIRST;
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Wed Oct 21 20:02:35 1998
- * Modified at: Tue Feb 9 15:38:16 1999
+ * Modified at: Mon Apr 12 11:56:35 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli, All Rights Reserved.
/* Remove support for 38400 if this is not a 220L+ dongle */
if ( idev->io.dongle_id == ACTISYS_DONGLE)
qos->baud_rate.bits &= ~IR_38400;
-
- qos->min_turn_time.bits &= 0xfe; /* All except 0 ms */
+
+ qos->min_turn_time.bits &= 0x40; /* Needs 0.01 ms */
}
#ifdef MODULE
* Status: Experimental.
* Author: Thomas Davis, <ratbert@radiks.net>
* Created at: Sat Feb 21 18:54:38 1998
- * Modified at: Tue Feb 9 15:36:47 1999
+ * Modified at: Mon Apr 12 11:55:30 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
* Sources: esi.c
*
esi_qos_init,
};
-__initfunc(void esi_init(void))
+__initfunc(int esi_init(void))
{
- irtty_register_dongle( &dongle);
+ return irtty_register_dongle(&dongle);
}
void esi_cleanup(void)
}
/* Change speed of serial driver */
tty->termios->c_cflag = cflag;
- tty->driver.set_termios( tty, &old_termios);
+ tty->driver.set_termios(tty, &old_termios);
irtty_set_dtr_rts(tty, dtr, rts);
}
static void esi_qos_init( struct irda_device *idev, struct qos_info *qos)
{
qos->baud_rate.bits &= IR_9600|IR_19200|IR_115200;
+ qos->min_turn_time.bits &= 0x01; /* Needs at least 10 ms */
}
#ifdef MODULE
*/
int init_module(void)
{
- esi_init();
- return(0);
+ return esi_init();
}
/*
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sat Feb 6 21:02:33 1999
- * Modified at: Tue Feb 9 15:36:36 1999
+ * Modified at: Sat Apr 10 19:53:12 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
{
struct irtty_cb *self;
struct tty_struct *tty;
- __u8 control = GIRBIL_TXEN | GIRBIL_RXEN /* | GIRBIL_ECAN */;
+ __u8 control = GIRBIL_TXEN | GIRBIL_RXEN;
ASSERT(idev != NULL, return;);
ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
/*********************************************************************
*
* Filename: irport.c
- * Version: 0.8
+ * Version: 0.9
* Description: Serial driver for IrDA.
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Modified at: Sat May 23 23:15:20 1998
* Modified by: Dag Brattli <dagb@cs.uit.no>
* Sources: serial.c by Linus Torvalds
- * serial_serial.c by Aage Kvalnes <aage@cs.uit.no>
*
* Copyright (c) 1997,1998 Dag Brattli <dagb@cs.uit.no>
* All Rights Reserved.
*
* NOTICE:
*
- * This driver is ment to be a small serial driver to be used for
- * IR-chipsets that has a UART (16550) compatibility mode. If your
- * chipset is is UART only, you should probably use IrTTY instead since
- * the Linux serial driver is probably more robust and optimized.
+ * This driver is ment to be a small half duplex serial driver to be
+ * used for IR-chipsets that has a UART (16550) compatibility mode. If
+ * your chipset is is UART only, you should probably use IrTTY instead
+ * since the Linux serial driver is probably more robust and optimized.
*
* The functions in this file may be used by FIR drivers, but this
* driver knows nothing about FIR drivers so don't ever insert such
#include <net/irda/irport.h>
#define IO_EXTENT 8
-#define CONFIG_HALF_DUPLEX
/* static unsigned int io[] = { 0x3e8, ~0, ~0, ~0 }; */
/* static unsigned int irq[] = { 11, 0, 0, 0 }; */
* Start IO port
*
*/
-int irport_open( int iobase)
+int irport_open(int iobase)
{
DEBUG(4, __FUNCTION__ "(), iobase=%#x\n", iobase);
* Stop IO port
*
*/
-void irport_close( int iobase)
+void irport_close(int iobase)
{
DEBUG(4, __FUNCTION__ "()\n");
outb(0, iobase+UART_MCR);
/* Turn off interrupts */
- outb(0, iobase+UART_IER);
+ outb(0, iobase+UART_IER);
}
/*
* more packets to send, we send them here.
*
*/
-static void irport_write_wakeup( struct irda_device *idev)
+static void irport_write_wakeup(struct irda_device *idev)
{
- int actual = 0, count;
+ int actual = 0;
int iobase;
ASSERT(idev != NULL, return;);
ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
/* Finished with frame? */
- if (idev->tx_buff.offset == idev->tx_buff.len) {
+ if (idev->tx_buff.len > 0) {
+ /* Write data left in transmit buffer */
+ actual = irport_write(idev->io.iobase2, idev->io.fifo_size,
+ idev->tx_buff.data, idev->tx_buff.len);
+ idev->tx_buff.data += actual;
+ idev->tx_buff.len -= actual;
+ } else {
iobase = idev->io.iobase2;
/*
* Now serial buffer is almost free & we can start
/* Schedule network layer, so we can get some more frames */
mark_bh(NET_BH);
-#ifdef CONFIG_HALF_DUPLEX
+
outb(UART_FCR_ENABLE_FIFO |
UART_FCR_TRIGGER_14 |
UART_FCR_CLEAR_RCVR, iobase+UART_FCR); /* Enable FIFO's */
/* Turn on receive interrupts */
outb(UART_IER_RLSI|UART_IER_RDI, iobase+UART_IER);
-#endif
- return;
}
-
- /* Write data left in transmit buffer */
- count = idev->tx_buff.len - idev->tx_buff.offset;
- actual = irport_write(idev->io.iobase2, idev->io.fifo_size,
- idev->tx_buff.head, count);
- idev->tx_buff.offset += actual;
- idev->tx_buff.head += actual;
}
/*
if (irda_lock((void *) &dev->tbusy) == FALSE)
return -EBUSY;
- /*
- * Transfer skb to tx_buff while wrapping, stuffing and making CRC
- */
+ /* Init tx buffer */
+ idev->tx_buff.data = idev->tx_buff.head;
+
+ /* Copy skb to tx_buff while wrapping, stuffing and making CRC */
idev->tx_buff.len = async_wrap_skb(skb, idev->tx_buff.data,
idev->tx_buff.truesize);
-/* actual = irport_write(idev->io.iobase2, idev->io.fifo_size, */
-/* idev->tx_buff.data, idev->tx_buff.len); */
-
- idev->tx_buff.offset = actual;
- idev->tx_buff.head = idev->tx_buff.data + actual;
+ idev->tx_buff.data += actual;
+ idev->tx_buff.len -= actual;
-#ifdef CONFIG_HALF_DUPLEX
/* Turn on transmit finished interrupt. Will fire immediately! */
outb(UART_IER_THRI, iobase+UART_IER);
-#endif
+
dev_kfree_skb(skb);
return 0;
if (!idev)
return;
- DEBUG( 4, __FUNCTION__ "()\n");
+ DEBUG(4, __FUNCTION__ "()\n");
iobase = idev->io.iobase2;
- if (idev->rx_buff.len == 0)
- idev->rx_buff.head = idev->rx_buff.data;
-
/*
* Receive all characters in Rx FIFO, unwrap and unstuff them.
* async_unwrap_char will deliver all found frames
*/
do {
- async_unwrap_char(idev, inb( iobase+UART_RX));
+ async_unwrap_char(idev, inb(iobase+UART_RX));
/* Make sure we don't stay here to long */
if (boguscount++ > 32) {
DEBUG(0,__FUNCTION__ "(), breaking!\n");
break;
}
-
} while (inb(iobase+UART_LSR) & UART_LSR_DR);
}
/*
* Function irport_interrupt (irq, dev_id, regs)
*
- *
+ * Interrupt handler
*/
void irport_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct irda_device *idev = (struct irda_device *) dev_id;
-
int iobase;
int iir, lsr;
int boguscount = 0;
- DEBUG(5, __FUNCTION__ "(), irq %d\n", irq);
-
if (!idev) {
printk(KERN_WARNING __FUNCTION__
"() irq %d for unknown device.\n", irq);
iir = inb(iobase + UART_IIR) & UART_IIR_ID;
while (iir) {
- DEBUG(4,__FUNCTION__ "(), iir=%#x\n", iir);
-
/* Clear interrupt */
lsr = inb(iobase+UART_LSR);
if ((iir & UART_IIR_THRI) && (lsr & UART_LSR_THRE)) {
/* Transmitter ready for data */
irport_write_wakeup(idev);
- }
-#ifdef CONFIG_HALF_DUPLEX
- else
-#endif
- if ((iir & UART_IIR_RDI) && (lsr & UART_LSR_DR)) {
+ } else if ((iir & UART_IIR_RDI) && (lsr & UART_LSR_DR)) {
/* Receive interrupt */
irport_receive(idev);
}
idev->netdev.interrupt = 0;
}
-
#ifdef MODULE
/*
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Tue Dec 9 21:18:38 1997
- * Modified at: Tue Apr 6 21:35:25 1999
+ * Modified at: Thu Apr 22 09:20:24 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
* Sources: slip.c by Laurence Culhane, <loz@holmes.demon.co.uk>
* Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
static struct tty_ldisc irda_ldisc;
-static int irtty_hard_xmit( struct sk_buff *skb, struct device *dev);
-static void irtty_wait_until_sent( struct irda_device *driver);
-static int irtty_is_receiving( struct irda_device *idev);
-static int irtty_net_init( struct device *dev);
+static int irtty_hard_xmit(struct sk_buff *skb, struct device *dev);
+static void irtty_wait_until_sent(struct irda_device *driver);
+static int irtty_is_receiving(struct irda_device *idev);
+static int irtty_net_init(struct device *dev);
static int irtty_net_open(struct device *dev);
static int irtty_net_close(struct device *dev);
-static int irtty_open( struct tty_struct *tty);
-static void irtty_close( struct tty_struct *tty);
-static int irtty_ioctl( struct tty_struct *, void *, int, void *);
-static int irtty_receive_room( struct tty_struct *tty);
-static void irtty_change_speed( struct irda_device *dev, int baud);
-static void irtty_write_wakeup( struct tty_struct *tty);
+static int irtty_open(struct tty_struct *tty);
+static void irtty_close(struct tty_struct *tty);
+static int irtty_ioctl(struct tty_struct *, void *, int, void *);
+static int irtty_receive_room(struct tty_struct *tty);
+static void irtty_change_speed(struct irda_device *dev, int baud);
+static void irtty_write_wakeup(struct tty_struct *tty);
-static void irtty_receive_buf( struct tty_struct *, const unsigned char *,
- char *, int);
+static void irtty_receive_buf(struct tty_struct *, const unsigned char *,
+ char *, int);
char *driver_name = "irtty";
__initfunc(int irtty_init(void))
return -ENOMEM;
}
- dongles = hashbin_new( HB_LOCAL);
- if ( dongles == NULL) {
- printk( KERN_WARNING
- "IrDA: Can't allocate dongles hashbin!\n");
+ dongles = hashbin_new(HB_LOCAL);
+ if (dongles == NULL) {
+ printk(KERN_WARNING
+ "IrDA: Can't allocate dongles hashbin!\n");
return -ENOMEM;
}
/* Fill in our line protocol discipline, and register it */
- memset( &irda_ldisc, 0, sizeof( irda_ldisc));
+ memset(&irda_ldisc, 0, sizeof( irda_ldisc));
irda_ldisc.magic = TTY_LDISC_MAGIC;
irda_ldisc.name = "irda";
irda_ldisc.receive_room = irtty_receive_room;
irda_ldisc.write_wakeup = irtty_write_wakeup;
- if (( status = tty_register_ldisc( N_IRDA, &irda_ldisc)) != 0) {
- printk( KERN_ERR
- "IrDA: can't register line discipline (err = %d)\n",
- status);
+ if (( status = tty_register_ldisc(N_IRDA, &irda_ldisc)) != 0) {
+ printk(KERN_ERR
+ "IrDA: can't register line discipline (err = %d)\n",
+ status);
}
return status;
/*
* Unregister tty line-discipline
*/
- if (( ret = tty_register_ldisc( N_IRDA, NULL))) {
- printk( KERN_ERR
- "IrTTY: can't unregister line discipline (err = %d)\n",
- ret);
+ if ((ret = tty_register_ldisc(N_IRDA, NULL))) {
+ ERROR(__FUNCTION__
+ "(), can't unregister line discipline (err = %d)\n",
+ ret);
}
/*
* callback to irtty_close(), therefore we do give any deallocation
* function to hashbin_destroy().
*/
- hashbin_delete( irtty, NULL);
- hashbin_delete( dongles, NULL);
+ hashbin_delete(irtty, NULL);
+ hashbin_delete(dongles, NULL);
}
#endif /* MODULE */
* discipline is called for. Because we are sure the tty line exists,
* we only have to link it to a free IrDA channel.
*/
-static int irtty_open( struct tty_struct *tty)
+static int irtty_open(struct tty_struct *tty)
{
struct irtty_cb *self;
char name[16];
- ASSERT( tty != NULL, return -EEXIST;);
+ ASSERT(tty != NULL, return -EEXIST;);
/* First make sure we're not already connected. */
self = (struct irtty_cb *) tty->disc_data;
- if ( self != NULL && self->magic == IRTTY_MAGIC)
+ if (self != NULL && self->magic == IRTTY_MAGIC)
return -EEXIST;
/*
* Allocate new instance of the driver
*/
- self = kmalloc( sizeof(struct irtty_cb), GFP_KERNEL);
- if ( self == NULL) {
- printk( KERN_ERR "IrDA: Can't allocate memory for "
- "IrDA control block!\n");
+ self = kmalloc(sizeof(struct irtty_cb), GFP_KERNEL);
+ if (self == NULL) {
+ printk(KERN_ERR "IrDA: Can't allocate memory for "
+ "IrDA control block!\n");
return -ENOMEM;
}
- memset( self, 0, sizeof(struct irtty_cb));
+ memset(self, 0, sizeof(struct irtty_cb));
self->tty = tty;
tty->disc_data = self;
/* Give self a name */
- sprintf( name, "%s%d", tty->driver.name,
- MINOR(tty->device) - tty->driver.minor_start +
- tty->driver.name_base);
-
+ sprintf(name, "%s%d", tty->driver.name,
+ MINOR(tty->device) - tty->driver.minor_start +
+ tty->driver.name_base);
+
/* hashbin_insert( irtty, (QUEUE*) self, 0, self->name); */
- hashbin_insert( irtty, (QUEUE*) self, (int) self, NULL);
+ hashbin_insert(irtty, (QUEUE*) self, (int) self, NULL);
- if (tty->driver.flush_buffer) {
+ if (tty->driver.flush_buffer)
tty->driver.flush_buffer(tty);
- }
-
- if (tty->ldisc.flush_buffer) {
+
+ if (tty->ldisc.flush_buffer)
tty->ldisc.flush_buffer(tty);
- }
self->magic = IRTTY_MAGIC;
* that are not device dependent (such as link disconnect time) so
* this parameter can be set by IrLAP (or the user) instead. DB
*/
- irda_init_max_qos_capabilies( &self->idev.qos);
+ irda_init_max_qos_capabilies(&self->idev.qos);
/* The only value we must override it the baudrate */
self->idev.qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
IR_115200;
- self->idev.qos.min_turn_time.bits = 0x03;
+ self->idev.qos.min_turn_time.bits = 0x0f;
self->idev.flags = IFF_SIR | IFF_PIO;
irda_qos_bits_to_value(&self->idev.qos);
}
/*
- * Function irtty_close ( tty)
+ * Function irtty_close (tty)
*
* Close down a IrDA channel. This means flushing out any pending queues,
* and then restoring the TTY line discipline to what it was before it got
MOD_DEC_USE_COUNT;
}
+/*
+ * Function irtty_stop_receiver (irda_device, stop)
+ *
+ *
+ *
+ */
+static void irtty_stop_receiver(struct irda_device *idev, int stop)
+{
+ struct termios old_termios;
+ struct irtty_cb *self;
+ int cflag;
+
+ self = (struct irtty_cb *) idev->priv;
+
+ old_termios = *(self->tty->termios);
+ cflag = self->tty->termios->c_cflag;
+
+ if (stop)
+ cflag &= ~CREAD;
+ else
+ cflag |= CREAD;
+
+ self->tty->termios->c_cflag = cflag;
+ self->tty->driver.set_termios(self->tty, &old_termios);
+}
+
/*
- * Function irtty_change_speed ( self, baud)
+ * Function irtty_change_speed (self, baud)
*
* Change the speed of the serial port. The driver layer must check that
* all transmission has finished using the irtty_wait_until_sent()
* function.
*/
-static void irtty_change_speed( struct irda_device *idev, int baud)
+static void irtty_change_speed(struct irda_device *idev, int baud)
{
struct termios old_termios;
struct irtty_cb *self;
DEBUG(4,__FUNCTION__ "(), <%ld>\n", jiffies);
- ASSERT( idev != NULL, return;);
- ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+ ASSERT(idev != NULL, return;);
+ ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
self = (struct irtty_cb *) idev->priv;
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == IRTTY_MAGIC, return;);
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRTTY_MAGIC, return;);
old_termios = *(self->tty->termios);
cflag = self->tty->termios->c_cflag;
cflag &= ~CBAUD;
- DEBUG( 4, __FUNCTION__ "(), Setting speed to %d\n", baud);
+ DEBUG(4, __FUNCTION__ "(), Setting speed to %d\n", baud);
- switch( baud) {
+ switch (baud) {
case 1200:
cflag |= B1200;
break;
}
self->tty->termios->c_cflag = cflag;
- self->tty->driver.set_termios( self->tty, &old_termios);
+ self->tty->driver.set_termios(self->tty, &old_termios);
}
/*
* Initialize attached dongle. Warning, must be called with a process
* context!
*/
-static void irtty_init_dongle( struct irtty_cb *self, int type)
+static void irtty_init_dongle(struct irtty_cb *self, int type)
{
struct dongle_q *node;
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == IRTTY_MAGIC, return;);
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRTTY_MAGIC, return;);
#ifdef CONFIG_KMOD
/* Try to load the module needed */
switch( type) {
case ESI_DONGLE:
- DEBUG( 0, __FUNCTION__ "(), ESI dongle!\n");
- request_module( "esi");
+ MESSAGE("IrDA: Trying to initialize ESI dongle!\n");
+ request_module("esi");
break;
case TEKRAM_DONGLE:
- DEBUG( 0, __FUNCTION__ "(), Tekram dongle!\n");
- request_module( "tekram");
+ MESSAGE("IrDA: Trying to initialize Tekram dongle!\n");
+ request_module("tekram");
break;
case ACTISYS_DONGLE: /* FALLTHROUGH */
case ACTISYS_PLUS_DONGLE:
- DEBUG( 0, __FUNCTION__ "(), ACTiSYS dongle!\n");
- request_module( "actisys");
+ MESSAGE("IrDA: Trying to initialize ACTiSYS dongle!\n");
+ request_module("actisys");
break;
case GIRBIL_DONGLE:
- DEBUG( 0, __FUNCTION__ "(), GIrBIL dongle!\n");
- request_module( "girbil");
+ MESSAGE("IrDA: Trying to initialize GIrBIL dongle!\n");
+ request_module("girbil");
break;
default:
- DEBUG( 0, __FUNCTION__ "(), Unknown dongle type!\n");
+ ERROR("Unknown dongle type!\n");
return;
- break;
}
#endif /* CONFIG_KMOD */
- node = hashbin_find( dongles, type, NULL);
+ node = hashbin_find(dongles, type, NULL);
if ( !node) {
- DEBUG(0, __FUNCTION__ "(), Unable to find requested dongle\n");
+ ERROR("Unable to find requested dongle\n");
return;
}
self->dongle_q = node;
/*
* Now initialize the dongle!
*/
- node->dongle->open( &self->idev, type);
- node->dongle->qos_init( &self->idev, &self->idev.qos);
+ node->dongle->open(&self->idev, type);
+ node->dongle->qos_init(&self->idev, &self->idev.qos);
/* Reset dongle */
- node->dongle->reset( &self->idev, 0);
+ node->dongle->reset(&self->idev, 0);
/* Set to default baudrate */
- node->dongle->change_speed( &self->idev, 9600);
+ node->dongle->change_speed(&self->idev, 9600);
}
/*
self = (struct irtty_cb *) tty->disc_data;
- ASSERT( self != NULL, return -ENODEV;);
- ASSERT( self->magic == IRTTY_MAGIC, return -EBADR;);
+ ASSERT(self != NULL, return -ENODEV;);
+ ASSERT(self->magic == IRTTY_MAGIC, return -EBADR;);
- if ( _IOC_DIR(cmd) & _IOC_READ)
+ if (_IOC_DIR(cmd) & _IOC_READ)
err = verify_area( VERIFY_WRITE, (void *) arg, size);
- else if ( _IOC_DIR(cmd) & _IOC_WRITE)
+ else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = verify_area( VERIFY_READ, (void *) arg, size);
- if ( err)
+ if (err)
return err;
switch(cmd) {
case TCGETS:
case TCGETA:
- return n_tty_ioctl( tty, (struct file *) file, cmd,
- (unsigned long) arg);
+ return n_tty_ioctl(tty, (struct file *) file, cmd,
+ (unsigned long) arg);
break;
case IRTTY_IOCTDONGLE:
/* Initialize dongle */
- irtty_init_dongle( self, (int) arg);
+ irtty_init_dongle(self, (int) arg);
break;
default:
return -ENOIOCTLCMD;
{
struct irtty_cb *self = (struct irtty_cb *) tty->disc_data;
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == IRTTY_MAGIC, return;);
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRTTY_MAGIC, return;);
/* Read the characters out of the buffer */
while (count--) {
if (fp && *fp++) {
DEBUG( 0, "Framing or parity error!\n");
irda_device_set_media_busy( &self->idev, TRUE);
- /* sl->rx_errors++; */
+
cp++;
continue;
}
/* Unwrap and destuff one byte */
- async_unwrap_char( &self->idev, *cp++);
+ async_unwrap_char(&self->idev, *cp++);
}
}
/*
* Function irtty_hard_xmit (skb, dev)
*
- * Transmit skb
+ * Transmit frame
*
*/
static int irtty_hard_xmit(struct sk_buff *skb, struct device *dev)
struct irda_device *idev;
int actual = 0;
- ASSERT( dev != NULL, return 0;);
- ASSERT( skb != NULL, return 0;);
-
idev = (struct irda_device *) dev->priv;
ASSERT(idev != NULL, return 0;);
/* Lock transmit buffer */
if (irda_lock((void *) &dev->tbusy) == FALSE)
return -EBUSY;
-
- /*
- * Transfer skb to tx_buff while wrapping, stuffing and making CRC
- */
+
+ /* Init tx buffer*/
+ idev->tx_buff.data = idev->tx_buff.head;
+
+ /* Copy skb to tx_buff while wrapping, stuffing and making CRC */
idev->tx_buff.len = async_wrap_skb(skb, idev->tx_buff.data,
idev->tx_buff.truesize);
dev->trans_start = jiffies;
- if ( self->tty->driver.write)
+ if (self->tty->driver.write)
actual = self->tty->driver.write(self->tty, 0,
idev->tx_buff.data,
idev->tx_buff.len);
- idev->tx_buff.offset = actual;
- idev->tx_buff.head = idev->tx_buff.data + actual;
+ /* Hide the part we just transmitted */
+ idev->tx_buff.data += actual;
+ idev->tx_buff.len -= actual;
idev->stats.tx_packets++;
idev->stats.tx_bytes += idev->tx_buff.len;
* Did we transmit the whole frame? Commented out for now since
* I must check if this optimalization really works. DB.
*/
- if (( idev->tx.count - idev->tx.ptr) <= 0) {
+ if ((idev->tx_buff.len) == 0) {
DEBUG( 4, "irtty_xmit_buf: finished with frame!\n");
self->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
irda_unlock( &self->tbusy);
* Used by the TTY to find out how much data we can receive at a time
*
*/
-static int irtty_receive_room( struct tty_struct *tty)
+static int irtty_receive_room(struct tty_struct *tty)
{
+ DEBUG(0, __FUNCTION__ "()\n");
return 65536; /* We can handle an infinite amount of data. :-) */
}
* more packets to send, we send them here.
*
*/
-static void irtty_write_wakeup( struct tty_struct *tty)
+static void irtty_write_wakeup(struct tty_struct *tty)
{
- int actual = 0, count;
struct irtty_cb *self = (struct irtty_cb *) tty->disc_data;
struct irda_device *idev;
+ int actual = 0;
/*
* First make sure we're connected.
*/
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == IRTTY_MAGIC, return;);
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRTTY_MAGIC, return;);
idev = &self->idev;
- /*
- * Finished with frame?
- */
- if ( idev->tx_buff.offset == idev->tx_buff.len) {
+ /* Finished with frame? */
+ if (idev->tx_buff.len > 0) {
+ /* Write data left in transmit buffer */
+ actual = tty->driver.write(tty, 0, idev->tx_buff.data,
+ idev->tx_buff.len);
+ idev->tx_buff.data += actual;
+ idev->tx_buff.len -= actual;
+ } else {
/*
* Now serial buffer is almost free & we can start
* transmission of another packet
*/
DEBUG(5, __FUNCTION__ "(), finished with frame!\n");
-
+
tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
idev->netdev.tbusy = 0; /* Unlock */
-
+
/* Tell network layer that we want more frames */
- mark_bh( NET_BH);
-
- return;
+ mark_bh(NET_BH);
}
- /*
- * Write data left in transmit buffer
- */
- count = idev->tx_buff.len - idev->tx_buff.offset;
- actual = tty->driver.write( tty, 0, idev->tx_buff.head, count);
- idev->tx_buff.offset += actual;
- idev->tx_buff.head += actual;
}
/*
* Return TRUE is we are currently receiving a frame
*
*/
-static int irtty_is_receiving( struct irda_device *idev)
+static int irtty_is_receiving(struct irda_device *idev)
{
- return ( idev->rx_buff.state != OUTSIDE_FRAME);
+ return (idev->rx_buff.state != OUTSIDE_FRAME);
}
/*
* to change the speed of the serial port. Warning this function must
* be called with a process context!
*/
-static void irtty_wait_until_sent( struct irda_device *idev)
+static void irtty_wait_until_sent(struct irda_device *idev)
{
struct irtty_cb *self = (struct irtty_cb *) idev->priv;
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == IRTTY_MAGIC, return;);
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRTTY_MAGIC, return;);
- DEBUG( 4, "Chars in buffer %d\n",
- self->tty->driver.chars_in_buffer( self->tty));
+ DEBUG(4, "Chars in buffer %d\n",
+ self->tty->driver.chars_in_buffer(self->tty));
- tty_wait_until_sent( self->tty, 0);
+ tty_wait_until_sent(self->tty, 0);
}
-int irtty_register_dongle( struct dongle *dongle)
+int irtty_register_dongle(struct dongle *dongle)
{
struct dongle_q *new;
}
/* Make new IrDA dongle */
- new = (struct dongle_q *)kmalloc(sizeof(struct dongle_q), GFP_KERNEL);
- if (new == NULL) {
- return 1;
+ new = (struct dongle_q *) kmalloc(sizeof(struct dongle_q), GFP_KERNEL);
+ if (new == NULL)
+ return -1;
- }
- memset( new, 0, sizeof( struct dongle_q));
+ memset(new, 0, sizeof( struct dongle_q));
new->dongle = dongle;
/* Insert IrDA dongle into hashbin */
return 0;
}
-void irtty_unregister_dongle( struct dongle *dongle)
+void irtty_unregister_dongle(struct dongle *dongle)
{
struct dongle_q *node;
- node = hashbin_remove( dongles, dongle->type, NULL);
- if ( !node) {
- DEBUG( 0, __FUNCTION__ "(), dongle not found!\n");
+ node = hashbin_remove(dongles, dongle->type, NULL);
+ if (!node) {
+ ERROR(__FUNCTION__ "(), dongle not found!\n");
return;
}
- kfree( node);
+ kfree(node);
}
set_fs(get_ds());
if (tty->driver.ioctl(tty, NULL, TIOCMSET, (unsigned long) &arg)) {
- DEBUG(0, __FUNCTION__ "(), error!\n");
+ ERROR(__FUNCTION__ "(), error doing ioctl!\n");
}
set_fs(fs);
}
-
-static int irtty_net_init( struct device *dev)
+static int irtty_net_init(struct device *dev)
{
/* Set up to be a normal IrDA network device driver */
- irda_device_setup( dev);
+ irda_device_setup(dev);
/* Insert overrides below this line! */
return 0;
}
-
-static int irtty_net_open( struct device *dev)
+static int irtty_net_open(struct device *dev)
{
- ASSERT( dev != NULL, return -1;);
+ ASSERT(dev != NULL, return -1;);
/* Ready to play! */
dev->tbusy = 0;
static int irtty_net_close(struct device *dev)
{
- ASSERT( dev != NULL, return -1;);
+ ASSERT(dev != NULL, return -1;);
/* Stop device */
dev->tbusy = 1;
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sat Nov 7 21:43:15 1998
- * Modified at: Sat Apr 3 15:54:47 1999
+ * Modified at: Tue Apr 20 11:11:39 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>
/* Decide if we should use PIO or DMA transfer */
if ( idev->io.baudrate > 115200) {
- memcpy( idev->tx_buff.data, skb->data, skb->len);
+ idev->tx_buff.data = idev->tx_buff.head;
+ memcpy(idev->tx_buff.data, skb->data, skb->len);
idev->tx_buff.len = skb->len;
- idev->tx_buff.head = idev->tx_buff.data;
- idev->tx_buff.offset = 0;
mtt = irda_get_mtt( skb);
if ( mtt > 50) {
pc87108_dma_write( idev, iobase);
}
} else {
- idev->tx_buff.len = async_wrap_skb( skb, idev->tx_buff.data,
- idev->tx_buff.truesize);
+ idev->tx_buff.len = async_wrap_skb(skb, idev->tx_buff.data,
+ idev->tx_buff.truesize);
- idev->tx_buff.offset = 0;
- idev->tx_buff.head = idev->tx_buff.data;
+ idev->tx_buff.data = idev->tx_buff.head;
/* Add interrupt on tx low level (will fire immediately) */
switch_bank( iobase, BANK0);
/* driver->media_busy = FALSE; */
idev->io.direction = IO_RECV;
- idev->rx_buff.head = idev->rx_buff.data;
- idev->rx_buff.offset = 0;
+ idev->rx_buff.data = idev->rx_buff.head;
/* Reset Rx FIFO. This will also flush the ST_FIFO */
outb(FCR_RXTH|FCR_TXTH|FCR_RXSR|FCR_FIFO_EN, iobase+FCR);
/* Skip frame */
idev->stats.rx_errors++;
- idev->rx_buff.offset += len;
- idev->rx_buff.head += len;
+ idev->rx_buff.data += len;
- if ( status & FRM_ST_MAX_LEN)
+ if (status & FRM_ST_MAX_LEN)
idev->stats.rx_length_errors++;
- if ( status & FRM_ST_PHY_ERR)
+ if (status & FRM_ST_PHY_ERR)
idev->stats.rx_frame_errors++;
- if ( status & FRM_ST_BAD_CRC)
+ if (status & FRM_ST_BAD_CRC)
idev->stats.rx_crc_errors++;
}
/* The errors below can be reported in both cases */
- if ( status & FRM_ST_OVR1)
+ if (status & FRM_ST_OVR1)
idev->stats.rx_fifo_errors++;
- if ( status & FRM_ST_OVR2)
+ if (status & FRM_ST_OVR2)
idev->stats.rx_fifo_errors++;
} else {
/* Check if we have transfered all data to memory */
- if ( inb( iobase+LSR) & LSR_RXDA) {
+ if (inb(iobase+LSR) & LSR_RXDA) {
/* Put this entry back in fifo */
st_fifo->head--;
st_fifo->len++;
st_fifo->entries[st_fifo->head].status = status;
- st_fifo->entries[ st_fifo->head].len = len;
+ st_fifo->entries[st_fifo->head].len = len;
/* Restore bank register */
- outb( bank, iobase+BSR);
+ outb(bank, iobase+BSR);
return FALSE; /* I'll be back! */
}
/* Should be OK then */
- skb = dev_alloc_skb( len+1);
+ skb = dev_alloc_skb(len+1);
if (skb == NULL) {
printk( KERN_INFO __FUNCTION__
"(), memory squeeze, dropping frame.\n");
}
/* Make sure IP header gets aligned */
- skb_reserve( skb, 1);
+ skb_reserve(skb, 1);
/* Copy frame without CRC */
- if ( idev->io.baudrate < 4000000) {
- skb_put( skb, len-2);
- memcpy( skb->data, idev->rx_buff.head, len-2);
+ if (idev->io.baudrate < 4000000) {
+ skb_put(skb, len-2);
+ memcpy(skb->data, idev->rx_buff.data, len-2);
} else {
- skb_put( skb, len-4);
- memcpy( skb->data, idev->rx_buff.head, len-4);
+ skb_put(skb, len-4);
+ memcpy(skb->data, idev->rx_buff.data, len-4);
}
/* Move to next frame */
- idev->rx_buff.offset += len;
- idev->rx_buff.head += len;
+ idev->rx_buff.data += len;
idev->stats.rx_packets++;
skb->dev = &idev->netdev;
}
}
/* Restore bank register */
- outb( bank, iobase+BSR);
+ outb(bank, iobase+BSR);
return TRUE;
}
__u8 byte = 0x00;
int iobase;
- DEBUG( 4, __FUNCTION__ "()\n");
+ DEBUG(4, __FUNCTION__ "()\n");
- ASSERT( idev != NULL, return;);
- ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+ ASSERT(idev != NULL, return;);
+ ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
iobase = idev->io.iobase;
- if ( idev->rx_buff.len == 0) {
- idev->rx_buff.head = idev->rx_buff.data;
- }
-
/* Receive all characters in Rx FIFO */
do {
- byte = inb( iobase+RXD);
- async_unwrap_char( idev, byte);
+ byte = inb(iobase+RXD);
+ async_unwrap_char(idev, byte);
- } while ( inb( iobase+LSR) & LSR_RXDA); /* Data available */
+ } while (inb(iobase+LSR) & LSR_RXDA); /* Data available */
}
/*
* Handle SIR interrupt
*
*/
-static __u8 pc87108_sir_interrupt( struct irda_device *idev, int eir)
+static __u8 pc87108_sir_interrupt(struct irda_device *idev, int eir)
{
- int len;
int actual;
__u8 new_ier = 0;
/* Transmit FIFO low on data */
if ( eir & EIR_TXLDL_EV) {
/* Write data left in transmit buffer */
- len = idev->tx_buff.len - idev->tx_buff.offset;
-
- ASSERT( len > 0, return 0;);
- actual = pc87108_pio_write( idev->io.iobase,
- idev->tx_buff.head,
- len, idev->io.fifo_size);
- idev->tx_buff.offset += actual;
- idev->tx_buff.head += actual;
+ actual = pc87108_pio_write(idev->io.iobase,
+ idev->tx_buff.data,
+ idev->tx_buff.len,
+ idev->io.fifo_size);
+ idev->tx_buff.data += actual;
+ idev->tx_buff.len -= actual;
idev->io.direction = IO_XMIT;
- ASSERT( actual <= len, return 0;);
/* Check if finished */
- if ( actual == len) {
- DEBUG( 4, __FUNCTION__ "(), finished with frame!\n");
+ if (idev->tx_buff.len > 0)
+ new_ier |= IER_TXLDL_IE;
+ else {
idev->netdev.tbusy = 0; /* Unlock */
idev->stats.tx_packets++;
-
+
mark_bh(NET_BH);
new_ier |= IER_TXEMP_IE;
- } else
- new_ier |= IER_TXLDL_IE;
+ }
+
}
/* Check if transmission has completed */
if ( eir & EIR_TXEMP_EV) {
/*********************************************************************
*
* Filename: tekram.c
- * Version: 0.5
+ * Version: 1.0
* Description: Implementation of the Tekram IrMate IR-210B dongle
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Wed Oct 21 20:02:35 1998
- * Modified at: Mon Feb 15 14:13:17 1999
+ * Modified at: Tue Apr 13 16:33:54 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli, All Rights Reserved.
#include <asm/uaccess.h>
#include <net/irda/irda.h>
-#include <net/irda/irmod.h>
#include <net/irda/irda_device.h>
#include <net/irda/irtty.h>
#include <net/irda/dongle.h>
-static void tekram_reset( struct irda_device *dev, int unused);
-static void tekram_open( struct irda_device *dev, int type);
-static void tekram_close( struct irda_device *dev);
-static void tekram_change_speed( struct irda_device *dev, int baud);
-static void tekram_init_qos( struct irda_device *idev, struct qos_info *qos);
+static void tekram_reset(struct irda_device *dev, int unused);
+static void tekram_open(struct irda_device *dev, int type);
+static void tekram_close(struct irda_device *dev);
+static void tekram_change_speed(struct irda_device *dev, int baud);
+static void tekram_init_qos(struct irda_device *idev, struct qos_info *qos);
+
+#define TEKRAM_115200 0x00
+#define TEKRAM_57600 0x01
+#define TEKRAM_38400 0x02
+#define TEKRAM_19200 0x03
+#define TEKRAM_9600 0x04
+
+#define TEKRAM_PW 0x10 /* Pulse select bit */
static struct dongle dongle = {
TEKRAM_DONGLE,
tekram_init_qos,
};
-__initfunc(void tekram_init(void))
+__initfunc(int tekram_init(void))
{
- irtty_register_dongle( &dongle);
+ return irtty_register_dongle(&dongle);
}
void tekram_cleanup(void)
int cflag;
__u8 byte;
- DEBUG( 4, __FUNCTION__ "()\n");
+ DEBUG(4, __FUNCTION__ "()\n");
- ASSERT( dev != NULL, return;);
- ASSERT( dev->magic == IRDA_DEVICE_MAGIC, return;);
+ ASSERT(dev != NULL, return;);
+ ASSERT(dev->magic == IRDA_DEVICE_MAGIC, return;);
self = (struct irtty_cb *) dev->priv;
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == IRTTY_MAGIC, return;);
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRTTY_MAGIC, return;);
- if ( !self->tty)
+ if (!self->tty)
return;
tty = self->tty;
cflag &= ~CBAUD;
switch (baud) {
- case 9600:
default:
+ /* FALLTHROUGH */
+ case 9600:
cflag |= B9600;
- byte = 4;
+ byte = TEKRAM_PW|TEKRAM_9600;
break;
case 19200:
cflag |= B19200;
- byte = 3;
+ byte = TEKRAM_PW|TEKRAM_19200;
break;
case 34800:
cflag |= B38400;
- byte = 2;
+ byte = TEKRAM_PW|TEKRAM_38400;
break;
case 57600:
cflag |= B57600;
- byte = 1;
+ byte = TEKRAM_PW|TEKRAM_57600;
break;
case 115200:
cflag |= B115200;
- byte = 0;
+ byte = TEKRAM_PW|TEKRAM_115200;
break;
}
irtty_set_dtr_rts(tty, TRUE, FALSE);
/* Wait at least 7us */
- udelay( 7);
+ udelay(7);
/* Write control byte */
- if ( tty->driver.write)
- tty->driver.write( self->tty, 0, &byte, 1);
+ if (tty->driver.write)
+ tty->driver.write(self->tty, 0, &byte, 1);
/* Wait at least 100 ms */
current->state = TASK_INTERRUPTIBLE;
- schedule_timeout( 10);
+ schedule_timeout(MSECS_TO_JIFFIES(100));
/* Set DTR, Set RTS */
irtty_set_dtr_rts(tty, TRUE, TRUE);
/* Now change the speed of the serial port */
tty->termios->c_cflag = cflag;
- tty->driver.set_termios( tty, &old_termios);
+ tty->driver.set_termios(tty, &old_termios);
}
/*
* must be called with a process context!!
*
* Algorithm:
- * 0. set RTS and DTR, and wait 50 ms
- * ( power off the IR-210 )
+ * 0. Clear RTS and DTR, and wait 50 ms (power off the IR-210 )
* 1. clear RTS
* 2. set DTR, and wait at least 1 ms
* 3. clear DTR to SPACE state, wait at least 50 us for further
* operation
*/
-void tekram_reset( struct irda_device *dev, int unused)
+void tekram_reset(struct irda_device *dev, int unused)
{
struct irtty_cb *self;
struct tty_struct *tty;
- DEBUG( 4, __FUNCTION__ "()\n");
+ DEBUG(4, __FUNCTION__ "()\n");
- ASSERT( dev != NULL, return;);
- ASSERT( dev->magic == IRDA_DEVICE_MAGIC, return;);
+ ASSERT(dev != NULL, return;);
+ ASSERT(dev->magic == IRDA_DEVICE_MAGIC, return;);
self = (struct irtty_cb *) dev->priv;
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == IRTTY_MAGIC, return;);
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRTTY_MAGIC, return;);
tty = self->tty;
- if ( !tty)
+ if (!tty)
return;
/* Power off dongle */
/* Sleep 50 ms */
current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(5);
+ schedule_timeout(MSECS_TO_JIFFIES(50));
/* Clear DTR, Set RTS */
irtty_set_dtr_rts(tty, FALSE, TRUE);
/* Should sleep 1 ms, but 10-20 should not do any harm */
current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(2);
+ schedule_timeout(MSECS_TO_JIFFIES(20));
/* Set DTR, Set RTS */
irtty_set_dtr_rts(tty, TRUE, TRUE);
+ udelay(50);
+
/* Finished! */
}
* Initialize QoS capabilities
*
*/
-static void tekram_init_qos( struct irda_device *idev, struct qos_info *qos)
+static void tekram_init_qos(struct irda_device *idev, struct qos_info *qos)
{
qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
- qos->min_turn_time.bits &= 0xfe; /* All except 0 ms */
+ qos->min_turn_time.bits &= 0x01; /* Needs at least 10 ms */
+ irda_qos_bits_to_value(qos);
}
#ifdef MODULE
*/
int init_module(void)
{
- tekram_init();
- return(0);
+ return tekram_init();
}
/*
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sat Dec 26 10:59:03 1998
- * Modified at: Sat Apr 3 15:54:41 1999
+ * Modified at: Tue Apr 20 11:15:52 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli, All Rights Reserved.
/* The only value we must override it the baudrate */
idev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
- IR_115200|/*IR_576000|IR_1152000| */(IR_4000000 << 8);
+ IR_115200/*IR_576000|IR_1152000 |(IR_4000000 << 8)*/;
idev->qos.min_turn_time.bits = 0x0f;
irda_qos_bits_to_value(&idev->qos);
DEBUG(0, __FUNCTION__ "(), handling baud of 4000000\n");
/* Set self pole address */
- outb(0x10, iobase+UIRCC_CR8);
+ //outb(0xfe, iobase+UIRCC_CR8);
/* outb(0x10, iobase+UIRCC_CR11); */
break;
skb->len++;
idev->tx_buff.len = skb->len;
- idev->tx_buff.head = idev->tx_buff.data;
- idev->tx_buff.offset = 0;
+ idev->tx_buff.data = idev->tx_buff.head;
mtt = irda_get_mtt(skb);
outb(self->cr3, iobase+UIRCC_CR3);
/* Transmit reset (just in case) */
- outb(UIRCC_CR0_XMIT_RST, iobase+UIRCC_CR0);
+ outb(UIRCC_CR0_XMIT_RST|0x17, iobase+UIRCC_CR0);
/* Set modem */
outb(0x08, iobase+UIRCC_CR10);
+ /* Enable receiving with CRC */
+ self->cr3 = (UIRCC_CR3_RECV_EN|UIRCC_CR3_RX_CRC_EN);
+ outb(self->cr3, iobase+UIRCC_CR3);
+
/* Make sure Rx DMA is set */
outb(UIRCC_CR1_RX_DMA|UIRCC_CR1_MUST_SET, iobase+UIRCC_CR1);
/* driver->media_busy = FALSE; */
idev->io.direction = IO_RECV;
- idev->rx_buff.head = idev->rx_buff.data;
- idev->rx_buff.offset = 0;
+ idev->rx_buff.data = idev->rx_buff.head;
+#if 0
/* Enable receiving with CRC */
self->cr3 = (UIRCC_CR3_RECV_EN|UIRCC_CR3_RX_CRC_EN);
outb(self->cr3, iobase+UIRCC_CR3);
-
+#endif
DEBUG(4, __FUNCTION__ "(), cr3=%#x\n", self->cr3);
/* Address check? */
/* } */
skb_put(skb, len);
- memcpy(skb->data, idev->rx_buff.head, len);
+ memcpy(skb->data, idev->rx_buff.data, len);
idev->stats.rx_packets++;
skb->dev = &idev->netdev;
uircc_dma_xmit_complete(idev, FALSE);
uircc_dma_receive(idev);
- /* outb(0, iobase+UIRCC_CR2); */
+ outb(0x0d, iobase+UIRCC_CR2);
break;
case UIRCC_SR3_TMR_OUT:
/* Disable timer */
* Status: Experimental.
* Author: Paul VanderSpek
* Created at: Wed Nov 4 11:46:16 1998
- * Modified at: Wed Apr 7 17:35:59 1999
+ * Modified at: Tue Apr 20 11:15:00 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Corel Computer Corp.
if (idev->io.baudrate > 115200) {
memcpy(idev->tx_buff.data, skb->data, skb->len);
idev->tx_buff.len = skb->len;
- idev->tx_buff.head = idev->tx_buff.data;
- idev->tx_buff.offset = 0;
+ idev->tx_buff.data = idev->tx_buff.head;
mtt = irda_get_mtt(skb);
if (mtt > 50) {
w83977af_dma_write(idev, iobase);
}
} else {
+ idev->tx_buff.data = idev->tx_buff.head;
idev->tx_buff.len = async_wrap_skb(skb, idev->tx_buff.data,
idev->tx_buff.truesize);
- idev->tx_buff.offset = 0;
- idev->tx_buff.head = idev->tx_buff.data;
-
/* Add interrupt on tx low level (will fire immediately) */
- switch_bank( iobase, SET0);
+ switch_bank(iobase, SET0);
outb(ICR_ETXTHI, iobase+ICR);
}
dev_kfree_skb(skb);
/* Restore set register */
- outb( set, iobase+SSR);
+ outb(set, iobase+SSR);
return 0;
}
}
/* Fill FIFO with current frame */
- while (( fifo_size-- > 0) && (actual < len)) {
+ while ((fifo_size-- > 0) && (actual < len)) {
/* Transmit next byte */
- outb( buf[actual++], iobase+TBR);
+ outb(buf[actual++], iobase+TBR);
}
DEBUG(4, __FUNCTION__ "(), fifo_size %d ; %d sent of %d\n",
#endif
/* driver->media_busy = FALSE; */
idev->io.direction = IO_RECV;
- idev->rx_buff.head = idev->rx_buff.data;
- idev->rx_buff.offset = 0;
+ idev->rx_buff.data = idev->rx_buff.head;
/*
* Reset Rx FIFO. This will also flush the ST_FIFO, it's very
__u8 set;
__u8 status;
- DEBUG( 0, __FUNCTION__ "\n");
+ DEBUG(0, __FUNCTION__ "\n");
iobase = idev->io.iobase;
/* Save current set */
- set = inb( iobase+SSR);
+ set = inb(iobase+SSR);
iobase = idev->io.iobase;
switch_bank(iobase, SET5);
- if ( prev.status & FS_FO_FSFDR) {
+ if (prev.status & FS_FO_FSFDR) {
status = prev.status;
len = prev.len;
-
+
prev.status = 0;
} else {
- status = inb( iobase+FS_FO);
- len = inb( iobase+RFLFL);
- len |= inb( iobase+RFLFH) << 8;
+ status = inb(iobase+FS_FO);
+ len = inb(iobase+RFLFL);
+ len |= inb(iobase+RFLFH) << 8;
}
- while ( status & FS_FO_FSFDR) {
+ while (status & FS_FO_FSFDR) {
/* Check for errors */
- if ( status & FS_FO_ERR_MSK) {
+ if (status & FS_FO_ERR_MSK) {
if ( status & FS_FO_LST_FR) {
/* Add number of lost frames to stats */
idev->stats.rx_errors += len;
/* Skip frame */
idev->stats.rx_errors++;
- idev->rx_buff.offset += len;
- idev->rx_buff.head += len;
+ idev->rx_buff.data += len;
- if ( status & FS_FO_MX_LEX)
+ if (status & FS_FO_MX_LEX)
idev->stats.rx_length_errors++;
- if ( status & FS_FO_PHY_ERR)
+ if (status & FS_FO_PHY_ERR)
idev->stats.rx_frame_errors++;
- if ( status & FS_FO_CRC_ERR)
+ if (status & FS_FO_CRC_ERR)
idev->stats.rx_crc_errors++;
}
/* The errors below can be reported in both cases */
- if ( status & FS_FO_RX_OV)
+ if (status & FS_FO_RX_OV)
idev->stats.rx_fifo_errors++;
- if ( status & FS_FO_FSF_OV)
+ if (status & FS_FO_FSF_OV)
idev->stats.rx_fifo_errors++;
} else {
/* Check if we have transfered all data to memory */
switch_bank(iobase, SET0);
- if ( inb( iobase+USR) & USR_RDR) {
+ if (inb(iobase+USR) & USR_RDR) {
/* Put this entry back in fifo */
prev.status = status;
prev.len = len;
return FALSE; /* I'll be back! */
}
- skb = dev_alloc_skb( len+1);
+ skb = dev_alloc_skb(len+1);
if (skb == NULL) {
- printk( KERN_INFO __FUNCTION__
- "(), memory squeeze, dropping frame.\n");
+ printk(KERN_INFO __FUNCTION__
+ "(), memory squeeze, dropping frame.\n");
/* Restore set register */
- outb( set, iobase+SSR);
+ outb(set, iobase+SSR);
return FALSE;
}
/* Align to 20 bytes */
- skb_reserve( skb, 1);
+ skb_reserve(skb, 1);
/* Copy frame without CRC */
if ( idev->io.baudrate < 4000000) {
skb_put( skb, len-2);
- memcpy( skb->data, idev->rx_buff.head, len-2);
+ memcpy( skb->data, idev->rx_buff.data, len-2);
} else {
skb_put( skb, len-4);
- memcpy( skb->data, idev->rx_buff.head, len-4);
+ memcpy( skb->data, idev->rx_buff.data, len-4);
}
/* Move to next frame */
- idev->rx_buff.offset += len;
- idev->rx_buff.head += len;
+ idev->rx_buff.data += len;
skb->dev = &idev->netdev;
skb->mac.raw = skb->data;
len |= inb( iobase+RFLFH) << 8;
}
/* Restore set register */
- outb( set, iobase+SSR);
+ outb(set, iobase+SSR);
return TRUE;
}
* Receive all data in receiver FIFO
*
*/
-static void w83977af_pio_receive( struct irda_device *idev)
+static void w83977af_pio_receive(struct irda_device *idev)
{
__u8 byte = 0x00;
int iobase;
- DEBUG( 4, __FUNCTION__ "()\n");
+ DEBUG(4, __FUNCTION__ "()\n");
- ASSERT( idev != NULL, return;);
- ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+ ASSERT(idev != NULL, return;);
+ ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
iobase = idev->io.iobase;
- if ( idev->rx_buff.len == 0) {
- idev->rx_buff.head = idev->rx_buff.data;
- }
-
/* Receive all characters in Rx FIFO */
do {
- byte = inb( iobase+RBR);
- async_unwrap_char( idev, byte);
+ byte = inb(iobase+RBR);
+ async_unwrap_char(idev, byte);
- } while ( inb( iobase+USR) & USR_RDR); /* Data available */
+ } while (inb(iobase+USR) & USR_RDR); /* Data available */
}
/*
*/
static __u8 w83977af_sir_interrupt(struct irda_device *idev, int isr)
{
- int len;
int actual;
__u8 new_icr = 0;
/* Transmit FIFO low on data */
if (isr & ISR_TXTH_I) {
/* Write data left in transmit buffer */
- len = idev->tx_buff.len - idev->tx_buff.offset;
-
- ASSERT(len > 0, return 0;);
actual = w83977af_pio_write(idev->io.iobase,
- idev->tx_buff.head,
- len, idev->io.fifo_size);
- idev->tx_buff.offset += actual;
- idev->tx_buff.head += actual;
+ idev->tx_buff.data,
+ idev->tx_buff.len,
+ idev->io.fifo_size);
+ idev->tx_buff.data += actual;
+ idev->tx_buff.len -= actual;
idev->io.direction = IO_XMIT;
- ASSERT( actual <= len, return 0;);
+
/* Check if finished */
- if ( actual == len) {
+ if (idev->tx_buff.len > 0)
+ new_icr |= ICR_ETXTHI;
+ else {
DEBUG( 4, __FUNCTION__ "(), finished with frame!\n");
idev->netdev.tbusy = 0; /* Unlock */
idev->stats.tx_packets++;
mark_bh(NET_BH);
new_icr |= ICR_ETBREI;
- } else
- new_icr |= ICR_ETXTHI;
+ }
+
}
/* Check if transmission has completed */
if (isr & ISR_TXEMP_I) {
return dev;
}
+
+void unregister_hipdev(struct device *dev)
+{
+ int i;
+ rtnl_lock();
+ unregister_netdevice(dev);
+ for (i = 0; i < MAX_HIP_CARDS; ++i) {
+ if (hipdev_index[i] == dev) {
+ hipdev_index[i] = NULL;
+ break;
+ }
+ }
+ rtnl_unlock();
+}
+
+
static int hippi_neigh_setup_dev(struct device *dev, struct neigh_parms *p)
{
/* Never send broadcast/multicast ARP messages */
* This driver is for PCnet32 and PCnetPCI based ethercards
*/
-static const char *version = "pcnet32.c:v1.11 17.1.99 tsbogend@alpha.franken.de\n";
+static const char *version = "pcnet32.c:v1.21 31.3.99 tsbogend@alpha.franken.de\n";
#include <linux/config.h>
#include <linux/module.h>
#define PORT_PORTSEL 0x03
#define PORT_ASEL 0x04
+#define PORT_100 0x40
#define PORT_FD 0x80
-static int options = PORT_ASEL; /* port selection */
+
+/*
+ * table to translate option values from tulip
+ * to internal options
+ */
+static unsigned char options_mapping[] = {
+ PORT_ASEL, /* 0 Auto-select */
+ PORT_AUI, /* 1 BNC/AUI */
+ PORT_AUI, /* 2 AUI/BNC */
+ PORT_ASEL, /* 3 not supported */
+ PORT_10BT | PORT_FD, /* 4 10baseT-FD */
+ PORT_ASEL, /* 5 not supported */
+ PORT_ASEL, /* 6 not supported */
+ PORT_ASEL, /* 7 not supported */
+ PORT_ASEL, /* 8 not supported */
+ PORT_MII, /* 9 MII 10baseT */
+ PORT_MII | PORT_FD, /* 10 MII 10baseT-FD */
+ PORT_MII, /* 11 MII (autosel) */
+ PORT_10BT, /* 12 10BaseT */
+ PORT_MII | PORT_100, /* 13 MII 100BaseTx */
+ PORT_MII | PORT_100 | PORT_FD, /* 14 MII 100BaseTx-FD */
+ PORT_ASEL /* 15 not supported */
+};
+
+#define MAX_UNITS 8
+static int options[MAX_UNITS] = {0, };
+static int full_duplex[MAX_UNITS] = {0, };
/*
* Theory of Operation
* added port selection for modules
* detect special T1/E1 WAN card and setup port selection
* v1.11: fixed wrong checking of Tx errors
+ * v1.20: added check of return value kmalloc (cpeterso@cs.washington.edu)
+ * added save original kmalloc addr for freeing (mcr@solidum.com)
+ * added support for PCnetHome chip (joe@MIT.EDU)
+ * rewritten PCI card detection
+ * added dwio mode to get driver working on some PPC machines
+ * v1.21: added mii selection and mii ioctl
*/
#define PKT_BUF_SZ 1544
/* Offsets from base I/O address. */
-#define PCNET32_DATA 0x10
-#define PCNET32_ADDR 0x12
-#define PCNET32_RESET 0x14
-#define PCNET32_BUS_IF 0x16
-#define PCNET32_TOTAL_SIZE 0x18
+#define PCNET32_WIO_RDP 0x10
+#define PCNET32_WIO_RAP 0x12
+#define PCNET32_WIO_RESET 0x14
+#define PCNET32_WIO_BDP 0x16
+
+#define PCNET32_DWIO_RDP 0x10
+#define PCNET32_DWIO_RAP 0x14
+#define PCNET32_DWIO_RESET 0x18
+#define PCNET32_DWIO_BDP 0x1C
+
+#define PCNET32_TOTAL_SIZE 0x20
#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */
/* The PCNET32 Rx and Tx ring descriptors. */
struct pcnet32_rx_head {
- u32 base;
- s16 buf_length;
- s16 status;
- u32 msg_length;
- u32 reserved;
+ u32 base;
+ s16 buf_length;
+ s16 status;
+ u32 msg_length;
+ u32 reserved;
};
struct pcnet32_tx_head {
- u32 base;
- s16 length;
- s16 status;
- u32 misc;
- u32 reserved;
+ u32 base;
+ s16 length;
+ s16 status;
+ u32 misc;
+ u32 reserved;
};
-
/* The PCNET32 32-Bit initialization block, described in databook. */
struct pcnet32_init_block {
- u16 mode;
- u16 tlen_rlen;
- u8 phys_addr[6];
- u16 reserved;
- u32 filter[2];
- /* Receive and transmit ring base, along with extra bits. */
- u32 rx_ring;
- u32 tx_ring;
+ u16 mode;
+ u16 tlen_rlen;
+ u8 phys_addr[6];
+ u16 reserved;
+ u32 filter[2];
+ /* Receive and transmit ring base, along with extra bits. */
+ u32 rx_ring;
+ u32 tx_ring;
+};
+
+/* PCnet32 access functions */
+struct pcnet32_access {
+ u16 (*read_csr)(unsigned long, int);
+ void (*write_csr)(unsigned long, int, u16);
+ u16 (*read_bcr)(unsigned long, int);
+ void (*write_bcr)(unsigned long, int, u16);
+ u16 (*read_rap)(unsigned long);
+ void (*write_rap)(unsigned long, u16);
+ void (*reset)(unsigned long);
};
struct pcnet32_private {
- /* The Tx and Rx ring entries must be aligned on 16-byte boundaries in 32bit mode. */
- struct pcnet32_rx_head rx_ring[RX_RING_SIZE];
- struct pcnet32_tx_head tx_ring[TX_RING_SIZE];
- struct pcnet32_init_block init_block;
- const char *name;
- /* The saved address of a sent-in-place packet/buffer, for skfree(). */
- struct sk_buff *tx_skbuff[TX_RING_SIZE];
- struct sk_buff *rx_skbuff[RX_RING_SIZE];
- int cur_rx, cur_tx; /* The next free ring entry */
- int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
- struct net_device_stats stats;
- char tx_full;
- int options;
- int shared_irq:1, /* shared irq possible */
- full_duplex:1; /* full duplex possible */
+ /* The Tx and Rx ring entries must be aligned on 16-byte boundaries in 32bit mode. */
+ struct pcnet32_rx_head rx_ring[RX_RING_SIZE];
+ struct pcnet32_tx_head tx_ring[TX_RING_SIZE];
+ struct pcnet32_init_block init_block;
+ const char *name;
+ /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+ struct sk_buff *tx_skbuff[TX_RING_SIZE];
+ struct sk_buff *rx_skbuff[RX_RING_SIZE];
+ struct pcnet32_access a;
+ void *origmem;
+ int cur_rx, cur_tx; /* The next free ring entry */
+ int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
+ struct net_device_stats stats;
+ char tx_full;
+ int options;
+ int shared_irq:1, /* shared irq possible */
+ full_duplex:1, /* full duplex possible */
+ mii:1; /* mii port available */
#ifdef MODULE
- struct device *next;
+ struct device *next;
#endif
};
-int pcnet32_probe(struct device *dev);
-static int pcnet32_probe1(struct device *dev, unsigned int ioaddr, unsigned char irq_line, int shared);
-static int pcnet32_open(struct device *dev);
-static int pcnet32_init_ring(struct device *dev);
-static int pcnet32_start_xmit(struct sk_buff *skb, struct device *dev);
-static int pcnet32_rx(struct device *dev);
-static void pcnet32_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static int pcnet32_close(struct device *dev);
-static struct net_device_stats *pcnet32_get_stats(struct device *dev);
-static void pcnet32_set_multicast_list(struct device *dev);
+int pcnet32_probe(struct device *);
+static int pcnet32_probe1(struct device *, unsigned long, unsigned char, int, int);
+static int pcnet32_open(struct device *);
+static int pcnet32_init_ring(struct device *);
+static int pcnet32_start_xmit(struct sk_buff *, struct device *);
+static int pcnet32_rx(struct device *);
+static void pcnet32_interrupt(int, void *, struct pt_regs *);
+static int pcnet32_close(struct device *);
+static struct net_device_stats *pcnet32_get_stats(struct device *);
+static void pcnet32_set_multicast_list(struct device *);
+#ifdef HAVE_PRIVATE_IOCTL
+static int pcnet32_mii_ioctl(struct device *, struct ifreq *, int);
+#endif
+
+enum pci_flags_bit {
+ PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
+ PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,
+};
+
+struct pcnet32_pci_id_info {
+ const char *name;
+ u16 vendor_id, device_id, device_id_mask, flags;
+ int io_size;
+ int (*probe1) (struct device *, unsigned long, unsigned char, int, int);
+};
+
+static struct pcnet32_pci_id_info pcnet32_tbl[] = {
+ { "AMD PCnetPCI series",
+ 0x1022, 0x2000, 0xfffe, PCI_USES_IO|PCI_USES_MASTER, PCNET32_TOTAL_SIZE,
+ pcnet32_probe1},
+ {0,}
+};
+
+static u16 pcnet32_wio_read_csr (unsigned long addr, int index)
+{
+ outw (index, addr+PCNET32_WIO_RAP);
+ return inw (addr+PCNET32_WIO_RDP);
+}
+
+static void pcnet32_wio_write_csr (unsigned long addr, int index, u16 val)
+{
+ outw (index, addr+PCNET32_WIO_RAP);
+ outw (val, addr+PCNET32_WIO_RDP);
+}
+
+static u16 pcnet32_wio_read_bcr (unsigned long addr, int index)
+{
+ outw (index, addr+PCNET32_WIO_RAP);
+ return inw (addr+PCNET32_WIO_BDP);
+}
+
+static void pcnet32_wio_write_bcr (unsigned long addr, int index, u16 val)
+{
+ outw (index, addr+PCNET32_WIO_RAP);
+ outw (val, addr+PCNET32_WIO_BDP);
+}
+
+static u16 pcnet32_wio_read_rap (unsigned long addr)
+{
+ return inw (addr+PCNET32_WIO_RAP);
+}
+
+static void pcnet32_wio_write_rap (unsigned long addr, u16 val)
+{
+ outw (val, addr+PCNET32_WIO_RAP);
+}
+
+static void pcnet32_wio_reset (unsigned long addr)
+{
+ inw (addr+PCNET32_WIO_RESET);
+}
+
+static int pcnet32_wio_check (unsigned long addr)
+{
+ outw (88, addr+PCNET32_WIO_RAP);
+ return (inw (addr+PCNET32_WIO_RAP) == 88);
+}
+
+static struct pcnet32_access pcnet32_wio = {
+ pcnet32_wio_read_csr,
+ pcnet32_wio_write_csr,
+ pcnet32_wio_read_bcr,
+ pcnet32_wio_write_bcr,
+ pcnet32_wio_read_rap,
+ pcnet32_wio_write_rap,
+ pcnet32_wio_reset
+};
+
+static u16 pcnet32_dwio_read_csr (unsigned long addr, int index)
+{
+ outl (index, addr+PCNET32_DWIO_RAP);
+ return (inl (addr+PCNET32_DWIO_RDP) & 0xffff);
+}
+
+static void pcnet32_dwio_write_csr (unsigned long addr, int index, u16 val)
+{
+ outl (index, addr+PCNET32_DWIO_RAP);
+ outl (val, addr+PCNET32_DWIO_RDP);
+}
+
+static u16 pcnet32_dwio_read_bcr (unsigned long addr, int index)
+{
+ outl (index, addr+PCNET32_DWIO_RAP);
+ return (inl (addr+PCNET32_DWIO_BDP) & 0xffff);
+}
+
+static void pcnet32_dwio_write_bcr (unsigned long addr, int index, u16 val)
+{
+ outl (index, addr+PCNET32_DWIO_RAP);
+ outl (val, addr+PCNET32_DWIO_BDP);
+}
+
+static u16 pcnet32_dwio_read_rap (unsigned long addr)
+{
+ return (inl (addr+PCNET32_DWIO_RAP) & 0xffff);
+}
+
+static void pcnet32_dwio_write_rap (unsigned long addr, u16 val)
+{
+ outl (val, addr+PCNET32_DWIO_RAP);
+}
+
+static void pcnet32_dwio_reset (unsigned long addr)
+{
+ inl (addr+PCNET32_DWIO_RESET);
+}
+
+static int pcnet32_dwio_check (unsigned long addr)
+{
+ outl (88, addr+PCNET32_DWIO_RAP);
+ return (inl (addr+PCNET32_DWIO_RAP) == 88);
+}
+
+static struct pcnet32_access pcnet32_dwio = {
+ pcnet32_dwio_read_csr,
+ pcnet32_dwio_write_csr,
+ pcnet32_dwio_read_bcr,
+ pcnet32_dwio_write_bcr,
+ pcnet32_dwio_read_rap,
+ pcnet32_dwio_write_rap,
+ pcnet32_dwio_reset
+
+};
\f
-__initfunc(int pcnet32_probe (struct device *dev))
+int __init pcnet32_probe (struct device *dev)
{
- unsigned int ioaddr = dev ? dev->base_addr: 0;
+ unsigned long ioaddr = dev ? dev->base_addr: 0;
unsigned int irq_line = dev ? dev->irq : 0;
int *port;
+ int cards_found = 0;
- if (ioaddr > 0x1ff)
- return pcnet32_probe1(dev, ioaddr, irq_line, 0);
- else if(ioaddr != 0)
- return ENXIO;
+
+#ifndef __powerpc__
+ if (ioaddr > 0x1ff) {
+ if (check_region(ioaddr, PCNET32_TOTAL_SIZE) == 0)
+ return pcnet32_probe1(dev, ioaddr, irq_line, 0, 0);
+ else
+ return ENODEV;
+ } else
+#endif
+ if(ioaddr != 0)
+ return ENXIO;
#if defined(CONFIG_PCI)
if (pci_present()) {
- struct pci_dev *pdev = NULL;
+ struct pci_dev *pdev;
+ unsigned char pci_bus, pci_device_fn;
+ int pci_index;
printk("pcnet32.c: PCI bios is present, checking for devices...\n");
- while ((pdev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, pdev))) {
- unsigned short pci_command;
+ for (pci_index = 0; pci_index < 0xff; pci_index++) {
+ u16 vendor, device, pci_command;
+ int chip_idx;
- irq_line = pdev->irq;
+ if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8,
+ pci_index, &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL)
+ break;
+
+ pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, &vendor);
+ pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &device);
+
+ for (chip_idx = 0; pcnet32_tbl[chip_idx].vendor_id; chip_idx++)
+ if (vendor == pcnet32_tbl[chip_idx].vendor_id &&
+ (device & pcnet32_tbl[chip_idx].device_id_mask) == pcnet32_tbl[chip_idx].device_id)
+ break;
+ if (pcnet32_tbl[chip_idx].vendor_id == 0)
+ continue;
+
+ pdev = pci_find_slot(pci_bus, pci_device_fn);
ioaddr = pdev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
+#if defined(ADDR_64BITS) && defined(__alpha__)
+ ioaddr |= ((long)pdev->base_address[1]) << 32;
+#endif
+ irq_line = pdev->irq;
+
+ /* Avoid already found cards from previous pcnet32_probe() calls */
+ if ((pcnet32_tbl[chip_idx].flags & PCI_USES_IO) &&
+ check_region(ioaddr, pcnet32_tbl[chip_idx].io_size))
+ continue;
+
/* PCI Spec 2.1 states that it is either the driver or PCI card's
* responsibility to set the PCI Master Enable Bit if needed.
* (From Mark Stockton <marks@schooner.sys.hou.compaq.com>)
*/
pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
-
- /* Avoid already found cards from previous pcnet32_probe() calls */
- if (check_region(ioaddr, PCNET32_TOTAL_SIZE))
- continue;
-
if ( ! (pci_command & PCI_COMMAND_MASTER)) {
printk("PCI Master Bit has not been set. Setting...\n");
pci_command |= PCI_COMMAND_MASTER|PCI_COMMAND_IO;
pci_write_config_word(pdev, PCI_COMMAND, pci_command);
}
+ printk("Found PCnet/PCI at %#lx, irq %d.\n", ioaddr, irq_line);
- printk("Found PCnet/PCI at %#x, irq %d.\n",
- ioaddr, irq_line);
-
- if (pcnet32_probe1(dev, ioaddr, irq_line, 1) != 0) { /* Shouldn't happen. */
- printk(KERN_ERR "pcnet32.c: Probe of PCI card at %#x failed.\n", ioaddr);
- break;
+ if (pcnet32_tbl[chip_idx].probe1(dev, ioaddr, irq_line, 1, cards_found) == 0) {
+ cards_found++;
+ dev = NULL;
}
- return 0;
}
} else
#endif /* defined(CONFIG_PCI) */
/* now look for PCnet32 VLB cards */
for (port = pcnet32_portlist; *port; port++) {
- unsigned int ioaddr = *port;
+ unsigned long ioaddr = *port;
if ( check_region(ioaddr, PCNET32_TOTAL_SIZE) == 0) {
/* check if there is really a pcnet chip on that ioaddr */
if ((inb(ioaddr + 14) == 0x57) &&
(inb(ioaddr + 15) == 0x57) &&
- (pcnet32_probe1(dev, ioaddr, 0, 0) == 0))
- return 0;
+ (pcnet32_probe1(dev, ioaddr, 0, 0, 0) == 0))
+ cards_found++;
}
}
- return ENODEV;
+ return cards_found ? 0: ENODEV;
}
/* pcnet32_probe1 */
-__initfunc(static int pcnet32_probe1(struct device *dev, unsigned int ioaddr, unsigned char irq_line, int shared))
+static int __init
+pcnet32_probe1(struct device *dev, unsigned long ioaddr, unsigned char irq_line, int shared, int card_idx)
{
struct pcnet32_private *lp;
- int i,full_duplex = 0;
+ int i,media,fdx = 0, mii = 0;
+ int chip_version;
char *chipname;
+ char *priv;
+ struct pcnet32_access *a;
+
+ /* reset the chip */
+ pcnet32_dwio_reset(ioaddr);
+ pcnet32_wio_reset(ioaddr);
+
+ if (pcnet32_wio_read_csr (ioaddr, 0) == 4 && pcnet32_wio_check (ioaddr)) {
+ a = &pcnet32_wio;
+ } else {
+ if (pcnet32_dwio_read_csr (ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) {
+ a = &pcnet32_dwio;
+ } else
+ return ENODEV;
+ }
- inw(ioaddr+PCNET32_RESET); /* Reset the PCNET32 */
-
- outw(0x0000, ioaddr+PCNET32_ADDR); /* Switch to window 0 */
- if (inw(ioaddr+PCNET32_DATA) != 0x0004)
- return ENODEV;
-
- /* Get the version of the chip. */
- outw(88, ioaddr+PCNET32_ADDR);
- if (inw(ioaddr+PCNET32_ADDR) != 88) {
- /* should never happen */
+ chip_version = a->read_csr (ioaddr, 88) | (a->read_csr (ioaddr,89) << 16);
+ if (pcnet32_debug > 2)
+ printk(" PCnet chip version is %#x.\n", chip_version);
+ if ((chip_version & 0xfff) != 0x003)
return ENODEV;
- } else { /* Good, it's a newer chip. */
- int chip_version = inw(ioaddr+PCNET32_DATA);
- outw(89, ioaddr+PCNET32_ADDR);
- chip_version |= inw(ioaddr+PCNET32_DATA) << 16;
+ chip_version = (chip_version >> 12) & 0xffff;
+ switch (chip_version) {
+ case 0x2420:
+ chipname = "PCnet/PCI 79C970";
+ break;
+ case 0x2430:
+ if (shared)
+ chipname = "PCnet/PCI 79C970"; /* 970 gives the wrong chip id back */
+ else
+ chipname = "PCnet/32 79C965";
+ break;
+ case 0x2621:
+ chipname = "PCnet/PCI II 79C970A";
+ fdx = 1;
+ break;
+ case 0x2623:
+ chipname = "PCnet/FAST 79C971";
+ fdx = 1; mii = 1;
+ break;
+ case 0x2624:
+ chipname = "PCnet/FAST+ 79C972";
+ fdx = 1; mii = 1;
+ break;
+ case 0x2626:
+ chipname = "PCnet/Home 79C978";
+ fdx = 1;
+ /*
+ * This is based on specs published at www.amd.com. This section
+ * assumes that a card with a 79C978 wants to go into 1Mb HomePNA
+ * mode. The 79C978 can also go into standard ethernet, and there
+ * probably should be some sort of module option to select the
+ * mode by which the card should operate
+ */
+ /* switch to home wiring mode */
+ media = a->read_bcr (ioaddr, 49);
if (pcnet32_debug > 2)
- printk(" PCnet chip version is %#x.\n", chip_version);
- if ((chip_version & 0xfff) != 0x003)
- return ENODEV;
- chip_version = (chip_version >> 12) & 0xffff;
- switch (chip_version) {
- case 0x2420:
- chipname = "PCnet/PCI 79C970";
- break;
- case 0x2430:
- if (shared)
- chipname = "PCnet/PCI 79C970"; /* 970 gives the wrong chip id back */
- else
- chipname = "PCnet/32 79C965";
- break;
- case 0x2621:
- chipname = "PCnet/PCI II 79C970A";
- full_duplex = 1;
- break;
- case 0x2623:
- chipname = "PCnet/FAST 79C971";
- full_duplex = 1;
- break;
- case 0x2624:
- chipname = "PCnet/FAST+ 79C972";
- full_duplex = 1;
- break;
- default:
- printk("pcnet32: PCnet version %#x, no PCnet32 chip.\n",chip_version);
- return ENODEV;
- }
+ printk("pcnet32: pcnet32 media value %#x.\n", media);
+ media &= ~3;
+ media |= 1;
+ if (pcnet32_debug > 2)
+ printk("pcnet32: pcnet32 media reset to %#x.\n", media);
+ a->write_bcr (ioaddr, 49, media);
+ break;
+ default:
+ printk("pcnet32: PCnet version %#x, no PCnet32 chip.\n",chip_version);
+ return ENODEV;
}
- if (dev == NULL)
- dev = init_etherdev(0, 0);
+ dev = init_etherdev(dev, 0);
- printk("%s: %s at %#3x,", dev->name, chipname, ioaddr);
+ printk(KERN_INFO "%s: %s at %#3lx,", dev->name, chipname, ioaddr);
/* There is a 16 byte station address PROM at the base address.
The first six bytes are the station address. */
dev->base_addr = ioaddr;
request_region(ioaddr, PCNET32_TOTAL_SIZE, chipname);
- /* Make certain the data structures used by the PCnet32 are 16byte aligned and DMAble. */
- lp = (struct pcnet32_private *) (((unsigned long)kmalloc(sizeof(*lp)+15, GFP_DMA | GFP_KERNEL)+15) & ~15);
+ if ((priv = kmalloc(sizeof(*lp)+15,GFP_KERNEL)) == NULL)
+ return ENOMEM;
+
+ /*
+ * Make certain the data structures used by
+ * the PCnet32 are 16byte aligned
+ */
+ lp = (struct pcnet32_private *)(((unsigned long)priv+15) & ~15);
memset(lp, 0, sizeof(*lp));
dev->priv = lp;
lp->name = chipname;
lp->shared_irq = shared;
- lp->full_duplex = full_duplex;
- lp->options = options;
+ lp->full_duplex = fdx;
+ lp->mii = mii;
+ if (options[card_idx] > sizeof (options_mapping))
+ lp->options = PORT_ASEL;
+ else
+ lp->options = options_mapping[options[card_idx]];
+
+ if (fdx && !(lp->options & PORT_ASEL) && full_duplex[card_idx])
+ lp->options |= PORT_FD;
+
+ lp->origmem = priv;
+ lp->a = *a;
/* detect special T1/E1 WAN card by checking for MAC address */
if (dev->dev_addr[0] == 0x00 && dev->dev_addr[1] == 0xe0 && dev->dev_addr[2] == 0x75)
lp->init_block.tx_ring = (u32)le32_to_cpu(virt_to_bus(lp->tx_ring));
/* switch pcnet32 to 32bit mode */
- outw(0x0014, ioaddr+PCNET32_ADDR);
- outw(0x0002, ioaddr+PCNET32_BUS_IF);
-
- outw(0x0001, ioaddr+PCNET32_ADDR);
- inw(ioaddr+PCNET32_ADDR);
- outw(virt_to_bus(&lp->init_block) & 0xffff, ioaddr+PCNET32_DATA);
- outw(0x0002, ioaddr+PCNET32_ADDR);
- inw(ioaddr+PCNET32_ADDR);
- outw(virt_to_bus(&lp->init_block) >> 16, ioaddr+PCNET32_DATA);
- outw(0x0000, ioaddr+PCNET32_ADDR);
- inw(ioaddr+PCNET32_ADDR);
+ a->write_bcr (ioaddr, 20, 2);
+
+ a->write_csr (ioaddr, 1, virt_to_bus(&lp->init_block) & 0xffff);
+ a->write_csr (ioaddr, 2, virt_to_bus(&lp->init_block) >> 16);
if (irq_line) {
dev->irq = irq_line;
}
if (dev->irq >= 2)
- printk(" assigned IRQ %d.\n", dev->irq);
+ printk(" assigned IRQ %d.\n", dev->irq);
else {
unsigned long irq_mask = probe_irq_on();
* boards will work.
*/
/* Trigger an initialization just for the interrupt. */
- outw(0x0041, ioaddr+PCNET32_DATA);
+ a->write_csr (ioaddr, 0, 0x41);
mdelay (1);
dev->irq = probe_irq_off (irq_mask);
}
if (pcnet32_debug > 0)
- printk(version);
+ printk(version);
/* The PCNET32-specific entries in the device structure. */
dev->open = &pcnet32_open;
dev->stop = &pcnet32_close;
dev->get_stats = &pcnet32_get_stats;
dev->set_multicast_list = &pcnet32_set_multicast_list;
+#ifdef HAVE_PRIVATE_IOCTL
+ dev->do_ioctl = &pcnet32_mii_ioctl;
+#endif
+
#ifdef MODULE
lp->next = pcnet32_dev;
static int
pcnet32_open(struct device *dev)
{
- struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
- unsigned int ioaddr = dev->base_addr;
- unsigned short val;
- int i;
-
- if (dev->irq == 0 ||
- request_irq(dev->irq, &pcnet32_interrupt,
- lp->shared_irq ? SA_SHIRQ : 0, lp->name, (void *)dev)) {
- return -EAGAIN;
- }
-
- /* Reset the PCNET32 */
- inw(ioaddr+PCNET32_RESET);
+ struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+ u16 val;
+ int i;
+
+ if (dev->irq == 0 ||
+ request_irq(dev->irq, &pcnet32_interrupt,
+ lp->shared_irq ? SA_SHIRQ : 0, lp->name, (void *)dev)) {
+ return -EAGAIN;
+ }
- /* switch pcnet32 to 32bit mode */
- outw(0x0014, ioaddr+PCNET32_ADDR);
- outw(0x0002, ioaddr+PCNET32_BUS_IF);
+ /* Reset the PCNET32 */
+ lp->a.reset (ioaddr);
- if (pcnet32_debug > 1)
- printk("%s: pcnet32_open() irq %d tx/rx rings %#x/%#x init %#x.\n",
- dev->name, dev->irq,
- (u32) virt_to_bus(lp->tx_ring),
- (u32) virt_to_bus(lp->rx_ring),
- (u32) virt_to_bus(&lp->init_block));
+ /* switch pcnet32 to 32bit mode */
+ lp->a.write_csr (ioaddr, 20, 2);
+
+ if (pcnet32_debug > 1)
+ printk("%s: pcnet32_open() irq %d tx/rx rings %#x/%#x init %#x.\n",
+ dev->name, dev->irq,
+ (u32) virt_to_bus(lp->tx_ring),
+ (u32) virt_to_bus(lp->rx_ring),
+ (u32) virt_to_bus(&lp->init_block));
- /* set/reset autoselect bit */
- outw(0x0002, ioaddr+PCNET32_ADDR);
- val = inw(ioaddr+PCNET32_BUS_IF) & ~2;
- if (lp->options & PORT_ASEL)
- val |= 2;
- outw(val, ioaddr+PCNET32_BUS_IF);
-
- /* handle full duplex setting */
- if (lp->full_duplex) {
- outw (0x0009, ioaddr+PCNET32_ADDR);
- val = inw(ioaddr+PCNET32_BUS_IF) & ~3;
- if (lp->options & PORT_FD) {
- val |= 1;
- if (lp->options == (PORT_FD | PORT_AUI))
- val |= 2;
- }
- outw(val, ioaddr+PCNET32_BUS_IF);
+ /* set/reset autoselect bit */
+ val = lp->a.read_bcr (ioaddr, 2) & ~2;
+ if (lp->options & PORT_ASEL)
+ val |= 2;
+ lp->a.write_bcr (ioaddr, 2, val);
+
+ /* handle full duplex setting */
+ if (lp->full_duplex) {
+ val = lp->a.read_bcr (ioaddr, 9) & ~3;
+ if (lp->options & PORT_FD) {
+ val |= 1;
+ if (lp->options == (PORT_FD | PORT_AUI))
+ val |= 2;
}
+ lp->a.write_bcr (ioaddr, 9, val);
+ }
- /* set/reset GPSI bit in test register */
- outw (0x007c, ioaddr+PCNET32_ADDR);
- val = inw(ioaddr+PCNET32_DATA) & ~0x10;
- if ((lp->options & PORT_PORTSEL) == PORT_GPSI)
- val |= 0x10;
- outw(val, ioaddr+PCNET32_DATA);
-
- lp->init_block.mode = le16_to_cpu((lp->options & PORT_PORTSEL) << 7);
- lp->init_block.filter[0] = 0x00000000;
- lp->init_block.filter[1] = 0x00000000;
- if (pcnet32_init_ring(dev))
- return -ENOMEM;
+ /* set/reset GPSI bit in test register */
+ val = lp->a.read_csr (ioaddr, 124) & ~0x10;
+ if ((lp->options & PORT_PORTSEL) == PORT_GPSI)
+ val |= 0x10;
+ lp->a.write_csr (ioaddr, 124, val);
- /* Re-initialize the PCNET32, and start it when done. */
- outw(0x0001, ioaddr+PCNET32_ADDR);
- outw(virt_to_bus(&lp->init_block) &0xffff, ioaddr+PCNET32_DATA);
- outw(0x0002, ioaddr+PCNET32_ADDR);
- outw(virt_to_bus(&lp->init_block) >> 16, ioaddr+PCNET32_DATA);
-
- outw(0x0004, ioaddr+PCNET32_ADDR);
- outw(0x0915, ioaddr+PCNET32_DATA);
-
- outw(0x0000, ioaddr+PCNET32_ADDR);
- outw(0x0001, ioaddr+PCNET32_DATA);
-
- dev->tbusy = 0;
- dev->interrupt = 0;
- dev->start = 1;
- i = 0;
- while (i++ < 100)
- if (inw(ioaddr+PCNET32_DATA) & 0x0100)
- break;
- /*
- * We used to clear the InitDone bit, 0x0100, here but Mark Stockton
- * reports that doing so triggers a bug in the '974.
- */
- outw(0x0042, ioaddr+PCNET32_DATA);
-
- if (pcnet32_debug > 2)
- printk("%s: PCNET32 open after %d ticks, init block %#x csr0 %4.4x.\n",
- dev->name, i, (u32) virt_to_bus(&lp->init_block), inw(ioaddr+PCNET32_DATA));
-
- MOD_INC_USE_COUNT;
+ if (lp->mii & (lp->options & PORT_ASEL)) {
+ val = lp->a.read_bcr (ioaddr, 32) & ~0x38; /* disable Auto Negotiation, set 10Mpbs, HD */
+ if (lp->options & PORT_FD)
+ val |= 0x10;
+ if (lp->options & PORT_100)
+ val |= 0x08;
+ lp->a.write_bcr (ioaddr, 32, val);
+ }
+
+ lp->init_block.mode = le16_to_cpu((lp->options & PORT_PORTSEL) << 7);
+ lp->init_block.filter[0] = 0x00000000;
+ lp->init_block.filter[1] = 0x00000000;
+ if (pcnet32_init_ring(dev))
+ return -ENOMEM;
+
+ /* Re-initialize the PCNET32, and start it when done. */
+ lp->a.write_csr (ioaddr, 1, virt_to_bus(&lp->init_block) &0xffff);
+ lp->a.write_csr (ioaddr, 2, virt_to_bus(&lp->init_block) >> 16);
+
+ lp->a.write_csr (ioaddr, 4, 0x0915);
+ lp->a.write_csr (ioaddr, 0, 0x0001);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+ i = 0;
+ while (i++ < 100)
+ if (lp->a.read_csr (ioaddr, 0) & 0x0100)
+ break;
+ /*
+ * We used to clear the InitDone bit, 0x0100, here but Mark Stockton
+ * reports that doing so triggers a bug in the '974.
+ */
+ lp->a.write_csr (ioaddr, 0, 0x0042);
+
+ if (pcnet32_debug > 2)
+ printk("%s: PCNET32 open after %d ticks, init block %#x csr0 %4.4x.\n",
+ dev->name, i, (u32) virt_to_bus(&lp->init_block),
+ lp->a.read_csr (ioaddr, 0));
+
+ MOD_INC_USE_COUNT;
- return 0; /* Always succeed */
+ return 0; /* Always succeed */
}
/*
static void
pcnet32_purge_tx_ring(struct device *dev)
{
- struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
- int i;
+ struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
+ int i;
- for (i = 0; i < TX_RING_SIZE; i++) {
- if (lp->tx_skbuff[i]) {
- dev_kfree_skb(lp->tx_skbuff[i]);
- lp->tx_skbuff[i] = NULL;
- }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ if (lp->tx_skbuff[i]) {
+ dev_kfree_skb(lp->tx_skbuff[i]);
+ lp->tx_skbuff[i] = NULL;
}
+ }
}
static int
pcnet32_init_ring(struct device *dev)
{
- struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
- int i;
-
- lp->tx_full = 0;
- lp->cur_rx = lp->cur_tx = 0;
- lp->dirty_rx = lp->dirty_tx = 0;
-
- for (i = 0; i < RX_RING_SIZE; i++) {
- if (lp->rx_skbuff[i] == NULL) {
- if (!(lp->rx_skbuff[i] = dev_alloc_skb (PKT_BUF_SZ))) {
- /* there is not much, we can do at this point */
- printk ("%s: pcnet32_init_ring dev_alloc_skb failed.\n",dev->name);
- return -1;
- }
- skb_reserve (lp->rx_skbuff[i], 2);
+ struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
+ int i;
+
+ lp->tx_full = 0;
+ lp->cur_rx = lp->cur_tx = 0;
+ lp->dirty_rx = lp->dirty_tx = 0;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ if (lp->rx_skbuff[i] == NULL) {
+ if (!(lp->rx_skbuff[i] = dev_alloc_skb (PKT_BUF_SZ))) {
+ /* there is not much, we can do at this point */
+ printk ("%s: pcnet32_init_ring dev_alloc_skb failed.\n",dev->name);
+ return -1;
}
- lp->rx_ring[i].base = (u32)le32_to_cpu(virt_to_bus(lp->rx_skbuff[i]->tail));
- lp->rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ);
- lp->rx_ring[i].status = le16_to_cpu(0x8000);
- }
- /* The Tx buffer address is filled in as needed, but we do need to clear
- the upper ownership bit. */
- for (i = 0; i < TX_RING_SIZE; i++) {
- lp->tx_ring[i].base = 0;
- lp->tx_ring[i].status = 0;
+ skb_reserve (lp->rx_skbuff[i], 2);
}
+ lp->rx_ring[i].base = (u32)le32_to_cpu(virt_to_bus(lp->rx_skbuff[i]->tail));
+ lp->rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ);
+ lp->rx_ring[i].status = le16_to_cpu(0x8000);
+ }
+ /* The Tx buffer address is filled in as needed, but we do need to clear
+ the upper ownership bit. */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ lp->tx_ring[i].base = 0;
+ lp->tx_ring[i].status = 0;
+ }
- lp->init_block.tlen_rlen = TX_RING_LEN_BITS | RX_RING_LEN_BITS;
- for (i = 0; i < 6; i++)
- lp->init_block.phys_addr[i] = dev->dev_addr[i];
- lp->init_block.rx_ring = (u32)le32_to_cpu(virt_to_bus(lp->rx_ring));
- lp->init_block.tx_ring = (u32)le32_to_cpu(virt_to_bus(lp->tx_ring));
- return 0;
+ lp->init_block.tlen_rlen = TX_RING_LEN_BITS | RX_RING_LEN_BITS;
+ for (i = 0; i < 6; i++)
+ lp->init_block.phys_addr[i] = dev->dev_addr[i];
+ lp->init_block.rx_ring = (u32)le32_to_cpu(virt_to_bus(lp->rx_ring));
+ lp->init_block.tx_ring = (u32)le32_to_cpu(virt_to_bus(lp->tx_ring));
+ return 0;
}
static void
pcnet32_restart(struct device *dev, unsigned int csr0_bits)
{
- int i;
- unsigned int ioaddr = dev->base_addr;
+ struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+ int i;
- pcnet32_purge_tx_ring(dev);
- if (pcnet32_init_ring(dev))
- return;
+ pcnet32_purge_tx_ring(dev);
+ if (pcnet32_init_ring(dev))
+ return;
- outw(0x0000, ioaddr + PCNET32_ADDR);
- /* ReInit Ring */
- outw(0x0001, ioaddr + PCNET32_DATA);
- i = 0;
- while (i++ < 100)
- if (inw(ioaddr+PCNET32_DATA) & 0x0100)
- break;
-
- outw(csr0_bits, ioaddr + PCNET32_DATA);
+ /* ReInit Ring */
+ lp->a.write_csr (ioaddr, 0, 1);
+ i = 0;
+ while (i++ < 100)
+ if (lp->a.read_csr (ioaddr, 0) & 0x0100)
+ break;
+
+ lp->a.write_csr (ioaddr, 0, csr0_bits);
}
static int
pcnet32_start_xmit(struct sk_buff *skb, struct device *dev)
{
- struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
- unsigned int ioaddr = dev->base_addr;
- int entry;
- unsigned long flags;
-
- /* Transmitter timeout, serious problems. */
- if (dev->tbusy) {
- int tickssofar = jiffies - dev->trans_start;
- if (tickssofar < HZ/2)
- return 1;
- outw(0, ioaddr+PCNET32_ADDR);
- printk("%s: transmit timed out, status %4.4x, resetting.\n",
- dev->name, inw(ioaddr+PCNET32_DATA));
- outw(0x0004, ioaddr+PCNET32_DATA);
- lp->stats.tx_errors++;
- if (pcnet32_debug > 2) {
- int i;
- printk(" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",
- lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "",
- lp->cur_rx);
- for (i = 0 ; i < RX_RING_SIZE; i++)
- printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",
- lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,
- lp->rx_ring[i].msg_length, (unsigned)lp->rx_ring[i].status);
- for (i = 0 ; i < TX_RING_SIZE; i++)
- printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",
- lp->tx_ring[i].base, -lp->tx_ring[i].length,
- lp->tx_ring[i].misc, (unsigned)lp->tx_ring[i].status);
- printk("\n");
- }
- pcnet32_restart(dev, 0x0042);
-
- dev->tbusy = 0;
- dev->trans_start = jiffies;
- dev_kfree_skb(skb);
- return 0;
+ struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
+ unsigned int ioaddr = dev->base_addr;
+ int entry;
+ unsigned long flags;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < HZ/2)
+ return 1;
+ printk("%s: transmit timed out, status %4.4x, resetting.\n",
+ dev->name, lp->a.read_csr (ioaddr, 0));
+ lp->a.write_csr (ioaddr, 0, 0x0004);
+ lp->stats.tx_errors++;
+ if (pcnet32_debug > 2) {
+ int i;
+ printk(" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",
+ lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "",
+ lp->cur_rx);
+ for (i = 0 ; i < RX_RING_SIZE; i++)
+ printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",
+ lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,
+ lp->rx_ring[i].msg_length, (unsigned)lp->rx_ring[i].status);
+ for (i = 0 ; i < TX_RING_SIZE; i++)
+ printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",
+ lp->tx_ring[i].base, -lp->tx_ring[i].length,
+ lp->tx_ring[i].misc, (unsigned)lp->tx_ring[i].status);
+ printk("\n");
}
+ pcnet32_restart(dev, 0x0042);
- if (pcnet32_debug > 3) {
- outw(0x0000, ioaddr+PCNET32_ADDR);
- printk("%s: pcnet32_start_xmit() called, csr0 %4.4x.\n", dev->name,
- inw(ioaddr+PCNET32_DATA));
- }
+ dev->tbusy = 0;
+ dev->trans_start = jiffies;
+ dev_kfree_skb(skb);
+ return 0;
+ }
- /* Block a timer-based transmit from overlapping. This could better be
- done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
- if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
- printk("%s: Transmitter access conflict.\n", dev->name);
- return 1;
- }
+ if (pcnet32_debug > 3) {
+ printk("%s: pcnet32_start_xmit() called, csr0 %4.4x.\n",
+ dev->name, lp->a.read_csr (ioaddr, 0));
+ }
- save_flags (flags);
- cli ();
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ return 1;
+ }
- /* Fill in a Tx ring entry */
+ save_flags (flags);
+ cli ();
- /* Mask to ring buffer boundary. */
- entry = lp->cur_tx & TX_RING_MOD_MASK;
+ /* Fill in a Tx ring entry */
- /* Caution: the write order is important here, set the base address
- with the "ownership" bits last. */
+ /* Mask to ring buffer boundary. */
+ entry = lp->cur_tx & TX_RING_MOD_MASK;
- lp->tx_ring[entry].length = le16_to_cpu(-skb->len);
+ /* Caution: the write order is important here, set the base address
+ with the "ownership" bits last. */
- lp->tx_ring[entry].misc = 0x00000000;
+ lp->tx_ring[entry].length = le16_to_cpu(-skb->len);
- lp->tx_skbuff[entry] = skb;
- lp->tx_ring[entry].base = (u32)le32_to_cpu(virt_to_bus(skb->data));
- lp->tx_ring[entry].status = le16_to_cpu(0x8300);
+ lp->tx_ring[entry].misc = 0x00000000;
- lp->cur_tx++;
- lp->stats.tx_bytes += skb->len;
+ lp->tx_skbuff[entry] = skb;
+ lp->tx_ring[entry].base = (u32)le32_to_cpu(virt_to_bus(skb->data));
+ lp->tx_ring[entry].status = le16_to_cpu(0x8300);
- /* Trigger an immediate send poll. */
- outw(0x0000, ioaddr+PCNET32_ADDR);
- outw(0x0048, ioaddr+PCNET32_DATA);
+ lp->cur_tx++;
+ lp->stats.tx_bytes += skb->len;
- dev->trans_start = jiffies;
+ /* Trigger an immediate send poll. */
+ lp->a.write_csr (ioaddr, 0, 0x0048);
- if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)
- clear_bit (0, (void *)&dev->tbusy);
- else
- lp->tx_full = 1;
- restore_flags(flags);
- return 0;
+ dev->trans_start = jiffies;
+
+ if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)
+ clear_bit (0, (void *)&dev->tbusy);
+ else
+ lp->tx_full = 1;
+ restore_flags(flags);
+ return 0;
}
/* The PCNET32 interrupt handler. */
static void
pcnet32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
- struct device *dev = (struct device *)dev_id;
- struct pcnet32_private *lp;
- unsigned int csr0, ioaddr;
- int boguscnt = max_interrupt_work;
- int must_restart;
-
- if (dev == NULL) {
- printk ("pcnet32_interrupt(): irq %d for unknown device.\n", irq);
- return;
- }
+ struct device *dev = (struct device *)dev_id;
+ struct pcnet32_private *lp;
+ unsigned long ioaddr;
+ u16 csr0;
+ int boguscnt = max_interrupt_work;
+ int must_restart;
- ioaddr = dev->base_addr;
- lp = (struct pcnet32_private *)dev->priv;
- if (dev->interrupt)
- printk("%s: Re-entering the interrupt handler.\n", dev->name);
+ if (dev == NULL) {
+ printk ("pcnet32_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
- dev->interrupt = 1;
+ ioaddr = dev->base_addr;
+ lp = (struct pcnet32_private *)dev->priv;
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
- outw(0x00, dev->base_addr + PCNET32_ADDR);
- while ((csr0 = inw(dev->base_addr + PCNET32_DATA)) & 0x8600 && --boguscnt >= 0) {
- /* Acknowledge all of the current interrupt sources ASAP. */
- outw(csr0 & ~0x004f, dev->base_addr + PCNET32_DATA);
+ dev->interrupt = 1;
- must_restart = 0;
+ while ((csr0 = lp->a.read_csr (ioaddr, 0)) & 0x8600 && --boguscnt >= 0) {
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ lp->a.write_csr (ioaddr, 0, csr0 & ~0x004f);
- if (pcnet32_debug > 5)
- printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n",
- dev->name, csr0, inw(dev->base_addr + PCNET32_DATA));
+ must_restart = 0;
- if (csr0 & 0x0400) /* Rx interrupt */
- pcnet32_rx(dev);
+ if (pcnet32_debug > 5)
+ printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n",
+ dev->name, csr0, lp->a.read_csr (ioaddr, 0));
- if (csr0 & 0x0200) { /* Tx-done interrupt */
- int dirty_tx = lp->dirty_tx;
+ if (csr0 & 0x0400) /* Rx interrupt */
+ pcnet32_rx(dev);
- while (dirty_tx < lp->cur_tx) {
- int entry = dirty_tx & TX_RING_MOD_MASK;
- int status = (short)le16_to_cpu(lp->tx_ring[entry].status);
+ if (csr0 & 0x0200) { /* Tx-done interrupt */
+ int dirty_tx = lp->dirty_tx;
+
+ while (dirty_tx < lp->cur_tx) {
+ int entry = dirty_tx & TX_RING_MOD_MASK;
+ int status = (short)le16_to_cpu(lp->tx_ring[entry].status);
- if (status < 0)
- break; /* It still hasn't been Txed */
-
- lp->tx_ring[entry].base = 0;
-
- if (status & 0x4000) {
- /* There was an major error, log it. */
- int err_status = le32_to_cpu(lp->tx_ring[entry].misc);
- lp->stats.tx_errors++;
- if (err_status & 0x04000000) lp->stats.tx_aborted_errors++;
- if (err_status & 0x08000000) lp->stats.tx_carrier_errors++;
- if (err_status & 0x10000000) lp->stats.tx_window_errors++;
- if (err_status & 0x40000000) {
- /* Ackk! On FIFO errors the Tx unit is turned off! */
- lp->stats.tx_fifo_errors++;
- /* Remove this verbosity later! */
- printk("%s: Tx FIFO error! Status %4.4x.\n",
- dev->name, csr0);
- must_restart = 1;
+ if (status < 0)
+ break; /* It still hasn't been Txed */
+
+ lp->tx_ring[entry].base = 0;
+
+ if (status & 0x4000) {
+ /* There was an major error, log it. */
+ int err_status = le32_to_cpu(lp->tx_ring[entry].misc);
+ lp->stats.tx_errors++;
+ if (err_status & 0x04000000) lp->stats.tx_aborted_errors++;
+ if (err_status & 0x08000000) lp->stats.tx_carrier_errors++;
+ if (err_status & 0x10000000) lp->stats.tx_window_errors++;
+ if (err_status & 0x40000000) {
+ /* Ackk! On FIFO errors the Tx unit is turned off! */
+ lp->stats.tx_fifo_errors++;
+ /* Remove this verbosity later! */
+ printk("%s: Tx FIFO error! Status %4.4x.\n",
+ dev->name, csr0);
+ must_restart = 1;
}
- } else {
- if (status & 0x1800)
- lp->stats.collisions++;
- lp->stats.tx_packets++;
- }
-
- /* We must free the original skb */
- if (lp->tx_skbuff[entry]) {
- dev_kfree_skb(lp->tx_skbuff[entry]);
- lp->tx_skbuff[entry] = 0;
- }
- dirty_tx++;
- }
+ } else {
+ if (status & 0x1800)
+ lp->stats.collisions++;
+ lp->stats.tx_packets++;
+ }
+
+ /* We must free the original skb */
+ if (lp->tx_skbuff[entry]) {
+ dev_kfree_skb(lp->tx_skbuff[entry]);
+ lp->tx_skbuff[entry] = 0;
+ }
+ dirty_tx++;
+ }
#ifndef final_version
- if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
- printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
- dirty_tx, lp->cur_tx, lp->tx_full);
- dirty_tx += TX_RING_SIZE;
- }
+ if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
+ printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+ dirty_tx, lp->cur_tx, lp->tx_full);
+ dirty_tx += TX_RING_SIZE;
+ }
#endif
+ if (lp->tx_full && dev->tbusy
+ && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
+ /* The ring is no longer full, clear tbusy. */
+ lp->tx_full = 0;
+ clear_bit(0, (void *)&dev->tbusy);
+ mark_bh(NET_BH);
+ }
+ lp->dirty_tx = dirty_tx;
+ }
- if (lp->tx_full && dev->tbusy
- && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
- /* The ring is no longer full, clear tbusy. */
- lp->tx_full = 0;
- clear_bit(0, (void *)&dev->tbusy);
- mark_bh(NET_BH);
- }
- lp->dirty_tx = dirty_tx;
- }
-
- /* Log misc errors. */
- if (csr0 & 0x4000) lp->stats.tx_errors++; /* Tx babble. */
- if (csr0 & 0x1000) {
- /*
- * this happens when our receive ring is full. This shouldn't
- * be a problem as we will see normal rx interrupts for the frames
- * in the receive ring. But there are some PCI chipsets (I can reproduce
- * this on SP3G with Intel saturn chipset) which have sometimes problems
- * and will fill up the receive ring with error descriptors. In this
- * situation we don't get a rx interrupt, but a missed frame interrupt sooner
- * or later. So we try to clean up our receive ring here.
- */
- pcnet32_rx(dev);
- lp->stats.rx_errors++; /* Missed a Rx frame. */
- }
- if (csr0 & 0x0800) {
- printk("%s: Bus master arbitration failure, status %4.4x.\n",
- dev->name, csr0);
- /* unlike for the lance, there is no restart needed */
- }
+ /* Log misc errors. */
+ if (csr0 & 0x4000) lp->stats.tx_errors++; /* Tx babble. */
+ if (csr0 & 0x1000) {
+ /*
+ * this happens when our receive ring is full. This shouldn't
+ * be a problem as we will see normal rx interrupts for the frames
+ * in the receive ring. But there are some PCI chipsets (I can reproduce
+ * this on SP3G with Intel saturn chipset) which have sometimes problems
+ * and will fill up the receive ring with error descriptors. In this
+ * situation we don't get a rx interrupt, but a missed frame interrupt sooner
+ * or later. So we try to clean up our receive ring here.
+ */
+ pcnet32_rx(dev);
+ lp->stats.rx_errors++; /* Missed a Rx frame. */
+ }
+ if (csr0 & 0x0800) {
+ printk("%s: Bus master arbitration failure, status %4.4x.\n",
+ dev->name, csr0);
+ /* unlike for the lance, there is no restart needed */
+ }
- if (must_restart) {
- /* stop the chip to clear the error condition, then restart */
- outw(0x0000, dev->base_addr + PCNET32_ADDR);
- outw(0x0004, dev->base_addr + PCNET32_DATA);
- pcnet32_restart(dev, 0x0002);
- }
+ if (must_restart) {
+ /* stop the chip to clear the error condition, then restart */
+ lp->a.write_csr (ioaddr, 0, 0x0004);
+ pcnet32_restart(dev, 0x0002);
}
+ }
/* Clear any other interrupt, and set interrupt enable. */
- outw(0x0000, dev->base_addr + PCNET32_ADDR);
- outw(0x7940, dev->base_addr + PCNET32_DATA);
+ lp->a.write_csr (ioaddr, 0, 0x7940);
- if (pcnet32_debug > 4)
- printk("%s: exiting interrupt, csr%d=%#4.4x.\n",
- dev->name, inw(ioaddr + PCNET32_ADDR),
- inw(dev->base_addr + PCNET32_DATA));
+ if (pcnet32_debug > 4)
+ printk("%s: exiting interrupt, csr0=%#4.4x.\n",
+ dev->name, lp->a.read_csr (ioaddr, 0));
- dev->interrupt = 0;
- return;
+ dev->interrupt = 0;
+ return;
}
static int
pcnet32_rx(struct device *dev)
{
- struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
- int entry = lp->cur_rx & RX_RING_MOD_MASK;
- int i;
-
- /* If we own the next entry, it's a new packet. Send it up. */
- while ((short)le16_to_cpu(lp->rx_ring[entry].status) >= 0) {
- int status = (short)le16_to_cpu(lp->rx_ring[entry].status) >> 8;
-
- if (status != 0x03) { /* There was an error. */
- /* There is a tricky error noted by John Murphy,
- <murf@perftech.com> to Russ Nelson: Even with full-sized
- buffers it's possible for a jabber packet to use two
- buffers, with only the last correctly noting the error. */
- if (status & 0x01) /* Only count a general error at the */
- lp->stats.rx_errors++; /* end of a packet.*/
- if (status & 0x20) lp->stats.rx_frame_errors++;
- if (status & 0x10) lp->stats.rx_over_errors++;
- if (status & 0x08) lp->stats.rx_crc_errors++;
- if (status & 0x04) lp->stats.rx_fifo_errors++;
- lp->rx_ring[entry].status &= le16_to_cpu(0x03ff);
- }
- else
- {
- /* Malloc up new buffer, compatible with net-2e. */
- short pkt_len = (le32_to_cpu(lp->rx_ring[entry].msg_length) & 0xfff)-4;
- struct sk_buff *skb;
+ struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
+ int entry = lp->cur_rx & RX_RING_MOD_MASK;
+ int i;
+
+ /* If we own the next entry, it's a new packet. Send it up. */
+ while ((short)le16_to_cpu(lp->rx_ring[entry].status) >= 0) {
+ int status = (short)le16_to_cpu(lp->rx_ring[entry].status) >> 8;
+
+ if (status != 0x03) { /* There was an error. */
+ /*
+ * There is a tricky error noted by John Murphy,
+ * <murf@perftech.com> to Russ Nelson: Even with full-sized
+ * buffers it's possible for a jabber packet to use two
+ * buffers, with only the last correctly noting the error.
+ */
+ if (status & 0x01) /* Only count a general error at the */
+ lp->stats.rx_errors++; /* end of a packet.*/
+ if (status & 0x20) lp->stats.rx_frame_errors++;
+ if (status & 0x10) lp->stats.rx_over_errors++;
+ if (status & 0x08) lp->stats.rx_crc_errors++;
+ if (status & 0x04) lp->stats.rx_fifo_errors++;
+ lp->rx_ring[entry].status &= le16_to_cpu(0x03ff);
+ } else {
+ /* Malloc up new buffer, compatible with net-2e. */
+ short pkt_len = (le32_to_cpu(lp->rx_ring[entry].msg_length) & 0xfff)-4;
+ struct sk_buff *skb;
- if(pkt_len < 60) {
- printk("%s: Runt packet!\n",dev->name);
- lp->stats.rx_errors++;
- } else {
- int rx_in_place = 0;
+ if(pkt_len < 60) {
+ printk("%s: Runt packet!\n",dev->name);
+ lp->stats.rx_errors++;
+ } else {
+ int rx_in_place = 0;
- if (pkt_len > rx_copybreak) {
- struct sk_buff *newskb;
+ if (pkt_len > rx_copybreak) {
+ struct sk_buff *newskb;
- if ((newskb = dev_alloc_skb (PKT_BUF_SZ))) {
- skb_reserve (newskb, 2);
- skb = lp->rx_skbuff[entry];
- skb_put (skb, pkt_len);
- lp->rx_skbuff[entry] = newskb;
- newskb->dev = dev;
- lp->rx_ring[entry].base = le32_to_cpu(virt_to_bus(newskb->tail));
- rx_in_place = 1;
- } else
- skb = NULL;
- } else
- skb = dev_alloc_skb(pkt_len+2);
+ if ((newskb = dev_alloc_skb (PKT_BUF_SZ))) {
+ skb_reserve (newskb, 2);
+ skb = lp->rx_skbuff[entry];
+ skb_put (skb, pkt_len);
+ lp->rx_skbuff[entry] = newskb;
+ newskb->dev = dev;
+ lp->rx_ring[entry].base = le32_to_cpu(virt_to_bus(newskb->tail));
+ rx_in_place = 1;
+ } else
+ skb = NULL;
+ } else
+ skb = dev_alloc_skb(pkt_len+2);
- if (skb == NULL) {
- printk("%s: Memory squeeze, deferring packet.\n", dev->name);
- for (i=0; i < RX_RING_SIZE; i++)
- if ((short)le16_to_cpu(lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].status) < 0)
- break;
-
- if (i > RX_RING_SIZE -2) {
- lp->stats.rx_dropped++;
- lp->rx_ring[entry].status |= le16_to_cpu(0x8000);
- lp->cur_rx++;
- }
- break;
- }
- skb->dev = dev;
- if (!rx_in_place) {
- skb_reserve(skb,2); /* 16 byte align */
- skb_put(skb,pkt_len); /* Make room */
- eth_copy_and_sum(skb,
- (unsigned char *)bus_to_virt(le32_to_cpu(lp->rx_ring[entry].base)),
- pkt_len,0);
- }
- lp->stats.rx_bytes += skb->len;
- skb->protocol=eth_type_trans(skb,dev);
- netif_rx(skb);
- lp->stats.rx_packets++;
- }
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+ for (i=0; i < RX_RING_SIZE; i++)
+ if ((short)le16_to_cpu(lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].status) < 0)
+ break;
+
+ if (i > RX_RING_SIZE -2) {
+ lp->stats.rx_dropped++;
+ lp->rx_ring[entry].status |= le16_to_cpu(0x8000);
+ lp->cur_rx++;
+ }
+ break;
+ }
+ skb->dev = dev;
+ if (!rx_in_place) {
+ skb_reserve(skb,2); /* 16 byte align */
+ skb_put(skb,pkt_len); /* Make room */
+ eth_copy_and_sum(skb,
+ (unsigned char *)bus_to_virt(le32_to_cpu(lp->rx_ring[entry].base)),
+ pkt_len,0);
}
- /* The docs say that the buffer length isn't touched, but Andrew Boyd
- of QNX reports that some revs of the 79C965 clear it. */
- lp->rx_ring[entry].buf_length = le16_to_cpu(-PKT_BUF_SZ);
- lp->rx_ring[entry].status |= le16_to_cpu(0x8000);
- entry = (++lp->cur_rx) & RX_RING_MOD_MASK;
+ lp->stats.rx_bytes += skb->len;
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
}
+ /*
+ * The docs say that the buffer length isn't touched, but Andrew Boyd
+ * of QNX reports that some revs of the 79C965 clear it.
+ */
+ lp->rx_ring[entry].buf_length = le16_to_cpu(-PKT_BUF_SZ);
+ lp->rx_ring[entry].status |= le16_to_cpu(0x8000);
+ entry = (++lp->cur_rx) & RX_RING_MOD_MASK;
+ }
- /* We should check that at least two ring entries are free. If not,
- we should free one and mark stats->rx_dropped++. */
-
- return 0;
+ return 0;
}
static int
pcnet32_close(struct device *dev)
{
- unsigned int ioaddr = dev->base_addr;
- struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
- int i;
+ unsigned long ioaddr = dev->base_addr;
+ struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
+ int i;
- dev->start = 0;
- set_bit (0, (void *)&dev->tbusy);
+ dev->start = 0;
+ set_bit (0, (void *)&dev->tbusy);
- outw(112, ioaddr+PCNET32_ADDR);
- lp->stats.rx_missed_errors = inw(ioaddr+PCNET32_DATA);
+ lp->stats.rx_missed_errors = lp->a.read_csr (ioaddr, 112);
- outw(0, ioaddr+PCNET32_ADDR);
+ if (pcnet32_debug > 1)
+ printk("%s: Shutting down ethercard, status was %2.2x.\n",
+ dev->name, lp->a.read_csr (ioaddr, 0));
- if (pcnet32_debug > 1)
- printk("%s: Shutting down ethercard, status was %2.2x.\n",
- dev->name, inw(ioaddr+PCNET32_DATA));
+ /* We stop the PCNET32 here -- it occasionally polls memory if we don't. */
+ lp->a.write_csr (ioaddr, 0, 0x0004);
- /* We stop the PCNET32 here -- it occasionally polls
- memory if we don't. */
- outw(0x0004, ioaddr+PCNET32_DATA);
+ /*
+ * Switch back to 16bit mode to avoid problems with dumb
+ * DOS packet driver after a warm reboot
+ */
+ lp->a.write_bcr (ioaddr, 20, 4);
- free_irq(dev->irq, dev);
+ free_irq(dev->irq, dev);
- /* free all allocated skbuffs */
- for (i = 0; i < RX_RING_SIZE; i++) {
- lp->rx_ring[i].status = 0;
- if (lp->rx_skbuff[i])
- dev_kfree_skb(lp->rx_skbuff[i]);
- lp->rx_skbuff[i] = NULL;
- }
+ /* free all allocated skbuffs */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ lp->rx_ring[i].status = 0;
+ if (lp->rx_skbuff[i])
+ dev_kfree_skb(lp->rx_skbuff[i]);
+ lp->rx_skbuff[i] = NULL;
+ }
- for (i = 0; i < TX_RING_SIZE; i++) {
- if (lp->tx_skbuff[i])
- dev_kfree_skb(lp->tx_skbuff[i]);
- lp->rx_skbuff[i] = NULL;
- }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ if (lp->tx_skbuff[i])
+ dev_kfree_skb(lp->tx_skbuff[i]);
+ lp->rx_skbuff[i] = NULL;
+ }
- MOD_DEC_USE_COUNT;
+ MOD_DEC_USE_COUNT;
- return 0;
+ return 0;
}
static struct net_device_stats *
pcnet32_get_stats(struct device *dev)
{
- struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
- unsigned int ioaddr = dev->base_addr;
- unsigned short saved_addr;
- unsigned long flags;
-
- save_flags(flags);
- cli();
- saved_addr = inw(ioaddr+PCNET32_ADDR);
- outw(112, ioaddr+PCNET32_ADDR);
- lp->stats.rx_missed_errors = inw(ioaddr+PCNET32_DATA);
- outw(saved_addr, ioaddr+PCNET32_ADDR);
- restore_flags(flags);
-
- return &lp->stats;
+ struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+ u16 saved_addr;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ saved_addr = lp->a.read_rap(ioaddr);
+ lp->stats.rx_missed_errors = lp->a.read_csr (ioaddr, 112);
+ lp->a.write_rap(ioaddr, saved_addr);
+ restore_flags(flags);
+
+ return &lp->stats;
}
-
/* taken from the sunlance driver, which it took from the depca driver */
static void pcnet32_load_multicast (struct device *dev)
{
}
-/* Set or clear the multicast filter for this adaptor.
+/*
+ * Set or clear the multicast filter for this adaptor.
*/
-
static void pcnet32_set_multicast_list(struct device *dev)
{
- unsigned int ioaddr = dev->base_addr;
- struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
-
- if (dev->flags&IFF_PROMISC) {
- /* Log any net taps. */
- printk("%s: Promiscuous mode enabled.\n", dev->name);
- lp->init_block.mode = le16_to_cpu(0x8000 | (lp->options & PORT_PORTSEL) << 7);
- } else {
- lp->init_block.mode = le16_to_cpu((lp->options & PORT_PORTSEL) << 7);
- pcnet32_load_multicast (dev);
- }
+ unsigned long ioaddr = dev->base_addr;
+ struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
+
+ if (dev->flags&IFF_PROMISC) {
+ /* Log any net taps. */
+ printk("%s: Promiscuous mode enabled.\n", dev->name);
+ lp->init_block.mode = le16_to_cpu(0x8000 | (lp->options & PORT_PORTSEL) << 7);
+ } else {
+ lp->init_block.mode = le16_to_cpu((lp->options & PORT_PORTSEL) << 7);
+ pcnet32_load_multicast (dev);
+ }
- outw(0, ioaddr+PCNET32_ADDR);
- outw(0x0004, ioaddr+PCNET32_DATA); /* Temporarily stop the lance. */
+ lp->a.write_csr (ioaddr, 0, 0x0004); /* Temporarily stop the lance. */
- pcnet32_restart(dev, 0x0042); /* Resume normal operation */
+ pcnet32_restart(dev, 0x0042); /* Resume normal operation */
}
+#ifdef HAVE_PRIVATE_IOCTL
+static int pcnet32_mii_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+ unsigned long ioaddr = dev->base_addr;
+ struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
+ u16 *data = (u16 *)&rq->ifr_data;
+ int phyaddr = lp->a.read_bcr (ioaddr, 33);
+
+ if (lp->mii) {
+ switch(cmd) {
+ case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
+ data[0] = (phyaddr >> 5) & 0x1f;
+ /* Fall Through */
+ case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
+ lp->a.write_bcr (ioaddr, 33, ((data[0] & 0x1f) << 5) | (data[1] & 0x1f));
+ data[3] = lp->a.read_bcr (ioaddr, 34);
+ lp->a.write_bcr (ioaddr, 33, phyaddr);
+ return 0;
+ case SIOCDEVPRIVATE+2: /* Write the specified MII register */
+ if (!suser())
+ return -EPERM;
+ lp->a.write_bcr (ioaddr, 33, ((data[0] & 0x1f) << 5) | (data[1] & 0x1f));
+ lp->a.write_bcr (ioaddr, 34, data[2]);
+ lp->a.write_bcr (ioaddr, 33, phyaddr);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ }
+ return -EOPNOTSUPP;
+}
+#endif /* HAVE_PRIVATE_IOCTL */
+
#ifdef MODULE
MODULE_PARM(debug, "i");
-MODULE_PARM(options, "i");
MODULE_PARM(max_interrupt_work, "i");
MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+
/* An additional parameter that may be passed in... */
static int debug = -1;
int
init_module(void)
{
- if (debug > 0)
- pcnet32_debug = debug;
+ if (debug > 0)
+ pcnet32_debug = debug;
- pcnet32_dev = NULL;
- return pcnet32_probe(NULL);
+ pcnet32_dev = NULL;
+ return pcnet32_probe(NULL);
}
void
cleanup_module(void)
{
- struct device *next_dev;
-
- /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
- while (pcnet32_dev) {
- next_dev = ((struct pcnet32_private *) pcnet32_dev->priv)->next;
- unregister_netdev(pcnet32_dev);
- release_region(pcnet32_dev->base_addr, PCNET32_TOTAL_SIZE);
- kfree(pcnet32_dev->priv);
- kfree(pcnet32_dev);
- pcnet32_dev = next_dev;
- }
+ struct device *next_dev;
+
+ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+ while (pcnet32_dev) {
+ next_dev = ((struct pcnet32_private *) pcnet32_dev->priv)->next;
+ unregister_netdev(pcnet32_dev);
+ release_region(pcnet32_dev->base_addr, PCNET32_TOTAL_SIZE);
+ kfree(((struct pcnet32_private *)pcnet32_dev->priv)->origmem);
+ kfree(pcnet32_dev);
+ pcnet32_dev = next_dev;
+ }
}
#endif /* MODULE */
* stack will need to know about I/O vectors or something similar.
*/
-static const char *version = "rrunner.c: v0.09 12/14/98 Jes Sorensen (Jes.Sorensen@cern.ch)\n";
-
-static unsigned int read_eeprom(struct rr_private *rrpriv,
- unsigned long offset,
- unsigned char *buf,
- unsigned long length);
-static u32 read_eeprom_word(struct rr_private *rrpriv,
- void * offset);
-static int rr_load_firmware(struct device *dev);
+static const char __initdata *version = "rrunner.c: v0.17 03/09/99 Jes Sorensen (Jes.Sorensen@cern.ch)\n";
/*
extern __u32 sysctl_wmem_max;
extern __u32 sysctl_rmem_max;
+static int probed __initdata = 0;
+
__initfunc(int rr_hippi_probe (struct device *dev))
{
- static int i = 0;
int boards_found = 0;
int version_disp; /* was version info already displayed? */
- u8 pci_bus; /* PCI bus number (0-255) */
- u8 pci_dev_fun; /* PCI device and function numbers (0-255) */
+ struct pci_dev *pdev = NULL;
+ struct pci_dev *opdev = NULL;
u8 pci_latency;
- u16 command; /* PCI Configuration space Command register */
- unsigned int tmp;
- u8 irq;
struct rr_private *rrpriv;
+ if (probed)
+ return -ENODEV;
+ probed++;
+
if (!pci_present()) /* is PCI BIOS even present? */
return -ENODEV;
version_disp = 0;
- for (; i < 255; i++)
+ while((pdev = pci_find_device(PCI_VENDOR_ID_ESSENTIAL,
+ PCI_DEVICE_ID_ESSENTIAL_ROADRUNNER,
+ pdev)))
{
- if (pcibios_find_device(PCI_VENDOR_ID_ESSENTIAL,
- PCI_DEVICE_ID_ESSENTIAL_ROADRUNNER,
- i, &pci_bus, &pci_dev_fun) != 0)
- break;
-
- pcibios_read_config_word(pci_bus, pci_dev_fun,
- PCI_COMMAND, &command);
-
- /* Enable mastering */
-
- command |= PCI_COMMAND_MASTER;
- pcibios_write_config_word(pci_bus, pci_dev_fun,
- PCI_COMMAND, command);
-
- if (!(command & PCI_COMMAND_MEMORY)){
- printk("shared mem not enabled - unable to configure RoadRunner\n");
- break;
- }
+ if (pdev == opdev)
+ return 0;
/*
* So we found our HIPPI ... time to tell the system.
dev = init_hippi_dev(dev, sizeof(struct rr_private));
- if (dev == NULL)
+ if (!dev)
break;
if (!dev->priv)
dev->priv = kmalloc(sizeof(*rrpriv), GFP_KERNEL);
- rrpriv = (struct rr_private *)dev->priv;
-
- /* Read register base address from
- PCI Configuration Space */
-
- pcibios_read_config_dword(pci_bus, pci_dev_fun,
- PCI_BASE_ADDRESS_0, &tmp);
+ if (!dev->priv)
+ return -ENOMEM;
- pcibios_read_config_byte(pci_bus, pci_dev_fun,
- PCI_INTERRUPT_LINE, &irq);
+ rrpriv = (struct rr_private *)dev->priv;
+ memset(rrpriv, 0, sizeof(*rrpriv));
- dev->irq = irq;
- rrpriv->pci_bus = pci_bus;
- rrpriv->pci_dev_fun = pci_dev_fun;
- sprintf(rrpriv->name, "RoadRunner serial HIPPI");
#ifdef __SMP__
spin_lock_init(&rrpriv->lock);
#endif
+ sprintf(rrpriv->name, "RoadRunner serial HIPPI");
+ dev->irq = pdev->irq;
dev->open = &rr_open;
dev->hard_start_xmit = &rr_start_xmit;
dev->stop = &rr_close;
printk(version);
}
- printk(KERN_INFO "%s: Essential RoadRunner serial HIPPI at 0x%08x, irq %i\n",
- dev->name, tmp, dev->irq);
-
- pcibios_read_config_byte(pci_bus, pci_dev_fun,
- PCI_LATENCY_TIMER, &pci_latency);
-#if 0
- if (pci_latency <= 48){
- printk(" PCI latency counter too low (%i), setting to 48 clocks\n", pci_latency);
- pcibios_write_config_byte(pci_bus, pci_dev_fun,
- PCI_LATENCY_TIMER, 48);
+ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);
+ if (pci_latency <= 0x58){
+ pci_latency = 0x58;
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER,
+ pci_latency);
}
-#else
- if (pci_latency <= 0x58)
- pcibios_write_config_byte(pci_bus, pci_dev_fun,
- PCI_LATENCY_TIMER, 0x58);
-#endif
+
+ pci_set_master(pdev);
+
+ printk(KERN_INFO "%s: Essential RoadRunner serial HIPPI "
+ "at 0x%08lx, irq %i, PCI latency %i\n", dev->name,
+ pdev->base_address[0], dev->irq, pci_latency);
+
/*
* Remap the regs into kernel space.
*/
- rrpriv->regs = (struct rr_regs *)ioremap(tmp, 0x1000);
+ rrpriv->regs = (struct rr_regs *)
+ ioremap(pdev->base_address[0], 0x1000);
+
if (!rrpriv->regs){
- printk(KERN_ERR "%s: Unable to map I/O register, RoadRunner %i will be disabled.\n", dev->name, i);
+ printk(KERN_ERR "%s: Unable to map I/O register, "
+ "RoadRunner %i will be disabled.\n",
+ dev->name, boards_found);
break;
}
* Don't access any registes before this point!
*/
#ifdef __BIG_ENDIAN
- regs->HostCtrl |= NO_SWAP;
+ writel(readl(®s->HostCtrl) | NO_SWAP, ®s->HostCtrl);
#endif
/*
* Need to add a case for little-endian 64-bit hosts here.
boards_found++;
dev->base_addr = 0;
dev = NULL;
+ opdev = pdev;
}
/*
* 1 or more boards. Otherwise, return failure (-ENODEV).
*/
+#ifdef MODULE
+ return boards_found;
+#else
if (boards_found > 0)
return 0;
else
return -ENODEV;
+#endif
}
+static struct device *root_dev = NULL;
+
+#ifdef MODULE
+#if LINUX_VERSION_CODE > 0x20118
+MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@cern.ch>");
+MODULE_DESCRIPTION("Essential RoadRunner HIPPI driver");
+#endif
+
+
+int init_module(void)
+{
+ int cards;
+
+ root_dev = NULL;
+
+ cards = rr_hippi_probe(NULL);
+ return cards ? 0 : -ENODEV;
+}
+
+void cleanup_module(void)
+{
+ struct rr_private *rr;
+ struct device *next;
+
+ while (root_dev) {
+ next = ((struct rr_private *)root_dev->priv)->next;
+ rr = (struct rr_private *)root_dev->priv;
+
+ if (!(readl(&rr->regs->HostCtrl) & NIC_HALTED)){
+ printk(KERN_ERR "%s: trying to unload running NIC\n",
+ root_dev->name);
+ writel(HALT_NIC, &rr->regs->HostCtrl);
+ }
+
+ iounmap(rr->regs);
+ unregister_hipdev(root_dev);
+ kfree(root_dev);
+
+ root_dev = next;
+ }
+}
+#endif
+
/*
* Commands are considered to be slow, thus there is no reason to
* This is temporary - it will go away in the final version.
* We probably also want to make this function inline.
*/
- if (regs->HostCtrl & NIC_HALTED){
- printk("issuing command for halted NIC, code 0x%x, HostCtrl %08x\n", cmd->code, regs->HostCtrl);
- if (regs->Mode & FATAL_ERR)
- printk("error code %02x\n", regs->Fail1);
+ if (readl(®s->HostCtrl) & NIC_HALTED){
+ printk("issuing command for halted NIC, code 0x%x, "
+ "HostCtrl %08x\n", cmd->code, readl(®s->HostCtrl));
+ if (readl(®s->Mode) & FATAL_ERR)
+ printk("error codes Fail1 %02x, Fail2 %02x\n",
+ readl(®s->Fail1), readl(®s->Fail2));
}
idx = rrpriv->info->cmd_ctrl.pi;
- regs->CmdRing[idx] = *(u32*)(cmd);
+ writel(*(u32*)(cmd), ®s->CmdRing[idx]);
+ mb();
idx = (idx - 1) % CMD_RING_ENTRIES;
rrpriv->info->cmd_ctrl.pi = idx;
+ mb();
- if (regs->Mode & FATAL_ERR)
- printk("error code %02x\n", regs->Fail1);
+ if (readl(®s->Mode) & FATAL_ERR)
+ printk("error code %02x\n", readl(®s->Fail1));
}
rr_load_firmware(dev);
- regs->TX_state = 0x01000000;
- regs->RX_state = 0xff800000;
- regs->AssistState = 0;
- regs->LocalCtrl = CLEAR_INTA;
- regs->BrkPt = 0x01;
- regs->Timer = 0;
- regs->TimerRef = 0;
- regs->DmaReadState = RESET_DMA;
- regs->DmaWriteState = RESET_DMA;
- regs->DmaWriteHostHi = 0;
- regs->DmaWriteHostLo = 0;
- regs->DmaReadHostHi = 0;
- regs->DmaReadHostLo = 0;
- regs->DmaReadLen = 0;
- regs->DmaWriteLen = 0;
- regs->DmaWriteLcl = 0;
- regs->DmaWriteIPchecksum = 0;
- regs->DmaReadLcl = 0;
- regs->DmaReadIPchecksum = 0;
- regs->PciState = 0; /* 0x90 for GE? */
- regs->Mode = SWAP_DATA;
+ writel(0x01000000, ®s->TX_state);
+ writel(0xff800000, ®s->RX_state);
+ writel(0, ®s->AssistState);
+ writel(CLEAR_INTA, ®s->LocalCtrl);
+ writel(0x01, ®s->BrkPt);
+ writel(0, ®s->Timer);
+ writel(0, ®s->TimerRef);
+ writel(RESET_DMA, ®s->DmaReadState);
+ writel(RESET_DMA, ®s->DmaWriteState);
+ writel(0, ®s->DmaWriteHostHi);
+ writel(0, ®s->DmaWriteHostLo);
+ writel(0, ®s->DmaReadHostHi);
+ writel(0, ®s->DmaReadHostLo);
+ writel(0, ®s->DmaReadLen);
+ writel(0, ®s->DmaWriteLen);
+ writel(0, ®s->DmaWriteLcl);
+ writel(0, ®s->DmaWriteIPchecksum);
+ writel(0, ®s->DmaReadLcl);
+ writel(0, ®s->DmaReadIPchecksum);
+ writel(0, ®s->PciState);
+#if (BITS_PER_LONG == 64) && defined __LITTLE_ENDIAN
+ writel(SWAP_DATA | PTR64BIT | PTR_WD_SWAP, ®s->Mode);
+#elif (BITS_PER_LONG == 64)
+ writel(SWAP_DATA | PTR64BIT | PTR_WD_NOSWAP, ®s->Mode);
+#else
+ writel(SWAP_DATA | PTR32BIT | PTR_WD_NOSWAP, ®s->Mode);
+#endif
#if 0
/*
* Don't worry, this is just black magic.
*/
- regs->RxBase = 0xdf000;
- regs->RxPrd = 0xdf000;
- regs->RxCon = 0xdf000;
- regs->TxBase = 0xce000;
- regs->TxPrd = 0xce000;
- regs->TxCon = 0xce000;
- regs->RxIndPro = 0;
- regs->RxIndCon = 0;
- regs->RxIndRef = 0;
- regs->TxIndPro = 0;
- regs->TxIndCon = 0;
- regs->TxIndRef = 0;
- regs->pad10[0] = 0xcc000;
- regs->DrCmndPro = 0;
- regs->DrCmndCon = 0;
- regs->DwCmndPro = 0;
- regs->DwCmndCon = 0;
- regs->DwCmndRef = 0;
- regs->DrDataPro = 0;
- regs->DrDataCon = 0;
- regs->DrDataRef = 0;
- regs->DwDataPro = 0;
- regs->DwDataCon = 0;
- regs->DwDataRef = 0;
+ writel(0xdf000, ®s->RxBase);
+ writel(0xdf000, ®s->RxPrd);
+ writel(0xdf000, ®s->RxCon);
+ writel(0xce000, ®s->TxBase);
+ writel(0xce000, ®s->TxPrd);
+ writel(0xce000, ®s->TxCon);
+ writel(0, ®s->RxIndPro);
+ writel(0, ®s->RxIndCon);
+ writel(0, ®s->RxIndRef);
+ writel(0, ®s->TxIndPro);
+ writel(0, ®s->TxIndCon);
+ writel(0, ®s->TxIndRef);
+ writel(0xcc000, ®s->pad10[0]);
+ writel(0, ®s->DrCmndPro);
+ writel(0, ®s->DrCmndCon);
+ writel(0, ®s->DwCmndPro);
+ writel(0, ®s->DwCmndCon);
+ writel(0, ®s->DwCmndRef);
+ writel(0, ®s->DrDataPro);
+ writel(0, ®s->DrDataCon);
+ writel(0, ®s->DrDataRef);
+ writel(0, ®s->DwDataPro);
+ writel(0, ®s->DwDataCon);
+ writel(0, ®s->DwDataRef);
#endif
- regs->MbEvent = 0xffffffff;
- regs->Event = 0;
+ writel(0xffffffff, ®s->MbEvent);
+ writel(0, ®s->Event);
- regs->TxPi = 0;
- regs->IpRxPi = 0;
+ writel(0, ®s->TxPi);
+ writel(0, ®s->IpRxPi);
- regs->EvtCon = 0;
- regs->EvtPrd = 0;
+ writel(0, ®s->EvtCon);
+ writel(0, ®s->EvtPrd);
rrpriv->info->evt_ctrl.pi = 0;
for (i = 0; i < CMD_RING_ENTRIES; i++)
- regs->CmdRing[i] = 0;
+ writel(0, ®s->CmdRing[i]);
- regs->PciState = 0;
+/*
+ * Why 32 ? is this not cache line size dependant?
+ */
+ writel(WBURST_32, ®s->PciState);
+ mb();
- start_pc = read_eeprom_word(rrpriv, &hw->rncd_info.FwStart);
+ start_pc = rr_read_eeprom_word(rrpriv, &hw->rncd_info.FwStart);
#if (DEBUG > 1)
printk("%s: Executing firmware at address 0x%06x\n",
dev->name, start_pc);
#endif
- regs->Pc = start_pc + 0x800;
+ writel(start_pc + 0x800, ®s->Pc);
+ mb();
udelay(5);
- regs->Pc = start_pc;
+ writel(start_pc, ®s->Pc);
+ mb();
return 0;
}
/*
* Read a string from the EEPROM.
*/
-static unsigned int read_eeprom(struct rr_private *rrpriv,
+static unsigned int rr_read_eeprom(struct rr_private *rrpriv,
unsigned long offset,
unsigned char *buf,
unsigned long length)
struct rr_regs *regs = rrpriv->regs;
u32 misc, io, host, i;
- io = regs->ExtIo;
- regs->ExtIo = 0;
- misc = regs->LocalCtrl;
- regs->LocalCtrl = 0;
- host = regs->HostCtrl;
- regs->HostCtrl |= HALT_NIC;
+ io = readl(®s->ExtIo);
+ writel(0, ®s->ExtIo);
+ misc = readl(®s->LocalCtrl);
+ writel(0, ®s->LocalCtrl);
+ host = readl(®s->HostCtrl);
+ writel(host | HALT_NIC, ®s->HostCtrl);
+ mb();
for (i = 0; i < length; i++){
- regs->WinBase = (EEPROM_BASE + ((offset+i) << 3));
- buf[i] = (regs->WinData >> 24) & 0xff;
+ writel((EEPROM_BASE + ((offset+i) << 3)), ®s->WinBase);
+ mb();
+ buf[i] = (readl(®s->WinData) >> 24) & 0xff;
+ mb();
}
- regs->HostCtrl = host;
- regs->LocalCtrl = misc;
- regs->ExtIo = io;
-
+ writel(host, ®s->HostCtrl);
+ writel(misc, ®s->LocalCtrl);
+ writel(io, ®s->ExtIo);
+ mb();
return i;
}
* Shortcut to read one word (4 bytes) out of the EEPROM and convert
* it to our CPU byte-order.
*/
-static u32 read_eeprom_word(struct rr_private *rrpriv,
+static u32 rr_read_eeprom_word(struct rr_private *rrpriv,
void * offset)
{
u32 word;
- if ((read_eeprom(rrpriv, (unsigned long)offset,
- (char *)&word, 4) == 4))
+ if ((rr_read_eeprom(rrpriv, (unsigned long)offset,
+ (char *)&word, 4) == 4))
return be32_to_cpu(word);
return 0;
}
* This is only called when the firmware is not running.
*/
static unsigned int write_eeprom(struct rr_private *rrpriv,
- unsigned long offset,
- unsigned char *buf,
- unsigned long length)
+ unsigned long offset,
+ unsigned char *buf,
+ unsigned long length)
{
struct rr_regs *regs = rrpriv->regs;
u32 misc, io, data, i, j, ready, error = 0;
- io = regs->ExtIo;
- regs->ExtIo = 0;
- misc = regs->LocalCtrl;
- regs->LocalCtrl = ENABLE_EEPROM_WRITE;
+ io = readl(®s->ExtIo);
+ writel(0, ®s->ExtIo);
+ misc = readl(®s->LocalCtrl);
+ writel(ENABLE_EEPROM_WRITE, ®s->LocalCtrl);
+ mb();
for (i = 0; i < length; i++){
- regs->WinBase = (EEPROM_BASE + ((offset+i) << 3));
+ writel((EEPROM_BASE + ((offset+i) << 3)), ®s->WinBase);
+ mb();
data = buf[i] << 24;
/*
* Only try to write the data if it is not the same
* value already.
*/
- if ((regs->WinData & 0xff000000) != data){
- regs->WinData = data;
+ if ((readl(®s->WinData) & 0xff000000) != data){
+ writel(data, ®s->WinData);
ready = 0;
j = 0;
mb();
while(!ready){
- udelay(1000);
- if ((regs->WinData & 0xff000000) == data)
+ udelay(20);
+ if ((readl(®s->WinData) & 0xff000000) ==
+ data)
ready = 1;
+ mb();
if (j++ > 5000){
printk("data mismatch: %08x, "
"WinData %08x\n", data,
- regs->WinData);
+ readl(®s->WinData));
ready = 1;
error = 1;
}
}
}
- regs->LocalCtrl = misc;
- regs->ExtIo = io;
+ writel(misc, ®s->LocalCtrl);
+ writel(io, ®s->ExtIo);
+ mb();
return error;
}
rrpriv = (struct rr_private *)dev->priv;
regs = rrpriv->regs;
- rev = regs->FwRev;
+ rev = readl(®s->FwRev);
+ rrpriv->fw_rev = rev;
if (rev > 0x00020024)
printk(" Firmware revision: %i.%i.%i\n", (rev >> 16),
((rev >> 8) & 0xff), (rev & 0xff));
printk(" Firmware revision too old: %i.%i.%i, please "
"upgrade to 2.0.37 or later.\n",
(rev >> 16), ((rev >> 8) & 0xff), (rev & 0xff));
- return -EFAULT;
-
}
- printk(" Maximum receive rings %i\n", regs->MaxRxRng);
+#if (DEBUG > 2)
+ printk(" Maximum receive rings %i\n", readl(®s->MaxRxRng));
+#endif
- sram_size = read_eeprom_word(rrpriv, (void *)8);
+ sram_size = rr_read_eeprom_word(rrpriv, (void *)8);
printk(" SRAM size 0x%06x\n", sram_size);
if (sysctl_rmem_max < 262144){
sysctl_wmem_max = 262144;
}
+ rrpriv->next = root_dev;
+ root_dev = dev;
+
return 0;
}
struct rr_private *rrpriv;
struct rr_regs *regs;
u32 hostctrl;
- unsigned long myjif, flags, tmp_ptr;
+ unsigned long myjif, flags;
struct cmd cmd;
short i;
spin_lock_irqsave(&rrpriv->lock, flags);
- hostctrl = regs->HostCtrl;
- regs->HostCtrl |= HALT_NIC;
+ hostctrl = readl(®s->HostCtrl);
+ writel(hostctrl | HALT_NIC | RR_CLEAR_INT, ®s->HostCtrl);
+ mb();
if (hostctrl & PARITY_ERR){
printk("%s: Parity error halting NIC - this is serious!\n",
return -EFAULT;
}
-
- memset(rrpriv->rx_ctrl, 0, 256 * sizeof(struct ring_ctrl));
- memset(rrpriv->info, 0, sizeof(struct rr_info));
-
- tmp_ptr = virt_to_bus((void *)rrpriv->rx_ctrl);
-#if (BITS_PER_LONG == 64)
- regs->RxRingHi = (tmp_ptr >> 32);
-#else
- regs->RxRingHi = 0;
-#endif
- regs->RxRingLo = ((tmp_ptr) & 0xffffffff);
-
- tmp_ptr = virt_to_bus((void *)rrpriv->info);
-#if (BITS_PER_LONG == 64)
- regs->InfoPtrHi = (tmp_ptr >> 32);
-#else
- regs->InfoPtrHi = 0;
-#endif
- regs->InfoPtrLo = ((tmp_ptr) & 0xffffffff);
+ set_rxaddr(regs, rrpriv->rx_ctrl);
+ set_infoaddr(regs, rrpriv->info);
rrpriv->info->evt_ctrl.entry_size = sizeof(struct event);
rrpriv->info->evt_ctrl.entries = EVT_RING_ENTRIES;
rrpriv->info->evt_ctrl.mode = 0;
rrpriv->info->evt_ctrl.pi = 0;
- rrpriv->info->evt_ctrl.rngptr = virt_to_bus(rrpriv->evt_ring);
+ set_rraddr(&rrpriv->info->evt_ctrl.rngptr, rrpriv->evt_ring);
rrpriv->info->cmd_ctrl.entry_size = sizeof(struct cmd);
rrpriv->info->cmd_ctrl.entries = CMD_RING_ENTRIES;
rrpriv->info->cmd_ctrl.pi = 15;
for (i = 0; i < CMD_RING_ENTRIES; i++) {
- regs->CmdRing[i] = 0;
+ writel(0, ®s->CmdRing[i]);
}
for (i = 0; i < TX_RING_ENTRIES; i++) {
rrpriv->tx_ring[i].size = 0;
- rrpriv->tx_ring[i].addr = 0;
+ set_rraddr(&rrpriv->tx_ring[i].addr, 0);
rrpriv->tx_skbuff[i] = 0;
}
-
rrpriv->info->tx_ctrl.entry_size = sizeof(struct tx_desc);
rrpriv->info->tx_ctrl.entries = TX_RING_ENTRIES;
rrpriv->info->tx_ctrl.mode = 0;
rrpriv->info->tx_ctrl.pi = 0;
- rrpriv->info->tx_ctrl.rngptr = virt_to_bus(rrpriv->tx_ring);
+ set_rraddr(&rrpriv->info->tx_ctrl.rngptr, rrpriv->tx_ring);
/*
* Set dirty_tx before we start receiving interrupts, otherwise
rr_reset(dev);
- regs->IntrTmr = 0x60;
- regs->WriteDmaThresh = 0x80 | 0x1f;
- regs->ReadDmaThresh = 0x80 | 0x1f;
+ writel(0x60, ®s->IntrTmr);
+ /*
+ * These seem to have no real effect as the Firmware sets
+ * it's own default values
+ */
+ writel(0x10, ®s->WriteDmaThresh);
+ writel(0x20, ®s->ReadDmaThresh);
rrpriv->fw_running = 0;
+ mb();
hostctrl &= ~(HALT_NIC | INVALID_INST_B | PARITY_ERR);
- regs->HostCtrl = hostctrl;
+ writel(hostctrl, ®s->HostCtrl);
+ mb();
spin_unlock_irqrestore(&rrpriv->lock, flags);
if ((((unsigned long)skb->data) & 0xfff) > ~65320)
printk("skb alloc error\n");
-#if (BITS_PER_LONG == 32)
- rrpriv->rx_ring[i].zero = 0;
-#endif
- rrpriv->rx_ring[i].addr = virt_to_bus(skb->data);
+ set_rraddr(&rrpriv->rx_ring[i].addr, skb->data);
rrpriv->rx_ring[i].size = dev->mtu + HIPPI_HLEN;
}
rrpriv->rx_ctrl[4].entries = RX_RING_ENTRIES;
rrpriv->rx_ctrl[4].mode = 8;
rrpriv->rx_ctrl[4].pi = 0;
- rrpriv->rx_ctrl[4].rngptr = virt_to_bus(rrpriv->rx_ring);
+ mb();
+ set_rraddr(&rrpriv->rx_ctrl[4].rngptr, rrpriv->rx_ring);
cmd.code = C_NEW_RNG;
cmd.ring = 4;
#if 0
{
u32 tmp;
- tmp = regs->ExtIo;
- regs->ExtIo = 0x80;
+ tmp = readl(®s->ExtIo);
+ writel(0x80, ®s->ExtIo);
i = jiffies + 1 * HZ;
while (jiffies < i);
- regs->ExtIo = tmp;
+ writel(tmp, ®s->ExtIo);
}
#endif
dev->tbusy = 0;
-#if 0
- dev->interrupt = 0;
-#endif
dev->start = 1;
return 0;
}
* events) and are handled here, outside the main interrupt handler,
* to reduce the size of the handler.
*/
-static u32 rr_handle_event(struct device *dev, u32 prodidx)
+static u32 rr_handle_event(struct device *dev, u32 prodidx, u32 eidx)
{
struct rr_private *rrpriv;
struct rr_regs *regs;
- u32 tmp, eidx;
+ u32 tmp;
rrpriv = (struct rr_private *)dev->priv;
regs = rrpriv->regs;
- eidx = rrpriv->info->evt_ctrl.pi;
while (prodidx != eidx){
switch (rrpriv->evt_ring[eidx].code){
case E_NIC_UP:
- tmp = regs->FwRev;
+ tmp = readl(®s->FwRev);
printk("%s: Firmware revision %i.%i.%i up and running\n",
dev->name, (tmp >> 16), ((tmp >> 8) & 0xff),
(tmp & 0xff));
rrpriv->fw_running = 1;
+ mb();
break;
case E_LINK_ON:
printk("%s: Optical link ON\n", dev->name);
#if (DEBUG > 2)
printk("%s: RX ring valid event\n", dev->name);
#endif
- regs->IpRxPi = RX_RING_ENTRIES - 1;
+ writel(RX_RING_ENTRIES - 1, ®s->IpRxPi);
break;
case E_INV_RNG:
printk("%s: RX ring invalid event\n", dev->name);
}
rrpriv->info->evt_ctrl.pi = eidx;
+ mb();
return eidx;
}
-static int rx_int(struct device *dev, u32 rxlimit)
+static void rx_int(struct device *dev, u32 rxlimit, u32 index)
{
struct rr_private *rrpriv = (struct rr_private *)dev->priv;
- u32 index, pkt_len;
+ u32 pkt_len;
struct rr_regs *regs = rrpriv->regs;
- index = rrpriv->cur_rx;
-
- while(index != rxlimit){
+ do {
pkt_len = rrpriv->rx_ring[index].size;
#if (DEBUG > 2)
printk("index %i, rxlimit %i\n", index, rxlimit);
printk("len %x, mode %x\n", pkt_len,
rrpriv->rx_ring[index].mode);
#endif
-#if 0
-/*
- * I have never seen this occur
- */
- if(!(rrpriv->rx_skbuff[index])){
- printk("Trying to receive in empty skbuff\n");
- goto out;
- }
-#endif
-
if (pkt_len > 0){
struct sk_buff *skb;
skb = rrpriv->rx_skbuff[index];
skb_put(skb, pkt_len);
rrpriv->rx_skbuff[index] = newskb;
- rrpriv->rx_ring[index].addr = virt_to_bus(newskb->data);
+ set_rraddr(&rrpriv->rx_ring[index].addr, newskb->data);
}else{
printk("%s: Out of memory, deferring "
"packet\n", dev->name);
rrpriv->rx_ring[index].size = dev->mtu + HIPPI_HLEN;
if ((index & 7) == 7)
- regs->IpRxPi = index;
+ writel(index, ®s->IpRxPi);
index = (index + 1) % RX_RING_ENTRIES;
- }
+ } while(index != rxlimit);
rrpriv->cur_rx = index;
- return index;
+ mb();
}
struct rr_private *rrpriv;
struct rr_regs *regs;
struct device *dev = (struct device *)dev_id;
- u32 prodidx, eidx, txcsmr, rxlimit, txcon;
+ u32 prodidx, rxindex, eidx, txcsmr, rxlimit, txcon;
unsigned long flags;
rrpriv = (struct rr_private *)dev->priv;
regs = rrpriv->regs;
- if (!(regs->HostCtrl & RR_INT))
+ if (!(readl(®s->HostCtrl) & RR_INT))
return;
-#if 0
- if (test_and_set_bit(0, (void*)&dev->interrupt) != 0) {
- printk("%s: Re-entering the interrupt handler.\n", dev->name);
- return;
- }
-#endif
-
spin_lock_irqsave(&rrpriv->lock, flags);
- prodidx = regs->EvtPrd;
+ prodidx = readl(®s->EvtPrd);
txcsmr = (prodidx >> 8) & 0xff;
rxlimit = (prodidx >> 16) & 0xff;
prodidx &= 0xff;
prodidx, rrpriv->info->evt_ctrl.pi);
#endif
+ rxindex = rrpriv->cur_rx;
+ if (rxindex != rxlimit)
+ rx_int(dev, rxlimit, rxindex);
+
txcon = rrpriv->dirty_tx;
if (txcsmr != txcon) {
do {
rrpriv->tx_skbuff[txcon] = NULL;
rrpriv->tx_ring[txcon].size = 0;
- rrpriv->tx_ring[txcon].addr = 0;
+ set_rraddr(&rrpriv->tx_ring[txcon].addr, 0);
rrpriv->tx_ring[txcon].mode = 0;
txcon = (txcon + 1) % TX_RING_ENTRIES;
} while (txcsmr != txcon);
+ mb();
rrpriv->dirty_tx = txcon;
if (rrpriv->tx_full && dev->tbusy &&
}
}
- rx_int(dev, rxlimit);
-
eidx = rrpriv->info->evt_ctrl.pi;
-
if (prodidx != eidx)
- eidx = rr_handle_event(dev, prodidx);
+ eidx = rr_handle_event(dev, prodidx, eidx);
eidx |= ((txcsmr << 8) | (rxlimit << 16));
- regs->EvtCon = eidx;
+ writel(eidx, ®s->EvtCon);
+ mb();
spin_unlock_irqrestore(&rrpriv->lock, flags);
-
-#if 0
- dev->interrupt = 0;
-#endif
}
{
struct rr_private *rrpriv;
struct rr_regs *regs;
+ int ecode = 0;
+ unsigned long flags;
rrpriv = (struct rr_private *)dev->priv;
regs = rrpriv->regs;
-#if 0
- regs->HostCtrl |= (HALT_NIC | RR_CLEAR_INT);
-#endif
+ if (rrpriv->fw_rev < 0x00020000) {
+ printk(KERN_WARNING "%s: trying to configure device with "
+ "obsolete firmware\n", dev->name);
+ ecode = -EBUSY;
+ goto error;
+ }
+
+ rrpriv->rx_ctrl = kmalloc(256*sizeof(struct ring_ctrl),
+ GFP_KERNEL | GFP_DMA);
+ if (!rrpriv->rx_ctrl) {
+ ecode = -ENOMEM;
+ goto error;
+ }
+
+ rrpriv->info = kmalloc(sizeof(struct rr_info), GFP_KERNEL | GFP_DMA);
+ if (!rrpriv->info){
+ kfree(rrpriv->rx_ctrl);
+ ecode = -ENOMEM;
+ goto error;
+ }
+ memset(rrpriv->rx_ctrl, 0, 256 * sizeof(struct ring_ctrl));
+ memset(rrpriv->info, 0, sizeof(struct rr_info));
+ mb();
+
+ spin_lock_irqsave(&rrpriv->lock, flags);
+ writel(readl(®s->HostCtrl)|HALT_NIC|RR_CLEAR_INT, ®s->HostCtrl);
+ spin_unlock_irqrestore(&rrpriv->lock, flags);
if (request_irq(dev->irq, rr_interrupt, SA_SHIRQ, rrpriv->name, dev))
{
printk(KERN_WARNING "%s: Requested IRQ %d is busy\n",
dev->name, dev->irq);
- return -EAGAIN;
+ ecode = -EAGAIN;
+ goto error;
}
- rrpriv->rx_ctrl = kmalloc(256*sizeof(struct ring_ctrl),
- GFP_KERNEL | GFP_DMA);
- rrpriv->info = kmalloc(sizeof(struct rr_info), GFP_KERNEL | GFP_DMA);
-
rr_init1(dev);
dev->tbusy = 0;
-#if 0
- dev->interrupt = 0;
-#endif
dev->start = 1;
MOD_INC_USE_COUNT;
return 0;
+
+ error:
+ spin_lock_irqsave(&rrpriv->lock, flags);
+ writel(readl(®s->HostCtrl)|HALT_NIC|RR_CLEAR_INT, ®s->HostCtrl);
+ spin_unlock_irqrestore(&rrpriv->lock, flags);
+
+ dev->tbusy = 1;
+ dev->start = 0;
+ return -ENOMEM;
}
printk("%s: dumping NIC TX rings\n", dev->name);
printk("RxPrd %08x, TxPrd %02x, EvtPrd %08x, TxPi %02x, TxCtrlPi %02x\n",
- regs->RxPrd, regs->TxPrd, regs->EvtPrd, regs->TxPi,
+ readl(®s->RxPrd), readl(®s->TxPrd),
+ readl(®s->EvtPrd), readl(®s->TxPi),
rrpriv->info->tx_ctrl.pi);
- printk("Error code 0x%x\n", regs->Fail1);
+ printk("Error code 0x%x\n", readl(®s->Fail1));
- index = (((regs->EvtPrd >> 8) & 0xff ) - 1) % EVT_RING_ENTRIES;
+ index = (((readl(®s->EvtPrd) >> 8) & 0xff ) - 1) % EVT_RING_ENTRIES;
cons = rrpriv->dirty_tx;
printk("TX ring index %i, TX consumer %i\n",
index, cons);
if (rrpriv->tx_skbuff[cons]){
len = min(0x80, rrpriv->tx_skbuff[cons]->len);
printk("skbuff for cons %i is valid - dumping data (0x%x bytes - skbuff len 0x%x)\n", cons, len, rrpriv->tx_skbuff[cons]->len);
- printk("mode 0x%x, size 0x%x,\n phys %08x (virt %08x), skbuff-addr %08x, truesize 0x%x\n",
+ printk("mode 0x%x, size 0x%x,\n phys %08x (virt %08lx), skbuff-addr %08lx, truesize 0x%x\n",
rrpriv->tx_ring[cons].mode,
rrpriv->tx_ring[cons].size,
- rrpriv->tx_ring[cons].addr,
- (unsigned int)bus_to_virt(rrpriv->tx_ring[cons].addr),
- (unsigned int)rrpriv->tx_skbuff[cons]->data,
+ rrpriv->tx_ring[cons].addr.addrlo,
+ (unsigned long)bus_to_virt(rrpriv->tx_ring[cons].addr.addrlo),
+ (unsigned long)rrpriv->tx_skbuff[cons]->data,
(unsigned int)rrpriv->tx_skbuff[cons]->truesize);
for (i = 0; i < len; i++){
if (!(i & 7))
printk("mode 0x%x, size 0x%x, phys-addr %08x\n",
rrpriv->tx_ring[i].mode,
rrpriv->tx_ring[i].size,
- rrpriv->tx_ring[i].addr);
+ rrpriv->tx_ring[i].addr.addrlo);
}
*/
spin_lock(&rrpriv->lock);
- tmp = regs->HostCtrl;
+ tmp = readl(®s->HostCtrl);
if (tmp & NIC_HALTED){
printk("%s: NIC already halted\n", dev->name);
rr_dump(dev);
- }else
- tmp |= HALT_NIC;
- regs->HostCtrl = tmp;
+ }else{
+ tmp |= HALT_NIC | RR_CLEAR_INT;
+ writel(tmp, ®s->HostCtrl);
+ mb();
+ }
rrpriv->fw_running = 0;
- regs->TxPi = 0;
- regs->IpRxPi = 0;
+ writel(0, ®s->TxPi);
+ writel(0, ®s->IpRxPi);
- regs->EvtCon = 0;
- regs->EvtPrd = 0;
+ writel(0, ®s->EvtCon);
+ writel(0, ®s->EvtPrd);
for (i = 0; i < CMD_RING_ENTRIES; i++)
- regs->CmdRing[i] = 0;
+ writel(0, ®s->CmdRing[i]);
rrpriv->info->tx_ctrl.entries = 0;
rrpriv->info->cmd_ctrl.pi = 0;
for (i = 0; i < TX_RING_ENTRIES; i++) {
if (rrpriv->tx_skbuff[i]) {
rrpriv->tx_ring[i].size = 0;
- rrpriv->tx_ring[i].addr = 0;
+ set_rraddr(&rrpriv->tx_ring[i].addr, 0);
dev_kfree_skb(rrpriv->tx_skbuff[i]);
}
}
for (i = 0; i < RX_RING_ENTRIES; i++) {
if (rrpriv->rx_skbuff[i]) {
rrpriv->rx_ring[i].size = 0;
- rrpriv->rx_ring[i].addr = 0;
+ set_rraddr(&rrpriv->rx_ring[i].addr, 0);
dev_kfree_skb(rrpriv->rx_skbuff[i]);
}
}
u32 *ifield;
struct sk_buff *new_skb;
+ if (readl(®s->Mode) & FATAL_ERR)
+ printk("error codes Fail1 %02x, Fail2 %02x\n",
+ readl(®s->Fail1), readl(®s->Fail2));
+
/*
* We probably need to deal with tbusy here to prevent overruns.
*/
index = txctrl->pi;
rrpriv->tx_skbuff[index] = skb;
- rrpriv->tx_ring[index].addr = virt_to_bus(skb->data);
+ set_rraddr(&rrpriv->tx_ring[index].addr, skb->data);
rrpriv->tx_ring[index].size = len + 8; /* include IFIELD */
rrpriv->tx_ring[index].mode = PACKET_START | PACKET_END;
txctrl->pi = (index + 1) % TX_RING_ENTRIES;
- regs->TxPi = txctrl->pi;
+ writel(txctrl->pi, ®s->TxPi);
if (txctrl->pi == rrpriv->dirty_tx){
rrpriv->tx_full = 1;
{
struct rr_private *rrpriv;
struct rr_regs *regs;
+ unsigned long eptr, segptr;
int i, j;
- u32 localctrl, eptr, sptr, segptr, len, tmp;
+ u32 localctrl, sptr, len, tmp;
u32 p2len, p2size, nr_seg, revision, io, sram_size;
struct eeprom *hw = NULL;
if (dev->flags & IFF_UP)
return -EBUSY;
- if (!(regs->HostCtrl & NIC_HALTED)){
+ if (!(readl(®s->HostCtrl) & NIC_HALTED)){
printk("%s: Trying to load firmware to a running NIC.\n",
dev->name);
return -EBUSY;
}
- localctrl = regs->LocalCtrl;
- regs->LocalCtrl = 0;
+ localctrl = readl(®s->LocalCtrl);
+ writel(0, ®s->LocalCtrl);
- regs->EvtPrd = 0;
- regs->RxPrd = 0;
- regs->TxPrd = 0;
+ writel(0, ®s->EvtPrd);
+ writel(0, ®s->RxPrd);
+ writel(0, ®s->TxPrd);
/*
* First wipe the entire SRAM, otherwise we might run into all
* kinds of trouble ... sigh, this took almost all afternoon
* to track down ;-(
*/
- io = regs->ExtIo;
- regs->ExtIo = 0;
- sram_size = read_eeprom_word(rrpriv, (void *)8);
+ io = readl(®s->ExtIo);
+ writel(0, ®s->ExtIo);
+ sram_size = rr_read_eeprom_word(rrpriv, (void *)8);
for (i = 200; i < sram_size / 4; i++){
- regs->WinBase = i * 4;
- regs->WinData = 0;
+ writel(i * 4, ®s->WinBase);
+ mb();
+ writel(0, ®s->WinData);
+ mb();
}
- regs->ExtIo = io;
+ writel(io, ®s->ExtIo);
+ mb();
- eptr = read_eeprom_word(rrpriv, &hw->rncd_info.AddrRunCodeSegs);
+ eptr = (unsigned long)rr_read_eeprom_word(rrpriv,
+ &hw->rncd_info.AddrRunCodeSegs);
eptr = ((eptr & 0x1fffff) >> 3);
- p2len = read_eeprom_word(rrpriv, (void *)(0x83*4));
+ p2len = rr_read_eeprom_word(rrpriv, (void *)(0x83*4));
p2len = (p2len << 2);
- p2size = read_eeprom_word(rrpriv, (void *)(0x84*4));
+ p2size = rr_read_eeprom_word(rrpriv, (void *)(0x84*4));
p2size = ((p2size & 0x1fffff) >> 3);
if ((eptr < p2size) || (eptr > (p2size + p2len))){
goto out;
}
- revision = read_eeprom_word(rrpriv, &hw->manf.HeaderFmt);
+ revision = rr_read_eeprom_word(rrpriv, &hw->manf.HeaderFmt);
if (revision != 1){
printk("%s: invalid firmware format (%i)\n",
goto out;
}
- nr_seg = read_eeprom_word(rrpriv, (void *)eptr);
+ nr_seg = rr_read_eeprom_word(rrpriv, (void *)eptr);
eptr +=4;
#if (DEBUG > 1)
printk("%s: nr_seg %i\n", dev->name, nr_seg);
#endif
for (i = 0; i < nr_seg; i++){
- sptr = read_eeprom_word(rrpriv, (void *)eptr);
+ sptr = rr_read_eeprom_word(rrpriv, (void *)eptr);
eptr += 4;
- len = read_eeprom_word(rrpriv, (void *)eptr);
+ len = rr_read_eeprom_word(rrpriv, (void *)eptr);
eptr += 4;
- segptr = read_eeprom_word(rrpriv, (void *)eptr);
+ segptr = (unsigned long)rr_read_eeprom_word(rrpriv, (void *)eptr);
segptr = ((segptr & 0x1fffff) >> 3);
eptr += 4;
#if (DEBUG > 1)
dev->name, i, sptr, len, segptr);
#endif
for (j = 0; j < len; j++){
- tmp = read_eeprom_word(rrpriv, (void *)segptr);
- regs->WinBase = sptr;
- regs->WinData = tmp;
+ tmp = rr_read_eeprom_word(rrpriv, (void *)segptr);
+ writel(sptr, ®s->WinBase);
+ mb();
+ writel(tmp, ®s->WinData);
+ mb();
segptr += 4;
sptr += 4;
}
}
out:
- regs->LocalCtrl = localctrl;
+ writel(localctrl, ®s->LocalCtrl);
+ mb();
return 0;
}
error = -ENOMEM;
goto out;
}
- i = read_eeprom(rrpriv, 0, image, EEPROM_BYTES);
+ i = rr_read_eeprom(rrpriv, 0, image, EEPROM_BYTES);
if (i != EEPROM_BYTES){
kfree(image);
printk(KERN_ERR "%s: Error reading EEPROM\n",
}
oldimage = kmalloc(EEPROM_WORDS * sizeof(u32), GFP_KERNEL);
- if (!image){
+ if (!oldimage){
printk(KERN_ERR "%s: Unable to allocate memory "
"for old EEPROM image\n", dev->name);
error = -ENOMEM;
printk(KERN_ERR "%s: Error writing EEPROM\n",
dev->name);
- i = read_eeprom(rrpriv, 0, oldimage, EEPROM_BYTES);
+ i = rr_read_eeprom(rrpriv, 0, oldimage, EEPROM_BYTES);
if (i != EEPROM_BYTES)
printk(KERN_ERR "%s: Error reading back EEPROM "
"image\n", dev->name);
dev->name);
error = -EFAULT;
}
-
kfree(image);
kfree(oldimage);
break;
/*
* Local variables:
- * compile-command: "gcc -D__SMP__ -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -pipe -fomit-frame-pointer -fno-strength-reduce -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=686 -c rrunner.c"
+ * compile-command: "gcc -D__SMP__ -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -pipe -fomit-frame-pointer -fno-strength-reduce -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=686 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -c rrunner.c"
* End:
*/
#define TRACE_ON_WHAT_BIT 0x00020000 /* Traces on */
#define ONEM_BUF_WHAT_BIT 0x00040000 /* 1Meg vs 256K */
#define CHAR_API_WHAT_BIT 0x00080000 /* Char API vs network only */
-#define MS_DOS_WHAT_BIT 0x00100000 /* MS_DOS */
#define CMD_EVT_WHAT_BIT 0x00200000 /* Command event */
#define LONG_TX_WHAT_BIT 0x00400000
#define LONG_RX_WHAT_BIT 0x00800000
#define SAME_IFIELD 0x80
+typedef struct {
+#if (BITS_PER_LONG == 64)
+ u64 addrlo;
+#else
+ u32 addrhi;
+ u32 addrlo;
+#endif
+} rraddr;
+
+
+static inline void set_rraddr(rraddr *ra, volatile void *addr)
+{
+ unsigned long baddr = virt_to_bus((void *)addr);
+#if (BITS_PER_LONG == 64)
+ ra->addrlo = baddr;
+#else
+ /* Don't bother setting zero every time */
+ ra->addrlo = baddr;
+#endif
+ mb();
+}
+
+
+static inline void set_rxaddr(struct rr_regs *regs, volatile void *addr)
+{
+ unsigned long baddr = virt_to_bus((void *)addr);
+#if (BITS_PER_LONG == 64) && defined(__LITTLE_ENDIAN)
+ writel(baddr & 0xffffffff, ®s->RxRingHi);
+ writel(baddr >> 32, ®s->RxRingLo);
+#elif (BITS_PER_LONG == 64)
+ writel(baddr >> 32, ®s->RxRingHi);
+ writel(baddr & 0xffffffff, ®s->RxRingLo);
+#else
+ writel(0, ®s->RxRingHi);
+ writel(baddr, ®s->RxRingLo);
+#endif
+ mb();
+}
+
+
+static inline void set_infoaddr(struct rr_regs *regs, volatile void *addr)
+{
+ unsigned long baddr = virt_to_bus((void *)addr);
+#if (BITS_PER_LONG == 64) && defined(__LITTLE_ENDIAN)
+ writel(baddr & 0xffffffff, ®s->InfoPtrHi);
+ writel(baddr >> 32, ®s->InfoPtrLo);
+#elif (BITS_PER_LONG == 64)
+ writel(baddr >> 32, ®s->InfoPtrHi);
+ writel(baddr & 0xffffffff, ®s->InfoPtrLo);
+#else
+ writel(0, ®s->InfoPtrHi);
+ writel(baddr, ®s->InfoPtrLo);
+#endif
+ mb();
+}
+
+
/*
* TX ring
*/
#define TX_RING_SIZE (TX_RING_ENTRIES * sizeof(struct tx_desc))
struct tx_desc{
-#if (BITS_PER_LONG == 64)
- u64 addr;
-#else
- u32 zero;
- u32 addr;
-#endif
+ rraddr addr;
u32 res;
#ifdef __LITTLE_ENDIAN
u16 size;
#define RX_RING_SIZE (RX_RING_ENTRIES * sizeof(struct rx_desc))
struct rx_desc{
-#if (BITS_PER_LONG == 64)
- u64 addr;
-#else
- u32 zero;
- u32 addr;
-#endif
+ rraddr addr;
u32 res;
#ifdef __LITTLE_ENDIAN
u16 size;
* This struct is shared with the NIC firmware.
*/
struct ring_ctrl {
-#if (BITS_PER_LONG == 64)
- u64 rngptr;
-#else
- u32 zero;
- u32 rngptr;
-#endif
+ rraddr rngptr;
#ifdef __LITTLE_ENDIAN
u16 entries;
u8 pad;
struct rx_desc rx_ring[RX_RING_ENTRIES];
struct tx_desc tx_ring[TX_RING_ENTRIES];
struct event evt_ring[EVT_RING_ENTRIES];
- struct sk_buff *tx_skbuff[TX_RING_ENTRIES];
struct sk_buff *rx_skbuff[RX_RING_ENTRIES];
+ struct sk_buff *tx_skbuff[TX_RING_ENTRIES];
struct rr_regs *regs; /* Register base */
struct ring_ctrl *rx_ctrl; /* Receive ring control */
struct rr_info *info; /* Shared info page */
+ struct device *next;
spinlock_t lock;
struct timer_list timer;
u32 cur_rx, cur_cmd, cur_evt;
u32 dirty_rx, dirty_tx;
u32 tx_full;
+ u32 fw_rev;
short fw_running;
- u8 pci_bus; /* PCI bus number */
- u8 pci_dev_fun; /* PCI device numbers */
char name[24]; /* The assigned name */
struct net_device_stats stats;
};
static int rr_close(struct device *dev);
static struct net_device_stats *rr_get_stats(struct device *dev);
static int rr_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+static unsigned int rr_read_eeprom(struct rr_private *rrpriv,
+ unsigned long offset,
+ unsigned char *buf,
+ unsigned long length);
+static u32 rr_read_eeprom_word(struct rr_private *rrpriv, void * offset);
+static int rr_load_firmware(struct device *dev);
#endif /* _RRUNNER_H_ */
#include <asm/spinlock.h>
#include "z85230.h"
+#include "syncppp.h"
static spinlock_t z8530_buffer_lock = SPIN_LOCK_UNLOCKED;
return 0;
}
}
- if(scancode == 0) {
- prev_scancode = 0;
- return 0;
- }
return 1;
}
-int pcikbd_pretranslate(unsigned char scancode, char raw_mode)
+int pcikbd_translate(unsigned char scancode, unsigned char *keycode,
+ char raw_mode)
{
- if(scancode == 0xff) {
- prev_scancode = 0;
+ static int prev_scancode = 0;
+
+ if (scancode == 0xe0 || scancode == 0xe1) {
+ prev_scancode = scancode;
return 0;
}
- if(scancode == 0xe0 || scancode == 0xe1) {
- prev_scancode = scancode;
+ if (scancode == 0x00 || scancode == 0xff) {
+ prev_scancode = 0;
return 0;
}
- return 1;
-}
-
-int pcikbd_translate(unsigned char scancode, unsigned char *keycode,
- char raw_mode)
-{
if(prev_scancode) {
if(prev_scancode != 0xe0) {
if(prev_scancode == 0xe1 && scancode == 0x1d) {
break;
scancode = pcikbd_inb(pcikbd_iobase + KBD_DATA_REG);
if((status & KBD_STAT_OBF) && do_acknowledge(scancode))
- handle_scancode(scancode);
+ handle_scancode(scancode, !(scancode & 0x80));
status = pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG);
} while(status & KBD_STAT_OBF);
mark_bh(KEYBOARD_BH);
--- /dev/null
+Credits for the Simple Linux USB Driver:
+
+The following people have contributed to this code (in alphabetical
+order by last name). I'm sure this list should be longer, its
+difficult to maintain.
+
+ Johannes Erdfelt <jerdfelt@sventech.com>
+ ham <ham@unsuave.com>
+ Bradley M Keryan <keryan@andrew.cmu.edu>
+ Vojtech Pavlik <vojtech@twilight.ucw.cz>
+ Gregory P. Smith <greg@electricrain.com>
+ Linus Torvalds <torvalds@transmeta.com>
+ <Kazuki.Yasumatsu@fujixerox.co.jp>
+
+Special thanks to:
+
+ Inaky Perez Gonzalez <inaky@peloncho.fis.ucm.es> for starting the
+ Linux USB driver effort and writing much of the larger uusbd driver.
+ Much has been learned from that effort.
+
+ The NetBSD & FreeBSD USB developers. For being on the Linux USB list
+ and offering suggestions and sharing implementation experiences.
+
--- /dev/null
+#
+# USB device configuration
+#
+# NOTE NOTE NOTE! This is still considered extremely experimental.
+# Right now hubs, mice and keyboards work - at least with UHCI.
+# But that may be more a lucky coincidence than anything else..
+#
+# This was all developed modularly, but I've been lazy in cleaning
+# it up, so right now they are all bools.
+#
+mainmenu_option next_comment
+comment 'USB drivers - not for the faint of heart'
+
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool 'Support for USB (EXPERIMENTAL!)' CONFIG_USB
+ if [ "$CONFIG_USB" = "y" ]; then
+ bool 'UHCI (intel PIIX4 and others) support?' CONFIG_USB_UHCI
+ bool 'OHCI (compaq and some others) support?' CONFIG_USB_OHCI
+
+ bool 'USB mouse support' CONFIG_USB_MOUSE
+ bool 'USB keyboard support' CONFIG_USB_KBD
+ fi
+fi
+
+endmenu
--- /dev/null
+#
+# Makefile for the kernel usb device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+#
+# This isn't actually supported yet. Don't try to use it.
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := usb.a
+M_OBJS :=
+L_OBJS := usb.o hub.o usb-debug.o
+LX_OBJS :=
+
+ifeq ($(CONFIG_USB_UHCI),y)
+ L_OBJS += uhci.o uhci-debug.o
+else
+ ifeq ($(CONFIG_USB_UHCI),m)
+ MX_OBJS += usb-uhci.o uhci-debug.o
+ endif
+endif
+
+ifeq ($(CONFIG_USB_OHCI),y)
+ L_OBJS += ohci.o ohci-debug.o
+else
+ ifeq ($(CONFIG_USB_UHCI),m)
+ MX_OBJS += ohci.o ohci-debug.o
+ endif
+endif
+
+ifeq ($(CONFIG_USB_MOUSE),y)
+ L_OBJS += mouse.o
+else
+ ifeq ($(CONFIG_USB_UHCI),m)
+ MX_OBJS += mouse.o
+ endif
+endif
+
+ifeq ($(CONFIG_USB_KBD),y)
+ L_OBJS += keyboard.o keymap.o
+else
+ ifeq ($(CONFIG_USB_UHCI),m)
+ MX_OBJS += keyboard.o
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+keymap.o: keymap.c
+
+keymap.c: maps/serial.map maps/usb.map maps/fixup.map
+ ./mkmap > $@
--- /dev/null
+This is a simple USB keyboard driver written from Linus'
+USB driver (started with Greg's usb-0.03b.tar.gz source
+tree)
+
+It works fine with my BTC keyboard but I'm still investigating
+trouble with my MS keyboard (trouble starts with an inability
+to set into boot protocol mode, though, this very well could
+be all due to crappy hardware).
+
+Anyway, I would appreciate you taking a look if you have
+any USB keyboards lying around. Oh also, I'm doing this on
+UHCI so sorry if it breaks with OHCI.
+
+-ham
+
+
+
+Keyboard patch
+--------------
+
+Instead of using the multiple keyboard patch and then running into all
+of the kernel version problems that the current Linux-USB project has
+had, I'm just mapping the USB keycodes to the standard AT-101 keycodes
+and sending them directly to "handle_scancode".
+
+This may or may not be considered a hack. Anyway it is effective, and
+I think safe, and allows USB keyboards to coexist with a serial
+keyboard (oh yeah, one side effect is that you can for example hold
+down the control key on the serial keyboard and press "a" on the USB
+keyboard and you get Control-a like with Windows USB) and works
+fine for console and X.
+
+You do need to make a *tiny* patch the kernel source tree so that the
+function "handle_scancode" is exported from keyboard.c though.
+
+ $ cd /usr/src/linux
+ $ patch -p0 < kbd.patch
+
+And, of course, then, you need to rebuild and install the kernel.
+
+** [Vojtech]: Alternately, just 'insmod kbd-stub', if you don't want
+to use the keyboard and are too lazy to patch the kernel.
+
+Keyboard map
+------------
+
+I'm including a stupid utility "mkmap" which generates the USB->serial
+keymap. It takes in maps/serial.map (the current serial keymap,
+generated by "dumpkeys"), maps/usb.map (the USB keymap), and
+maps/fixup.map (fixes for e0 keys and misc.) and spits out keymap.c
+Anyway, it is not beautiful but should serve its purpose for the
+moment.
+
+Other changes
+-------------
+uhci.c:
+ * added a context value to the irq callback function
+ (this is exactly like the "dev_id" field to request_irq)
+ * played with uhci_reset_port to get better hot-plug results
+ (eg. do a wait_ms(200) before calling uhci_reset_port)
+usb.c:
+ * disconnect all devices after uhci-control thread is killed
+ * skip over the HID descriptor
+ * disconnect the high-level driver in usb_disconnect
+
--- /dev/null
+April 24, 1999 04:37:42 PST
+
+Okay, I've written a lot more of the OHCI code and actually got it back to
+a compiling state now with all of the recent improvements to the way stuff
+is structured. It is completely untested.
+
+- greg@electricrain.com
+
--- /dev/null
+/*
+ * USB hub driver.
+ *
+ * This is horrible, it knows about the UHCI driver
+ * internals, but it's just meant as a rough example,
+ * let's do the virtualization later when this works.
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999 Johannes Erdfelt
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/malloc.h>
+#include <linux/smp_lock.h>
+
+#include <asm/spinlock.h>
+
+#include "usb.h"
+#include "uhci.h"
+#include "hub.h"
+
+extern struct usb_operations uhci_device_operations;
+
+/* Wakes up khubd */
+static struct wait_queue *usb_hub_wait = NULL;
+static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED;
+
+/* List of hubs needing servicing */
+static struct list_head hub_event_list;
+
+/*
+ * A irq handler returns non-zero to indicate to
+ * the low-level driver that it wants to be re-activated,
+ * or zero to say "I'm done".
+ */
+static int hub_irq(int status, void *__buffer, void *dev_id)
+{
+ struct usb_hub *hub = dev_id;
+ unsigned long flags;
+
+ if (waitqueue_active(&usb_hub_wait)) {
+ /* Add the hub to the event queue */
+ spin_lock_irqsave(&hub_event_lock, flags);
+ if (hub->event_list.next == &hub->event_list) {
+ list_add(&hub->event_list, &hub_event_list);
+ /* Wake up khubd */
+ wake_up(&usb_hub_wait);
+ }
+ spin_unlock_irqrestore(&hub_event_lock, flags);
+ }
+
+ return 1;
+}
+
+static void usb_hub_configure(struct usb_hub *hub)
+{
+ struct usb_device *dev = hub->dev;
+ unsigned char hubdescriptor[8], buf[4];
+ int charac, i;
+
+ usb_set_configuration(dev, dev->config[0].bConfigurationValue);
+
+ if (usb_get_hub_descriptor(dev, hubdescriptor, 8))
+ return;
+
+ hub->nports = dev->maxchild = hubdescriptor[2];
+ printk("hub: %d-port%s detected\n", hub->nports,
+ (hub->nports == 1) ? "" : "s");
+
+ charac = (hubdescriptor[4] << 8) + hubdescriptor[3];
+ switch (charac & HUB_CHAR_LPSM) {
+ case 0x00:
+ printk("hub: ganged power switching\n");
+ break;
+ case 0x01:
+ printk("hub: individual port power switching\n");
+ break;
+ case 0x02:
+ case 0x03:
+ printk("hub: unknown reserved power switching mode\n");
+ break;
+ }
+
+ if (charac & HUB_CHAR_COMPOUND)
+ printk("hub: part of a compound device\n");
+ else
+ printk("hub: standalone hub\n");
+
+ switch (charac & HUB_CHAR_OCPM) {
+ case 0x00:
+ printk("hub: global over current protection\n");
+ break;
+ case 0x08:
+ printk("hub: individual port over current protection\n");
+ break;
+ case 0x10:
+ case 0x18:
+ printk("hub: no over current protection\n");
+ break;
+ }
+
+ printk("hub: power on to power good time: %dms\n",
+ hubdescriptor[5] * 2);
+
+ printk("hub: hub controller current requirement: %dmA\n",
+ hubdescriptor[6]);
+
+ for (i = 0; i < dev->maxchild; i++)
+ printk("hub: port %d is%s removable\n", i + 1,
+ hubdescriptor[7 + ((i + 1)/8)] & (1 << ((i + 1) % 8))
+ ? " not" : "");
+
+ if (usb_get_hub_status(dev, buf))
+ return;
+
+ printk("hub: local power source is %s\n",
+ (buf[0] & 1) ? "lost (inactive)" : "good");
+
+ printk("hub: %sover current condition exists\n",
+ (buf[0] & 2) ? "" : "no ");
+
+#if 0
+ for (i = 0; i < hub->nports; i++) {
+ int portstat, portchange;
+ unsigned char portstatus[4];
+
+ if (usb_get_port_status(dev, i + 1, portstatus))
+ return;
+ portstat = (portstatus[1] << 8) + portstatus[0];
+ portchange = (portstatus[3] << 8) + portstatus[2];
+
+ printk("hub: port %d status\n", i + 1);
+ printk("hub: %sdevice present\n", (portstat & 1) ? "" : "no ");
+ printk("hub: %s\n", (portstat & 2) ? "enabled" : "disabled");
+ printk("hub: %ssuspended\n", (portstat & 4) ? "" : "not ");
+ printk("hub: %sover current\n", (portstat & 8) ? "" : "not ");
+ printk("hub: has %spower\n", (portstat & 0x100) ? "" : "no ");
+ printk("hub: %s speed\n", (portstat & 0x200) ? "low" : "full");
+ }
+#endif
+
+ /* Enable power to the ports */
+ printk("enabling power on all ports\n");
+ for (i = 0; i < hub->nports; i++)
+ usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER);
+}
+
+static int hub_probe(struct usb_device *dev)
+{
+ struct usb_interface_descriptor *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_hub *hub;
+
+ /* We don't handle multi-config hubs */
+ if (dev->descriptor.bNumConfigurations != 1)
+ return -1;
+
+ /* We don't handle multi-interface hubs */
+ if (dev->config[0].bNumInterfaces != 1)
+ return -1;
+
+ interface = &dev->config[0].interface[0];
+
+ /* Is it a hub? */
+ if (interface->bInterfaceClass != 9)
+ return -1;
+ if (interface->bInterfaceSubClass != 0)
+ return -1;
+
+ /* Multiple endpoints? What kind of mutant ninja-hub is this? */
+ if (interface->bNumEndpoints != 1)
+ return -1;
+
+ endpoint = &interface->endpoint[0];
+
+ /* Output endpoint? Curiousier and curiousier.. */
+ if (!(endpoint->bEndpointAddress & 0x80))
+ return -1;
+
+ /* If it's not an interrupt endpoint, we'd better punt! */
+ if ((endpoint->bmAttributes & 3) != 3)
+ return -1;
+
+ /* We found a hub */
+ printk("USB hub found\n");
+
+ if ((hub = kmalloc(sizeof(*hub), GFP_KERNEL)) == NULL) {
+ printk("couldn't kmalloc hub struct\n");
+ return -1;
+ }
+
+ memset(hub, 0, sizeof(*hub));
+
+ dev->private = hub;
+
+ INIT_LIST_HEAD(&hub->event_list);
+ hub->dev = dev;
+
+ usb_hub_configure(hub);
+
+ usb_request_irq(dev, usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), hub_irq, endpoint->bInterval, hub);
+
+ /* Wake up khubd */
+ wake_up(&usb_hub_wait);
+
+ return 0;
+}
+
+static void hub_disconnect(struct usb_device *dev)
+{
+ struct usb_hub *hub = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hub_event_lock, flags);
+
+ /* Delete it and then reset it */
+ list_del(&hub->event_list);
+ INIT_LIST_HEAD(&hub->event_list);
+
+ spin_unlock_irqrestore(&hub_event_lock, flags);
+
+ /* Free the memory */
+ kfree(hub);
+}
+
+static void usb_hub_port_connect_change(struct usb_device *hub, int port)
+{
+ struct usb_device *usb;
+ unsigned char buf[4];
+ unsigned short portstatus, portchange;
+
+ usb_disconnect(&hub->children[port]);
+
+ usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET);
+
+ wait_ms(50); /* FIXME: This is from the *BSD stack, thanks! :) */
+
+ if (usb_get_port_status(hub, port + 1, buf)) {
+ printk("get_port_status failed\n");
+ return;
+ }
+
+ portstatus = *((unsigned short *)buf + 0);
+ portchange = *((unsigned short *)buf + 1);
+
+ if ((!(portstatus & USB_PORT_STAT_CONNECTION)) &&
+ (!(portstatus & USB_PORT_STAT_ENABLE))) {
+ /* We're done now, we already disconnected the device */
+ /* printk("not connected/enabled\n"); */
+ return;
+ }
+
+ usb = hub->bus->op->allocate(hub);
+ if (!usb) {
+ printk("couldn't allocate usb_device\n");
+ return;
+ }
+
+ usb_connect(usb);
+
+ usb->slow = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0;
+
+ hub->children[port] = usb;
+
+ usb_new_device(usb);
+}
+
+static void usb_hub_events(void)
+{
+ unsigned long flags;
+ unsigned char buf[4];
+ unsigned short portstatus, portchange;
+ int i;
+ struct list_head *next, *tmp, *head = &hub_event_list;
+ struct usb_device *dev;
+ struct usb_hub *hub;
+
+ spin_lock_irqsave(&hub_event_lock, flags);
+
+ tmp = head->next;
+ while (tmp != head) {
+ hub = list_entry(tmp, struct usb_hub, event_list);
+ dev = hub->dev;
+
+ next = tmp->next;
+
+ list_del(tmp);
+ INIT_LIST_HEAD(tmp);
+
+ for (i = 0; i < hub->nports; i++) {
+ if (usb_get_port_status(dev, i + 1, buf)) {
+ printk("get_port_status failed\n");
+ continue;
+ }
+
+ portstatus = *((unsigned short *)buf + 0);
+ portchange = *((unsigned short *)buf + 1);
+
+ if (portchange & USB_PORT_STAT_C_CONNECTION) {
+ printk("hub: port %d connection change\n", i + 1);
+
+ usb_clear_port_feature(dev, i + 1,
+ USB_PORT_FEAT_C_CONNECTION);
+
+ usb_hub_port_connect_change(dev, i);
+ }
+
+ if (portchange & USB_PORT_STAT_C_ENABLE) {
+ printk("hub: port %d enable change\n", i + 1);
+ usb_clear_port_feature(dev, i + 1,
+ USB_PORT_FEAT_C_ENABLE);
+ }
+
+ if (portchange & USB_PORT_STAT_C_SUSPEND)
+ printk("hub: port %d suspend change\n", i + 1);
+
+ if (portchange & USB_PORT_STAT_C_OVERCURRENT)
+ printk("hub: port %d over-current change\n", i + 1);
+
+ if (portchange & USB_PORT_STAT_C_RESET) {
+ printk("hub: port %d reset change\n", i + 1);
+ usb_clear_port_feature(dev, i + 1,
+ USB_PORT_FEAT_C_RESET);
+ }
+
+#if 0
+ if (!portchange)
+ continue;
+
+ if (usb_get_port_status(dev, i + 1, buf))
+ return;
+
+ portstatus = (buf[1] << 8) + buf[0];
+ portchange = (buf[3] << 8) + buf[2];
+
+ printk("hub: port %d status\n", i + 1);
+ printk("hub: %sdevice present\n", (portstatus & 1) ? "" : "no ");
+ printk("hub: %s\n", (portstatus & 2) ? "enabled" : "disabled");
+ printk("hub: %ssuspended\n", (portstatus & 4) ? "" : "not ");
+ printk("hub: %sover current\n", (portstatus & 8) ? "" : "not ");
+ printk("hub: has %spower\n", (portstatus & 0x100) ? "" : "no ");
+ printk("hub: %s speed\n", (portstatus & 0x200) ? "low" : "full");
+#endif
+ }
+ tmp = next;
+#if 0
+ wait_ms(1000);
+#endif
+ }
+
+ spin_unlock_irqrestore(&hub_event_lock, flags);
+}
+
+static int usb_hub_thread(void *__hub)
+{
+ lock_kernel();
+
+ /*
+ * This thread doesn't need any user-level access,
+ * so get rid of all our resources
+ */
+ printk("usb_hub_thread at %p\n", &usb_hub_thread);
+ exit_mm(current);
+ exit_files(current);
+ exit_fs(current);
+
+ /* Setup a nice name */
+ strcpy(current->comm, "khubd");
+
+ /* Send me a signal to get me die (for debugging) */
+ do {
+ interruptible_sleep_on(&usb_hub_wait);
+ usb_hub_events();
+ } while (!signal_pending(current));
+
+ printk("usb_hub_thread exiting\n");
+
+ return 0;
+}
+
+static struct usb_driver hub_driver = {
+ "hub",
+ hub_probe,
+ hub_disconnect,
+ { NULL, NULL }
+};
+
+/*
+ * This should be a separate module.
+ */
+int hub_init(void)
+{
+ int pid;
+
+ INIT_LIST_HEAD(&hub_event_list);
+
+ usb_register(&hub_driver);
+ pid = kernel_thread(usb_hub_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ if (pid >= 0)
+ return 0;
+
+ /* Fall through if kernel_thread failed */
+ usb_deregister(&hub_driver);
+
+ return 0;
+}
+
--- /dev/null
+#ifndef __LINUX_HUB_H
+#define __LINUX_HUB_H
+
+#include <linux/list.h>
+
+/*
+ * Hub feature numbers
+ */
+#define C_HUB_LOCAL_POWER 0
+#define C_HUB_OVER_CURRENT 1
+
+/*
+ * Port feature numbers
+ */
+#define USB_PORT_FEAT_ENABLE 1
+#define USB_PORT_FEAT_SUSPEND 2
+#define USB_PORT_FEAT_OVER_CURRENT 3
+#define USB_PORT_FEAT_RESET 4
+#define USB_PORT_FEAT_POWER 8
+#define USB_PORT_FEAT_LOWSPEED 9
+#define USB_PORT_FEAT_C_CONNECTION 16
+#define USB_PORT_FEAT_C_ENABLE 17
+#define USB_PORT_FEAT_C_SUSPEND 18
+#define USB_PORT_FEAT_C_OVER_CURRENT 19
+#define USB_PORT_FEAT_C_RESET 20
+
+/* wPortStatus */
+#define USB_PORT_STAT_CONNECTION 0x0001
+#define USB_PORT_STAT_ENABLE 0x0002
+#define USB_PORT_STAT_SUSPEND 0x0004
+#define USB_PORT_STAT_OVERCURRENT 0x0008
+#define USB_PORT_STAT_RESET 0x0010
+#define USB_PORT_STAT_POWER 0x0100
+#define USB_PORT_STAT_LOW_SPEED 0x0200
+
+/* wPortChange */
+#define USB_PORT_STAT_C_CONNECTION 0x0001
+#define USB_PORT_STAT_C_ENABLE 0x0002
+#define USB_PORT_STAT_C_SUSPEND 0x0004
+#define USB_PORT_STAT_C_OVERCURRENT 0x0008
+#define USB_PORT_STAT_C_RESET 0x0010
+
+/* Characteristics */
+#define HUB_CHAR_LPSM 0x0003
+#define HUB_CHAR_COMPOUND 0x0004
+#define HUB_CHAR_OCPM 0x0018
+
+struct usb_device;
+
+typedef enum {
+ USB_PORT_UNPOWERED = 0, /* Default state */
+ USB_PORT_POWERED, /* When we've put power to it */
+ USB_PORT_ENABLED, /* When it's been enabled */
+ USB_PORT_DISABLED, /* If it's been disabled */
+ USB_PORT_ADMINDISABLED, /* Forced down */
+} usb_hub_port_state;
+
+struct usb_hub_port {
+ usb_hub_port_state cstate; /* Configuration state */
+
+ struct usb_device *child; /* Device attached to this port */
+
+ struct usb_hub *parent; /* Parent hub */
+};
+
+struct usb_hub {
+ /* Device structure */
+ struct usb_device *dev;
+
+ /* Temporary event list */
+ struct list_head event_list;
+
+ /* Number of ports on the hub */
+ int nports;
+
+ struct usb_hub_port ports[0]; /* Dynamically allocated */
+};
+
+#endif
+
--- /dev/null
+int bp_mouse_init(void);
+int usb_kbd_init(void);
+int hub_init(void);
--- /dev/null
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/kbd_ll.h>
+#include "usb.h"
+
+#define PCKBD_PRESSED 0x00
+#define PCKBD_RELEASED 0x80
+#define PCKBD_NEEDS_E0 0x80
+
+#define USBKBD_MODIFIER_BASE 120
+#define USBKBD_KEYCODE_OFFSET 2
+#define USBKBD_KEYCODE_COUNT 6
+
+#define USBKBD_VALID_KEYCODE(key) ((unsigned char)(key) > 3)
+#define USBKBD_FIND_KEYCODE(down, key, count) \
+ ((unsigned char*) memscan((down), (key), (count)) < ((down) + (count)))
+
+#define USBKBD_REPEAT_DELAY (HZ / 4)
+#define USBKBD_REPEAT_RATE (HZ / 20)
+
+struct usb_keyboard
+{
+ struct usb_device *dev;
+ unsigned long down[2];
+ unsigned char repeat_key;
+ struct timer_list repeat_timer;
+ struct list_head list;
+};
+
+extern unsigned char usb_kbd_map[];
+
+static int usb_kbd_probe(struct usb_device *dev);
+static void usb_kbd_disconnect(struct usb_device *dev);
+static void usb_kbd_repeat(unsigned long dummy);
+
+static LIST_HEAD(usb_kbd_list);
+
+static struct usb_driver usb_kbd_driver =
+{
+ "keyboard",
+ usb_kbd_probe,
+ usb_kbd_disconnect,
+ {NULL, NULL}
+};
+
+
+static void
+usb_kbd_handle_key(unsigned char key, int down)
+{
+ int scancode = (int) usb_kbd_map[key];
+ if(scancode)
+ {
+ if(scancode & PCKBD_NEEDS_E0)
+ {
+ handle_scancode(0xe0, 1);
+ }
+ handle_scancode((scancode & ~PCKBD_NEEDS_E0), down);
+ }
+}
+
+static void
+usb_kbd_repeat(unsigned long dev_id)
+{
+ struct usb_keyboard *kbd = (struct usb_keyboard*) dev_id;
+
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+
+ if(kbd->repeat_key)
+ {
+ usb_kbd_handle_key(kbd->repeat_key, 1);
+
+ /* reset repeat timer */
+ kbd->repeat_timer.function = usb_kbd_repeat;
+ kbd->repeat_timer.expires = jiffies + USBKBD_REPEAT_RATE;
+ kbd->repeat_timer.data = (unsigned long) kbd;
+ kbd->repeat_timer.prev = NULL;
+ kbd->repeat_timer.next = NULL;
+ add_timer(&kbd->repeat_timer);
+ }
+
+ restore_flags(flags);
+}
+
+static int
+usb_kbd_irq(int state, void *buffer, void *dev_id)
+{
+ struct usb_keyboard *kbd = (struct usb_keyboard*) dev_id;
+ unsigned long *down = (unsigned long*) buffer;
+
+ if(kbd->down[0] != down[0] || kbd->down[1] != down[1])
+ {
+ unsigned char *olddown, *newdown;
+ unsigned char modsdelta, key;
+ int i;
+
+ /* handle modifier change */
+ modsdelta = (*(unsigned char*) down ^ *(unsigned char*) kbd->down);
+ if(modsdelta)
+ {
+ for(i = 0; i < 8; i++)
+ {
+ if(modsdelta & 0x01)
+ {
+ int pressed = (*(unsigned char*) down >> i) & 0x01;
+ usb_kbd_handle_key(
+ i + USBKBD_MODIFIER_BASE,
+ pressed);
+ }
+ modsdelta >>= 1;
+ }
+ }
+
+ olddown = (unsigned char*) kbd->down + USBKBD_KEYCODE_OFFSET;
+ newdown = (unsigned char*) down + USBKBD_KEYCODE_OFFSET;
+
+ /* handle released keys */
+ for(i = 0; i < USBKBD_KEYCODE_COUNT; i++)
+ {
+ key = olddown[i];
+ if(USBKBD_VALID_KEYCODE(key)
+ && !USBKBD_FIND_KEYCODE(newdown, key, USBKBD_KEYCODE_COUNT))
+ {
+ usb_kbd_handle_key(key, 0);
+ }
+ }
+
+ /* handle pressed keys */
+ kbd->repeat_key = 0;
+ for(i = 0; i < USBKBD_KEYCODE_COUNT; i++)
+ {
+ key = newdown[i];
+ if(USBKBD_VALID_KEYCODE(key)
+ && !USBKBD_FIND_KEYCODE(olddown, key, USBKBD_KEYCODE_COUNT))
+ {
+ usb_kbd_handle_key(key, 1);
+ kbd->repeat_key = key;
+ }
+ }
+
+ /* set repeat timer if any keys were pressed */
+ if(kbd->repeat_key)
+ {
+ del_timer(&kbd->repeat_timer);
+ kbd->repeat_timer.function = usb_kbd_repeat;
+ kbd->repeat_timer.expires = jiffies + USBKBD_REPEAT_DELAY;
+ kbd->repeat_timer.data = (unsigned long) kbd;
+ kbd->repeat_timer.prev = NULL;
+ kbd->repeat_timer.next = NULL;
+ add_timer(&kbd->repeat_timer);
+ }
+
+ kbd->down[0] = down[0];
+ kbd->down[1] = down[1];
+ }
+
+ return 1;
+}
+
+static int
+usb_kbd_probe(struct usb_device *dev)
+{
+ struct usb_interface_descriptor *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_keyboard *kbd;
+
+ interface = &dev->config[0].interface[0];
+ endpoint = &interface->endpoint[0];
+
+ if(interface->bInterfaceClass != 3
+ || interface->bInterfaceSubClass != 1
+ || interface->bInterfaceProtocol != 1)
+ {
+ return -1;
+ }
+
+ printk(KERN_INFO "USB HID boot protocol keyboard detected.\n");
+
+ kbd = kmalloc(sizeof(struct usb_keyboard), GFP_KERNEL);
+ if(kbd)
+ {
+ memset(kbd, 0, sizeof(*kbd));
+ kbd->dev = dev;
+ dev->private = kbd;
+
+ usb_set_configuration(dev, dev->config[0].bConfigurationValue);
+ usb_set_protocol(dev, 0);
+ usb_set_idle(dev, 0, 0);
+
+ usb_request_irq(dev,
+ usb_rcvctrlpipe(dev, endpoint->bEndpointAddress),
+ usb_kbd_irq,
+ endpoint->bInterval,
+ kbd);
+
+ list_add(&kbd->list, &usb_kbd_list);
+ }
+
+ return 0;
+}
+
+static void
+usb_kbd_disconnect(struct usb_device *dev)
+{
+ struct usb_keyboard *kbd = (struct usb_keyboard*) dev->private;
+ if(kbd)
+ {
+ dev->private = NULL;
+ list_del(&kbd->list);
+ del_timer(&kbd->repeat_timer);
+ kfree(kbd);
+ }
+
+ printk(KERN_INFO "USB HID boot protocol keyboard removed.\n");
+}
+
+int
+usb_kbd_init(void)
+{
+ usb_register(&usb_kbd_driver);
+ return 0;
+}
--- /dev/null
+unsigned char usb_kbd_map[256] =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x2e, 0x20,
+ 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26,
+
+ 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1f, 0x14,
+ 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x02, 0x03,
+
+ 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x1c, 0x01, 0xd3, 0x0f, 0x39, 0x0c, 0x0d, 0x1a,
+
+ 0x1b, 0x2b, 0x00, 0x27, 0x28, 0x29, 0x33, 0x34,
+ 0x35, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
+
+ 0x41, 0x42, 0x43, 0x44, 0x57, 0x58, 0xb7, 0x46,
+ 0x00, 0xd2, 0xc7, 0xc9, 0x63, 0xcf, 0xd1, 0xcd,
+
+ 0xcb, 0xd0, 0xc8, 0x45, 0xb5, 0x37, 0x4a, 0x4e,
+ 0x9c, 0x4f, 0x50, 0x51, 0x4b, 0x4c, 0x4d, 0x47,
+
+ 0x48, 0x49, 0x52, 0x53, 0x00, 0x6d, 0x00, 0x00,
+ 0xbd, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1d, 0x2a, 0x38, 0xdb, 0x9d, 0x36, 0xb8, 0xdc,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
--- /dev/null
+# misc fixes
+keycode 0 = Pause
+keycode 29 = Control
+keycode 99 = Remove
+keycode 42 = Shift
+keycode 54 = Shift_R
+keycode 109 = Application
+
+# E0 keys (or'ed with 0x80)
+keycode 156 = KP_Enter
+keycode 157 = Control_R
+keycode 181 = KP_Divide
+keycode 183 = Print_Screen
+keycode 184 = Alt_R
+keycode 189 = F13
+keycode 190 = F14
+keycode 193 = F17
+keycode 198 = Break
+keycode 199 = Home
+keycode 200 = Up
+keycode 201 = Prior
+keycode 203 = Left
+keycode 205 = Right
+keycode 207 = End
+keycode 208 = Down
+keycode 209 = Next
+keycode 210 = Insert
+keycode 211 = Delete
+keycode 219 = Window
+keycode 220 = Window_R
+keycode 221 = Menu
--- /dev/null
+keymaps 0-2,4-6,8-9,12
+keycode 1 = Escape
+ alt keycode 1 = Meta_Escape
+ shift alt keycode 1 = Meta_Escape
+ control alt keycode 1 = Meta_Escape
+keycode 2 = one exclam
+ alt keycode 2 = Meta_one
+ shift alt keycode 2 = Meta_exclam
+keycode 3 = two at at nul nul
+ alt keycode 3 = Meta_two
+ shift alt keycode 3 = Meta_at
+ control alt keycode 3 = Meta_nul
+keycode 4 = three numbersign
+ control keycode 4 = Escape
+ alt keycode 4 = Meta_three
+ shift alt keycode 4 = Meta_numbersign
+keycode 5 = four dollar dollar Control_backslash
+ alt keycode 5 = Meta_four
+ shift alt keycode 5 = Meta_dollar
+ control alt keycode 5 = Meta_Control_backslash
+keycode 6 = five percent
+ control keycode 6 = Control_bracketright
+ alt keycode 6 = Meta_five
+ shift alt keycode 6 = Meta_percent
+keycode 7 = six asciicircum
+ control keycode 7 = Control_asciicircum
+ alt keycode 7 = Meta_six
+ shift alt keycode 7 = Meta_asciicircum
+keycode 8 = seven ampersand braceleft Control_underscore
+ alt keycode 8 = Meta_seven
+ shift alt keycode 8 = Meta_ampersand
+ control alt keycode 8 = Meta_Control_underscore
+keycode 9 = eight asterisk bracketleft Delete
+ alt keycode 9 = Meta_eight
+ shift alt keycode 9 = Meta_asterisk
+ control alt keycode 9 = Meta_Delete
+keycode 10 = nine parenleft bracketright
+ alt keycode 10 = Meta_nine
+ shift alt keycode 10 = Meta_parenleft
+keycode 11 = zero parenright braceright
+ alt keycode 11 = Meta_zero
+ shift alt keycode 11 = Meta_parenright
+keycode 12 = minus underscore backslash Control_underscore Control_underscore
+ alt keycode 12 = Meta_minus
+ shift alt keycode 12 = Meta_underscore
+ control alt keycode 12 = Meta_Control_underscore
+keycode 13 = equal plus
+ alt keycode 13 = Meta_equal
+ shift alt keycode 13 = Meta_plus
+keycode 14 = Delete
+ alt keycode 14 = Meta_Delete
+ shift alt keycode 14 = Meta_Delete
+ control alt keycode 14 = Meta_Delete
+keycode 15 = Tab
+ alt keycode 15 = Meta_Tab
+ shift alt keycode 15 = Meta_Tab
+ control alt keycode 15 = Meta_Tab
+keycode 16 = q
+keycode 17 = w
+keycode 18 = e
+keycode 19 = r
+keycode 20 = t
+keycode 21 = y
+keycode 22 = u
+keycode 23 = i
+keycode 24 = o
+keycode 25 = p
+keycode 26 = bracketleft braceleft
+ control keycode 26 = Escape
+ alt keycode 26 = Meta_bracketleft
+ shift alt keycode 26 = Meta_braceleft
+keycode 27 = bracketright braceright asciitilde Control_bracketright
+ alt keycode 27 = Meta_bracketright
+ shift alt keycode 27 = Meta_braceright
+ control alt keycode 27 = Meta_Control_bracketright
+keycode 28 = Return
+ alt keycode 28 = Meta_Control_m
+keycode 29 = Control
+keycode 30 = a
+keycode 31 = s
+keycode 32 = d
+keycode 33 = f
+keycode 34 = g
+keycode 35 = h
+keycode 36 = j
+keycode 37 = k
+keycode 38 = l
+keycode 39 = semicolon colon
+ alt keycode 39 = Meta_semicolon
+ shift alt keycode 39 = Meta_colon
+keycode 40 = apostrophe quotedbl
+ control keycode 40 = Control_g
+ alt keycode 40 = Meta_apostrophe
+ shift alt keycode 40 = Meta_quotedbl
+keycode 41 = grave asciitilde
+ control keycode 41 = nul
+ alt keycode 41 = Meta_grave
+ shift alt keycode 41 = Meta_asciitilde
+keycode 42 = Shift
+keycode 43 = backslash bar
+ control keycode 43 = Control_backslash
+ alt keycode 43 = Meta_backslash
+ shift alt keycode 43 = Meta_bar
+keycode 44 = z
+keycode 45 = x
+keycode 46 = c
+keycode 47 = v
+keycode 48 = b
+keycode 49 = n
+keycode 50 = m
+keycode 51 = comma less
+ alt keycode 51 = Meta_comma
+ shift alt keycode 51 = Meta_less
+keycode 52 = period greater
+ alt keycode 52 = Meta_period
+ shift alt keycode 52 = Meta_greater
+keycode 53 = slash question
+ control keycode 53 = Delete
+ alt keycode 53 = Meta_slash
+ shift alt keycode 53 = Meta_question
+keycode 54 = Shift
+keycode 55 = KP_Multiply
+ altgr keycode 55 = Hex_C
+keycode 56 = Alt
+keycode 57 = space
+ control keycode 57 = nul
+ alt keycode 57 = Meta_space
+ shift alt keycode 57 = Meta_space
+ control alt keycode 57 = Meta_nul
+keycode 58 = Caps_Lock
+keycode 59 = F1 F13 Console_13 F25
+ alt keycode 59 = Console_1
+ control alt keycode 59 = Console_1
+keycode 60 = F2 F14 Console_14 F26
+ alt keycode 60 = Console_2
+ control alt keycode 60 = Console_2
+keycode 61 = F3 F15 Console_15 F27
+ alt keycode 61 = Console_3
+ control alt keycode 61 = Console_3
+keycode 62 = F4 F16 Console_16 F28
+ alt keycode 62 = Console_4
+ control alt keycode 62 = Console_4
+keycode 63 = F5 F17 Console_17 F29
+ alt keycode 63 = Console_5
+ control alt keycode 63 = Console_5
+keycode 64 = F6 F18 Console_18 F30
+ alt keycode 64 = Console_6
+ control alt keycode 64 = Console_6
+keycode 65 = F7 F19 Console_19 F31
+ alt keycode 65 = Console_7
+ control alt keycode 65 = Console_7
+keycode 66 = F8 F20 Console_20 F32
+ alt keycode 66 = Console_8
+ control alt keycode 66 = Console_8
+keycode 67 = F9 F21 Console_21 F33
+ alt keycode 67 = Console_9
+ control alt keycode 67 = Console_9
+keycode 68 = F10 F22 Console_22 F34
+ alt keycode 68 = Console_10
+ control alt keycode 68 = Console_10
+keycode 69 = Num_Lock
+ altgr keycode 69 = Hex_E
+keycode 70 = Scroll_Lock Show_Memory Show_Registers Show_State
+ alt keycode 70 = Scroll_Lock
+keycode 71 = KP_7
+ altgr keycode 71 = Hex_7
+ alt keycode 71 = Ascii_7
+keycode 72 = KP_8
+ altgr keycode 72 = Hex_8
+ alt keycode 72 = Ascii_8
+keycode 73 = KP_9
+ altgr keycode 73 = Hex_9
+ alt keycode 73 = Ascii_9
+keycode 74 = KP_Subtract
+keycode 75 = KP_4
+ altgr keycode 75 = Hex_4
+ alt keycode 75 = Ascii_4
+keycode 76 = KP_5
+ altgr keycode 76 = Hex_5
+ alt keycode 76 = Ascii_5
+keycode 77 = KP_6
+ altgr keycode 77 = Hex_6
+ alt keycode 77 = Ascii_6
+keycode 78 = KP_Add
+keycode 79 = KP_1
+ altgr keycode 79 = Hex_1
+ alt keycode 79 = Ascii_1
+keycode 80 = KP_2
+ altgr keycode 80 = Hex_2
+ alt keycode 80 = Ascii_2
+keycode 81 = KP_3
+ altgr keycode 81 = Hex_3
+ alt keycode 81 = Ascii_3
+keycode 82 = KP_0
+ altgr keycode 82 = Hex_0
+ alt keycode 82 = Ascii_0
+keycode 83 = KP_Period
+ altgr control keycode 83 = Boot
+ control alt keycode 83 = Boot
+keycode 84 = Last_Console
+keycode 85 =
+keycode 86 = less greater bar
+ alt keycode 86 = Meta_less
+ shift alt keycode 86 = Meta_greater
+keycode 87 = F11 F23 Console_23 F35
+ alt keycode 87 = Console_11
+ control alt keycode 87 = Console_11
+keycode 88 = F12 F24 Console_24 F36
+ alt keycode 88 = Console_12
+ control alt keycode 88 = Console_12
+keycode 89 =
+keycode 90 =
+keycode 91 =
+keycode 92 =
+keycode 93 =
+keycode 94 =
+keycode 95 =
+keycode 96 = KP_Enter
+keycode 97 = Control
+keycode 98 = KP_Divide
+ altgr keycode 98 = Hex_B
+keycode 99 = Control_backslash
+ alt keycode 99 = Meta_Control_backslash
+ shift alt keycode 99 = Meta_Control_backslash
+ control alt keycode 99 = Meta_Control_backslash
+keycode 100 = AltGr
+keycode 101 = Break
+keycode 102 = Find
+keycode 103 = Up
+ alt keycode 103 = KeyboardSignal
+keycode 104 = Prior
+ shift keycode 104 = Scroll_Backward
+keycode 105 = Left
+ alt keycode 105 = Decr_Console
+keycode 106 = Right
+ alt keycode 106 = Incr_Console
+keycode 107 = Select
+keycode 108 = Down
+keycode 109 = Next
+ shift keycode 109 = Scroll_Forward
+keycode 110 = Insert
+keycode 111 = Remove
+ altgr control keycode 111 = Boot
+ control alt keycode 111 = Boot
+keycode 112 = Macro
+ altgr control keycode 112 = VoidSymbol
+ shift alt keycode 112 = VoidSymbol
+keycode 113 = F13
+ altgr control keycode 113 = VoidSymbol
+ shift alt keycode 113 = VoidSymbol
+keycode 114 = F14
+ altgr control keycode 114 = VoidSymbol
+ shift alt keycode 114 = VoidSymbol
+keycode 115 = Help
+ altgr control keycode 115 = VoidSymbol
+ shift alt keycode 115 = VoidSymbol
+keycode 116 = Do
+ altgr control keycode 116 = VoidSymbol
+ shift alt keycode 116 = VoidSymbol
+keycode 117 = F17
+ altgr control keycode 117 = VoidSymbol
+ shift alt keycode 117 = VoidSymbol
+keycode 118 = KP_MinPlus
+ altgr control keycode 118 = VoidSymbol
+ shift alt keycode 118 = VoidSymbol
+keycode 119 = Pause
+keycode 120 =
+keycode 121 =
+keycode 122 =
+keycode 123 =
+keycode 124 =
+keycode 125 =
+keycode 126 =
+keycode 127 =
+string F1 = "\033[[A"
+string F2 = "\033[[B"
+string F3 = "\033[[C"
+string F4 = "\033[[D"
+string F5 = "\033[[E"
+string F6 = "\033[17~"
+string F7 = "\033[18~"
+string F8 = "\033[19~"
+string F9 = "\033[20~"
+string F10 = "\033[21~"
+string F11 = "\033[23~"
+string F12 = "\033[24~"
+string F13 = "\033[25~"
+string F14 = "\033[26~"
+string F15 = "\033[28~"
+string F16 = "\033[29~"
+string F17 = "\033[31~"
+string F18 = "\033[32~"
+string F19 = "\033[33~"
+string F20 = "\033[34~"
+string Find = "\033[1~"
+string Insert = "\033[2~"
+string Remove = "\033[3~"
+string Select = "\033[4~"
+string Prior = "\033[5~"
+string Next = "\033[6~"
+string Macro = "\033[M"
+string Pause = "\033[P"
+compose '`' 'A' to 'À'
+compose '`' 'a' to 'à'
+compose '\'' 'A' to 'Á'
+compose '\'' 'a' to 'á'
+compose '^' 'A' to 'Â'
+compose '^' 'a' to 'â'
+compose '~' 'A' to 'Ã'
+compose '~' 'a' to 'ã'
+compose '"' 'A' to 'Ä'
+compose '"' 'a' to 'ä'
+compose 'O' 'A' to 'Å'
+compose 'o' 'a' to 'å'
+compose '0' 'A' to 'Å'
+compose '0' 'a' to 'å'
+compose 'A' 'A' to 'Å'
+compose 'a' 'a' to 'å'
+compose 'A' 'E' to 'Æ'
+compose 'a' 'e' to 'æ'
+compose ',' 'C' to 'Ç'
+compose ',' 'c' to 'ç'
+compose '`' 'E' to 'È'
+compose '`' 'e' to 'è'
+compose '\'' 'E' to 'É'
+compose '\'' 'e' to 'é'
+compose '^' 'E' to 'Ê'
+compose '^' 'e' to 'ê'
+compose '"' 'E' to 'Ë'
+compose '"' 'e' to 'ë'
+compose '`' 'I' to 'Ì'
+compose '`' 'i' to 'ì'
+compose '\'' 'I' to 'Í'
+compose '\'' 'i' to 'í'
+compose '^' 'I' to 'Î'
+compose '^' 'i' to 'î'
+compose '"' 'I' to 'Ï'
+compose '"' 'i' to 'ï'
+compose '-' 'D' to 'Ð'
+compose '-' 'd' to 'ð'
+compose '~' 'N' to 'Ñ'
+compose '~' 'n' to 'ñ'
+compose '`' 'O' to 'Ò'
+compose '`' 'o' to 'ò'
+compose '\'' 'O' to 'Ó'
+compose '\'' 'o' to 'ó'
+compose '^' 'O' to 'Ô'
+compose '^' 'o' to 'ô'
+compose '~' 'O' to 'Õ'
+compose '~' 'o' to 'õ'
+compose '"' 'O' to 'Ö'
+compose '"' 'o' to 'ö'
+compose '/' 'O' to 'Ø'
+compose '/' 'o' to 'ø'
+compose '`' 'U' to 'Ù'
+compose '`' 'u' to 'ù'
+compose '\'' 'U' to 'Ú'
+compose '\'' 'u' to 'ú'
+compose '^' 'U' to 'Û'
+compose '^' 'u' to 'û'
+compose '"' 'U' to 'Ü'
+compose '"' 'u' to 'ü'
+compose '\'' 'Y' to 'Ý'
+compose '\'' 'y' to 'ý'
+compose 'T' 'H' to 'Þ'
+compose 't' 'h' to 'þ'
+compose 's' 's' to 'ß'
+compose '"' 'y' to 'ÿ'
+compose 's' 'z' to 'ß'
+compose 'i' 'j' to 'ÿ'
--- /dev/null
+# USB kernel keymap.
+keymaps 0-2,4-5,8,12
+
+keycode 4 = a
+ altgr keycode 30 = Hex_A
+keycode 5 = b
+ altgr keycode 48 = Hex_B
+keycode 6 = c
+ altgr keycode 46 = Hex_C
+keycode 7 = d
+ altgr keycode 32 = Hex_D
+keycode 8 = e
+ altgr keycode 18 = Hex_E
+keycode 9 = f
+ altgr keycode 33 = Hex_F
+keycode 10 = g
+keycode 11 = h
+keycode 12 = i
+keycode 13 = j
+keycode 14 = k
+keycode 15 = l
+keycode 16 = m
+keycode 17 = n
+keycode 18 = o
+keycode 19 = p
+keycode 20 = q
+keycode 21 = r
+keycode 22 = s
+keycode 23 = t
+keycode 24 = u
+keycode 25 = v
+keycode 26 = w
+keycode 27 = x
+keycode 28 = y
+keycode 29 = z
+keycode 30 = one exclam
+ alt keycode 2 = Meta_one
+keycode 31 = two at
+ control keycode 3 = nul
+ shift control keycode 3 = nul
+ alt keycode 3 = Meta_two
+keycode 32 = three numbersign
+ control keycode 4 = Escape
+ alt keycode 4 = Meta_three
+keycode 33 = four dollar
+ control keycode 5 = Control_backslash
+ alt keycode 5 = Meta_four
+keycode 34 = five percent
+ control keycode 6 = Control_bracketright
+ alt keycode 6 = Meta_five
+keycode 35 = six asciicircum
+ control keycode 7 = Control_asciicircum
+ alt keycode 7 = Meta_six
+keycode 36 = seven ampersand
+ control keycode 8 = Control_underscore
+ alt keycode 8 = Meta_seven
+keycode 37 = eight asterisk
+ control keycode 9 = Delete
+ alt keycode 9 = Meta_eight
+keycode 38 = nine parenleft
+ alt keycode 10 = Meta_nine
+keycode 39 = zero parenright
+ alt keycode 11 = Meta_zero
+keycode 40 = Return
+ alt keycode 28 = Meta_Control_m
+keycode 41 = Escape Escape
+ alt keycode 1 = Meta_Escape
+keycode 42 = Delete Delete
+ control keycode 14 = BackSpace
+ alt keycode 14 = Meta_Delete
+keycode 43 = Tab Tab
+ alt keycode 15 = Meta_Tab
+keycode 44 = space space
+ control keycode 57 = nul
+ alt keycode 57 = Meta_space
+keycode 45 = minus underscore backslash
+ control keycode 12 = Control_underscore
+ shift control keycode 12 = Control_underscore
+ alt keycode 12 = Meta_minus
+keycode 46 = equal plus
+ alt keycode 13 = Meta_equal
+keycode 47 = bracketleft braceleft
+ control keycode 26 = Escape
+ alt keycode 26 = Meta_bracketleft
+keycode 48 = bracketright braceright asciitilde
+ control keycode 27 = Control_bracketright
+ alt keycode 27 = Meta_bracketright
+keycode 49 = backslash bar
+ control keycode 43 = Control_backslash
+ alt keycode 43 = Meta_backslash
+keycode 50 =
+keycode 51 = semicolon colon
+ alt keycode 39 = Meta_semicolon
+keycode 52 = apostrophe quotedbl
+ control keycode 40 = Control_g
+ alt keycode 40 = Meta_apostrophe
+keycode 53 = grave asciitilde
+ control keycode 41 = nul
+ alt keycode 41 = Meta_grave
+keycode 54 = comma less
+ alt keycode 51 = Meta_comma
+keycode 55 = period greater
+ control keycode 52 = Compose
+ alt keycode 52 = Meta_period
+keycode 56 = slash question
+ control keycode 53 = Delete
+ alt keycode 53 = Meta_slash
+keycode 57 = Caps_Lock
+keycode 58 = F1 F11 Console_13
+ control keycode 59 = F1
+ alt keycode 59 = Console_1
+ control alt keycode 59 = Console_1
+keycode 59 = F2 F12 Console_14
+ control keycode 60 = F2
+ alt keycode 60 = Console_2
+ control alt keycode 60 = Console_2
+keycode 60 = F3 F13 Console_15
+ control keycode 61 = F3
+ alt keycode 61 = Console_3
+ control alt keycode 61 = Console_3
+keycode 61 = F4 F14 Console_16
+ control keycode 62 = F4
+ alt keycode 62 = Console_4
+ control alt keycode 62 = Console_4
+keycode 62 = F5 F15 Console_17
+ control keycode 63 = F5
+ alt keycode 63 = Console_5
+ control alt keycode 63 = Console_5
+keycode 63 = F6 F16 Console_18
+ control keycode 64 = F6
+ alt keycode 64 = Console_6
+ control alt keycode 64 = Console_6
+keycode 64 = F7 F17 Console_19
+ control keycode 65 = F7
+ alt keycode 65 = Console_7
+ control alt keycode 65 = Console_7
+keycode 65 = F8 F18 Console_20
+ control keycode 66 = F8
+ alt keycode 66 = Console_8
+ control alt keycode 66 = Console_8
+keycode 66 = F9 F19 Console_21
+ control keycode 67 = F9
+ alt keycode 67 = Console_9
+ control alt keycode 67 = Console_9
+keycode 67 = F10 F20 Console_22
+ control keycode 68 = F10
+ alt keycode 68 = Console_10
+ control alt keycode 68 = Console_10
+keycode 68 = F11 F11 Console_23
+ control keycode 87 = F11
+ alt keycode 87 = Console_11
+ control alt keycode 87 = Console_11
+keycode 69 = F12 F12 Console_24
+ control keycode 88 = F12
+ alt keycode 88 = Console_12
+ control alt keycode 88 = Console_12
+keycode 70 = Print_Screen
+keycode 71 = Scroll_Lock Show_Memory Show_Registers
+ control keycode 70 = Show_State
+ alt keycode 70 = Scroll_Lock
+keycode 72 = Pause
+keycode 73 = Insert
+keycode 74 = Home
+keycode 75 = Prior
+ shift keycode 104 = Scroll_Backward
+keycode 76 = Remove
+# altgr control keycode 111 = Boot
+ control alt keycode 111 = Boot
+keycode 77 = End
+keycode 78 = Next
+ shift keycode 109 = Scroll_Forward
+keycode 79 = Right
+ alt keycode 106 = Incr_Console
+keycode 80 = Left
+ alt keycode 105 = Decr_Console
+keycode 81 = Down
+keycode 82 = Up
+keycode 83 = Num_Lock
+ shift keycode 69 = Bare_Num_Lock
+keycode 84 = KP_Divide
+keycode 85 = KP_Multiply
+keycode 86 = KP_Subtract
+keycode 87 = KP_Add
+keycode 88 = KP_Enter
+keycode 89 = KP_1
+ alt keycode 79 = Ascii_1
+ altgr keycode 79 = Hex_1
+keycode 90 = KP_2
+ alt keycode 80 = Ascii_2
+ altgr keycode 80 = Hex_2
+keycode 91 = KP_3
+ alt keycode 81 = Ascii_3
+ altgr keycode 81 = Hex_3
+keycode 92 = KP_4
+ alt keycode 75 = Ascii_4
+ altgr keycode 75 = Hex_4
+keycode 93 = KP_5
+ alt keycode 76 = Ascii_5
+ altgr keycode 76 = Hex_5
+keycode 94 = KP_6
+ alt keycode 77 = Ascii_6
+ altgr keycode 77 = Hex_6
+keycode 95 = KP_7
+ alt keycode 71 = Ascii_7
+ altgr keycode 71 = Hex_7
+keycode 96 = KP_8
+ alt keycode 72 = Ascii_8
+ altgr keycode 72 = Hex_8
+keycode 97 = KP_9
+ alt keycode 73 = Ascii_9
+ altgr keycode 73 = Hex_9
+keycode 98 = KP_0
+ alt keycode 82 = Ascii_0
+ altgr keycode 82 = Hex_0
+keycode 99 = KP_Period
+# altgr control keycode 83 = Boot
+ control alt keycode 83 = Boot
+keycode 100 =
+keycode 101 = Application
+keycode 102 =
+keycode 103 =
+keycode 104 = F13
+keycode 105 = F14
+
+# modifiers
+keycode 120 = Control
+keycode 121 = Shift
+keycode 122 = Alt
+keycode 123 = Window
+keycode 124 = Control_R
+keycode 125 = Shift_R
+keycode 126 = Alt_R
+keycode 127 = Window_R
--- /dev/null
+#!/usr/bin/perl
+
+($ME = $0) =~ s|.*/||;
+
+$file = "maps/serial.map";
+$line = 1;
+open(PC, $file) || die("$!");
+while(<PC>)
+{
+ if(/^\s*keycode\s+(\d+)\s*=\s*(\S+)/)
+ {
+ my($idx) = int($1);
+ my($sym) = $2;
+ if(defined($map{uc($sym)}))
+ {
+ # print STDERR "$file:$line: warning: `$sym' redefined\n";
+ }
+ $map{uc($sym)} = $idx;
+ }
+ $line++;
+}
+close(PC);
+
+$file = "maps/fixup.map";
+$line = 1;
+open(FIXUP, $file) || die("$!");
+while(<FIXUP>)
+{
+ if(/^\s*keycode\s+(\d+)\s*=\s*/)
+ {
+ my($idx) = int($1);
+ for $sym (split(/\s+/, $'))
+ {
+ $map{uc($sym)} = $idx;
+ }
+ }
+ $line++;
+}
+close(FIXUP);
+
+$file = "maps/usb.map";
+$line = 1;
+open(USB, $file) || die("$!");
+while(<USB>)
+{
+ if(/^\s*keycode\s+(\d+)\s*=\s*/)
+ {
+ my($idx) = int($1);
+ for $sym (split(/\s+/, $'))
+ {
+ my($val) = $map{uc($sym)};
+ $map[$idx] = $val;
+ if(!defined($val))
+ {
+ print STDERR "$file:$line: warning: `$sym' undefined\n";
+ }
+ else
+ {
+ last;
+ }
+ }
+ }
+ $line++;
+}
+close(USB);
+
+print "unsigned char usb_kbd_map[256] = \n{\n";
+for($x = 0; $x < 32; $x++)
+{
+ if($x && !($x % 2))
+ {
+ print "\n";
+ }
+ print " ";
+ for($y = 0; $y < 8; $y++)
+ {
+ my($idx) = $x * 8 + $y;
+ print sprintf(" 0x%02x,",
+ int(defined($map[$idx]) ? $map[$idx]:0));
+ }
+ print "\n";
+}
+print "};\n";
--- /dev/null
+/*
+ * USB HID boot protocol mouse support based on MS BusMouse driver, psaux
+ * driver, and Linus's skeleton USB mouse driver
+ *
+ * Brad Keryan 4/3/1999
+ *
+ * version 0.02: Hmm, the mouse seems drunk because I'm queueing the events.
+ * This is wrong: when an application (like X or gpm) reads the mouse device,
+ * it wants to find out the mouse's current position, not its recent history.
+ * The button thing turned out to be UHCI not flipping data toggle, so half the
+ * packets were thrown out.
+ *
+ * version 0.01: Switched over to busmouse protocol, and changed the minor
+ * number to 32 (same as uusbd's hidbp driver). Buttons work more sanely now,
+ * but it still doesn't generate button events unless you move the mouse.
+ *
+ * version 0.0: Driver emulates a PS/2 mouse, stealing /dev/psaux (sorry, I
+ * know that's not very nice). Moving in the X and Y axes works. Buttons don't
+ * work right yet: X sees a lot of MotionNotify/ButtonPress/ButtonRelease
+ * combos when you hold down a button and drag the mouse around. Probably has
+ * some additional bugs on an SMP machine.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+
+#include <asm/spinlock.h>
+
+#include "usb.h"
+
+#define USB_MOUSE_MINOR 32
+
+struct mouse_state {
+ unsigned char buttons; /* current button state */
+ long dx; /* dx, dy, dz are change since last read */
+ long dy;
+ long dz;
+ int present; /* this mouse is plugged in */
+ int active; /* someone is has this mouse's device open */
+ int ready; /* the mouse has changed state since the last read */
+ struct wait_queue *wait; /* for polling */
+ struct fasync_struct *fasync;
+ /* later, add a list here to support multiple mice */
+ /* but we will also need a list of file pointers to identify it */
+};
+
+static struct mouse_state static_mouse_state;
+
+spinlock_t usb_mouse_lock = SPIN_LOCK_UNLOCKED;
+
+static int mouse_irq(int state, void *__buffer, void *dev_id)
+{
+ signed char *data = __buffer;
+ /* finding the mouse is easy when there's only one */
+ struct mouse_state *mouse = &static_mouse_state;
+
+ /* if a mouse moves with no one listening, do we care? no */
+ if(!mouse->active)
+ return 1;
+
+ /* if the USB mouse sends an interrupt, then something noteworthy
+ must have happened */
+ mouse->buttons = data[0] & 0x07;
+ mouse->dx += data[1]; /* data[] is signed, so this works */
+ mouse->dy -= data[2]; /* y-axis is reversed */
+ mouse->dz += data[3];
+ mouse->ready = 1;
+
+ add_mouse_randomness((mouse->buttons << 24) + (mouse->dz << 16 ) +
+ (mouse->dy << 8) + mouse->dx);
+
+ wake_up_interruptible(&mouse->wait);
+ if (mouse->fasync)
+ kill_fasync(mouse->fasync, SIGIO);
+
+ return 1;
+}
+
+static int fasync_mouse(int fd, struct file *filp, int on)
+{
+ int retval;
+ struct mouse_state *mouse = &static_mouse_state;
+
+ retval = fasync_helper(fd, filp, on, &mouse->fasync);
+ if (retval < 0)
+ return retval;
+ return 0;
+}
+
+static int release_mouse(struct inode * inode, struct file * file)
+{
+ struct mouse_state *mouse = &static_mouse_state;
+
+ fasync_mouse(-1, file, 0);
+ if (--mouse->active)
+ return 0;
+ return 0;
+}
+
+static int open_mouse(struct inode * inode, struct file * file)
+{
+ struct mouse_state *mouse = &static_mouse_state;
+
+ if (!mouse->present)
+ return -EINVAL;
+ if (mouse->active++)
+ return 0;
+ /* flush state */
+ mouse->buttons = mouse->dx = mouse->dy = mouse->dz = 0;
+ return 0;
+}
+
+static ssize_t write_mouse(struct file * file,
+ const char * buffer, size_t count, loff_t *ppos)
+{
+ return -EINVAL;
+}
+
+/*
+ * Look like a PS/2 mouse, please..
+ *
+ * The PS/2 protocol is fairly strange, but
+ * oh, well, it's at least common..
+ */
+static ssize_t read_mouse(struct file * file, char * buffer, size_t count, loff_t *ppos)
+{
+ int retval = 0;
+ static int state = 0;
+ struct mouse_state *mouse = &static_mouse_state;
+
+ if (count) {
+ mouse->ready = 0;
+ switch (state) {
+ case 0: { /* buttons and sign */
+ int buttons = mouse->buttons;
+ mouse->buttons = 0;
+ if (mouse->dx < 0)
+ buttons |= 0x10;
+ if (mouse->dy < 0)
+ buttons |= 0x20;
+ put_user(buttons, buffer);
+ buffer++;
+ retval++;
+ state = 1;
+ if (!--count)
+ break;
+ }
+ case 1: { /* dx */
+ int dx = mouse->dx;
+ mouse->dx = 0;
+ put_user(dx, buffer);
+ buffer++;
+ retval++;
+ state = 2;
+ if (!--count)
+ break;
+ }
+ case 2: { /* dy */
+ int dy = mouse->dy;
+ mouse->dy = 0;
+ put_user(dy, buffer);
+ buffer++;
+ retval++;
+ state = 0;
+ }
+ break;
+ }
+ }
+ return retval;
+}
+
+static unsigned int mouse_poll(struct file *file, poll_table * wait)
+{
+ struct mouse_state *mouse = &static_mouse_state;
+
+ poll_wait(file, &mouse->wait, wait);
+ if (mouse->ready)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+struct file_operations usb_mouse_fops = {
+ NULL, /* mouse_seek */
+ read_mouse,
+ write_mouse,
+ NULL, /* mouse_readdir */
+ mouse_poll, /* mouse_poll */
+ NULL, /* mouse_ioctl */
+ NULL, /* mouse_mmap */
+ open_mouse,
+ NULL, /* flush */
+ release_mouse,
+ NULL,
+ fasync_mouse,
+};
+
+static struct miscdevice usb_mouse = {
+ USB_MOUSE_MINOR, "USB mouse", &usb_mouse_fops
+};
+
+static int mouse_probe(struct usb_device *dev)
+{
+ struct usb_interface_descriptor *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct mouse_state *mouse = &static_mouse_state;
+
+ /* We don't handle multi-config mice */
+ if (dev->descriptor.bNumConfigurations != 1)
+ return -1;
+
+ /* We don't handle multi-interface mice */
+ if (dev->config[0].bNumInterfaces != 1)
+ return -1;
+
+ /* Is it a mouse interface? */
+ interface = &dev->config[0].interface[0];
+ if (interface->bInterfaceClass != 3)
+ return -1;
+ if (interface->bInterfaceSubClass != 1)
+ return -1;
+ if (interface->bInterfaceProtocol != 2)
+ return -1;
+
+ /* Multiple endpoints? What kind of mutant ninja-mouse is this? */
+ if (interface->bNumEndpoints != 1)
+ return -1;
+
+ endpoint = &interface->endpoint[0];
+
+ /* Output endpoint? Curiousier and curiousier.. */
+ if (!(endpoint->bEndpointAddress & 0x80))
+ return -1;
+
+ /* If it's not an interrupt endpoint, we'd better punt! */
+ if ((endpoint->bmAttributes & 3) != 3)
+ return -1;
+
+ printk("USB mouse found\n");
+
+ usb_set_configuration(dev, dev->config[0].bConfigurationValue);
+
+ usb_request_irq(dev, usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), mouse_irq, endpoint->bInterval, NULL);
+
+ mouse->present = 1;
+ return 0;
+}
+
+static void mouse_disconnect(struct usb_device *dev)
+{
+ struct mouse_state *mouse = &static_mouse_state;
+
+ /* this might need work */
+ mouse->present = 0;
+}
+
+static struct usb_driver mouse_driver = {
+ "mouse",
+ mouse_probe,
+ mouse_disconnect,
+ { NULL, NULL }
+};
+
+int usb_mouse_init(void)
+{
+ struct mouse_state *mouse = &static_mouse_state;
+
+ misc_register(&usb_mouse);
+
+ mouse->present = mouse->active = 0;
+ mouse->wait = NULL;
+ mouse->fasync = NULL;
+
+ usb_register(&mouse_driver);
+ printk(KERN_INFO "USB HID boot protocol mouse registered.\n");
+ return 0;
+}
+
+#if 0
+
+int init_module(void)
+{
+ return usb_mouse_init();
+}
+
+void cleanup_module(void)
+{
+ /* this, too, probably needs work */
+ usb_deregister(&mouse_driver);
+ misc_deregister(&usb_mouse);
+}
+
+#endif
--- /dev/null
+/*
+ * OHCI debugging code. It's gross.
+ *
+ * (C) Copyright 1999 Gregory P. Smith
+ */
+
+#include <linux/kernel.h>
+#include <asm/io.h>
+
+#include "ohci.h"
+
+void show_ohci_status(struct ohci *ohci)
+{
+ struct ohci_regs regs;
+ int i;
+
+ regs.revision = readl(ohci->regs->revision);
+ regs.control = readl(ohci->regs->control);
+ regs.cmdstatus = readl(ohci->regs->cmdstatus);
+ regs.intrstatus = readl(ohci->regs->intrstatus);
+ regs.intrenable = readl(ohci->regs->intrenable);
+ regs.intrdisable = readl(ohci->regs->intrdisable);
+ regs.hcca = readl(ohci->regs->hcca);
+ regs.ed_periodcurrent = readl(ohci->regs->ed_periodcurrent);
+ regs.ed_controlhead = readl(ohci->regs->ed_controlhead);
+ regs.ed_bulkhead = readl(ohci->regs->ed_bulkhead);
+ regs.ed_bulkcurrent = readl(ohci->regs->ed_bulkcurrent);
+ regs.current_donehead = readl(ohci->regs->current_donehead);
+ regs.fminterval = readl(ohci->regs->fminterval);
+ regs.fmremaining = readl(ohci->regs->fmremaining);
+ regs.fmnumber = readl(ohci->regs->fmnumber);
+ regs.periodicstart = readl(ohci->regs->periodicstart);
+ regs.lsthresh = readl(ohci->regs->lsthresh);
+ regs.roothub.a = readl(ohci->regs->roothub.a);
+ regs.roothub.b = readl(ohci->regs->roothub.b);
+ regs.roothub.status = readl(ohci->regs->roothub.status);
+ for (i=0; i<MAX_ROOT_PORTS; ++i)
+ regs.roothub.portstatus[i] = readl(ohci->regs->roothub.portstatus[i]);
+
+ printk(" ohci revision = 0x%x\n", regs.revision);
+ printk(" ohci control = 0x%x\n", regs.control);
+ printk(" ohci cmdstatus = 0x%x\n", regs.cmdstatus);
+ printk(" ohci intrstatus = 0x%x\n", regs.intrstatus);
+ printk(" ohci roothub.a = 0x%x\n", regs.roothub.a);
+ printk(" ohci roothub.b = 0x%x\n", regs.roothub.b);
+ printk(" ohci root status = 0x%x\n", regs.roothub.status);
+} /* show_ohci_status() */
+
+
+static void show_ohci_ed(struct ohci_ed *ed)
+{
+ int stat = ed->status;
+ int skip = (stat & OHCI_ED_SKIP);
+ int mps = (stat & OHCI_ED_MPS) >> 16;
+ int isoc = (stat & OHCI_ED_F_ISOC);
+ int low_speed = (stat & OHCI_ED_S_LOW);
+ int dir = (stat & OHCI_ED_D);
+ int endnum = (stat & OHCI_ED_EN) >> 7;
+ int funcaddr = (stat & OHCI_ED_FA);
+ int halted = (ed->head_td & 1);
+ int toggle = (ed->head_td & 2) >> 1;
+
+ printk(" ohci ED:\n");
+ printk(" status = 0x%x\n", stat);
+ printk(" %sMPS %d%s%s%s%s tc%d e%d fa%d\n",
+ skip ? "Skip " : "",
+ mps,
+ isoc ? "Isoc. " : "",
+ low_speed ? "LowSpd " : "",
+ (dir == OHCI_ED_D_IN) ? "Input " :
+ (dir == OHCI_ED_D_OUT) ? "Output " : "",
+ halted ? "Halted " : "",
+ toggle,
+ endnum,
+ funcaddr);
+ printk(" tail_td = 0x%x\n", ed->tail_td);
+ printk(" head_td = 0x%x\n", ed->head_td);
+ printk(" next_ed = 0x%x\n", ed->next_ed);
+} /* show_ohci_ed() */
+
+
+static void show_ohci_td(struct ohci_td *td)
+{
+ int td_round = td->info & OHCI_TD_ROUND;
+ int td_dir = td->info & OHCI_TD_D;
+ int td_int_delay = td->info & OHCI_TD_IOC_DELAY;
+ int td_toggle = td->info & OHCI_TD_DT;
+ int td_errcnt = td_errorcount(td->info);
+ int td_cc = td->info & OHCI_TD_CC;
+
+ printk(" ohci TD hardware fields:\n");
+ printk(" info = 0x%x\n", td->info);
+ printk(" %s%s%s%d%s%s%d%s%d\n",
+ td_round ? "Rounding " : "",
+ (td_dir == OHCI_TD_D_IN) ? "Input " :
+ (td_dir == OHCI_TD_D_OUT) ? "Output " :
+ (td_dir == OHCI_TD_D_SETUP) ? "Setup " : "",
+ "IntDelay ", td_int_delay >> 21,
+ td_toggle ? "Data1 " : "Data0 ",
+ "ErrorCnt ", td_errcnt,
+ "ComplCode ", td_cc);
+ printk(" %sAccessed, %sActive\n",
+ td_cc_accessed(td->info) ? "" : "Not ",
+ td_active(td->info) ? "" : "Not ");
+
+ printk(" cur_buf = 0x%x\n", td->cur_buf);
+ printk(" next_td = 0x%x\n", td->next_td);
+ printk(" buf_end = 0x%x\n", td->buf_end);
+ printk(" ohci TD driver fields:\n");
+ printk(" data = %p\n", td->data);
+ printk(" dev_id = %p\n", td->dev_id);
+ printk(" ed_bus = %x\n", td->ed_bus);
+} /* show_ohci_td() */
+
+
+void show_ohci_device(struct ohci_device *dev)
+{
+ int idx;
+ printk(" ohci_device usb = %p\n", dev->usb);
+ printk(" ohci_device ohci = %p\n", dev->ohci);
+ printk(" ohci_device ohci_hcca = %p\n", dev->hcca);
+ for (idx=0; idx<8 /*NUM_EDS*/; ++idx) {
+ printk(" [ed num %d] ", idx);
+ show_ohci_ed(&dev->ed[idx]);
+ }
+ for (idx=0; idx<8 /*NUM_TDS*/; ++idx) {
+ printk(" [td num %d] ", idx);
+ show_ohci_td(&dev->td[idx]);
+ }
+ printk(" ohci_device data\n ");
+ for (idx=0; idx<4; ++idx) {
+ printk(" %08lx", dev->data[idx]);
+ }
+ printk("\n");
+} /* show_ohci_device() */
+
+
+/* vim:sw=8
+ */
--- /dev/null
+/*
+ * Open Host Controller Interface driver for USB.
+ *
+ * (C) Copyright 1999 Gregory P. Smith <greg@electricrain.com>
+ *
+ * This is the "other" host controller interface for USB. You will
+ * find this on many non-Intel based motherboards, and of course the
+ * Mac. As Linus hacked his UHCI driver together first, I modeled
+ * this after his.. (it should be obvious)
+ *
+ * From the programming standpoint the OHCI interface seems a little
+ * prettier and potentially less CPU intensive. This remains to be
+ * proven. In reality, I don't believe it'll make one darn bit of
+ * difference. USB v1.1 is a slow bus by today's standards.
+ *
+ * OHCI hardware takes care of most of the scheduling of different
+ * transfer types with the correct prioritization for us.
+ *
+ * To get started in USB, I used the "Universal Serial Bus System
+ * Architecture" book by Mindshare, Inc. It was a reasonable introduction
+ * and overview of USB and the two dominant host controller interfaces
+ * however you're better off just reading the real specs available
+ * from www.usb.org as you'll need them to get enough detailt to
+ * actually implement a HCD. The book has many typos and omissions
+ * Beware, the specs are the victim of a committee.
+ *
+ * This code was written with Guinness on the brain, xsnow on the desktop
+ * and Orbital, Orb, Enya & Massive Attack on the CD player. What a life! ;)
+ *
+ * No filesystems were harmed in the development of this code.
+ *
+ * $Id: ohci.c,v 1.11 1999/04/25 00:18:52 greg Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+
+#include <asm/spinlock.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "ohci.h"
+#include "inits.h"
+
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+static int handle_apm_event(apm_event_t event);
+static int apm_resume = 0;
+#endif
+
+static struct wait_queue *ohci_configure = NULL;
+
+
+static int ohci_td_result(struct ohci_device *dev, struct ohci_td *td)
+{
+ unsigned int status;
+
+ status = td->info & OHCI_TD_CC;
+
+ /* TODO Debugging code for TD failures goes here */
+
+ return status;
+}
+
+
+static spinlock_t ohci_edtd_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * Add a TD to the end of the TD list on a given ED. td->next_td is
+ * assumed to be set correctly for the situation of no TDs already
+ * being on the list (ie: pointing to NULL).
+ */
+static void ohci_add_td_to_ed(struct ohci_td *td, struct ohci_ed *ed)
+{
+ struct ohci_td *tail = bus_to_virt(ed->tail_td);
+ struct ohci_td *head = bus_to_virt(ed->head_td);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ohci_edtd_lock, flags);
+
+ if (tail == head) { /* empty list, put it on the head */
+ head = (struct ohci_td *) virt_to_bus(td);
+ tail = 0;
+ } else {
+ if (!tail) { /* no tail, single element list */
+ td->next_td = head->next_td;
+ head->next_td = virt_to_bus(td);
+ tail = (struct ohci_td *) virt_to_bus(td);
+ } else { /* append to the list */
+ td->next_td = tail->next_td;
+ tail->next_td = virt_to_bus(td);
+ tail = (struct ohci_td *) virt_to_bus(td);
+ }
+ }
+ /* save the reverse link */
+ td->ed_bus = virt_to_bus(ed);
+
+ spin_unlock_irqrestore(&ohci_edtd_lock, flags);
+} /* ohci_add_td_to_ed() */
+
+
+/*
+ * Remove a TD from the given EDs TD list
+ */
+static void ohci_remove_td_from_ed(struct ohci_td *td, struct ohci_ed *ed)
+{
+ struct ohci_td *head = bus_to_virt(ed->head_td);
+ struct ohci_td *tmp_td;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ohci_edtd_lock, flags);
+
+ /* set the "skip me bit" in this ED */
+ writel_set(OHCI_ED_SKIP, ed->status);
+
+ /* XXX Assuming this list will never be circular */
+
+ if (td == head) {
+ /* unlink this TD; it was at the beginning */
+ ed->head_td = head->next_td;
+ }
+
+ tmp_td = head;
+ head = (struct ohci_td *) ed->head_td;
+
+ while (head != NULL) {
+
+ if (td == head) {
+ /* unlink this TD from the middle or end */
+ tmp_td->next_td = head->next_td;
+ }
+
+ tmp_td = head;
+ head = bus_to_virt(head->next_td);
+ }
+
+ td->next_td = virt_to_bus(NULL); /* remove links to ED list */
+
+ /* XXX mark this TD for possible cleanup? */
+
+ /* unset the "skip me bit" in this ED */
+ writel_mask(~(__u32)OHCI_ED_SKIP, ed->status);
+
+ spin_unlock_irqrestore(&ohci_edtd_lock, flags);
+} /* ohci_remove_td_from_ed() */
+
+
+/*
+ * Get a pointer (virtual) to an available TD from the given device's
+ * pool.
+ *
+ * Return NULL if none are left.
+ */
+static struct ohci_td *ohci_get_free_td(struct ohci_device *dev)
+{
+ int idx;
+
+ for (idx=0; idx < NUM_TDS; idx++) {
+ if (td_done(dev->td[idx].info)) {
+ /* XXX should this also zero out the structure? */
+ /* mark all new TDs as unaccessed */
+ dev->td[idx].info = OHCI_TD_CC_NEW;
+ return &dev->td[idx];
+ }
+ }
+
+ return NULL;
+} /* ohci_get_free_td() */
+
+
+/**********************************
+ * OHCI interrupt list operations *
+ **********************************/
+static spinlock_t irqlist_lock = SPIN_LOCK_UNLOCKED;
+
+static void ohci_add_irq_list(struct ohci *ohci, struct ohci_td *td, usb_device_irq completed, void *dev_id)
+{
+ unsigned long flags;
+
+ /* save the irq in our private portion of the TD */
+ td->completed = completed;
+ td->dev_id = dev_id;
+
+ spin_lock_irqsave(&irqlist_lock, flags);
+ list_add(&td->irq_list, &ohci->interrupt_list);
+ spin_unlock_irqrestore(&irqlist_lock, flags);
+} /* ohci_add_irq_list() */
+
+static void ohci_remove_irq_list(struct ohci_td *td)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&irqlist_lock, flags);
+ list_del(&td->irq_list);
+ spin_unlock_irqrestore(&irqlist_lock, flags);
+} /* ohci_remove_irq_list() */
+
+/*
+ * Request an interrupt handler for one "pipe" of a USB device.
+ * (this function is pretty minimal right now)
+ *
+ * At the moment this is only good for input interrupts. (ie: for a
+ * mouse)
+ *
+ * period is desired polling interval in ms. The closest, shorter
+ * match will be used. Powers of two from 1-32 are supported by OHCI.
+ */
+static int ohci_request_irq(struct usb_device *usb, unsigned int pipe,
+ usb_device_irq handler, int period, void *dev_id)
+{
+ struct ohci_device *dev = usb_to_ohci(usb);
+ struct ohci_td *td = dev->td; /* */
+ struct ohci_ed *interrupt_ed; /* endpoint descriptor for this irq */
+
+ /*
+ * Pick a good frequency endpoint based on the requested period
+ */
+ interrupt_ed = &dev->ohci->root_hub->ed[ms_to_ed_int(period)];
+
+ /*
+ * Set the max packet size, device speed, endpoint number, usb
+ * device number (function address), and type of TD.
+ *
+ * FIXME: Isochronous transfers need a pool of special 32 byte
+ * TDs (32 byte aligned) in order to be supported.
+ */
+ interrupt_ed->status = \
+ ed_set_maxpacket(usb_maxpacket(pipe)) |
+ ed_set_speed(usb_pipeslow(pipe)) |
+ usb_pipe_endpdev(pipe) |
+ OHCI_ED_F_NORM;
+
+ /*
+ * Set the not accessed condition code, allow odd sized data,
+ * and set the data transfer direction.
+ */
+ td->info = OHCI_TD_CC_NEW | OHCI_TD_ROUND |
+ td_set_dir_out(usb_pipeout(pipe));
+
+ /* point it to our data buffer */
+ td->cur_buf = virt_to_bus(dev->data);
+
+ /* FIXME: we're only using 1 TD right now! */
+ td->next_td = virt_to_bus(&td);
+
+ /*
+ * FIXME: be aware that OHCI won't advance out of the 4kb
+ * page cur_buf started in. It'll wrap around to the start
+ * of the page... annoying or useful? you decide.
+ *
+ * A pointer to the last *byte* in the buffer (ergh.. we get
+ * to work around C's pointer arithmatic here with a typecast)
+ */
+ td->buf_end = virt_to_bus(((u8*)(dev->data + DATA_BUF_LEN)) - 1);
+
+ /* does this make sense for ohci?.. time to think.. */
+ ohci_add_irq_list(dev->ohci, td, handler, dev_id);
+ wmb(); /* found in asm/system.h; scary concept... */
+ ohci_add_td_to_ed(td, interrupt_ed);
+
+ return 0;
+} /* ohci_request_irq() */
+
+/*
+ * Control thread operations:
+ */
+static struct wait_queue *control_wakeup;
+
+static int ohci_control_completed(int stats, void *buffer, void *dev_id)
+{
+ wake_up(&control_wakeup);
+ return 0;
+} /* ohci_control_completed() */
+
+
+/*
+ * Run a control transaction from the root hub's control endpoint.
+ * The passed in TD is the control transfer's Status TD.
+ */
+static int ohci_run_control(struct ohci_device *dev, struct ohci_td *status_td)
+{
+ struct wait_queue wait = { current, NULL };
+ struct ohci_ed *control_ed = &dev->ohci->root_hub->ed[ED_CONTROL];
+
+ current->state = TASK_UNINTERRUPTIBLE;
+ add_wait_queue(&control_wakeup, &wait);
+
+ ohci_add_irq_list(dev->ohci, status_td, ohci_control_completed, NULL);
+ ohci_add_td_to_ed(status_td, control_ed);
+
+ /* FIXME? isn't this a little gross */
+ schedule_timeout(HZ/10);
+
+ ohci_remove_irq_list(status_td);
+ ohci_remove_td_from_ed(status_td, control_ed);
+
+ return ohci_td_result(dev, status_td);
+} /* ohci_run_control() */
+
+/*
+ * Send or receive a control message on a "pipe"
+ *
+ * A control message contains:
+ * - The command itself
+ * - An optional data phase
+ * - Status complete phase
+ *
+ * The data phase can be an arbitrary number of TD's. Currently since
+ * we use statically allocated TDs if too many come in we'll just
+ * start tossing them and printk() some warning goo... Most control
+ * messages won't have much data anyways.
+ */
+static int ohci_control_msg(struct usb_device *usb, unsigned int pipe, void *cmd, void *data, int len)
+{
+ struct ohci_device *dev = usb_to_ohci(usb);
+ /*
+ * ideally dev->ed should be linked into the root hub's
+ * control_ed list and used instead of just using it directly.
+ * This could present a problem as is with more than one
+ * device. (but who wants to use a keyboard AND a mouse
+ * anyways? ;)
+ */
+ struct ohci_ed *control_ed = &dev->ohci->root_hub->ed[ED_CONTROL];
+ struct ohci_td *control_td;
+ struct ohci_td *data_td;
+ struct ohci_td *last_td;
+ __u32 data_td_info;
+
+ /*
+ * Set the max packet size, device speed, endpoint number, usb
+ * device number (function address), and type of TD.
+ *
+ */
+ control_ed->status = \
+ ed_set_maxpacket(usb_maxpacket(pipe)) |
+ ed_set_speed(usb_pipeslow(pipe)) |
+ usb_pipe_endpdev(pipe) |
+ OHCI_ED_F_NORM;
+
+ /*
+ * Build the control TD
+ */
+
+ /* get a TD to send this control message with */
+ control_td = ohci_get_free_td(dev);
+ /* TODO check for NULL */
+
+ /*
+ * Set the not accessed condition code, allow odd sized data,
+ * and set the data transfer type to SETUP. Setup DATA always
+ * uses a DATA0 packet.
+ */
+ control_td->info = OHCI_TD_CC_NEW | OHCI_TD_ROUND |
+ OHCI_TD_D_SETUP | OHCI_TD_IOC_OFF | td_force_toggle(0);
+
+ /* point it to the command */
+ control_td->cur_buf = virt_to_bus(cmd);
+
+ /* link to a free TD for the control data input */
+ data_td = ohci_get_free_td(dev); /* TODO check for NULL */
+ control_td->next_td = virt_to_bus(data_td);
+
+ /*
+ * Build the DATA TDs
+ */
+
+ data_td_info = OHCI_TD_CC_NEW | OHCI_TD_ROUND | OHCI_TD_IOC_OFF |
+ td_set_dir_out(usb_pipeout(pipe));
+
+ while (len > 0) {
+ int pktsize = len;
+ struct ohci_td *tmp_td;
+
+ if (pktsize > usb_maxpacket(pipe))
+ pktsize = usb_maxpacket(pipe);
+
+ /* set the data transaction type */
+ data_td->info = data_td_info;
+ /* point to the current spot in the data buffer */
+ data_td->cur_buf = virt_to_bus(data);
+ /* point to the end of this data */
+ data_td->buf_end = virt_to_bus(data+pktsize-1);
+
+ /* allocate the next TD */
+ tmp_td = ohci_get_free_td(dev); /* TODO check for NULL */
+ data_td->next_td = virt_to_bus(tmp_td);
+ data_td = tmp_td;
+
+ /* move on.. */
+ data += pktsize;
+ len -= pktsize;
+ }
+
+ /* point it at the newly allocated TD from above */
+ last_td = data_td;
+
+ /* The control status packet always uses a DATA1 */
+ last_td->info = OHCI_TD_CC_NEW | OHCI_TD_ROUND | td_force_toggle(1);
+ last_td->next_td = 0; /* end of TDs */
+ last_td->cur_buf = 0; /* no data in this packet */
+ last_td->buf_end = 0;
+
+ /*
+ * Start the control transaction.. give it the last TD so the
+ * result can be returned.
+ */
+ return ohci_run_control(dev, last_td);
+} /* ohci_control_msg() */
+
+
+static struct usb_device *ohci_usb_allocate(struct usb_device *parent)
+{
+ struct usb_device *usb_dev;
+ struct ohci_device *dev;
+
+ usb_dev = kmalloc(sizeof(*usb_dev), GFP_KERNEL);
+ if (!usb_dev)
+ return NULL;
+
+ memset(usb_dev, 0, sizeof(*usb_dev));
+
+ dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ kfree(usb_dev);
+ return NULL;
+ }
+
+ /* Initialize "dev" */
+ memset(dev, 0, sizeof(*dev));
+
+ usb_dev->hcpriv = dev;
+ dev->usb = usb_dev;
+
+ usb_dev->parent = parent;
+
+ if (parent) {
+ usb_dev->bus = parent->bus;
+ dev->ohci = usb_to_ohci(parent)->ohci;
+ }
+
+ return usb_dev;
+}
+
+static int ohci_usb_deallocate(struct usb_device *usb_dev)
+{
+ kfree(usb_to_ohci(usb_dev));
+ kfree(usb_dev);
+ return 0;
+}
+
+/*
+ * functions for the generic USB driver
+ */
+struct usb_operations ohci_device_operations = {
+ ohci_usb_allocate,
+ ohci_usb_deallocate,
+ ohci_control_msg,
+ ohci_request_irq,
+};
+
+/*
+ * Reset an OHCI controller
+ */
+static void reset_hc(struct ohci *ohci)
+{
+ writel((1<<31), &ohci->regs->intrdisable); /* Disable HC interrupts */
+ writel(1, &ohci->regs->cmdstatus); /* HC Reset */
+ writel_mask(0x3f, &ohci->regs->control); /* move to UsbReset state */
+} /* reset_hc() */
+
+
+/*
+ * Reset and start an OHCI controller
+ */
+static void start_hc(struct ohci *ohci)
+{
+ int timeout = 1000; /* used to prevent an infinite loop. */
+
+ reset_hc(ohci);
+
+ while ((readl(&ohci->regs->control) & 0xc0) == 0) {
+ if (!--timeout) {
+ printk("USB HC Reset timed out!\n");
+ break;
+ }
+ }
+
+ /* Choose the interrupts we care about */
+ writel( OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_SF |
+ OHCI_INTR_WDH | OHCI_INTR_SO | OHCI_INTR_UE |
+ OHCI_INTR_FNO,
+ &ohci->regs->intrenable);
+
+ /* Enter the USB Operational state & start the frames a flowing.. */
+ writel_set(OHCI_USB_OPER, &ohci->regs->control);
+
+} /* start_hc() */
+
+
+/*
+ * Reset a root hub port
+ */
+static void ohci_reset_port(struct ohci *ohci, unsigned int port)
+{
+ short ms;
+ int status;
+
+ /* Don't allow overflows. */
+ if (port >= MAX_ROOT_PORTS) {
+ printk("Bad port # passed to ohci_reset_port\n");
+ port = MAX_ROOT_PORTS-1;
+ }
+
+ writel(PORT_PRS, &ohci->regs->roothub.portstatus[port]); /* Reset */
+
+ /*
+ * Get the time required for a root hub port to reset and wait
+ * it out (adding 1ms for good measure).
+ */
+ ms = (readl(&ohci->regs->roothub.a) >> 24) * 2 + 1;
+ wait_ms(ms);
+
+ /* check port status to see that the reset completed */
+ status = readl(&ohci->regs->roothub.portstatus[port]);
+ if (status & PORT_PRS) {
+ /* reset failed, try harder? */
+ printk("usb-ohci: port %d reset failed, retrying\n", port);
+ writel(PORT_PRS, &ohci->regs->roothub.portstatus[port]);
+ wait_ms(50);
+ }
+
+ /* TODO we might need to re-enable the port here or is that
+ * done elsewhere? */
+
+} /* ohci_reset_port */
+
+
+/*
+ * This gets called if the connect status on the root hub changes.
+ */
+static void ohci_connect_change(struct ohci * ohci, int port)
+{
+ struct usb_device *usb_dev;
+ struct ohci_device *dev;
+ /* memory I/O address of the port status register */
+ void *portaddr = &ohci->regs->roothub.portstatus[port];
+ int portstatus;
+
+ /*
+ * Because of the status change we have to forget
+ * everything we think we know about the device
+ * on this root hub port. It may have changed.
+ */
+ usb_disconnect(ohci->root_hub->usb->children + port);
+
+ portstatus = readl(portaddr);
+
+ /* disable the port if nothing is connected */
+ if (!(portstatus & PORT_CCS)) {
+ writel(PORT_CCS, portaddr);
+ return;
+ }
+
+ /*
+ * Allocate a device for the new thingy that's been attached
+ */
+ usb_dev = ohci_usb_allocate(ohci->root_hub->usb);
+ dev = usb_dev->hcpriv;
+
+ dev->ohci = ohci;
+
+ usb_connect(dev->usb);
+
+ /* link it into the bus's device tree */
+ ohci->root_hub->usb->children[port] = usb_dev;
+
+ wait_ms(200); /* wait for powerup; XXX is this needed? */
+ ohci_reset_port(ohci, port);
+
+ /* Get information on speed by using LSD */
+ usb_dev->slow = readl(portaddr) & PORT_LSDA ? 1 : 0;
+
+ /*
+ * Do generic USB device tree processing on the new device.
+ */
+ usb_new_device(usb_dev);
+} /* ohci_connect_change() */
+
+
+/*
+ * This gets called when the root hub configuration
+ * has changed. Just go through each port, seeing if
+ * there is something interesting happening.
+ */
+static void ohci_check_configuration(struct ohci *ohci)
+{
+ int num = 0;
+ int maxport = readl(&ohci->regs->roothub) & 0xff;
+
+ do {
+ if (readl(ohci->regs->roothub.portstatus[num]) & PORT_CSC)
+ ohci_connect_change(ohci, num);
+ } while (++num < maxport);
+} /* ohci_check_configuration() */
+
+
+/*
+ * Get annoyed at the controller for bothering us.
+ */
+static void ohci_interrupt(int irq, void *__ohci, struct pt_regs *r)
+{
+ struct ohci *ohci = __ohci;
+ struct ohci_regs *regs = ohci->regs;
+ struct ohci_hcca *hcca = ohci->root_hub->hcca;
+ __u32 donehead = hcca->donehead;
+
+ /*
+ * Check the interrupt status register if needed
+ */
+ if (!donehead || (donehead & 1)) {
+ __u32 intrstatus = readl(®s->intrstatus);
+
+ /*
+ * XXX eek! printk's in an interrupt handler. shoot me!
+ */
+ if (intrstatus & OHCI_INTR_SO) {
+ printk(KERN_DEBUG "usb-ohci: scheduling overrun\n");
+ }
+ if (intrstatus & OHCI_INTR_RD) {
+ printk(KERN_DEBUG "usb-ohci: resume detected\n");
+ }
+ if (intrstatus & OHCI_INTR_UE) {
+ printk(KERN_DEBUG "usb-ohci: unrecoverable error\n");
+ }
+ if (intrstatus & OHCI_INTR_OC) {
+ printk(KERN_DEBUG "usb-ohci: ownership change?\n");
+ }
+
+ if (intrstatus & OHCI_INTR_RHSC) {
+ /* TODO Process events on the root hub */
+ }
+ }
+
+ /*
+ * Process the done list
+ */
+ if (donehead &= ~0x1) {
+ /*
+ * TODO See which TD's completed..
+ */
+ }
+
+ /* Re-enable done queue interrupts and reset the donehead */
+ hcca->donehead = 0;
+ writel(OHCI_INTR_WDH, ®s->intrenable);
+
+} /* ohci_interrupt() */
+
+
+/*
+ * Allocate the resources required for running an OHCI controller.
+ * Host controller interrupts must not be running while calling this
+ * function or the penguins will get angry.
+ *
+ * The mem_base parameter must be the usable -virtual- address of the
+ * host controller's memory mapped I/O registers.
+ */
+static struct ohci *alloc_ohci(void* mem_base)
+{
+ int i;
+ struct ohci *ohci;
+ struct usb_bus *bus;
+ struct ohci_device *dev;
+ struct usb_device *usb;
+
+ ohci = kmalloc(sizeof(*ohci), GFP_KERNEL);
+ if (!ohci)
+ return NULL;
+
+ memset(ohci, 0, sizeof(*ohci));
+
+ ohci->irq = -1;
+ ohci->regs = mem_base;
+ INIT_LIST_HEAD(&ohci->interrupt_list);
+
+ bus = kmalloc(sizeof(*bus), GFP_KERNEL);
+ if (!bus)
+ return NULL;
+
+ memset(bus, 0, sizeof(*bus));
+
+ ohci->bus = bus;
+ bus->hcpriv = ohci;
+ bus->op = &ohci_device_operations;
+
+ /*
+ * Here we allocate our own root hub and TDs as well as the
+ * OHCI host controller communications area. The HCCA is just
+ * a nice pool of memory with pointers to endpoint descriptors
+ * for the different interrupts.
+ */
+ usb = ohci_usb_allocate(NULL);
+ if (!usb)
+ return NULL;
+
+ dev = ohci->root_hub = usb_to_ohci(usb);
+
+ usb->bus = bus;
+
+ /* Initialize the root hub */
+ memset(dev, 0, sizeof(*dev));
+ dev->ohci = ohci; /* link back to the controller */
+
+ /*
+ * Allocate the Host Controller Communications Area
+ */
+ dev->hcca = (struct ohci_hcca *) kmalloc(sizeof(*dev->hcca), GFP_KERNEL);
+
+ /* Tell the controller where the HCCA is */
+ writel(virt_to_bus(dev->hcca), &ohci->regs->hcca);
+
+ /* Get the number of ports on the root hub */
+ usb->maxchild = readl(&ohci->regs->roothub.a) & 0xff;
+ if (usb->maxchild > MAX_ROOT_PORTS) {
+ printk("usb-ohci: Limited to %d ports\n", MAX_ROOT_PORTS);
+ usb->maxchild = MAX_ROOT_PORTS;
+ }
+ if (usb->maxchild < 1) {
+ printk("usb-ohci: Less than one root hub port? Impossible!\n");
+ usb->maxchild = 1;
+ }
+ printk("usb-ohci: %d root hub ports found\n", usb->maxchild);
+
+ printk("alloc_ohci() controller\n");
+ show_ohci_status(ohci);
+ printk("alloc_ohci() root_hub device\n");
+ show_ohci_device(dev);
+
+ /*
+ * Initialize the ED polling "tree" (for simplicity's sake in
+ * this driver many nodes in the tree will be identical)
+ */
+ dev->ed[ED_INT_32].next_ed = virt_to_bus(&dev->ed[ED_INT_16]);
+ dev->ed[ED_INT_16].next_ed = virt_to_bus(&dev->ed[ED_INT_8]);
+ dev->ed[ED_INT_8].next_ed = virt_to_bus(&dev->ed[ED_INT_4]);
+ dev->ed[ED_INT_4].next_ed = virt_to_bus(&dev->ed[ED_INT_2]);
+ dev->ed[ED_INT_2].next_ed = virt_to_bus(&dev->ed[ED_INT_1]);
+
+ /*
+ * Initialize the polling table to call interrupts at the
+ * intended intervals.
+ */
+ for (i = 0; i < NUM_INTS; i++) {
+ if (i == 0)
+ dev->hcca->int_table[i] =
+ virt_to_bus(&dev->ed[ED_INT_32]);
+ else if (i & 1)
+ dev->hcca->int_table[i] =
+ virt_to_bus(&dev->ed[ED_INT_16]);
+ else if (i & 2)
+ dev->hcca->int_table[i] =
+ virt_to_bus(&dev->ed[ED_INT_8]);
+ else if (i & 4)
+ dev->hcca->int_table[i] =
+ virt_to_bus(&dev->ed[ED_INT_4]);
+ else if (i & 8)
+ dev->hcca->int_table[i] =
+ virt_to_bus(&dev->ed[ED_INT_2]);
+ else if (i & 16)
+ dev->hcca->int_table[i] =
+ virt_to_bus(&dev->ed[ED_INT_1]);
+ }
+
+ /*
+ * Tell the controller where the control and bulk lists are
+ */
+ writel(virt_to_bus(&dev->ed[ED_CONTROL]), &ohci->regs->ed_controlhead);
+ writel(virt_to_bus(&dev->ed[ED_BULK]), &ohci->regs->ed_bulkhead);
+
+ return ohci;
+} /* alloc_ohci() */
+
+
+/*
+ * De-allocate all resoueces..
+ */
+static void release_ohci(struct ohci *ohci)
+{
+ if (ohci->irq >= 0) {
+ free_irq(ohci->irq, ohci);
+ ohci->irq = -1;
+ }
+
+ if (ohci->root_hub) {
+ /* ensure that HC is stopped before releasing the HCCA */
+ writel(OHCI_USB_SUSPEND, &ohci->regs->control);
+ free_pages((unsigned int) ohci->root_hub->hcca, 1);
+ free_pages((unsigned int) ohci->root_hub, 1);
+ ohci->root_hub->hcca = NULL;
+ ohci->root_hub = NULL;
+ }
+
+ /* unmap the IO address space */
+ iounmap(ohci->regs);
+
+ /* If the ohci itself were dynamic we'd free it here */
+
+} /* release_ohci() */
+
+/*
+ * USB OHCI control thread
+ */
+static int ohci_control_thread(void * __ohci)
+{
+ struct ohci *ohci = (struct ohci *)__ohci;
+
+ /*
+ * I'm unfamiliar with the SMP kernel locking.. where should
+ * this be released? -greg
+ */
+ lock_kernel();
+
+ /*
+ * This thread doesn't need any user-level access,
+ * so get rid of all of our resources..
+ */
+ printk("ohci_control_thread at %p\n", &ohci_control_thread);
+ exit_mm(current);
+ exit_files(current);
+ exit_fs(current);
+
+ strcpy(current->comm, "ohci-control");
+
+ /*
+ * Damn the torpedoes, full speed ahead
+ */
+ start_hc(ohci);
+ do {
+ interruptible_sleep_on(&ohci_configure);
+#ifdef CONFIG_APM
+ if (apm_resume) {
+ apm_resume = 0;
+ start_hc(ohci);
+ continue;
+ }
+#endif
+ ohci_check_configuration(ohci);
+ } while (!signal_pending(current));
+
+ reset_hc(ohci);
+
+ release_ohci(ohci);
+ MOD_DEC_USE_COUNT;
+ return 0;
+} /* ohci_control_thread() */
+
+
+#ifdef CONFIG_APM
+static int handle_apm_event(apm_event_t event)
+{
+ static int down = 0;
+
+ switch (event) {
+ case APM_SYS_SUSPEND:
+ case APM_USER_SUSPEND:
+ if (down) {
+ printk(KERN_DEBUG "usb-ohci: received extra suspend event\n");
+ break;
+ }
+ down = 1;
+ break;
+ case APM_NORMAL_RESUME:
+ case APM_CRITICAL_RESUME:
+ if (!down) {
+ printk(KERN_DEBUG "usb-ohci: received bogus resume event\n");
+ break;
+ }
+ down = 0;
+ if (waitqueue_active(&ohci_configure)) {
+ apm_resume = 1;
+ wake_up(&ohci_configure);
+ }
+ break;
+ }
+ return 0;
+}
+#endif
+
+/* ... */
+
+/*
+ * Increment the module usage count, start the control thread and
+ * return success if the controller is good.
+ */
+static int found_ohci(int irq, void* mem_base)
+{
+ int retval;
+ struct ohci *ohci;
+
+ /* Allocate the running OHCI structures */
+ ohci = alloc_ohci(mem_base);
+ if (!ohci)
+ return -ENOMEM;
+
+ printk("usb-ohci: alloc_ohci() = %p\n", ohci);
+
+ reset_hc(ohci);
+
+ retval = -EBUSY;
+ if (request_irq(irq, ohci_interrupt, SA_SHIRQ, "usb-ohci", ohci) == 0) {
+ int pid;
+
+ MOD_INC_USE_COUNT;
+ ohci->irq = irq;
+
+ /* fork off the handler */
+ pid = kernel_thread(ohci_control_thread, ohci,
+ CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ if (pid >= 0)
+ return 0;
+
+ MOD_DEC_USE_COUNT;
+ retval = pid;
+ }
+ release_ohci(ohci);
+ return retval;
+} /* found_ohci() */
+
+
+/*
+ * If this controller is for real, map the IO memory and proceed
+ */
+static int init_ohci(struct pci_dev *dev)
+{
+ unsigned int mem_base = dev->base_address[0];
+
+ printk("usb-ohci: mem_base is %p\n", (void*)mem_base);
+
+ /* If its OHCI, its memory */
+ if (mem_base & PCI_BASE_ADDRESS_SPACE_IO)
+ return -ENODEV;
+
+ /* Get the memory address and map it for IO */
+ mem_base &= PCI_BASE_ADDRESS_MEM_MASK;
+ /*
+ * FIXME ioremap_nocache isn't implemented on all CPUs (such
+ * as the Alpha) [?] What should I use instead...
+ *
+ * The iounmap() is done on in release_ohci.
+ */
+ mem_base = (unsigned int) ioremap_nocache(mem_base, 4096);
+
+ if (!mem_base) {
+ printk("Error mapping OHCI memory\n");
+ return -EFAULT;
+ }
+
+ return found_ohci(dev->irq, (void *) mem_base);
+} /* init_ohci() */
+
+
+#ifdef MODULE
+
+/*
+ * Clean up when unloading the module
+ */
+void cleanup_module(void)
+{
+#ifdef CONFIG_APM
+ apm_unregister_callback(&handle_apm_event);
+#endif
+}
+
+#define ohci_init init_module
+
+#endif
+
+
+/* TODO this should be named following Linux convention and go in pci.h */
+#define PCI_CLASS_SERIAL_USB_OHCI ((PCI_CLASS_SERIAL_USB << 8) | 0x0010)
+
+/*
+ * Search the PCI bus for an OHCI USB controller and set it up
+ *
+ * If anyone wants multiple controllers this will need to be
+ * updated.. Right now, it just picks the first one it finds.
+ */
+int ohci_init(void)
+{
+ int retval;
+ struct pci_dev *dev = NULL;
+ /*u8 type;*/
+
+ if (sizeof(struct ohci_device) > 4096) {
+ printk("usb-ohci: struct ohci_device to large\n");
+ return -ENODEV;
+ }
+
+ retval = -ENODEV;
+ for (;;) {
+ /* Find an OHCI USB controller */
+ dev = pci_find_class(PCI_CLASS_SERIAL_USB_OHCI, dev);
+ if (!dev)
+ break;
+
+ /* Verify that its OpenHCI by checking for MMIO */
+ /* pci_read_config_byte(dev, PCI_CLASS_PROG, &type);
+ if (!type)
+ continue; */
+
+ /* Ok, set it up */
+ retval = init_ohci(dev);
+ if (retval < 0)
+ continue;
+
+ /* TODO check module params here to determine what to load */
+
+/* usb_mouse_init(); */
+/* usb_kbd_init();
+ hub_init(); */
+#ifdef CONFIG_APM
+ apm_register_callback(&handle_apm_event);
+#endif
+
+ return 0; /* no error */
+ }
+ return retval;
+} /* init_module() */
+
+/* vim:sw=8
+ */
--- /dev/null
+#ifndef __LINUX_OHCI_H
+#define __LINUX_OHCI_H
+
+/*
+ * Open Host Controller Interface data structures and defines.
+ *
+ * (C) Copyright 1999 Gregory P. Smith <greg@electricrain.com>
+ *
+ * $Id: ohci.h,v 1.6 1999/04/24 22:50:06 greg Exp $
+ */
+
+#include <linux/list.h>
+#include <asm/io.h>
+
+#include "usb.h"
+
+/*
+ * Each TD must be aligned on a 16-byte boundary. From the OHCI v1.0 spec
+ * it does not state that TDs must be contiguious in memory (due to the
+ * use of the next_td field). This gives us extra room at the end of a
+ * TD for our own driver specific data.
+ *
+ * This structure's size must be a multiple of 16 bytes.
+ */
+struct ohci_td {
+ /* OHCI Hardware fields */
+ __u32 info;
+ __u32 cur_buf; /* Current Buffer Pointer */
+ __u32 next_td; /* Next TD Pointer */
+ __u32 buf_end; /* Memory Buffer End Pointer */
+
+ /* Driver specific fields */
+ struct list_head irq_list; /* Active interrupt list */
+ usb_device_irq completed; /* Completion handler routine */
+ void *data; /* XXX ? */
+ void *dev_id; /* XXX ? */
+ __u32 ed_bus; /* bus address of original ED */
+} __attribute((aligned(32)));
+
+#define OHCI_TD_ROUND (1 << 18) /* buffer rounding bit */
+#define OHCI_TD_D (3 << 11) /* direction of xfer: */
+#define OHCI_TD_D_IN (2 << 11)
+#define OHCI_TD_D_OUT (1 << 11)
+#define OHCI_TD_D_SETUP (0)
+#define td_set_dir_in(d) ((d) ? OHCI_TD_D_IN : OHCI_TD_D_OUT )
+#define td_set_dir_out(d) ((d) ? OHCI_TD_D_OUT : OHCI_TD_D_IN )
+#define OHCI_TD_IOC_DELAY (7 << 21) /* frame delay allowed before int. */
+#define OHCI_TD_IOC_OFF (OHCI_TD_IOC_DELAY) /* no interrupt on complete */
+#define OHCI_TD_DT (3 << 24) /* data toggle bits */
+#define td_force_toggle(b) (((b) | 2) << 24)
+#define OHCI_TD_ERRCNT (3 << 26) /* error count */
+#define td_errorcount(td) (((td) >> 26) & 3)
+#define OHCI_TD_CC (0xf << 28) /* condition code */
+#define OHCI_TD_CC_NEW (OHCI_TD_CC) /* set this on all unaccessed TDs! */
+#define td_cc_notaccessed(td) ((td >> 29) == 7)
+#define td_cc_accessed(td) ((td >> 29) != 7)
+#define td_cc_noerror(td) (((td) & OHCI_TD_CC) == 0)
+#define td_active(td) (!td_cc_noerror((td)) && (td_errorcount((td)) < 3))
+#define td_done(td) (td_cc_noerror((td)) || (td_errorcount((td)) == 3))
+
+/*
+ * The endpoint descriptors also requires 16-byte alignment
+ */
+struct ohci_ed {
+ /* OHCI hardware fields */
+ __u32 status;
+ __u32 tail_td; /* TD Queue tail pointer */
+ __u32 head_td; /* TD Queue head pointer */
+ __u32 next_ed; /* Next ED */
+} __attribute((aligned(16)));
+
+#define OHCI_ED_SKIP (1 << 14)
+#define OHCI_ED_MPS (0x7ff << 16)
+/* FIXME: should cap at the USB max packet size [0x4ff] */
+#define ed_set_maxpacket(s) (((s) << 16) & OHCI_ED_MPS)
+#define OHCI_ED_F_NORM (0)
+#define OHCI_ED_F_ISOC (1 << 15)
+#define ed_set_type_isoc(i) ((i) ? OHCI_ED_F_ISOC : OHCI_ED_F_NORM)
+#define OHCI_ED_S_LOW (1 << 13)
+#define OHCI_ED_S_HIGH (0)
+#define ed_set_speed(s) ((s) ? OHCI_ED_S_LOW : OHCI_ED_S_HIGH)
+#define OHCI_ED_D (3 << 11)
+#define OHCI_ED_D_IN (2 << 11)
+#define OHCI_ED_D_OUT (1 << 11)
+#define ed_set_dir_in(d) ((d) ? OHCI_ED_D_IN : OHCI_ED_D_OUT)
+#define ed_set_dir_out(d) ((d) ? OHCI_ED_D_OUT : OHCI_ED_D_IN)
+#define OHCI_ED_EN (0xf << 7)
+#define OHCI_ED_FA (0x7f)
+
+
+/* NOTE: bits 27-31 of the status dword are reserved for the driver */
+/*
+ * We'll use this status flag for the non-predefined EDs to mark if
+ * they're in use or not.
+ *
+ * FIXME: unimplemented (needed?)
+ */
+#define ED_USED (1 << 31)
+
+/*
+ * The HCCA (Host Controller Communications Area) is a 256 byte
+ * structure defined in the OHCI spec. that the host controller is
+ * told the base address of. It must be 256-byte aligned.
+ */
+#define NUM_INTS 32 /* part of the OHCI standard */
+struct ohci_hcca {
+ __u32 int_table[NUM_INTS]; /* Interrupt ED table */
+ __u16 frame_no; /* current frame number */
+ __u16 pad1; /* set to 0 on each frame_no change */
+ __u32 donehead; /* info returned for an interrupt */
+ u8 reserved_for_hc[116];
+} __attribute((aligned(256)));
+
+/*
+ * The TD entries here are pre-allocated as Linus did with his simple
+ * UHCI implementation. With the current state of this driver that
+ * shouldn't be a problem. However if someone ever connects 127
+ * supported devices to this driver and tries to use them all at once:
+ * a) They're insane!
+ * b) They should code in dynamic allocation
+ */
+struct ohci;
+
+/*
+ * Warning: These constants must not be so large as to cause the
+ * ohci_device structure to exceed one 4096 byte page. Or "weird
+ * things will happen" as the alloc_ohci() function assumes that
+ * its less than one page. (FIXME)
+ */
+#define NUM_TDS 32 /* num of preallocated transfer descriptors */
+#define DATA_BUF_LEN 16 /* num of unsigned long's for the data buf */
+
+/*
+ * For this "simple" driver we only support a single ED for each
+ * polling rate.
+ *
+ * Later on this driver should be extended to use a full tree of EDs
+ * so that there can be 32 different 32ms polling frames, etc.
+ * Individual devices shouldn't need as many as the root hub in that
+ * case; complicating how things are done at the moment.
+ *
+ * Bulk and Control transfers hang off of their own ED lists.
+ */
+#define NUM_EDS 16 /* num of preallocated endpoint descriptors */
+
+#define ohci_to_usb(ohci) ((ohci)->usb)
+#define usb_to_ohci(usb) ((struct ohci_device *)(usb)->hcpriv)
+
+/* The usb_device must be first! */
+struct ohci_device {
+ struct usb_device *usb;
+
+ struct ohci *ohci;
+ struct ohci_hcca *hcca; /* OHCI mem. mapped IO area */
+
+ struct ohci_ed ed[NUM_EDS]; /* Endpoint Descriptors */
+ struct ohci_td td[NUM_TDS]; /* Transfer Descriptors */
+
+ unsigned long data[DATA_BUF_LEN];
+};
+
+/* .... */
+
+#define ED_INT_1 0
+#define ED_INT_2 1
+#define ED_INT_4 2
+#define ED_INT_8 3
+#define ED_INT_16 4
+#define ED_INT_32 5
+#define ED_CONTROL 6
+#define ED_BULK 7
+#define ED_ISO ED_INT_1 /* same as 1ms interrupt queue */
+#define ED_FIRST_AVAIL 8 /* first non-reserved ED */
+
+/*
+ * Given a period p in ms, convert it to the closest endpoint
+ * interrupt frequency; rounding down. I'm sure many feel that this
+ * is a gross macro. Feel free to toss it for actual code.
+ */
+#define ms_to_ed_int(p) \
+ ((p >= 32) ? ED_INT_32 : \
+ ((p & 16) ? ED_INT_16 : \
+ ((p & 8) ? ED_INT_8 : \
+ ((p & 4) ? ED_INT_4 : \
+ ((p & 2) ? ED_INT_2 : \
+ ED_INT_1))))) /* hmm.. scheme or lisp anyone? */
+
+/*
+ * This is the maximum number of root hub ports. I don't think we'll
+ * ever see more than two as that's the space available on an ATX
+ * motherboard's case, but it could happen. The OHCI spec allows for
+ * up to 15... (which is insane!)
+ *
+ * Although I suppose several "ports" could be connected directly to
+ * internal laptop devices such as a keyboard, mouse, camera and
+ * serial/parallel ports. hmm... That'd be neat.
+ */
+#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */
+
+/*
+ * This is the structure of the OHCI controller's memory mapped I/O
+ * region. This is Memory Mapped I/O. You must use the readl() and
+ * writel() macros defined in asm/io.h to access these!!
+ */
+struct ohci_regs {
+ /* control and status registers */
+ __u32 revision;
+ __u32 control;
+ __u32 cmdstatus;
+ __u32 intrstatus;
+ __u32 intrenable;
+ __u32 intrdisable;
+ /* memory pointers */
+ __u32 hcca;
+ __u32 ed_periodcurrent;
+ __u32 ed_controlhead;
+ __u32 ed_controlcurrent;
+ __u32 ed_bulkhead;
+ __u32 ed_bulkcurrent;
+ __u32 current_donehead; /* The driver should get this from the HCCA */
+ /* frame counters */
+ __u32 fminterval;
+ __u32 fmremaining;
+ __u32 fmnumber;
+ __u32 periodicstart;
+ __u32 lsthresh;
+ /* Root hub ports */
+ struct ohci_roothub_regs {
+ __u32 a;
+ __u32 b;
+ __u32 status;
+ __u32 portstatus[MAX_ROOT_PORTS];
+ } roothub;
+} __attribute((aligned(32)));
+
+/*
+ * Read a MMIO register and re-write it after ANDing with (m)
+ */
+#define writel_mask(m, a) writel( (readl((__u32)(a))) & (__u32)(m), (__u32)(a) )
+
+/*
+ * Read a MMIO register and re-write it after ORing with (b)
+ */
+#define writel_set(b, a) writel( (readl((__u32)(a))) | (__u32)(b), (__u32)(a) )
+
+
+#define PORT_CCS (1) /* port current connect status */
+#define PORT_PES (1 << 1) /* port enable status */
+#define PORT_PSS (1 << 2) /* port suspend status */
+#define PORT_POCI (1 << 3) /* port overcurrent indicator */
+#define PORT_PRS (1 << 4) /* port reset status */
+#define PORT_PPS (1 << 8) /* port power status */
+#define PORT_LSDA (1 << 9) /* port low speed dev. attached */
+#define PORT_CSC (1 << 16) /* port connect status change */
+#define PORT_PESC (1 << 17) /* port enable status change */
+#define PORT_PSSC (1 << 18) /* port suspend status change */
+#define PORT_OCIC (1 << 19) /* port over current indicator chg */
+#define PORT_PRSC (1 << 20) /* port reset status change */
+
+
+/*
+ * Interrupt register masks
+ */
+#define OHCI_INTR_SO (1)
+#define OHCI_INTR_WDH (1 << 1)
+#define OHCI_INTR_SF (1 << 2)
+#define OHCI_INTR_RD (1 << 3)
+#define OHCI_INTR_UE (1 << 4)
+#define OHCI_INTR_FNO (1 << 5)
+#define OHCI_INTR_RHSC (1 << 6)
+#define OHCI_INTR_OC (1 << 30)
+#define OHCI_INTR_MIE (1 << 31)
+
+/*
+ * Control register masks
+ */
+#define OHCI_USB_OPER (2 << 6)
+#define OHCI_USB_SUSPEND (3 << 6)
+
+/*
+ * This is the full ohci controller description
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs. (Linus)
+ */
+struct ohci {
+ int irq;
+ struct ohci_regs *regs; /* OHCI controller's memory */
+ struct usb_bus *bus;
+ struct ohci_device *root_hub; /* Root hub & controller */
+ struct list_head interrupt_list; /* List of interrupt active TDs for this OHCI */
+};
+
+/* Debugging code */
+void show_ed(struct ohci_ed *ed);
+void show_td(struct ohci_td *td);
+void show_status(struct ohci *ohci);
+
+#endif
+/* vim:sw=8
+ */
--- /dev/null
+#!/bin/sh
+
+ME=`basename $0`
+
+#UMOD=`lsmod | grep '^bp-mouse' | grep -v grep`
+#if test "$UMOD"; then
+# echo "$ME: removing bp-mouse.o"
+# if ! rmmod bp-mouse; then
+# echo "$ME: cannot remove bp-mouse.o"
+# exit 1
+# fi
+#fi
+
+UPID=`ps aux | grep uhci-control | grep -v grep | awk '{print $2}'`
+if test "$UPID"; then
+ echo "$ME: killing $UPID"
+ kill $UPID
+fi
+
+UMOD=`lsmod | grep '^usb-uhci' | grep -v grep`
+if test "$UMOD"; then
+ echo "$ME: removing usb-uhci.o"
+ sleep 1
+ if ! rmmod usb-uhci; then
+ echo "$ME: cannot remove usb-uhci.o"
+ exit 1
+ fi
+fi
+
+dmesg -c > /dev/null
+
+echo "$ME: starting usb-uhci.o"
+insmod -m usb-uhci.o > usb-uhci.map
+#echo "$ME: starting bp-mouse.o"
+#insmod -m bp-mouse.o > bp-mouse.map
--- /dev/null
+#!/bin/sh
+
+killall uhci-control
+killall khubd
+
+sleep 1
+
+rmmod usb-uhci
--- /dev/null
+/*
+ * UHCI-specific debugging code. Invaluable when something
+ * goes wrong, but don't get in my face.
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ */
+
+#include <linux/kernel.h>
+#include <asm/io.h>
+
+#include "uhci.h"
+
+void show_td(struct uhci_td * td)
+{
+ printk("%08x ", td->link);
+ printk("%se%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
+ ((td->status >> 29) & 1) ? "SPD " : "",
+ ((td->status >> 27) & 3),
+ ((td->status >> 26) & 1) ? "LS " : "",
+ ((td->status >> 25) & 1) ? "IOS " : "",
+ ((td->status >> 24) & 1) ? "IOC " : "",
+ ((td->status >> 23) & 1) ? "Active " : "",
+ ((td->status >> 22) & 1) ? "Stalled " : "",
+ ((td->status >> 21) & 1) ? "DataBufErr " : "",
+ ((td->status >> 20) & 1) ? "Babble " : "",
+ ((td->status >> 19) & 1) ? "NAK " : "",
+ ((td->status >> 18) & 1) ? "CRC/Timeo " : "",
+ ((td->status >> 17) & 1) ? "BitStuff " : "",
+ td->status & 0x7ff);
+ printk("MaxLen=%x %sEndPt=%x Dev=%x, PID=%x ",
+ td->info >> 21,
+ ((td->info >> 19) & 1) ? "DT " : "",
+ (td->info >> 15) & 15,
+ (td->info >> 8) & 127,
+ td->info & 0xff);
+ printk("(buf=%08x)\n", td->buffer);
+}
+
+static void show_sc(int port, unsigned short status)
+{
+ printk(" stat%d = %04x %s%s%s%s%s%s%s%s\n",
+ port,
+ status,
+ (status & (1 << 12)) ? " PortSuspend" : "",
+ (status & (1 << 9)) ? " PortReset" : "",
+ (status & (1 << 8)) ? " LowSpeed" : "",
+ (status & 0x40) ? " ResumeDetect" : "",
+ (status & 0x08) ? " EnableChange" : "",
+ (status & 0x04) ? " PortEnabled" : "",
+ (status & 0x02) ? " ConnectChange" : "",
+ (status & 0x01) ? " PortConnected" : "");
+}
+
+void show_status(struct uhci *uhci)
+{
+ unsigned int io_addr = uhci->io_addr;
+ unsigned short usbcmd, usbstat, usbint, usbfrnum;
+ unsigned int flbaseadd;
+ unsigned char sof;
+ unsigned short portsc1, portsc2;
+
+ usbcmd = inw(io_addr + 0);
+ usbstat = inw(io_addr + 2);
+ usbint = inw(io_addr + 4);
+ usbfrnum = inw(io_addr + 6);
+ flbaseadd = inl(io_addr + 8);
+ sof = inb(io_addr + 12);
+ portsc1 = inw(io_addr + 16);
+ portsc2 = inw(io_addr + 18);
+
+ printk(" usbcmd = %04x %s%s%s%s%s%s%s%s\n",
+ usbcmd,
+ (usbcmd & 0x80) ? " Maxp64" : " Maxp32",
+ (usbcmd & 0x40) ? " CF" : "",
+ (usbcmd & 0x20) ? " SWDBG" : "",
+ (usbcmd & 0x10) ? " FGR" : "",
+ (usbcmd & 0x08) ? " EGSM" : "",
+ (usbcmd & 0x04) ? " GRESET" : "",
+ (usbcmd & 0x02) ? " HCRESET" : "",
+ (usbcmd & 0x01) ? " RS" : "");
+
+ printk(" usbstat = %04x %s%s%s%s%s%s\n",
+ usbstat,
+ (usbstat & 0x20) ? " HCHalted" : "",
+ (usbstat & 0x10) ? " HostControllerProcessError" : "",
+ (usbstat & 0x08) ? " HostSystemError" : "",
+ (usbstat & 0x04) ? " ResumeDetect" : "",
+ (usbstat & 0x02) ? " USBError" : "",
+ (usbstat & 0x01) ? " USBINT" : "");
+
+ printk(" usbint = %04x\n", usbint);
+ printk(" usbfrnum = (%d)%03x\n", (usbfrnum >> 10) & 1, 0xfff & (4*(unsigned int)usbfrnum));
+ printk(" flbaseadd = %08x\n", flbaseadd);
+ printk(" sof = %02x\n", sof);
+ show_sc(1, portsc1);
+ show_sc(2, portsc2);
+}
+
+#define uhci_link_to_qh(x) ((struct uhci_qh *) uhci_link_to_td(x))
+
+struct uhci_td * uhci_link_to_td(unsigned int link)
+{
+ if (link & 1)
+ return NULL;
+
+ return bus_to_virt(link & ~15);
+}
+
+void show_queue(struct uhci_qh *qh)
+{
+ struct uhci_td *td;
+ int i = 0;
+
+#if 0
+ printk(" link = %p, element = %p\n", qh->link, qh->element);
+#endif
+ if(!qh->element) {
+ printk(" td 0 = NULL\n");
+ return;
+ }
+
+ for(td = uhci_link_to_td(qh->element); td;
+ td = uhci_link_to_td(td->link)) {
+ printk(" td %d = %p\n", i++, td);
+ printk(" ");
+ show_td(td);
+ }
+}
+
+int is_skeleton_qh(struct uhci *uhci, struct uhci_qh *qh)
+{
+ int j;
+
+ for (j = 0; j < UHCI_MAXQH; j++)
+ if (qh == uhci->root_hub->qh + j)
+ return 1;
+
+ return 0;
+}
+
+static const char *qh_names[] = {"isochronous", "interrupt2", "interrupt4",
+ "interrupt8", "interrupt16", "interrupt32",
+ "interrupt64", "interrupt128", "interrupt256",
+ "control", "bulk0", "bulk1", "bulk2", "bulk3",
+ "unused", "unused"};
+
+void show_queues(struct uhci *uhci)
+{
+ int i;
+ struct uhci_qh *qh;
+
+ for (i = 0; i < UHCI_MAXQH; ++i) {
+ printk(" %s:\n", qh_names[i]);
+#if 0
+ printk(" qh #%d, %p\n", i, virt_to_bus(uhci->root_hub->qh + i));
+ show_queue(uhci->root_hub->qh + i);
+#endif
+
+ qh = uhci_link_to_qh(uhci->root_hub->qh[i].link);
+ for (; qh; qh = uhci_link_to_qh(qh->link)) {
+ if (is_skeleton_qh(uhci, qh))
+ break;
+
+ show_queue(qh);
+ }
+ }
+}
+
--- /dev/null
+/*
+ * Universal Host Controller Interface driver for USB.
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ *
+ * Intel documents this fairly well, and as far as I know there
+ * are no royalties or anything like that, but even so there are
+ * people who decided that they want to do the same thing in a
+ * completely different way.
+ *
+ * Oh, well. The intel version is the more common by far. As such,
+ * that's the one I care about right now.
+ *
+ * WARNING! The USB documentation is downright evil. Most of it
+ * is just crap, written by a committee. You're better off ignoring
+ * most of it, the important stuff is:
+ * - the low-level protocol (fairly simple but lots of small details)
+ * - working around the horridness of the rest
+ */
+
+/* 4/4/1999 added data toggle for interrupt pipes -keryan */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+
+#include <asm/spinlock.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "uhci.h"
+#include "inits.h"
+
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+static int handle_apm_event(apm_event_t event);
+static int apm_resume = 0;
+#endif
+
+#define compile_assert(x) do { switch (0) { case 1: case !(x): } } while (0)
+
+int usb_mouse_init(void);
+int hub_init(void);
+
+static struct wait_queue *uhci_configure = NULL;
+
+/*
+ * Return the result of a TD..
+ */
+static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td)
+{
+ unsigned int status;
+
+ status = (td->status >> 16) & 0xff;
+
+ /* Some debugging code */
+ if (status) {
+ int i = 10;
+ struct uhci_td *tmp = dev->control_td;
+ printk("uhci_td_result() failed with status %d\n", status);
+ show_status(dev->uhci);
+ do {
+ show_td(tmp);
+ tmp++;
+ if (!--i)
+ break;
+ } while (tmp <= td);
+ }
+ return status;
+}
+
+/*
+ * Inserts a td into qh list at the top.
+ *
+ * Careful about atomicity: even on UP this
+ * requires a locked access due to the concurrent
+ * DMA engine.
+ *
+ * NOTE! This assumes that first->last is a valid
+ * list of TD's with the proper backpointers set
+ * up and all..
+ */
+static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct uhci_td *first, struct uhci_td *last)
+{
+ unsigned int link = qh->element;
+ unsigned int new = 4 | virt_to_bus(first);
+
+ for (;;) {
+ unsigned char success;
+
+ last->link = link;
+ first->backptr = &qh->element;
+ asm volatile("lock ; cmpxchg %4,%2 ; sete %0"
+ :"=q" (success), "=a" (link)
+ :"m" (qh->element), "1" (link), "r" (new)
+ :"memory");
+ if (success) {
+ /* Was there a successor entry? Fix it's backpointer.. */
+ if ((link & 1) == 0) {
+ struct uhci_td *next = bus_to_virt(link & ~15);
+ next->backptr = &last->link;
+ }
+ break;
+ }
+ }
+}
+
+static inline void uhci_insert_td_in_qh(struct uhci_qh *qh, struct uhci_td *td)
+{
+ uhci_insert_tds_in_qh(qh, td, td);
+}
+
+static void uhci_insert_qh(struct uhci_qh *qh, struct uhci_qh *newqh)
+{
+ newqh->link = qh->link;
+ qh->link = virt_to_bus(newqh) | 2;
+}
+
+static void uhci_remove_qh(struct uhci_qh *qh, struct uhci_qh *remqh)
+{
+ unsigned int remphys = virt_to_bus(remqh);
+ struct uhci_qh *lqh = qh;
+
+ while ((lqh->link & ~0xF) != remphys) {
+ if (lqh->link & 1)
+ break;
+
+ lqh = bus_to_virt(lqh->link & ~0xF);
+ }
+
+ if (lqh->link & 1) {
+ printk("couldn't find qh in chain!\n");
+ return;
+ }
+
+ lqh->link = remqh->link;
+}
+
+/*
+ * Removes td from qh if present.
+ *
+ * NOTE! We keep track of both forward and back-pointers,
+ * so this should be trivial, right?
+ *
+ * Wrong. While all TD insert/remove operations are synchronous
+ * on the CPU, the UHCI controller can (and does) play with the
+ * very first forward pointer. So we need to validate the backptr
+ * before we change it, so that we don't by mistake reset the QH
+ * head to something old.
+ */
+static void uhci_remove_td(struct uhci_td *td)
+{
+ unsigned int *backptr = td->backptr;
+ unsigned int link = td->link;
+ unsigned int me;
+
+ if (!backptr)
+ return;
+
+ td->backptr = NULL;
+
+ /*
+ * This is the easy case: the UHCI will never change "td->link",
+ * so we can always just look at that and fix up the backpointer
+ * of any next element..
+ */
+ if (!(link & 1)) {
+ struct uhci_td *next = bus_to_virt(link & ~15);
+ next->backptr = backptr;
+ }
+
+ /*
+ * The nasty case is "backptr->next", which we need to
+ * update to "link" _only_ if "backptr" still points
+ * to us (it may not: maybe backptr is a QH->element
+ * pointer and the UHCI has changed the value).
+ */
+ me = virt_to_bus(td) | (0xe & *backptr);
+ asm volatile("lock ; cmpxchg %0,%1"
+ :
+ :"r" (link), "m" (*backptr), "a" (me)
+ :"memory");
+}
+
+static struct uhci_qh *uhci_qh_allocate(struct uhci_device *dev)
+{
+ struct uhci_qh *qh;
+ int inuse;
+
+ qh = dev->qh;
+ for (; (inuse = test_and_set_bit(0, &qh->inuse)) != 0 && qh < &dev->qh[UHCI_MAXQH]; qh++)
+ ;
+
+ if (!inuse)
+ return(qh);
+
+ printk("ran out of qh's for dev %p\n", dev);
+ return(NULL);
+}
+
+static void uhci_qh_deallocate(struct uhci_qh *qh)
+{
+ if (qh->element != 1)
+ printk("qh %p leaving dangling entries? (%X)\n", qh, qh->element);
+
+ qh->element = 1;
+ qh->link = 1;
+
+ clear_bit(0, &qh->inuse);
+}
+
+static struct uhci_td *uhci_td_allocate(struct uhci_device *dev)
+{
+ struct uhci_td *td;
+ int inuse;
+
+ td = dev->td;
+ for (; (inuse = test_and_set_bit(0, &td->inuse)) != 0 && td < &dev->td[UHCI_MAXTD]; td++)
+ ;
+
+ if (!inuse)
+ return(td);
+
+ printk("ran out of td's for dev %p\n", dev);
+ return(NULL);
+}
+
+/*
+ * This MUST only be called when it has been removed from a QH already (or
+ * the QH has been removed from the skeleton
+ */
+static void uhci_td_deallocate(struct uhci_td *td)
+{
+ td->link = 1;
+
+ clear_bit(0, &td->inuse);
+}
+
+/*
+ * UHCI interrupt list operations..
+ */
+static spinlock_t irqlist_lock = SPIN_LOCK_UNLOCKED;
+
+static void uhci_add_irq_list(struct uhci *uhci, struct uhci_td *td, usb_device_irq completed, void *dev_id)
+{
+ unsigned long flags;
+
+ td->completed = completed;
+ td->dev_id = dev_id;
+
+ spin_lock_irqsave(&irqlist_lock, flags);
+ list_add(&td->irq_list, &uhci->interrupt_list);
+ spin_unlock_irqrestore(&irqlist_lock, flags);
+}
+
+static void uhci_remove_irq_list(struct uhci_td *td)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&irqlist_lock, flags);
+ list_del(&td->irq_list);
+ spin_unlock_irqrestore(&irqlist_lock, flags);
+}
+
+/*
+ * Request a interrupt handler..
+ */
+static int uhci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id)
+{
+ struct uhci_device *dev = usb_to_uhci(usb_dev);
+ struct uhci_td *td = uhci_td_allocate(dev);
+ struct uhci_qh *interrupt_qh = uhci_qh_allocate(dev);
+
+ unsigned int destination, status;
+
+ /* Destination: pipe destination with INPUT */
+ destination = (pipe & 0x0007ff00) | 0x69;
+
+ /* Status: slow/fast, Interrupt, Active, Short Packet Detect Infinite Errors */
+ status = (pipe & (1 << 26)) | (1 << 24) | (1 << 23) | (1 << 29) | (0 << 27);
+
+ if(interrupt_qh->element != 1)
+ printk("interrupt_qh->element = 0x%x\n",
+ interrupt_qh->element);
+
+ td->link = 1;
+ td->status = status; /* In */
+ td->info = destination | (7 << 21); /* 8 bytes of data */
+ td->buffer = virt_to_bus(dev->data);
+ td->qh = interrupt_qh;
+ interrupt_qh->skel = &dev->uhci->root_hub->skel_int8_qh;
+
+ uhci_add_irq_list(dev->uhci, td, handler, dev_id);
+
+ uhci_insert_td_in_qh(interrupt_qh, td);
+
+ /* Add it into the skeleton */
+ uhci_insert_qh(&dev->uhci->root_hub->skel_int8_qh, interrupt_qh);
+ return 0;
+}
+
+/*
+ * Control thread operations: we just mark the last TD
+ * in a control thread as an interrupt TD, and wake up
+ * the front-end on completion.
+ *
+ * We need to remove the TD from the lists (both interrupt
+ * list and TD lists) by hand if something bad happens!
+ */
+static struct wait_queue *control_wakeup;
+
+static int uhci_control_completed(int status, void *buffer, void *dev_id)
+{
+ wake_up(&control_wakeup);
+ return 0; /* Don't re-instate */
+}
+
+/* td points to the last td in the list, which interrupts on completion */
+static int uhci_run_control(struct uhci_device *dev, struct uhci_td *first, struct uhci_td *last)
+{
+ struct wait_queue wait = { current, NULL };
+ struct uhci_qh *ctrl_qh = uhci_qh_allocate(dev);
+ struct uhci_td *curtd;
+
+ current->state = TASK_UNINTERRUPTIBLE;
+ add_wait_queue(&control_wakeup, &wait);
+
+ uhci_add_irq_list(dev->uhci, last, uhci_control_completed, NULL);
+
+ /* FIXME: This is kinda kludged */
+ /* Walk the TD list and update the QH pointer */
+ {
+ int maxcount = 100;
+
+ curtd = first;
+ do {
+ curtd->qh = ctrl_qh;
+ if (curtd->link & 1)
+ break;
+
+ curtd = bus_to_virt(curtd->link & ~0xF);
+ if (!--maxcount) {
+ printk("runaway tds!\n");
+ break;
+ }
+ } while (1);
+ }
+
+ uhci_insert_tds_in_qh(ctrl_qh, first, last);
+
+ /* Add it into the skeleton */
+ uhci_insert_qh(&dev->uhci->root_hub->skel_control_qh, ctrl_qh);
+
+ schedule_timeout(HZ/10);
+
+ remove_wait_queue(&control_wakeup, &wait);
+
+ /* Clean up in case it failed.. */
+ uhci_remove_irq_list(last);
+
+#if 0
+ printk("Looking for tds [%p, %p]\n", dev->control_td, td);
+#endif
+
+ /* Remove it from the skeleton */
+ uhci_remove_qh(&dev->uhci->root_hub->skel_control_qh, ctrl_qh);
+
+ uhci_qh_deallocate(ctrl_qh);
+
+ return uhci_td_result(dev, last);
+}
+
+/*
+ * Send or receive a control message on a pipe.
+ *
+ * Note that the "pipe" structure is set up to map
+ * easily to the uhci destination fields.
+ *
+ * A control message is built up from three parts:
+ * - The command itself
+ * - [ optional ] data phase
+ * - Status complete phase
+ *
+ * The data phase can be an arbitrary number of TD's
+ * although we currently had better not have more than
+ * 29 TD's here (we have 31 TD's allocated for control
+ * operations, and two of them are used for command and
+ * status).
+ *
+ * 29 TD's is a minimum of 232 bytes worth of control
+ * information, that's just ridiculously high. Most
+ * control messages have just a few bytes of data.
+ */
+static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, void *cmd, void *data, int len)
+{
+ struct uhci_device *dev = usb_to_uhci(usb_dev);
+ struct uhci_td *first, *td, *prevtd;
+ unsigned long destination, status;
+ int ret;
+
+ if (len > usb_maxpacket(usb_dev->maxpacketsize) * 29)
+ printk("Warning, too much data for a control packet, crashing\n");
+
+ first = td = uhci_td_allocate(dev);
+
+ /* The "pipe" thing contains the destination in bits 8--18, 0x2D is SETUP */
+ destination = (pipe & 0x0007ff00) | 0x2D;
+
+ /* Status: slow/fast, Active, Short Packet Detect Three Errors */
+ status = (pipe & (1 << 26)) | (1 << 23) | (1 << 29) | (3 << 27);
+
+ /*
+ * Build the TD for the control request
+ */
+ td->status = status; /* Try forever */
+ td->info = destination | (7 << 21); /* 8 bytes of data */
+ td->buffer = virt_to_bus(cmd);
+
+ /*
+ * If direction is "send", change the frame from SETUP (0x2D)
+ * to OUT (0xE1). Else change it from SETUP to IN (0x69)
+ */
+ destination ^= (0x2D ^ 0x69); /* SETUP -> IN */
+ if (usb_pipeout(pipe))
+ destination ^= (0xE1 ^ 0x69); /* IN -> OUT */
+
+ prevtd = td;
+ td = uhci_td_allocate(dev);
+ prevtd->link = 4 | virt_to_bus(td);
+
+ /*
+ * Build the DATA TD's
+ */
+ while (len > 0) {
+ /* Build the TD for control status */
+ int pktsze = len;
+ int maxsze = usb_maxpacket(pipe);
+
+ if (pktsze > maxsze)
+ pktsze = maxsze;
+
+ /* Alternate Data0/1 (start with Data1) */
+ destination ^= 1 << 19;
+
+ td->status = status; /* Status */
+ td->info = destination | ((pktsze-1) << 21); /* pktsze bytes of data */
+ td->buffer = virt_to_bus(data);
+ td->backptr = &prevtd->link;
+
+ prevtd = td;
+ td = uhci_td_allocate(dev);
+ prevtd->link = 4 | virt_to_bus(td); /* Update previous TD */
+
+ data += maxsze;
+ len -= maxsze;
+ }
+
+ /*
+ * Build the final TD for control status
+ */
+ destination ^= (0xE1 ^ 0x69); /* OUT -> IN */
+ destination |= 1 << 19; /* End in Data1 */
+
+ td->link = 1; /* Terminate */
+ td->status = status | (1 << 24); /* IOC */
+ td->info = destination | (0x7ff << 21); /* 0 bytes of data */
+ td->buffer = 0;
+ td->backptr = &prevtd->link;
+
+ /* Start it up.. */
+ ret = uhci_run_control(dev, first, td);
+
+ {
+ int maxcount = 100;
+ struct uhci_td *curtd = first;
+ unsigned int nextlink;
+
+ do {
+ nextlink = curtd->link;
+ uhci_remove_td(curtd);
+ uhci_td_deallocate(curtd);
+ if (nextlink & 1) /* Tail? */
+ break;
+
+ curtd = bus_to_virt(nextlink & ~0xF);
+ if (!--maxcount) {
+ printk("runaway td's!?\n");
+ break;
+ }
+ } while (1);
+ }
+
+ return ret;
+}
+
+static struct usb_device *uhci_usb_allocate(struct usb_device *parent)
+{
+ struct usb_device *usb_dev;
+ struct uhci_device *dev;
+ int i;
+
+ usb_dev = kmalloc(sizeof(*usb_dev), GFP_KERNEL);
+ if (!usb_dev)
+ return NULL;
+
+ memset(usb_dev, 0, sizeof(*usb_dev));
+
+ dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ kfree(usb_dev);
+ return NULL;
+ }
+
+ /* Initialize "dev" */
+ memset(dev, 0, sizeof(*dev));
+
+ usb_dev->hcpriv = dev;
+ dev->usb = usb_dev;
+
+ usb_dev->parent = parent;
+
+ if (parent) {
+ usb_dev->bus = parent->bus;
+ dev->uhci = usb_to_uhci(parent)->uhci;
+ }
+
+ /* Reset the QH's and TD's */
+ for (i = 0; i < UHCI_MAXQH; i++) {
+ dev->qh[i].link = 1;
+ dev->qh[i].element = 1;
+ dev->qh[i].inuse = 0;
+ }
+
+ for (i = 0; i < UHCI_MAXTD; i++) {
+ dev->td[i].link = 1;
+ dev->td[i].inuse = 0;
+ }
+
+ return usb_dev;
+}
+
+static int uhci_usb_deallocate(struct usb_device *usb_dev)
+{
+ struct uhci_device *dev = usb_to_uhci(usb_dev);
+ int i;
+
+ /* There are UHCI_MAXTD preallocated tds */
+ for (i = 0; i < UHCI_MAXTD; ++i) {
+ struct uhci_td *td = dev->td + i;
+
+ /* And remove it from the irq list, if it's active */
+ if (td->status & (1 << 23))
+ uhci_remove_irq_list(td);
+
+ if (td->inuse)
+ uhci_remove_td(td);
+ }
+
+ /* Remove the td from any queues */
+ for (i = 0; i < UHCI_MAXQH; ++i) {
+ struct uhci_qh *qh = dev->qh + i;
+
+ if (qh->inuse)
+ uhci_remove_qh(qh->skel, qh);
+ }
+
+ kfree(dev);
+ kfree(usb_dev);
+
+ return 0;
+}
+
+struct usb_operations uhci_device_operations = {
+ uhci_usb_allocate,
+ uhci_usb_deallocate,
+ uhci_control_msg,
+ uhci_request_irq,
+};
+
+/*
+ * This is just incredibly fragile. The timings must be just
+ * right, and they aren't really documented very well.
+ *
+ * Note the short delay between disabling reset and enabling
+ * the port..
+ */
+static void uhci_reset_port(unsigned int port)
+{
+ unsigned short status;
+
+ status = inw(port);
+ outw(status | USBPORTSC_PR, port); /* reset port */
+ wait_ms(10);
+ outw(status & ~USBPORTSC_PR, port);
+ udelay(5);
+
+ status = inw(port);
+ outw(status | USBPORTSC_PE, port); /* enable port */
+ wait_ms(10);
+
+ status = inw(port);
+ if(!(status & USBPORTSC_PE)) {
+ outw(status | USBPORTSC_PE, port); /* one more try at enabling port */
+ wait_ms(50);
+ }
+
+}
+
+
+/*
+ * This gets called if the connect status on the root
+ * hub (and the root hub only) changes.
+ */
+static void uhci_connect_change(struct uhci *uhci, unsigned int port, unsigned int nr)
+{
+ struct usb_device *usb_dev;
+ struct uhci_device *dev;
+ unsigned short status;
+
+ printk("uhci_connect_change: called for %d\n", nr);
+
+ /*
+ * Even if the status says we're connected,
+ * the fact that the status bits changed may
+ * that we got disconnected and then reconnected.
+ *
+ * So start off by getting rid of any old devices..
+ */
+ usb_disconnect(&uhci->root_hub->usb->children[nr]);
+
+ status = inw(port);
+
+ /* If we have nothing connected, then clear change status and disable the port */
+ status = (status & ~USBPORTSC_PE) | USBPORTSC_PEC;
+ if (!(status & USBPORTSC_CCS)) {
+ outw(status, port);
+ return;
+ }
+
+ /*
+ * Ok, we got a new connection. Allocate a device to it,
+ * and find out what it wants to do..
+ */
+ usb_dev = uhci_usb_allocate(uhci->root_hub->usb);
+ dev = usb_dev->hcpriv;
+
+ dev->uhci = uhci;
+
+ usb_connect(usb_dev);
+
+ uhci->root_hub->usb->children[nr] = usb_dev;
+
+ wait_ms(200); /* wait for powerup */
+ uhci_reset_port(port);
+
+ /* Get speed information */
+ usb_dev->slow = (inw(port) & USBPORTSC_LSDA) ? 1 : 0;
+
+ /*
+ * Ok, all the stuff specific to the root hub has been done.
+ * The rest is generic for any new USB attach, regardless of
+ * hub type.
+ */
+ usb_new_device(usb_dev);
+}
+
+/*
+ * This gets called when the root hub configuration
+ * has changed. Just go through each port, seeing if
+ * there is something interesting happening.
+ */
+static void uhci_check_configuration(struct uhci *uhci)
+{
+ unsigned int io_addr = uhci->io_addr + USBPORTSC1;
+ int maxchild = uhci->root_hub->usb->maxchild;
+ int nr = 0;
+
+ do {
+ unsigned short status = inw(io_addr);
+
+ if (status & USBPORTSC_CSC)
+ uhci_connect_change(uhci, io_addr, nr);
+
+ nr++; io_addr += 2;
+ } while (nr < maxchild);
+}
+
+static void uhci_interrupt_notify(struct uhci *uhci)
+{
+ struct list_head *head = &uhci->interrupt_list;
+ struct list_head *tmp;
+
+ spin_lock(&irqlist_lock);
+ tmp = head->next;
+ while (tmp != head) {
+ struct uhci_td *td = list_entry(tmp, struct uhci_td, irq_list);
+ struct list_head *next;
+
+ next = tmp->next;
+
+ if (!(td->status & (1 << 23))) { /* No longer active? */
+ /* remove from IRQ list */
+ __list_del(tmp->prev, next);
+ INIT_LIST_HEAD(tmp);
+ if (td->completed(td->status, bus_to_virt(td->buffer), td->dev_id)) {
+ struct uhci_qh *interrupt_qh = td->qh;
+
+ list_add(&td->irq_list, &uhci->interrupt_list);
+ td->info ^= 1 << 19; /* toggle between data0 and data1 */
+ td->status = (td->status & 0x2f000000) | (1 << 23) | (1 << 24); /* active */
+
+ /* Remove then readd? Is that necessary */
+ uhci_remove_td(td);
+ uhci_insert_td_in_qh(interrupt_qh, td);
+ }
+ /* If completed wants to not reactivate, then it's */
+ /* responsible for free'ing the TD's and QH's */
+ /* or another function (such as run_control) */
+ }
+ tmp = next;
+ }
+ spin_unlock(&irqlist_lock);
+}
+
+/*
+ * Check port status - Connect Status Change - for
+ * each of the attached ports (defaults to two ports,
+ * but at least in theory there can be more of them).
+ *
+ * Wake up the configurator if something happened, we
+ * can't really do much at interrupt time.
+ */
+static void uhci_root_hub_events(struct uhci *uhci, unsigned int io_addr)
+{
+ if (waitqueue_active(&uhci_configure)) {
+ int ports = uhci->root_hub->usb->maxchild;
+ io_addr += USBPORTSC1;
+ do {
+ if (inw(io_addr) & USBPORTSC_CSC) {
+ wake_up(&uhci_configure);
+ return;
+ }
+ io_addr += 2;
+ } while (--ports > 0);
+ }
+}
+
+static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs)
+{
+ struct uhci *uhci = __uhci;
+ unsigned int io_addr = uhci->io_addr;
+ unsigned short status;
+
+ /*
+ * Read the interrupt status, and write it back to clear the interrupt cause
+ */
+ status = inw(io_addr + USBSTS);
+ outw(status, io_addr + USBSTS);
+
+ /* Walk the list of pending TD's to see which ones completed.. */
+ uhci_interrupt_notify(uhci);
+
+ /* Check if there are any events on the root hub.. */
+ uhci_root_hub_events(uhci, io_addr);
+}
+
+/*
+ * We init one packet, and mark it just IOC and _not_
+ * active. Which will result in no actual USB traffic,
+ * but _will_ result in an interrupt every second.
+ *
+ * Which is exactly what we want.
+ */
+static void uhci_init_ticktd(struct uhci *uhci)
+{
+ struct uhci_device *dev = uhci->root_hub;
+ struct uhci_td *td = uhci_td_allocate(dev);
+
+ td->link = 1;
+ td->status = (1 << 24); /* interrupt on completion */
+ td->info = (15 << 21) | 0x7f69; /* (ignored) input packet, 16 bytes, device 127 */
+ td->buffer = 0;
+ td->qh = NULL;
+
+ uhci->fl->frame[0] = virt_to_bus(td);
+}
+
+static void reset_hc(struct uhci *uhci)
+{
+ unsigned int io_addr = uhci->io_addr;
+
+ /* Global reset for 50ms */
+ outw(USBCMD_GRESET, io_addr+USBCMD);
+ wait_ms(50);
+ outw(0, io_addr+USBCMD);
+ wait_ms(10);
+}
+
+static void start_hc(struct uhci *uhci)
+{
+ unsigned int io_addr = uhci->io_addr;
+ int timeout = 1000;
+
+ uhci_init_ticktd(uhci);
+
+ /*
+ * Reset the HC - this will force us to get a
+ * new notification of any already connected
+ * ports due to the virtual disconnect that it
+ * implies.
+ */
+ outw(USBCMD_HCRESET, io_addr + USBCMD);
+ while (inw(io_addr + USBCMD) & USBCMD_HCRESET) {
+ if (!--timeout) {
+ printk("USBCMD_HCRESET timed out!\n");
+ break;
+ }
+ }
+
+ outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR);
+ outw(0, io_addr + USBFRNUM);
+ outl(virt_to_bus(uhci->fl), io_addr + USBFLBASEADD);
+
+ /* Run and mark it configured with a 64-byte max packet */
+ outw(USBCMD_RS | USBCMD_CF, io_addr + USBCMD);
+}
+
+/*
+ * Allocate a frame list, and four regular queues.
+ *
+ * The hardware doesn't really know any difference
+ * in the queues, but the order does matter for the
+ * protocols higher up. The order is:
+ *
+ * - any isochronous events handled before any
+ * of the queues. We don't do that here, because
+ * we'll create the actual TD entries on demand.
+ * - The first queue is the "interrupt queue".
+ * - The second queue is the "control queue".
+ * - The third queue is "bulk data".
+ *
+ * We could certainly have multiple queues of the same
+ * type, and maybe we should. We could have per-device
+ * queues, for example. We begin small.
+ */
+static struct uhci *alloc_uhci(unsigned int io_addr)
+{
+ int i;
+ struct uhci *uhci;
+ struct usb_bus *bus;
+ struct uhci_device *dev;
+ struct usb_device *usb;
+
+ uhci = kmalloc(sizeof(*uhci), GFP_KERNEL);
+ if (!uhci)
+ return NULL;
+
+ memset(uhci, 0, sizeof(*uhci));
+
+ uhci->irq = -1;
+ uhci->io_addr = io_addr;
+ INIT_LIST_HEAD(&uhci->interrupt_list);
+
+ /* We need exactly one page (per UHCI specs), how convenient */
+ uhci->fl = (void *)__get_free_page(GFP_KERNEL);
+
+ bus = kmalloc(sizeof(*bus), GFP_KERNEL);
+ if (!bus)
+ return NULL;
+
+ memset(bus, 0, sizeof(*bus));
+
+ uhci->bus = bus;
+ bus->hcpriv = uhci;
+ bus->op = &uhci_device_operations;
+
+ /*
+ * We allocate a 8kB area for the UHCI hub. The area
+ * is described by the uhci_device structure, and basically
+ * contains everything needed for normal operation.
+ *
+ * The first page is the actual device descriptor for the
+ * hub.
+ *
+ * The second page is used for the frame list.
+ */
+ usb = uhci_usb_allocate(NULL);
+ if (!usb)
+ return NULL;
+
+ dev = uhci->root_hub = usb_to_uhci(usb);
+
+ usb->bus = bus;
+
+ /* Initialize the root hub */
+ /* UHCI specs says devices must have 2 ports, but goes on to say */
+ /* they may have more but give no way to determine how many they */
+ /* have, so default to 2 */
+ usb->maxchild = 2;
+ usb_init_root_hub(usb);
+
+ /*
+ * Initialize the queues. They all start out empty,
+ * linked to each other in the proper order.
+ */
+ for (i = 1 ; i < 9; i++) {
+ dev->qh[i].link = 2 | virt_to_bus(&dev->skel_control_qh);
+ dev->qh[i].element = 1;
+ }
+
+ dev->skel_control_qh.link = 2 | virt_to_bus(&dev->skel_bulk0_qh);
+ dev->skel_control_qh.element = 1;
+
+ dev->skel_bulk0_qh.link = 2 | virt_to_bus(&dev->skel_bulk1_qh);
+ dev->skel_bulk0_qh.element = 1;
+
+ dev->skel_bulk1_qh.link = 2 | virt_to_bus(&dev->skel_bulk2_qh);
+ dev->skel_bulk1_qh.element = 1;
+
+ dev->skel_bulk2_qh.link = 2 | virt_to_bus(&dev->skel_bulk3_qh);
+ dev->skel_bulk2_qh.element = 1;
+
+ dev->skel_bulk3_qh.link = 1;
+ dev->skel_bulk3_qh.element = 1;
+
+ /*
+ * Fill the frame list: make all entries point to
+ * the proper interrupt queue.
+ *
+ * This is probably silly, but it's a simple way to
+ * scatter the interrupt queues in a way that gives
+ * us a reasonable dynamic range for irq latencies.
+ */
+ for (i = 0; i < 1024; i++) {
+ struct uhci_qh * irq = &dev->skel_int2_qh;
+ if (i & 1) {
+ irq++;
+ if (i & 2) {
+ irq++;
+ if (i & 4) {
+ irq++;
+ if (i & 8) {
+ irq++;
+ if (i & 16) {
+ irq++;
+ if (i & 32) {
+ irq++;
+ if (i & 64) {
+ irq++;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ uhci->fl->frame[i] = 2 | virt_to_bus(irq);
+ }
+
+ return uhci;
+}
+
+
+/*
+ * De-allocate all resources..
+ */
+static void release_uhci(struct uhci *uhci)
+{
+ if (uhci->irq >= 0) {
+ free_irq(uhci->irq, uhci);
+ uhci->irq = -1;
+ }
+
+#if 0
+ if (uhci->root_hub) {
+ uhci_usb_deallocate(uhci_to_usb(uhci->root_hub));
+ uhci->root_hub = NULL;
+ }
+#endif
+
+ if (uhci->fl) {
+ free_page((unsigned long)uhci->fl);
+ uhci->fl = NULL;
+ }
+
+ kfree(uhci->bus);
+ kfree(uhci);
+}
+
+static int uhci_control_thread(void * __uhci)
+{
+ struct uhci *uhci = (struct uhci *)__uhci;
+
+ lock_kernel();
+ request_region(uhci->io_addr, 32, "usb-uhci");
+
+ /*
+ * This thread doesn't need any user-level access,
+ * so get rid of all our resources..
+ */
+ printk("uhci_control_thread at %p\n", &uhci_control_thread);
+ exit_mm(current);
+ exit_files(current);
+ exit_fs(current);
+
+ strcpy(current->comm, "uhci-control");
+
+ /*
+ * Ok, all systems are go..
+ */
+ start_hc(uhci);
+ for(;;) {
+ siginfo_t info;
+ int unsigned long signr;
+
+ interruptible_sleep_on(&uhci_configure);
+#ifdef CONFIG_APM
+ if (apm_resume) {
+ apm_resume = 0;
+ start_hc(uhci);
+ continue;
+ }
+#endif
+ uhci_check_configuration(uhci);
+
+ if(signal_pending(current)) {
+ /* sending SIGUSR1 makes us print out some info */
+ spin_lock_irq(¤t->sigmask_lock);
+ signr = dequeue_signal(¤t->blocked, &info);
+ spin_unlock_irq(¤t->sigmask_lock);
+
+ if(signr == SIGUSR1) {
+ printk("UHCI queue dump:\n");
+ show_queues(uhci);
+ } else {
+ break;
+ }
+ }
+ }
+
+#if 0
+ if(uhci->root_hub)
+ for(i = 0; i < uhci->root_hub->usb->maxchild; i++)
+ usb_disconnect(uhci->root_hub->usb->children + i);
+#endif
+
+ reset_hc(uhci);
+ release_region(uhci->io_addr, 32);
+
+ release_uhci(uhci);
+ MOD_DEC_USE_COUNT;
+
+ printk("uhci_control_thread exiting\n");
+
+ return 0;
+}
+
+/*
+ * If we've successfully found a UHCI, now is the time to increment the
+ * module usage count, start the control thread, and return success..
+ */
+static int found_uhci(int irq, unsigned int io_addr)
+{
+ int retval;
+ struct uhci *uhci;
+
+ uhci = alloc_uhci(io_addr);
+ if (!uhci)
+ return -ENOMEM;
+
+ reset_hc(uhci);
+
+ retval = -EBUSY;
+ if (request_irq(irq, uhci_interrupt, SA_SHIRQ, "usb", uhci) == 0) {
+ int pid;
+
+ MOD_INC_USE_COUNT;
+ uhci->irq = irq;
+ pid = kernel_thread(uhci_control_thread, uhci, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ if (pid >= 0)
+ return 0;
+
+ MOD_DEC_USE_COUNT;
+ retval = pid;
+ }
+ release_uhci(uhci);
+ return retval;
+}
+
+static int start_uhci(struct pci_dev *dev)
+{
+ int i;
+
+ /* Search for the IO base address.. */
+ for (i = 0; i < 6; i++) {
+ unsigned int io_addr = dev->base_address[i];
+
+ /* IO address? */
+ if (!(io_addr & 1))
+ continue;
+
+ io_addr &= PCI_BASE_ADDRESS_IO_MASK;
+
+ /* Is it already in use? */
+ if (check_region(io_addr, 32))
+ break;
+
+ return found_uhci(dev->irq, io_addr);
+ }
+ return -1;
+}
+
+#ifdef CONFIG_APM
+static int handle_apm_event(apm_event_t event)
+{
+ static int down = 0;
+
+ switch (event) {
+ case APM_SYS_SUSPEND:
+ case APM_USER_SUSPEND:
+ if (down) {
+ printk(KERN_DEBUG "uhci: received extra suspend event\n");
+ break;
+ }
+ down = 1;
+ break;
+ case APM_NORMAL_RESUME:
+ case APM_CRITICAL_RESUME:
+ if (!down) {
+ printk(KERN_DEBUG "uhci: received bogus resume event\n");
+ break;
+ }
+ down = 0;
+ if (waitqueue_active(&uhci_configure)) {
+ apm_resume = 1;
+ wake_up(&uhci_configure);
+ }
+ break;
+ }
+ return 0;
+}
+#endif
+
+#ifdef MODULE
+
+void cleanup_module(void)
+{
+#ifdef CONFIG_APM
+ apm_unregister_callback(&handle_apm_event);
+#endif
+}
+
+#define uhci_init init_module
+
+#endif
+
+int uhci_init(void)
+{
+ int retval;
+ struct pci_dev *dev = NULL;
+ u8 type;
+
+ retval = -ENODEV;
+ for (;;) {
+ dev = pci_find_class(PCI_CLASS_SERIAL_USB<<8, dev);
+ if (!dev)
+ break;
+ /* Is it UHCI */
+ pci_read_config_byte(dev, PCI_CLASS_PROG, &type);
+ if(type != 0)
+ continue;
+ /* Ok set it up */
+ retval = start_uhci(dev);
+ if (retval < 0)
+ continue;
+
+ usb_mouse_init();
+ usb_kbd_init();
+ hub_init();
+#ifdef CONFIG_APM
+ apm_register_callback(&handle_apm_event);
+#endif
+
+ return 0;
+ }
+ return retval;
+}
--- /dev/null
+#ifndef __LINUX_UHCI_H
+#define __LINUX_UHCI_H
+
+#include <linux/list.h>
+
+#include "usb.h"
+
+/*
+ * Universal Host Controller Interface data structures and defines
+ */
+
+/* Command register */
+#define USBCMD 0
+#define USBCMD_RS 0x0001 /* Run/Stop */
+#define USBCMD_HCRESET 0x0002 /* Host reset */
+#define USBCMD_GRESET 0x0004 /* Global reset */
+#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */
+#define USBCMD_FGR 0x0010 /* Force Global Resume */
+#define USBCMD_SWDBG 0x0020 /* SW Debug mode */
+#define USBCMD_CF 0x0040 /* Config Flag (sw only) */
+#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */
+
+/* Status register */
+#define USBSTS 2
+#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */
+#define USBSTS_ERROR 0x0002 /* Interrupt due to error */
+#define USBSTS_RD 0x0004 /* Resume Detect */
+#define USBSTS_HSE 0x0008 /* Host System Error - basically PCI problems */
+#define USBSTS_HCPE 0x0010 /* Host Controller Process Error - the scripts were buggy */
+#define USBSTS_HCH 0x0020 /* HC Halted */
+
+/* Interrupt enable register */
+#define USBINTR 4
+#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */
+#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */
+#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */
+#define USBINTR_SP 0x0008 /* Short packet interrupt enable */
+
+#define USBFRNUM 6
+#define USBFLBASEADD 8
+#define USBSOF 12
+
+/* USB port status and control registers */
+#define USBPORTSC1 16
+#define USBPORTSC2 18
+#define USBPORTSC_CCS 0x0001 /* Current Connect Status ("device present") */
+#define USBPORTSC_CSC 0x0002 /* Connect Status Change */
+#define USBPORTSC_PE 0x0004 /* Port Enable */
+#define USBPORTSC_PEC 0x0008 /* Port Enable Change */
+#define USBPORTSC_LS 0x0030 /* Line Status */
+#define USBPORTSC_RD 0x0040 /* Resume Detect */
+#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */
+#define USBPORTSC_PR 0x0200 /* Port Reset */
+#define USBPORTSC_SUSP 0x1000 /* Suspend */
+
+struct uhci_qh {
+ unsigned int link; /* Next queue */
+ unsigned int element; /* Queue element pointer */
+ int inuse; /* Inuse? */
+ struct uhci_qh *skel; /* Skeleton head */
+} __attribute__((aligned(16)));
+
+struct uhci_framelist {
+ unsigned int frame[1024];
+} __attribute__((aligned(4096)));
+
+/*
+ * The documentation says "4 words for hardware, 4 words for software".
+ *
+ * That's silly, the hardware doesn't care. The hardware only cares that
+ * the hardware words are 16-byte aligned, and we can have any amount of
+ * sw space after the TD entry as far as I can tell.
+ *
+ * But let's just go with the documentation, at least for 32-bit machines.
+ * On 64-bit machines we probably want to take advantage of the fact that
+ * hw doesn't really care about the size of the sw-only area.
+ *
+ * Alas, not anymore, we have more than 4 words of software, woops
+ */
+struct uhci_td {
+ /* Hardware fields */
+ __u32 link;
+ __u32 status;
+ __u32 info;
+ __u32 buffer;
+
+ /* Software fields */
+ struct list_head irq_list; /* Active interrupt list.. */
+ usb_device_irq completed; /* Completion handler routine */
+ unsigned int *backptr; /* Where to remove this from.. */
+ void *dev_id;
+ int inuse; /* Inuse? */
+ struct uhci_qh *qh;
+} __attribute__((aligned(32)));
+
+/*
+ * Note the alignment requirements of the entries
+ *
+ * Each UHCI device has pre-allocated QH and TD entries.
+ * You can use more than the pre-allocated ones, but I
+ * don't see you usually needing to.
+ */
+struct uhci;
+
+#define UHCI_MAXTD 32
+
+#define UHCI_MAXQH 16
+
+/* The usb device part must be first! */
+struct uhci_device {
+ struct usb_device *usb;
+
+ struct uhci *uhci;
+ struct uhci_qh qh[UHCI_MAXQH]; /* These are the "common" qh's for each device */
+ struct uhci_td td[UHCI_MAXTD];
+
+ unsigned long data[16];
+};
+
+#define uhci_to_usb(uhci) ((uhci)->usb)
+#define usb_to_uhci(usb) ((struct uhci_device *)(usb)->hcpriv)
+
+/*
+ * The root hub pre-allocated QH's and TD's have
+ * some special global uses..
+ */
+#define control_td td /* Td's 0-30 */
+/* This is only for the root hub's TD list */
+#define tick_td td[31]
+
+/*
+ * There are various standard queues. We set up several different
+ * queues for each of the three basic queue types: interrupt,
+ * control, and bulk.
+ *
+ * - There are various different interrupt latencies: ranging from
+ * every other USB frame (2 ms apart) to every 256 USB frames (ie
+ * 256 ms apart). Make your choice according to how obnoxious you
+ * want to be on the wire, vs how critical latency is for you.
+ * - The control list is done every frame.
+ * - There are 4 bulk lists, so that up to four devices can have a
+ * bulk list of their own and when run concurrently all four lists
+ * will be be serviced.
+ *
+ * This is a bit misleading, there are various interrupt latencies, but they
+ * vary a bit, interrupt2 isn't exactly 2ms, it can vary up to 4ms since the
+ * other queues can "override" it. interrupt4 can vary up to 8ms, etc. Minor
+ * problem
+ *
+ * In the case of the root hub, these QH's are just head's of qh's. Don't
+ * be scared, it kinda makes sense. Look at this wonderful picture care of
+ * Linus:
+ *
+ * generic-iso-QH -> dev1-iso-QH -> generic-irq-QH -> dev1-irq-QH -> ...
+ * | | | |
+ * End dev1-iso-TD1 End dev1-irq-TD1
+ * |
+ * dev1-iso-TD2
+ * |
+ * ....
+ *
+ * This may vary a bit (the UHCI docs don't explicitly say you can put iso
+ * transfers in QH's and all of their pictures don't have that either) but
+ * other than that, that is what we're doing now
+ *
+ * To keep with Linus' nomenclature, this is called the qh skeleton. These
+ * labels (below) are only signficant to the root hub's qh's
+ */
+#define skel_iso_qh qh[0]
+
+#define skel_int2_qh qh[1]
+#define skel_int4_qh qh[2]
+#define skel_int8_qh qh[3]
+#define skel_int16_qh qh[4]
+#define skel_int32_qh qh[5]
+#define skel_int64_qh qh[6]
+#define skel_int128_qh qh[7]
+#define skel_int256_qh qh[8]
+
+#define skel_control_qh qh[9]
+
+#define skel_bulk0_qh qh[10]
+#define skel_bulk1_qh qh[11]
+#define skel_bulk2_qh qh[12]
+#define skel_bulk3_qh qh[13]
+
+/*
+ * These are significant to the devices allocation of QH's
+ */
+#if 0
+#define iso_qh qh[0]
+#define int_qh qh[1] /* We have 2 "common" interrupt QH's */
+#define control_qh qh[3]
+#define bulk_qh qh[4] /* We have 4 "common" bulk QH's */
+#define extra_qh qh[8] /* The rest, anything goes */
+#endif
+
+/*
+ * This describes the full uhci information.
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs.
+ */
+struct uhci {
+ int irq;
+ unsigned int io_addr;
+
+ struct usb_bus *bus;
+
+#if 0
+ /* These are "standard" QH's for the entire bus */
+ struct uhci_qh qh[UHCI_MAXQH];
+#endif
+ struct uhci_device *root_hub; /* Root hub device descriptor.. */
+
+ struct uhci_framelist *fl; /* Frame list */
+ struct list_head interrupt_list; /* List of interrupt-active TD's for this uhci */
+};
+
+/* needed for the debugging code */
+struct uhci_td *uhci_link_to_td(unsigned int element);
+
+/* Debugging code */
+void show_td(struct uhci_td * td);
+void show_status(struct uhci *uhci);
+void show_queues(struct uhci *uhci);
+
+#endif
+
--- /dev/null
+/*
+ * debug.c - USB debug helper routines.
+ *
+ * I just want these out of the way where they aren't in your
+ * face, but so that you can still use them..
+ */
+#include <linux/kernel.h>
+
+#include "usb.h"
+
+static void usb_show_endpoint(struct usb_endpoint_descriptor *endpoint)
+{
+ usb_show_endpoint_descriptor(endpoint);
+}
+
+static void usb_show_interface(struct usb_interface_descriptor *interface)
+{
+ int i;
+
+ usb_show_interface_descriptor(interface);
+ for (i = 0 ; i < interface->bNumEndpoints; i++)
+ usb_show_endpoint(interface->endpoint + i);
+}
+
+static void usb_show_config(struct usb_config_descriptor *config)
+{
+ int i;
+
+ usb_show_config_descriptor(config);
+ for (i = 0 ; i < config->bNumInterfaces; i++)
+ usb_show_interface(config->interface + i);
+}
+
+void usb_show_device(struct usb_device *dev)
+{
+ int i;
+
+ usb_show_device_descriptor(&dev->descriptor);
+ for (i = 0; i < dev->descriptor.bNumConfigurations; i++)
+ usb_show_config(dev->config + i);
+}
+
+
+/*
+ * Parse and show the different USB descriptors.
+ */
+void usb_show_device_descriptor(struct usb_device_descriptor *desc)
+{
+ printk(" USB version %x.%02x\n", desc->bcdUSB >> 8, desc->bcdUSB & 0xff);
+ printk(" Vendor: %04x\n", desc->idVendor);
+ printk(" Product: %04x\n", desc->idProduct);
+ printk(" Configurations: %d\n", desc->bNumConfigurations);
+
+ printk(" Device Class: %d\n", desc->bDeviceClass);
+ switch (desc->bDeviceClass) {
+ case 0:
+ printk(" Per-interface classes\n");
+ break;
+ case 9:
+ printk(" Hub device class\n");
+ break;
+ case 0xff:
+ printk(" Vendor class\n");
+ break;
+ default:
+ printk(" Unknown class\n");
+ }
+}
+
+void usb_show_config_descriptor(struct usb_config_descriptor * desc)
+{
+ printk("Configuration:\n");
+ printk(" bLength = %4d%s\n", desc->bLength,
+ desc->bLength == 9 ? "" : " (!!!)");
+ printk(" bDescriptorType = %02x\n", desc->bDescriptorType);
+ printk(" wTotalLength = %04x\n", desc->wTotalLength);
+ printk(" bNumInterfaces = %02x\n", desc->bNumInterfaces);
+ printk(" bConfigurationValue = %02x\n", desc->bConfigurationValue);
+ printk(" iConfiguration = %02x\n", desc->iConfiguration);
+ printk(" bmAttributes = %02x\n", desc->bmAttributes);
+ printk(" MaxPower = %4dmA\n", desc->MaxPower * 2);
+}
+
+void usb_show_interface_descriptor(struct usb_interface_descriptor * desc)
+{
+ printk(" Interface:\n");
+ printk(" bLength = %4d%s\n", desc->bLength,
+ desc->bLength == 9 ? "" : " (!!!)");
+ printk(" bDescriptorType = %02x\n", desc->bDescriptorType);
+ printk(" bInterfaceNumber = %02x\n", desc->bInterfaceNumber);
+ printk(" bAlternateSetting = %02x\n", desc->bAlternateSetting);
+ printk(" bNumEndpoints = %02x\n", desc->bNumEndpoints);
+ printk(" bInterfaceClass = %02x\n", desc->bInterfaceClass);
+ printk(" bInterfaceSubClass = %02x\n", desc->bInterfaceSubClass);
+ printk(" bInterfaceProtocol = %02x\n", desc->bInterfaceProtocol);
+ printk(" iInterface = %02x\n", desc->iInterface);
+}
+
+void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor * desc)
+{
+ char *EndpointType[4] = { "Control", "Isochronous", "Bulk", "Interrupt" };
+ printk(" Endpoint:\n");
+ printk(" bLength = %4d%s\n", desc->bLength,
+ desc->bLength == 7 ? "" : " (!!!)");
+ printk(" bDescriptorType = %02x\n", desc->bDescriptorType);
+ printk(" bEndpointAddress = %02x (%s)\n", desc->bEndpointAddress,
+ (desc->bEndpointAddress & 0x80) ? "in" : "out");
+ printk(" bmAttributes = %02x (%s)\n", desc->bmAttributes,
+ EndpointType[3 & desc->bmAttributes]);
+ printk(" wMaxPacketSize = %04x\n", desc->wMaxPacketSize);
+ printk(" bInterval = %02x\n", desc->bInterval);
+}
+
+void usb_show_hub_descriptor(struct usb_hub_descriptor * desc)
+{
+ int len = 7;
+ unsigned char *ptr = (unsigned char *) desc;
+
+ printk("Interface:");
+ while (len) {
+ printk(" %02x", *ptr);
+ ptr++; len--;
+ }
+ printk("\n");
+}
+
+
--- /dev/null
+/*
+ * driver/usb/usb.c
+ *
+ * (C) Copyright Linus Torvalds 1999
+ *
+ * NOTE! This is not actually a driver at all, rather this is
+ * just a collection of helper routines that implement the
+ * generic USB things that the real drivers can use..
+ *
+ * Think of this as a "USB library" rather than anything else.
+ * It should be considered a slave, with no callbacks. Callbacks
+ * are evil.
+ */
+
+/*
+ * Table 9-2
+ *
+ * Offset Field Size Value Desc
+ * 0 bmRequestType 1 Bitmap D7: Direction
+ * 0 = Host-to-device
+ * 1 = Device-to-host
+ * D6..5: Type
+ * 0 = Standard
+ * 1 = Class
+ * 2 = Vendor
+ * 3 = Reserved
+ * D4..0: Recipient
+ * 0 = Device
+ * 1 = Interface
+ * 2 = Endpoint
+ * 3 = Other
+ * 4..31 = Reserved
+ * 1 bRequest 1 Value Specific request (9-3)
+ * 2 wValue 2 Value Varies
+ * 4 wIndex 2 Index/Offset Varies
+ * 6 wLength 2 Count Bytes for data
+ */
+
+#include <linux/string.h>
+#include <linux/bitops.h>
+#include <linux/malloc.h>
+
+#include "usb.h"
+
+/*
+ * We have a per-interface "registered driver" list.
+ */
+static LIST_HEAD(usb_driver_list);
+
+int usb_register(struct usb_driver *new_driver)
+{
+ /* Add it to the list of known drivers */
+ list_add(&new_driver->driver_list, &usb_driver_list);
+
+ /*
+ * We should go through all existing devices, and see if any of
+ * them would be acceptable to the new driver.. Let's do that
+ * in version 2.0.
+ */
+ return 0;
+}
+
+void usb_deregister(struct usb_driver *driver)
+{
+ list_del(&driver->driver_list);
+}
+
+/*
+ * This entrypoint gets called for each new device.
+ *
+ * We now walk the list of registered USB drivers,
+ * looking for one that will accept this device as
+ * his..
+ */
+void usb_device_descriptor(struct usb_device *dev)
+{
+ struct list_head *tmp = usb_driver_list.next;
+
+ while (tmp != &usb_driver_list) {
+ struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list);
+ tmp = tmp->next;
+ if (driver->probe(dev))
+ continue;
+ dev->driver = driver;
+ return;
+ }
+
+ /*
+ * Ok, no driver accepted the device, so show the info
+ * for debugging..
+ */
+ printk("Unknown new USB device:\n");
+ usb_show_device(dev);
+}
+
+/*
+ * Parse the fairly incomprehensible output of
+ * the USB configuration data, and build up the
+ * USB device database.
+ */
+static int usb_expect_descriptor(unsigned char *ptr, int len, unsigned char desctype, unsigned char descindex)
+{
+ int parsed = 0;
+
+ for (;;) {
+ unsigned short n_desc;
+ int n_len, i;
+
+ if (len < descindex)
+ return -1;
+ n_desc = *(unsigned short *)ptr;
+ if (n_desc == ((desctype << 8) + descindex))
+ return parsed;
+ printk(
+ "Expected descriptor %02X/%02X, got %02X/%02X - skipping\n",
+ desctype, descindex,
+ (n_desc >> 8) & 0xFF, n_desc & 0xFF);
+ n_len = n_desc & 0xff;
+ if (n_len < 2 || n_len > len)
+ return -1;
+ for (i = 0 ; i < n_len; i++)
+ printk(" %d %02x\n", i, ptr[i]);
+ len -= n_len;
+ ptr += n_len;
+ parsed += n_len;
+ }
+}
+
+static int usb_parse_endpoint(struct usb_endpoint_descriptor *endpoint, unsigned char *ptr, int len)
+{
+ int parsed = usb_expect_descriptor(ptr, len, USB_DT_ENDPOINT, 7);
+
+ if (parsed < 0)
+ return parsed;
+
+ memcpy(endpoint, ptr + parsed, 7);
+ return parsed + 7;
+}
+
+static int usb_parse_interface(struct usb_interface_descriptor *interface, unsigned char *ptr, int len)
+{
+ int i;
+ int parsed = usb_expect_descriptor(ptr, len, USB_DT_INTERFACE, 9);
+ int retval;
+
+ if (parsed < 0)
+ return parsed;
+
+ memcpy(interface, ptr + parsed, 9);
+ len -= 9;
+ parsed += 9;
+
+ if (interface->bNumEndpoints > USB_MAXENDPOINTS)
+ return -1;
+
+ for (i = 0; i < interface->bNumEndpoints; i++) {
+ if(((USB_DT_HID << 8) | 9) == *(unsigned short*)(ptr + parsed)) {
+ parsed += 9; /* skip over the HID descriptor for now */
+ len -= 9;
+ }
+ retval = usb_parse_endpoint(interface->endpoint + i, ptr + parsed, len);
+ if (retval < 0) return retval;
+ parsed += retval;
+ len -= retval;
+ }
+ return parsed;
+}
+
+static int usb_parse_config(struct usb_config_descriptor *config, unsigned char *ptr, int len)
+{
+ int i;
+ int parsed = usb_expect_descriptor(ptr, len, USB_DT_CONFIG, 9);
+
+ if (parsed < 0)
+ return parsed;
+
+ memcpy(config, ptr + parsed, 9);
+ len -= 9;
+ parsed += 9;
+
+ if (config->bNumInterfaces > USB_MAXINTERFACES)
+ return -1;
+
+ for (i = 0; i < config->bNumInterfaces; i++) {
+ int retval = usb_parse_interface(config->interface + i, ptr + parsed, len);
+ if (retval < 0)
+ return retval;
+ parsed += retval;
+ len -= retval;
+ }
+ return parsed;
+}
+
+int usb_parse_configuration(struct usb_device *dev, void *__buf, int bytes)
+{
+ int i;
+ unsigned char *ptr = __buf;
+
+ if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG)
+ return -1;
+
+ for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
+ int retval = usb_parse_config(dev->config + i, ptr, bytes);
+ if (retval < 0)
+ return retval;
+ ptr += retval;
+ bytes += retval;
+ }
+ return 0;
+}
+
+void usb_init_root_hub(struct usb_device *dev)
+{
+ dev->devnum = -1;
+ dev->slow = 0;
+}
+
+/*
+ * Something got disconnected. Get rid of it, and all of its children.
+ */
+void usb_disconnect(struct usb_device **pdev)
+{
+ struct usb_device * dev = *pdev;
+
+ if (dev) {
+ int i;
+
+ *pdev = NULL;
+
+ printk("USB disconnect on device %d\n", dev->devnum);
+
+ if(dev->driver) dev->driver->disconnect(dev);
+
+ /* Free up all the children.. */
+ for (i = 0; i < USB_MAXCHILDREN; i++) {
+ struct usb_device **child = dev->children + i;
+ usb_disconnect(child);
+ }
+
+ /* Free up the device itself, including its device number */
+ if (dev->devnum > 0)
+ clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+ dev->bus->op->deallocate(dev);
+ }
+}
+
+
+/*
+ * Connect a new USB device. This basically just initializes
+ * the USB device information and sets up the topology - it's
+ * up to the low-level driver to reset the port and actually
+ * do the setup (the upper levels don't know how to do that).
+ */
+void usb_connect(struct usb_device *dev)
+{
+ int devnum;
+
+ dev->descriptor.bMaxPacketSize0 = 8; /* XXX fixed 8 bytes for now */
+
+ devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1);
+ if (devnum < 128) {
+ set_bit(devnum, dev->bus->devmap.devicemap);
+ dev->devnum = devnum;
+ }
+}
+
+/*
+ * These are the actual routines to send
+ * and receive control messages.
+ */
+int usb_set_address(struct usb_device *dev)
+{
+ devrequest dr;
+
+ dr.requesttype = 0;
+ dr.request = USB_REQ_SET_ADDRESS;
+ dr.value = dev->devnum;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_snddefctrl(dev), &dr, NULL, 0);
+}
+
+int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size)
+{
+ devrequest dr;
+
+ dr.requesttype = 0x80;
+ dr.request = USB_REQ_GET_DESCRIPTOR;
+ dr.value = (type << 8) + index;
+ dr.index = 0;
+ dr.length = size;
+
+ return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size);
+}
+
+int usb_get_device_descriptor(struct usb_device *dev)
+{
+ return usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, sizeof(dev->descriptor));
+}
+
+int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size)
+{
+ devrequest dr;
+
+ dr.requesttype = USB_RT_HUB | 0x80;
+ dr.request = USB_REQ_GET_DESCRIPTOR;
+ dr.value = (USB_DT_HUB << 8);
+ dr.index = 0;
+ dr.length = size;
+
+ return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, size);
+}
+
+int usb_clear_port_feature(struct usb_device *dev, int port, int feature)
+{
+ devrequest dr;
+
+ dr.requesttype = USB_RT_PORT;
+ dr.request = USB_REQ_CLEAR_FEATURE;
+ dr.value = feature;
+ dr.index = port;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_set_port_feature(struct usb_device *dev, int port, int feature)
+{
+ devrequest dr;
+
+ dr.requesttype = USB_RT_PORT;
+ dr.request = USB_REQ_SET_FEATURE;
+ dr.value = feature;
+ dr.index = port;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_get_hub_status(struct usb_device *dev, void *data)
+{
+ devrequest dr;
+
+ dr.requesttype = USB_RT_HUB | 0x80;
+ dr.request = USB_REQ_GET_STATUS;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 4;
+
+ return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, 4);
+}
+
+int usb_get_port_status(struct usb_device *dev, int port, void *data)
+{
+ devrequest dr;
+
+ dr.requesttype = USB_RT_PORT | 0x80;
+ dr.request = USB_REQ_GET_STATUS;
+ dr.value = 0;
+ dr.index = port;
+ dr.length = 4;
+
+ return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, 4);
+}
+
+int usb_get_protocol(struct usb_device *dev)
+{
+ unsigned char buf[8];
+ devrequest dr;
+
+ dr.requesttype = USB_RT_HIDD | 0x80;
+ dr.request = USB_REQ_GET_PROTOCOL;
+ dr.value = 0;
+ dr.index = 1;
+ dr.length = 1;
+
+ if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, 1))
+ return -1;
+
+ return buf[0];
+}
+
+int usb_set_protocol(struct usb_device *dev, int protocol)
+{
+ devrequest dr;
+
+ dr.requesttype = USB_RT_HIDD;
+ dr.request = USB_REQ_SET_PROTOCOL;
+ dr.value = protocol;
+ dr.index = 1;
+ dr.length = 0;
+
+ if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
+ return -1;
+
+ return 0;
+}
+
+/* keyboards want a nonzero duration according to HID spec, but
+ mice should use infinity (0) -keryan */
+int usb_set_idle(struct usb_device *dev, int duration, int report_id)
+{
+ devrequest dr;
+
+ dr.requesttype = USB_RT_HIDD;
+ dr.request = USB_REQ_SET_IDLE;
+ dr.value = (duration << 8) | report_id;
+ dr.index = 1;
+ dr.length = 0;
+
+ if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
+ return -1;
+
+ return 0;
+}
+
+int usb_set_configuration(struct usb_device *dev, int configuration)
+{
+ devrequest dr;
+
+ dr.requesttype = 0;
+ dr.request = USB_REQ_SET_CONFIGURATION;
+ dr.value = configuration;
+ dr.index = 0;
+ dr.length = 0;
+
+ if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
+ return -1;
+
+ return 0;
+}
+
+int usb_get_report(struct usb_device *dev)
+{
+ unsigned char buf[8];
+ devrequest dr;
+
+ dr.requesttype = USB_RT_HIDD | 0x80;
+ dr.request = USB_REQ_GET_REPORT;
+ dr.value = 0x100;
+ dr.index = 1;
+ dr.length = 3;
+
+ if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, 3))
+ return -1;
+
+ return buf[0];
+}
+
+int usb_get_configuration(struct usb_device *dev)
+{
+ unsigned int size;
+ unsigned char buffer[128];
+
+ /* Get the first 8 bytes - guaranteed */
+ if (usb_get_descriptor(dev, USB_DT_CONFIG, 0, buffer, 8))
+ return -1;
+
+ /* Get the full buffer */
+ size = *(unsigned short *)(buffer+2);
+ if (size > sizeof(buffer))
+ size = sizeof(buffer);
+
+ if (usb_get_descriptor(dev, USB_DT_CONFIG, 0, buffer, size))
+ return -1;
+
+ return usb_parse_configuration(dev, buffer, size);
+}
+
+/*
+ * By the time we get here, the device has gotten a new device ID
+ * and is in the default state. We need to identify the thing and
+ * get the ball rolling..
+ */
+void usb_new_device(struct usb_device *dev)
+{
+ int addr, i;
+
+ printk("USB new device connect, assigned device number %d\n",
+ dev->devnum);
+
+ dev->maxpacketsize = 0; /* Default to 8 byte max packet size */
+
+ addr = dev->devnum;
+ dev->devnum = 0;
+
+ /* Slow devices */
+ for (i = 0; i < 5; i++) {
+ if (!usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8))
+ break;
+
+ printk("get_descriptor failed, waiting\n");
+ wait_ms(200);
+ }
+ if (i == 5) {
+ printk("giving up\n");
+ return;
+ }
+
+#if 0
+ printk("maxpacketsize: %d\n", dev->descriptor.bMaxPacketSize0);
+#endif
+ switch (dev->descriptor.bMaxPacketSize0) {
+ case 8: dev->maxpacketsize = 0; break;
+ case 16: dev->maxpacketsize = 1; break;
+ case 32: dev->maxpacketsize = 2; break;
+ case 64: dev->maxpacketsize = 3; break;
+ }
+#if 0
+ printk("dev->mps: %d\n", dev->maxpacketsize);
+#endif
+
+ dev->devnum = addr;
+
+ if (usb_set_address(dev)) {
+ printk("Unable to set address\n");
+ /* FIXME: We should disable the port */
+ return;
+ }
+
+ wait_ms(10); /* Let the SET_ADDRESS settle */
+
+ if (usb_get_device_descriptor(dev)) {
+ printk("Unable to get device descriptor\n");
+ return;
+ }
+
+ if (usb_get_configuration(dev)) {
+ printk("Unable to get configuration\n");
+ return;
+ }
+
+#if 0
+ printk("Vendor: %X\n", dev->descriptor.idVendor);
+ printk("Product: %X\n", dev->descriptor.idProduct);
+#endif
+
+ usb_device_descriptor(dev);
+}
+
+int usb_request_irq(struct usb_device *dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id)
+{
+ return dev->bus->op->request_irq(dev, pipe, handler, period, dev_id);
+}
+
--- /dev/null
+#ifndef __LINUX_USB_H
+#define __LINUX_USB_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+
+static __inline__ void wait_ms(unsigned int ms)
+{
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(1 + ms / 10);
+}
+
+
+typedef struct {
+ unsigned char requesttype;
+ unsigned char request;
+ unsigned short value;
+ unsigned short index;
+ unsigned short length;
+} devrequest;
+
+/*
+ * Class codes
+ */
+#define USB_CLASS_HUB 9
+
+/*
+ * Descriptor types
+ */
+#define USB_DT_DEVICE 0x01
+#define USB_DT_CONFIG 0x02
+#define USB_DT_STRING 0x03
+#define USB_DT_INTERFACE 0x04
+#define USB_DT_ENDPOINT 0x05
+
+#define USB_DT_HUB 0x29
+#define USB_DT_HID 0x21
+
+/*
+ * Standard requests
+ */
+#define USB_REQ_GET_STATUS 0x00
+#define USB_REQ_CLEAR_FEATURE 0x01
+/* 0x02 is reserved */
+#define USB_REQ_SET_FEATURE 0x03
+/* 0x04 is reserved */
+#define USB_REQ_SET_ADDRESS 0x05
+#define USB_REQ_GET_DESCRIPTOR 0x06
+#define USB_REQ_SET_DESCRIPTOR 0x07
+#define USB_REQ_GET_CONFIGURATION 0x08
+#define USB_REQ_SET_CONFIGURATION 0x09
+#define USB_REQ_GET_INTERFACE 0x0A
+#define USB_REQ_SET_INTERFACE 0x0B
+#define USB_REQ_SYNCH_FRAME 0x0C
+
+/*
+ * HIDD requests
+ */
+#define USB_REQ_GET_REPORT 0x01
+#define USB_REQ_GET_IDLE 0x02
+#define USB_REQ_GET_PROTOCOL 0x03
+#define USB_REQ_SET_REPORT 0x09
+#define USB_REQ_SET_IDLE 0x0A
+#define USB_REQ_SET_PROTOCOL 0x0B
+
+#define USB_TYPE_STANDARD (0x00 << 5)
+#define USB_TYPE_CLASS (0x01 << 5)
+#define USB_TYPE_VENDOR (0x02 << 5)
+#define USB_TYPE_RESERVED (0x03 << 5)
+
+#define USB_RECIP_DEVICE 0x00
+#define USB_RECIP_INTERFACE 0x01
+#define USB_RECIP_ENDPOINT 0x02
+#define USB_RECIP_OTHER 0x03
+
+/*
+ * Request target types.
+ */
+#define USB_RT_DEVICE 0x00
+#define USB_RT_INTERFACE 0x01
+#define USB_RT_ENDPOINT 0x02
+
+#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE)
+#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER)
+
+#define USB_RT_HIDD (USB_TYPE_CLASS | USB_RECIP_INTERFACE)
+
+/*
+ * USB device number allocation bitmap. There's one bitmap
+ * per USB tree.
+ */
+struct usb_devmap {
+ unsigned long devicemap[128 / (8*sizeof(unsigned long))];
+};
+
+/*
+ * This is a USB device descriptor.
+ *
+ * USB device information
+ *
+ * Make this MUCH dynamic, right now
+ * it contains enough information for
+ * a USB floppy controller, and nothing
+ * else.
+ *
+ * I'm not proud. I just want this dang
+ * thing to start working.
+ */
+#define USB_MAXCONFIG 1
+#define USB_MAXINTERFACES 3
+#define USB_MAXENDPOINTS 3
+
+struct usb_device_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u16 bcdUSB;
+ __u8 bDeviceClass;
+ __u8 bDeviceSubClass;
+ __u8 bDeviceProtocol;
+ __u8 bMaxPacketSize0;
+ __u16 idVendor;
+ __u16 idProduct;
+ __u16 bcdDevice;
+ __u8 iManufacturer;
+ __u8 iProduct;
+ __u8 iSerialNumber;
+ __u8 bNumConfigurations;
+};
+
+/* Endpoint descriptor */
+struct usb_endpoint_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bEndpointAddress;
+ __u8 bmAttributes;
+ __u16 wMaxPacketSize;
+ __u8 bInterval;
+};
+
+/* Interface descriptor */
+struct usb_interface_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bInterfaceNumber;
+ __u8 bAlternateSetting;
+ __u8 bNumEndpoints;
+ __u8 bInterfaceClass;
+ __u8 bInterfaceSubClass;
+ __u8 bInterfaceProtocol;
+ __u8 iInterface;
+
+ struct usb_endpoint_descriptor endpoint[USB_MAXENDPOINTS];
+};
+
+/* Configuration descriptor information.. */
+struct usb_config_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u16 wTotalLength;
+ __u8 bNumInterfaces;
+ __u8 bConfigurationValue;
+ __u8 iConfiguration;
+ __u8 bmAttributes;
+ __u8 MaxPower;
+
+ struct usb_interface_descriptor interface[USB_MAXINTERFACES];
+};
+
+/* String descriptor */
+struct usb_string_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+};
+
+/* Hub descriptor */
+struct usb_hub_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bNbrPorts;
+ __u16 wHubCharacteristics;
+ __u8 bPwrOn2PwrGood;
+ __u8 bHubContrCurrent;
+ /* DeviceRemovable and PortPwrCtrlMask want to be variable-length
+ bitmaps that hold max 256 entries, but for now they're ignored */
+ __u8 filler;
+};
+
+struct usb_device;
+
+struct usb_driver {
+ const char * name;
+ int (*probe)(struct usb_device *);
+ void (*disconnect)(struct usb_device *);
+ struct list_head driver_list;
+};
+
+/*
+ * Pointer to a device endpoint interrupt function -greg
+ */
+typedef int (*usb_device_irq)(int, void *, void *);
+
+struct usb_operations {
+ struct usb_device *(*allocate)(struct usb_device *);
+ int (*deallocate)(struct usb_device *);
+ int (*control_msg)(struct usb_device *, unsigned int, void *, void *, int);
+ int (*request_irq)(struct usb_device *, unsigned int, usb_device_irq, int, void *);
+};
+
+/*
+ * Allocated per bus we have
+ */
+struct usb_bus {
+ struct usb_devmap devmap; /* Device map */
+ struct usb_operations *op; /* Operations (specific to the HC) */
+ struct usb_device *root_hub; /* Root hub */
+ void *hcpriv; /* Host Controller private data */
+};
+
+
+#define USB_MAXCHILDREN (8)
+
+struct usb_device {
+ int devnum; /* Device number on USB bus */
+ int slow; /* Slow device? */
+ int maxpacketsize; /* Maximum packet size */
+
+ struct usb_bus *bus; /* Bus we're apart of */
+ struct usb_driver *driver; /* Driver */
+ struct usb_device_descriptor descriptor; /* Descriptor */
+ struct usb_config_descriptor config[USB_MAXCONFIG]; /* All of the configs */
+ struct usb_device *parent;
+
+ /*
+ * Child devices - these can be either new devices
+ * (if this is a hub device), or different instances
+ * of this same device.
+ *
+ * Each instance needs its own set of data structuctures.
+ */
+
+ int maxchild; /* Number of ports if hub */
+ struct usb_device *children[USB_MAXCHILDREN];
+
+ void *hcpriv; /* Host Controller private data */
+ void *private; /* Upper layer private data */
+};
+
+extern int usb_register(struct usb_driver *);
+extern void usb_deregister(struct usb_driver *);
+
+extern int usb_request_irq(struct usb_device *, unsigned int, usb_device_irq, int, void *);
+
+extern void usb_init_root_hub(struct usb_device *dev);
+extern void usb_connect(struct usb_device *dev);
+extern void usb_disconnect(struct usb_device **);
+extern void usb_device_descriptor(struct usb_device *dev);
+
+extern int usb_parse_configuration(struct usb_device *dev, void *buf, int len);
+
+/*
+ * Calling this entity a "pipe" is glorifying it. A USB pipe
+ * is something embarrassingly simple: it basically consists
+ * of the following information:
+ * - device number (7 bits)
+ * - endpoint number (4 bits)
+ * - current Data0/1 state (1 bit)
+ * - direction (1 bit)
+ * - speed (1 bit)
+ * - max packet size (2 bits: 8, 16, 32 or 64)
+ * - pipe type (2 bits: control, interrupt, bulk, isochronous)
+ *
+ * That's 18 bits. Really. Nothing more. And the USB people have
+ * documented these eighteen bits as some kind of glorious
+ * virtual data structure.
+ *
+ * Let's not fall in that trap. We'll just encode it as a simple
+ * unsigned int. The encoding is:
+ *
+ * - device: bits 8-14
+ * - endpoint: bits 15-18
+ * - Data0/1: bit 19
+ * - direction: bit 7 (0 = Host-to-Device, 1 = Device-to-Host)
+ * - speed: bit 26 (0 = High, 1 = Low Speed)
+ * - max size: bits 0-1 (00 = 8, 01 = 16, 10 = 32, 11 = 64)
+ * - pipe type: bits 30-31 (00 = isochronous, 01 = interrupt, 10 = control, 11 = bulk)
+ *
+ * Why? Because it's arbitrary, and whatever encoding we select is really
+ * up to us. This one happens to share a lot of bit positions with the UCHI
+ * specification, so that much of the uhci driver can just mask the bits
+ * appropriately.
+ */
+
+#define usb_maxpacket(pipe) (8 << ((pipe) & 3))
+#define usb_packetid(pipe) (((pipe) & 0x80) ? 0x69 : 0xE1)
+
+#define usb_pipedevice(pipe) (((pipe) >> 8) & 0x7f)
+#define usb_pipeendpoint(pipe) (((pipe) >> 15) & 0xf)
+#define usb_pipedata(pipe) (((pipe) >> 19) & 1)
+#define usb_pipeout(pipe) (((pipe) & 0x80) == 0)
+#define usb_pipeslow(pipe) (((pipe) >> 26) & 1)
+
+#define usb_pipetype(pipe) (((pipe) >> 30) & 3)
+#define usb_pipeisoc(pipe) (usb_pipetype((pipe)) == 0)
+#define usb_pipeint(pipe) (usb_pipetype((pipe)) == 1)
+#define usb_pipecontrol(pipe) (usb_pipetype((pipe)) == 2)
+#define usb_pipebulk(pipe) (usb_pipetype((pipe)) == 3)
+
+#define usb_pipe_endpdev(pipe) (((pipe) >> 8) & 0x7ff)
+
+static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint)
+{
+ return (dev->devnum << 8) | (endpoint << 15) | (dev->slow << 26) | dev->maxpacketsize;
+}
+
+static inline unsigned int __default_pipe(struct usb_device *dev)
+{
+ return (dev->slow << 26);
+}
+
+/* Create control pipes.. */
+#define usb_sndctrlpipe(dev,endpoint) ((2 << 30) | __create_pipe(dev,endpoint))
+#define usb_rcvctrlpipe(dev,endpoint) ((2 << 30) | __create_pipe(dev,endpoint) | 0x80)
+#define usb_snddefctrl(dev) ((2 << 30) | __default_pipe(dev))
+#define usb_rcvdefctrl(dev) ((2 << 30) | __default_pipe(dev) | 0x80)
+
+/* Create .. */
+
+/*
+ * Send and receive control messages..
+ */
+void usb_new_device(struct usb_device *dev);
+int usb_set_address(struct usb_device *dev);
+int usb_get_descriptor(struct usb_device *dev, unsigned char desctype, unsigned
+char descindex, void *buf, int size);
+int usb_get_device_descriptor(struct usb_device *dev);
+int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size);
+int usb_clear_port_feature(struct usb_device *dev, int port, int feature);
+int usb_set_port_feature(struct usb_device *dev, int port, int feature);
+int usb_get_hub_status(struct usb_device *dev, void *data);
+int usb_get_port_status(struct usb_device *dev, int port, void *data);
+int usb_get_protocol(struct usb_device *dev);
+int usb_set_protocol(struct usb_device *dev, int protocol);
+int usb_set_idle(struct usb_device *dev, int duration, int report_id);
+int usb_set_configuration(struct usb_device *dev, int configuration);
+int usb_get_report(struct usb_device *dev);
+
+/*
+ * Debugging helpers..
+ */
+void usb_show_device_descriptor(struct usb_device_descriptor *);
+void usb_show_config_descriptor(struct usb_config_descriptor *);
+void usb_show_interface_descriptor(struct usb_interface_descriptor *);
+void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *);
+void usb_show_hub_descriptor(struct usb_hub_descriptor *);
+void usb_show_device(struct usb_device *);
+
+#endif
+
unsigned long parent_object_id, dir_object_id;
int buffers, pos;
- if (!inode || !S_ISDIR(inode->i_mode))
- return -EBADF;
sb = inode->i_sb;
if (filp->f_pos > ADFS_NUM_DIR_ENTRIES + 2)
return 0;
}
-int adfs_lookup (struct inode *dir, struct dentry *dentry)
+struct dentry *adfs_lookup (struct inode *dir, struct dentry *dentry)
{
struct inode *inode = NULL;
struct adfs_idir_entry de;
unsigned long ino;
if (dentry->d_name.len > ADFS_NAME_LEN)
- return -ENAMETOOLONG;
+ return ERR_PTR(-ENAMETOOLONG);
if (adfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de)) {
ino = de.inode_no;
inode = iget (dir->i_sb, ino);
if (!inode)
- return -EACCES;
+ return ERR_PTR(-EACCES);
}
d_add(dentry, inode);
- return 0;
+ return NULL;
}
pr_debug("AFFS: readdir(ino=%lu,f_pos=%lu)\n",inode->i_ino,(unsigned long)filp->f_pos);
- if (!inode || !S_ISDIR(inode->i_mode))
- return -EBADF;
-
stored = 0;
dir_bh = NULL;
fh_bh = NULL;
return bh;
}
-int
+struct dentry *
affs_lookup(struct inode *dir, struct dentry *dentry)
{
unsigned long ino;
affs_brelse(bh);
inode = iget(dir->i_sb,ino);
if (!inode)
- return -EACCES;
+ return ERR_PTR(-EACCES);
}
dentry->d_op = &affs_dentry_operations;
d_add(dentry,inode);
- return 0;
+ return NULL;
}
int
/*
* No entries except for "." and "..", both of which are handled by the VFS layer
*/
-static int autofs_dir_lookup(struct inode *dir, struct dentry * dentry)
+static struct dentry *autofs_dir_lookup(struct inode *dir,struct dentry *dentry)
{
d_add(dentry, NULL);
- return 0;
+ return NULL;
}
static struct file_operations autofs_dir_operations = {
#include "autofs_i.h"
static int autofs_root_readdir(struct file *,void *,filldir_t);
-static int autofs_root_lookup(struct inode *,struct dentry *);
+static struct dentry *autofs_root_lookup(struct inode *,struct dentry *);
static int autofs_root_symlink(struct inode *,struct dentry *,const char *);
static int autofs_root_unlink(struct inode *,struct dentry *);
static int autofs_root_rmdir(struct inode *,struct dentry *);
NULL, /* d_compare */
};
-static int autofs_root_lookup(struct inode *dir, struct dentry *dentry)
+static struct dentry *autofs_root_lookup(struct inode *dir, struct dentry *dentry)
{
struct autofs_sb_info *sbi;
int oz_mode;
DPRINTK(("autofs_root_lookup: name = "));
autofs_say(dentry->d_name.name,dentry->d_name.len);
- if (!S_ISDIR(dir->i_mode))
- return -ENOTDIR;
-
if (dentry->d_name.len > NAME_MAX)
- return -ENOENT; /* File name too long to exist */
+ return ERR_PTR(-ENOENT);/* File name too long to exist */
sbi = autofs_sbi(dir->i_sb);
*/
if (dentry->d_flags & DCACHE_AUTOFS_PENDING) {
if (signal_pending(current))
- return -ERESTARTNOINTR;
+ return ERR_PTR(-ERESTARTNOINTR);
}
/*
* be OK for the operations we permit from an autofs.
*/
if ( dentry->d_inode && list_empty(&dentry->d_hash) )
- return -ENOENT;
+ return ERR_PTR(-ENOENT);
- return 0;
+ return NULL;
}
static int autofs_root_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
/* If dirty, mark the time this buffer should be written back. */
set_writetime(buf, 0);
refile_buffer(buf);
+ touch_buffer(buf);
if (buf->b_count) {
buf->b_count--;
struct buffer_head * bh;
bh = getblk(dev, block, size);
- touch_buffer(bh);
if (buffer_uptodate(bh))
return bh;
ll_rw_block(READ, 1, &bh);
bh = getblk(dev, block, bufsize);
index = BUFSIZE_INDEX(bh->b_size);
- touch_buffer(bh);
if (buffer_uptodate(bh))
return(bh);
else ll_rw_block(READ, 1, &bh);
/* dir inode-ops */
static int coda_create(struct inode *dir, struct dentry *new, int mode);
static int coda_mknod(struct inode *dir, struct dentry *new, int mode, int rdev);
-static int coda_lookup(struct inode *dir, struct dentry *target);
+static struct dentry *coda_lookup(struct inode *dir, struct dentry *target);
static int coda_link(struct dentry *old_dentry, struct inode *dir_inode,
struct dentry *entry);
static int coda_unlink(struct inode *dir_inode, struct dentry *entry);
/* inode operations for directories */
/* acces routines: lookup, readlink, permission */
-static int coda_lookup(struct inode *dir, struct dentry *entry)
+static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry)
{
struct coda_inode_info *dircnp;
struct inode *res_inode = NULL;
if ( length > CODA_MAXNAMLEN ) {
printk("name too long: lookup, %s (%*s)\n",
coda_f2s(&dircnp->c_fid), length, name);
- return -ENAMETOOLONG;
+ return ERR_PTR(-ENAMETOOLONG);
}
-
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk("coda_lookup: inode is NULL or not a directory\n");
- return -ENOTDIR;
- }
-
-
CDEBUG(D_INODE, "name %s, len %d in ino %ld, fid %s\n",
name, length, dir->i_ino, coda_f2s(&dircnp->c_fid));
}
error = coda_cnode_make(&res_inode, &resfid, dir->i_sb);
if (error)
- return error;
+ return ERR_PTR(error);
} else if (error != -ENOENT) {
CDEBUG(D_INODE, "error for %s(%*s)%d\n",
coda_f2s(&dircnp->c_fid), length, name, error);
- return error;
+ return ERR_PTR(error);
}
CDEBUG(D_INODE, "lookup: %s is (%s), type %d result %d, dropme %d\n",
name, coda_f2s(&resfid), type, error, dropme);
ITOC(res_inode)->c_flags |= C_VATTR;
}
EXIT;
- return 0;
+ return NULL;
}
CDEBUG(D_INODE, "name: %s, length %d, mode %o\n",name, length, mode);
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk("coda_create: inode is null or not a directory\n");
- return -ENOENT;
- }
-
if (coda_isroot(dir) && coda_iscontrol(name, length))
return -EPERM;
CDEBUG(D_INODE, "name: %s, length %d, mode %o, rdev %x\n",name, length, mode, rdev);
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk("coda_mknod: inode is null or not a directory\n");
- return -ENOENT;
- }
-
if (coda_isroot(dir) && coda_iscontrol(name, length))
return -EPERM;
ENTRY;
coda_vfs_stat.mkdir++;
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk("coda_mkdir: inode is NULL or not a directory\n");
- return -ENOENT;
- }
-
if (coda_isroot(dir) && coda_iscontrol(name, len))
return -EPERM;
ENTRY;
coda_vfs_stat.rmdir++;
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk("coda_rmdir: inode is NULL or not a directory\n");
- return -ENOENT;
- }
dircnp = ITOC(dir);
if (!list_empty(&de->d_hash))
ENTRY;
coda_vfs_stat.readdir++;
- if (!inode || !inode->i_sb || !S_ISDIR(inode->i_mode)) {
- printk("coda_readdir: inode is NULL or not a directory\n");
- return -EBADF;
- }
-
cnp = ITOC(inode);
if ( !cnp->c_ovp ) {
CDEBUG(D_FILE, "open inode pointer = NULL.\n");
*/
void shrink_dcache_memory(int priority, unsigned int gfp_mask)
{
- if (gfp_mask & __GFP_IO)
- prune_dcache(0);
+ if (gfp_mask & __GFP_IO) {
+ int count = 0;
+ if (priority)
+ count = dentry_stat.nr_unused / priority;
+ prune_dcache(count);
+ }
}
#define NAME_ALLOC_LEN(len) ((len+16) & ~15)
#include "devpts_i.h"
static int devpts_root_readdir(struct file *,void *,filldir_t);
-static int devpts_root_lookup(struct inode *,struct dentry *);
+static struct dentry *devpts_root_lookup(struct inode *,struct dentry *);
static int devpts_revalidate(struct dentry *);
static struct file_operations devpts_root_operations = {
off_t nr;
char numbuf[16];
- if (!inode || !S_ISDIR(inode->i_mode))
- return -ENOTDIR;
-
nr = filp->f_pos;
switch(nr)
return ( sbi->inodes[dentry->d_inode->i_ino - 2] == dentry->d_inode );
}
-static int devpts_root_lookup(struct inode * dir, struct dentry * dentry)
+static struct dentry *devpts_root_lookup(struct inode * dir, struct dentry * dentry)
{
struct devpts_sb_info *sbi = SBI(dir->i_sb);
unsigned int entry;
int i;
const char *p;
- if (!S_ISDIR(dir->i_mode))
- return -ENOTDIR;
-
dentry->d_inode = NULL; /* Assume failure */
dentry->d_op = &devpts_dentry_operations;
if ( dentry->d_name.len == 1 && dentry->d_name.name[0] == '0' ) {
entry = 0;
} else if ( dentry->d_name.len < 1 ) {
- return 0;
+ return NULL;
} else {
p = dentry->d_name.name;
if ( *p < '1' || *p > '9' )
- return 0;
+ return NULL;
entry = *p++ - '0';
for ( i = dentry->d_name.len-1 ; i ; i-- ) {
unsigned int nentry = *p++ - '0';
if ( nentry > 9 )
- return 0;
+ return NULL;
nentry += entry * 10;
if (nentry < entry)
- return 0;
+ return NULL;
entry = nentry;
}
}
if ( entry >= sbi->max_ptys )
- return 0;
+ return NULL;
dentry->d_inode = sbi->inodes[entry];
if ( dentry->d_inode )
d_add(dentry, dentry->d_inode);
- return 0;
+ return NULL;
}
int err;
struct inode *inode = filp->f_dentry->d_inode;
- if (!inode || !S_ISDIR(inode->i_mode))
- return -EBADF;
sb = inode->i_sb;
stored = 0;
return NULL;
}
-int ext2_lookup(struct inode * dir, struct dentry *dentry)
+struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry)
{
struct inode * inode;
struct ext2_dir_entry_2 * de;
struct buffer_head * bh;
if (dentry->d_name.len > EXT2_NAME_LEN)
- return -ENAMETOOLONG;
+ return ERR_PTR(-ENAMETOOLONG);
bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de);
inode = NULL;
inode = iget(dir->i_sb, ino);
if (!inode)
- return -EACCES;
+ return ERR_PTR(-EACCES);
}
d_add(dentry, inode);
- return 0;
+ return NULL;
}
/*
*
* VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu>
* Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk>
+ * Plugged buffer overrun in readdir(). AV
*/
#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
* characters are a sort of uuencoded 16 bit Unicode value. This lets
* us do a full dump and restore of Unicode filenames. We could get
* into some trouble with long Unicode names, but ignore that right now.
+ * Ahem... Stack smashing in ring 0 isn't fun. Fixed.
*/
static int
uni16_to_x8(unsigned char *ascii, unsigned char *uni, int uni_xlate,
*op++ = '?';
}
}
+ /* We have some slack there, so it's OK */
+ if (op>ascii+256) {
+ op = ascii + 256;
+ break;
+ }
}
*op = 0;
return (op - ascii);
unsigned char *unicode = NULL;
struct nls_table *nls = MSDOS_SB(sb)->nls_io;
- if (!inode || !S_ISDIR(inode->i_mode))
- return -EBADF;
/* Fake . and .. for the root directory. */
if (inode->i_ino == MSDOS_ROOT_INO) {
while (oldpos < 2) {
id = ds->id;
if (id & 0x40) {
slots = id & ~0x40;
- long_slots = slots;
- is_long = 1;
- alias_checksum = ds->alias_checksum;
+ /*
+ * Dirty, but not dirtier than the original,
+ * and plugs the hole.
+ */
+ if (slots > 20)
+ slots = 0;
+ else {
+ long_slots = slots;
+ is_long = 1;
+ alias_checksum = ds->alias_checksum;
+ }
}
get_new_entry = 1;
fat_brelse (sb, bh);
goto out_no_bread;
}
- set_blocksize(sb->s_dev, blksize);
/*
* The DOS3 partition size limit is *not* 32M as many people think.
|| !b->secs_track || !b->heads;
}
fat_brelse(sb, bh);
+ set_blocksize(sb->s_dev, blksize);
/*
This must be done after the brelse because the bh is a dummy
allocated by fat_bread (see buffer.c)
/*================ Forward declarations ================*/
-static int cap_lookup(struct inode *, struct dentry *);
+static struct dentry *cap_lookup(struct inode *, struct dentry *);
static int cap_readdir(struct file *, void *, filldir_t);
/*================ Global variables ================*/
* inode corresponding to an entry in a directory, given the inode for
* the directory and the name (and its length) of the entry.
*/
-static int cap_lookup(struct inode * dir, struct dentry *dentry)
+static struct dentry *cap_lookup(struct inode * dir, struct dentry *dentry)
{
ino_t dtype;
struct hfs_name cname;
struct hfs_cat_key key;
struct inode *inode = NULL;
- if (!dir || !S_ISDIR(dir->i_mode)) {
- return -ENOENT;
- }
-
dentry->d_op = &hfs_dentry_operations;
entry = HFS_I(dir)->entry;
dtype = HFS_ITYPE(dir->i_ino);
done:
d_add(dentry, inode);
- return 0;
+ return NULL;
}
/*
/*================ Forward declarations ================*/
-static int dbl_lookup(struct inode *, struct dentry *);
+static struct dentry *dbl_lookup(struct inode *, struct dentry *);
static int dbl_readdir(struct file *, void *, filldir_t);
static int dbl_create(struct inode *, struct dentry *, int);
static int dbl_mkdir(struct inode *, struct dentry *, int);
* the inode for the directory and the name (and its length) of the
* entry.
*/
-static int dbl_lookup(struct inode * dir, struct dentry *dentry)
+static struct dentry *dbl_lookup(struct inode * dir, struct dentry *dentry)
{
struct hfs_name cname;
struct hfs_cat_entry *entry;
struct hfs_cat_key key;
struct inode *inode = NULL;
- if (!dir || !S_ISDIR(dir->i_mode)) {
- return -ENOENT;
- }
-
dentry->d_op = &hfs_dentry_operations;
entry = HFS_I(dir)->entry;
done:
d_add(dentry, inode);
- return 0;
+ return NULL;
}
/*
/*================ Forward declarations ================*/
-static int nat_lookup(struct inode *, struct dentry *);
+static struct dentry *nat_lookup(struct inode *, struct dentry *);
static int nat_readdir(struct file *, void *, filldir_t);
static int nat_rmdir(struct inode *, struct dentry *);
static int nat_hdr_unlink(struct inode *, struct dentry *);
* the inode corresponding to an entry in a directory, given the inode
* for the directory and the name (and its length) of the entry.
*/
-static int nat_lookup(struct inode * dir, struct dentry *dentry)
+static struct dentry *nat_lookup(struct inode * dir, struct dentry *dentry)
{
ino_t dtype;
struct hfs_name cname;
struct hfs_cat_key key;
struct inode *inode = NULL;
- if (!dir || !S_ISDIR(dir->i_mode)) {
- return -ENOENT;
- }
-
dentry->d_op = &hfs_dentry_operations;
entry = HFS_I(dir)->entry;
dtype = HFS_ITYPE(dir->i_ino);
done:
d_add(dentry, inode);
- return 0;
+ return NULL;
}
/*
size_t count, loff_t *ppos);
static int hpfs_readdir(struct file *filp,
void *dirent, filldir_t filldir);
-static int hpfs_lookup(struct inode *, struct dentry *);
+static struct dentry *hpfs_lookup(struct inode *, struct dentry *);
static const struct file_operations hpfs_dir_ops =
{
* the boondocks.)
*/
-static int hpfs_lookup(struct inode *dir, struct dentry *dentry)
+static struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry)
{
const char *name = dentry->d_name.name;
int len = dentry->d_name.len;
int retval;
struct quad_buffer_head qbh;
- /* In case of madness */
-
- retval = -ENOTDIR;
- if (dir == 0)
- goto out;
- if (!S_ISDIR(dir->i_mode))
- goto out;
-
/*
* Read in the directory entry. "." is there under the name ^A^A .
* Always read the dir even for . and .. in case we need the dates.
brelse4(&qbh);
out:
- return retval;
+ return ERR_PTR(retval);
}
/*
long old_pos;
struct inode *inode = filp->f_dentry->d_inode;
- if (inode == 0
- || inode->i_sb == 0
- || !S_ISDIR(inode->i_mode))
- return -EBADF;
-
tempname = (char *) __get_free_page(GFP_KERNEL);
if (!tempname)
return -ENOMEM;
found = 1;
}
- if (found) {
+ if (found)
dispose_list(freeable);
- found = 1; /* silly compiler */
- }
return found;
}
* Initialize the hash tables and default
* value for max inodes
*/
-#define MAX_INODE (12288)
+#define MAX_INODE (16384)
void __init inode_init(void)
{
struct iso_directory_record * tmpde;
struct inode *inode = filp->f_dentry->d_inode;
- if (!inode || !S_ISDIR(inode->i_mode))
- return -EBADF;
-
tmpname = (char *) __get_free_page(GFP_KERNEL);
if (!tmpname)
return -ENOMEM;
return retval;
}
-int isofs_lookup(struct inode * dir, struct dentry * dentry)
+struct dentry *isofs_lookup(struct inode * dir, struct dentry * dentry)
{
unsigned long ino;
struct buffer_head * bh;
#ifdef DEBUG
printk("lookup: %x %s\n",dir->i_ino, dentry->d_name.name);
#endif
- if (!dir)
- return -ENOENT;
-
- if (!S_ISDIR(dir->i_mode))
- return -ENOENT;
-
dentry->d_op = dir->i_sb->s_root->d_op;
bh = isofs_find_entry(dir, dentry, &ino);
inode = iget(dir->i_sb,ino);
if (!inode)
- return -EACCES;
+ return ERR_PTR(-EACCES);
}
d_add(dentry, inode);
- return 0;
+ return NULL;
}
struct minix_sb_info * info;
struct inode *inode = filp->f_dentry->d_inode;
- if (!inode || !inode->i_sb || !S_ISDIR(inode->i_mode))
- return -EBADF;
info = &inode->i_sb->u.minix_sb;
if (filp->f_pos & (info->s_dirsize - 1))
return -EBADF;
minix_free_inode(inode);
}
-static void minix_commit_super (struct super_block * sb,
- struct minix_super_block * ms)
+static void minix_commit_super(struct super_block * sb)
{
mark_buffer_dirty(sb->u.minix_sb.s_sbh, 1);
sb->s_dirt = 0;
}
-static void minix_write_super (struct super_block * sb)
+static void minix_write_super(struct super_block * sb)
{
struct minix_super_block * ms;
if (ms->s_state & MINIX_VALID_FS)
ms->s_state &= ~MINIX_VALID_FS;
- minix_commit_super (sb, ms);
+ minix_commit_super(sb);
}
sb->s_dirt = 0;
}
ms->s_state = sb->u.minix_sb.s_mount_state;
mark_buffer_dirty(sb->u.minix_sb.s_sbh, 1);
sb->s_dirt = 1;
- minix_commit_super (sb, ms);
+ minix_commit_super(sb);
}
else {
/* Mount a partition which is read-only, read-write. */
0 /* compare */
};
-int minix_lookup(struct inode * dir, struct dentry *dentry)
+struct dentry *minix_lookup(struct inode * dir, struct dentry *dentry)
{
struct inode * inode = NULL;
struct minix_dir_entry * de;
inode = iget(dir->i_sb, ino);
if (!inode)
- return -EACCES;
+ return ERR_PTR(-EACCES);
}
d_add(dentry, inode);
- return 0;
+ return NULL;
}
/*
struct buffer_head * bh;
struct minix_dir_entry * de;
- if (!dir)
- return -ENOENT;
inode = minix_new_inode(dir);
if (!inode)
return -ENOSPC;
struct buffer_head * bh;
struct minix_dir_entry * de;
- if (!dir)
- return -ENOENT;
bh = minix_find_entry(dir, dentry->d_name.name,
dentry->d_name.len, &de);
if (bh) {
struct minix_dir_entry * de;
struct minix_sb_info * info;
- if (!dir || !dir->i_sb)
- return -EINVAL;
info = &dir->i_sb->u.minix_sb;
bh = minix_find_entry(dir, dentry->d_name.name,
dentry->d_name.len, &de);
/***** Get inode using directory and name */
-int msdos_lookup(struct inode *dir,struct dentry *dentry)
+struct dentry *msdos_lookup(struct inode *dir,struct dentry *dentry)
{
struct super_block *sb = dir->i_sb;
struct inode *inode = NULL;
d_add(dentry, inode);
res = 0;
out:
- return res;
+ return ERR_PTR(res);
}
struct dentry * dentry = d_alloc(parent, name);
result = ERR_PTR(-ENOMEM);
if (dentry) {
- int error = dir->i_op->lookup(dir, dentry);
- result = dentry;
- if (error) {
+ result = dir->i_op->lookup(dir, dentry);
+ if (result)
dput(dentry);
- result = ERR_PTR(error);
- }
+ else
+ result = dentry;
}
}
up(&dir->i_sem);
static int ncp_readdir(struct file *, void *, filldir_t);
static int ncp_create(struct inode *, struct dentry *, int);
-static int ncp_lookup(struct inode *, struct dentry *);
+static struct dentry *ncp_lookup(struct inode *, struct dentry *);
static int ncp_unlink(struct inode *, struct dentry *);
static int ncp_mkdir(struct inode *, struct dentry *, int);
static int ncp_rmdir(struct inode *, struct dentry *);
DDPRINTK(KERN_DEBUG "ncp_readdir: inode->i_ino = %ld, c_ino = %ld\n",
inode->i_ino, c_ino);
- result = -EBADF;
- if (!inode || !S_ISDIR(inode->i_mode)) {
- printk(KERN_WARNING "ncp_readdir: inode is NULL or not a directory\n");
- goto out;
- }
result = -EIO;
if (!ncp_conn_valid(server))
goto out;
return result;
}
-static int ncp_lookup(struct inode *dir, struct dentry *dentry)
+static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry)
{
struct ncp_server *server;
struct inode *inode = NULL;
struct ncpfs_inode_info finfo;
__u8 __name[dentry->d_name.len + 1];
- error = -ENOENT;
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk(KERN_WARNING "ncp_lookup: inode is NULL or not a directory.\n");
- goto finished;
- }
server = NCP_SERVER(dir);
error = -EIO;
if (res != 0) {
goto add_entry;
} else vol2io(server, finfo.nw_info.i.entryName,
- ncp_preserve_entry_case(dir,
+ !ncp_preserve_entry_case(dir,
finfo.nw_info.i.NSCreator));
}
#ifdef NCPFS_PARANOIA
printk(KERN_DEBUG "ncp_lookup: result=%d\n", error);
#endif
- return error;
+ return ERR_PTR(error);
}
/*
printk(KERN_DEBUG "ncp_create_new: creating %s/%s, mode=%x\n",
dentry->d_parent->d_name.name, dentry->d_name.name, mode);
#endif
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk(KERN_WARNING "ncp_create_new: inode is NULL or not a directory\n");
- return -ENOENT;
- }
error = -EIO;
if (!ncp_conn_valid(NCP_SERVER(dir)))
goto out;
DPRINTK(KERN_DEBUG "ncp_mkdir: making %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
- error = -ENOTDIR;
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk(KERN_WARNING "ncp_mkdir: inode is NULL or not a directory\n");
- goto out;
- }
error = -EIO;
if (!ncp_conn_valid(NCP_SERVER(dir)))
goto out;
DPRINTK(KERN_DEBUG "ncp_rmdir: removing %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
-
- error = -ENOENT;
- if (!dir || !S_ISDIR(dir->i_mode))
- {
- printk(KERN_WARNING "ncp_rmdir: inode is NULL or not a directory\n");
- goto out;
- }
error = -EIO;
if (!ncp_conn_valid(NCP_SERVER(dir)))
DPRINTK(KERN_DEBUG "ncp_unlink: unlinking %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
- error = -ENOTDIR;
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk(KERN_WARNING "ncp_unlink: inode is NULL or not a directory\n");
- goto out;
- }
error = -EIO;
if (!ncp_conn_valid(NCP_SERVER(dir)))
goto out;
old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
- error = -ENOTDIR;
- if (!old_dir || !S_ISDIR(old_dir->i_mode)) {
- printk(KERN_WARNING "ncp_rename: old inode is NULL or not a directory\n");
- goto out;
- }
- if (!new_dir || !S_ISDIR(new_dir->i_mode)) {
- printk(KERN_WARNING "ncp_rename: new inode is NULL or not a directory\n");
- goto out;
- }
error = -EIO;
if (!ncp_conn_valid(NCP_SERVER(old_dir)))
goto out;
static int nfs_safe_remove(struct dentry *);
-static int nfs_dir_open(struct inode *, struct file *);
static ssize_t nfs_dir_read(struct file *, char *, size_t, loff_t *);
static int nfs_readdir(struct file *, void *, filldir_t);
-static int nfs_lookup(struct inode *, struct dentry *);
+static struct dentry *nfs_lookup(struct inode *, struct dentry *);
static int nfs_create(struct inode *, struct dentry *, int);
static int nfs_mkdir(struct inode *, struct dentry *, int);
static int nfs_rmdir(struct inode *, struct dentry *);
NULL, /* select - default */
NULL, /* ioctl - default */
NULL, /* mmap */
- nfs_dir_open, /* open - revalidate */
+ NULL, /* no special open is needed */
NULL, /* flush */
NULL, /* no special release code */
NULL /* fsync */
nfs_revalidate, /* revalidate */
};
-static int
-nfs_dir_open(struct inode *dir, struct file *file)
-{
- struct dentry *dentry = file->f_dentry;
-
- dfprintk(VFS, "NFS: nfs_dir_open(%s/%s)\n",
- dentry->d_parent->d_name.name, dentry->d_name.name);
- return _nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
-}
-
static ssize_t
nfs_dir_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
{
dfprintk(VFS, "NFS: nfs_readdir(%s/%s)\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
- result = -EBADF;
- if (!inode || !S_ISDIR(inode->i_mode)) {
- printk("nfs_readdir: inode is NULL or not a directory\n");
- goto out;
- }
result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
if (result < 0)
{
struct dentry * parent = dentry->d_parent;
struct inode * inode = dentry->d_inode;
- unsigned long time = jiffies - dentry->d_time;
int error;
struct nfs_fh fhandle;
struct nfs_fattr fattr;
* If we don't have an inode, let's just assume
* a 5-second "live" time for negative dentries.
*/
- if (!inode) {
- if (time < NFS_REVALIDATE_INTERVAL)
- goto out_valid;
- goto out_bad;
- }
+ if (!inode)
+ goto do_lookup;
if (is_bad_inode(inode)) {
-#ifdef NFS_PARANOIA
-printk("nfs_lookup_validate: %s/%s has dud inode\n",
-parent->d_name.name, dentry->d_name.name);
-#endif
+ dfprintk(VFS, "nfs_lookup_validate: %s/%s has dud inode\n",
+ parent->d_name.name, dentry->d_name.name);
goto out_bad;
}
- if (time < NFS_ATTRTIMEO(inode))
+ if (_nfs_revalidate_inode(NFS_DSERVER(dentry), dentry))
+ goto out_bad;
+
+ if (time_before(jiffies,dentry->d_time+NFS_ATTRTIMEO(inode)))
goto out_valid;
if (IS_ROOT(dentry))
goto out_valid;
+do_lookup:
/*
* Do a new lookup and check the dentry attributes.
*/
error = nfs_proc_lookup(NFS_DSERVER(parent), NFS_FH(parent),
dentry->d_name.name, &fhandle, &fattr);
+ if (dentry->d_inode == NULL) {
+ if (error == -ENOENT &&
+ time_before(jiffies,dentry->d_time+NFS_REVALIDATE_INTERVAL))
+ goto out_valid;
+ goto out_bad;
+ }
if (error)
goto out_bad;
/* Filehandle matches? */
if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh))) {
- if (dentry->d_count < 2 || nfs_revalidate(dentry))
+ if (dentry->d_count < 2)
goto out_bad;
}
out_valid:
return 1;
out_bad:
+ if (dentry->d_parent->d_inode)
+ nfs_invalidate_dircache(dentry->d_parent->d_inode);
+ if (inode && S_ISDIR(inode->i_mode))
+ nfs_invalidate_dircache(inode);
return 0;
}
}
#endif
-static int nfs_lookup(struct inode *dir, struct dentry * dentry)
+static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry)
{
struct inode *inode;
int error;
}
}
out:
- return error;
+ return ERR_PTR(error);
}
/*
{
struct qstr sqstr;
struct dentry *sdentry;
- int error;
+ struct dentry *res;
sqstr.name = silly;
sqstr.len = slen;
sdentry = d_alloc(parent, &sqstr);
if (sdentry == NULL)
return ERR_PTR(-ENOMEM);
- error = nfs_lookup(parent->d_inode, sdentry);
- if (error) {
+ res = nfs_lookup(parent->d_inode, sdentry);
+ if (res) {
dput(sdentry);
- return ERR_PTR(error);
+ return res;
}
}
return sdentry;
static ssize_t nfs_file_write(struct file *, const char *, size_t, loff_t *);
static int nfs_file_flush(struct file *);
static int nfs_fsync(struct file *, struct dentry *dentry);
-static int nfs_file_open(struct inode *inode, struct file *filp);
static struct file_operations nfs_file_operations = {
NULL, /* lseek - default */
NULL, /* select - default */
NULL, /* ioctl - default */
nfs_file_mmap, /* mmap */
- nfs_file_open, /* open */
+ NULL, /* no special open is needed */
nfs_file_flush, /* flush */
NULL, /* release */
nfs_fsync, /* fsync */
return status;
}
-/*
- * Open the file.
- * Just checks the cache is synchronized.
- */
-static int
-nfs_file_open(struct inode *inode, struct file *filp)
-{
- struct dentry *dentry = filp->f_dentry;
-
- return _nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
-}
-
-
static ssize_t
nfs_file_read(struct file * file, char * buf, size_t count, loff_t *ppos)
{
#define NFS_PARANOIA 1
static struct inode * __nfs_fhget(struct super_block *, struct nfs_fattr *);
+static void nfs_zap_caches(struct inode *);
+static void nfs_invalidate_inode(struct inode *);
static void nfs_read_inode(struct inode *);
static void nfs_put_inode(struct inode *);
* could cause file corruption. But since the dentry
* count is 0 and all pending IO for a dentry has been
* flushed when the count went to 0, we're safe here.
+ * Also returns the number of unhashed dentries
*/
-void nfs_free_dentries(struct inode *inode)
+static int
+nfs_free_dentries(struct inode *inode)
{
struct list_head *tmp, *head = &inode->i_dentry;
+ int unhashed;
restart:
tmp = head;
+ unhashed = 0;
while ((tmp = tmp->next) != head) {
struct dentry *dentry = list_entry(tmp, struct dentry, d_alias);
-printk("nfs_free_dentries: found %s/%s, d_count=%d, hashed=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name,
-dentry->d_count, !list_empty(&dentry->d_hash));
+ dprintk("nfs_free_dentries: found %s/%s, d_count=%d, hashed=%d\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ dentry->d_count, !list_empty(&dentry->d_hash));
if (!dentry->d_count) {
dget(dentry);
d_drop(dentry);
dput(dentry);
goto restart;
}
+ if (!list_empty(&dentry->d_hash))
+ unhashed++;
}
+ return unhashed;
+}
+
+/*
+ * Invalidate the local caches
+ */
+static void
+nfs_zap_caches(struct inode *inode)
+{
+ NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+ NFS_CACHEINV(inode);
+
+ if (S_ISDIR(inode->i_mode))
+ nfs_invalidate_dircache(inode);
+ else
+ invalidate_inode_pages(inode);
+}
+
+/*
+ * Invalidate, but do not unhash, the inode
+ */
+static void
+nfs_invalidate_inode(struct inode *inode)
+{
+ umode_t save_mode = inode->i_mode;
+
+ make_bad_inode(inode);
+ inode->i_mode = save_mode;
+ nfs_inval(inode);
+ nfs_zap_caches(inode);
}
/*
__nfs_fhget(struct super_block *sb, struct nfs_fattr *fattr)
{
struct inode *inode;
- int max_count;
+ int max_count, stale_inode, unhashed = 0;
retry:
inode = iget(sb, fattr->fileid);
* as the inode may have become a different object.
* (We can probably handle modes changes here, too.)
*/
+ stale_inode = inode->i_mode &&
+ ((fattr->mode ^ inode->i_mode) & S_IFMT);
+ stale_inode |= inode->i_count && inode->i_count == unhashed;
max_count = S_ISDIR(fattr->mode) ? 1 : fattr->nlink;
- if (inode->i_count > max_count) {
-printk("__nfs_fhget: inode %ld busy, i_count=%d, i_nlink=%d\n",
-inode->i_ino, inode->i_count, inode->i_nlink);
- nfs_free_dentries(inode);
- if (inode->i_count > max_count) {
-printk("__nfs_fhget: inode %ld still busy, i_count=%d\n",
-inode->i_ino, inode->i_count);
+ if (stale_inode || inode->i_count > max_count + unhashed) {
+ dprintk("__nfs_fhget: inode %ld busy, i_count=%d, i_nlink=%d\n",
+ inode->i_ino, inode->i_count, inode->i_nlink);
+ unhashed = nfs_free_dentries(inode);
+ if (stale_inode || inode->i_count > max_count + unhashed) {
+ printk("__nfs_fhget: inode %ld still busy, i_count=%d\n",
+ inode->i_ino, inode->i_count);
if (!list_empty(&inode->i_dentry)) {
struct dentry *dentry;
dentry = list_entry(inode->i_dentry.next,
struct dentry, d_alias);
-printk("__nfs_fhget: killing %s/%s filehandle\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
- memset(dentry->d_fsdata, 0,
+ printk("__nfs_fhget: killing %s/%s filehandle\n",
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name);
+ memset(dentry->d_fsdata, 0,
sizeof(struct nfs_fh));
- } else
- printk("NFS: inode %ld busy, no aliases?\n",
- inode->i_ino);
- make_bad_inode(inode);
+ }
remove_inode_hash(inode);
+ nfs_invalidate_inode(inode);
+ unhashed = 0;
}
iput(inode);
goto retry;
int error;
u32 *fh;
struct nfs_fh fhandle;
-#ifdef NFS_PARANOIA
-printk("nfs_revalidate_inode: %s/%s getattr failed, ino=%ld, error=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, status);
-#endif
+ dfprintk(PAGECACHE, "nfs_revalidate_inode: %s/%s getattr failed, ino=%ld, error=%d\n",
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name, inode->i_ino, status);
if (status != -ESTALE)
goto out;
/*
* and find out what the filehandle should be.
*/
fh = (u32 *) NFS_FH(dentry);
- printk("NFS: bad fh %08x%08x%08x%08x%08x%08x%08x%08x\n",
+ dfprintk(PAGECACHE, "NFS: bad fh %08x%08x%08x%08x%08x%08x%08x%08x\n",
fh[0],fh[1],fh[2],fh[3],fh[4],fh[5],fh[6],fh[7]);
error = nfs_proc_lookup(server, NFS_FH(dentry->d_parent),
dentry->d_name.name, &fhandle, &fattr);
if (error) {
- printk("NFS: lookup failed, error=%d\n", error);
+ dfprintk(PAGECACHE, "NFS: lookup failed, error=%d\n", error);
goto out;
}
fh = (u32 *) &fhandle;
- printk(" %08x%08x%08x%08x%08x%08x%08x%08x\n",
+ dfprintk(PAGECACHE, " %08x%08x%08x%08x%08x%08x%08x%08x\n",
fh[0],fh[1],fh[2],fh[3],fh[4],fh[5],fh[6],fh[7]);
goto out;
}
status = nfs_refresh_inode(inode, &fattr);
if (status) {
-#ifdef NFS_PARANOIA
-printk("nfs_revalidate_inode: %s/%s refresh failed, ino=%ld, error=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, status);
-#endif
+ dfprintk(PAGECACHE, "nfs_revalidate_inode: %s/%s refresh failed, ino=%ld, error=%d\n",
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name, inode->i_ino, status);
goto out;
}
dfprintk(PAGECACHE, "NFS: %s/%s revalidation complete\n",
printk("nfs_refresh_inode: inode %ld mode changed, %07o to %07o\n",
inode->i_ino, inode->i_mode, fattr->mode);
#endif
- fattr->mode = inode->i_mode; /* save mode */
- make_bad_inode(inode);
- nfs_inval(inode);
- inode->i_mode = fattr->mode; /* restore mode */
/*
* No need to worry about unhashing the dentry, as the
* lookup validation will know that the inode is bad.
- * (But we fall through to invalidate the caches.)
*/
+ nfs_invalidate_inode(inode);
+ goto out;
out_invalid:
- /*
- * Invalidate the local caches
- */
#ifdef NFS_DEBUG_VERBOSE
printk("nfs_refresh_inode: invalidating %ld pages\n", inode->i_nrpages);
#endif
- if (!S_ISDIR(inode->i_mode))
- invalidate_inode_pages(inode);
- else
- nfs_invalidate_dircache(inode);
- NFS_CACHEINV(inode);
- NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+ nfs_zap_caches(inode);
goto out;
}
ntfs_debug(DEBUG_OTHER, "ntfs_readdir ino %x mode %x\n",
(unsigned)dir->i_ino,(unsigned int)dir->i_mode);
- if(!dir || (dir->i_ino==0) || !S_ISDIR(dir->i_mode))return -EBADF;
ntfs_debug(DEBUG_OTHER, "readdir: Looking for file %x dircount %d\n",
(unsigned)filp->f_pos,dir->i_count);
return 0;
}
-static int ntfs_lookup(struct inode *dir, struct dentry *d)
+static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *d)
{
struct inode *res=0;
char *item=0;
error=ntfs_decodeuni(NTFS_INO2VOL(dir),(char*)d->d_name.name,
d->d_name.len,&walk.name,&walk.namelen);
if(error)
- return error;
+ return ERR_PTR(-error);
item=ntfs_malloc(ITEM_SIZE);
if( !item )
- return ENOMEM;
+ return ERR_PTR(-ENOMEM);
/* ntfs_getdir will place the directory entry into item,
and the first long long is the MFT record number */
walk.type=BY_NAME;
ntfs_free(item);
ntfs_free(walk.name);
/* Always return success, the dcache will handle negative entries. */
- return 0;
+ return NULL;
}
static struct file_operations ntfs_file_operations_nommap = {
error = ntfs_get_volumesize( NTFS_SB2VOL( sb ), &fs.f_blocks );
if( error )
- return error;
+ return -error;
fs.f_bfree=ntfs_get_free_cluster_count(vol->bitmap);
fs.f_bavail=fs.f_bfree;
ntfs_insert_fixups(buf,vol->blocksize);
io.param=buf;
io.size=vol->mft_recordsize;
+ io.fn_put = ntfs_put;
+ io.fn_get = ntfs_get;
error=ntfs_write_attr(vol->mft_ino,vol->at_data,0,
(rcount-1)*vol->mft_recordsize,&io);
if(error)return error;
#include <asm/uaccess.h>
static int proc_readfd(struct file *, void *, filldir_t);
-static int proc_lookupfd(struct inode *, struct dentry *);
+static struct dentry *proc_lookupfd(struct inode *, struct dentry *);
static struct file_operations proc_fd_operations = {
NULL, /* lseek - default */
*
* Thus just return -ENOENT instead.
*/
-static int proc_lookupfd(struct inode * dir, struct dentry * dentry)
+static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry)
{
unsigned int ino, pid, fd, c;
struct task_struct * p;
int len, err;
err = -ENOENT;
- if (!dir)
- goto out;
ino = dir->i_ino;
pid = ino >> 16;
ino &= 0x0000ffff;
- if (!pid || ino != PROC_PID_FD || !S_ISDIR(dir->i_mode))
+ if (!pid || ino != PROC_PID_FD)
goto out;
fd = 0;
if (inode) {
dentry->d_op = &proc_dentry_operations;
d_add(dentry, inode);
- err = 0;
+ return NULL;
}
out:
- return err;
+ return ERR_PTR(err);
}
#define NUMBUF 10
int retval;
char buf[NUMBUF];
- retval = -EBADF;
- if (!inode || !S_ISDIR(inode->i_mode))
- goto out;
-
retval = 0;
ino = inode->i_ino;
pid = ino >> 16;
struct openpromfs_dev *d;
char buffer2[64];
- if (!inode || !S_ISDIR (inode->i_mode)) return -ENOTDIR;
ino = inode->i_ino;
i = filp->f_pos;
switch (i) {
#define FIRST_PROCESS_ENTRY 256
static int proc_root_readdir(struct file *, void *, filldir_t);
-static int proc_root_lookup(struct inode *,struct dentry *);
+static struct dentry *proc_root_lookup(struct inode *,struct dentry *);
static int proc_unlink(struct inode *, struct dentry *);
static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8] = {0};
* Don't create negative dentries here, return -ENOENT by hand
* instead.
*/
-int proc_lookup(struct inode * dir, struct dentry *dentry)
+struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry)
{
struct inode *inode;
struct proc_dir_entry * de;
int error;
- error = -ENOTDIR;
- if (!dir || !S_ISDIR(dir->i_mode))
- goto out;
-
error = -ENOENT;
inode = NULL;
de = (struct proc_dir_entry *) dir->u.generic_ip;
if (inode) {
dentry->d_op = &proc_dentry_operations;
d_add(dentry, inode);
- error = 0;
+ return NULL;
}
-out:
- return error;
+ return ERR_PTR(error);
}
-static int proc_root_lookup(struct inode * dir, struct dentry * dentry)
+static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentry)
{
unsigned int pid, c;
struct task_struct *p;
}
if (!proc_lookup(dir, dentry))
- return 0;
+ return NULL;
pid = 0;
name = dentry->d_name.name;
unsigned long ino = (pid << 16) + PROC_PID_INO;
inode = proc_get_inode(dir->i_sb, ino, &proc_pid);
if (!inode)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
inode->i_flags|=S_IMMUTABLE;
}
dentry->d_op = &proc_dentry_operations;
d_add(dentry, inode);
- return 0;
+ return NULL;
}
/*
int i;
struct inode *inode = filp->f_dentry->d_inode;
- if (!inode || !S_ISDIR(inode->i_mode))
- return -ENOTDIR;
ino = inode->i_ino;
de = (struct proc_dir_entry *) inode->u.generic_ip;
if (!de)
blknum = inode->u.qnx4_i.i_first_xtnt.xtnt_blk - 1 +
((filp->f_pos >> 6) >> 3);
- if (!inode || !inode->i_sb || !S_ISDIR(inode->i_mode)) {
- return -EBADF;
- }
QNX4DEBUG(("qnx4_readdir:i_size = %ld\n", (long) inode->i_size));
QNX4DEBUG(("filp->f_pos = %ld\n", (long) filp->f_pos));
QNX4DEBUG(("BlkNum = %ld\n", (long) blknum));
return NULL;
}
-int qnx4_lookup(struct inode *dir, struct dentry *dentry)
+struct dentry * qnx4_lookup(struct inode *dir, struct dentry *dentry)
{
int ino;
struct qnx4_inode_entry *de;
struct buffer_head *bh;
const char *name = dentry->d_name.name;
int len = dentry->d_name.len;
- struct inode *foundinode;
+ struct inode *foundinode = NULL;
- if (!dir) {
- return -EBADF;
- }
- if (!S_ISDIR(dir->i_mode)) {
- return -EBADF;
- }
- if (!(bh = qnx4_find_entry(len, dir, name, &de, &ino))) {
- return -ENOENT;
- }
+ if (!(bh = qnx4_find_entry(len, dir, name, &de, &ino)))
+ goto out;
/* The entry is linked, let's get the real info */
if ((de->di_status & QNX4_FILE_LINK) == QNX4_FILE_LINK) {
lnk = (struct qnx4_link_info *) de;
if ((foundinode = iget(dir->i_sb, ino)) == NULL) {
QNX4DEBUG(("qnx4: lookup->iget -> NULL\n"));
- return -EACCES;
+ return ERR_PTR(-EACCES);
}
+out:
d_add(dentry, foundinode);
- return 0;
+ return NULL;
}
#ifdef CONFIG_QNX4FS_RW
int stored = 0;
char fsname[ROMFS_MAXFN]; /* XXX dynamic? */
- if (!i || !S_ISDIR(i->i_mode))
- return -EBADF;
-
maxoff = i->i_sb->u.romfs_sb.s_maxsize;
offset = filp->f_pos;
}
}
-static int
+static struct dentry *
romfs_lookup(struct inode *dir, struct dentry *dentry)
{
unsigned long offset, maxoff;
const char *name; /* got from dentry */
int len;
- res = -EBADF;
- if (!dir || !S_ISDIR(dir->i_mode))
- goto out;
-
res = 0; /* instead of ENOENT */
offset = dir->i_ino & ROMFH_MASK;
if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0)
}
out:
- return res;
+ return ERR_PTR(res);
}
/*
static int smb_readdir(struct file *, void *, filldir_t);
static int smb_dir_open(struct inode *, struct file *);
-static int smb_lookup(struct inode *, struct dentry *);
+static struct dentry *smb_lookup(struct inode *, struct dentry *);
static int smb_create(struct inode *, struct dentry *, int);
static int smb_mkdir(struct inode *, struct dentry *, int);
static int smb_rmdir(struct inode *, struct dentry *);
}
}
-static int
+static struct dentry *
smb_lookup(struct inode *dir, struct dentry *dentry)
{
struct smb_fattr finfo;
}
}
out:
- return error;
+ return ERR_PTR(error);
}
/*
static int sysv_readdir(struct file * filp, void * dirent, filldir_t filldir)
{
struct inode *inode = filp->f_dentry->d_inode;
- struct super_block * sb;
+ struct super_block * sb = inode->i_sb;
unsigned int offset,i;
struct buffer_head * bh;
char* bh_data;
struct sysv_dir_entry * de, sde;
- if (!inode || !(sb = inode->i_sb) || !S_ISDIR(inode->i_mode))
- return -EBADF;
if ((unsigned long)(filp->f_pos) % SYSV_DIRSIZE)
return -EBADF;
while (filp->f_pos < inode->i_size) {
return NULL;
}
-int sysv_lookup(struct inode * dir, struct dentry * dentry)
+struct dentry *sysv_lookup(struct inode * dir, struct dentry * dentry)
{
struct inode * inode = NULL;
struct sysv_dir_entry * de;
struct buffer_head * bh;
- if (!dir)
- return -ENOENT;
- if (!S_ISDIR(dir->i_mode)) {
- return -ENOENT;
- }
bh = sysv_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de);
if (bh) {
inode = iget(dir->i_sb, ino);
if (!inode)
- return -EACCES;
+ return ERR_PTR(-EACCES);
}
d_add(dentry, inode);
- return 0;
+ return NULL;
}
/*
struct buffer_head * bh;
struct sysv_dir_entry * de;
- if (!dir)
- return -ENOENT;
inode = sysv_new_inode(dir);
if (!inode)
return -ENOSPC;
struct buffer_head * bh;
struct sysv_dir_entry * de;
- if (!dir)
- return -ENOENT;
bh = sysv_find_entry(dir, dentry->d_name.name,
dentry->d_name.len, &de);
if (bh) {
struct buffer_head * bh, *dir_block;
struct sysv_dir_entry * de;
- if (!dir)
- return -EINVAL;
bh = sysv_find_entry(dir, dentry->d_name.name,
dentry->d_name.len, &de);
if (bh) {
int de_reclen;
unsigned flags, swab;
-
- /* Isn't that already done in the upper layer???
- * the VFS layer really needs some explicit documentation!
- */
- if (!inode || !S_ISDIR(inode->i_mode))
- return -EBADF;
-
sb = inode->i_sb;
swab = sb->u.ufs_sb.s_swab;
flags = sb->u.ufs_sb.s_flags;
return NULL;
}
-int ufs_lookup(struct inode * dir, struct dentry *dentry)
+struct dentry *ufs_lookup(struct inode * dir, struct dentry *dentry)
{
struct super_block * sb;
struct inode * inode;
swab = sb->u.ufs_sb.s_swab;
if (dentry->d_name.len > UFS_MAXNAMLEN)
- return -ENAMETOOLONG;
+ return ERR_PTR(-ENAMETOOLONG);
bh = ufs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de);
inode = NULL;
brelse (bh);
inode = iget(sb, ino);
if (!inode)
- return -EACCES;
+ return ERR_PTR(-EACCES);
}
d_add(dentry, inode);
UFSD(("EXIT\n"))
- return 0;
+ return NULL;
}
/*
/*
* Do a real lookup on the short name.
*/
- dret = umsdos_lookup_dentry(filp->f_dentry, info.fake.fname,
- info.fake.len, 1);
+ dret = umsdos_covered(filp->f_dentry, info.fake.fname,
+ info.fake.len);
ret = PTR_ERR(dret);
if (IS_ERR(dret))
break;
* entry from the EMD file, and return ENOENT.
*/
-int umsdos_lookup_x (struct inode *dir, struct dentry *dentry, int nopseudo)
+struct dentry *umsdos_lookup_x (struct inode *dir, struct dentry *dentry, int nopseudo)
{
struct dentry *dret = NULL;
struct inode *inode;
info.fake.len, info.fake.fname, info.f_pos, ret, info.fake.len));
/* do a real lookup to get the short name ... */
- dret = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
- info.fake.len, 1);
+ dret = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len);
ret = PTR_ERR(dret);
if (IS_ERR(dret)) {
printk("umsdos_lookup_x: %s/%s real lookup failed, ret=%d\n",
dput(dret);
out:
umsdos_endlookup (dir);
- return ret;
+ return ERR_PTR(ret);
out_remove:
printk(KERN_WARNING "UMSDOS: entry %s/%s out of sync, erased\n",
* Called by VFS; should fill dentry->d_inode via d_add.
*/
-int UMSDOS_lookup (struct inode *dir, struct dentry *dentry)
+struct dentry *UMSDOS_lookup (struct inode *dir, struct dentry *dentry)
{
- int ret;
+ struct dentry *ret;
ret = umsdos_lookup_x (dir, dentry, 0);
/* Create negative dentry if not found. */
- if (ret == -ENOENT) {
+ if (ret == ERR_PTR(-ENOENT)) {
Printk ((KERN_DEBUG
"UMSDOS_lookup: converting -ENOENT to negative\n"));
d_add (dentry, NULL);
dentry->d_op = &umsdos_dentry_operations;
- ret = 0;
+ ret = NULL;
}
return ret;
}
struct dentry *umsdos_covered(struct dentry *parent, char *name, int len)
{
struct dentry *result, *dentry;
- int error;
struct qstr qstr;
qstr.name = name;
result = ERR_PTR(-ENOMEM);
dentry = d_alloc(parent, &qstr);
if (dentry) {
- result = dentry;
/* XXXXXXXXXXXXXXXXXXX Race alert! */
- error = UMSDOS_rlookup(parent->d_inode, result);
- d_drop(result);
- if (error)
+ result = UMSDOS_rlookup(parent->d_inode, dentry);
+ d_drop(dentry);
+ if (result)
goto out_fail;
+ return dentry;
}
out:
return result;
out_fail:
- dput(result);
- result = ERR_PTR(error);
+ dput(dentry);
goto out;
}
int real)
{
struct dentry *result, *dentry;
- int error;
struct qstr qstr;
qstr.name = name;
result = ERR_PTR(-ENOMEM);
dentry = d_alloc(parent, &qstr);
if (dentry) {
- result = dentry;
- error = real ?
- UMSDOS_rlookup(parent->d_inode, result) :
- UMSDOS_lookup(parent->d_inode, result);
- if (error)
+ result = real ?
+ UMSDOS_rlookup(parent->d_inode, dentry) :
+ UMSDOS_lookup(parent->d_inode, dentry);
+ if (result)
goto out_fail;
+ return dentry;
}
}
out:
return result;
out_fail:
- dput(result);
- result = ERR_PTR(error);
+ dput(dentry);
goto out;
}
#endif
-/*
- * Check whether we can delete from the directory.
- */
-static int is_sticky(struct inode *dir, int uid)
-{
- return !((dir->i_mode & S_ISVTX) == 0 ||
- current->fsuid == uid ||
- current->fsuid == dir->i_uid ||
- capable (CAP_FOWNER));
-}
-
-
static int umsdos_nevercreat (struct inode *dir, struct dentry *dentry,
int errcod)
{
olddentry->d_parent->d_name.name, olddentry->d_name.name, old_info.fake.fname));
/* Do a real lookup to get the short name dentry */
- temp = umsdos_lookup_dentry(olddentry->d_parent,
- old_info.fake.fname,
- old_info.fake.len, 1);
+ temp = umsdos_covered(olddentry->d_parent, old_info.fake.fname,
+ old_info.fake.len);
ret = PTR_ERR(temp);
if (IS_ERR(temp))
goto out_unlock;
goto out;
/* lookup the short name dentry */
- temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
- info.fake.len, 1);
+ temp = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len);
ret = PTR_ERR(temp);
if (IS_ERR(temp))
goto out_remove;
- /* Keep the short name dentry anonymous */
- if (temp != dentry)
- d_drop(temp);
-
/* Make sure the short name doesn't exist */
ret = -EEXIST;
if (temp->d_inode) {
inode = temp->d_inode;
down(&inode->i_sem);
- /*
- * Note! The long and short name might be the same,
- * so check first before doing the instantiate ...
- */
- if (dentry != temp) {
-if (dentry->d_inode)
-printk("umsdos_mkdir: dentry not negative!\n");
- inode->i_count++;
- d_instantiate(dentry, inode);
- }
+ inode->i_count++;
+ d_instantiate(dentry, inode);
+
/* N.B. this should have an option to create the EMD ... */
umsdos_lookup_patch_new(dentry, &info);
umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
/* Call findentry to complete the mangling */
umsdos_findentry (dentry->d_parent, &info, 2);
- temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
- info.fake.len, 1);
+ temp = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len);
ret = PTR_ERR(temp);
if (IS_ERR(temp))
goto out;
- /*
- * If the short name is an alias, dput() it now;
- * otherwise d_drop() it to keep it anonymous.
- */
- if (temp == dentry)
- dput(temp);
- else
- d_drop(temp);
-
/*
* Attempt to remove the msdos name.
*/
/* dput() temp if we didn't do it above */
out_dput:
- if (temp != dentry)
- dput(temp);
+ dput(temp);
out:
Printk (("umsdos_rmdir %d\n", ret));
Printk (("UMSDOS_unlink %.*s ", info.fake.len, info.fake.fname));
- ret = -EPERM;
- /* check sticky bit */
- if (is_sticky(dir, info.entry.uid)) {
-printk("umsdos_unlink: %s/%s is sticky\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
- goto out_unlock;
- }
-
/*
* Note! If this is a hardlink and the names are aliased,
* the short-name lookup will return the hardlink dentry.
}
/* Do a real lookup to get the short name dentry */
- temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
- info.fake.len, 1);
+ temp = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len);
ret = PTR_ERR(temp);
if (IS_ERR(temp))
goto out_unlock;
link = umsdos_solve_hlink(dget(temp));
}
- /*
- * If the short and long names are aliased,
- * dput() it now so the dentry isn't busy.
- */
- if (temp == dentry)
- dput(temp);
-
/* Delete the EMD entry */
ret = umsdos_delentry (dentry->d_parent, &info, 0);
if (ret && ret != -ENOENT) {
goto out_dput;
}
- ret = msdos_unlink_umsdos (dir, temp);
+ ret = msdos_unlink(dir, temp);
#ifdef UMSDOS_PARANOIA
if (ret)
printk("umsdos_unlink: %s/%s unlink failed, ret=%d\n",
/* dput() temp if we didn't do it above */
out_dput:
- if (temp != dentry) {
- d_drop(temp);
- dput(temp);
- if (!ret)
- d_delete (dentry);
- }
+ dput(temp);
+ if (!ret)
+ d_delete (dentry);
out_unlock:
umsdos_unlockcreate (dir);
* In the real root directory (c:\), the directory ..
* is the pseudo root (c:\linux).
*/
-int umsdos_rlookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo)
+struct dentry *umsdos_rlookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo)
{
- int ret;
+ struct dentry *ret;
if (saved_root && dir == saved_root->d_inode && !nopseudo &&
dentry->d_name.len == UMSDOS_PSDROOT_LEN &&
* /linux won't show
*/
- ret = -ENOENT;
+ ret = ERR_PTR(-ENOENT);
goto out;
}
ret = msdos_lookup (dir, dentry);
if (ret) {
printk(KERN_WARNING
- "umsdos_rlookup_x: %s/%s failed, ret=%d\n",
- dentry->d_parent->d_name.name, dentry->d_name.name,ret);
+ "umsdos_rlookup_x: %s/%s failed, ret=%ld\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ PTR_ERR(ret));
goto out;
}
if (dentry->d_inode) {
}
-int UMSDOS_rlookup ( struct inode *dir, struct dentry *dentry)
+struct dentry *UMSDOS_rlookup ( struct inode *dir, struct dentry *dentry)
{
return umsdos_rlookup_x (dir, dentry, 0);
}
return res;
}
-int vfat_lookup(struct inode *dir,struct dentry *dentry)
+/* Find a hashed dentry for inode; NULL if there are none */
+static struct dentry *find_alias(struct inode *inode)
+{
+ struct list_head *head, *next, *tmp;
+ struct dentry *alias;
+
+ head = &inode->i_dentry;
+ next = inode->i_dentry.next;
+ while (next != head) {
+ tmp = next;
+ next = tmp->next;
+ alias = list_entry(tmp, struct dentry, d_alias);
+ if (!list_empty(&alias->d_hash))
+ return dget(alias);
+ }
+ return NULL;
+}
+
+struct dentry *vfat_lookup(struct inode *dir,struct dentry *dentry)
{
int res;
struct vfat_slot_info sinfo;
struct inode *result;
+ struct dentry *alias;
int table;
PRINTK2(("vfat_lookup: name=%s, len=%d\n",
}
PRINTK3(("vfat_lookup 4.5\n"));
if (!(result = iget(dir->i_sb,sinfo.ino)))
- return -EACCES;
+ return ERR_PTR(-EACCES);
PRINTK3(("vfat_lookup 5\n"));
if (MSDOS_I(result)->i_busy) { /* mkdir in progress */
iput(result);
table++;
goto error;
}
+ alias = find_alias(result);
+ if (alias) {
+ if (d_invalidate(alias)==0)
+ dput(alias);
+ else {
+ iput(result);
+ return alias;
+ }
+ }
PRINTK3(("vfat_lookup 6\n"));
error:
dentry->d_op = &vfat_dentry_ops[table];
return 0;
}
-/* Drop all aliases */
-static void drop_aliases(struct dentry *dentry)
-{
- struct list_head *head, *next, *tmp;
- struct dentry *alias;
-
- PRINTK1(("drop_replace_inodes: dentry=%p, inode=%p\n", dentry, inode));
- head = &dentry->d_inode->i_dentry;
- if (dentry->d_inode) {
- next = dentry->d_inode->i_dentry.next;
- while (next != head) {
- tmp = next;
- next = tmp->next;
- alias = list_entry(tmp, struct dentry, d_alias);
- if (alias == dentry)
- continue;
-
- d_drop(alias);
- }
- }
-}
-
static int vfat_rmdirx(struct inode *dir,struct dentry* dentry)
{
int res;
if (res >= 0 && sinfo.total_slots > 0) {
if (!list_empty(&dentry->d_hash))
return -EBUSY;
- /* Take care of aliases */
- if (dentry->d_inode->i_count > 1) {
- shrink_dcache_parent(dentry->d_parent);
- if (dentry->d_inode->i_count > 1)
- return -EBUSY;
- }
res = vfat_empty(dentry->d_inode);
if (res)
return res;
PRINTK1(("vfat_unlink: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
res = vfat_unlinkx (dir,dentry,1);
- if (res >= 0) {
- drop_aliases(dentry);
+ if (res >= 0)
d_delete(dentry);
- }
return res;
}
loff_t old_offset,new_offset,old_longname_offset;
int old_slots,old_ino,new_ino,dotdot_ino;
struct inode *old_inode, *new_inode, *dotdot_inode;
- struct dentry *walk;
int res, is_dir, i;
int locked = 0;
struct vfat_slot_info sinfo;
old_inode = old_dentry->d_inode;
is_dir = S_ISDIR(old_inode->i_mode);
- if (is_dir) {
- /* We can't use is_subdir() here. Even now. Arrgh. */
- for (walk=new_dentry;walk!=walk->d_parent;walk=walk->d_parent) {
- if (walk->d_inode != old_dentry->d_inode)
- continue;
- res = -EINVAL;
- goto rename_done;
- }
- }
-
fat_lock_creation(); locked = 1;
if (new_dentry->d_inode) {
}
if (is_dir) {
- /*
- * Target is a directory. No other owners will
- * be tolerated.
- */
- res = -EBUSY;
- /*
- * OK, let's try to get rid of other dentries.
- * No need to do it if i_count is 1.
- */
- if (new_inode->i_count>1) {
- shrink_dcache_parent(new_dentry->d_parent);
- if (new_inode->i_count>1)
- goto rename_done;
- }
res = vfat_empty(new_inode);
if (res)
goto rename_done;
- } else {
- drop_aliases(new_dentry);
}
res = vfat_remove_entry(new_dir,&sinfo,new_inode);
if (res)
extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode);
extern int pckbd_getkeycode(unsigned int scancode);
-extern int pckbd_pretranslate(unsigned char scancode, char raw_mode);
extern int pckbd_translate(unsigned char scancode, unsigned char *keycode,
char raw_mode);
extern char pckbd_unexpected_up(unsigned char keycode);
#define kbd_setkeycode pckbd_setkeycode
#define kbd_getkeycode pckbd_getkeycode
-#define kbd_pretranslate pckbd_pretranslate
#define kbd_translate pckbd_translate
#define kbd_unexpected_up pckbd_unexpected_up
#define kbd_leds pckbd_leds
it's return address in $28. The pv is loaded as usual.
The gp is clobbered (in the module case) as usual. */
+ /* This little bit of silliness is to get the GP loaded for
+ a function that ordinarily wouldn't. Otherwise we could
+ have it done by the macro directly, which can be optimized
+ the linker. */
+ register void *pv __asm__("$27") = __down_failed;
+
__asm__ __volatile__ (
"/* semaphore down operation */\n"
- "1: ldl_l $27,%0\n"
- " subl $27,1,$27\n"
- " mov $27,$28\n"
- " stl_c $28,%0\n"
+ "1: ldl_l $24,%1\n"
+ " subl $24,1,$24\n"
+ " mov $24,$28\n"
+ " stl_c $28,%1\n"
" beq $28,2f\n"
- " blt $27,3f\n"
+ " blt $24,3f\n"
"4: mb\n"
".section .text2,\"ax\"\n"
"2: br 1b\n"
- "3: lda $24,%0\n"
- " jsr $28,__down_failed\n"
+ "3: lda $24,%1\n"
+ " jsr $28,($27),__down_failed\n"
" ldgp $29,0($28)\n"
" br 4b\n"
".previous"
- : : "m"(sem->count)
- : "$24", "$27", "$28", "memory");
+ : "=r"(pv)
+ : "m"(sem->count), "r"(pv)
+ : "$24", "$28", "memory");
}
extern inline int down_interruptible(struct semaphore * sem)
value is in $24. */
register int ret __asm__("$24");
+ register void *pv __asm__("$27") = __down_failed_interruptible;
__asm__ __volatile__ (
"/* semaphore down interruptible operation */\n"
- "1: ldl_l $27,%1\n"
- " subl $27,1,$27\n"
- " mov $27,$28\n"
- " stl_c $28,%1\n"
+ "1: ldl_l $24,%2\n"
+ " subl $24,1,$24\n"
+ " mov $24,$28\n"
+ " stl_c $28,%2\n"
" beq $28,2f\n"
- " blt $27,3f\n"
+ " blt $24,3f\n"
" mov $31,%0\n"
"4: mb\n"
".section .text2,\"ax\"\n"
"2: br 1b\n"
- "3: lda $24,%1\n"
- " jsr $28,__down_failed_interruptible\n"
+ "3: lda $24,%2\n"
+ " jsr $28,($27),__down_failed_interruptible\n"
" ldgp $29,0($28)\n"
" br 4b\n"
".previous"
- : "=r"(ret)
- : "m"(sem->count)
- : "$27", "$28", "memory");
+ : "=r"(ret), "=r"(pv)
+ : "m"(sem->count), "r"(pv)
+ : "$28", "memory");
return ret;
}
it's return address in $28. The pv is loaded as usual.
The gp is clobbered (in the module case) as usual. */
+ register void *pv __asm__("$27") = __up_wakeup;
+
__asm__ __volatile__ (
"/* semaphore up operation */\n"
" mb\n"
- "1: ldl_l $27,%0\n"
- " addl $27,1,$27\n"
- " mov $27,$28\n"
- " stl_c $28,%0\n"
+ "1: ldl_l $24,%1\n"
+ " addl $24,1,$24\n"
+ " mov $24,$28\n"
+ " stl_c $28,%1\n"
" beq $28,2f\n"
" mb\n"
" ble $27,3f\n"
"4:\n"
".section .text2,\"ax\"\n"
"2: br 1b\n"
- "3: lda $24,%0\n"
- " jsr $28,__up_wakeup\n"
+ "3: lda $24,%1\n"
+ " jsr $28,($27),__up_wakeup\n"
" ldgp $29,0($28)\n"
" br 4b\n"
".previous"
- : : "m"(sem->count)
- : "$24", "$27", "$28", "memory");
+ : "=r"(pv)
+ : "m"(sem->count), "r"(pv)
+ : "$24", "$28", "memory");
}
#endif
: "=r"(__gu_val), "=r"(__gu_err) \
: "m"(__m(addr)), "1"(__gu_err))
-#ifdef __HAVE_CPU_BWX
+#ifdef __alpha_bwx__
/* Those lucky bastards with ev56 and later CPUs can do byte/word moves. */
#define __get_user_16(addr) \
: "=r"(__pu_err) \
: "m"(__m(addr)), "rJ"(x), "0"(__pu_err))
-#ifdef __HAVE_CPU_BWX
+#ifdef __alpha_bwx__
/* Those lucky bastards with ev56 and later CPUs can do byte/word moves. */
#define __put_user_16(x,addr) \
extern inline long
__copy_tofrom_user_nocheck(void *to, const void *from, long len)
{
+ /* This little bit of silliness is to get the GP loaded for
+ a function that ordinarily wouldn't. Otherwise we could
+ have it done by the macro directly, which can be optimized
+ the linker. */
+ register void * pv __asm__("$27") = __copy_user;
+
register void * __cu_to __asm__("$6") = to;
register const void * __cu_from __asm__("$7") = from;
register long __cu_len __asm__("$0") = len;
__asm__ __volatile__(
- "jsr $28,__copy_user"
- : "=r" (__cu_len), "=r" (__cu_from), "=r" (__cu_to)
- : "0" (__cu_len), "1" (__cu_from), "2" (__cu_to)
- : "$1","$2","$3","$4","$5","$27","$28","memory");
+ "jsr $28,(%3),__copy_user\n\tldgp $29,0($28)"
+ : "=r" (__cu_len), "=r" (__cu_from), "=r" (__cu_to), "=r"(pv)
+ : "0" (__cu_len), "1" (__cu_from), "2" (__cu_to), "3"(pv)
+ : "$1","$2","$3","$4","$5","$28","memory");
return __cu_len;
}
__copy_tofrom_user(void *to, const void *from, long len, const void *validate)
{
if (__access_ok((long)validate, len, get_fs())) {
+ register void * pv __asm__("$27") = __copy_user;
register void * __cu_to __asm__("$6") = to;
register const void * __cu_from __asm__("$7") = from;
register long __cu_len __asm__("$0") = len;
__asm__ __volatile__(
- "jsr $28,__copy_user"
- : "=r" (__cu_len), "=r" (__cu_from), "=r" (__cu_to)
- : "0" (__cu_len), "1" (__cu_from), "2" (__cu_to)
- : "$1","$2","$3","$4","$5","$27","$28","memory");
+ "jsr $28,(%3),__copy_user\n\tldgp $29,0($28)"
+ : "=r"(__cu_len), "=r"(__cu_from), "=r"(__cu_to),
+ "=r" (pv)
+ : "0" (__cu_len), "1" (__cu_from), "2" (__cu_to),
+ "3" (pv)
+ : "$1","$2","$3","$4","$5","$28","memory");
len = __cu_len;
}
return len;
extern inline long
__clear_user(void *to, long len)
{
+ /* This little bit of silliness is to get the GP loaded for
+ a function that ordinarily wouldn't. Otherwise we could
+ have it done by the macro directly, which can be optimized
+ the linker. */
+ register void * pv __asm__("$27") = __do_clear_user;
+
register void * __cl_to __asm__("$6") = to;
register long __cl_len __asm__("$0") = len;
__asm__ __volatile__(
- "jsr $28,__do_clear_user"
- : "=r"(__cl_len), "=r"(__cl_to)
- : "0"(__cl_len), "1"(__cl_to)
- : "$1","$2","$3","$4","$5","$27","$28","memory");
+ "jsr $28,(%2),__do_clear_user\n\tldgp $29,0($28)"
+ : "=r"(__cl_len), "=r"(__cl_to), "=r"(pv)
+ : "0"(__cl_len), "1"(__cl_to), "2"(pv)
+ : "$1","$2","$3","$4","$5","$28","memory");
return __cl_len;
}
clear_user(void *to, long len)
{
if (__access_ok((long)to, len, get_fs())) {
+ register void * pv __asm__("$27") = __do_clear_user;
register void * __cl_to __asm__("$6") = to;
register long __cl_len __asm__("$0") = len;
__asm__ __volatile__(
- "jsr $28,__do_clear_user"
- : "=r"(__cl_len), "=r"(__cl_to)
- : "0"(__cl_len), "1"(__cl_to)
- : "$1","$2","$3","$4","$5","$27","$28","memory");
+ "jsr $28,(%2),__do_clear_user\n\tldgp $29,0($28)"
+ : "=r"(__cl_len), "=r"(__cl_to), "=r"(pv)
+ : "0"(__cl_len), "1"(__cl_to), "2"(pv)
+ : "$1","$2","$3","$4","$5","$28","memory");
len = __cl_len;
}
return len;
#define kbd_setkeycode(sc,kc) (-EINVAL)
#define kbd_getkeycode(sc) (-EINVAL)
-/* Prototype: int kbd_pretranslate(scancode, raw_mode)
- * Returns : 0 to ignore scancode
- */
-#define kbd_pretranslate(sc,rm) (1)
-
/* Prototype: int kbd_translate(scancode, *keycode, *up_flag, raw_mode)
* Returns : 0 to ignore scancode, *keycode set to keycode, *up_flag
* set to 0200 if scancode indicates release
extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode);
extern int pckbd_getkeycode(unsigned int scancode);
-extern int pckbd_pretranslate(unsigned char scancode, char raw_mode);
extern int pckbd_translate(unsigned char scancode, unsigned char *keycode,
char raw_mode);
extern char pckbd_unexpected_up(unsigned char keycode);
#define kbd_setkeycode pckbd_setkeycode
#define kbd_getkeycode pckbd_getkeycode
-#define kbd_pretranslate pckbd_pretranslate
#define kbd_translate(sc, kcp, ufp, rm) ({ *ufp = sc & 0200; \
pckbd_translate(sc & 0x7f, kcp, rm);})
#define kbd_setkeycode(sc,kc) (-EINVAL)
#define kbd_getkeycode(sc) (-EINVAL)
-/* Prototype: int kbd_pretranslate(scancode, raw_mode)
- * Returns : 0 to ignore scancode
- */
-#define kbd_pretranslate(sc,rm) (1)
-
/* Prototype: int kbd_translate(scancode, *keycode, *up_flag, raw_mode)
* Returns : 0 to ignore scancode, *keycode set to keycode, *up_flag
* set to 0200 if scancode indicates release
#define NR_SCANCODES 128
-extern int ps2kbd_pretranslate(unsigned char scancode);
extern int ps2kbd_translate(unsigned char scancode, unsigned char *keycode_p, char *up_flag_p);
extern void ps2kbd_leds(unsigned char leds);
extern void ps2kbd_init_hw(void);
#define kbd_setkeycode(sc,kc) (-EINVAL)
#define kbd_getkeycode(sc) (-EINVAL)
-/* Prototype: int kbd_pretranslate(scancode, raw_mode)
- * Returns : 0 to ignore scancode
- */
-#define kbd_pretranslate(sc,rm) ps2kbd_pretranslate(sc)
-
/* Prototype: int kbd_translate(scancode, *keycode, *up_flag, raw_mode)
* Returns : 0 to ignore scancode, *keycode set to keycode, *up_flag
* set to 0200 if scancode indicates release
extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode);
extern int pckbd_getkeycode(unsigned int scancode);
-extern int pckbd_pretranslate(unsigned char scancode, char raw_mode);
extern int pckbd_translate(unsigned char scancode, unsigned char *keycode,
char raw_mode);
extern char pckbd_unexpected_up(unsigned char keycode);
#define kbd_setkeycode pckbd_setkeycode
#define kbd_getkeycode pckbd_getkeycode
-#define kbd_pretranslate pckbd_pretranslate
#define kbd_translate(sc, kcp, ufp, rm) ({ *ufp = sc & 0200; \
pckbd_translate(sc & 0x7f, kcp, rm);})
extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode);
extern int pckbd_getkeycode(unsigned int scancode);
-extern int pckbd_pretranslate(unsigned char scancode, char raw_mode);
extern int pckbd_translate(unsigned char scancode, unsigned char *keycode,
char raw_mode);
extern char pckbd_unexpected_up(unsigned char keycode);
#define kbd_setkeycode pckbd_setkeycode
#define kbd_getkeycode pckbd_getkeycode
-#define kbd_pretranslate pckbd_pretranslate
#define kbd_translate pckbd_translate
#define kbd_unexpected_up pckbd_unexpected_up
#define kbd_leds pckbd_leds
return scancode > 127 ? -EINVAL : scancode;
}
-static __inline__ int kbd_pretranslate(unsigned char scancode, char raw_mode)
-{
- return 1;
-}
-
static __inline__ int kbd_translate(unsigned char scancode,
unsigned char *keycode, char raw_mode)
{
extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode);
extern int pckbd_getkeycode(unsigned int scancode);
-extern int pckbd_pretranslate(unsigned char scancode, char raw_mode);
extern int pckbd_translate(unsigned char scancode, unsigned char *keycode,
char raw_mode);
extern char pckbd_unexpected_up(unsigned char keycode);
#define kbd_setkeycode pckbd_setkeycode
#define kbd_getkeycode pckbd_getkeycode
-#define kbd_pretranslate pckbd_pretranslate
#define kbd_translate pckbd_translate
#define kbd_unexpected_up pckbd_unexpected_up
#define kbd_leds pckbd_leds
extern int mackbd_setkeycode(unsigned int scancode, unsigned int keycode);
extern int mackbd_getkeycode(unsigned int scancode);
-extern int mackbd_pretranslate(unsigned char scancode, char raw_mode);
extern int mackbd_translate(unsigned char scancode, unsigned char *keycode,
char raw_mode);
extern int mackbd_unexpected_up(unsigned char keycode);
extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode);
extern int pckbd_getkeycode(unsigned int scancode);
-extern int pckbd_pretranslate(unsigned char scancode, char raw_mode);
extern int pckbd_translate(unsigned char scancode, unsigned char *keycode,
char raw_mode);
extern char pckbd_unexpected_up(unsigned char keycode);
return mackbd_getkeycode(x);
}
-static inline int kbd_pretranslate(unsigned char x,char y)
-{
- if ( is_prep || (_machine == _MACH_mbx) )
- return pckbd_pretranslate(x,y);
- else if ( is_chrp )
-#ifndef CONFIG_MAC_KEYBOARD
- return pckbd_pretranslate(x,y);
-#else
- if ( adb_hardware == ADB_NONE )
- return pckbd_pretranslate(x,y);
- else
- return mackbd_pretranslate(x,y);
-#endif
- else
- return mackbd_pretranslate(x,y);
-}
-
static inline int kbd_translate(unsigned char keycode, unsigned char *keycodep,
char raw_mode)
{
extern int pcikbd_setkeycode(unsigned int scancode, unsigned int keycode);
extern int pcikbd_getkeycode(unsigned int scancode);
-extern int pcikbd_pretranslate(unsigned char scancode, char raw_mode);
extern int pcikbd_translate(unsigned char scancode, unsigned char *keycode,
char raw_mode);
extern char pcikbd_unexpected_up(unsigned char keycode);
#define kbd_setkeycode pcikbd_setkeycode
#define kbd_getkeycode pcikbd_getkeycode
-#define kbd_pretranslate pcikbd_pretranslate
#define kbd_translate pcikbd_translate
#define kbd_unexpected_up pcikbd_unexpected_up
#define kbd_leds pcikbd_leds
extern int pcikbd_setkeycode(unsigned int scancode, unsigned int keycode);
extern int pcikbd_getkeycode(unsigned int scancode);
-extern int pcikbd_pretranslate(unsigned char scancode, char raw_mode);
extern int pcikbd_translate(unsigned char scancode, unsigned char *keycode,
char raw_mode);
extern char pcikbd_unexpected_up(unsigned char keycode);
#define kbd_setkeycode pcikbd_setkeycode
#define kbd_getkeycode pcikbd_getkeycode
-#define kbd_pretranslate pcikbd_pretranslate
#define kbd_translate pcikbd_translate
#define kbd_unexpected_up pcikbd_unexpected_up
#define kbd_leds pcikbd_leds
extern int adfs_map_lookup (struct super_block *sb, int frag_id, int offset);
/* namei.c */
-extern int adfs_lookup (struct inode *dir, struct dentry *dentry);
+extern struct dentry *adfs_lookup (struct inode *dir, struct dentry *dentry);
/* super.c */
extern int init_adfs_fs (void);
/* namei.c */
extern int affs_hash_name(const unsigned char *name, int len, int intl, int hashsize);
-extern int affs_lookup(struct inode *dir, struct dentry *dentry);
+extern struct dentry *affs_lookup(struct inode *dir, struct dentry *dentry);
extern int affs_unlink(struct inode *dir, struct dentry *dentry);
extern int affs_create(struct inode *dir, struct dentry *dentry, int mode);
extern int affs_mkdir(struct inode *dir, struct dentry *dentry, int mode);
/* namei.c */
extern void ext2_release (struct inode *, struct file *);
-extern int ext2_lookup (struct inode *, struct dentry *);
+extern struct dentry *ext2_lookup (struct inode *, struct dentry *);
extern int ext2_create (struct inode *,struct dentry *,int);
extern int ext2_mkdir (struct inode *,struct dentry *,int);
extern int ext2_rmdir (struct inode *,struct dentry *);
#define FB_TYPE_PLANES 1 /* Non interleaved planes */
#define FB_TYPE_INTERLEAVED_PLANES 2 /* Interleaved planes */
#define FB_TYPE_TEXT 3 /* Text/attributes */
+#define FB_TYPE_VGA_PLANES 4 /* EGA/VGA planes */
#define FB_AUX_TEXT_MDA 0 /* Monochrome text */
#define FB_AUX_TEXT_CGA 1 /* CGA/EGA/VGA Color text */
struct fb_info {
char modename[40]; /* default video mode */
- int node;
+ kdev_t node;
int flags;
#define FBINFO_FLAG_MODULE 1 /* Low-level driver is a module */
struct fb_ops *fbops;
return test_bit(BH_Protected, &bh->b_state);
}
-/*
- * Deprecated - we don't keep per-buffer reference flags
- * any more.
- *
- * We _could_ try to update the page reference, but that
- * doesn't seem to really be worth it either. If we did,
- * it would look something like this:
- *
- * #define buffer_page(bh) (mem_map + MAP_NR((bh)->b_data))
- * #define touch_buffer(bh) set_bit(PG_referenced, &buffer_page(bh)->flags)
- */
-#define touch_buffer(bh) do { } while (0)
+#define buffer_page(bh) (mem_map + MAP_NR((bh)->b_data))
+#define touch_buffer(bh) set_bit(PG_referenced, &buffer_page(bh)->flags)
#include <linux/pipe_fs_i.h>
#include <linux/minix_fs_i.h>
struct inode_operations {
struct file_operations * default_file_ops;
int (*create) (struct inode *,struct dentry *,int);
- int (*lookup) (struct inode *,struct dentry *);
+ struct dentry * (*lookup) (struct inode *,struct dentry *);
int (*link) (struct dentry *,struct inode *,struct dentry *);
int (*unlink) (struct inode *,struct dentry *);
int (*symlink) (struct inode *,struct dentry *,const char *);
--- /dev/null
+/*********************************************************************
+ *
+ * Filename: irda.h
+ * Version:
+ * Description:
+ * Status: Experimental.
+ * Author: Dag Brattli <dagb@cs.uit.no>
+ * Created at: Mon Mar 8 14:06:12 1999
+ * Modified at: Mon Mar 22 14:14:54 1999
+ * Modified by: Dag Brattli <dagb@cs.uit.no>
+ *
+ * Copyright (c) 1999 Dag Brattli, 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 Dag Brattli nor University of Tromsø admit liability nor
+ * provide warranty for any of this software. This material is
+ * provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#ifndef KERNEL_IRDA_H
+#define KERNEL_IRDA_H
+
+/* Hint bit positions for first hint byte */
+#define HINT_PNP 0x01
+#define HINT_PDA 0x02
+#define HINT_COMPUTER 0x04
+#define HINT_PRINTER 0x08
+#define HINT_MODEM 0x10
+#define HINT_FAX 0x20
+#define HINT_LAN 0x40
+#define HINT_EXTENSION 0x80
+
+/* Hint bit positions for second hint byte (first extension byte) */
+#define HINT_TELEPHONY 0x01
+#define HINT_FILE_SERVER 0x02
+#define HINT_COMM 0x04
+#define HINT_MESSAGE 0x08
+#define HINT_HTTP 0x10
+#define HINT_OBEX 0x20
+
+/* IrLMP character code values */
+#define CS_ASCII 0x00
+#define CS_ISO_8859_1 0x01
+#define CS_ISO_8859_2 0x02
+#define CS_ISO_8859_3 0x03
+#define CS_ISO_8859_4 0x04
+#define CS_ISO_8859_5 0x05
+#define CS_ISO_8859_6 0x06
+#define CS_ISO_8859_7 0x07
+#define CS_ISO_8859_8 0x08
+#define CS_ISO_8859_9 0x09
+#define CS_UNICODE 0xff
+
+#define SOL_IRLMP 266 /* Same as SOL_IRDA for now */
+#define SOL_IRTTP 266 /* Same as SOL_IRDA for now */
+
+#define IRLMP_ENUMDEVICES 1
+#define IRLMP_IAS_SET 2
+#define IRLMP_IAS_QUERY 3
+#define IRLMP_DISCOVERY_MASK_SET 4
+
+#define IRTTP_QOS_SET 5
+#define IRTTP_QOS_GET 6
+#define IRTTP_MAX_SDU_SIZE 7
+
+#define IAS_MAX_STRING 256
+#define IAS_MAX_OCTET_STRING 1024
+#define IAS_MAX_CLASSNAME 64
+#define IAS_MAX_ATTRIBNAME 256
+
+#define LSAP_ANY 0xff
+
+struct sockaddr_irda {
+ sa_family_t sir_family; /* AF_IRDA */
+ unsigned char sir_lsap_sel; /* LSAP/TSAP selector */
+ unsigned int sir_addr; /* Device address */
+ char sir_name[25]; /* Usually <service>:IrDA:TinyTP */
+};
+
+struct irda_device_info {
+ unsigned int saddr; /* Address of remote device */
+ unsigned int daddr; /* Link where it was discovered */
+ char info[22]; /* Description */
+ unsigned char charset; /* Charset used for description */
+ unsigned char hints[2]; /* Hint bits */
+};
+
+struct irda_device_list {
+ unsigned int len;
+ struct irda_device_info dev[0];
+};
+
+struct irda_ias_set {
+ char irda_class_name[IAS_MAX_CLASSNAME];
+ char irda_attrib_name[IAS_MAX_ATTRIBNAME];
+ unsigned int irda_attrib_type;
+ union {
+ unsigned int irda_attrib_int;
+ struct {
+ unsigned short len;
+ u_char OctetSeq[IAS_MAX_OCTET_STRING];
+ } irda_attrib_octet_seq;
+ struct {
+ unsigned char len;
+ unsigned char charset;
+ unsigned char string[IAS_MAX_STRING];
+ } irda_attrib_string;
+ } attribute;
+};
+
+#endif /* KERNEL_IRDA_H */
+
+
+
+
extern int isofs_open(struct inode * inode, struct file * filp);
extern void isofs_release(struct inode * inode, struct file * filp);
-extern int isofs_lookup(struct inode * dir, struct dentry *);
+extern struct dentry *isofs_lookup(struct inode * dir, struct dentry *);
extern unsigned long isofs_count_free_inodes(struct super_block *sb);
extern int isofs_new_block(int dev);
extern int isofs_free_block(int dev, int block);
extern struct pt_regs *kbd_pt_regs;
-void handle_scancode(unsigned char scancode);
+void handle_scancode(unsigned char scancode, int down);
#endif /* _KBD_LL_H */
#ifdef __KERNEL__
-extern int minix_lookup(struct inode * dir, struct dentry *dentry);
+extern struct dentry *minix_lookup(struct inode * dir, struct dentry *dentry);
extern int minix_create(struct inode * dir, struct dentry *dentry, int mode);
extern int minix_mkdir(struct inode * dir, struct dentry *dentry, int mode);
extern int minix_rmdir(struct inode * dir, struct dentry *dentry);
/* msdos.c - these are for Umsdos */
extern void msdos_read_inode(struct inode *inode);
-extern int msdos_lookup(struct inode *dir,struct dentry *);
+extern struct dentry *msdos_lookup(struct inode *dir,struct dentry *);
extern int msdos_create(struct inode *dir,struct dentry *dentry,int mode);
extern int msdos_rmdir(struct inode *dir,struct dentry *dentry);
extern int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode);
extern struct super_block *vfat_read_super(struct super_block *sb,void *data,
int silent);
extern void vfat_read_inode(struct inode *inode);
-extern int vfat_lookup(struct inode *dir,struct dentry *);
+extern struct dentry *vfat_lookup(struct inode *dir,struct dentry *);
/* vfat/vfatfs_syms.c */
extern struct file_system_type vfat_fs_type;
* of the /proc/<pid> subdirectories.
*/
extern int proc_readdir(struct file *, void *, filldir_t);
-extern int proc_lookup(struct inode *, struct dentry *);
+extern struct dentry *proc_lookup(struct inode *, struct dentry *);
struct openpromfs_dev {
struct openpromfs_dev *next;
#define QNX4DEBUG(X) (void) 0
#endif
-extern int qnx4_lookup(struct inode *dir, struct dentry *dentry);
+extern struct dentry *qnx4_lookup(struct inode *dir, struct dentry *dentry);
extern unsigned long qnx4_count_free_inodes(struct super_block *sb);
extern unsigned long qnx4_count_free_blocks(struct super_block *sb);
CTL_PROC=4, /* Process info */
CTL_FS=5, /* Filesystems */
CTL_DEBUG=6, /* Debugging */
- CTL_DEV=7 /* Devices */
+ CTL_DEV=7, /* Devices */
+ CTL_BUS=8 /* Buses */
};
+/* CTL_BUS names: */
+enum
+{
+ BUS_ISA=1 /* ISA */
+};
/* CTL_KERN names: */
enum
NET_IPV6=12,
NET_X25=13,
NET_TR=14,
- NET_DECNET=15
+ NET_DECNET=15,
+ NET_ECONET=16
};
+/* /proc/sys/bus/isa */
+enum
+{
+ BUS_ISA_MEM_BASE=1,
+ BUS_ISA_PORT_BASE=2,
+ BUS_ISA_PORT_SHIFT=3
+};
/* /proc/sys/net/core */
enum
* Function prototypes
*/
-extern int sysv_lookup(struct inode * dir, struct dentry * dentry);
+extern struct dentry *sysv_lookup(struct inode * dir, struct dentry * dentry);
extern int sysv_create(struct inode * dir, struct dentry * dentry, int mode);
extern int sysv_mkdir(struct inode * dir, struct dentry * dentry, int mode);
extern int sysv_rmdir(struct inode * dir, struct dentry * dentry);
extern struct buffer_head * ufs_bread (struct inode *, unsigned, int, int *);
/* namei.c */
-extern int ufs_lookup (struct inode *, struct dentry *);
+extern struct dentry *ufs_lookup (struct inode *, struct dentry *);
extern int ufs_mkdir(struct inode *, struct dentry *, int);
extern int ufs_rmdir (struct inode *, struct dentry *);
extern int ufs_unlink (struct inode *, struct dentry *);
char * umsdos_d_path(struct dentry *, char *, int);
void umsdos_lookup_patch_new(struct dentry *, struct umsdos_info *);
int umsdos_is_pseudodos (struct inode *dir, struct dentry *dentry);
-int umsdos_lookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo);
-int UMSDOS_lookup(struct inode *, struct dentry *);
+struct dentry *umsdos_lookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo);
+struct dentry *UMSDOS_lookup(struct inode *, struct dentry *);
struct dentry *umsdos_lookup_dentry(struct dentry *, char *, int, int);
struct dentry *umsdos_covered(struct dentry *, char *, int);
struct dentry *new_dentry);
/* rdir.c 22/03/95 03.31.42 */
-int umsdos_rlookup_x (struct inode *dir, struct dentry *dentry, int nopseudo);
-int UMSDOS_rlookup (struct inode *dir, struct dentry *dentry);
+struct dentry *umsdos_rlookup_x (struct inode *dir, struct dentry *dentry, int nopseudo);
+struct dentry *UMSDOS_rlookup (struct inode *dir, struct dentry *dentry);
/* symlink.c 23/01/95 03.38.30 */
/*
* This file define a set of standard wireless extensions
*
- * Version : 6 18.2.99
+ * Version : 7 23.4.99
*
* Authors : Jean Tourrilhes - HPLB - <jt@hplb.hpl.hp.com>
*/
*
* V5 to V6
* --------
- * - 802.11 support
+ * - 802.11 support (ESSID ioctls)
+ *
+ * V6 to V7
+ * --------
+ * - define IW_ESSID_MAX_SIZE and IW_MAX_AP
*/
/* -------------------------- IOCTL LIST -------------------------- */
/* As the ESSID is a string up to 32 bytes long, it doesn't fit within the
* 'iwreq' structure, so we need to use the 'data' member to point to a
* string in user space, like it is done for RANGE...
+ * The "flags" member indicate if the ESSID is active or not.
*/
/* ------------------------- IOCTL STUFF ------------------------- */
/* Maximum of address that you may set with SPY */
#define IW_MAX_SPY 8
+/* Maximum of address that you may get in the
+ list of access points in range */
+#define IW_MAX_AP 8
+
+/* Maximum size of the ESSID string */
+#define IW_ESSID_MAX_SIZE 32
+
/****************************** TYPES ******************************/
/* --------------------------- SUBTYPES --------------------------- */
caddr_t pointer; /* Pointer to the data
* (in user space) */
__u16 length; /* fields or byte size */
- __u16 flags; /* Unused */
+ __u16 flags; /* Optional params */
} data;
} u;
};
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Tue Apr 6 16:53:53 1999
- * Modified at: Tue Apr 6 20:44:35 1999
+ * Modified at: Thu Apr 22 11:04:56 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
#include <net/irda/irqueue.h>
#define DISCOVERY_EXPIRE_TIMEOUT 6*HZ
+#define DISCOVERY_DEFAULT_SLOTS 0
/*
* The DISCOVERY structure is used for both discovery requests and responses
#include <net/irda/irmod.h>
typedef enum {
- COMM_DISCOVERY,
COMM_IDLE,
+
+ COMM_DISCOVERY_WAIT,
+ COMM_QUERYPARAM_WAIT,
+ COMM_QUERYLSAP_WAIT,
+
COMM_WAITI,
COMM_WAITR,
COMM_CONN,
IRCOMM_DATA_REQUEST,
LMP_DATA_INDICATION,
IRCOMM_CONTROL_REQUEST,
+
+ DISCOVERY_INDICATION,
+ GOT_PARAMETERS,
+ GOT_LSAPSEL,
+ QUERYIAS_ERROR,
+
} IRCOMM_EVENT;
typedef enum {
__u32 daddr; /* Device address of the peer device */
__u32 saddr;
+ __u32 skey;
+ __u32 ckey;
+ int queryias_lock;
int ias_type;
int disconnect_priority; /* P_NORMAL or P_HIGH. see irttp.h */
struct notify_t notify; /* container of callbacks */
__u8 peer_port_type;
__u8 servicetype;
+ __u8 peer_servicetype;
__u8 data_format;
__u8 peer_data_format;
__u8 flow_ctrl;
-int ircomm_query_ias_and_connect(struct ircomm_cb *self, __u8 servicetype);
-void ircomm_connect_request(struct ircomm_cb *self);
+void ircomm_connect_request(struct ircomm_cb *self, __u8 servicetype);
void ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata,
__u32 maxsdusize);
void ircomm_disconnect_request(struct ircomm_cb *self,
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Tue Dec 9 21:13:12 1997
- * Modified at: Tue Apr 6 20:31:08 1999
+ * Modified at: Wed Apr 21 17:49:00 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli, All Rights Reserved.
#define ALIGN __attribute__((aligned))
#define PACK __attribute__((packed))
-/* use 0 for production, 1 for verification, >2 for debug */
+
#ifdef CONFIG_IRDA_DEBUG
extern __u32 irda_debug;
-#define IRDA_DEBUG 0
+/* use 0 for production, 1 for verification, >2 for debug */
+#define IRDA_DEBUG_LEVEL 0
-#define DEBUG(n, args...) if (irda_debug >= (n)) printk( KERN_DEBUG args)
+#define DEBUG(n, args...) if (irda_debug >= (n)) printk(KERN_DEBUG args)
#define ASSERT(expr, func) \
if(!(expr)) { \
printk( "Assertion failed! %s,%s,%s,line=%d\n",\
#define ASSERT(expr, func)
#endif /* CONFIG_IRDA_DEBUG */
+#define WARNING(args...) printk(KERN_WARNING args)
+#define MESSAGE(args...) printk(KERN_INFO args)
+#define ERROR(args...) printk(KERN_ERR args)
+
+#define MSECS_TO_JIFFIES(ms) (ms*HZ/1000)
+
/*
* Magic numbers used by Linux/IR. Random numbers which must be unique to
* give the best protection
int nslots; /* Number of slots to use for discovery */
+ int errno;
+
struct sock *sk;
struct wait_queue *ias_wait; /* Wait for LM-IAS answer */
* Status: Experimental.
* Author: Haris Zukanovic <haris@stud.cs.uit.no>
* Created at: Tue Apr 14 12:41:42 1998
- * Modified at: Wed Apr 7 17:17:16 1999
+ * Modified at: Tue Apr 20 11:06:28 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Haris Zukanovic, <haris@stud.cs.uit.no>
#include <net/irda/irda.h>
#include <net/irda/qos.h>
#include <net/irda/irqueue.h>
+#include <net/irda/irlap_frame.h>
/* Some non-standard interface flags (should not conflict with any in if.h) */
-#define IFF_SIR 0x01 /* Supports SIR speeds */
-#define IFF_MIR 0x02 /* Supports MIR speeds */
-#define IFF_FIR 0x04 /* Supports FIR speeds */
-#define IFF_PIO 0x08 /* Supports PIO transfer of data */
-#define IFF_DMA 0x10 /* Supports DMA transfer of data */
-#define IFF_SHM 0x20 /* Supports shared memory data transfers */
-#define IFF_DONGLE 0x40 /* Interface has a dongle attached */
+#define IFF_SIR 0x0001 /* Supports SIR speeds */
+#define IFF_MIR 0x0002 /* Supports MIR speeds */
+#define IFF_FIR 0x0004 /* Supports FIR speeds */
+#define IFF_VFIR 0x0008 /* Supports VFIR speeds */
+#define IFF_PIO 0x0010 /* Supports PIO transfer of data */
+#define IFF_DMA 0x0020 /* Supports DMA transfer of data */
+#define IFF_SHM 0x0040 /* Supports shared memory data transfers */
+#define IFF_DONGLE 0x0080 /* Interface has a dongle attached */
+#define IFF_AIR 0x0100 /* Supports A(dvanced)IR standards */
#define IO_XMIT 0x01
#define IO_RECV 0x02
int dongle_id; /* Dongle or transceiver currently used */
};
-/* Buffer specific info */
+/* IO buffer specific info (inspired by struct sk_buff) */
struct iobuff_t {
int state; /* Receiving state (transmit state not used) */
int in_frame; /* True if receiving frame */
- __u8 *data; /* the buffer */
- __u8 *head; /* start of data in buffer */
+ __u8 *head; /* start of buffer */
+ __u8 *data; /* start of data in buffer */
__u8 *tail; /* end of data in buffer */
- int offset; /* Usually data + offset = head */
- int len; /* currently used bytes in buffer */
- int truesize; /* total size of the data area */
+ int len; /* length of data */
+ int truesize; /* total size of buffer */
__u16 fcs;
int flags; /* Allocation flags (GFP_KERNEL | GFP_DMA ) */
* stuff from IrDA port implementations.
*/
struct irda_device {
- QUEUE q; /* Must be first */
+ QUEUE q; /* Must be first */
int magic; /* Our magic bullet */
char name[16]; /* Name of device "irda0" */
struct iobuff_t tx_buff;
struct iobuff_t rx_buff;
- int xbofs;
- int media_busy;
/* spinlock_t lock; */ /* For serializing operations */
/* Media busy stuff */
+ int media_busy;
struct timer_list media_busy_timer;
- /* Driver specific implementation */
+ /* Callbacks for driver specific implementation */
void (*change_speed)(struct irda_device *driver, int baud);
- int (*is_receiving)(struct irda_device *); /* receiving? */
+ int (*is_receiving)(struct irda_device *); /* receiving? */
/* int (*is_tbusy)(struct irda_device *); */ /* transmitting? */
void (*wait_until_sent)(struct irda_device *);
+ void (*set_caddr)(struct irda_device *); /* Set connection addr */
};
extern hashbin_t *irda_device;
int irda_device_setup(struct device *dev);
-inline unsigned short irda_get_mtt(struct sk_buff *skb);
-
void setup_dma(int channel, char *buffer, int count, int mode);
+/*
+ * Function irda_get_mtt (skb)
+ *
+ * Utility function for getting the minimum turnaround time out of
+ * the skb, where it has been hidden in the cb field.
+ */
+inline static __u16 irda_get_mtt(struct sk_buff *skb)
+{
+ __u16 mtt;
+
+ if (((struct irlap_skb_cb *)(skb->cb))->magic != LAP_MAGIC)
+ mtt = 10000;
+ else
+ mtt = ((struct irlap_skb_cb *)(skb->cb))->mtt;
+
+ ASSERT(mtt <= 10000, return 10000;);
+
+ return mtt;
+}
+
#endif
+
+
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Thu Aug 21 00:02:07 1997
- * Modified at: Mon Mar 22 13:15:04 1999
+ * Modified at: Wed Apr 21 16:37:21 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1997 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
#define IAS_SUCCESS 0
#define IAS_CLASS_UNKNOWN 1
#define IAS_ATTRIB_UNKNOWN 2
+#define IAS_DISCONNECT 10
-typedef void (*CONFIRM_CALLBACK)( __u16 obj_id, struct ias_value *value,
- void *priv);
+typedef void (*CONFIRM_CALLBACK)(int result, __u16 obj_id,
+ struct ias_value *value, void *priv);
struct iriap_cb {
QUEUE queue; /* Must be first */
+++ /dev/null
-/*********************************************************************
- *
- * Filename: irkbd.h
- * Version: 0.2
- * Description: IrDA Keyboard/Mouse driver (Tekram IR-660)
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Mon Mar 1 00:24:19 1999
- * Modified at: Thu Mar 11 14:54:00 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1999 Dag Brattli, 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 Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef IRKBD_H
-#define IRKBD_H
-
-/* Some commands */
-#define IRKBD_CMD_INIT_KBD 0xfe
-#define IRKBD_CMD_INIT_MOUSE 0xff
-#define IRKBD_CMD_ENABLE 0x41
-#define IRKBD_CMD_LED 0x31
-#define IRKBD_CMD_KDB_SPEED 0x33
-
-/* Some responses */
-#define IRKBD_RSP_KBDOK 0x11
-#define IRKBD_RSP_KBDERR 0x12
-#define IRKBD_RSP_MSOK 0x21
-#define IRKBD_RSP_MSERR 0x22
-#define IRKBD_RSP_LEDOK 0x31
-#define IRKBD_RSP_KBDSPEEDOK 0x33
-#define IRKBD_RSP_RSPN41 0x41
-
-#define IRKBD_RATE 2 /* Polling rate, should be 15 ms */
-#define IRKBD_TIMEOUT 100 /* 1000 ms */
-
-#define SUBFRAME_MASK 0xc0
-#define SUBFRAME_MOUSE 0x80
-#define SUBFRAME_KEYBOARD 0x40
-#define SUBFRAME_RESPONSE 0x00
-
-#define IRKBD_MAX_HEADER (TTP_HEADER+LMP_HEADER+LAP_HEADER)
-
-#define IRKBD_BUF_SIZE 4096 /* Must be power of 2! */
-
-enum {
- IRKBD_IDLE, /* Not connected */
- IRKBD_INIT_KBD, /* Initializing keyboard */
- IRKBD_INIT_MOUSE, /* Initializing mouse */
- IRKBD_POLLING, /* Polling device */
-};
-
-/* Main structure */
-struct irkbd_cb {
- struct miscdevice dev;
- char devname[9]; /* name of the registered device */
- int state;
-
- int count; /* Open count */
-
- __u32 saddr; /* my local address */
- __u32 daddr; /* peer address */
-
- struct tsap_cb *tsap;
- __u8 dtsap_sel; /* remote TSAP address */
- __u8 stsap_sel; /* local TSAP address */
-
- struct timer_list watchdog_timer;
-
- LOCAL_FLOW tx_flow;
- LOCAL_FLOW rx_flow;
-
- __u8 scancodes[IRKBD_BUF_SIZE]; /* Buffer for mouse events */
- int head;
- int tail;
-
- struct wait_queue *read_wait;
- struct fasync_struct *async;
-};
-
-#endif /* IRKBD_H */
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Aug 31 20:14:37 1997
- * Modified at: Thu Feb 25 21:05:53 1999
+ * Modified at: Thu Apr 22 14:13:34 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
#include <linux/skbuff.h>
#include <linux/netdevice.h>
+#include <net/irda/irias_object.h>
#include <net/irda/irlan_event.h>
void irlan_client_start_kick_timer(struct irlan_cb *self, int timeout);
void irlan_client_open_ctrl_tsap( struct irlan_cb *self);
-void irlan_client_extract_params(struct irlan_cb *self, struct sk_buff *skb);
-void irlan_client_get_value_confirm( __u16 obj_id, struct ias_value *value,
- void *priv);
+void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb);
+void irlan_client_get_value_confirm(int result, __u16 obj_id,
+ struct ias_value *value, void *priv);
#endif
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Aug 31 20:14:37 1997
- * Modified at: Tue Apr 6 16:19:41 1999
+ * Modified at: Thu Apr 22 14:30:37 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
int broadcast_open;
struct timer_list kick_timer;
- int start_new_provider;
};
/*
struct irlan_cb *irlan_open(__u32 saddr, __u32 daddr, int netdev);
void irlan_close(struct irlan_cb *self);
+void irlan_close_tsaps(struct irlan_cb *self);
+void irlan_mod_inc_use_count(void);
+void irlan_mod_dec_use_count(void);
+
int irlan_register_netdev(struct irlan_cb *self);
void irlan_ias_register(struct irlan_cb *self, __u8 tsap_sel);
void irlan_start_watchdog_timer(struct irlan_cb *self, int timeout);
int irlan_insert_array_param(struct sk_buff *skb, char *name, __u8 *value,
__u16 value_len);
-int irlan_get_param(__u8 *buf, char *name, char *value, __u16 *len);
+int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len);
void print_ret_code(__u8 code);
extern hashbin_t *irlan;
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Thu Oct 15 08:36:58 1998
- * Modified at: Mon Mar 22 12:57:11 1999
+ * Modified at: Thu Apr 22 14:09:37 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli, All Rights Reserved.
#ifndef IRLAN_ETH_H
#define IRLAN_ETH_H
+int irlan_eth_init(struct device *dev);
+int irlan_eth_open(struct device *dev);
+int irlan_eth_close(struct device *dev);
int irlan_eth_receive(void *instance, void *sap, struct sk_buff *skb);
int irlan_eth_xmit(struct sk_buff *skb, struct device *dev);
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Aug 31 20:14:37 1997
- * Modified at: Mon Mar 8 15:32:35 1999
+ * Modified at: Thu Apr 22 14:29:16 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
void irlan_provider_connect_response(struct irlan_cb *, struct tsap_cb *);
int irlan_parse_open_data_cmd(struct irlan_cb *self, struct sk_buff *skb);
-int irlan_provider_extract_params(struct irlan_cb *self, int cmd,
- struct sk_buff *skb);
+int irlan_provider_parse_command(struct irlan_cb *self, int cmd,
+ struct sk_buff *skb);
void irlan_provider_send_reply(struct irlan_cb *self, int command,
int ret_code);
-void irlan_provider_open_ctrl_tsap( struct irlan_cb *self);
+int irlan_provider_open_ctrl_tsap(struct irlan_cb *self);
#endif
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Mon Aug 4 20:40:53 1997
- * Modified at: Fri Mar 26 15:15:17 1999
+ * Modified at: Fri Apr 23 09:51:15 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
int irlap_generate_rand_time_slot( int S, int s);
void irlap_initiate_connection_state( struct irlap_cb *);
-void irlap_flush_all_queues( struct irlap_cb *);
-void irlap_change_speed( struct irlap_cb *, int);
-void irlap_wait_min_turn_around( struct irlap_cb *, struct qos_info *);
+void irlap_flush_all_queues(struct irlap_cb *);
+void irlap_change_speed(struct irlap_cb *, int);
+void irlap_wait_min_turn_around(struct irlap_cb *, struct qos_info *);
-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 *, struct qos_info *);
+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 *, struct qos_info *);
#endif
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Tue Aug 19 10:27:26 1997
- * Modified at: Fri Mar 26 14:10:53 1999
+ * Modified at: Fri Apr 23 09:33:55 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
int vr; /* next frame to receive */
};
-__inline__ void irlap_insert_mtt( 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 *);
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Aug 17 20:54:32 1997
- * Modified at: Tue Apr 6 20:05:14 1999
+ * Modified at: Fri Apr 23 09:15:07 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
void irlmp_register_link(struct irlap_cb *, __u32 saddr, struct notify_t *);
void irlmp_unregister_link(__u32 saddr);
-int irlmp_connect_request( struct lsap_cb *, __u8 dlsap_sel,
- __u32 saddr, __u32 daddr,
- struct qos_info *, struct sk_buff *);
-void irlmp_connect_indication( struct lsap_cb *self, struct sk_buff *skb);
-void irlmp_connect_response( struct lsap_cb *, struct sk_buff *);
-void irlmp_connect_confirm( struct lsap_cb *, struct sk_buff *);
+int irlmp_connect_request(struct lsap_cb *, __u8 dlsap_sel,
+ __u32 saddr, __u32 daddr,
+ struct qos_info *, struct sk_buff *);
+void irlmp_connect_indication(struct lsap_cb *self, struct sk_buff *skb);
+void irlmp_connect_response(struct lsap_cb *, struct sk_buff *);
+void irlmp_connect_confirm(struct lsap_cb *, struct sk_buff *);
struct lsap_cb *irlmp_dup(struct lsap_cb *self, void *instance);
void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason,
void irlmp_do_discovery(int nslots);
discovery_t *irlmp_get_discovery_response(void);
-void irlmp_data_request( struct lsap_cb *, struct sk_buff *);
-void irlmp_udata_request( struct lsap_cb *, struct sk_buff *);
-void irlmp_data_indication( struct lsap_cb *, struct sk_buff *);
-void irlmp_udata_indication( struct lsap_cb *, struct sk_buff *);
+void irlmp_data_request(struct lsap_cb *, struct sk_buff *);
+inline void irlmp_udata_request(struct lsap_cb *, struct sk_buff *);
+inline void irlmp_data_indication(struct lsap_cb *, struct sk_buff *);
+inline void irlmp_udata_indication(struct lsap_cb *, struct sk_buff *);
void irlmp_status_request(void);
-void irlmp_status_indication( LINK_STATUS link, LOCK_STATUS lock);
+void irlmp_status_indication(LINK_STATUS link, LOCK_STATUS lock);
-int irlmp_slsap_inuse( __u8 slsap);
+int irlmp_slsap_inuse(__u8 slsap);
__u8 irlmp_find_free_slsap(void);
-LM_REASON irlmp_convert_lap_reason( LAP_REASON);
+LM_REASON irlmp_convert_lap_reason(LAP_REASON);
__u32 irlmp_get_saddr(struct lsap_cb *self);
__u32 irlmp_get_daddr(struct lsap_cb *self);
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sat Feb 21 18:54:38 1998
- * Modified at: Mon Jan 11 15:58:16 1999
+ * Modified at: Wed Apr 21 16:46:26 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998, Thomas Davis, <ratbert@radiks.net>
* if it's static, it doesn't go in here.
*/
-void irlpt_client_get_value_confirm(__u16 obj_id,
+void irlpt_client_get_value_confirm(int result, __u16 obj_id,
struct ias_value *value,
void *priv);
void irlpt_client_connect_indication( void *instance,
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Mon Dec 15 13:58:52 1997
- * Modified at: Tue Mar 16 22:27:41 1999
+ * Modified at: Fri Apr 9 11:13:39 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli, All Rights Reserved.
int irmod_init_module(void);
void irmod_cleanup_module(void);
-inline int irda_lock(int *lock);
+/*
+ * Function irda_lock (lock)
+ *
+ * Lock variable. Returns false if the lock is already set.
+ *
+ */
+static inline int irda_lock(int *lock)
+{
+ if (test_and_set_bit( 0, (void *) lock)) {
+ DEBUG(3, __FUNCTION__
+ "(), Trying to lock, already locked variable!\n");
+ return FALSE;
+ }
+ return TRUE;
+}
+
inline int irda_unlock(int *lock);
void irda_notify_init(struct notify_t *notify);
+++ /dev/null
-/*********************************************************************
- *
- * Filename: irobex.h
- * Version: 0.8
- * Description:
- * Status: Experimental.
- * Author: Dag Brattli <dagb@cs.uit.no>
- * Created at: Sat Jul 4 22:43:57 1998
- * Modified at: Thu Mar 11 16:11:54 1999
- * Modified by: Dag Brattli <dagb@cs.uit.no>
- *
- * Copyright (c) 1998 Dag Brattli, 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 Dag Brattli nor University of Tromsø admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#ifndef IROBEX_H
-#define IROBEX_H
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/miscdevice.h>
-
-#include <net/irda/timer.h>
-#include <net/irda/qos.h>
-#include <net/irda/irmod.h>
-
-#define LOW_THRESHOLD 4
-#define HIGH_THRESHOLD 8
-#define IROBEX_MAX_QUEUE 12
-
-/* Small structure to be used by the IOCTL call */
-struct irobex_ioc_t {
- __u32 daddr;
-};
-
-#define IROBEX_IOC_MAGIC 'k'
-
-#define IROBEX_IOCSCONNECT _IOW(IROBEX_IOC_MAGIC, 1, 4)
-#define IROBEX_IOCSDISCONNECT _IOW(IROBEX_IOC_MAGIC, 2, 4)
-#define IROBEX_IOC_MAXNR 2
-
-#define IROBEX_MAX_HEADER (TTP_HEADER+LMP_HEADER+LAP_HEADER)
-
-typedef enum {
- OBEX_IDLE, /* Doing nothing */
- OBEX_DISCOVER, /* Trying to discovery remote device */
- OBEX_QUERY, /* Querying remote LM-IAS */
- OBEX_CONN, /* Trying to connect to remote device */
- OBEX_DATA, /* Data transfer ready */
-} OBEX_STATE;
-
-struct irobex_cb {
- QUEUE queue; /* Must be first! */
-
- int magic; /* magic used to detect corruption of the struct */
-
- OBEX_STATE state; /* Current state */
-
- __u32 saddr; /* my local address */
- __u32 daddr; /* peer address */
- unsigned long time_discovered;
-
- __u32 ckey; /* IrLMP client handle */
- __u32 skey; /* IrLMP service handle */
-
- char devname[9]; /* name of the registered device */
- struct tsap_cb *tsap;
- int eof;
-
- __u8 dtsap_sel; /* remote TSAP address */
- __u8 stsap_sel; /* local TSAP address */
-
- int irlap_data_size;
-
- struct miscdevice dev;
-
- int count; /* open count */
-
- struct sk_buff_head rx_queue; /* Receive queue */
-
- struct wait_queue *read_wait;
- struct wait_queue *write_wait;
-
- struct fasync_struct *async;
-
- struct timer_list watchdog_timer;
-
- LOCAL_FLOW tx_flow;
- LOCAL_FLOW rx_flow;
-};
-
-int irobex_init(void);
-
-void irobex_watchdog_timer_expired( unsigned long data);
-
-inline void irobex_start_watchdog_timer( struct irobex_cb *self, int timeout)
-{
- irda_start_timer( &self->watchdog_timer, timeout, (unsigned long) self,
- irobex_watchdog_timer_expired);
-}
-
-extern struct irobex_cb *irobex;
-
-#endif
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Aug 31 20:14:31 1997
- * Modified at: Mon Mar 22 13:17:30 1999
+ * Modified at: Sat Apr 10 10:19:56 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
__u32 rx_max_sdu_size; /* Max receive user data size */
int tx_sdu_busy; /* TxSdu.busy */
- int tx_max_sdu_size; /* Max transmit user data size */
+ __u32 tx_max_sdu_size; /* Max transmit user data size */
int close_pend; /* Close, but disconnect_pend */
int disconnect_pend; /* Disconnect, but still data to send */
extern void u14_34f_setup(char *str, int *ints);
extern void fdomain_setup(char *str, int *ints);
extern void ibmmca_scsi_setup(char *str, int *ints);
+extern void fd_mcs_setup(char *str, int *ints);
extern void in2000_setup(char *str, int *ints);
extern void NCR53c406a_setup(char *str, int *ints);
extern void sym53c416_setup(char *str, int *ints);
#ifdef CONFIG_SCSI_IBMMCA
{ "ibmmcascsi=", ibmmca_scsi_setup },
#endif
+#ifdef CONFIG_SCSI_FD_MCS
+ { "fd_mcs=", fd_mcs_setup },
+#endif
#if defined(CONFIG_SCSI_DC390T) && ! defined(CONFIG_SCSI_DC390T_NOGENSUPP)
{ "tmscsim=", dc390_setup },
#endif
#include <linux/trdevice.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
-#include <linux/string.h>
#include <linux/timer.h>
#include <linux/net.h>
#include <linux/proc_fs.h>
rif_cache rif_table[RIF_TABLE_SIZE]={ NULL, };
+static spinlock_t rif_lock = SPIN_LOCK_UNLOCKED;
+
#define RIF_TIMEOUT 60*10*HZ
#define RIF_CHECK_INTERVAL 60*HZ
unsigned int hash;
rif_cache entry;
unsigned char *olddata;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rif_lock, flags);
/*
* Broadcasts are single route as stated in RFC 1042
else
slack = 18 - ((ntohs(trh->rcf) & TR_RCF_LEN_MASK)>>8);
olddata = skb->data;
+ spin_unlock_irqrestore(&rif_lock, flags);
+
skb_pull(skb, slack);
memmove(skb->data, olddata, sizeof(struct trh_hdr) - slack);
}
int i;
unsigned int hash, rii_p = 0;
rif_cache entry;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rif_lock, flags);
+
/*
* Firstly see if the entry exists
*/
if(!entry)
{
printk(KERN_DEBUG "tr.c: Couldn't malloc rif cache entry !\n");
+ spin_unlock_irqrestore(&rif_lock, flags);
return;
}
}
entry->last_used=jiffies;
}
+ spin_unlock_irqrestore(&rif_lock, flags);
}
/*
int i;
unsigned long now=jiffies,flags;
- save_flags(flags);
- cli();
-
+ spin_lock_irqsave(&rif_lock, flags);
+
for(i=0; i < RIF_TABLE_SIZE;i++)
{
rif_cache entry, *pentry=rif_table+i;
pentry=&entry->next;
}
}
- restore_flags(flags);
+
+ spin_unlock_irqrestore(&rif_lock, flags);
/*
* Reset the timer
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun May 31 10:12:43 1998
- * Modified at: Wed Apr 7 17:32:27 1999
+ * Modified at: Thu Apr 22 12:08:04 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
* Sources: af_netroom.c, af_ax25.c, af_rose.c, af_x25.c etc.
*
#include <net/irda/iriap.h>
#include <net/irda/irias_object.h>
#include <net/irda/irttp.h>
+#include <net/irda/discovery.h>
extern int irda_init(void);
extern void irda_cleanup(void);
* Got answer from remote LM-IAS
*
*/
-static void irda_get_value_confirm(__u16 obj_id, struct ias_value *value,
- void *priv)
+static void irda_get_value_confirm(int result, __u16 obj_id,
+ struct ias_value *value, void *priv)
{
struct irda_sock *self;
return;
/* Check if request succeeded */
- if (!value) {
+ if (result != IAS_SUCCESS) {
DEBUG(0, __FUNCTION__ "(), IAS query failed!\n");
+ self->errno = result;
+
/* Wake up any processes waiting for result */
wake_up_interruptible(&self->ias_wait);
+
return;
}
self->mask = 0xffff;
self->rx_flow = self->tx_flow = FLOW_START;
self->max_sdu_size_rx = SAR_DISABLE; /* Default value */
- self->nslots = 6; /* Default for now */
+ self->nslots = DISCOVERY_DEFAULT_SLOTS;
/* Notify that we are using the irda module, so nobody removes it */
irda_mod_inc_use_count();
return copied;
}
+/*
+ * Function irda_shutdown (sk, how)
+ *
+ *
+ *
+ */
static int irda_shutdown( struct socket *sk, int how)
{
DEBUG( 0, __FUNCTION__ "()\n");
}
+/*
+ * Function irda_poll (file, sock, wait)
+ *
+ *
+ *
+ */
unsigned int irda_poll(struct file *file, struct socket *sock,
struct poll_table_struct *wait)
{
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Tue Apr 6 15:33:50 1999
- * Modified at: Tue Apr 6 20:26:46 1999
+ * Modified at: Sun Apr 11 00:41:58 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
{
discovery_t *old;
- DEBUG(2, __FUNCTION__ "()\n");
+ DEBUG(4, __FUNCTION__ "()\n");
/* Check if we have discovered this device before */
old = hashbin_remove(cachelog, discovery->daddr, NULL);
{
discovery_t *discovery;
- DEBUG(2, __FUNCTION__ "()\n");
+ DEBUG(4, __FUNCTION__ "()\n");
/*
* If log is missing this means that IrLAP was unable to perform the
{
discovery_t *discovery, *curr;
- DEBUG(3, __FUNCTION__ "()\n");
+ DEBUG(4, __FUNCTION__ "()\n");
discovery = (discovery_t *) hashbin_get_first(log);
while (discovery != NULL) {
#include <net/irda/ircomm_common.h>
-static char *revision_date = "Tue Mar 2 02:03:58 1999";
+static char *revision_date = "Sun Apr 18 00:40:19 1999";
-static void ircomm_state_discovery(struct ircomm_cb *self,
- IRCOMM_EVENT event, struct sk_buff *skb );
static void ircomm_state_idle( struct ircomm_cb *self, IRCOMM_EVENT event,
struct sk_buff *skb );
+
+static void ircomm_state_discoverywait( struct ircomm_cb *self, IRCOMM_EVENT event,
+ struct sk_buff *skb );
+
+static void ircomm_state_queryparamwait( struct ircomm_cb *self, IRCOMM_EVENT event,
+ struct sk_buff *skb );
+
+static void ircomm_state_querylsapwait( struct ircomm_cb *self, IRCOMM_EVENT event,
+ struct sk_buff *skb );
+
static void ircomm_state_waiti( struct ircomm_cb *self, IRCOMM_EVENT event,
struct sk_buff *skb );
static void ircomm_state_waitr( struct ircomm_cb *self, IRCOMM_EVENT event,
static int ircomm_proc_read(char *buf, char **start, off_t offset,
int len, int unused);
+static void start_discovering(struct ircomm_cb *self);
static void query_lsapsel(struct ircomm_cb * self);
-static void ircomm_getvalue_confirm( __u16 obj_id, struct ias_value *value,
- void *priv);
+static void query_parameters(struct ircomm_cb *self);
+static void queryias_done(struct ircomm_cb *self);
+static void ircomm_getvalue_confirm(int result, __u16 obj_id,
+ struct ias_value *value, void *priv);
-static __u32 ckey;
-static __u32 skey;
struct ircomm_cb *discovering_instance;
/*
static char *ircommstate[] = {
- "DISCOVERY",
"IDLE",
+
+ "DISCOVERY_WAIT",
+ "QUERYPARAM_WAIT",
+ "QUERYLSAP_WAIT",
+
"WAITI",
"WAITR",
"CONN",
"IRCOMM_DATA_REQUEST",
"LMP_DATA_INDICATION",
"IRCOMM_CONTROL_REQUEST",
+
+ "DISCOVERY_INDICATION",
+ "GOT_PARAMETERS",
+ "GOT_LSAPSEL",
+ "QUERYIAS_ERROR",
};
#ifdef CONFIG_PROC_FS
static void (*state[])( struct ircomm_cb *self, IRCOMM_EVENT event,
struct sk_buff *skb) =
{
- ircomm_state_discovery,
ircomm_state_idle,
+
+ ircomm_state_discoverywait,
+ ircomm_state_queryparamwait,
+ ircomm_state_querylsapwait,
+
ircomm_state_waiti,
ircomm_state_waitr,
ircomm_state_conn,
}
+
+/*
+ * ircomm_discovery_indication()
+ * Remote device is discovered, try query the remote IAS to see which
+ * device it is, and which services it has.
+ */
+
+static void ircomm_discovery_indication(discovery_t *discovery)
+{
+ struct ircomm_cb *self;
+
+ self = discovering_instance;
+ if(self == NULL)
+ return;
+ ASSERT(self->magic == IRCOMM_MAGIC, return;);
+
+ self->daddr = discovery->daddr;
+ self->saddr = discovery->saddr;
+
+ DEBUG( 0, __FUNCTION__"():daddr=%08x\n", self->daddr);
+
+ ircomm_do_event(self, DISCOVERY_INDICATION, NULL);
+ return;
+}
+
+/*
+ * ircomm_getvalue_confirm()
+ * handler for iriap_getvaluebyclass_request()
+ */
+static void ircomm_getvalue_confirm(int result, __u16 obj_id,
+ struct ias_value *value, void *priv)
+{
+ struct ircomm_cb *self = (struct ircomm_cb *) priv;
+ struct sk_buff *skb= NULL;
+ __u8 *frame;
+ __u8 servicetype = 0 ;
+ ASSERT( self != NULL, return;);
+ ASSERT( self->magic == IRCOMM_MAGIC, return;);
+
+ /* Check if request succeeded */
+ if (result != IAS_SUCCESS) {
+ DEBUG( 0, __FUNCTION__ "(), got NULL value!\n");
+ ircomm_do_event(self, QUERYIAS_ERROR, NULL);
+ return;
+ }
+
+ DEBUG(4, __FUNCTION__"():type(%d)\n", value->type);
+
+ self->ias_type = value->type;
+ switch(value->type){
+ case IAS_OCT_SEQ:
+
+ DEBUG(4, __FUNCTION__"():got octet sequence:\n");
+#if 0
+ {
+ int i;
+ for ( i=0;i<value->len;i++)
+ printk("%02x",
+ (__u8)(*(value->t.oct_seq + i)));
+ printk("\n");
+ }
+#endif
+ skb = dev_alloc_skb((value->len) + 2);
+ ASSERT(skb != NULL, ircomm_do_event(self, QUERYIAS_ERROR, NULL);return;);
+ frame = skb_put(skb,2);
+ /* MSB first */
+ frame[0] = ( value->len >> 8 ) & 0xff;
+ frame[1] = value->len & 0xff;
+
+ frame = skb_put(skb,value->len);
+ memcpy(frame, value->t.oct_seq, value->len);
+ ircomm_parse_tuples(self, skb, IAS_PARAM);
+ kfree_skb(skb);
+
+ /*
+ * check if servicetype we want is available
+ */
+
+ DEBUG(0,__FUNCTION__"():peer capability is:\n");
+ DEBUG(0,"3wire raw: %s\n",
+ ((self->peer_servicetype & THREE_WIRE_RAW) ? "yes":"no"));
+ DEBUG(0,"3wire : %s\n",
+ ((self->peer_servicetype & THREE_WIRE) ? "yes":"no"));
+ DEBUG(0,"9wire : %s\n",
+ ((self->peer_servicetype & NINE_WIRE) ? "yes":"no"));
+ DEBUG(0,"IEEE1284 : %s\n",
+ ((self->peer_servicetype & CENTRONICS) ? "yes":"no"));
+
+ self->servicetype &= self->peer_servicetype;
+ if(!(self->servicetype)){
+ DEBUG(0,__FUNCTION__"(): servicetype mismatch!\n");
+ ircomm_do_event(self, QUERYIAS_ERROR, NULL);
+ break;
+ }
+
+ /*
+ * then choose better one
+ */
+ if(self->servicetype & THREE_WIRE_RAW)
+ servicetype = THREE_WIRE_RAW;
+ if(self->servicetype & THREE_WIRE)
+ servicetype = THREE_WIRE;
+ if(self->servicetype & NINE_WIRE)
+ servicetype = NINE_WIRE;
+ if(self->servicetype & CENTRONICS)
+ servicetype = CENTRONICS;
+
+ self->servicetype = servicetype;
+
+ /* enter next state */
+ ircomm_do_event(self, GOT_PARAMETERS, NULL);
+ break;
+
+ case IAS_INTEGER:
+ /* LsapSel seems to be sent to me */
+ DEBUG(0, __FUNCTION__"():got lsapsel = %d\n", value->t.integer);
+
+ if ( value->t.integer == -1){
+ DEBUG( 0, __FUNCTION__"():invalid value!\n");
+ ircomm_do_event(self, QUERYIAS_ERROR, NULL);
+ return;
+ }
+ self->dlsap = value->t.integer;
+ ircomm_do_event(self, GOT_LSAPSEL, NULL);
+ break;
+
+ case IAS_MISSING:
+ DEBUG( 0, __FUNCTION__":got IAS_MISSING\n");
+ ircomm_do_event(self, QUERYIAS_ERROR, NULL);
+ break;
+
+ default:
+ DEBUG( 0, __FUNCTION__":got unknown (strange?)type!\n");
+ ircomm_do_event(self, QUERYIAS_ERROR, NULL);
+ break;
+ }
+}
+
+
+
/*
* ----------------------------------------------------------------------
* Impl. of actions (descrived in section 7.4 of the reference)
}
-/*
- * we currently need dummy (discovering) state for debugging,
- * which state is not defined in the reference.
- */
-
-static void ircomm_state_discovery( struct ircomm_cb *self,
- IRCOMM_EVENT event, struct sk_buff *skb )
-{
- printk(KERN_ERR __FUNCTION__"():why call me? something is wrong..\n");
- if(skb)
- dev_kfree_skb( skb);
-}
-
/*
* ircomm_state_idle
switch(event){
case IRCOMM_CONNECT_REQUEST:
- ircomm_next_state(self, COMM_WAITI);
- issue_connect_request( self, skb );
+ /* ircomm_next_state(self, COMM_WAITI); */
+ /* issue_connect_request( self, skb ); */
+
+ ircomm_next_state(self, COMM_DISCOVERY_WAIT);
+ start_discovering(self);
break;
case TTP_CONNECT_INDICATION:
}
}
+/*
+ * ircomm_state_discoverywait
+ */
+static void ircomm_state_discoverywait(struct ircomm_cb *self, IRCOMM_EVENT event,
+ struct sk_buff *skb )
+{
+ switch(event){
+
+ case TTP_CONNECT_INDICATION:
+
+ ircomm_next_state(self, COMM_WAITR);
+ queryias_done(self);
+ connect_indication( self, self->qos, skb);
+ break;
+
+ case DISCOVERY_INDICATION:
+ ircomm_next_state(self, COMM_QUERYPARAM_WAIT);
+ query_parameters(self);
+ break;
+
+ case IRCOMM_DISCONNECT_REQUEST:
+ ircomm_next_state(self, COMM_IDLE);
+ queryias_done(self);
+ break;
+
+ case QUERYIAS_ERROR:
+ ircomm_next_state(self, COMM_IDLE);
+ disconnect_indication(self, NULL);
+ queryias_done(self);
+ break;
+
+ default:
+ DEBUG(0,__FUNCTION__"():unknown event =%d(%s)\n",
+ event, ircommevent[event]);
+ }
+}
+
+/*
+ * ircomm_state_queryparamwait
+ */
+
+static void ircomm_state_queryparamwait(struct ircomm_cb *self, IRCOMM_EVENT event,
+ struct sk_buff *skb )
+{
+ switch(event){
+
+ case TTP_CONNECT_INDICATION:
+
+ ircomm_next_state(self, COMM_WAITR);
+ connect_indication( self, self->qos, skb);
+ break;
+
+ case GOT_PARAMETERS:
+
+ ircomm_next_state(self, COMM_QUERYLSAP_WAIT);
+ query_lsapsel( self );
+ break;
+
+ case IRCOMM_DISCONNECT_REQUEST:
+ ircomm_next_state(self, COMM_IDLE);
+ queryias_done(self);
+ break;
+
+ case QUERYIAS_ERROR:
+ ircomm_next_state(self, COMM_IDLE);
+ disconnect_indication(self, NULL);
+ queryias_done(self);
+ break;
+
+ default:
+ DEBUG(0,__FUNCTION__"():unknown event =%d(%s)\n",
+ event, ircommevent[event]);
+ }
+}
+
+/*
+ * ircomm_state_querylsapwait
+ */
+
+static void ircomm_state_querylsapwait(struct ircomm_cb *self, IRCOMM_EVENT event,
+ struct sk_buff *skb )
+{
+ switch(event){
+
+ case TTP_CONNECT_INDICATION:
+
+ ircomm_next_state(self, COMM_WAITR);
+ connect_indication( self, self->qos, skb);
+ break;
+
+ case GOT_LSAPSEL:
+
+ ircomm_next_state(self, COMM_WAITI);
+ queryias_done(self);
+ issue_connect_request( self, skb );
+ break;
+
+ case IRCOMM_DISCONNECT_REQUEST:
+ ircomm_next_state(self, COMM_IDLE);
+ queryias_done(self);
+ break;
+
+ case QUERYIAS_ERROR:
+ ircomm_next_state(self, COMM_IDLE);
+ disconnect_indication(self, NULL);
+ queryias_done(self);
+ break;
+
+
+ default:
+ DEBUG(0,__FUNCTION__"():unknown event =%d(%s)\n",
+ event, ircommevent[event]);
+ }
+}
+
/*
* ircomm_state_waiti
*/
case IRCOMM_DISCONNECT_REQUEST:
ircomm_next_state(self, COMM_IDLE);
issue_disconnect_request(self, skb);
+ queryias_done(self);
break;
case TTP_DISCONNECT_INDICATION:
disconnect_indication(self, skb);
break;
+ case DISCOVERY_INDICATION:
+ DEBUG(0, __FUNCTION__"():DISCOVERY_INDICATION\n");
+ queryias_done(self);
+ break;
+ case GOT_PARAMETERS:
+ DEBUG(0, __FUNCTION__"():GOT_PARAMETERS\n");
+ queryias_done(self);
+ break;
+ case GOT_LSAPSEL:
+ DEBUG(0, __FUNCTION__"():GOT_LSAPSEL\n");
+ queryias_done(self);
+ break;
+
/* case LMP_DISCONNECT_INDICATION: */
/* disconnect_indication(); */
/* ircomm_next_state(self, COMM_IDLE); */
case IRCOMM_DISCONNECT_REQUEST:
ircomm_next_state(self, COMM_IDLE);
issue_disconnect_request(self, skb);
+ queryias_done(self);
break;
/* case LM_DISCONNECT_INDICATION: */
/* disconnect_indication(); */
/* ircomm_next_state(self, COMM_IDLE); */
/* break; */
+
+ case DISCOVERY_INDICATION:
+ DEBUG(0, __FUNCTION__"():DISCOVERY_INDICATION\n");
+ queryias_done(self);
+ break;
+ case GOT_PARAMETERS:
+ DEBUG(0, __FUNCTION__"():GOT_PARAMETERS\n");
+ queryias_done(self);
+ break;
+ case GOT_LSAPSEL:
+ DEBUG(0, __FUNCTION__"():GOT_LSAPSEL\n");
+ queryias_done(self);
+ break;
+
default:
DEBUG(0,"ircomm_state_conn:unknown event =%d(%s)\n",
event, ircommevent[event]);
}
+
/*
* ----------------------------------------------------------------------
* IrCOMM service interfaces and supporting functions
* ----------------------------------------------------------------------
*/
-int ircomm_query_ias_and_connect(struct ircomm_cb *self, __u8 servicetype)
-{
- int retval=0;
- __u16 hints;
-
- ASSERT( self != NULL, return -EFAULT;);
- ASSERT( self->magic == IRCOMM_MAGIC, return -EFAULT;);
- DEBUG(4,__FUNCTION__"():servicetype = %d\n",servicetype);
+/*
+ * start_discovering()
+ *
+ * start discovering and enter DISCOVERY_WAIT state
+ */
- /*
- * wait if another instance is discovering now
- */
- if(discovering_instance){
- interruptible_sleep_on( &self->discovery_wait);
- if(signal_pending(current)){
- return -EINTR; /* cought a signal */
- }
- if(self->state == COMM_CONN)
- return 0; /* got connected */
- }
- ASSERT(discovering_instance == NULL, return -EFAULT;);
- discovering_instance = self;
+static void start_discovering(struct ircomm_cb *self)
+{
+ __u16 hints;
+ ASSERT( self != NULL, return;);
+ ASSERT( self->magic == IRCOMM_MAGIC, return;);
+ DEBUG(4,__FUNCTION__"():servicetype = %d\n",self->servicetype);
- /*
- * start discovering
- */
+
hints = irlmp_service_to_hint(S_COMM);
DEBUG(0,__FUNCTION__"():start discovering..\n");
switch (ircomm_cs) {
case 0:
- skey = irlmp_register_service(hints);
- ckey = irlmp_register_client(hints, ircomm_discovery_indication,
+ MOD_INC_USE_COUNT;
+ self->queryias_lock = 1;
+ discovering_instance = self;
+ self->skey = irlmp_register_service(hints);
+ self->ckey = irlmp_register_client(hints, ircomm_discovery_indication,
NULL);
break;
case 1: /* client only */
+ MOD_INC_USE_COUNT;
+ self->queryias_lock = 1;
+ discovering_instance = self;
DEBUG( 0, __FUNCTION__"():client only mode\n");
- ckey = irlmp_register_client(hints, ircomm_discovery_indication,
+ self->ckey = irlmp_register_client(hints, ircomm_discovery_indication,
NULL);
break;
case 2: /* server only */
default:
DEBUG( 0, __FUNCTION__"():server only mode\n");
- skey = irlmp_register_service(hints);
+ self->skey = irlmp_register_service(hints);
discovering_instance = NULL;
- return 0;
+ break;
}
+
+ return;
+}
+/*
+ * queryias_done(self)
+ *
+ * called when discovery process got wrong results, completed, or terminated.
+ */
- /*
- * waiting for discovery
- */
- interruptible_sleep_on( &self->discovery_wait);
- if(signal_pending(current)){
- retval = -EINTR; goto bailout; /* cought a signal */
+static void queryias_done(struct ircomm_cb *self)
+{
+ DEBUG(0, __FUNCTION__"():\n");
+ if(self->queryias_lock){
+ self->queryias_lock = 0;
+ discovering_instance = NULL;
+ MOD_DEC_USE_COUNT;
+ irlmp_unregister_client(self->ckey);
}
- if(self->state == COMM_CONN)
- goto bailout; /* got connected */
+ if(ircomm_cs != 1)
+ irlmp_unregister_service(self->skey);
+ return;
+}
- /*
- * query Parameters field of IAS and waiting for answer
- */
- self->servicetype = 0;
+
+static void query_parameters(struct ircomm_cb *self)
+{
+
DEBUG(0, __FUNCTION__"():querying IAS: Parameters..\n");
iriap_getvaluebyclass_request( "IrDA:IrCOMM", "Parameters",
self->saddr, self->daddr,
ircomm_getvalue_confirm, self );
-
- interruptible_sleep_on( &self->ias_wait);
- if(signal_pending(current)){
- retval = -EINTR; goto bailout; /* cought a signal */
- }
- if(self->state == COMM_CONN)
- goto bailout; /* got connected */
+}
- /* really got Parameters field? */
- if(self->ias_type != IAS_OCT_SEQ){
- retval = -EFAULT;
- goto bailout;
- }
-
- /*
- * check if servicetype we want is available
- */
- self->peer_cap = self->servicetype;
-
- DEBUG(0,__FUNCTION__"():peer capability is:\n");
- DEBUG(0,"3wire raw: %s\n",((self->servicetype & THREE_WIRE_RAW) ? "yes":"no"));
- DEBUG(0,"3wire : %s\n",((self->servicetype & THREE_WIRE) ? "yes":"no"));
- DEBUG(0,"9wire : %s\n",((self->servicetype & NINE_WIRE) ? "yes":"no"));
- DEBUG(0,"IEEE1284 : %s\n",((self->servicetype & CENTRONICS) ? "yes":"no"));
-
- self->servicetype &= servicetype;
- if(!(self->servicetype)){
- retval = -ENODEV;
- goto bailout;
- }
-
- /*
- * then choose better one
- */
+static void query_lsapsel(struct ircomm_cb * self)
+{
+ DEBUG(0, __FUNCTION__"():querying IAS: Lsapsel...\n");
- if(self->servicetype & THREE_WIRE_RAW)
- servicetype = THREE_WIRE_RAW;
- if(self->servicetype & THREE_WIRE)
- servicetype = THREE_WIRE;
- if(self->servicetype & NINE_WIRE)
- servicetype = NINE_WIRE;
- if(self->servicetype & CENTRONICS)
- servicetype = CENTRONICS;
-
- self->servicetype = servicetype;
-#if 1
- /*
- * waiting for discovery again
- */
- interruptible_sleep_on( &self->discovery_wait);
- if(signal_pending(current)){
- retval = -EINTR; goto bailout; /* cought a signal */
- }
- if(self->state == COMM_CONN)
- goto bailout; /* got connected */
-#endif
- /*
- * query lsapsel field and waiting for answer
- */
- query_lsapsel(self);
- interruptible_sleep_on( &self->ias_wait);
- if(signal_pending(current)){
- retval = -EINTR; goto bailout; /* cought a signal */
- }
- if(self->state == COMM_CONN)
- goto bailout; /* got connected */
-
- /* really got Lsapsel field? */
- if(self->ias_type != IAS_INTEGER){
- retval = -EFAULT;
- goto bailout;
- }
-#if 1
- /*
- * waiting for discovery again...
- */
- interruptible_sleep_on( &self->discovery_wait);
- if(signal_pending(current)){
- retval = -EINTR; goto bailout; /* cought a signal */
+ if (!(self->servicetype & THREE_WIRE_RAW)) {
+ iriap_getvaluebyclass_request(
+ "IrDA:IrCOMM", "IrDA:TinyTP:LsapSel",
+ self->saddr, self->daddr,
+ ircomm_getvalue_confirm, self );
+ } else {
+ DEBUG(0, __FUNCTION__ "THREE_WIRE_RAW is not implemented!\n");
}
- if(self->state == COMM_CONN)
- goto bailout; /* got connected */
-#endif
-
- /* succeed! ready to connect */
- discovering_instance = NULL;
- ircomm_connect_request(self);
- return 0;
-
- bailout:
- /* failed. not ready to connect */
- discovering_instance = NULL;
- irlmp_unregister_service(skey);
- irlmp_unregister_client(ckey);
- return retval;
}
/*
*/
-void ircomm_connect_request(struct ircomm_cb *self)
+void ircomm_connect_request(struct ircomm_cb *self, __u8 servicetype)
{
/*
* TODO:build a packet which contains "initial control parameters"
DEBUG(0, __FUNCTION__"():sending connect_request...\n");
- ircomm_control_request(self, SERVICETYPE); /*servictype*/
+ self->servicetype= servicetype;
+ /* ircomm_control_request(self, SERVICETYPE); */ /*servictype*/
self->maxsdusize = SAR_DISABLE;
ircomm_do_event( self, IRCOMM_CONNECT_REQUEST, NULL);
ASSERT( self->magic == IRCOMM_MAGIC, return;);
DEBUG(0,__FUNCTION__"()\n");
+#if 0
/* unregister layer */
switch (ircomm_cs) {
case 1: /* client only */
irlmp_unregister_service(skey);
break;
}
+#endif
self->disconnect_priority = priority;
if(priority != P_HIGH)
break;
case SERVICETYPE:
- self->servicetype = data[2];
+ self->peer_servicetype = data[2];
break;
case PORT_TYPE:
-/*
- * ircomm_getvalue_confirm()
- * handler for iriap_getvaluebyclass_request()
- */
-
-static void ircomm_getvalue_confirm( __u16 obj_id, struct ias_value *value,
- void *priv)
-{
- struct ircomm_cb *self = (struct ircomm_cb *) priv;
- struct sk_buff *skb= NULL;
- __u8 *frame;
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == IRCOMM_MAGIC, return;);
-
- /* Check if request succeeded */
- if ( !value) {
- DEBUG( 0, __FUNCTION__ "(), got NULL value!\n");
- return;
- }
-
- DEBUG(4, __FUNCTION__"():type(%d)\n", value->type);
-
- self->ias_type = value->type;
- switch(value->type){
- case IAS_OCT_SEQ:
-
- DEBUG(4, __FUNCTION__"():got octet sequence:\n");
-#if 0
- {
- int i;
- for ( i=0;i<value->len;i++)
- printk("%02x",
- (__u8)(*(value->t.oct_seq + i)));
- printk("\n");
- }
-#endif
- skb = dev_alloc_skb((value->len) + 2);
- ASSERT(skb != NULL, return;);
- frame = skb_put(skb,2);
- /* MSB first */
- frame[0] = ( value->len >> 8 ) & 0xff;
- frame[1] = value->len & 0xff;
-
- frame = skb_put(skb,value->len);
- memcpy(frame, value->t.oct_seq, value->len);
- ircomm_parse_tuples(self, skb, IAS_PARAM);
- kfree_skb(skb);
-
- wake_up_interruptible( &self->ias_wait);
- break;
-
- case IAS_INTEGER:
- /* LsapSel seems to be sent to me */
- DEBUG(0, __FUNCTION__"():got lsapsel = %d\n", value->t.integer);
-
- if ( value->t.integer == -1){
- DEBUG( 0, __FUNCTION__"():invalid value!\n");
- return;
- }
-
- if(self->state == COMM_IDLE){
- self->dlsap = value->t.integer;
-
- wake_up_interruptible( &self->ias_wait);
- }
- break;
-
- case IAS_MISSING:
- DEBUG( 0, __FUNCTION__":got IAS_MISSING\n");
- break;
-
- default:
- DEBUG( 0, __FUNCTION__":got unknown (strange?)type!\n");
- break;
- }
-}
-
-
-/*
- * query_lsapsel()
- * quering the remote IAS to ask which
- * dlsap we should use
- */
-
-static void query_lsapsel(struct ircomm_cb * self)
-{
- DEBUG(0, __FUNCTION__"():querying IAS: Lsapsel...\n");
-
- if (!(self->servicetype & THREE_WIRE_RAW)) {
- iriap_getvaluebyclass_request(
- "IrDA:IrCOMM", "IrDA:TinyTP:LsapSel",
- self->saddr, self->daddr,
- ircomm_getvalue_confirm, self );
- } else {
- DEBUG(0, __FUNCTION__ "THREE_WIRE_RAW is not implemented!\n");
- }
-}
-
-/*
- * ircomm_discovery_indication()
- * Remote device is discovered, try query the remote IAS to see which
- * device it is, and which services it has.
- */
-
-static void ircomm_discovery_indication(discovery_t *discovery)
-{
- struct ircomm_cb *self;
-
- self = discovering_instance;
- ASSERT(self != NULL, return;);
- ASSERT(self->magic == IRCOMM_MAGIC, return;);
-
- self->daddr = discovery->daddr;
- self->saddr = discovery->saddr;
-
- DEBUG( 0, __FUNCTION__"():daddr=%08x\n", self->daddr);
-
- wake_up_interruptible( &self->discovery_wait);
- return;
-}
-
-
struct ircomm_cb * ircomm_open_instance( struct notify_t client_notify)
{
int i;
static int irvtd_refcount;
struct irvtd_cb **irvtd = NULL;
-static char *revision_date = "Wed Mar 10 15:33:03 1999";
+static char *revision_date = "Sun Apr 18 17:31:53 1999";
/*
void irvtd_start(struct tty_struct *tty);
void irvtd_hangup(struct tty_struct *tty);
void irvtd_flush_buffer(struct tty_struct *tty);
+void irvtd_flush_chars(struct tty_struct *tty);
static void change_speed(struct irvtd_cb *driver);
static void irvtd_write_to_tty( struct irvtd_cb *);
int *eof, void *unused);
-/*
- * ----------------------------------------------------------------------
- *
- *
-
- * ----------------------------------------------------------------------
- */
-
/*
**********************************************************************
*
struct sk_buff *skb;
struct tty_struct *tty = driver->tty;
+ if(driver->rx_disable)
+ return;
+
skb = skb_dequeue(&driver->rxbuff);
if(skb == NULL)
return; /* there's nothing */
-
/*
* we should parse controlchannel field here.
* (see process_data() in ircomm.c)
irttp_flow_request(driver->comm->tsap, FLOW_STOP);
driver->ttp_stoprx = 1;
}
+ irvtd_write_to_tty(driver);
return 0;
}
driver->timer.data = (unsigned long) driver;
driver->timer.function = &irvtd_timer_expired;
- driver->timer.expires = jiffies + (HZ / 20); /* 50msec */
+ driver->timer.expires = jiffies + (HZ / 5); /* 200msec */
add_timer( &driver->timer);
}
ASSERT(driver->magic == IRVTD_MAGIC,return;);
DEBUG(4, __FUNCTION__"()\n");
- if(!(driver->tty->hw_stopped) && !(driver->tx_disable))
- irvtd_send_data_request(driver);
+ irvtd_send_data_request(driver);
+
+ irvtd_write_to_tty(driver);
- if(!(driver->rx_disable)){
- irvtd_write_to_tty(driver);
- }
-
/* start our timer again and again */
irvtd_start_timer(driver);
}
ASSERT(skb != NULL,return;);
DEBUG(4, __FUNCTION__"()\n");
+ if(driver->tty->hw_stopped || driver->tx_disable)
+ return;
if(!skb->len)
return; /* no data to send */
}
#endif
- DEBUG(4, __FUNCTION__"():sending %d bytes\n",(int)skb->len );
+ DEBUG(1, __FUNCTION__"():sending %d octets\n",(int)skb->len );
driver->icount.tx += skb->len;
err = ircomm_data_request(driver->comm, driver->txbuff);
if (err){
/* allocate a new frame */
skb = driver->txbuff = dev_alloc_skb(driver->comm->max_txbuff_size);
if (skb == NULL){
- printk(__FUNCTION__"():flush_txbuff():alloc_skb failed!\n");
+ printk(__FUNCTION__"():alloc_skb failed!\n");
} else {
skb_reserve(skb, COMM_HEADER_SIZE);
}
/*
* sending initial control parameters here
*/
-#if 1
if(driver->comm->servicetype == THREE_WIRE_RAW)
return; /* do nothing */
+ driver->comm->dte |= (MCR_DTR | MCR_RTS | DELTA_DTR | DELTA_RTS);
+
ircomm_control_request(driver->comm, SERVICETYPE);
ircomm_control_request(driver->comm, DATA_RATE);
ircomm_control_request(driver->comm, DATA_FORMAT);
break;
default:
}
-#endif
driver->tx_disable = 0;
wake_up_interruptible(&driver->open_wait);
DEBUG(4,"irvtd_connect_indication:sending connect_response...\n");
- /*TODO: connect_response should send initialcontrolparameters! TH*/
-
ircomm_connect_response(comm, NULL, SAR_DISABLE );
+ driver->tx_disable = 0;
+
/*
- * set default value
+ * send initial control parameters
*/
+ if(driver->comm->servicetype == THREE_WIRE_RAW)
+ return; /* do nothing */
+
+ driver->comm->dte |= (MCR_DTR | MCR_RTS | DELTA_DTR | DELTA_RTS);
+
+ switch(driver->comm->servicetype){
+ case NINE_WIRE:
+ ircomm_control_request(driver->comm, DTELINE_STATE);
+ break;
+ default:
+ }
+
+
driver->msr |= (MSR_DCD|MSR_RI|MSR_DSR|MSR_CTS);
- driver->tx_disable = 0;
wake_up_interruptible(&driver->open_wait);
}
DEBUG(4,"irvtd_disconnect_indication:\n");
driver->tx_disable = 1;
- driver->disconnect_pend = 1;
+ if(skb_queue_empty(&driver->rxbuff)){
+ /* disconnect */
+ driver->rx_disable = 1;
+ tty_hangup(driver->tty);
+ } else {
+ driver->disconnect_pend = 1;
+ }
}
/*
}
break;
+ case FLOW_CONTROL:
+ case DATA_RATE:
+ case XON_XOFF_CHAR:
+ case DTELINE_STATE:
+ /* (maybe) nothing to do */
+ break;
default:
DEBUG(0,__FUNCTION__"():PI = 0x%02x is not implemented\n",
(int)driver->comm->pi);
while (1) {
current->state = TASK_INTERRUPTIBLE;
- if (driver->comm->state == COMM_CONN){
- /*
- * signal DTR and RTS
- */
- driver->comm->dte = driver->mcr |= (MCR_DTR |
- MCR_RTS |
- DELTA_DTR|
- DELTA_RTS );
-
- ircomm_control_request(driver->comm, DTELINE_STATE);
- }
-
-
if (tty_hung_up_p(filp) ||
!(driver->flags & ASYNC_INITIALIZED)) {
#ifdef DO_RESTART
static int irvtd_startup(struct irvtd_cb *driver)
{
- int retval = 0;
struct ias_object* obj;
struct notify_t irvtd_notify;
irias_insert_object( obj);
driver->flags |= ASYNC_INITIALIZED;
+
/*
* discover a peer device
* TODO: other servicetype(i.e. 3wire,3wireraw) support
*/
- retval = ircomm_query_ias_and_connect(driver->comm, NINE_WIRE);
- if(retval){
- DEBUG(0, __FUNCTION__"(): ircomm_query_ias returns %d\n",
- retval);
- return retval;
- }
-
+ ircomm_connect_request(driver->comm, NINE_WIRE);
+
/*
* TODO:we have to initialize control-channel here!
* i.e.set something into RTS,CTS and so on....
ASSERT(driver->magic == IRVTD_MAGIC, return;);
ASSERT(driver->comm != NULL, return;);
- DEBUG(0, __FUNCTION__"():\n");
+ DEBUG(1, __FUNCTION__"():\n");
if(!tty->closing)
return; /* nothing to do */
orig_jiffies = jiffies;
while (driver->comm->tsap->disconnect_pend) {
- DEBUG(0, __FUNCTION__"():wait..\n");
+ DEBUG(1, __FUNCTION__"():wait..\n");
current->state = TASK_INTERRUPTIBLE;
current->counter = 0; /* make us low-priority */
schedule_timeout(HZ); /* 1sec */
if (!(driver->flags & ASYNC_INITIALIZED))
return;
- DEBUG(0,__FUNCTION__"()\n");
+ DEBUG(1,__FUNCTION__"()\n");
/*
* This comment is written in serial.c:
int line;
unsigned long flags;
- DEBUG(0, __FUNCTION__"():refcount= %d\n",irvtd_refcount);
+ DEBUG(1, __FUNCTION__"():refcount= %d\n",irvtd_refcount);
ASSERT(driver != NULL, return;);
ASSERT(driver->magic == IRVTD_MAGIC, return;);
* upper tty layer caught a HUP signal and called irvtd_hangup()
* before. so we do nothing here.
*/
- DEBUG(0, __FUNCTION__"():tty_hung_up_p.\n");
+ DEBUG(1, __FUNCTION__"():tty_hung_up_p.\n");
MOD_DEC_USE_COUNT;
restore_flags(flags);
return;
DEBUG(4, __FUNCTION__"()\n");
-
save_flags(flags);
while(1){
cli();
wrote += c;
count -= c;
buf += c;
+ irvtd_send_data_request(driver);
}
restore_flags(flags);
return (wrote);
}
+void irvtd_flush_chars(struct tty_struct *tty)
+{
+ struct irvtd_cb *driver = (struct irvtd_cb *)tty->driver_data;
+ ASSERT( driver != NULL, return;);
+ ASSERT( driver->magic == IRVTD_MAGIC, return;);
+
+ DEBUG(4, __FUNCTION__"()\n");
+ irvtd_send_data_request(driver);
+}
+
+
/*
* Function irvtd_put_char (tty, ch)
*
static void irvtd_send_xchar(struct tty_struct *tty, char ch){
- DEBUG(0, __FUNCTION__"():\n");
+ DEBUG(1, __FUNCTION__"():\n");
irvtd_put_char(tty, ch);
}
void irvtd_throttle(struct tty_struct *tty){
struct irvtd_cb *driver = (struct irvtd_cb *)tty->driver_data;
- DEBUG(0, "irvtd_throttle:\n");
+ DEBUG(1, "irvtd_throttle:\n");
if (I_IXOFF(tty))
irvtd_put_char(tty, STOP_CHAR(tty));
void irvtd_unthrottle(struct tty_struct *tty){
struct irvtd_cb *driver = (struct irvtd_cb *)tty->driver_data;
- DEBUG(0, "irvtd_unthrottle:\n");
+ DEBUG(1, "irvtd_unthrottle:\n");
if (I_IXOFF(tty))
irvtd_put_char(tty, START_CHAR(tty));
irvtd_drv.close = irvtd_close;
irvtd_drv.write = irvtd_write;
irvtd_drv.put_char = irvtd_put_char;
- irvtd_drv.flush_chars = NULL;
+ irvtd_drv.flush_chars = irvtd_flush_chars;
irvtd_drv.write_room = irvtd_write_room;
irvtd_drv.chars_in_buffer = irvtd_chars_in_buffer;
irvtd_drv.flush_buffer = irvtd_flush_buffer;
memset( irvtd[i], 0, sizeof(struct irvtd_cb));
irvtd[i]->magic = IRVTD_MAGIC;
irvtd[i]->line = i;
- irvtd[i]->closing_wait = 30*HZ ;
+ irvtd[i]->closing_wait = 10*HZ ;
irvtd[i]->close_delay = 5*HZ/10 ;
}
/*********************************************************************
*
* Filename: irda_device.c
- * Version: 0.4
+ * Version: 0.5
* Description: Abstract device driver layer and helper functions
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Wed Sep 2 20:22:08 1998
- * Modified at: Wed Apr 7 17:16:54 1999
+ * Modified at: Wed Apr 21 09:48:19 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli, All Rights Reserved.
/* Allocate memory if needed */
if (self->rx_buff.truesize > 0) {
- self->rx_buff.data = ( __u8 *) kmalloc(self->rx_buff.truesize,
+ self->rx_buff.head = ( __u8 *) kmalloc(self->rx_buff.truesize,
self->rx_buff.flags);
- if (self->rx_buff.data == NULL)
+ if (self->rx_buff.head == NULL)
return -ENOMEM;
- memset(self->rx_buff.data, 0, self->rx_buff.truesize);
+ memset(self->rx_buff.head, 0, self->rx_buff.truesize);
}
if (self->tx_buff.truesize > 0) {
- self->tx_buff.data = ( __u8 *) kmalloc(self->tx_buff.truesize,
+ self->tx_buff.head = ( __u8 *) kmalloc(self->tx_buff.truesize,
self->tx_buff.flags);
- if (self->tx_buff.data == NULL)
+ if (self->tx_buff.head == NULL)
return -ENOMEM;
- memset(self->tx_buff.data, 0, self->tx_buff.truesize);
+ memset(self->tx_buff.head, 0, self->tx_buff.truesize);
}
self->magic = IRDA_DEVICE_MAGIC;
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;
/* Initialize timers */
init_timer(&self->media_busy_timer);
/* Open network device */
dev_open(&self->netdev);
- printk("IrDA device %s registered.\n", self->name);
+ MESSAGE("IrDA: Registred device %s\n", self->name);
irda_device_set_media_busy(self, FALSE);
/* It's now safe to initilize the saddr */
memcpy(self->netdev.dev_addr, &self->irlap->saddr, 4);
- DEBUG(4, __FUNCTION__ "()->\n");
-
return 0;
}
/* Stop timers */
del_timer(&self->media_busy_timer);
- if (self->tx_buff.data)
- kfree(self->tx_buff.data);
+ if (self->tx_buff.head)
+ kfree(self->tx_buff.head);
- if (self->rx_buff.data)
- kfree(self->rx_buff.data);
+ if (self->rx_buff.head)
+ kfree(self->rx_buff.head);
self->magic = 0;
}
return TRUE;
}
-/*
- * Function irda_get_mtt (skb)
- *
- * Utility function for getting the minimum turnaround time out of
- * the skb, where it has been hidden in the cb field.
- */
-inline unsigned short irda_get_mtt(struct sk_buff *skb)
-{
- unsigned short mtt;
-
- if (((struct irlap_skb_cb *)(skb->cb))->magic != LAP_MAGIC)
- mtt = 10000;
- else
- mtt = ((struct irlap_skb_cb *)(skb->cb))->mtt;
-
- ASSERT(mtt <= 10000, return 10000;);
-
- return mtt;
-}
-
/*
* Function setup_dma (idev, buffer, count, mode)
*
self->description);
len += irda_device_print_flags(self, buf+len);
-
len += sprintf(buf+len, "\tbps\tmaxtt\tdsize\twinsize\taddbofs\tmintt\tldisc\n");
len += sprintf(buf+len, "\t%d\t",
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Thu Aug 21 00:02:07 1997
- * Modified at: Tue Mar 23 19:38:46 1999
+ * Modified at: Fri Apr 23 09:57:12 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>,
#include <linux/irda.h>
#include <asm/byteorder.h>
+#include <asm/unaligned.h>
#include <net/irda/irda.h>
#include <net/irda/irttp.h>
DEBUG( 0, "iriap_open: Unable to allocated LSAP!\n");
return NULL;
}
+ slsap_sel = lsap->slsap_sel;
DEBUG( 4, __FUNCTION__ "(), source LSAP sel=%02x\n", slsap_sel);
self->magic = IAS_MAGIC;
self->slsap_sel = slsap_sel;
self->mode = mode;
- /* init_timer( &self->watchdog_timer); */
+ init_timer( &self->watchdog_timer);
hashbin_insert( iriap, (QUEUE*) self, slsap_sel, NULL);
ASSERT( self != NULL, return;);
ASSERT( self->magic == IAS_MAGIC, return;);
- /* del_timer( &self->watchdog_timer); */
+ del_timer( &self->watchdog_timer);
self->magic = 0;
ASSERT( iriap != NULL, return;);
- /* del_timer( &self->watchdog_timer); */
+ del_timer( &self->watchdog_timer);
if ( self->mode == IAS_CLIENT) {
DEBUG( 4, __FUNCTION__ "(), disconnect as client\n");
* Inform service user that the request failed by sending
* it a NULL value.
*/
- if ( self->confirm)
- self->confirm( 0, NULL, self->priv);
+ if (self->confirm)
+ self->confirm(IAS_DISCONNECT, 0, NULL, self->priv);
iriap_do_client_event( self, IAP_LM_DISCONNECT_INDICATION,
* Retreive all values from attribute in all objects with given class
* name
*/
-void iriap_getvaluebyclass_request( char *name, char *attr,
- __u32 saddr, __u32 daddr,
- CONFIRM_CALLBACK callback, void *priv)
+void iriap_getvaluebyclass_request(char *name, char *attr,
+ __u32 saddr, __u32 daddr,
+ CONFIRM_CALLBACK callback, void *priv)
{
struct sk_buff *skb;
struct iriap_cb *self;
int name_len, attr_len;
__u8 slsap = LSAP_ANY; /* Source LSAP to use */
- DEBUG( 4, __FUNCTION__ "()\n");
+ DEBUG(4, __FUNCTION__ "()\n");
- self = iriap_open( slsap, IAS_CLIENT);
+ self = iriap_open(slsap, IAS_CLIENT);
if (!self)
return;
*/
self->operation = GET_VALUE_BY_CLASS;
- /* Give ourselves 7 secs to finish this operation */
- /* iriap_start_watchdog_timer( self, 700); */
+ /* Give ourselves 10 secs to finish this operation */
+ iriap_start_watchdog_timer(self, 10*HZ);
skb = dev_alloc_skb( 64);
if (!skb)
return;
- name_len = strlen( name);
- attr_len = strlen( attr);
+ name_len = strlen(name);
+ attr_len = strlen(attr);
/* Reserve space for MUX and LAP header */
- skb_reserve( skb, LMP_CONTROL_HEADER+LAP_HEADER);
- skb_put( skb, 3+name_len+attr_len);
+ skb_reserve(skb, LMP_CONTROL_HEADER+LAP_HEADER);
+ skb_put(skb, 3+name_len+attr_len);
frame = skb->data;
/* Build frame */
frame[0] = IAP_LST | GET_VALUE_BY_CLASS;
frame[1] = name_len; /* Insert length of name */
- memcpy( frame+2, name, name_len); /* Insert name */
+ memcpy(frame+2, name, name_len); /* Insert name */
frame[2+name_len] = attr_len; /* Insert length of attr */
- memcpy( frame+3+name_len, attr, attr_len); /* Insert attr */
+ memcpy(frame+3+name_len, attr, attr_len); /* Insert attr */
- iriap_do_client_event( self, IAP_CALL_REQUEST_GVBC, skb);
+ iriap_do_client_event(self, IAP_CALL_REQUEST_GVBC, skb);
}
/*
int charset;
__u32 value_len;
__u32 tmp_cpu32;
- __u16 tmp_cpu16;
__u16 obj_id;
__u16 len;
__u8 type;
n = 2;
/* Get length, MSB first */
- memcpy(&len, fp+n, 2); n += 2;
- be16_to_cpus(&len);
+ len = be16_to_cpu(get_unaligned((__u16 *)(fp+n))); n += 2;
DEBUG(4, __FUNCTION__ "(), len=%d\n", len);
/* Get object ID, MSB first */
- memcpy(&obj_id, fp+n, 2); n += 2;
- be16_to_cpus(&obj_id);
+ obj_id = be16_to_cpu(get_unaligned((__u16 *)(fp+n))); n += 2;
+/* memcpy(&obj_id, fp+n, 2); n += 2; */
+/* be16_to_cpus(&obj_id); */
type = fp[n++];
DEBUG( 4, __FUNCTION__ "(), Value type = %d\n", type);
value = irias_new_string_value(fp+n);
break;
case IAS_OCT_SEQ:
- memcpy(&tmp_cpu16, fp+n, 2); n += 2;
- be16_to_cpus(&tmp_cpu16);
- value_len = tmp_cpu16;
+ value_len = be16_to_cpu(get_unaligned((__u16 *)(fp+n)));
+ n += 2;
/* FIXME:should be 1024, but.... */
DEBUG(0, __FUNCTION__ "():octet sequence:len=%d\n", value_len);
break;
}
- if (self->confirm)
- self->confirm(obj_id, value, self->priv);
-
/* Finished, close connection! */
iriap_disconnect_request(self);
+
+ if (self->confirm)
+ self->confirm(IAS_SUCCESS, obj_id, value, self->priv);
}
/*
fp[n++] = ret_code;
/* Insert list length (MSB first) */
- tmp_be16 = __constant_htons( 0x0001);
+ tmp_be16 = __constant_htons(0x0001);
memcpy(fp+n, &tmp_be16, 2); n += 2;
/* Insert object identifier ( MSB first) */
{
struct iriap_cb *self;
- self = ( struct iriap_cb *) instance;
+ self = (struct iriap_cb *) instance;
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == IAS_MAGIC, return;);
- ASSERT( userdata != NULL, return;);
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IAS_MAGIC, return;);
+ ASSERT(userdata != NULL, return;);
- DEBUG( 4, __FUNCTION__ "()\n");
+ DEBUG(4, __FUNCTION__ "()\n");
/* del_timer( &self->watchdog_timer); */
- iriap_do_client_event( self, IAP_LM_CONNECT_CONFIRM, userdata);
+ iriap_do_client_event(self, IAP_LM_CONNECT_CONFIRM, userdata);
}
/*
}
if (~opcode & IAP_ACK) {
- DEBUG(0, __FUNCTION__ "() Got ack frame!\n");
+ DEBUG(2, __FUNCTION__ "() Got ack frame!\n");
/* return; */
}
break;
case IAS_CLASS_UNKNOWN:
printk(KERN_WARNING "IrIAP No such class!\n");
+ /* Finished, close connection! */
+ iriap_disconnect_request(self);
+
+ if (self->confirm)
+ self->confirm(IAS_CLASS_UNKNOWN, 0, NULL,
+ self->priv);
break;
case IAS_ATTRIB_UNKNOWN:
printk(KERN_WARNING "IrIAP No such attribute!\n");
+ /* Finished, close connection! */
+ iriap_disconnect_request(self);
+
+ if (self->confirm)
+ self->confirm(IAS_CLASS_UNKNOWN, 0, NULL,
+ self->priv);
break;
}
iriap_do_call_event( self, IAP_RECV_F_LST, skb);
}
}
+/*
+ * Function iriap_watchdog_timer_expired (data)
+ *
+ *
+ *
+ */
void iriap_watchdog_timer_expired( unsigned long data)
{
struct iriap_cb *self = ( struct iriap_cb *) data;
- DEBUG( 0, __FUNCTION__ "()\n");
-
- return;
-
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == IAS_MAGIC, return;);
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IAS_MAGIC, return;);
- DEBUG( 0, __FUNCTION__ "() Timeout! closing myself!\n");
+ DEBUG(0, __FUNCTION__ "() Timeout! closing myself!\n");
iriap_close( self);
}
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Aug 31 20:14:37 1997
- * Modified at: Wed Apr 7 16:56:35 1999
+ * Modified at: Thu Apr 22 23:03:55 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
* Sources: skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
* slip.c by Laurence Culhane, <loz@holmes.demon.co.uk>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/netdevice.h>
-#include <linux/inetdevice.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <net/arp.h>
{
struct irmanager_event mgr_event;
- DEBUG(2, __FUNCTION__ "()\n");
+ DEBUG(0, __FUNCTION__ "()\n");
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRLAN_MAGIC, return;);
if (self->dev.start) {
/* Open TSAPs */
irlan_client_open_ctrl_tsap(self);
- irlan_provider_open_ctrl_tsap(self);
- irlan_open_data_tsap(self);
+ irlan_provider_open_ctrl_tsap(self);
+ irlan_open_data_tsap(self);
irlan_do_client_event(self, IRLAN_DISCOVERY_INDICATION, NULL);
} else if (self->notify_irmanager) {
struct irlan_cb *self, *entry;
__u32 saddr, daddr;
- DEBUG(2, __FUNCTION__"()\n");
+ DEBUG(0, __FUNCTION__"()\n");
ASSERT(irlan != NULL, return;);
ASSERT(discovery != NULL, return;);
daddr = discovery->daddr;
/*
- * Check if we already have an instance for dealing with this
- * provider.
+ * Check if we already dealing with this provider.
*/
self = (struct irlan_cb *) hashbin_find(irlan, daddr, NULL);
if (self) {
*/
self = hashbin_find(irlan, DEV_ADDR_ANY, NULL);
if (self) {
- DEBUG(2, __FUNCTION__ "(), Found instance with DEV_ADDR_ANY!\n");
+ DEBUG(0, __FUNCTION__ "(), Found instance with DEV_ADDR_ANY!\n");
/*
* Rehash instance, now we have a client (daddr) to serve.
*/
self->daddr = daddr;
self->saddr = saddr;
- DEBUG(2, __FUNCTION__ "(), daddr=%08x\n", self->daddr);
+ DEBUG(0, __FUNCTION__ "(), daddr=%08x\n", self->daddr);
hashbin_insert(irlan, (QUEUE*) self, self->daddr, NULL);
/* Check if network device has been registered */
if (!self->netdev_registered)
irlan_register_netdev(self);
- /* Remember that we might have to start a new provider */
- self->client.start_new_provider = TRUE;
- } else {
- DEBUG(2, __FUNCTION__ "(), Found none, starting new one!\n");
- /* No instance available, so we have to start one! */
- self = irlan_open(saddr, daddr, TRUE);
- if (!self) {
- DEBUG(2, __FUNCTION__ "(), irlan_open failed!\n");
- return;
- }
- ASSERT( self != NULL, return;);
+ /* Restart watchdog timer */
+ irlan_start_watchdog_timer(self, IRLAN_TIMEOUT);
}
- /* Restart watchdog timer */
- irlan_start_watchdog_timer(self, IRLAN_TIMEOUT);
}
/*
}
/*
- * Function irlan_client_extract_params (skb)
+ * Function irlan_client_parse_response (self, skb)
*
* Extract all parameters from received buffer, then feed them to
* check_params for parsing
- *
*/
-void irlan_client_extract_params(struct irlan_cb *self, struct sk_buff *skb)
+void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb)
{
__u8 *frame;
__u8 *ptr;
ASSERT(self->magic == IRLAN_MAGIC, return;);
if (!skb) {
- DEBUG(2, __FUNCTION__ "(), Got NULL skb!\n");
+ ERROR( __FUNCTION__ "(), Got NULL skb!\n");
return;
}
frame = skb->data;
/* For all parameters */
for (i=0; i<count;i++) {
- ret = irlan_get_param(ptr, name, value, &val_len);
+ ret = irlan_extract_param(ptr, name, value, &val_len);
if (ret == -1) {
DEBUG(2, __FUNCTION__ "(), IrLAN, Error!\n");
break;
static void irlan_check_response_param(struct irlan_cb *self, char *param,
char *value, int val_len)
{
-#ifdef CONFIG_IRLAN_GRATUITOUS_ARP
- struct in_device *in_dev;
-#endif
__u16 tmp_cpu; /* Temporary value in host order */
__u8 *bytes;
int i;
bytes[5]);
for (i = 0; i < 6; i++)
self->dev.dev_addr[i] = bytes[i];
-
-#ifdef CONFIG_IRLAN_GRATUITOUS_ARP
- /*
- * When we get a new MAC address do a gratuitous ARP. This
- * is useful if we have changed access points on the same
- * subnet.
- */
- DEBUG(4, "IrLAN: Sending gratuitous ARP\n");
- in_dev = self->dev.ip_ptr;
- arp_send(ARPOP_REQUEST, ETH_P_ARP,
- in_dev->ifa_list->ifa_address,
- &self->dev,
- in_dev->ifa_list->ifa_address,
- NULL, self->dev.dev_addr, NULL);
-#endif
}
}
* Got results from remote LM-IAS
*
*/
-void irlan_client_get_value_confirm(__u16 obj_id, struct ias_value *value,
- void *priv)
+void irlan_client_get_value_confirm(int result, __u16 obj_id,
+ struct ias_value *value, void *priv)
{
struct irlan_cb *self;
ASSERT(self->magic == IRLAN_MAGIC, return;);
/* Check if request succeeded */
- if (!value) {
+ if (result != IAS_SUCCESS) {
DEBUG(2, __FUNCTION__ "(), got NULL value!\n");
irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL,
NULL);
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Aug 31 20:14:37 1997
- * Modified at: Thu Feb 4 16:08:07 1999
+ * Modified at: Thu Apr 22 12:23:22 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>,
case IRLAN_DATA_INDICATION:
ASSERT(skb != NULL, return -1;);
- irlan_client_extract_params(self, skb);
+ irlan_client_parse_response(self, skb);
irlan_next_client_state(self, IRLAN_MEDIA);
switch(event) {
case IRLAN_DATA_INDICATION:
- irlan_client_extract_params(self, skb);
+ irlan_client_parse_response(self, skb);
irlan_open_data_channel(self);
irlan_next_client_state(self, IRLAN_OPEN);
break;
switch(event) {
case IRLAN_DATA_INDICATION:
- irlan_client_extract_params(self, skb);
+ irlan_client_parse_response(self, skb);
/*
* Check if we have got the remote TSAP for data
IRLAN_MTU, NULL);
irlan_next_client_state(self, IRLAN_DATA);
-
- if (self->client.start_new_provider) {
- irlan_open(DEV_ADDR_ANY, DEV_ADDR_ANY, FALSE);
- self->client.start_new_provider = FALSE;
- }
break;
default:
DEBUG(2, __FUNCTION__ "(), unknown access type!\n");
switch(event) {
case IRLAN_DATA_INDICATION:
- irlan_client_extract_params(self, skb);
+ irlan_client_parse_response(self, skb);
break;
case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */
case IRLAN_LAP_DISCONNECT:
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Aug 31 20:14:37 1997
- * Modified at: Wed Apr 7 17:03:21 1999
+ * Modified at: Thu Apr 22 23:13:47 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1997 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
void irlan_watchdog_timer_expired(unsigned long data)
{
struct irmanager_event mgr_event;
- struct irlan_cb *self = (struct irlan_cb *) data;
+ struct irlan_cb *self, *entry;
- DEBUG(2, __FUNCTION__ "()\n");
+ DEBUG(0, __FUNCTION__ "()\n");
+
+ self = (struct irlan_cb *) data;
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRLAN_MAGIC, return;);
* by the user.
*/
self->notify_irmanager = FALSE;
- } else
- irlan_close(self);
-}
-
-void irlan_start_watchdog_timer(struct irlan_cb *self, int timeout)
-{
- DEBUG(4, __FUNCTION__ "()\n");
-
- irda_start_timer(&self->watchdog_timer, timeout, (unsigned long) self,
- irlan_watchdog_timer_expired);
+ } else {
+ DEBUG(0, __FUNCTION__ "(), recycling instance!\n");
+ if (self->netdev_registered) {
+ DEBUG(0, __FUNCTION__ "(), removing netdev!\n");
+ unregister_netdev(&self->dev);
+ self->netdev_registered = FALSE;
+ }
+
+ /* Unbind from daddr */
+ entry = hashbin_remove(irlan, self->daddr, NULL);
+ ASSERT(entry == self, return;);
+
+ self->daddr = DEV_ADDR_ANY;
+ self->saddr = DEV_ADDR_ANY;
+
+ DEBUG(2, __FUNCTION__ "(), daddr=%08x\n", self->daddr);
+ hashbin_insert(irlan, (QUEUE*) self, self->daddr, NULL);
+ }
}
/*
- * Function irlan_eth_open (dev)
+ * Function irlan_start_watchdog_timer (self, timeout)
*
- * Network device has been opened by user
- *
- */
-static int irlan_eth_open(struct device *dev)
-{
- struct irlan_cb *self;
-
- DEBUG(4, __FUNCTION__ "()\n");
-
- ASSERT(dev != NULL, return -1;);
-
- self = (struct irlan_cb *) dev->priv;
-
- ASSERT(self != NULL, return -1;);
-
- /* Ready to play! */
-/* dev->tbusy = 0; */ /* Wait until data link is ready */
- dev->interrupt = 0;
- dev->start = 1;
-
- self->notify_irmanager = TRUE;
-
- /* We are now open, so time to do some work */
- irlan_client_wakeup(self, self->saddr, self->daddr);
-
- MOD_INC_USE_COUNT;
-
- return 0;
-}
-
-/*
- * Function irlan_eth_close (dev)
+ *
*
- * Stop the ether network device, his function will usually be called by
- * ifconfig down. We should now disconnect the link, We start the
- * close timer, so that the instance will be removed if we are unable
- * to discover the remote device after the disconnect.
*/
-static int irlan_eth_close(struct device *dev)
+void irlan_start_watchdog_timer(struct irlan_cb *self, int timeout)
{
- struct irlan_cb *self = (struct irlan_cb *) dev->priv;
-
DEBUG(4, __FUNCTION__ "()\n");
- /* Stop device */
- dev->tbusy = 1;
- dev->start = 0;
-
- MOD_DEC_USE_COUNT;
-
- irlan_close_data_channel(self);
-
- irlan_close_tsaps(self);
-
- irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
- irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL);
-
- irlan_start_watchdog_timer(self, IRLAN_TIMEOUT);
-
- /* Device closed by user! */
- if (self->notify_irmanager)
- self->notify_irmanager = FALSE;
- else
- self->notify_irmanager = TRUE;
-
- return 0;
-}
-/*
- * Function irlan_eth_init (dev)
- *
- * The network device initialization function.
- *
- */
-int irlan_eth_init(struct device *dev)
-{
- struct irmanager_event mgr_event;
- struct irlan_cb *self;
-
- DEBUG(4, __FUNCTION__"()\n");
-
- ASSERT(dev != NULL, return -1;);
-
- self = (struct irlan_cb *) dev->priv;
-
- dev->open = irlan_eth_open;
- dev->stop = irlan_eth_close;
- dev->hard_start_xmit = irlan_eth_xmit;
- dev->get_stats = irlan_eth_get_stats;
- dev->set_multicast_list = irlan_eth_set_multicast_list;
-
- dev->tbusy = 1;
-
- ether_setup(dev);
-
- dev->tx_queue_len = TTP_MAX_QUEUE;
-
-#if 0
- /*
- * OK, since we are emulating an IrLAN sever we will have to give
- * ourself an ethernet address!
- * FIXME: this must be more dynamically
- */
- dev->dev_addr[0] = 0x40;
- dev->dev_addr[1] = 0x00;
- dev->dev_addr[2] = 0x00;
- dev->dev_addr[3] = 0x00;
- dev->dev_addr[4] = 0x23;
- dev->dev_addr[5] = 0x45;
-#endif
- /*
- * Network device has now been registered, so tell irmanager about
- * it, so it can be configured with network parameters
- */
- mgr_event.event = EVENT_IRLAN_START;
- sprintf(mgr_event.devname, "%s", self->ifname);
- irmanager_notify(&mgr_event);
-
- /*
- * We set this so that we only notify once, since if
- * configuration of the network device fails, the user
- * will have to sort it out first anyway. No need to
- * try again.
- */
- self->notify_irmanager = FALSE;
-
- return 0;
+ irda_start_timer(&self->watchdog_timer, timeout, (unsigned long) self,
+ irlan_watchdog_timer_expired);
}
/*
/* Start the first IrLAN instance */
new = irlan_open(DEV_ADDR_ANY, DEV_ADDR_ANY, FALSE);
+ irlan_open_data_tsap(new);
+ irlan_client_open_ctrl_tsap(new);
+ irlan_provider_open_ctrl_tsap(new);
+
/* Do some fast discovery! */
- irlmp_discovery_request(8);
+ irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
return 0;
}
{
int i=0;
- DEBUG(4, __FUNCTION__ "()\n");
+ DEBUG(0, __FUNCTION__ "()\n");
/* Check if we should call the device eth<x> or irlan<x> */
if (!eth) {
irlan_next_client_state(self, IRLAN_IDLE);
irlan_next_provider_state(self, IRLAN_IDLE);
- irlan_open_data_tsap(self);
- irlan_provider_open_ctrl_tsap(self);
- irlan_client_open_ctrl_tsap(self);
-
/* Register network device now, or wait until some later time? */
if (netdev)
irlan_register_netdev(self);
*/
static void __irlan_close(struct irlan_cb *self)
{
- DEBUG(2, __FUNCTION__ "()\n");
+ DEBUG(0, __FUNCTION__ "()\n");
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRLAN_MAGIC, return;);
/* Close all open connections and remove TSAPs */
irlan_close_tsaps(self);
- if (self->netdev_registered)
+ if (self->netdev_registered) {
unregister_netdev(&self->dev);
+ self->netdev_registered = FALSE;
+ }
self->magic = 0;
- kfree(self);
+ kfree(self);
}
/*
{
struct irlan_cb *entry;
- DEBUG(2, __FUNCTION__ "()\n");
+ DEBUG(0, __FUNCTION__ "()\n");
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRLAN_MAGIC, return;);
switch(reason) {
case LM_USER_REQUEST: /* User request */
- irlan_close(self);
+ //irlan_close(self);
break;
case LM_LAP_DISCONNECT: /* Unexpected IrLAP disconnect */
irlan_start_watchdog_timer(self, IRLAN_TIMEOUT);
self->stsap_sel_data = self->tsap_data->stsap_sel;
}
-static void irlan_close_tsaps(struct irlan_cb *self)
+void irlan_close_tsaps(struct irlan_cb *self)
{
DEBUG(4, __FUNCTION__ "()\n");
}
/*
- * Function irlan_get_response_param (buf, param, value)
+ * Function irlan_extract_param (buf, name, value, len)
*
* Extracts a single parameter name/value pair from buffer and updates
* the buffer pointer to point to the next name/value pair.
*/
-int irlan_get_param(__u8 *buf, char *name, char *value, __u16 *len)
+int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len)
{
__u8 name_len;
__u16 val_len;
}
}
+void irlan_mod_inc_use_count(void)
+{
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+}
+
+void irlan_mod_dec_use_count(void)
+{
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+}
+
#ifdef MODULE
MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Thu Oct 15 08:37:58 1998
- * Modified at: Mon Mar 22 17:41:59 1999
+ * Modified at: Thu Apr 22 14:26:39 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
* Sources: skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
* slip.c by Laurence Culhane, <loz@holmes.demon.co.uk>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/inetdevice.h>
#include <linux/if_arp.h>
#include <net/arp.h>
#include <net/irda/irda.h>
#include <net/irda/irmod.h>
#include <net/irda/irlan_common.h>
+#include <net/irda/irlan_client.h>
+#include <net/irda/irlan_event.h>
#include <net/irda/irlan_eth.h>
+/*
+ * Function irlan_eth_init (dev)
+ *
+ * The network device initialization function.
+ *
+ */
+int irlan_eth_init(struct device *dev)
+{
+ struct irmanager_event mgr_event;
+ struct irlan_cb *self;
+
+ DEBUG(0, __FUNCTION__"()\n");
+
+ ASSERT(dev != NULL, return -1;);
+
+ self = (struct irlan_cb *) dev->priv;
+
+ dev->open = irlan_eth_open;
+ dev->stop = irlan_eth_close;
+ dev->hard_start_xmit = irlan_eth_xmit;
+ dev->get_stats = irlan_eth_get_stats;
+ dev->set_multicast_list = irlan_eth_set_multicast_list;
+
+ dev->tbusy = 1;
+
+ ether_setup(dev);
+
+ dev->tx_queue_len = TTP_MAX_QUEUE;
+
+#if 0
+ /*
+ * OK, since we are emulating an IrLAN sever we will have to give
+ * ourself an ethernet address!
+ * FIXME: this must be more dynamically
+ */
+ dev->dev_addr[0] = 0x40;
+ dev->dev_addr[1] = 0x00;
+ dev->dev_addr[2] = 0x00;
+ dev->dev_addr[3] = 0x00;
+ dev->dev_addr[4] = 0x23;
+ dev->dev_addr[5] = 0x45;
+#endif
+ /*
+ * Network device has now been registered, so tell irmanager about
+ * it, so it can be configured with network parameters
+ */
+ mgr_event.event = EVENT_IRLAN_START;
+ sprintf(mgr_event.devname, "%s", self->ifname);
+ irmanager_notify(&mgr_event);
+
+ /*
+ * We set this so that we only notify once, since if
+ * configuration of the network device fails, the user
+ * will have to sort it out first anyway. No need to
+ * try again.
+ */
+ self->notify_irmanager = FALSE;
+
+ return 0;
+}
+
+/*
+ * Function irlan_eth_open (dev)
+ *
+ * Network device has been opened by user
+ *
+ */
+int irlan_eth_open(struct device *dev)
+{
+ struct irlan_cb *self;
+
+ DEBUG(0, __FUNCTION__ "()\n");
+
+ ASSERT(dev != NULL, return -1;);
+
+ self = (struct irlan_cb *) dev->priv;
+
+ ASSERT(self != NULL, return -1;);
+
+ /* Ready to play! */
+/* dev->tbusy = 0; */ /* Wait until data link is ready */
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ self->notify_irmanager = TRUE;
+
+ /* We are now open, so time to do some work */
+ irlan_client_wakeup(self, self->saddr, self->daddr);
+
+ irlan_mod_inc_use_count();
+
+ return 0;
+}
+
+/*
+ * Function irlan_eth_close (dev)
+ *
+ * Stop the ether network device, his function will usually be called by
+ * ifconfig down. We should now disconnect the link, We start the
+ * close timer, so that the instance will be removed if we are unable
+ * to discover the remote device after the disconnect.
+ */
+int irlan_eth_close(struct device *dev)
+{
+ struct irlan_cb *self = (struct irlan_cb *) dev->priv;
+
+ DEBUG(0, __FUNCTION__ "()\n");
+
+ /* Stop device */
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ irlan_mod_dec_use_count();
+
+ irlan_close_data_channel(self);
+
+ irlan_close_tsaps(self);
+
+ irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
+ irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL);
+
+ irlan_start_watchdog_timer(self, IRLAN_TIMEOUT);
+
+ /* Device closed by user! */
+ if (self->notify_irmanager)
+ self->notify_irmanager = FALSE;
+ else
+ self->notify_irmanager = TRUE;
+
+ return 0;
+}
+
/*
* Function irlan_eth_tx (skb)
*
/* return 0; */
}
+/*
+ * Function irlan_etc_send_gratuitous_arp (dev)
+ *
+ * Send gratuitous ARP to announce that we have changed
+ * hardware address, so that all peers updates their ARP tables
+ */
+void irlan_etc_send_gratuitous_arp(struct device *dev)
+{
+ struct in_device *in_dev;
+
+ /*
+ * When we get a new MAC address do a gratuitous ARP. This
+ * is useful if we have changed access points on the same
+ * subnet.
+ */
+ DEBUG(4, "IrLAN: Sending gratuitous ARP\n");
+ in_dev = dev->ip_ptr;
+ arp_send(ARPOP_REQUEST, ETH_P_ARP,
+ in_dev->ifa_list->ifa_address,
+ &dev,
+ in_dev->ifa_list->ifa_address,
+ NULL, dev->dev_addr, NULL);
+}
+
/*
* Function set_multicast_list (dev)
*
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Aug 31 20:14:37 1997
- * Modified at: Tue Apr 6 19:08:20 1999
+ * Modified at: Thu Apr 22 14:28:52 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
* Sources: skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
* slip.c by Laurence Culhane, <loz@holmes.demon.co.uk>
ASSERT(tsap == self->provider.tsap_ctrl,return;);
ASSERT(self->provider.state == IRLAN_IDLE, return;);
- /* Check if this provider is unused */
+ /* Check if this provider is currently unused */
if (self->daddr == DEV_ADDR_ANY) {
/*
* Rehash instance, now we have a client (daddr) to serve.
if ((self->access_type == ACCESS_PEER) &&
(self->client.state == IRLAN_IDLE))
irlan_client_wakeup(self, self->saddr, self->daddr);
-
- /*
- * This provider is now in use, so start a new provider instance to
- * serve other clients. This will also change the LM-IAS entry so that
- * other clients don't try to connect to us, now that we are busy.
- */
- new = irlan_open(DEV_ADDR_ANY, DEV_ADDR_ANY, FALSE);
- self->client.start_new_provider = FALSE;
}
/*
{
int ret;
- ret = irlan_provider_extract_params(self, CMD_OPEN_DATA_CHANNEL, skb);
+ ret = irlan_provider_parse_command(self, CMD_OPEN_DATA_CHANNEL, skb);
return ret;
}
/*
- * Function extract_params (skb)
+ * Function parse_command (skb)
*
* Extract all parameters from received buffer, then feed them to
* check_params for parsing
*
*/
-int irlan_provider_extract_params(struct irlan_cb *self, int cmd,
- struct sk_buff *skb)
+int irlan_provider_parse_command(struct irlan_cb *self, int cmd,
+ struct sk_buff *skb)
{
__u8 *frame;
__u8 *ptr;
/* For all parameters */
for (i=0; i<count;i++) {
- ret = irlan_get_param(ptr, name, value, &val_len);
+ ret = irlan_extract_param(ptr, name, value, &val_len);
if (ret < 0) {
DEBUG(2, __FUNCTION__ "(), IrLAN, Error!\n");
break;
* Register provider support so we can accept incomming connections.
*
*/
-void irlan_provider_open_ctrl_tsap(struct irlan_cb *self)
+int irlan_provider_open_ctrl_tsap(struct irlan_cb *self)
{
struct notify_t notify;
struct tsap_cb *tsap;
DEBUG(4, __FUNCTION__ "()\n");
- ASSERT(self != NULL, return;);
- ASSERT(self->magic == IRLAN_MAGIC, return;);
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRLAN_MAGIC, return -1;);
/* Check if already open */
if (self->provider.tsap_ctrl)
- return;
+ return -1;
/*
* First register well known control TSAP
tsap = irttp_open_tsap(LSAP_ANY, 1, ¬ify);
if (!tsap) {
DEBUG(2, __FUNCTION__ "(), Got no tsap!\n");
- return;
+ return -1;
}
self->provider.tsap_ctrl = tsap;
/* Register with LM-IAS */
- irlan_ias_register(self,tsap->stsap_sel);
+ irlan_ias_register(self, tsap->stsap_sel);
+
+ return 0;
}
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Aug 31 20:14:37 1997
- * Modified at: Wed Feb 3 21:43:06 1999
+ * Modified at: Thu Apr 22 10:46:28 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
switch(event) {
case IRLAN_GET_INFO_CMD:
/* Be sure to use 802.3 in case of peer mode */
- if (self->access_type == ACCESS_PEER)
+ if (self->access_type == ACCESS_PEER) {
self->media = MEDIA_802_3;
+
+ /* Check if client has started yet */
+ if (self->client.state == IRLAN_IDLE) {
+ /* This should get the client going */
+ irlmp_discovery_request(8);
+ }
+ }
irlan_provider_send_reply(self, CMD_GET_PROVIDER_INFO,
RSP_SUCCESS);
switch(event) {
case IRLAN_FILTER_CONFIG_CMD:
- irlan_provider_extract_params(self, CMD_FILTER_OPERATION, skb);
+ irlan_provider_parse_command(self, CMD_FILTER_OPERATION, skb);
irlan_provider_send_reply(self, CMD_FILTER_OPERATION,
RSP_SUCCESS);
/* Keep state */
switch(event) {
case IRLAN_FILTER_CONFIG_CMD:
- irlan_provider_extract_params(self, CMD_FILTER_OPERATION, skb);
+ irlan_provider_parse_command(self, CMD_FILTER_OPERATION, skb);
irlan_provider_send_reply(self, CMD_FILTER_OPERATION,
RSP_SUCCESS);
break;
* Status: Stable.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Mon Aug 4 20:40:53 1997
- * Modified at: Tue Apr 6 21:07:08 1999
+ * Modified at: Fri Apr 23 10:12:29 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>,
#include <net/irda/irlap_comp.h>
hashbin_t *irlap = NULL;
-int sysctl_slot_timeout = SLOT_TIMEOUT;
+int sysctl_slot_timeout = SLOT_TIMEOUT * 1000 / HZ;
-static void __irlap_close( struct irlap_cb *self);
+static void __irlap_close(struct irlap_cb *self);
static char *lap_reasons[] = {
"ERROR, NOT USED",
* IrLMP for further processing
*
*/
-inline void irlap_data_indication( struct irlap_cb *self, struct sk_buff *skb)
+inline void irlap_data_indication(struct irlap_cb *self, struct sk_buff *skb)
{
- DEBUG( 4, __FUNCTION__ "()\n");
-
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == LAP_MAGIC, return;);
- ASSERT( skb != NULL, return;);
-
/* Hide LAP header from IrLMP layer */
- skb_pull( skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
+ skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
#ifdef CONFIG_IRDA_COMPRESSION
- if ( self->qos_tx.compression.value) {
- skb = irlap_decompress_frame( self, skb);
- if ( !skb) {
- DEBUG( 1, __FUNCTION__ "(), Decompress error!\n");
+ if (self->qos_tx.compression.value) {
+ skb = irlap_decompress_frame(self, skb);
+ if (!skb) {
+ DEBUG(1, __FUNCTION__ "(), Decompress error!\n");
return;
}
}
#endif
- irlmp_link_data_indication( self->notify.instance, LAP_RELIABLE, skb);
+ irlmp_link_data_indication(self->notify.instance, LAP_RELIABLE, skb);
}
/*
info.discovery = discovery;
/* Check if the slot timeout is within limits */
- if ( sysctl_slot_timeout < 2) {
- DEBUG( 1, __FUNCTION__
- "(), to low value for slot timeout!\n");
- sysctl_slot_timeout = 2;
+ if (sysctl_slot_timeout < 20) {
+ ERROR(__FUNCTION__
+ "(), to low value for slot timeout!\n");
+ sysctl_slot_timeout = 20;
}
/*
* Highest value is actually 8, but we allow higher since
* some devices seems to require it.
*/
- if ( sysctl_slot_timeout > 16) {
- DEBUG( 1, __FUNCTION__
- "(), to high value for slot timeout!\n");
- sysctl_slot_timeout = 16;
+ if (sysctl_slot_timeout > 160) {
+ ERROR(__FUNCTION__
+ "(), to high value for slot timeout!\n");
+ sysctl_slot_timeout = 160;
}
- self->slot_timeout = sysctl_slot_timeout;
+ self->slot_timeout = sysctl_slot_timeout * HZ / 1000;
irlap_do_event( self, DISCOVERY_REQUEST, NULL, &info);
} else {
{
int usecs;
int speed;
- int bytes = 0;
+ int bytes ;
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == LAP_MAGIC, return;);
- ASSERT( qos != NULL, return;);
-
/* Get QoS values. */
speed = qos->baud_rate.value;
usecs = qos->min_turn_time.value;
/* No need to calculate XBOFs for speeds over 115200 bps */
- if ( speed > 115200) {
+ if (speed > 115200) {
self->mtt_required = usecs;
return;
}
- DEBUG( 4, __FUNCTION__ "(), delay=%d usecs\n", usecs);
-
/*
* Send additional BOF's for the next frame for the requested
* min turn time, so now we must calculate how many chars (XBOF's) we
*/
bytes = speed * usecs / 10000000;
- DEBUG( 4, __FUNCTION__ "(), xbofs delay = %d\n", bytes);
-
self->xbofs_delay = bytes;
}
self->qos_tx.data_size.value = 64;
self->qos_tx.additional_bofs.value = 11;
- irlap_flush_all_queues( self);
+ irlap_flush_all_queues(self);
self->disconnect_pending = FALSE;
}
/*
* Initialize timeout values, some of the rules are listed on
- * page 92 in IrLAP. Divide by 10 since the kernel timers has a
- * resolution of 10 ms.
+ * page 92 in IrLAP.
*/
- self->poll_timeout = qos->max_turn_time.value / 10;
- self->final_timeout = qos->max_turn_time.value / 10;
+ self->poll_timeout = qos->max_turn_time.value * HZ / 1000;
+ self->final_timeout = qos->max_turn_time.value * HZ / 1000;
self->wd_timeout = self->poll_timeout * 2;
#ifdef CONFIG_IRDA_COMPRESSION
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sat Aug 16 00:59:29 1997
- * Modified at: Fri Mar 26 14:24:09 1999
+ * Modified at: Fri Apr 23 11:55:12 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>,
#include <net/irda/irda_device.h>
-static int irlap_state_ndm ( struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_query ( struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_reply ( struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_conn ( struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_setup ( struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_offline( struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_xmit_p ( struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_pclose ( struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_nrm_p ( struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
+#if CONFIG_IRDA_FAST_RR
+int sysctl_fast_poll_increase = 50;
+#endif
+
+static int irlap_state_ndm (struct irlap_cb *self, IRLAP_EVENT event,
+ struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_query (struct irlap_cb *self, IRLAP_EVENT event,
+ struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_reply (struct irlap_cb *self, IRLAP_EVENT event,
+ struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_conn (struct irlap_cb *self, IRLAP_EVENT event,
+ struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_setup (struct irlap_cb *self, IRLAP_EVENT event,
+ struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_offline(struct irlap_cb *self, IRLAP_EVENT event,
+ struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_xmit_p (struct irlap_cb *self, IRLAP_EVENT event,
+ struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_pclose (struct irlap_cb *self, IRLAP_EVENT event,
+ struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_nrm_p (struct irlap_cb *self, IRLAP_EVENT event,
+ struct sk_buff *skb, struct irlap_info *info);
static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event,
struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_reset ( struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_nrm_s ( struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_xmit_s ( struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_sclose ( struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_reset_check( struct irlap_cb *, IRLAP_EVENT event,
- struct sk_buff *, struct irlap_info *);
+static int irlap_state_reset (struct irlap_cb *self, IRLAP_EVENT event,
+ struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_nrm_s (struct irlap_cb *self, IRLAP_EVENT event,
+ struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_xmit_s (struct irlap_cb *self, IRLAP_EVENT event,
+ struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_sclose (struct irlap_cb *self, IRLAP_EVENT event,
+ struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_reset_check(struct irlap_cb *, IRLAP_EVENT event,
+ struct sk_buff *, struct irlap_info *);
static const char *irlap_event[] = {
"DISCOVERY_REQUEST",
/*
* Function irda_poll_timer_expired (data)
*
- *
- *
+ * Poll timer has expired. Normally we must now send a RR frame to the
+ * remote device
*/
-static void irlap_poll_timer_expired( unsigned long data)
+static void irlap_poll_timer_expired(unsigned long data)
{
struct irlap_cb *self = (struct irlap_cb *) data;
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == LAP_MAGIC, return;);
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == LAP_MAGIC, return;);
- irlap_do_event( self, POLL_TIMER_EXPIRED, NULL, NULL);
+ irlap_do_event(self, POLL_TIMER_EXPIRED, NULL, NULL);
}
-void irlap_start_poll_timer( struct irlap_cb *self, int timeout)
+void irlap_start_poll_timer(struct irlap_cb *self, int timeout)
{
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == LAP_MAGIC, return;);
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == LAP_MAGIC, return;);
#ifdef CONFIG_IRDA_FAST_RR
- if ( skb_queue_len( &self->tx_list) == 0) {
- if ( self->fast_RR == TRUE) {
+ if (skb_queue_len(&self->tx_list) == 0) {
+ if (self->fast_RR == TRUE) {
/*
* Assert that the fast poll timer has not reached the
* normal poll timer yet
*/
- if ( self->fast_RR_timeout < timeout) {
+ if (self->fast_RR_timeout < timeout) {
/*
* FIXME: this should be a more configurable
* function
*/
- self->fast_RR_timeout += 15;
+ self->fast_RR_timeout += (sysctl_fast_poll_increase * HZ/1000);
/* Use this fast(er) timeout instead */
timeout = self->fast_RR_timeout;
} else {
self->fast_RR = TRUE;
- /* Start with just 1 ms */
- self->fast_RR_timeout = 1;
- timeout = 1;
+ /* Start with just 0 ms */
+ self->fast_RR_timeout = 0;
+ timeout = 0;
}
} else
self->fast_RR = FALSE;
- DEBUG( 4, __FUNCTION__ "(), Timeout=%d\n", timeout);
+ DEBUG(4, __FUNCTION__ "(), Timeout=%d\n", timeout);
#endif
- irda_start_timer( &self->poll_timer, timeout,
- (unsigned long) self, irlap_poll_timer_expired);
+ if (timeout == 0)
+ irlap_do_event(self, POLL_TIMER_EXPIRED, NULL, NULL);
+ else
+ irda_start_timer(&self->poll_timer, timeout,
+ (unsigned long) self, irlap_poll_timer_expired);
}
/*
case DISCOVERY_REQUEST:
ASSERT( info != NULL, return -1;);
- if ( irda_device_is_media_busy( self->irdev)) {
+ if (irda_device_is_media_busy(self->irdev)) {
DEBUG(0, __FUNCTION__ "(), media busy!\n");
/* irlap->log.condition = MEDIA_BUSY; */
/* Always switch state before calling upper layers */
- irlap_next_state( self, LAP_NDM);
+ irlap_next_state(self, LAP_NDM);
/* This will make IrLMP try again */
- irlap_discovery_confirm( self, NULL);
+ irlap_discovery_confirm(self, NULL);
return 0;
}
self->S = info->S;
self->s = info->s;
- irlap_send_discovery_xid_frame( self, info->S, info->s, TRUE,
- info->discovery);
+ irlap_send_discovery_xid_frame(self, info->S, info->s, TRUE,
+ info->discovery);
self->s++;
- irlap_start_slot_timer( self, self->slot_timeout);
+ irlap_start_slot_timer(self, self->slot_timeout);
irlap_next_state(self, LAP_QUERY);
break;
irlap_next_state( self, LAP_QUERY);
break;
case SLOT_TIMER_EXPIRED:
- if ( self->s < self->S) {
- irlap_send_discovery_xid_frame( self, self->S,
- self->s, TRUE,
- self->discovery_cmd);
+ if (self->s < self->S) {
+ irlap_send_discovery_xid_frame(self, self->S,
+ self->s, TRUE,
+ self->discovery_cmd);
self->s++;
- irlap_start_slot_timer( self, self->slot_timeout);
+ irlap_start_slot_timer(self, self->slot_timeout);
/* Keep state */
- irlap_next_state( self, LAP_QUERY);
+ irlap_next_state(self, LAP_QUERY);
} else {
/* This is the final slot! */
- irlap_send_discovery_xid_frame( self, self->S, 0xff,
- TRUE,
- self->discovery_cmd);
+ irlap_send_discovery_xid_frame(self, self->S, 0xff,
+ TRUE,
+ self->discovery_cmd);
/* Always switch state before calling upper layers */
- irlap_next_state( self, LAP_NDM);
+ irlap_next_state(self, LAP_NDM);
/*
* We are now finished with the discovery procedure,
* so now we must return the results
*/
- irlap_discovery_confirm( self, self->discovery_log);
+ irlap_discovery_confirm(self, self->discovery_log);
}
break;
default:
DEBUG(2, __FUNCTION__ "(), Unknown event %d, %s\n", event,
irlap_event[event]);
- if ( skb != NULL) {
+ if (skb)
dev_kfree_skb( skb);
- }
+
ret = -1;
break;
}
* are waiting for the right time slot to send a response XID frame
*
*/
-static int irlap_state_reply( struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
+static int irlap_state_reply(struct irlap_cb *self, IRLAP_EVENT event,
+ struct sk_buff *skb, struct irlap_info *info)
{
discovery_t *discovery_rsp;
int ret=0;
- DEBUG( 4, __FUNCTION__ "()\n");
+ DEBUG(4, __FUNCTION__ "()\n");
- ASSERT( self != NULL, return -1;);
- ASSERT( self->magic == LAP_MAGIC, return -1;);
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == LAP_MAGIC, return -1;);
- switch( event) {
+ switch(event) {
case QUERY_TIMER_EXPIRED:
DEBUG(2, __FUNCTION__ "(), QUERY_TIMER_EXPIRED <%ld>\n",
jiffies);
- irlap_next_state( self, LAP_NDM);
+ irlap_next_state(self, LAP_NDM);
break;
case RECV_DISCOVERY_XID_CMD:
- ASSERT( info != NULL, return -1;);
+ ASSERT(info != NULL, return -1;);
/*
* Last frame?
*/
- if ( info->s == 0xff) {
- del_timer( &self->query_timer);
+ if (info->s == 0xff) {
+ del_timer(&self->query_timer);
/* info->log.condition = REMOTE; */
/* Always switch state before calling upper layers */
- irlap_next_state( self, LAP_NDM);
+ irlap_next_state(self, LAP_NDM);
- irlap_discovery_indication( self, info->discovery);
+ irlap_discovery_indication(self, info->discovery);
} else if ((info->s >= self->slot) && (!self->frame_sent)) {
discovery_rsp = irlmp_get_discovery_response();
discovery_rsp->daddr = info->daddr;
* layer to accept or refuse connection
*
*/
-static int irlap_state_conn( struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
+static int irlap_state_conn(struct irlap_cb *self, IRLAP_EVENT event,
+ struct sk_buff *skb, struct irlap_info *info)
{
int ret = 0;
- DEBUG( 4, __FUNCTION__ "(), event=%s\n", irlap_event[ event]);
+ DEBUG(4, __FUNCTION__ "(), event=%s\n", irlap_event[ event]);
- ASSERT( self != NULL, return -1;);
- ASSERT( self->magic == LAP_MAGIC, return -1;);
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == LAP_MAGIC, return -1;);
- switch( event) {
+ switch (event) {
case CONNECT_RESPONSE:
skb_pull( skb, 11);
DEBUG( 4, __FUNCTION__ "(), event=%s, vs=%d, vr=%d",
irlap_event[ event], self->vs, self->vr);
- switch( event) {
+ switch (event) {
case SEND_I_CMD:
ASSERT( skb != NULL, return -1;);
DEBUG( 4, __FUNCTION__ "(), Window=%d\n", self->window);
} else {
DEBUG( 4, __FUNCTION__
"(), Unable to send! remote busy?\n");
- skb_queue_head( &self->tx_list, skb);
+ skb_queue_head(&self->tx_list, skb);
/*
* The next ret is important, because it tells
ret = -EPROTO;
}
break;
+ case POLL_TIMER_EXPIRED:
+ irlap_send_rr_frame(self, CMD_FRAME);
+ irlap_start_final_timer(self, self->final_timeout);
+ irlap_next_state(self, LAP_NRM_P);
+ break;
case DISCONNECT_REQUEST:
- del_timer( &self->poll_timer);
- irlap_wait_min_turn_around( self, &self->qos_tx);
- irlap_send_disc_frame( self);
- irlap_flush_all_queues( self);
- irlap_start_final_timer( self, self->final_timeout);
+ del_timer(&self->poll_timer);
+ irlap_wait_min_turn_around(self, &self->qos_tx);
+ irlap_send_disc_frame(self);
+ irlap_flush_all_queues(self);
+ irlap_start_final_timer(self, self->final_timeout);
self->retry_count = 0;
- irlap_next_state( self, LAP_PCLOSE);
- break;
- case POLL_TIMER_EXPIRED:
- irlap_send_rr_frame( self, CMD_FRAME);
- irlap_start_final_timer( self, self->final_timeout);
- irlap_next_state( self, LAP_NRM_P);
+ irlap_next_state(self, LAP_PCLOSE);
break;
default:
DEBUG(0, __FUNCTION__ "(), Unknown event %s\n",
* transmit any frames and is expecting to receive frames only from the
* secondary to which transmission permissions has been given.
*/
-static int irlap_state_nrm_p( struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
+static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
+ struct sk_buff *skb, struct irlap_info *info)
{
int ret = 0;
int ns_status;
int nr_status;
- ASSERT( self != NULL, return -1;);
- ASSERT( self->magic == LAP_MAGIC, return -1;);
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == LAP_MAGIC, return -1;);
- switch( event) {
- case RECV_RR_RSP:
- DEBUG( 4, __FUNCTION__ "(), RECV_RR_FRAME: "
- "Retrans:%d, nr=%d, va=%d, vs=%d, vr=%d\n",
- self->retry_count, info->nr, self->va, self->vs,
- self->vr);
-
- ASSERT( info != NULL, return -1;);
-
- /*
- * If you get a RR, the remote isn't busy anymore,
- * no matter what the NR
- */
- self->remote_busy = FALSE;
-
- /*
- * Nr as expected?
- */
- ret = irlap_validate_nr_received( self, info->nr);
- if ( ret == NR_EXPECTED) {
- /* Stop final timer */
- del_timer( &self->final_timer);
-
- /* Update Nr received */
- irlap_update_nr_received( self, info->nr);
-
- /*
- * Got expected NR, so reset the retry_count. This
- * is not done by the IrLAP standard , which is
- * strange! DB.
- */
- self->retry_count = 0;
- irlap_wait_min_turn_around( self, &self->qos_tx);
-
- /* Start poll timer */
- irlap_start_poll_timer( self, self->poll_timeout);
-
- irlap_next_state( self, LAP_XMIT_P);
- } else if ( ret == NR_UNEXPECTED) {
- ASSERT( info != NULL, return -1;);
- /*
- * Unexpected nr!
- */
-
- /* Update Nr received */
- irlap_update_nr_received( self, info->nr);
-
- DEBUG( 4, "RECV_RR_FRAME: Retrans:%d, nr=%d, va=%d, "
- "vs=%d, vr=%d\n",
- self->retry_count, info->nr, self->va,
- self->vs, self->vr);
-
- /* Resend rejected frames */
- irlap_resend_rejected_frames( self, CMD_FRAME);
-
- /*
- * Start only if not running, DB
- * TODO: Should this one be here?
- */
- /* if ( !self->final_timer.prev) */
-/* irda_start_timer( FINAL_TIMER, self->final_timeout); */
-
- /* Keep state */
- irlap_next_state( self, LAP_NRM_P);
- } else if ( ret == NR_INVALID) {
- DEBUG(1, "irlap_state_nrm_p: received RR with "
- "invalid nr !\n");
- del_timer( &self->final_timer);
-
- irlap_next_state( self, LAP_RESET_WAIT);
-
- irlap_disconnect_indication( self,
- LAP_RESET_INDICATION);
- self->xmitflag = TRUE;
- }
- if (skb)
- dev_kfree_skb( skb);
- break;
- case RECV_RNR_FRAME:
- DEBUG( 4, "irlap_state_nrm_p: RECV_RNR_FRAME: Retrans:%d, "
- "nr=%d, va=%d, vs=%d, vr=%d\n",
- self->retry_count, info->nr, self->va, self->vs,
- self->vr);
-
- ASSERT( info != NULL, return -1;);
-
- /* Stop final timer */
- del_timer( &self->final_timer);
- self->remote_busy = TRUE;
-
- /* Update Nr received */
- irlap_update_nr_received( self, info->nr);
-
- /* Start poll timer */
- irlap_start_poll_timer( self, self->poll_timeout);
-
- irlap_next_state( self, LAP_XMIT_P);
-
- dev_kfree_skb( skb);
- break;
- case RECV_I_RSP:
+ switch (event) {
+ case RECV_I_RSP: /* Optimize for the common case */
/* FIXME: must check for remote_busy below */
#ifdef CONFIG_IRDA_FAST_RR
/*
ASSERT( info != NULL, return -1;);
- ns_status = irlap_validate_ns_received( self, info->ns);
- nr_status = irlap_validate_nr_received( self, info->nr);
+ ns_status = irlap_validate_ns_received(self, info->ns);
+ nr_status = irlap_validate_nr_received(self, info->nr);
/*
* Check for expected I(nformation) frame
/*
* poll bit cleared?
*/
- if ( !info->pf) {
+ if (!info->pf) {
self->vr = (self->vr + 1) % 8;
/* Update Nr received */
self->ack_required = TRUE;
/* Keep state, do not move this line */
- irlap_next_state( self, LAP_NRM_P);
+ irlap_next_state(self, LAP_NRM_P);
- irlap_data_indication( self, skb);
+ irlap_data_indication(self, skb);
} else {
- del_timer( &self->final_timer);
+ del_timer(&self->final_timer);
self->vr = (self->vr + 1) % 8;
/* Update Nr received */
- irlap_update_nr_received( self, info->nr);
+ irlap_update_nr_received(self, info->nr);
/*
* Got expected NR, so reset the
self->retry_count = 0;
self->ack_required = TRUE;
- /* This is the last frame */
- irlap_start_poll_timer( self, self->poll_timeout);
- irlap_wait_min_turn_around( self, &self->qos_tx);
- /* Do not move this line */
- irlap_next_state( self, LAP_XMIT_P);
+ irlap_wait_min_turn_around(self, &self->qos_tx);
+ /*
+ * Important to switch state before calling
+ * upper layers
+ */
+ irlap_next_state(self, LAP_XMIT_P);
- irlap_data_indication( self, skb);
+ irlap_data_indication(self, skb);
+
+ /* This is the last frame */
+ irlap_start_poll_timer(self, self->poll_timeout);
}
break;
/*
* Unexpected next to send (Ns)
*/
- if (( ns_status == NS_UNEXPECTED) &&
- ( nr_status == NR_EXPECTED))
+ if ((ns_status == NS_UNEXPECTED) && (nr_status == NR_EXPECTED))
{
if ( !info->pf) {
irlap_update_nr_received( self, info->nr);
/*
* Unexpected next to receive (Nr)
*/
- if (( ns_status == NS_EXPECTED) &&
- ( nr_status == NR_UNEXPECTED))
+ if ((ns_status == NS_EXPECTED) && (nr_status == NR_UNEXPECTED))
{
if ( info->pf) {
self->vr = (self->vr + 1) % 8;
* Unexpected next to send (Ns) and next to receive (Nr)
* Not documented by IrLAP!
*/
- if (( ns_status == NS_UNEXPECTED) &&
- ( nr_status == NR_UNEXPECTED))
+ if ((ns_status == NS_UNEXPECTED) &&
+ (nr_status == NR_UNEXPECTED))
{
DEBUG( 4, "IrLAP: unexpected nr and ns!\n");
if ( info->pf) {
/*
* Invalid NR or NS
*/
- if (( nr_status == NR_INVALID) || ( ns_status == NS_INVALID)) {
- if ( info->pf) {
- del_timer( &self->final_timer);
+ if ((nr_status == NR_INVALID) || (ns_status == NS_INVALID)) {
+ if (info->pf) {
+ del_timer(&self->final_timer);
- irlap_next_state( self, LAP_RESET_WAIT);
+ irlap_next_state(self, LAP_RESET_WAIT);
- irlap_disconnect_indication( self, LAP_RESET_INDICATION);
+ irlap_disconnect_indication(self, LAP_RESET_INDICATION);
self->xmitflag = TRUE;
} else {
- del_timer( &self->final_timer);
+ del_timer(&self->final_timer);
- irlap_disconnect_indication( self, LAP_RESET_INDICATION);
+ irlap_disconnect_indication(self, LAP_RESET_INDICATION);
self->xmitflag = FALSE;
}
break;
}
DEBUG(1, __FUNCTION__ "(), Not implemented!\n");
- DEBUG(1, __FUNCTION__ "(), event=%s, ns_status=%d, nr_status=%d\n",
- irlap_event[ event], ns_status, nr_status);
+ DEBUG(1, __FUNCTION__
+ "(), event=%s, ns_status=%d, nr_status=%d\n",
+ irlap_event[ event], ns_status, nr_status);
break;
case RECV_UI_FRAME:
/* poll bit cleared? */
- if ( !info->pf) {
- irlap_unit_data_indication( self, skb);
- irlap_next_state( self, LAP_NRM_P);
+ if (!info->pf) {
+ irlap_unit_data_indication(self, skb);
+ irlap_next_state(self, LAP_NRM_P);
} else {
+ del_timer(&self->final_timer);
+ irlap_unit_data_indication(self, skb);
+ irlap_start_poll_timer(self, self->poll_timeout);
+ }
+ break;
+ case RECV_RR_RSP:
+ DEBUG(4, __FUNCTION__ "(), RECV_RR_FRAME: "
+ "Retrans:%d, nr=%d, va=%d, vs=%d, vr=%d\n",
+ self->retry_count, info->nr, self->va, self->vs,
+ self->vr);
+
+ ASSERT(info != NULL, return -1;);
+
+ /*
+ * If you get a RR, the remote isn't busy anymore,
+ * no matter what the NR
+ */
+ self->remote_busy = FALSE;
+
+ /*
+ * Nr as expected?
+ */
+ ret = irlap_validate_nr_received(self, info->nr);
+ if (ret == NR_EXPECTED) {
+ /* Stop final timer */
+ del_timer(&self->final_timer);
+
+ /* Update Nr received */
+ irlap_update_nr_received(self, info->nr);
+
+ /*
+ * Got expected NR, so reset the retry_count. This
+ * is not done by the IrLAP standard , which is
+ * strange! DB.
+ */
+ self->retry_count = 0;
+ irlap_wait_min_turn_around(self, &self->qos_tx);
+
+ irlap_next_state(self, LAP_XMIT_P);
+
+ /* Start poll timer */
+ irlap_start_poll_timer(self, self->poll_timeout);
+ } else if (ret == NR_UNEXPECTED) {
+ ASSERT( info != NULL, return -1;);
+ /*
+ * Unexpected nr!
+ */
+
+ /* Update Nr received */
+ irlap_update_nr_received( self, info->nr);
+
+ DEBUG( 4, "RECV_RR_FRAME: Retrans:%d, nr=%d, va=%d, "
+ "vs=%d, vr=%d\n",
+ self->retry_count, info->nr, self->va,
+ self->vs, self->vr);
+
+ /* Resend rejected frames */
+ irlap_resend_rejected_frames( self, CMD_FRAME);
+
+ /*
+ * Start only if not running, DB
+ * TODO: Should this one be here?
+ */
+ /* if ( !self->final_timer.prev) */
+/* irda_start_timer( FINAL_TIMER, self->final_timeout); */
+
+ /* Keep state */
+ irlap_next_state( self, LAP_NRM_P);
+ } else if (ret == NR_INVALID) {
+ DEBUG(1, "irlap_state_nrm_p: received RR with "
+ "invalid nr !\n");
del_timer( &self->final_timer);
- irlap_unit_data_indication( self, skb);
- irlap_start_poll_timer( self, self->poll_timeout);
+
+ irlap_next_state( self, LAP_RESET_WAIT);
+
+ irlap_disconnect_indication( self,
+ LAP_RESET_INDICATION);
+ self->xmitflag = TRUE;
}
+ if (skb)
+ dev_kfree_skb( skb);
+ break;
+ case RECV_RNR_FRAME:
+ DEBUG( 4, "irlap_state_nrm_p: RECV_RNR_FRAME: Retrans:%d, "
+ "nr=%d, va=%d, vs=%d, vr=%d\n",
+ self->retry_count, info->nr, self->va, self->vs,
+ self->vr);
+
+ ASSERT( info != NULL, return -1;);
+
+ /* Stop final timer */
+ del_timer( &self->final_timer);
+ self->remote_busy = TRUE;
+
+ /* Update Nr received */
+ irlap_update_nr_received( self, info->nr);
+
+ irlap_next_state( self, LAP_XMIT_P);
+
+ /* Start poll timer */
+ irlap_start_poll_timer( self, self->poll_timeout);
+
+ dev_kfree_skb( skb);
break;
case RECV_FRMR_RSP:
del_timer( &self->final_timer);
* awaiting reset of disconnect request.
*
*/
-int irlap_state_reset_wait( struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
+static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event,
+ struct sk_buff *skb, struct irlap_info *info)
{
int ret = 0;
* reply.
*
*/
-int irlap_state_reset( struct irlap_cb *self, IRLAP_EVENT event,
- struct sk_buff *skb, struct irlap_info *info)
+static int irlap_state_reset( struct irlap_cb *self, IRLAP_EVENT event,
+ struct sk_buff *skb, struct irlap_info *info)
{
int ret = 0;
- DEBUG( 3, __FUNCTION__ "(), event = %s\n", irlap_event[event]);
+ DEBUG(3, __FUNCTION__ "(), event = %s\n", irlap_event[event]);
- ASSERT( self != NULL, return -1;);
- ASSERT( self->magic == LAP_MAGIC, return -1;);
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == LAP_MAGIC, return -1;);
- switch( event) {
+ switch(event) {
case RECV_DISC_FRAME:
- del_timer( &self->final_timer);
+ del_timer(&self->final_timer);
irlap_apply_default_connection_parameters( self);
irlap_reset_confirm();
self->remote_busy = FALSE;
- irlap_start_poll_timer( self, self->poll_timeout);
+
irlap_next_state( self, LAP_XMIT_P);
+
+ irlap_start_poll_timer( self, self->poll_timeout);
break;
case FINAL_TIMER_EXPIRED:
if ( self->retry_count < 3) {
ASSERT( self != NULL, return -1;);
ASSERT( self->magic == LAP_MAGIC, return -1;);
- switch( event) {
- case RECV_RR_CMD:
- self->retry_count = 0;
-
- /*
- * Nr as expected?
- */
- nr_status = irlap_validate_nr_received( self, info->nr);
- if ( nr_status == NR_EXPECTED) {
- if (( skb_queue_len( &self->tx_list) > 0) &&
- ( self->window > 0)) {
- self->remote_busy = FALSE;
-
- /* Update Nr received */
- irlap_update_nr_received( self, info->nr);
- del_timer( &self->wd_timer);
-
- irlap_wait_min_turn_around( self, &self->qos_tx);
- irlap_next_state( self, LAP_XMIT_S);
- } else {
- self->remote_busy = FALSE;
- /* Update Nr received */
- irlap_update_nr_received( self, info->nr);
- irlap_wait_min_turn_around( self, &self->qos_tx);
-
- irlap_send_rr_frame( self, RSP_FRAME);
-
- irlap_start_wd_timer( self, self->wd_timeout);
- irlap_next_state( self, LAP_NRM_S);
- }
- } else if ( nr_status == NR_UNEXPECTED) {
- self->remote_busy = FALSE;
- irlap_update_nr_received( self, info->nr);
- irlap_resend_rejected_frames( self, RSP_FRAME);
-
- irlap_start_wd_timer( self, self->wd_timeout);
-
- /* Keep state */
- irlap_next_state( self, LAP_NRM_S);
- } else {
- DEBUG(1, __FUNCTION__ "(), invalid nr not implemented!\n");
- }
- if ( skb)
- dev_kfree_skb( skb);
-
- break;
- case RECV_I_CMD:
+ switch(event) {
+ case RECV_I_CMD: /* Optimize for the common case */
/* FIXME: must check for remote_busy below */
DEBUG( 4, __FUNCTION__ "(), event=%s nr=%d, vs=%d, ns=%d, "
"vr=%d, pf=%d\n", irlap_event[event], info->nr,
irlap_next_state( self, LAP_NRM_S);
}
}
+ break;
+ case RECV_RR_CMD:
+ self->retry_count = 0;
+
+ /*
+ * Nr as expected?
+ */
+ nr_status = irlap_validate_nr_received( self, info->nr);
+ if ( nr_status == NR_EXPECTED) {
+ if (( skb_queue_len( &self->tx_list) > 0) &&
+ ( self->window > 0)) {
+ self->remote_busy = FALSE;
+
+ /* Update Nr received */
+ irlap_update_nr_received( self, info->nr);
+ del_timer( &self->wd_timer);
+
+ irlap_wait_min_turn_around( self, &self->qos_tx);
+ irlap_next_state( self, LAP_XMIT_S);
+ } else {
+ self->remote_busy = FALSE;
+ /* Update Nr received */
+ irlap_update_nr_received( self, info->nr);
+ irlap_wait_min_turn_around( self, &self->qos_tx);
+
+ irlap_send_rr_frame( self, RSP_FRAME);
+
+ irlap_start_wd_timer( self, self->wd_timeout);
+ irlap_next_state( self, LAP_NRM_S);
+ }
+ } else if ( nr_status == NR_UNEXPECTED) {
+ self->remote_busy = FALSE;
+ irlap_update_nr_received( self, info->nr);
+ irlap_resend_rejected_frames( self, RSP_FRAME);
+
+ irlap_start_wd_timer( self, self->wd_timeout);
+
+ /* Keep state */
+ irlap_next_state( self, LAP_NRM_S);
+ } else {
+ DEBUG(1, __FUNCTION__ "(), invalid nr not implemented!\n");
+ }
+ if ( skb)
+ dev_kfree_skb( skb);
+
break;
case RECV_SNRM_CMD:
del_timer( &self->wd_timer);
return ret;
}
-
-
-
-
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Tue Aug 19 10:27:26 1997
- * Modified at: Tue Apr 6 16:35:21 1999
+ * Modified at: Fri Apr 23 09:30:42 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Resrved.
* need to do this since it's per packet relevant information.
*
*/
-void irlap_insert_mtt( struct irlap_cb *self, struct sk_buff *skb)
+static inline void irlap_insert_mtt(struct irlap_cb *self, struct sk_buff *skb)
{
struct irlap_skb_cb *cb;
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == LAP_MAGIC, return;);
-
cb = (struct irlap_skb_cb *) skb->cb;
cb->magic = LAP_MAGIC;
/* Reset XBOF's delay (used only for getting min turn time) */
self->xbofs_delay = 0;
-
- DEBUG( 4, __FUNCTION__ "(), using %d xbofs\n", cb->xbofs);
}
/*
*/
void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb)
{
- /* Some init stuff */
+ /* Some common init stuff */
skb->dev = self->netdev;
skb->h.raw = skb->nh.raw = skb->mac.raw = skb->data;
skb->protocol = htons(ETH_P_IRDA);
+ skb->priority = TC_PRIO_BESTEFFORT;
/*
* Insert MTT (min. turn time) into skb, so that the device driver
* Build and transmit RR (Receive Ready) frame. Notice that it is currently
* only possible to send RR frames with the poll bit set.
*/
-void irlap_send_rr_frame( struct irlap_cb *self, int command)
+void irlap_send_rr_frame(struct irlap_cb *self, int command)
{
- struct sk_buff *skb = NULL;
+ struct sk_buff *skb;
__u8 *frame;
-
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == LAP_MAGIC, return;);
skb = dev_alloc_skb(32);
if (!skb)
frame[1] = RR | PF_BIT | (self->vr << 5);
- DEBUG(4, __FUNCTION__ "(), vr=%d, %ld\n", self->vr, jiffies);
-
irlap_queue_xmit(self, skb);
}
struct sk_buff *skb,
struct irlap_info *info, int command)
{
- __u8 *frame;
-
- frame = skb->data;
- info->nr = frame[1] >> 5;
-
- DEBUG(4, __FUNCTION__ "(), nr=%d, %ld\n", info->nr, jiffies);
-
- /*
- * Make sure the state-machine is in the right state for receiving,
- * if not, then we just discard the received frame for now!
- * TODO: check if we should queue this frame, or make tty tell that
- * it is receiving frames until the frame is delivered instead of
- * until it is outside a frame.
- */
-#if 0
- if ((self->state != LAP_NRM_P) && (self->state != LAP_NRM_S)) {
- DEBUG(0, __FUNCTION__ "(), Wrong state, dropping frame!\n");
- dev_kfree_skb(skb);
- return;
- }
-#endif
+ info->nr = skb->data[1] >> 5;
/* Check if this is a command or a response frame */
if (command)
{
struct sk_buff *tx_skb;
- DEBUG( 4, __FUNCTION__ "()\n");
-
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == LAP_MAGIC, return;);
- ASSERT( skb != NULL, return;);
-
- /* Initialize variables */
- tx_skb = NULL;
-
- if ( skb->data[1] == I_FRAME) {
+ if (skb->data[1] == I_FRAME) {
/*
* Insert frame sequence number (Vs) in control field before
*/
skb->data[1] = I_FRAME | (self->vs << 1);
- /* * Copy buffer */
- tx_skb = skb_clone( skb, GFP_ATOMIC);
- if ( tx_skb == NULL) {
- dev_kfree_skb( skb);
+ /* Copy buffer */
+ tx_skb = skb_clone(skb, GFP_ATOMIC);
+ if (tx_skb == NULL) {
+ dev_kfree_skb(skb);
return;
}
* make sure the skb->sk accounting of memory usage is sane
*/
if (skb->sk != NULL)
- skb_set_owner_w( tx_skb, skb->sk);
+ skb_set_owner_w(tx_skb, skb->sk);
/*
* Insert frame in store, in case of retransmissions
*/
- skb_queue_tail( &self->wx_list, skb);
+ skb_queue_tail(&self->wx_list, skb);
self->vs = (self->vs + 1) % 8;
self->ack_required = FALSE;
irlap_send_i_frame( self, tx_skb, CMD_FRAME);
} else {
DEBUG( 4, __FUNCTION__ "(), sending unreliable frame\n");
- irlap_send_ui_frame( self, skb, CMD_FRAME);
+ irlap_send_ui_frame(self, skb, CMD_FRAME);
self->window -= 1;
}
}
{
struct sk_buff *tx_skb;
- DEBUG( 4, __FUNCTION__ "()\n");
-
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == LAP_MAGIC, return;);
- ASSERT( skb != NULL, return;);
-
- /* Initialize variables */
- tx_skb = NULL;
-
/* Is this reliable or unreliable data? */
- if ( skb->data[1] == I_FRAME) {
+ if (skb->data[1] == I_FRAME) {
/*
* Insert frame sequence number (Vs) in control field before
skb->data[1] = I_FRAME | (self->vs << 1);
/* Copy buffer */
- tx_skb = skb_clone( skb, GFP_ATOMIC);
- if ( tx_skb == NULL) {
- dev_kfree_skb( skb);
+ tx_skb = skb_clone(skb, GFP_ATOMIC);
+ if (tx_skb == NULL) {
+ dev_kfree_skb(skb);
return;
}
* make sure the skb->sk accounting of memory usage is sane
*/
if (skb->sk != NULL)
- skb_set_owner_w( tx_skb, skb->sk);
+ skb_set_owner_w(tx_skb, skb->sk);
/*
* Insert frame in store, in case of retransmissions
*/
- skb_queue_tail( &self->wx_list, skb);
+ skb_queue_tail(&self->wx_list, skb);
/*
* Set poll bit if necessary. We do this to the copied
* skb, since retransmitted need to set or clear the poll
- * bit depending on when * they are sent.
+ * bit depending on when they are sent.
*/
/* Stop P timer */
- del_timer( &self->poll_timer);
+ del_timer(&self->poll_timer);
tx_skb->data[1] |= PF_BIT;
self->ack_required = FALSE;
self->window = self->window_size;
- irlap_start_final_timer( self, self->final_timeout);
+ irlap_start_final_timer(self, self->final_timeout);
- irlap_send_i_frame( self, tx_skb, CMD_FRAME);
+ irlap_send_i_frame(self, tx_skb, CMD_FRAME);
} else {
- DEBUG( 4, __FUNCTION__ "(), sending unreliable frame\n");
+ DEBUG(4, __FUNCTION__ "(), sending unreliable frame\n");
- del_timer( &self->poll_timer);
+ del_timer(&self->poll_timer);
- if ( self->ack_required) {
- irlap_send_ui_frame( self, skb, CMD_FRAME);
- irlap_send_rr_frame( self, CMD_FRAME);
+ if (self->ack_required) {
+ irlap_send_ui_frame(self, skb, CMD_FRAME);
+ irlap_send_rr_frame(self, CMD_FRAME);
self->ack_required = FALSE;
} else {
skb->data[1] |= PF_BIT;
- irlap_send_ui_frame( self, skb, CMD_FRAME);
+ irlap_send_ui_frame(self, skb, CMD_FRAME);
}
self->window = self->window_size;
- irlap_start_final_timer( self, self->final_timeout);
+ irlap_start_final_timer(self, self->final_timeout);
}
}
ASSERT( self != NULL, return;);
ASSERT( self->magic == LAP_MAGIC, return;);
- DEBUG( 4, __FUNCTION__ "(), retry_count=%d\n", self->retry_count);
+ DEBUG(2, __FUNCTION__ "(), retry_count=%d\n", self->retry_count);
/* Initialize variables */
skb = tx_skb = NULL;
while ( skb != NULL) {
irlap_wait_min_turn_around( self, &self->qos_tx);
- tx_skb = skb_clone( skb, GFP_ATOMIC);
+ /* We copy the skb to be retransmitted since we will have to
+ * modify it. Cloning will confuse packet sniffers
+ */
+ /* tx_skb = skb_clone( skb, GFP_ATOMIC); */
+ tx_skb = skb_copy(skb, GFP_ATOMIC);
if ( tx_skb == NULL) {
/* Unlink tx_skb from list */
tx_skb->next = tx_skb->prev = NULL;
* If send window > 1 then send frame with pf
* bit cleared
*/
- if (( self->window > 1) &&
- skb_queue_len( &self->tx_list) > 0)
+ if ((self->window > 1) &&
+ skb_queue_len(&self->tx_list) > 0)
{
- irlap_send_data_primary( self, skb);
+ irlap_send_data_primary(self, skb);
} else {
- irlap_send_data_primary_poll( self, skb);
+ irlap_send_data_primary_poll(self, skb);
}
}
}
/* Insert next to receive (Vr) */
frame[1] |= (self->vr << 5); /* insert nr */
+#if 0
+ {
+ int ns;
+ ns = (frame[1] >> 1) & 0x07; /* Next to send */
+
+ DEBUG(0, __FUNCTION__ "(), ns=%d\n", ns);
+ }
+#endif
+
irlap_queue_xmit(self, skb);
}
struct sk_buff *skb,
struct irlap_info *info, int command)
{
- __u8 *frame;
-
- frame = skb->data;
-
- info->nr = frame[1] >> 5; /* Next to receive */
- info->pf = frame[1] & PF_BIT; /* Final bit */
- info->ns = (frame[1] >> 1) & 0x07; /* Next to send */
+ info->nr = skb->data[1] >> 5; /* Next to receive */
+ info->pf = skb->data[1] & PF_BIT; /* Final bit */
+ info->ns = (skb->data[1] >> 1) & 0x07; /* Next to send */
DEBUG(4, __FUNCTION__"(), ns=%d, nr=%d, pf=%d, %ld\n",
info->ns, info->nr, info->pf>>4, jiffies);
- /*
- * Make sure the state-machine is in the right state for receiving,
- * if not, then we just discard the received frame for now!
- * TODO: check if we should queue this frame, or make tty tell that
- * it is receiving frames until the frame is delivered instead of
- * until it is outside a frame.
- */
- if ((self->state != LAP_NRM_P) && ( self->state != LAP_NRM_S)) {
- DEBUG(0, __FUNCTION__ "(), Wrong state, dropping frame!\n");
- dev_kfree_skb(skb);
- return;
- }
-
/* Check if this is a command or a response frame */
if (command)
irlap_do_event(self, RECV_I_CMD, skb, info);
ASSERT( self != NULL, return -1;);
ASSERT( self->magic == LAP_MAGIC, return -1;);
- ASSERT(( skb != NULL) && (skb->len > 1), return -1;);
+ ASSERT( skb->len > 1, return -1;);
frame = skb->data;
* Status: Stable.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Aug 17 20:54:32 1997
- * Modified at: Wed Apr 7 17:31:48 1999
+ * Modified at: Fri Apr 23 09:13:24 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>,
*/
void irlmp_discovery_request(int nslots)
{
-
DEBUG(4, __FUNCTION__ "(), nslots=%d\n", nslots);
+ /* Check if user wants to override the default */
+ if (nslots == DISCOVERY_DEFAULT_SLOTS)
+ nslots = sysctl_discovery_slots;
+
/*
* If discovery is already running, then just return the current
* discovery log
* Send some data to peer device
*
*/
-void irlmp_data_request( struct lsap_cb *self, struct sk_buff *skb)
+void irlmp_data_request(struct lsap_cb *self, struct sk_buff *skb)
{
- DEBUG(4, __FUNCTION__ "()\n");
-
ASSERT(skb != NULL, return;);
ASSERT(self != NULL, return;);
ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
* Got data from LAP layer so pass it up to upper layer
*
*/
-void irlmp_data_indication(struct lsap_cb *self, struct sk_buff *skb)
+inline void irlmp_data_indication(struct lsap_cb *self, struct sk_buff *skb)
{
- DEBUG(4, __FUNCTION__ "()\n");
-
- ASSERT(self != NULL, return;);
- ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
- ASSERT(skb != NULL, return;);
-
/* Hide LMP header from layer above */
skb_pull(skb, LMP_HEADER);
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Mon Aug 4 20:40:53 1997
- * Modified at: Thu Apr 8 16:26:41 1999
+ * Modified at: Fri Apr 23 08:57:23 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>,
};
/* Do connection control events */
-void irlmp_do_lsap_event( struct lsap_cb *self, IRLMP_EVENT event,
- struct sk_buff *skb)
+void irlmp_do_lsap_event(struct lsap_cb *self, IRLMP_EVENT event,
+ struct sk_buff *skb)
{
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == LMP_LSAP_MAGIC, return;);
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
- DEBUG( 4, __FUNCTION__ "(), EVENT = %s, STATE = %s\n",
- irlmp_event[ event], irlmp_state[ self->lsap_state]);
+ DEBUG(4, __FUNCTION__ "(), EVENT = %s, STATE = %s\n",
+ irlmp_event[ event], irlmp_state[ self->lsap_state]);
- (*lsap_state[ self->lsap_state]) ( self, event, skb);
+ (*lsap_state[self->lsap_state]) (self, event, skb);
}
/*
* Do IrLAP control events
*
*/
-void irlmp_do_lap_event( struct lap_cb *self, IRLMP_EVENT event,
- struct sk_buff *skb)
+void irlmp_do_lap_event(struct lap_cb *self, IRLMP_EVENT event,
+ struct sk_buff *skb)
{
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == LMP_LAP_MAGIC, return;);
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == LMP_LAP_MAGIC, return;);
- DEBUG( 4, __FUNCTION__ "(), EVENT = %s, STATE = %s\n",
- irlmp_event[event],
- irlmp_state[self->lap_state]);
+ DEBUG(4, __FUNCTION__ "(), EVENT = %s, STATE = %s\n",
+ irlmp_event[event],
+ irlmp_state[self->lap_state]);
- (*lap_state[ self->lap_state]) ( self, event, skb);
+ (*lap_state[self->lap_state]) (self, event, skb);
}
void irlmp_discovery_timer_expired( unsigned long data)
{
-/* struct irlmp_cb *self = ( struct irlmp_cb *) data; */
-
DEBUG(4, "IrLMP, discovery timer expired!\n");
if (sysctl_discovery)
* ACTIVE, IrLAP connection is active
*
*/
-static void irlmp_state_active( struct lap_cb *self, IRLMP_EVENT event,
- struct sk_buff *skb)
+static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event,
+ struct sk_buff *skb)
{
struct lsap_cb *lsap;
struct lsap_cb *lsap_current;
/* Keep state */
break;
case LM_LAP_DISCONNECT_REQUEST:
- DEBUG( 4, __FUNCTION__ "(), LM_LAP_DISCONNECT_REQUEST\n");
+ DEBUG(4, __FUNCTION__ "(), LM_LAP_DISCONNECT_REQUEST\n");
/*
* Need to find out if we should close IrLAP or not. If there
break;
case LM_LAP_IDLE_TIMEOUT:
if (hashbin_get_size(self->lsaps) == 0) {
- DEBUG(0, __FUNCTION__
+ DEBUG(2, __FUNCTION__
"(), no more LSAPs so time to close IrLAP\n");
irlmp_next_lap_state(self, LAP_STANDBY);
}
break;
case LM_LAP_DISCONNECT_INDICATION:
- DEBUG( 4, __FUNCTION__ "(), IRLAP_DISCONNECT_INDICATION\n");
+ DEBUG(4, __FUNCTION__ "(), IRLAP_DISCONNECT_INDICATION\n");
irlmp_next_lap_state( self, LAP_STANDBY);
* DATA_TRANSFER_READY
*
*/
-static void irlmp_state_dtr( struct lsap_cb *self, IRLMP_EVENT event,
- struct sk_buff *skb)
+static void irlmp_state_dtr(struct lsap_cb *self, IRLMP_EVENT event,
+ struct sk_buff *skb)
{
LM_REASON reason;
- DEBUG( 4, __FUNCTION__ "()\n");
+ DEBUG(4, __FUNCTION__ "()\n");
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == LMP_LSAP_MAGIC, return;);
- ASSERT( self->lap != NULL, return;);
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
+ ASSERT(self->lap != NULL, return;);
- switch( event) {
+ switch (event) {
+ case LM_DATA_REQUEST: /* Optimize for the common case */
+ irlmp_send_data_pdu(self->lap, self->dlsap_sel,
+ self->slsap_sel, FALSE, skb);
+ /* irlmp_next_lsap_state( DATA_TRANSFER_READY, info->handle);*/
+ break;
+ case LM_DATA_INDICATION: /* Optimize for the common case */
+ irlmp_data_indication(self, skb);
+ /* irlmp_next_lsap_state( DATA_TRANSFER_READY, info->handle);*/
+ break;
+ case LM_UDATA_REQUEST:
+ ASSERT(skb != NULL, return;);
+ irlmp_send_data_pdu(self->lap, self->dlsap_sel,
+ self->slsap_sel, TRUE, skb);
+ break;
+ case LM_UDATA_INDICATION:
+ irlmp_udata_indication(self, skb);
+ /* irlmp_next_lsap_state( DATA_TRANSFER_READY, info->handle);*/
+ break;
case LM_CONNECT_REQUEST:
DEBUG(0, __FUNCTION__ "(), LM_CONNECT_REQUEST, "
"error, LSAP already connected\n");
/* Keep state */
break;
case LM_DISCONNECT_REQUEST:
- ASSERT( skb != NULL, return;);
+ ASSERT(skb != NULL, return;);
- irlmp_send_lcf_pdu( self->lap, self->dlsap_sel,
- self->slsap_sel, DISCONNECT, skb);
- irlmp_next_lsap_state( self, LSAP_DISCONNECTED);
+ irlmp_send_lcf_pdu(self->lap, self->dlsap_sel,
+ self->slsap_sel, DISCONNECT, skb);
+ irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
/* Try to close the LAP connection if its still there */
- if ( self->lap) {
- DEBUG( 4, __FUNCTION__ "(), trying to close IrLAP\n");
- irlmp_do_lap_event( self->lap,
- LM_LAP_DISCONNECT_REQUEST,
- NULL);
+ if (self->lap) {
+ DEBUG(4, __FUNCTION__ "(), trying to close IrLAP\n");
+ irlmp_do_lap_event(self->lap,
+ LM_LAP_DISCONNECT_REQUEST,
+ NULL);
}
-
- break;
- case LM_DATA_REQUEST:
- ASSERT( skb != NULL, return;);
- irlmp_send_data_pdu( self->lap, self->dlsap_sel,
- self->slsap_sel, FALSE, skb);
- /* irlmp_next_lsap_state( DATA_TRANSFER_READY, info->handle);*/
- break;
- case LM_UDATA_REQUEST:
- ASSERT( skb != NULL, return;);
- irlmp_send_data_pdu( self->lap, self->dlsap_sel,
- self->slsap_sel, TRUE, skb);
- break;
- case LM_DATA_INDICATION:
- irlmp_data_indication( self, skb);
- /* irlmp_next_lsap_state( DATA_TRANSFER_READY, info->handle);*/
- break;
- case LM_UDATA_INDICATION:
- irlmp_udata_indication( self, skb);
- /* irlmp_next_lsap_state( DATA_TRANSFER_READY, info->handle);*/
break;
case LM_LAP_DISCONNECT_INDICATION:
- irlmp_next_lsap_state( self, LSAP_DISCONNECTED);
+ irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
- reason = irlmp_convert_lap_reason( self->lap->reason);
+ reason = irlmp_convert_lap_reason(self->lap->reason);
- irlmp_disconnect_indication( self, reason, NULL);
+ irlmp_disconnect_indication(self, reason, NULL);
break;
case LM_DISCONNECT_INDICATION:
- irlmp_next_lsap_state( self, LSAP_DISCONNECTED);
+ irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
- ASSERT( self->lap != NULL, return;);
- ASSERT( self->lap->magic == LMP_LAP_MAGIC, return;);
+ ASSERT(self->lap != NULL, return;);
+ ASSERT(self->lap->magic == LMP_LAP_MAGIC, return;);
ASSERT(skb != NULL, return;);
ASSERT(skb->len > 3, return;);
reason = skb->data[3];
/* Try to close the LAP connection */
- DEBUG( 4, __FUNCTION__ "(), trying to close IrLAP\n");
+ DEBUG(4, __FUNCTION__ "(), trying to close IrLAP\n");
irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
- irlmp_disconnect_indication( self, reason, skb);
+ irlmp_disconnect_indication(self, reason, skb);
break;
default:
- DEBUG( 4, __FUNCTION__ "(), Unknown event %d\n", event);
+ DEBUG(4, __FUNCTION__ "(), Unknown event %d\n", event);
break;
}
}
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Tue Aug 19 02:09:59 1997
- * Modified at: Tue Apr 6 18:31:11 1999
+ * Modified at: Fri Apr 23 09:12:23 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>
#include <net/irda/irlmp_frame.h>
#include <net/irda/discovery.h>
-static struct lsap_cb *irlmp_find_lsap( struct lap_cb *self, __u8 dlsap,
- __u8 slsap, int status, hashbin_t *);
+static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap,
+ __u8 slsap, int status, hashbin_t *);
-inline void irlmp_send_data_pdu( struct lap_cb *self, __u8 dlsap, __u8 slsap,
- int expedited, struct sk_buff *skb)
+inline void irlmp_send_data_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
+ int expedited, struct sk_buff *skb)
{
__u8 *frame;
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == LMP_LAP_MAGIC, return;);
- ASSERT( skb != NULL, return;);
-
frame = skb->data;
frame[0] = dlsap;
frame[1] = slsap;
- if ( expedited) {
+ if (expedited) {
DEBUG( 4, __FUNCTION__ "(), sending expedited data\n");
irlap_data_request( self->irlap, skb, FALSE);
- } else {
- DEBUG( 4, __FUNCTION__ "(), sending reliable data\n");
- irlap_data_request( self->irlap, skb, TRUE);
- }
+ } else
+ irlap_data_request( self->irlap, skb, TRUE);
}
/*
* Used by IrLAP to pass received data frames to IrLMP layer
*
*/
-void irlmp_link_data_indication( struct lap_cb *self, int reliable,
- struct sk_buff *skb)
+void irlmp_link_data_indication(struct lap_cb *self, int reliable,
+ struct sk_buff *skb)
{
- __u8 *fp;
- __u8 slsap_sel; /* Source (this) LSAP address */
- __u8 dlsap_sel; /* Destination LSAP address */
struct lsap_cb *lsap;
+ __u8 slsap_sel; /* Source (this) LSAP address */
+ __u8 dlsap_sel; /* Destination LSAP address */
+ __u8 *fp;
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == LMP_LAP_MAGIC, return;);
- ASSERT( skb != NULL, return;);
- ASSERT( skb->len > 2, return;);
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == LMP_LAP_MAGIC, return;);
+ ASSERT(skb->len > 2, return;);
fp = skb->data;
* Check if this is an incoming connection, since we must deal with
* it in a different way than other established connections.
*/
- if ((fp[0] & CONTROL_BIT) && ( fp[2] == CONNECT_CMD)) {
+ if ((fp[0] & CONTROL_BIT) && (fp[2] == CONNECT_CMD)) {
DEBUG(3,"Incoming connection, source LSAP=%d, dest LSAP=%d\n",
slsap_sel, dlsap_sel);
break;
}
} else if (reliable == LAP_RELIABLE) {
- /* Must be pure data */
- irlmp_do_lsap_event( lsap, LM_DATA_INDICATION, skb);
+ /* Optimize and bypass the state machine if possible */
+ if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY)
+ irlmp_data_indication(lsap, skb);
+ else
+ irlmp_do_lsap_event(lsap, LM_DATA_INDICATION, skb);
} else if (reliable == LAP_UNRELIABLE) {
- irlmp_do_lsap_event( lsap, LM_UDATA_INDICATION, skb);
+ /* Optimize and bypass the state machine if possible */
+ if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY)
+ irlmp_data_indication(lsap, skb);
+ else
+ irlmp_do_lsap_event(lsap, LM_UDATA_INDICATION, skb);
}
}
irlmp_add_discovery_log(irlmp->cachelog, log);
irlmp_do_lap_event(self, LM_LAP_DISCOVERY_CONFIRM, NULL);
-
- DEBUG( 4, __FUNCTION__ "() -->\n");
}
#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
{
struct lsap_cb *lsap;
- ASSERT( self != NULL, return NULL;);
- ASSERT( self->magic == LMP_LAP_MAGIC, return NULL;);
-
/*
* Optimize for the common case. We assume that the last frame
* received is in the same connection as the last one, so check in
* cache first to avoid the linear search
*/
#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
- if (( irlmp->cache.valid) &&
- ( irlmp->cache.slsap_sel == slsap_sel) &&
- ( irlmp->cache.dlsap_sel == dlsap_sel))
+ if ((irlmp->cache.valid) &&
+ (irlmp->cache.slsap_sel == slsap_sel) &&
+ (irlmp->cache.dlsap_sel == dlsap_sel))
{
return (irlmp->cache.lsap);
}
#endif
- lsap = ( struct lsap_cb *) hashbin_get_first(queue);
- while ( lsap != NULL) {
+ lsap = (struct lsap_cb *) hashbin_get_first(queue);
+ while (lsap != NULL) {
/*
* If this is an incomming connection, then the destination
* LSAP selector may have been specified as LM_ANY so that
* any client can connect. In that case we only need to check
* if the source LSAP (in our view!) match!
*/
- if (( status == CONNECT_CMD) &&
- ( lsap->slsap_sel == slsap_sel) &&
- ( lsap->dlsap_sel == LSAP_ANY))
+ if ((status == CONNECT_CMD) &&
+ (lsap->slsap_sel == slsap_sel) &&
+ (lsap->dlsap_sel == LSAP_ANY))
{
- DEBUG( 4,"Incoming connection: Setting dlsap_sel=%d\n",
- dlsap_sel);
lsap->dlsap_sel = dlsap_sel;
#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
/*
* Check if source LSAP and dest LSAP selectors match.
*/
- if (( lsap->slsap_sel == slsap_sel) &&
- ( lsap->dlsap_sel == dlsap_sel))
+ if ((lsap->slsap_sel == slsap_sel) &&
+ (lsap->dlsap_sel == dlsap_sel))
{
#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
- irlmp_update_cache( lsap);
+ irlmp_update_cache(lsap);
#endif
return lsap;
}
- lsap = ( struct lsap_cb *) hashbin_get_next( queue);
+ lsap = ( struct lsap_cb *) hashbin_get_next(queue);
}
/* Sorry not found! */
* Fixed to match changes in iriap.h, DB.
*
*/
-void irlpt_client_get_value_confirm(__u16 obj_id, struct ias_value *value,
- void *priv)
+void irlpt_client_get_value_confirm(int result, __u16 obj_id,
+ struct ias_value *value, void *priv)
{
struct irlpt_info info;
struct irlpt_cb *self;
ASSERT( self->magic == IRLPT_MAGIC, return;);
/* Check if request succeeded */
- if ( !value) {
+ if (result != IAS_SUCCESS) {
DEBUG( 0, __FUNCTION__ "(), got NULL value!\n");
irlpt_client_do_event( self, IAS_PROVIDER_NOT_AVAIL, NULL,
&info);
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Mon Dec 15 13:55:39 1997
- * Modified at: Mon Mar 29 09:06:52 1999
+ * Modified at: Mon Apr 12 11:31:01 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1997 Dag Brattli, All Rights Reserved.
struct irda_cb irda; /* One global instance */
#ifdef CONFIG_IRDA_DEBUG
-__u32 irda_debug = IRDA_DEBUG;
+__u32 irda_debug = IRDA_DEBUG_LEVEL;
#endif
extern void irda_proc_register(void);
extern void irda_proc_unregister(void);
-extern int irda_sysctl_register(void);
+extern int irda_sysctl_register(void);
extern void irda_sysctl_unregister(void);
extern void irda_proto_init(struct net_proto *pro);
}
#endif /* MODULE */
-/*
- * Function irda_lock (lock)
- *
- * Lock variable. Returns false if the lock is already set.
- *
- */
-inline int irda_lock(int *lock)
-{
- if (test_and_set_bit( 0, (void *) lock)) {
- DEBUG(3, __FUNCTION__
- "(), Trying to lock, already locked variable!\n");
- return FALSE;
- }
- return TRUE;
-}
-
/*
* Function irda_unlock (lock)
*
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun May 24 22:12:06 1998
- * Modified at: Mon Jan 25 13:55:54 1999
+ * Modified at: Fri Apr 23 09:46:38 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1997 Dag Brattli, All Rights Reserved.
extern int sysctl_discovery;
extern int sysctl_discovery_slots;
extern int sysctl_slot_timeout;
+extern int sysctl_fast_poll_increase;
int sysctl_compression = 0;
extern char sysctl_devname[];
{ COMPRESSION, "compression", &sysctl_compression,
sizeof(int), 0644, NULL, &proc_dointvec },
#ifdef CONFIG_IRDA_DEBUG
- { DEBUG, "debug", &irda_debug,
+ { DEBUG, "debug", &irda_debug,
+ sizeof(int), 0644, NULL, &proc_dointvec },
+#endif
+#ifdef CONFIG_IRDA_FAST_RR
+ { SLOTS, "fast_poll_increase", &sysctl_fast_poll_increase,
sizeof(int), 0644, NULL, &proc_dointvec },
#endif
{ SLOTS, "discovery_slots", &sysctl_discovery_slots,
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Aug 31 20:14:31 1997
- * Modified at: Thu Mar 25 10:27:08 1999
+ * Modified at: Sat Apr 10 10:32:21 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>,
#include <linux/init.h>
#include <asm/byteorder.h>
+#include <asm/unaligned.h>
#include <net/irda/irda.h>
#include <net/irda/irmod.h>
struct sk_buff *userdata)
{
struct sk_buff *skb;
- __u16 tmp_be;
__u8 *frame;
__u8 n;
frame[2] = 0x01; /* MaxSduSize */
frame[3] = 0x02; /* Value length */
- tmp_be = cpu_to_be16((__u16) max_sdu_size);
- memcpy(frame+4, &tmp_be, 2);
+ put_unaligned(cpu_to_be16((__u16) max_sdu_size),
+ (__u16 *)(frame+4));
} else {
/* Insert plain TTP header */
frame = skb_push(skb, TTP_HEADER);
__u32 max_seg_size, struct sk_buff *skb)
{
struct tsap_cb *self;
- __u16 tmp_cpu;
- __u8 *frame;
- __u8 n;
int parameters;
+ __u8 *frame;
__u8 plen, pi, pl;
+ __u8 n;
DEBUG(4, __FUNCTION__ "()\n");
pi = frame[2];
pl = frame[3];
- ASSERT(pl == 2, return;);
-
- memcpy(&tmp_cpu, frame+4, 2); /* Align value */
- be16_to_cpus(&tmp_cpu); /* Convert to host order */
- self->tx_max_sdu_size = tmp_cpu;
+ switch (pl) {
+ case 1:
+ self->tx_max_sdu_size = *(frame+4);
+ break;
+ case 2:
+ self->tx_max_sdu_size =
+ be16_to_cpu(get_unaligned((__u16 *)(frame+4)));
+ break;
+ case 4:
+ self->tx_max_sdu_size =
+ be32_to_cpu(get_unaligned((__u32 *)(frame+4)));
+ break;
+ default:
+ printk(KERN_ERR __FUNCTION__
+ "() illegal value length for max_sdu_size!\n");
+ self->tx_max_sdu_size = 0;
+ };
DEBUG(4, __FUNCTION__ "(), RxMaxSduSize=%d\n",
- self->tx_max_sdu_size);
+ self->tx_max_sdu_size);
}
DEBUG(4, __FUNCTION__ "() send=%d,avail=%d,remote=%d\n",
{
struct tsap_cb *self;
struct lsap_cb *lsap;
- __u16 tmp_cpu;
- __u8 *frame;
int parameters;
- int n;
+ __u8 *frame;
__u8 plen, pi, pl;
+ __u8 n;
self = (struct tsap_cb *) instance;
lsap = (struct lsap_cb *) sap;
- /* FIXME: just remove this when we know its working */
- ASSERT(max_seg_size == qos->data_size.value, return;);
-
self->max_seg_size = max_seg_size-LMP_HEADER-LAP_HEADER;
DEBUG(4, __FUNCTION__ "(), TSAP sel=%02x\n", self->stsap_sel);
parameters = frame[0] & 0x80;
if (parameters) {
- DEBUG(4, __FUNCTION__ "(), Contains parameters!\n");
+ DEBUG(3, __FUNCTION__ "(), Contains parameters!\n");
plen = frame[1];
pi = frame[2];
pl = frame[3];
- ASSERT(pl == 2, return;);
-
- memcpy(&tmp_cpu, frame+4, 2); /* Align value */
- be16_to_cpus(&tmp_cpu); /* Convert to host order */
-
- self->tx_max_sdu_size = tmp_cpu;
- DEBUG(4, __FUNCTION__ "(), MaxSduSize=%d\n",
+ switch (pl) {
+ case 1:
+ self->tx_max_sdu_size = *(frame+4);
+ break;
+ case 2:
+ self->tx_max_sdu_size =
+ be16_to_cpu(get_unaligned((__u16 *)(frame+4)));
+ break;
+ case 4:
+ self->tx_max_sdu_size =
+ be32_to_cpu(get_unaligned((__u32 *)(frame+4)));
+ break;
+ default:
+ printk(KERN_ERR __FUNCTION__
+ "() illegal value length for max_sdu_size!\n");
+ self->tx_max_sdu_size = 0;
+ };
+
+
+ DEBUG(3, __FUNCTION__ "(), MaxSduSize=%d\n",
self->tx_max_sdu_size);
}
struct sk_buff *userdata)
{
struct sk_buff *skb;
- __u32 tmp_be;
__u8 *frame;
__u8 n;
frame[2] = 0x01; /* MaxSduSize */
frame[3] = 0x02; /* Value length */
- tmp_be = cpu_to_be16((__u16)max_sdu_size);
- memcpy(frame+4, &tmp_be, 2);
+ put_unaligned(cpu_to_be16((__u16) max_sdu_size),
+ (__u16 *)(frame+4));
} else {
/* Insert TTP header */
frame = skb_push(skb, TTP_HEADER);
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Tue Sep 9 00:00:26 1997
- * Modified at: Mon Feb 1 09:56:21 1999
+ * Modified at: Mon Apr 12 11:49:24 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
qos->compression.bits &= new->compression.bits;
#endif
- irda_qos_bits_to_value( qos);
+ irda_qos_bits_to_value(qos);
}
/*
/*********************************************************************
*
* Filename: wrapper.c
- * Version: 1.0
+ * Version: 1.1
* Description: SIR wrapper layer
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Mon Aug 4 20:40:53 1997
- * Modified at: Fri Mar 26 21:52:53 1999
+ * Modified at: Wed Apr 21 12:45:55 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>,
#include <net/irda/irlap_frame.h>
#include <net/irda/irda_device.h>
-#define MIN_LENGTH 14
-
-inline static int stuff_byte( __u8 byte, __u8 *buf);
+inline static int stuff_byte(__u8 byte, __u8 *buf);
/*
* Function async_wrap (skb, *tx_buff)
* Makes a new buffer with wrapping and stuffing, should check that
* we don't get tx buffer overflow.
*/
-int async_wrap_skb( struct sk_buff *skb, __u8 *tx_buff, int buffsize)
+int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize)
{
- __u8 byte;
- int i, n;
+ int i;
+ int n;
int xbofs;
union {
__u16 value;
__u8 bytes[2];
} fcs;
- ASSERT( skb != NULL, return 0;);
+ ASSERT(skb != NULL, return 0;);
/* Initialize variables */
fcs.value = INIT_FCS;
*/
if (((struct irlap_skb_cb *)(skb->cb))->magic != LAP_MAGIC) {
DEBUG(1, __FUNCTION__ "(), wrong magic in skb!\n");
- xbofs = 11;
+ xbofs = 10;
} else
xbofs = ((struct irlap_skb_cb *)(skb->cb))->xbofs;
- for (i=0; i<xbofs; i++) {
- tx_buff[n++] = XBOF;
- }
-
+#if 0
+ for (i=0; i<xbofs; i++)
+ tx_buff[n++] = XBOF;
+#else
+ memset(tx_buff+n, XBOF, xbofs);
+ n += xbofs;
+#endif
/* Start of packet character BOF */
tx_buff[n++] = BOF;
/* Insert frame and calc CRC */
for (i=0; i < skb->len; i++) {
- byte = skb->data[i];
-
/*
* Check for the possibility of tx buffer overflow. We use
* bufsize-5 since the maximum number of bytes that can be
* transmitted after this point is 5.
*/
- if ( n > buffsize-5) {
- printk( KERN_WARNING
- "IrDA Wrapper: TX-buffer overflow!\n");
- return n;
- }
- n+=stuff_byte( byte, tx_buff+n);
- fcs.value = IR_FCS( fcs.value, byte);
+ ASSERT(n < (buffsize-5), return n;);
+
+ n += stuff_byte(skb->data[i], tx_buff+n);
+ fcs.value = IR_FCS(fcs.value, skb->data[i]);
}
/* Insert CRC in little endian format (LSB first) */
n += stuff_byte(fcs.bytes[0], tx_buff+n);
#endif
tx_buff[n++] = EOF;
-
+
+#if 0
+ {
+ int i;
+
+ for (i=0;i<n;i++)
+ printk("%02x", tx_buff[i]);
+ printk("\n");
+ }
+#endif
return n;
}
* Got a frame, make a copy of it, and pass it up the stack!
*
*/
-static inline void async_bump( struct irda_device *idev, __u8 *buf, int len)
+static inline void async_bump(struct irda_device *idev, __u8 *buf, int len)
{
struct sk_buff *skb;
-
+
skb = dev_alloc_skb(len+1);
if (!skb) {
idev->stats.rx_dropped++;
}
/* Align IP header to 20 bytes */
- skb_reserve( skb, 1);
+ skb_reserve(skb, 1);
- ASSERT( len-2 > 0, return;);
-
/* Copy data without CRC */
- skb_put( skb, len-2);
- memcpy( skb->data, buf, len-2);
+ memcpy(skb_put(skb, len-2), buf, len-2);
/*
* Feed it to IrLAP layer
*/
- /* memcpy(skb_put(skb,count), ax->rbuff, count); */
skb->dev = &idev->netdev;
skb->mac.raw = skb->data;
skb->protocol = htons(ETH_P_IRDA);
* Parse and de-stuff frame received from the IR-port
*
*/
-void async_unwrap_char( struct irda_device *idev, __u8 byte)
+void async_unwrap_char(struct irda_device *idev, __u8 byte)
{
/* State machine for receiving frames */
- switch( idev->rx_buff.state) {
+ switch (idev->rx_buff.state) {
case OUTSIDE_FRAME:
- switch( byte) {
+ switch(byte) {
case BOF:
idev->rx_buff.state = BEGIN_FRAME;
idev->rx_buff.in_frame = TRUE;
break;
case XBOF:
- idev->xbofs++;
+ /* idev->xbofs++; */
break;
case EOF:
irda_device_set_media_busy( idev, TRUE);
}
break;
case BEGIN_FRAME:
- switch ( byte) {
+ switch (byte) {
case BOF:
-
/* Continue */
break;
case CE:
break;
default:
/* Got first byte of frame */
- idev->rx_buff.data[ idev->rx_buff.len++] = byte;
+ idev->rx_buff.data = idev->rx_buff.head;
+ idev->rx_buff.len = 0;
+
+ idev->rx_buff.data[idev->rx_buff.len++] = byte;
- idev->rx_buff.fcs = IR_FCS( INIT_FCS, byte);
+ idev->rx_buff.fcs = IR_FCS(INIT_FCS, byte);
idev->rx_buff.state = INSIDE_FRAME;
break;
}
break;
case LINK_ESCAPE:
- switch ( byte) {
+ switch (byte) {
case BOF:
/* New frame? */
idev->rx_buff.state = BEGIN_FRAME;
- idev->rx_buff.len = 0;
- irda_device_set_media_busy( idev, TRUE);
+ irda_device_set_media_busy(idev, TRUE);
break;
case CE:
- DEBUG( 4, "WARNING: State not defined\n");
+ DEBUG(4, "WARNING: State not defined\n");
break;
case EOF:
/* Abort frame */
idev->rx_buff.state = OUTSIDE_FRAME;
- idev->rx_buff.len = 0;
break;
default:
/*
* following CE, IrLAP p.114
*/
byte ^= IR_TRANS;
- if ( idev->rx_buff.len < idev->rx_buff.truesize) {
- idev->rx_buff.data[ idev->rx_buff.len++] = byte;
-
+ if (idev->rx_buff.len < idev->rx_buff.truesize) {
+ idev->rx_buff.data[idev->rx_buff.len++] = byte;
idev->rx_buff.fcs = IR_FCS(idev->rx_buff.fcs,
byte);
idev->rx_buff.state = INSIDE_FRAME;
} else {
- DEBUG( 1, __FUNCTION__
+ DEBUG(1, __FUNCTION__
"(), Rx buffer overflow, aborting\n");
idev->rx_buff.state = OUTSIDE_FRAME;
- idev->rx_buff.len = 0;
}
break;
}
break;
case INSIDE_FRAME:
- switch ( byte) {
+ switch (byte) {
case BOF:
/* New frame? */
idev->rx_buff.state = BEGIN_FRAME;
- idev->rx_buff.len = 0;
- irda_device_set_media_busy( idev, TRUE);
+ irda_device_set_media_busy(idev, TRUE);
break;
case CE:
/* Stuffed char */
/*
* Test FCS and deliver frame if it's good
*/
- if ( idev->rx_buff.fcs == GOOD_FCS) {
- async_bump( idev, idev->rx_buff.data,
- idev->rx_buff.len);
- idev->rx_buff.len = 0;
+ if (idev->rx_buff.fcs == GOOD_FCS) {
+ async_bump(idev, idev->rx_buff.data,
+ idev->rx_buff.len);
} else {
/* Wrong CRC, discard frame! */
- irda_device_set_media_busy( idev, TRUE);
- idev->rx_buff.len = 0;
+ irda_device_set_media_busy(idev, TRUE);
idev->stats.rx_errors++;
idev->stats.rx_crc_errors++;
break;
default:
/* Next byte of frame */
- if ( idev->rx_buff.len < idev->rx_buff.truesize) {
- idev->rx_buff.data[ idev->rx_buff.len++] = byte;
-
- idev->rx_buff.fcs = IR_FCS( idev->rx_buff.fcs,
- byte);
+ if (idev->rx_buff.len < idev->rx_buff.truesize) {
+ idev->rx_buff.data[idev->rx_buff.len++] = byte;
+ idev->rx_buff.fcs = IR_FCS(idev->rx_buff.fcs,
+ byte);
} else {
- DEBUG( 1, __FUNCTION__
- "(), Rx buffer overflow, aborting\n");
+ DEBUG(1, __FUNCTION__
+ "(), Rx buffer overflow, aborting\n");
idev->rx_buff.state = OUTSIDE_FRAME;
- idev->rx_buff.len = 0;
}
break;
}
* buf. The buffer must at all times be able to have two bytes inserted.
*
*/
-inline static int stuff_byte( __u8 byte, __u8 *buf)
+inline static int stuff_byte(__u8 byte, __u8 *buf)
{
- switch ( byte) {
+ switch (byte) {
case BOF: /* FALLTHROUGH */
case EOF: /* FALLTHROUGH */
case CE:
#include <net/dst.h>
#include <net/checksum.h>
#include <linux/etherdevice.h>
+#ifdef CONFIG_HIPPI
+#include <linux/hippidevice.h>
+#endif
#include <net/pkt_sched.h>
#ifdef CONFIG_BRIDGE
#include <linux/igmp.h>
extern struct net_proto_family inet_family_ops;
+extern __u32 sysctl_wmem_max;
+extern __u32 sysctl_rmem_max;
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
#include <linux/in6.h>
EXPORT_SYMBOL(if_port_text);
+#ifdef CONFIG_HIPPI
+EXPORT_SYMBOL(hippi_type_trans);
+EXPORT_SYMBOL(init_hippi_dev);
+EXPORT_SYMBOL(unregister_hipdev);
+#endif
+
+EXPORT_SYMBOL(sysctl_wmem_max);
+EXPORT_SYMBOL(sysctl_rmem_max);
+
#if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE)
#include<linux/if_ltalk.h>
EXPORT_SYMBOL(ltalk_setup);
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/notifier.h>
-#include <linux/module.h>
#include <net/ip.h>
#include <net/route.h>
#include <linux/skbuff.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/bitops.h>
-#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>