Commit 90a89de4 authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://linuxusb.bkbits.net/linus-2.5

into home.transmeta.com:/home/torvalds/v2.5/linux
parents f289aa67 1966de6f
......@@ -1175,7 +1175,7 @@ void usb_hub_cleanup(void)
int usb_reset_device(struct usb_device *dev)
{
struct usb_device *parent = dev->parent;
struct usb_device_descriptor descriptor;
struct usb_device_descriptor *descriptor;
int i, ret, port = -1;
if (!parent) {
......@@ -1224,17 +1224,24 @@ int usb_reset_device(struct usb_device *dev)
* If nothing changed, we reprogram the configuration and then
* the alternate settings.
*/
ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &descriptor,
sizeof(descriptor));
if (ret < 0)
descriptor = kmalloc(sizeof *descriptor, GFP_NOIO);
if (!descriptor) {
return -ENOMEM;
}
ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, descriptor,
sizeof(*descriptor));
if (ret < 0) {
kfree(descriptor);
return ret;
}
le16_to_cpus(&descriptor.bcdUSB);
le16_to_cpus(&descriptor.idVendor);
le16_to_cpus(&descriptor.idProduct);
le16_to_cpus(&descriptor.bcdDevice);
le16_to_cpus(&descriptor->bcdUSB);
le16_to_cpus(&descriptor->idVendor);
le16_to_cpus(&descriptor->idProduct);
le16_to_cpus(&descriptor->bcdDevice);
if (memcmp(&dev->descriptor, &descriptor, sizeof(descriptor))) {
if (memcmp(&dev->descriptor, descriptor, sizeof(*descriptor))) {
kfree(descriptor);
usb_destroy_configuration(dev);
ret = usb_get_device_descriptor(dev);
......@@ -1268,6 +1275,8 @@ int usb_reset_device(struct usb_device *dev)
return 1;
}
kfree(descriptor);
ret = usb_set_configuration(dev, dev->actconfig->desc.bConfigurationValue);
if (ret < 0) {
err("failed to set dev %s active configuration (error=%d)",
......
......@@ -88,7 +88,7 @@ int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe,
int retv;
int length;
urb = usb_alloc_urb(0, GFP_KERNEL);
urb = usb_alloc_urb(0, GFP_NOIO);
if (!urb)
return -ENOMEM;
......@@ -131,7 +131,7 @@ int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe,
int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype,
__u16 value, __u16 index, void *data, __u16 size, int timeout)
{
struct usb_ctrlrequest *dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
struct usb_ctrlrequest *dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
int ret;
if (!dr)
......
......@@ -117,10 +117,10 @@ static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {}
static void __attribute__((__unused__))
dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
{
dbg ("%s %p info1 %x info2 %x hw_curr %x qtd_next %x", label,
qh, qh->hw_info1, qh->hw_info2,
dbg ("%s %p n%08x info1 %x info2 %x hw_curr %x qtd_next %x", label,
qh, qh->hw_next, qh->hw_info1, qh->hw_info2,
qh->hw_current, qh->hw_qtd_next);
dbg (" alt+errs= %x, token= %x, page0= %x, page1= %x",
dbg (" alt+nak+t= %x, token= %x, page0= %x, page1= %x",
qh->hw_alt_next, qh->hw_token,
qh->hw_buf [0], qh->hw_buf [1]);
if (qh->hw_buf [2]) {
......
......@@ -576,7 +576,7 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state)
int ports;
int i;
dbg ("%s: suspend to %d", hcd_to_bus (hcd)->bus_name, state);
ehci_dbg (ehci, "suspend to %d\n", state);
ports = HCS_N_PORTS (ehci->hcs_params);
......@@ -593,7 +593,7 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state)
if ((temp & PORT_PE) == 0
|| (temp & PORT_OWNER) != 0)
continue;
dbg ("%s: suspend port %d", hcd_to_bus (hcd)->bus_name, i);
ehci_dbg (ehci, "suspend port %d", i);
temp |= PORT_SUSPEND;
writel (temp, &ehci->regs->port_status [i]);
}
......@@ -615,7 +615,7 @@ static int ehci_resume (struct usb_hcd *hcd)
int ports;
int i;
dbg ("%s: resume", hcd_to_bus (hcd)->bus_name);
ehci_dbg (ehci, "resume\n");
ports = HCS_N_PORTS (ehci->hcs_params);
......@@ -635,7 +635,7 @@ static int ehci_resume (struct usb_hcd *hcd)
if ((temp & PORT_PE) == 0
|| (temp & PORT_SUSPEND) != 0)
continue;
dbg ("%s: resume port %d", hcd_to_bus (hcd)->bus_name, i);
ehci_dbg (ehci, "resume port %d", i);
temp |= PORT_RESUME;
writel (temp, &ehci->regs->port_status [i]);
readl (&ehci->regs->command); /* unblock posted writes */
......@@ -880,8 +880,8 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
/* ASSERT: no requests/urbs are still linked (so no TDs) */
/* ASSERT: nobody can be submitting urbs for this any more */
dbg ("%s: free_config devnum %d",
hcd_to_bus (hcd)->bus_name, udev->devnum);
ehci_dbg (ehci, "free_config %s devnum %d\n",
udev->devpath, udev->devnum);
spin_lock_irqsave (&ehci->lock, flags);
for (i = 0; i < 32; i++) {
......@@ -912,7 +912,8 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
dev->ep [i] = 0;
if (qh->qh_state == QH_STATE_IDLE)
goto idle;
dbg ("free_config, async ep 0x%02x qh %p", i, qh);
ehci_dbg (ehci, "free_config, async ep 0x%02x qh %p",
i, qh);
/* scan_async() empties the ring as it does its work,
* using IAA, but doesn't (yet?) turn it off. if it
......
......@@ -347,8 +347,9 @@
* - Don't print errors when the device is busy.
*
* 0.4.11 2003-02-25
* - Added vendor/product ids for Artec, Avision, Brother, Medion, Primax,
* Prolink, Fujitsu, Plustek, and SYSCAN scanners.
* - Added vendor/product ids for Artec, Avision, Brother, Canon, Compaq,
* Fujitsu, Hewlett-Packard, Lexmark, LG Electronics, Medion, Microtek,
* Primax, Prolink, Plustek, SYSCAN, Trust and UMAX scanners.
* - Fixed generation of devfs names if dynamic minors are disabled.
* - Used kobject reference counting to free the scn struct when the device
* is closed and disconnected. Avoids crashes when writing to a
......
......@@ -105,6 +105,7 @@ static struct usb_device_id scanner_device_ids [] = {
{ USB_DEVICE(0x0638, 0x0a10) }, /* iVina FB1600 (=Umax Astra 4500) */
/* Benq: see Acer */
/* Brother */
{ USB_DEVICE(0x04f9, 0x010f) }, /* MFC 5100C */
{ USB_DEVICE(0x04f9, 0x0111) }, /* MFC 6800 */
/* Canon */
{ USB_DEVICE(0x04a9, 0x2201) }, /* CanoScan FB320U */
......@@ -118,9 +119,11 @@ static struct usb_device_id scanner_device_ids [] = {
{ USB_DEVICE(0x04a9, 0x220c) }, /* CanoScan D1250U2 */
{ USB_DEVICE(0x04a9, 0x220d) }, /* CanoScan N670U/N676U/LIDE 20 */
{ USB_DEVICE(0x04a9, 0x220e) }, /* CanoScan N1240U/LIDE 30 */
{ USB_DEVICE(0x04a9, 0x2213) }, /* LIDE 50 */
{ USB_DEVICE(0x04a9, 0x3042) }, /* FS4000US */
/* Colorado -- See Primax/Colorado below */
/* Compaq */
{ USB_DEVICE(0x049f, 0x001a) }, /* S4 100 */
{ USB_DEVICE(0x049f, 0x0021) }, /* S200 */
/* Epson -- See Seiko/Epson below */
/* Fujitsu */
......@@ -152,6 +155,8 @@ static struct usb_device_id scanner_device_ids [] = {
{ USB_DEVICE(0x03f0, 0x0705) }, /* ScanJet 4400C */
// { USB_DEVICE(0x03f0, 0x0801) }, /* ScanJet 7400C - NOT SUPPORTED - use hpusbscsi driver */
{ USB_DEVICE(0x03f0, 0x0901) }, /* ScanJet 2300C */
{ USB_DEVICE(0x03F0, 0x1005) }, /* ScanJet 5400C */
{ USB_DEVICE(0x03F0, 0x1105) }, /* ScanJet 5470C */
{ USB_DEVICE(0x03f0, 0x1305) }, /* Scanjet 4570c */
{ USB_DEVICE(0x03f0, 0x2005) }, /* ScanJet 3570c */
{ USB_DEVICE(0x03f0, 0x2205) }, /* ScanJet 3500c */
......@@ -159,12 +164,16 @@ static struct usb_device_id scanner_device_ids [] = {
{ USB_DEVICE(0x0638, 0x0268) }, /* 1200U */
/* Lexmark */
{ USB_DEVICE(0x043d, 0x002d) }, /* X70/X73 */
{ USB_DEVICE(0x043d, 0x003d) }, /* X83 */
/* LG Electronics */
{ USB_DEVICE(0x0461, 0x0364) }, /* Scanworks 600U (repackaged Primax?) */
/* Medion */
{ USB_DEVICE(0x0461, 0x0377) }, /* MD 5345 - repackaged Primax? */
/* Memorex */
{ USB_DEVICE(0x0461, 0x0346) }, /* 6136u - repackaged Primax ? */
/* Microtek */
{ USB_DEVICE(0x05da, 0x30ce) }, /* ScanMaker 3800 */
{ USB_DEVICE(0x05da, 0x30cf) }, /* ScanMaker 4800 */
/* The following SCSI-over-USB Microtek devices are supported by the
microtek driver: Enable SCSI and USB Microtek in kernel config */
// { USB_DEVICE(0x05da, 0x0099) }, /* ScanMaker X6 - X6U */
......@@ -259,7 +268,11 @@ static struct usb_device_id scanner_device_ids [] = {
{ USB_DEVICE(0x04b8, 0x0802) }, /* Stylus CX3200 */
/* SYSCAN */
{ USB_DEVICE(0x0a82, 0x4600) }, /* TravelScan 460/464 */
/* Trust */
{ USB_DEVICE(0x05cb, 0x1483) }, /* CombiScan 19200 */
{ USB_DEVICE(0x05d8, 0x4006) }, /* Easy Webscan 19200 (repackaged Artec?) */
/* Umax */
{ USB_DEVICE(0x05d8, 0x4009) }, /* Astraslim (actually Artec?) */
{ USB_DEVICE(0x1606, 0x0010) }, /* Astra 1220U */
{ USB_DEVICE(0x1606, 0x0030) }, /* Astra 2000U */
{ USB_DEVICE(0x1606, 0x0060) }, /* Astra 3400U/3450U */
......
......@@ -1334,6 +1334,9 @@ void hid_init_reports(struct hid_device *hid)
#define USB_VENDOR_ID_TANGTOP 0x0d3d
#define USB_DEVICE_ID_TANGTOP_USBPS2 0x0001
#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f
#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
struct hid_blacklist {
__u16 idVendor;
__u16 idProduct;
......@@ -1377,6 +1380,7 @@ struct hid_blacklist {
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_TANGTOP, USB_DEVICE_ID_TANGTOP_USBPS2, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE },
{ 0, 0 }
};
......
......@@ -12,5 +12,3 @@ obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o
obj-$(CONFIG_USB_TEST) += usbtest.o
obj-$(CONFIG_USB_TIGL) += tiglusb.o
obj-$(CONFIG_USB_USS720) += uss720.o
speedtch-objs := speedtouch.o atmsar.o
/******************************************************************************
* atmsar.c -- General SAR library for ATM devices.
*
* Copyright (C) 2000, Johan Verrept
*
* 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.
*
******************************************************************************/
/*
* Written by Johan Verrept (Johan.Verrept@advalvas.be)
*
* 0.2.4A: - Version for inclusion in 2.5 series kernel
* - Modifications by Richard Purdie (rpurdie@rpsys.net)
* - replaced "sarlib" with "atmsar"
* - adaptations for inclusion in kernel tree
*
* 0.2.4: - Fixed wrong buffer overrun check in atmsar_decode_rawcell()
* reported by Stephen Robinson <stephen.robinson@zen.co.uk>
* - Fixed bug when input skb did not contain a multple of 52/53
* bytes (would happen when the speedtouch device resynced)
* also reported by Stephen Robinson <stephen.robinson@zen.co.uk>
*
* 0.2.3: - Fixed wrong allocation size. caused memory corruption in some
* cases. Reported by Vladimir Dergachev <volodya@mindspring.com>
* - Added some comments
*
* 0.2.2: - Fixed CRCASM
* patch from Linus Flannagan <linusf@netservices.eng.net>
* - Fixed problem when user did NOT use the
* ATMSAR_USE_53BYTE_CELL flag.
* reported by Piers Scannell <email@lot105.com>
* - No more in-buffer rewriting for cloned buffers.
* - Removed the PII specific CFLAGS in the Makefile.
*
* 0.2.1: - removed dependency on alloc_tx. tis presented problems when
* using this with the br2684 code.
*
* 0.2: - added AAL0 reassembly
* - added alloc_tx support
* - replaced alloc_skb in decode functions to dev_alloc_skb to
* allow calling from interrupt
* - fixed embarassing AAL5 bug. I was setting the pti bit in the
* wrong byte...
* - fixed another emabrassing bug.. picked up the wrong crc type
* and forgot to invert the crc result...
* - fixed AAL5 length calculations.
* - removed automatic skb freeing from encode functions.
* This caused problems because i did kfree_skb it, while it
* needed to be popped. I cannot determine though whether it
* needs to be popped or not. Figu'e it out ye'self ;-)
* - added mru field. This is the buffersize. atmsar_decode_aal0
* will use when it allocates a receive buffer. A stop gap for
* real buffer management.
*
* 0.1: - library created.
* - only contains AAL5, AAL0 can be easily added. (actually, only
* AAL0 reassembly is missing)
*
*/
#include <linux/crc32.h>
#include "atmsar.h"
/***********************
**
** things to remember
**
***********************/
/*
1. the atmsar_vcc_data list pointer MUST be initialized to NULL
2. atmsar_encode_rawcell will drop incomplete cells.
3. ownership of the skb goes to the library !
*/
#define ATM_HDR_VPVC_MASK (ATM_HDR_VPI_MASK | ATM_HDR_VCI_MASK)
/***********************
**
** LOCAL STRUCTURES
**
***********************/
/***********************
**
** LOCAL MACROS
**
***********************/
/*
#define DEBUG 1
*/
#ifdef DEBUG
#define PDEBUG(arg...) printk(KERN_DEBUG "atmsar: " arg)
#else
#define PDEBUG(arg...)
#endif
#define ADD_HEADER(dest, header) \
*dest++ = (unsigned char) (header >> 24); \
*dest++ = (unsigned char) (header >> 16); \
*dest++ = (unsigned char) (header >> 8); \
*dest++ = (unsigned char) (header & 0xff);
struct atmsar_vcc_data *atmsar_open (struct atmsar_vcc_data **list, struct atm_vcc *vcc, uint type,
ushort vpi, ushort vci, unchar pti, unchar gfc, uint flags)
{
struct atmsar_vcc_data *new;
if (!vcc)
return NULL;
new = kmalloc (sizeof (struct atmsar_vcc_data), GFP_KERNEL);
if (!new)
return NULL;
memset (new, 0, sizeof (struct atmsar_vcc_data));
new->vcc = vcc;
new->stats = vcc->stats;
new->type = type;
new->next = NULL;
new->gfc = gfc;
new->vp = vpi;
new->vc = vci;
new->pti = pti;
switch (type) {
case ATMSAR_TYPE_AAL0:
new->mtu = ATMSAR_DEF_MTU_AAL0;
break;
case ATMSAR_TYPE_AAL1:
new->mtu = ATMSAR_DEF_MTU_AAL1;
break;
case ATMSAR_TYPE_AAL2:
new->mtu = ATMSAR_DEF_MTU_AAL2;
break;
case ATMSAR_TYPE_AAL34:
/* not supported */
new->mtu = ATMSAR_DEF_MTU_AAL34;
break;
case ATMSAR_TYPE_AAL5:
new->mtu = ATMSAR_DEF_MTU_AAL5;
break;
}
new->atmHeader = ((unsigned long) gfc << ATM_HDR_GFC_SHIFT)
| ((unsigned long) vpi << ATM_HDR_VPI_SHIFT)
| ((unsigned long) vci << ATM_HDR_VCI_SHIFT)
| ((unsigned long) pti << ATM_HDR_PTI_SHIFT);
new->flags = flags;
new->next = NULL;
new->reasBuffer = NULL;
new->next = *list;
*list = new;
PDEBUG ("Allocated new SARLib vcc 0x%p with vp %d vc %d\n", new, vpi, vci);
return new;
}
void atmsar_close (struct atmsar_vcc_data **list, struct atmsar_vcc_data *vcc)
{
struct atmsar_vcc_data *work;
if (*list == vcc) {
*list = (*list)->next;
} else {
for (work = *list; work && work->next && (work->next != vcc); work = work->next);
/* return if not found */
if (work->next != vcc)
return;
work->next = work->next->next;
}
if (vcc->reasBuffer) {
dev_kfree_skb (vcc->reasBuffer);
}
PDEBUG ("Allocated SARLib vcc 0x%p with vp %d vc %d\n", vcc, vcc->vp, vcc->vc);
kfree (vcc);
}
/***********************
**
** DECODE FUNCTIONS
**
***********************/
struct sk_buff *atmsar_decode_rawcell (struct atmsar_vcc_data *list, struct sk_buff *skb,
struct atmsar_vcc_data **ctx)
{
while (skb->len) {
unsigned char *cell = skb->data;
unsigned char *cell_payload;
struct atmsar_vcc_data *vcc = list;
unsigned long atmHeader =
((unsigned long) (cell[0]) << 24) | ((unsigned long) (cell[1]) << 16) |
((unsigned long) (cell[2]) << 8) | (cell[3] & 0xff);
PDEBUG ("atmsar_decode_rawcell (0x%p, 0x%p, 0x%p) called\n", list, skb, ctx);
PDEBUG ("atmsar_decode_rawcell skb->data %p, skb->tail %p\n", skb->data, skb->tail);
if (!list || !skb || !ctx)
return NULL;
if (!skb->data || !skb->tail)
return NULL;
/* here should the header CRC check be... */
/* look up correct vcc */
for (;
vcc
&& ((vcc->atmHeader & ATM_HDR_VPVC_MASK) != (atmHeader & ATM_HDR_VPVC_MASK));
vcc = vcc->next);
PDEBUG ("atmsar_decode_rawcell found vcc %p for packet on vp %d, vc %d\n", vcc,
(int) ((atmHeader & ATM_HDR_VPI_MASK) >> ATM_HDR_VPI_SHIFT),
(int) ((atmHeader & ATM_HDR_VCI_MASK) >> ATM_HDR_VCI_SHIFT));
if (vcc && (skb->len >= (vcc->flags & ATMSAR_USE_53BYTE_CELL ? 53 : 52))) {
cell_payload = cell + (vcc->flags & ATMSAR_USE_53BYTE_CELL ? 5 : 4);
switch (vcc->type) {
case ATMSAR_TYPE_AAL0:
/* case ATMSAR_TYPE_AAL1: when we have a decode AAL1 function... */
{
struct sk_buff *tmp = dev_alloc_skb (vcc->mtu);
if (tmp) {
memcpy (tmp->tail, cell_payload, 48);
skb_put (tmp, 48);
if (vcc->stats)
atomic_inc (&vcc->stats->rx);
skb_pull (skb,
(vcc->
flags & ATMSAR_USE_53BYTE_CELL ? 53 :
52));
PDEBUG
("atmsar_decode_rawcell returns ATMSAR_TYPE_AAL0 pdu 0x%p with length %d\n",
tmp, tmp->len);
return tmp;
};
}
break;
case ATMSAR_TYPE_AAL1:
case ATMSAR_TYPE_AAL2:
case ATMSAR_TYPE_AAL34:
/* not supported */
break;
case ATMSAR_TYPE_AAL5:
if (!vcc->reasBuffer)
vcc->reasBuffer = dev_alloc_skb (vcc->mtu);
/* if alloc fails, we just drop the cell. it is possible that we can still
* receive cells on other vcc's
*/
if (vcc->reasBuffer) {
/* if (buffer overrun) discard received cells until now */
if ((vcc->reasBuffer->len) > (vcc->mtu - 48))
skb_trim (vcc->reasBuffer, 0);
/* copy data */
memcpy (vcc->reasBuffer->tail, cell_payload, 48);
skb_put (vcc->reasBuffer, 48);
/* check for end of buffer */
if (cell[3] & 0x2) {
struct sk_buff *tmp;
/* the aal5 buffer ends here, cut the buffer. */
/* buffer will always have at least one whole cell, so */
/* don't need to check return from skb_pull */
skb_pull (skb,
(vcc->
flags & ATMSAR_USE_53BYTE_CELL ? 53 :
52));
*ctx = vcc;
tmp = vcc->reasBuffer;
vcc->reasBuffer = NULL;
PDEBUG
("atmsar_decode_rawcell returns ATMSAR_TYPE_AAL5 pdu 0x%p with length %d\n",
tmp, tmp->len);
return tmp;
}
}
break;
};
/* flush the cell */
/* buffer will always contain at least one whole cell, so don't */
/* need to check return value from skb_pull */
skb_pull (skb, (vcc->flags & ATMSAR_USE_53BYTE_CELL ? 53 : 52));
} else {
/* If data is corrupt and skb doesn't hold a whole cell, flush the lot */
if (skb_pull (skb, (list->flags & ATMSAR_USE_53BYTE_CELL ? 53 : 52)) ==
NULL)
return NULL;
}
}
return NULL;
};
struct sk_buff *atmsar_decode_aal5 (struct atmsar_vcc_data *ctx, struct sk_buff *skb)
{
uint crc = 0xffffffff;
uint length, pdu_crc, pdu_length;
PDEBUG ("atmsar_decode_aal5 (0x%p, 0x%p) called\n", ctx, skb);
if (skb->len && (skb->len % 48))
return NULL;
length = (skb->tail[-6] << 8) + skb->tail[-5];
pdu_crc =
(skb->tail[-4] << 24) + (skb->tail[-3] << 16) + (skb->tail[-2] << 8) + skb->tail[-1];
pdu_length = ((length + 47 + 8) / 48) * 48;
PDEBUG ("atmsar_decode_aal5: skb->len = %d, length = %d, pdu_crc = 0x%x, pdu_length = %d\n",
skb->len, length, pdu_crc, pdu_length);
/* is skb long enough ? */
if (skb->len < pdu_length) {
if (ctx->stats)
atomic_inc (&ctx->stats->rx_err);
return NULL;
}
/* is skb too long ? */
if (skb->len > pdu_length) {
PDEBUG ("atmsar_decode_aal5: Warning: readjusting illeagl size %d -> %d\n",
skb->len, pdu_length);
/* buffer is too long. we can try to recover
* if we discard the first part of the skb.
* the crc will decide whether this was ok
*/
skb_pull (skb, skb->len - pdu_length);
}
crc = ~crc32_be (crc, skb->data, pdu_length - 4);
/* check crc */
if (pdu_crc != crc) {
PDEBUG ("atmsar_decode_aal5: crc check failed!\n");
if (ctx->stats)
atomic_inc (&ctx->stats->rx_err);
return NULL;
}
/* pdu is ok */
skb_trim (skb, length);
/* update stats */
if (ctx->stats)
atomic_inc (&ctx->stats->rx);
PDEBUG ("atmsar_decode_aal5 returns pdu 0x%p with length %d\n", skb, skb->len);
return skb;
};
#ifndef _ATMSAR_H_
#define _ATMSAR_H_
/******************************************************************************
* atmsar.h -- General SAR library for ATM devices.
*
* Copyright (C) 2000, Johan Verrept
*
* 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/kernel.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/atmdev.h>
#include <linux/skbuff.h>
#include <linux/types.h>
#include <linux/atm.h>
#define ATMSAR_USE_53BYTE_CELL 0x1L
#define ATMSAR_SET_PTI 0x2L
#define ATM_CELL_HEADER (ATM_CELL_SIZE - ATM_CELL_PAYLOAD)
/* types */
#define ATMSAR_TYPE_AAL0 ATM_AAL0
#define ATMSAR_TYPE_AAL1 ATM_AAL1
#define ATMSAR_TYPE_AAL2 ATM_AAL2
#define ATMSAR_TYPE_AAL34 ATM_AAL34
#define ATMSAR_TYPE_AAL5 ATM_AAL5
/* default MTU's */
#define ATMSAR_DEF_MTU_AAL0 48
#define ATMSAR_DEF_MTU_AAL1 47
#define ATMSAR_DEF_MTU_AAL2 0 /* not supported */
#define ATMSAR_DEF_MTU_AAL34 0 /* not supported */
#define ATMSAR_DEF_MTU_AAL5 65535 /* max mtu .. */
struct atmsar_vcc_data {
struct atmsar_vcc_data *next;
/* general atmsar flags, per connection */
int flags;
int type;
/* connection specific non-atmsar data */
struct atm_vcc *vcc;
struct k_atm_aal_stats *stats;
unsigned short mtu; /* max is actually 65k for AAL5... */
/* cell data */
unsigned int vp;
unsigned int vc;
unsigned char gfc;
unsigned char pti;
unsigned int headerFlags;
unsigned long atmHeader;
/* raw cell reassembly */
struct sk_buff *reasBuffer;
};
extern struct atmsar_vcc_data *atmsar_open (struct atmsar_vcc_data **list, struct atm_vcc *vcc,
uint type, ushort vpi, ushort vci, unchar pti,
unchar gfc, uint flags);
extern void atmsar_close (struct atmsar_vcc_data **list, struct atmsar_vcc_data *vcc);
struct sk_buff *atmsar_decode_rawcell (struct atmsar_vcc_data *list, struct sk_buff *skb,
struct atmsar_vcc_data **ctx);
struct sk_buff *atmsar_decode_aal5 (struct atmsar_vcc_data *ctx, struct sk_buff *skb);
#endif /* _ATMSAR_H_ */
......@@ -61,7 +61,6 @@
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <linux/crc32.h>
#include "atmsar.h"
/*
#define DEBUG 1
......@@ -102,6 +101,8 @@ static int udsl_print_packet (const unsigned char *data, int len);
#define UDSL_ENDPOINT_DATA_OUT 0x07
#define UDSL_ENDPOINT_DATA_IN 0x87
#define ATM_CELL_HEADER (ATM_CELL_SIZE - ATM_CELL_PAYLOAD)
#define hex2int(c) ( (c >= '0')&&(c <= '9') ? (c - '0') : ((c & 0xf)+9) )
/* usb_device_id struct */
......@@ -147,6 +148,30 @@ struct udsl_control {
#define UDSL_SKB(x) ((struct udsl_control *)(x)->cb)
struct atmsar_vcc_data {
struct atmsar_vcc_data *next;
/* general atmsar flags, per connection */
int flags;
int type;
/* connection specific non-atmsar data */
struct atm_vcc *vcc;
struct k_atm_aal_stats *stats;
unsigned short mtu; /* max is actually 65k for AAL5... */
/* cell data */
unsigned int vp;
unsigned int vc;
unsigned char gfc;
unsigned char pti;
unsigned int headerFlags;
unsigned long atmHeader;
/* raw cell reassembly */
struct sk_buff *reasBuffer;
};
/*
* UDSL main driver data
*/
......@@ -229,6 +254,188 @@ static struct usb_driver udsl_usb_driver = {
};
/*************
** decode **
*************/
#define ATM_HDR_VPVC_MASK (ATM_HDR_VPI_MASK | ATM_HDR_VCI_MASK)
#define ATMSAR_USE_53BYTE_CELL 0x1L
struct sk_buff *atmsar_decode_rawcell (struct atmsar_vcc_data *list, struct sk_buff *skb,
struct atmsar_vcc_data **ctx)
{
while (skb->len) {
unsigned char *cell = skb->data;
unsigned char *cell_payload;
struct atmsar_vcc_data *vcc = list;
unsigned long atmHeader =
((unsigned long) (cell[0]) << 24) | ((unsigned long) (cell[1]) << 16) |
((unsigned long) (cell[2]) << 8) | (cell[3] & 0xff);
dbg ("atmsar_decode_rawcell (0x%p, 0x%p, 0x%p) called", list, skb, ctx);
dbg ("atmsar_decode_rawcell skb->data %p, skb->tail %p", skb->data, skb->tail);
if (!list || !skb || !ctx)
return NULL;
if (!skb->data || !skb->tail)
return NULL;
/* here should the header CRC check be... */
/* look up correct vcc */
for (;
vcc
&& ((vcc->atmHeader & ATM_HDR_VPVC_MASK) != (atmHeader & ATM_HDR_VPVC_MASK));
vcc = vcc->next);
dbg ("atmsar_decode_rawcell found vcc %p for packet on vp %d, vc %d", vcc,
(int) ((atmHeader & ATM_HDR_VPI_MASK) >> ATM_HDR_VPI_SHIFT),
(int) ((atmHeader & ATM_HDR_VCI_MASK) >> ATM_HDR_VCI_SHIFT));
if (vcc && (skb->len >= (vcc->flags & ATMSAR_USE_53BYTE_CELL ? 53 : 52))) {
cell_payload = cell + (vcc->flags & ATMSAR_USE_53BYTE_CELL ? 5 : 4);
switch (vcc->type) {
case ATM_AAL0:
/* case ATM_AAL1: when we have a decode AAL1 function... */
{
struct sk_buff *tmp = dev_alloc_skb (vcc->mtu);
if (tmp) {
memcpy (tmp->tail, cell_payload, 48);
skb_put (tmp, 48);
if (vcc->stats)
atomic_inc (&vcc->stats->rx);
skb_pull (skb,
(vcc->
flags & ATMSAR_USE_53BYTE_CELL ? 53 :
52));
dbg
("atmsar_decode_rawcell returns ATM_AAL0 pdu 0x%p with length %d",
tmp, tmp->len);
return tmp;
};
}
break;
case ATM_AAL1:
case ATM_AAL2:
case ATM_AAL34:
/* not supported */
break;
case ATM_AAL5:
if (!vcc->reasBuffer)
vcc->reasBuffer = dev_alloc_skb (vcc->mtu);
/* if alloc fails, we just drop the cell. it is possible that we can still
* receive cells on other vcc's
*/
if (vcc->reasBuffer) {
/* if (buffer overrun) discard received cells until now */
if ((vcc->reasBuffer->len) > (vcc->mtu - 48))
skb_trim (vcc->reasBuffer, 0);
/* copy data */
memcpy (vcc->reasBuffer->tail, cell_payload, 48);
skb_put (vcc->reasBuffer, 48);
/* check for end of buffer */
if (cell[3] & 0x2) {
struct sk_buff *tmp;
/* the aal5 buffer ends here, cut the buffer. */
/* buffer will always have at least one whole cell, so */
/* don't need to check return from skb_pull */
skb_pull (skb,
(vcc->
flags & ATMSAR_USE_53BYTE_CELL ? 53 :
52));
*ctx = vcc;
tmp = vcc->reasBuffer;
vcc->reasBuffer = NULL;
dbg
("atmsar_decode_rawcell returns ATM_AAL5 pdu 0x%p with length %d",
tmp, tmp->len);
return tmp;
}
}
break;
};
/* flush the cell */
/* buffer will always contain at least one whole cell, so don't */
/* need to check return value from skb_pull */
skb_pull (skb, (vcc->flags & ATMSAR_USE_53BYTE_CELL ? 53 : 52));
} else {
/* If data is corrupt and skb doesn't hold a whole cell, flush the lot */
if (skb_pull (skb, (list->flags & ATMSAR_USE_53BYTE_CELL ? 53 : 52)) ==
NULL)
return NULL;
}
}
return NULL;
};
struct sk_buff *atmsar_decode_aal5 (struct atmsar_vcc_data *ctx, struct sk_buff *skb)
{
uint crc = 0xffffffff;
uint length, pdu_crc, pdu_length;
dbg ("atmsar_decode_aal5 (0x%p, 0x%p) called", ctx, skb);
if (skb->len && (skb->len % 48))
return NULL;
length = (skb->tail[-6] << 8) + skb->tail[-5];
pdu_crc =
(skb->tail[-4] << 24) + (skb->tail[-3] << 16) + (skb->tail[-2] << 8) + skb->tail[-1];
pdu_length = ((length + 47 + 8) / 48) * 48;
dbg ("atmsar_decode_aal5: skb->len = %d, length = %d, pdu_crc = 0x%x, pdu_length = %d",
skb->len, length, pdu_crc, pdu_length);
/* is skb long enough ? */
if (skb->len < pdu_length) {
if (ctx->stats)
atomic_inc (&ctx->stats->rx_err);
return NULL;
}
/* is skb too long ? */
if (skb->len > pdu_length) {
dbg ("atmsar_decode_aal5: Warning: readjusting illeagl size %d -> %d",
skb->len, pdu_length);
/* buffer is too long. we can try to recover
* if we discard the first part of the skb.
* the crc will decide whether this was ok
*/
skb_pull (skb, skb->len - pdu_length);
}
crc = ~crc32_be (crc, skb->data, pdu_length - 4);
/* check crc */
if (pdu_crc != crc) {
dbg ("atmsar_decode_aal5: crc check failed!");
if (ctx->stats)
atomic_inc (&ctx->stats->rx_err);
return NULL;
}
/* pdu is ok */
skb_trim (skb, length);
/* update stats */
if (ctx->stats)
atomic_inc (&ctx->stats->rx);
dbg ("atmsar_decode_aal5 returns pdu 0x%p with length %d", skb, skb->len);
return skb;
};
/*************
** encode **
*************/
......@@ -396,7 +603,7 @@ static void udsl_process_receive (unsigned long data)
dbg ("(after cell processing)skb->len = %d", new->len);
switch (atmsar_vcc->type) {
case ATMSAR_TYPE_AAL5:
case ATM_AAL5:
tmp = new;
new = atmsar_decode_aal5 (atmsar_vcc, new);
......@@ -690,15 +897,98 @@ static int udsl_atm_send (struct atm_vcc *vcc, struct sk_buff *skb)
}
/************
** ATM **
************/
/**********
** ATM **
**********/
#define ATMSAR_DEF_MTU_AAL0 48
#define ATMSAR_DEF_MTU_AAL1 47
#define ATMSAR_DEF_MTU_AAL2 0 /* not supported */
#define ATMSAR_DEF_MTU_AAL34 0 /* not supported */
#define ATMSAR_DEF_MTU_AAL5 65535 /* max mtu .. */
struct atmsar_vcc_data *atmsar_open (struct atmsar_vcc_data **list, struct atm_vcc *vcc, uint type,
ushort vpi, ushort vci, unchar pti, unchar gfc, uint flags)
{
struct atmsar_vcc_data *new;
if (!vcc)
return NULL;
new = kmalloc (sizeof (struct atmsar_vcc_data), GFP_KERNEL);
if (!new)
return NULL;
memset (new, 0, sizeof (struct atmsar_vcc_data));
new->vcc = vcc;
new->stats = vcc->stats;
new->type = type;
new->next = NULL;
new->gfc = gfc;
new->vp = vpi;
new->vc = vci;
new->pti = pti;
switch (type) {
case ATM_AAL0:
new->mtu = ATMSAR_DEF_MTU_AAL0;
break;
case ATM_AAL1:
new->mtu = ATMSAR_DEF_MTU_AAL1;
break;
case ATM_AAL2:
new->mtu = ATMSAR_DEF_MTU_AAL2;
break;
case ATM_AAL34:
/* not supported */
new->mtu = ATMSAR_DEF_MTU_AAL34;
break;
case ATM_AAL5:
new->mtu = ATMSAR_DEF_MTU_AAL5;
break;
}
new->atmHeader = ((unsigned long) gfc << ATM_HDR_GFC_SHIFT)
| ((unsigned long) vpi << ATM_HDR_VPI_SHIFT)
| ((unsigned long) vci << ATM_HDR_VCI_SHIFT)
| ((unsigned long) pti << ATM_HDR_PTI_SHIFT);
new->flags = flags;
new->next = NULL;
new->reasBuffer = NULL;
new->next = *list;
*list = new;
dbg ("Allocated new SARLib vcc 0x%p with vp %d vc %d", new, vpi, vci);
return new;
}
void atmsar_close (struct atmsar_vcc_data **list, struct atmsar_vcc_data *vcc)
{
struct atmsar_vcc_data *work;
if (*list == vcc) {
*list = (*list)->next;
} else {
for (work = *list; work && work->next && (work->next != vcc); work = work->next);
/* return if not found */
if (work->next != vcc)
return;
/***************************************************************************
*
* init functions
*
****************************************************************************/
work->next = work->next->next;
}
if (vcc->reasBuffer) {
dev_kfree_skb (vcc->reasBuffer);
}
dbg ("Allocated SARLib vcc 0x%p with vp %d vc %d", vcc, vcc->vp, vcc->vc);
kfree (vcc);
}
static void udsl_atm_dev_close (struct atm_dev *dev)
{
......@@ -718,13 +1008,6 @@ static void udsl_atm_dev_close (struct atm_dev *dev)
dev->dev_data = NULL;
}
/***************************************************************************
*
* ATM helper functions
*
****************************************************************************/
static int udsl_atm_proc_read (struct atm_dev *atm_dev, loff_t *pos, char *page)
{
struct udsl_instance_data *instance = atm_dev->dev_data;
......@@ -778,12 +1061,7 @@ static int udsl_atm_proc_read (struct atm_dev *atm_dev, loff_t *pos, char *page)
return 0;
}
/***************************************************************************
*
* SAR driver entries
*
****************************************************************************/
#define ATMSAR_SET_PTI 0x2L
static int udsl_atm_open (struct atm_vcc *vcc, short vpi, int vci)
{
......@@ -803,7 +1081,7 @@ static int udsl_atm_open (struct atm_vcc *vcc, short vpi, int vci)
MOD_INC_USE_COUNT;
vcc->dev_data =
atmsar_open (&(instance->atmsar_vcc_list), vcc, ATMSAR_TYPE_AAL5, vpi, vci, 0, 0,
atmsar_open (&(instance->atmsar_vcc_list), vcc, ATM_AAL5, vpi, vci, 0, 0,
ATMSAR_USE_53BYTE_CELL | ATMSAR_SET_PTI);
if (!vcc->dev_data) {
MOD_DEC_USE_COUNT;
......@@ -866,9 +1144,9 @@ static int udsl_atm_ioctl (struct atm_dev *dev, unsigned int cmd, void *arg)
}
/************
** USB **
************/
/**********
** USB **
**********/
static int udsl_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data)
{
......@@ -1180,11 +1458,9 @@ static void udsl_usb_disconnect (struct usb_interface *intf)
}
/***************************************************************************
*
* Driver Init
*
****************************************************************************/
/***********
** init **
***********/
static int __init udsl_usb_init (void)
{
......@@ -1215,13 +1491,11 @@ MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_LICENSE ("GPL");
#ifdef DEBUG_PACKET
/*******************************************************************************
*
* Debug
*
*******************************************************************************/
/************
** debug **
************/
#ifdef DEBUG_PACKET
static int udsl_print_packet (const unsigned char *data, int len)
{
unsigned char buffer [256];
......@@ -1237,5 +1511,4 @@ static int udsl_print_packet (const unsigned char *data, int len)
}
return i;
}
#endif /* PACKETDEBUG */
#endif
......@@ -1064,15 +1064,23 @@ static void set_ethernet_addr( ether_dev_t *ether_dev )
// Used by driver's probe routine ////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
void log_device_info(ether_dev_t *ether_dev)
static void log_device_info(ether_dev_t *ether_dev)
{
int len;
int string_num;
unsigned char manu[256];
unsigned char prod[256];
unsigned char sern[256];
unsigned char *manu = NULL;
unsigned char *prod = NULL;
unsigned char *sern = NULL;
unsigned char *mac_addr;
manu = kmalloc(256, GFP_KERNEL);
prod = kmalloc(256, GFP_KERNEL);
sern = kmalloc(256, GFP_KERNEL);
if (!manu || !prod || !sern) {
dbg("no mem for log_device_info");
goto fini;
}
// Default empty strings in case we don't find a real one
manu[0] = 0x00;
prod[0] = 0x00;
......@@ -1113,6 +1121,10 @@ void log_device_info(ether_dev_t *ether_dev)
ether_dev->net->name, manu, prod, sern, mac_addr[0],
mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4],
mac_addr[5] );
fini:
kfree(manu);
kfree(prod);
kfree(sern);
}
/* Forward declaration */
......
......@@ -121,7 +121,7 @@ static int get_registers(pegasus_t * pegasus, __u16 indx, __u16 size,
char *buffer;
DECLARE_WAITQUEUE(wait, current);
buffer = kmalloc(size, GFP_DMA);
buffer = kmalloc(size, GFP_KERNEL);
if (!buffer) {
warn("%s: looks like we're out of memory", __FUNCTION__);
return -ENOMEM;
......@@ -170,7 +170,7 @@ static int set_registers(pegasus_t * pegasus, __u16 indx, __u16 size,
char *buffer;
DECLARE_WAITQUEUE(wait, current);
buffer = kmalloc(size, GFP_DMA);
buffer = kmalloc(size, GFP_KERNEL);
if (!buffer) {
warn("%s: looks like we're out of memory", __FUNCTION__);
return -ENOMEM;
......@@ -218,7 +218,7 @@ static int set_register(pegasus_t * pegasus, __u16 indx, __u8 data)
char *tmp;
DECLARE_WAITQUEUE(wait, current);
tmp = kmalloc(1, GFP_DMA);
tmp = kmalloc(1, GFP_KERNEL);
if (!tmp) {
warn("%s: looks like we're out of memory", __FUNCTION__);
return -ENOMEM;
......
......@@ -783,7 +783,7 @@ static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, un
if (info->mcr & UART_MCR_RTS)
modem_signals |= TIOCM_RTS;
if (copy_to_user((unsigned int *)arg, &modem_signals, sizeof(unsigned int)));
if (copy_to_user((unsigned int *)arg, &modem_signals, sizeof(unsigned int)))
return -EFAULT;
break;
......
/*
* USB Skeleton driver - 0.9
* USB Skeleton driver - 1.0
*
* Copyright (c) 2001-2002 Greg Kroah-Hartman (greg@kroah.com)
*
......@@ -12,14 +12,17 @@
* USB driver quickly. The design of it is based on the usb-serial and
* dc2xx drivers.
*
* Thanks to Oliver Neukum and David Brownell for their help in debugging
* this driver.
* Thanks to Oliver Neukum, David Brownell, and Alan Stern for their help
* in debugging this driver.
*
* TODO:
* - fix urb->status race condition in write sequence
*
* History:
*
* 2003-02-25 - 1.0 - fix races involving urb->status, unlink_urb(), and
* disconnect. Fix transfer amount in read(). Use
* macros instead of magic numbers in probe(). Change
* size variables to size_t. Show how to eliminate
* DMA bounce buffer.
* 2002_12_12 - 0.9 - compile fixes and got rid of fixed minor array.
* 2002_09_26 - 0.8 - changes due to USB core conversion to struct device
* driver.
......@@ -33,7 +36,7 @@
* 2001_05_29 - 0.3 - more bug fixes based on review from linux-usb-devel
* 2001_05_24 - 0.2 - bug fixes based on review from linux-usb-devel people
* 2001_05_01 - 0.1 - first version
*
*
*/
#include <linux/config.h>
......@@ -42,8 +45,8 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/smp_lock.h>
#include <linux/completion.h>
#include <linux/devfs_fs_kernel.h>
#include <asm/uaccess.h>
#include <linux/usb.h>
......@@ -60,7 +63,7 @@
/* Version Information */
#define DRIVER_VERSION "v0.4"
#define DRIVER_VERSION "v1.0"
#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com"
#define DRIVER_DESC "USB Skeleton Driver"
......@@ -101,15 +104,16 @@ struct usb_skel {
char num_bulk_out; /* number of bulk out endpoints we have */
unsigned char * bulk_in_buffer; /* the buffer to receive data */
int bulk_in_size; /* the size of the receive buffer */
size_t bulk_in_size; /* the size of the receive buffer */
__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
unsigned char * bulk_out_buffer; /* the buffer to send data */
int bulk_out_size; /* the size of the send buffer */
size_t bulk_out_size; /* the size of the send buffer */
struct urb * write_urb; /* the urb used to send data */
__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
atomic_t write_busy; /* true iff write urb is busy */
struct completion write_finished; /* wait for the write to finish */
struct work_struct work; /* work queue entry for line discipline waking up */
int open; /* if the port is open or not */
struct semaphore sem; /* locks this structure */
};
......@@ -118,6 +122,8 @@ struct usb_skel {
/* the global usb devfs handle */
extern devfs_handle_t usb_devfs_handle;
/* prevent races between open() and disconnect() */
static DECLARE_MUTEX (disconnect_sem);
/* local function prototypes */
static ssize_t skel_read (struct file *file, char *buffer, size_t count, loff_t *ppos);
......@@ -125,7 +131,7 @@ static ssize_t skel_write (struct file *file, const char *buffer, size_t count,
static int skel_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
static int skel_open (struct inode *inode, struct file *file);
static int skel_release (struct inode *inode, struct file *file);
static int skel_probe (struct usb_interface *intf, const struct usb_device_id *id);
static void skel_disconnect (struct usb_interface *intf);
......@@ -138,7 +144,7 @@ static void skel_write_bulk_callback (struct urb *urb, struct pt_regs *regs);
* to have a node in the /dev directory. If the USB
* device were for a network interface then the driver
* would use "struct net_driver" instead, and a serial
* device would use "struct tty_driver".
* device would use "struct tty_driver".
*/
static struct file_operations skel_fops = {
/*
......@@ -167,7 +173,7 @@ static struct file_operations skel_fops = {
.ioctl = skel_ioctl,
.open = skel_open,
.release = skel_release,
};
};
/* usb specific object needed to register this driver with the usb subsystem */
......@@ -188,8 +194,8 @@ static inline void usb_skel_debug_data (const char *function, int size, const un
if (!debug)
return;
printk (KERN_DEBUG __FILE__": %s - length = %d, data = ",
printk (KERN_DEBUG __FILE__": %s - length = %d, data = ",
function, size);
for (i = 0; i < size; ++i) {
printk ("%.2x ", data[i]);
......@@ -206,7 +212,9 @@ static inline void skel_delete (struct usb_skel *dev)
if (dev->bulk_in_buffer != NULL)
kfree (dev->bulk_in_buffer);
if (dev->bulk_out_buffer != NULL)
kfree (dev->bulk_out_buffer);
usb_buffer_free (dev->udev, dev->bulk_out_size,
dev->bulk_out_buffer,
dev->write_urb->transfer_dma);
if (dev->write_urb != NULL)
usb_free_urb (dev->write_urb);
kfree (dev);
......@@ -222,22 +230,28 @@ static int skel_open (struct inode *inode, struct file *file)
struct usb_interface *interface;
int subminor;
int retval = 0;
dbg("%s", __FUNCTION__);
subminor = minor (inode->i_rdev);
/* prevent disconnects */
down (&disconnect_sem);
interface = usb_find_interface (&skel_driver,
mk_kdev(USB_MAJOR, subminor));
if (!interface) {
err ("%s - error, can't find device for minor %d",
__FUNCTION__, subminor);
return -ENODEV;
retval = -ENODEV;
goto exit_no_device;
}
dev = usb_get_intfdata(interface);
if (!dev)
return -ENODEV;
if (!dev) {
retval = -ENODEV;
goto exit_no_device;
}
/* lock this device */
down (&dev->sem);
......@@ -251,6 +265,8 @@ static int skel_open (struct inode *inode, struct file *file)
/* unlock this device */
up (&dev->sem);
exit_no_device:
up (&disconnect_sem);
return retval;
}
......@@ -280,6 +296,12 @@ static int skel_release (struct inode *inode, struct file *file)
goto exit_not_opened;
}
/* wait for any bulk writes that might be going on to finish up */
if (atomic_read (&dev->write_busy))
wait_for_completion (&dev->write_finished);
dev->open = 0;
if (dev->udev == NULL) {
/* the device was unplugged before the file was released */
up (&dev->sem);
......@@ -287,11 +309,6 @@ static int skel_release (struct inode *inode, struct file *file)
return 0;
}
/* shutdown any bulk writes that might be going on */
usb_unlink_urb (dev->write_urb);
dev->open = 0;
exit_not_opened:
up (&dev->sem);
......@@ -308,7 +325,7 @@ static ssize_t skel_read (struct file *file, char *buffer, size_t count, loff_t
int retval = 0;
dev = (struct usb_skel *)file->private_data;
dbg("%s - minor %d, count = %d", __FUNCTION__, dev->minor, count);
/* lock this object */
......@@ -319,12 +336,13 @@ static ssize_t skel_read (struct file *file, char *buffer, size_t count, loff_t
up (&dev->sem);
return -ENODEV;
}
/* do an immediate bulk read to get data from the device */
/* do a blocking bulk read to get data from the device */
retval = usb_bulk_msg (dev->udev,
usb_rcvbulkpipe (dev->udev,
usb_rcvbulkpipe (dev->udev,
dev->bulk_in_endpointAddr),
dev->bulk_in_buffer, dev->bulk_in_size,
dev->bulk_in_buffer,
min (dev->bulk_in_size, count),
&count, HZ*10);
/* if the read was successful, copy the data to userspace */
......@@ -334,7 +352,7 @@ static ssize_t skel_read (struct file *file, char *buffer, size_t count, loff_t
else
retval = count;
}
/* unlock the device */
up (&dev->sem);
return retval;
......@@ -343,6 +361,18 @@ static ssize_t skel_read (struct file *file, char *buffer, size_t count, loff_t
/**
* skel_write
*
* A device driver has to decide how to report I/O errors back to the
* user. The safest course is to wait for the transfer to finish before
* returning so that any errors will be reported reliably. skel_read()
* works like this. But waiting for I/O is slow, so many drivers only
* check for errors during I/O initiation and do not report problems
* that occur during the actual transfer. That's what we will do here.
*
* A driver concerned with maximum I/O throughput would use double-
* buffering: Two urbs would be devoted to write transfers, so that
* one urb could always be active while the other was waiting for the
* user to send more data.
*/
static ssize_t skel_write (struct file *file, const char *buffer, size_t count, loff_t *ppos)
{
......@@ -369,37 +399,38 @@ static ssize_t skel_write (struct file *file, const char *buffer, size_t count,
goto exit;
}
/* see if we are already in the middle of a write */
if (dev->write_urb->status == -EINPROGRESS) {
dbg ("%s - already writing", __FUNCTION__);
goto exit;
}
/* wait for a previous write to finish up; we don't use a timeout
* and so a nonresponsive device can delay us indefinitely.
*/
if (atomic_read (&dev->write_busy))
wait_for_completion (&dev->write_finished);
/* we can only write as much as 1 urb will hold */
bytes_written = (count > dev->bulk_out_size) ?
dev->bulk_out_size : count;
/* we can only write as much as our buffer will hold */
bytes_written = min (dev->bulk_out_size, count);
/* copy the data from userspace into our urb */
if (copy_from_user(dev->write_urb->transfer_buffer, buffer,
/* copy the data from userspace into our transfer buffer;
* this is the only copy required.
*/
if (copy_from_user(dev->write_urb->transfer_buffer, buffer,
bytes_written)) {
retval = -EFAULT;
goto exit;
}
usb_skel_debug_data (__FUNCTION__, bytes_written,
usb_skel_debug_data (__FUNCTION__, bytes_written,
dev->write_urb->transfer_buffer);
/* set up our urb */
usb_fill_bulk_urb(dev->write_urb, dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
dev->write_urb->transfer_buffer, bytes_written,
skel_write_bulk_callback, dev);
/* this urb was already set up, except for this write size */
dev->write_urb->transfer_buffer_length = bytes_written;
/* send the data out the bulk port */
/* a character device write uses GFP_KERNEL,
unless a spinlock is held */
init_completion (&dev->write_finished);
atomic_set (&dev->write_busy, 1);
retval = usb_submit_urb(dev->write_urb, GFP_KERNEL);
if (retval) {
atomic_set (&dev->write_busy, 0);
err("%s - failed submitting write urb, error %d",
__FUNCTION__, retval);
} else {
......@@ -435,12 +466,11 @@ static int skel_ioctl (struct inode *inode, struct file *file, unsigned int cmd,
dbg("%s - minor %d, cmd 0x%.4x, arg %ld", __FUNCTION__,
dev->minor, cmd, arg);
/* fill in your device specific stuff here */
/* unlock the device */
up (&dev->sem);
/* return that we did not understand this ioctl call */
return -ENOTTY;
}
......@@ -455,14 +485,16 @@ static void skel_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
dbg("%s - minor %d", __FUNCTION__, dev->minor);
if ((urb->status != -ENOENT) &&
(urb->status != -ECONNRESET)) {
/* sync/async unlink faults aren't errors */
if (urb->status && !(urb->status == -ENOENT ||
urb->status == -ECONNRESET)) {
dbg("%s - nonzero write bulk status received: %d",
__FUNCTION__, urb->status);
return;
}
return;
/* notify anyone waiting that the write has finished */
atomic_set (&dev->write_busy, 0);
complete (&dev->write_finished);
}
......@@ -479,12 +511,12 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
int minor;
int buffer_size;
size_t buffer_size;
int i;
int retval;
char name[10];
/* See if the device offered us matches what we can accept */
if ((udev->descriptor.idVendor != USB_SKEL_VENDOR_ID) ||
(udev->descriptor.idProduct != USB_SKEL_PRODUCT_ID)) {
......@@ -513,12 +545,15 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
/* set up the endpoint information */
/* check out the endpoints */
/* use only the first bulk-in and bulk-out endpoints */
iface_desc = &interface->altsetting[0];
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
if ((endpoint->bEndpointAddress & 0x80) &&
((endpoint->bmAttributes & 3) == 0x02)) {
if (!dev->bulk_in_endpointAddr &&
(endpoint->bEndpointAddress & USB_DIR_IN) &&
((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_BULK)) {
/* we found a bulk in endpoint */
buffer_size = endpoint->wMaxPacketSize;
dev->bulk_in_size = buffer_size;
......@@ -529,9 +564,11 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
goto error;
}
}
if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
((endpoint->bmAttributes & 3) == 0x02)) {
if (!dev->bulk_out_endpointAddr &&
!(endpoint->bEndpointAddress & USB_DIR_IN) &&
((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_BULK)) {
/* we found a bulk out endpoint */
/* a probe() may sleep and has no restrictions on memory allocations */
dev->write_urb = usb_alloc_urb(0, GFP_KERNEL);
......@@ -539,40 +576,56 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
err("No free urbs available");
goto error;
}
dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
/* on some platforms using this kind of buffer alloc
* call eliminates a dma "bounce buffer".
*
* NOTE: you'd normally want i/o buffers that hold
* more than one packet, so that i/o delays between
* packets don't hurt throughput.
*/
buffer_size = endpoint->wMaxPacketSize;
dev->bulk_out_size = buffer_size;
dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
dev->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL);
dev->write_urb->transfer_flags = (URB_NO_DMA_MAP |
URB_ASYNC_UNLINK);
dev->bulk_out_buffer = usb_buffer_alloc (udev,
buffer_size, GFP_KERNEL,
&dev->write_urb->transfer_dma);
if (!dev->bulk_out_buffer) {
err("Couldn't allocate bulk_out_buffer");
goto error;
}
usb_fill_bulk_urb(dev->write_urb, udev,
usb_sndbulkpipe(udev,
usb_fill_bulk_urb(dev->write_urb, udev,
usb_sndbulkpipe(udev,
endpoint->bEndpointAddress),
dev->bulk_out_buffer, buffer_size,
skel_write_bulk_callback, dev);
}
}
if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
err("Couldn't find both bulk-in and bulk-out endpoints");
goto error;
}
/* initialize the devfs node for this device and register it */
sprintf(name, "skel%d", dev->minor);
dev->devfs = devfs_register (usb_devfs_handle, name,
DEVFS_FL_DEFAULT, USB_MAJOR,
dev->minor,
S_IFCHR | S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP | S_IROTH,
S_IFCHR | S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP | S_IROTH,
&skel_fops, NULL);
/* let the user know what node this device is now attached to */
info ("USB Skeleton device now attached to USBSkel%d", dev->minor);
info ("USB Skeleton device now attached to USBSkel-%d", dev->minor);
/* add device id so the device works when advertised */
interface->kdev = mk_kdev(USB_MAJOR, dev->minor);
goto exit;
error:
skel_delete (dev);
dev = NULL;
......@@ -593,12 +646,21 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
* skel_disconnect
*
* Called by the usb core when the device is removed from the system.
*
* This routine guarantees that the driver will not submit any more urbs
* by clearing dev->udev. It is also supposed to terminate any currently
* active urbs. Unfortunately, usb_bulk_msg(), used in skel_read(), does
* not provide any way to do this. But at least we can cancel an active
* write.
*/
static void skel_disconnect(struct usb_interface *interface)
{
struct usb_skel *dev;
int minor;
/* prevent races with open() */
down (&disconnect_sem);
dev = usb_get_intfdata (interface);
usb_set_intfdata (interface, NULL);
......@@ -606,7 +668,7 @@ static void skel_disconnect(struct usb_interface *interface)
return;
down (&dev->sem);
/* remove device id to disable open() */
interface->kdev = NODEV;
......@@ -617,15 +679,21 @@ static void skel_disconnect(struct usb_interface *interface)
/* give back our dynamic minor */
usb_deregister_dev (1, minor);
/* terminate an ongoing write */
if (atomic_read (&dev->write_busy)) {
usb_unlink_urb (dev->write_urb);
wait_for_completion (&dev->write_finished);
}
dev->udev = NULL;
up (&dev->sem);
/* if the device is not opened, then we clean up right now */
if (!dev->open) {
up (&dev->sem);
if (!dev->open)
skel_delete (dev);
} else {
dev->udev = NULL;
up (&dev->sem);
}
up (&disconnect_sem);
info("USB Skeleton #%d now disconnected", minor);
}
......@@ -668,4 +736,3 @@ module_exit (usb_skel_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment