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) ...@@ -1175,7 +1175,7 @@ void usb_hub_cleanup(void)
int usb_reset_device(struct usb_device *dev) int usb_reset_device(struct usb_device *dev)
{ {
struct usb_device *parent = dev->parent; struct usb_device *parent = dev->parent;
struct usb_device_descriptor descriptor; struct usb_device_descriptor *descriptor;
int i, ret, port = -1; int i, ret, port = -1;
if (!parent) { if (!parent) {
...@@ -1224,17 +1224,24 @@ int usb_reset_device(struct usb_device *dev) ...@@ -1224,17 +1224,24 @@ int usb_reset_device(struct usb_device *dev)
* If nothing changed, we reprogram the configuration and then * If nothing changed, we reprogram the configuration and then
* the alternate settings. * the alternate settings.
*/ */
ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &descriptor, descriptor = kmalloc(sizeof *descriptor, GFP_NOIO);
sizeof(descriptor)); if (!descriptor) {
if (ret < 0) return -ENOMEM;
}
ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, descriptor,
sizeof(*descriptor));
if (ret < 0) {
kfree(descriptor);
return ret; return ret;
}
le16_to_cpus(&descriptor.bcdUSB); le16_to_cpus(&descriptor->bcdUSB);
le16_to_cpus(&descriptor.idVendor); le16_to_cpus(&descriptor->idVendor);
le16_to_cpus(&descriptor.idProduct); le16_to_cpus(&descriptor->idProduct);
le16_to_cpus(&descriptor.bcdDevice); 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); usb_destroy_configuration(dev);
ret = usb_get_device_descriptor(dev); ret = usb_get_device_descriptor(dev);
...@@ -1268,6 +1275,8 @@ int usb_reset_device(struct usb_device *dev) ...@@ -1268,6 +1275,8 @@ int usb_reset_device(struct usb_device *dev)
return 1; return 1;
} }
kfree(descriptor);
ret = usb_set_configuration(dev, dev->actconfig->desc.bConfigurationValue); ret = usb_set_configuration(dev, dev->actconfig->desc.bConfigurationValue);
if (ret < 0) { if (ret < 0) {
err("failed to set dev %s active configuration (error=%d)", 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, ...@@ -88,7 +88,7 @@ int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe,
int retv; int retv;
int length; int length;
urb = usb_alloc_urb(0, GFP_KERNEL); urb = usb_alloc_urb(0, GFP_NOIO);
if (!urb) if (!urb)
return -ENOMEM; return -ENOMEM;
...@@ -131,7 +131,7 @@ int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, ...@@ -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, 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) __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; int ret;
if (!dr) if (!dr)
......
...@@ -117,10 +117,10 @@ static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {} ...@@ -117,10 +117,10 @@ static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {}
static void __attribute__((__unused__)) static void __attribute__((__unused__))
dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) 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, dbg ("%s %p n%08x info1 %x info2 %x hw_curr %x qtd_next %x", label,
qh, qh->hw_info1, qh->hw_info2, qh, qh->hw_next, qh->hw_info1, qh->hw_info2,
qh->hw_current, qh->hw_qtd_next); 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_alt_next, qh->hw_token,
qh->hw_buf [0], qh->hw_buf [1]); qh->hw_buf [0], qh->hw_buf [1]);
if (qh->hw_buf [2]) { if (qh->hw_buf [2]) {
......
...@@ -576,7 +576,7 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state) ...@@ -576,7 +576,7 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state)
int ports; int ports;
int i; 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); ports = HCS_N_PORTS (ehci->hcs_params);
...@@ -593,7 +593,7 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state) ...@@ -593,7 +593,7 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state)
if ((temp & PORT_PE) == 0 if ((temp & PORT_PE) == 0
|| (temp & PORT_OWNER) != 0) || (temp & PORT_OWNER) != 0)
continue; continue;
dbg ("%s: suspend port %d", hcd_to_bus (hcd)->bus_name, i); ehci_dbg (ehci, "suspend port %d", i);
temp |= PORT_SUSPEND; temp |= PORT_SUSPEND;
writel (temp, &ehci->regs->port_status [i]); writel (temp, &ehci->regs->port_status [i]);
} }
...@@ -615,7 +615,7 @@ static int ehci_resume (struct usb_hcd *hcd) ...@@ -615,7 +615,7 @@ static int ehci_resume (struct usb_hcd *hcd)
int ports; int ports;
int i; int i;
dbg ("%s: resume", hcd_to_bus (hcd)->bus_name); ehci_dbg (ehci, "resume\n");
ports = HCS_N_PORTS (ehci->hcs_params); ports = HCS_N_PORTS (ehci->hcs_params);
...@@ -635,7 +635,7 @@ static int ehci_resume (struct usb_hcd *hcd) ...@@ -635,7 +635,7 @@ static int ehci_resume (struct usb_hcd *hcd)
if ((temp & PORT_PE) == 0 if ((temp & PORT_PE) == 0
|| (temp & PORT_SUSPEND) != 0) || (temp & PORT_SUSPEND) != 0)
continue; continue;
dbg ("%s: resume port %d", hcd_to_bus (hcd)->bus_name, i); ehci_dbg (ehci, "resume port %d", i);
temp |= PORT_RESUME; temp |= PORT_RESUME;
writel (temp, &ehci->regs->port_status [i]); writel (temp, &ehci->regs->port_status [i]);
readl (&ehci->regs->command); /* unblock posted writes */ readl (&ehci->regs->command); /* unblock posted writes */
...@@ -880,8 +880,8 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev) ...@@ -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: no requests/urbs are still linked (so no TDs) */
/* ASSERT: nobody can be submitting urbs for this any more */ /* ASSERT: nobody can be submitting urbs for this any more */
dbg ("%s: free_config devnum %d", ehci_dbg (ehci, "free_config %s devnum %d\n",
hcd_to_bus (hcd)->bus_name, udev->devnum); udev->devpath, udev->devnum);
spin_lock_irqsave (&ehci->lock, flags); spin_lock_irqsave (&ehci->lock, flags);
for (i = 0; i < 32; i++) { for (i = 0; i < 32; i++) {
...@@ -912,7 +912,8 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev) ...@@ -912,7 +912,8 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
dev->ep [i] = 0; dev->ep [i] = 0;
if (qh->qh_state == QH_STATE_IDLE) if (qh->qh_state == QH_STATE_IDLE)
goto 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, /* scan_async() empties the ring as it does its work,
* using IAA, but doesn't (yet?) turn it off. if it * using IAA, but doesn't (yet?) turn it off. if it
......
...@@ -347,8 +347,9 @@ ...@@ -347,8 +347,9 @@
* - Don't print errors when the device is busy. * - Don't print errors when the device is busy.
* *
* 0.4.11 2003-02-25 * 0.4.11 2003-02-25
* - Added vendor/product ids for Artec, Avision, Brother, Medion, Primax, * - Added vendor/product ids for Artec, Avision, Brother, Canon, Compaq,
* Prolink, Fujitsu, Plustek, and SYSCAN scanners. * 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. * - Fixed generation of devfs names if dynamic minors are disabled.
* - Used kobject reference counting to free the scn struct when the device * - Used kobject reference counting to free the scn struct when the device
* is closed and disconnected. Avoids crashes when writing to a * is closed and disconnected. Avoids crashes when writing to a
......
...@@ -105,6 +105,7 @@ static struct usb_device_id scanner_device_ids [] = { ...@@ -105,6 +105,7 @@ static struct usb_device_id scanner_device_ids [] = {
{ USB_DEVICE(0x0638, 0x0a10) }, /* iVina FB1600 (=Umax Astra 4500) */ { USB_DEVICE(0x0638, 0x0a10) }, /* iVina FB1600 (=Umax Astra 4500) */
/* Benq: see Acer */ /* Benq: see Acer */
/* Brother */ /* Brother */
{ USB_DEVICE(0x04f9, 0x010f) }, /* MFC 5100C */
{ USB_DEVICE(0x04f9, 0x0111) }, /* MFC 6800 */ { USB_DEVICE(0x04f9, 0x0111) }, /* MFC 6800 */
/* Canon */ /* Canon */
{ USB_DEVICE(0x04a9, 0x2201) }, /* CanoScan FB320U */ { USB_DEVICE(0x04a9, 0x2201) }, /* CanoScan FB320U */
...@@ -118,9 +119,11 @@ static struct usb_device_id scanner_device_ids [] = { ...@@ -118,9 +119,11 @@ static struct usb_device_id scanner_device_ids [] = {
{ USB_DEVICE(0x04a9, 0x220c) }, /* CanoScan D1250U2 */ { USB_DEVICE(0x04a9, 0x220c) }, /* CanoScan D1250U2 */
{ USB_DEVICE(0x04a9, 0x220d) }, /* CanoScan N670U/N676U/LIDE 20 */ { USB_DEVICE(0x04a9, 0x220d) }, /* CanoScan N670U/N676U/LIDE 20 */
{ USB_DEVICE(0x04a9, 0x220e) }, /* CanoScan N1240U/LIDE 30 */ { USB_DEVICE(0x04a9, 0x220e) }, /* CanoScan N1240U/LIDE 30 */
{ USB_DEVICE(0x04a9, 0x2213) }, /* LIDE 50 */
{ USB_DEVICE(0x04a9, 0x3042) }, /* FS4000US */ { USB_DEVICE(0x04a9, 0x3042) }, /* FS4000US */
/* Colorado -- See Primax/Colorado below */ /* Colorado -- See Primax/Colorado below */
/* Compaq */ /* Compaq */
{ USB_DEVICE(0x049f, 0x001a) }, /* S4 100 */
{ USB_DEVICE(0x049f, 0x0021) }, /* S200 */ { USB_DEVICE(0x049f, 0x0021) }, /* S200 */
/* Epson -- See Seiko/Epson below */ /* Epson -- See Seiko/Epson below */
/* Fujitsu */ /* Fujitsu */
...@@ -152,6 +155,8 @@ static struct usb_device_id scanner_device_ids [] = { ...@@ -152,6 +155,8 @@ static struct usb_device_id scanner_device_ids [] = {
{ USB_DEVICE(0x03f0, 0x0705) }, /* ScanJet 4400C */ { USB_DEVICE(0x03f0, 0x0705) }, /* ScanJet 4400C */
// { USB_DEVICE(0x03f0, 0x0801) }, /* ScanJet 7400C - NOT SUPPORTED - use hpusbscsi driver */ // { USB_DEVICE(0x03f0, 0x0801) }, /* ScanJet 7400C - NOT SUPPORTED - use hpusbscsi driver */
{ USB_DEVICE(0x03f0, 0x0901) }, /* ScanJet 2300C */ { 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, 0x1305) }, /* Scanjet 4570c */
{ USB_DEVICE(0x03f0, 0x2005) }, /* ScanJet 3570c */ { USB_DEVICE(0x03f0, 0x2005) }, /* ScanJet 3570c */
{ USB_DEVICE(0x03f0, 0x2205) }, /* ScanJet 3500c */ { USB_DEVICE(0x03f0, 0x2205) }, /* ScanJet 3500c */
...@@ -159,12 +164,16 @@ static struct usb_device_id scanner_device_ids [] = { ...@@ -159,12 +164,16 @@ static struct usb_device_id scanner_device_ids [] = {
{ USB_DEVICE(0x0638, 0x0268) }, /* 1200U */ { USB_DEVICE(0x0638, 0x0268) }, /* 1200U */
/* Lexmark */ /* Lexmark */
{ USB_DEVICE(0x043d, 0x002d) }, /* X70/X73 */ { USB_DEVICE(0x043d, 0x002d) }, /* X70/X73 */
{ USB_DEVICE(0x043d, 0x003d) }, /* X83 */
/* LG Electronics */
{ USB_DEVICE(0x0461, 0x0364) }, /* Scanworks 600U (repackaged Primax?) */
/* Medion */ /* Medion */
{ USB_DEVICE(0x0461, 0x0377) }, /* MD 5345 - repackaged Primax? */ { USB_DEVICE(0x0461, 0x0377) }, /* MD 5345 - repackaged Primax? */
/* Memorex */ /* Memorex */
{ USB_DEVICE(0x0461, 0x0346) }, /* 6136u - repackaged Primax ? */ { USB_DEVICE(0x0461, 0x0346) }, /* 6136u - repackaged Primax ? */
/* Microtek */ /* Microtek */
{ USB_DEVICE(0x05da, 0x30ce) }, /* ScanMaker 3800 */ { USB_DEVICE(0x05da, 0x30ce) }, /* ScanMaker 3800 */
{ USB_DEVICE(0x05da, 0x30cf) }, /* ScanMaker 4800 */
/* The following SCSI-over-USB Microtek devices are supported by the /* The following SCSI-over-USB Microtek devices are supported by the
microtek driver: Enable SCSI and USB Microtek in kernel config */ microtek driver: Enable SCSI and USB Microtek in kernel config */
// { USB_DEVICE(0x05da, 0x0099) }, /* ScanMaker X6 - X6U */ // { USB_DEVICE(0x05da, 0x0099) }, /* ScanMaker X6 - X6U */
...@@ -259,7 +268,11 @@ static struct usb_device_id scanner_device_ids [] = { ...@@ -259,7 +268,11 @@ static struct usb_device_id scanner_device_ids [] = {
{ USB_DEVICE(0x04b8, 0x0802) }, /* Stylus CX3200 */ { USB_DEVICE(0x04b8, 0x0802) }, /* Stylus CX3200 */
/* SYSCAN */ /* SYSCAN */
{ USB_DEVICE(0x0a82, 0x4600) }, /* TravelScan 460/464 */ { USB_DEVICE(0x0a82, 0x4600) }, /* TravelScan 460/464 */
/* Trust */
{ USB_DEVICE(0x05cb, 0x1483) }, /* CombiScan 19200 */
{ USB_DEVICE(0x05d8, 0x4006) }, /* Easy Webscan 19200 (repackaged Artec?) */
/* Umax */ /* Umax */
{ USB_DEVICE(0x05d8, 0x4009) }, /* Astraslim (actually Artec?) */
{ USB_DEVICE(0x1606, 0x0010) }, /* Astra 1220U */ { USB_DEVICE(0x1606, 0x0010) }, /* Astra 1220U */
{ USB_DEVICE(0x1606, 0x0030) }, /* Astra 2000U */ { USB_DEVICE(0x1606, 0x0030) }, /* Astra 2000U */
{ USB_DEVICE(0x1606, 0x0060) }, /* Astra 3400U/3450U */ { USB_DEVICE(0x1606, 0x0060) }, /* Astra 3400U/3450U */
......
...@@ -1334,6 +1334,9 @@ void hid_init_reports(struct hid_device *hid) ...@@ -1334,6 +1334,9 @@ void hid_init_reports(struct hid_device *hid)
#define USB_VENDOR_ID_TANGTOP 0x0d3d #define USB_VENDOR_ID_TANGTOP 0x0d3d
#define USB_DEVICE_ID_TANGTOP_USBPS2 0x0001 #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 { struct hid_blacklist {
__u16 idVendor; __u16 idVendor;
__u16 idProduct; __u16 idProduct;
...@@ -1377,6 +1380,7 @@ struct hid_blacklist { ...@@ -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 + 400, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, 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_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 } { 0, 0 }
}; };
......
...@@ -12,5 +12,3 @@ obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o ...@@ -12,5 +12,3 @@ obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o
obj-$(CONFIG_USB_TEST) += usbtest.o obj-$(CONFIG_USB_TEST) += usbtest.o
obj-$(CONFIG_USB_TIGL) += tiglusb.o obj-$(CONFIG_USB_TIGL) += tiglusb.o
obj-$(CONFIG_USB_USS720) += uss720.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 @@ ...@@ -61,7 +61,6 @@
#include <linux/atm.h> #include <linux/atm.h>
#include <linux/atmdev.h> #include <linux/atmdev.h>
#include <linux/crc32.h> #include <linux/crc32.h>
#include "atmsar.h"
/* /*
#define DEBUG 1 #define DEBUG 1
...@@ -102,6 +101,8 @@ static int udsl_print_packet (const unsigned char *data, int len); ...@@ -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_OUT 0x07
#define UDSL_ENDPOINT_DATA_IN 0x87 #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) ) #define hex2int(c) ( (c >= '0')&&(c <= '9') ? (c - '0') : ((c & 0xf)+9) )
/* usb_device_id struct */ /* usb_device_id struct */
...@@ -147,6 +148,30 @@ struct udsl_control { ...@@ -147,6 +148,30 @@ struct udsl_control {
#define UDSL_SKB(x) ((struct udsl_control *)(x)->cb) #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 * UDSL main driver data
*/ */
...@@ -229,6 +254,188 @@ static struct usb_driver udsl_usb_driver = { ...@@ -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 ** ** encode **
*************/ *************/
...@@ -396,7 +603,7 @@ static void udsl_process_receive (unsigned long data) ...@@ -396,7 +603,7 @@ static void udsl_process_receive (unsigned long data)
dbg ("(after cell processing)skb->len = %d", new->len); dbg ("(after cell processing)skb->len = %d", new->len);
switch (atmsar_vcc->type) { switch (atmsar_vcc->type) {
case ATMSAR_TYPE_AAL5: case ATM_AAL5:
tmp = new; tmp = new;
new = atmsar_decode_aal5 (atmsar_vcc, 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) ...@@ -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;
/*************************************************************************** work->next = work->next->next;
* }
* init functions
* 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) static void udsl_atm_dev_close (struct atm_dev *dev)
{ {
...@@ -718,13 +1008,6 @@ 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; dev->dev_data = NULL;
} }
/***************************************************************************
*
* ATM helper functions
*
****************************************************************************/
static int udsl_atm_proc_read (struct atm_dev *atm_dev, loff_t *pos, char *page) 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; 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) ...@@ -778,12 +1061,7 @@ static int udsl_atm_proc_read (struct atm_dev *atm_dev, loff_t *pos, char *page)
return 0; return 0;
} }
#define ATMSAR_SET_PTI 0x2L
/***************************************************************************
*
* SAR driver entries
*
****************************************************************************/
static int udsl_atm_open (struct atm_vcc *vcc, short vpi, int vci) 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) ...@@ -803,7 +1081,7 @@ static int udsl_atm_open (struct atm_vcc *vcc, short vpi, int vci)
MOD_INC_USE_COUNT; MOD_INC_USE_COUNT;
vcc->dev_data = 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); ATMSAR_USE_53BYTE_CELL | ATMSAR_SET_PTI);
if (!vcc->dev_data) { if (!vcc->dev_data) {
MOD_DEC_USE_COUNT; MOD_DEC_USE_COUNT;
...@@ -866,9 +1144,9 @@ static int udsl_atm_ioctl (struct atm_dev *dev, unsigned int cmd, void *arg) ...@@ -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) 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) ...@@ -1180,11 +1458,9 @@ static void udsl_usb_disconnect (struct usb_interface *intf)
} }
/*************************************************************************** /***********
* ** init **
* Driver Init ***********/
*
****************************************************************************/
static int __init udsl_usb_init (void) static int __init udsl_usb_init (void)
{ {
...@@ -1215,13 +1491,11 @@ MODULE_DESCRIPTION (DRIVER_DESC); ...@@ -1215,13 +1491,11 @@ MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_LICENSE ("GPL"); MODULE_LICENSE ("GPL");
#ifdef DEBUG_PACKET /************
/******************************************************************************* ** debug **
* ************/
* Debug
*
*******************************************************************************/
#ifdef DEBUG_PACKET
static int udsl_print_packet (const unsigned char *data, int len) static int udsl_print_packet (const unsigned char *data, int len)
{ {
unsigned char buffer [256]; unsigned char buffer [256];
...@@ -1237,5 +1511,4 @@ static int udsl_print_packet (const unsigned char *data, int len) ...@@ -1237,5 +1511,4 @@ static int udsl_print_packet (const unsigned char *data, int len)
} }
return i; return i;
} }
#endif
#endif /* PACKETDEBUG */
...@@ -1064,15 +1064,23 @@ static void set_ethernet_addr( ether_dev_t *ether_dev ) ...@@ -1064,15 +1064,23 @@ static void set_ethernet_addr( ether_dev_t *ether_dev )
// Used by driver's probe routine //////////////////////////////////////////// // 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 len;
int string_num; int string_num;
unsigned char manu[256]; unsigned char *manu = NULL;
unsigned char prod[256]; unsigned char *prod = NULL;
unsigned char sern[256]; unsigned char *sern = NULL;
unsigned char *mac_addr; 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 // Default empty strings in case we don't find a real one
manu[0] = 0x00; manu[0] = 0x00;
prod[0] = 0x00; prod[0] = 0x00;
...@@ -1113,6 +1121,10 @@ void log_device_info(ether_dev_t *ether_dev) ...@@ -1113,6 +1121,10 @@ void log_device_info(ether_dev_t *ether_dev)
ether_dev->net->name, manu, prod, sern, mac_addr[0], ether_dev->net->name, manu, prod, sern, mac_addr[0],
mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4],
mac_addr[5] ); mac_addr[5] );
fini:
kfree(manu);
kfree(prod);
kfree(sern);
} }
/* Forward declaration */ /* Forward declaration */
......
...@@ -121,7 +121,7 @@ static int get_registers(pegasus_t * pegasus, __u16 indx, __u16 size, ...@@ -121,7 +121,7 @@ static int get_registers(pegasus_t * pegasus, __u16 indx, __u16 size,
char *buffer; char *buffer;
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
buffer = kmalloc(size, GFP_DMA); buffer = kmalloc(size, GFP_KERNEL);
if (!buffer) { if (!buffer) {
warn("%s: looks like we're out of memory", __FUNCTION__); warn("%s: looks like we're out of memory", __FUNCTION__);
return -ENOMEM; return -ENOMEM;
...@@ -170,7 +170,7 @@ static int set_registers(pegasus_t * pegasus, __u16 indx, __u16 size, ...@@ -170,7 +170,7 @@ static int set_registers(pegasus_t * pegasus, __u16 indx, __u16 size,
char *buffer; char *buffer;
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
buffer = kmalloc(size, GFP_DMA); buffer = kmalloc(size, GFP_KERNEL);
if (!buffer) { if (!buffer) {
warn("%s: looks like we're out of memory", __FUNCTION__); warn("%s: looks like we're out of memory", __FUNCTION__);
return -ENOMEM; return -ENOMEM;
...@@ -218,7 +218,7 @@ static int set_register(pegasus_t * pegasus, __u16 indx, __u8 data) ...@@ -218,7 +218,7 @@ static int set_register(pegasus_t * pegasus, __u16 indx, __u8 data)
char *tmp; char *tmp;
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
tmp = kmalloc(1, GFP_DMA); tmp = kmalloc(1, GFP_KERNEL);
if (!tmp) { if (!tmp) {
warn("%s: looks like we're out of memory", __FUNCTION__); warn("%s: looks like we're out of memory", __FUNCTION__);
return -ENOMEM; return -ENOMEM;
......
...@@ -783,7 +783,7 @@ static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, un ...@@ -783,7 +783,7 @@ static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, un
if (info->mcr & UART_MCR_RTS) if (info->mcr & UART_MCR_RTS)
modem_signals |= TIOCM_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; return -EFAULT;
break; break;
......
/* /*
* USB Skeleton driver - 0.9 * USB Skeleton driver - 1.0
* *
* Copyright (c) 2001-2002 Greg Kroah-Hartman (greg@kroah.com) * Copyright (c) 2001-2002 Greg Kroah-Hartman (greg@kroah.com)
* *
...@@ -12,14 +12,17 @@ ...@@ -12,14 +12,17 @@
* USB driver quickly. The design of it is based on the usb-serial and * USB driver quickly. The design of it is based on the usb-serial and
* dc2xx drivers. * dc2xx drivers.
* *
* Thanks to Oliver Neukum and David Brownell for their help in debugging * Thanks to Oliver Neukum, David Brownell, and Alan Stern for their help
* this driver. * in debugging this driver.
* *
* TODO:
* - fix urb->status race condition in write sequence
* *
* History: * 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_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 * 2002_09_26 - 0.8 - changes due to USB core conversion to struct device
* driver. * driver.
...@@ -33,7 +36,7 @@ ...@@ -33,7 +36,7 @@
* 2001_05_29 - 0.3 - more bug fixes based on review from linux-usb-devel * 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_24 - 0.2 - bug fixes based on review from linux-usb-devel people
* 2001_05_01 - 0.1 - first version * 2001_05_01 - 0.1 - first version
* *
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -42,8 +45,8 @@ ...@@ -42,8 +45,8 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/completion.h>
#include <linux/devfs_fs_kernel.h> #include <linux/devfs_fs_kernel.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <linux/usb.h> #include <linux/usb.h>
...@@ -60,7 +63,7 @@ ...@@ -60,7 +63,7 @@
/* Version Information */ /* Version Information */
#define DRIVER_VERSION "v0.4" #define DRIVER_VERSION "v1.0"
#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com" #define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com"
#define DRIVER_DESC "USB Skeleton Driver" #define DRIVER_DESC "USB Skeleton Driver"
...@@ -101,15 +104,16 @@ struct usb_skel { ...@@ -101,15 +104,16 @@ struct usb_skel {
char num_bulk_out; /* number of bulk out endpoints we have */ char num_bulk_out; /* number of bulk out endpoints we have */
unsigned char * bulk_in_buffer; /* the buffer to receive data */ 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 */ __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
unsigned char * bulk_out_buffer; /* the buffer to send data */ 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 */ struct urb * write_urb; /* the urb used to send data */
__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ __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 */ int open; /* if the port is open or not */
struct semaphore sem; /* locks this structure */ struct semaphore sem; /* locks this structure */
}; };
...@@ -118,6 +122,8 @@ struct usb_skel { ...@@ -118,6 +122,8 @@ struct usb_skel {
/* the global usb devfs handle */ /* the global usb devfs handle */
extern devfs_handle_t usb_devfs_handle; extern devfs_handle_t usb_devfs_handle;
/* prevent races between open() and disconnect() */
static DECLARE_MUTEX (disconnect_sem);
/* local function prototypes */ /* local function prototypes */
static ssize_t skel_read (struct file *file, char *buffer, size_t count, loff_t *ppos); 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, ...@@ -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_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_open (struct inode *inode, struct file *file);
static int skel_release (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 int skel_probe (struct usb_interface *intf, const struct usb_device_id *id);
static void skel_disconnect (struct usb_interface *intf); 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); ...@@ -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 * to have a node in the /dev directory. If the USB
* device were for a network interface then the driver * device were for a network interface then the driver
* would use "struct net_driver" instead, and a serial * 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 = { static struct file_operations skel_fops = {
/* /*
...@@ -167,7 +173,7 @@ static struct file_operations skel_fops = { ...@@ -167,7 +173,7 @@ static struct file_operations skel_fops = {
.ioctl = skel_ioctl, .ioctl = skel_ioctl,
.open = skel_open, .open = skel_open,
.release = skel_release, .release = skel_release,
}; };
/* usb specific object needed to register this driver with the usb subsystem */ /* 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 ...@@ -188,8 +194,8 @@ static inline void usb_skel_debug_data (const char *function, int size, const un
if (!debug) if (!debug)
return; return;
printk (KERN_DEBUG __FILE__": %s - length = %d, data = ", printk (KERN_DEBUG __FILE__": %s - length = %d, data = ",
function, size); function, size);
for (i = 0; i < size; ++i) { for (i = 0; i < size; ++i) {
printk ("%.2x ", data[i]); printk ("%.2x ", data[i]);
...@@ -206,7 +212,9 @@ static inline void skel_delete (struct usb_skel *dev) ...@@ -206,7 +212,9 @@ static inline void skel_delete (struct usb_skel *dev)
if (dev->bulk_in_buffer != NULL) if (dev->bulk_in_buffer != NULL)
kfree (dev->bulk_in_buffer); kfree (dev->bulk_in_buffer);
if (dev->bulk_out_buffer != NULL) 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) if (dev->write_urb != NULL)
usb_free_urb (dev->write_urb); usb_free_urb (dev->write_urb);
kfree (dev); kfree (dev);
...@@ -222,22 +230,28 @@ static int skel_open (struct inode *inode, struct file *file) ...@@ -222,22 +230,28 @@ static int skel_open (struct inode *inode, struct file *file)
struct usb_interface *interface; struct usb_interface *interface;
int subminor; int subminor;
int retval = 0; int retval = 0;
dbg("%s", __FUNCTION__); dbg("%s", __FUNCTION__);
subminor = minor (inode->i_rdev); subminor = minor (inode->i_rdev);
/* prevent disconnects */
down (&disconnect_sem);
interface = usb_find_interface (&skel_driver, interface = usb_find_interface (&skel_driver,
mk_kdev(USB_MAJOR, subminor)); mk_kdev(USB_MAJOR, subminor));
if (!interface) { if (!interface) {
err ("%s - error, can't find device for minor %d", err ("%s - error, can't find device for minor %d",
__FUNCTION__, subminor); __FUNCTION__, subminor);
return -ENODEV; retval = -ENODEV;
goto exit_no_device;
} }
dev = usb_get_intfdata(interface); dev = usb_get_intfdata(interface);
if (!dev) if (!dev) {
return -ENODEV; retval = -ENODEV;
goto exit_no_device;
}
/* lock this device */ /* lock this device */
down (&dev->sem); down (&dev->sem);
...@@ -251,6 +265,8 @@ static int skel_open (struct inode *inode, struct file *file) ...@@ -251,6 +265,8 @@ static int skel_open (struct inode *inode, struct file *file)
/* unlock this device */ /* unlock this device */
up (&dev->sem); up (&dev->sem);
exit_no_device:
up (&disconnect_sem);
return retval; return retval;
} }
...@@ -280,6 +296,12 @@ static int skel_release (struct inode *inode, struct file *file) ...@@ -280,6 +296,12 @@ static int skel_release (struct inode *inode, struct file *file)
goto exit_not_opened; 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) { if (dev->udev == NULL) {
/* the device was unplugged before the file was released */ /* the device was unplugged before the file was released */
up (&dev->sem); up (&dev->sem);
...@@ -287,11 +309,6 @@ static int skel_release (struct inode *inode, struct file *file) ...@@ -287,11 +309,6 @@ static int skel_release (struct inode *inode, struct file *file)
return 0; return 0;
} }
/* shutdown any bulk writes that might be going on */
usb_unlink_urb (dev->write_urb);
dev->open = 0;
exit_not_opened: exit_not_opened:
up (&dev->sem); up (&dev->sem);
...@@ -308,7 +325,7 @@ static ssize_t skel_read (struct file *file, char *buffer, size_t count, loff_t ...@@ -308,7 +325,7 @@ static ssize_t skel_read (struct file *file, char *buffer, size_t count, loff_t
int retval = 0; int retval = 0;
dev = (struct usb_skel *)file->private_data; dev = (struct usb_skel *)file->private_data;
dbg("%s - minor %d, count = %d", __FUNCTION__, dev->minor, count); dbg("%s - minor %d, count = %d", __FUNCTION__, dev->minor, count);
/* lock this object */ /* lock this object */
...@@ -319,12 +336,13 @@ static ssize_t skel_read (struct file *file, char *buffer, size_t count, loff_t ...@@ -319,12 +336,13 @@ static ssize_t skel_read (struct file *file, char *buffer, size_t count, loff_t
up (&dev->sem); up (&dev->sem);
return -ENODEV; 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, retval = usb_bulk_msg (dev->udev,
usb_rcvbulkpipe (dev->udev, usb_rcvbulkpipe (dev->udev,
dev->bulk_in_endpointAddr), 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); &count, HZ*10);
/* if the read was successful, copy the data to userspace */ /* 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 ...@@ -334,7 +352,7 @@ static ssize_t skel_read (struct file *file, char *buffer, size_t count, loff_t
else else
retval = count; retval = count;
} }
/* unlock the device */ /* unlock the device */
up (&dev->sem); up (&dev->sem);
return retval; return retval;
...@@ -343,6 +361,18 @@ static ssize_t skel_read (struct file *file, char *buffer, size_t count, loff_t ...@@ -343,6 +361,18 @@ static ssize_t skel_read (struct file *file, char *buffer, size_t count, loff_t
/** /**
* skel_write * 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) 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, ...@@ -369,37 +399,38 @@ static ssize_t skel_write (struct file *file, const char *buffer, size_t count,
goto exit; goto exit;
} }
/* see if we are already in the middle of a write */ /* wait for a previous write to finish up; we don't use a timeout
if (dev->write_urb->status == -EINPROGRESS) { * and so a nonresponsive device can delay us indefinitely.
dbg ("%s - already writing", __FUNCTION__); */
goto exit; if (atomic_read (&dev->write_busy))
} wait_for_completion (&dev->write_finished);
/* we can only write as much as 1 urb will hold */ /* we can only write as much as our buffer will hold */
bytes_written = (count > dev->bulk_out_size) ? bytes_written = min (dev->bulk_out_size, count);
dev->bulk_out_size : count;
/* copy the data from userspace into our urb */ /* copy the data from userspace into our transfer buffer;
if (copy_from_user(dev->write_urb->transfer_buffer, buffer, * this is the only copy required.
*/
if (copy_from_user(dev->write_urb->transfer_buffer, buffer,
bytes_written)) { bytes_written)) {
retval = -EFAULT; retval = -EFAULT;
goto exit; goto exit;
} }
usb_skel_debug_data (__FUNCTION__, bytes_written, usb_skel_debug_data (__FUNCTION__, bytes_written,
dev->write_urb->transfer_buffer); dev->write_urb->transfer_buffer);
/* set up our urb */ /* this urb was already set up, except for this write size */
usb_fill_bulk_urb(dev->write_urb, dev->udev, dev->write_urb->transfer_buffer_length = bytes_written;
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
dev->write_urb->transfer_buffer, bytes_written,
skel_write_bulk_callback, dev);
/* send the data out the bulk port */ /* send the data out the bulk port */
/* a character device write uses GFP_KERNEL, /* a character device write uses GFP_KERNEL,
unless a spinlock is held */ 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); retval = usb_submit_urb(dev->write_urb, GFP_KERNEL);
if (retval) { if (retval) {
atomic_set (&dev->write_busy, 0);
err("%s - failed submitting write urb, error %d", err("%s - failed submitting write urb, error %d",
__FUNCTION__, retval); __FUNCTION__, retval);
} else { } else {
...@@ -435,12 +466,11 @@ static int skel_ioctl (struct inode *inode, struct file *file, unsigned int cmd, ...@@ -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__, dbg("%s - minor %d, cmd 0x%.4x, arg %ld", __FUNCTION__,
dev->minor, cmd, arg); dev->minor, cmd, arg);
/* fill in your device specific stuff here */ /* fill in your device specific stuff here */
/* unlock the device */ /* unlock the device */
up (&dev->sem); up (&dev->sem);
/* return that we did not understand this ioctl call */ /* return that we did not understand this ioctl call */
return -ENOTTY; return -ENOTTY;
} }
...@@ -455,14 +485,16 @@ static void skel_write_bulk_callback (struct urb *urb, struct pt_regs *regs) ...@@ -455,14 +485,16 @@ static void skel_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
dbg("%s - minor %d", __FUNCTION__, dev->minor); dbg("%s - minor %d", __FUNCTION__, dev->minor);
if ((urb->status != -ENOENT) && /* sync/async unlink faults aren't errors */
(urb->status != -ECONNRESET)) { if (urb->status && !(urb->status == -ENOENT ||
urb->status == -ECONNRESET)) {
dbg("%s - nonzero write bulk status received: %d", dbg("%s - nonzero write bulk status received: %d",
__FUNCTION__, urb->status); __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 ...@@ -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_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint; struct usb_endpoint_descriptor *endpoint;
int minor; int minor;
int buffer_size; size_t buffer_size;
int i; int i;
int retval; int retval;
char name[10]; char name[10];
/* See if the device offered us matches what we can accept */ /* See if the device offered us matches what we can accept */
if ((udev->descriptor.idVendor != USB_SKEL_VENDOR_ID) || if ((udev->descriptor.idVendor != USB_SKEL_VENDOR_ID) ||
(udev->descriptor.idProduct != USB_SKEL_PRODUCT_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 ...@@ -513,12 +545,15 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
/* set up the endpoint information */ /* set up the endpoint information */
/* check out the endpoints */ /* check out the endpoints */
/* use only the first bulk-in and bulk-out endpoints */
iface_desc = &interface->altsetting[0]; iface_desc = &interface->altsetting[0];
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc; endpoint = &iface_desc->endpoint[i].desc;
if ((endpoint->bEndpointAddress & 0x80) && if (!dev->bulk_in_endpointAddr &&
((endpoint->bmAttributes & 3) == 0x02)) { (endpoint->bEndpointAddress & USB_DIR_IN) &&
((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_BULK)) {
/* we found a bulk in endpoint */ /* we found a bulk in endpoint */
buffer_size = endpoint->wMaxPacketSize; buffer_size = endpoint->wMaxPacketSize;
dev->bulk_in_size = buffer_size; dev->bulk_in_size = buffer_size;
...@@ -529,9 +564,11 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i ...@@ -529,9 +564,11 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
goto error; goto error;
} }
} }
if (((endpoint->bEndpointAddress & 0x80) == 0x00) && if (!dev->bulk_out_endpointAddr &&
((endpoint->bmAttributes & 3) == 0x02)) { !(endpoint->bEndpointAddress & USB_DIR_IN) &&
((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_BULK)) {
/* we found a bulk out endpoint */ /* we found a bulk out endpoint */
/* a probe() may sleep and has no restrictions on memory allocations */ /* a probe() may sleep and has no restrictions on memory allocations */
dev->write_urb = usb_alloc_urb(0, GFP_KERNEL); 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 ...@@ -539,40 +576,56 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
err("No free urbs available"); err("No free urbs available");
goto error; 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; buffer_size = endpoint->wMaxPacketSize;
dev->bulk_out_size = buffer_size; dev->bulk_out_size = buffer_size;
dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; dev->write_urb->transfer_flags = (URB_NO_DMA_MAP |
dev->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL); 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) { if (!dev->bulk_out_buffer) {
err("Couldn't allocate bulk_out_buffer"); err("Couldn't allocate bulk_out_buffer");
goto error; goto error;
} }
usb_fill_bulk_urb(dev->write_urb, udev, usb_fill_bulk_urb(dev->write_urb, udev,
usb_sndbulkpipe(udev, usb_sndbulkpipe(udev,
endpoint->bEndpointAddress), endpoint->bEndpointAddress),
dev->bulk_out_buffer, buffer_size, dev->bulk_out_buffer, buffer_size,
skel_write_bulk_callback, dev); 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 */ /* initialize the devfs node for this device and register it */
sprintf(name, "skel%d", dev->minor); sprintf(name, "skel%d", dev->minor);
dev->devfs = devfs_register (usb_devfs_handle, name, dev->devfs = devfs_register (usb_devfs_handle, name,
DEVFS_FL_DEFAULT, USB_MAJOR, DEVFS_FL_DEFAULT, USB_MAJOR,
dev->minor, dev->minor,
S_IFCHR | S_IRUSR | S_IWUSR | S_IFCHR | S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP | S_IROTH, S_IRGRP | S_IWGRP | S_IROTH,
&skel_fops, NULL); &skel_fops, NULL);
/* let the user know what node this device is now attached to */ /* 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 */ /* add device id so the device works when advertised */
interface->kdev = mk_kdev(USB_MAJOR, dev->minor); interface->kdev = mk_kdev(USB_MAJOR, dev->minor);
goto exit; goto exit;
error: error:
skel_delete (dev); skel_delete (dev);
dev = NULL; dev = NULL;
...@@ -593,12 +646,21 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i ...@@ -593,12 +646,21 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
* skel_disconnect * skel_disconnect
* *
* Called by the usb core when the device is removed from the system. * 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) static void skel_disconnect(struct usb_interface *interface)
{ {
struct usb_skel *dev; struct usb_skel *dev;
int minor; int minor;
/* prevent races with open() */
down (&disconnect_sem);
dev = usb_get_intfdata (interface); dev = usb_get_intfdata (interface);
usb_set_intfdata (interface, NULL); usb_set_intfdata (interface, NULL);
...@@ -606,7 +668,7 @@ static void skel_disconnect(struct usb_interface *interface) ...@@ -606,7 +668,7 @@ static void skel_disconnect(struct usb_interface *interface)
return; return;
down (&dev->sem); down (&dev->sem);
/* remove device id to disable open() */ /* remove device id to disable open() */
interface->kdev = NODEV; interface->kdev = NODEV;
...@@ -617,15 +679,21 @@ static void skel_disconnect(struct usb_interface *interface) ...@@ -617,15 +679,21 @@ static void skel_disconnect(struct usb_interface *interface)
/* give back our dynamic minor */ /* give back our dynamic minor */
usb_deregister_dev (1, 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 the device is not opened, then we clean up right now */
if (!dev->open) { if (!dev->open)
up (&dev->sem);
skel_delete (dev); skel_delete (dev);
} else {
dev->udev = NULL; up (&disconnect_sem);
up (&dev->sem);
}
info("USB Skeleton #%d now disconnected", minor); info("USB Skeleton #%d now disconnected", minor);
} }
...@@ -668,4 +736,3 @@ module_exit (usb_skel_exit); ...@@ -668,4 +736,3 @@ module_exit (usb_skel_exit);
MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC); MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL"); 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