//Make sure you call the init method!!!
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <linux/malloc.h>
#include <linux/list.h>
#include <asm/uaccess.h>
#include <linux/usb.h>
#include <linux/smp_lock.h>
#include <linux/atm.h>
#include <linux/atmdev.h>
#include "sarlib.h"
/*
#define DEBUG 1
*/
#define DEBUG_PACKET 1
#ifdef DEBUG
#define PDEBUG(arg...) printk(KERN_DEBUG "SpeedTouch USB: " arg)
#else
#define PDEBUG(arg...)
#endif
#ifdef DEBUG_PACKET
#define PACKETDEBUG(arg...) udsl_print_packet ( arg )
#else
#define PACKETDEBUG(arg...)
#endif
MODULE_AUTHOR ("Johan Verrept (Johan.Verrept@advalvas.be)");
MODULE_DESCRIPTION ("Driver for the Alcatel Speed Touch USB ADSL modem.");
#define SPEEDTOUCH_VENDORID 0x0466
#define SPEEDTOUCH_PRODUCTID 0x4261
#define MAX_UDSL 1
#define UDSL_OBUF_SIZE 32768
#define UDSL_MINOR 48
#define UDSL_NUMBER_RCV_URBS 1
#define UDSL_RECEIVE_BUFFER_SIZE 64*53
#define UDSL_MAX_AAL5_MRU 1500 /* max should be (1500 IP mtu + 2 ppp bytes + 32 * 5 cellheader overhead) for PPPoA and (1500 + 14 + 32*5 cellheader overhead) for PPPoE */
#define UDSL_SEND_CONTEXTS 8
#define UDSL_IOCTL_START 1
#define UDSL_IOCTL_STOP 2
/* endpoint declarations */
#define UDSL_ENDPOINT_DATA_OUT 0x7
#define UDSL_ENDPOINT_DATA_IN 0x87
/* usb_device_id struct */
static struct usb_device_id udsl_usb_ids [] =
{
{ USB_DEVICE (SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID) },
{ } /* list terminator */
};
/* not exporting this prevents the depmod from generating the map that causes the modules to be isnserted as driver.
* we do not want this, we want the script run.
MODULE_DEVICE_TABLE ( usb, udsl_usb_ids);
*/
/* context declarations */
typedef struct udsl_data_ctx {
struct sk_buff *skb;
urb_t *urb;
struct udsl_instance_data *instance;
} udsl_data_ctx_t;
typedef struct udsl_usb_send_data_context {
urb_t urb;
struct sk_buff *skb;
struct atm_vcc *vcc;
} udsl_usb_send_data_context_t;
/*
* UDSL main driver data
*/
struct udsl_instance_data {
int minor;
/* usb device part */
struct usb_device *usb_dev;
udsl_data_ctx_t *rcvbufs;
struct sk_buff_head skb_queue;
udsl_usb_send_data_context_t send_ctx;
int data_started;
/* atm device part */
struct atm_dev *atm_dev;
struct sk_buff_head recvqueue;
struct sarlib_vcc_data *sarlib_vcc_list;
};
struct udsl_instance_data *minor_data[MAX_UDSL];
static const char udsl_driver_name[] = "Alcatel SpeedTouch USB";
/* data thread */
static int datapid = 0;
DECLARE_WAIT_QUEUE_HEAD(udsl_wqh);
#ifdef DEBUG_PACKET
int udsl_print_packet (const unsigned char *data, int len);
#endif
/*
* atm driver prototypes and stuctures
*/
static int udsl_atm_open(struct atm_vcc *vcc, short vpi, int vci);
static void udsl_atm_close(struct atm_vcc *vcc);
static int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd, void *arg);
static int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb);
int udsl_atm_proc_read (struct atm_dev * atm_dev, loff_t * pos, char * page);
void udsl_atm_processqueue ( struct udsl_instance_data *instance);
static struct atmdev_ops udsl_atm_devops =
{
open: udsl_atm_open,
close: udsl_atm_close,
ioctl: udsl_atm_ioctl,
send: udsl_atm_send,
proc_read: udsl_atm_proc_read,
};
typedef struct udsl_atm_dev_data {
struct sarlib_vcc_data *sarlib_vcc;
} udsl_atm_dev_data_t;
/*
* usb driver prototypes and structures
*/
static void * udsl_usb_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id);
static void udsl_usb_disconnect(struct usb_device *dev, void *ptr);
int udsl_usb_send_data (struct udsl_instance_data *instance, struct atm_vcc *vcc, struct sk_buff *skb);
static int udsl_usb_ioctl (struct usb_device *hub, unsigned int code, void *user_data);
static struct usb_driver udsl_usb_driver =
{
name: udsl_driver_name,
probe: udsl_usb_probe,
disconnect: udsl_usb_disconnect,
ioctl: udsl_usb_ioctl,
id_table: udsl_usb_ids,
};
/************
** ATM **
************/
/***************************************************************************
*
* init functions
*
****************************************************************************/
struct atm_dev *udsl_atm_startdevice(struct udsl_instance_data *instance, struct atmdev_ops *devops) {
MOD_INC_USE_COUNT;
instance->atm_dev = atm_dev_register(udsl_driver_name, devops, -1, 0);
instance->atm_dev->dev_data = instance;
instance->atm_dev->ci_range.vpi_bits = ATM_CI_MAX;
instance->atm_dev->ci_range.vci_bits = ATM_CI_MAX;
instance->atm_dev->signal = ATM_PHY_SIG_LOST;
skb_queue_head_init(&instance->recvqueue);
/* tmp init atm device, set to 128kbit */
instance->atm_dev->link_rate = 128*1000/424;
return instance->atm_dev;
}
void udsl_atm_stopdevice(struct udsl_instance_data *instance) {
struct atm_vcc *walk;
struct sk_buff *skb;
struct atm_dev *atm_dev;
if (!instance->atm_dev)
return;
atm_dev = instance->atm_dev;
/* clean queue */
while (!skb_queue_empty(&instance->recvqueue)) {
skb = skb_dequeue (&instance->recvqueue);
dev_kfree_skb(skb);
};
atm_dev->signal = ATM_PHY_SIG_LOST;
walk = atm_dev->vccs;
shutdown_atm_dev(atm_dev);
for (; walk; walk = walk->next)
wake_up(&walk->sleep);
MOD_DEC_USE_COUNT;
}
void udsl_atm_set_mac(struct udsl_instance_data *instance, const char mac[6]) {
if (!instance->atm_dev)
return;
memcpy (instance->atm_dev->esi, mac, 6);
}
/***************************************************************************
*
* ATM helper functions
*
****************************************************************************/
struct sk_buff * udsl_atm_alloc_tx(struct atm_vcc *vcc,unsigned int size) {
sarlib_vcc_data_t *sarlib_vcc = ((struct udsl_atm_dev_data *)vcc->dev_data)->sarlib_vcc;
if (sarlib_vcc)
return sarlib_alloc_tx(sarlib_vcc, size);
printk(KERN_INFO "SpeedTouch USB: udsl_atm_alloc_tx could not find correct alloc_tx function !\n");
return NULL;
}
int udsl_atm_proc_read (struct atm_dev * atm_dev, loff_t * pos, char * page) {
struct udsl_instance_data *instance = (struct udsl_instance_data *) atm_dev->dev_data;
int left = *pos;
if (!left--)
return sprintf(page, "Speed Touch USB:%d (%02x:%02x:%02x:%02x:%02x:%02x)\n", instance->minor,
atm_dev->esi[0], atm_dev->esi[1], atm_dev->esi[2], atm_dev->esi[3], atm_dev->esi[4], atm_dev->esi[5]);
if (!left--)
return sprintf(page, "AAL0: tx %d ( %d err ), rx %d ( %d err, %d drop )\n",
atomic_read(&atm_dev->stats.aal0.tx), atomic_read(&atm_dev->stats.aal0.tx_err),
atomic_read(&atm_dev->stats.aal0.rx), atomic_read(&atm_dev->stats.aal0.rx_err), atomic_read(&atm_dev->stats.aal0.rx_drop));
if (!left--)
return sprintf(page, "AAL5: tx %d ( %d err ), rx %d ( %d err, %d drop )\n",
atomic_read(&atm_dev->stats.aal5.tx), atomic_read(&atm_dev->stats.aal5.tx_err),
atomic_read(&atm_dev->stats.aal5.rx), atomic_read(&atm_dev->stats.aal5.rx_err), atomic_read(&atm_dev->stats.aal5.rx_drop));
return 0;
}
/***************************************************************************
*
* ATM DATA functions
*
****************************************************************************/
int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb) {
struct udsl_atm_dev_data *dev_data = (struct udsl_atm_dev_data *) vcc->dev_data;
struct udsl_instance_data *instance = (struct udsl_instance_data *) vcc->dev->dev_data;
struct sk_buff *new = NULL;
int err;
PDEBUG("udsl_atm_send called\n");
if (!dev_data)
return -EINVAL;
switch (vcc->qos.aal) {
case ATM_AAL5:
new = sarlib_encode_aal5(dev_data->sarlib_vcc, skb);
if (!new) return -ENOMEM;
if (new != skb) {
vcc->pop(vcc, skb);
skb = new;
}
new = sarlib_encode_rawcell(dev_data->sarlib_vcc, skb);
if (!new) return -ENOMEM;
if (new != skb) {
vcc->pop(vcc, skb);
skb = new;
}
err = udsl_usb_send_data (instance, vcc, skb);
PDEBUG("udsl_atm_send successfull (%d)\n", err);
return err;
break;
default:
return -EINVAL;
};
PDEBUG("udsl_atm_send unsuccessfull\n");
return 0;
};
void udsl_atm_processqueue ( struct udsl_instance_data *instance) {
struct sarlib_vcc_data *sarlib_vcc = NULL;
struct sk_buff *new = NULL, *skb = NULL;
/* quick check */
if (skb_queue_empty(&instance->recvqueue))
return;
PDEBUG("udsl_atm_processqueue entered\n");
while (!skb_queue_empty(&instance->recvqueue)) {
skb = skb_dequeue (&instance->recvqueue);
PDEBUG("skb = %p, skb->len = %d\n", skb, skb->len);
PACKETDEBUG (skb->data, skb->len);
while ( (new = sarlib_decode_rawcell (instance->sarlib_vcc_list, skb, &sarlib_vcc)) != NULL) {
PDEBUG("(after cell processing)skb->len = %d\n", new->len);
switch (sarlib_vcc->type) {
case SARLIB_TYPE_AAL5:
new = sarlib_decode_aal5 (sarlib_vcc, new);
/* we can't send NULL skbs upstream, the ATM layer would try to close the vcc... */
if (new) {
PDEBUG("(after aal5 decap) skb->len = %d\n", new->len);
if (new->len && atm_charge(sarlib_vcc->vcc, new->truesize)) {
PACKETDEBUG (new->data, new->len);
sarlib_vcc->vcc->push(sarlib_vcc->vcc, new);
}
else {
PDEBUG("dropping incoming packet : rx_inuse = %d, vcc->sk->rcvbuf = %d, skb->true_size = %d\n", atomic_read(&sarlib_vcc->vcc->rx_inuse), sarlib_vcc->vcc->sk->rcvbuf, new->truesize);
dev_kfree_skb(new);
}
}
else
PDEBUG("sarlib_decode_aal5 returned NULL!\n");
break;
default:
/* not supported. we delete the skb. */
printk(KERN_INFO "SpeedTouch USB: illegal vcc type. Dropping packet.\n");
dev_kfree_skb(new);
break;
}
};
dev_kfree_skb (skb);
};
PDEBUG("udsl_atm_processqueue successfull\n");
}
int udsl_atm_processqueue_thread (void *data) {
int i=0;
DECLARE_WAITQUEUE(wait, current);
lock_kernel();
exit_files(current); /* daemonize doesn't do exit_files */
daemonize();
/* Setup a nice name */
strcpy(current->comm, "kSpeedSARd");
add_wait_queue(&udsl_wqh, &wait);
for(;

{
interruptible_sleep_on(&udsl_wqh);
if (signal_pending(current)) break;
PDEBUG("SpeedSARd awoke\n");
for (i=0; i < MAX_UDSL ; i++)
if (minor_data
)
udsl_atm_processqueue(minor_data);
};
datapid = 0;
PDEBUG("SpeedSARd is exiting\n");
return 0;
}
void udsl_atm_sar_start (void) {
datapid = kernel_thread(udsl_atm_processqueue_thread, (void *)NULL,
CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
}
void udsl_atm_sar_stop (void) {
int ret;
/* Kill the thread */
ret = kill_proc(datapid, SIGTERM, 1);
if (!ret) {
/* Wait 10 seconds */
int count = 10 * 100;
while (datapid && --count) {
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(1);
}
if (!count)
err("giving up on killing SpeedSAR thread.");
}
}
/***************************************************************************
*
* SAR driver entries
*
****************************************************************************/
int udsl_atm_open(struct atm_vcc *vcc, short vpi, int vci) {
struct udsl_atm_dev_data *dev_data;
struct udsl_instance_data *instance = (struct udsl_instance_data *)vcc->dev->dev_data;
PDEBUG("udsl_atm_open called\n");
/* at the moment only AAL5 support */
if (vcc->qos.aal != ATM_AAL5)
return -EINVAL;
MOD_INC_USE_COUNT;
dev_data = (struct udsl_atm_dev_data *)kmalloc (sizeof(struct udsl_atm_dev_data), GFP_KERNEL);
if (!dev_data)
return -ENOMEM;
dev_data->sarlib_vcc = sarlib_open (&(instance->sarlib_vcc_list), vcc, SARLIB_TYPE_AAL5, vpi, vci, 0, 0, SARLIB_USE_53BYTE_CELL | SARLIB_SET_PTI);
if (!dev_data->sarlib_vcc) {
kfree(dev_data);
return -ENOMEM; /* this is the only reason sarlib_open can fail... */
}
vcc->vpi = vpi;
vcc->vci = vci;
set_bit(ATM_VF_ADDR, &vcc->flags);
set_bit(ATM_VF_PARTIAL, &vcc->flags);
set_bit(ATM_VF_READY, &vcc->flags);
vcc->dev_data = dev_data;
vcc->alloc_tx = udsl_atm_alloc_tx;
dev_data->sarlib_vcc->mtu = UDSL_MAX_AAL5_MRU;
PDEBUG("udsl_atm_open successfull\n");
return 0;
}
void udsl_atm_close(struct atm_vcc *vcc) {
struct udsl_atm_dev_data *dev_data = (struct udsl_atm_dev_data *)vcc->dev_data;
struct udsl_instance_data *instance = (struct udsl_instance_data *)vcc->dev->dev_data;
PDEBUG("udsl_atm_close called\n");
/* freeing resources */
sarlib_close (&(instance->sarlib_vcc_list), dev_data->sarlib_vcc);
kfree (dev_data);
vcc->dev_data = NULL;
clear_bit(ATM_VF_PARTIAL, &vcc->flags);
/* freeing address */
vcc->vpi = ATM_VPI_UNSPEC;
vcc->vci = ATM_VCI_UNSPEC;
clear_bit(ATM_VF_ADDR, &vcc->flags);
MOD_DEC_USE_COUNT;
PDEBUG("udsl_atm_close successfull\n");
return;
};
int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd, void *arg) {
switch (cmd) {
case ATM_QUERYLOOP:
return put_user(ATM_LM_NONE, (int *) arg) ? -EFAULT : 0;
default:
return -ENOIOCTLCMD;
}
};
/************
** USB **
************/
/***************************************************************************
*
* usb data functions
*
****************************************************************************/
typedef struct udsl_cb {
struct atm_vcc *vcc;
} udsl_cb_t;
static void udsl_usb_send_data_complete(urb_t *urb) {
struct udsl_instance_data *instance = (struct udsl_instance_data *)urb->context;
udsl_usb_send_data_context_t *ctx = &instance->send_ctx;
int err;
PDEBUG("udsl_usb_send_data_completion (vcc = %p, skb = %p, status %d)\n", ctx->vcc, ctx->skb, urb->status);
/* remove completed skb */
ctx->skb = skb_dequeue(&(instance->skb_queue));
ctx->vcc->pop (ctx->vcc, ctx->skb);
ctx->skb = NULL;
if (skb_queue_empty(&instance->skb_queue))
return;
/* submit next skb */
ctx->skb = skb_peek (&(instance->skb_queue));
ctx->vcc = ((udsl_cb_t *)(ctx->skb->cb))->vcc;
FILL_BULK_URB(urb,
instance->usb_dev,
usb_sndbulkpipe(instance->usb_dev, UDSL_ENDPOINT_DATA_OUT),
(unsigned char *)ctx->skb->data,
ctx->skb->len,
(usb_complete_t)udsl_usb_send_data_complete,
instance
);
err = usb_submit_urb(urb);
PDEBUG("udsl_usb_send_data_completion (send packet %p with length %d), retval = %d\n", ctx->skb, ctx->skb->len, err);
}
/**** send ******/
int udsl_usb_send_data (struct udsl_instance_data *instance, struct atm_vcc *vcc, struct sk_buff *skb) {
int err;
urb_t *urb;
PDEBUG("udsl_usb_send_data entered, sending packet %p with length %d\n", skb, skb->len);
if (!instance->data_started)
return -EAGAIN;
PACKETDEBUG (skb->data, skb->len);
if (!skb_queue_empty(&instance->skb_queue)) {
((udsl_cb_t *)skb->cb)->vcc = vcc;
skb_queue_tail (&instance->skb_queue, skb);
PDEBUG("udsl_usb_send_data: skb (0x%p) queued\n", skb);
return 0;
}
((udsl_cb_t *)skb->cb)->vcc = vcc;
skb_queue_tail (&instance->skb_queue, skb);
/* submit packet */
urb = &(instance->send_ctx.urb);
instance->send_ctx.skb = skb;
instance->send_ctx.vcc = vcc;
FILL_BULK_URB(urb,
instance->usb_dev,
usb_sndbulkpipe(instance->usb_dev, UDSL_ENDPOINT_DATA_OUT),
(unsigned char *)skb->data,
skb->len,
(usb_complete_t)udsl_usb_send_data_complete,
instance
);
err = usb_submit_urb(urb);
if (err != 0)
skb_unlink(skb);
PDEBUG("udsl_usb_send_data done (retval = %d)\n", err);
return err;
}
/********* receive *******/
void udsl_usb_data_receive(urb_t *urb) {
udsl_data_ctx_t *ctx;
struct udsl_instance_data *instance;
if (!urb) return;
PDEBUG("udsl_usb_receive_data entered, got packet %p with length %d an status %d\n", urb, urb->actual_length, urb->status);
ctx = (udsl_data_ctx_t *)urb->context;
if (!ctx || !ctx->skb) return;
instance = ctx->instance;
switch (urb->status) {
case 0:
PDEBUG("udsl_usb_data_receive: processing urb with ctx %p, urb %p (%p), skb %p\n", ctx, ctx ? ctx->urb : NULL , urb, ctx ? ctx->skb : NULL);
/* update the skb structure */
skb_put(ctx->skb, urb->actual_length);
/* queue the skb for processing and wake the SAR */
skb_queue_tail (&instance->recvqueue, ctx->skb);
wake_up(&udsl_wqh);
/* get a new skb */
ctx->skb = dev_alloc_skb(UDSL_RECEIVE_BUFFER_SIZE);
if (!ctx->skb) {
PDEBUG("No skb, loosing urb.\n");
usb_free_urb(ctx->urb);
ctx->urb = NULL;
return;
}
break;
case -EPIPE: /* stall or babble */
usb_clear_halt(urb->dev, usb_rcvbulkpipe(urb->dev, UDSL_ENDPOINT_DATA_IN));
break;
case -ENOENT: /* buffer was unlinked */
case -EILSEQ: /* unplug or timeout */
case -ETIMEDOUT: /* unplug or timeout */
/*
* we don't do anything here and we don't resubmit
*/
return;
}
FILL_BULK_URB(urb,
instance->usb_dev,
usb_rcvbulkpipe(instance->usb_dev, UDSL_ENDPOINT_DATA_IN),
(unsigned char *)ctx->skb->data,
UDSL_RECEIVE_BUFFER_SIZE,
(usb_complete_t)udsl_usb_data_receive,
ctx
);
urb->transfer_flags |= USB_QUEUE_BULK;
usb_submit_urb(urb);
return;
};
int udsl_usb_data_init(struct udsl_instance_data *instance) {
int i, succes;
/* set alternate setting 1 on interface 1 */
usb_set_interface(instance->usb_dev, 1, 2);
PDEBUG("max packet size on endpoint %d is %d\n", UDSL_ENDPOINT_DATA_OUT, usb_maxpacket (instance->usb_dev, usb_sndbulkpipe(instance->usb_dev, UDSL_ENDPOINT_DATA_OUT), 0));
instance->rcvbufs = (udsl_data_ctx_t *)kmalloc (sizeof(udsl_data_ctx_t) * UDSL_NUMBER_RCV_URBS , GFP_KERNEL);
if (!instance->rcvbufs)
return -ENOMEM;
memset (instance->rcvbufs, 0, sizeof(udsl_data_ctx_t) * UDSL_NUMBER_RCV_URBS);
for (i=0, succes = 0 ; i < UDSL_NUMBER_RCV_URBS; i++) {
udsl_data_ctx_t *ctx = &(instance->rcvbufs);
ctx->urb = NULL;
ctx->skb = dev_alloc_skb(UDSL_RECEIVE_BUFFER_SIZE);
if (!ctx->skb) continue;
ctx->urb = usb_alloc_urb(0);
if (!ctx->urb) {
kfree_skb(ctx->skb);
ctx->skb = NULL;
break;
};
FILL_BULK_URB(ctx->urb,
instance->usb_dev,
usb_rcvbulkpipe(instance->usb_dev, UDSL_ENDPOINT_DATA_IN),
(unsigned char *)ctx->skb->data,
UDSL_RECEIVE_BUFFER_SIZE,
(usb_complete_t)udsl_usb_data_receive,
ctx
);
ctx->urb->transfer_flags |= USB_QUEUE_BULK;
ctx->instance = instance;
PDEBUG("udsl_usb_data_init: usb with skb->truesize = %d (Asked for %d)\n", ctx->skb->truesize, UDSL_RECEIVE_BUFFER_SIZE);
if (usb_submit_urb(ctx->urb) < 0)
PDEBUG("udsl_usb_data_init: Submit failed, loosing urb.\n");
else
succes++;
}
instance->data_started=1;
instance->atm_dev->signal = ATM_PHY_SIG_FOUND;
skb_queue_head_init(&instance->skb_queue);
PDEBUG("udsl_usb_data_init %d urb%s queued for receive\n", succes, (succes!=1) ? "s" : "");
return 0;
}
int udsl_usb_data_exit(struct udsl_instance_data *instance) {
int i;
if (!instance->data_started)
return 0;
if (!instance->rcvbufs)
return 0;
/* destroy urbs */
for (i=0; i < UDSL_NUMBER_RCV_URBS; i++) {
udsl_data_ctx_t *ctx = &(instance->rcvbufs);
if (!ctx->urb)
continue;
if (ctx->urb->status == -EINPROGRESS)
usb_unlink_urb(ctx->urb);
usb_free_urb(ctx->urb);
kfree_skb(ctx->skb);
};
/* free receive contexts */
kfree (instance->rcvbufs);
instance->rcvbufs = NULL;
instance->data_started=0;
instance->atm_dev->signal = ATM_PHY_SIG_LOST;
return 0;
};
/***************************************************************************
*
* usb driver entries
*
****************************************************************************/
#define hex2int(c) ( (c >= '0')&&(c <= '9') ? (c - '0') : ((c & 0xf)+9) )
static int udsl_usb_ioctl (struct usb_device *dev, unsigned int code, void *user_data) {
struct udsl_instance_data *instance;
int i;
for (i=0; i < MAX_UDSL; i++)
if (minor_data && (minor_data->usb_dev == dev))
break;
if (i==MAX_UDSL)
return -EINVAL;
instance = minor_data;
switch(code) {
case UDSL_IOCTL_START:
return udsl_usb_data_init (instance);
break;
case UDSL_IOCTL_STOP:
return udsl_usb_data_exit (instance);
break;
default:
break;
}
return -EINVAL;
}
void *udsl_usb_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id)
{
int i;
unsigned char mac[6];
unsigned char mac_str[13];
struct udsl_instance_data *instance = NULL;
PDEBUG("Trying device with Vendor=0x%x, Product=0x%x, ifnum %d\n", dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum);
if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC)||
(dev->descriptor.idVendor != SPEEDTOUCH_VENDORID)||
(dev->descriptor.idProduct != SPEEDTOUCH_PRODUCTID)||
(ifnum!=1))
return NULL;
MOD_INC_USE_COUNT;
for (i=0; i < MAX_UDSL; i++)
if (minor_data == NULL)
break;
if (i >= MAX_UDSL) {
printk(KERN_INFO "No minor table space available for SpeedTouch USB\n");
return NULL;
};
PDEBUG("Device Accepted, assigning minor %d\n", i);
/* device init */
instance = kmalloc(sizeof(struct udsl_instance_data), GFP_KERNEL);
if (!instance) {
PDEBUG("No memory for Instance data!\n");
return NULL;
}
/* initialize structure */
memset(instance, 0, sizeof(struct udsl_instance_data));
instance->minor = i;
instance->usb_dev = dev;
instance->rcvbufs = NULL;
udsl_atm_startdevice(instance, &udsl_atm_devops);
/* set MAC address, it is stored in the serial number */
usb_string(instance->usb_dev, instance->usb_dev->descriptor.iSerialNumber, mac_str, 13);
for (i=0; i<6; i++)
mac = (hex2int(mac_str[i*2])*16) + (hex2int(mac_str[i*2+1]));
PDEBUG("MAC is %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
udsl_atm_set_mac(instance, mac);
minor_data[instance->minor] = instance;
return instance;
}
void udsl_usb_disconnect(struct usb_device *dev, void *ptr)
{
struct udsl_instance_data *instance = (struct udsl_instance_data *)ptr;
int i = instance->minor;
/* unlinking receive buffers */
udsl_usb_data_exit(instance);
/* removing atm device */
if (instance->atm_dev)
udsl_atm_stopdevice(instance);
PDEBUG("disconnecting minor %d\n", i);
while (MOD_IN_USE > 1) {
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(1);
}
kfree (instance);
minor_data = NULL;
MOD_DEC_USE_COUNT;
}
/***************************************************************************
*
* Driver Init
*
****************************************************************************/
//Call this first -- neobyte
int udsl_usb_init(void)
{
int i;
PDEBUG ("Initializing driver\n");
for (i =0; i < MAX_UDSL ; i++)
minor_data = NULL;
init_waitqueue_head(&udsl_wqh);
udsl_atm_sar_start();
return usb_register(&udsl_usb_driver);
}
int udsl_usb_cleanup(void)
{
/* killing threads */
udsl_atm_sar_stop();
usb_deregister(&udsl_usb_driver);
return 0;
}
#ifdef MODULE
int init_module(void)
{
return udsl_usb_init();
}
int cleanup_module(void)
{
return udsl_usb_cleanup();
}
#endif
#ifdef DEBUG_PACKET
/*******************************************************************************
*
* Debug
*
*******************************************************************************/
int udsl_print_packet (const unsigned char *data, int len) {
unsigned char buffer[256];
int i=0, j=0;
for (i = 0; i < len ; ) {
buffer[0] = '\0';
sprintf(buffer,"%.3d :", i);
for (j = 0; (j < 16) && (i< len) ; j++, i++) {
sprintf(buffer, "%s %2.2x", buffer, data);
}
PDEBUG("%s\n", buffer);
}
return i;
};
#endif /* PACKETDEBUG */