mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-03-17 20:44:37 +00:00
Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6
This commit is contained in:
commit
5420520973
80 changed files with 7407 additions and 4077 deletions
203
Documentation/input/yealink.txt
Normal file
203
Documentation/input/yealink.txt
Normal file
|
@ -0,0 +1,203 @@
|
|||
Driver documentation for yealink usb-p1k phones
|
||||
|
||||
0. Status
|
||||
~~~~~~~~~
|
||||
|
||||
The p1k is a relatively cheap usb 1.1 phone with:
|
||||
- keyboard full support, yealink.ko / input event API
|
||||
- LCD full support, yealink.ko / sysfs API
|
||||
- LED full support, yealink.ko / sysfs API
|
||||
- dialtone full support, yealink.ko / sysfs API
|
||||
- ringtone full support, yealink.ko / sysfs API
|
||||
- audio playback full support, snd_usb_audio.ko / alsa API
|
||||
- audio record full support, snd_usb_audio.ko / alsa API
|
||||
|
||||
For vendor documentation see http://www.yealink.com
|
||||
|
||||
|
||||
1. Compilation (stand alone version)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Currently only kernel 2.6.x.y versions are supported.
|
||||
In order to build the yealink.ko module do:
|
||||
|
||||
make
|
||||
|
||||
If you encounter problems please check if in the MAKE_OPTS variable in
|
||||
the Makefile is pointing to the location where your kernel sources
|
||||
are located, default /usr/src/linux.
|
||||
|
||||
|
||||
|
||||
2. keyboard features
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
The current mapping in the kernel is provided by the map_p1k_to_key
|
||||
function:
|
||||
|
||||
Physical USB-P1K button layout input events
|
||||
|
||||
|
||||
up up
|
||||
IN OUT left, right
|
||||
down down
|
||||
|
||||
pickup C hangup enter, backspace, escape
|
||||
1 2 3 1, 2, 3
|
||||
4 5 6 4, 5, 6,
|
||||
7 8 9 7, 8, 9,
|
||||
* 0 # *, 0, #,
|
||||
|
||||
The "up" and "down" keys, are symbolised by arrows on the button.
|
||||
The "pickup" and "hangup" keys are symbolised by a green and red phone
|
||||
on the button.
|
||||
|
||||
|
||||
3. LCD features
|
||||
~~~~~~~~~~~~~~~
|
||||
The LCD is divided and organised as a 3 line display:
|
||||
|
||||
|[] [][] [][] [][] in |[][]
|
||||
|[] M [][] D [][] : [][] out |[][]
|
||||
store
|
||||
|
||||
NEW REP SU MO TU WE TH FR SA
|
||||
|
||||
[] [] [] [] [] [] [] [] [] [] [] []
|
||||
[] [] [] [] [] [] [] [] [] [] [] []
|
||||
|
||||
|
||||
Line 1 Format (see below) : 18.e8.M8.88...188
|
||||
Icon names : M D : IN OUT STORE
|
||||
Line 2 Format : .........
|
||||
Icon name : NEW REP SU MO TU WE TH FR SA
|
||||
Line 3 Format : 888888888888
|
||||
|
||||
|
||||
Format description:
|
||||
From a user space perspective the world is seperated in "digits" and "icons".
|
||||
A digit can have a character set, an icon can only be ON or OFF.
|
||||
|
||||
Format specifier
|
||||
'8' : Generic 7 segment digit with individual addressable segments
|
||||
|
||||
Reduced capabillity 7 segm digit, when segments are hard wired together.
|
||||
'1' : 2 segments digit only able to produce a 1.
|
||||
'e' : Most significant day of the month digit,
|
||||
able to produce at least 1 2 3.
|
||||
'M' : Most significant minute digit,
|
||||
able to produce at least 0 1 2 3 4 5.
|
||||
|
||||
Icons or pictograms:
|
||||
'.' : For example like AM, PM, SU, a 'dot' .. or other single segment
|
||||
elements.
|
||||
|
||||
|
||||
4. Driver usage
|
||||
~~~~~~~~~~~~~~~
|
||||
For userland the following interfaces are available using the sysfs interface:
|
||||
/sys/.../
|
||||
line1 Read/Write, lcd line1
|
||||
line2 Read/Write, lcd line2
|
||||
line3 Read/Write, lcd line3
|
||||
|
||||
get_icons Read, returns a set of available icons.
|
||||
hide_icon Write, hide the element by writing the icon name.
|
||||
show_icon Write, display the element by writing the icon name.
|
||||
|
||||
map_seg7 Read/Write, the 7 segments char set, common for all
|
||||
yealink phones. (see map_to_7segment.h)
|
||||
|
||||
ringtone Write, upload binary representation of a ringtone,
|
||||
see yealink.c. status EXPERIMENTAL due to potential
|
||||
races between async. and sync usb calls.
|
||||
|
||||
|
||||
4.1 lineX
|
||||
~~~~~~~~~
|
||||
Reading /sys/../lineX will return the format string with its current value:
|
||||
|
||||
Example:
|
||||
cat ./line3
|
||||
888888888888
|
||||
Linux Rocks!
|
||||
|
||||
Writing to /sys/../lineX will set the coresponding LCD line.
|
||||
- Excess characters are ignored.
|
||||
- If less characters are written than allowed, the remaining digits are
|
||||
unchanged.
|
||||
- The tab '\t'and '\n' char does not overwrite the original content.
|
||||
- Writing a space to an icon will always hide its content.
|
||||
|
||||
Example:
|
||||
date +"%m.%e.%k:%M" | sed 's/^0/ /' > ./line1
|
||||
|
||||
Will update the LCD with the current date & time.
|
||||
|
||||
|
||||
4.2 get_icons
|
||||
~~~~~~~~~~~~~
|
||||
Reading will return all available icon names and its current settings:
|
||||
|
||||
cat ./get_icons
|
||||
on M
|
||||
on D
|
||||
on :
|
||||
IN
|
||||
OUT
|
||||
STORE
|
||||
NEW
|
||||
REP
|
||||
SU
|
||||
MO
|
||||
TU
|
||||
WE
|
||||
TH
|
||||
FR
|
||||
SA
|
||||
LED
|
||||
DIALTONE
|
||||
RINGTONE
|
||||
|
||||
|
||||
4.3 show/hide icons
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
Writing to these files will update the state of the icon.
|
||||
Only one icon at a time can be updated.
|
||||
|
||||
If an icon is also on a ./lineX the corresponding value is
|
||||
updated with the first letter of the icon.
|
||||
|
||||
Example - light up the store icon:
|
||||
echo -n "STORE" > ./show_icon
|
||||
|
||||
cat ./line1
|
||||
18.e8.M8.88...188
|
||||
S
|
||||
|
||||
Example - sound the ringtone for 10 seconds:
|
||||
echo -n RINGTONE > /sys/..../show_icon
|
||||
sleep 10
|
||||
echo -n RINGTONE > /sys/..../hide_icon
|
||||
|
||||
|
||||
5. Sound features
|
||||
~~~~~~~~~~~~~~~~~
|
||||
Sound is supported by the ALSA driver: snd_usb_audio
|
||||
|
||||
One 16-bit channel with sample and playback rates of 8000 Hz is the practical
|
||||
limit of the device.
|
||||
|
||||
Example - recording test:
|
||||
arecord -v -d 10 -r 8000 -f S16_LE -t wav foobar.wav
|
||||
|
||||
Example - playback test:
|
||||
aplay foobar.wav
|
||||
|
||||
|
||||
6. Credits & Acknowledgments
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
- Olivier Vandorpe, for starting the usbb2k-api project doing much of
|
||||
the reverse engineering.
|
||||
- Martin Diehl, for pointing out how to handle USB memory allocation.
|
||||
- Dmitry Torokhov, for the numerous code reviews and suggestions.
|
||||
|
|
@ -116,6 +116,12 @@ M: ajk@iehk.rwth-aachen.de
|
|||
L: linux-hams@vger.kernel.org
|
||||
S: Maintained
|
||||
|
||||
YEALINK PHONE DRIVER
|
||||
P: Henk Vergonet
|
||||
M: Henk.Vergonet@gmail.com
|
||||
L: usbb2k-api-dev@nongnu.org
|
||||
S: Maintained
|
||||
|
||||
8139CP 10/100 FAST ETHERNET DRIVER
|
||||
P: Jeff Garzik
|
||||
M: jgarzik@pobox.com
|
||||
|
|
|
@ -16,9 +16,10 @@
|
|||
* -- verify the 13 conditions and do bulk resets
|
||||
* -- kill last_pipe and simply do two-state clearing on both pipes
|
||||
* -- verify protocol (bulk) from USB descriptors (maybe...)
|
||||
* -- highmem and sg
|
||||
* -- highmem
|
||||
* -- move top_sense and work_bcs into separate allocations (if they survive)
|
||||
* for cache purists and esoteric architectures.
|
||||
* -- Allocate structure for LUN 0 before the first ub_sync_tur, avoid NULL. ?
|
||||
* -- prune comments, they are too volumnous
|
||||
* -- Exterminate P3 printks
|
||||
* -- Resove XXX's
|
||||
|
@ -171,7 +172,7 @@ struct bulk_cs_wrap {
|
|||
*/
|
||||
struct ub_dev;
|
||||
|
||||
#define UB_MAX_REQ_SG 1
|
||||
#define UB_MAX_REQ_SG 4
|
||||
#define UB_MAX_SECTORS 64
|
||||
|
||||
/*
|
||||
|
@ -234,13 +235,10 @@ struct ub_scsi_cmd {
|
|||
|
||||
int stat_count; /* Retries getting status. */
|
||||
|
||||
/*
|
||||
* We do not support transfers from highmem pages
|
||||
* because the underlying USB framework does not do what we need.
|
||||
*/
|
||||
char *data; /* Requested buffer */
|
||||
unsigned int len; /* Requested length */
|
||||
// struct scatterlist sgv[UB_MAX_REQ_SG];
|
||||
unsigned int current_sg;
|
||||
unsigned int nsg; /* sgv[nsg] */
|
||||
struct scatterlist sgv[UB_MAX_REQ_SG];
|
||||
|
||||
struct ub_lun *lun;
|
||||
void (*done)(struct ub_dev *, struct ub_scsi_cmd *);
|
||||
|
@ -389,17 +387,18 @@ struct ub_dev {
|
|||
struct bulk_cs_wrap work_bcs;
|
||||
struct usb_ctrlrequest work_cr;
|
||||
|
||||
int sg_stat[UB_MAX_REQ_SG+1];
|
||||
struct ub_scsi_trace tr;
|
||||
};
|
||||
|
||||
/*
|
||||
*/
|
||||
static void ub_cleanup(struct ub_dev *sc);
|
||||
static int ub_bd_rq_fn_1(struct ub_lun *lun, struct request *rq);
|
||||
static int ub_request_fn_1(struct ub_lun *lun, struct request *rq);
|
||||
static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
|
||||
struct ub_scsi_cmd *cmd, struct request *rq);
|
||||
static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
|
||||
struct request *rq);
|
||||
static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
|
||||
struct ub_scsi_cmd *cmd, struct request *rq);
|
||||
static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
|
||||
static void ub_end_rq(struct request *rq, int uptodate);
|
||||
static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
|
||||
|
@ -407,6 +406,7 @@ static void ub_urb_complete(struct urb *urb, struct pt_regs *pt);
|
|||
static void ub_scsi_action(unsigned long _dev);
|
||||
static void ub_scsi_dispatch(struct ub_dev *sc);
|
||||
static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
|
||||
static void ub_data_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
|
||||
static void ub_state_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int rc);
|
||||
static int __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
|
||||
static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
|
||||
|
@ -500,7 +500,8 @@ static void ub_cmdtr_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
|
|||
}
|
||||
}
|
||||
|
||||
static ssize_t ub_diag_show(struct device *dev, struct device_attribute *attr, char *page)
|
||||
static ssize_t ub_diag_show(struct device *dev, struct device_attribute *attr,
|
||||
char *page)
|
||||
{
|
||||
struct usb_interface *intf;
|
||||
struct ub_dev *sc;
|
||||
|
@ -523,6 +524,13 @@ static ssize_t ub_diag_show(struct device *dev, struct device_attribute *attr, c
|
|||
cnt += sprintf(page + cnt,
|
||||
"qlen %d qmax %d\n",
|
||||
sc->cmd_queue.qlen, sc->cmd_queue.qmax);
|
||||
cnt += sprintf(page + cnt,
|
||||
"sg %d %d %d %d %d\n",
|
||||
sc->sg_stat[0],
|
||||
sc->sg_stat[1],
|
||||
sc->sg_stat[2],
|
||||
sc->sg_stat[3],
|
||||
sc->sg_stat[4]);
|
||||
|
||||
list_for_each (p, &sc->luns) {
|
||||
lun = list_entry(p, struct ub_lun, link);
|
||||
|
@ -744,20 +752,20 @@ static struct ub_scsi_cmd *ub_cmdq_pop(struct ub_dev *sc)
|
|||
* The request function is our main entry point
|
||||
*/
|
||||
|
||||
static void ub_bd_rq_fn(request_queue_t *q)
|
||||
static void ub_request_fn(request_queue_t *q)
|
||||
{
|
||||
struct ub_lun *lun = q->queuedata;
|
||||
struct request *rq;
|
||||
|
||||
while ((rq = elv_next_request(q)) != NULL) {
|
||||
if (ub_bd_rq_fn_1(lun, rq) != 0) {
|
||||
if (ub_request_fn_1(lun, rq) != 0) {
|
||||
blk_stop_queue(q);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int ub_bd_rq_fn_1(struct ub_lun *lun, struct request *rq)
|
||||
static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
|
||||
{
|
||||
struct ub_dev *sc = lun->udev;
|
||||
struct ub_scsi_cmd *cmd;
|
||||
|
@ -774,9 +782,8 @@ static int ub_bd_rq_fn_1(struct ub_lun *lun, struct request *rq)
|
|||
memset(cmd, 0, sizeof(struct ub_scsi_cmd));
|
||||
|
||||
blkdev_dequeue_request(rq);
|
||||
|
||||
if (blk_pc_request(rq)) {
|
||||
rc = ub_cmd_build_packet(sc, cmd, rq);
|
||||
rc = ub_cmd_build_packet(sc, lun, cmd, rq);
|
||||
} else {
|
||||
rc = ub_cmd_build_block(sc, lun, cmd, rq);
|
||||
}
|
||||
|
@ -791,7 +798,7 @@ static int ub_bd_rq_fn_1(struct ub_lun *lun, struct request *rq)
|
|||
cmd->back = rq;
|
||||
|
||||
cmd->tag = sc->tagcnt++;
|
||||
if ((rc = ub_submit_scsi(sc, cmd)) != 0) {
|
||||
if (ub_submit_scsi(sc, cmd) != 0) {
|
||||
ub_put_cmd(lun, cmd);
|
||||
ub_end_rq(rq, 0);
|
||||
return 0;
|
||||
|
@ -804,58 +811,31 @@ static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
|
|||
struct ub_scsi_cmd *cmd, struct request *rq)
|
||||
{
|
||||
int ub_dir;
|
||||
#if 0 /* We use rq->buffer for now */
|
||||
struct scatterlist *sg;
|
||||
int n_elem;
|
||||
#endif
|
||||
unsigned int block, nblks;
|
||||
|
||||
if (rq_data_dir(rq) == WRITE)
|
||||
ub_dir = UB_DIR_WRITE;
|
||||
else
|
||||
ub_dir = UB_DIR_READ;
|
||||
cmd->dir = ub_dir;
|
||||
|
||||
/*
|
||||
* get scatterlist from block layer
|
||||
*/
|
||||
#if 0 /* We use rq->buffer for now */
|
||||
sg = &cmd->sgv[0];
|
||||
n_elem = blk_rq_map_sg(q, rq, sg);
|
||||
n_elem = blk_rq_map_sg(lun->disk->queue, rq, &cmd->sgv[0]);
|
||||
if (n_elem <= 0) {
|
||||
ub_put_cmd(lun, cmd);
|
||||
ub_end_rq(rq, 0);
|
||||
blk_start_queue(q);
|
||||
return 0; /* request with no s/g entries? */
|
||||
printk(KERN_INFO "%s: failed request map (%d)\n",
|
||||
sc->name, n_elem); /* P3 */
|
||||
return -1; /* request with no s/g entries? */
|
||||
}
|
||||
|
||||
if (n_elem != 1) { /* Paranoia */
|
||||
if (n_elem > UB_MAX_REQ_SG) { /* Paranoia */
|
||||
printk(KERN_WARNING "%s: request with %d segments\n",
|
||||
sc->name, n_elem);
|
||||
ub_put_cmd(lun, cmd);
|
||||
ub_end_rq(rq, 0);
|
||||
blk_start_queue(q);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* XXX Unfortunately, this check does not work. It is quite possible
|
||||
* to get bogus non-null rq->buffer if you allow sg by mistake.
|
||||
*/
|
||||
if (rq->buffer == NULL) {
|
||||
/*
|
||||
* This must not happen if we set the queue right.
|
||||
* The block level must create bounce buffers for us.
|
||||
*/
|
||||
static int do_print = 1;
|
||||
if (do_print) {
|
||||
printk(KERN_WARNING "%s: unmapped block request"
|
||||
" flags 0x%lx sectors %lu\n",
|
||||
sc->name, rq->flags, rq->nr_sectors);
|
||||
do_print = 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
cmd->nsg = n_elem;
|
||||
sc->sg_stat[n_elem]++;
|
||||
|
||||
/*
|
||||
* build the command
|
||||
|
@ -876,30 +856,15 @@ static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
|
|||
cmd->cdb[8] = nblks;
|
||||
cmd->cdb_len = 10;
|
||||
|
||||
cmd->dir = ub_dir;
|
||||
cmd->data = rq->buffer;
|
||||
cmd->len = rq->nr_sectors * 512;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
|
||||
struct request *rq)
|
||||
static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
|
||||
struct ub_scsi_cmd *cmd, struct request *rq)
|
||||
{
|
||||
|
||||
if (rq->data_len != 0 && rq->data == NULL) {
|
||||
static int do_print = 1;
|
||||
if (do_print) {
|
||||
printk(KERN_WARNING "%s: unmapped packet request"
|
||||
" flags 0x%lx length %d\n",
|
||||
sc->name, rq->flags, rq->data_len);
|
||||
do_print = 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(&cmd->cdb, rq->cmd, rq->cmd_len);
|
||||
cmd->cdb_len = rq->cmd_len;
|
||||
int n_elem;
|
||||
|
||||
if (rq->data_len == 0) {
|
||||
cmd->dir = UB_DIR_NONE;
|
||||
|
@ -908,8 +873,29 @@ static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
|
|||
cmd->dir = UB_DIR_WRITE;
|
||||
else
|
||||
cmd->dir = UB_DIR_READ;
|
||||
|
||||
}
|
||||
cmd->data = rq->data;
|
||||
|
||||
/*
|
||||
* get scatterlist from block layer
|
||||
*/
|
||||
n_elem = blk_rq_map_sg(lun->disk->queue, rq, &cmd->sgv[0]);
|
||||
if (n_elem < 0) {
|
||||
printk(KERN_INFO "%s: failed request map (%d)\n",
|
||||
sc->name, n_elem); /* P3 */
|
||||
return -1;
|
||||
}
|
||||
if (n_elem > UB_MAX_REQ_SG) { /* Paranoia */
|
||||
printk(KERN_WARNING "%s: request with %d segments\n",
|
||||
sc->name, n_elem);
|
||||
return -1;
|
||||
}
|
||||
cmd->nsg = n_elem;
|
||||
sc->sg_stat[n_elem]++;
|
||||
|
||||
memcpy(&cmd->cdb, rq->cmd, rq->cmd_len);
|
||||
cmd->cdb_len = rq->cmd_len;
|
||||
|
||||
cmd->len = rq->data_len;
|
||||
|
||||
return 0;
|
||||
|
@ -919,24 +905,34 @@ static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|||
{
|
||||
struct request *rq = cmd->back;
|
||||
struct ub_lun *lun = cmd->lun;
|
||||
struct gendisk *disk = lun->disk;
|
||||
request_queue_t *q = disk->queue;
|
||||
int uptodate;
|
||||
|
||||
if (blk_pc_request(rq)) {
|
||||
/* UB_SENSE_SIZE is smaller than SCSI_SENSE_BUFFERSIZE */
|
||||
memcpy(rq->sense, sc->top_sense, UB_SENSE_SIZE);
|
||||
rq->sense_len = UB_SENSE_SIZE;
|
||||
}
|
||||
|
||||
if (cmd->error == 0)
|
||||
if (cmd->error == 0) {
|
||||
uptodate = 1;
|
||||
else
|
||||
|
||||
if (blk_pc_request(rq)) {
|
||||
if (cmd->act_len >= rq->data_len)
|
||||
rq->data_len = 0;
|
||||
else
|
||||
rq->data_len -= cmd->act_len;
|
||||
}
|
||||
} else {
|
||||
uptodate = 0;
|
||||
|
||||
if (blk_pc_request(rq)) {
|
||||
/* UB_SENSE_SIZE is smaller than SCSI_SENSE_BUFFERSIZE */
|
||||
memcpy(rq->sense, sc->top_sense, UB_SENSE_SIZE);
|
||||
rq->sense_len = UB_SENSE_SIZE;
|
||||
if (sc->top_sense[0] != 0)
|
||||
rq->errors = SAM_STAT_CHECK_CONDITION;
|
||||
else
|
||||
rq->errors = DID_ERROR << 16;
|
||||
}
|
||||
}
|
||||
|
||||
ub_put_cmd(lun, cmd);
|
||||
ub_end_rq(rq, uptodate);
|
||||
blk_start_queue(q);
|
||||
blk_start_queue(lun->disk->queue);
|
||||
}
|
||||
|
||||
static void ub_end_rq(struct request *rq, int uptodate)
|
||||
|
@ -1014,7 +1010,7 @@ static int ub_scsi_cmd_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|||
sc->last_pipe = sc->send_bulk_pipe;
|
||||
usb_fill_bulk_urb(&sc->work_urb, sc->dev, sc->send_bulk_pipe,
|
||||
bcb, US_BULK_CB_WRAP_LEN, ub_urb_complete, sc);
|
||||
sc->work_urb.transfer_flags = URB_ASYNC_UNLINK;
|
||||
sc->work_urb.transfer_flags = 0;
|
||||
|
||||
/* Fill what we shouldn't be filling, because usb-storage did so. */
|
||||
sc->work_urb.actual_length = 0;
|
||||
|
@ -1103,7 +1099,6 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|||
{
|
||||
struct urb *urb = &sc->work_urb;
|
||||
struct bulk_cs_wrap *bcs;
|
||||
int pipe;
|
||||
int rc;
|
||||
|
||||
if (atomic_read(&sc->poison)) {
|
||||
|
@ -1204,38 +1199,13 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|||
goto Bad_End;
|
||||
}
|
||||
|
||||
if (cmd->dir == UB_DIR_NONE) {
|
||||
if (cmd->dir == UB_DIR_NONE || cmd->nsg < 1) {
|
||||
ub_state_stat(sc, cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
UB_INIT_COMPLETION(sc->work_done);
|
||||
|
||||
if (cmd->dir == UB_DIR_READ)
|
||||
pipe = sc->recv_bulk_pipe;
|
||||
else
|
||||
pipe = sc->send_bulk_pipe;
|
||||
sc->last_pipe = pipe;
|
||||
usb_fill_bulk_urb(&sc->work_urb, sc->dev, pipe,
|
||||
cmd->data, cmd->len, ub_urb_complete, sc);
|
||||
sc->work_urb.transfer_flags = URB_ASYNC_UNLINK;
|
||||
sc->work_urb.actual_length = 0;
|
||||
sc->work_urb.error_count = 0;
|
||||
sc->work_urb.status = 0;
|
||||
|
||||
if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
|
||||
/* XXX Clear stalls */
|
||||
printk("ub: data #%d submit failed (%d)\n", cmd->tag, rc); /* P3 */
|
||||
ub_complete(&sc->work_done);
|
||||
ub_state_done(sc, cmd, rc);
|
||||
return;
|
||||
}
|
||||
|
||||
sc->work_timer.expires = jiffies + UB_DATA_TIMEOUT;
|
||||
add_timer(&sc->work_timer);
|
||||
|
||||
cmd->state = UB_CMDST_DATA;
|
||||
ub_cmdtr_state(sc, cmd);
|
||||
// udelay(125); // usb-storage has this
|
||||
ub_data_start(sc, cmd);
|
||||
|
||||
} else if (cmd->state == UB_CMDST_DATA) {
|
||||
if (urb->status == -EPIPE) {
|
||||
|
@ -1257,16 +1227,22 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|||
if (urb->status == -EOVERFLOW) {
|
||||
/*
|
||||
* A babble? Failure, but we must transfer CSW now.
|
||||
* XXX This is going to end in perpetual babble. Reset.
|
||||
*/
|
||||
cmd->error = -EOVERFLOW; /* A cheap trick... */
|
||||
} else {
|
||||
if (urb->status != 0)
|
||||
goto Bad_End;
|
||||
ub_state_stat(sc, cmd);
|
||||
return;
|
||||
}
|
||||
if (urb->status != 0)
|
||||
goto Bad_End;
|
||||
|
||||
cmd->act_len = urb->actual_length;
|
||||
cmd->act_len += urb->actual_length;
|
||||
ub_cmdtr_act_len(sc, cmd);
|
||||
|
||||
if (++cmd->current_sg < cmd->nsg) {
|
||||
ub_data_start(sc, cmd);
|
||||
return;
|
||||
}
|
||||
ub_state_stat(sc, cmd);
|
||||
|
||||
} else if (cmd->state == UB_CMDST_STAT) {
|
||||
|
@ -1399,6 +1375,46 @@ Bad_End: /* Little Excel is dead */
|
|||
ub_state_done(sc, cmd, -EIO);
|
||||
}
|
||||
|
||||
/*
|
||||
* Factorization helper for the command state machine:
|
||||
* Initiate a data segment transfer.
|
||||
*/
|
||||
static void ub_data_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
||||
{
|
||||
struct scatterlist *sg = &cmd->sgv[cmd->current_sg];
|
||||
int pipe;
|
||||
int rc;
|
||||
|
||||
UB_INIT_COMPLETION(sc->work_done);
|
||||
|
||||
if (cmd->dir == UB_DIR_READ)
|
||||
pipe = sc->recv_bulk_pipe;
|
||||
else
|
||||
pipe = sc->send_bulk_pipe;
|
||||
sc->last_pipe = pipe;
|
||||
usb_fill_bulk_urb(&sc->work_urb, sc->dev, pipe,
|
||||
page_address(sg->page) + sg->offset, sg->length,
|
||||
ub_urb_complete, sc);
|
||||
sc->work_urb.transfer_flags = 0;
|
||||
sc->work_urb.actual_length = 0;
|
||||
sc->work_urb.error_count = 0;
|
||||
sc->work_urb.status = 0;
|
||||
|
||||
if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
|
||||
/* XXX Clear stalls */
|
||||
printk("ub: data #%d submit failed (%d)\n", cmd->tag, rc); /* P3 */
|
||||
ub_complete(&sc->work_done);
|
||||
ub_state_done(sc, cmd, rc);
|
||||
return;
|
||||
}
|
||||
|
||||
sc->work_timer.expires = jiffies + UB_DATA_TIMEOUT;
|
||||
add_timer(&sc->work_timer);
|
||||
|
||||
cmd->state = UB_CMDST_DATA;
|
||||
ub_cmdtr_state(sc, cmd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Factorization helper for the command state machine:
|
||||
* Finish the command.
|
||||
|
@ -1426,7 +1442,7 @@ static int __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|||
sc->last_pipe = sc->recv_bulk_pipe;
|
||||
usb_fill_bulk_urb(&sc->work_urb, sc->dev, sc->recv_bulk_pipe,
|
||||
&sc->work_bcs, US_BULK_CS_WRAP_LEN, ub_urb_complete, sc);
|
||||
sc->work_urb.transfer_flags = URB_ASYNC_UNLINK;
|
||||
sc->work_urb.transfer_flags = 0;
|
||||
sc->work_urb.actual_length = 0;
|
||||
sc->work_urb.error_count = 0;
|
||||
sc->work_urb.status = 0;
|
||||
|
@ -1484,6 +1500,7 @@ static void ub_state_stat_counted(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|||
static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
||||
{
|
||||
struct ub_scsi_cmd *scmd;
|
||||
struct scatterlist *sg;
|
||||
int rc;
|
||||
|
||||
if (cmd->cdb[0] == REQUEST_SENSE) {
|
||||
|
@ -1492,12 +1509,17 @@ static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|||
}
|
||||
|
||||
scmd = &sc->top_rqs_cmd;
|
||||
memset(scmd, 0, sizeof(struct ub_scsi_cmd));
|
||||
scmd->cdb[0] = REQUEST_SENSE;
|
||||
scmd->cdb[4] = UB_SENSE_SIZE;
|
||||
scmd->cdb_len = 6;
|
||||
scmd->dir = UB_DIR_READ;
|
||||
scmd->state = UB_CMDST_INIT;
|
||||
scmd->data = sc->top_sense;
|
||||
scmd->nsg = 1;
|
||||
sg = &scmd->sgv[0];
|
||||
sg->page = virt_to_page(sc->top_sense);
|
||||
sg->offset = (unsigned int)sc->top_sense & (PAGE_SIZE-1);
|
||||
sg->length = UB_SENSE_SIZE;
|
||||
scmd->len = UB_SENSE_SIZE;
|
||||
scmd->lun = cmd->lun;
|
||||
scmd->done = ub_top_sense_done;
|
||||
|
@ -1541,7 +1563,7 @@ static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
|
|||
|
||||
usb_fill_control_urb(&sc->work_urb, sc->dev, sc->send_ctrl_pipe,
|
||||
(unsigned char*) cr, NULL, 0, ub_urb_complete, sc);
|
||||
sc->work_urb.transfer_flags = URB_ASYNC_UNLINK;
|
||||
sc->work_urb.transfer_flags = 0;
|
||||
sc->work_urb.actual_length = 0;
|
||||
sc->work_urb.error_count = 0;
|
||||
sc->work_urb.status = 0;
|
||||
|
@ -1560,7 +1582,7 @@ static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
|
|||
*/
|
||||
static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd)
|
||||
{
|
||||
unsigned char *sense = scmd->data;
|
||||
unsigned char *sense = sc->top_sense;
|
||||
struct ub_scsi_cmd *cmd;
|
||||
|
||||
/*
|
||||
|
@ -1852,6 +1874,7 @@ static int ub_sync_read_cap(struct ub_dev *sc, struct ub_lun *lun,
|
|||
struct ub_capacity *ret)
|
||||
{
|
||||
struct ub_scsi_cmd *cmd;
|
||||
struct scatterlist *sg;
|
||||
char *p;
|
||||
enum { ALLOC_SIZE = sizeof(struct ub_scsi_cmd) + 8 };
|
||||
unsigned long flags;
|
||||
|
@ -1872,7 +1895,11 @@ static int ub_sync_read_cap(struct ub_dev *sc, struct ub_lun *lun,
|
|||
cmd->cdb_len = 10;
|
||||
cmd->dir = UB_DIR_READ;
|
||||
cmd->state = UB_CMDST_INIT;
|
||||
cmd->data = p;
|
||||
cmd->nsg = 1;
|
||||
sg = &cmd->sgv[0];
|
||||
sg->page = virt_to_page(p);
|
||||
sg->offset = (unsigned int)p & (PAGE_SIZE-1);
|
||||
sg->length = 8;
|
||||
cmd->len = 8;
|
||||
cmd->lun = lun;
|
||||
cmd->done = ub_probe_done;
|
||||
|
@ -2289,7 +2316,7 @@ static int ub_probe_lun(struct ub_dev *sc, int lnum)
|
|||
disk->driverfs_dev = &sc->intf->dev; /* XXX Many to one ok? */
|
||||
|
||||
rc = -ENOMEM;
|
||||
if ((q = blk_init_queue(ub_bd_rq_fn, &sc->lock)) == NULL)
|
||||
if ((q = blk_init_queue(ub_request_fn, &sc->lock)) == NULL)
|
||||
goto err_blkqinit;
|
||||
|
||||
disk->queue = q;
|
||||
|
|
|
@ -267,7 +267,7 @@ static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self)
|
|||
frame, IRDA_USB_SPEED_MTU,
|
||||
speed_bulk_callback, self);
|
||||
urb->transfer_buffer_length = USB_IRDA_HEADER;
|
||||
urb->transfer_flags = URB_ASYNC_UNLINK;
|
||||
urb->transfer_flags = 0;
|
||||
|
||||
/* Irq disabled -> GFP_ATOMIC */
|
||||
if ((ret = usb_submit_urb(urb, GFP_ATOMIC))) {
|
||||
|
@ -401,15 +401,12 @@ static int irda_usb_hard_xmit(struct sk_buff *skb, struct net_device *netdev)
|
|||
skb->data, IRDA_SKB_MAX_MTU,
|
||||
write_bulk_callback, skb);
|
||||
urb->transfer_buffer_length = skb->len;
|
||||
/* Note : unlink *must* be Asynchronous because of the code in
|
||||
* irda_usb_net_timeout() -> call in irq - Jean II */
|
||||
urb->transfer_flags = URB_ASYNC_UNLINK;
|
||||
/* This flag (URB_ZERO_PACKET) indicates that what we send is not
|
||||
* a continuous stream of data but separate packets.
|
||||
* In this case, the USB layer will insert an empty USB frame (TD)
|
||||
* after each of our packets that is exact multiple of the frame size.
|
||||
* This is how the dongle will detect the end of packet - Jean II */
|
||||
urb->transfer_flags |= URB_ZERO_PACKET;
|
||||
urb->transfer_flags = URB_ZERO_PACKET;
|
||||
|
||||
/* Generate min turn time. FIXME: can we do better than this? */
|
||||
/* Trying to a turnaround time at this level is trying to measure
|
||||
|
@ -630,8 +627,6 @@ static void irda_usb_net_timeout(struct net_device *netdev)
|
|||
* in completion handler, because urb->status will
|
||||
* be -ENOENT. We will fix that at the next watchdog,
|
||||
* leaving more time to USB to recover...
|
||||
* Also, we are in interrupt, so we need to have
|
||||
* URB_ASYNC_UNLINK to work properly...
|
||||
* Jean II */
|
||||
done = 1;
|
||||
break;
|
||||
|
@ -1008,9 +1003,7 @@ static int irda_usb_net_close(struct net_device *netdev)
|
|||
}
|
||||
}
|
||||
/* Cancel Tx and speed URB - need to be synchronous to avoid races */
|
||||
self->tx_urb->transfer_flags &= ~URB_ASYNC_UNLINK;
|
||||
usb_kill_urb(self->tx_urb);
|
||||
self->speed_urb->transfer_flags &= ~URB_ASYNC_UNLINK;
|
||||
usb_kill_urb(self->speed_urb);
|
||||
|
||||
/* Stop and remove instance of IrLAP */
|
||||
|
@ -1521,9 +1514,7 @@ static void irda_usb_disconnect(struct usb_interface *intf)
|
|||
usb_kill_urb(self->rx_urb[i]);
|
||||
/* Cancel Tx and speed URB.
|
||||
* Toggle flags to make sure it's synchronous. */
|
||||
self->tx_urb->transfer_flags &= ~URB_ASYNC_UNLINK;
|
||||
usb_kill_urb(self->tx_urb);
|
||||
self->speed_urb->transfer_flags &= ~URB_ASYNC_UNLINK;
|
||||
usb_kill_urb(self->speed_urb);
|
||||
}
|
||||
|
||||
|
|
|
@ -715,13 +715,11 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
|
|||
usb_dev, usb_rcvintpipe(usb_dev, CXACRU_EP_CMD),
|
||||
instance->rcv_buf, PAGE_SIZE,
|
||||
cxacru_blocking_completion, &instance->rcv_done, 1);
|
||||
instance->rcv_urb->transfer_flags |= URB_ASYNC_UNLINK;
|
||||
|
||||
usb_fill_int_urb(instance->snd_urb,
|
||||
usb_dev, usb_sndintpipe(usb_dev, CXACRU_EP_CMD),
|
||||
instance->snd_buf, PAGE_SIZE,
|
||||
cxacru_blocking_completion, &instance->snd_done, 4);
|
||||
instance->snd_urb->transfer_flags |= URB_ASYNC_UNLINK;
|
||||
|
||||
init_MUTEX(&instance->cm_serialize);
|
||||
|
||||
|
|
|
@ -4,9 +4,22 @@
|
|||
comment "USB Device Class drivers"
|
||||
depends on USB
|
||||
|
||||
config OBSOLETE_OSS_USB_DRIVER
|
||||
bool "Obsolete OSS USB drivers"
|
||||
depends on USB && SOUND
|
||||
help
|
||||
This option enables support for the obsolete USB Audio and Midi
|
||||
drivers that are scheduled for removal in the near future since
|
||||
there are ALSA drivers for the same hardware.
|
||||
|
||||
Please contact Adrian Bunk <bunk@stusta.de> if you had to
|
||||
say Y here because of missing support in the ALSA drivers.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config USB_AUDIO
|
||||
tristate "USB Audio support"
|
||||
depends on USB && SOUND
|
||||
depends on USB && SOUND && OBSOLETE_OSS_USB_DRIVER
|
||||
help
|
||||
Say Y here if you want to connect USB audio equipment such as
|
||||
speakers to your computer's USB port. You only need this if you use
|
||||
|
@ -40,10 +53,12 @@ config USB_BLUETOOTH_TTY
|
|||
|
||||
config USB_MIDI
|
||||
tristate "USB MIDI support"
|
||||
depends on USB && SOUND
|
||||
depends on USB && SOUND && OBSOLETE_OSS_USB_DRIVER
|
||||
---help---
|
||||
Say Y here if you want to connect a USB MIDI device to your
|
||||
computer's USB port. This driver is for devices that comply with
|
||||
computer's USB port. You only need this if you use the OSS
|
||||
sound system; USB MIDI devices are supported by ALSA's USB
|
||||
audio driver. This driver is for devices that comply with
|
||||
'Universal Serial Bus Device Class Definition for MIDI Device'.
|
||||
|
||||
The following devices are known to work:
|
||||
|
|
|
@ -310,8 +310,9 @@ static int usblp_check_status(struct usblp *usblp, int err)
|
|||
|
||||
error = usblp_read_status (usblp, usblp->statusbuf);
|
||||
if (error < 0) {
|
||||
err("usblp%d: error %d reading printer status",
|
||||
usblp->minor, error);
|
||||
if (printk_ratelimit())
|
||||
err("usblp%d: error %d reading printer status",
|
||||
usblp->minor, error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -604,7 +605,9 @@ static int usblp_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
|
|||
|
||||
case LPGETSTATUS:
|
||||
if (usblp_read_status(usblp, usblp->statusbuf)) {
|
||||
err("usblp%d: failed reading printer status", usblp->minor);
|
||||
if (printk_ratelimit())
|
||||
err("usblp%d: failed reading printer status",
|
||||
usblp->minor);
|
||||
retval = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
#
|
||||
|
||||
usbcore-objs := usb.o hub.o hcd.o urb.o message.o \
|
||||
config.o file.o buffer.o sysfs.o
|
||||
config.o file.o buffer.o sysfs.o devio.o
|
||||
|
||||
ifeq ($(CONFIG_PCI),y)
|
||||
usbcore-objs += hcd-pci.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_USB_DEVICEFS),y)
|
||||
usbcore-objs += devio.o inode.o devices.o
|
||||
usbcore-objs += inode.o devices.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_USB) += usbcore.o
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
@ -50,6 +51,10 @@
|
|||
#include "hcd.h" /* for usbcore internals */
|
||||
#include "usb.h"
|
||||
|
||||
#define USB_MAXBUS 64
|
||||
#define USB_DEVICE_MAX USB_MAXBUS * 128
|
||||
static struct class *usb_device_class;
|
||||
|
||||
struct async {
|
||||
struct list_head asynclist;
|
||||
struct dev_state *ps;
|
||||
|
@ -71,6 +76,8 @@ MODULE_PARM_DESC (usbfs_snoop, "true to log all usbfs traffic");
|
|||
dev_info( dev , format , ## arg); \
|
||||
} while (0)
|
||||
|
||||
#define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0)
|
||||
|
||||
|
||||
#define MAX_USBFS_BUFFER_SIZE 16384
|
||||
|
||||
|
@ -487,7 +494,7 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsig
|
|||
*/
|
||||
static int usbdev_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct usb_device *dev;
|
||||
struct usb_device *dev = NULL;
|
||||
struct dev_state *ps;
|
||||
int ret;
|
||||
|
||||
|
@ -501,11 +508,16 @@ static int usbdev_open(struct inode *inode, struct file *file)
|
|||
|
||||
lock_kernel();
|
||||
ret = -ENOENT;
|
||||
dev = usb_get_dev(inode->u.generic_ip);
|
||||
/* check if we are called from a real node or usbfs */
|
||||
if (imajor(inode) == USB_DEVICE_MAJOR)
|
||||
dev = usbdev_lookup_minor(iminor(inode));
|
||||
if (!dev)
|
||||
dev = inode->u.generic_ip;
|
||||
if (!dev) {
|
||||
kfree(ps);
|
||||
goto out;
|
||||
}
|
||||
usb_get_dev(dev);
|
||||
ret = 0;
|
||||
ps->dev = dev;
|
||||
ps->file = file;
|
||||
|
@ -1226,7 +1238,6 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg)
|
|||
int retval = 0;
|
||||
struct usb_interface *intf = NULL;
|
||||
struct usb_driver *driver = NULL;
|
||||
int i;
|
||||
|
||||
/* get input parameters and alloc buffer */
|
||||
if (copy_from_user(&ctrl, arg, sizeof (ctrl)))
|
||||
|
@ -1258,15 +1269,6 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg)
|
|||
/* disconnect kernel driver from interface */
|
||||
case USBDEVFS_DISCONNECT:
|
||||
|
||||
/* don't allow the user to unbind the hub driver from
|
||||
* a hub with children to manage */
|
||||
for (i = 0; i < ps->dev->maxchild; ++i) {
|
||||
if (ps->dev->children[i])
|
||||
retval = -EBUSY;
|
||||
}
|
||||
if (retval)
|
||||
break;
|
||||
|
||||
down_write(&usb_bus_type.subsys.rwsem);
|
||||
if (intf->dev.driver) {
|
||||
driver = to_usb_driver(intf->dev.driver);
|
||||
|
@ -1477,3 +1479,79 @@ struct file_operations usbfs_device_file_operations = {
|
|||
.open = usbdev_open,
|
||||
.release = usbdev_release,
|
||||
};
|
||||
|
||||
struct usb_device *usbdev_lookup_minor(int minor)
|
||||
{
|
||||
struct class_device *class_dev;
|
||||
struct usb_device *dev = NULL;
|
||||
|
||||
down(&usb_device_class->sem);
|
||||
list_for_each_entry(class_dev, &usb_device_class->children, node) {
|
||||
if (class_dev->devt == MKDEV(USB_DEVICE_MAJOR, minor)) {
|
||||
dev = class_dev->class_data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
up(&usb_device_class->sem);
|
||||
|
||||
return dev;
|
||||
};
|
||||
|
||||
void usbdev_add(struct usb_device *dev)
|
||||
{
|
||||
int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1);
|
||||
|
||||
dev->class_dev = class_device_create(usb_device_class,
|
||||
MKDEV(USB_DEVICE_MAJOR, minor), &dev->dev,
|
||||
"usbdev%d.%d", dev->bus->busnum, dev->devnum);
|
||||
|
||||
dev->class_dev->class_data = dev;
|
||||
}
|
||||
|
||||
void usbdev_remove(struct usb_device *dev)
|
||||
{
|
||||
class_device_unregister(dev->class_dev);
|
||||
}
|
||||
|
||||
static struct cdev usb_device_cdev = {
|
||||
.kobj = {.name = "usb_device", },
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
int __init usbdev_init(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX,
|
||||
"usb_device");
|
||||
if (retval) {
|
||||
err("unable to register minors for usb_device");
|
||||
goto out;
|
||||
}
|
||||
cdev_init(&usb_device_cdev, &usbfs_device_file_operations);
|
||||
retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);
|
||||
if (retval) {
|
||||
err("unable to get usb_device major %d", USB_DEVICE_MAJOR);
|
||||
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
|
||||
goto out;
|
||||
}
|
||||
usb_device_class = class_create(THIS_MODULE, "usb_device");
|
||||
if (IS_ERR(usb_device_class)) {
|
||||
err("unable to register usb_device class");
|
||||
retval = PTR_ERR(usb_device_class);
|
||||
usb_device_class = NULL;
|
||||
cdev_del(&usb_device_cdev);
|
||||
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
|
||||
}
|
||||
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
void usbdev_cleanup(void)
|
||||
{
|
||||
class_destroy(usb_device_class);
|
||||
cdev_del(&usb_device_cdev);
|
||||
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
|
||||
}
|
||||
|
||||
|
|
|
@ -339,11 +339,11 @@ extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb);
|
|||
* to preallocate bandwidth)
|
||||
*/
|
||||
#define USB2_HOST_DELAY 5 /* nsec, guess */
|
||||
#define HS_NSECS(bytes) ( ((55 * 8 * 2083)/1000) \
|
||||
+ ((2083UL * (3167 + BitTime (bytes)))/1000) \
|
||||
#define HS_NSECS(bytes) ( ((55 * 8 * 2083) \
|
||||
+ (2083UL * (3 + BitTime(bytes))))/1000 \
|
||||
+ USB2_HOST_DELAY)
|
||||
#define HS_NSECS_ISO(bytes) ( ((38 * 8 * 2083)/1000) \
|
||||
+ ((2083UL * (3167 + BitTime (bytes)))/1000) \
|
||||
#define HS_NSECS_ISO(bytes) ( ((38 * 8 * 2083) \
|
||||
+ (2083UL * (3 + BitTime(bytes))))/1000 \
|
||||
+ USB2_HOST_DELAY)
|
||||
#define HS_USECS(bytes) NS_TO_US (HS_NSECS(bytes))
|
||||
#define HS_USECS_ISO(bytes) NS_TO_US (HS_NSECS_ISO(bytes))
|
||||
|
|
|
@ -492,6 +492,23 @@ static int hub_hub_status(struct usb_hub *hub,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
|
||||
{
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
int ret;
|
||||
|
||||
if (hdev->children[port1-1] && set_state) {
|
||||
usb_set_device_state(hdev->children[port1-1],
|
||||
USB_STATE_NOTATTACHED);
|
||||
}
|
||||
ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
|
||||
if (ret)
|
||||
dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
|
||||
port1, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hub_configure(struct usb_hub *hub,
|
||||
struct usb_endpoint_descriptor *endpoint)
|
||||
{
|
||||
|
@ -610,19 +627,33 @@ static int hub_configure(struct usb_hub *hub,
|
|||
break;
|
||||
}
|
||||
|
||||
/* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */
|
||||
switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_TTTT) {
|
||||
case 0x00:
|
||||
if (hdev->descriptor.bDeviceProtocol != 0)
|
||||
dev_dbg(hub_dev, "TT requires at most 8 FS bit times\n");
|
||||
case HUB_TTTT_8_BITS:
|
||||
if (hdev->descriptor.bDeviceProtocol != 0) {
|
||||
hub->tt.think_time = 666;
|
||||
dev_dbg(hub_dev, "TT requires at most %d "
|
||||
"FS bit times (%d ns)\n",
|
||||
8, hub->tt.think_time);
|
||||
}
|
||||
break;
|
||||
case 0x20:
|
||||
dev_dbg(hub_dev, "TT requires at most 16 FS bit times\n");
|
||||
case HUB_TTTT_16_BITS:
|
||||
hub->tt.think_time = 666 * 2;
|
||||
dev_dbg(hub_dev, "TT requires at most %d "
|
||||
"FS bit times (%d ns)\n",
|
||||
16, hub->tt.think_time);
|
||||
break;
|
||||
case 0x40:
|
||||
dev_dbg(hub_dev, "TT requires at most 24 FS bit times\n");
|
||||
case HUB_TTTT_24_BITS:
|
||||
hub->tt.think_time = 666 * 3;
|
||||
dev_dbg(hub_dev, "TT requires at most %d "
|
||||
"FS bit times (%d ns)\n",
|
||||
24, hub->tt.think_time);
|
||||
break;
|
||||
case 0x60:
|
||||
dev_dbg(hub_dev, "TT requires at most 32 FS bit times\n");
|
||||
case HUB_TTTT_32_BITS:
|
||||
hub->tt.think_time = 666 * 4;
|
||||
dev_dbg(hub_dev, "TT requires at most %d "
|
||||
"FS bit times (%d ns)\n",
|
||||
32, hub->tt.think_time);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -712,20 +743,36 @@ fail:
|
|||
|
||||
static unsigned highspeed_hubs;
|
||||
|
||||
/* Called after the hub driver is unbound from a hub with children */
|
||||
static void hub_remove_children_work(void *__hub)
|
||||
{
|
||||
struct usb_hub *hub = __hub;
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
int i;
|
||||
|
||||
kfree(hub);
|
||||
|
||||
usb_lock_device(hdev);
|
||||
for (i = 0; i < hdev->maxchild; ++i) {
|
||||
if (hdev->children[i])
|
||||
usb_disconnect(&hdev->children[i]);
|
||||
}
|
||||
usb_unlock_device(hdev);
|
||||
usb_put_dev(hdev);
|
||||
}
|
||||
|
||||
static void hub_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_hub *hub = usb_get_intfdata (intf);
|
||||
struct usb_device *hdev;
|
||||
int n, port1;
|
||||
|
||||
if (!hub)
|
||||
return;
|
||||
usb_set_intfdata (intf, NULL);
|
||||
hdev = hub->hdev;
|
||||
|
||||
if (hdev->speed == USB_SPEED_HIGH)
|
||||
highspeed_hubs--;
|
||||
|
||||
usb_set_intfdata (intf, NULL);
|
||||
|
||||
hub_quiesce(hub);
|
||||
usb_free_urb(hub->urb);
|
||||
hub->urb = NULL;
|
||||
|
@ -746,8 +793,27 @@ static void hub_disconnect(struct usb_interface *intf)
|
|||
hub->buffer = NULL;
|
||||
}
|
||||
|
||||
/* Free the memory */
|
||||
kfree(hub);
|
||||
/* If there are any children then this is an unbind only, not a
|
||||
* physical disconnection. The active ports must be disabled
|
||||
* and later on we must call usb_disconnect(). We can't call
|
||||
* it now because we may not hold the hub's device lock.
|
||||
*/
|
||||
n = 0;
|
||||
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
|
||||
if (hdev->children[port1 - 1]) {
|
||||
++n;
|
||||
hub_port_disable(hub, port1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 0)
|
||||
kfree(hub);
|
||||
else {
|
||||
/* Reuse the hub->leds work_struct for our own purposes */
|
||||
INIT_WORK(&hub->leds, hub_remove_children_work, hub);
|
||||
schedule_work(&hub->leds);
|
||||
usb_get_dev(hdev);
|
||||
}
|
||||
}
|
||||
|
||||
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
|
@ -1051,6 +1117,7 @@ void usb_disconnect(struct usb_device **pdev)
|
|||
dev_dbg (&udev->dev, "unregistering device\n");
|
||||
release_address(udev);
|
||||
usbfs_remove_device(udev);
|
||||
usbdev_remove(udev);
|
||||
usb_remove_sysfs_dev_files(udev);
|
||||
|
||||
/* Avoid races with recursively_mark_NOTATTACHED() */
|
||||
|
@ -1290,6 +1357,7 @@ int usb_new_device(struct usb_device *udev)
|
|||
/* USB device state == configured ... usable */
|
||||
|
||||
/* add a /proc/bus/usb entry */
|
||||
usbdev_add(udev);
|
||||
usbfs_add_device(udev);
|
||||
return 0;
|
||||
|
||||
|
@ -1428,23 +1496,6 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
|
|||
return status;
|
||||
}
|
||||
|
||||
static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
|
||||
{
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
int ret;
|
||||
|
||||
if (hdev->children[port1-1] && set_state) {
|
||||
usb_set_device_state(hdev->children[port1-1],
|
||||
USB_STATE_NOTATTACHED);
|
||||
}
|
||||
ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
|
||||
if (ret)
|
||||
dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
|
||||
port1, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable a port and mark a logical connnect-change event, so that some
|
||||
* time later khubd will disconnect() any existing usb_device on the port
|
||||
|
|
|
@ -157,6 +157,12 @@ enum hub_led_mode {
|
|||
|
||||
struct usb_device;
|
||||
|
||||
/* Transaction Translator Think Times, in bits */
|
||||
#define HUB_TTTT_8_BITS 0x00
|
||||
#define HUB_TTTT_16_BITS 0x20
|
||||
#define HUB_TTTT_24_BITS 0x40
|
||||
#define HUB_TTTT_32_BITS 0x60
|
||||
|
||||
/*
|
||||
* As of USB 2.0, full/low speed devices are segregated into trees.
|
||||
* One type grows from USB 1.1 host controllers (OHCI, UHCI etc).
|
||||
|
@ -170,6 +176,7 @@ struct usb_device;
|
|||
struct usb_tt {
|
||||
struct usb_device *hub; /* upstream highspeed hub */
|
||||
int multi; /* true means one TT per port */
|
||||
unsigned think_time; /* think time in ns */
|
||||
|
||||
/* for control/bulk error recovery (CLEAR_TT_BUFFER) */
|
||||
spinlock_t lock;
|
||||
|
|
|
@ -728,16 +728,10 @@ int __init usbfs_init(void)
|
|||
{
|
||||
int retval;
|
||||
|
||||
retval = usb_register(&usbfs_driver);
|
||||
retval = register_filesystem(&usb_fs_type);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = register_filesystem(&usb_fs_type);
|
||||
if (retval) {
|
||||
usb_deregister(&usbfs_driver);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* create mount point for usbfs */
|
||||
usbdir = proc_mkdir("usb", proc_bus);
|
||||
|
||||
|
@ -746,7 +740,6 @@ int __init usbfs_init(void)
|
|||
|
||||
void usbfs_cleanup(void)
|
||||
{
|
||||
usb_deregister(&usbfs_driver);
|
||||
unregister_filesystem(&usb_fs_type);
|
||||
if (usbdir)
|
||||
remove_proc_entry("usb", proc_bus);
|
||||
|
|
|
@ -48,7 +48,6 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
|
|||
|
||||
init_completion(&done);
|
||||
urb->context = &done;
|
||||
urb->transfer_flags |= URB_ASYNC_UNLINK;
|
||||
urb->actual_length = 0;
|
||||
status = usb_submit_urb(urb, GFP_NOIO);
|
||||
|
||||
|
@ -266,7 +265,9 @@ static void sg_complete (struct urb *urb, struct pt_regs *regs)
|
|||
continue;
|
||||
if (found) {
|
||||
status = usb_unlink_urb (io->urbs [i]);
|
||||
if (status != -EINPROGRESS && status != -EBUSY)
|
||||
if (status != -EINPROGRESS
|
||||
&& status != -ENODEV
|
||||
&& status != -EBUSY)
|
||||
dev_err (&io->dev->dev,
|
||||
"%s, unlink --> %d\n",
|
||||
__FUNCTION__, status);
|
||||
|
@ -357,8 +358,7 @@ int usb_sg_init (
|
|||
if (!io->urbs)
|
||||
goto nomem;
|
||||
|
||||
urb_flags = URB_ASYNC_UNLINK | URB_NO_TRANSFER_DMA_MAP
|
||||
| URB_NO_INTERRUPT;
|
||||
urb_flags = URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT;
|
||||
if (usb_pipein (pipe))
|
||||
urb_flags |= URB_SHORT_NOT_OK;
|
||||
|
||||
|
|
|
@ -309,9 +309,8 @@ int usb_submit_urb(struct urb *urb, unsigned mem_flags)
|
|||
unsigned int allowed;
|
||||
|
||||
/* enforce simple/standard policy */
|
||||
allowed = URB_ASYNC_UNLINK; // affects later unlinks
|
||||
allowed |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
|
||||
allowed |= URB_NO_INTERRUPT;
|
||||
allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP |
|
||||
URB_NO_INTERRUPT);
|
||||
switch (temp) {
|
||||
case PIPE_BULK:
|
||||
if (is_out)
|
||||
|
@ -400,14 +399,8 @@ int usb_submit_urb(struct urb *urb, unsigned mem_flags)
|
|||
* canceled (rather than any other code) and will quickly be removed
|
||||
* from host controller data structures.
|
||||
*
|
||||
* In the past, clearing the URB_ASYNC_UNLINK transfer flag for the
|
||||
* URB indicated that the request was synchronous. This usage is now
|
||||
* deprecated; if the flag is clear the call will be forwarded to
|
||||
* usb_kill_urb() and the return value will be 0. In the future, drivers
|
||||
* should call usb_kill_urb() directly for synchronous unlinking.
|
||||
*
|
||||
* When the URB_ASYNC_UNLINK transfer flag for the URB is set, this
|
||||
* request is asynchronous. Success is indicated by returning -EINPROGRESS,
|
||||
* This request is always asynchronous.
|
||||
* Success is indicated by returning -EINPROGRESS,
|
||||
* at which time the URB will normally have been unlinked but not yet
|
||||
* given back to the device driver. When it is called, the completion
|
||||
* function will see urb->status == -ECONNRESET. Failure is indicated
|
||||
|
@ -453,17 +446,6 @@ int usb_unlink_urb(struct urb *urb)
|
|||
{
|
||||
if (!urb)
|
||||
return -EINVAL;
|
||||
if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) {
|
||||
#ifdef CONFIG_DEBUG_KERNEL
|
||||
if (printk_ratelimit()) {
|
||||
printk(KERN_NOTICE "usb_unlink_urb() is deprecated for "
|
||||
"synchronous unlinks. Use usb_kill_urb() instead.\n");
|
||||
WARN_ON(1);
|
||||
}
|
||||
#endif
|
||||
usb_kill_urb(urb);
|
||||
return 0;
|
||||
}
|
||||
if (!(urb->dev && urb->dev->bus && urb->dev->bus->op))
|
||||
return -ENODEV;
|
||||
return urb->dev->bus->op->unlink_urb(urb, -ECONNRESET);
|
||||
|
|
|
@ -65,6 +65,16 @@ static int generic_probe (struct device *dev)
|
|||
}
|
||||
static int generic_remove (struct device *dev)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
|
||||
/* if this is only an unbind, not a physical disconnect, then
|
||||
* unconfigure the device */
|
||||
if (udev->state == USB_STATE_CONFIGURED)
|
||||
usb_set_configuration(udev, 0);
|
||||
|
||||
/* in case the call failed or the device was suspended */
|
||||
if (udev->state >= USB_STATE_CONFIGURED)
|
||||
usb_disable_device(udev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -912,7 +922,7 @@ int usb_trylock_device(struct usb_device *udev)
|
|||
* is neither BINDING nor BOUND. Rather than sleeping to wait for the
|
||||
* lock, the routine polls repeatedly. This is to prevent deadlock with
|
||||
* disconnect; in some drivers (such as usb-storage) the disconnect()
|
||||
* callback will block waiting for a device reset to complete.
|
||||
* or suspend() method will block waiting for a device reset to complete.
|
||||
*
|
||||
* Returns a negative error code for failure, otherwise 1 or 0 to indicate
|
||||
* that the device will or will not have to be unlocked. (0 can be
|
||||
|
@ -922,6 +932,8 @@ int usb_trylock_device(struct usb_device *udev)
|
|||
int usb_lock_device_for_reset(struct usb_device *udev,
|
||||
struct usb_interface *iface)
|
||||
{
|
||||
unsigned long jiffies_expire = jiffies + HZ;
|
||||
|
||||
if (udev->state == USB_STATE_NOTATTACHED)
|
||||
return -ENODEV;
|
||||
if (udev->state == USB_STATE_SUSPENDED)
|
||||
|
@ -938,6 +950,12 @@ int usb_lock_device_for_reset(struct usb_device *udev,
|
|||
}
|
||||
|
||||
while (!usb_trylock_device(udev)) {
|
||||
|
||||
/* If we can't acquire the lock after waiting one second,
|
||||
* we're probably deadlocked */
|
||||
if (time_after(jiffies, jiffies_expire))
|
||||
return -EBUSY;
|
||||
|
||||
msleep(15);
|
||||
if (udev->state == USB_STATE_NOTATTACHED)
|
||||
return -ENODEV;
|
||||
|
@ -1478,13 +1496,18 @@ static int __init usb_init(void)
|
|||
retval = usb_major_init();
|
||||
if (retval)
|
||||
goto major_init_failed;
|
||||
retval = usb_register(&usbfs_driver);
|
||||
if (retval)
|
||||
goto driver_register_failed;
|
||||
retval = usbdev_init();
|
||||
if (retval)
|
||||
goto usbdevice_init_failed;
|
||||
retval = usbfs_init();
|
||||
if (retval)
|
||||
goto fs_init_failed;
|
||||
retval = usb_hub_init();
|
||||
if (retval)
|
||||
goto hub_init_failed;
|
||||
|
||||
retval = driver_register(&usb_generic_driver);
|
||||
if (!retval)
|
||||
goto out;
|
||||
|
@ -1493,7 +1516,11 @@ static int __init usb_init(void)
|
|||
hub_init_failed:
|
||||
usbfs_cleanup();
|
||||
fs_init_failed:
|
||||
usb_major_cleanup();
|
||||
usbdev_cleanup();
|
||||
usbdevice_init_failed:
|
||||
usb_deregister(&usbfs_driver);
|
||||
driver_register_failed:
|
||||
usb_major_cleanup();
|
||||
major_init_failed:
|
||||
usb_host_cleanup();
|
||||
host_init_failed:
|
||||
|
@ -1514,6 +1541,8 @@ static void __exit usb_exit(void)
|
|||
driver_unregister(&usb_generic_driver);
|
||||
usb_major_cleanup();
|
||||
usbfs_cleanup();
|
||||
usb_deregister(&usbfs_driver);
|
||||
usbdev_cleanup();
|
||||
usb_hub_cleanup();
|
||||
usb_host_cleanup();
|
||||
bus_unregister(&usb_bus_type);
|
||||
|
|
|
@ -37,6 +37,11 @@ extern struct file_operations usbfs_devices_fops;
|
|||
extern struct file_operations usbfs_device_file_operations;
|
||||
extern void usbfs_conn_disc_event(void);
|
||||
|
||||
extern int usbdev_init(void);
|
||||
extern void usbdev_cleanup(void);
|
||||
extern void usbdev_add(struct usb_device *dev);
|
||||
extern void usbdev_remove(struct usb_device *dev);
|
||||
extern struct usb_device *usbdev_lookup_minor(int minor);
|
||||
|
||||
struct dev_state {
|
||||
struct list_head list; /* state list */
|
||||
|
|
|
@ -2181,6 +2181,7 @@ eth_bind (struct usb_gadget *gadget)
|
|||
u8 cdc = 1, zlp = 1, rndis = 1;
|
||||
struct usb_ep *in_ep, *out_ep, *status_ep = NULL;
|
||||
int status = -ENOMEM;
|
||||
int gcnum;
|
||||
|
||||
/* these flags are only ever cleared; compiler take note */
|
||||
#ifndef DEV_CONFIG_CDC
|
||||
|
@ -2194,44 +2195,26 @@ eth_bind (struct usb_gadget *gadget)
|
|||
* standard protocol is _strongly_ preferred for interop purposes.
|
||||
* (By everyone except Microsoft.)
|
||||
*/
|
||||
if (gadget_is_net2280 (gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0201);
|
||||
} else if (gadget_is_dummy (gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0202);
|
||||
} else if (gadget_is_pxa (gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0203);
|
||||
if (gadget_is_pxa (gadget)) {
|
||||
/* pxa doesn't support altsettings */
|
||||
cdc = 0;
|
||||
} else if (gadget_is_sh(gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0204);
|
||||
/* sh doesn't support multiple interfaces or configs */
|
||||
cdc = 0;
|
||||
rndis = 0;
|
||||
} else if (gadget_is_sa1100 (gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0205);
|
||||
/* hardware can't write zlps */
|
||||
zlp = 0;
|
||||
/* sa1100 CAN do CDC, without status endpoint ... we use
|
||||
* non-CDC to be compatible with ARM Linux-2.4 "usb-eth".
|
||||
*/
|
||||
cdc = 0;
|
||||
} else if (gadget_is_goku (gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0206);
|
||||
} else if (gadget_is_mq11xx (gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0207);
|
||||
} else if (gadget_is_omap (gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208);
|
||||
} else if (gadget_is_lh7a40x(gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209);
|
||||
} else if (gadget_is_n9604(gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0210);
|
||||
} else if (gadget_is_pxa27x(gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0211);
|
||||
} else if (gadget_is_s3c2410(gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0212);
|
||||
} else if (gadget_is_at91(gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0213);
|
||||
} else {
|
||||
}
|
||||
|
||||
gcnum = usb_gadget_controller_number (gadget);
|
||||
if (gcnum >= 0)
|
||||
device_desc.bcdDevice = cpu_to_le16 (0x0200 + gcnum);
|
||||
else {
|
||||
/* can't assume CDC works. don't want to default to
|
||||
* anything less functional on CDC-capable hardware,
|
||||
* so we fail in this case.
|
||||
|
|
|
@ -3713,6 +3713,7 @@ static void fsg_unbind(struct usb_gadget *gadget)
|
|||
static int __init check_parameters(struct fsg_dev *fsg)
|
||||
{
|
||||
int prot;
|
||||
int gcnum;
|
||||
|
||||
/* Store the default values */
|
||||
mod_data.transport_type = USB_PR_BULK;
|
||||
|
@ -3724,33 +3725,13 @@ static int __init check_parameters(struct fsg_dev *fsg)
|
|||
mod_data.can_stall = 0;
|
||||
|
||||
if (mod_data.release == 0xffff) { // Parameter wasn't set
|
||||
if (gadget_is_net2280(fsg->gadget))
|
||||
mod_data.release = 0x0301;
|
||||
else if (gadget_is_dummy(fsg->gadget))
|
||||
mod_data.release = 0x0302;
|
||||
else if (gadget_is_pxa(fsg->gadget))
|
||||
mod_data.release = 0x0303;
|
||||
else if (gadget_is_sh(fsg->gadget))
|
||||
mod_data.release = 0x0304;
|
||||
|
||||
/* The sa1100 controller is not supported */
|
||||
|
||||
else if (gadget_is_goku(fsg->gadget))
|
||||
mod_data.release = 0x0306;
|
||||
else if (gadget_is_mq11xx(fsg->gadget))
|
||||
mod_data.release = 0x0307;
|
||||
else if (gadget_is_omap(fsg->gadget))
|
||||
mod_data.release = 0x0308;
|
||||
else if (gadget_is_lh7a40x(fsg->gadget))
|
||||
mod_data.release = 0x0309;
|
||||
else if (gadget_is_n9604(fsg->gadget))
|
||||
mod_data.release = 0x0310;
|
||||
else if (gadget_is_pxa27x(fsg->gadget))
|
||||
mod_data.release = 0x0311;
|
||||
else if (gadget_is_s3c2410(gadget))
|
||||
mod_data.release = 0x0312;
|
||||
else if (gadget_is_at91(fsg->gadget))
|
||||
mod_data.release = 0x0313;
|
||||
if (gadget_is_sa1100(fsg->gadget))
|
||||
gcnum = -1;
|
||||
else
|
||||
gcnum = usb_gadget_controller_number(fsg->gadget);
|
||||
if (gcnum >= 0)
|
||||
mod_data.release = 0x0300 + gcnum;
|
||||
else {
|
||||
WARN(fsg, "controller '%s' not recognized\n",
|
||||
fsg->gadget->name);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*
|
||||
* This could eventually work like the ARM mach_is_*() stuff, driven by
|
||||
* some config file that gets updated as new hardware is supported.
|
||||
* (And avoiding the runtime comparisons in typical one-choice cases.)
|
||||
*
|
||||
* NOTE: some of these controller drivers may not be available yet.
|
||||
*/
|
||||
|
@ -86,7 +87,61 @@
|
|||
#define gadget_is_at91(g) 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_IMX
|
||||
#define gadget_is_imx(g) !strcmp("imx_udc", (g)->name)
|
||||
#else
|
||||
#define gadget_is_imx(g) 0
|
||||
#endif
|
||||
|
||||
// CONFIG_USB_GADGET_SX2
|
||||
// CONFIG_USB_GADGET_AU1X00
|
||||
// ...
|
||||
|
||||
|
||||
/**
|
||||
* usb_gadget_controller_number - support bcdDevice id convention
|
||||
* @gadget: the controller being driven
|
||||
*
|
||||
* Return a 2-digit BCD value associated with the peripheral controller,
|
||||
* suitable for use as part of a bcdDevice value, or a negative error code.
|
||||
*
|
||||
* NOTE: this convention is purely optional, and has no meaning in terms of
|
||||
* any USB specification. If you want to use a different convention in your
|
||||
* gadget driver firmware -- maybe a more formal revision ID -- feel free.
|
||||
*
|
||||
* Hosts see these bcdDevice numbers, and are allowed (but not encouraged!)
|
||||
* to change their behavior accordingly. For example it might help avoiding
|
||||
* some chip bug.
|
||||
*/
|
||||
static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
|
||||
{
|
||||
if (gadget_is_net2280(gadget))
|
||||
return 0x01;
|
||||
else if (gadget_is_dummy(gadget))
|
||||
return 0x02;
|
||||
else if (gadget_is_pxa(gadget))
|
||||
return 0x03;
|
||||
else if (gadget_is_sh(gadget))
|
||||
return 0x04;
|
||||
else if (gadget_is_sa1100(gadget))
|
||||
return 0x05;
|
||||
else if (gadget_is_goku(gadget))
|
||||
return 0x06;
|
||||
else if (gadget_is_mq11xx(gadget))
|
||||
return 0x07;
|
||||
else if (gadget_is_omap(gadget))
|
||||
return 0x08;
|
||||
else if (gadget_is_lh7a40x(gadget))
|
||||
return 0x09;
|
||||
else if (gadget_is_n9604(gadget))
|
||||
return 0x10;
|
||||
else if (gadget_is_pxa27x(gadget))
|
||||
return 0x11;
|
||||
else if (gadget_is_s3c2410(gadget))
|
||||
return 0x12;
|
||||
else if (gadget_is_at91(gadget))
|
||||
return 0x13;
|
||||
else if (gadget_is_imx(gadget))
|
||||
return 0x14;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
|
|
@ -1422,49 +1422,20 @@ static int gs_bind(struct usb_gadget *gadget)
|
|||
int ret;
|
||||
struct usb_ep *ep;
|
||||
struct gs_dev *dev;
|
||||
int gcnum;
|
||||
|
||||
/* device specific */
|
||||
if (gadget_is_net2280(gadget)) {
|
||||
gs_device_desc.bcdDevice =
|
||||
__constant_cpu_to_le16(GS_VERSION_NUM|0x0001);
|
||||
} else if (gadget_is_pxa(gadget)) {
|
||||
gs_device_desc.bcdDevice =
|
||||
__constant_cpu_to_le16(GS_VERSION_NUM|0x0002);
|
||||
} else if (gadget_is_sh(gadget)) {
|
||||
gs_device_desc.bcdDevice =
|
||||
__constant_cpu_to_le16(GS_VERSION_NUM|0x0003);
|
||||
/* sh doesn't support multiple interfaces or configs */
|
||||
/* Some controllers can't support CDC ACM:
|
||||
* - sh doesn't support multiple interfaces or configs;
|
||||
* - sa1100 doesn't have a third interrupt endpoint
|
||||
*/
|
||||
if (gadget_is_sh(gadget) || gadget_is_sa1100(gadget))
|
||||
use_acm = 0;
|
||||
} else if (gadget_is_sa1100(gadget)) {
|
||||
|
||||
gcnum = usb_gadget_controller_number(gadget);
|
||||
if (gcnum >= 0)
|
||||
gs_device_desc.bcdDevice =
|
||||
__constant_cpu_to_le16(GS_VERSION_NUM|0x0004);
|
||||
/* sa1100 doesn't support necessary endpoints */
|
||||
use_acm = 0;
|
||||
} else if (gadget_is_goku(gadget)) {
|
||||
gs_device_desc.bcdDevice =
|
||||
__constant_cpu_to_le16(GS_VERSION_NUM|0x0005);
|
||||
} else if (gadget_is_mq11xx(gadget)) {
|
||||
gs_device_desc.bcdDevice =
|
||||
__constant_cpu_to_le16(GS_VERSION_NUM|0x0006);
|
||||
} else if (gadget_is_omap(gadget)) {
|
||||
gs_device_desc.bcdDevice =
|
||||
__constant_cpu_to_le16(GS_VERSION_NUM|0x0007);
|
||||
} else if (gadget_is_lh7a40x(gadget)) {
|
||||
gs_device_desc.bcdDevice =
|
||||
__constant_cpu_to_le16(GS_VERSION_NUM|0x0008);
|
||||
} else if (gadget_is_n9604(gadget)) {
|
||||
gs_device_desc.bcdDevice =
|
||||
__constant_cpu_to_le16(GS_VERSION_NUM|0x0009);
|
||||
} else if (gadget_is_pxa27x(gadget)) {
|
||||
gs_device_desc.bcdDevice =
|
||||
__constant_cpu_to_le16(GS_VERSION_NUM|0x0011);
|
||||
} else if (gadget_is_s3c2410(gadget)) {
|
||||
gs_device_desc.bcdDevice =
|
||||
__constant_cpu_to_le16(GS_VERSION_NUM|0x0012);
|
||||
} else if (gadget_is_at91(gadget)) {
|
||||
gs_device_desc.bcdDevice =
|
||||
__constant_cpu_to_le16(GS_VERSION_NUM|0x0013);
|
||||
} else {
|
||||
cpu_to_le16(GS_VERSION_NUM | gcnum);
|
||||
else {
|
||||
printk(KERN_WARNING "gs_bind: controller '%s' not recognized\n",
|
||||
gadget->name);
|
||||
/* unrecognized, but safe unless bulk is REALLY quirky */
|
||||
|
|
|
@ -1139,6 +1139,13 @@ zero_bind (struct usb_gadget *gadget)
|
|||
{
|
||||
struct zero_dev *dev;
|
||||
struct usb_ep *ep;
|
||||
int gcnum;
|
||||
|
||||
/* FIXME this can't yet work right with SH ... it has only
|
||||
* one configuration, numbered one.
|
||||
*/
|
||||
if (gadget_is_sh(gadget))
|
||||
return -ENODEV;
|
||||
|
||||
/* Bulk-only drivers like this one SHOULD be able to
|
||||
* autoconfigure on any sane usb controller driver,
|
||||
|
@ -1161,43 +1168,10 @@ autoconf_fail:
|
|||
EP_OUT_NAME = ep->name;
|
||||
ep->driver_data = ep; /* claim */
|
||||
|
||||
|
||||
/*
|
||||
* DRIVER POLICY CHOICE: you may want to do this differently.
|
||||
* One thing to avoid is reusing a bcdDevice revision code
|
||||
* with different host-visible configurations or behavior
|
||||
* restrictions -- using ep1in/ep2out vs ep1out/ep3in, etc
|
||||
*/
|
||||
if (gadget_is_net2280 (gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0201);
|
||||
} else if (gadget_is_pxa (gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0203);
|
||||
#if 0
|
||||
} else if (gadget_is_sh(gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0204);
|
||||
/* SH has only one configuration; see "loopdefault" */
|
||||
device_desc.bNumConfigurations = 1;
|
||||
/* FIXME make 1 == default.bConfigurationValue */
|
||||
#endif
|
||||
} else if (gadget_is_sa1100 (gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0205);
|
||||
} else if (gadget_is_goku (gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0206);
|
||||
} else if (gadget_is_mq11xx (gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0207);
|
||||
} else if (gadget_is_omap (gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208);
|
||||
} else if (gadget_is_lh7a40x(gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209);
|
||||
} else if (gadget_is_n9604(gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0210);
|
||||
} else if (gadget_is_pxa27x(gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0211);
|
||||
} else if (gadget_is_s3c2410(gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0212);
|
||||
} else if (gadget_is_at91(gadget)) {
|
||||
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0213);
|
||||
} else {
|
||||
gcnum = usb_gadget_controller_number (gadget);
|
||||
if (gcnum >= 0)
|
||||
device_desc.bcdDevice = cpu_to_le16 (0x0200 + gcnum);
|
||||
else {
|
||||
/* gadget zero is so simple (for now, no altsettings) that
|
||||
* it SHOULD NOT have problems with bulk-capable hardware.
|
||||
* so warn about unrcognized controllers, don't panic.
|
||||
|
|
|
@ -677,6 +677,9 @@ qh_make (
|
|||
goto done;
|
||||
}
|
||||
} else {
|
||||
struct usb_tt *tt = urb->dev->tt;
|
||||
int think_time;
|
||||
|
||||
/* gap is f(FS/LS transfer times) */
|
||||
qh->gap_uf = 1 + usb_calc_bus_time (urb->dev->speed,
|
||||
is_input, 0, maxp) / (125 * 1000);
|
||||
|
@ -690,6 +693,10 @@ qh_make (
|
|||
qh->c_usecs = HS_USECS (0);
|
||||
}
|
||||
|
||||
think_time = tt ? tt->think_time : 0;
|
||||
qh->tt_usecs = NS_TO_US (think_time +
|
||||
usb_calc_bus_time (urb->dev->speed,
|
||||
is_input, 0, max_packet (maxp)));
|
||||
qh->period = urb->interval;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -700,6 +700,7 @@ iso_stream_init (
|
|||
|
||||
} else {
|
||||
u32 addr;
|
||||
int think_time;
|
||||
|
||||
addr = dev->ttport << 24;
|
||||
if (!ehci_is_TDI(ehci)
|
||||
|
@ -709,6 +710,9 @@ iso_stream_init (
|
|||
addr |= epnum << 8;
|
||||
addr |= dev->devnum;
|
||||
stream->usecs = HS_USECS_ISO (maxp);
|
||||
think_time = dev->tt ? dev->tt->think_time : 0;
|
||||
stream->tt_usecs = NS_TO_US (think_time + usb_calc_bus_time (
|
||||
dev->speed, is_input, 1, maxp));
|
||||
if (is_input) {
|
||||
u32 tmp;
|
||||
|
||||
|
|
|
@ -421,6 +421,7 @@ struct ehci_qh {
|
|||
u8 usecs; /* intr bandwidth */
|
||||
u8 gap_uf; /* uframes split/csplit gap */
|
||||
u8 c_usecs; /* ... split completion bw */
|
||||
u16 tt_usecs; /* tt downstream bandwidth */
|
||||
unsigned short period; /* polling interval */
|
||||
unsigned short start; /* where polling starts */
|
||||
#define NO_FRAME ((unsigned short)~0) /* pick new start */
|
||||
|
@ -479,6 +480,7 @@ struct ehci_iso_stream {
|
|||
*/
|
||||
u8 interval;
|
||||
u8 usecs, c_usecs;
|
||||
u16 tt_usecs;
|
||||
u16 maxp;
|
||||
u16 raw_mask;
|
||||
unsigned bandwidth;
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
#include "../core/hcd.h"
|
||||
#include "isp116x.h"
|
||||
|
||||
#define DRIVER_VERSION "08 Apr 2005"
|
||||
#define DRIVER_VERSION "05 Aug 2005"
|
||||
#define DRIVER_DESC "ISP116x USB Host Controller Driver"
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
|
@ -629,14 +629,12 @@ static irqreturn_t isp116x_irq(struct usb_hcd *hcd, struct pt_regs *regs)
|
|||
ERR("Unrecoverable error\n");
|
||||
/* What should we do here? Reset? */
|
||||
}
|
||||
if (intstat & HCINT_RHSC) {
|
||||
isp116x->rhstatus =
|
||||
isp116x_read_reg32(isp116x, HCRHSTATUS);
|
||||
isp116x->rhport[0] =
|
||||
isp116x_read_reg32(isp116x, HCRHPORT1);
|
||||
isp116x->rhport[1] =
|
||||
isp116x_read_reg32(isp116x, HCRHPORT2);
|
||||
}
|
||||
if (intstat & HCINT_RHSC)
|
||||
/* When root hub or any of its ports is going
|
||||
to come out of suspend, it may take more
|
||||
than 10ms for status bits to stabilize. */
|
||||
mod_timer(&hcd->rh_timer, jiffies
|
||||
+ msecs_to_jiffies(20) + 1);
|
||||
if (intstat & HCINT_RD) {
|
||||
DBG("---- remote wakeup\n");
|
||||
schedule_work(&isp116x->rh_resume);
|
||||
|
@ -925,20 +923,27 @@ static int isp116x_hub_status_data(struct usb_hcd *hcd, char *buf)
|
|||
{
|
||||
struct isp116x *isp116x = hcd_to_isp116x(hcd);
|
||||
int ports, i, changed = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if (!HC_IS_RUNNING(hcd->state))
|
||||
return -ESHUTDOWN;
|
||||
|
||||
ports = isp116x->rhdesca & RH_A_NDP;
|
||||
/* Report no status change now, if we are scheduled to be
|
||||
called later */
|
||||
if (timer_pending(&hcd->rh_timer))
|
||||
return 0;
|
||||
|
||||
/* init status */
|
||||
ports = isp116x->rhdesca & RH_A_NDP;
|
||||
spin_lock_irqsave(&isp116x->lock, flags);
|
||||
isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS);
|
||||
if (isp116x->rhstatus & (RH_HS_LPSC | RH_HS_OCIC))
|
||||
buf[0] = changed = 1;
|
||||
else
|
||||
buf[0] = 0;
|
||||
|
||||
for (i = 0; i < ports; i++) {
|
||||
u32 status = isp116x->rhport[i];
|
||||
u32 status = isp116x->rhport[i] =
|
||||
isp116x_read_reg32(isp116x, i ? HCRHPORT2 : HCRHPORT1);
|
||||
|
||||
if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
|
||||
| RH_PS_OCIC | RH_PS_PRSC)) {
|
||||
|
@ -947,6 +952,7 @@ static int isp116x_hub_status_data(struct usb_hcd *hcd, char *buf)
|
|||
continue;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&isp116x->lock, flags);
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
@ -1463,10 +1469,6 @@ static int isp116x_sw_reset(struct isp116x *isp116x)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
Reset. Tries to perform platform-specific hardware
|
||||
reset first; falls back to software reset.
|
||||
*/
|
||||
static int isp116x_reset(struct usb_hcd *hcd)
|
||||
{
|
||||
struct isp116x *isp116x = hcd_to_isp116x(hcd);
|
||||
|
@ -1474,17 +1476,7 @@ static int isp116x_reset(struct usb_hcd *hcd)
|
|||
u16 clkrdy = 0;
|
||||
int ret = 0, timeout = 15 /* ms */ ;
|
||||
|
||||
if (isp116x->board && isp116x->board->reset) {
|
||||
/* Hardware reset */
|
||||
isp116x->board->reset(hcd->self.controller, 1);
|
||||
msleep(10);
|
||||
if (isp116x->board->clock)
|
||||
isp116x->board->clock(hcd->self.controller, 1);
|
||||
msleep(1);
|
||||
isp116x->board->reset(hcd->self.controller, 0);
|
||||
} else
|
||||
ret = isp116x_sw_reset(isp116x);
|
||||
|
||||
ret = isp116x_sw_reset(isp116x);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -1501,10 +1493,7 @@ static int isp116x_reset(struct usb_hcd *hcd)
|
|||
ERR("Clock not ready after 20ms\n");
|
||||
/* After sw_reset the clock won't report to be ready, if
|
||||
H_WAKEUP pin is high. */
|
||||
if (!isp116x->board || !isp116x->board->reset)
|
||||
ERR("The driver does not support hardware wakeup.\n");
|
||||
ERR("Please make sure that the H_WAKEUP pin "
|
||||
"is pulled low!\n");
|
||||
ERR("Please make sure that the H_WAKEUP pin is pulled low!\n");
|
||||
ret = -ENODEV;
|
||||
}
|
||||
return ret;
|
||||
|
@ -1527,15 +1516,7 @@ static void isp116x_stop(struct usb_hcd *hcd)
|
|||
isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPS);
|
||||
spin_unlock_irqrestore(&isp116x->lock, flags);
|
||||
|
||||
/* Put the chip into reset state */
|
||||
if (isp116x->board && isp116x->board->reset)
|
||||
isp116x->board->reset(hcd->self.controller, 0);
|
||||
else
|
||||
isp116x_sw_reset(isp116x);
|
||||
|
||||
/* Stop the clock */
|
||||
if (isp116x->board && isp116x->board->clock)
|
||||
isp116x->board->clock(hcd->self.controller, 0);
|
||||
isp116x_sw_reset(isp116x);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1561,6 +1542,9 @@ static int isp116x_start(struct usb_hcd *hcd)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* To be removed in future */
|
||||
hcd->uses_new_polling = 1;
|
||||
|
||||
isp116x_write_reg16(isp116x, HCITLBUFLEN, ISP116x_ITL_BUFSIZE);
|
||||
isp116x_write_reg16(isp116x, HCATLBUFLEN, ISP116x_ATL_BUFSIZE);
|
||||
|
||||
|
@ -1569,7 +1553,7 @@ static int isp116x_start(struct usb_hcd *hcd)
|
|||
if (board->sel15Kres)
|
||||
val |= HCHWCFG_15KRSEL;
|
||||
/* Remote wakeup won't work without working clock */
|
||||
if (board->clknotstop || board->remote_wakeup_enable)
|
||||
if (board->remote_wakeup_enable)
|
||||
val |= HCHWCFG_CLKNOTSTOP;
|
||||
if (board->oc_enable)
|
||||
val |= HCHWCFG_ANALOG_OC;
|
||||
|
@ -1580,16 +1564,13 @@ static int isp116x_start(struct usb_hcd *hcd)
|
|||
isp116x_write_reg16(isp116x, HCHWCFG, val);
|
||||
|
||||
/* ----- Root hub conf */
|
||||
val = 0;
|
||||
/* AN10003_1.pdf recommends NPS to be always 1 */
|
||||
if (board->no_power_switching)
|
||||
val |= RH_A_NPS;
|
||||
if (board->power_switching_mode)
|
||||
val |= RH_A_PSM;
|
||||
if (board->potpg)
|
||||
val |= (board->potpg << 24) & RH_A_POTPGT;
|
||||
else
|
||||
val |= (25 << 24) & RH_A_POTPGT;
|
||||
val = (25 << 24) & RH_A_POTPGT;
|
||||
/* AN10003_1.pdf recommends RH_A_NPS (no power switching) to
|
||||
be always set. Yet, instead, we request individual port
|
||||
power switching. */
|
||||
val |= RH_A_PSM;
|
||||
/* Report overcurrent per port */
|
||||
val |= RH_A_OCPM;
|
||||
isp116x_write_reg32(isp116x, HCRHDESCA, val);
|
||||
isp116x->rhdesca = isp116x_read_reg32(isp116x, HCRHDESCA);
|
||||
|
||||
|
@ -1619,9 +1600,6 @@ static int isp116x_start(struct usb_hcd *hcd)
|
|||
|
||||
/* Go operational */
|
||||
val = HCCONTROL_USB_OPER;
|
||||
/* Remote wakeup connected - NOT SUPPORTED */
|
||||
/* if (board->remote_wakeup_connected)
|
||||
val |= HCCONTROL_RWC; */
|
||||
if (board->remote_wakeup_enable)
|
||||
val |= HCCONTROL_RWE;
|
||||
isp116x_write_reg32(isp116x, HCCONTROL, val);
|
||||
|
@ -1670,7 +1648,7 @@ static int __init_or_module isp116x_remove(struct device *dev)
|
|||
struct platform_device *pdev;
|
||||
struct resource *res;
|
||||
|
||||
if(!hcd)
|
||||
if (!hcd)
|
||||
return 0;
|
||||
isp116x = hcd_to_isp116x(hcd);
|
||||
pdev = container_of(dev, struct platform_device, dev);
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
* This file is licenced under the GPL.
|
||||
*/
|
||||
|
||||
#include <asm/usb.h>
|
||||
|
||||
/* configure so an HC device and id are always provided */
|
||||
/* always called with process context; sleeping is OK */
|
||||
|
||||
|
@ -23,9 +21,7 @@
|
|||
* usb_hcd_ppc_soc_probe - initialize On-Chip HCDs
|
||||
* Context: !in_interrupt()
|
||||
*
|
||||
* Allocates basic resources for this USB host controller, and
|
||||
* then invokes the start() method for the HCD associated with it
|
||||
* through the hotplug entry's driver_data.
|
||||
* Allocates basic resources for this USB host controller.
|
||||
*
|
||||
* Store this function in the HCD's struct pci_driver as probe().
|
||||
*/
|
||||
|
@ -37,7 +33,6 @@ static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver,
|
|||
struct ohci_hcd *ohci;
|
||||
struct resource *res;
|
||||
int irq;
|
||||
struct usb_hcd_platform_data *pd = pdev->dev.platform_data;
|
||||
|
||||
pr_debug("initializing PPC-SOC USB Controller\n");
|
||||
|
||||
|
@ -73,9 +68,6 @@ static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver,
|
|||
goto err2;
|
||||
}
|
||||
|
||||
if (pd->start && (retval = pd->start(pdev)))
|
||||
goto err3;
|
||||
|
||||
ohci = hcd_to_ohci(hcd);
|
||||
ohci->flags |= OHCI_BIG_ENDIAN;
|
||||
ohci_hcd_init(ohci);
|
||||
|
@ -85,9 +77,7 @@ static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver,
|
|||
return retval;
|
||||
|
||||
pr_debug("Removing PPC-SOC USB Controller\n");
|
||||
if (pd && pd->stop)
|
||||
pd->stop(pdev);
|
||||
err3:
|
||||
|
||||
iounmap(hcd->regs);
|
||||
err2:
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
|
@ -105,25 +95,21 @@ static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver,
|
|||
* @pdev: USB Host Controller being removed
|
||||
* Context: !in_interrupt()
|
||||
*
|
||||
* Reverses the effect of usb_hcd_ppc_soc_probe(), first invoking
|
||||
* the HCD's stop() method. It is always called from a thread
|
||||
* Reverses the effect of usb_hcd_ppc_soc_probe().
|
||||
* It is always called from a thread
|
||||
* context, normally "rmmod", "apmd", or something similar.
|
||||
*
|
||||
*/
|
||||
static void usb_hcd_ppc_soc_remove(struct usb_hcd *hcd,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd_platform_data *pd = pdev->dev.platform_data;
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
|
||||
pr_debug("stopping PPC-SOC USB Controller\n");
|
||||
if (pd && pd->stop)
|
||||
pd->stop(pdev);
|
||||
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_hcd_put(hcd);
|
||||
usb_put_hcd(hcd);
|
||||
}
|
||||
|
||||
static int __devinit
|
||||
|
|
|
@ -129,7 +129,7 @@ static void s3c2410_usb_set_power(struct s3c2410_hcd_info *info,
|
|||
|
||||
if (info->power_control != NULL) {
|
||||
info->port[port-1].power = to;
|
||||
(info->power_control)(port, to);
|
||||
(info->power_control)(port-1, to);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -339,8 +339,8 @@ int usb_hcd_s3c2410_probe (const struct hc_driver *driver,
|
|||
struct usb_hcd *hcd = NULL;
|
||||
int retval;
|
||||
|
||||
s3c2410_usb_set_power(dev->dev.platform_data, 0, 1);
|
||||
s3c2410_usb_set_power(dev->dev.platform_data, 1, 1);
|
||||
s3c2410_usb_set_power(dev->dev.platform_data, 2, 1);
|
||||
|
||||
hcd = usb_create_hcd(driver, &dev->dev, "s3c24xx");
|
||||
if (hcd == NULL)
|
||||
|
|
|
@ -230,6 +230,20 @@ config USB_EGALAX
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called touchkitusb.
|
||||
|
||||
config USB_YEALINK
|
||||
tristate "Yealink usb-p1k voip phone"
|
||||
depends on USB && INPUT && EXPERIMENTAL
|
||||
---help---
|
||||
Say Y here if you want to enable keyboard and LCD functions of the
|
||||
Yealink usb-p1k usb phones. The audio part is enabled by the generic
|
||||
usb sound driver, so you might want to enable that as well.
|
||||
|
||||
For information about how to use these additional functions, see
|
||||
<file:Documentation/input/yealink.txt>.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called yealink.
|
||||
|
||||
config USB_XPAD
|
||||
tristate "X-Box gamepad support"
|
||||
depends on USB && INPUT
|
||||
|
|
|
@ -39,4 +39,5 @@ obj-$(CONFIG_USB_EGALAX) += touchkitusb.o
|
|||
obj-$(CONFIG_USB_POWERMATE) += powermate.o
|
||||
obj-$(CONFIG_USB_WACOM) += wacom.o
|
||||
obj-$(CONFIG_USB_ACECAD) += acecad.o
|
||||
obj-$(CONFIG_USB_YEALINK) += yealink.o
|
||||
obj-$(CONFIG_USB_XPAD) += xpad.o
|
||||
|
|
|
@ -1444,6 +1444,8 @@ void hid_init_reports(struct hid_device *hid)
|
|||
#define USB_DEVICE_ID_NETWORKANALYSER 0x2020
|
||||
#define USB_DEVICE_ID_POWERCONTROL 0x2030
|
||||
|
||||
#define USB_VENDOR_ID_APPLE 0x05ac
|
||||
#define USB_DEVICE_ID_APPLE_BLUETOOTH 0x1000
|
||||
|
||||
/*
|
||||
* Alphabetically sorted blacklist by quirk type.
|
||||
|
@ -1462,6 +1464,7 @@ static struct hid_blacklist {
|
|||
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_BLUETOOTH, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24, HID_QUIRK_IGNORE },
|
||||
|
@ -1685,7 +1688,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
|
|||
usb_fill_int_urb(hid->urbin, dev, pipe, hid->inbuf, 0,
|
||||
hid_irq_in, hid, interval);
|
||||
hid->urbin->transfer_dma = hid->inbuf_dma;
|
||||
hid->urbin->transfer_flags |=(URB_NO_TRANSFER_DMA_MAP | URB_ASYNC_UNLINK);
|
||||
hid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
} else {
|
||||
if (hid->urbout)
|
||||
continue;
|
||||
|
@ -1695,7 +1698,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
|
|||
usb_fill_int_urb(hid->urbout, dev, pipe, hid->outbuf, 0,
|
||||
hid_irq_out, hid, interval);
|
||||
hid->urbout->transfer_dma = hid->outbuf_dma;
|
||||
hid->urbout->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_ASYNC_UNLINK);
|
||||
hid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1747,7 +1750,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
|
|||
hid->ctrlbuf, 1, hid_ctrl, hid);
|
||||
hid->urbctrl->setup_dma = hid->cr_dma;
|
||||
hid->urbctrl->transfer_dma = hid->ctrlbuf_dma;
|
||||
hid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP | URB_ASYNC_UNLINK);
|
||||
hid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
|
||||
|
||||
return hid;
|
||||
|
||||
|
|
|
@ -431,11 +431,6 @@ static int keyspan_probe(struct usb_interface *interface, const struct usb_devic
|
|||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct usb_device *udev = usb_get_dev(interface_to_usbdev(interface));
|
||||
|
||||
/* See if the offered device matches what we can accept */
|
||||
if ((udev->descriptor.idVendor != USB_KEYSPAN_VENDOR_ID) ||
|
||||
(udev->descriptor.idProduct != USB_KEYSPAN_PRODUCT_UIA11) )
|
||||
return -ENODEV;
|
||||
|
||||
/* allocate memory for our device state and initialize it */
|
||||
remote = kmalloc(sizeof(*remote), GFP_KERNEL);
|
||||
if (remote == NULL) {
|
||||
|
|
189
drivers/usb/input/map_to_7segment.h
Normal file
189
drivers/usb/input/map_to_7segment.h
Normal file
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* drivers/usb/input/map_to_7segment.h
|
||||
*
|
||||
* Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef MAP_TO_7SEGMENT_H
|
||||
#define MAP_TO_7SEGMENT_H
|
||||
|
||||
/* This file provides translation primitives and tables for the conversion
|
||||
* of (ASCII) characters to a 7-segments notation.
|
||||
*
|
||||
* The 7 segment's wikipedia notation below is used as standard.
|
||||
* See: http://en.wikipedia.org/wiki/Seven_segment_display
|
||||
*
|
||||
* Notation: +-a-+
|
||||
* f b
|
||||
* +-g-+
|
||||
* e c
|
||||
* +-d-+
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* Register a map variable, and fill it with a character set:
|
||||
* static SEG7_DEFAULT_MAP(map_seg7);
|
||||
*
|
||||
*
|
||||
* Then use for conversion:
|
||||
* seg7 = map_to_seg7(&map_seg7, some_char);
|
||||
* ...
|
||||
*
|
||||
* In device drivers it is recommended, if required, to make the char map
|
||||
* accessible via the sysfs interface using the following scheme:
|
||||
*
|
||||
* static ssize_t show_map(struct device *dev, char *buf) {
|
||||
* memcpy(buf, &map_seg7, sizeof(map_seg7));
|
||||
* return sizeof(map_seg7);
|
||||
* }
|
||||
* static ssize_t store_map(struct device *dev, const char *buf, size_t cnt) {
|
||||
* if(cnt != sizeof(map_seg7))
|
||||
* return -EINVAL;
|
||||
* memcpy(&map_seg7, buf, cnt);
|
||||
* return cnt;
|
||||
* }
|
||||
* static DEVICE_ATTR(map_seg7, PERMS_RW, show_map, store_map);
|
||||
*
|
||||
* History:
|
||||
* 2005-05-31 RFC linux-kernel@vger.kernel.org
|
||||
*/
|
||||
#include <linux/errno.h>
|
||||
|
||||
|
||||
#define BIT_SEG7_A 0
|
||||
#define BIT_SEG7_B 1
|
||||
#define BIT_SEG7_C 2
|
||||
#define BIT_SEG7_D 3
|
||||
#define BIT_SEG7_E 4
|
||||
#define BIT_SEG7_F 5
|
||||
#define BIT_SEG7_G 6
|
||||
#define BIT_SEG7_RESERVED 7
|
||||
|
||||
struct seg7_conversion_map {
|
||||
unsigned char table[128];
|
||||
};
|
||||
|
||||
static inline int map_to_seg7(struct seg7_conversion_map *map, int c)
|
||||
{
|
||||
return c & 0x7f ? map->table[c] : -EINVAL;
|
||||
}
|
||||
|
||||
#define SEG7_CONVERSION_MAP(_name, _map) \
|
||||
struct seg7_conversion_map _name = { .table = { _map } }
|
||||
|
||||
/*
|
||||
* It is recommended to use a facility that allows user space to redefine
|
||||
* custom character sets for LCD devices. Please use a sysfs interface
|
||||
* as described above.
|
||||
*/
|
||||
#define MAP_TO_SEG7_SYSFS_FILE "map_seg7"
|
||||
|
||||
/*******************************************************************************
|
||||
* ASCII conversion table
|
||||
******************************************************************************/
|
||||
|
||||
#define _SEG7(l,a,b,c,d,e,f,g) \
|
||||
( a<<BIT_SEG7_A | b<<BIT_SEG7_B | c<<BIT_SEG7_C | d<<BIT_SEG7_D | \
|
||||
e<<BIT_SEG7_E | f<<BIT_SEG7_F | g<<BIT_SEG7_G )
|
||||
|
||||
#define _MAP_0_32_ASCII_SEG7_NON_PRINTABLE \
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
|
||||
#define _MAP_33_47_ASCII_SEG7_SYMBOL \
|
||||
_SEG7('!',0,0,0,0,1,1,0), _SEG7('"',0,1,0,0,0,1,0), _SEG7('#',0,1,1,0,1,1,0),\
|
||||
_SEG7('$',1,0,1,1,0,1,1), _SEG7('%',0,0,1,0,0,1,0), _SEG7('&',1,0,1,1,1,1,1),\
|
||||
_SEG7('\'',0,0,0,0,0,1,0),_SEG7('(',1,0,0,1,1,1,0), _SEG7(')',1,1,1,1,0,0,0),\
|
||||
_SEG7('*',0,1,1,0,1,1,1), _SEG7('+',0,1,1,0,0,0,1), _SEG7(',',0,0,0,0,1,0,0),\
|
||||
_SEG7('-',0,0,0,0,0,0,1), _SEG7('.',0,0,0,0,1,0,0), _SEG7('/',0,1,0,0,1,0,1),
|
||||
|
||||
#define _MAP_48_57_ASCII_SEG7_NUMERIC \
|
||||
_SEG7('0',1,1,1,1,1,1,0), _SEG7('1',0,1,1,0,0,0,0), _SEG7('2',1,1,0,1,1,0,1),\
|
||||
_SEG7('3',1,1,1,1,0,0,1), _SEG7('4',0,1,1,0,0,1,1), _SEG7('5',1,0,1,1,0,1,1),\
|
||||
_SEG7('6',1,0,1,1,1,1,1), _SEG7('7',1,1,1,0,0,0,0), _SEG7('8',1,1,1,1,1,1,1),\
|
||||
_SEG7('9',1,1,1,1,0,1,1),
|
||||
|
||||
#define _MAP_58_64_ASCII_SEG7_SYMBOL \
|
||||
_SEG7(':',0,0,0,1,0,0,1), _SEG7(';',0,0,0,1,0,0,1), _SEG7('<',1,0,0,0,0,1,1),\
|
||||
_SEG7('=',0,0,0,1,0,0,1), _SEG7('>',1,1,0,0,0,0,1), _SEG7('?',1,1,1,0,0,1,0),\
|
||||
_SEG7('@',1,1,0,1,1,1,1),
|
||||
|
||||
#define _MAP_65_90_ASCII_SEG7_ALPHA_UPPR \
|
||||
_SEG7('A',1,1,1,0,1,1,1), _SEG7('B',1,1,1,1,1,1,1), _SEG7('C',1,0,0,1,1,1,0),\
|
||||
_SEG7('D',1,1,1,1,1,1,0), _SEG7('E',1,0,0,1,1,1,1), _SEG7('F',1,0,0,0,1,1,1),\
|
||||
_SEG7('G',1,1,1,1,0,1,1), _SEG7('H',0,1,1,0,1,1,1), _SEG7('I',0,1,1,0,0,0,0),\
|
||||
_SEG7('J',0,1,1,1,0,0,0), _SEG7('K',0,1,1,0,1,1,1), _SEG7('L',0,0,0,1,1,1,0),\
|
||||
_SEG7('M',1,1,1,0,1,1,0), _SEG7('N',1,1,1,0,1,1,0), _SEG7('O',1,1,1,1,1,1,0),\
|
||||
_SEG7('P',1,1,0,0,1,1,1), _SEG7('Q',1,1,1,1,1,1,0), _SEG7('R',1,1,1,0,1,1,1),\
|
||||
_SEG7('S',1,0,1,1,0,1,1), _SEG7('T',0,0,0,1,1,1,1), _SEG7('U',0,1,1,1,1,1,0),\
|
||||
_SEG7('V',0,1,1,1,1,1,0), _SEG7('W',0,1,1,1,1,1,1), _SEG7('X',0,1,1,0,1,1,1),\
|
||||
_SEG7('Y',0,1,1,0,0,1,1), _SEG7('Z',1,1,0,1,1,0,1),
|
||||
|
||||
#define _MAP_91_96_ASCII_SEG7_SYMBOL \
|
||||
_SEG7('[',1,0,0,1,1,1,0), _SEG7('\\',0,0,1,0,0,1,1),_SEG7(']',1,1,1,1,0,0,0),\
|
||||
_SEG7('^',1,1,0,0,0,1,0), _SEG7('_',0,0,0,1,0,0,0), _SEG7('`',0,1,0,0,0,0,0),
|
||||
|
||||
#define _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
|
||||
_SEG7('A',1,1,1,0,1,1,1), _SEG7('b',0,0,1,1,1,1,1), _SEG7('c',0,0,0,1,1,0,1),\
|
||||
_SEG7('d',0,1,1,1,1,0,1), _SEG7('E',1,0,0,1,1,1,1), _SEG7('F',1,0,0,0,1,1,1),\
|
||||
_SEG7('G',1,1,1,1,0,1,1), _SEG7('h',0,0,1,0,1,1,1), _SEG7('i',0,0,1,0,0,0,0),\
|
||||
_SEG7('j',0,0,1,1,0,0,0), _SEG7('k',0,0,1,0,1,1,1), _SEG7('L',0,0,0,1,1,1,0),\
|
||||
_SEG7('M',1,1,1,0,1,1,0), _SEG7('n',0,0,1,0,1,0,1), _SEG7('o',0,0,1,1,1,0,1),\
|
||||
_SEG7('P',1,1,0,0,1,1,1), _SEG7('q',1,1,1,0,0,1,1), _SEG7('r',0,0,0,0,1,0,1),\
|
||||
_SEG7('S',1,0,1,1,0,1,1), _SEG7('T',0,0,0,1,1,1,1), _SEG7('u',0,0,1,1,1,0,0),\
|
||||
_SEG7('v',0,0,1,1,1,0,0), _SEG7('W',0,1,1,1,1,1,1), _SEG7('X',0,1,1,0,1,1,1),\
|
||||
_SEG7('y',0,1,1,1,0,1,1), _SEG7('Z',1,1,0,1,1,0,1),
|
||||
|
||||
#define _MAP_123_126_ASCII_SEG7_SYMBOL \
|
||||
_SEG7('{',1,0,0,1,1,1,0), _SEG7('|',0,0,0,0,1,1,0), _SEG7('}',1,1,1,1,0,0,0),\
|
||||
_SEG7('~',1,0,0,0,0,0,0),
|
||||
|
||||
/* Maps */
|
||||
|
||||
/* This set tries to map as close as possible to the visible characteristics
|
||||
* of the ASCII symbol, lowercase and uppercase letters may differ in
|
||||
* presentation on the display.
|
||||
*/
|
||||
#define MAP_ASCII7SEG_ALPHANUM \
|
||||
_MAP_0_32_ASCII_SEG7_NON_PRINTABLE \
|
||||
_MAP_33_47_ASCII_SEG7_SYMBOL \
|
||||
_MAP_48_57_ASCII_SEG7_NUMERIC \
|
||||
_MAP_58_64_ASCII_SEG7_SYMBOL \
|
||||
_MAP_65_90_ASCII_SEG7_ALPHA_UPPR \
|
||||
_MAP_91_96_ASCII_SEG7_SYMBOL \
|
||||
_MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
|
||||
_MAP_123_126_ASCII_SEG7_SYMBOL
|
||||
|
||||
/* This set tries to map as close as possible to the symbolic characteristics
|
||||
* of the ASCII character for maximum discrimination.
|
||||
* For now this means all alpha chars are in lower case representations.
|
||||
* (This for example facilitates the use of hex numbers with uppercase input.)
|
||||
*/
|
||||
#define MAP_ASCII7SEG_ALPHANUM_LC \
|
||||
_MAP_0_32_ASCII_SEG7_NON_PRINTABLE \
|
||||
_MAP_33_47_ASCII_SEG7_SYMBOL \
|
||||
_MAP_48_57_ASCII_SEG7_NUMERIC \
|
||||
_MAP_58_64_ASCII_SEG7_SYMBOL \
|
||||
_MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
|
||||
_MAP_91_96_ASCII_SEG7_SYMBOL \
|
||||
_MAP_97_122_ASCII_SEG7_ALPHA_LOWER \
|
||||
_MAP_123_126_ASCII_SEG7_SYMBOL
|
||||
|
||||
#define SEG7_DEFAULT_MAP(_name) \
|
||||
SEG7_CONVERSION_MAP(_name,MAP_ASCII7SEG_ALPHANUM)
|
||||
|
||||
#endif /* MAP_TO_7SEGMENT_H */
|
||||
|
1013
drivers/usb/input/yealink.c
Normal file
1013
drivers/usb/input/yealink.c
Normal file
File diff suppressed because it is too large
Load diff
220
drivers/usb/input/yealink.h
Normal file
220
drivers/usb/input/yealink.h
Normal file
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* drivers/usb/input/yealink.h
|
||||
*
|
||||
* Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef INPUT_YEALINK_H
|
||||
#define INPUT_YEALINK_H
|
||||
|
||||
/* Using the control channel on interface 3 various aspects of the phone
|
||||
* can be controlled like LCD, LED, dialtone and the ringtone.
|
||||
*/
|
||||
|
||||
struct yld_ctl_packet {
|
||||
u8 cmd; /* command code, see below */
|
||||
u8 size; /* 1-11, size of used data bytes. */
|
||||
u16 offset; /* internal packet offset */
|
||||
u8 data[11];
|
||||
s8 sum; /* negative sum of 15 preceding bytes */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define USB_PKT_LEN sizeof(struct yld_ctl_packet)
|
||||
|
||||
/* The following yld_ctl_packet's are available: */
|
||||
|
||||
/* Init registers
|
||||
*
|
||||
* cmd 0x8e
|
||||
* size 10
|
||||
* offset 0
|
||||
* data 0,0,0,0....
|
||||
*/
|
||||
#define CMD_INIT 0x8e
|
||||
|
||||
/* Request key scan
|
||||
*
|
||||
* cmd 0x80
|
||||
* size 1
|
||||
* offset 0
|
||||
* data[0] on return returns the key number, if it changes there's a new
|
||||
* key pressed.
|
||||
*/
|
||||
#define CMD_KEYPRESS 0x80
|
||||
|
||||
/* Request scancode
|
||||
*
|
||||
* cmd 0x81
|
||||
* size 1
|
||||
* offset key number [0-1f]
|
||||
* data[0] on return returns the scancode
|
||||
*/
|
||||
#define CMD_SCANCODE 0x81
|
||||
|
||||
/* Set LCD
|
||||
*
|
||||
* cmd 0x04
|
||||
* size 1-11
|
||||
* offset 0-23
|
||||
* data segment bits
|
||||
*/
|
||||
#define CMD_LCD 0x04
|
||||
|
||||
/* Set led
|
||||
*
|
||||
* cmd 0x05
|
||||
* size 1
|
||||
* offset 0
|
||||
* data[0] 0 OFF / 1 ON
|
||||
*/
|
||||
#define CMD_LED 0x05
|
||||
|
||||
/* Set ringtone volume
|
||||
*
|
||||
* cmd 0x11
|
||||
* size 1
|
||||
* offset 0
|
||||
* data[0] 0-0xff volume
|
||||
*/
|
||||
#define CMD_RING_VOLUME 0x11
|
||||
|
||||
/* Set ringtone notes
|
||||
*
|
||||
* cmd 0x02
|
||||
* size 1-11
|
||||
* offset 0->
|
||||
* data binary representation LE16(-freq), LE16(duration) ....
|
||||
*/
|
||||
#define CMD_RING_NOTE 0x02
|
||||
|
||||
/* Sound ringtone via the speaker on the back
|
||||
*
|
||||
* cmd 0x03
|
||||
* size 1
|
||||
* offset 0
|
||||
* data[0] 0 OFF / 0x24 ON
|
||||
*/
|
||||
#define CMD_RINGTONE 0x03
|
||||
|
||||
/* Sound dial tone via the ear speaker
|
||||
*
|
||||
* cmd 0x09
|
||||
* size 1
|
||||
* offset 0
|
||||
* data[0] 0 OFF / 1 ON
|
||||
*/
|
||||
#define CMD_DIALTONE 0x09
|
||||
|
||||
#endif /* INPUT_YEALINK_H */
|
||||
|
||||
|
||||
#if defined(_SEG) && defined(_PIC)
|
||||
/* This table maps the LCD segments onto individual bit positions in the
|
||||
* yld_status struct.
|
||||
*/
|
||||
|
||||
/* LCD, each segment must be driven seperately.
|
||||
*
|
||||
* Layout:
|
||||
*
|
||||
* |[] [][] [][] [][] in |[][]
|
||||
* |[] M [][] D [][] : [][] out |[][]
|
||||
* store
|
||||
*
|
||||
* NEW REP SU MO TU WE TH FR SA
|
||||
*
|
||||
* [] [] [] [] [] [] [] [] [] [] [] []
|
||||
* [] [] [] [] [] [] [] [] [] [] [] []
|
||||
*/
|
||||
|
||||
/* Line 1
|
||||
* Format : 18.e8.M8.88...188
|
||||
* Icon names : M D : IN OUT STORE
|
||||
*/
|
||||
#define LCD_LINE1_OFFSET 0
|
||||
#define LCD_LINE1_SIZE 17
|
||||
|
||||
/* Note: first g then f => ! ! */
|
||||
/* _SEG( type a b c d e g f ) */
|
||||
_SEG('1', 0,0 , 22,2 , 22,2 , 0,0 , 0,0 , 0,0 , 0,0 ),
|
||||
_SEG('8', 20,1 , 20,2 , 20,4 , 20,8 , 21,4 , 21,2 , 21,1 ),
|
||||
_PIC('.', 22,1 , "M" ),
|
||||
_SEG('e', 18,1 , 18,2 , 18,4 , 18,1 , 19,2 , 18,1 , 19,1 ),
|
||||
_SEG('8', 16,1 , 16,2 , 16,4 , 16,8 , 17,4 , 17,2 , 17,1 ),
|
||||
_PIC('.', 15,8 , "D" ),
|
||||
_SEG('M', 14,1 , 14,2 , 14,4 , 14,1 , 15,4 , 15,2 , 15,1 ),
|
||||
_SEG('8', 12,1 , 12,2 , 12,4 , 12,8 , 13,4 , 13,2 , 13,1 ),
|
||||
_PIC('.', 11,8 , ":" ),
|
||||
_SEG('8', 10,1 , 10,2 , 10,4 , 10,8 , 11,4 , 11,2 , 11,1 ),
|
||||
_SEG('8', 8,1 , 8,2 , 8,4 , 8,8 , 9,4 , 9,2 , 9,1 ),
|
||||
_PIC('.', 7,1 , "IN" ),
|
||||
_PIC('.', 7,2 , "OUT" ),
|
||||
_PIC('.', 7,4 , "STORE" ),
|
||||
_SEG('1', 0,0 , 5,1 , 5,1 , 0,0 , 0,0 , 0,0 , 0,0 ),
|
||||
_SEG('8', 4,1 , 4,2 , 4,4 , 4,8 , 5,8 , 5,4 , 5,2 ),
|
||||
_SEG('8', 2,1 , 2,2 , 2,4 , 2,8 , 3,4 , 3,2 , 3,1 ),
|
||||
|
||||
/* Line 2
|
||||
* Format : .........
|
||||
* Pict. name : NEW REP SU MO TU WE TH FR SA
|
||||
*/
|
||||
#define LCD_LINE2_OFFSET LCD_LINE1_OFFSET + LCD_LINE1_SIZE
|
||||
#define LCD_LINE2_SIZE 9
|
||||
|
||||
_PIC('.', 23,2 , "NEW" ),
|
||||
_PIC('.', 23,4 , "REP" ),
|
||||
_PIC('.', 1,8 , "SU" ),
|
||||
_PIC('.', 1,4 , "MO" ),
|
||||
_PIC('.', 1,2 , "TU" ),
|
||||
_PIC('.', 1,1 , "WE" ),
|
||||
_PIC('.', 0,1 , "TH" ),
|
||||
_PIC('.', 0,2 , "FR" ),
|
||||
_PIC('.', 0,4 , "SA" ),
|
||||
|
||||
/* Line 3
|
||||
* Format : 888888888888
|
||||
*/
|
||||
#define LCD_LINE3_OFFSET LCD_LINE2_OFFSET + LCD_LINE2_SIZE
|
||||
#define LCD_LINE3_SIZE 12
|
||||
|
||||
_SEG('8', 22,16, 22,32, 22,64, 22,128, 23,128, 23,64, 23,32 ),
|
||||
_SEG('8', 20,16, 20,32, 20,64, 20,128, 21,128, 21,64, 21,32 ),
|
||||
_SEG('8', 18,16, 18,32, 18,64, 18,128, 19,128, 19,64, 19,32 ),
|
||||
_SEG('8', 16,16, 16,32, 16,64, 16,128, 17,128, 17,64, 17,32 ),
|
||||
_SEG('8', 14,16, 14,32, 14,64, 14,128, 15,128, 15,64, 15,32 ),
|
||||
_SEG('8', 12,16, 12,32, 12,64, 12,128, 13,128, 13,64, 13,32 ),
|
||||
_SEG('8', 10,16, 10,32, 10,64, 10,128, 11,128, 11,64, 11,32 ),
|
||||
_SEG('8', 8,16, 8,32, 8,64, 8,128, 9,128, 9,64, 9,32 ),
|
||||
_SEG('8', 6,16, 6,32, 6,64, 6,128, 7,128, 7,64, 7,32 ),
|
||||
_SEG('8', 4,16, 4,32, 4,64, 4,128, 5,128, 5,64, 5,32 ),
|
||||
_SEG('8', 2,16, 2,32, 2,64, 2,128, 3,128, 3,64, 3,32 ),
|
||||
_SEG('8', 0,16, 0,32, 0,64, 0,128, 1,128, 1,64, 1,32 ),
|
||||
|
||||
/* Line 4
|
||||
*
|
||||
* The LED, DIALTONE and RINGTONE are implemented as icons and use the same
|
||||
* sysfs interface.
|
||||
*/
|
||||
#define LCD_LINE4_OFFSET LCD_LINE3_OFFSET + LCD_LINE3_SIZE
|
||||
|
||||
_PIC('.', offsetof(struct yld_status, led) , 0x01, "LED" ),
|
||||
_PIC('.', offsetof(struct yld_status, dialtone) , 0x01, "DIALTONE" ),
|
||||
_PIC('.', offsetof(struct yld_status, ringtone) , 0x24, "RINGTONE" ),
|
||||
|
||||
#undef _SEG
|
||||
#undef _PIC
|
||||
#endif /* _SEG && _PIC */
|
|
@ -426,7 +426,7 @@ static int auerchain_submit_urb (pauerchain_t acp, struct urb * urb)
|
|||
|
||||
/* cancel an urb which is submitted to the chain
|
||||
the result is 0 if the urb is cancelled, or -EINPROGRESS if
|
||||
URB_ASYNC_UNLINK is set and the function is successfully started.
|
||||
the function is successfully started.
|
||||
*/
|
||||
static int auerchain_unlink_urb (pauerchain_t acp, struct urb * urb)
|
||||
{
|
||||
|
@ -515,7 +515,6 @@ static void auerchain_unlink_all (pauerchain_t acp)
|
|||
acep = acp->active;
|
||||
if (acep) {
|
||||
urbp = acep->urbp;
|
||||
urbp->transfer_flags &= ~URB_ASYNC_UNLINK;
|
||||
dbg ("unlink active urb");
|
||||
usb_kill_urb (urbp);
|
||||
}
|
||||
|
|
|
@ -464,7 +464,7 @@ static ssize_t ld_usb_read(struct file *file, char __user *buffer, size_t count,
|
|||
actual_buffer = (size_t*)(dev->ring_buffer + dev->ring_tail*(sizeof(size_t)+dev->interrupt_in_endpoint_size));
|
||||
bytes_to_read = min(count, *actual_buffer);
|
||||
if (bytes_to_read < *actual_buffer)
|
||||
dev_warn(&dev->intf->dev, "Read buffer overflow, %d bytes dropped\n",
|
||||
dev_warn(&dev->intf->dev, "Read buffer overflow, %zd bytes dropped\n",
|
||||
*actual_buffer-bytes_to_read);
|
||||
|
||||
/* copy one interrupt_in_buffer from ring_buffer into userspace */
|
||||
|
@ -528,8 +528,8 @@ static ssize_t ld_usb_write(struct file *file, const char __user *buffer,
|
|||
/* write the data into interrupt_out_buffer from userspace */
|
||||
bytes_to_write = min(count, write_buffer_size*dev->interrupt_out_endpoint_size);
|
||||
if (bytes_to_write < count)
|
||||
dev_warn(&dev->intf->dev, "Write buffer overflow, %d bytes dropped\n",count-bytes_to_write);
|
||||
dbg_info(&dev->intf->dev, "%s: count = %d, bytes_to_write = %d\n", __FUNCTION__, count, bytes_to_write);
|
||||
dev_warn(&dev->intf->dev, "Write buffer overflow, %zd bytes dropped\n",count-bytes_to_write);
|
||||
dbg_info(&dev->intf->dev, "%s: count = %zd, bytes_to_write = %zd\n", __FUNCTION__, count, bytes_to_write);
|
||||
|
||||
if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write)) {
|
||||
retval = -EFAULT;
|
||||
|
|
|
@ -229,7 +229,7 @@ sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, unsigned int pipe,
|
|||
usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len,
|
||||
sisusb_bulk_completeout, &sisusb->urbout_context[index]);
|
||||
|
||||
urb->transfer_flags |= (tflags | URB_ASYNC_UNLINK);
|
||||
urb->transfer_flags |= tflags;
|
||||
urb->actual_length = 0;
|
||||
|
||||
if ((urb->transfer_dma = transfer_dma))
|
||||
|
@ -295,7 +295,7 @@ sisusb_bulkin_msg(struct sisusb_usb_data *sisusb, unsigned int pipe, void *data,
|
|||
usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len,
|
||||
sisusb_bulk_completein, sisusb);
|
||||
|
||||
urb->transfer_flags |= (tflags | URB_ASYNC_UNLINK);
|
||||
urb->transfer_flags |= tflags;
|
||||
urb->actual_length = 0;
|
||||
|
||||
if ((urb->transfer_dma = transfer_dma))
|
||||
|
|
|
@ -986,7 +986,6 @@ test_ctrl_queue (struct usbtest_dev *dev, struct usbtest_param *param)
|
|||
|
||||
u->context = &context;
|
||||
u->complete = ctrl_complete;
|
||||
u->transfer_flags |= URB_ASYNC_UNLINK;
|
||||
}
|
||||
|
||||
/* queue the urbs */
|
||||
|
@ -1052,7 +1051,6 @@ static int unlink1 (struct usbtest_dev *dev, int pipe, int size, int async)
|
|||
urb = simple_alloc_urb (testdev_to_usbdev (dev), pipe, size);
|
||||
if (!urb)
|
||||
return -ENOMEM;
|
||||
urb->transfer_flags |= URB_ASYNC_UNLINK;
|
||||
urb->context = &completion;
|
||||
urb->complete = unlink1_callback;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# Makefile for USB Core files and filesystem
|
||||
#
|
||||
|
||||
usbmon-objs := mon_main.o mon_stat.o mon_text.o
|
||||
usbmon-objs := mon_main.o mon_stat.o mon_text.o mon_dma.o
|
||||
|
||||
# This does not use CONFIG_USB_MON because we want this to use a tristate.
|
||||
obj-$(CONFIG_USB) += usbmon.o
|
||||
|
|
55
drivers/usb/mon/mon_dma.c
Normal file
55
drivers/usb/mon/mon_dma.c
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* The USB Monitor, inspired by Dave Harding's USBMon.
|
||||
*
|
||||
* mon_dma.c: Library which snoops on DMA areas.
|
||||
*
|
||||
* Copyright (C) 2005 Pete Zaitcev (zaitcev@redhat.com)
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
#include <linux/usb.h> /* Only needed for declarations in usb_mon.h */
|
||||
#include "usb_mon.h"
|
||||
|
||||
#ifdef __i386__ /* CONFIG_ARCH_I386 does not exit */
|
||||
#define MON_HAS_UNMAP 1
|
||||
|
||||
#define phys_to_page(phys) pfn_to_page((phys) >> PAGE_SHIFT)
|
||||
|
||||
char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len)
|
||||
{
|
||||
struct page *pg;
|
||||
unsigned long flags;
|
||||
unsigned char *map;
|
||||
unsigned char *ptr;
|
||||
|
||||
/*
|
||||
* On i386, a DMA handle is the "physical" address of a page.
|
||||
* In other words, the bus address is equal to physical address.
|
||||
* There is no IOMMU.
|
||||
*/
|
||||
pg = phys_to_page(dma_addr);
|
||||
|
||||
/*
|
||||
* We are called from hardware IRQs in case of callbacks.
|
||||
* But we can be called from softirq or process context in case
|
||||
* of submissions. In such case, we need to protect KM_IRQ0.
|
||||
*/
|
||||
local_irq_save(flags);
|
||||
map = kmap_atomic(pg, KM_IRQ0);
|
||||
ptr = map + (dma_addr & (PAGE_SIZE-1));
|
||||
memcpy(dst, ptr, len);
|
||||
kunmap_atomic(map, KM_IRQ0);
|
||||
local_irq_restore(flags);
|
||||
return 0;
|
||||
}
|
||||
#endif /* __i386__ */
|
||||
|
||||
#ifndef MON_HAS_UNMAP
|
||||
char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len)
|
||||
{
|
||||
return 'D';
|
||||
}
|
||||
#endif
|
|
@ -91,25 +91,11 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
|
|||
int len, char ev_type)
|
||||
{
|
||||
int pipe = urb->pipe;
|
||||
unsigned char *data;
|
||||
|
||||
/*
|
||||
* The check to see if it's safe to poke at data has an enormous
|
||||
* number of corner cases, but it seems that the following is
|
||||
* more or less safe.
|
||||
*
|
||||
* We do not even try to look transfer_buffer, because it can
|
||||
* contain non-NULL garbage in case the upper level promised to
|
||||
* set DMA for the HCD.
|
||||
*/
|
||||
if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
|
||||
return 'D';
|
||||
|
||||
if (len <= 0)
|
||||
return 'L';
|
||||
|
||||
if ((data = urb->transfer_buffer) == NULL)
|
||||
return 'Z'; /* '0' would be not as pretty. */
|
||||
if (len >= DATA_MAX)
|
||||
len = DATA_MAX;
|
||||
|
||||
/*
|
||||
* Bulk is easy to shortcut reliably.
|
||||
|
@ -126,8 +112,21 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
|
|||
}
|
||||
}
|
||||
|
||||
if (len >= DATA_MAX)
|
||||
len = DATA_MAX;
|
||||
/*
|
||||
* The check to see if it's safe to poke at data has an enormous
|
||||
* number of corner cases, but it seems that the following is
|
||||
* more or less safe.
|
||||
*
|
||||
* We do not even try to look transfer_buffer, because it can
|
||||
* contain non-NULL garbage in case the upper level promised to
|
||||
* set DMA for the HCD.
|
||||
*/
|
||||
if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
|
||||
return mon_dmapeek(ep->data, urb->transfer_dma, len);
|
||||
|
||||
if (urb->transfer_buffer == NULL)
|
||||
return 'Z'; /* '0' would be not as pretty. */
|
||||
|
||||
memcpy(ep->data, urb->transfer_buffer, len);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -45,6 +45,10 @@ struct mon_reader {
|
|||
void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r);
|
||||
void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r);
|
||||
|
||||
/*
|
||||
*/
|
||||
extern char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len);
|
||||
|
||||
extern struct semaphore mon_lock;
|
||||
|
||||
extern struct file_operations mon_fops_text;
|
||||
|
|
|
@ -99,7 +99,7 @@ config USB_USBNET
|
|||
with "minidrivers" built around a common network driver core
|
||||
that supports deep queues for efficient transfers. (This gives
|
||||
better performance with small packets and at high speeds).
|
||||
|
||||
|
||||
The USB host runs "usbnet", and the other end of the link might be:
|
||||
|
||||
- Another USB host, when using USB "network" or "data transfer"
|
||||
|
@ -125,38 +125,63 @@ config USB_USBNET
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called usbnet.
|
||||
|
||||
comment "USB Host-to-Host Cables"
|
||||
depends on USB_USBNET
|
||||
config USB_NET_AX8817X
|
||||
tristate "ASIX AX88xxx Based USB 2.0 Ethernet Adapters"
|
||||
depends on USB_USBNET && NET_ETHERNET
|
||||
select CRC32
|
||||
select MII
|
||||
default y
|
||||
help
|
||||
This option adds support for ASIX AX88xxx based USB 2.0
|
||||
10/100 Ethernet adapters.
|
||||
|
||||
config USB_ALI_M5632
|
||||
boolean "ALi M5632 based 'USB 2.0 Data Link' cables"
|
||||
This driver should work with at least the following devices:
|
||||
* Aten UC210T
|
||||
* ASIX AX88172
|
||||
* Billionton Systems, USB2AR
|
||||
* Buffalo LUA-U2-KTX
|
||||
* Corega FEther USB2-TX
|
||||
* D-Link DUB-E100
|
||||
* Hawking UF200
|
||||
* Linksys USB200M
|
||||
* Netgear FA120
|
||||
* Sitecom LN-029
|
||||
* Intellinet USB 2.0 Ethernet
|
||||
* ST Lab USB 2.0 Ethernet
|
||||
* TrendNet TU2-ET100
|
||||
|
||||
This driver creates an interface named "ethX", where X depends on
|
||||
what other networking devices you have in use.
|
||||
|
||||
|
||||
config USB_NET_CDCETHER
|
||||
tristate "CDC Ethernet support (smart devices such as cable modems)"
|
||||
depends on USB_USBNET
|
||||
default y
|
||||
help
|
||||
Choose this option if you're using a host-to-host cable
|
||||
based on this design, which supports USB 2.0 high speed.
|
||||
This option supports devices conforming to the Communication Device
|
||||
Class (CDC) Ethernet Control Model, a specification that's easy to
|
||||
implement in device firmware. The CDC specifications are available
|
||||
from <http://www.usb.org/>.
|
||||
|
||||
config USB_AN2720
|
||||
boolean "AnchorChips 2720 based cables (Xircom PGUNET, ...)"
|
||||
depends on USB_USBNET
|
||||
default y
|
||||
help
|
||||
Choose this option if you're using a host-to-host cable
|
||||
based on this design. Note that AnchorChips is now a
|
||||
Cypress brand.
|
||||
CDC Ethernet is an implementation option for DOCSIS cable modems
|
||||
that support USB connectivity, used for non-Microsoft USB hosts.
|
||||
The Linux-USB CDC Ethernet Gadget driver is an open implementation.
|
||||
This driver should work with at least the following devices:
|
||||
|
||||
config USB_BELKIN
|
||||
boolean "eTEK based host-to-host cables (Advance, Belkin, ...)"
|
||||
depends on USB_USBNET
|
||||
default y
|
||||
help
|
||||
Choose this option if you're using a host-to-host cable
|
||||
based on this design: two NetChip 2890 chips and an Atmel
|
||||
microcontroller, with LEDs that indicate traffic.
|
||||
* Ericsson PipeRider (all variants)
|
||||
* Motorola (DM100 and SB4100)
|
||||
* Broadcom Cable Modem (reference design)
|
||||
* Toshiba PCX1100U
|
||||
* ...
|
||||
|
||||
config USB_GENESYS
|
||||
boolean "GeneSys GL620USB-A based cables"
|
||||
default y
|
||||
This driver creates an interface named "ethX", where X depends on
|
||||
what other networking devices you have in use. However, if the
|
||||
IEEE 802 "local assignment" bit is set in the address, a "usbX"
|
||||
name is used instead.
|
||||
|
||||
config USB_NET_GL620A
|
||||
tristate "GeneSys GL620USB-A based cables"
|
||||
depends on USB_USBNET
|
||||
help
|
||||
Choose this option if you're using a host-to-host cable,
|
||||
|
@ -164,38 +189,78 @@ config USB_GENESYS
|
|||
|
||||
Note that the half-duplex "GL620USB" is not supported.
|
||||
|
||||
config USB_NET1080
|
||||
boolean "NetChip 1080 based cables (Laplink, ...)"
|
||||
config USB_NET_NET1080
|
||||
tristate "NetChip 1080 based cables (Laplink, ...)"
|
||||
default y
|
||||
depends on USB_USBNET
|
||||
help
|
||||
Choose this option if you're using a host-to-host cable based
|
||||
on this design: one NetChip 1080 chips and supporting logic,
|
||||
supporting LEDs that indicate traffic
|
||||
on this design: one NetChip 1080 chip and supporting logic,
|
||||
optionally with LEDs that indicate traffic
|
||||
|
||||
config USB_PL2301
|
||||
boolean "Prolific PL-2301/2302 based cables"
|
||||
default y
|
||||
# handshake/init/reset problems, from original 'plusb' driver
|
||||
config USB_NET_PLUSB
|
||||
tristate "Prolific PL-2301/2302 based cables"
|
||||
# if the handshake/init/reset problems, from original 'plusb',
|
||||
# are ever resolved ... then remove "experimental"
|
||||
depends on USB_USBNET && EXPERIMENTAL
|
||||
help
|
||||
Choose this option if you're using a host-to-host cable
|
||||
with one of these chips.
|
||||
|
||||
config USB_KC2190
|
||||
boolean "KT Technology KC2190 based cables (InstaNet)"
|
||||
default y
|
||||
config USB_NET_RNDIS_HOST
|
||||
tristate "Host for RNDIS devices (EXPERIMENTAL)"
|
||||
depends on USB_USBNET && EXPERIMENTAL
|
||||
select USB_NET_CDCETHER
|
||||
help
|
||||
Choose this option if you're using a host-to-host cable
|
||||
with one of these chips.
|
||||
This option enables hosting "Remote NDIS" USB networking links,
|
||||
as encouraged by Microsoft (instead of CDC Ethernet!) for use in
|
||||
various devices that may only support this protocol.
|
||||
|
||||
comment "Intelligent USB Devices/Gadgets"
|
||||
Avoid using this protocol unless you have no better options.
|
||||
The protocol specification is incomplete, and is controlled by
|
||||
(and for) Microsoft; it isn't an "Open" ecosystem or market.
|
||||
|
||||
config USB_NET_CDC_SUBSET
|
||||
tristate "Simple USB Network Links (CDC Ethernet subset)"
|
||||
depends on USB_USBNET
|
||||
help
|
||||
This driver module supports USB network devices that can work
|
||||
without any device-specific information. Select it if you have
|
||||
one of these drivers.
|
||||
|
||||
Note that while many USB host-to-host cables can work in this mode,
|
||||
that may mean not being able to talk to Win32 systems or more
|
||||
commonly not being able to handle certain events (like replugging
|
||||
the host on the other end) very well. Also, these devices will
|
||||
not generally have permanently assigned Ethernet addresses.
|
||||
|
||||
config USB_ALI_M5632
|
||||
boolean "ALi M5632 based 'USB 2.0 Data Link' cables"
|
||||
depends on USB_NET_CDC_SUBSET
|
||||
help
|
||||
Choose this option if you're using a host-to-host cable
|
||||
based on this design, which supports USB 2.0 high speed.
|
||||
|
||||
config USB_AN2720
|
||||
boolean "AnchorChips 2720 based cables (Xircom PGUNET, ...)"
|
||||
depends on USB_NET_CDC_SUBSET
|
||||
help
|
||||
Choose this option if you're using a host-to-host cable
|
||||
based on this design. Note that AnchorChips is now a
|
||||
Cypress brand.
|
||||
|
||||
config USB_BELKIN
|
||||
boolean "eTEK based host-to-host cables (Advance, Belkin, ...)"
|
||||
depends on USB_NET_CDC_SUBSET
|
||||
default y
|
||||
help
|
||||
Choose this option if you're using a host-to-host cable
|
||||
based on this design: two NetChip 2890 chips and an Atmel
|
||||
microcontroller, with LEDs that indicate traffic.
|
||||
|
||||
config USB_ARMLINUX
|
||||
boolean "Embedded ARM Linux links (iPaq, ...)"
|
||||
depends on USB_USBNET
|
||||
depends on USB_NET_CDC_SUBSET
|
||||
default y
|
||||
help
|
||||
Choose this option to support the "usb-eth" networking driver
|
||||
|
@ -212,15 +277,15 @@ config USB_ARMLINUX
|
|||
|
||||
config USB_EPSON2888
|
||||
boolean "Epson 2888 based firmware (DEVELOPMENT)"
|
||||
depends on USB_USBNET
|
||||
default y
|
||||
depends on USB_NET_CDC_SUBSET
|
||||
help
|
||||
Choose this option to support the usb networking links used
|
||||
by some sample firmware from Epson.
|
||||
|
||||
config USB_ZAURUS
|
||||
boolean "Sharp Zaurus (stock ROMs) and compatible"
|
||||
config USB_NET_ZAURUS
|
||||
tristate "Sharp Zaurus (stock ROMs) and compatible"
|
||||
depends on USB_USBNET
|
||||
select USB_NET_CDCETHER
|
||||
select CRC32
|
||||
default y
|
||||
help
|
||||
|
@ -235,61 +300,6 @@ config USB_ZAURUS
|
|||
really need this non-conformant variant of CDC Ethernet (or in
|
||||
some cases CDC MDLM) protocol, not "g_ether".
|
||||
|
||||
config USB_CDCETHER
|
||||
boolean "CDC Ethernet support (smart devices such as cable modems)"
|
||||
depends on USB_USBNET
|
||||
default y
|
||||
help
|
||||
This option supports devices conforming to the Communication Device
|
||||
Class (CDC) Ethernet Control Model, a specification that's easy to
|
||||
implement in device firmware. The CDC specifications are available
|
||||
from <http://www.usb.org/>.
|
||||
|
||||
CDC Ethernet is an implementation option for DOCSIS cable modems
|
||||
that support USB connectivity, used for non-Microsoft USB hosts.
|
||||
This driver should work with at least the following devices:
|
||||
|
||||
* Ericsson PipeRider (all variants)
|
||||
* Motorola (DM100 and SB4100)
|
||||
* Broadcom Cable Modem (reference design)
|
||||
* Toshiba PCX1100U
|
||||
* ...
|
||||
|
||||
This driver creates an interface named "ethX", where X depends on
|
||||
what other networking devices you have in use. However, if the
|
||||
IEEE 802 "local assignment" bit is set in the address, a "usbX"
|
||||
name is used instead.
|
||||
|
||||
comment "USB Network Adapters"
|
||||
depends on USB_USBNET
|
||||
|
||||
config USB_AX8817X
|
||||
boolean "ASIX AX88xxx Based USB 2.0 Ethernet Devices"
|
||||
depends on USB_USBNET && NET_ETHERNET
|
||||
select CRC32
|
||||
select MII
|
||||
default y
|
||||
help
|
||||
This option adds support for ASIX AX88xxx based USB 2.0
|
||||
10/100 Ethernet devices.
|
||||
|
||||
This driver should work with at least the following devices:
|
||||
* Aten UC210T
|
||||
* ASIX AX88172
|
||||
* Billionton Systems, USB2AR
|
||||
* Buffalo LUA-U2-KTX
|
||||
* Corega FEther USB2-TX
|
||||
* D-Link DUB-E100
|
||||
* Hawking UF200
|
||||
* Linksys USB200M
|
||||
* Netgear FA120
|
||||
* Sitecom LN-029
|
||||
* Intellinet USB 2.0 Ethernet
|
||||
* ST Lab USB 2.0 Ethernet
|
||||
* TrendNet TU2-ET100
|
||||
|
||||
This driver creates an interface named "ethX", where X depends on
|
||||
what other networking devices you have in use.
|
||||
|
||||
config USB_ZD1201
|
||||
tristate "USB ZD1201 based Wireless device support"
|
||||
|
|
|
@ -6,5 +6,13 @@ obj-$(CONFIG_USB_CATC) += catc.o
|
|||
obj-$(CONFIG_USB_KAWETH) += kaweth.o
|
||||
obj-$(CONFIG_USB_PEGASUS) += pegasus.o
|
||||
obj-$(CONFIG_USB_RTL8150) += rtl8150.o
|
||||
obj-$(CONFIG_USB_NET_AX8817X) += asix.o
|
||||
obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o
|
||||
obj-$(CONFIG_USB_NET_GL620A) += gl620a.o
|
||||
obj-$(CONFIG_USB_NET_NET1080) += net1080.o
|
||||
obj-$(CONFIG_USB_NET_PLUSB) += plusb.o
|
||||
obj-$(CONFIG_USB_NET_RNDIS_HOST) += rndis_host.o
|
||||
obj-$(CONFIG_USB_NET_CDC_SUBSET) += cdc_subset.o
|
||||
obj-$(CONFIG_USB_NET_ZAURUS) += zaurus.o
|
||||
obj-$(CONFIG_USB_USBNET) += usbnet.o
|
||||
obj-$(CONFIG_USB_ZD1201) += zd1201.o
|
||||
|
|
948
drivers/usb/net/asix.c
Normal file
948
drivers/usb/net/asix.c
Normal file
|
@ -0,0 +1,948 @@
|
|||
/*
|
||||
* ASIX AX8817X based USB 2.0 Ethernet Devices
|
||||
* Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
|
||||
* Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net>
|
||||
* Copyright (c) 2002-2003 TiVo Inc.
|
||||
*
|
||||
* 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 DEBUG // error path messages, extra info
|
||||
// #define VERBOSE // more; success messages
|
||||
|
||||
#include <linux/config.h>
|
||||
#ifdef CONFIG_USB_DEBUG
|
||||
# define DEBUG
|
||||
#endif
|
||||
#include <linux/module.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/crc32.h>
|
||||
|
||||
#include "usbnet.h"
|
||||
|
||||
|
||||
/* ASIX AX8817X based USB 2.0 Ethernet Devices */
|
||||
|
||||
#define AX_CMD_SET_SW_MII 0x06
|
||||
#define AX_CMD_READ_MII_REG 0x07
|
||||
#define AX_CMD_WRITE_MII_REG 0x08
|
||||
#define AX_CMD_SET_HW_MII 0x0a
|
||||
#define AX_CMD_READ_EEPROM 0x0b
|
||||
#define AX_CMD_WRITE_EEPROM 0x0c
|
||||
#define AX_CMD_WRITE_ENABLE 0x0d
|
||||
#define AX_CMD_WRITE_DISABLE 0x0e
|
||||
#define AX_CMD_WRITE_RX_CTL 0x10
|
||||
#define AX_CMD_READ_IPG012 0x11
|
||||
#define AX_CMD_WRITE_IPG0 0x12
|
||||
#define AX_CMD_WRITE_IPG1 0x13
|
||||
#define AX_CMD_WRITE_IPG2 0x14
|
||||
#define AX_CMD_WRITE_MULTI_FILTER 0x16
|
||||
#define AX_CMD_READ_NODE_ID 0x17
|
||||
#define AX_CMD_READ_PHY_ID 0x19
|
||||
#define AX_CMD_READ_MEDIUM_STATUS 0x1a
|
||||
#define AX_CMD_WRITE_MEDIUM_MODE 0x1b
|
||||
#define AX_CMD_READ_MONITOR_MODE 0x1c
|
||||
#define AX_CMD_WRITE_MONITOR_MODE 0x1d
|
||||
#define AX_CMD_WRITE_GPIOS 0x1f
|
||||
#define AX_CMD_SW_RESET 0x20
|
||||
#define AX_CMD_SW_PHY_STATUS 0x21
|
||||
#define AX_CMD_SW_PHY_SELECT 0x22
|
||||
#define AX88772_CMD_READ_NODE_ID 0x13
|
||||
|
||||
#define AX_MONITOR_MODE 0x01
|
||||
#define AX_MONITOR_LINK 0x02
|
||||
#define AX_MONITOR_MAGIC 0x04
|
||||
#define AX_MONITOR_HSFS 0x10
|
||||
|
||||
/* AX88172 Medium Status Register values */
|
||||
#define AX_MEDIUM_FULL_DUPLEX 0x02
|
||||
#define AX_MEDIUM_TX_ABORT_ALLOW 0x04
|
||||
#define AX_MEDIUM_FLOW_CONTROL_EN 0x10
|
||||
|
||||
#define AX_MCAST_FILTER_SIZE 8
|
||||
#define AX_MAX_MCAST 64
|
||||
|
||||
#define AX_EEPROM_LEN 0x40
|
||||
|
||||
#define AX_SWRESET_CLEAR 0x00
|
||||
#define AX_SWRESET_RR 0x01
|
||||
#define AX_SWRESET_RT 0x02
|
||||
#define AX_SWRESET_PRTE 0x04
|
||||
#define AX_SWRESET_PRL 0x08
|
||||
#define AX_SWRESET_BZ 0x10
|
||||
#define AX_SWRESET_IPRL 0x20
|
||||
#define AX_SWRESET_IPPD 0x40
|
||||
|
||||
#define AX88772_IPG0_DEFAULT 0x15
|
||||
#define AX88772_IPG1_DEFAULT 0x0c
|
||||
#define AX88772_IPG2_DEFAULT 0x12
|
||||
|
||||
#define AX88772_MEDIUM_FULL_DUPLEX 0x0002
|
||||
#define AX88772_MEDIUM_RESERVED 0x0004
|
||||
#define AX88772_MEDIUM_RX_FC_ENABLE 0x0010
|
||||
#define AX88772_MEDIUM_TX_FC_ENABLE 0x0020
|
||||
#define AX88772_MEDIUM_PAUSE_FORMAT 0x0080
|
||||
#define AX88772_MEDIUM_RX_ENABLE 0x0100
|
||||
#define AX88772_MEDIUM_100MB 0x0200
|
||||
#define AX88772_MEDIUM_DEFAULT \
|
||||
(AX88772_MEDIUM_FULL_DUPLEX | AX88772_MEDIUM_RX_FC_ENABLE | \
|
||||
AX88772_MEDIUM_TX_FC_ENABLE | AX88772_MEDIUM_100MB | \
|
||||
AX88772_MEDIUM_RESERVED | AX88772_MEDIUM_RX_ENABLE )
|
||||
|
||||
#define AX_EEPROM_MAGIC 0xdeadbeef
|
||||
|
||||
/* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */
|
||||
struct ax8817x_data {
|
||||
u8 multi_filter[AX_MCAST_FILTER_SIZE];
|
||||
};
|
||||
|
||||
struct ax88172_int_data {
|
||||
u16 res1;
|
||||
u8 link;
|
||||
u16 res2;
|
||||
u8 status;
|
||||
u16 res3;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
static int ax8817x_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
|
||||
u16 size, void *data)
|
||||
{
|
||||
return usb_control_msg(
|
||||
dev->udev,
|
||||
usb_rcvctrlpipe(dev->udev, 0),
|
||||
cmd,
|
||||
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
value,
|
||||
index,
|
||||
data,
|
||||
size,
|
||||
USB_CTRL_GET_TIMEOUT);
|
||||
}
|
||||
|
||||
static int ax8817x_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
|
||||
u16 size, void *data)
|
||||
{
|
||||
return usb_control_msg(
|
||||
dev->udev,
|
||||
usb_sndctrlpipe(dev->udev, 0),
|
||||
cmd,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
value,
|
||||
index,
|
||||
data,
|
||||
size,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
|
||||
static void ax8817x_async_cmd_callback(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
|
||||
|
||||
if (urb->status < 0)
|
||||
printk(KERN_DEBUG "ax8817x_async_cmd_callback() failed with %d",
|
||||
urb->status);
|
||||
|
||||
kfree(req);
|
||||
usb_free_urb(urb);
|
||||
}
|
||||
|
||||
static void ax8817x_status(struct usbnet *dev, struct urb *urb)
|
||||
{
|
||||
struct ax88172_int_data *event;
|
||||
int link;
|
||||
|
||||
if (urb->actual_length < 8)
|
||||
return;
|
||||
|
||||
event = urb->transfer_buffer;
|
||||
link = event->link & 0x01;
|
||||
if (netif_carrier_ok(dev->net) != link) {
|
||||
if (link) {
|
||||
netif_carrier_on(dev->net);
|
||||
usbnet_defer_kevent (dev, EVENT_LINK_RESET );
|
||||
} else
|
||||
netif_carrier_off(dev->net);
|
||||
devdbg(dev, "ax8817x - Link Status is: %d", link);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ax8817x_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
|
||||
u16 size, void *data)
|
||||
{
|
||||
struct usb_ctrlrequest *req;
|
||||
int status;
|
||||
struct urb *urb;
|
||||
|
||||
if ((urb = usb_alloc_urb(0, GFP_ATOMIC)) == NULL) {
|
||||
devdbg(dev, "Error allocating URB in write_cmd_async!");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC)) == NULL) {
|
||||
deverr(dev, "Failed to allocate memory for control request");
|
||||
usb_free_urb(urb);
|
||||
return;
|
||||
}
|
||||
|
||||
req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
|
||||
req->bRequest = cmd;
|
||||
req->wValue = cpu_to_le16(value);
|
||||
req->wIndex = cpu_to_le16(index);
|
||||
req->wLength = cpu_to_le16(size);
|
||||
|
||||
usb_fill_control_urb(urb, dev->udev,
|
||||
usb_sndctrlpipe(dev->udev, 0),
|
||||
(void *)req, data, size,
|
||||
ax8817x_async_cmd_callback, req);
|
||||
|
||||
if((status = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
|
||||
deverr(dev, "Error submitting the control message: status=%d",
|
||||
status);
|
||||
kfree(req);
|
||||
usb_free_urb(urb);
|
||||
}
|
||||
}
|
||||
|
||||
static void ax8817x_set_multicast(struct net_device *net)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
|
||||
u8 rx_ctl = 0x8c;
|
||||
|
||||
if (net->flags & IFF_PROMISC) {
|
||||
rx_ctl |= 0x01;
|
||||
} else if (net->flags & IFF_ALLMULTI
|
||||
|| net->mc_count > AX_MAX_MCAST) {
|
||||
rx_ctl |= 0x02;
|
||||
} else if (net->mc_count == 0) {
|
||||
/* just broadcast and directed */
|
||||
} else {
|
||||
/* We use the 20 byte dev->data
|
||||
* for our 8 byte filter buffer
|
||||
* to avoid allocating memory that
|
||||
* is tricky to free later */
|
||||
struct dev_mc_list *mc_list = net->mc_list;
|
||||
u32 crc_bits;
|
||||
int i;
|
||||
|
||||
memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE);
|
||||
|
||||
/* Build the multicast hash filter. */
|
||||
for (i = 0; i < net->mc_count; i++) {
|
||||
crc_bits =
|
||||
ether_crc(ETH_ALEN,
|
||||
mc_list->dmi_addr) >> 26;
|
||||
data->multi_filter[crc_bits >> 3] |=
|
||||
1 << (crc_bits & 7);
|
||||
mc_list = mc_list->next;
|
||||
}
|
||||
|
||||
ax8817x_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
|
||||
AX_MCAST_FILTER_SIZE, data->multi_filter);
|
||||
|
||||
rx_ctl |= 0x10;
|
||||
}
|
||||
|
||||
ax8817x_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL);
|
||||
}
|
||||
|
||||
static int ax8817x_mdio_read(struct net_device *netdev, int phy_id, int loc)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(netdev);
|
||||
u16 res;
|
||||
u8 buf[1];
|
||||
|
||||
ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, &buf);
|
||||
ax8817x_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id,
|
||||
(__u16)loc, 2, (u16 *)&res);
|
||||
ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf);
|
||||
|
||||
return res & 0xffff;
|
||||
}
|
||||
|
||||
/* same as above, but converts resulting value to cpu byte order */
|
||||
static int ax8817x_mdio_read_le(struct net_device *netdev, int phy_id, int loc)
|
||||
{
|
||||
return le16_to_cpu(ax8817x_mdio_read(netdev,phy_id, loc));
|
||||
}
|
||||
|
||||
static void
|
||||
ax8817x_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(netdev);
|
||||
u16 res = val;
|
||||
u8 buf[1];
|
||||
|
||||
ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, &buf);
|
||||
ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
|
||||
(__u16)loc, 2, (u16 *)&res);
|
||||
ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf);
|
||||
}
|
||||
|
||||
/* same as above, but converts new value to le16 byte order before writing */
|
||||
static void
|
||||
ax8817x_mdio_write_le(struct net_device *netdev, int phy_id, int loc, int val)
|
||||
{
|
||||
ax8817x_mdio_write( netdev, phy_id, loc, cpu_to_le16(val) );
|
||||
}
|
||||
|
||||
static int ax88172_link_reset(struct usbnet *dev)
|
||||
{
|
||||
u16 lpa;
|
||||
u16 adv;
|
||||
u16 res;
|
||||
u8 mode;
|
||||
|
||||
mode = AX_MEDIUM_TX_ABORT_ALLOW | AX_MEDIUM_FLOW_CONTROL_EN;
|
||||
lpa = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_LPA);
|
||||
adv = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
|
||||
res = mii_nway_result(lpa|adv);
|
||||
if (res & LPA_DUPLEX)
|
||||
mode |= AX_MEDIUM_FULL_DUPLEX;
|
||||
ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ax8817x_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
u8 opt;
|
||||
|
||||
if (ax8817x_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 0, 0, 1, &opt) < 0) {
|
||||
wolinfo->supported = 0;
|
||||
wolinfo->wolopts = 0;
|
||||
return;
|
||||
}
|
||||
wolinfo->supported = WAKE_PHY | WAKE_MAGIC;
|
||||
wolinfo->wolopts = 0;
|
||||
if (opt & AX_MONITOR_MODE) {
|
||||
if (opt & AX_MONITOR_LINK)
|
||||
wolinfo->wolopts |= WAKE_PHY;
|
||||
if (opt & AX_MONITOR_MAGIC)
|
||||
wolinfo->wolopts |= WAKE_MAGIC;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
ax8817x_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
u8 opt = 0;
|
||||
u8 buf[1];
|
||||
|
||||
if (wolinfo->wolopts & WAKE_PHY)
|
||||
opt |= AX_MONITOR_LINK;
|
||||
if (wolinfo->wolopts & WAKE_MAGIC)
|
||||
opt |= AX_MONITOR_MAGIC;
|
||||
if (opt != 0)
|
||||
opt |= AX_MONITOR_MODE;
|
||||
|
||||
if (ax8817x_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE,
|
||||
opt, 0, 0, &buf) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ax8817x_get_eeprom_len(struct net_device *net)
|
||||
{
|
||||
return AX_EEPROM_LEN;
|
||||
}
|
||||
|
||||
static int ax8817x_get_eeprom(struct net_device *net,
|
||||
struct ethtool_eeprom *eeprom, u8 *data)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
u16 *ebuf = (u16 *)data;
|
||||
int i;
|
||||
|
||||
/* Crude hack to ensure that we don't overwrite memory
|
||||
* if an odd length is supplied
|
||||
*/
|
||||
if (eeprom->len % 2)
|
||||
return -EINVAL;
|
||||
|
||||
eeprom->magic = AX_EEPROM_MAGIC;
|
||||
|
||||
/* ax8817x returns 2 bytes from eeprom on read */
|
||||
for (i=0; i < eeprom->len / 2; i++) {
|
||||
if (ax8817x_read_cmd(dev, AX_CMD_READ_EEPROM,
|
||||
eeprom->offset + i, 0, 2, &ebuf[i]) < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ax8817x_get_drvinfo (struct net_device *net,
|
||||
struct ethtool_drvinfo *info)
|
||||
{
|
||||
/* Inherit standard device info */
|
||||
usbnet_get_drvinfo(net, info);
|
||||
info->eedump_len = 0x3e;
|
||||
}
|
||||
|
||||
static int ax8817x_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
|
||||
return mii_ethtool_gset(&dev->mii,cmd);
|
||||
}
|
||||
|
||||
static int ax8817x_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
|
||||
return mii_ethtool_sset(&dev->mii,cmd);
|
||||
}
|
||||
|
||||
/* We need to override some ethtool_ops so we require our
|
||||
own structure so we don't interfere with other usbnet
|
||||
devices that may be connected at the same time. */
|
||||
static struct ethtool_ops ax8817x_ethtool_ops = {
|
||||
.get_drvinfo = ax8817x_get_drvinfo,
|
||||
.get_link = ethtool_op_get_link,
|
||||
.get_msglevel = usbnet_get_msglevel,
|
||||
.set_msglevel = usbnet_set_msglevel,
|
||||
.get_wol = ax8817x_get_wol,
|
||||
.set_wol = ax8817x_set_wol,
|
||||
.get_eeprom_len = ax8817x_get_eeprom_len,
|
||||
.get_eeprom = ax8817x_get_eeprom,
|
||||
.get_settings = ax8817x_get_settings,
|
||||
.set_settings = ax8817x_set_settings,
|
||||
};
|
||||
|
||||
static int ax8817x_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
|
||||
{
|
||||
struct usbnet *dev = netdev_priv(net);
|
||||
|
||||
return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
|
||||
}
|
||||
|
||||
static int ax8817x_bind(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
int ret = 0;
|
||||
void *buf;
|
||||
int i;
|
||||
unsigned long gpio_bits = dev->driver_info->data;
|
||||
|
||||
usbnet_get_endpoints(dev,intf);
|
||||
|
||||
buf = kmalloc(ETH_ALEN, GFP_KERNEL);
|
||||
if(!buf) {
|
||||
ret = -ENOMEM;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
/* Toggle the GPIOs in a manufacturer/model specific way */
|
||||
for (i = 2; i >= 0; i--) {
|
||||
if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
|
||||
(gpio_bits >> (i * 8)) & 0xff, 0, 0,
|
||||
buf)) < 0)
|
||||
goto out2;
|
||||
msleep(5);
|
||||
}
|
||||
|
||||
if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL,
|
||||
0x80, 0, 0, buf)) < 0) {
|
||||
dbg("send AX_CMD_WRITE_RX_CTL failed: %d", ret);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
/* Get the MAC address */
|
||||
memset(buf, 0, ETH_ALEN);
|
||||
if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_NODE_ID,
|
||||
0, 0, 6, buf)) < 0) {
|
||||
dbg("read AX_CMD_READ_NODE_ID failed: %d", ret);
|
||||
goto out2;
|
||||
}
|
||||
memcpy(dev->net->dev_addr, buf, ETH_ALEN);
|
||||
|
||||
/* Get the PHY id */
|
||||
if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID,
|
||||
0, 0, 2, buf)) < 0) {
|
||||
dbg("error on read AX_CMD_READ_PHY_ID: %02x", ret);
|
||||
goto out2;
|
||||
} else if (ret < 2) {
|
||||
/* this should always return 2 bytes */
|
||||
dbg("AX_CMD_READ_PHY_ID returned less than 2 bytes: ret=%02x",
|
||||
ret);
|
||||
ret = -EIO;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
/* Initialize MII structure */
|
||||
dev->mii.dev = dev->net;
|
||||
dev->mii.mdio_read = ax8817x_mdio_read;
|
||||
dev->mii.mdio_write = ax8817x_mdio_write;
|
||||
dev->mii.phy_id_mask = 0x3f;
|
||||
dev->mii.reg_num_mask = 0x1f;
|
||||
dev->mii.phy_id = *((u8 *)buf + 1);
|
||||
dev->net->do_ioctl = ax8817x_ioctl;
|
||||
|
||||
dev->net->set_multicast_list = ax8817x_set_multicast;
|
||||
dev->net->ethtool_ops = &ax8817x_ethtool_ops;
|
||||
|
||||
ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
|
||||
ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
|
||||
ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
|
||||
mii_nway_restart(&dev->mii);
|
||||
|
||||
return 0;
|
||||
out2:
|
||||
kfree(buf);
|
||||
out1:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct ethtool_ops ax88772_ethtool_ops = {
|
||||
.get_drvinfo = ax8817x_get_drvinfo,
|
||||
.get_link = ethtool_op_get_link,
|
||||
.get_msglevel = usbnet_get_msglevel,
|
||||
.set_msglevel = usbnet_set_msglevel,
|
||||
.get_wol = ax8817x_get_wol,
|
||||
.set_wol = ax8817x_set_wol,
|
||||
.get_eeprom_len = ax8817x_get_eeprom_len,
|
||||
.get_eeprom = ax8817x_get_eeprom,
|
||||
.get_settings = ax8817x_get_settings,
|
||||
.set_settings = ax8817x_set_settings,
|
||||
};
|
||||
|
||||
static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
int ret;
|
||||
void *buf;
|
||||
|
||||
usbnet_get_endpoints(dev,intf);
|
||||
|
||||
buf = kmalloc(6, GFP_KERNEL);
|
||||
if(!buf) {
|
||||
dbg ("Cannot allocate memory for buffer");
|
||||
ret = -ENOMEM;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
|
||||
0x00B0, 0, 0, buf)) < 0)
|
||||
goto out2;
|
||||
|
||||
msleep(5);
|
||||
if ((ret = ax8817x_write_cmd(dev, AX_CMD_SW_PHY_SELECT,
|
||||
0x0001, 0, 0, buf)) < 0) {
|
||||
dbg("Select PHY #1 failed: %d", ret);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
if ((ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPPD,
|
||||
0, 0, buf)) < 0) {
|
||||
dbg("Failed to power down internal PHY: %d", ret);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
msleep(150);
|
||||
if ((ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_CLEAR,
|
||||
0, 0, buf)) < 0) {
|
||||
dbg("Failed to perform software reset: %d", ret);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
msleep(150);
|
||||
if ((ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
|
||||
AX_SWRESET_IPRL | AX_SWRESET_PRL,
|
||||
0, 0, buf)) < 0) {
|
||||
dbg("Failed to set Internal/External PHY reset control: %d",
|
||||
ret);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
msleep(150);
|
||||
if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL,
|
||||
0x0000, 0, 0, buf)) < 0) {
|
||||
dbg("Failed to reset RX_CTL: %d", ret);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
/* Get the MAC address */
|
||||
memset(buf, 0, ETH_ALEN);
|
||||
if ((ret = ax8817x_read_cmd(dev, AX88772_CMD_READ_NODE_ID,
|
||||
0, 0, ETH_ALEN, buf)) < 0) {
|
||||
dbg("Failed to read MAC address: %d", ret);
|
||||
goto out2;
|
||||
}
|
||||
memcpy(dev->net->dev_addr, buf, ETH_ALEN);
|
||||
|
||||
if ((ret = ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII,
|
||||
0, 0, 0, buf)) < 0) {
|
||||
dbg("Enabling software MII failed: %d", ret);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
if (((ret = ax8817x_read_cmd(dev, AX_CMD_READ_MII_REG,
|
||||
0x0010, 2, 2, buf)) < 0)
|
||||
|| (*((u16 *)buf) != 0x003b)) {
|
||||
dbg("Read PHY register 2 must be 0x3b00: %d", ret);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
/* Initialize MII structure */
|
||||
dev->mii.dev = dev->net;
|
||||
dev->mii.mdio_read = ax8817x_mdio_read;
|
||||
dev->mii.mdio_write = ax8817x_mdio_write;
|
||||
dev->mii.phy_id_mask = 0xff;
|
||||
dev->mii.reg_num_mask = 0xff;
|
||||
dev->net->do_ioctl = ax8817x_ioctl;
|
||||
|
||||
/* Get the PHY id */
|
||||
if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID,
|
||||
0, 0, 2, buf)) < 0) {
|
||||
dbg("Error reading PHY ID: %02x", ret);
|
||||
goto out2;
|
||||
} else if (ret < 2) {
|
||||
/* this should always return 2 bytes */
|
||||
dbg("AX_CMD_READ_PHY_ID returned less than 2 bytes: ret=%02x",
|
||||
ret);
|
||||
ret = -EIO;
|
||||
goto out2;
|
||||
}
|
||||
dev->mii.phy_id = *((u8 *)buf + 1);
|
||||
|
||||
if ((ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_PRL,
|
||||
0, 0, buf)) < 0) {
|
||||
dbg("Set external PHY reset pin level: %d", ret);
|
||||
goto out2;
|
||||
}
|
||||
msleep(150);
|
||||
if ((ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
|
||||
AX_SWRESET_IPRL | AX_SWRESET_PRL,
|
||||
0, 0, buf)) < 0) {
|
||||
dbg("Set Internal/External PHY reset control: %d", ret);
|
||||
goto out2;
|
||||
}
|
||||
msleep(150);
|
||||
|
||||
|
||||
dev->net->set_multicast_list = ax8817x_set_multicast;
|
||||
dev->net->ethtool_ops = &ax88772_ethtool_ops;
|
||||
|
||||
ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
|
||||
ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
|
||||
ADVERTISE_ALL | ADVERTISE_CSMA);
|
||||
mii_nway_restart(&dev->mii);
|
||||
|
||||
if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
|
||||
AX88772_MEDIUM_DEFAULT, 0, 0, buf)) < 0) {
|
||||
dbg("Write medium mode register: %d", ret);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_IPG0,
|
||||
AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
|
||||
AX88772_IPG2_DEFAULT, 0, buf)) < 0) {
|
||||
dbg("Write IPG,IPG1,IPG2 failed: %d", ret);
|
||||
goto out2;
|
||||
}
|
||||
if ((ret =
|
||||
ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf)) < 0) {
|
||||
dbg("Failed to set hardware MII: %02x", ret);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
/* Set RX_CTL to default values with 2k buffer, and enable cactus */
|
||||
if ((ret =
|
||||
ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x0088, 0, 0,
|
||||
buf)) < 0) {
|
||||
dbg("Reset RX_CTL failed: %d", ret);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
|
||||
/* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
|
||||
if (dev->driver_info->flags & FLAG_FRAMING_AX) {
|
||||
/* hard_mtu is still the default - the device does not support
|
||||
jumbo eth frames */
|
||||
dev->rx_urb_size = 2048;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out2:
|
||||
kfree(buf);
|
||||
out1:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ax88772_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
|
||||
{
|
||||
u8 *head;
|
||||
u32 header;
|
||||
char *packet;
|
||||
struct sk_buff *ax_skb;
|
||||
u16 size;
|
||||
|
||||
head = (u8 *) skb->data;
|
||||
memcpy(&header, head, sizeof(header));
|
||||
le32_to_cpus(&header);
|
||||
packet = head + sizeof(header);
|
||||
|
||||
skb_pull(skb, 4);
|
||||
|
||||
while (skb->len > 0) {
|
||||
if ((short)(header & 0x0000ffff) !=
|
||||
~((short)((header & 0xffff0000) >> 16))) {
|
||||
devdbg(dev,"header length data is error");
|
||||
}
|
||||
/* get the packet length */
|
||||
size = (u16) (header & 0x0000ffff);
|
||||
|
||||
if ((skb->len) - ((size + 1) & 0xfffe) == 0)
|
||||
return 2;
|
||||
if (size > ETH_FRAME_LEN) {
|
||||
devdbg(dev,"invalid rx length %d", size);
|
||||
return 0;
|
||||
}
|
||||
ax_skb = skb_clone(skb, GFP_ATOMIC);
|
||||
if (ax_skb) {
|
||||
ax_skb->len = size;
|
||||
ax_skb->data = packet;
|
||||
ax_skb->tail = packet + size;
|
||||
usbnet_skb_return(dev, ax_skb);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
skb_pull(skb, (size + 1) & 0xfffe);
|
||||
|
||||
if (skb->len == 0)
|
||||
break;
|
||||
|
||||
head = (u8 *) skb->data;
|
||||
memcpy(&header, head, sizeof(header));
|
||||
le32_to_cpus(&header);
|
||||
packet = head + sizeof(header);
|
||||
skb_pull(skb, 4);
|
||||
}
|
||||
|
||||
if (skb->len < 0) {
|
||||
devdbg(dev,"invalid rx length %d", skb->len);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct sk_buff *ax88772_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
|
||||
unsigned flags)
|
||||
{
|
||||
int padlen;
|
||||
int headroom = skb_headroom(skb);
|
||||
int tailroom = skb_tailroom(skb);
|
||||
u32 packet_len;
|
||||
u32 padbytes = 0xffff0000;
|
||||
|
||||
padlen = ((skb->len + 4) % 512) ? 0 : 4;
|
||||
|
||||
if ((!skb_cloned(skb))
|
||||
&& ((headroom + tailroom) >= (4 + padlen))) {
|
||||
if ((headroom < 4) || (tailroom < padlen)) {
|
||||
skb->data = memmove(skb->head + 4, skb->data, skb->len);
|
||||
skb->tail = skb->data + skb->len;
|
||||
}
|
||||
} else {
|
||||
struct sk_buff *skb2;
|
||||
skb2 = skb_copy_expand(skb, 4, padlen, flags);
|
||||
dev_kfree_skb_any(skb);
|
||||
skb = skb2;
|
||||
if (!skb)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
skb_push(skb, 4);
|
||||
packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4);
|
||||
memcpy(skb->data, &packet_len, sizeof(packet_len));
|
||||
|
||||
if ((skb->len % 512) == 0) {
|
||||
memcpy( skb->tail, &padbytes, sizeof(padbytes));
|
||||
skb_put(skb, sizeof(padbytes));
|
||||
}
|
||||
return skb;
|
||||
}
|
||||
|
||||
static int ax88772_link_reset(struct usbnet *dev)
|
||||
{
|
||||
u16 lpa;
|
||||
u16 adv;
|
||||
u16 res;
|
||||
u16 mode;
|
||||
|
||||
mode = AX88772_MEDIUM_DEFAULT;
|
||||
lpa = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_LPA);
|
||||
adv = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
|
||||
res = mii_nway_result(lpa|adv);
|
||||
|
||||
if ((res & LPA_DUPLEX) == 0)
|
||||
mode &= ~AX88772_MEDIUM_FULL_DUPLEX;
|
||||
if ((res & LPA_100) == 0)
|
||||
mode &= ~AX88772_MEDIUM_100MB;
|
||||
ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct driver_info ax8817x_info = {
|
||||
.description = "ASIX AX8817x USB 2.0 Ethernet",
|
||||
.bind = ax8817x_bind,
|
||||
.status = ax8817x_status,
|
||||
.link_reset = ax88172_link_reset,
|
||||
.reset = ax88172_link_reset,
|
||||
.flags = FLAG_ETHER,
|
||||
.data = 0x00130103,
|
||||
};
|
||||
|
||||
static const struct driver_info dlink_dub_e100_info = {
|
||||
.description = "DLink DUB-E100 USB Ethernet",
|
||||
.bind = ax8817x_bind,
|
||||
.status = ax8817x_status,
|
||||
.link_reset = ax88172_link_reset,
|
||||
.reset = ax88172_link_reset,
|
||||
.flags = FLAG_ETHER,
|
||||
.data = 0x009f9d9f,
|
||||
};
|
||||
|
||||
static const struct driver_info netgear_fa120_info = {
|
||||
.description = "Netgear FA-120 USB Ethernet",
|
||||
.bind = ax8817x_bind,
|
||||
.status = ax8817x_status,
|
||||
.link_reset = ax88172_link_reset,
|
||||
.reset = ax88172_link_reset,
|
||||
.flags = FLAG_ETHER,
|
||||
.data = 0x00130103,
|
||||
};
|
||||
|
||||
static const struct driver_info hawking_uf200_info = {
|
||||
.description = "Hawking UF200 USB Ethernet",
|
||||
.bind = ax8817x_bind,
|
||||
.status = ax8817x_status,
|
||||
.link_reset = ax88172_link_reset,
|
||||
.reset = ax88172_link_reset,
|
||||
.flags = FLAG_ETHER,
|
||||
.data = 0x001f1d1f,
|
||||
};
|
||||
|
||||
static const struct driver_info ax88772_info = {
|
||||
.description = "ASIX AX88772 USB 2.0 Ethernet",
|
||||
.bind = ax88772_bind,
|
||||
.status = ax8817x_status,
|
||||
.link_reset = ax88772_link_reset,
|
||||
.reset = ax88772_link_reset,
|
||||
.flags = FLAG_ETHER | FLAG_FRAMING_AX,
|
||||
.rx_fixup = ax88772_rx_fixup,
|
||||
.tx_fixup = ax88772_tx_fixup,
|
||||
.data = 0x00130103,
|
||||
};
|
||||
|
||||
static const struct usb_device_id products [] = {
|
||||
{
|
||||
// Linksys USB200M
|
||||
USB_DEVICE (0x077b, 0x2226),
|
||||
.driver_info = (unsigned long) &ax8817x_info,
|
||||
}, {
|
||||
// Netgear FA120
|
||||
USB_DEVICE (0x0846, 0x1040),
|
||||
.driver_info = (unsigned long) &netgear_fa120_info,
|
||||
}, {
|
||||
// DLink DUB-E100
|
||||
USB_DEVICE (0x2001, 0x1a00),
|
||||
.driver_info = (unsigned long) &dlink_dub_e100_info,
|
||||
}, {
|
||||
// Intellinet, ST Lab USB Ethernet
|
||||
USB_DEVICE (0x0b95, 0x1720),
|
||||
.driver_info = (unsigned long) &ax8817x_info,
|
||||
}, {
|
||||
// Hawking UF200, TrendNet TU2-ET100
|
||||
USB_DEVICE (0x07b8, 0x420a),
|
||||
.driver_info = (unsigned long) &hawking_uf200_info,
|
||||
}, {
|
||||
// Billionton Systems, USB2AR
|
||||
USB_DEVICE (0x08dd, 0x90ff),
|
||||
.driver_info = (unsigned long) &ax8817x_info,
|
||||
}, {
|
||||
// ATEN UC210T
|
||||
USB_DEVICE (0x0557, 0x2009),
|
||||
.driver_info = (unsigned long) &ax8817x_info,
|
||||
}, {
|
||||
// Buffalo LUA-U2-KTX
|
||||
USB_DEVICE (0x0411, 0x003d),
|
||||
.driver_info = (unsigned long) &ax8817x_info,
|
||||
}, {
|
||||
// Sitecom LN-029 "USB 2.0 10/100 Ethernet adapter"
|
||||
USB_DEVICE (0x6189, 0x182d),
|
||||
.driver_info = (unsigned long) &ax8817x_info,
|
||||
}, {
|
||||
// corega FEther USB2-TX
|
||||
USB_DEVICE (0x07aa, 0x0017),
|
||||
.driver_info = (unsigned long) &ax8817x_info,
|
||||
}, {
|
||||
// Surecom EP-1427X-2
|
||||
USB_DEVICE (0x1189, 0x0893),
|
||||
.driver_info = (unsigned long) &ax8817x_info,
|
||||
}, {
|
||||
// goodway corp usb gwusb2e
|
||||
USB_DEVICE (0x1631, 0x6200),
|
||||
.driver_info = (unsigned long) &ax8817x_info,
|
||||
}, {
|
||||
// ASIX AX88772 10/100
|
||||
USB_DEVICE (0x0b95, 0x7720),
|
||||
.driver_info = (unsigned long) &ax88772_info,
|
||||
},
|
||||
{ }, // END
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, products);
|
||||
|
||||
static struct usb_driver asix_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "asix",
|
||||
.id_table = products,
|
||||
.probe = usbnet_probe,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
.disconnect = usbnet_disconnect,
|
||||
};
|
||||
|
||||
static int __init asix_init(void)
|
||||
{
|
||||
return usb_register(&asix_driver);
|
||||
}
|
||||
module_init(asix_init);
|
||||
|
||||
static void __exit asix_exit(void)
|
||||
{
|
||||
usb_deregister(&asix_driver);
|
||||
}
|
||||
module_exit(asix_exit);
|
||||
|
||||
MODULE_AUTHOR("David Hollis");
|
||||
MODULE_DESCRIPTION("ASIX AX8817X based USB 2.0 Ethernet Devices");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -383,7 +383,6 @@ static void catc_tx_done(struct urb *urb, struct pt_regs *regs)
|
|||
|
||||
if (urb->status == -ECONNRESET) {
|
||||
dbg("Tx Reset.");
|
||||
urb->transfer_flags &= ~URB_ASYNC_UNLINK;
|
||||
urb->status = 0;
|
||||
catc->netdev->trans_start = jiffies;
|
||||
catc->stats.tx_errors++;
|
||||
|
@ -445,7 +444,6 @@ static void catc_tx_timeout(struct net_device *netdev)
|
|||
struct catc *catc = netdev_priv(netdev);
|
||||
|
||||
warn("Transmit timed out.");
|
||||
catc->tx_urb->transfer_flags |= URB_ASYNC_UNLINK;
|
||||
usb_unlink_urb(catc->tx_urb);
|
||||
}
|
||||
|
||||
|
|
509
drivers/usb/net/cdc_ether.c
Normal file
509
drivers/usb/net/cdc_ether.c
Normal file
|
@ -0,0 +1,509 @@
|
|||
/*
|
||||
* CDC Ethernet based networking peripherals
|
||||
* Copyright (C) 2003-2005 by David Brownell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
// #define DEBUG // error path messages, extra info
|
||||
// #define VERBOSE // more; success messages
|
||||
|
||||
#include <linux/config.h>
|
||||
#ifdef CONFIG_USB_DEBUG
|
||||
# define DEBUG
|
||||
#endif
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb_cdc.h>
|
||||
|
||||
#include "usbnet.h"
|
||||
|
||||
|
||||
/*
|
||||
* probes control interface, claims data interface, collects the bulk
|
||||
* endpoints, activates data interface (if needed), maybe sets MTU.
|
||||
* all pure cdc, except for certain firmware workarounds, and knowing
|
||||
* that rndis uses one different rule.
|
||||
*/
|
||||
int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
u8 *buf = intf->cur_altsetting->extra;
|
||||
int len = intf->cur_altsetting->extralen;
|
||||
struct usb_interface_descriptor *d;
|
||||
struct cdc_state *info = (void *) &dev->data;
|
||||
int status;
|
||||
int rndis;
|
||||
struct usb_driver *driver = driver_of(intf);
|
||||
|
||||
if (sizeof dev->data < sizeof *info)
|
||||
return -EDOM;
|
||||
|
||||
/* expect strict spec conformance for the descriptors, but
|
||||
* cope with firmware which stores them in the wrong place
|
||||
*/
|
||||
if (len == 0 && dev->udev->actconfig->extralen) {
|
||||
/* Motorola SB4100 (and others: Brad Hards says it's
|
||||
* from a Broadcom design) put CDC descriptors here
|
||||
*/
|
||||
buf = dev->udev->actconfig->extra;
|
||||
len = dev->udev->actconfig->extralen;
|
||||
if (len)
|
||||
dev_dbg(&intf->dev,
|
||||
"CDC descriptors on config\n");
|
||||
}
|
||||
|
||||
/* this assumes that if there's a non-RNDIS vendor variant
|
||||
* of cdc-acm, it'll fail RNDIS requests cleanly.
|
||||
*/
|
||||
rndis = (intf->cur_altsetting->desc.bInterfaceProtocol == 0xff);
|
||||
|
||||
memset(info, 0, sizeof *info);
|
||||
info->control = intf;
|
||||
while (len > 3) {
|
||||
if (buf [1] != USB_DT_CS_INTERFACE)
|
||||
goto next_desc;
|
||||
|
||||
/* use bDescriptorSubType to identify the CDC descriptors.
|
||||
* We expect devices with CDC header and union descriptors.
|
||||
* For CDC Ethernet we need the ethernet descriptor.
|
||||
* For RNDIS, ignore two (pointless) CDC modem descriptors
|
||||
* in favor of a complicated OID-based RPC scheme doing what
|
||||
* CDC Ethernet achieves with a simple descriptor.
|
||||
*/
|
||||
switch (buf [2]) {
|
||||
case USB_CDC_HEADER_TYPE:
|
||||
if (info->header) {
|
||||
dev_dbg(&intf->dev, "extra CDC header\n");
|
||||
goto bad_desc;
|
||||
}
|
||||
info->header = (void *) buf;
|
||||
if (info->header->bLength != sizeof *info->header) {
|
||||
dev_dbg(&intf->dev, "CDC header len %u\n",
|
||||
info->header->bLength);
|
||||
goto bad_desc;
|
||||
}
|
||||
break;
|
||||
case USB_CDC_UNION_TYPE:
|
||||
if (info->u) {
|
||||
dev_dbg(&intf->dev, "extra CDC union\n");
|
||||
goto bad_desc;
|
||||
}
|
||||
info->u = (void *) buf;
|
||||
if (info->u->bLength != sizeof *info->u) {
|
||||
dev_dbg(&intf->dev, "CDC union len %u\n",
|
||||
info->u->bLength);
|
||||
goto bad_desc;
|
||||
}
|
||||
|
||||
/* we need a master/control interface (what we're
|
||||
* probed with) and a slave/data interface; union
|
||||
* descriptors sort this all out.
|
||||
*/
|
||||
info->control = usb_ifnum_to_if(dev->udev,
|
||||
info->u->bMasterInterface0);
|
||||
info->data = usb_ifnum_to_if(dev->udev,
|
||||
info->u->bSlaveInterface0);
|
||||
if (!info->control || !info->data) {
|
||||
dev_dbg(&intf->dev,
|
||||
"master #%u/%p slave #%u/%p\n",
|
||||
info->u->bMasterInterface0,
|
||||
info->control,
|
||||
info->u->bSlaveInterface0,
|
||||
info->data);
|
||||
goto bad_desc;
|
||||
}
|
||||
if (info->control != intf) {
|
||||
dev_dbg(&intf->dev, "bogus CDC Union\n");
|
||||
/* Ambit USB Cable Modem (and maybe others)
|
||||
* interchanges master and slave interface.
|
||||
*/
|
||||
if (info->data == intf) {
|
||||
info->data = info->control;
|
||||
info->control = intf;
|
||||
} else
|
||||
goto bad_desc;
|
||||
}
|
||||
|
||||
/* a data interface altsetting does the real i/o */
|
||||
d = &info->data->cur_altsetting->desc;
|
||||
if (d->bInterfaceClass != USB_CLASS_CDC_DATA) {
|
||||
dev_dbg(&intf->dev, "slave class %u\n",
|
||||
d->bInterfaceClass);
|
||||
goto bad_desc;
|
||||
}
|
||||
break;
|
||||
case USB_CDC_ETHERNET_TYPE:
|
||||
if (info->ether) {
|
||||
dev_dbg(&intf->dev, "extra CDC ether\n");
|
||||
goto bad_desc;
|
||||
}
|
||||
info->ether = (void *) buf;
|
||||
if (info->ether->bLength != sizeof *info->ether) {
|
||||
dev_dbg(&intf->dev, "CDC ether len %u\n",
|
||||
info->ether->bLength);
|
||||
goto bad_desc;
|
||||
}
|
||||
dev->hard_mtu = le16_to_cpu(
|
||||
info->ether->wMaxSegmentSize);
|
||||
/* because of Zaurus, we may be ignoring the host
|
||||
* side link address we were given.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
next_desc:
|
||||
len -= buf [0]; /* bLength */
|
||||
buf += buf [0];
|
||||
}
|
||||
|
||||
if (!info->header || !info->u || (!rndis && !info->ether)) {
|
||||
dev_dbg(&intf->dev, "missing cdc %s%s%sdescriptor\n",
|
||||
info->header ? "" : "header ",
|
||||
info->u ? "" : "union ",
|
||||
info->ether ? "" : "ether ");
|
||||
goto bad_desc;
|
||||
}
|
||||
|
||||
/* claim data interface and set it up ... with side effects.
|
||||
* network traffic can't flow until an altsetting is enabled.
|
||||
*/
|
||||
status = usb_driver_claim_interface(driver, info->data, dev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
status = usbnet_get_endpoints(dev, info->data);
|
||||
if (status < 0) {
|
||||
/* ensure immediate exit from usbnet_disconnect */
|
||||
usb_set_intfdata(info->data, NULL);
|
||||
usb_driver_release_interface(driver, info->data);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* status endpoint: optional for CDC Ethernet, not RNDIS (or ACM) */
|
||||
dev->status = NULL;
|
||||
if (info->control->cur_altsetting->desc.bNumEndpoints == 1) {
|
||||
struct usb_endpoint_descriptor *desc;
|
||||
|
||||
dev->status = &info->control->cur_altsetting->endpoint [0];
|
||||
desc = &dev->status->desc;
|
||||
if (desc->bmAttributes != USB_ENDPOINT_XFER_INT
|
||||
|| !(desc->bEndpointAddress & USB_DIR_IN)
|
||||
|| (le16_to_cpu(desc->wMaxPacketSize)
|
||||
< sizeof(struct usb_cdc_notification))
|
||||
|| !desc->bInterval) {
|
||||
dev_dbg(&intf->dev, "bad notification endpoint\n");
|
||||
dev->status = NULL;
|
||||
}
|
||||
}
|
||||
if (rndis && !dev->status) {
|
||||
dev_dbg(&intf->dev, "missing RNDIS status endpoint\n");
|
||||
usb_set_intfdata(info->data, NULL);
|
||||
usb_driver_release_interface(driver, info->data);
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
|
||||
bad_desc:
|
||||
dev_info(&dev->udev->dev, "bad CDC descriptors\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbnet_generic_cdc_bind);
|
||||
|
||||
void usbnet_cdc_unbind(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
struct cdc_state *info = (void *) &dev->data;
|
||||
struct usb_driver *driver = driver_of(intf);
|
||||
|
||||
/* disconnect master --> disconnect slave */
|
||||
if (intf == info->control && info->data) {
|
||||
/* ensure immediate exit from usbnet_disconnect */
|
||||
usb_set_intfdata(info->data, NULL);
|
||||
usb_driver_release_interface(driver, info->data);
|
||||
info->data = NULL;
|
||||
}
|
||||
|
||||
/* and vice versa (just in case) */
|
||||
else if (intf == info->data && info->control) {
|
||||
/* ensure immediate exit from usbnet_disconnect */
|
||||
usb_set_intfdata(info->control, NULL);
|
||||
usb_driver_release_interface(driver, info->control);
|
||||
info->control = NULL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbnet_cdc_unbind);
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* Communications Device Class, Ethernet Control model
|
||||
*
|
||||
* Takes two interfaces. The DATA interface is inactive till an altsetting
|
||||
* is selected. Configuration data includes class descriptors. There's
|
||||
* an optional status endpoint on the control interface.
|
||||
*
|
||||
* This should interop with whatever the 2.4 "CDCEther.c" driver
|
||||
* (by Brad Hards) talked with, with more functionality.
|
||||
*
|
||||
*-------------------------------------------------------------------------*/
|
||||
|
||||
static void dumpspeed(struct usbnet *dev, __le32 *speeds)
|
||||
{
|
||||
if (netif_msg_timer(dev))
|
||||
devinfo(dev, "link speeds: %u kbps up, %u kbps down",
|
||||
__le32_to_cpu(speeds[0]) / 1000,
|
||||
__le32_to_cpu(speeds[1]) / 1000);
|
||||
}
|
||||
|
||||
static void cdc_status(struct usbnet *dev, struct urb *urb)
|
||||
{
|
||||
struct usb_cdc_notification *event;
|
||||
|
||||
if (urb->actual_length < sizeof *event)
|
||||
return;
|
||||
|
||||
/* SPEED_CHANGE can get split into two 8-byte packets */
|
||||
if (test_and_clear_bit(EVENT_STS_SPLIT, &dev->flags)) {
|
||||
dumpspeed(dev, (__le32 *) urb->transfer_buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
event = urb->transfer_buffer;
|
||||
switch (event->bNotificationType) {
|
||||
case USB_CDC_NOTIFY_NETWORK_CONNECTION:
|
||||
if (netif_msg_timer(dev))
|
||||
devdbg(dev, "CDC: carrier %s",
|
||||
event->wValue ? "on" : "off");
|
||||
if (event->wValue)
|
||||
netif_carrier_on(dev->net);
|
||||
else
|
||||
netif_carrier_off(dev->net);
|
||||
break;
|
||||
case USB_CDC_NOTIFY_SPEED_CHANGE: /* tx/rx rates */
|
||||
if (netif_msg_timer(dev))
|
||||
devdbg(dev, "CDC: speed change (len %d)",
|
||||
urb->actual_length);
|
||||
if (urb->actual_length != (sizeof *event + 8))
|
||||
set_bit(EVENT_STS_SPLIT, &dev->flags);
|
||||
else
|
||||
dumpspeed(dev, (__le32 *) &event[1]);
|
||||
break;
|
||||
/* USB_CDC_NOTIFY_RESPONSE_AVAILABLE can happen too (e.g. RNDIS),
|
||||
* but there are no standard formats for the response data.
|
||||
*/
|
||||
default:
|
||||
deverr(dev, "CDC: unexpected notification %02x!",
|
||||
event->bNotificationType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static u8 nibble(unsigned char c)
|
||||
{
|
||||
if (likely(isdigit(c)))
|
||||
return c - '0';
|
||||
c = toupper(c);
|
||||
if (likely(isxdigit(c)))
|
||||
return 10 + c - 'A';
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
get_ethernet_addr(struct usbnet *dev, struct usb_cdc_ether_desc *e)
|
||||
{
|
||||
int tmp, i;
|
||||
unsigned char buf [13];
|
||||
|
||||
tmp = usb_string(dev->udev, e->iMACAddress, buf, sizeof buf);
|
||||
if (tmp != 12) {
|
||||
dev_dbg(&dev->udev->dev,
|
||||
"bad MAC string %d fetch, %d\n", e->iMACAddress, tmp);
|
||||
if (tmp >= 0)
|
||||
tmp = -EINVAL;
|
||||
return tmp;
|
||||
}
|
||||
for (i = tmp = 0; i < 6; i++, tmp += 2)
|
||||
dev->net->dev_addr [i] =
|
||||
(nibble(buf [tmp]) << 4) + nibble(buf [tmp + 1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdc_bind(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
int status;
|
||||
struct cdc_state *info = (void *) &dev->data;
|
||||
|
||||
status = usbnet_generic_cdc_bind(dev, intf);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
status = get_ethernet_addr(dev, info->ether);
|
||||
if (status < 0) {
|
||||
usb_set_intfdata(info->data, NULL);
|
||||
usb_driver_release_interface(driver_of(intf), info->data);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* FIXME cdc-ether has some multicast code too, though it complains
|
||||
* in routine cases. info->ether describes the multicast support.
|
||||
* Implement that here, manipulating the cdc filter as needed.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct driver_info cdc_info = {
|
||||
.description = "CDC Ethernet Device",
|
||||
.flags = FLAG_ETHER,
|
||||
// .check_connect = cdc_check_connect,
|
||||
.bind = cdc_bind,
|
||||
.unbind = usbnet_cdc_unbind,
|
||||
.status = cdc_status,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
static const struct usb_device_id products [] = {
|
||||
/*
|
||||
* BLACKLIST !!
|
||||
*
|
||||
* First blacklist any products that are egregiously nonconformant
|
||||
* with the CDC Ethernet specs. Minor braindamage we cope with; when
|
||||
* they're not even trying, needing a separate driver is only the first
|
||||
* of the differences to show up.
|
||||
*/
|
||||
|
||||
#define ZAURUS_MASTER_INTERFACE \
|
||||
.bInterfaceClass = USB_CLASS_COMM, \
|
||||
.bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, \
|
||||
.bInterfaceProtocol = USB_CDC_PROTO_NONE
|
||||
|
||||
/* SA-1100 based Sharp Zaurus ("collie"), or compatible;
|
||||
* wire-incompatible with true CDC Ethernet implementations.
|
||||
* (And, it seems, needlessly so...)
|
||||
*/
|
||||
{
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
.idProduct = 0x8004,
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = 0,
|
||||
},
|
||||
|
||||
/* PXA-25x based Sharp Zaurii. Note that it seems some of these
|
||||
* (later models especially) may have shipped only with firmware
|
||||
* advertising false "CDC MDLM" compatibility ... but we're not
|
||||
* clear which models did that, so for now let's assume the worst.
|
||||
*/
|
||||
{
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
.idProduct = 0x8005, /* A-300 */
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = 0,
|
||||
}, {
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
.idProduct = 0x8006, /* B-500/SL-5600 */
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = 0,
|
||||
}, {
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
.idProduct = 0x8007, /* C-700 */
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = 0,
|
||||
}, {
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
.idProduct = 0x9031, /* C-750 C-760 */
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = 0,
|
||||
}, {
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
.idProduct = 0x9032, /* SL-6000 */
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = 0,
|
||||
}, {
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
/* reported with some C860 units */
|
||||
.idProduct = 0x9050, /* C-860 */
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = 0,
|
||||
},
|
||||
|
||||
/*
|
||||
* WHITELIST!!!
|
||||
*
|
||||
* CDC Ether uses two interfaces, not necessarily consecutive.
|
||||
* We match the main interface, ignoring the optional device
|
||||
* class so we could handle devices that aren't exclusively
|
||||
* CDC ether.
|
||||
*
|
||||
* NOTE: this match must come AFTER entries blacklisting devices
|
||||
* because of bugs/quirks in a given product (like Zaurus, above).
|
||||
*/
|
||||
{
|
||||
USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET,
|
||||
USB_CDC_PROTO_NONE),
|
||||
.driver_info = (unsigned long) &cdc_info,
|
||||
},
|
||||
{ }, // END
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, products);
|
||||
|
||||
static struct usb_driver cdc_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "cdc_ether",
|
||||
.id_table = products,
|
||||
.probe = usbnet_probe,
|
||||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
};
|
||||
|
||||
|
||||
static int __init cdc_init(void)
|
||||
{
|
||||
BUG_ON((sizeof(((struct usbnet *)0)->data)
|
||||
< sizeof(struct cdc_state)));
|
||||
|
||||
return usb_register(&cdc_driver);
|
||||
}
|
||||
module_init(cdc_init);
|
||||
|
||||
static void __exit cdc_exit(void)
|
||||
{
|
||||
usb_deregister(&cdc_driver);
|
||||
}
|
||||
module_exit(cdc_exit);
|
||||
|
||||
MODULE_AUTHOR("David Brownell");
|
||||
MODULE_DESCRIPTION("USB CDC Ethernet devices");
|
||||
MODULE_LICENSE("GPL");
|
335
drivers/usb/net/cdc_subset.c
Normal file
335
drivers/usb/net/cdc_subset.c
Normal file
|
@ -0,0 +1,335 @@
|
|||
/*
|
||||
* Simple "CDC Subset" USB Networking Links
|
||||
* Copyright (C) 2000-2005 by David Brownell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#ifdef CONFIG_USB_DEBUG
|
||||
# define DEBUG
|
||||
#endif
|
||||
#include <linux/module.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "usbnet.h"
|
||||
|
||||
|
||||
/*
|
||||
* This supports simple USB network links that don't require any special
|
||||
* framing or hardware control operations. The protocol used here is a
|
||||
* strict subset of CDC Ethernet, with three basic differences reflecting
|
||||
* the goal that almost any hardware should run it:
|
||||
*
|
||||
* - Minimal runtime control: one interface, no altsettings, and
|
||||
* no vendor or class specific control requests. If a device is
|
||||
* configured, it is allowed to exchange packets with the host.
|
||||
* Fancier models would mean not working on some hardware.
|
||||
*
|
||||
* - Minimal manufacturing control: no IEEE "Organizationally
|
||||
* Unique ID" required, or an EEPROMs to store one. Each host uses
|
||||
* one random "locally assigned" Ethernet address instead, which can
|
||||
* of course be overridden using standard tools like "ifconfig".
|
||||
* (With 2^46 such addresses, same-net collisions are quite rare.)
|
||||
*
|
||||
* - There is no additional framing data for USB. Packets are written
|
||||
* exactly as in CDC Ethernet, starting with an Ethernet header and
|
||||
* terminated by a short packet. However, the host will never send a
|
||||
* zero length packet; some systems can't handle those robustly.
|
||||
*
|
||||
* Anything that can transmit and receive USB bulk packets can implement
|
||||
* this protocol. That includes both smart peripherals and quite a lot
|
||||
* of "host-to-host" USB cables (which embed two devices back-to-back).
|
||||
*
|
||||
* Note that although Linux may use many of those host-to-host links
|
||||
* with this "cdc_subset" framing, that doesn't mean there may not be a
|
||||
* better approach. Handling the "other end unplugs/replugs" scenario
|
||||
* well tends to require chip-specific vendor requests. Also, Windows
|
||||
* peers at the other end of host-to-host cables may expect their own
|
||||
* framing to be used rather than this "cdc_subset" model.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_USB_EPSON2888) || defined(CONFIG_USB_ARMLINUX)
|
||||
/* PDA style devices are always connected if present */
|
||||
static int always_connected (struct usbnet *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_ALI_M5632
|
||||
#define HAVE_HARDWARE
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* ALi M5632 driver ... does high speed
|
||||
*
|
||||
*-------------------------------------------------------------------------*/
|
||||
|
||||
static const struct driver_info ali_m5632_info = {
|
||||
.description = "ALi M5632",
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_AN2720
|
||||
#define HAVE_HARDWARE
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* AnchorChips 2720 driver ... http://www.cypress.com
|
||||
*
|
||||
* This doesn't seem to have a way to detect whether the peer is
|
||||
* connected, or need any reset handshaking. It's got pretty big
|
||||
* internal buffers (handles most of a frame's worth of data).
|
||||
* Chip data sheets don't describe any vendor control messages.
|
||||
*
|
||||
*-------------------------------------------------------------------------*/
|
||||
|
||||
static const struct driver_info an2720_info = {
|
||||
.description = "AnchorChips/Cypress 2720",
|
||||
// no reset available!
|
||||
// no check_connect available!
|
||||
|
||||
.in = 2, .out = 2, // direction distinguishes these
|
||||
};
|
||||
|
||||
#endif /* CONFIG_USB_AN2720 */
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_BELKIN
|
||||
#define HAVE_HARDWARE
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* Belkin F5U104 ... two NetChip 2280 devices + Atmel AVR microcontroller
|
||||
*
|
||||
* ... also two eTEK designs, including one sold as "Advance USBNET"
|
||||
*
|
||||
*-------------------------------------------------------------------------*/
|
||||
|
||||
static const struct driver_info belkin_info = {
|
||||
.description = "Belkin, eTEK, or compatible",
|
||||
};
|
||||
|
||||
#endif /* CONFIG_USB_BELKIN */
|
||||
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_EPSON2888
|
||||
#define HAVE_HARDWARE
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* EPSON USB clients
|
||||
*
|
||||
* This is the same idea as Linux PDAs (below) except the firmware in the
|
||||
* device might not be Tux-powered. Epson provides reference firmware that
|
||||
* implements this interface. Product developers can reuse or modify that
|
||||
* code, such as by using their own product and vendor codes.
|
||||
*
|
||||
* Support was from Juro Bystricky <bystricky.juro@erd.epson.com>
|
||||
*
|
||||
*-------------------------------------------------------------------------*/
|
||||
|
||||
static const struct driver_info epson2888_info = {
|
||||
.description = "Epson USB Device",
|
||||
.check_connect = always_connected,
|
||||
|
||||
.in = 4, .out = 3,
|
||||
};
|
||||
|
||||
#endif /* CONFIG_USB_EPSON2888 */
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_KC2190
|
||||
#define HAVE_HARDWARE
|
||||
static const struct driver_info kc2190_info = {
|
||||
.description = "KC Technology KC-190",
|
||||
};
|
||||
#endif /* CONFIG_USB_KC2190 */
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_ARMLINUX
|
||||
#define HAVE_HARDWARE
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* Intel's SA-1100 chip integrates basic USB support, and is used
|
||||
* in PDAs like some iPaqs, the Yopy, some Zaurus models, and more.
|
||||
* When they run Linux, arch/arm/mach-sa1100/usb-eth.c may be used to
|
||||
* network using minimal USB framing data.
|
||||
*
|
||||
* This describes the driver currently in standard ARM Linux kernels.
|
||||
* The Zaurus uses a different driver (see later).
|
||||
*
|
||||
* PXA25x and PXA210 use XScale cores (ARM v5TE) with better USB support
|
||||
* and different USB endpoint numbering than the SA1100 devices. The
|
||||
* mach-pxa/usb-eth.c driver re-uses the device ids from mach-sa1100
|
||||
* so we rely on the endpoint descriptors.
|
||||
*
|
||||
*-------------------------------------------------------------------------*/
|
||||
|
||||
static const struct driver_info linuxdev_info = {
|
||||
.description = "Linux Device",
|
||||
.check_connect = always_connected,
|
||||
};
|
||||
|
||||
static const struct driver_info yopy_info = {
|
||||
.description = "Yopy",
|
||||
.check_connect = always_connected,
|
||||
};
|
||||
|
||||
static const struct driver_info blob_info = {
|
||||
.description = "Boot Loader OBject",
|
||||
.check_connect = always_connected,
|
||||
};
|
||||
|
||||
#endif /* CONFIG_USB_ARMLINUX */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef HAVE_HARDWARE
|
||||
#error You need to configure some hardware for this driver
|
||||
#endif
|
||||
|
||||
/*
|
||||
* chip vendor names won't normally be on the cables, and
|
||||
* may not be on the device.
|
||||
*/
|
||||
|
||||
static const struct usb_device_id products [] = {
|
||||
|
||||
#ifdef CONFIG_USB_ALI_M5632
|
||||
{
|
||||
USB_DEVICE (0x0402, 0x5632), // ALi defaults
|
||||
.driver_info = (unsigned long) &ali_m5632_info,
|
||||
},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_AN2720
|
||||
{
|
||||
USB_DEVICE (0x0547, 0x2720), // AnchorChips defaults
|
||||
.driver_info = (unsigned long) &an2720_info,
|
||||
}, {
|
||||
USB_DEVICE (0x0547, 0x2727), // Xircom PGUNET
|
||||
.driver_info = (unsigned long) &an2720_info,
|
||||
},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_BELKIN
|
||||
{
|
||||
USB_DEVICE (0x050d, 0x0004), // Belkin
|
||||
.driver_info = (unsigned long) &belkin_info,
|
||||
}, {
|
||||
USB_DEVICE (0x056c, 0x8100), // eTEK
|
||||
.driver_info = (unsigned long) &belkin_info,
|
||||
}, {
|
||||
USB_DEVICE (0x0525, 0x9901), // Advance USBNET (eTEK)
|
||||
.driver_info = (unsigned long) &belkin_info,
|
||||
},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_EPSON2888
|
||||
{
|
||||
USB_DEVICE (0x0525, 0x2888), // EPSON USB client
|
||||
.driver_info = (unsigned long) &epson2888_info,
|
||||
},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_KC2190
|
||||
{
|
||||
USB_DEVICE (0x050f, 0x0190), // KC-190
|
||||
.driver_info = (unsigned long) &kc2190_info,
|
||||
},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_ARMLINUX
|
||||
/*
|
||||
* SA-1100 using standard ARM Linux kernels, or compatible.
|
||||
* Often used when talking to Linux PDAs (iPaq, Yopy, etc).
|
||||
* The sa-1100 "usb-eth" driver handles the basic framing.
|
||||
*
|
||||
* PXA25x or PXA210 ... these use a "usb-eth" driver much like
|
||||
* the sa1100 one, but hardware uses different endpoint numbers.
|
||||
*
|
||||
* Or the Linux "Ethernet" gadget on hardware that can't talk
|
||||
* CDC Ethernet (e.g., no altsettings), in either of two modes:
|
||||
* - acting just like the old "usb-eth" firmware, though
|
||||
* the implementation is different
|
||||
* - supporting RNDIS as the first/default configuration for
|
||||
* MS-Windows interop; Linux needs to use the other config
|
||||
*/
|
||||
{
|
||||
// 1183 = 0x049F, both used as hex values?
|
||||
// Compaq "Itsy" vendor/product id
|
||||
USB_DEVICE (0x049F, 0x505A), // usb-eth, or compatible
|
||||
.driver_info = (unsigned long) &linuxdev_info,
|
||||
}, {
|
||||
USB_DEVICE (0x0E7E, 0x1001), // G.Mate "Yopy"
|
||||
.driver_info = (unsigned long) &yopy_info,
|
||||
}, {
|
||||
USB_DEVICE (0x8086, 0x07d3), // "blob" bootloader
|
||||
.driver_info = (unsigned long) &blob_info,
|
||||
}, {
|
||||
// Linux Ethernet/RNDIS gadget on pxa210/25x/26x, second config
|
||||
// e.g. Gumstix, current OpenZaurus, ...
|
||||
USB_DEVICE_VER (0x0525, 0xa4a2, 0x0203, 0x0203),
|
||||
.driver_info = (unsigned long) &linuxdev_info,
|
||||
},
|
||||
#endif
|
||||
|
||||
{ }, // END
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, products);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct usb_driver cdc_subset_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "cdc_subset",
|
||||
.probe = usbnet_probe,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
.disconnect = usbnet_disconnect,
|
||||
.id_table = products,
|
||||
};
|
||||
|
||||
static int __init cdc_subset_init(void)
|
||||
{
|
||||
return usb_register(&cdc_subset_driver);
|
||||
}
|
||||
module_init(cdc_subset_init);
|
||||
|
||||
static void __exit cdc_subset_exit(void)
|
||||
{
|
||||
usb_deregister(&cdc_subset_driver);
|
||||
}
|
||||
module_exit(cdc_subset_exit);
|
||||
|
||||
MODULE_AUTHOR("David Brownell");
|
||||
MODULE_DESCRIPTION("Simple 'CDC Subset' USB networking links");
|
||||
MODULE_LICENSE("GPL");
|
407
drivers/usb/net/gl620a.c
Normal file
407
drivers/usb/net/gl620a.c
Normal file
|
@ -0,0 +1,407 @@
|
|||
/*
|
||||
* GeneSys GL620USB-A based links
|
||||
* Copyright (C) 2001 by Jiun-Jie Huang <huangjj@genesyslogic.com.tw>
|
||||
* Copyright (C) 2001 by Stanislav Brabec <utx@penguin.cz>
|
||||
*
|
||||
* 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 DEBUG // error path messages, extra info
|
||||
// #define VERBOSE // more; success messages
|
||||
|
||||
#include <linux/config.h>
|
||||
#ifdef CONFIG_USB_DEBUG
|
||||
# define DEBUG
|
||||
#endif
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "usbnet.h"
|
||||
|
||||
|
||||
/*
|
||||
* GeneSys GL620USB-A (www.genesyslogic.com.tw)
|
||||
*
|
||||
* ... should partially interop with the Win32 driver for this hardware.
|
||||
* The GeneSys docs imply there's some NDIS issue motivating this framing.
|
||||
*
|
||||
* Some info from GeneSys:
|
||||
* - GL620USB-A is full duplex; GL620USB is only half duplex for bulk.
|
||||
* (Some cables, like the BAFO-100c, use the half duplex version.)
|
||||
* - For the full duplex model, the low bit of the version code says
|
||||
* which side is which ("left/right").
|
||||
* - For the half duplex type, a control/interrupt handshake settles
|
||||
* the transfer direction. (That's disabled here, partially coded.)
|
||||
* A control URB would block until other side writes an interrupt.
|
||||
*
|
||||
* Original code from Jiun-Jie Huang <huangjj@genesyslogic.com.tw>
|
||||
* and merged into "usbnet" by Stanislav Brabec <utx@penguin.cz>.
|
||||
*/
|
||||
|
||||
// control msg write command
|
||||
#define GENELINK_CONNECT_WRITE 0xF0
|
||||
// interrupt pipe index
|
||||
#define GENELINK_INTERRUPT_PIPE 0x03
|
||||
// interrupt read buffer size
|
||||
#define INTERRUPT_BUFSIZE 0x08
|
||||
// interrupt pipe interval value
|
||||
#define GENELINK_INTERRUPT_INTERVAL 0x10
|
||||
// max transmit packet number per transmit
|
||||
#define GL_MAX_TRANSMIT_PACKETS 32
|
||||
// max packet length
|
||||
#define GL_MAX_PACKET_LEN 1514
|
||||
// max receive buffer size
|
||||
#define GL_RCV_BUF_SIZE \
|
||||
(((GL_MAX_PACKET_LEN + 4) * GL_MAX_TRANSMIT_PACKETS) + 4)
|
||||
|
||||
struct gl_packet {
|
||||
u32 packet_length;
|
||||
char packet_data [1];
|
||||
};
|
||||
|
||||
struct gl_header {
|
||||
u32 packet_count;
|
||||
struct gl_packet packets;
|
||||
};
|
||||
|
||||
#ifdef GENELINK_ACK
|
||||
|
||||
// FIXME: this code is incomplete, not debugged; it doesn't
|
||||
// handle interrupts correctly; it should use the generic
|
||||
// status IRQ code (which didn't exist back in 2001).
|
||||
|
||||
struct gl_priv {
|
||||
struct urb *irq_urb;
|
||||
char irq_buf [INTERRUPT_BUFSIZE];
|
||||
};
|
||||
|
||||
static inline int gl_control_write(struct usbnet *dev, u8 request, u16 value)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = usb_control_msg(dev->udev,
|
||||
usb_sndctrlpipe(dev->udev, 0),
|
||||
request,
|
||||
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
value,
|
||||
0, // index
|
||||
0, // data buffer
|
||||
0, // size
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void gl_interrupt_complete(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
int status = urb->status;
|
||||
|
||||
switch (status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dbg("%s - urb shutting down with status: %d",
|
||||
__FUNCTION__, status);
|
||||
return;
|
||||
default:
|
||||
dbg("%s - nonzero urb status received: %d",
|
||||
__FUNCTION__, urb->status);
|
||||
}
|
||||
|
||||
status = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (status)
|
||||
err("%s - usb_submit_urb failed with result %d",
|
||||
__FUNCTION__, status);
|
||||
}
|
||||
|
||||
static int gl_interrupt_read(struct usbnet *dev)
|
||||
{
|
||||
struct gl_priv *priv = dev->priv_data;
|
||||
int retval;
|
||||
|
||||
// issue usb interrupt read
|
||||
if (priv && priv->irq_urb) {
|
||||
// submit urb
|
||||
if ((retval = usb_submit_urb(priv->irq_urb, GFP_KERNEL)) != 0)
|
||||
dbg("gl_interrupt_read: submit fail - %X...", retval);
|
||||
else
|
||||
dbg("gl_interrupt_read: submit success...");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check whether another side is connected
|
||||
static int genelink_check_connect(struct usbnet *dev)
|
||||
{
|
||||
int retval;
|
||||
|
||||
dbg("genelink_check_connect...");
|
||||
|
||||
// detect whether another side is connected
|
||||
if ((retval = gl_control_write(dev, GENELINK_CONNECT_WRITE, 0)) != 0) {
|
||||
dbg("%s: genelink_check_connect write fail - %X",
|
||||
dev->net->name, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
// usb interrupt read to ack another side
|
||||
if ((retval = gl_interrupt_read(dev)) != 0) {
|
||||
dbg("%s: genelink_check_connect read fail - %X",
|
||||
dev->net->name, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
dbg("%s: genelink_check_connect read success", dev->net->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// allocate and initialize the private data for genelink
|
||||
static int genelink_init(struct usbnet *dev)
|
||||
{
|
||||
struct gl_priv *priv;
|
||||
|
||||
// allocate the private data structure
|
||||
if ((priv = kmalloc(sizeof *priv, GFP_KERNEL)) == 0) {
|
||||
dbg("%s: cannot allocate private data per device",
|
||||
dev->net->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
// allocate irq urb
|
||||
if ((priv->irq_urb = usb_alloc_urb(0, GFP_KERNEL)) == 0) {
|
||||
dbg("%s: cannot allocate private irq urb per device",
|
||||
dev->net->name);
|
||||
kfree(priv);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
// fill irq urb
|
||||
usb_fill_int_urb(priv->irq_urb, dev->udev,
|
||||
usb_rcvintpipe(dev->udev, GENELINK_INTERRUPT_PIPE),
|
||||
priv->irq_buf, INTERRUPT_BUFSIZE,
|
||||
gl_interrupt_complete, 0,
|
||||
GENELINK_INTERRUPT_INTERVAL);
|
||||
|
||||
// set private data pointer
|
||||
dev->priv_data = priv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// release the private data
|
||||
static int genelink_free(struct usbnet *dev)
|
||||
{
|
||||
struct gl_priv *priv = dev->priv_data;
|
||||
|
||||
if (!priv)
|
||||
return 0;
|
||||
|
||||
// FIXME: can't cancel here; it's synchronous, and
|
||||
// should have happened earlier in any case (interrupt
|
||||
// handling needs to be generic)
|
||||
|
||||
// cancel irq urb first
|
||||
usb_kill_urb(priv->irq_urb);
|
||||
|
||||
// free irq urb
|
||||
usb_free_urb(priv->irq_urb);
|
||||
|
||||
// free the private data structure
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int genelink_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
|
||||
{
|
||||
struct gl_header *header;
|
||||
struct gl_packet *packet;
|
||||
struct sk_buff *gl_skb;
|
||||
u32 size;
|
||||
|
||||
header = (struct gl_header *) skb->data;
|
||||
|
||||
// get the packet count of the received skb
|
||||
le32_to_cpus(&header->packet_count);
|
||||
if ((header->packet_count > GL_MAX_TRANSMIT_PACKETS)
|
||||
|| (header->packet_count < 0)) {
|
||||
dbg("genelink: invalid received packet count %d",
|
||||
header->packet_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// set the current packet pointer to the first packet
|
||||
packet = &header->packets;
|
||||
|
||||
// decrement the length for the packet count size 4 bytes
|
||||
skb_pull(skb, 4);
|
||||
|
||||
while (header->packet_count > 1) {
|
||||
// get the packet length
|
||||
size = le32_to_cpu(packet->packet_length);
|
||||
|
||||
// this may be a broken packet
|
||||
if (size > GL_MAX_PACKET_LEN) {
|
||||
dbg("genelink: invalid rx length %d", size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// allocate the skb for the individual packet
|
||||
gl_skb = alloc_skb(size, GFP_ATOMIC);
|
||||
if (gl_skb) {
|
||||
|
||||
// copy the packet data to the new skb
|
||||
memcpy(skb_put(gl_skb, size),
|
||||
packet->packet_data, size);
|
||||
usbnet_skb_return(dev, gl_skb);
|
||||
}
|
||||
|
||||
// advance to the next packet
|
||||
packet = (struct gl_packet *)
|
||||
&packet->packet_data [size];
|
||||
header->packet_count--;
|
||||
|
||||
// shift the data pointer to the next gl_packet
|
||||
skb_pull(skb, size + 4);
|
||||
}
|
||||
|
||||
// skip the packet length field 4 bytes
|
||||
skb_pull(skb, 4);
|
||||
|
||||
if (skb->len > GL_MAX_PACKET_LEN) {
|
||||
dbg("genelink: invalid rx length %d", skb->len);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct sk_buff *
|
||||
genelink_tx_fixup(struct usbnet *dev, struct sk_buff *skb, unsigned flags)
|
||||
{
|
||||
int padlen;
|
||||
int length = skb->len;
|
||||
int headroom = skb_headroom(skb);
|
||||
int tailroom = skb_tailroom(skb);
|
||||
u32 *packet_count;
|
||||
u32 *packet_len;
|
||||
|
||||
// FIXME: magic numbers, bleech
|
||||
padlen = ((skb->len + (4 + 4*1)) % 64) ? 0 : 1;
|
||||
|
||||
if ((!skb_cloned(skb))
|
||||
&& ((headroom + tailroom) >= (padlen + (4 + 4*1)))) {
|
||||
if ((headroom < (4 + 4*1)) || (tailroom < padlen)) {
|
||||
skb->data = memmove(skb->head + (4 + 4*1),
|
||||
skb->data, skb->len);
|
||||
skb->tail = skb->data + skb->len;
|
||||
}
|
||||
} else {
|
||||
struct sk_buff *skb2;
|
||||
skb2 = skb_copy_expand(skb, (4 + 4*1) , padlen, flags);
|
||||
dev_kfree_skb_any(skb);
|
||||
skb = skb2;
|
||||
if (!skb)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// attach the packet count to the header
|
||||
packet_count = (u32 *) skb_push(skb, (4 + 4*1));
|
||||
packet_len = packet_count + 1;
|
||||
|
||||
*packet_count = cpu_to_le32(1);
|
||||
*packet_len = cpu_to_le32(length);
|
||||
|
||||
// add padding byte
|
||||
if ((skb->len % dev->maxpacket) == 0)
|
||||
skb_put(skb, 1);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static int genelink_bind(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
dev->hard_mtu = GL_RCV_BUF_SIZE;
|
||||
dev->net->hard_header_len += 4;
|
||||
dev->in = usb_rcvbulkpipe(dev->udev, dev->driver_info->in);
|
||||
dev->out = usb_sndbulkpipe(dev->udev, dev->driver_info->out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct driver_info genelink_info = {
|
||||
.description = "Genesys GeneLink",
|
||||
.flags = FLAG_FRAMING_GL | FLAG_NO_SETINT,
|
||||
.bind = genelink_bind,
|
||||
.rx_fixup = genelink_rx_fixup,
|
||||
.tx_fixup = genelink_tx_fixup,
|
||||
|
||||
.in = 1, .out = 2,
|
||||
|
||||
#ifdef GENELINK_ACK
|
||||
.check_connect =genelink_check_connect,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct usb_device_id products [] = {
|
||||
|
||||
{
|
||||
USB_DEVICE(0x05e3, 0x0502), // GL620USB-A
|
||||
.driver_info = (unsigned long) &genelink_info,
|
||||
},
|
||||
/* NOT: USB_DEVICE(0x05e3, 0x0501), // GL620USB
|
||||
* that's half duplex, not currently supported
|
||||
*/
|
||||
{ }, // END
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, products);
|
||||
|
||||
static struct usb_driver gl620a_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "gl620a",
|
||||
.id_table = products,
|
||||
.probe = usbnet_probe,
|
||||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
};
|
||||
|
||||
static int __init usbnet_init(void)
|
||||
{
|
||||
return usb_register(&gl620a_driver);
|
||||
}
|
||||
module_init(usbnet_init);
|
||||
|
||||
static void __exit usbnet_exit(void)
|
||||
{
|
||||
usb_deregister(&gl620a_driver);
|
||||
}
|
||||
module_exit(usbnet_exit);
|
||||
|
||||
MODULE_AUTHOR("Jiun-Jie Huang");
|
||||
MODULE_DESCRIPTION("GL620-USB-A Host-to-Host Link cables");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -787,7 +787,6 @@ static int kaweth_start_xmit(struct sk_buff *skb, struct net_device *net)
|
|||
kaweth_usb_transmit_complete,
|
||||
kaweth);
|
||||
kaweth->end = 0;
|
||||
kaweth->tx_urb->transfer_flags |= URB_ASYNC_UNLINK;
|
||||
|
||||
if((res = usb_submit_urb(kaweth->tx_urb, GFP_ATOMIC)))
|
||||
{
|
||||
|
|
622
drivers/usb/net/net1080.c
Normal file
622
drivers/usb/net/net1080.c
Normal file
|
@ -0,0 +1,622 @@
|
|||
/*
|
||||
* Net1080 based USB host-to-host cables
|
||||
* Copyright (C) 2000-2005 by David Brownell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
// #define DEBUG // error path messages, extra info
|
||||
// #define VERBOSE // more; success messages
|
||||
|
||||
#include <linux/config.h>
|
||||
#ifdef CONFIG_USB_DEBUG
|
||||
# define DEBUG
|
||||
#endif
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "usbnet.h"
|
||||
|
||||
|
||||
/*
|
||||
* Netchip 1080 driver ... http://www.netchip.com
|
||||
* (Sept 2004: End-of-life announcement has been sent.)
|
||||
* Used in (some) LapLink cables
|
||||
*/
|
||||
|
||||
#define frame_errors data[1]
|
||||
|
||||
/*
|
||||
* NetChip framing of ethernet packets, supporting additional error
|
||||
* checks for links that may drop bulk packets from inside messages.
|
||||
* Odd USB length == always short read for last usb packet.
|
||||
* - nc_header
|
||||
* - Ethernet header (14 bytes)
|
||||
* - payload
|
||||
* - (optional padding byte, if needed so length becomes odd)
|
||||
* - nc_trailer
|
||||
*
|
||||
* This framing is to be avoided for non-NetChip devices.
|
||||
*/
|
||||
|
||||
struct nc_header { // packed:
|
||||
__le16 hdr_len; // sizeof nc_header (LE, all)
|
||||
__le16 packet_len; // payload size (including ethhdr)
|
||||
__le16 packet_id; // detects dropped packets
|
||||
#define MIN_HEADER 6
|
||||
|
||||
// all else is optional, and must start with:
|
||||
// __le16 vendorId; // from usb-if
|
||||
// __le16 productId;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define PAD_BYTE ((unsigned char)0xAC)
|
||||
|
||||
struct nc_trailer {
|
||||
__le16 packet_id;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
// packets may use FLAG_FRAMING_NC and optional pad
|
||||
#define FRAMED_SIZE(mtu) (sizeof (struct nc_header) \
|
||||
+ sizeof (struct ethhdr) \
|
||||
+ (mtu) \
|
||||
+ 1 \
|
||||
+ sizeof (struct nc_trailer))
|
||||
|
||||
#define MIN_FRAMED FRAMED_SIZE(0)
|
||||
|
||||
/* packets _could_ be up to 64KB... */
|
||||
#define NC_MAX_PACKET 32767
|
||||
|
||||
|
||||
/*
|
||||
* Zero means no timeout; else, how long a 64 byte bulk packet may be queued
|
||||
* before the hardware drops it. If that's done, the driver will need to
|
||||
* frame network packets to guard against the dropped USB packets. The win32
|
||||
* driver sets this for both sides of the link.
|
||||
*/
|
||||
#define NC_READ_TTL_MS ((u8)255) // ms
|
||||
|
||||
/*
|
||||
* We ignore most registers and EEPROM contents.
|
||||
*/
|
||||
#define REG_USBCTL ((u8)0x04)
|
||||
#define REG_TTL ((u8)0x10)
|
||||
#define REG_STATUS ((u8)0x11)
|
||||
|
||||
/*
|
||||
* Vendor specific requests to read/write data
|
||||
*/
|
||||
#define REQUEST_REGISTER ((u8)0x10)
|
||||
#define REQUEST_EEPROM ((u8)0x11)
|
||||
|
||||
static int
|
||||
nc_vendor_read(struct usbnet *dev, u8 req, u8 regnum, u16 *retval_ptr)
|
||||
{
|
||||
int status = usb_control_msg(dev->udev,
|
||||
usb_rcvctrlpipe(dev->udev, 0),
|
||||
req,
|
||||
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
0, regnum,
|
||||
retval_ptr, sizeof *retval_ptr,
|
||||
USB_CTRL_GET_TIMEOUT);
|
||||
if (status > 0)
|
||||
status = 0;
|
||||
if (!status)
|
||||
le16_to_cpus(retval_ptr);
|
||||
return status;
|
||||
}
|
||||
|
||||
static inline int
|
||||
nc_register_read(struct usbnet *dev, u8 regnum, u16 *retval_ptr)
|
||||
{
|
||||
return nc_vendor_read(dev, REQUEST_REGISTER, regnum, retval_ptr);
|
||||
}
|
||||
|
||||
// no retval ... can become async, usable in_interrupt()
|
||||
static void
|
||||
nc_vendor_write(struct usbnet *dev, u8 req, u8 regnum, u16 value)
|
||||
{
|
||||
usb_control_msg(dev->udev,
|
||||
usb_sndctrlpipe(dev->udev, 0),
|
||||
req,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
value, regnum,
|
||||
NULL, 0, // data is in setup packet
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
nc_register_write(struct usbnet *dev, u8 regnum, u16 value)
|
||||
{
|
||||
nc_vendor_write(dev, REQUEST_REGISTER, regnum, value);
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
static void nc_dump_registers(struct usbnet *dev)
|
||||
{
|
||||
u8 reg;
|
||||
u16 *vp = kmalloc(sizeof (u16));
|
||||
|
||||
if (!vp) {
|
||||
dbg("no memory?");
|
||||
return;
|
||||
}
|
||||
|
||||
dbg("%s registers:", dev->net->name);
|
||||
for (reg = 0; reg < 0x20; reg++) {
|
||||
int retval;
|
||||
|
||||
// reading some registers is trouble
|
||||
if (reg >= 0x08 && reg <= 0xf)
|
||||
continue;
|
||||
if (reg >= 0x12 && reg <= 0x1e)
|
||||
continue;
|
||||
|
||||
retval = nc_register_read(dev, reg, vp);
|
||||
if (retval < 0)
|
||||
dbg("%s reg [0x%x] ==> error %d",
|
||||
dev->net->name, reg, retval);
|
||||
else
|
||||
dbg("%s reg [0x%x] = 0x%x",
|
||||
dev->net->name, reg, *vp);
|
||||
}
|
||||
kfree(vp);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Control register
|
||||
*/
|
||||
|
||||
#define USBCTL_WRITABLE_MASK 0x1f0f
|
||||
// bits 15-13 reserved, r/o
|
||||
#define USBCTL_ENABLE_LANG (1 << 12)
|
||||
#define USBCTL_ENABLE_MFGR (1 << 11)
|
||||
#define USBCTL_ENABLE_PROD (1 << 10)
|
||||
#define USBCTL_ENABLE_SERIAL (1 << 9)
|
||||
#define USBCTL_ENABLE_DEFAULTS (1 << 8)
|
||||
// bits 7-4 reserved, r/o
|
||||
#define USBCTL_FLUSH_OTHER (1 << 3)
|
||||
#define USBCTL_FLUSH_THIS (1 << 2)
|
||||
#define USBCTL_DISCONN_OTHER (1 << 1)
|
||||
#define USBCTL_DISCONN_THIS (1 << 0)
|
||||
|
||||
static inline void nc_dump_usbctl(struct usbnet *dev, u16 usbctl)
|
||||
{
|
||||
if (!netif_msg_link(dev))
|
||||
return;
|
||||
devdbg(dev, "net1080 %s-%s usbctl 0x%x:%s%s%s%s%s;"
|
||||
" this%s%s;"
|
||||
" other%s%s; r/o 0x%x",
|
||||
dev->udev->bus->bus_name, dev->udev->devpath,
|
||||
usbctl,
|
||||
(usbctl & USBCTL_ENABLE_LANG) ? " lang" : "",
|
||||
(usbctl & USBCTL_ENABLE_MFGR) ? " mfgr" : "",
|
||||
(usbctl & USBCTL_ENABLE_PROD) ? " prod" : "",
|
||||
(usbctl & USBCTL_ENABLE_SERIAL) ? " serial" : "",
|
||||
(usbctl & USBCTL_ENABLE_DEFAULTS) ? " defaults" : "",
|
||||
|
||||
(usbctl & USBCTL_FLUSH_OTHER) ? " FLUSH" : "",
|
||||
(usbctl & USBCTL_DISCONN_OTHER) ? " DIS" : "",
|
||||
(usbctl & USBCTL_FLUSH_THIS) ? " FLUSH" : "",
|
||||
(usbctl & USBCTL_DISCONN_THIS) ? " DIS" : "",
|
||||
usbctl & ~USBCTL_WRITABLE_MASK
|
||||
);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Status register
|
||||
*/
|
||||
|
||||
#define STATUS_PORT_A (1 << 15)
|
||||
|
||||
#define STATUS_CONN_OTHER (1 << 14)
|
||||
#define STATUS_SUSPEND_OTHER (1 << 13)
|
||||
#define STATUS_MAILBOX_OTHER (1 << 12)
|
||||
#define STATUS_PACKETS_OTHER(n) (((n) >> 8) && 0x03)
|
||||
|
||||
#define STATUS_CONN_THIS (1 << 6)
|
||||
#define STATUS_SUSPEND_THIS (1 << 5)
|
||||
#define STATUS_MAILBOX_THIS (1 << 4)
|
||||
#define STATUS_PACKETS_THIS(n) (((n) >> 0) && 0x03)
|
||||
|
||||
#define STATUS_UNSPEC_MASK 0x0c8c
|
||||
#define STATUS_NOISE_MASK ((u16)~(0x0303|STATUS_UNSPEC_MASK))
|
||||
|
||||
|
||||
static inline void nc_dump_status(struct usbnet *dev, u16 status)
|
||||
{
|
||||
if (!netif_msg_link(dev))
|
||||
return;
|
||||
devdbg(dev, "net1080 %s-%s status 0x%x:"
|
||||
" this (%c) PKT=%d%s%s%s;"
|
||||
" other PKT=%d%s%s%s; unspec 0x%x",
|
||||
dev->udev->bus->bus_name, dev->udev->devpath,
|
||||
status,
|
||||
|
||||
// XXX the packet counts don't seem right
|
||||
// (1 at reset, not 0); maybe UNSPEC too
|
||||
|
||||
(status & STATUS_PORT_A) ? 'A' : 'B',
|
||||
STATUS_PACKETS_THIS(status),
|
||||
(status & STATUS_CONN_THIS) ? " CON" : "",
|
||||
(status & STATUS_SUSPEND_THIS) ? " SUS" : "",
|
||||
(status & STATUS_MAILBOX_THIS) ? " MBOX" : "",
|
||||
|
||||
STATUS_PACKETS_OTHER(status),
|
||||
(status & STATUS_CONN_OTHER) ? " CON" : "",
|
||||
(status & STATUS_SUSPEND_OTHER) ? " SUS" : "",
|
||||
(status & STATUS_MAILBOX_OTHER) ? " MBOX" : "",
|
||||
|
||||
status & STATUS_UNSPEC_MASK
|
||||
);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* TTL register
|
||||
*/
|
||||
|
||||
#define TTL_THIS(ttl) (0x00ff & ttl)
|
||||
#define TTL_OTHER(ttl) (0x00ff & (ttl >> 8))
|
||||
#define MK_TTL(this,other) ((u16)(((other)<<8)|(0x00ff&(this))))
|
||||
|
||||
static inline void nc_dump_ttl(struct usbnet *dev, u16 ttl)
|
||||
{
|
||||
if (netif_msg_link(dev))
|
||||
devdbg(dev, "net1080 %s-%s ttl 0x%x this = %d, other = %d",
|
||||
dev->udev->bus->bus_name, dev->udev->devpath,
|
||||
ttl, TTL_THIS(ttl), TTL_OTHER(ttl));
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int net1080_reset(struct usbnet *dev)
|
||||
{
|
||||
u16 usbctl, status, ttl;
|
||||
u16 *vp = kmalloc(sizeof (u16), GFP_KERNEL);
|
||||
int retval;
|
||||
|
||||
if (!vp)
|
||||
return -ENOMEM;
|
||||
|
||||
// nc_dump_registers(dev);
|
||||
|
||||
if ((retval = nc_register_read(dev, REG_STATUS, vp)) < 0) {
|
||||
dbg("can't read %s-%s status: %d",
|
||||
dev->udev->bus->bus_name, dev->udev->devpath, retval);
|
||||
goto done;
|
||||
}
|
||||
status = *vp;
|
||||
nc_dump_status(dev, status);
|
||||
|
||||
if ((retval = nc_register_read(dev, REG_USBCTL, vp)) < 0) {
|
||||
dbg("can't read USBCTL, %d", retval);
|
||||
goto done;
|
||||
}
|
||||
usbctl = *vp;
|
||||
nc_dump_usbctl(dev, usbctl);
|
||||
|
||||
nc_register_write(dev, REG_USBCTL,
|
||||
USBCTL_FLUSH_THIS | USBCTL_FLUSH_OTHER);
|
||||
|
||||
if ((retval = nc_register_read(dev, REG_TTL, vp)) < 0) {
|
||||
dbg("can't read TTL, %d", retval);
|
||||
goto done;
|
||||
}
|
||||
ttl = *vp;
|
||||
// nc_dump_ttl(dev, ttl);
|
||||
|
||||
nc_register_write(dev, REG_TTL,
|
||||
MK_TTL(NC_READ_TTL_MS, TTL_OTHER(ttl)) );
|
||||
dbg("%s: assigned TTL, %d ms", dev->net->name, NC_READ_TTL_MS);
|
||||
|
||||
if (netif_msg_link(dev))
|
||||
devinfo(dev, "port %c, peer %sconnected",
|
||||
(status & STATUS_PORT_A) ? 'A' : 'B',
|
||||
(status & STATUS_CONN_OTHER) ? "" : "dis"
|
||||
);
|
||||
retval = 0;
|
||||
|
||||
done:
|
||||
kfree(vp);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int net1080_check_connect(struct usbnet *dev)
|
||||
{
|
||||
int retval;
|
||||
u16 status;
|
||||
u16 *vp = kmalloc(sizeof (u16), GFP_KERNEL);
|
||||
|
||||
if (!vp)
|
||||
return -ENOMEM;
|
||||
retval = nc_register_read(dev, REG_STATUS, vp);
|
||||
status = *vp;
|
||||
kfree(vp);
|
||||
if (retval != 0) {
|
||||
dbg("%s net1080_check_conn read - %d", dev->net->name, retval);
|
||||
return retval;
|
||||
}
|
||||
if ((status & STATUS_CONN_OTHER) != STATUS_CONN_OTHER)
|
||||
return -ENOLINK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nc_flush_complete(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
kfree(urb->context);
|
||||
usb_free_urb(urb);
|
||||
}
|
||||
|
||||
static void nc_ensure_sync(struct usbnet *dev)
|
||||
{
|
||||
dev->frame_errors++;
|
||||
if (dev->frame_errors > 5) {
|
||||
struct urb *urb;
|
||||
struct usb_ctrlrequest *req;
|
||||
int status;
|
||||
|
||||
/* Send a flush */
|
||||
urb = usb_alloc_urb(0, SLAB_ATOMIC);
|
||||
if (!urb)
|
||||
return;
|
||||
|
||||
req = kmalloc(sizeof *req, GFP_ATOMIC);
|
||||
if (!req) {
|
||||
usb_free_urb(urb);
|
||||
return;
|
||||
}
|
||||
|
||||
req->bRequestType = USB_DIR_OUT
|
||||
| USB_TYPE_VENDOR
|
||||
| USB_RECIP_DEVICE;
|
||||
req->bRequest = REQUEST_REGISTER;
|
||||
req->wValue = cpu_to_le16(USBCTL_FLUSH_THIS
|
||||
| USBCTL_FLUSH_OTHER);
|
||||
req->wIndex = cpu_to_le16(REG_USBCTL);
|
||||
req->wLength = cpu_to_le16(0);
|
||||
|
||||
/* queue an async control request, we don't need
|
||||
* to do anything when it finishes except clean up.
|
||||
*/
|
||||
usb_fill_control_urb(urb, dev->udev,
|
||||
usb_sndctrlpipe(dev->udev, 0),
|
||||
(unsigned char *) req,
|
||||
NULL, 0,
|
||||
nc_flush_complete, req);
|
||||
status = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (status) {
|
||||
kfree(req);
|
||||
usb_free_urb(urb);
|
||||
return;
|
||||
}
|
||||
|
||||
if (netif_msg_rx_err(dev))
|
||||
devdbg(dev, "flush net1080; too many framing errors");
|
||||
dev->frame_errors = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int net1080_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
|
||||
{
|
||||
struct nc_header *header;
|
||||
struct nc_trailer *trailer;
|
||||
u16 hdr_len, packet_len;
|
||||
|
||||
if (!(skb->len & 0x01)) {
|
||||
#ifdef DEBUG
|
||||
struct net_device *net = dev->net;
|
||||
dbg("rx framesize %d range %d..%d mtu %d", skb->len,
|
||||
net->hard_header_len, dev->hard_mtu, net->mtu);
|
||||
#endif
|
||||
dev->stats.rx_frame_errors++;
|
||||
nc_ensure_sync(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
header = (struct nc_header *) skb->data;
|
||||
hdr_len = le16_to_cpup(&header->hdr_len);
|
||||
packet_len = le16_to_cpup(&header->packet_len);
|
||||
if (FRAMED_SIZE(packet_len) > NC_MAX_PACKET) {
|
||||
dev->stats.rx_frame_errors++;
|
||||
dbg("packet too big, %d", packet_len);
|
||||
nc_ensure_sync(dev);
|
||||
return 0;
|
||||
} else if (hdr_len < MIN_HEADER) {
|
||||
dev->stats.rx_frame_errors++;
|
||||
dbg("header too short, %d", hdr_len);
|
||||
nc_ensure_sync(dev);
|
||||
return 0;
|
||||
} else if (hdr_len > MIN_HEADER) {
|
||||
// out of band data for us?
|
||||
dbg("header OOB, %d bytes", hdr_len - MIN_HEADER);
|
||||
nc_ensure_sync(dev);
|
||||
// switch (vendor/product ids) { ... }
|
||||
}
|
||||
skb_pull(skb, hdr_len);
|
||||
|
||||
trailer = (struct nc_trailer *)
|
||||
(skb->data + skb->len - sizeof *trailer);
|
||||
skb_trim(skb, skb->len - sizeof *trailer);
|
||||
|
||||
if ((packet_len & 0x01) == 0) {
|
||||
if (skb->data [packet_len] != PAD_BYTE) {
|
||||
dev->stats.rx_frame_errors++;
|
||||
dbg("bad pad");
|
||||
return 0;
|
||||
}
|
||||
skb_trim(skb, skb->len - 1);
|
||||
}
|
||||
if (skb->len != packet_len) {
|
||||
dev->stats.rx_frame_errors++;
|
||||
dbg("bad packet len %d (expected %d)",
|
||||
skb->len, packet_len);
|
||||
nc_ensure_sync(dev);
|
||||
return 0;
|
||||
}
|
||||
if (header->packet_id != get_unaligned(&trailer->packet_id)) {
|
||||
dev->stats.rx_fifo_errors++;
|
||||
dbg("(2+ dropped) rx packet_id mismatch 0x%x 0x%x",
|
||||
le16_to_cpu(header->packet_id),
|
||||
le16_to_cpu(trailer->packet_id));
|
||||
return 0;
|
||||
}
|
||||
#if 0
|
||||
devdbg(dev, "frame <rx h %d p %d id %d", header->hdr_len,
|
||||
header->packet_len, header->packet_id);
|
||||
#endif
|
||||
dev->frame_errors = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct sk_buff *
|
||||
net1080_tx_fixup(struct usbnet *dev, struct sk_buff *skb, unsigned flags)
|
||||
{
|
||||
int padlen;
|
||||
struct sk_buff *skb2;
|
||||
struct nc_header *header = NULL;
|
||||
struct nc_trailer *trailer = NULL;
|
||||
int len = skb->len;
|
||||
|
||||
padlen = ((len + sizeof (struct nc_header)
|
||||
+ sizeof (struct nc_trailer)) & 0x01) ? 0 : 1;
|
||||
if (!skb_cloned(skb)) {
|
||||
int headroom = skb_headroom(skb);
|
||||
int tailroom = skb_tailroom(skb);
|
||||
|
||||
if ((padlen + sizeof (struct nc_trailer)) <= tailroom
|
||||
&& sizeof (struct nc_header) <= headroom)
|
||||
/* There's enough head and tail room */
|
||||
goto encapsulate;
|
||||
|
||||
if ((sizeof (struct nc_header) + padlen
|
||||
+ sizeof (struct nc_trailer)) <
|
||||
(headroom + tailroom)) {
|
||||
/* There's enough total room, so just readjust */
|
||||
skb->data = memmove(skb->head
|
||||
+ sizeof (struct nc_header),
|
||||
skb->data, skb->len);
|
||||
skb->tail = skb->data + len;
|
||||
goto encapsulate;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a new skb to use with the correct size */
|
||||
skb2 = skb_copy_expand(skb,
|
||||
sizeof (struct nc_header),
|
||||
sizeof (struct nc_trailer) + padlen,
|
||||
flags);
|
||||
dev_kfree_skb_any(skb);
|
||||
if (!skb2)
|
||||
return skb2;
|
||||
skb = skb2;
|
||||
|
||||
encapsulate:
|
||||
/* header first */
|
||||
header = (struct nc_header *) skb_push(skb, sizeof *header);
|
||||
header->hdr_len = cpu_to_le16(sizeof (*header));
|
||||
header->packet_len = cpu_to_le16(len);
|
||||
header->packet_id = cpu_to_le16((u16)dev->xid++);
|
||||
|
||||
/* maybe pad; then trailer */
|
||||
if (!((skb->len + sizeof *trailer) & 0x01))
|
||||
*skb_put(skb, 1) = PAD_BYTE;
|
||||
trailer = (struct nc_trailer *) skb_put(skb, sizeof *trailer);
|
||||
put_unaligned(header->packet_id, &trailer->packet_id);
|
||||
#if 0
|
||||
devdbg(dev, "frame >tx h %d p %d id %d",
|
||||
header->hdr_len, header->packet_len,
|
||||
header->packet_id);
|
||||
#endif
|
||||
return skb;
|
||||
}
|
||||
|
||||
static int net1080_bind(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
unsigned extra = sizeof (struct nc_header)
|
||||
+ 1
|
||||
+ sizeof (struct nc_trailer);
|
||||
|
||||
dev->net->hard_header_len += extra;
|
||||
dev->rx_urb_size = dev->net->hard_header_len + dev->net->mtu;
|
||||
dev->hard_mtu = NC_MAX_PACKET;
|
||||
return usbnet_get_endpoints (dev, intf);
|
||||
}
|
||||
|
||||
static const struct driver_info net1080_info = {
|
||||
.description = "NetChip TurboCONNECT",
|
||||
.flags = FLAG_FRAMING_NC,
|
||||
.bind = net1080_bind,
|
||||
.reset = net1080_reset,
|
||||
.check_connect = net1080_check_connect,
|
||||
.rx_fixup = net1080_rx_fixup,
|
||||
.tx_fixup = net1080_tx_fixup,
|
||||
};
|
||||
|
||||
static const struct usb_device_id products [] = {
|
||||
{
|
||||
USB_DEVICE(0x0525, 0x1080), // NetChip ref design
|
||||
.driver_info = (unsigned long) &net1080_info,
|
||||
}, {
|
||||
USB_DEVICE(0x06D0, 0x0622), // Laplink Gold
|
||||
.driver_info = (unsigned long) &net1080_info,
|
||||
},
|
||||
{ }, // END
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, products);
|
||||
|
||||
static struct usb_driver net1080_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "net1080",
|
||||
.id_table = products,
|
||||
.probe = usbnet_probe,
|
||||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
};
|
||||
|
||||
static int __init net1080_init(void)
|
||||
{
|
||||
return usb_register(&net1080_driver);
|
||||
}
|
||||
module_init(net1080_init);
|
||||
|
||||
static void __exit net1080_exit(void)
|
||||
{
|
||||
usb_deregister(&net1080_driver);
|
||||
}
|
||||
module_exit(net1080_exit);
|
||||
|
||||
MODULE_AUTHOR("David Brownell");
|
||||
MODULE_DESCRIPTION("NetChip 1080 based USB Host-to-Host Links");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -825,7 +825,6 @@ static void pegasus_tx_timeout(struct net_device *net)
|
|||
pegasus_t *pegasus = netdev_priv(net);
|
||||
if (netif_msg_timer(pegasus))
|
||||
printk(KERN_WARNING "%s: tx timeout\n", net->name);
|
||||
pegasus->tx_urb->transfer_flags |= URB_ASYNC_UNLINK;
|
||||
usb_unlink_urb(pegasus->tx_urb);
|
||||
pegasus->stats.tx_errors++;
|
||||
}
|
||||
|
|
156
drivers/usb/net/plusb.c
Normal file
156
drivers/usb/net/plusb.c
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* PL-2301/2302 USB host-to-host link cables
|
||||
* Copyright (C) 2000-2005 by David Brownell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
// #define DEBUG // error path messages, extra info
|
||||
// #define VERBOSE // more; success messages
|
||||
|
||||
#include <linux/config.h>
|
||||
#ifdef CONFIG_USB_DEBUG
|
||||
# define DEBUG
|
||||
#endif
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "usbnet.h"
|
||||
|
||||
|
||||
/*
|
||||
* Prolific PL-2301/PL-2302 driver ... http://www.prolifictech.com
|
||||
*
|
||||
* The protocol and handshaking used here should be bug-compatible
|
||||
* with the Linux 2.2 "plusb" driver, by Deti Fliegl.
|
||||
*
|
||||
* HEADS UP: this handshaking isn't all that robust. This driver
|
||||
* gets confused easily if you unplug one end of the cable then
|
||||
* try to connect it again; you'll need to restart both ends. The
|
||||
* "naplink" software (used by some PlayStation/2 deveopers) does
|
||||
* the handshaking much better! Also, sometimes this hardware
|
||||
* seems to get wedged under load. Prolific docs are weak, and
|
||||
* don't identify differences between PL2301 and PL2302, much less
|
||||
* anything to explain the different PL2302 versions observed.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Bits 0-4 can be used for software handshaking; they're set from
|
||||
* one end, cleared from the other, "read" with the interrupt byte.
|
||||
*/
|
||||
#define PL_S_EN (1<<7) /* (feature only) suspend enable */
|
||||
/* reserved bit -- rx ready (6) ? */
|
||||
#define PL_TX_READY (1<<5) /* (interrupt only) transmit ready */
|
||||
#define PL_RESET_OUT (1<<4) /* reset output pipe */
|
||||
#define PL_RESET_IN (1<<3) /* reset input pipe */
|
||||
#define PL_TX_C (1<<2) /* transmission complete */
|
||||
#define PL_TX_REQ (1<<1) /* transmission received */
|
||||
#define PL_PEER_E (1<<0) /* peer exists */
|
||||
|
||||
static inline int
|
||||
pl_vendor_req(struct usbnet *dev, u8 req, u8 val, u8 index)
|
||||
{
|
||||
return usb_control_msg(dev->udev,
|
||||
usb_rcvctrlpipe(dev->udev, 0),
|
||||
req,
|
||||
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
val, index,
|
||||
NULL, 0,
|
||||
USB_CTRL_GET_TIMEOUT);
|
||||
}
|
||||
|
||||
static inline int
|
||||
pl_clear_QuickLink_features(struct usbnet *dev, int val)
|
||||
{
|
||||
return pl_vendor_req(dev, 1, (u8) val, 0);
|
||||
}
|
||||
|
||||
static inline int
|
||||
pl_set_QuickLink_features(struct usbnet *dev, int val)
|
||||
{
|
||||
return pl_vendor_req(dev, 3, (u8) val, 0);
|
||||
}
|
||||
|
||||
static int pl_reset(struct usbnet *dev)
|
||||
{
|
||||
/* some units seem to need this reset, others reject it utterly.
|
||||
* FIXME be more like "naplink" or windows drivers.
|
||||
*/
|
||||
(void) pl_set_QuickLink_features(dev,
|
||||
PL_S_EN|PL_RESET_OUT|PL_RESET_IN|PL_PEER_E);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct driver_info prolific_info = {
|
||||
.description = "Prolific PL-2301/PL-2302",
|
||||
.flags = FLAG_NO_SETINT,
|
||||
/* some PL-2302 versions seem to fail usb_set_interface() */
|
||||
.reset = pl_reset,
|
||||
};
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Proilific's name won't normally be on the cables, and
|
||||
* may not be on the device.
|
||||
*/
|
||||
|
||||
static const struct usb_device_id products [] = {
|
||||
|
||||
{
|
||||
USB_DEVICE(0x067b, 0x0000), // PL-2301
|
||||
.driver_info = (unsigned long) &prolific_info,
|
||||
}, {
|
||||
USB_DEVICE(0x067b, 0x0001), // PL-2302
|
||||
.driver_info = (unsigned long) &prolific_info,
|
||||
},
|
||||
|
||||
{ }, // END
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, products);
|
||||
|
||||
static struct usb_driver plusb_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "plusb",
|
||||
.id_table = products,
|
||||
.probe = usbnet_probe,
|
||||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
};
|
||||
|
||||
static int __init plusb_init(void)
|
||||
{
|
||||
return usb_register(&plusb_driver);
|
||||
}
|
||||
module_init(plusb_init);
|
||||
|
||||
static void __exit plusb_exit(void)
|
||||
{
|
||||
usb_deregister(&plusb_driver);
|
||||
}
|
||||
module_exit(plusb_exit);
|
||||
|
||||
MODULE_AUTHOR("David Brownell");
|
||||
MODULE_DESCRIPTION("Prolific PL-2301/2302 USB Host to Host Link Driver");
|
||||
MODULE_LICENSE("GPL");
|
615
drivers/usb/net/rndis_host.c
Normal file
615
drivers/usb/net/rndis_host.c
Normal file
|
@ -0,0 +1,615 @@
|
|||
/*
|
||||
* Host Side support for RNDIS Networking Links
|
||||
* Copyright (C) 2005 by David Brownell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
// #define DEBUG // error path messages, extra info
|
||||
// #define VERBOSE // more; success messages
|
||||
|
||||
#include <linux/config.h>
|
||||
#ifdef CONFIG_USB_DEBUG
|
||||
# define DEBUG
|
||||
#endif
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb_cdc.h>
|
||||
|
||||
#include "usbnet.h"
|
||||
|
||||
|
||||
/*
|
||||
* RNDIS is NDIS remoted over USB. It's a MSFT variant of CDC ACM ... of
|
||||
* course ACM was intended for modems, not Ethernet links! USB's standard
|
||||
* for Ethernet links is "CDC Ethernet", which is significantly simpler.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CONTROL uses CDC "encapsulated commands" with funky notifications.
|
||||
* - control-out: SEND_ENCAPSULATED
|
||||
* - interrupt-in: RESPONSE_AVAILABLE
|
||||
* - control-in: GET_ENCAPSULATED
|
||||
*
|
||||
* We'll try to ignore the RESPONSE_AVAILABLE notifications.
|
||||
*/
|
||||
struct rndis_msg_hdr {
|
||||
__le32 msg_type; /* RNDIS_MSG_* */
|
||||
__le32 msg_len;
|
||||
// followed by data that varies between messages
|
||||
__le32 request_id;
|
||||
__le32 status;
|
||||
// ... and more
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* RNDIS defines this (absurdly huge) control timeout */
|
||||
#define RNDIS_CONTROL_TIMEOUT_MS (10 * 1000)
|
||||
|
||||
|
||||
#define ccpu2 __constant_cpu_to_le32
|
||||
|
||||
#define RNDIS_MSG_COMPLETION ccpu2(0x80000000)
|
||||
|
||||
/* codes for "msg_type" field of rndis messages;
|
||||
* only the data channel uses packet messages (maybe batched);
|
||||
* everything else goes on the control channel.
|
||||
*/
|
||||
#define RNDIS_MSG_PACKET ccpu2(0x00000001) /* 1-N packets */
|
||||
#define RNDIS_MSG_INIT ccpu2(0x00000002)
|
||||
#define RNDIS_MSG_INIT_C (RNDIS_MSG_INIT|RNDIS_MSG_COMPLETION)
|
||||
#define RNDIS_MSG_HALT ccpu2(0x00000003)
|
||||
#define RNDIS_MSG_QUERY ccpu2(0x00000004)
|
||||
#define RNDIS_MSG_QUERY_C (RNDIS_MSG_QUERY|RNDIS_MSG_COMPLETION)
|
||||
#define RNDIS_MSG_SET ccpu2(0x00000005)
|
||||
#define RNDIS_MSG_SET_C (RNDIS_MSG_SET|RNDIS_MSG_COMPLETION)
|
||||
#define RNDIS_MSG_RESET ccpu2(0x00000006)
|
||||
#define RNDIS_MSG_RESET_C (RNDIS_MSG_RESET|RNDIS_MSG_COMPLETION)
|
||||
#define RNDIS_MSG_INDICATE ccpu2(0x00000007)
|
||||
#define RNDIS_MSG_KEEPALIVE ccpu2(0x00000008)
|
||||
#define RNDIS_MSG_KEEPALIVE_C (RNDIS_MSG_KEEPALIVE|RNDIS_MSG_COMPLETION)
|
||||
|
||||
/* codes for "status" field of completion messages */
|
||||
#define RNDIS_STATUS_SUCCESS ccpu2(0x00000000)
|
||||
#define RNDIS_STATUS_FAILURE ccpu2(0xc0000001)
|
||||
#define RNDIS_STATUS_INVALID_DATA ccpu2(0xc0010015)
|
||||
#define RNDIS_STATUS_NOT_SUPPORTED ccpu2(0xc00000bb)
|
||||
#define RNDIS_STATUS_MEDIA_CONNECT ccpu2(0x4001000b)
|
||||
#define RNDIS_STATUS_MEDIA_DISCONNECT ccpu2(0x4001000c)
|
||||
|
||||
|
||||
struct rndis_data_hdr {
|
||||
__le32 msg_type; /* RNDIS_MSG_PACKET */
|
||||
__le32 msg_len; // rndis_data_hdr + data_len + pad
|
||||
__le32 data_offset; // 36 -- right after header
|
||||
__le32 data_len; // ... real packet size
|
||||
|
||||
__le32 oob_data_offset; // zero
|
||||
__le32 oob_data_len; // zero
|
||||
__le32 num_oob; // zero
|
||||
__le32 packet_data_offset; // zero
|
||||
|
||||
__le32 packet_data_len; // zero
|
||||
__le32 vc_handle; // zero
|
||||
__le32 reserved; // zero
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rndis_init { /* OUT */
|
||||
// header and:
|
||||
__le32 msg_type; /* RNDIS_MSG_INIT */
|
||||
__le32 msg_len; // 24
|
||||
__le32 request_id;
|
||||
__le32 major_version; // of rndis (1.0)
|
||||
__le32 minor_version;
|
||||
__le32 max_transfer_size;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rndis_init_c { /* IN */
|
||||
// header and:
|
||||
__le32 msg_type; /* RNDIS_MSG_INIT_C */
|
||||
__le32 msg_len;
|
||||
__le32 request_id;
|
||||
__le32 status;
|
||||
__le32 major_version; // of rndis (1.0)
|
||||
__le32 minor_version;
|
||||
__le32 device_flags;
|
||||
__le32 medium; // zero == 802.3
|
||||
__le32 max_packets_per_message;
|
||||
__le32 max_transfer_size;
|
||||
__le32 packet_alignment; // max 7; (1<<n) bytes
|
||||
__le32 af_list_offset; // zero
|
||||
__le32 af_list_size; // zero
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rndis_halt { /* OUT (no reply) */
|
||||
// header and:
|
||||
__le32 msg_type; /* RNDIS_MSG_HALT */
|
||||
__le32 msg_len;
|
||||
__le32 request_id;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rndis_query { /* OUT */
|
||||
// header and:
|
||||
__le32 msg_type; /* RNDIS_MSG_QUERY */
|
||||
__le32 msg_len;
|
||||
__le32 request_id;
|
||||
__le32 oid;
|
||||
__le32 len;
|
||||
__le32 offset;
|
||||
/*?*/ __le32 handle; // zero
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rndis_query_c { /* IN */
|
||||
// header and:
|
||||
__le32 msg_type; /* RNDIS_MSG_QUERY_C */
|
||||
__le32 msg_len;
|
||||
__le32 request_id;
|
||||
__le32 status;
|
||||
__le32 len;
|
||||
__le32 offset;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rndis_set { /* OUT */
|
||||
// header and:
|
||||
__le32 msg_type; /* RNDIS_MSG_SET */
|
||||
__le32 msg_len;
|
||||
__le32 request_id;
|
||||
__le32 oid;
|
||||
__le32 len;
|
||||
__le32 offset;
|
||||
/*?*/ __le32 handle; // zero
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rndis_set_c { /* IN */
|
||||
// header and:
|
||||
__le32 msg_type; /* RNDIS_MSG_SET_C */
|
||||
__le32 msg_len;
|
||||
__le32 request_id;
|
||||
__le32 status;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rndis_reset { /* IN */
|
||||
// header and:
|
||||
__le32 msg_type; /* RNDIS_MSG_RESET */
|
||||
__le32 msg_len;
|
||||
__le32 reserved;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rndis_reset_c { /* OUT */
|
||||
// header and:
|
||||
__le32 msg_type; /* RNDIS_MSG_RESET_C */
|
||||
__le32 msg_len;
|
||||
__le32 status;
|
||||
__le32 addressing_lost;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rndis_indicate { /* IN (unrequested) */
|
||||
// header and:
|
||||
__le32 msg_type; /* RNDIS_MSG_INDICATE */
|
||||
__le32 msg_len;
|
||||
__le32 status;
|
||||
__le32 length;
|
||||
__le32 offset;
|
||||
/**/ __le32 diag_status;
|
||||
__le32 error_offset;
|
||||
/**/ __le32 message;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rndis_keepalive { /* OUT (optionally IN) */
|
||||
// header and:
|
||||
__le32 msg_type; /* RNDIS_MSG_KEEPALIVE */
|
||||
__le32 msg_len;
|
||||
__le32 request_id;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rndis_keepalive_c { /* IN (optionally OUT) */
|
||||
// header and:
|
||||
__le32 msg_type; /* RNDIS_MSG_KEEPALIVE_C */
|
||||
__le32 msg_len;
|
||||
__le32 request_id;
|
||||
__le32 status;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* NOTE: about 30 OIDs are "mandatory" for peripherals to support ... and
|
||||
* there are gobs more that may optionally be supported. We'll avoid as much
|
||||
* of that mess as possible.
|
||||
*/
|
||||
#define OID_802_3_PERMANENT_ADDRESS ccpu2(0x01010101)
|
||||
#define OID_GEN_CURRENT_PACKET_FILTER ccpu2(0x0001010e)
|
||||
|
||||
/*
|
||||
* RNDIS notifications from device: command completion; "reverse"
|
||||
* keepalives; etc
|
||||
*/
|
||||
static void rndis_status(struct usbnet *dev, struct urb *urb)
|
||||
{
|
||||
devdbg(dev, "rndis status urb, len %d stat %d",
|
||||
urb->actual_length, urb->status);
|
||||
// FIXME for keepalives, respond immediately (asynchronously)
|
||||
// if not an RNDIS status, do like cdc_status(dev,urb) does
|
||||
}
|
||||
|
||||
/*
|
||||
* RPC done RNDIS-style. Caller guarantees:
|
||||
* - message is properly byteswapped
|
||||
* - there's no other request pending
|
||||
* - buf can hold up to 1KB response (required by RNDIS spec)
|
||||
* On return, the first few entries are already byteswapped.
|
||||
*
|
||||
* Call context is likely probe(), before interface name is known,
|
||||
* which is why we won't try to use it in the diagnostics.
|
||||
*/
|
||||
static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf)
|
||||
{
|
||||
struct cdc_state *info = (void *) &dev->data;
|
||||
int retval;
|
||||
unsigned count;
|
||||
__le32 rsp;
|
||||
u32 xid = 0, msg_len, request_id;
|
||||
|
||||
/* REVISIT when this gets called from contexts other than probe() or
|
||||
* disconnect(): either serialize, or dispatch responses on xid
|
||||
*/
|
||||
|
||||
/* Issue the request; don't bother byteswapping our xid */
|
||||
if (likely(buf->msg_type != RNDIS_MSG_HALT
|
||||
&& buf->msg_type != RNDIS_MSG_RESET)) {
|
||||
xid = dev->xid++;
|
||||
if (!xid)
|
||||
xid = dev->xid++;
|
||||
buf->request_id = (__force __le32) xid;
|
||||
}
|
||||
retval = usb_control_msg(dev->udev,
|
||||
usb_sndctrlpipe(dev->udev, 0),
|
||||
USB_CDC_SEND_ENCAPSULATED_COMMAND,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
0, info->u->bMasterInterface0,
|
||||
buf, le32_to_cpu(buf->msg_len),
|
||||
RNDIS_CONTROL_TIMEOUT_MS);
|
||||
if (unlikely(retval < 0 || xid == 0))
|
||||
return retval;
|
||||
|
||||
// FIXME Seems like some devices discard responses when
|
||||
// we time out and cancel our "get response" requests...
|
||||
// so, this is fragile. Probably need to poll for status.
|
||||
|
||||
/* ignore status endpoint, just poll the control channel;
|
||||
* the request probably completed immediately
|
||||
*/
|
||||
rsp = buf->msg_type | RNDIS_MSG_COMPLETION;
|
||||
for (count = 0; count < 10; count++) {
|
||||
memset(buf, 0, 1024);
|
||||
retval = usb_control_msg(dev->udev,
|
||||
usb_rcvctrlpipe(dev->udev, 0),
|
||||
USB_CDC_GET_ENCAPSULATED_RESPONSE,
|
||||
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
0, info->u->bMasterInterface0,
|
||||
buf, 1024,
|
||||
RNDIS_CONTROL_TIMEOUT_MS);
|
||||
if (likely(retval >= 8)) {
|
||||
msg_len = le32_to_cpu(buf->msg_len);
|
||||
request_id = (__force u32) buf->request_id;
|
||||
if (likely(buf->msg_type == rsp)) {
|
||||
if (likely(request_id == xid)) {
|
||||
if (unlikely(rsp == RNDIS_MSG_RESET_C))
|
||||
return 0;
|
||||
if (likely(RNDIS_STATUS_SUCCESS
|
||||
== buf->status))
|
||||
return 0;
|
||||
dev_dbg(&info->control->dev,
|
||||
"rndis reply status %08x\n",
|
||||
le32_to_cpu(buf->status));
|
||||
return -EL3RST;
|
||||
}
|
||||
dev_dbg(&info->control->dev,
|
||||
"rndis reply id %d expected %d\n",
|
||||
request_id, xid);
|
||||
/* then likely retry */
|
||||
} else switch (buf->msg_type) {
|
||||
case RNDIS_MSG_INDICATE: { /* fault */
|
||||
// struct rndis_indicate *msg = (void *)buf;
|
||||
dev_info(&info->control->dev,
|
||||
"rndis fault indication\n");
|
||||
}
|
||||
break;
|
||||
case RNDIS_MSG_KEEPALIVE: { /* ping */
|
||||
struct rndis_keepalive_c *msg = (void *)buf;
|
||||
|
||||
msg->msg_type = RNDIS_MSG_KEEPALIVE_C;
|
||||
msg->msg_len = ccpu2(sizeof *msg);
|
||||
msg->status = RNDIS_STATUS_SUCCESS;
|
||||
retval = usb_control_msg(dev->udev,
|
||||
usb_sndctrlpipe(dev->udev, 0),
|
||||
USB_CDC_SEND_ENCAPSULATED_COMMAND,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
0, info->u->bMasterInterface0,
|
||||
msg, sizeof *msg,
|
||||
RNDIS_CONTROL_TIMEOUT_MS);
|
||||
if (unlikely(retval < 0))
|
||||
dev_dbg(&info->control->dev,
|
||||
"rndis keepalive err %d\n",
|
||||
retval);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_dbg(&info->control->dev,
|
||||
"unexpected rndis msg %08x len %d\n",
|
||||
le32_to_cpu(buf->msg_type), msg_len);
|
||||
}
|
||||
} else {
|
||||
/* device probably issued a protocol stall; ignore */
|
||||
dev_dbg(&info->control->dev,
|
||||
"rndis response error, code %d\n", retval);
|
||||
}
|
||||
msleep(2);
|
||||
}
|
||||
dev_dbg(&info->control->dev, "rndis response timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
int retval;
|
||||
struct net_device *net = dev->net;
|
||||
union {
|
||||
void *buf;
|
||||
struct rndis_msg_hdr *header;
|
||||
struct rndis_init *init;
|
||||
struct rndis_init_c *init_c;
|
||||
struct rndis_query *get;
|
||||
struct rndis_query_c *get_c;
|
||||
struct rndis_set *set;
|
||||
struct rndis_set_c *set_c;
|
||||
} u;
|
||||
u32 tmp;
|
||||
|
||||
/* we can't rely on i/o from stack working, or stack allocation */
|
||||
u.buf = kmalloc(1024, GFP_KERNEL);
|
||||
if (!u.buf)
|
||||
return -ENOMEM;
|
||||
retval = usbnet_generic_cdc_bind(dev, intf);
|
||||
if (retval < 0)
|
||||
goto done;
|
||||
|
||||
net->hard_header_len += sizeof (struct rndis_data_hdr);
|
||||
|
||||
/* initialize; max transfer is 16KB at full speed */
|
||||
u.init->msg_type = RNDIS_MSG_INIT;
|
||||
u.init->msg_len = ccpu2(sizeof *u.init);
|
||||
u.init->major_version = ccpu2(1);
|
||||
u.init->minor_version = ccpu2(0);
|
||||
u.init->max_transfer_size = ccpu2(net->mtu + net->hard_header_len);
|
||||
|
||||
retval = rndis_command(dev, u.header);
|
||||
if (unlikely(retval < 0)) {
|
||||
/* it might not even be an RNDIS device!! */
|
||||
dev_err(&intf->dev, "RNDIS init failed, %d\n", retval);
|
||||
fail:
|
||||
usb_driver_release_interface(driver_of(intf),
|
||||
((struct cdc_state *)&(dev->data))->data);
|
||||
goto done;
|
||||
}
|
||||
dev->hard_mtu = le32_to_cpu(u.init_c->max_transfer_size);
|
||||
/* REVISIT: peripheral "alignment" request is ignored ... */
|
||||
dev_dbg(&intf->dev, "hard mtu %u, align %d\n", dev->hard_mtu,
|
||||
1 << le32_to_cpu(u.init_c->packet_alignment));
|
||||
|
||||
/* get designated host ethernet address */
|
||||
memset(u.get, 0, sizeof *u.get);
|
||||
u.get->msg_type = RNDIS_MSG_QUERY;
|
||||
u.get->msg_len = ccpu2(sizeof *u.get);
|
||||
u.get->oid = OID_802_3_PERMANENT_ADDRESS;
|
||||
|
||||
retval = rndis_command(dev, u.header);
|
||||
if (unlikely(retval < 0)) {
|
||||
dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval);
|
||||
goto fail;
|
||||
}
|
||||
tmp = le32_to_cpu(u.get_c->offset);
|
||||
if (unlikely((tmp + 8) > (1024 - ETH_ALEN)
|
||||
|| u.get_c->len != ccpu2(ETH_ALEN))) {
|
||||
dev_err(&intf->dev, "rndis ethaddr off %d len %d ?\n",
|
||||
tmp, le32_to_cpu(u.get_c->len));
|
||||
retval = -EDOM;
|
||||
goto fail;
|
||||
}
|
||||
memcpy(net->dev_addr, tmp + (char *)&u.get_c->request_id, ETH_ALEN);
|
||||
|
||||
/* set a nonzero filter to enable data transfers */
|
||||
memset(u.set, 0, sizeof *u.set);
|
||||
u.set->msg_type = RNDIS_MSG_SET;
|
||||
u.set->msg_len = ccpu2(4 + sizeof *u.set);
|
||||
u.set->oid = OID_GEN_CURRENT_PACKET_FILTER;
|
||||
u.set->len = ccpu2(4);
|
||||
u.set->offset = ccpu2((sizeof *u.set) - 8);
|
||||
*(__le32 *)(u.buf + sizeof *u.set) = ccpu2(DEFAULT_FILTER);
|
||||
|
||||
retval = rndis_command(dev, u.header);
|
||||
if (unlikely(retval < 0)) {
|
||||
dev_err(&intf->dev, "rndis set packet filter, %d\n", retval);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
done:
|
||||
kfree(u.buf);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
struct rndis_halt *halt;
|
||||
|
||||
/* try to clear any rndis state/activity (no i/o from stack!) */
|
||||
halt = kcalloc(1, sizeof *halt, SLAB_KERNEL);
|
||||
if (halt) {
|
||||
halt->msg_type = RNDIS_MSG_HALT;
|
||||
halt->msg_len = ccpu2(sizeof *halt);
|
||||
(void) rndis_command(dev, (void *)halt);
|
||||
kfree(halt);
|
||||
}
|
||||
|
||||
return usbnet_cdc_unbind(dev, intf);
|
||||
}
|
||||
|
||||
/*
|
||||
* DATA -- host must not write zlps
|
||||
*/
|
||||
static int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
|
||||
{
|
||||
/* peripheral may have batched packets to us... */
|
||||
while (likely(skb->len)) {
|
||||
struct rndis_data_hdr *hdr = (void *)skb->data;
|
||||
struct sk_buff *skb2;
|
||||
u32 msg_len, data_offset, data_len;
|
||||
|
||||
msg_len = le32_to_cpu(hdr->msg_len);
|
||||
data_offset = le32_to_cpu(hdr->data_offset);
|
||||
data_len = le32_to_cpu(hdr->data_len);
|
||||
|
||||
/* don't choke if we see oob, per-packet data, etc */
|
||||
if (unlikely(hdr->msg_type != RNDIS_MSG_PACKET
|
||||
|| skb->len < msg_len
|
||||
|| (data_offset + data_len + 8) > msg_len)) {
|
||||
dev->stats.rx_frame_errors++;
|
||||
devdbg(dev, "bad rndis message %d/%d/%d/%d, len %d",
|
||||
le32_to_cpu(hdr->msg_type),
|
||||
msg_len, data_offset, data_len, skb->len);
|
||||
return 0;
|
||||
}
|
||||
skb_pull(skb, 8 + data_offset);
|
||||
|
||||
/* at most one packet left? */
|
||||
if (likely((data_len - skb->len) <= sizeof *hdr)) {
|
||||
skb_trim(skb, data_len);
|
||||
break;
|
||||
}
|
||||
|
||||
/* try to return all the packets in the batch */
|
||||
skb2 = skb_clone(skb, GFP_ATOMIC);
|
||||
if (unlikely(!skb2))
|
||||
break;
|
||||
skb_pull(skb, msg_len - sizeof *hdr);
|
||||
skb_trim(skb2, data_len);
|
||||
usbnet_skb_return(dev, skb2);
|
||||
}
|
||||
|
||||
/* caller will usbnet_skb_return the remaining packet */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct sk_buff *
|
||||
rndis_tx_fixup(struct usbnet *dev, struct sk_buff *skb, unsigned flags)
|
||||
{
|
||||
struct rndis_data_hdr *hdr;
|
||||
struct sk_buff *skb2;
|
||||
unsigned len = skb->len;
|
||||
|
||||
if (likely(!skb_cloned(skb))) {
|
||||
int room = skb_headroom(skb);
|
||||
|
||||
/* enough head room as-is? */
|
||||
if (unlikely((sizeof *hdr) <= room))
|
||||
goto fill;
|
||||
|
||||
/* enough room, but needs to be readjusted? */
|
||||
room += skb_tailroom(skb);
|
||||
if (likely((sizeof *hdr) <= room)) {
|
||||
skb->data = memmove(skb->head + sizeof *hdr,
|
||||
skb->data, len);
|
||||
skb->tail = skb->data + len;
|
||||
goto fill;
|
||||
}
|
||||
}
|
||||
|
||||
/* create a new skb, with the correct size (and tailpad) */
|
||||
skb2 = skb_copy_expand(skb, sizeof *hdr, 1, flags);
|
||||
dev_kfree_skb_any(skb);
|
||||
if (unlikely(!skb2))
|
||||
return skb2;
|
||||
skb = skb2;
|
||||
|
||||
/* fill out the RNDIS header. we won't bother trying to batch
|
||||
* packets; Linux minimizes wasted bandwidth through tx queues.
|
||||
*/
|
||||
fill:
|
||||
hdr = (void *) __skb_push(skb, sizeof *hdr);
|
||||
memset(hdr, 0, sizeof *hdr);
|
||||
hdr->msg_type = RNDIS_MSG_PACKET;
|
||||
hdr->msg_len = cpu_to_le32(skb->len);
|
||||
hdr->data_offset = ccpu2(sizeof(*hdr) - 8);
|
||||
hdr->data_len = cpu_to_le32(len);
|
||||
|
||||
/* FIXME make the last packet always be short ... */
|
||||
return skb;
|
||||
}
|
||||
|
||||
|
||||
static const struct driver_info rndis_info = {
|
||||
.description = "RNDIS device",
|
||||
.flags = FLAG_ETHER | FLAG_FRAMING_RN,
|
||||
.bind = rndis_bind,
|
||||
.unbind = rndis_unbind,
|
||||
.status = rndis_status,
|
||||
.rx_fixup = rndis_rx_fixup,
|
||||
.tx_fixup = rndis_tx_fixup,
|
||||
};
|
||||
|
||||
#undef ccpu2
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static const struct usb_device_id products [] = {
|
||||
{
|
||||
/* RNDIS is MSFT's un-official variant of CDC ACM */
|
||||
USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
|
||||
.driver_info = (unsigned long) &rndis_info,
|
||||
},
|
||||
{ }, // END
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, products);
|
||||
|
||||
static struct usb_driver rndis_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "rndis_host",
|
||||
.id_table = products,
|
||||
.probe = usbnet_probe,
|
||||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
};
|
||||
|
||||
static int __init rndis_init(void)
|
||||
{
|
||||
return usb_register(&rndis_driver);
|
||||
}
|
||||
module_init(rndis_init);
|
||||
|
||||
static void __exit rndis_exit(void)
|
||||
{
|
||||
usb_deregister(&rndis_driver);
|
||||
}
|
||||
module_exit(rndis_exit);
|
||||
|
||||
MODULE_AUTHOR("David Brownell");
|
||||
MODULE_DESCRIPTION("USB Host side RNDIS driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -653,7 +653,6 @@ static void rtl8150_tx_timeout(struct net_device *netdev)
|
|||
{
|
||||
rtl8150_t *dev = netdev_priv(netdev);
|
||||
warn("%s: Tx timeout.", netdev->name);
|
||||
dev->tx_urb->transfer_flags |= URB_ASYNC_UNLINK;
|
||||
usb_unlink_urb(dev->tx_urb);
|
||||
dev->stats.tx_errors++;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
193
drivers/usb/net/usbnet.h
Normal file
193
drivers/usb/net/usbnet.h
Normal file
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* USB Networking Link Interface
|
||||
*
|
||||
* Copyright (C) 2000-2005 by David Brownell <dbrownell@users.sourceforge.net>
|
||||
* Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __USBNET_H
|
||||
#define __USBNET_H
|
||||
|
||||
|
||||
/* interface from usbnet core to each USB networking link we handle */
|
||||
struct usbnet {
|
||||
/* housekeeping */
|
||||
struct usb_device *udev;
|
||||
struct driver_info *driver_info;
|
||||
wait_queue_head_t *wait;
|
||||
|
||||
/* i/o info: pipes etc */
|
||||
unsigned in, out;
|
||||
struct usb_host_endpoint *status;
|
||||
unsigned maxpacket;
|
||||
struct timer_list delay;
|
||||
|
||||
/* protocol/interface state */
|
||||
struct net_device *net;
|
||||
struct net_device_stats stats;
|
||||
int msg_enable;
|
||||
unsigned long data [5];
|
||||
u32 xid;
|
||||
u32 hard_mtu; /* count any extra framing */
|
||||
size_t rx_urb_size; /* size for rx urbs */
|
||||
struct mii_if_info mii;
|
||||
|
||||
/* various kinds of pending driver work */
|
||||
struct sk_buff_head rxq;
|
||||
struct sk_buff_head txq;
|
||||
struct sk_buff_head done;
|
||||
struct urb *interrupt;
|
||||
struct tasklet_struct bh;
|
||||
|
||||
struct work_struct kevent;
|
||||
unsigned long flags;
|
||||
# define EVENT_TX_HALT 0
|
||||
# define EVENT_RX_HALT 1
|
||||
# define EVENT_RX_MEMORY 2
|
||||
# define EVENT_STS_SPLIT 3
|
||||
# define EVENT_LINK_RESET 4
|
||||
};
|
||||
|
||||
static inline struct usb_driver *driver_of(struct usb_interface *intf)
|
||||
{
|
||||
return to_usb_driver(intf->dev.driver);
|
||||
}
|
||||
|
||||
/* interface from the device/framing level "minidriver" to core */
|
||||
struct driver_info {
|
||||
char *description;
|
||||
|
||||
int flags;
|
||||
/* framing is CDC Ethernet, not writing ZLPs (hw issues), or optionally: */
|
||||
#define FLAG_FRAMING_NC 0x0001 /* guard against device dropouts */
|
||||
#define FLAG_FRAMING_GL 0x0002 /* genelink batches packets */
|
||||
#define FLAG_FRAMING_Z 0x0004 /* zaurus adds a trailer */
|
||||
#define FLAG_FRAMING_RN 0x0008 /* RNDIS batches, plus huge header */
|
||||
|
||||
#define FLAG_NO_SETINT 0x0010 /* device can't set_interface() */
|
||||
#define FLAG_ETHER 0x0020 /* maybe use "eth%d" names */
|
||||
|
||||
#define FLAG_FRAMING_AX 0x0040 /* AX88772/178 packets */
|
||||
|
||||
/* init device ... can sleep, or cause probe() failure */
|
||||
int (*bind)(struct usbnet *, struct usb_interface *);
|
||||
|
||||
/* cleanup device ... can sleep, but can't fail */
|
||||
void (*unbind)(struct usbnet *, struct usb_interface *);
|
||||
|
||||
/* reset device ... can sleep */
|
||||
int (*reset)(struct usbnet *);
|
||||
|
||||
/* see if peer is connected ... can sleep */
|
||||
int (*check_connect)(struct usbnet *);
|
||||
|
||||
/* for status polling */
|
||||
void (*status)(struct usbnet *, struct urb *);
|
||||
|
||||
/* link reset handling, called from defer_kevent */
|
||||
int (*link_reset)(struct usbnet *);
|
||||
|
||||
/* fixup rx packet (strip framing) */
|
||||
int (*rx_fixup)(struct usbnet *dev, struct sk_buff *skb);
|
||||
|
||||
/* fixup tx packet (add framing) */
|
||||
struct sk_buff *(*tx_fixup)(struct usbnet *dev,
|
||||
struct sk_buff *skb, unsigned flags);
|
||||
|
||||
/* for new devices, use the descriptor-reading code instead */
|
||||
int in; /* rx endpoint */
|
||||
int out; /* tx endpoint */
|
||||
|
||||
unsigned long data; /* Misc driver specific data */
|
||||
};
|
||||
|
||||
/* Minidrivers are just drivers using the "usbnet" core as a powerful
|
||||
* network-specific subroutine library ... that happens to do pretty
|
||||
* much everything except custom framing and chip-specific stuff.
|
||||
*/
|
||||
extern int usbnet_probe(struct usb_interface *, const struct usb_device_id *);
|
||||
extern int usbnet_suspend (struct usb_interface *, pm_message_t );
|
||||
extern int usbnet_resume (struct usb_interface *);
|
||||
extern void usbnet_disconnect(struct usb_interface *);
|
||||
|
||||
|
||||
/* Drivers that reuse some of the standard USB CDC infrastructure
|
||||
* (notably, using multiple interfaces according to the the CDC
|
||||
* union descriptor) get some helper code.
|
||||
*/
|
||||
struct cdc_state {
|
||||
struct usb_cdc_header_desc *header;
|
||||
struct usb_cdc_union_desc *u;
|
||||
struct usb_cdc_ether_desc *ether;
|
||||
struct usb_interface *control;
|
||||
struct usb_interface *data;
|
||||
};
|
||||
|
||||
extern int usbnet_generic_cdc_bind (struct usbnet *, struct usb_interface *);
|
||||
extern void usbnet_cdc_unbind (struct usbnet *, struct usb_interface *);
|
||||
|
||||
/* CDC and RNDIS support the same host-chosen packet filters for IN transfers */
|
||||
#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \
|
||||
|USB_CDC_PACKET_TYPE_ALL_MULTICAST \
|
||||
|USB_CDC_PACKET_TYPE_PROMISCUOUS \
|
||||
|USB_CDC_PACKET_TYPE_DIRECTED)
|
||||
|
||||
|
||||
/* we record the state for each of our queued skbs */
|
||||
enum skb_state {
|
||||
illegal = 0,
|
||||
tx_start, tx_done,
|
||||
rx_start, rx_done, rx_cleanup
|
||||
};
|
||||
|
||||
struct skb_data { /* skb->cb is one of these */
|
||||
struct urb *urb;
|
||||
struct usbnet *dev;
|
||||
enum skb_state state;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
|
||||
extern int usbnet_get_endpoints(struct usbnet *, struct usb_interface *);
|
||||
extern void usbnet_defer_kevent (struct usbnet *, int);
|
||||
extern void usbnet_skb_return (struct usbnet *, struct sk_buff *);
|
||||
|
||||
extern u32 usbnet_get_msglevel (struct net_device *);
|
||||
extern void usbnet_set_msglevel (struct net_device *, u32);
|
||||
extern void usbnet_get_drvinfo (struct net_device *, struct ethtool_drvinfo *);
|
||||
|
||||
/* messaging support includes the interface name, so it must not be
|
||||
* used before it has one ... notably, in minidriver bind() calls.
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
#define devdbg(usbnet, fmt, arg...) \
|
||||
printk(KERN_DEBUG "%s: " fmt "\n" , (usbnet)->net->name , ## arg)
|
||||
#else
|
||||
#define devdbg(usbnet, fmt, arg...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#define deverr(usbnet, fmt, arg...) \
|
||||
printk(KERN_ERR "%s: " fmt "\n" , (usbnet)->net->name , ## arg)
|
||||
#define devwarn(usbnet, fmt, arg...) \
|
||||
printk(KERN_WARNING "%s: " fmt "\n" , (usbnet)->net->name , ## arg)
|
||||
|
||||
#define devinfo(usbnet, fmt, arg...) \
|
||||
printk(KERN_INFO "%s: " fmt "\n" , (usbnet)->net->name , ## arg); \
|
||||
|
||||
|
||||
#endif /* __USBNET_H */
|
386
drivers/usb/net/zaurus.c
Normal file
386
drivers/usb/net/zaurus.c
Normal file
|
@ -0,0 +1,386 @@
|
|||
/*
|
||||
* Copyright (C) 2002 Pavel Machek <pavel@ucw.cz>
|
||||
* Copyright (C) 2002-2005 by David Brownell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
// #define DEBUG // error path messages, extra info
|
||||
// #define VERBOSE // more; success messages
|
||||
|
||||
#include <linux/config.h>
|
||||
#ifdef CONFIG_USB_DEBUG
|
||||
# define DEBUG
|
||||
#endif
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb_cdc.h>
|
||||
|
||||
#include "usbnet.h"
|
||||
|
||||
|
||||
/*
|
||||
* All known Zaurii lie about their standards conformance. At least
|
||||
* the earliest SA-1100 models lie by saying they support CDC Ethernet.
|
||||
* Some later models (especially PXA-25x and PXA-27x based ones) lie
|
||||
* and say they support CDC MDLM (for access to cell phone modems).
|
||||
*
|
||||
* There are non-Zaurus products that use these same protocols too.
|
||||
*
|
||||
* The annoying thing is that at the same time Sharp was developing
|
||||
* that annoying standards-breaking software, the Linux community had
|
||||
* a simple "CDC Subset" working reliably on the same SA-1100 hardware.
|
||||
* That is, the same functionality but not violating standards.
|
||||
*
|
||||
* The CDC Ethernet nonconformance points are troublesome to hosts
|
||||
* with a true CDC Ethernet implementation:
|
||||
* - Framing appends a CRC, which the spec says drivers "must not" do;
|
||||
* - Transfers data in altsetting zero, instead of altsetting 1;
|
||||
* - All these peripherals use the same ethernet address.
|
||||
*
|
||||
* The CDC MDLM nonconformance is less immediately troublesome, since all
|
||||
* MDLM implementations are quasi-proprietary anyway.
|
||||
*/
|
||||
|
||||
static struct sk_buff *
|
||||
zaurus_tx_fixup(struct usbnet *dev, struct sk_buff *skb, unsigned flags)
|
||||
{
|
||||
int padlen;
|
||||
struct sk_buff *skb2;
|
||||
|
||||
padlen = 2;
|
||||
if (!skb_cloned(skb)) {
|
||||
int tailroom = skb_tailroom(skb);
|
||||
if ((padlen + 4) <= tailroom)
|
||||
goto done;
|
||||
}
|
||||
skb2 = skb_copy_expand(skb, 0, 4 + padlen, flags);
|
||||
dev_kfree_skb_any(skb);
|
||||
skb = skb2;
|
||||
if (skb) {
|
||||
u32 fcs;
|
||||
done:
|
||||
fcs = crc32_le(~0, skb->data, skb->len);
|
||||
fcs = ~fcs;
|
||||
|
||||
*skb_put (skb, 1) = fcs & 0xff;
|
||||
*skb_put (skb, 1) = (fcs>> 8) & 0xff;
|
||||
*skb_put (skb, 1) = (fcs>>16) & 0xff;
|
||||
*skb_put (skb, 1) = (fcs>>24) & 0xff;
|
||||
}
|
||||
return skb;
|
||||
}
|
||||
|
||||
static int zaurus_bind(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
/* Belcarra's funky framing has other options; mostly
|
||||
* TRAILERS (!) with 4 bytes CRC, and maybe 2 pad bytes.
|
||||
*/
|
||||
dev->net->hard_header_len += 6;
|
||||
dev->rx_urb_size = dev->net->hard_header_len + dev->net->mtu;
|
||||
return usbnet_generic_cdc_bind(dev, intf);
|
||||
}
|
||||
|
||||
/* PDA style devices are always connected if present */
|
||||
static int always_connected (struct usbnet *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct driver_info zaurus_sl5x00_info = {
|
||||
.description = "Sharp Zaurus SL-5x00",
|
||||
.flags = FLAG_FRAMING_Z,
|
||||
.check_connect = always_connected,
|
||||
.bind = zaurus_bind,
|
||||
.unbind = usbnet_cdc_unbind,
|
||||
.tx_fixup = zaurus_tx_fixup,
|
||||
};
|
||||
#define ZAURUS_STRONGARM_INFO ((unsigned long)&zaurus_sl5x00_info)
|
||||
|
||||
static const struct driver_info zaurus_pxa_info = {
|
||||
.description = "Sharp Zaurus, PXA-2xx based",
|
||||
.flags = FLAG_FRAMING_Z,
|
||||
.check_connect = always_connected,
|
||||
.bind = zaurus_bind,
|
||||
.unbind = usbnet_cdc_unbind,
|
||||
.tx_fixup = zaurus_tx_fixup,
|
||||
};
|
||||
#define ZAURUS_PXA_INFO ((unsigned long)&zaurus_pxa_info)
|
||||
|
||||
static const struct driver_info olympus_mxl_info = {
|
||||
.description = "Olympus R1000",
|
||||
.flags = FLAG_FRAMING_Z,
|
||||
.check_connect = always_connected,
|
||||
.bind = zaurus_bind,
|
||||
.unbind = usbnet_cdc_unbind,
|
||||
.tx_fixup = zaurus_tx_fixup,
|
||||
};
|
||||
#define OLYMPUS_MXL_INFO ((unsigned long)&olympus_mxl_info)
|
||||
|
||||
|
||||
/* Some more recent products using Lineo/Belcarra code will wrongly claim
|
||||
* CDC MDLM conformance. They aren't conformant: data endpoints live
|
||||
* in the control interface, there's no data interface, and it's not used
|
||||
* to talk to a cell phone radio. But at least we can detect these two
|
||||
* pseudo-classes, rather than growing this product list with entries for
|
||||
* each new nonconformant product (sigh).
|
||||
*/
|
||||
static const u8 safe_guid[16] = {
|
||||
0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6,
|
||||
0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f,
|
||||
};
|
||||
static const u8 blan_guid[16] = {
|
||||
0x74, 0xf0, 0x3d, 0xbd, 0x1e, 0xc1, 0x44, 0x70,
|
||||
0xa3, 0x67, 0x71, 0x34, 0xc9, 0xf5, 0x54, 0x37,
|
||||
};
|
||||
|
||||
static int blan_mdlm_bind(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
u8 *buf = intf->cur_altsetting->extra;
|
||||
int len = intf->cur_altsetting->extralen;
|
||||
struct usb_cdc_mdlm_desc *desc = NULL;
|
||||
struct usb_cdc_mdlm_detail_desc *detail = NULL;
|
||||
|
||||
while (len > 3) {
|
||||
if (buf [1] != USB_DT_CS_INTERFACE)
|
||||
goto next_desc;
|
||||
|
||||
/* use bDescriptorSubType, and just verify that we get a
|
||||
* "BLAN" (or "SAFE") descriptor.
|
||||
*/
|
||||
switch (buf [2]) {
|
||||
case USB_CDC_MDLM_TYPE:
|
||||
if (desc) {
|
||||
dev_dbg(&intf->dev, "extra MDLM\n");
|
||||
goto bad_desc;
|
||||
}
|
||||
desc = (void *) buf;
|
||||
if (desc->bLength != sizeof *desc) {
|
||||
dev_dbg(&intf->dev, "MDLM len %u\n",
|
||||
desc->bLength);
|
||||
goto bad_desc;
|
||||
}
|
||||
/* expect bcdVersion 1.0, ignore */
|
||||
if (memcmp(&desc->bGUID, blan_guid, 16)
|
||||
&& memcmp(&desc->bGUID, safe_guid, 16) ) {
|
||||
/* hey, this one might _really_ be MDLM! */
|
||||
dev_dbg(&intf->dev, "MDLM guid\n");
|
||||
goto bad_desc;
|
||||
}
|
||||
break;
|
||||
case USB_CDC_MDLM_DETAIL_TYPE:
|
||||
if (detail) {
|
||||
dev_dbg(&intf->dev, "extra MDLM detail\n");
|
||||
goto bad_desc;
|
||||
}
|
||||
detail = (void *) buf;
|
||||
switch (detail->bGuidDescriptorType) {
|
||||
case 0: /* "SAFE" */
|
||||
if (detail->bLength != (sizeof *detail + 2))
|
||||
goto bad_detail;
|
||||
break;
|
||||
case 1: /* "BLAN" */
|
||||
if (detail->bLength != (sizeof *detail + 3))
|
||||
goto bad_detail;
|
||||
break;
|
||||
default:
|
||||
goto bad_detail;
|
||||
}
|
||||
|
||||
/* assuming we either noticed BLAN already, or will
|
||||
* find it soon, there are some data bytes here:
|
||||
* - bmNetworkCapabilities (unused)
|
||||
* - bmDataCapabilities (bits, see below)
|
||||
* - bPad (ignored, for PADAFTER -- BLAN-only)
|
||||
* bits are:
|
||||
* - 0x01 -- Zaurus framing (add CRC)
|
||||
* - 0x02 -- PADBEFORE (CRC includes some padding)
|
||||
* - 0x04 -- PADAFTER (some padding after CRC)
|
||||
* - 0x08 -- "fermat" packet mangling (for hw bugs)
|
||||
* the PADBEFORE appears not to matter; we interop
|
||||
* with devices that use it and those that don't.
|
||||
*/
|
||||
if ((detail->bDetailData[1] & ~0x02) != 0x01) {
|
||||
/* bmDataCapabilites == 0 would be fine too,
|
||||
* but framing is minidriver-coupled for now.
|
||||
*/
|
||||
bad_detail:
|
||||
dev_dbg(&intf->dev,
|
||||
"bad MDLM detail, %d %d %d\n",
|
||||
detail->bLength,
|
||||
detail->bDetailData[0],
|
||||
detail->bDetailData[2]);
|
||||
goto bad_desc;
|
||||
}
|
||||
break;
|
||||
}
|
||||
next_desc:
|
||||
len -= buf [0]; /* bLength */
|
||||
buf += buf [0];
|
||||
}
|
||||
|
||||
if (!desc || !detail) {
|
||||
dev_dbg(&intf->dev, "missing cdc mdlm %s%sdescriptor\n",
|
||||
desc ? "" : "func ",
|
||||
detail ? "" : "detail ");
|
||||
goto bad_desc;
|
||||
}
|
||||
|
||||
/* There's probably a CDC Ethernet descriptor there, but we can't
|
||||
* rely on the Ethernet address it provides since not all vendors
|
||||
* bother to make it unique. Likewise there's no point in tracking
|
||||
* of the CDC event notifications.
|
||||
*/
|
||||
return usbnet_get_endpoints(dev, intf);
|
||||
|
||||
bad_desc:
|
||||
dev_info(&dev->udev->dev, "unsupported MDLM descriptors\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static const struct driver_info bogus_mdlm_info = {
|
||||
.description = "pseudo-MDLM (BLAN) device",
|
||||
.flags = FLAG_FRAMING_Z,
|
||||
.check_connect = always_connected,
|
||||
.tx_fixup = zaurus_tx_fixup,
|
||||
.bind = blan_mdlm_bind,
|
||||
};
|
||||
|
||||
static const struct usb_device_id products [] = {
|
||||
#define ZAURUS_MASTER_INTERFACE \
|
||||
.bInterfaceClass = USB_CLASS_COMM, \
|
||||
.bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, \
|
||||
.bInterfaceProtocol = USB_CDC_PROTO_NONE
|
||||
|
||||
/* SA-1100 based Sharp Zaurus ("collie"), or compatible. */
|
||||
{
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
.idProduct = 0x8004,
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = ZAURUS_STRONGARM_INFO,
|
||||
},
|
||||
|
||||
/* PXA-2xx based models are also lying-about-cdc. If you add any
|
||||
* more devices that claim to be CDC Ethernet, make sure they get
|
||||
* added to the blacklist in cdc_ether too.
|
||||
*
|
||||
* NOTE: OpenZaurus versions with 2.6 kernels won't use these entries,
|
||||
* unlike the older ones with 2.4 "embedix" kernels.
|
||||
*/
|
||||
{
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
.idProduct = 0x8005, /* A-300 */
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = ZAURUS_PXA_INFO,
|
||||
}, {
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
.idProduct = 0x8006, /* B-500/SL-5600 */
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = ZAURUS_PXA_INFO,
|
||||
}, {
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
.idProduct = 0x8007, /* C-700 */
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = ZAURUS_PXA_INFO,
|
||||
}, {
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
.idProduct = 0x9031, /* C-750 C-760 */
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = ZAURUS_PXA_INFO,
|
||||
}, {
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
.idProduct = 0x9032, /* SL-6000 */
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = ZAURUS_PXA_INFO,
|
||||
}, {
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
/* reported with some C860 units */
|
||||
.idProduct = 0x9050, /* C-860 */
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = ZAURUS_PXA_INFO,
|
||||
},
|
||||
|
||||
|
||||
/* At least some of the newest PXA units have very different lies about
|
||||
* their standards support: they claim to be cell phones offering
|
||||
* direct access to their radios! (No, they don't conform to CDC MDLM.)
|
||||
*/
|
||||
{
|
||||
USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM,
|
||||
USB_CDC_PROTO_NONE),
|
||||
.driver_info = (unsigned long) &bogus_mdlm_info,
|
||||
},
|
||||
|
||||
/* Olympus has some models with a Zaurus-compatible option.
|
||||
* R-1000 uses a FreeScale i.MXL cpu (ARMv4T)
|
||||
*/
|
||||
{
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x07B4,
|
||||
.idProduct = 0x0F02, /* R-1000 */
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = OLYMPUS_MXL_INFO,
|
||||
},
|
||||
{ }, // END
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, products);
|
||||
|
||||
static struct usb_driver zaurus_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "zaurus",
|
||||
.id_table = products,
|
||||
.probe = usbnet_probe,
|
||||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
};
|
||||
|
||||
static int __init zaurus_init(void)
|
||||
{
|
||||
return usb_register(&zaurus_driver);
|
||||
}
|
||||
module_init(zaurus_init);
|
||||
|
||||
static void __exit zaurus_exit(void)
|
||||
{
|
||||
usb_deregister(&zaurus_driver);
|
||||
}
|
||||
module_exit(zaurus_exit);
|
||||
|
||||
MODULE_AUTHOR("Pavel Machek, David Brownell");
|
||||
MODULE_DESCRIPTION("Sharp Zaurus PDA, and compatible products");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -847,7 +847,6 @@ static void zd1201_tx_timeout(struct net_device *dev)
|
|||
return;
|
||||
dev_warn(&zd->usb->dev, "%s: TX timeout, shooting down urb\n",
|
||||
dev->name);
|
||||
zd->tx_urb->transfer_flags |= URB_ASYNC_UNLINK;
|
||||
usb_unlink_urb(zd->tx_urb);
|
||||
zd->stats.tx_errors++;
|
||||
/* Restart the timeout to quiet the watchdog: */
|
||||
|
|
|
@ -453,8 +453,8 @@ static int generic_startup (struct usb_serial *serial)
|
|||
priv->cbr_mask = B300;
|
||||
usb_set_serial_port_data(serial->port[0], priv);
|
||||
|
||||
return (0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int cypress_earthmate_startup (struct usb_serial *serial)
|
||||
|
@ -464,14 +464,15 @@ static int cypress_earthmate_startup (struct usb_serial *serial)
|
|||
dbg("%s", __FUNCTION__);
|
||||
|
||||
if (generic_startup(serial)) {
|
||||
dbg("%s - Failed setting up port %d", __FUNCTION__, serial->port[0]->number);
|
||||
dbg("%s - Failed setting up port %d", __FUNCTION__,
|
||||
serial->port[0]->number);
|
||||
return 1;
|
||||
}
|
||||
|
||||
priv = usb_get_serial_port_data(serial->port[0]);
|
||||
priv->chiptype = CT_EARTHMATE;
|
||||
|
||||
return (0);
|
||||
|
||||
return 0;
|
||||
} /* cypress_earthmate_startup */
|
||||
|
||||
|
||||
|
@ -482,14 +483,15 @@ static int cypress_hidcom_startup (struct usb_serial *serial)
|
|||
dbg("%s", __FUNCTION__);
|
||||
|
||||
if (generic_startup(serial)) {
|
||||
dbg("%s - Failed setting up port %d", __FUNCTION__, serial->port[0]->number);
|
||||
dbg("%s - Failed setting up port %d", __FUNCTION__,
|
||||
serial->port[0]->number);
|
||||
return 1;
|
||||
}
|
||||
|
||||
priv = usb_get_serial_port_data(serial->port[0]);
|
||||
priv->chiptype = CT_CYPHIDCOM;
|
||||
|
||||
return (0);
|
||||
return 0;
|
||||
} /* cypress_hidcom_startup */
|
||||
|
||||
|
||||
|
@ -909,7 +911,8 @@ static int cypress_ioctl (struct usb_serial_port *port, struct file * file, unsi
|
|||
} /* cypress_ioctl */
|
||||
|
||||
|
||||
static void cypress_set_termios (struct usb_serial_port *port, struct termios *old_termios)
|
||||
static void cypress_set_termios (struct usb_serial_port *port,
|
||||
struct termios *old_termios)
|
||||
{
|
||||
struct cypress_private *priv = usb_get_serial_port_data(port);
|
||||
struct tty_struct *tty;
|
||||
|
@ -918,7 +921,7 @@ static void cypress_set_termios (struct usb_serial_port *port, struct termios *o
|
|||
unsigned long flags;
|
||||
__u8 oldlines;
|
||||
int linechange = 0;
|
||||
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
||||
tty = port->tty;
|
||||
|
@ -931,10 +934,12 @@ static void cypress_set_termios (struct usb_serial_port *port, struct termios *o
|
|||
if (!priv->termios_initialized) {
|
||||
if (priv->chiptype == CT_EARTHMATE) {
|
||||
*(tty->termios) = tty_std_termios;
|
||||
tty->termios->c_cflag = B4800 | CS8 | CREAD | HUPCL | CLOCAL;
|
||||
tty->termios->c_cflag = B4800 | CS8 | CREAD | HUPCL |
|
||||
CLOCAL;
|
||||
} else if (priv->chiptype == CT_CYPHIDCOM) {
|
||||
*(tty->termios) = tty_std_termios;
|
||||
tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
|
||||
tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL |
|
||||
CLOCAL;
|
||||
}
|
||||
priv->termios_initialized = 1;
|
||||
}
|
||||
|
@ -946,12 +951,15 @@ static void cypress_set_termios (struct usb_serial_port *port, struct termios *o
|
|||
/* check if there are new settings */
|
||||
if (old_termios) {
|
||||
if ((cflag != old_termios->c_cflag) ||
|
||||
(RELEVANT_IFLAG(iflag) != RELEVANT_IFLAG(old_termios->c_iflag))) {
|
||||
dbg("%s - attempting to set new termios settings", __FUNCTION__);
|
||||
/* should make a copy of this in case something goes wrong in the function, we can restore it */
|
||||
(RELEVANT_IFLAG(iflag) !=
|
||||
RELEVANT_IFLAG(old_termios->c_iflag))) {
|
||||
dbg("%s - attempting to set new termios settings",
|
||||
__FUNCTION__);
|
||||
/* should make a copy of this in case something goes
|
||||
* wrong in the function, we can restore it */
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
priv->tmp_termios = *(tty->termios);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
} else {
|
||||
dbg("%s - nothing to do, exiting", __FUNCTION__);
|
||||
return;
|
||||
|
@ -962,21 +970,34 @@ static void cypress_set_termios (struct usb_serial_port *port, struct termios *o
|
|||
/* set number of data bits, parity, stop bits */
|
||||
/* when parity is disabled the parity type bit is ignored */
|
||||
|
||||
stop_bits = cflag & CSTOPB ? 1 : 0; /* 1 means 2 stop bits, 0 means 1 stop bit */
|
||||
|
||||
/* 1 means 2 stop bits, 0 means 1 stop bit */
|
||||
stop_bits = cflag & CSTOPB ? 1 : 0;
|
||||
|
||||
if (cflag & PARENB) {
|
||||
parity_enable = 1;
|
||||
parity_type = cflag & PARODD ? 1 : 0; /* 1 means odd parity, 0 means even parity */
|
||||
/* 1 means odd parity, 0 means even parity */
|
||||
parity_type = cflag & PARODD ? 1 : 0;
|
||||
} else
|
||||
parity_enable = parity_type = 0;
|
||||
|
||||
if (cflag & CSIZE) {
|
||||
switch (cflag & CSIZE) {
|
||||
case CS5: data_bits = 0; break;
|
||||
case CS6: data_bits = 1; break;
|
||||
case CS7: data_bits = 2; break;
|
||||
case CS8: data_bits = 3; break;
|
||||
default: err("%s - CSIZE was set, but not CS5-CS8", __FUNCTION__); data_bits = 3;
|
||||
case CS5:
|
||||
data_bits = 0;
|
||||
break;
|
||||
case CS6:
|
||||
data_bits = 1;
|
||||
break;
|
||||
case CS7:
|
||||
data_bits = 2;
|
||||
break;
|
||||
case CS8:
|
||||
data_bits = 3;
|
||||
break;
|
||||
default:
|
||||
err("%s - CSIZE was set, but not CS5-CS8",
|
||||
__FUNCTION__);
|
||||
data_bits = 3;
|
||||
}
|
||||
} else
|
||||
data_bits = 3;
|
||||
|
@ -991,63 +1012,85 @@ static void cypress_set_termios (struct usb_serial_port *port, struct termios *o
|
|||
} else {
|
||||
baud_mask = (cflag & CBAUD);
|
||||
switch(baud_mask) {
|
||||
case B300: dbg("%s - setting baud 300bps", __FUNCTION__); break;
|
||||
case B600: dbg("%s - setting baud 600bps", __FUNCTION__); break;
|
||||
case B1200: dbg("%s - setting baud 1200bps", __FUNCTION__); break;
|
||||
case B2400: dbg("%s - setting baud 2400bps", __FUNCTION__); break;
|
||||
case B4800: dbg("%s - setting baud 4800bps", __FUNCTION__); break;
|
||||
case B9600: dbg("%s - setting baud 9600bps", __FUNCTION__); break;
|
||||
case B19200: dbg("%s - setting baud 19200bps", __FUNCTION__); break;
|
||||
case B38400: dbg("%s - setting baud 38400bps", __FUNCTION__); break;
|
||||
case B57600: dbg("%s - setting baud 57600bps", __FUNCTION__); break;
|
||||
case B115200: dbg("%s - setting baud 115200bps", __FUNCTION__); break;
|
||||
default: dbg("%s - unknown masked baud rate", __FUNCTION__);
|
||||
case B300:
|
||||
dbg("%s - setting baud 300bps", __FUNCTION__);
|
||||
break;
|
||||
case B600:
|
||||
dbg("%s - setting baud 600bps", __FUNCTION__);
|
||||
break;
|
||||
case B1200:
|
||||
dbg("%s - setting baud 1200bps", __FUNCTION__);
|
||||
break;
|
||||
case B2400:
|
||||
dbg("%s - setting baud 2400bps", __FUNCTION__);
|
||||
break;
|
||||
case B4800:
|
||||
dbg("%s - setting baud 4800bps", __FUNCTION__);
|
||||
break;
|
||||
case B9600:
|
||||
dbg("%s - setting baud 9600bps", __FUNCTION__);
|
||||
break;
|
||||
case B19200:
|
||||
dbg("%s - setting baud 19200bps", __FUNCTION__);
|
||||
break;
|
||||
case B38400:
|
||||
dbg("%s - setting baud 38400bps", __FUNCTION__);
|
||||
break;
|
||||
case B57600:
|
||||
dbg("%s - setting baud 57600bps", __FUNCTION__);
|
||||
break;
|
||||
case B115200:
|
||||
dbg("%s - setting baud 115200bps", __FUNCTION__);
|
||||
break;
|
||||
default:
|
||||
dbg("%s - unknown masked baud rate", __FUNCTION__);
|
||||
}
|
||||
priv->line_control = (CONTROL_DTR | CONTROL_RTS);
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
dbg("%s - sending %d stop_bits, %d parity_enable, %d parity_type, %d data_bits (+5)", __FUNCTION__,
|
||||
stop_bits, parity_enable, parity_type, data_bits);
|
||||
|
||||
cypress_serial_control(port, baud_mask, data_bits, stop_bits, parity_enable,
|
||||
parity_type, 0, CYPRESS_SET_CONFIG);
|
||||
dbg("%s - sending %d stop_bits, %d parity_enable, %d parity_type, "
|
||||
"%d data_bits (+5)", __FUNCTION__, stop_bits,
|
||||
parity_enable, parity_type, data_bits);
|
||||
|
||||
/* we perform a CYPRESS_GET_CONFIG so that the current settings are filled into the private structure
|
||||
* this should confirm that all is working if it returns what we just set */
|
||||
cypress_serial_control(port, baud_mask, data_bits, stop_bits,
|
||||
parity_enable, parity_type, 0, CYPRESS_SET_CONFIG);
|
||||
|
||||
/* we perform a CYPRESS_GET_CONFIG so that the current settings are
|
||||
* filled into the private structure this should confirm that all is
|
||||
* working if it returns what we just set */
|
||||
cypress_serial_control(port, 0, 0, 0, 0, 0, 0, CYPRESS_GET_CONFIG);
|
||||
|
||||
/* Here we can define custom tty settings for devices
|
||||
*
|
||||
* the main tty termios flag base comes from empeg.c
|
||||
*/
|
||||
/* Here we can define custom tty settings for devices; the main tty
|
||||
* termios flag base comes from empeg.c */
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
if ( (priv->chiptype == CT_EARTHMATE) && (priv->baud_rate == 4800) ) {
|
||||
|
||||
dbg("Using custom termios settings for a baud rate of 4800bps.");
|
||||
dbg("Using custom termios settings for a baud rate of "
|
||||
"4800bps.");
|
||||
/* define custom termios settings for NMEA protocol */
|
||||
|
||||
tty->termios->c_iflag /* input modes - */
|
||||
&= ~(IGNBRK /* disable ignore break */
|
||||
| BRKINT /* disable break causes interrupt */
|
||||
| PARMRK /* disable mark parity errors */
|
||||
| ISTRIP /* disable clear high bit of input characters */
|
||||
| INLCR /* disable translate NL to CR */
|
||||
| IGNCR /* disable ignore CR */
|
||||
| ICRNL /* disable translate CR to NL */
|
||||
| IXON); /* disable enable XON/XOFF flow control */
|
||||
|
||||
tty->termios->c_oflag /* output modes */
|
||||
&= ~OPOST; /* disable postprocess output characters */
|
||||
|
||||
tty->termios->c_lflag /* line discipline modes */
|
||||
&= ~(ECHO /* disable echo input characters */
|
||||
| ECHONL /* disable echo new line */
|
||||
| ICANON /* disable erase, kill, werase, and rprnt special characters */
|
||||
| ISIG /* disable interrupt, quit, and suspend special characters */
|
||||
| IEXTEN); /* disable non-POSIX special characters */
|
||||
&= ~(IGNBRK /* disable ignore break */
|
||||
| BRKINT /* disable break causes interrupt */
|
||||
| PARMRK /* disable mark parity errors */
|
||||
| ISTRIP /* disable clear high bit of input char */
|
||||
| INLCR /* disable translate NL to CR */
|
||||
| IGNCR /* disable ignore CR */
|
||||
| ICRNL /* disable translate CR to NL */
|
||||
| IXON); /* disable enable XON/XOFF flow control */
|
||||
|
||||
tty->termios->c_oflag /* output modes */
|
||||
&= ~OPOST; /* disable postprocess output char */
|
||||
|
||||
tty->termios->c_lflag /* line discipline modes */
|
||||
&= ~(ECHO /* disable echo input characters */
|
||||
| ECHONL /* disable echo new line */
|
||||
| ICANON /* disable erase, kill, werase, and rprnt
|
||||
special characters */
|
||||
| ISIG /* disable interrupt, quit, and suspend
|
||||
special characters */
|
||||
| IEXTEN); /* disable non-POSIX special characters */
|
||||
} /* CT_CYPHIDCOM: Application should handle this for device */
|
||||
|
||||
linechange = (priv->line_control != oldlines);
|
||||
|
@ -1060,7 +1103,7 @@ static void cypress_set_termios (struct usb_serial_port *port, struct termios *o
|
|||
}
|
||||
} /* cypress_set_termios */
|
||||
|
||||
|
||||
|
||||
/* returns amount of data still left in soft buffer */
|
||||
static int cypress_chars_in_buffer(struct usb_serial_port *port)
|
||||
{
|
||||
|
@ -1088,7 +1131,7 @@ static void cypress_throttle (struct usb_serial_port *port)
|
|||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
priv->rx_flags = THROTTLED;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1110,7 +1153,8 @@ static void cypress_unthrottle (struct usb_serial_port *port)
|
|||
|
||||
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
|
||||
if (result)
|
||||
dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __FUNCTION__, result);
|
||||
dev_err(&port->dev, "%s - failed submitting read urb, "
|
||||
"error %d\n", __FUNCTION__, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1122,7 +1166,7 @@ static void cypress_read_int_callback(struct urb *urb, struct pt_regs *regs)
|
|||
struct tty_struct *tty;
|
||||
unsigned char *data = urb->transfer_buffer;
|
||||
unsigned long flags;
|
||||
char tty_flag = TTY_NORMAL;
|
||||
char tty_flag = TTY_NORMAL;
|
||||
int havedata = 0;
|
||||
int bytes = 0;
|
||||
int result;
|
||||
|
@ -1131,7 +1175,8 @@ static void cypress_read_int_callback(struct urb *urb, struct pt_regs *regs)
|
|||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
||||
if (urb->status) {
|
||||
dbg("%s - nonzero read status received: %d", __FUNCTION__, urb->status);
|
||||
dbg("%s - nonzero read status received: %d", __FUNCTION__,
|
||||
urb->status);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1155,51 +1200,55 @@ static void cypress_read_int_callback(struct urb *urb, struct pt_regs *regs)
|
|||
case 32:
|
||||
/* This is for the CY7C64013... */
|
||||
priv->current_status = data[0] & 0xF8;
|
||||
bytes = data[1]+2;
|
||||
i=2;
|
||||
bytes = data[1] + 2;
|
||||
i = 2;
|
||||
if (bytes > 2)
|
||||
havedata = 1;
|
||||
break;
|
||||
case 8:
|
||||
/* This is for the CY7C63743... */
|
||||
priv->current_status = data[0] & 0xF8;
|
||||
bytes = (data[0] & 0x07)+1;
|
||||
i=1;
|
||||
bytes = (data[0] & 0x07) + 1;
|
||||
i = 1;
|
||||
if (bytes > 1)
|
||||
havedata = 1;
|
||||
break;
|
||||
default:
|
||||
dbg("%s - wrong packet size - received %d bytes", __FUNCTION__, urb->actual_length);
|
||||
dbg("%s - wrong packet size - received %d bytes",
|
||||
__FUNCTION__, urb->actual_length);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
goto continue_read;
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
usb_serial_debug_data (debug, &port->dev, __FUNCTION__, urb->actual_length, data);
|
||||
usb_serial_debug_data (debug, &port->dev, __FUNCTION__,
|
||||
urb->actual_length, data);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
/* check to see if status has changed */
|
||||
if (priv != NULL) {
|
||||
if (priv->current_status != priv->prev_status) {
|
||||
priv->diff_status |= priv->current_status ^ priv->prev_status;
|
||||
priv->diff_status |= priv->current_status ^
|
||||
priv->prev_status;
|
||||
wake_up_interruptible(&priv->delta_msr_wait);
|
||||
priv->prev_status = priv->current_status;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
/* hangup, as defined in acm.c... this might be a bad place for it though */
|
||||
if (tty && !(tty->termios->c_cflag & CLOCAL) && !(priv->current_status & UART_CD)) {
|
||||
/* hangup, as defined in acm.c... this might be a bad place for it
|
||||
* though */
|
||||
if (tty && !(tty->termios->c_cflag & CLOCAL) &&
|
||||
!(priv->current_status & UART_CD)) {
|
||||
dbg("%s - calling hangup", __FUNCTION__);
|
||||
tty_hangup(tty);
|
||||
goto continue_read;
|
||||
}
|
||||
|
||||
/* There is one error bit... I'm assuming it is a parity error indicator
|
||||
* as the generic firmware will set this bit to 1 if a parity error occurs.
|
||||
* I can not find reference to any other error events.
|
||||
*
|
||||
*/
|
||||
/* There is one error bit... I'm assuming it is a parity error
|
||||
* indicator as the generic firmware will set this bit to 1 if a
|
||||
* parity error occurs.
|
||||
* I can not find reference to any other error events. */
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
if (priv->current_status & CYP_ERROR) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
@ -1211,7 +1260,8 @@ static void cypress_read_int_callback(struct urb *urb, struct pt_regs *regs)
|
|||
/* process read if there is data other than line status */
|
||||
if (tty && (bytes > i)) {
|
||||
for (; i < bytes ; ++i) {
|
||||
dbg("pushing byte number %d - %d - %c",i,data[i],data[i]);
|
||||
dbg("pushing byte number %d - %d - %c", i, data[i],
|
||||
data[i]);
|
||||
if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
|
||||
tty_flip_buffer_push(tty);
|
||||
}
|
||||
|
@ -1221,25 +1271,28 @@ static void cypress_read_int_callback(struct urb *urb, struct pt_regs *regs)
|
|||
}
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
priv->bytes_in += bytes; /* control and status byte(s) are also counted */
|
||||
/* control and status byte(s) are also counted */
|
||||
priv->bytes_in += bytes;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
continue_read:
|
||||
|
||||
/* Continue trying to always read... unless the port has closed. */
|
||||
|
||||
/* Continue trying to always read... unless the port has closed. */
|
||||
|
||||
if (port->open_count > 0) {
|
||||
usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev,
|
||||
usb_rcvintpipe(port->serial->dev, port->interrupt_in_endpointAddress),
|
||||
port->interrupt_in_urb->transfer_buffer,
|
||||
port->interrupt_in_urb->transfer_buffer_length,
|
||||
cypress_read_int_callback, port,
|
||||
interval);
|
||||
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
|
||||
if (result)
|
||||
dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
|
||||
usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev,
|
||||
usb_rcvintpipe(port->serial->dev,
|
||||
port->interrupt_in_endpointAddress),
|
||||
port->interrupt_in_urb->transfer_buffer,
|
||||
port->interrupt_in_urb->transfer_buffer_length,
|
||||
cypress_read_int_callback, port, interval);
|
||||
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
|
||||
if (result)
|
||||
dev_err(&urb->dev->dev, "%s - failed resubmitting "
|
||||
"read urb, error %d\n", __FUNCTION__,
|
||||
result);
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
} /* cypress_read_int_callback */
|
||||
|
||||
|
|
|
@ -269,6 +269,8 @@
|
|||
#define DRIVER_DESC "USB FTDI Serial Converters Driver"
|
||||
|
||||
static int debug;
|
||||
static __u16 vendor = FTDI_VID;
|
||||
static __u16 product;
|
||||
|
||||
/* struct ftdi_sio_quirk is used by devices requiring special attention. */
|
||||
struct ftdi_sio_quirk {
|
||||
|
@ -407,6 +409,34 @@ static struct usb_device_id id_table_combined [] = {
|
|||
{ USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88F_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_ELV_UO100_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_ELV_UM100_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_ELV_UR100_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_ELV_ALC8500_PID) },
|
||||
/*
|
||||
* These will probably use user-space drivers. Uncomment them if
|
||||
* you need them or use the user-specified vendor/product module
|
||||
* parameters (see ftdi_sio.h for the numbers). Make a fuss if
|
||||
* you think the driver should recognize any of them by default.
|
||||
*/
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_CLI7000_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_PPS7330_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_TFM100_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_UDF77_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_UIO88_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_UAD8_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_UDA7_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_USI2_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_T1100_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_PCD200_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_ULA200_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1000PC_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_CSI8_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_EM1000DL_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_PCK100_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_RFP500_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_FS20SIG_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_WS300PC_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1300PC_PID) }, */
|
||||
/* { USB_DEVICE(FTDI_VID, FTDI_ELV_WS500_PID) }, */
|
||||
{ USB_DEVICE(FTDI_VID, LINX_SDMUSBQSS_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, LINX_MASTERDEVEL2_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, LINX_FUTURE_0_PID) },
|
||||
|
@ -418,6 +448,7 @@ static struct usb_device_id id_table_combined [] = {
|
|||
{ USB_DEVICE(INTREPID_VID, INTREPID_VALUECAN_PID) },
|
||||
{ USB_DEVICE(INTREPID_VID, INTREPID_NEOVI_PID) },
|
||||
{ USB_DEVICE(FALCOM_VID, FALCOM_TWIST_PID) },
|
||||
{ USB_DEVICE(FALCOM_VID, FALCOM_SAMBA_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_SUUNTO_SPORTS_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_RM_CANVIEW_PID) },
|
||||
{ USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) },
|
||||
|
@ -427,12 +458,21 @@ static struct usb_device_id id_table_combined [] = {
|
|||
{ USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_0_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_1_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_2_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_0_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_1_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_2_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_3_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_4_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_5_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_6_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_7_PID) },
|
||||
{ USB_DEVICE(MOBILITY_VID, MOBILITY_USB_SERIAL_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_ACTIVE_ROBOTS_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_MHAM_Y6_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_MHAM_Y8_PID) },
|
||||
{ USB_DEVICE(EVOLUTION_VID, EVOLUTION_ER1_PID) },
|
||||
{ } /* Terminating entry */
|
||||
{ }, /* Optional parameter entry */
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE (usb, id_table_combined);
|
||||
|
@ -2030,6 +2070,15 @@ static int __init ftdi_init (void)
|
|||
int retval;
|
||||
|
||||
dbg("%s", __FUNCTION__);
|
||||
if (vendor > 0 && product > 0) {
|
||||
/* Add user specified VID/PID to reserved element of table. */
|
||||
int i;
|
||||
for (i = 0; id_table_combined[i].idVendor; i++)
|
||||
;
|
||||
id_table_combined[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
|
||||
id_table_combined[i].idVendor = vendor;
|
||||
id_table_combined[i].idProduct = product;
|
||||
}
|
||||
retval = usb_serial_register(&ftdi_sio_device);
|
||||
if (retval)
|
||||
goto failed_sio_register;
|
||||
|
@ -2066,4 +2115,9 @@ MODULE_LICENSE("GPL");
|
|||
|
||||
module_param(debug, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(debug, "Debug enabled or not");
|
||||
module_param(vendor, ushort, 0);
|
||||
MODULE_PARM_DESC(vendor, "User specified vendor ID (default="
|
||||
__MODULE_STRING(FTDI_VID)")");
|
||||
module_param(product, ushort, 0);
|
||||
MODULE_PARM_DESC(vendor, "User specified product ID");
|
||||
|
||||
|
|
|
@ -142,10 +142,43 @@
|
|||
/* http://home.earthlink.net/~jrhees/USBUIRT/index.htm */
|
||||
#define FTDI_USB_UIRT_PID 0xF850 /* Product Id */
|
||||
|
||||
/* ELV USB Module UO100 (PID sent by Stefan Frings) */
|
||||
#define FTDI_ELV_UO100_PID 0xFB58 /* Product Id */
|
||||
/* ELV USB Module UM100 (PID sent by Arnim Laeuger) */
|
||||
#define FTDI_ELV_UM100_PID 0xFB5A /* Product Id */
|
||||
/*
|
||||
* ELV USB devices submitted by Christian Abt of ELV (www.elv.de).
|
||||
* All of these devices use FTDI's vendor ID (0x0403).
|
||||
*
|
||||
* The previously included PID for the UO 100 module was incorrect.
|
||||
* In fact, that PID was for ELV's UR 100 USB-RS232 converter (0xFB58).
|
||||
*
|
||||
* Armin Laeuger originally sent the PID for the UM 100 module.
|
||||
*/
|
||||
#define FTDI_ELV_UR100_PID 0xFB58 /* USB-RS232-Umsetzer (UR 100) */
|
||||
#define FTDI_ELV_UM100_PID 0xFB5A /* USB-Modul UM 100 */
|
||||
#define FTDI_ELV_UO100_PID 0xFB5B /* USB-Modul UO 100 */
|
||||
#define FTDI_ELV_ALC8500_PID 0xF06E /* ALC 8500 Expert */
|
||||
/* Additional ELV PIDs that default to using the FTDI D2XX drivers on
|
||||
* MS Windows, rather than the FTDI Virtual Com Port drivers.
|
||||
* Maybe these will be easier to use with the libftdi/libusb user-space
|
||||
* drivers, or possibly the Comedi drivers in some cases. */
|
||||
#define FTDI_ELV_CLI7000_PID 0xFB59 /* Computer-Light-Interface (CLI 7000) */
|
||||
#define FTDI_ELV_PPS7330_PID 0xFB5C /* Processor-Power-Supply (PPS 7330) */
|
||||
#define FTDI_ELV_TFM100_PID 0xFB5D /* Temperartur-Feuchte Messgeraet (TFM 100) */
|
||||
#define FTDI_ELV_UDF77_PID 0xFB5E /* USB DCF Funkurh (UDF 77) */
|
||||
#define FTDI_ELV_UIO88_PID 0xFB5F /* USB-I/O Interface (UIO 88) */
|
||||
#define FTDI_ELV_UAD8_PID 0xF068 /* USB-AD-Wandler (UAD 8) */
|
||||
#define FTDI_ELV_UDA7_PID 0xF069 /* USB-DA-Wandler (UDA 7) */
|
||||
#define FTDI_ELV_USI2_PID 0xF06A /* USB-Schrittmotoren-Interface (USI 2) */
|
||||
#define FTDI_ELV_T1100_PID 0xF06B /* Thermometer (T 1100) */
|
||||
#define FTDI_ELV_PCD200_PID 0xF06C /* PC-Datenlogger (PCD 200) */
|
||||
#define FTDI_ELV_ULA200_PID 0xF06D /* USB-LCD-Ansteuerung (ULA 200) */
|
||||
#define FTDI_ELV_FHZ1000PC_PID 0xF06F /* FHZ 1000 PC */
|
||||
#define FTDI_ELV_CSI8_PID 0xE0F0 /* Computer-Schalt-Interface (CSI 8) */
|
||||
#define FTDI_ELV_EM1000DL_PID 0xE0F1 /* PC-Datenlogger fuer Energiemonitor (EM 1000 DL) */
|
||||
#define FTDI_ELV_PCK100_PID 0xE0F2 /* PC-Kabeltester (PCK 100) */
|
||||
#define FTDI_ELV_RFP500_PID 0xE0F3 /* HF-Leistungsmesser (RFP 500) */
|
||||
#define FTDI_ELV_FS20SIG_PID 0xE0F4 /* Signalgeber (FS 20 SIG) */
|
||||
#define FTDI_ELV_WS300PC_PID 0xE0F6 /* PC-Wetterstation (WS 300 PC) */
|
||||
#define FTDI_ELV_FHZ1300PC_PID 0xE0E8 /* FHZ 1300 PC */
|
||||
#define FTDI_ELV_WS500_PID 0xE0E9 /* PC-Wetterstation (WS 500) */
|
||||
|
||||
/*
|
||||
* Definitions for ID TECH (www.idt-net.com) devices
|
||||
|
@ -222,6 +255,7 @@
|
|||
*/
|
||||
#define FALCOM_VID 0x0F94 /* Vendor Id */
|
||||
#define FALCOM_TWIST_PID 0x0001 /* Falcom Twist USB GPRS modem */
|
||||
#define FALCOM_SAMBA_PID 0x0005 /* Falcom Samba USB GPRS modem */
|
||||
|
||||
/*
|
||||
* SUUNTO product ids
|
||||
|
@ -276,6 +310,18 @@
|
|||
*/
|
||||
#define FTDI_ACTIVE_ROBOTS_PID 0xE548 /* USB comms board */
|
||||
|
||||
/*
|
||||
* Xsens Technologies BV products (http://www.xsens.com).
|
||||
*/
|
||||
#define XSENS_CONVERTER_0_PID 0xD388
|
||||
#define XSENS_CONVERTER_1_PID 0xD389
|
||||
#define XSENS_CONVERTER_2_PID 0xD38A
|
||||
#define XSENS_CONVERTER_3_PID 0xD38B
|
||||
#define XSENS_CONVERTER_4_PID 0xD38C
|
||||
#define XSENS_CONVERTER_5_PID 0xD38D
|
||||
#define XSENS_CONVERTER_6_PID 0xD38E
|
||||
#define XSENS_CONVERTER_7_PID 0xD38F
|
||||
|
||||
/*
|
||||
* Evolution Robotics products (http://www.evolution.com/).
|
||||
* Submitted by Shawn M. Lavelle.
|
||||
|
|
|
@ -383,11 +383,8 @@ static int keyspan_write(struct usb_serial_port *port,
|
|||
dbg("%s - endpoint %d flip %d", __FUNCTION__, usb_pipeendpoint(this_urb->pipe), flip);
|
||||
|
||||
if (this_urb->status == -EINPROGRESS) {
|
||||
if (this_urb->transfer_flags & URB_ASYNC_UNLINK)
|
||||
break;
|
||||
if (time_before(jiffies, p_priv->tx_start_time[flip] + 10 * HZ))
|
||||
break;
|
||||
this_urb->transfer_flags |= URB_ASYNC_UNLINK;
|
||||
usb_unlink_urb(this_urb);
|
||||
break;
|
||||
}
|
||||
|
@ -402,7 +399,6 @@ static int keyspan_write(struct usb_serial_port *port,
|
|||
/* send the data out the bulk port */
|
||||
this_urb->transfer_buffer_length = todo + dataOffset;
|
||||
|
||||
this_urb->transfer_flags &= ~URB_ASYNC_UNLINK;
|
||||
this_urb->dev = port->serial->dev;
|
||||
if ((err = usb_submit_urb(this_urb, GFP_ATOMIC)) != 0) {
|
||||
dbg("usb_submit_urb(write bulk) failed (%d)", err);
|
||||
|
@ -1119,10 +1115,8 @@ static int keyspan_open (struct usb_serial_port *port, struct file *filp)
|
|||
|
||||
static inline void stop_urb(struct urb *urb)
|
||||
{
|
||||
if (urb && urb->status == -EINPROGRESS) {
|
||||
urb->transfer_flags &= ~URB_ASYNC_UNLINK;
|
||||
if (urb && urb->status == -EINPROGRESS)
|
||||
usb_kill_urb(urb);
|
||||
}
|
||||
}
|
||||
|
||||
static void keyspan_close(struct usb_serial_port *port, struct file *filp)
|
||||
|
|
|
@ -45,29 +45,29 @@
|
|||
#include "usb-serial.h"
|
||||
|
||||
/* Function prototypes */
|
||||
static int option_open (struct usb_serial_port *port, struct file *filp);
|
||||
static void option_close (struct usb_serial_port *port, struct file *filp);
|
||||
static int option_startup (struct usb_serial *serial);
|
||||
static void option_shutdown (struct usb_serial *serial);
|
||||
static void option_rx_throttle (struct usb_serial_port *port);
|
||||
static void option_rx_unthrottle (struct usb_serial_port *port);
|
||||
static int option_write_room (struct usb_serial_port *port);
|
||||
static int option_open(struct usb_serial_port *port, struct file *filp);
|
||||
static void option_close(struct usb_serial_port *port, struct file *filp);
|
||||
static int option_startup(struct usb_serial *serial);
|
||||
static void option_shutdown(struct usb_serial *serial);
|
||||
static void option_rx_throttle(struct usb_serial_port *port);
|
||||
static void option_rx_unthrottle(struct usb_serial_port *port);
|
||||
static int option_write_room(struct usb_serial_port *port);
|
||||
|
||||
static void option_instat_callback(struct urb *urb, struct pt_regs *regs);
|
||||
|
||||
static int option_write (struct usb_serial_port *port,
|
||||
const unsigned char *buf, int count);
|
||||
static int option_write(struct usb_serial_port *port,
|
||||
const unsigned char *buf, int count);
|
||||
|
||||
static int option_chars_in_buffer (struct usb_serial_port *port);
|
||||
static int option_ioctl (struct usb_serial_port *port, struct file *file,
|
||||
unsigned int cmd, unsigned long arg);
|
||||
static void option_set_termios (struct usb_serial_port *port,
|
||||
struct termios *old);
|
||||
static void option_break_ctl (struct usb_serial_port *port, int break_state);
|
||||
static int option_tiocmget (struct usb_serial_port *port, struct file *file);
|
||||
static int option_tiocmset (struct usb_serial_port *port, struct file *file,
|
||||
unsigned int set, unsigned int clear);
|
||||
static int option_send_setup (struct usb_serial_port *port);
|
||||
static int option_chars_in_buffer(struct usb_serial_port *port);
|
||||
static int option_ioctl(struct usb_serial_port *port, struct file *file,
|
||||
unsigned int cmd, unsigned long arg);
|
||||
static void option_set_termios(struct usb_serial_port *port,
|
||||
struct termios *old);
|
||||
static void option_break_ctl(struct usb_serial_port *port, int break_state);
|
||||
static int option_tiocmget(struct usb_serial_port *port, struct file *file);
|
||||
static int option_tiocmset(struct usb_serial_port *port, struct file *file,
|
||||
unsigned int set, unsigned int clear);
|
||||
static int option_send_setup(struct usb_serial_port *port);
|
||||
|
||||
/* Vendor and product IDs */
|
||||
#define OPTION_VENDOR_ID 0x0AF0
|
||||
|
@ -76,7 +76,6 @@ static int option_send_setup (struct usb_serial_port *port);
|
|||
#define OPTION_PRODUCT_FUSION 0x6000
|
||||
#define OPTION_PRODUCT_FUSION2 0x6300
|
||||
|
||||
|
||||
static struct usb_device_id option_ids[] = {
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
|
||||
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
|
||||
|
@ -129,7 +128,6 @@ static int debug;
|
|||
#define debug 0
|
||||
#endif
|
||||
|
||||
|
||||
/* per port private data */
|
||||
|
||||
#define N_IN_URB 4
|
||||
|
@ -156,10 +154,8 @@ struct option_port_private {
|
|||
unsigned long tx_start_time[N_OUT_URB];
|
||||
};
|
||||
|
||||
|
||||
/* Functions used by new usb-serial code. */
|
||||
static int __init
|
||||
option_init (void)
|
||||
static int __init option_init(void)
|
||||
{
|
||||
int retval;
|
||||
retval = usb_serial_register(&option_3port_device);
|
||||
|
@ -179,8 +175,7 @@ failed_3port_device_register:
|
|||
return retval;
|
||||
}
|
||||
|
||||
static void __exit
|
||||
option_exit (void)
|
||||
static void __exit option_exit(void)
|
||||
{
|
||||
usb_deregister (&option_driver);
|
||||
usb_serial_deregister (&option_3port_device);
|
||||
|
@ -189,39 +184,31 @@ option_exit (void)
|
|||
module_init(option_init);
|
||||
module_exit(option_exit);
|
||||
|
||||
static void
|
||||
option_rx_throttle (struct usb_serial_port *port)
|
||||
static void option_rx_throttle(struct usb_serial_port *port)
|
||||
{
|
||||
dbg("%s", __FUNCTION__);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
option_rx_unthrottle (struct usb_serial_port *port)
|
||||
static void option_rx_unthrottle(struct usb_serial_port *port)
|
||||
{
|
||||
dbg("%s", __FUNCTION__);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
option_break_ctl (struct usb_serial_port *port, int break_state)
|
||||
static void option_break_ctl(struct usb_serial_port *port, int break_state)
|
||||
{
|
||||
/* Unfortunately, I don't know how to send a break */
|
||||
dbg("%s", __FUNCTION__);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
option_set_termios (struct usb_serial_port *port,
|
||||
struct termios *old_termios)
|
||||
static void option_set_termios(struct usb_serial_port *port,
|
||||
struct termios *old_termios)
|
||||
{
|
||||
dbg("%s", __FUNCTION__);
|
||||
|
||||
option_send_setup(port);
|
||||
}
|
||||
|
||||
static int
|
||||
option_tiocmget (struct usb_serial_port *port, struct file *file)
|
||||
static int option_tiocmget(struct usb_serial_port *port, struct file *file)
|
||||
{
|
||||
unsigned int value;
|
||||
struct option_port_private *portdata;
|
||||
|
@ -238,9 +225,8 @@ option_tiocmget (struct usb_serial_port *port, struct file *file)
|
|||
return value;
|
||||
}
|
||||
|
||||
static int
|
||||
option_tiocmset (struct usb_serial_port *port, struct file *file,
|
||||
unsigned int set, unsigned int clear)
|
||||
static int option_tiocmset(struct usb_serial_port *port, struct file *file,
|
||||
unsigned int set, unsigned int clear)
|
||||
{
|
||||
struct option_port_private *portdata;
|
||||
|
||||
|
@ -258,17 +244,15 @@ option_tiocmset (struct usb_serial_port *port, struct file *file,
|
|||
return option_send_setup(port);
|
||||
}
|
||||
|
||||
static int
|
||||
option_ioctl (struct usb_serial_port *port, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
static int option_ioctl(struct usb_serial_port *port, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
/* Write */
|
||||
static int
|
||||
option_write (struct usb_serial_port *port,
|
||||
const unsigned char *buf, int count)
|
||||
static int option_write(struct usb_serial_port *port,
|
||||
const unsigned char *buf, int count)
|
||||
{
|
||||
struct option_port_private *portdata;
|
||||
int i;
|
||||
|
@ -289,28 +273,29 @@ option_write (struct usb_serial_port *port,
|
|||
|
||||
this_urb = portdata->out_urbs[i];
|
||||
if (this_urb->status == -EINPROGRESS) {
|
||||
if (this_urb->transfer_flags & URB_ASYNC_UNLINK)
|
||||
if (time_before(jiffies,
|
||||
portdata->tx_start_time[i] + 10 * HZ))
|
||||
continue;
|
||||
if (time_before(jiffies, portdata->tx_start_time[i] + 10 * HZ))
|
||||
continue;
|
||||
this_urb->transfer_flags |= URB_ASYNC_UNLINK;
|
||||
usb_unlink_urb(this_urb);
|
||||
continue;
|
||||
}
|
||||
if (this_urb->status != 0)
|
||||
dbg("usb_write %p failed (err=%d)", this_urb, this_urb->status);
|
||||
dbg("usb_write %p failed (err=%d)",
|
||||
this_urb, this_urb->status);
|
||||
|
||||
dbg("%s: endpoint %d buf %d", __FUNCTION__, usb_pipeendpoint(this_urb->pipe), i);
|
||||
dbg("%s: endpoint %d buf %d", __FUNCTION__,
|
||||
usb_pipeendpoint(this_urb->pipe), i);
|
||||
|
||||
/* send the data */
|
||||
memcpy (this_urb->transfer_buffer, buf, todo);
|
||||
this_urb->transfer_buffer_length = todo;
|
||||
|
||||
this_urb->transfer_flags &= ~URB_ASYNC_UNLINK;
|
||||
this_urb->dev = port->serial->dev;
|
||||
err = usb_submit_urb(this_urb, GFP_ATOMIC);
|
||||
if (err) {
|
||||
dbg("usb_submit_urb %p (write bulk) failed (%d, has %d)", this_urb, err, this_urb->status);
|
||||
dbg("usb_submit_urb %p (write bulk) failed "
|
||||
"(%d, has %d)", this_urb,
|
||||
err, this_urb->status);
|
||||
continue;
|
||||
}
|
||||
portdata->tx_start_time[i] = jiffies;
|
||||
|
@ -323,8 +308,7 @@ option_write (struct usb_serial_port *port,
|
|||
return count;
|
||||
}
|
||||
|
||||
static void
|
||||
option_indat_callback (struct urb *urb, struct pt_regs *regs)
|
||||
static void option_indat_callback(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
int i, err;
|
||||
int endpoint;
|
||||
|
@ -357,14 +341,14 @@ option_indat_callback (struct urb *urb, struct pt_regs *regs)
|
|||
if (port->open_count && urb->status != -ESHUTDOWN) {
|
||||
err = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (err)
|
||||
printk(KERN_ERR "%s: resubmit read urb failed. (%d)", __FUNCTION__, err);
|
||||
printk(KERN_ERR "%s: resubmit read urb failed. "
|
||||
"(%d)", __FUNCTION__, err);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
option_outdat_callback (struct urb *urb, struct pt_regs *regs)
|
||||
static void option_outdat_callback(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct usb_serial_port *port;
|
||||
|
||||
|
@ -376,8 +360,7 @@ option_outdat_callback (struct urb *urb, struct pt_regs *regs)
|
|||
schedule_work(&port->work);
|
||||
}
|
||||
|
||||
static void
|
||||
option_instat_callback (struct urb *urb, struct pt_regs *regs)
|
||||
static void option_instat_callback(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
int err;
|
||||
struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
|
||||
|
@ -395,10 +378,12 @@ option_instat_callback (struct urb *urb, struct pt_regs *regs)
|
|||
dbg("%s: NULL req_pkt\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
if ((req_pkt->bRequestType == 0xA1) && (req_pkt->bRequest == 0x20)) {
|
||||
if ((req_pkt->bRequestType == 0xA1) &&
|
||||
(req_pkt->bRequest == 0x20)) {
|
||||
int old_dcd_state;
|
||||
unsigned char signals = *((unsigned char *)
|
||||
urb->transfer_buffer + sizeof(struct usb_ctrlrequest));
|
||||
urb->transfer_buffer +
|
||||
sizeof(struct usb_ctrlrequest));
|
||||
|
||||
dbg("%s: signal x%x", __FUNCTION__, signals);
|
||||
|
||||
|
@ -408,12 +393,13 @@ option_instat_callback (struct urb *urb, struct pt_regs *regs)
|
|||
portdata->dsr_state = ((signals & 0x02) ? 1 : 0);
|
||||
portdata->ri_state = ((signals & 0x08) ? 1 : 0);
|
||||
|
||||
if (port->tty && !C_CLOCAL(port->tty)
|
||||
&& old_dcd_state && !portdata->dcd_state) {
|
||||
if (port->tty && !C_CLOCAL(port->tty) &&
|
||||
old_dcd_state && !portdata->dcd_state)
|
||||
tty_hangup(port->tty);
|
||||
}
|
||||
} else
|
||||
dbg("%s: type %x req %x", __FUNCTION__, req_pkt->bRequestType,req_pkt->bRequest);
|
||||
} else {
|
||||
dbg("%s: type %x req %x", __FUNCTION__,
|
||||
req_pkt->bRequestType,req_pkt->bRequest);
|
||||
}
|
||||
} else
|
||||
dbg("%s: error %d", __FUNCTION__, urb->status);
|
||||
|
||||
|
@ -422,13 +408,12 @@ option_instat_callback (struct urb *urb, struct pt_regs *regs)
|
|||
urb->dev = serial->dev;
|
||||
err = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (err)
|
||||
dbg("%s: resubmit intr urb failed. (%d)", __FUNCTION__, err);
|
||||
dbg("%s: resubmit intr urb failed. (%d)",
|
||||
__FUNCTION__, err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
option_write_room (struct usb_serial_port *port)
|
||||
static int option_write_room(struct usb_serial_port *port)
|
||||
{
|
||||
struct option_port_private *portdata;
|
||||
int i;
|
||||
|
@ -447,9 +432,7 @@ option_write_room (struct usb_serial_port *port)
|
|||
return data_len;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
option_chars_in_buffer (struct usb_serial_port *port)
|
||||
static int option_chars_in_buffer(struct usb_serial_port *port)
|
||||
{
|
||||
struct option_port_private *portdata;
|
||||
int i;
|
||||
|
@ -467,9 +450,7 @@ option_chars_in_buffer (struct usb_serial_port *port)
|
|||
return data_len;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
option_open (struct usb_serial_port *port, struct file *filp)
|
||||
static int option_open(struct usb_serial_port *port, struct file *filp)
|
||||
{
|
||||
struct option_port_private *portdata;
|
||||
struct usb_serial *serial = port->serial;
|
||||
|
@ -490,17 +471,21 @@ option_open (struct usb_serial_port *port, struct file *filp)
|
|||
if (! urb)
|
||||
continue;
|
||||
if (urb->dev != serial->dev) {
|
||||
dbg("%s: dev %p != %p", __FUNCTION__, urb->dev, serial->dev);
|
||||
dbg("%s: dev %p != %p", __FUNCTION__,
|
||||
urb->dev, serial->dev);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* make sure endpoint data toggle is synchronized with the device */
|
||||
|
||||
/*
|
||||
* make sure endpoint data toggle is synchronized with the
|
||||
* device
|
||||
*/
|
||||
usb_clear_halt(urb->dev, urb->pipe);
|
||||
|
||||
err = usb_submit_urb(urb, GFP_KERNEL);
|
||||
if (err) {
|
||||
dbg("%s: submit urb %d failed (%d) %d", __FUNCTION__, i, err,
|
||||
dbg("%s: submit urb %d failed (%d) %d",
|
||||
__FUNCTION__, i, err,
|
||||
urb->transfer_buffer_length);
|
||||
}
|
||||
}
|
||||
|
@ -511,7 +496,8 @@ option_open (struct usb_serial_port *port, struct file *filp)
|
|||
if (! urb)
|
||||
continue;
|
||||
urb->dev = serial->dev;
|
||||
/* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 0); */
|
||||
/* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
|
||||
usb_pipeout(urb->pipe), 0); */
|
||||
}
|
||||
|
||||
port->tty->low_latency = 1;
|
||||
|
@ -521,17 +507,13 @@ option_open (struct usb_serial_port *port, struct file *filp)
|
|||
return (0);
|
||||
}
|
||||
|
||||
static inline void
|
||||
stop_urb (struct urb *urb)
|
||||
static inline void stop_urb(struct urb *urb)
|
||||
{
|
||||
if (urb && urb->status == -EINPROGRESS) {
|
||||
urb->transfer_flags &= ~URB_ASYNC_UNLINK;
|
||||
if (urb && urb->status == -EINPROGRESS)
|
||||
usb_kill_urb(urb);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
option_close (struct usb_serial_port *port, struct file *filp)
|
||||
static void option_close(struct usb_serial_port *port, struct file *filp)
|
||||
{
|
||||
int i;
|
||||
struct usb_serial *serial = port->serial;
|
||||
|
@ -555,12 +537,10 @@ option_close (struct usb_serial_port *port, struct file *filp)
|
|||
port->tty = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Helper functions used by option_setup_urbs */
|
||||
static struct urb *
|
||||
option_setup_urb (struct usb_serial *serial, int endpoint,
|
||||
int dir, void *ctx, char *buf, int len,
|
||||
void (*callback)(struct urb *, struct pt_regs *regs))
|
||||
static struct urb *option_setup_urb(struct usb_serial *serial, int endpoint,
|
||||
int dir, void *ctx, char *buf, int len,
|
||||
void (*callback)(struct urb *, struct pt_regs *regs))
|
||||
{
|
||||
struct urb *urb;
|
||||
|
||||
|
@ -582,8 +562,7 @@ option_setup_urb (struct usb_serial *serial, int endpoint,
|
|||
}
|
||||
|
||||
/* Setup urbs */
|
||||
static void
|
||||
option_setup_urbs (struct usb_serial *serial)
|
||||
static void option_setup_urbs(struct usb_serial *serial)
|
||||
{
|
||||
int j;
|
||||
struct usb_serial_port *port;
|
||||
|
@ -609,9 +588,7 @@ option_setup_urbs (struct usb_serial *serial)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
option_send_setup (struct usb_serial_port *port)
|
||||
static int option_send_setup(struct usb_serial_port *port)
|
||||
{
|
||||
struct usb_serial *serial = port->serial;
|
||||
struct option_port_private *portdata;
|
||||
|
@ -627,16 +604,15 @@ option_send_setup (struct usb_serial_port *port)
|
|||
if (portdata->rts_state)
|
||||
val |= 0x02;
|
||||
|
||||
return usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
|
||||
0x22,0x21,val,0,NULL,0,USB_CTRL_SET_TIMEOUT);
|
||||
return usb_control_msg(serial->dev,
|
||||
usb_rcvctrlpipe(serial->dev, 0),
|
||||
0x22,0x21,val,0,NULL,0,USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
option_startup (struct usb_serial *serial)
|
||||
static int option_startup(struct usb_serial *serial)
|
||||
{
|
||||
int i, err;
|
||||
struct usb_serial_port *port;
|
||||
|
@ -647,9 +623,10 @@ option_startup (struct usb_serial *serial)
|
|||
/* Now setup per port private data */
|
||||
for (i = 0; i < serial->num_ports; i++) {
|
||||
port = serial->port[i];
|
||||
portdata = kmalloc(sizeof(struct option_port_private), GFP_KERNEL);
|
||||
portdata = kmalloc(sizeof(*portdata), GFP_KERNEL);
|
||||
if (!portdata) {
|
||||
dbg("%s: kmalloc for option_port_private (%d) failed!.", __FUNCTION__, i);
|
||||
dbg("%s: kmalloc for option_port_private (%d) failed!.",
|
||||
__FUNCTION__, i);
|
||||
return (1);
|
||||
}
|
||||
memset(portdata, 0, sizeof(struct option_port_private));
|
||||
|
@ -660,7 +637,8 @@ option_startup (struct usb_serial *serial)
|
|||
continue;
|
||||
err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
|
||||
if (err)
|
||||
dbg("%s: submit irq_in urb failed %d", __FUNCTION__, err);
|
||||
dbg("%s: submit irq_in urb failed %d",
|
||||
__FUNCTION__, err);
|
||||
}
|
||||
|
||||
option_setup_urbs(serial);
|
||||
|
@ -668,8 +646,7 @@ option_startup (struct usb_serial *serial)
|
|||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
option_shutdown (struct usb_serial *serial)
|
||||
static void option_shutdown(struct usb_serial *serial)
|
||||
{
|
||||
int i, j;
|
||||
struct usb_serial_port *port;
|
||||
|
|
|
@ -538,8 +538,10 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp)
|
|||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
||||
usb_clear_halt(serial->dev, port->write_urb->pipe);
|
||||
usb_clear_halt(serial->dev, port->read_urb->pipe);
|
||||
if (priv->type != HX) {
|
||||
usb_clear_halt(serial->dev, port->write_urb->pipe);
|
||||
usb_clear_halt(serial->dev, port->read_urb->pipe);
|
||||
}
|
||||
|
||||
buf = kmalloc(10, GFP_KERNEL);
|
||||
if (buf==NULL)
|
||||
|
|
|
@ -531,7 +531,7 @@ bailout_kref_put:
|
|||
|
||||
static void serial_close(struct tty_struct *tty, struct file * filp)
|
||||
{
|
||||
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
|
||||
if (!port)
|
||||
return;
|
||||
|
@ -561,7 +561,7 @@ static void serial_close(struct tty_struct *tty, struct file * filp)
|
|||
|
||||
static int serial_write (struct tty_struct * tty, const unsigned char *buf, int count)
|
||||
{
|
||||
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
int retval = -EINVAL;
|
||||
|
||||
dbg("%s - port %d, %d byte(s)", __FUNCTION__, port->number, count);
|
||||
|
@ -580,7 +580,7 @@ exit:
|
|||
|
||||
static int serial_write_room (struct tty_struct *tty)
|
||||
{
|
||||
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
int retval = -EINVAL;
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
@ -599,7 +599,7 @@ exit:
|
|||
|
||||
static int serial_chars_in_buffer (struct tty_struct *tty)
|
||||
{
|
||||
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
int retval = -EINVAL;
|
||||
|
||||
dbg("%s = port %d", __FUNCTION__, port->number);
|
||||
|
@ -618,7 +618,7 @@ exit:
|
|||
|
||||
static void serial_throttle (struct tty_struct * tty)
|
||||
{
|
||||
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
||||
|
@ -634,7 +634,7 @@ static void serial_throttle (struct tty_struct * tty)
|
|||
|
||||
static void serial_unthrottle (struct tty_struct * tty)
|
||||
{
|
||||
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
||||
|
@ -650,7 +650,7 @@ static void serial_unthrottle (struct tty_struct * tty)
|
|||
|
||||
static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
int retval = -ENODEV;
|
||||
|
||||
dbg("%s - port %d, cmd 0x%.4x", __FUNCTION__, port->number, cmd);
|
||||
|
@ -672,7 +672,7 @@ exit:
|
|||
|
||||
static void serial_set_termios (struct tty_struct *tty, struct termios * old)
|
||||
{
|
||||
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
||||
|
@ -688,7 +688,7 @@ static void serial_set_termios (struct tty_struct *tty, struct termios * old)
|
|||
|
||||
static void serial_break (struct tty_struct *tty, int break_state)
|
||||
{
|
||||
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
||||
|
@ -749,7 +749,7 @@ done:
|
|||
|
||||
static int serial_tiocmget (struct tty_struct *tty, struct file *file)
|
||||
{
|
||||
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
||||
|
@ -768,7 +768,7 @@ exit:
|
|||
static int serial_tiocmset (struct tty_struct *tty, struct file *file,
|
||||
unsigned int set, unsigned int clear)
|
||||
{
|
||||
struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
||||
|
@ -786,7 +786,7 @@ exit:
|
|||
|
||||
void usb_serial_port_softint(void *private)
|
||||
{
|
||||
struct usb_serial_port *port = (struct usb_serial_port *)private;
|
||||
struct usb_serial_port *port = private;
|
||||
struct tty_struct *tty;
|
||||
|
||||
dbg("%s - port %d", __FUNCTION__, port->number);
|
||||
|
|
|
@ -111,3 +111,15 @@ config USB_STORAGE_JUMPSHOT
|
|||
Say Y here to include additional code to support the Lexar Jumpshot
|
||||
USB CompactFlash reader.
|
||||
|
||||
|
||||
config USB_STORAGE_ONETOUCH
|
||||
bool "Support OneTouch Button on Maxtor Hard Drives (EXPERIMENTAL)"
|
||||
depends on USB_STORAGE && INPUT_EVDEV && EXPERIMENTAL
|
||||
help
|
||||
Say Y here to include additional code to support the Maxtor OneTouch
|
||||
USB hard drive's onetouch button.
|
||||
|
||||
This code registers the button on the front of Maxtor OneTouch USB
|
||||
hard drive's as an input device. An action can be associated with
|
||||
this input in any keybinding software. (e.g. gnome's keyboard short-
|
||||
cuts)
|
||||
|
|
|
@ -18,6 +18,7 @@ usb-storage-obj-$(CONFIG_USB_STORAGE_DPCM) += dpcm.o
|
|||
usb-storage-obj-$(CONFIG_USB_STORAGE_ISD200) += isd200.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_DATAFAB) += datafab.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += jumpshot.o
|
||||
usb-storage-obj-$(CONFIG_USB_STORAGE_ONETOUCH) += onetouch.o
|
||||
|
||||
usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \
|
||||
initializers.o $(usb-storage-obj-y)
|
||||
|
|
210
drivers/usb/storage/onetouch.c
Normal file
210
drivers/usb/storage/onetouch.c
Normal file
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* Support for the Maxtor OneTouch USB hard drive's button
|
||||
*
|
||||
* Current development and maintenance by:
|
||||
* Copyright (c) 2005 Nick Sillik <n.sillik@temple.edu>
|
||||
*
|
||||
* Initial work by:
|
||||
* Copyright (c) 2003 Erik Thyren <erth7411@student.uu.se>
|
||||
*
|
||||
* Based on usbmouse.c (Vojtech Pavlik) and xpad.c (Marko Friedemann)
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb_ch9.h>
|
||||
#include <linux/usb_input.h>
|
||||
#include "usb.h"
|
||||
#include "onetouch.h"
|
||||
#include "debug.h"
|
||||
|
||||
void onetouch_release_input(void *onetouch_);
|
||||
|
||||
struct usb_onetouch {
|
||||
char name[128];
|
||||
char phys[64];
|
||||
struct input_dev dev; /* input device interface */
|
||||
struct usb_device *udev; /* usb device */
|
||||
|
||||
struct urb *irq; /* urb for interrupt in report */
|
||||
unsigned char *data; /* input data */
|
||||
dma_addr_t data_dma;
|
||||
};
|
||||
|
||||
static void usb_onetouch_irq(struct urb *urb, struct pt_regs *regs)
|
||||
{
|
||||
struct usb_onetouch *onetouch = urb->context;
|
||||
signed char *data = onetouch->data;
|
||||
struct input_dev *dev = &onetouch->dev;
|
||||
int status;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0: /* success */
|
||||
break;
|
||||
case -ECONNRESET: /* unlink */
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
return;
|
||||
/* -EPIPE: should clear the halt */
|
||||
default: /* error */
|
||||
goto resubmit;
|
||||
}
|
||||
|
||||
input_regs(dev, regs);
|
||||
|
||||
input_report_key(&onetouch->dev, ONETOUCH_BUTTON,
|
||||
data[0] & 0x02);
|
||||
|
||||
input_sync(dev);
|
||||
resubmit:
|
||||
status = usb_submit_urb (urb, SLAB_ATOMIC);
|
||||
if (status)
|
||||
err ("can't resubmit intr, %s-%s/input0, status %d",
|
||||
onetouch->udev->bus->bus_name,
|
||||
onetouch->udev->devpath, status);
|
||||
}
|
||||
|
||||
static int usb_onetouch_open(struct input_dev *dev)
|
||||
{
|
||||
struct usb_onetouch *onetouch = dev->private;
|
||||
|
||||
onetouch->irq->dev = onetouch->udev;
|
||||
if (usb_submit_urb(onetouch->irq, GFP_KERNEL)) {
|
||||
err("usb_submit_urb failed");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_onetouch_close(struct input_dev *dev)
|
||||
{
|
||||
struct usb_onetouch *onetouch = dev->private;
|
||||
|
||||
usb_kill_urb(onetouch->irq);
|
||||
}
|
||||
|
||||
int onetouch_connect_input(struct us_data *ss)
|
||||
{
|
||||
struct usb_device *udev = ss->pusb_dev;
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct usb_onetouch *onetouch;
|
||||
int pipe, maxp;
|
||||
char path[64];
|
||||
|
||||
interface = ss->pusb_intf->cur_altsetting;
|
||||
|
||||
if (interface->desc.bNumEndpoints != 3)
|
||||
return -ENODEV;
|
||||
|
||||
endpoint = &interface->endpoint[2].desc;
|
||||
if(!(endpoint->bEndpointAddress & USB_DIR_IN))
|
||||
return -ENODEV;
|
||||
if((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
|
||||
!= USB_ENDPOINT_XFER_INT)
|
||||
return -ENODEV;
|
||||
|
||||
pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
|
||||
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
|
||||
|
||||
if (!(onetouch = kcalloc(1, sizeof(struct usb_onetouch), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
|
||||
onetouch->data = usb_buffer_alloc(udev, ONETOUCH_PKT_LEN,
|
||||
SLAB_ATOMIC, &onetouch->data_dma);
|
||||
if (!onetouch->data){
|
||||
kfree(onetouch);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
onetouch->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!onetouch->irq){
|
||||
kfree(onetouch);
|
||||
usb_buffer_free(udev, ONETOUCH_PKT_LEN,
|
||||
onetouch->data, onetouch->data_dma);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
onetouch->udev = udev;
|
||||
|
||||
set_bit(EV_KEY, onetouch->dev.evbit);
|
||||
set_bit(ONETOUCH_BUTTON, onetouch->dev.keybit);
|
||||
clear_bit(0, onetouch->dev.keybit);
|
||||
|
||||
onetouch->dev.private = onetouch;
|
||||
onetouch->dev.open = usb_onetouch_open;
|
||||
onetouch->dev.close = usb_onetouch_close;
|
||||
|
||||
usb_make_path(udev, path, sizeof(path));
|
||||
sprintf(onetouch->phys, "%s/input0", path);
|
||||
|
||||
onetouch->dev.name = onetouch->name;
|
||||
onetouch->dev.phys = onetouch->phys;
|
||||
|
||||
usb_to_input_id(udev, &onetouch->dev.id);
|
||||
|
||||
onetouch->dev.dev = &udev->dev;
|
||||
|
||||
if (udev->manufacturer)
|
||||
strcat(onetouch->name, udev->manufacturer);
|
||||
if (udev->product)
|
||||
sprintf(onetouch->name, "%s %s", onetouch->name,
|
||||
udev->product);
|
||||
if (!strlen(onetouch->name))
|
||||
sprintf(onetouch->name, "Maxtor Onetouch %04x:%04x",
|
||||
onetouch->dev.id.vendor, onetouch->dev.id.product);
|
||||
|
||||
usb_fill_int_urb(onetouch->irq, udev, pipe, onetouch->data,
|
||||
(maxp > 8 ? 8 : maxp),
|
||||
usb_onetouch_irq, onetouch, endpoint->bInterval);
|
||||
onetouch->irq->transfer_dma = onetouch->data_dma;
|
||||
onetouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
ss->extra_destructor = onetouch_release_input;
|
||||
ss->extra = onetouch;
|
||||
|
||||
input_register_device(&onetouch->dev);
|
||||
printk(KERN_INFO "usb-input: %s on %s\n", onetouch->dev.name, path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void onetouch_release_input(void *onetouch_)
|
||||
{
|
||||
struct usb_onetouch *onetouch = (struct usb_onetouch *) onetouch_;
|
||||
|
||||
if (onetouch) {
|
||||
usb_kill_urb(onetouch->irq);
|
||||
input_unregister_device(&onetouch->dev);
|
||||
usb_free_urb(onetouch->irq);
|
||||
usb_buffer_free(onetouch->udev, ONETOUCH_PKT_LEN,
|
||||
onetouch->data, onetouch->data_dma);
|
||||
printk(KERN_INFO "usb-input: deregistering %s\n",
|
||||
onetouch->dev.name);
|
||||
}
|
||||
}
|
9
drivers/usb/storage/onetouch.h
Normal file
9
drivers/usb/storage/onetouch.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#ifndef _ONETOUCH_H_
|
||||
#define _ONETOUCH_H_
|
||||
|
||||
#define ONETOUCH_PKT_LEN 0x02
|
||||
#define ONETOUCH_BUTTON KEY_PROG1
|
||||
|
||||
int onetouch_connect_input(struct us_data *ss);
|
||||
|
||||
#endif
|
|
@ -156,6 +156,14 @@ static int slave_configure(struct scsi_device *sdev)
|
|||
if (us->flags & US_FL_FIX_CAPACITY)
|
||||
sdev->fix_capacity = 1;
|
||||
|
||||
/* Some devices report a SCSI revision level above 2 but are
|
||||
* unable to handle the REPORT LUNS command (for which
|
||||
* support is mandatory at level 3). Since we already have
|
||||
* a Get-Max-LUN request, we won't lose much by setting the
|
||||
* revision level down to 2. The only devices that would be
|
||||
* affected are those with sparse LUNs. */
|
||||
sdev->scsi_level = SCSI_2;
|
||||
|
||||
/* USB-IDE bridges tend to report SK = 0x04 (Non-recoverable
|
||||
* Hardware Error) when any low-level error occurs,
|
||||
* recoverable or not. Setting this flag tells the SCSI
|
||||
|
|
|
@ -839,34 +839,31 @@ static int usbat_identify_device(struct us_data *us,
|
|||
rc = usbat_device_reset(us);
|
||||
if (rc != USB_STOR_TRANSPORT_GOOD)
|
||||
return rc;
|
||||
msleep(25);
|
||||
|
||||
/*
|
||||
* By examining the device signature after a reset, we can identify
|
||||
* whether the device supports the ATAPI packet interface.
|
||||
* The flash-devices do not support this, whereas the HP CDRW's obviously
|
||||
* do.
|
||||
*
|
||||
* This method is not ideal, but works because no other devices have been
|
||||
* produced based on the USBAT/USBAT02.
|
||||
*
|
||||
* Section 9.1 of the ATAPI-4 spec states (amongst other things) that
|
||||
* after a device reset, a Cylinder low of 0x14 indicates that the device
|
||||
* does support packet commands.
|
||||
* In attempt to distinguish between HP CDRW's and Flash readers, we now
|
||||
* execute the IDENTIFY PACKET DEVICE command. On ATA devices (i.e. flash
|
||||
* readers), this command should fail with error. On ATAPI devices (i.e.
|
||||
* CDROM drives), it should succeed.
|
||||
*/
|
||||
rc = usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_ME, &status);
|
||||
if (rc != USB_STOR_XFER_GOOD)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
rc = usbat_write(us, USBAT_ATA, USBAT_ATA_CMD, 0xA1);
|
||||
if (rc != USB_STOR_XFER_GOOD)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
US_DEBUGP("usbat_identify_device: Cylinder low is %02X\n", status);
|
||||
rc = usbat_get_status(us, &status);
|
||||
if (rc != USB_STOR_XFER_GOOD)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
if (status == 0x14) {
|
||||
// Check for error bit
|
||||
if (status & 0x01) {
|
||||
// Device is a CompactFlash reader/writer
|
||||
US_DEBUGP("usbat_identify_device: Detected Flash reader/writer\n");
|
||||
info->devicetype = USBAT_DEV_FLASH;
|
||||
} else {
|
||||
// Device is HP 8200
|
||||
US_DEBUGP("usbat_identify_device: Detected HP8200 CDRW\n");
|
||||
info->devicetype = USBAT_DEV_HP8200;
|
||||
} else {
|
||||
// Device is a CompactFlash reader/writer
|
||||
US_DEBUGP("usbat_identify_device: Detected Flash reader/writer\n");
|
||||
info->devicetype = USBAT_DEV_FLASH;
|
||||
}
|
||||
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
|
@ -1239,16 +1236,10 @@ static int usbat_select_and_test_registers(struct us_data *us)
|
|||
{
|
||||
int selector;
|
||||
unsigned char *status = us->iobuf;
|
||||
unsigned char max_selector = 0xB0;
|
||||
if (usbat_get_device_type(us) == USBAT_DEV_FLASH)
|
||||
max_selector = 0xA0;
|
||||
|
||||
// try device = master, then device = slave.
|
||||
|
||||
for (selector = 0xA0; selector <= max_selector; selector += 0x10) {
|
||||
|
||||
if (usbat_get_device_type(us) == USBAT_DEV_HP8200 &&
|
||||
usbat_write(us, USBAT_ATA, USBAT_ATA_DEVICE, selector) !=
|
||||
for (selector = 0xA0; selector <= 0xB0; selector += 0x10) {
|
||||
if (usbat_write(us, USBAT_ATA, USBAT_ATA_DEVICE, selector) !=
|
||||
USB_STOR_XFER_GOOD)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
|
@ -1334,60 +1325,30 @@ int init_usbat(struct us_data *us)
|
|||
|
||||
US_DEBUGP("INIT 3\n");
|
||||
|
||||
// At this point, we need to detect which device we are using
|
||||
if (usbat_set_transport(us, info))
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
US_DEBUGP("INIT 4\n");
|
||||
|
||||
if (usbat_get_device_type(us) == USBAT_DEV_HP8200) {
|
||||
msleep(250);
|
||||
|
||||
// Write 0x80 to ISA port 0x3F
|
||||
rc = usbat_write(us, USBAT_ISA, 0x3F, 0x80);
|
||||
if (rc != USB_STOR_XFER_GOOD)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
US_DEBUGP("INIT 5\n");
|
||||
|
||||
// Read ISA port 0x27
|
||||
rc = usbat_read(us, USBAT_ISA, 0x27, status);
|
||||
if (rc != USB_STOR_XFER_GOOD)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
US_DEBUGP("INIT 6\n");
|
||||
|
||||
rc = usbat_read_user_io(us, status);
|
||||
if (rc != USB_STOR_XFER_GOOD)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
US_DEBUGP("INIT 7\n");
|
||||
}
|
||||
|
||||
rc = usbat_select_and_test_registers(us);
|
||||
if (rc != USB_STOR_TRANSPORT_GOOD)
|
||||
return rc;
|
||||
|
||||
US_DEBUGP("INIT 8\n");
|
||||
US_DEBUGP("INIT 4\n");
|
||||
|
||||
rc = usbat_read_user_io(us, status);
|
||||
if (rc != USB_STOR_XFER_GOOD)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
US_DEBUGP("INIT 9\n");
|
||||
US_DEBUGP("INIT 5\n");
|
||||
|
||||
// Enable peripheral control signals and card detect
|
||||
rc = usbat_device_enable_cdt(us);
|
||||
if (rc != USB_STOR_TRANSPORT_GOOD)
|
||||
return rc;
|
||||
|
||||
US_DEBUGP("INIT 10\n");
|
||||
US_DEBUGP("INIT 6\n");
|
||||
|
||||
rc = usbat_read_user_io(us, status);
|
||||
if (rc != USB_STOR_XFER_GOOD)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
US_DEBUGP("INIT 11\n");
|
||||
US_DEBUGP("INIT 7\n");
|
||||
|
||||
msleep(1400);
|
||||
|
||||
|
@ -1395,13 +1356,19 @@ int init_usbat(struct us_data *us)
|
|||
if (rc != USB_STOR_XFER_GOOD)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
US_DEBUGP("INIT 12\n");
|
||||
US_DEBUGP("INIT 8\n");
|
||||
|
||||
rc = usbat_select_and_test_registers(us);
|
||||
if (rc != USB_STOR_TRANSPORT_GOOD)
|
||||
return rc;
|
||||
|
||||
US_DEBUGP("INIT 13\n");
|
||||
US_DEBUGP("INIT 9\n");
|
||||
|
||||
// At this point, we need to detect which device we are using
|
||||
if (usbat_set_transport(us, info))
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
US_DEBUGP("INIT 10\n");
|
||||
|
||||
if (usbat_get_device_type(us) == USBAT_DEV_FLASH) {
|
||||
subcountH = 0x02;
|
||||
|
@ -1412,7 +1379,7 @@ int init_usbat(struct us_data *us)
|
|||
if (rc != USB_STOR_XFER_GOOD)
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
|
||||
US_DEBUGP("INIT 14\n");
|
||||
US_DEBUGP("INIT 11\n");
|
||||
|
||||
return USB_STOR_TRANSPORT_GOOD;
|
||||
}
|
||||
|
|
|
@ -96,8 +96,8 @@
|
|||
* or before the URB_ACTIVE bit was set. If so, it's essential to cancel
|
||||
* the URB if it hasn't been cancelled already (i.e., if the URB_ACTIVE bit
|
||||
* is still set). Either way, the function must then wait for the URB to
|
||||
* finish. Note that because the URB_ASYNC_UNLINK flag is set, the URB can
|
||||
* still be in progress even after a call to usb_unlink_urb() returns.
|
||||
* finish. Note that the URB can still be in progress even after a call to
|
||||
* usb_unlink_urb() returns.
|
||||
*
|
||||
* The idea is that (1) once the ABORTING or DISCONNECTING bit is set,
|
||||
* either the stop_transport() function or the submitting function
|
||||
|
@ -158,8 +158,7 @@ static int usb_stor_msg_common(struct us_data *us, int timeout)
|
|||
* hasn't been mapped for DMA. Yes, this is clunky, but it's
|
||||
* easier than always having the caller tell us whether the
|
||||
* transfer buffer has already been mapped. */
|
||||
us->current_urb->transfer_flags =
|
||||
URB_ASYNC_UNLINK | URB_NO_SETUP_DMA_MAP;
|
||||
us->current_urb->transfer_flags = URB_NO_SETUP_DMA_MAP;
|
||||
if (us->current_urb->transfer_buffer == us->iobuf)
|
||||
us->current_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
us->current_urb->transfer_dma = us->iobuf_dma;
|
||||
|
@ -611,7 +610,6 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
|
|||
unsigned char old_sc_data_direction;
|
||||
unsigned char old_cmd_len;
|
||||
unsigned char old_cmnd[MAX_COMMAND_SIZE];
|
||||
unsigned long old_serial_number;
|
||||
int old_resid;
|
||||
|
||||
US_DEBUGP("Issuing auto-REQUEST_SENSE\n");
|
||||
|
@ -648,10 +646,6 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
|
|||
old_sg = srb->use_sg;
|
||||
srb->use_sg = 0;
|
||||
|
||||
/* change the serial number -- toggle the high bit*/
|
||||
old_serial_number = srb->serial_number;
|
||||
srb->serial_number ^= 0x80000000;
|
||||
|
||||
/* issue the auto-sense command */
|
||||
old_resid = srb->resid;
|
||||
srb->resid = 0;
|
||||
|
@ -662,7 +656,6 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
|
|||
srb->request_buffer = old_request_buffer;
|
||||
srb->request_bufflen = old_request_bufflen;
|
||||
srb->use_sg = old_sg;
|
||||
srb->serial_number = old_serial_number;
|
||||
srb->sc_data_direction = old_sc_data_direction;
|
||||
srb->cmd_len = old_cmd_len;
|
||||
memcpy(srb->cmnd, old_cmnd, MAX_COMMAND_SIZE);
|
||||
|
@ -985,7 +978,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
|
|||
bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
|
||||
bcb->DataTransferLength = cpu_to_le32(transfer_length);
|
||||
bcb->Flags = srb->sc_data_direction == DMA_FROM_DEVICE ? 1 << 7 : 0;
|
||||
bcb->Tag = srb->serial_number;
|
||||
bcb->Tag = ++us->tag;
|
||||
bcb->Lun = srb->device->lun;
|
||||
if (us->flags & US_FL_SCM_MULT_TARG)
|
||||
bcb->Lun |= srb->device->id << 4;
|
||||
|
@ -1074,7 +1067,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
|
|||
US_DEBUGP("Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n",
|
||||
le32_to_cpu(bcs->Signature), bcs->Tag,
|
||||
residue, bcs->Status);
|
||||
if (bcs->Tag != srb->serial_number || bcs->Status > US_BULK_STAT_PHASE) {
|
||||
if (bcs->Tag != us->tag || bcs->Status > US_BULK_STAT_PHASE) {
|
||||
US_DEBUGP("Bulk logical error\n");
|
||||
return USB_STOR_TRANSPORT_ERROR;
|
||||
}
|
||||
|
|
|
@ -79,6 +79,13 @@ UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x0001,
|
|||
US_SC_8070, US_PR_SCM_ATAPI, init_usbat, 0),
|
||||
#endif
|
||||
|
||||
/* Patch submitted by Mihnea-Costin Grigore <mihnea@zulu.ro> */
|
||||
UNUSUAL_DEV( 0x040d, 0x6205, 0x0003, 0x0003,
|
||||
"VIA Technologies Inc.",
|
||||
"USB 2.0 Card Reader",
|
||||
US_SC_DEVICE, US_PR_DEVICE, NULL,
|
||||
US_FL_IGNORE_RESIDUE ),
|
||||
|
||||
/* Deduced by Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
|
||||
* Entry needed for flags: US_FL_FIX_INQUIRY because initial inquiry message
|
||||
* always fails and confuses drive.
|
||||
|
@ -929,6 +936,18 @@ UNUSUAL_DEV( 0x0c0b, 0xa109, 0x0000, 0xffff,
|
|||
US_FL_SINGLE_LUN ),
|
||||
#endif
|
||||
|
||||
/* Submitted by: Nick Sillik <n.sillik@temple.edu>
|
||||
* Needed for OneTouch extension to usb-storage
|
||||
*
|
||||
*/
|
||||
#ifdef CONFIG_USB_STORAGE_ONETOUCH
|
||||
UNUSUAL_DEV( 0x0d49, 0x7010, 0x0000, 0x9999,
|
||||
"Maxtor",
|
||||
"OneTouch External Harddrive",
|
||||
US_SC_DEVICE, US_PR_DEVICE, onetouch_connect_input,
|
||||
0),
|
||||
#endif
|
||||
|
||||
/* Submitted by Joris Struyve <joris@struyve.be> */
|
||||
UNUSUAL_DEV( 0x0d96, 0x410a, 0x0001, 0xffff,
|
||||
"Medion",
|
||||
|
|
|
@ -90,7 +90,9 @@
|
|||
#ifdef CONFIG_USB_STORAGE_JUMPSHOT
|
||||
#include "jumpshot.h"
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_STORAGE_ONETOUCH
|
||||
#include "onetouch.h"
|
||||
#endif
|
||||
|
||||
/* Some informational data */
|
||||
MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
|
||||
|
@ -786,6 +788,7 @@ static void usb_stor_release_resources(struct us_data *us)
|
|||
* any more commands.
|
||||
*/
|
||||
US_DEBUGP("-- sending exit command to thread\n");
|
||||
set_bit(US_FLIDX_DISCONNECTING, &us->flags);
|
||||
up(&us->sema);
|
||||
|
||||
/* Call the destructor routine, if it exists */
|
||||
|
@ -816,6 +819,49 @@ static void dissociate_dev(struct us_data *us)
|
|||
usb_set_intfdata(us->pusb_intf, NULL);
|
||||
}
|
||||
|
||||
/* First stage of disconnect processing: stop all commands and remove
|
||||
* the host */
|
||||
static void quiesce_and_remove_host(struct us_data *us)
|
||||
{
|
||||
/* Prevent new USB transfers, stop the current command, and
|
||||
* interrupt a SCSI-scan or device-reset delay */
|
||||
set_bit(US_FLIDX_DISCONNECTING, &us->flags);
|
||||
usb_stor_stop_transport(us);
|
||||
wake_up(&us->delay_wait);
|
||||
|
||||
/* It doesn't matter if the SCSI-scanning thread is still running.
|
||||
* The thread will exit when it sees the DISCONNECTING flag. */
|
||||
|
||||
/* Wait for the current command to finish, then remove the host */
|
||||
down(&us->dev_semaphore);
|
||||
up(&us->dev_semaphore);
|
||||
|
||||
/* queuecommand won't accept any new commands and the control
|
||||
* thread won't execute a previously-queued command. If there
|
||||
* is such a command pending, complete it with an error. */
|
||||
if (us->srb) {
|
||||
us->srb->result = DID_NO_CONNECT << 16;
|
||||
scsi_lock(us_to_host(us));
|
||||
us->srb->scsi_done(us->srb);
|
||||
us->srb = NULL;
|
||||
scsi_unlock(us_to_host(us));
|
||||
}
|
||||
|
||||
/* Now we own no commands so it's safe to remove the SCSI host */
|
||||
scsi_remove_host(us_to_host(us));
|
||||
}
|
||||
|
||||
/* Second stage of disconnect processing: deallocate all resources */
|
||||
static void release_everything(struct us_data *us)
|
||||
{
|
||||
usb_stor_release_resources(us);
|
||||
dissociate_dev(us);
|
||||
|
||||
/* Drop our reference to the host; the SCSI core will free it
|
||||
* (and "us" along with it) when the refcount becomes 0. */
|
||||
scsi_host_put(us_to_host(us));
|
||||
}
|
||||
|
||||
/* Thread to carry out delayed SCSI-device scanning */
|
||||
static int usb_stor_scan_thread(void * __us)
|
||||
{
|
||||
|
@ -956,7 +1002,7 @@ static int storage_probe(struct usb_interface *intf,
|
|||
if (result < 0) {
|
||||
printk(KERN_WARNING USB_STORAGE
|
||||
"Unable to start the device-scanning thread\n");
|
||||
scsi_remove_host(host);
|
||||
quiesce_and_remove_host(us);
|
||||
goto BadDevice;
|
||||
}
|
||||
atomic_inc(&total_threads);
|
||||
|
@ -969,10 +1015,7 @@ static int storage_probe(struct usb_interface *intf,
|
|||
/* We come here if there are any problems */
|
||||
BadDevice:
|
||||
US_DEBUGP("storage_probe() failed\n");
|
||||
set_bit(US_FLIDX_DISCONNECTING, &us->flags);
|
||||
usb_stor_release_resources(us);
|
||||
dissociate_dev(us);
|
||||
scsi_host_put(host);
|
||||
release_everything(us);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -982,28 +1025,8 @@ static void storage_disconnect(struct usb_interface *intf)
|
|||
struct us_data *us = usb_get_intfdata(intf);
|
||||
|
||||
US_DEBUGP("storage_disconnect() called\n");
|
||||
|
||||
/* Prevent new USB transfers, stop the current command, and
|
||||
* interrupt a SCSI-scan or device-reset delay */
|
||||
set_bit(US_FLIDX_DISCONNECTING, &us->flags);
|
||||
usb_stor_stop_transport(us);
|
||||
wake_up(&us->delay_wait);
|
||||
|
||||
/* It doesn't matter if the SCSI-scanning thread is still running.
|
||||
* The thread will exit when it sees the DISCONNECTING flag. */
|
||||
|
||||
/* Wait for the current command to finish, then remove the host */
|
||||
down(&us->dev_semaphore);
|
||||
up(&us->dev_semaphore);
|
||||
scsi_remove_host(us_to_host(us));
|
||||
|
||||
/* Wait for everything to become idle and release all our resources */
|
||||
usb_stor_release_resources(us);
|
||||
dissociate_dev(us);
|
||||
|
||||
/* Drop our reference to the host; the SCSI core will free it
|
||||
* (and "us" along with it) when the refcount becomes 0. */
|
||||
scsi_host_put(us_to_host(us));
|
||||
quiesce_and_remove_host(us);
|
||||
release_everything(us);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
|
|
|
@ -158,6 +158,7 @@ struct us_data {
|
|||
|
||||
/* SCSI interfaces */
|
||||
struct scsi_cmnd *srb; /* current srb */
|
||||
unsigned int tag; /* current dCBWTag */
|
||||
|
||||
/* thread information */
|
||||
int pid; /* control thread */
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <linux/usb_ch9.h>
|
||||
|
||||
#define USB_MAJOR 180
|
||||
#define USB_DEVICE_MAJOR 189
|
||||
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
@ -349,6 +350,7 @@ struct usb_device {
|
|||
char *manufacturer;
|
||||
char *serial; /* static strings from the device */
|
||||
struct list_head filelist;
|
||||
struct class_device *class_dev;
|
||||
struct dentry *usbfs_dentry; /* usbfs dentry entry for the device */
|
||||
|
||||
/*
|
||||
|
@ -614,7 +616,6 @@ extern int usb_disabled(void);
|
|||
#define URB_ISO_ASAP 0x0002 /* iso-only, urb->start_frame ignored */
|
||||
#define URB_NO_TRANSFER_DMA_MAP 0x0004 /* urb->transfer_dma valid on submit */
|
||||
#define URB_NO_SETUP_DMA_MAP 0x0008 /* urb->setup_dma valid on submit */
|
||||
#define URB_ASYNC_UNLINK 0x0010 /* usb_unlink_urb() returns asap */
|
||||
#define URB_NO_FSBR 0x0020 /* UHCI-specific */
|
||||
#define URB_ZERO_PACKET 0x0040 /* Finish bulk OUTs with short packet */
|
||||
#define URB_NO_INTERRUPT 0x0080 /* HINT: no non-error interrupt needed */
|
||||
|
@ -722,13 +723,7 @@ typedef void (*usb_complete_t)(struct urb *, struct pt_regs *);
|
|||
* Initialization:
|
||||
*
|
||||
* All URBs submitted must initialize the dev, pipe, transfer_flags (may be
|
||||
* zero), and complete fields.
|
||||
* The URB_ASYNC_UNLINK transfer flag affects later invocations of
|
||||
* the usb_unlink_urb() routine. Note: Failure to set URB_ASYNC_UNLINK
|
||||
* with usb_unlink_urb() is deprecated. For synchronous unlinks use
|
||||
* usb_kill_urb() instead.
|
||||
*
|
||||
* All URBs must also initialize
|
||||
* zero), and complete fields. All URBs must also initialize
|
||||
* transfer_buffer and transfer_buffer_length. They may provide the
|
||||
* URB_SHORT_NOT_OK transfer flag, indicating that short reads are
|
||||
* to be treated as errors; that flag is invalid for write requests.
|
||||
|
|
|
@ -7,36 +7,18 @@
|
|||
struct isp116x_platform_data {
|
||||
/* Enable internal resistors on downstream ports */
|
||||
unsigned sel15Kres:1;
|
||||
/* Chip's internal clock won't be stopped in suspended state.
|
||||
Setting/unsetting this bit takes effect only if
|
||||
'remote_wakeup_enable' below is not set. */
|
||||
unsigned clknotstop:1;
|
||||
/* On-chip overcurrent protection */
|
||||
/* On-chip overcurrent detection */
|
||||
unsigned oc_enable:1;
|
||||
/* INT output polarity */
|
||||
unsigned int_act_high:1;
|
||||
/* INT edge or level triggered */
|
||||
unsigned int_edge_triggered:1;
|
||||
/* WAKEUP pin connected - NOT SUPPORTED */
|
||||
/* unsigned remote_wakeup_connected:1; */
|
||||
/* Wakeup by devices on usb bus enabled */
|
||||
/* Enable wakeup by devices on usb bus (e.g. wakeup
|
||||
by attachment/detachment or by device activity
|
||||
such as moving a mouse). When chosen, this option
|
||||
prevents stopping internal clock, increasing
|
||||
thereby power consumption in suspended state. */
|
||||
unsigned remote_wakeup_enable:1;
|
||||
/* Switch or not to switch (keep always powered) */
|
||||
unsigned no_power_switching:1;
|
||||
/* Ganged port power switching (0) or individual port
|
||||
power switching (1) */
|
||||
unsigned power_switching_mode:1;
|
||||
/* Given port_power, msec/2 after power on till power good */
|
||||
u8 potpg;
|
||||
/* Hardware reset set/clear. If implemented, this function must:
|
||||
if set == 0, deassert chip's HW reset pin
|
||||
otherwise, assert chip's HW reset pin */
|
||||
void (*reset) (struct device * dev, int set);
|
||||
/* Hardware clock start/stop. If implemented, this function must:
|
||||
if start == 0, stop the external clock
|
||||
otherwise, start the external clock
|
||||
*/
|
||||
void (*clock) (struct device * dev, int start);
|
||||
/* Inter-io delay (ns). The chip is picky about access timings; it
|
||||
expects at least:
|
||||
150ns delay between consecutive accesses to DATA_REG,
|
||||
|
|
|
@ -735,10 +735,9 @@ static int deactivate_urbs(snd_usb_substream_t *subs, int force, int can_sleep)
|
|||
if (test_bit(i, &subs->active_mask)) {
|
||||
if (! test_and_set_bit(i, &subs->unlink_mask)) {
|
||||
struct urb *u = subs->dataurb[i].urb;
|
||||
if (async) {
|
||||
u->transfer_flags |= URB_ASYNC_UNLINK;
|
||||
if (async)
|
||||
usb_unlink_urb(u);
|
||||
} else
|
||||
else
|
||||
usb_kill_urb(u);
|
||||
}
|
||||
}
|
||||
|
@ -748,10 +747,9 @@ static int deactivate_urbs(snd_usb_substream_t *subs, int force, int can_sleep)
|
|||
if (test_bit(i+16, &subs->active_mask)) {
|
||||
if (! test_and_set_bit(i+16, &subs->unlink_mask)) {
|
||||
struct urb *u = subs->syncurb[i].urb;
|
||||
if (async) {
|
||||
u->transfer_flags |= URB_ASYNC_UNLINK;
|
||||
if (async)
|
||||
usb_unlink_urb(u);
|
||||
} else
|
||||
else
|
||||
usb_kill_urb(u);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue