Commit 3a80046a authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://kernel.bkbits.net/gregkh/linux/linus-2.5

into home.transmeta.com:/home/torvalds/v2.5/linux
parents e73291c3 33f0554d
...@@ -6,31 +6,32 @@ ...@@ -6,31 +6,32 @@
<edition>02 June 2003</edition> <edition>02 June 2003</edition>
<legalnotice> <legalnotice>
<para>Permission is granted to copy, distribute, and/or modify <para>
this document under the terms of the GNU Free Documentation This documentation is free software; you can redistribute
License, version 1.2, or any later version published by the it and/or modify it under the terms of the GNU General Public
Free Software Foundation; with the Invariant Sections being License as published by the Free Software Foundation; either
the "GNU Free Documentation License", version 2 of the License, or (at your option) any later
no Front-Cover Texts, version.
and </para>
no Back-Cover Texts.
</para> <para>
This program is distributed in the hope that it will be
<para>This documentation is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
See the GNU Free Documentation License for more details. </para>
</para>
<para>
<para>Note that certain sections of this document are merged You should have received a copy of the GNU General Public
into Linux kernel source code. License along with this program; if not, write to the Free
That content is the bulk of Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
<xref linkend="core" /> and MA 02111-1307 USA
<xref linkend="utils" />, </para>
where the "GNU Free Documentation License" is identified
as an alternate licence for its documentation. <para>
</para> For more details see the file COPYING in the source
distribution of Linux.
</para>
</legalnotice> </legalnotice>
<copyright> <copyright>
<year>2003</year> <year>2003</year>
...@@ -325,9 +326,10 @@ the "chunky" nature of USB messages: I/O requests are in terms ...@@ -325,9 +326,10 @@ the "chunky" nature of USB messages: I/O requests are in terms
of one or more "packets", and packet boundaries are visible to drivers. of one or more "packets", and packet boundaries are visible to drivers.
Compared to RS-232 serial protocols, USB resembles Compared to RS-232 serial protocols, USB resembles
synchronous protocols like HDLC synchronous protocols like HDLC
(N bytes per frame, multipoint addressing from the host) (N bytes per frame, multipoint addressing, host as the primary
station and devices as secondary stations)
more than asynchronous ones more than asynchronous ones
(tty style, like 8 bytes, no parity, one stop bit). (tty style: 8 data bits per frame, no parity, one stop bit).
So for example the controller drivers won't buffer So for example the controller drivers won't buffer
two single byte writes into a single two-byte USB IN packet, two single byte writes into a single two-byte USB IN packet,
although gadget drivers may do so when they implement although gadget drivers may do so when they implement
...@@ -528,438 +530,6 @@ over time, as this driver framework evolves. ...@@ -528,438 +530,6 @@ over time, as this driver framework evolves.
</chapter> </chapter>
<appendix id="gfdl">
<title>GNU Free Documentation License</title>
<subtitle>Version 1.2, November 2002</subtitle>
<blockquote id="fsf-copyright">
<para>Copyright (C) 2000,2001,2002 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.</para>
</blockquote>
<sect1 id="gfdl-0"><title>PREAMBLE</title>
<para>The purpose of this License is to make a manual, textbook, or
other functional and useful document "free" in the sense of freedom: to
assure everyone the effective freedom to copy and redistribute it, with
or without modifying it, either commercially or noncommercially.
Secondarily, this License preserves for the author and publisher a way
to get credit for their work, while not being considered responsible for
modifications made by others.</para>
<para>This License is a kind of "copyleft", which means that derivative
works of the document must themselves be free in the same sense. It
complements the GNU General Public License, which is a copyleft license
designed for free software.</para>
<para>We have designed this License in order to use it for manuals for
free software, because free software needs free documentation: a free
program should come with manuals providing the same freedoms that the
software does. But this License is not limited to software manuals; it
can be used for any textual work, regardless of subject matter or
whether it is published as a printed book. We recommend this License
principally for works whose purpose is instruction or reference.</para>
</sect1>
<sect1 id="gfdl-1"><title>APPLICABILITY AND DEFINITIONS</title>
<para id="gfdl-doc">This License applies to any manual or other work, in
any medium, that contains a notice placed by the copyright holder saying
it can be distributed under the terms of this License. Such a notice
grants a world-wide, royalty-free license, unlimited in duration, to use
that work under the conditions stated herein. The "Document", below,
refers to any such manual or work. Any member of the public is a
licensee, and is addressed as "you". You accept the license if you
copy, modify or distribute the work in a way requiring permission under
copyright law.</para>
<para id="gfdl-mod-ver">A "Modified Version" of the Document means any
work containing the Document or a portion of it, either copied verbatim,
or with modifications and/or translated into another language.</para>
<para id="gfdl-secnd-sect">A "Secondary Section" is a named appendix or
a front-matter section of the Document that deals exclusively with the
relationship of the publishers or authors of the Document to the
Document's overall subject (or to related matters) and contains nothing
that could fall directly within that overall subject. (Thus, if the
Document is in part a textbook of mathematics, a Secondary Section may
not explain any mathematics.) The relationship could be a matter of
historical connection with the subject or with related matters, or of
legal, commercial, philosophical, ethical or political position
regarding them.</para>
<para id="gfdl-inv-sect">The "Invariant Sections" are certain Secondary
Sections whose titles are designated, as being those of Invariant
Sections, in the notice that says that the Document is released under
this License. If a section does not fit the above definition of
Secondary then it is not allowed to be designated as Invariant. The
Document may contain zero Invariant Sections. If the Document does not
identify any Invariant Sections then there are none.</para>
<para id="gfdl-cov-text">The "Cover Texts" are certain short passages of
text that are listed, as Front-Cover Texts or Back-Cover Texts, in the
notice that says that the Document is released under this License. A
Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at
most 25 words.</para>
<para id="gfdl-transparent">A "Transparent" copy of the Document means a
machine-readable copy, represented in a format whose specification is
available to the general public, that is suitable for revising the
document straightforwardly with generic text editors or (for images
composed of pixels) generic paint programs or (for drawings) some widely
available drawing editor, and that is suitable for input to text
formatters or for automatic translation to a variety of formats suitable
for input to text formatters. A copy made in an otherwise Transparent
file format whose markup, or absence of markup, has been arranged to
thwart or discourage subsequent modification by readers is not
Transparent. An image format is not Transparent if used for any
substantial amount of text. A copy that is not "Transparent" is called
"Opaque".</para>
<para>Examples of suitable formats for Transparent copies include plain
ASCII without markup, Texinfo input format, LaTeX input format, SGML or
XML using a publicly available DTD, and standard-conforming simple HTML,
PostScript or PDF designed for human modification. Examples of
transparent image formats include PNG, XCF and JPG. Opaque formats
include proprietary formats that can be read and edited only by
proprietary word processors, SGML or XML for which the DTD and/or
processing tools are not generally available, and the machine-generated
HTML, PostScript or PDF produced by some word processors for output
purposes only.</para>
<para id="gfdl-title-page">The "Title Page" means, for a printed book,
the title page itself, plus such following pages as are needed to hold,
legibly, the material this License requires to appear in the title page.
For works in formats which do not have any title page as such, "Title
Page" means the text near the most prominent appearance of the work's
title, preceding the beginning of the body of the text.</para>
<para id="gfdl-entitled">A section "Entitled XYZ" means a named subunit
of the Document whose title either is precisely XYZ or contains XYZ in
parentheses following text that translates XYZ in another language.
(Here XYZ stands for a specific section name mentioned below, such as
"Acknowledgements", "Dedications", "Endorsements", or "History".) To
"Preserve the Title" of such a section when you modify the Document
means that it remains a section "Entitled XYZ" according to this
definition.</para>
<para>The Document may include Warranty Disclaimers next to the notice
which states that this License applies to the Document. These Warranty
Disclaimers are considered to be included by reference in this License,
but only as regards disclaiming warranties: any other implication that
these Warranty Disclaimers may have is void and has no effect on the
meaning of this License.</para>
</sect1>
<sect1 id="gfdl-2"><title>VERBATIM COPYING</title>
<para>You may copy and distribute the Document in any medium, either
commercially or noncommercially, provided that this License, the
copyright notices, and the license notice saying this License applies to
the Document are reproduced in all copies, and that you add no other
conditions whatsoever to those of this License. You may not use
technical measures to obstruct or control the reading or further copying
of the copies you make or distribute. However, you may accept
compensation in exchange for copies. If you distribute a large enough
number of copies you must also follow the conditions in section 3.
</para>
<para>You may also lend copies, under the same conditions stated above,
and you may publicly display copies.</para>
</sect1>
<sect1 id="gfdl-3"><title>COPYING IN QUANTITY</title>
<para>If you publish printed copies (or copies in media that commonly
have printed covers) of the Document, numbering more than 100, and the
Document's license notice requires Cover Texts, you must enclose the
copies in covers that carry, clearly and legibly, all these Cover Texts:
Front-Cover Texts on the front cover, and Back-Cover Texts on the back
cover. Both covers must also clearly and legibly identify you as the
publisher of these copies. The front cover must present the full title
with all words of the title equally prominent and visible. You may add
other material on the covers in addition. Copying with changes limited
to the covers, as long as they preserve the title of the Document and
satisfy these conditions, can be treated as verbatim copying in other
respects.</para>
<para>If the required texts for either cover are too voluminous to fit
legibly, you should put the first ones listed (as many as fit
reasonably) on the actual cover, and continue the rest onto adjacent
pages.</para>
<para>If you publish or distribute Opaque copies of the Document
numbering more than 100, you must either include a machine-readable
Transparent copy along with each Opaque copy, or state in or with each
Opaque copy a computer-network location from which the general
network-using public has access to download using public-standard
network protocols a complete Transparent copy of the Document, free of
added material. If you use the latter option, you must take reasonably
prudent steps, when you begin distribution of Opaque copies in quantity,
to ensure that this Transparent copy will remain thus accessible at the
stated location until at least one year after the last time you
distribute an Opaque copy (directly or through your agents or retailers)
of that edition to the public.</para>
<para>It is requested, but not required, that you contact the authors of
the Document well before redistributing any large number of copies, to
give them a chance to provide you with an updated version of the
Document.</para>
</sect1>
<sect1 id="gfdl-4"><title>MODIFICATIONS</title>
<para>You may copy and distribute a Modified Version of the Document
under the conditions of sections 2 and 3 above, provided that you
release the Modified Version under precisely this License, with the
Modified Version filling the role of the Document, thus licensing
distribution and modification of the Modified Version to whoever
possesses a copy of it. In addition, you must do these things in the
Modified Version:</para>
<orderedlist id="gfdl-modif-cond" numeration="upperalpha">
<listitem><simpara>Use in the Title Page (and on the covers, if any) a
title distinct from that of the Document, and from those of previous
versions (which should, if there were any, be listed in the History
section of the Document). You may use the same title as a previous
version if the original publisher of that version gives permission.
</simpara></listitem>
<listitem><simpara>List on the Title Page, as authors, one or more
persons or entities responsible for authorship of the modifications in
the Modified Version, together with at least five of the principal
authors of the Document (all of its principal authors, if it has fewer
than five), unless they release you from this requirement.
</simpara></listitem>
<listitem><simpara>State on the Title page the name of the publisher of
the Modified Version, as the publisher.</simpara></listitem>
<listitem><simpara>Preserve all the copyright notices of the Document.
</simpara></listitem>
<listitem><simpara>Add an appropriate copyright notice for your
modifications adjacent to the other copyright notices.
</simpara></listitem>
<listitem><simpara>Include, immediately after the copyright notices, a
license notice giving the public permission to use the Modified
Version under the terms of this License, in the form shown in the
<link linkend="gfdl-addendum">Addendum</link> below.
</simpara></listitem>
<listitem><simpara>Preserve in that license notice the full lists of
Invariant Sections and required Cover Texts given in the Document's
license notice.</simpara></listitem>
<listitem><simpara>Include an unaltered copy of this License.
</simpara></listitem>
<listitem><simpara>Preserve the section Entitled "History", Preserve its
Title, and add to it an item stating at least the title, year, new
authors, and publisher of the Modified Version as given on the Title
Page. If there is no section Entitled "History" in the Document,
create one stating the title, year, authors, and publisher of the
Document as given on its Title Page, then add an item describing the
Modified Version as stated in the previous sentence.
</simpara></listitem>
<listitem><simpara>Preserve the network location, if any, given in the
Document for public access to a Transparent copy of the Document, and
likewise the network locations given in the Document for previous
versions it was based on. These may be placed in the "History"
section. You may omit a network location for a work that was
published at least four years before the Document itself, or if the
original publisher of the version it refers to gives permission.
</simpara></listitem>
<listitem><simpara>For any section Entitled "Acknowledgements" or
"Dedications", Preserve the Title of the section, and preserve in the
section all the substance and tone of each of the contributor
acknowledgements and/or dedications given therein.
</simpara></listitem>
<listitem><simpara>Preserve all the Invariant Sections of the Document,
unaltered in their text and in their titles. Section numbers or the
equivalent are not considered part of the section titles.
</simpara></listitem>
<listitem><simpara>Delete any section Entitled "Endorsements".
Such a section may not be included in the Modified Version.
</simpara></listitem>
<listitem><simpara>Do not retitle any existing section to be Entitled
"Endorsements" or to conflict in title with any Invariant Section.
</simpara></listitem>
<listitem><simpara>Preserve any Warranty Disclaimers.
</simpara></listitem>
</orderedlist>
<para>If the Modified Version includes new front-matter sections or
appendices that qualify as Secondary Sections and contain no material
copied from the Document, you may at your option designate some or all
of these sections as invariant. To do this, add their titles to the
list of Invariant Sections in the Modified Version's license notice.
These titles must be distinct from any other section titles.</para>
<para>You may add a section Entitled "Endorsements", provided it
contains nothing but endorsements of your Modified Version by various
parties--for example, statements of peer review or that the text has
been approved by an organization as the authoritative definition of a
standard.</para>
<para>You may add a passage of up to five words as a Front-Cover Text,
and a passage of up to 25 words as a Back-Cover Text, to the end of the
list of Cover Texts in the Modified Version. Only one passage of
Front-Cover Text and one of Back-Cover Text may be added by (or through
arrangements made by) any one entity. If the Document already includes
a cover text for the same cover, previously added by you or by
arrangement made by the same entity you are acting on behalf of, you may
not add another; but you may replace the old one, on explicit permission
from the previous publisher that added the old one.</para>
<para>The author(s) and publisher(s) of the Document do not by this
License give permission to use their names for publicity for or to
assert or imply endorsement of any Modified Version.</para>
</sect1>
<sect1 id="gfdl-5"><title>COMBINING DOCUMENTS</title>
<para>You may combine the Document with other documents released under
this License, under the terms defined in <link linkend="gfdl-4">section
4</link> above for modified versions, provided that you include in the
combination all of the Invariant Sections of all of the original
documents, unmodified, and list them all as Invariant Sections of your
combined work in its license notice, and that you preserve all their
Warranty Disclaimers.</para>
<para>The combined work need only contain one copy of this License, and
multiple identical Invariant Sections may be replaced with a single
copy. If there are multiple Invariant Sections with the same name but
different contents, make the title of each such section unique by adding
at the end of it, in parentheses, the name of the original author or
publisher of that section if known, or else a unique number. Make the
same adjustment to the section titles in the list of Invariant Sections
in the license notice of the combined work.</para>
<para>In the combination, you must combine any sections Entitled
"History" in the various original documents, forming one section
Entitled "History"; likewise combine any sections Entitled
"Acknowledgements", and any sections Entitled "Dedications". You must
delete all sections Entitled "Endorsements".</para>
</sect1>
<sect1 id="gfdl-6"><title>COLLECTIONS OF DOCUMENTS</title>
<para>You may make a collection consisting of the Document and other
documents released under this License, and replace the individual copies
of this License in the various documents with a single copy that is
included in the collection, provided that you follow the rules of this
License for verbatim copying of each of the documents in all other
respects.</para>
<para>You may extract a single document from such a collection, and
distribute it individually under this License, provided you insert a
copy of this License into the extracted document, and follow this
License in all other respects regarding verbatim copying of that
document.</para>
</sect1>
<sect1 id="gfdl-7"><title>AGGREGATION WITH INDEPENDENT WORKS</title>
<para>A compilation of the Document or its derivatives with other
separate and independent documents or works, in or on a volume of a
storage or distribution medium, is called an "aggregate" if the
copyright resulting from the compilation is not used to limit the legal
rights of the compilation's users beyond what the individual works
permit. When the Document is included an aggregate, this License does
not apply to the other works in the aggregate which are not themselves
derivative works of the Document.</para>
<para>If the Cover Text requirement of section 3 is applicable to these
copies of the Document, then if the Document is less than one half of
the entire aggregate, the Document's Cover Texts may be placed on covers
that bracket the Document within the aggregate, or the electronic
equivalent of covers if the Document is in electronic form. Otherwise
they must appear on printed covers that bracket the whole
aggregate.</para>
</sect1>
<sect1 id="gfdl-8"><title>TRANSLATION</title>
<para>Translation is considered a kind of modification, so you may
distribute translations of the Document under the terms of section 4.
Replacing Invariant Sections with translations requires special
permission from their copyright holders, but you may include
translations of some or all Invariant Sections in addition to the
original versions of these Invariant Sections. You may include a
translation of this License, and all the license notices in the
Document, and any Warranty Disclaimers, provided that you also include
the original English version of this License and the original versions
of those notices and disclaimers. In case of a disagreement between the
translation and the original version of this License or a notice or
disclaimer, the original version will prevail.</para>
<para>If a section in the Document is Entitled "Acknowledgements",
"Dedications", or "History", the requirement (section 4) to Preserve its
Title (section 1) will typically require changing the actual
title.</para>
</sect1>
<sect1 id="gfdl-9"><title>TERMINATION</title>
<para>You may not copy, modify, sublicense, or distribute the Document
except as expressly provided for under this License. Any other attempt
to copy, modify, sublicense or distribute the Document is void, and will
automatically terminate your rights under this License. However,
parties who have received copies, or rights, from you under this License
will not have their licenses terminated so long as such parties remain
in full compliance.</para>
</sect1>
<sect1 id="gfdl-10"><title>FUTURE REVISIONS OF THIS LICENSE</title>
<para>The Free Software Foundation may publish new, revised versions of
the GNU Free Documentation License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in
detail to address new problems or concerns. See
http://www.gnu.org/copyleft/.</para>
<para>Each version of the License is given a distinguishing version
number. If the Document specifies that a particular numbered version of
this License "or any later version" applies to it, you have the option
of following the terms and conditions either of that specified version
or of any later version that has been published (not as a draft) by the
Free Software Foundation. If the Document does not specify a version
number of this License, you may choose any version ever published (not
as a draft) by the Free Software Foundation.</para>
</sect1>
<sect1 id="gfdl-addendum"><title>ADDENDUM: How to use this License for
your documents</title>
<para>To use this License in a document you have written, include a copy
of the License in the document and put the following copyright and
license notices just after the title page:</para>
<blockquote id="copyright-sample"><para>
Copyright (c) YEAR YOUR NAME.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.2
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
A copy of the license is included in the section entitled "GNU
Free Documentation License".
</para></blockquote>
<para>If you have Invariant Sections, Front-Cover Texts and Back-Cover
Texts, replace the "with...Texts." line with this:</para>
<blockquote id="inv-cover-sample"><para>
with the Invariant Sections being LIST THEIR TITLES, with the
Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
</para></blockquote>
<para>If you have Invariant Sections without Cover Texts, or some other
combination of the three, merge those two alternatives to suit the
situation.</para>
<para>If your document contains nontrivial examples of program code, we
recommend releasing these examples in parallel under your choice of free
software license, such as the GNU General Public License, to permit
their use in free software.</para>
</sect1>
</appendix>
</book> </book>
<!-- <!--
vim:syntax=sgml:sw=4 vim:syntax=sgml:sw=4
......
...@@ -513,7 +513,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) ...@@ -513,7 +513,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
} }
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
#define MAX_OUTPUT (PAGE_SIZE * 16) #define MAX_OUTPUT (64 * 1024)
static struct proc_dir_entry *uhci_proc_root = NULL; static struct proc_dir_entry *uhci_proc_root = NULL;
......
// Portions of this file taken from
// Petko Manolov - Petkan (petkan@dce.bg)
// from his driver pegasus.c
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/types.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/usb.h>
#include <linux/module.h>
#include "cdc-ether.h"
static const char *version = __FILE__ ": v0.98.5 22 Sep 2001 Brad Hards and another";
/* Take any CDC device, and sort it out in probe() */
static struct usb_device_id CDCEther_ids[] = {
{ USB_DEVICE_INFO(USB_CLASS_COMM, 0, 0) },
{ } /* Terminating null entry */
};
/*
* module parameter that provides an alternate upper limit on the
* number of multicast filters we use, with a default to use all
* the filters available to us. Note that the actual number used
* is the lesser of this parameter and the number returned in the
* descriptor for the particular device. See Table 41 of the CDC
* spec for more info on the descriptor limit.
*/
static int multicast_filter_limit = 32767;
//////////////////////////////////////////////////////////////////////////////
// Callback routines from USB device /////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
static void read_bulk_callback( struct urb *urb, struct pt_regs *regs )
{
ether_dev_t *ether_dev = urb->context;
struct net_device *net;
int count = urb->actual_length, res;
struct sk_buff *skb;
// Sanity check
if ( !ether_dev || !(ether_dev->flags & CDC_ETHER_RUNNING) ) {
dbg("BULK IN callback but driver is not active!");
return;
}
net = ether_dev->net;
if ( !netif_device_present(net) ) {
// Somebody killed our network interface...
return;
}
if ( ether_dev->flags & CDC_ETHER_RX_BUSY ) {
// Are we already trying to receive a frame???
ether_dev->stats.rx_errors++;
dbg("ether_dev Rx busy");
return;
}
// We are busy, leave us alone!
ether_dev->flags |= CDC_ETHER_RX_BUSY;
switch ( urb->status ) {
case 0:
break;
case -ETIMEDOUT:
dbg( "no repsonse in BULK IN" );
ether_dev->flags &= ~CDC_ETHER_RX_BUSY;
break;
default:
dbg( "%s: RX status %d", net->name, urb->status );
goto goon;
}
// Check to make sure we got some data...
if ( !count ) {
// We got no data!!!
goto goon;
}
// Tell the kernel we want some memory
if ( !(skb = dev_alloc_skb(count)) ) {
// We got no receive buffer.
goto goon;
}
// Here's where it came from
skb->dev = net;
// Now we copy it over
eth_copy_and_sum(skb, ether_dev->rx_buff, count, 0);
// Not sure
skb_put(skb, count);
// Not sure here either
skb->protocol = eth_type_trans(skb, net);
// Ship it off to the kernel
netif_rx(skb);
// update out statistics
ether_dev->stats.rx_packets++;
ether_dev->stats.rx_bytes += count;
goon:
// Prep the USB to wait for another frame
usb_fill_bulk_urb( ether_dev->rx_urb, ether_dev->usb,
usb_rcvbulkpipe(ether_dev->usb, ether_dev->data_ep_in),
ether_dev->rx_buff, ether_dev->wMaxSegmentSize,
read_bulk_callback, ether_dev );
// Give this to the USB subsystem so it can tell us
// when more data arrives.
if ( (res = usb_submit_urb(ether_dev->rx_urb, GFP_ATOMIC)) ) {
warn("%s failed submint rx_urb %d", __FUNCTION__, res);
}
// We are no longer busy, show us the frames!!!
ether_dev->flags &= ~CDC_ETHER_RX_BUSY;
}
static void write_bulk_callback( struct urb *urb, struct pt_regs *regs )
{
ether_dev_t *ether_dev = urb->context;
// Sanity check
if ( !ether_dev || !(ether_dev->flags & CDC_ETHER_RUNNING) ) {
// We are insane!!!
err( "write_bulk_callback: device not running" );
return;
}
// Do we still have a valid kernel network device?
if ( !netif_device_present(ether_dev->net) ) {
// Someone killed our network interface.
err( "write_bulk_callback: net device not present" );
return;
}
// Hmm... What on Earth could have happened???
if ( urb->status ) {
info("%s: TX status %d", ether_dev->net->name, urb->status);
}
// Update the network interface and tell it we are
// ready for another frame
ether_dev->net->trans_start = jiffies;
netif_wake_queue( ether_dev->net );
}
//static void intr_callback( struct urb *urb )
//{
// ether_dev_t *ether_dev = urb->context;
// struct net_device *net;
// __u8 *d;
//
// if ( !ether_dev )
// return;
//
// switch ( urb->status ) {
// case 0:
// break;
// case -ENOENT:
// return;
// default:
// info("intr status %d", urb->status);
// }
//
// d = urb->transfer_buffer;
// net = ether_dev->net;
// if ( d[0] & 0xfc ) {
// ether_dev->stats.tx_errors++;
// if ( d[0] & TX_UNDERRUN )
// ether_dev->stats.tx_fifo_errors++;
// if ( d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT) )
// ether_dev->stats.tx_aborted_errors++;
// if ( d[0] & LATE_COL )
// ether_dev->stats.tx_window_errors++;
// if ( d[0] & (NO_CARRIER | LOSS_CARRIER) )
// ether_dev->stats.tx_carrier_errors++;
// }
//}
//////////////////////////////////////////////////////////////////////////////
// Routines for turning net traffic on and off on the USB side ///////////////
//////////////////////////////////////////////////////////////////////////////
static inline int enable_net_traffic( ether_dev_t *ether_dev )
{
struct usb_device *usb = ether_dev->usb;
// Here would be the time to set the data interface to the configuration where
// it has two endpoints that use a protocol we can understand.
if (usb_set_interface( usb,
ether_dev->data_bInterfaceNumber,
ether_dev->data_bAlternateSetting_with_traffic ) ) {
err("usb_set_interface() failed" );
err("Attempted to set interface %d", ether_dev->data_bInterfaceNumber);
err("To alternate setting %d", ether_dev->data_bAlternateSetting_with_traffic);
return -1;
}
return 0;
}
static inline void disable_net_traffic( ether_dev_t *ether_dev )
{
// The thing to do is to set the data interface to the alternate setting that has
// no endpoints. This is what the spec suggests.
if (ether_dev->data_interface_altset_num_without_traffic >= 0 ) {
if (usb_set_interface( ether_dev->usb,
ether_dev->data_bInterfaceNumber,
ether_dev->data_bAlternateSetting_without_traffic ) ) {
err("usb_set_interface() failed");
}
} else {
// Some devices just may not support this...
warn("No way to disable net traffic");
}
}
//////////////////////////////////////////////////////////////////////////////
// Callback routines for kernel Ethernet Device //////////////////////////////
//////////////////////////////////////////////////////////////////////////////
static void CDCEther_tx_timeout( struct net_device *net )
{
ether_dev_t *ether_dev = net->priv;
// Sanity check
if ( !ether_dev ) {
// Seems to be a case of insanity here
return;
}
// Tell syslog we are hosed.
warn("%s: Tx timed out.", net->name);
// Tear the waiting frame off the list
ether_dev->tx_urb->transfer_flags |= URB_ASYNC_UNLINK;
usb_unlink_urb( ether_dev->tx_urb );
// Update statistics
ether_dev->stats.tx_errors++;
}
static int CDCEther_start_xmit( struct sk_buff *skb, struct net_device *net )
{
ether_dev_t *ether_dev = net->priv;
int res;
// Tell the kernel, "No more frames 'til we are done
// with this one.'
netif_stop_queue( net );
// Copy it from kernel memory to OUR memory
memcpy(ether_dev->tx_buff, skb->data, skb->len);
// Fill in the URB for shipping it out.
usb_fill_bulk_urb( ether_dev->tx_urb, ether_dev->usb,
usb_sndbulkpipe(ether_dev->usb, ether_dev->data_ep_out),
ether_dev->tx_buff, ether_dev->wMaxSegmentSize,
write_bulk_callback, ether_dev );
// Tell the URB how much it will be transporting today
ether_dev->tx_urb->transfer_buffer_length = skb->len;
/* Deal with the zero length problem, I hope */
ether_dev->tx_urb->transfer_flags |= URB_ZERO_PACKET;
// Send the URB on its merry way.
if ((res = usb_submit_urb(ether_dev->tx_urb, GFP_ATOMIC))) {
// Hmm... It didn't go. Tell someone...
warn("failed tx_urb %d", res);
// update some stats...
ether_dev->stats.tx_errors++;
// and tell the kernel to give us another.
// Maybe we'll get it right next time.
netif_start_queue( net );
} else {
// Okay, it went out.
// Update statistics
ether_dev->stats.tx_packets++;
ether_dev->stats.tx_bytes += skb->len;
// And tell the kernel when the last transmit occurred.
net->trans_start = jiffies;
}
// We are done with the kernel's memory
dev_kfree_skb(skb);
// We are done here.
return 0;
}
static struct net_device_stats *CDCEther_netdev_stats( struct net_device *net )
{
// Easy enough!
return &((ether_dev_t *)net->priv)->stats;
}
static int CDCEther_open(struct net_device *net)
{
ether_dev_t *ether_dev = (ether_dev_t *)net->priv;
int res;
// Turn on the USB and let the packets flow!!!
if ( (res = enable_net_traffic( ether_dev )) ) {
err("%s can't enable_net_traffic() - %d", __FUNCTION__, res );
return -EIO;
}
// Prep a receive URB
usb_fill_bulk_urb( ether_dev->rx_urb, ether_dev->usb,
usb_rcvbulkpipe(ether_dev->usb, ether_dev->data_ep_in),
ether_dev->rx_buff, ether_dev->wMaxSegmentSize,
read_bulk_callback, ether_dev );
// Put it out there so the device can send us stuff
if ( (res = usb_submit_urb(ether_dev->rx_urb, GFP_KERNEL)) )
{
// Hmm... Okay...
warn("%s failed rx_urb %d", __FUNCTION__, res );
}
// Tell the kernel we are ready to start receiving from it
netif_start_queue( net );
// We are up and running.
ether_dev->flags |= CDC_ETHER_RUNNING;
// Let's get ready to move frames!!!
return 0;
}
static int CDCEther_close( struct net_device *net )
{
ether_dev_t *ether_dev = net->priv;
// We are no longer running.
ether_dev->flags &= ~CDC_ETHER_RUNNING;
// Tell the kernel to stop sending us stuff
netif_stop_queue( net );
// If we are not already unplugged, turn off USB
// traffic
if ( !(ether_dev->flags & CDC_ETHER_UNPLUG) ) {
disable_net_traffic( ether_dev );
}
// We don't need the URBs anymore.
usb_unlink_urb( ether_dev->rx_urb );
usb_unlink_urb( ether_dev->tx_urb );
usb_unlink_urb( ether_dev->intr_urb );
// That's it. I'm done.
return 0;
}
static int CDCEther_ioctl( struct net_device *net, struct ifreq *rq, int cmd )
{
//__u16 *data = (__u16 *)&rq->ifr_data;
//ether_dev_t *ether_dev = net->priv;
// No support here yet.
// Do we need support???
switch(cmd) {
case SIOCDEVPRIVATE:
return -EOPNOTSUPP;
case SIOCDEVPRIVATE+1:
return -EOPNOTSUPP;
case SIOCDEVPRIVATE+2:
//return 0;
return -EOPNOTSUPP;
default:
return -EOPNOTSUPP;
}
}
#if 0
static void CDC_SetEthernetPacketFilter (ether_dev_t *ether_dev)
{
usb_control_msg(ether_dev->usb,
usb_sndctrlpipe(ether_dev->usb, 0),
SET_ETHERNET_PACKET_FILTER, /* request */
USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE, /* request type */
cpu_to_le16(ether_dev->mode_flags), /* value */
cpu_to_le16((u16)ether_dev->comm_interface), /* index */
NULL,
0, /* size */
HZ); /* timeout */
}
#endif
static void CDCEther_set_multicast( struct net_device *net )
{
ether_dev_t *ether_dev = net->priv;
int i;
__u8 *buff;
// Tell the kernel to stop sending us frames while we get this
// all set up.
// netif_stop_queue(net);
// FIXME: We hold xmit_lock. If you want to do the queue stuff you need
// to enable it from a completion handler
/* Note: do not reorder, GCC is clever about common statements. */
if (net->flags & IFF_PROMISC) {
/* Unconditionally log net taps. */
info( "%s: Promiscuous mode enabled", net->name);
ether_dev->mode_flags = MODE_FLAG_PROMISCUOUS |
MODE_FLAG_ALL_MULTICAST |
MODE_FLAG_DIRECTED |
MODE_FLAG_BROADCAST |
MODE_FLAG_MULTICAST;
} else if (net->mc_count > ether_dev->wNumberMCFilters) {
/* Too many to filter perfectly -- accept all multicasts. */
info("%s: set too many MC filters, using allmulti", net->name);
ether_dev->mode_flags = MODE_FLAG_ALL_MULTICAST |
MODE_FLAG_DIRECTED |
MODE_FLAG_BROADCAST |
MODE_FLAG_MULTICAST;
} else if (net->flags & IFF_ALLMULTI) {
/* Filter in software */
info("%s: using allmulti", net->name);
ether_dev->mode_flags = MODE_FLAG_ALL_MULTICAST |
MODE_FLAG_DIRECTED |
MODE_FLAG_BROADCAST |
MODE_FLAG_MULTICAST;
} else {
/* do multicast filtering in hardware */
struct dev_mc_list *mclist;
info("%s: set multicast filters", net->name);
ether_dev->mode_flags = MODE_FLAG_ALL_MULTICAST |
MODE_FLAG_DIRECTED |
MODE_FLAG_BROADCAST |
MODE_FLAG_MULTICAST;
buff = kmalloc(6 * net->mc_count, GFP_ATOMIC);
for (i = 0, mclist = net->mc_list;
mclist && i < net->mc_count;
i++, mclist = mclist->next) {
memcpy(&mclist->dmi_addr, &buff[i * 6], 6);
}
#if 0
usb_control_msg(ether_dev->usb,
// FIXME: We hold a spinlock. You must not use a synchronous API
usb_sndctrlpipe(ether_dev->usb, 0),
SET_ETHERNET_MULTICAST_FILTER, /* request */
USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE, /* request type */
cpu_to_le16(net->mc_count), /* value */
cpu_to_le16((u16)ether_dev->comm_interface), /* index */
buff,
(6* net->mc_count), /* size */
HZ); /* timeout */
#endif
kfree(buff);
}
#if 0
CDC_SetEthernetPacketFilter(ether_dev);
#endif
// Tell the kernel to start giving frames to us again.
// netif_wake_queue(net);
}
//////////////////////////////////////////////////////////////////////////////
// Routines used to parse out the Functional Descriptors /////////////////////
//////////////////////////////////////////////////////////////////////////////
static int parse_header_functional_descriptor( int *bFunctionLength,
int bDescriptorType,
int bDescriptorSubtype,
unsigned char *data,
ether_dev_t *ether_dev,
int *requirements )
{
// Check to make sure we haven't seen one of these already.
if ( (~*requirements) & REQ_HDR_FUNC_DESCR ) {
err( "Multiple Header Functional Descriptors found." );
return -1;
}
// Is it the right size???
if (*bFunctionLength != 5) {
info( "Invalid length in Header Functional Descriptor" );
// This is a hack to get around a particular device (NO NAMES)
// It has this function length set to the length of the
// whole class-specific descriptor
*bFunctionLength = 5;
}
// Nothing extremely useful here.
// We'll keep it for posterity
ether_dev->bcdCDC = data[0] + (data[1] << 8);
dbg( "Found Header descriptor, CDC version %x", ether_dev->bcdCDC);
// We've seen one of these
*requirements &= ~REQ_HDR_FUNC_DESCR;
// It's all good.
return 0;
}
static int parse_union_functional_descriptor( int *bFunctionLength,
int bDescriptorType,
int bDescriptorSubtype,
unsigned char *data,
ether_dev_t *ether_dev,
int *requirements )
{
// Check to make sure we haven't seen one of these already.
if ( (~*requirements) & REQ_UNION_FUNC_DESCR ) {
err( "Multiple Union Functional Descriptors found." );
return -1;
}
// Is it the right size?
if (*bFunctionLength != 5) {
// It is NOT the size we expected.
err( "Unsupported length in Union Functional Descriptor" );
return -1;
}
// Sanity check of sorts
if (ether_dev->comm_interface != data[0]) {
// This tells us that we are chasing the wrong comm
// interface or we are crazy or something else weird.
if (ether_dev->comm_interface == data[1]) {
info( "Probably broken Union descriptor, fudging data interface" );
// We'll need this in a few microseconds,
// so guess here, and hope for the best
ether_dev->data_interface = data[0];
} else {
err( "Union Functional Descriptor is broken beyond repair" );
return -1;
}
} else{ // Descriptor is OK
// We'll need this in a few microseconds!
ether_dev->data_interface = data[1];
}
// We've seen one of these now.
*requirements &= ~REQ_UNION_FUNC_DESCR;
// Done
return 0;
}
static int parse_ethernet_functional_descriptor( int *bFunctionLength,
int bDescriptorType,
int bDescriptorSubtype,
unsigned char *data,
ether_dev_t *ether_dev,
int *requirements )
{
// Check to make sure we haven't seen one of these already.
if ( (~*requirements) & REQ_ETH_FUNC_DESCR ) {
err( "Multiple Ethernet Functional Descriptors found." );
return -1;
}
// Is it the right size?
if (*bFunctionLength != 13) {
err( "Invalid length in Ethernet Networking Functional Descriptor" );
return -1;
}
// Lots of goodies from this one. They are all important.
ether_dev->iMACAddress = data[0];
ether_dev->bmEthernetStatistics = data[1] + (data[2] << 8) + (data[3] << 16) + (data[4] << 24);
ether_dev->wMaxSegmentSize = data[5] + (data[6] << 8);
ether_dev->wNumberMCFilters = (data[7] + (data[8] << 8)) & 0x00007FFF;
if (ether_dev->wNumberMCFilters > multicast_filter_limit) {
ether_dev->wNumberMCFilters = multicast_filter_limit;
}
ether_dev->bNumberPowerFilters = data[9];
// We've seen one of these now.
*requirements &= ~REQ_ETH_FUNC_DESCR;
// That's all she wrote.
return 0;
}
static int parse_protocol_unit_functional_descriptor( int *bFunctionLength,
int bDescriptorType,
int bDescriptorSubtype,
unsigned char *data,
ether_dev_t *ether_dev,
int *requirements )
{
// There should only be one type if we are sane
if (bDescriptorType != CS_INTERFACE) {
info( "Invalid bDescriptorType found." );
return -1;
}
// The Subtype tells the tale.
switch (bDescriptorSubtype){
case 0x00: // Header Functional Descriptor
return parse_header_functional_descriptor( bFunctionLength,
bDescriptorType,
bDescriptorSubtype,
data,
ether_dev,
requirements );
break;
case 0x06: // Union Functional Descriptor
return parse_union_functional_descriptor( bFunctionLength,
bDescriptorType,
bDescriptorSubtype,
data,
ether_dev,
requirements );
break;
case 0x0F: // Ethernet Networking Functional Descriptor
return parse_ethernet_functional_descriptor( bFunctionLength,
bDescriptorType,
bDescriptorSubtype,
data,
ether_dev,
requirements );
break;
default: // We don't support this at this time...
// However that doesn't necessarily indicate an error.
dbg( "Unexpected header type %x:", bDescriptorSubtype );
return 0;
}
// How did we get here???
return -1;
}
static int parse_ethernet_class_information( unsigned char *data, int length, ether_dev_t *ether_dev )
{
int loc = 0;
int rc;
int bFunctionLength;
int bDescriptorType;
int bDescriptorSubtype;
int requirements = REQUIREMENTS_TOTAL;
// As long as there is something here, we will try to parse it
while (loc < length) {
// Length
bFunctionLength = data[loc];
loc++;
// Type
bDescriptorType = data[loc];
loc++;
// Subtype
bDescriptorSubtype = data[loc];
loc++;
// ship this off to be processed elsewhere.
rc = parse_protocol_unit_functional_descriptor( &bFunctionLength,
bDescriptorType,
bDescriptorSubtype,
&data[loc],
ether_dev,
&requirements );
// Did it process okay?
if (rc) {
// Something was hosed somewhere.
// No need to continue;
err("Bad descriptor parsing: %x", rc );
return -1;
}
// We have already taken three bytes.
loc += (bFunctionLength - 3);
}
// Check to see if we got everything we need.
if (requirements) {
// We missed some of the requirements...
err( "Not all required functional descriptors present 0x%08X", requirements );
return -1;
}
// We got everything.
return 0;
}
//////////////////////////////////////////////////////////////////////////////
// Routine to check for the existence of the Functional Descriptors //////////
//////////////////////////////////////////////////////////////////////////////
static int find_and_parse_ethernet_class_information( struct usb_device *device, ether_dev_t *ether_dev )
{
struct usb_host_config *conf = NULL;
struct usb_interface *comm_intf_group = NULL;
struct usb_host_interface *comm_intf = NULL;
int rc = -1;
// The assumption here is that find_ethernet_comm_interface
// and find_valid_configuration
// have already filled in the information about where to find
// the a valid commication interface.
conf = &( device->config[ether_dev->configuration_num] );
comm_intf_group = &( conf->interface[ether_dev->comm_interface] );
comm_intf = &( comm_intf_group->altsetting[ether_dev->comm_interface_altset_num] );
// Let's check and see if it has the extra information we need...
if (comm_intf->extralen > 0) {
// This is where the information is SUPPOSED to be.
rc = parse_ethernet_class_information( comm_intf->extra, comm_intf->extralen, ether_dev );
} else if (conf->extralen > 0) {
// This is a hack. The spec says it should be at the interface
// location checked above. However I have seen it here also.
// This is the same device that requires the functional descriptor hack above
warn( "Ethernet information found at device configuration. This is broken." );
rc = parse_ethernet_class_information( conf->extra, conf->extralen, ether_dev );
} else {
// I don't know where else to look.
warn( "No ethernet information found." );
rc = -1;
}
return rc;
}
//////////////////////////////////////////////////////////////////////////////
// Routines to verify the data interface /////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
static int get_data_interface_endpoints( struct usb_device *device, ether_dev_t *ether_dev )
{
struct usb_host_config *conf = NULL;
struct usb_interface *data_intf_group = NULL;
struct usb_host_interface *data_intf = NULL;
// Walk through and get to the data interface we are checking.
conf = &( device->config[ether_dev->configuration_num] );
data_intf_group = &( conf->interface[ether_dev->data_interface] );
data_intf = &( data_intf_group->altsetting[ether_dev->data_interface_altset_num_with_traffic] );
// Start out assuming we won't find anything we can use
ether_dev->data_ep_in = 0;
ether_dev->data_ep_out = 0;
// If these are not BULK endpoints, we don't want them
if ( data_intf->endpoint[0].desc.bmAttributes != 0x02 ) {
return -1;
} if ( data_intf->endpoint[1].desc.bmAttributes != 0x02 ) {
return -1;
}
// Check the first endpoint to see if it is IN or OUT
if ( data_intf->endpoint[0].desc.bEndpointAddress & 0x80 ) {
// This endpoint is IN
ether_dev->data_ep_in = data_intf->endpoint[0].desc.bEndpointAddress & 0x7F;
} else {
// This endpoint is OUT
ether_dev->data_ep_out = data_intf->endpoint[0].desc.bEndpointAddress & 0x7F;
ether_dev->data_ep_out_size = data_intf->endpoint[0].desc.wMaxPacketSize;
}
// Check the second endpoint to see if it is IN or OUT
if ( data_intf->endpoint[1].desc.bEndpointAddress & 0x80 ) {
// This endpoint is IN
ether_dev->data_ep_in = data_intf->endpoint[1].desc.bEndpointAddress & 0x7F;
} else {
// This endpoint is OUT
ether_dev->data_ep_out = data_intf->endpoint[1].desc.bEndpointAddress & 0x7F;
ether_dev->data_ep_out_size = data_intf->endpoint[1].desc.wMaxPacketSize;
}
// Now make sure we got both an IN and an OUT
if (ether_dev->data_ep_in && ether_dev->data_ep_out) {
// We did get both, we are in good shape...
info( "detected BULK OUT packets of size %d", ether_dev->data_ep_out_size );
return 0;
}
return -1;
}
static int verify_ethernet_data_interface( struct usb_device *device, ether_dev_t *ether_dev )
{
struct usb_host_config *conf = NULL;
struct usb_interface *data_intf_group = NULL;
struct usb_interface_descriptor *data_intf = NULL;
int rc = -1;
int status;
int altset_num;
// The assumption here is that parse_ethernet_class_information()
// and find_valid_configuration()
// have already filled in the information about where to find
// a data interface
conf = &( device->config[ether_dev->configuration_num] );
data_intf_group = &( conf->interface[ether_dev->data_interface] );
// start out assuming we won't find what we are looking for.
ether_dev->data_interface_altset_num_with_traffic = -1;
ether_dev->data_bAlternateSetting_with_traffic = -1;
ether_dev->data_interface_altset_num_without_traffic = -1;
ether_dev->data_bAlternateSetting_without_traffic = -1;
// Walk through every possible setting for this interface until
// we find what makes us happy.
for ( altset_num = 0; altset_num < data_intf_group->num_altsetting; altset_num++ ) {
data_intf = &( data_intf_group->altsetting[altset_num].desc );
// Is this a data interface we like?
if ( ( data_intf->bInterfaceClass == 0x0A )
&& ( data_intf->bInterfaceSubClass == 0x00 )
&& ( data_intf->bInterfaceProtocol == 0x00 ) ) {
if ( data_intf->bNumEndpoints == 2 ) {
// We are required to have one of these.
// An interface with 2 endpoints to send Ethernet traffic back and forth
// It actually may be possible that the device might only
// communicate in a vendor specific manner.
// That would not be very nice.
// We can add that one later.
ether_dev->data_bInterfaceNumber = data_intf->bInterfaceNumber;
ether_dev->data_interface_altset_num_with_traffic = altset_num;
ether_dev->data_bAlternateSetting_with_traffic = data_intf->bAlternateSetting;
status = get_data_interface_endpoints( device, ether_dev );
if (!status) {
rc = 0;
}
}
if ( data_intf->bNumEndpoints == 0 ) {
// According to the spec we are SUPPOSED to have one of these
// In fact the device is supposed to come up in this state.
// However, I have seen a device that did not have such an interface.
// So it must be just optional for our driver...
ether_dev->data_bInterfaceNumber = data_intf->bInterfaceNumber;
ether_dev->data_interface_altset_num_without_traffic = altset_num;
ether_dev->data_bAlternateSetting_without_traffic = data_intf->bAlternateSetting;
}
}
}
return rc;
}
//////////////////////////////////////////////////////////////////////////////
// Routine to find a communication interface /////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
static int find_ethernet_comm_interface( struct usb_device *device, ether_dev_t *ether_dev )
{
struct usb_host_config *conf = NULL;
struct usb_interface *comm_intf_group = NULL;
struct usb_interface_descriptor *comm_intf = NULL;
int intf_num;
int altset_num;
int rc;
conf = &( device->config[ether_dev->configuration_num] );
// We need to check and see if any of these interfaces are something we want.
// Walk through each interface one at a time
for ( intf_num = 0; intf_num < conf->desc.bNumInterfaces; intf_num++ ) {
comm_intf_group = &( conf->interface[intf_num] );
// Now for each of those interfaces, check every possible
// alternate setting.
for ( altset_num = 0; altset_num < comm_intf_group->num_altsetting; altset_num++ ) {
comm_intf = &( comm_intf_group->altsetting[altset_num].desc);
// Is this a communication class of interface of the
// ethernet subclass variety.
if ( ( comm_intf->bInterfaceClass == 0x02 )
&& ( comm_intf->bInterfaceSubClass == 0x06 )
&& ( comm_intf->bInterfaceProtocol == 0x00 ) ) {
if ( comm_intf->bNumEndpoints == 1 ) {
// Good, we found one, we will try this one
// Fill in the structure...
ether_dev->comm_interface = intf_num;
ether_dev->comm_bInterfaceNumber = comm_intf->bInterfaceNumber;
ether_dev->comm_interface_altset_num = altset_num;
ether_dev->comm_bAlternateSetting = comm_intf->bAlternateSetting;
// Look for the Ethernet Functional Descriptors
rc = find_and_parse_ethernet_class_information( device, ether_dev );
if (rc) {
// Nope this was no good after all.
continue;
}
// Check that we really can talk to the data
// interface
// This includes # of endpoints, protocols,
// etc.
rc = verify_ethernet_data_interface( device, ether_dev );
if (rc) {
// We got something we didn't like
continue;
}
// This communication interface seems to give us everything
// we require. We have all the ethernet info we need.
// Let's get out of here and go home right now.
return 0;
} else {
// bNumEndPoints != 1
// We found an interface that had the wrong number of
// endpoints but would have otherwise been okay
} // end bNumEndpoints check.
} // end interface specifics check.
} // end for altset_num
} // end for intf_num
return -1;
}
//////////////////////////////////////////////////////////////////////////////
// Routine to go through all configurations and find one that ////////////////
// is an Ethernet Networking Device //////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
static int find_valid_configuration( struct usb_device *device, ether_dev_t *ether_dev )
{
struct usb_host_config *conf = NULL;
int conf_num;
int rc;
// We will try each and every possible configuration
for ( conf_num = 0; conf_num < device->descriptor.bNumConfigurations; conf_num++ ) {
conf = &( device->config[conf_num] );
// Our first requirement : 2 interfaces
if ( conf->desc.bNumInterfaces != 2 ) {
// I currently don't know how to handle devices with any number of interfaces
// other than 2.
continue;
}
// This one passed our first check, fill in some
// useful data
ether_dev->configuration_num = conf_num;
ether_dev->bConfigurationValue = conf->desc.bConfigurationValue;
// Now run it through the ringers and see what comes
// out the other side.
rc = find_ethernet_comm_interface( device, ether_dev );
// Check if we found an ethernet Communcation Device
if ( !rc ) {
// We found one.
return 0;
}
}
// None of the configurations suited us.
return -1;
}
//////////////////////////////////////////////////////////////////////////////
// Routine that checks a given configuration to see if any driver ////////////
// has claimed any of the devices interfaces /////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
static int check_for_claimed_interfaces( struct usb_host_config *config )
{
struct usb_interface *comm_intf_group;
int intf_num;
// Go through all the interfaces and make sure none are
// claimed by anybody else.
for ( intf_num = 0; intf_num < config->desc.bNumInterfaces; intf_num++ ) {
comm_intf_group = &( config->interface[intf_num] );
if ( usb_interface_claimed( comm_intf_group ) ) {
// Somebody has beat us to this guy.
// We can't change the configuration out from underneath of whoever
// is using this device, so we will go ahead and give up.
return -1;
}
}
// We made it all the way through.
// I guess no one has claimed any of these interfaces.
return 0;
}
//////////////////////////////////////////////////////////////////////////////
// Routines to ask for and set the kernel network interface's MAC address ////
// Used by driver's probe routine ////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
static inline unsigned char hex2dec( unsigned char digit )
{
// Is there a standard way to do this???
// I have written this code TOO MANY times.
if ( (digit >= '0') && (digit <= '9') ) {
return (digit - '0');
}
if ( (digit >= 'a') && (digit <= 'f') ) {
return (digit - 'a' + 10);
}
if ( (digit >= 'A') && (digit <= 'F') ) {
return (digit - 'A' + 10);
}
return 0;
}
static void set_ethernet_addr( ether_dev_t *ether_dev )
{
unsigned char mac_addr[6];
int i;
int len;
unsigned char buffer[13];
// Let's assume we don't get anything...
mac_addr[0] = 0x00;
mac_addr[1] = 0x00;
mac_addr[2] = 0x00;
mac_addr[3] = 0x00;
mac_addr[4] = 0x00;
mac_addr[5] = 0x00;
// Let's ask the device...
len = usb_string(ether_dev->usb, ether_dev->iMACAddress, buffer, 13);
// Sanity check!
if (len != 12) {
// You gotta love failing sanity checks
err("Attempting to get MAC address returned %d bytes", len);
return;
}
// Fill in the mac_addr
for (i = 0; i < 6; i++) {
mac_addr[i] = ( hex2dec( buffer[2 * i] ) << 4 ) + hex2dec( buffer[2 * i + 1] );
}
// Now copy it over to the kernel's network driver.
memcpy( ether_dev->net->dev_addr, mac_addr, sizeof(mac_addr) );
}
//////////////////////////////////////////////////////////////////////////////
// Routine to print to syslog information about the driver ///////////////////
// Used by driver's probe routine ////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
static void log_device_info(ether_dev_t *ether_dev)
{
int len;
int string_num;
unsigned char *manu = NULL;
unsigned char *prod = NULL;
unsigned char *sern = NULL;
unsigned char *mac_addr;
manu = kmalloc(256, GFP_KERNEL);
prod = kmalloc(256, GFP_KERNEL);
sern = kmalloc(256, GFP_KERNEL);
if (!manu || !prod || !sern) {
dbg("no mem for log_device_info");
goto fini;
}
// Default empty strings in case we don't find a real one
manu[0] = 0x00;
prod[0] = 0x00;
sern[0] = 0x00;
// Try to get the device Manufacturer
string_num = ether_dev->usb->descriptor.iManufacturer;
if (string_num) {
// Put it into its buffer
len = usb_string(ether_dev->usb, string_num, manu, 255);
// Just to be safe
manu[len] = 0x00;
}
// Try to get the device Product Name
string_num = ether_dev->usb->descriptor.iProduct;
if (string_num) {
// Put it into its buffer
len = usb_string(ether_dev->usb, string_num, prod, 255);
// Just to be safe
prod[len] = 0x00;
}
// Try to get the device Serial Number
string_num = ether_dev->usb->descriptor.iSerialNumber;
if (string_num) {
// Put it into its buffer
len = usb_string(ether_dev->usb, string_num, sern, 255);
// Just to be safe
sern[len] = 0x00;
}
// This makes it easier for us to print
mac_addr = ether_dev->net->dev_addr;
// Now send everything we found to the syslog
info( "%s: %s %s %s %02X:%02X:%02X:%02X:%02X:%02X",
ether_dev->net->name, manu, prod, sern, mac_addr[0],
mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4],
mac_addr[5] );
fini:
kfree(manu);
kfree(prod);
kfree(sern);
}
/* Forward declaration */
static struct usb_driver CDCEther_driver ;
//////////////////////////////////////////////////////////////////////////////
// Module's probe routine ////////////////////////////////////////////////////
// claims interfaces if they are for an Ethernet CDC /////////////////////////
//////////////////////////////////////////////////////////////////////////////
static int CDCEther_probe( struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *usb = interface_to_usbdev(intf);
struct net_device *net;
ether_dev_t *ether_dev;
int rc;
// First we should check the active configuration to see if
// any other driver has claimed any of the interfaces.
if ( check_for_claimed_interfaces( usb->actconfig ) ) {
// Someone has already put there grubby paws on this device.
// We don't want it now...
return -ENODEV;
}
// We might be finding a device we can use.
// We all go ahead and allocate our storage space.
// We need to because we have to start filling in the data that
// we are going to need later.
if(!(ether_dev = kmalloc(sizeof(ether_dev_t), GFP_KERNEL))) {
err("out of memory allocating device structure");
return -ENOMEM;
}
// Zero everything out.
memset(ether_dev, 0, sizeof(ether_dev_t));
ether_dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!ether_dev->rx_urb) {
kfree(ether_dev);
return -ENOMEM;
}
ether_dev->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!ether_dev->tx_urb) {
usb_free_urb(ether_dev->rx_urb);
kfree(ether_dev);
return -ENOMEM;
}
ether_dev->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!ether_dev->intr_urb) {
usb_free_urb(ether_dev->tx_urb);
usb_free_urb(ether_dev->rx_urb);
kfree(ether_dev);
return -ENOMEM;
}
// Let's see if we can find a configuration we can use.
rc = find_valid_configuration( usb, ether_dev );
if (rc) {
// Nope we couldn't find one we liked.
// This device was not meant for us to control.
goto error_all;
}
// Now that we FOUND a configuration. let's try to make the
// device go into it.
if ( usb_set_configuration( usb, ether_dev->bConfigurationValue ) ) {
err("usb_set_configuration() failed");
goto error_all;
}
// Now set the communication interface up as required.
if (usb_set_interface(usb, ether_dev->comm_bInterfaceNumber, ether_dev->comm_bAlternateSetting)) {
err("usb_set_interface() failed");
goto error_all;
}
// Only turn traffic on right now if we must...
if (ether_dev->data_interface_altset_num_without_traffic >= 0) {
// We found an alternate setting for the data
// interface that allows us to turn off traffic.
// We should use it.
if (usb_set_interface( usb,
ether_dev->data_bInterfaceNumber,
ether_dev->data_bAlternateSetting_without_traffic)) {
err("usb_set_interface() failed");
goto error_all;
}
} else {
// We didn't find an alternate setting for the data
// interface that would let us turn off traffic.
// Oh well, let's go ahead and do what we must...
if (usb_set_interface( usb,
ether_dev->data_bInterfaceNumber,
ether_dev->data_bAlternateSetting_with_traffic)) {
err("usb_set_interface() failed");
goto error_all;
}
}
// Now we need to get a kernel Ethernet interface.
net = alloc_etherdev(0);
if ( !net ) {
// Hmm... The kernel is not sharing today...
// Fine, we didn't want it anyway...
err( "Unable to initialize ethernet device" );
goto error_all;
}
// Now that we have an ethernet device, let's set it up
// (And I don't mean "set [it] up the bomb".)
net->priv = ether_dev;
SET_MODULE_OWNER(net);
net->open = CDCEther_open;
net->stop = CDCEther_close;
net->watchdog_timeo = CDC_ETHER_TX_TIMEOUT;
net->tx_timeout = CDCEther_tx_timeout; // TX timeout function
net->do_ioctl = CDCEther_ioctl;
net->hard_start_xmit = CDCEther_start_xmit;
net->set_multicast_list = CDCEther_set_multicast;
net->get_stats = CDCEther_netdev_stats;
net->mtu = ether_dev->wMaxSegmentSize - 14;
// We'll keep track of this information for later...
ether_dev->usb = usb;
ether_dev->net = net;
// and don't forget the MAC address.
set_ethernet_addr( ether_dev );
// Send a message to syslog about what we are handling
log_device_info( ether_dev );
// I claim this interface to be a CDC Ethernet Networking device
usb_driver_claim_interface( &CDCEther_driver,
&(usb->config[ether_dev->configuration_num].interface[ether_dev->comm_interface]),
ether_dev );
// I claim this interface to be a CDC Ethernet Networking device
usb_driver_claim_interface( &CDCEther_driver,
&(usb->config[ether_dev->configuration_num].interface[ether_dev->data_interface]),
ether_dev );
// Does this REALLY do anything???
usb_get_dev( usb );
// TODO - last minute HACK
ether_dev->comm_ep_in = 5;
if (register_netdev(net) != 0) {
usb_put_dev(usb);
goto out;
}
/* FIXME!!! This driver needs to be fixed to work with the new USB interface logic
* this is not the correct thing to be doing here, we need to set the interface
* driver specific data field.
*/
// Okay, we are finally done...
return 0;
out:
usb_driver_release_interface( &CDCEther_driver,
&(usb->config[ether_dev->configuration_num].interface[ether_dev->comm_interface]) );
usb_driver_release_interface( &CDCEther_driver,
&(usb->config[ether_dev->configuration_num].interface[ether_dev->data_interface]) );
// bailing out with our tail between our knees
error_all:
usb_free_urb(ether_dev->tx_urb);
usb_free_urb(ether_dev->rx_urb);
usb_free_urb(ether_dev->intr_urb);
kfree( ether_dev );
return -EIO;
}
//////////////////////////////////////////////////////////////////////////////
// Module's disconnect routine ///////////////////////////////////////////////
// Called when the driver is unloaded or the device is unplugged /////////////
// (Whichever happens first assuming the driver suceeded at its probe) ///////
//////////////////////////////////////////////////////////////////////////////
static void CDCEther_disconnect( struct usb_interface *intf )
{
ether_dev_t *ether_dev = usb_get_intfdata(intf);
struct usb_device *usb;
usb_set_intfdata(intf, NULL);
// Sanity check!!!
if ( !ether_dev || !ether_dev->usb ) {
// We failed. We are insane!!!
warn("unregistering non-existant device");
return;
}
// Make sure we fail the sanity check if we try this again.
ether_dev->usb = NULL;
usb = interface_to_usbdev(intf);
// It is possible that this function is called before
// the "close" function.
// This tells the close function we are already disconnected
ether_dev->flags |= CDC_ETHER_UNPLUG;
// We don't need the network device any more
unregister_netdev( ether_dev->net );
// For sanity checks
ether_dev->net = NULL;
// I ask again, does this do anything???
usb_put_dev( usb );
// We are done with this interface
usb_driver_release_interface( &CDCEther_driver,
&(usb->config[ether_dev->configuration_num].interface[ether_dev->comm_interface]) );
// We are done with this interface too
usb_driver_release_interface( &CDCEther_driver,
&(usb->config[ether_dev->configuration_num].interface[ether_dev->data_interface]) );
// No more tied up kernel memory
usb_free_urb(ether_dev->intr_urb);
usb_free_urb(ether_dev->rx_urb);
usb_free_urb(ether_dev->rx_urb);
kfree( ether_dev );
// This does no good, but it looks nice!
ether_dev = NULL;
}
//////////////////////////////////////////////////////////////////////////////
// Driver info ///////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
static struct usb_driver CDCEther_driver = {
.owner = THIS_MODULE,
.name = "CDCEther",
.probe = CDCEther_probe,
.disconnect = CDCEther_disconnect,
.id_table = CDCEther_ids,
};
//////////////////////////////////////////////////////////////////////////////
// init and exit routines called when driver is installed and uninstalled ////
//////////////////////////////////////////////////////////////////////////////
int __init CDCEther_init(void)
{
info( "%s", version );
return usb_register( &CDCEther_driver );
}
void __exit CDCEther_exit(void)
{
usb_deregister( &CDCEther_driver );
}
//////////////////////////////////////////////////////////////////////////////
// Module info ///////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
module_init( CDCEther_init );
module_exit( CDCEther_exit );
MODULE_AUTHOR("Brad Hards and another");
MODULE_DESCRIPTION("USB CDC Ethernet driver");
MODULE_LICENSE("GPL");
MODULE_PARM (multicast_filter_limit, "i");
MODULE_PARM_DESC (multicast_filter_limit, "CDCEther maximum number of filtered multicast addresses");
MODULE_DEVICE_TABLE (usb, CDCEther_ids);
//////////////////////////////////////////////////////////////////////////////
// End of file ///////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// Portions of this file taken from
// Petko Manolov - Petkan (petkan@dce.bg)
// from his driver pegasus.h
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define CS_INTERFACE 0x24
#define CDC_ETHER_MAX_MTU 1536
#define CDC_ETHER_PRESENT 0x00000001
#define CDC_ETHER_RUNNING 0x00000002
#define CDC_ETHER_TX_BUSY 0x00000004
#define CDC_ETHER_RX_BUSY 0x00000008
#define CDC_ETHER_UNPLUG 0x00000040
#define CDC_ETHER_TX_TIMEOUT (HZ*10)
#define TX_UNDERRUN 0x80
#define EXCESSIVE_COL 0x40
#define LATE_COL 0x20
#define NO_CARRIER 0x10
#define LOSS_CARRIER 0x08
#define JABBER_TIMEOUT 0x04
#define CDC_ETHER_REQT_READ 0xc0
#define CDC_ETHER_REQT_WRITE 0x40
#define CDC_ETHER_REQ_GET_REGS 0xf0
#define CDC_ETHER_REQ_SET_REGS 0xf1
#define CDC_ETHER_REQ_SET_REG PIPERIDER_REQ_SET_REGS
#define L1_ALIGN(x) x __attribute__((aligned(L1_CACHE_BYTES)))
#define MODE_FLAG_PROMISCUOUS (1<<0)
#define MODE_FLAG_ALL_MULTICAST (1<<1)
#define MODE_FLAG_DIRECTED (1<<2)
#define MODE_FLAG_BROADCAST (1<<3)
#define MODE_FLAG_MULTICAST (1<<4)
#define SET_ETHERNET_MULTICAST_FILTER 0x40
#define SET_ETHERNET_PACKET_FILTER 0x43
typedef struct _ether_dev_t {
struct usb_device *usb;
struct net_device *net;
struct net_device_stats stats;
unsigned flags;
int configuration_num;
int bConfigurationValue;
int comm_interface;
int comm_bInterfaceNumber;
int comm_interface_altset_num;
int comm_bAlternateSetting;
int comm_ep_in;
int data_interface;
int data_bInterfaceNumber;
int data_interface_altset_num_with_traffic;
int data_bAlternateSetting_with_traffic;
int data_interface_altset_num_without_traffic;
int data_bAlternateSetting_without_traffic;
int data_ep_in;
int data_ep_out;
int data_ep_out_size;
__u16 bcdCDC;
__u8 iMACAddress;
__u32 bmEthernetStatistics;
__u16 wMaxSegmentSize;
__u16 mode_flags;
__u16 wNumberMCFilters;
__u8 bNumberPowerFilters;
int intr_interval;
struct urb *rx_urb, *tx_urb, *intr_urb;
unsigned char L1_ALIGN(rx_buff[CDC_ETHER_MAX_MTU]);
unsigned char L1_ALIGN(tx_buff[CDC_ETHER_MAX_MTU]);
unsigned char L1_ALIGN(intr_buff[8]);
} ether_dev_t;
#define REQ_HDR_FUNC_DESCR 0x0001
#define REQ_UNION_FUNC_DESCR 0x0002
#define REQ_ETH_FUNC_DESCR 0x0004
#define REQUIREMENTS_TOTAL 0x0007
...@@ -56,9 +56,11 @@ ...@@ -56,9 +56,11 @@
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/pci.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/semaphore.h> #include <asm/semaphore.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <asm/dma-mapping.h>
#define DEBUG #define DEBUG
...@@ -215,8 +217,10 @@ struct kaweth_device ...@@ -215,8 +217,10 @@ struct kaweth_device
__u32 status; __u32 status;
int end; int end;
int removed; int removed;
int suspend_lowmem; int suspend_lowmem_rx;
int suspend_lowmem_ctrl;
int linkstate; int linkstate;
struct work_struct lowmem_work;
struct usb_device *dev; struct usb_device *dev;
struct net_device *net; struct net_device *net;
...@@ -475,14 +479,29 @@ static int kaweth_resubmit_rx_urb(struct kaweth_device *, int); ...@@ -475,14 +479,29 @@ static int kaweth_resubmit_rx_urb(struct kaweth_device *, int);
/**************************************************************** /****************************************************************
int_callback int_callback
*****************************************************************/ *****************************************************************/
static void kaweth_resubmit_int_urb(struct kaweth_device *kaweth, int mf)
{
int status;
status = usb_submit_urb (kaweth->irq_urb, mf);
if (unlikely(status == -ENOMEM)) {
kaweth->suspend_lowmem_ctrl = 1;
schedule_delayed_work(&kaweth->lowmem_work, HZ/4);
} else {
kaweth->suspend_lowmem_ctrl = 0;
}
if (status)
err ("can't resubmit intr, %s-%s, status %d",
kaweth->dev->bus->bus_name,
kaweth->dev->devpath, status);
}
static void int_callback(struct urb *u, struct pt_regs *regs) static void int_callback(struct urb *u, struct pt_regs *regs)
{ {
struct kaweth_device *kaweth = u->context; struct kaweth_device *kaweth = u->context;
int act_state, status; int act_state;
/* we abuse the interrupt urb for rebsubmitting under low memory saving a timer */
if (kaweth->suspend_lowmem)
kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC);
switch (u->status) { switch (u->status) {
case 0: /* success */ case 0: /* success */
...@@ -506,13 +525,24 @@ static void int_callback(struct urb *u, struct pt_regs *regs) ...@@ -506,13 +525,24 @@ static void int_callback(struct urb *u, struct pt_regs *regs)
kaweth->linkstate = act_state; kaweth->linkstate = act_state;
} }
resubmit: resubmit:
status = usb_submit_urb (u, SLAB_ATOMIC); kaweth_resubmit_int_urb(kaweth, GFP_ATOMIC);
if (status) }
err ("can't resubmit intr, %s-%s, status %d",
kaweth->dev->bus->bus_name, static void kaweth_resubmit_tl(void *d)
kaweth->dev->devpath, status); {
struct kaweth_device *kaweth = (struct kaweth_device *)d;
if (kaweth->status | KAWETH_STATUS_CLOSING)
return;
if (kaweth->suspend_lowmem_rx)
kaweth_resubmit_rx_urb(kaweth, GFP_NOIO);
if (kaweth->suspend_lowmem_ctrl)
kaweth_resubmit_int_urb(kaweth, GFP_NOIO);
} }
/**************************************************************** /****************************************************************
* kaweth_resubmit_rx_urb * kaweth_resubmit_rx_urb
****************************************************************/ ****************************************************************/
...@@ -532,11 +562,13 @@ static int kaweth_resubmit_rx_urb(struct kaweth_device *kaweth, ...@@ -532,11 +562,13 @@ static int kaweth_resubmit_rx_urb(struct kaweth_device *kaweth,
kaweth->rx_urb->transfer_dma = kaweth->rxbufferhandle; kaweth->rx_urb->transfer_dma = kaweth->rxbufferhandle;
if((result = usb_submit_urb(kaweth->rx_urb, mem_flags))) { if((result = usb_submit_urb(kaweth->rx_urb, mem_flags))) {
if (result == -ENOMEM) if (result == -ENOMEM) {
kaweth->suspend_lowmem = 1; kaweth->suspend_lowmem_rx = 1;
schedule_delayed_work(&kaweth->lowmem_work, HZ/4);
}
kaweth_err("resubmitting rx_urb %d failed", result); kaweth_err("resubmitting rx_urb %d failed", result);
} else { } else {
kaweth->suspend_lowmem = 0; kaweth->suspend_lowmem_rx = 0;
} }
return result; return result;
...@@ -665,6 +697,13 @@ static int kaweth_close(struct net_device *net) ...@@ -665,6 +697,13 @@ static int kaweth_close(struct net_device *net)
usb_unlink_urb(kaweth->irq_urb); usb_unlink_urb(kaweth->irq_urb);
usb_unlink_urb(kaweth->rx_urb); usb_unlink_urb(kaweth->rx_urb);
flush_scheduled_work();
/* a scheduled work may have resubmitted,
we hit them again */
usb_unlink_urb(kaweth->irq_urb);
usb_unlink_urb(kaweth->rx_urb);
kaweth->status &= ~KAWETH_STATUS_CLOSING; kaweth->status &= ~KAWETH_STATUS_CLOSING;
return 0; return 0;
...@@ -1075,10 +1114,15 @@ static int kaweth_probe( ...@@ -1075,10 +1114,15 @@ static int kaweth_probe(
memset(&kaweth->stats, 0, sizeof(kaweth->stats)); memset(&kaweth->stats, 0, sizeof(kaweth->stats));
INIT_WORK(&kaweth->lowmem_work, kaweth_resubmit_tl, (void *)kaweth);
SET_MODULE_OWNER(netdev); SET_MODULE_OWNER(netdev);
usb_set_intfdata(intf, kaweth); usb_set_intfdata(intf, kaweth);
if (dma_supported (&intf->dev, 0xffffffffffffffffULL))
kaweth->net->features |= NETIF_F_HIGHDMA;
if (register_netdev(netdev) != 0) { if (register_netdev(netdev) != 0) {
kaweth_err("Error calling init_etherdev."); kaweth_err("Error calling init_etherdev.");
goto err_intfdata; goto err_intfdata;
...@@ -1092,7 +1136,6 @@ static int kaweth_probe( ...@@ -1092,7 +1136,6 @@ static int kaweth_probe(
err_intfdata: err_intfdata:
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
err_all:
usb_buffer_free(kaweth->dev, KAWETH_BUF_SIZE, (void *)kaweth->rx_buf, kaweth->rxbufferhandle); usb_buffer_free(kaweth->dev, KAWETH_BUF_SIZE, (void *)kaweth->rx_buf, kaweth->rxbufferhandle);
err_all_but_rxbuf: err_all_but_rxbuf:
usb_buffer_free(kaweth->dev, INTBUFFERSIZE, (void *)kaweth->intbuffer, kaweth->intbufferhandle); usb_buffer_free(kaweth->dev, INTBUFFERSIZE, (void *)kaweth->intbuffer, kaweth->intbufferhandle);
......
...@@ -412,6 +412,7 @@ struct divisor_table_entry { ...@@ -412,6 +412,7 @@ struct divisor_table_entry {
// MCR.7 = 0. // MCR.7 = 0.
// //
static struct divisor_table_entry divisor_table[] = { static struct divisor_table_entry divisor_table[] = {
{ 50, 4608},
{ 75, 3072}, { 75, 3072},
{ 110, 2095}, /* 2094.545455 => 230450 => .0217 % over */ { 110, 2095}, /* 2094.545455 => 230450 => .0217 % over */
{ 134, 1713}, /* 1713.011152 => 230398.5 => .00065% under */ { 134, 1713}, /* 1713.011152 => 230398.5 => .00065% under */
...@@ -2591,7 +2592,7 @@ static int calc_baud_rate_divisor (int baudrate, int *divisor) ...@@ -2591,7 +2592,7 @@ static int calc_baud_rate_divisor (int baudrate, int *divisor)
// We have tried all of the standard baud rates // We have tried all of the standard baud rates
// lets try to calculate the divisor for this baud rate // lets try to calculate the divisor for this baud rate
// Make sure the baud rate is reasonable // Make sure the baud rate is reasonable
if (baudrate > 75 && baudrate < 230400) { if (baudrate < 230400) {
// get divisor // get divisor
custom = (__u16)(230400L / baudrate); custom = (__u16)(230400L / baudrate);
......
...@@ -537,16 +537,20 @@ static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file) ...@@ -537,16 +537,20 @@ static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file)
struct pl2303_private *priv = usb_get_serial_port_data(port); struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned long flags; unsigned long flags;
unsigned int mcr; unsigned int mcr;
unsigned int status;
unsigned int result; unsigned int result;
dbg("%s (%d)", __FUNCTION__, port->number); dbg("%s (%d)", __FUNCTION__, port->number);
spin_lock_irqsave (&priv->lock, flags); spin_lock_irqsave (&priv->lock, flags);
mcr = priv->line_control; mcr = priv->line_control;
status = priv->line_status;
spin_unlock_irqrestore (&priv->lock, flags); spin_unlock_irqrestore (&priv->lock, flags);
result = ((mcr & CONTROL_DTR) ? TIOCM_DTR : 0) result = ((mcr & CONTROL_DTR) ? TIOCM_DTR : 0)
| ((mcr & CONTROL_RTS) ? TIOCM_RTS : 0); | ((mcr & CONTROL_RTS) ? TIOCM_RTS : 0)
| ((status & UART_CTS) ? TIOCM_CTS : 0)
| ((status & UART_DSR) ? TIOCM_DSR : 0);
dbg("%s - result = %x", __FUNCTION__, result); dbg("%s - result = %x", __FUNCTION__, result);
......
...@@ -12,6 +12,10 @@ ...@@ -12,6 +12,10 @@
* *
* See Documentation/usb/usb-serial.txt for more information on using this driver * See Documentation/usb/usb-serial.txt for more information on using this driver
* *
* (06/03/2003) Judd Montgomery <judd at jpilot.org>
* Added support for module parameter options for untested/unknown
* devices.
*
* (03/09/2003) gkh * (03/09/2003) gkh
* Added support for the Sony Clie NZ90V device. Thanks to Martin Brachtl * Added support for the Sony Clie NZ90V device. Thanks to Martin Brachtl
* <brachtl@redgrep.cz> for the information. * <brachtl@redgrep.cz> for the information.
...@@ -188,6 +192,9 @@ static int treo_attach (struct usb_serial *serial); ...@@ -188,6 +192,9 @@ static int treo_attach (struct usb_serial *serial);
static int palm_os_3_probe (struct usb_serial *serial, const struct usb_device_id *id); static int palm_os_3_probe (struct usb_serial *serial, const struct usb_device_id *id);
static int palm_os_4_probe (struct usb_serial *serial, const struct usb_device_id *id); static int palm_os_4_probe (struct usb_serial *serial, const struct usb_device_id *id);
/* Parameters that may be passed into the module. */
static int vendor = -1;
static int product = -1;
static struct usb_device_id id_table [] = { static struct usb_device_id id_table [] = {
{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID), { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID),
...@@ -223,6 +230,7 @@ static struct usb_device_id id_table [] = { ...@@ -223,6 +230,7 @@ static struct usb_device_id id_table [] = {
.driver_info = (kernel_ulong_t)&palm_os_4_probe }, .driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID), { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe }, .driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ }, /* optional parameter entry */
{ } /* Terminating entry */ { } /* Terminating entry */
}; };
...@@ -250,6 +258,7 @@ static struct usb_device_id id_table_combined [] = { ...@@ -250,6 +258,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID) },
{ }, /* optional parameter entry */
{ } /* Terminating entry */ { } /* Terminating entry */
}; };
...@@ -942,6 +951,33 @@ static void visor_set_termios (struct usb_serial_port *port, struct termios *old ...@@ -942,6 +951,33 @@ static void visor_set_termios (struct usb_serial_port *port, struct termios *old
static int __init visor_init (void) static int __init visor_init (void)
{ {
int i;
/* Only if parameters were passed to us */
if ((vendor>0) && (product>0)) {
struct usb_device_id usb_dev_temp[]=
{{USB_DEVICE(vendor, product),
.driver_info = (kernel_ulong_t)&palm_os_4_probe }};
/* Find the last entry in id_table */
for (i=0; ; i++) {
if (id_table[i].idVendor==0) {
id_table[i] = usb_dev_temp[0];
break;
}
}
/* Find the last entry in id_table_combined */
for (i=0; ; i++) {
if (id_table_combined[i].idVendor==0) {
id_table_combined[i] = usb_dev_temp[0];
break;
}
}
info("Untested USB device specified at time of module insertion");
info("Warning: This is not guaranteed to work");
info("Using a newer kernel is preferred to this method");
info("Adding Palm OS protocol 4.x support for unknown device: 0x%x/0x%x",
vendor, product);
}
usb_serial_register (&handspring_device); usb_serial_register (&handspring_device);
usb_serial_register (&clie_3_5_device); usb_serial_register (&clie_3_5_device);
usb_register (&visor_driver); usb_register (&visor_driver);
...@@ -969,3 +1005,7 @@ MODULE_LICENSE("GPL"); ...@@ -969,3 +1005,7 @@ MODULE_LICENSE("GPL");
MODULE_PARM(debug, "i"); MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug enabled or not"); MODULE_PARM_DESC(debug, "Debug enabled or not");
MODULE_PARM(vendor, "i");
MODULE_PARM_DESC(vendor, "User specified vendor ID");
MODULE_PARM(product, "i");
MODULE_PARM_DESC(product, "User specified product ID");
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include "initializers.h" #include "initializers.h"
#include "debug.h" #include "debug.h"
#include "transport.h"
/* This places the Shuttle/SCM USB<->SCSI bridge devices in multi-target /* This places the Shuttle/SCM USB<->SCSI bridge devices in multi-target
* mode */ * mode */
...@@ -59,4 +60,50 @@ int usb_stor_euscsi_init(struct us_data *us) ...@@ -59,4 +60,50 @@ int usb_stor_euscsi_init(struct us_data *us)
return 0; return 0;
} }
/* This function is required to activate all four slots on the UCR-61S2B
* flash reader */
int usb_stor_ucr61s2b_init(struct us_data *us)
{
struct bulk_cb_wrap *bcb;
struct bulk_cs_wrap *bcs;
int res, partial;
bcb = kmalloc(sizeof *bcb, in_interrupt() ? GFP_ATOMIC : GFP_NOIO);
if (!bcb) {
return(-1);
}
bcs = kmalloc(sizeof *bcs, in_interrupt() ? GFP_ATOMIC : GFP_NOIO);
if (!bcs) {
kfree(bcb);
return(-1);
}
US_DEBUGP("Sending UCR-61S2B initialization packet...\n");
bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
bcb->Tag = 0;
bcb->DataTransferLength = cpu_to_le32(0);
bcb->Flags = bcb->Lun = 0;
bcb->Length = sizeof(UCR61S2B_INIT);
memset(bcb->CDB, 0, sizeof(bcb->CDB));
memcpy(bcb->CDB, UCR61S2B_INIT, sizeof(UCR61S2B_INIT));
res = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, bcb,
US_BULK_CB_WRAP_LEN, &partial);
US_DEBUGP("-- result is %d\n", res);
kfree(bcb);
if(res) {
kfree(bcs);
return(res);
}
res = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, bcs,
US_BULK_CS_WRAP_LEN, &partial);
US_DEBUGP("-- result of status read is %d\n", res);
kfree(bcs);
return(res ? -1 : 0);
}
...@@ -48,3 +48,9 @@ int usb_stor_euscsi_init(struct us_data *us); ...@@ -48,3 +48,9 @@ int usb_stor_euscsi_init(struct us_data *us);
#ifdef CONFIG_USB_STORAGE_SDDR09 #ifdef CONFIG_USB_STORAGE_SDDR09
int sddr09_init(struct us_data *us); int sddr09_init(struct us_data *us);
#endif #endif
#define UCR61S2B_INIT "\xec\x0a\x06\x00$PCCHIPS"
/* This function is required to activate all four slots on the UCR-61S2B
* flash reader */
int usb_stor_ucr61s2b_init(struct us_data *us);
...@@ -595,6 +595,16 @@ UNUSUAL_DEV( 0x0a16, 0x8888, 0x0100, 0x0100, ...@@ -595,6 +595,16 @@ UNUSUAL_DEV( 0x0a16, 0x8888, 0x0100, 0x0100,
US_SC_SCSI, US_PR_BULK, NULL, US_SC_SCSI, US_PR_BULK, NULL,
US_FL_FIX_INQUIRY ), US_FL_FIX_INQUIRY ),
/* Pentax Optio S digital camera
* adapted from http://www2.goldfisch.at/knowledge/233
* (Peter Pilsl <pilsl@goldfisch.at>)
* by Christoph Weidemann <cweidema@indiana.edu> */
UNUSUAL_DEV( 0x0a17, 0x0006, 0x0000, 0xffff,
"Pentax",
"Optio S",
US_SC_8070, US_PR_CB, NULL,
US_FL_MODE_XLATE|US_FL_FIX_INQUIRY),
#ifdef CONFIG_USB_STORAGE_ISD200 #ifdef CONFIG_USB_STORAGE_ISD200
UNUSUAL_DEV( 0x0bf6, 0xa001, 0x0100, 0x0110, UNUSUAL_DEV( 0x0bf6, 0xa001, 0x0100, 0x0110,
"ATI", "ATI",
...@@ -610,6 +620,16 @@ UNUSUAL_DEV( 0x1065, 0x2136, 0x0000, 0x0001, ...@@ -610,6 +620,16 @@ UNUSUAL_DEV( 0x1065, 0x2136, 0x0000, 0x0001,
US_SC_SCSI, US_PR_BULK, NULL, US_SC_SCSI, US_PR_BULK, NULL,
US_FL_MODE_XLATE | US_FL_FIX_INQUIRY ), US_FL_MODE_XLATE | US_FL_FIX_INQUIRY ),
/* Reported by Kevin Cernekee <kpc-usbdev@gelato.uiuc.edu>
* Tested on hardware version 1.10.
* Entry is needed only for the initializer function override.
*/
UNUSUAL_DEV( 0x1019, 0x0c55, 0x0000, 0x9999,
"Desknote",
"UCR-61S2B",
US_SC_DEVICE, US_PR_DEVICE, usb_stor_ucr61s2b_init,
0 ),
/* Reported by Dan Pilone <pilone@slac.com> /* Reported by Dan Pilone <pilone@slac.com>
* The device needs the flags only. * The device needs the flags only.
* Also reported by Brian Hall <brihall@pcisys.net>, again for flags. * Also reported by Brian Hall <brihall@pcisys.net>, again for flags.
......
...@@ -10,11 +10,6 @@ ...@@ -10,11 +10,6 @@
* All Rights Reserved. * All Rights Reserved.
* *
* This software is licensed under the GNU GPL version 2. * This software is licensed under the GNU GPL version 2.
*
* ALTERNATIVELY, the kernel API documentation which is included in this
* software may also be licenced under the "GNU Free Documentation
* License" (version 1.2 or, at your choice, any later version), when
* used as part of the "USB Gadget API for Linux" documentation.
*/ */
#ifndef __LINUX_USB_GADGET_H #ifndef __LINUX_USB_GADGET_H
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment