I am using a 2.4.26 kernel (knoppix scaled down). My problem is that once we plug in the device and begin talking to it we encounter a serious problem, the machine locks-up or reboots. This occurs in roughly 5 minutes or less time and happens repeatedly. I have run my upper-level code through several memory debugging utilities and have found no holes so I summised that it must be in the kernel driver. I have posted below the source to that file and its header. Please any help or ideas that anyone can give me will be greatly appreciated, as I have been struggling with this problem for two months time now.
Thanks all -Justin
// ============================================================================
//
// Modified by Justin Owen Metgames Inc.
// [EMAIL PROTECTED] added some changes to make it line up better with our project
//
// Copyright (c) 1999-2004 Sierra Design Group
// $Author: jlawrence $
// $Source: /agprepository/agp/kernel_2_4_18/modules/usbbally/bld/usbbally.c,v $
// $Revision: 1.1.4.3 $
// $Date: 2004/06/24 15:55:27 $
//
// $Log: usbbally.c,v $
// Revision 1.1.4.3 2004/06/24 15:55:27 jlawrence
// ISSUE : Support for Bally Mechanical Reel and Projector gadgets.
// CHANGE : Fixed to return POLLERR if select is called by client after
// the device has disconnected. Also updated to release poll's wait lock
// should the device be disconnected while poll is waiting.
// REF TTD# : 5506
//
// Revision 1.1.4.2 2004/06/07 17:33:24 jlawrence
// ISSUE : Support for Bally Mechanical Reel and Projector gadgets.
// CHANGE : Updated to implement poll so select is available. Also fixed
// a problem that would crash if unplugged before file released.
// REF TTD# : 5506
//
// Revision 1.1.4.1 2004/06/02 22:50:19 jlawrence
// ISSUE : Support for Bally Mechanical Reel and Projector gadgets.
// CHANGE : Created USB driver to communicate to USB device.
// REF TTD# : 5506
//
//
/* * USB Bally Reel Controller driver - 0.1 * * Copyright (c) 2004 Sierra Design Group ([EMAIL PROTECTED]) * * 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. * */
#include <linux/config.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> #include <linux/errno.h> #include <linux/poll.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/fcntl.h> #include <linux/module.h> #include <linux/spinlock.h> #include <linux/list.h> #include <linux/smp_lock.h> #include <linux/devfs_fs_kernel.h> #include <linux/usb.h> #include "reel.h"
/* Use our own dbg macro */ #define DEBUG
#ifdef DEBUG
#undef dbg
#define dbg(format, arg...) info( __FILE__ __FUNCTION__ ": " format , ## arg)
#else
#undef dbg
#define dbg(format, arg...)
#endif
/* Version Information */ #define DRIVER_VERSION "v0.2" #define DRIVER_AUTHOR "Shawn Quick ([EMAIL PROTECTED])" #define DRIVER_DESC "Bally Gaming USB/HID-Evo Gadget Driver"
/* These values are for Bally Reel Controller */ #define USB_BALLY_VENDOR_ID 0x0914 #define USB_REEL_PRODUCT_ID 0x0019 #define USB_LED_PRODUCT_ID 0x0011
// usb major number is 180
/* Get a minor range for your devices from the usb maintainer */
#define USB_REEL_MINOR_BASE DABUSB_MINOR
#define MAX_DEVICES MAX_GADGETS
/* we can have up to this number of device plugged in at once */
/* ring buffer container : good idea to choose multiple of 64 (packet size) */
#define BALLY_RINGBUFFER_SIZE 2048
typedef struct _bally_ringbuffer
{
unsigned char * buf; /* memory buffer */
int head; /* head: next spot to put into */
int tail; /* tail: next spot to get from */
int actSize; /* actual number of bytes present in buffer */
int bufSize; /* size of buffer memory */
}bally_ringbuffer, *pbally_ringbuffer;/* Structure to hold all of our device specific stuff */
struct usb_bally {
struct usb_device * udev; /* save off the usb device pointer */
struct usb_interface * interface; /* the interface for this device */
devfs_handle_t devfs; /* devfs device node */
unsigned char minor; /* the starting minor number for this device */
unsigned char num_ports; /* the number of ports this device has */
char num_interrupt_in; /* number of interrupt in endpoints we have */
char num_bulk_in; /* number of bulk in endpoints we have */
char num_bulk_out; /* number of bulk out endpoints we have */
struct urb * read_urb; /* read urb to receive data */
bally_ringbuffer ringbuffer; /* buffer to hold received data not read by client */
unsigned char * userBuffer; /* a temporary buffer to retrieve user data from ringbuffer */
int userBytesPending; /* used if copy_user_data fails to resend data */
int submitPending; /* for optimization: indicates module's requested a read urb */
wait_queue_head_t wait; /* used to wake up read handler from urb read callback */
struct semaphore mutex; /* used to lock/unlock ringbuffer */
unsigned char * bulk_in_buffer; /* the buffer to receive data */
int 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 */
struct urb * write_urb; /* the urb used to send data */
__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
struct tq_struct tqueue; /* task queue for line discipline waking up */
int open_count; /* number of times this port has been opened */
struct semaphore sem; /* locks this structure */
int idProduct; /* what type of bally gadget this is (productid) */
usb_gadget_report repinfo;
unsigned char repbuff[128];
};
/* the global usb devfs handle */ extern devfs_handle_t usb_devfs_handle;
/* local function prototypes */
static ssize_t bally_read (struct file *file, char *buffer, size_t count, loff_t *ppos);
static ssize_t bally_write (struct file *file, const char *buffer, size_t count, loff_t *ppos);
static int bally_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
static int bally_open (struct inode *inode, struct file *file);
static int bally_release (struct inode *inode, struct file *file);
static unsigned int bally_poll(struct file *file, poll_table *wait);
static void * bally_probe (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id);
static void bally_disconnect (struct usb_device *dev, void *ptr);
static void bally_write_bulk_callback (struct urb *urb);
static void bally_read_bulk_callback (struct urb *purb);
static void ringbuf_init(pbally_ringbuffer rb, int bufSize);
static void ringbuf_destroy(pbally_ringbuffer rb);
static void ringbuf_add(pbally_ringbuffer rb, unsigned char *src, int inSize);
static int ringbuf_remove(pbally_ringbuffer rb, unsigned char *dst, int outSize);
/* array of pointers to our devices that are currently connected */ static struct usb_bally *minor_table[MAX_DEVICES];
/* lock to protect the minor_table structure */ static DECLARE_MUTEX (minor_table_mutex);
/*
* File operations needed when we register this driver.
* This assumes that this driver NEEDS file operations,
* of course, which means that the driver is expected
* 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".
*/
static struct file_operations bally_fops = {
/*
* The owner field is part of the module-locking
* mechanism. The idea is that the kernel knows
* which module to increment the use-counter of
* BEFORE it calls the device's open() function.
* This also means that the kernel can decrement
* the use-counter again before calling release()
* or should the open() function fail.
*
* Not all device structures have an "owner" field
* yet. "struct file_operations" and "struct net_device"
* do, while "struct tty_driver" does not. If the struct
* has an "owner" field, then initialize it to the value
* THIS_MODULE and the kernel will handle all module
* locking for you automatically. Otherwise, you must
* increment the use-counter in the open() function
* and decrement it again in the release() function
* yourself.
*/
owner: THIS_MODULE,read: bally_read,
write: bally_write,
ioctl: bally_ioctl,
open: bally_open,
release: bally_release,
poll: bally_poll,
};
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver reel_driver = {
name: "usbbally",
probe: bally_probe,
disconnect: bally_disconnect,
fops: &bally_fops,
minor: USB_REEL_MINOR_BASE,
// id_table: reel_table,
};
/**
* usb_bally_debug_data
*/
static inline void usb_bally_debug_data (const char *function,
int size,
const unsigned char *data)
{
#if 0 //def DEBUG
int i;if (size==0) return;
dbg(": %s - length = %d, data = ", function, size);
for (i = 0; i < size; ++i)
{
dbg("%.2x ", data[i]);
}
dbg("\n");
#endif
}/**
* bally_delete
*/
static inline void bally_delete (struct usb_bally *dev)
{
dbg(__FUNCTION__); minor_table[dev->minor] = NULL;
if (dev->bulk_in_buffer != NULL)
kfree (dev->bulk_in_buffer);
if (dev->bulk_out_buffer != NULL)
kfree (dev->bulk_out_buffer);
if (dev->write_urb != NULL)
usb_free_urb (dev->write_urb);
if (dev->read_urb != NULL)
{
usb_unlink_urb (dev->read_urb);
usb_free_urb (dev->read_urb);
dev->read_urb = NULL;
}
if (dev->userBuffer!=NULL)
{
kfree(dev->userBuffer); dev->userBuffer=NULL;
}
ringbuf_destroy(&dev->ringbuffer);
kfree (dev);
}
/** * bally_open : called when associated file is closed. */ static int bally_open (struct inode *inode, struct file *file) { struct usb_bally *dev = NULL; int subminor; int retval = 0;
dbg("enter"); subminor = MINOR (inode->i_rdev) - USB_REEL_MINOR_BASE;
if ((subminor < 0) || (subminor >= MAX_DEVICES))
{
return -ENODEV;
}/* Increment our usage count for the module. * This is redundant here, because "struct file_operations" * has an "owner" field. This line is included here soley as * a reference for drivers using lesser structures... ;-) */ MOD_INC_USE_COUNT;
/* lock our minor table and get our local data for this minor */
down (&minor_table_mutex);
dev = minor_table[subminor];
if (dev == NULL)
{
up (&minor_table_mutex);
MOD_DEC_USE_COUNT;
return -ENODEV;
}/* lock this device */ down (&dev->sem);
/* unlock the minor table */ up (&minor_table_mutex);
/* increment our usage count for the driver */ ++dev->open_count;
/* save our object in the file's private structure */ file->private_data = dev;
/* unlock this device */ up (&dev->sem);
dbg("exit");return retval; }
/** * bally_release : called when associated file is closed. */ static int bally_release (struct inode *inode, struct file *file) { struct usb_bally *dev; int retval = 0;
dbg("enter");
dev = (struct usb_bally *)file->private_data;
if (dev == NULL)
{
dbg (" - object is NULL");
return -ENODEV;
}
dbg(" - minor %d", dev->minor);/* lock our minor table */ down (&minor_table_mutex);
/* lock our device */ down (&dev->sem);
lock_kernel();
if (dev->open_count <= 0)
{
dbg (" - device not opened");
retval = -ENODEV;
unlock_kernel();
goto exit_not_opened;
}
if (dev->udev == NULL)
{
dbg("return udev=NULL");
/* the device was unplugged before the file was released */
up (&dev->sem);
bally_delete (dev);
up (&minor_table_mutex);
MOD_DEC_USE_COUNT;
unlock_kernel();
return 0;
}
if (dev->read_urb != NULL)
{
// notify any waiting bally_read()
wake_up_interruptible(&dev->wait);
usb_unlink_urb (dev->read_urb);
dev->submitPending = 0;
}
/* decrement our usage count for the device */
--dev->open_count;
if (dev->open_count <= 0)
{
/* shutdown any bulk writes that might be going on */
usb_unlink_urb (dev->write_urb);
dev->open_count = 0;
}
/* decrement our usage count for the module */ MOD_DEC_USE_COUNT;
unlock_kernel();
exit_not_opened:
up (&dev->sem);
up (&minor_table_mutex);
dbg("exit");
return retval; }
/**
* bally_read
*/
static ssize_t bally_read (struct file *file,
char *buffer,
size_t count,
loff_t *ppos)
{
struct usb_bally *dev;
int retval = 0;
int result;
dev = (struct usb_bally *)file->private_data;
dbg("requested=%d ready=%d", count, dev->ringbuffer.actSize);
/* lock this object */ down (&dev->sem);
/* verify that the device wasn't unplugged */
if (dev->udev == NULL)
{
up (&dev->sem);
return -ENODEV;
}// check user buffer first. these are rare special case bytes that have
// already been removed from ring buffer but the copy to user failed.
// these must be sent to client first. if (dev->userBytesPending > 0)
{
if (copy_to_user (buffer, dev->userBuffer, dev->userBytesPending))
{
retval = -EFAULT;
}
else
{
retval = dev->userBytesPending;
dev->userBytesPending = 0; }
up (&dev->sem);
return (retval);
}
// see if any read data is ready
if (count <= dev->ringbuffer.actSize)
{
dbg("getting from ringbuffer");
// if so, get it from the ringbuffer
down(&dev->mutex);
retval= ringbuf_remove(&dev->ringbuffer, dev->userBuffer, count);
up(&dev->mutex);
// copy to the user's buffer
if (copy_to_user (buffer, dev->userBuffer, retval))
{
dev->userBytesPending = retval;
retval = -EFAULT;
}
up (&dev->sem);
info("returning from ringbuf get");
return (retval);
}
// submit urb request, unless we know we don't need to to
// note, it doesn't really hurt to resubmit API docs that it simply return err
if (!dev->submitPending)
{
dbg("submitting");
FILL_BULK_URB(dev->read_urb, dev->udev,
usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
dev->bulk_in_buffer, dev->bulk_in_size,
bally_read_bulk_callback, dev);
result = usb_submit_urb(dev->read_urb);
dev->submitPending= (result==0) ? 1 : 0; // result is 0 if submit worked ok
}
/* unlock the device */
up (&dev->sem);
return retval;
}
/** * bally_write */ static ssize_t bally_write (struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct usb_bally *dev; ssize_t bytes_written = 0; int retval = 0;
dev = (struct usb_bally *)file->private_data;
if (dev==NULL) return(-ENODEV);
dbg(" - minor %d, count = %d", dev->minor, count);/* lock this object */ down (&dev->sem);
/* verify that the device wasn't unplugged */
if (dev->udev == NULL)
{ retval = -ENODEV;
goto exit;
}
/* verify that we actually have some data to write */
if (count == 0)
{
dbg("write request of 0 bytes");
goto exit;
} /* see if we are already in the middle of a write */
if (dev->write_urb->status == -EINPROGRESS)
{
dbg("already writing");
goto exit;
}/* we can only write as much as 1 urb will hold */ bytes_written = (count > dev->bulk_out_size) ? dev->bulk_out_size : count;
/* copy the data from userspace into our urb */
if (copy_from_user(dev->write_urb->transfer_buffer, buffer, bytes_written))
{
dbg("already writing"); retval = -EFAULT;
goto exit;
}
usb_bally_debug_data (__FUNCTION__, bytes_written, dev->write_urb->transfer_buffer);
/* set up our urb */
FILL_BULK_URB(dev->write_urb, dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
dev->write_urb->transfer_buffer, bytes_written,
bally_write_bulk_callback, dev); /* send the data out the bulk port */
retval = usb_submit_urb(dev->write_urb);
if (retval)
{
dbg("failed submitting write urb, error %d", retval);
}
else
{
retval = bytes_written;
}exit: /* unlock the device */ up (&dev->sem);
return retval; }
/** * bally_write */ static ssize_t bally_write_control (struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct usb_bally *dev; ssize_t bytes_written = 0; int retval = 0;
dev = (struct usb_bally *)file->private_data;
if (dev==NULL) return(-ENODEV);
dbg(" - minor %d, count = %d", dev->minor, count);/* lock this object */ down (&dev->sem);
/* verify that the device wasn't unplugged */
if (dev->udev == NULL)
{
retval = -ENODEV;
goto exit;
} /* verify that we actually have some data to write */
if (count == 0)
{
dbg(" - write request of 0 bytes");
goto exit;
} /* see if we are already in the middle of a write */
if (dev->write_urb->status == -EINPROGRESS)
{
dbg (" - already writing");
goto exit;
}/* we can only write as much as 1 urb will hold */
bytes_written = (count > dev->bulk_out_size) ? dev->bulk_out_size : count;
/* copy the data from userspace into our urb */
if (copy_from_user(dev->write_urb->transfer_buffer, buffer, bytes_written))
{
retval = -EFAULT;
goto exit;
}
usb_bally_debug_data (__FUNCTION__, bytes_written, dev->write_urb->transfer_buffer);
/* set up our urb */
FILL_BULK_URB(dev->write_urb, dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
dev->write_urb->transfer_buffer, bytes_written,
bally_write_bulk_callback, dev); /* send the data out the bulk port */
retval = usb_submit_urb(dev->write_urb);
if (retval)
{
err(__FUNCTION__ " - failed submitting write urb, error %d", retval);
}
else
{
retval = bytes_written;
}exit: /* unlock the device */ up (&dev->sem);
return retval; }
/**
* bally_ioctl
*/
static int bally_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct usb_bally *dev;
int result,x;
result= -ENOTTY;
dev = (struct usb_bally *)file->private_data;
/* lock this object */ down (&dev->sem);
/* verify that the device wasn't unplugged */
if (dev->udev == NULL)
{
up (&dev->sem);
return -ENODEV;
} dbg(" - minor %d, cmd 0x%.4x, arg %ld", dev->minor, cmd, arg);if (cmd==IOCTRL_GETID)
{
result=copy_to_user((void *)arg,&dev->idProduct,2);
}
else if (cmd==IOCTRL_GETREPORT)
{
result=copy_from_user(&dev->repinfo,(void *)arg,sizeof(usb_gadget_report));
if (!result)
{ result= usb_get_report(dev->udev,0,(dev->repinfo.value>>8)&0xFF,
dev->repinfo.value&0xFF,dev->repbuff,sizeof(dev->repbuff));
if (result>=0)
{
x=result;
if (x>dev->repinfo.length)
x=dev->repinfo.length;
x=copy_to_user(dev->repinfo.buffer,dev->repbuff,x);
if (x)
result=-EFAULT;
}
}
else
result=-EFAULT;
}
else if (cmd==IOCTRL_SETREPORT)
{
result=copy_from_user(&dev->repinfo,(void *)arg,sizeof(usb_gadget_report));
if (!result)
{
result=copy_from_user(dev->repbuff,dev->repinfo.buffer,dev->repinfo.length);
if (!result)
{
result=usb_set_report(dev->udev,0,(dev->repinfo.value>>8)&0xFF,
dev->repinfo.value&0xFF,dev->repbuff,dev->repinfo.length);
}
else
result=-EFAULT;
}
else
result=-EFAULT;
}
/* unlock the device */
up (&dev->sem);
/* return that we did not understand this ioctl call */
return result;
}
/**
* */
static unsigned int bally_poll(struct file *file, poll_table *wait)
{
struct usb_bally *dev=NULL;
int result;
dbg("enter");
dev= (struct usb_bally *)file->private_data;
// if the device was unplugged, return an error.
if (dev->udev == NULL)
{
dbg("device was unplugged");
return POLLERR;
}
// submit urb request, unless we know we don't need to to
// note, it doesn't really hurt to resubmit API docs that it simply return err
if (!dev->submitPending)
{
dbg("submitting");
FILL_BULK_URB(dev->read_urb, dev->udev,
usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
dev->bulk_in_buffer, dev->bulk_in_size,
bally_read_bulk_callback, dev);
result = usb_submit_urb(dev->read_urb);
dev->submitPending= (result==0) ? 1 : 0; // result is 0 if submit worked ok
}
poll_wait(file, &dev->wait, wait);
if (dev->ringbuffer.actSize > 0)
{
dbg("data ready");
return POLLIN | POLLRDNORM;
}
else
{
dbg("not ready");
}/* write not implemented */ return 0; }
/**
* bally_write_bulk_callback
*/
static void bally_write_bulk_callback (struct urb *urb)
{
/* struct usb_bally *dev = (struct usb_bally *)urb->context; */ dbg(" - usrb status %d", urb->status); if ((urb->status != -ENOENT) && (urb->status != -ECONNRESET))
{
dbg(" - nonzero write bulk status received: %d", urb->status);
}
}/**
* bally_read_bulk_callback
*/
static void bally_read_bulk_callback (struct urb *purb)
{
struct usb_bally *dev = (struct usb_bally *)purb->context;
int result = 0;// if interrupted, just return
if (purb->status == -ENOENT)
{
return; }
dbg("adding %d to ringbuf", purb->actual_length);// add the data to the ringbuffer down(&dev->mutex); ringbuf_add(&dev->ringbuffer, purb->transfer_buffer, purb->actual_length); up(&dev->mutex);
dbg("submitting urb");//resubmit another read request. we want to always read!
FILL_BULK_URB(dev->read_urb, dev->udev,
usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
dev->bulk_in_buffer, dev->bulk_in_size,
bally_read_bulk_callback, dev);
result = usb_submit_urb(dev->read_urb);
dev->submitPending= (result==0) ? 1 : 0; // result is 0 if submit worked ok
// notify any waiting bally_read() wake_up_interruptible(&dev->wait);
dbg("exit");
}/**
* bally_probe
*
* Called by the usb core when a new device is connected that it thinks
* this driver might be interested in.
*/
static void * bally_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id)
{
struct usb_bally *dev = NULL;
struct usb_interface *interface;
struct usb_interface_descriptor *iface_desc;
struct usb_endpoint_descriptor *endpoint;
int found;
int minor;
int buffer_size;
int i;
char *s;
char name[10];
dbg("probing\n");found=0;
dbg("%04X %04X %04X", udev->descriptor.idVendor,udev->descriptor.idProduct,
udev->descriptor.bcdDevice);
// see the algorithm in section 3.2 of "Evo Gadgets Game Enumeration"
if (udev->descriptor.idVendor==0x0914) // this is a bally device
{
dbg("bally gadget %04X %04X %04X",
udev->descriptor.idVendor,udev->descriptor.idProduct,
udev->descriptor.bcdDevice);if ((udev->descriptor.idProduct>=0x0011) && (udev->descriptor.idProduct<=0x4999)) {
if (udev->descriptor.bcdDevice==0x0010)
found=1;
}
}
if (!found)
return (NULL);
/* select a "subminor" number (part of a minor number) */
down (&minor_table_mutex);
for (minor = 0; minor < MAX_DEVICES; ++minor)
{
if (minor_table[minor] == NULL)
break;
}
if (minor >= MAX_DEVICES)
{
dbg("Too many devices plugged in, can not handle this device.");
goto exit;
} /* allocate memory for our device state and intialize it */
dev = kmalloc (sizeof(struct usb_bally), GFP_KERNEL);
if (dev == NULL)
{
err ("Out of memory");
goto exit;
}
memset (dev, 0x00, sizeof (*dev));
minor_table[minor] = dev;interface = &udev->actconfig->interface[ifnum];
init_MUTEX (&dev->sem); dev->udev = udev; dev->interface = interface; dev->minor = minor; dev->idProduct=udev->descriptor.idProduct;
init_waitqueue_head (&dev->wait);
init_MUTEX (&dev->mutex);
/* set up the endpoint information */
/* check out the endpoints */
iface_desc = &interface->altsetting[0];
for (i = 0; i < iface_desc->bNumEndpoints; ++i)
{
endpoint = &iface_desc->endpoint[i];
if ((endpoint->bEndpointAddress & 0x80) &&
((endpoint->bmAttributes & 3) == 0x03))
{
/* found a bulk in endpoint */
buffer_size = endpoint->wMaxPacketSize;
dev->bulk_in_size = buffer_size;
dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
dev->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
if (!dev->bulk_in_buffer)
{
err("Couldn't allocate bulk_in_buffer");
goto error;
}
dev->read_urb = usb_alloc_urb (0);
dev->userBuffer = (unsigned char *) kmalloc(BALLY_RINGBUFFER_SIZE, GFP_KERNEL);
ringbuf_init(&dev->ringbuffer, BALLY_RINGBUFFER_SIZE);
dev->submitPending = 0;
dev->userBytesPending = 0;
}
if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
((endpoint->bmAttributes & 3) == 0x03))
{
/* we found a bulk out endpoint */
dev->write_urb = usb_alloc_urb(0);
if (!dev->write_urb)
{
err("No free urbs available");
goto error;
}
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);
if (!dev->bulk_out_buffer)
{
err("Couldn't allocate bulk_out_buffer");
goto error;
}
FILL_BULK_URB(dev->write_urb, udev,
usb_sndbulkpipe(udev,
endpoint->bEndpointAddress),
dev->bulk_out_buffer, buffer_size,
bally_write_bulk_callback, dev);
}
}
/* initialize the devfs node for this device and register it */
sprintf(name, "USBBally%d", dev->minor);
dev->devfs = devfs_register (usb_devfs_handle, name,
DEVFS_FL_DEFAULT, USB_MAJOR,
USB_REEL_MINOR_BASE + dev->minor,
S_IFCHR | S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP | S_IROTH,
&bally_fops, NULL);
s="unknown";
switch(udev->descriptor.idProduct)
{
case 0x0019:
s="Reel Controller";break;
case 0x0011:
s="7 Segment Display Controller";break;
}
/* let the user know what node this device is now attached to */
dbg ("Bally USB Gadget device (%s) now attached to %s", s,name);
goto exit;
error:
bally_delete (dev);
dev = NULL;
exit: up (&minor_table_mutex); return dev; }
/** * bally_disconnect * * Called by the usb core when the device is removed from the system. */ static void bally_disconnect(struct usb_device *udev, void *ptr) { struct usb_bally *dev; int minor;
dev = (struct usb_bally *)ptr;
dbg("bally_disconnect enter");
down (&minor_table_mutex);
down (&dev->sem);
lock_kernel(); minor = dev->minor;
/* remove our devfs node */ devfs_unregister(dev->devfs);
// notify any waiting bally_read() wake_up_interruptible(&dev->wait);
/* if the device is not opened, then we clean up right now */
if (!dev->open_count)
{
up (&dev->sem);
bally_delete (dev);
}
else
{ if (dev->read_urb != NULL)
{
usb_unlink_urb (dev->read_urb);
} if (dev->write_urb != NULL)
{
usb_unlink_urb (dev->write_urb);
} dev->udev = NULL;
up (&dev->sem);
}
unlock_kernel();
dbg("Bally Gadget Driver #%d now disconnected", minor);
up (&minor_table_mutex);
}/**
* usb_bally_init
*/
static int __init usb_bally_init(void)
{
int result; dbg("reels register\n"); /* register this driver with the USB subsystem */
result = usb_register(&reel_driver);
if (result < 0)
{
err("usb_register failed for the "__FILE__" driver. Error %d", result);
return -1;
}dbg(DRIVER_DESC " " DRIVER_VERSION); return 0; }
/**
* usb_bally_exit
*/
static void __exit usb_bally_exit(void)
{
/* deregister this driver with the USB subsystem */
usb_deregister(&reel_driver);
}/**
* initializes the ring buffer to given buffer size.
*/
static void ringbuf_init(pbally_ringbuffer rb, int bufSize)
{
rb->buf = (unsigned char *) kmalloc(bufSize, GFP_KERNEL);
rb->head = 0;
rb->tail = 0;
rb->actSize = 0;
rb->bufSize = bufSize;
}/**
* frees the ringbuffer.
*/
static void ringbuf_destroy(pbally_ringbuffer rb)
{
if (rb->buf!=NULL)
kfree(rb->buf);
rb->buf = NULL;
rb->head = 0;
rb->tail = 0;
rb->actSize = 0;
rb->bufSize = 0;
}/**
* empties the ringbuffer
*/
static void ringbuf_clear(pbally_ringbuffer rb)
{
rb->head = 0;
rb->tail = 0;
rb->actSize = 0;
}/**
* inserts a copy of an array's contents to the ringbuffer.
*/
static void ringbuf_add(pbally_ringbuffer rb, unsigned char *src, int inSize)
{
int bytesAtEnd = rb->bufSize - rb->head;
// can't add more than bufSize. should never happen but not fatal either
if (inSize > rb->bufSize)
{
src = src + (inSize-rb->bufSize); // point to last bufSize bytes.
inSize = rb->bufSize;
} // copy to buffer, checking if it crosses over physical end of buffer
if (bytesAtEnd < inSize)
{
memcpy (rb->buf+rb->head, src, bytesAtEnd);
memcpy (rb->buf, src+bytesAtEnd, inSize-bytesAtEnd);
}
else
{
memcpy (rb->buf+rb->head, src, inSize);
}// increment head pos and actual bytes in buffer rb->head = (rb->head + inSize) % rb->bufSize; rb->actSize += inSize;
// if buffer is full, make sure actual size doesn't reflect larger than that!
// oldest is automatically overwritten by definition of a ringbuffer.
if (rb->actSize>rb->bufSize)
rb->actSize=rb->bufSize;
}
/**
* removes an array contents from the ringbuffer to a copy * only returns data if at least total request size exists.
*
* returns how many bytes were copied.
*/
static int ringbuf_remove(pbally_ringbuffer rb, unsigned char *dst, int outSize)
{
int bytesAtEnd = 0;
// can't copy more than ringbuffer size. if (outSize > rb->bufSize)
outSize = rb->bufSize;
// only return data if total request exists.
if (rb->actSize < outSize || rb->actSize==0)
{
return (0); // not ready
}// copy from, checking if request crosses over physical end of buffer
if (rb->head>rb->tail)
{
memcpy (dst, rb->buf+rb->tail, rb->head-rb->tail);
}
else
{
bytesAtEnd = rb->bufSize-rb->tail;
memcpy (dst, rb->buf+rb->tail, bytesAtEnd);
memcpy (dst+bytesAtEnd, rb->buf, outSize-bytesAtEnd);
}
// increment tail pos and actual bytes in buffer rb->tail = (rb->tail + outSize) % rb->bufSize; rb->actSize -= outSize;
return (outSize); }
module_init (usb_bally_init); module_exit (usb_bally_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");// ============================================================================
//
// Copyright (c) 1999-2004 Sierra Design Group
// $Author: jlawrence $
// $Source: /agprepository/agp/kernel_2_4_18/modules/usbbally/bld/usbbally.h,v $
// $Revision: 1.1.4.1 $
// $Date: 2004/06/02 22:50:19 $
//
// $Log: usbbally.h,v $
// Revision 1.1.4.1 2004/06/02 22:50:19 jlawrence
// ISSUE : Support for Bally Mechanical Reel and Projector gadgets.
// CHANGE : Created USB driver to communicate to USB device.
// REF TTD# : 5506
//
//
#ifndef __reel_H_ #define __reel_H_
#define DABUSB_MINOR 220 /* some unassigned USB minor */ #define MAX_GADGETS 32
#define IOCTRL_GETID 0x0985 #define IOCTRL_GETREPORT 0x0986 #define IOCTRL_SETREPORT 0x0987 #define IOCTRL_CTRLMSG 0x0988
typedef struct {
unsigned short value;
unsigned short length;
unsigned char *buffer;
} usb_gadget_report;#endif
------------------------------------------------------- This SF.Net email is sponsored by: YOU BE THE JUDGE. Be one of 170 Project Admins to receive an Apple iPod Mini FREE for your judgement on who ports your project to Linux PPC the best. Sponsored by IBM. Deadline: Sept. 24. Go here: http://sf.net/ppc_contest.php _______________________________________________ [EMAIL PROTECTED] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
