VERSION = 2
PATCHLEVEL = 5
SUBLEVEL = 2
-EXTRAVERSION =-pre5
+EXTRAVERSION =-pre6
KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
static kmem_cache_t *usb_desc_cache;
static struct usb_bus *etrax_usb_bus;
-static void dump_urb (purb_t purb);
+static void dump_urb (struct urb *urb);
static void init_rx_buffers(void);
static int etrax_rh_unlink_urb (urb_t *urb);
static void etrax_rh_send_irq(urb_t *urb);
};
#ifdef USB_DEBUG_DESC
-static void dump_urb(purb_t purb)
+static void dump_urb(struct urb *urb)
{
- printk("\nurb :0x%08X\n", purb);
- printk("next :0x%08X\n", purb->next);
- printk("dev :0x%08X\n", purb->dev);
- printk("pipe :0x%08X\n", purb->pipe);
- printk("status :%d\n", purb->status);
- printk("transfer_flags :0x%08X\n", purb->transfer_flags);
- printk("transfer_buffer :0x%08X\n", purb->transfer_buffer);
- printk("transfer_buffer_length:%d\n", purb->transfer_buffer_length);
- printk("actual_length :%d\n", purb->actual_length);
- printk("setup_packet :0x%08X\n", purb->setup_packet);
- printk("start_frame :%d\n", purb->start_frame);
- printk("number_of_packets :%d\n", purb->number_of_packets);
- printk("interval :%d\n", purb->interval);
- printk("error_count :%d\n", purb->error_count);
- printk("context :0x%08X\n", purb->context);
- printk("complete :0x%08X\n\n", purb->complete);
+ printk("\nurb :0x%08X\n", urb);
+ printk("next :0x%08X\n", urb->next);
+ printk("dev :0x%08X\n", urb->dev);
+ printk("pipe :0x%08X\n", urb->pipe);
+ printk("status :%d\n", urb->status);
+ printk("transfer_flags :0x%08X\n", urb->transfer_flags);
+ printk("transfer_buffer :0x%08X\n", urb->transfer_buffer);
+ printk("transfer_buffer_length:%d\n", urb->transfer_buffer_length);
+ printk("actual_length :%d\n", urb->actual_length);
+ printk("setup_packet :0x%08X\n", urb->setup_packet);
+ printk("start_frame :%d\n", urb->start_frame);
+ printk("number_of_packets :%d\n", urb->number_of_packets);
+ printk("interval :%d\n", urb->interval);
+ printk("error_count :%d\n", urb->error_count);
+ printk("context :0x%08X\n", urb->context);
+ printk("complete :0x%08X\n\n", urb->complete);
}
static void dump_in_desc(USB_IN_Desc_t *in)
# CONFIG_AC3200 is not set
# CONFIG_APRICOT is not set
# CONFIG_CS89x0 is not set
+# CONFIG_DE2104X is not set
# CONFIG_TULIP is not set
# CONFIG_DE4X5 is not set
# CONFIG_DGRS is not set
# CONFIG_JFFS2_FS is not set
# CONFIG_CRAMFS is not set
CONFIG_TMPFS=y
-# CONFIG_RAMFS is not set
+CONFIG_RAMFS=y
CONFIG_ISO9660_FS=y
# CONFIG_JOLIET is not set
# CONFIG_ZISOFS is not set
major: MAJOR_NR,
major_name: "mfm",
minor_shift: 6,
- max_p: 1 << 6,
part: mfm,
sizes: mfm_sizes,
- real_devices: (void *)mfm_info,
};
static struct block_device_operations mfm_fops =
mfm_info[target].busy = 1;
restore_flags (flags);
- maxp = mfm_gendisk.max_p;
+ maxp = 1 << mfm_gendisk.minor_shift;
start = target << mfm_gendisk.minor_shift;
for (i = maxp - 1; i >= 0; i--) {
Controller->GenericDiskInfo.major = MajorNumber;
Controller->GenericDiskInfo.major_name = "rd";
Controller->GenericDiskInfo.minor_shift = DAC960_MaxPartitionsBits;
- Controller->GenericDiskInfo.max_p = DAC960_MaxPartitions;
Controller->GenericDiskInfo.nr_real = DAC960_MaxLogicalDrives;
- Controller->GenericDiskInfo.real_devices = Controller;
Controller->GenericDiskInfo.next = NULL;
Controller->GenericDiskInfo.fops = &DAC960_BlockDeviceOperations;
/*
Information Partition Sector Counts and Block Sizes.
*/
-static void DAC960_ComputeGenericDiskInfo(GenericDiskInfo_T *GenericDiskInfo)
+static void DAC960_ComputeGenericDiskInfo(DAC960_Controller_T *Controller)
{
- DAC960_Controller_T *Controller =
- (DAC960_Controller_T *) GenericDiskInfo->real_devices;
+ GenericDiskInfo_T *GenericDiskInfo = &Controller->GenericDiskInfo;
int LogicalDriveNumber, i;
for (LogicalDriveNumber = 0;
LogicalDriveNumber < DAC960_MaxLogicalDrives;
int LogicalDriveNumber;
if (Controller == NULL) continue;
DAC960_InitializeController(Controller);
- DAC960_ComputeGenericDiskInfo(&Controller->GenericDiskInfo);
+ DAC960_ComputeGenericDiskInfo(Controller);
for (LogicalDriveNumber = 0;
LogicalDriveNumber < DAC960_MaxLogicalDrives;
LogicalDriveNumber++)
Controller->ControllerNumber,
LogicalDriveNumber);
Controller->LogicalDriveCount = NewEnquiry->NumberOfLogicalDrives;
- DAC960_ComputeGenericDiskInfo(&Controller->GenericDiskInfo);
+ DAC960_ComputeGenericDiskInfo(Controller);
}
if (NewEnquiry->NumberOfLogicalDrives < Controller->LogicalDriveCount)
{
Controller->ControllerNumber,
LogicalDriveNumber);
Controller->LogicalDriveCount = NewEnquiry->NumberOfLogicalDrives;
- DAC960_ComputeGenericDiskInfo(&Controller->GenericDiskInfo);
+ DAC960_ComputeGenericDiskInfo(Controller);
}
if (NewEnquiry->StatusFlags.DeferredWriteError !=
OldEnquiry->StatusFlags.DeferredWriteError)
{
memset(LogicalDeviceInfo, 0,
sizeof(DAC960_V2_LogicalDeviceInfo_T));
- DAC960_ComputeGenericDiskInfo(&Controller->GenericDiskInfo);
+ DAC960_ComputeGenericDiskInfo(Controller);
}
}
if (LogicalDeviceInfo != NULL)
kfree(LogicalDeviceInfo);
Controller->LogicalDriveInitiallyAccessible
[LogicalDriveNumber] = false;
- DAC960_ComputeGenericDiskInfo(&Controller->GenericDiskInfo);
+ DAC960_ComputeGenericDiskInfo(Controller);
}
Controller->V2.NeedLogicalDeviceInformation = false;
}
if (!Controller->LogicalDriveInitiallyAccessible[LogicalDriveNumber])
{
Controller->LogicalDriveInitiallyAccessible[LogicalDriveNumber] = true;
- DAC960_ComputeGenericDiskInfo(&Controller->GenericDiskInfo);
+ DAC960_ComputeGenericDiskInfo(Controller);
DAC960_RegisterDisk(Controller, LogicalDriveNumber);
}
if (Controller->GenericDiskInfo.sizes[MINOR(Inode->i_rdev)] == 0)
major: MAJOR_NR,
major_name: "ad",
minor_shift: 4,
- max_p: 1 << 4,
part: acsi_part,
sizes: acsi_sizes,
- real_devices: (void *)acsi_info,
fops: &acsi_fops,
};
return -ENXIO;
/* existing drive? */
- drive = (MINOR(dev) >> g->minor_shift);
+ drive = (minor(dev) >> g->minor_shift);
first_minor = (drive << g->minor_shift);
- end_minor = first_minor + g->max_p;
+ end_minor = first_minor + (1 << g->minor_shift);
if (drive >= g->nr_real)
return -ENXIO;
/* drive and partition number OK? */
- if (first_minor != MINOR(dev) || p->pno <= 0 || p->pno >= g->max_p)
+ if (first_minor != minor(dev))
+ return -EINVAL;
+ if (p->pno <= 0 || p->pno >= (1 << g->minor_shift))
return -EINVAL;
/* partition number in use? */
return -ENXIO;
/* drive and partition number OK? */
- drive = (MINOR(dev) >> g->minor_shift);
+ drive = (minor(dev) >> g->minor_shift);
first_minor = (drive << g->minor_shift);
- if (first_minor != MINOR(dev) || p->pno <= 0 || p->pno >= g->max_p)
+
+ if (first_minor != minor(dev))
return -EINVAL;
+ if (p->pno <= 0 || p->pno >= (1 << g->minor_shift))
+ return -EINVAL;
/* existing drive and partition? */
minor = first_minor + p->pno;
return -ENXIO;
/* partition in use? Incomplete check for now. */
- devp = MKDEV(MAJOR(dev), minor);
+ devp = mk_kdev(major(dev), minor);
if (is_mounted(devp) || is_swap_partition(devp))
return -EBUSY;
int intval, *iptr;
unsigned short usval;
- if (!dev)
+ if (kdev_none(dev))
return -EINVAL;
intval = block_ioctl(dev, cmd, arg);
return -EACCES;
if(arg > 0xff)
return -EINVAL;
- read_ahead[MAJOR(dev)] = arg;
+ read_ahead[major(dev)] = arg;
return 0;
case BLKRAGET:
if (!arg)
return -EINVAL;
- return put_user(read_ahead[MAJOR(dev)], (long *) arg);
+ return put_user(read_ahead[major(dev)], (long *) arg);
case BLKFRASET:
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- if (!(iptr = max_readahead[MAJOR(dev)]))
+ if (!(iptr = max_readahead[major(dev)]))
return -EINVAL;
- iptr[MINOR(dev)] = arg;
+ iptr[minor(dev)] = arg;
return 0;
case BLKFRAGET:
- if (!(iptr = max_readahead[MAJOR(dev)]))
+ if (!(iptr = max_readahead[major(dev)]))
return -EINVAL;
- return put_user(iptr[MINOR(dev)], (long *) arg);
+ return put_user(iptr[minor(dev)], (long *) arg);
case BLKSECTGET:
if ((q = blk_get_queue(dev)) == NULL)
case BLKGETSIZE64:
g = get_gendisk(dev);
if (g)
- ullval = g->part[MINOR(dev)].nr_sects;
+ ullval = g->part[minor(dev)].nr_sects;
if (cmd == BLKGETSIZE)
return put_user((unsigned long)ullval, (unsigned long *)arg);
hba[i]->gendisk.major = MAJOR_NR + i;
hba[i]->gendisk.major_name = "cciss";
hba[i]->gendisk.minor_shift = NWD_SHIFT;
- hba[i]->gendisk.max_p = MAX_PART;
hba[i]->gendisk.part = hba[i]->hd;
hba[i]->gendisk.sizes = hba[i]->sizes;
hba[i]->gendisk.nr_real = hba[i]->num_luns;
#define NWD 16
#define NWD_SHIFT 4
-#define MAX_PART 16
+#define MAX_PART (1 << NWD_SHIFT)
#define IO_OK 0
#define IO_ERROR 1
ida_gendisk[i].major = MAJOR_NR + i;
ida_gendisk[i].major_name = "ida";
ida_gendisk[i].minor_shift = NWD_SHIFT;
- ida_gendisk[i].max_p = 16;
ida_gendisk[i].part = ida + (i*256);
ida_gendisk[i].sizes = ida_sizes + (i*256);
ida_gendisk[i].nr_real = 0;
* if the device is different (not a normal case) just check if
* bio is after rq
*/
- if (next_rq->rq_dev != rq->rq_dev)
+ if (!kdev_same(next_rq->rq_dev, rq->rq_dev))
return bio->bi_sector > rq->sector;
/*
/*
* same device and no special stuff set, merge is ok
*/
- if (rq->rq_dev == bio->bi_dev && !rq->waiting && !rq->special)
+ if (kdev_same(rq->rq_dev, bio->bi_dev) && !rq->waiting && !rq->special)
return 1;
return 0;
static int initialising=1;
static inline int TYPE(kdev_t x) {
- return (MINOR(x)>>2) & 0x1f;
+ return (minor(x)>>2) & 0x1f;
}
static inline int DRIVE(kdev_t x) {
- return (MINOR(x)&0x03) | ((MINOR(x)&0x80) >> 5);
+ return (minor(x)&0x03) | ((minor(x)&0x80) >> 5);
}
#define ITYPE(x) (((x)>>2) & 0x1f)
#define TOMINOR(x) ((x & 3) | ((x & 4) << 5))
unlock_fdc();
return;
}
- if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
+ if (major(CURRENT->rq_dev) != MAJOR_NR)
panic(DEVICE_NAME ": request list destroyed");
device = CURRENT->rq_dev;
if (ITYPE(drive_state[cnt].fd_device) == type &&
drive_state[cnt].fd_ref)
check_disk_change(
- MKDEV(FLOPPY_MAJOR,
+ mk_kdev(FLOPPY_MAJOR,
drive_state[cnt].fd_device));
}
} else {
if (TYPE(inode->i_rdev) >= NUMBER(floppy_type))
return -ENXIO;
old_dev = UDRS->fd_device;
- if (UDRS->fd_ref && old_dev != MINOR(inode->i_rdev))
+ if (UDRS->fd_ref && old_dev != minor(inode->i_rdev))
return -EBUSY;
if (!UDRS->fd_ref && (UDP->flags & FD_BROKEN_DCL)){
}
}
- UDRS->fd_device = MINOR(inode->i_rdev);
- if (old_dev != -1 && old_dev != MINOR(inode->i_rdev)) {
+ UDRS->fd_device = minor(inode->i_rdev);
+ if (old_dev != -1 && old_dev != minor(inode->i_rdev)) {
if (buffer_drive == drive)
buffer_track = -1;
- invalidate_buffers(MKDEV(FLOPPY_MAJOR,old_dev));
+ invalidate_buffers(mk_kdev(FLOPPY_MAJOR,old_dev));
}
/* Allow ioctls if we have write-permissions even if read-only open.
{
int drive = DRIVE(dev);
- if (MAJOR(dev) != MAJOR_NR) {
+ if (major(dev) != MAJOR_NR) {
DPRINT("check_floppy_change: not a floppy\n");
return 0;
}
UDRS->generation++;
if (NO_GEOM){
/* auto-sensing */
- int size = floppy_blocksizes[MINOR(dev)];
+ int size = floppy_blocksizes[minor(dev)];
if (!size)
size = 1024;
if (!(bh = getblk(dev,0,size))){
if (fdc_state[FDC(drive)].version == FDC_NONE)
continue;
for (i = 0; i<NUMBER(floppy_type); i++)
- register_disk(NULL, MKDEV(MAJOR_NR,TOMINOR(drive)+i*4),
+ register_disk(NULL, mk_kdev(MAJOR_NR,TOMINOR(drive)+i*4),
1, &floppy_fops, 0);
}
return have_no_fdc;
get_gendisk(kdev_t dev)
{
struct gendisk *gp = NULL;
- int maj = MAJOR(dev);
+ int maj = major(dev);
read_lock(&gendisk_lock);
for (gp = gendisk_head; gp; gp = gp->next)
gp = get_gendisk(dev);
if (gp)
- return gp->part[MINOR(dev)].start_sect;
+ return gp->part[minor(dev)].start_sect;
return 0;
}
gp = get_gendisk(dev);
if (gp)
- return gp->part[MINOR(dev)].nr_sects;
+ return gp->part[minor(dev)].nr_sects;
return 0;
}
**/
inline request_queue_t *blk_get_queue(kdev_t dev)
{
- struct blk_dev_struct *bdev = blk_dev + MAJOR(dev);
+ struct blk_dev_struct *bdev = blk_dev + major(dev);
if (bdev->queue)
return bdev->queue(dev);
else
- return &blk_dev[MAJOR(dev)].request_queue;
+ return &blk_dev[major(dev)].request_queue;
}
void blk_queue_prep_rq(request_queue_t *q, prep_rq_fn *pfn)
{
int bit;
- printk("%s: dev %x: ", msg, rq->rq_dev);
+ printk("%s: dev %02x:%02x: ", msg, major(rq->rq_dev), minor(rq->rq_dev));
bit = 0;
do {
if (rq->flags & (1 << bit))
{
int minor,major;
- major = MAJOR(dev);
- minor = MINOR(dev);
+ major = major(dev);
+ minor = minor(dev);
if (major < 0 || major >= MAX_BLKDEV) return 0;
return ro_bits[major][minor >> 5] & (1 << (minor & 31));
}
{
int minor,major;
- major = MAJOR(dev);
- minor = MINOR(dev);
+ major = major(dev);
+ minor = minor(dev);
if (major < 0 || major >= MAX_BLKDEV) return;
if (flag) ro_bits[major][minor >> 5] |= 1 << (minor & 31);
else ro_bits[major][minor >> 5] &= ~(1 << (minor & 31));
void drive_stat_acct(struct request *rq, int nr_sectors, int new_io)
{
- unsigned int major = MAJOR(rq->rq_dev);
+ unsigned int major = major(rq->rq_dev);
int rw = rq_data_dir(rq);
unsigned int index;
return;
if (rq_data_dir(req) != rq_data_dir(next)
- || req->rq_dev != next->rq_dev
+ || !kdev_same(req->rq_dev, next->rq_dev)
|| req->nr_sectors + next->nr_sectors > q->max_sectors
|| next->waiting || next->special)
return;
struct gendisk *g;
kdev_t dev0;
- major = MAJOR(bio->bi_dev);
+ major = major(bio->bi_dev);
if ((g = get_gendisk(bio->bi_dev))) {
- minor = MINOR(bio->bi_dev);
+ minor = minor(bio->bi_dev);
drive = (minor >> g->minor_shift);
minor0 = (drive << g->minor_shift); /* whole disk device */
/* that is, minor0 = (minor & ~((1<<g->minor_shift)-1)); */
- dev0 = MKDEV(major, minor0);
- if (dev0 != bio->bi_dev) {
+ dev0 = mk_kdev(major, minor0);
+ if (!kdev_same(dev0, bio->bi_dev)) {
bio->bi_dev = dev0;
bio->bi_sector += g->part[minor].start_sect;
}
* */
void generic_make_request(struct bio *bio)
{
- int major = MAJOR(bio->bi_dev);
- int minor = MINOR(bio->bi_dev);
+ int major = major(bio->bi_dev);
+ int minor = minor(bio->bi_dev);
request_queue_t *q;
sector_t minorsize = 0;
int ret, nr_sectors = bio_sectors(bio);
if (!nr)
return;
- major = MAJOR(bhs[0]->b_dev);
+ major = major(bhs[0]->b_dev);
/* Determine correct block size for this device. */
correct_size = get_hardsect_size(bhs[0]->b_dev);
major: PD_MAJOR,
major_name: PD_NAME,
minor_shift: PD_BITS,
- max_p: PD_PARTNS,
part: pd_hd,
sizes: pd_sizes,
fops: &pd_fops,
major: MAJOR_NR,
major_name: "ed",
minor_shift: 6,
- max_p: 1 << 6,
part: ps2esdi,
sizes: ps2esdi_sizes,
- real_devices: (void *)ps2esdi_info,
fops: &ps2esdi_fops,
};
major: MAJOR_NR,
major_name: "xd",
minor_shift: 6,
- max_p: 1 << 6,
part: xd_struct,
sizes: xd_sizes,
- real_devices: (void *)xd_info,
fops: &xd_fops,
};
int register_cdrom(struct cdrom_device_info *cdi)
{
static char banner_printed;
- int major = MAJOR(cdi->dev);
+ int major = major(cdi->dev);
struct cdrom_device_ops *cdo = cdi->ops;
int *change_capability = (int *)&cdo->capability; /* hack */
int unregister_cdrom(struct cdrom_device_info *unreg)
{
struct cdrom_device_info *cdi, *prev;
- int major = MAJOR(unreg->dev);
+ int major = major(unreg->dev);
cdinfo(CD_OPEN, "entering unregister_cdrom\n");
prev = NULL;
cdi = topCdromPtr;
- while (cdi != NULL && cdi->dev != unreg->dev) {
+ while (cdi != NULL && !kdev_same(cdi->dev, unreg->dev)) {
prev = cdi;
cdi = cdi->next;
}
struct cdrom_device_info *cdi;
cdi = topCdromPtr;
- while (cdi != NULL && cdi->dev != dev)
+ while (cdi != NULL && !kdev_same(cdi->dev, dev))
cdi = cdi->next;
return cdi;
static int agp_open(struct inode *inode, struct file *file)
{
- int minor = MINOR(inode->i_rdev);
+ int minor = minor(inode->i_rdev);
agp_file_private *priv;
agp_client *client;
int rc = -ENXIO;
static kdev_t vt_console_device(struct console *c)
{
- return MKDEV(TTY_MAJOR, c->index ? c->index : fg_console + 1);
+ return mk_kdev(TTY_MAJOR, c->index ? c->index : fg_console + 1);
}
struct console vt_console_driver = {
int console_num;
if (!tty)
return;
- console_num = MINOR(tty->device) - (tty->driver.minor_start);
+ console_num = minor(tty->device) - (tty->driver.minor_start);
if (!vc_cons_allocated(console_num))
return;
set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
int console_num;
if (!tty)
return;
- console_num = MINOR(tty->device) - (tty->driver.minor_start);
+ console_num = minor(tty->device) - (tty->driver.minor_start);
if (!vc_cons_allocated(console_num))
return;
clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
unsigned int currcons;
int i;
- currcons = MINOR(tty->device) - tty->driver.minor_start;
+ currcons = minor(tty->device) - tty->driver.minor_start;
i = vc_allocate(currcons);
if (i)
if (!tty)
return;
if (tty->count != 1) return;
- vcs_make_devfs (MINOR (tty->device) - tty->driver.minor_start, 1);
+ vcs_make_devfs (minor(tty->device) - tty->driver.minor_start, 1);
tty->driver_data = 0;
}
int i;
for (i = 0; i < DRM(numdevs); i++) {
- if (MINOR(inode->i_rdev) == DRM(minor)[i]) {
+ if (minor(inode->i_rdev) == DRM(minor)[i]) {
dev = &(DRM(device)[i]);
break;
}
int DRM(open_helper)(struct inode *inode, struct file *filp, drm_device_t *dev)
{
- kdev_t minor = MINOR(inode->i_rdev);
+ int minor = minor(inode->i_rdev);
drm_file_t *priv;
if (filp->f_flags & O_EXCL) return -EBUSY; /* No exclusive opens */
static int DRM(stub_open)(struct inode *inode, struct file *filp)
{
- int minor = MINOR(inode->i_rdev);
+ int minor = minor(inode->i_rdev);
int err = -ENODEV;
struct file_operations *old_fops;
static int memory_open(struct inode * inode, struct file * filp)
{
- switch (MINOR(inode->i_rdev)) {
+ switch (minor(inode->i_rdev)) {
case 1:
filp->f_op = &mem_fops;
break;
static int misc_open(struct inode * inode, struct file * file)
{
- int minor = MINOR(inode->i_rdev);
+ int minor = minor(inode->i_rdev);
struct miscdevice *c;
int err = -ENODEV;
struct file_operations *old_fops, *new_fops = NULL;
#include <asm/system.h>
#include <asm/bitops.h>
-#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
-#define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1)
+#define IS_CONSOLE_DEV(dev) (kdev_val(dev) == __mkdev(TTY_MAJOR,0))
+#define IS_SYSCONS_DEV(dev) (kdev_val(dev) == __mkdev(TTYAUX_MAJOR,1))
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
/* NOTE: not yet done after every sleep pending a thorough
check of the logic of this change. -- jlc */
/* don't stop on /dev/console */
- if (file->f_dentry->d_inode->i_rdev != CONSOLE_DEV &&
- file->f_dentry->d_inode->i_rdev != SYSCONS_DEV &&
+ if (!IS_CONSOLE_DEV(file->f_dentry->d_inode->i_rdev) &&
+ !IS_SYSCONS_DEV(file->f_dentry->d_inode->i_rdev) &&
current->tty == tty) {
if (tty->pgrp <= 0)
printk("read_chan: tty->pgrp <= 0!\n");
/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
if (L_TOSTOP(tty) &&
- file->f_dentry->d_inode->i_rdev != CONSOLE_DEV &&
- file->f_dentry->d_inode->i_rdev != SYSCONS_DEV) {
+ !IS_CONSOLE_DEV(file->f_dentry->d_inode->i_rdev) &&
+ !IS_SYSCONS_DEV(file->f_dentry->d_inode->i_rdev)) {
retval = tty_check_change(tty);
if (retval)
return retval;
set_bit(TTY_OTHER_CLOSED, &tty->flags);
#ifdef CONFIG_UNIX98_PTYS
{
- unsigned int major = MAJOR(tty->device) - UNIX98_PTY_MASTER_MAJOR;
+ unsigned int major = major(tty->device) - UNIX98_PTY_MASTER_MAJOR;
if ( major < UNIX98_NR_MAJORS ) {
- devpts_pty_kill( MINOR(tty->device)
+ devpts_pty_kill( minor(tty->device)
- tty->driver.minor_start + tty->driver.name_base );
}
}
#endif
- tty_unregister_devfs (&tty->link->driver, MINOR (tty->device));
+ tty_unregister_devfs (&tty->link->driver, minor(tty->device));
tty_vhangup(tty->link);
}
}
#ifdef CONFIG_UNIX98_PTYS
static int pty_get_device_number(struct tty_struct *tty, unsigned int *value)
{
- unsigned int result = MINOR(tty->device)
+ unsigned int result = minor(tty->device)
- tty->driver.minor_start + tty->driver.name_base;
return put_user(result, value);
}
retval = -ENODEV;
if (!tty || !tty->link)
goto out;
- line = MINOR(tty->device) - tty->driver.minor_start;
+ line = minor(tty->device) - tty->driver.minor_start;
if ((line < 0) || (line >= NR_PTYS))
goto out;
pty = (struct pty_struct *)(tty->driver.driver_state) + line;
tty_register_devfs(&tty->link->driver,
DEVFS_FL_CURRENT_OWNER | DEVFS_FL_WAIT,
tty->link->driver.minor_start +
- MINOR(tty->device)-tty->driver.minor_start);
+ minor(tty->device)-tty->driver.minor_start);
retval = 0;
out:
return retval;
int sector_size;
int sector_bits;
- minor = MINOR(inode->i_rdev);
+ minor = minor(inode->i_rdev);
/*
* Is it the control device?
int minor;
struct block_device *bdev;
- minor = MINOR(inode->i_rdev);
+ minor = minor(inode->i_rdev);
down(&raw_devices[minor].mutex);
bdev = raw_devices[minor].binding;
raw_devices[minor].inuse--;
* major/minor numbers make sense.
*/
- if ((rq.block_major == NODEV &&
- rq.block_minor != NODEV) ||
+ if ((rq.block_major == 0 &&
+ rq.block_minor != 0) ||
rq.block_major > MAX_BLKDEV ||
rq.block_minor > MINORMASK) {
err = -EINVAL;
if (raw_devices[minor].binding)
bdput(raw_devices[minor].binding);
raw_devices[minor].binding =
- bdget(kdev_t_to_nr(MKDEV(rq.block_major, rq.block_minor)));
+ bdget(kdev_t_to_nr(mk_kdev(rq.block_major, rq.block_minor)));
up(&raw_devices[minor].mutex);
} else {
struct block_device *bdev;
bdev = raw_devices[minor].binding;
if (bdev) {
dev = to_kdev_t(bdev->bd_dev);
- rq.block_major = MAJOR(dev);
- rq.block_minor = MINOR(dev);
+ rq.block_major = major(dev);
+ rq.block_minor = minor(dev);
} else {
rq.block_major = rq.block_minor = 0;
}
* First, a few checks on device size limits
*/
- minor = MINOR(filp->f_dentry->d_inode->i_rdev);
+ minor = minor(filp->f_dentry->d_inode->i_rdev);
new_iobuf = 0;
iobuf = filp->f_iobuf;
sector_bits = raw_devices[minor].sector_bits;
sector_mask = sector_size- 1;
- if (blk_size[MAJOR(dev)])
- limit = (((loff_t) blk_size[MAJOR(dev)][MINOR(dev)]) << BLOCK_SIZE_BITS) >> sector_bits;
+ if (blk_size[major(dev)])
+ limit = (((loff_t) blk_size[major(dev)][minor(dev)]) << BLOCK_SIZE_BITS) >> sector_bits;
else
limit = INT_MAX;
dprintk ("rw_raw_dev: dev %d:%d (+%d)\n",
- MAJOR(dev), MINOR(dev), limit);
+ major(dev), minor(dev), limit);
err = -EINVAL;
if ((*offp & sector_mask) || (size & sector_mask))
unsigned long page;
MOD_INC_USE_COUNT;
- line = MINOR(tty->device) - tty->driver.minor_start;
+ line = minor(tty->device) - tty->driver.minor_start;
if ((line < 0) || (line >= NR_PORTS)) {
MOD_DEC_USE_COUNT;
return -ENODEV;
#include <linux/kmod.h>
-#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
-#define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
-#define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1)
-#define PTMX_DEV MKDEV(TTYAUX_MAJOR,2)
+#define IS_CONSOLE_DEV(dev) (kdev_val(dev) == __mkdev(TTY_MAJOR,0))
+#define IS_TTY_DEV(dev) (kdev_val(dev) == __mkdev(TTYAUX_MAJOR,0))
+#define IS_SYSCONS_DEV(dev) (kdev_val(dev) == __mkdev(TTYAUX_MAJOR,1))
+#define IS_PTMX_DEV(dev) (kdev_val(dev) == __mkdev(TTYAUX_MAJOR,2))
#undef TTY_DEBUG_HANGUP
static char *
_tty_make_name(struct tty_struct *tty, const char *name, char *buf)
{
- int idx = (tty)?MINOR(tty->device) - tty->driver.minor_start:0;
+ int idx = (tty)? minor(tty->device) - tty->driver.minor_start:0;
if (!tty) /* Hmm. NULL pointer. That's fun. */
strcpy(buf, "NULL tty");
return buf;
}
-#define TTY_NUMBER(tty) (MINOR((tty)->device) - (tty)->driver.minor_start + \
+#define TTY_NUMBER(tty) (minor((tty)->device) - (tty)->driver.minor_start + \
(tty)->driver.name_base)
char *tty_name(struct tty_struct *tty, char *buf)
int major, minor;
struct tty_driver *p;
- minor = MINOR(device);
- major = MAJOR(device);
+ minor = minor(device);
+ major = major(device);
for (p = tty_drivers; p; p = p->next) {
if (p->major != major)
file_list_lock();
for (l = tty->tty_files.next; l != &tty->tty_files; l = l->next) {
struct file * filp = list_entry(l, struct file, f_list);
- if (filp->f_dentry->d_inode->i_rdev == CONSOLE_DEV ||
- filp->f_dentry->d_inode->i_rdev == SYSCONS_DEV) {
+ if (IS_CONSOLE_DEV(filp->f_dentry->d_inode->i_rdev) ||
+ IS_SYSCONS_DEV(filp->f_dentry->d_inode->i_rdev)) {
cons_filp = filp;
continue;
}
moved it to there. This should only be done for the N_TTY
line discipline, anyway. Same goes for write_chan(). -- jlc. */
#if 0
- if ((inode->i_rdev != CONSOLE_DEV) && /* don't stop on /dev/console */
+ if (!IS_CONSOLE_DEV(inode->i_rdev) && /* don't stop on /dev/console */
(tty->pgrp > 0) &&
(current->tty == tty) &&
(tty->pgrp != current->pgrp))
* well as /dev/tty0.
*/
inode = file->f_dentry->d_inode;
- is_console = (inode->i_rdev == SYSCONS_DEV ||
- inode->i_rdev == CONSOLE_DEV);
+ is_console = IS_SYSCONS_DEV(inode->i_rdev) ||
+ IS_CONSOLE_DEV(inode->i_rdev);
if (is_console && redirect)
tty = redirect;
if (!driver)
return -ENODEV;
- idx = MINOR(device) - driver->minor_start;
+ idx = minor(device) - driver->minor_start;
/*
* Check whether we need to acquire the tty semaphore to avoid
if (!o_tty)
goto free_mem_out;
initialize_tty_struct(o_tty);
- o_tty->device = (kdev_t) MKDEV(driver->other->major,
+ o_tty->device = mk_kdev(driver->other->major,
driver->other->minor_start + idx);
o_tty->driver = *driver->other;
tty_fasync(-1, filp, 0);
- idx = MINOR(tty->device) - tty->driver.minor_start;
+ idx = minor(tty->device) - tty->driver.minor_start;
pty_master = (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
tty->driver.subtype == PTY_TYPE_MASTER);
o_tty = tty->link;
retry_open:
noctty = filp->f_flags & O_NOCTTY;
device = inode->i_rdev;
- if (device == TTY_DEV) {
+ if (IS_TTY_DEV(device)) {
if (!current->tty)
return -ENXIO;
device = current->tty->device;
/* noctty = 1; */
}
#ifdef CONFIG_VT
- if (device == CONSOLE_DEV) {
+ if (IS_CONSOLE_DEV(device)) {
extern int fg_console;
- device = MKDEV(TTY_MAJOR, fg_console + 1);
+ device = mk_kdev(TTY_MAJOR, fg_console + 1);
noctty = 1;
}
#endif
- if (device == SYSCONS_DEV) {
+ if (IS_SYSCONS_DEV(device)) {
struct console *c = console_drivers;
while(c && !c->device)
c = c->next;
noctty = 1;
}
- if (device == PTMX_DEV) {
+ if (IS_PTMX_DEV(device)) {
#ifdef CONFIG_UNIX98_PTYS
/* find a free pty. */
for (minor = driver->minor_start ;
minor < driver->minor_start + driver->num ;
minor++) {
- device = MKDEV(driver->major, minor);
+ device = mk_kdev(driver->major, minor);
if (!init_dev(device, &tty)) goto ptmx_found; /* ok! */
}
}
ptmx_found:
set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
minor -= driver->minor_start;
- devpts_pty_new(driver->other->name_base + minor, MKDEV(driver->other->major, minor + driver->other->minor_start));
+ devpts_pty_new(driver->other->name_base + minor, mk_kdev(driver->other->major, minor + driver->other->minor_start));
tty_register_devfs(&pts_driver[major], DEVFS_FL_DEFAULT,
pts_driver[major].minor_start + minor);
noctty = 1;
static int tioccons(struct inode *inode,
struct tty_struct *tty, struct tty_struct *real_tty)
{
- if (inode->i_rdev == SYSCONS_DEV ||
- inode->i_rdev == CONSOLE_DEV) {
+ if (IS_SYSCONS_DEV(inode->i_rdev) ||
+ IS_CONSOLE_DEV(inode->i_rdev)) {
if (!suser())
return -EPERM;
redirect = NULL;
{
#ifdef CONFIG_DEVFS_FS
umode_t mode = S_IFCHR | S_IRUSR | S_IWUSR;
- kdev_t device = MKDEV (driver->major, minor);
+ kdev_t device = mk_kdev(driver->major, minor);
int idx = minor - driver->minor_start;
char buf[32];
dev_ptmx_driver = dev_tty_driver;
dev_ptmx_driver.driver_name = "/dev/ptmx";
dev_ptmx_driver.name = dev_ptmx_driver.driver_name + 5;
- dev_ptmx_driver.major= MAJOR(PTMX_DEV);
- dev_ptmx_driver.minor_start = MINOR(PTMX_DEV);
+ dev_ptmx_driver.major= TTYAUX_MAJOR;
+ dev_ptmx_driver.minor_start = 2;
dev_ptmx_driver.type = TTY_DRIVER_TYPE_SYSTEM;
dev_ptmx_driver.subtype = SYSTEM_TYPE_SYSPTMX;
vcs_size(struct inode *inode)
{
int size;
- int currcons = MINOR(inode->i_rdev) & 127;
+ int minor = minor(inode->i_rdev);
+ int currcons = minor & 127;
if (currcons == 0)
currcons = fg_console;
else
size = video_num_lines * video_num_columns;
- if (MINOR(inode->i_rdev) & 128)
+ if (minor & 128)
size = 2*size + HEADER_SIZE;
return size;
}
vcs_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
struct inode *inode = file->f_dentry->d_inode;
- unsigned int currcons = MINOR(inode->i_rdev);
+ unsigned int currcons = minor(inode->i_rdev);
long pos = *ppos;
long viewed, attr, read;
int col, maxcol;
vcs_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
struct inode *inode = file->f_dentry->d_inode;
- unsigned int currcons = MINOR(inode->i_rdev);
+ unsigned int currcons = minor(inode->i_rdev);
long pos = *ppos;
long viewed, attr, size, written;
char *con_buf0;
static int
vcs_open(struct inode *inode, struct file *filp)
{
- unsigned int currcons = (MINOR(inode->i_rdev) & 127);
+ unsigned int currcons = minor(inode->i_rdev) & 127;
if(currcons && !vc_cons_allocated(currcons-1))
return -ENXIO;
return 0;
ataraid_gendisk.major = ATAMAJOR;
ataraid_gendisk.major_name = "ataraid";
ataraid_gendisk.minor_shift = 4;
- ataraid_gendisk.max_p = 15;
ataraid_gendisk.sizes = &ataraid_gendisk_sizes[0];
ataraid_gendisk.nr_real = 16;
ataraid_gendisk.fops = &ataraid_fops;
major: MAJOR_NR,
major_name: "hd",
minor_shift: 6,
- max_p: 1 << 6,
part: hd,
sizes: hd_sizes,
fops: &hd_fops,
/* Now try to get the total cdrom capacity. */
minor = (drive->select.b.unit) << PARTN_BITS;
- dev = MKDEV(HWIF(drive)->major, minor);
+ dev = mk_kdev(HWIF(drive)->major, minor);
stat = cdrom_get_last_written(dev, &toc->capacity);
if (stat)
stat = cdrom_read_capacity(drive, &toc->capacity, sense);
struct cdrom_device_info *devinfo = &info->devinfo;
int minor = (drive->select.b.unit) << PARTN_BITS;
- devinfo->dev = MKDEV (HWIF(drive)->major, minor);
+ devinfo->dev = mk_kdev(HWIF(drive)->major, minor);
devinfo->ops = &ide_cdrom_dops;
devinfo->mask = 0;
*(int *)&devinfo->speed = CDROM_STATE_FLAGS (drive)->current_speed;
/*
* default to read-only always and fix latter at the bottom
*/
- set_device_ro(MKDEV(HWIF(drive)->major, minor), 1);
- set_blocksize(MKDEV(HWIF(drive)->major, minor), CD_FRAMESIZE);
+ set_device_ro(mk_kdev(HWIF(drive)->major, minor), 1);
+ set_blocksize(mk_kdev(HWIF(drive)->major, minor), CD_FRAMESIZE);
blk_queue_hardsect_size(&drive->queue, CD_FRAMESIZE);
blk_queue_prep_rq(&drive->queue, ll_10byte_cmd_build);
nslots = ide_cdrom_probe_capabilities (drive);
if (CDROM_CONFIG_FLAGS(drive)->dvd_ram)
- set_device_ro(MKDEV(HWIF(drive)->major, minor), 0);
+ set_device_ro(mk_kdev(HWIF(drive)->major, minor), 0);
if (ide_cdrom_register (drive, nslots)) {
printk ("%s: ide_cdrom_setup failed to register device with the cdrom driver.\n", drive->name);
static
int ide_cdrom_check_media_change (ide_drive_t *drive)
{
- return cdrom_media_changed(MKDEV (HWIF (drive)->major,
+ return cdrom_media_changed(mk_kdev (HWIF (drive)->major,
(drive->select.b.unit) << PARTN_BITS));
}
* 1024 even for CDROM's
*/
blk_size[HWIF(drive)->major] = HWIF(drive)->gd->sizes;
- set_blocksize(MKDEV(HWIF(drive)->major, minor), CD_FRAMESIZE);
+ set_blocksize(mk_kdev(HWIF(drive)->major, minor), CD_FRAMESIZE);
}
static
gd->major = hwif->major; /* our major device number */
gd->major_name = IDE_MAJOR_NAME; /* treated special in genhd.c */
gd->minor_shift = PARTN_BITS; /* num bits for partitions */
- gd->max_p = 1<<PARTN_BITS; /* 1 + max partitions / drive */
gd->nr_real = units; /* current num real drives */
- gd->real_devices= hwif; /* ptr to internal data */
gd->next = NULL; /* linked list of major devs */
gd->fops = ide_fops; /* file operations */
gd->de_arr = kmalloc (sizeof *gd->de_arr * units, GFP_KERNEL);
}
if (!end_that_request_first(rq, uptodate, nr_secs)) {
- add_blkdev_randomness(MAJOR(rq->rq_dev));
+ add_blkdev_randomness(major(rq->rq_dev));
blkdev_dequeue_request(rq);
hwgroup->rq = NULL;
end_that_request_last(rq);
continue;
if (drive->media!=ide_disk && drive->media!=ide_floppy)
continue;
- register_disk(gd,MKDEV(hwif->major,unit<<PARTN_BITS),
+ register_disk(gd,mk_kdev(hwif->major,unit<<PARTN_BITS),
#ifdef CONFIG_BLK_DEV_ISAPNP
(drive->forced_geom && drive->noprobe) ? 1 :
#endif /* CONFIG_BLK_DEV_ISAPNP */
{
ide_startstop_t startstop;
unsigned long block;
- unsigned int minor = MINOR(rq->rq_dev), unit = minor >> PARTN_BITS;
+ unsigned int minor = minor(rq->rq_dev), unit = minor >> PARTN_BITS;
ide_hwif_t *hwif = HWIF(drive);
BUG_ON(!(rq->flags & REQ_STARTED));
*/
request_queue_t *ide_get_queue (kdev_t dev)
{
- ide_hwif_t *hwif = (ide_hwif_t *)blk_dev[MAJOR(dev)].data;
+ ide_hwif_t *hwif = (ide_hwif_t *)blk_dev[major(dev)].data;
return &hwif->drives[DEVICE_NR(dev) & 1].queue;
}
*/
ide_drive_t *get_info_ptr (kdev_t i_rdev)
{
- int major = MAJOR(i_rdev);
+ int major = major(i_rdev);
unsigned int h;
for (h = 0; h < MAX_HWIFS; ++h) {
#endif
rq->errors = 0;
rq->rq_status = RQ_ACTIVE;
- rq->rq_dev = MKDEV(major,(drive->select.b.unit)<<PARTN_BITS);
+ rq->rq_dev = mk_kdev(major,(drive->select.b.unit)<<PARTN_BITS);
if (action == ide_wait)
rq->waiting = &wait;
spin_lock_irqsave(&ide_lock, flags);
{
struct gendisk *g = HWIF(drive)->gd;
int minor = (drive->select.b.unit << g->minor_shift);
- kdev_t dev = MKDEV(g->major, minor);
+ kdev_t dev = mk_kdev(g->major, minor);
grok_partitions(dev, current_capacity(drive));
}
if (drive->revalidate) {
drive->revalidate = 0;
if (!initializing)
- (void) ide_revalidate_disk(MKDEV(hwif->major, unit<<PARTN_BITS));
+ (void) ide_revalidate_disk(mk_kdev(hwif->major, unit<<PARTN_BITS));
}
}
}
minor = drive->select.b.unit << PARTN_BITS;
for (p = 0; p < (1<<PARTN_BITS); ++p) {
if (drive->part[p].nr_sects > 0) {
- kdev_t devp = MKDEV(hwif->major, minor+p);
+ kdev_t devp = mk_kdev(hwif->major, minor+p);
invalidate_device(devp, 0);
}
}
kdev_t dev;
ide_settings_t *setting;
- if (!inode || !(dev = inode->i_rdev))
- return -EINVAL;
- major = MAJOR(dev); minor = MINOR(dev);
+ dev = inode->i_rdev;
+ major = major(dev); minor = minor(dev);
if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
return -ENODEV;
err = ide_read_setting(drive, setting);
return err >= 0 ? put_user(err, (long *) arg) : err;
} else {
- if ((MINOR(inode->i_rdev) & PARTN_MASK))
+ if ((minor(inode->i_rdev) & PARTN_MASK))
return -EINVAL;
return ide_write_setting(drive, setting, arg);
}
if (put_user(drive->bios_head, (byte *) &loc->heads)) return -EFAULT;
if (put_user(drive->bios_sect, (byte *) &loc->sectors)) return -EFAULT;
if (put_user(bios_cyl, (unsigned short *) &loc->cylinders)) return -EFAULT;
- if (put_user((unsigned)drive->part[MINOR(inode->i_rdev)&PARTN_MASK].start_sect,
+ if (put_user((unsigned)drive->part[minor(inode->i_rdev)&PARTN_MASK].start_sect,
(unsigned long *) &loc->start)) return -EFAULT;
return 0;
}
if (put_user(drive->bios_head, (byte *) &loc->heads)) return -EFAULT;
if (put_user(drive->bios_sect, (byte *) &loc->sectors)) return -EFAULT;
if (put_user(drive->bios_cyl, (unsigned int *) &loc->cylinders)) return -EFAULT;
- if (put_user((unsigned)drive->part[MINOR(inode->i_rdev)&PARTN_MASK].start_sect,
+ if (put_user((unsigned)drive->part[minor(inode->i_rdev)&PARTN_MASK].start_sect,
(unsigned long *) &loc->start)) return -EFAULT;
return 0;
}
if (put_user(drive->head, (byte *) &loc->heads)) return -EFAULT;
if (put_user(drive->sect, (byte *) &loc->sectors)) return -EFAULT;
if (put_user(drive->cyl, (unsigned int *) &loc->cylinders)) return -EFAULT;
- if (put_user((unsigned)drive->part[MINOR(inode->i_rdev)&PARTN_MASK].start_sect,
+ if (put_user((unsigned)drive->part[minor(inode->i_rdev)&PARTN_MASK].start_sect,
(unsigned long *) &loc->start)) return -EFAULT;
return 0;
}
case HDIO_OBSOLETE_IDENTITY:
case HDIO_GET_IDENTITY:
- if (MINOR(inode->i_rdev) & PARTN_MASK)
+ if (minor(inode->i_rdev) & PARTN_MASK)
return -EINVAL;
if (drive->id == NULL)
return -ENOMSG;
major: MAJOR_NR,
major_name: LVM_NAME,
minor_shift: 0,
- max_p: 1,
part: lvm_hd_struct,
sizes: lvm_size,
nr_real: MAX_LV,
major: MD_MAJOR,
major_name: "md",
minor_shift: 0,
- max_p: 1,
part: md_hd_struct,
sizes: md_size,
nr_real: MAX_MD_DEVS,
- real_devices: NULL,
next: NULL,
fops: &md_fops,
};
major: MAJOR_NR,
major_name: "i2o/hd",
minor_shift: 4,
- max_p: 1<<4,
part: i2ob,
sizes: i2ob_sizes,
nr_real: MAX_I2OB,
#include <linux/hdreg.h>
#include <stdarg.h>
-#if (LINUX_VERSION_CODE >= 0x20100)
#include <linux/vmalloc.h>
-#endif
-#if (LINUX_VERSION_CODE >= 0x20303)
#include <linux/blkpg.h>
-#endif
#include <linux/mtd/ftl.h>
-/*====================================================================*/
-/* Stuff which really ought to be in compatmac.h */
-
-#if (LINUX_VERSION_CODE < 0x20328)
-#define register_disk(dev, drive, minors, ops, size) \
- do { (dev)->part[(drive)*(minors)].nr_sects = size; \
- if (size == 0) (dev)->part[(drive)*(minors)].start_sect = -1; \
- resetup_one_dev(dev, drive); } while (0);
-#endif
-#if (LINUX_VERSION_CODE < 0x20320)
-#define BLK_DEFAULT_QUEUE(n) blk_dev[n].request_fn
-#define blk_init_queue(q, req) q = (req)
-#define blk_cleanup_queue(q) q = NULL
-#define request_arg_t void
-#else
#define request_arg_t request_queue_t *q
-#endif
-
/*====================================================================*/
major: FTL_MAJOR,
major_name: "ftl",
minor_shift: PART_BITS,
- max_p: MAX_PART,
-#if (LINUX_VERSION_CODE < 0x20328)
- max_nr: MAX_DEV*MAX_PART,
-#endif
part: ftl_hd,
sizes: ftl_sizes,
};
static void ftl_erase_callback(struct erase_info *done);
-#if LINUX_VERSION_CODE < 0x20326
-static struct file_operations ftl_blk_fops = {
- open: ftl_open,
- release: ftl_close,
- ioctl: ftl_ioctl,
- read: block_read,
- write: block_write,
- fsync: block_fsync
-};
-#else
static struct block_device_operations ftl_blk_fops = {
owner: THIS_MODULE,
open: ftl_open,
release: ftl_close,
ioctl: ftl_ioctl,
};
-#endif
/*======================================================================
case BLKRRPART:
ret = ftl_reread_partitions(minor);
break;
-#if (LINUX_VERSION_CODE < 0x20303)
- case BLKFLSBUF:
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
- if (!capable(CAP_SYS_ADMIN)) return -EACCES;
-#endif
- fsync_dev(inode->i_rdev);
- invalidate_buffers(inode->i_rdev);
- break;
- RO_IOCTLS(inode->i_rdev, arg);
-#else
case BLKROSET:
case BLKROGET:
case BLKFLSBUF:
ret = blk_ioctl(inode->i_rdev, cmd, arg);
break;
-#endif
default:
ret = -EINVAL;
}
static struct gendisk nftl_gendisk = {
major: MAJOR_NR,
major_name: "nftl",
- minor_shift: NFTL_PARTN_BITS, /* Bits to shift to get real from partition */
- max_p: (1<<NFTL_PARTN_BITS)-1, /* Number of partitions per real */
-#if LINUX_VERSION_CODE < 0x20328
- max_nr: MAX_NFTLS, /* maximum number of real */
- init: dummy_init, /* init function */
-#endif
- part: part_table, /* hd struct */
- sizes: nftl_sizes, /* block sizes */
+ minor_shift: NFTL_PARTN_BITS, /* # of partition bits */
+ part: part_table, /* hd struct */
+ sizes: nftl_sizes, /* block sizes */
};
struct NFTLrecord *NFTLs[MAX_NFTLS];
static int irda_usb_hard_xmit(struct sk_buff *skb, struct net_device *dev);
static int irda_usb_open(struct irda_usb_cb *self);
static int irda_usb_close(struct irda_usb_cb *self);
-static void speed_bulk_callback(purb_t purb);
-static void write_bulk_callback(purb_t purb);
-static void irda_usb_receive(purb_t purb);
+static void speed_bulk_callback(struct urb *urb);
+static void write_bulk_callback(struct urb *urb);
+static void irda_usb_receive(struct urb *urb);
static int irda_usb_net_init(struct net_device *dev);
static int irda_usb_net_open(struct net_device *dev);
static int irda_usb_net_close(struct net_device *dev);
{
unsigned long flags;
__u8 *frame;
- purb_t purb;
+ struct urb *urb;
int ret;
IRDA_DEBUG(2, __FUNCTION__ "(), speed=%d, xbofs=%d\n",
self->new_speed, self->new_xbofs);
/* Grab the speed URB */
- purb = &self->speed_urb;
- if (purb->status != 0) {
+ urb = &self->speed_urb;
+ if (urb->status != 0) {
WARNING(__FUNCTION__ "(), URB still in use!\n");
return;
}
irda_usb_build_header(self, frame, 1);
/* Submit the 0 length IrDA frame to trigger new speed settings */
- FILL_BULK_URB(purb, self->usbdev,
+ FILL_BULK_URB(urb, self->usbdev,
usb_sndbulkpipe(self->usbdev, self->bulk_out_ep),
frame, IRDA_USB_SPEED_MTU,
speed_bulk_callback, self);
- purb->transfer_buffer_length = USB_IRDA_HEADER;
- purb->transfer_flags = USB_QUEUE_BULK | USB_ASYNC_UNLINK;
- purb->timeout = MSECS_TO_JIFFIES(100);
+ urb->transfer_buffer_length = USB_IRDA_HEADER;
+ urb->transfer_flags = USB_QUEUE_BULK | USB_ASYNC_UNLINK;
+ urb->timeout = MSECS_TO_JIFFIES(100);
- if ((ret = usb_submit_urb(purb))) {
+ if ((ret = usb_submit_urb(urb))) {
WARNING(__FUNCTION__ "(), failed Speed URB\n");
}
spin_unlock_irqrestore(&self->lock, flags);
/*
* Note : this function will be called with both speed_urb and empty_urb...
*/
-static void speed_bulk_callback(purb_t purb)
+static void speed_bulk_callback(struct urb *urb)
{
- struct irda_usb_cb *self = purb->context;
+ struct irda_usb_cb *self = urb->context;
IRDA_DEBUG(2, __FUNCTION__ "()\n");
}
/* Check for timeout and other USB nasties */
- if (purb->status != 0) {
+ if (urb->status != 0) {
/* I get a lot of -ECONNABORTED = -103 here - Jean II */
- IRDA_DEBUG(0, __FUNCTION__ "(), URB complete status %d, transfer_flags 0x%04X\n", purb->status, purb->transfer_flags);
+ IRDA_DEBUG(0, __FUNCTION__ "(), URB complete status %d, transfer_flags 0x%04X\n", urb->status, urb->transfer_flags);
/* Don't do anything here, that might confuse the USB layer.
* Instead, we will wait for irda_usb_net_timeout(), the
}
/* urb is now available */
- purb->status = 0;
+ urb->status = 0;
/* If it was the speed URB, allow the stack to send more packets */
- if(purb == &self->speed_urb) {
+ if(urb == &self->speed_urb) {
netif_wake_queue(self->netdev);
}
}
static int irda_usb_hard_xmit(struct sk_buff *skb, struct net_device *netdev)
{
struct irda_usb_cb *self = netdev->priv;
- purb_t purb = &self->tx_urb;
+ struct urb *urb = &self->tx_urb;
unsigned long flags;
s32 speed;
s16 xbofs;
}
}
- if (purb->status != 0) {
+ if (urb->status != 0) {
WARNING(__FUNCTION__ "(), URB still in use!\n");
dev_kfree_skb(skb);
return 0;
/* FIXME: Make macro out of this one */
((struct irda_skb_cb *)skb->cb)->context = self;
- FILL_BULK_URB(purb, self->usbdev,
+ FILL_BULK_URB(urb, self->usbdev,
usb_sndbulkpipe(self->usbdev, self->bulk_out_ep),
skb->data, IRDA_USB_MAX_MTU,
write_bulk_callback, skb);
- purb->transfer_buffer_length = skb->len;
+ urb->transfer_buffer_length = skb->len;
/* Note : unlink *must* be Asynchronous because of the code in
* irda_usb_net_timeout() -> call in irq - Jean II */
- purb->transfer_flags = USB_QUEUE_BULK | USB_ASYNC_UNLINK;
+ urb->transfer_flags = USB_QUEUE_BULK | USB_ASYNC_UNLINK;
/* This flag (USB_ZERO_PACKET) indicates that what we send is not
* a continuous stream of data but separate packets.
* In this case, the USB layer will insert an empty USB frame (TD)
* after each of our packets that is exact multiple of the frame size.
* This is how the dongle will detect the end of packet - Jean II */
- purb->transfer_flags |= USB_ZERO_PACKET;
+ urb->transfer_flags |= USB_ZERO_PACKET;
/* Timeout need to be shorter than NET watchdog timer */
- purb->timeout = MSECS_TO_JIFFIES(200);
+ urb->timeout = MSECS_TO_JIFFIES(200);
/* Generate min turn time. FIXME: can we do better than this? */
/* Trying to a turnaround time at this level is trying to measure
}
/* Ask USB to send the packet */
- if ((res = usb_submit_urb(purb))) {
+ if ((res = usb_submit_urb(urb))) {
WARNING(__FUNCTION__ "(), failed Tx URB\n");
self->stats.tx_errors++;
/* Let USB recover : We will catch that in the watchdog */
/*
* Note : this function will be called only for tx_urb...
*/
-static void write_bulk_callback(purb_t purb)
+static void write_bulk_callback(struct urb *urb)
{
- struct sk_buff *skb = purb->context;
+ struct sk_buff *skb = urb->context;
struct irda_usb_cb *self = ((struct irda_skb_cb *) skb->cb)->context;
IRDA_DEBUG(2, __FUNCTION__ "()\n");
/* Free up the skb */
dev_kfree_skb_any(skb);
- purb->context = NULL;
+ urb->context = NULL;
/* Check for timeout and other USB nasties */
- if (purb->status != 0) {
+ if (urb->status != 0) {
/* I get a lot of -ECONNABORTED = -103 here - Jean II */
- IRDA_DEBUG(0, __FUNCTION__ "(), URB complete status %d, transfer_flags 0x%04X\n", purb->status, purb->transfer_flags);
+ IRDA_DEBUG(0, __FUNCTION__ "(), URB complete status %d, transfer_flags 0x%04X\n", urb->status, urb->transfer_flags);
/* Don't do anything here, that might confuse the USB layer,
* and we could go in recursion and blow the kernel stack...
}
/* urb is now available */
- purb->status = 0;
+ urb->status = 0;
/* If the network is closed, stop everything */
if ((!self->netopen) || (!self->present)) {
static void irda_usb_net_timeout(struct net_device *netdev)
{
struct irda_usb_cb *self = netdev->priv;
- purb_t purb;
+ struct urb *urb;
int done = 0; /* If we have made any progress */
IRDA_DEBUG(0, __FUNCTION__ "(), Network layer thinks we timed out!\n");
}
/* Check speed URB */
- purb = &(self->speed_urb);
- if (purb->status != 0) {
- IRDA_DEBUG(0, "%s: Speed change timed out, urb->status=%d, urb->transfer_flags=0x%04X\n", netdev->name, purb->status, purb->transfer_flags);
+ urb = &(self->speed_urb);
+ if (urb->status != 0) {
+ IRDA_DEBUG(0, "%s: Speed change timed out, urb->status=%d, urb->transfer_flags=0x%04X\n", netdev->name, urb->status, urb->transfer_flags);
- switch (purb->status) {
+ switch (urb->status) {
case -EINPROGRESS:
- usb_unlink_urb(purb);
+ usb_unlink_urb(urb);
/* Note : above will *NOT* call netif_wake_queue()
* in completion handler, we will come back here.
* Jean II */
case -ETIMEDOUT: /* -110 */
case -ENOENT: /* -2 (urb unlinked by us) */
default: /* ??? - Play safe */
- purb->status = 0;
+ urb->status = 0;
netif_wake_queue(self->netdev);
done = 1;
break;
}
/* Check Tx URB */
- purb = &(self->tx_urb);
- if (purb->status != 0) {
- struct sk_buff *skb = purb->context;
+ urb = &(self->tx_urb);
+ if (urb->status != 0) {
+ struct sk_buff *skb = urb->context;
- IRDA_DEBUG(0, "%s: Tx timed out, urb->status=%d, urb->transfer_flags=0x%04X\n", netdev->name, purb->status, purb->transfer_flags);
+ IRDA_DEBUG(0, "%s: Tx timed out, urb->status=%d, urb->transfer_flags=0x%04X\n", netdev->name, urb->status, urb->transfer_flags);
/* Increase error count */
self->stats.tx_errors++;
irda_usb_change_speed_xbofs(self);
#endif /* IU_BUG_KICK_TIMEOUT */
- switch (purb->status) {
+ switch (urb->status) {
case -EINPROGRESS:
- usb_unlink_urb(purb);
+ usb_unlink_urb(urb);
/* Note : above will *NOT* call netif_wake_queue()
- * in completion handler, because purb->status will
+ * in completion handler, because urb->status will
* be -ENOENT. We will fix that at the next watchdog,
* leaving more time to USB to recover...
* Also, we are in interrupt, so we need to have
default: /* ??? - Play safe */
if(skb != NULL) {
dev_kfree_skb_any(skb);
- purb->context = NULL;
+ urb->context = NULL;
}
- purb->status = 0;
+ urb->status = 0;
netif_wake_queue(self->netdev);
done = 1;
break;
*
* Jean II
*/
-static void irda_usb_submit(struct irda_usb_cb *self, struct sk_buff *skb, purb_t purb)
+static void irda_usb_submit(struct irda_usb_cb *self, struct sk_buff *skb, struct urb *urb)
{
struct irda_skb_cb *cb;
int ret;
IRDA_DEBUG(2, __FUNCTION__ "()\n");
/* Check that we have an urb */
- if (!purb) {
- WARNING(__FUNCTION__ "(), Bug : purb == NULL\n");
+ if (!urb) {
+ WARNING(__FUNCTION__ "(), Bug : urb == NULL\n");
return;
}
cb->context = self;
/* Reinitialize URB */
- FILL_BULK_URB(purb, self->usbdev,
+ FILL_BULK_URB(urb, self->usbdev,
usb_rcvbulkpipe(self->usbdev, self->bulk_in_ep),
skb->data, skb->truesize,
irda_usb_receive, skb);
- purb->transfer_flags = USB_QUEUE_BULK;
+ urb->transfer_flags = USB_QUEUE_BULK;
/* Note : unlink *must* be synchronous because of the code in
* irda_usb_net_close() -> free the skb - Jean II */
- purb->status = 0;
- purb->next = NULL; /* Don't auto resubmit URBs */
+ urb->status = 0;
+ urb->next = NULL; /* Don't auto resubmit URBs */
- ret = usb_submit_urb(purb);
+ ret = usb_submit_urb(urb);
if (ret) {
/* If this ever happen, we are in deep s***.
* Basically, the Rx path will stop... */
/*------------------------------------------------------------------*/
/*
- * Function irda_usb_receive(purb)
+ * Function irda_usb_receive(urb)
*
* Called by the USB subsystem when a frame has been received
*
*/
-static void irda_usb_receive(purb_t purb)
+static void irda_usb_receive(struct urb *urb)
{
- struct sk_buff *skb = (struct sk_buff *) purb->context;
+ struct sk_buff *skb = (struct sk_buff *) urb->context;
struct irda_usb_cb *self;
struct irda_skb_cb *cb;
struct sk_buff *new;
- IRDA_DEBUG(2, __FUNCTION__ "(), len=%d\n", purb->actual_length);
+ IRDA_DEBUG(2, __FUNCTION__ "(), len=%d\n", urb->actual_length);
/* Find ourselves */
cb = (struct irda_skb_cb *) skb->cb;
}
/* Check the status */
- if (purb->status != 0) {
- switch (purb->status) {
+ if (urb->status != 0) {
+ switch (urb->status) {
case -EILSEQ:
self->stats.rx_errors++;
self->stats.rx_crc_errors++;
break;
case -ECONNRESET: /* -104 */
- IRDA_DEBUG(0, __FUNCTION__ "(), Connection Reset (-104), transfer_flags 0x%04X \n", purb->transfer_flags);
+ IRDA_DEBUG(0, __FUNCTION__ "(), Connection Reset (-104), transfer_flags 0x%04X \n", urb->transfer_flags);
/* uhci_cleanup_unlink() is going to kill the Rx
* URB just after we return. No problem, at this
* point the URB will be idle ;-) - Jean II */
break;
default:
- IRDA_DEBUG(0, __FUNCTION__ "(), RX status %d,transfer_flags 0x%04X \n", purb->status, purb->transfer_flags);
+ IRDA_DEBUG(0, __FUNCTION__ "(), RX status %d,transfer_flags 0x%04X \n", urb->status, urb->transfer_flags);
break;
}
goto done;
}
/* Check for empty frames */
- if (purb->actual_length <= USB_IRDA_HEADER) {
+ if (urb->actual_length <= USB_IRDA_HEADER) {
WARNING(__FUNCTION__ "(), empty frame!\n");
goto done;
}
get_fast_time(&self->stamp);
/* Fix skb, and remove USB-IrDA header */
- skb_put(skb, purb->actual_length);
+ skb_put(skb, urb->actual_length);
skb_pull(skb, USB_IRDA_HEADER);
/* Don't waste a lot of memory on small IrDA frames */
netif_rx(new);
done:
- /* Note : at this point, the URB we've just received (purb)
+ /* Note : at this point, the URB we've just received (urb)
* is still referenced by the USB layer. For example, if we
* have received a -ECONNRESET, uhci_cleanup_unlink() will
* continue to process it (in fact, cleaning it up).
/* Submit the idle URB to replace the URB we've just received */
irda_usb_submit(self, skb, self->idle_rx_urb);
/* Recycle Rx URB : Now, the idle URB is the present one */
- self->idle_rx_urb = purb;
- purb->context = NULL;
+ self->idle_rx_urb = urb;
+ urb->context = NULL;
}
/*------------------------------------------------------------------*/
/* Deallocate all the Rx path buffers (URBs and skb) */
for (i = 0; i < IU_MAX_RX_URBS; i++) {
- purb_t purb = &(self->rx_urb[i]);
- struct sk_buff *skb = (struct sk_buff *) purb->context;
+ struct urb *urb = &(self->rx_urb[i]);
+ struct sk_buff *skb = (struct sk_buff *) urb->context;
/* Cancel the receive command */
- usb_unlink_urb(purb);
+ usb_unlink_urb(urb);
/* The skb is ours, free it */
if(skb) {
dev_kfree_skb(skb);
- purb->context = NULL;
+ urb->context = NULL;
}
}
/* Cancel Tx and speed URB */
static int ds_open(struct inode *inode, struct file *file)
{
- socket_t i = MINOR(inode->i_rdev);
+ socket_t i = minor(inode->i_rdev);
socket_info_t *s;
user_info_t *user;
static int ds_release(struct inode *inode, struct file *file)
{
- socket_t i = MINOR(inode->i_rdev);
+ socket_t i = minor(inode->i_rdev);
socket_info_t *s;
user_info_t *user, **link;
static ssize_t ds_read(struct file *file, char *buf,
size_t count, loff_t *ppos)
{
- socket_t i = MINOR(file->f_dentry->d_inode->i_rdev);
+ socket_t i = minor(file->f_dentry->d_inode->i_rdev);
socket_info_t *s;
user_info_t *user;
static ssize_t ds_write(struct file *file, const char *buf,
size_t count, loff_t *ppos)
{
- socket_t i = MINOR(file->f_dentry->d_inode->i_rdev);
+ socket_t i = minor(file->f_dentry->d_inode->i_rdev);
socket_info_t *s;
user_info_t *user;
/* No kernel lock - fine */
static u_int ds_poll(struct file *file, poll_table *wait)
{
- socket_t i = MINOR(file->f_dentry->d_inode->i_rdev);
+ socket_t i = minor(file->f_dentry->d_inode->i_rdev);
socket_info_t *s;
user_info_t *user;
static int ds_ioctl(struct inode * inode, struct file * file,
u_int cmd, u_long arg)
{
- socket_t i = MINOR(inode->i_rdev);
+ socket_t i = minor(inode->i_rdev);
socket_info_t *s;
u_int size;
int ret, err;
major:D_MAJOR, \
major_name:D_NAME, \
minor_shift:D_PARTN_BITS, \
- max_p:1 << D_PARTN_BITS, \
max_nr:D_PER_MAJOR, \
nr_real:D_PER_MAJOR,
static inline struct request *
major:D_MAJOR, \
major_name:D_NAME, \
minor_shift:D_PARTN_BITS, \
- max_p:1 << D_PARTN_BITS, \
nr_real:D_PER_MAJOR, \
fops:&dasd_device_operations,
static inline struct request *
if (req->waiting)
complete(req->waiting);
- add_blkdev_randomness(MAJOR(req->rq_dev));
+ add_blkdev_randomness(major(req->rq_dev));
/*
* This will goose the queue request function at the end, so we don't
{
struct Scsi_Device_Template *spnt;
kdev_t dev = req->rq_dev;
- int major = MAJOR(dev);
+ int major = major(dev);
for (spnt = scsi_devicelist; spnt; spnt = spnt->next) {
/*
unsigned char *scsi_bios_ptable(kdev_t dev)
{
unsigned char *res = kmalloc(66, GFP_KERNEL);
- kdev_t rdev = MKDEV(MAJOR(dev), MINOR(dev) & ~0xf);
+ kdev_t rdev = mk_kdev(major(dev), minor(dev) & ~0x0f);
if (res) {
struct buffer_head *bh = bread(rdev, 0, block_size(rdev));
#define SCSI_DISKS_PER_MAJOR 16
#define SD_MAJOR_NUMBER(i) SD_MAJOR((i) >> 8)
#define SD_MINOR_NUMBER(i) ((i) & 255)
-#define MKDEV_SD_PARTITION(i) MKDEV(SD_MAJOR_NUMBER(i), (i) & 255)
+#define MKDEV_SD_PARTITION(i) mk_kdev(SD_MAJOR_NUMBER(i), (i) & 255)
#define MKDEV_SD(index) MKDEV_SD_PARTITION((index) << 4)
#define N_USED_SD_MAJORS (1 + ((sd_template.dev_max - 1) >> 4))
major: SCSI_DISK0_MAJOR,
major_name: "sd",
minor_shift: 4,
- max_p: 1 << 4,
fops: &sd_fops,
};
}
for (i = 0; i < N_USED_SD_MAJORS; i++) {
- request_queue_t *q = blk_get_queue(SD_MAJOR(i));
+ request_queue_t *q = blk_get_queue(mk_kdev(SD_MAJOR(i), 0));
int parts_per_major = (SCSI_DISKS_PER_MAJOR << 4);
blksize_size[SD_MAJOR(i)] =
sd_gendisks[i].major = SD_MAJOR(i);
sd_gendisks[i].major_name = "sd";
sd_gendisks[i].minor_shift = 4;
- sd_gendisks[i].max_p = 1 << 4;
sd_gendisks[i].part = sd + i * (N << 4);
sd_gendisks[i].sizes = sd_sizes + i * (N << 4);
sd_gendisks[i].nr_real = 0;
- sd_gendisks[i].real_devices =
- (void *) (rscsi_disks + i * SCSI_DISKS_PER_MAJOR);
}
return 0;
for (dpnt = rscsi_disks, i = 0; i < sd_template.dev_max; i++, dpnt++)
if (dpnt->device == SDp) {
- max_p = sd_gendisk.max_p;
+ max_p = 1 << sd_gendisk.minor_shift;
start = i << sd_gendisk.minor_shift;
dev = MKDEV_SD_PARTITION(start);
wipe_partitions(dev);
#define N_SD_MAJORS 8
#define SD_MAJOR_MASK (N_SD_MAJORS - 1)
-#define SD_PARTITION(i) (((MAJOR(i) & SD_MAJOR_MASK) << 8) | (MINOR(i) & 255))
+#define SD_PARTITION(i) (((major(i) & SD_MAJOR_MASK) << 8) | (minor(i) & 255))
#endif
* 03/01/1998
*
* WD7000 driver now work on kernels >= 2.1.x
+ *
+ *
+ * 12/31/2001 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *
+ * use host->host_lock, not io_request_lock, cleanups
*/
#include <linux/module.h>
#define ANY2SCSI_INLINE /* undef this to use old macros */
#undef WD7000_DEBUG /* general debug */
+#ifdef WD7000_DEBUG
+#define dprintk printk
+#else
+#define dprintk(format,args...)
+#endif
#include "wd7000.h"
#include <linux/stat.h>
} Icb;
#ifdef MODULE
-static char * wd7000 = NULL;
+static char *wd7000;
MODULE_PARM(wd7000, "s");
#endif
* structure is not part of the Adapter structure.
*/
static Scb scbs[MAX_SCBS];
-static Scb *scbfree = NULL; /* free list */
+static Scb *scbfree; /* free list */
static int freescbs = MAX_SCBS; /* free list counter */
/*
* END of data/declarations - code follows.
*/
-static void setup_error (char *mesg, int *ints)
+static void __init setup_error(char *mesg, int *ints)
{
if (ints[0] == 3)
- printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> %s\n",
- ints[1], ints[2], ints[3], mesg);
+ printk(KERN_ERR "wd7000_setup: \"wd7000=%d,%d,0x%x\" -> %s\n",
+ ints[1], ints[2], ints[3], mesg);
else if (ints[0] == 4)
- printk ("wd7000_setup: \"wd7000=%d,%d,0x%x,%d\" -> %s\n",
- ints[1], ints[2], ints[3], ints[4], mesg);
+ printk(KERN_ERR "wd7000_setup: \"wd7000=%d,%d,0x%x,%d\" -> %s\n",
+ ints[1], ints[2], ints[3], ints[4], mesg);
else
- printk ("wd7000_setup: \"wd7000=%d,%d,0x%x,%d,%d\" -> %s\n",
- ints[1], ints[2], ints[3], ints[4], ints[5], mesg);
+ printk(KERN_ERR "wd7000_setup: \"wd7000=%d,%d,0x%x,%d,%d\" -> %s\n",
+ ints[1], ints[2], ints[3], ints[4], ints[5], mesg);
}
*/
static int __init wd7000_setup(char *str)
{
- static short wd7000_card_num = 0;
- short i, j;
+ static short wd7000_card_num; /* .bss will zero this */
+ short i;
int ints[6];
(void)get_options(str, ARRAY_SIZE(ints), ints);
if (wd7000_card_num >= NUM_CONFIGS) {
- printk("wd7000_setup: Too many \"wd7000=\" configurations in "
- "command line!\n");
+ printk(KERN_ERR __FUNCTION__
+ ": Too many \"wd7000=\" configurations in "
+ "command line!\n");
return 0;
}
if ((ints[0] < 3) || (ints[0] > 5)) {
- printk("wd7000_setup: Error in command line! "
- "Usage: wd7000=<IRQ>,<DMA>,IO>[,<BUS_ON>[,<BUS_OFF>]]\n");
+ printk(KERN_ERR __FUNCTION__ ": Error in command line! "
+ "Usage: wd7000=<IRQ>,<DMA>,IO>[,<BUS_ON>"
+ "[,<BUS_OFF>]]\n");
} else {
for (i = 0; i < NUM_IRQS; i++)
if (ints[1] == wd7000_irq[i])
if (ints[0] > 3) {
if ((ints[4] < 500) || (ints[4] > 31875)) {
- setup_error("BUS_ON value is out of range (500 to 31875 nanoseconds)!", ints);
+ setup_error("BUS_ON value is out of range (500"
+ " to 31875 nanoseconds)!", ints);
configs[wd7000_card_num].bus_on = BUS_ON;
} else
configs[wd7000_card_num].bus_on = ints[4] / 125;
if (ints[0] > 4) {
if ((ints[5] < 500) || (ints[5] > 31875)) {
- setup_error("BUS_OFF value is out of range (500 to 31875 nanoseconds)!", ints);
+ setup_error("BUS_OFF value is out of range (500"
+ " to 31875 nanoseconds)!", ints);
configs[wd7000_card_num].bus_off = BUS_OFF;
} else
- configs[wd7000_card_num].bus_off = ints[5] / 125;
+ configs[wd7000_card_num].bus_off = ints[5] /
+ 125;
} else
configs[wd7000_card_num].bus_off = BUS_OFF;
if (wd7000_card_num) {
- for (i = 0; i < (wd7000_card_num - 1); i++)
- for (j = i + 1; j < wd7000_card_num; j++)
+ for (i = 0; i < (wd7000_card_num - 1); i++) {
+ int j = i + 1;
+
+ for (; j < wd7000_card_num; j++)
if (configs[i].irq == configs[j].irq) {
- setup_error("duplicated IRQ!", ints);
+ setup_error("duplicated IRQ!",
+ ints);
return 0;
- } else if (configs[i].dma == configs[j].dma) {
- setup_error("duplicated DMA channel!", ints);
+ }
+ if (configs[i].dma == configs[j].dma) {
+ setup_error("duplicated DMA "
+ "channel!", ints);
return 0;
- } else if (configs[i].iobase == configs[j].iobase) {
- setup_error ("duplicated I/O base address!", ints);
+ }
+ if (configs[i].iobase ==
+ configs[j].iobase) {
+ setup_error("duplicated I/O "
+ "base address!",
+ ints);
return 0;
}
+ }
}
-#ifdef WD7000_DEBUG
- printk ("wd7000_setup: IRQ=%d, DMA=%d, I/O=0x%x, BUS_ON=%dns, BUS_OFF=%dns\n",
+ dprintk(KERN_DEBUG "wd7000_setup: IRQ=%d, DMA=%d, I/O=0x%x, "
+ "BUS_ON=%dns, BUS_OFF=%dns\n",
configs[wd7000_card_num].irq,
configs[wd7000_card_num].dma,
configs[wd7000_card_num].iobase,
configs[wd7000_card_num].bus_on * 125,
configs[wd7000_card_num].bus_off * 125);
-#endif
wd7000_card_num++;
}
return (1);
}
- printk ("wd7000 command_out: WAIT failed(%d)\n", len + 1);
+ printk(KERN_WARNING "wd7000 command_out: WAIT failed(%d)\n", len + 1);
return (0);
}
* the satisfiability of a request is not dependent on the size of the
* request.
*/
-static inline Scb *alloc_scbs (int needed)
+static inline Scb *alloc_scbs(struct Scsi_Host *host, int needed)
{
register Scb *scb, *p;
register unsigned long flags;
save_flags (flags);
cli ();
while (busy) { /* someone else is allocating */
- spin_unlock_irq(&io_request_lock);
+ spin_unlock_irq(&host->host_lock);
for (now = jiffies; now == jiffies; ); /* wait a jiffy */
- spin_lock_irq(&io_request_lock);
+ spin_lock_irq(&host->host_lock);
}
busy = 1; /* not busy now; it's our turn */
while (freescbs < needed) {
timeout = jiffies + WAITnexttimeout;
do {
- spin_unlock_irq(&io_request_lock);
+ spin_unlock_irq(&host->host_lock);
for (now = jiffies; now == jiffies; ); /* wait a jiffy */
- spin_lock_irq(&io_request_lock);
+ spin_lock_irq(&host->host_lock);
} while (freescbs < needed && time_before_eq(jiffies, timeout));
/*
* If we get here with enough free Scbs, we can take them.
Mailbox *ogmbs = host->mb.ogmb;
int *next_ogmb = &(host->next_ogmb);
-#ifdef WD7000_DEBUG
- printk ("wd7000_mail_out: 0x%06lx", (long) scbptr);
-#endif
+ dprintk("wd7000_mail_out: 0x%06lx", (long) scbptr);
/* We first look for a free outgoing mailbox */
save_flags (flags);
ogmb = *next_ogmb;
for (i = 0; i < OGMB_CNT; i++) {
if (ogmbs[ogmb].status == 0) {
-#ifdef WD7000_DEBUG
- printk (" using OGMB 0x%x", ogmb);
-#endif
+ dprintk(" using OGMB 0x%x", ogmb);
ogmbs[ogmb].status = 1;
any2scsi ((unchar *) ogmbs[ogmb].scbptr, (int) scbptr);
}
restore_flags (flags);
-#ifdef WD7000_DEBUG
- printk (", scb is 0x%06lx", (long) scbptr);
-#endif
+ dprintk(", scb is 0x%06lx", (long) scbptr);
if (i >= OGMB_CNT) {
/*
* that marks OGMB's free, waiting even with interrupts off
* should work, since they are freed very quickly in most cases.
*/
-#ifdef WD7000_DEBUG
- printk (", no free OGMBs.\n");
-#endif
+ dprintk(", no free OGMBs.\n");
return (0);
}
start_ogmb = START_OGMB | ogmb;
command_out (host, &start_ogmb, 1);
-#ifdef WD7000_DEBUG
- printk (", awaiting interrupt.\n");
-#endif
+ dprintk(", awaiting interrupt.\n");
return (1);
}
}
#ifdef WD7000_DEBUG
if (scsierr || hosterr)
- printk ("\nSCSI command error: SCSI 0x%02x host 0x%04x return %d\n",
+ dprintk("\nSCSI command error: SCSI 0x%02x host 0x%04x return %d\n",
scsierr, in_error, hosterr);
#endif
return (scsierr | (hosterr << 16));
static void wd7000_scsi_done (Scsi_Cmnd *SCpnt)
{
-#ifdef WD7000_DEBUG
- printk ("wd7000_scsi_done: 0x%06lx\n", (long) SCpnt);
-#endif
-
+ dprintk("wd7000_scsi_done: 0x%06lx\n", (long)SCpnt);
SCpnt->SCp.phase = 0;
}
host->int_counter++;
-#ifdef WD7000_DEBUG
- printk ("wd7000_intr_handle: irq = %d, host = 0x%06lx\n", irq, (long) host);
-#endif
+ dprintk("wd7000_intr_handle: irq = %d, host = 0x%06lx\n", irq, (long) host);
flag = inb (host->iobase + ASC_INTR_STAT);
-#ifdef WD7000_DEBUG
- printk ("wd7000_intr_handle: intr stat = 0x%02x\n", flag);
-#endif
+ dprintk("wd7000_intr_handle: intr stat = 0x%02x\n", flag);
if (!(inb (host->iobase + ASC_STAT) & INT_IM)) {
/* NB: these are _very_ possible if IRQ 15 is being used, since
* can sort these out. Otherwise, electrical noise and other such
* problems would be indistinguishable from valid interrupts...
*/
-#ifdef WD7000_DEBUG
- printk ("wd7000_intr_handle: phantom interrupt...\n");
-#endif
+ dprintk("wd7000_intr_handle: phantom interrupt...\n");
wd7000_intr_ack (host);
return;
}
if (flag & MB_INTR) {
/* The interrupt is for a mailbox */
if (!(flag & IMB_INTR)) {
-#ifdef WD7000_DEBUG
- printk ("wd7000_intr_handle: free outgoing mailbox\n");
-#endif
+ dprintk("wd7000_intr_handle: free outgoing mailbox\n");
/*
* If sleep_on() and the "interrupt on free OGMB" command are
* used in mail_out(), wake_up() should correspondingly be called
icmb = flag & MB_MASK;
icmb_status = icmbs[icmb].status;
if (icmb_status & 0x80) { /* unsolicited - result in ICMB */
-#ifdef WD7000_DEBUG
- printk ("wd7000_intr_handle: unsolicited interrupt 0x%02x\n",
+ dprintk("wd7000_intr_handle: unsolicited interrupt 0x%02x\n",
icmb_status);
-#endif
wd7000_intr_ack (host);
return;
}
wd7000_intr_ack (host);
-#ifdef WD7000_DEBUG
- printk ("wd7000_intr_handle: return from interrupt handler\n");
-#endif
+ dprintk("wd7000_intr_handle: return from interrupt handler\n");
}
void do_wd7000_intr_handle (int irq, void *dev_id, struct pt_regs *regs)
{
unsigned long flags;
+ struct Scsi_Host *host = dev_id;
- spin_lock_irqsave(&io_request_lock, flags);
+ spin_lock_irqsave(&host->host_lock, flags);
wd7000_intr_handle(irq, dev_id, regs);
- spin_unlock_irqrestore(&io_request_lock, flags);
+ spin_unlock_irqrestore(&host->host_lock, flags);
}
idlun = ((SCpnt->target << 5) & 0xe0) | (SCpnt->lun & 7);
SCpnt->scsi_done = done;
SCpnt->SCp.phase = 1;
- scb = alloc_scbs (1);
+ scb = alloc_scbs(SCpnt->host, 1);
scb->idlun = idlun;
memcpy (scb->cdb, cdb, cdblen);
scb->direc = 0x40; /* Disable direction check */
if (SCpnt->host->sg_tablesize == SG_NONE) {
panic ("wd7000_queuecommand: scatter/gather not supported.\n");
}
-#ifdef WD7000_DEBUG
- printk ("Using scatter/gather with %d elements.\n", SCpnt->use_sg);
-#endif
+ dprintk ("Using scatter/gather with %d elements.\n", SCpnt->use_sg);
sgb = scb->sgb;
scb->op = 1;
save_flags (flags);
cli ();
-#ifdef WD7000_DEBUG
- printk ("Buffer = <%.*s>, length = %d\n", length, buffer, length);
-#endif
+ dprintk("Buffer = <%.*s>, length = %d\n", length, buffer, length);
/*
* Currently this is a no-op
*/
- printk ("Sorry, this function is currently out of order...\n");
+ dprintk("Sorry, this function is currently out of order...\n");
restore_flags (flags);
Adapter *host = NULL;
struct Scsi_Host *sh;
-#ifdef WD7000_DEBUG
- printk ("wd7000_detect: started\n");
-#endif
+ dprintk("wd7000_detect: started\n");
#ifdef MODULE
if (wd7000)
* BIOS SIGNATURE has been found.
*/
#ifdef WD7000_DEBUG
- printk ("wd7000_detect: pass %d\n", pass + 1);
+ dprintk("wd7000_detect: pass %d\n", pass + 1);
if (biosaddr_ptr == NUM_ADDRS)
- printk ("WD-7000 SST BIOS not detected...\n");
+ dprintk("WD-7000 SST BIOS not detected...\n");
else
- printk ("WD-7000 SST BIOS detected at 0x%lx: checking...\n",
+ dprintk("WD-7000 SST BIOS detected at 0x%lx: checking...\n",
wd7000_biosaddr[biosaddr_ptr]);
#endif
iobase = configs[pass].iobase;
-#ifdef WD7000_DEBUG
- printk ("wd7000_detect: check IO 0x%x region...\n", iobase);
-#endif
+ dprintk("wd7000_detect: check IO 0x%x region...\n", iobase);
if (request_region (iobase, 4, "wd7000")) {
-#ifdef WD7000_DEBUG
- printk ("wd7000_detect: ASC reset (IO 0x%x) ...", iobase);
-#endif
+ dprintk("wd7000_detect: ASC reset (IO 0x%x) ...", iobase);
/*
* ASC reset...
*/
delay (1);
outb (0, iobase + ASC_CONTROL);
- if (WAIT (iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0))
-#ifdef WD7000_DEBUG
- {
- printk ("failed!\n");
+ if (WAIT (iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) {
+ dprintk("failed!\n");
goto err_release;
- }
- else
- printk ("ok!\n");
-#else
- goto err_release;
-#endif
+ } else
+ dprintk("ok!\n");
if (inb (iobase + ASC_INTR_STAT) == 1) {
/*
host = (Adapter *) sh->hostdata;
-#ifdef WD7000_DEBUG
- printk ("wd7000_detect: adapter allocated at 0x%x\n", (int) host);
-#endif
-
+ dprintk("wd7000_detect: adapter allocated at 0x%x\n",
+ (int)host);
memset (host, 0, sizeof (Adapter));
host->irq = configs[pass].irq;
host->bus_off = configs[pass].bus_off;
host->sh = wd7000_host[host->irq - IRQ_MIN] = sh;
-#ifdef WD7000_DEBUG
- printk ("wd7000_detect: Trying init WD-7000 card at IO "
+ dprintk("wd7000_detect: Trying init WD-7000 card at IO "
"0x%x, IRQ %d, DMA %d...\n",
host->iobase, host->irq, host->dma);
-#endif
if (!wd7000_init (host)) /* Initialization failed */
goto err_unregister;
printk (" BUS_ON time: %dns, BUS_OFF time: %dns\n",
host->bus_on * 125, host->bus_off * 125);
}
- }
-
-#ifdef WD7000_DEBUG
- else
- printk ("wd7000_detect: IO 0x%x region already allocated!\n", iobase);
-#endif
+ } else
+ dprintk("wd7000_detect: IO 0x%x region already allocated!\n",
+ iobase);
continue;
*/
int wd7000_biosparam (Disk *disk, kdev_t dev, int *ip)
{
-#ifdef WD7000_DEBUG
- printk ("wd7000_biosparam: dev=%s, size=%d, ", kdevname (dev), disk->capacity);
-#endif
+ dprintk("wd7000_biosparam: dev=%s, size=%d, ", kdevname(dev),
+ disk->capacity);
/*
* try default translation
ip[2] = info[2];
if (info[0] == 255)
- printk ("wd7000_biosparam: current partition table is using extended translation.\n");
+ printk(KERN_INFO __FUNCTION__ ": current partition table is "
+ "using extended translation.\n");
}
}
-#ifdef WD7000_DEBUG
- printk ("bios geometry: head=%d, sec=%d, cyl=%d\n", ip[0], ip[1], ip[2]);
- printk ("WARNING: check, if the bios geometry is correct.\n");
-#endif
+ dprintk("bios geometry: head=%d, sec=%d, cyl=%d\n", ip[0], ip[1], ip[2]);
+ dprintk("WARNING: check, if the bios geometry is correct.\n");
return (0);
}
static int es1371_open_mixdev(struct inode *inode, struct file *file)
{
- int minor = MINOR(inode->i_rdev);
+ int minor = minor(inode->i_rdev);
struct list_head *list;
struct es1371_state *s;
static int es1371_open(struct inode *inode, struct file *file)
{
- int minor = MINOR(inode->i_rdev);
+ int minor = minor(inode->i_rdev);
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
struct list_head *list;
static int es1371_open_dac(struct inode *inode, struct file *file)
{
- int minor = MINOR(inode->i_rdev);
+ int minor = minor(inode->i_rdev);
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
struct list_head *list;
static int es1371_midi_open(struct inode *inode, struct file *file)
{
- int minor = MINOR(inode->i_rdev);
+ int minor = minor(inode->i_rdev);
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
struct list_head *list;
int soundcore_open(struct inode *inode, struct file *file)
{
int chain;
- int unit=MINOR(inode->i_rdev);
+ int unit = minor(inode->i_rdev);
struct sound_unit *s;
struct file_operations *new_fops = NULL;
}
}
-static int usbin_prepare_desc(struct usbin *u, purb_t urb)
+static int usbin_prepare_desc(struct usbin *u, struct urb *urb)
{
unsigned int i, maxsize, offs;
* return value: 0 if descriptor should be restarted, -1 otherwise
* convert sample format on the fly if necessary
*/
-static int usbin_retire_desc(struct usbin *u, purb_t urb)
+static int usbin_retire_desc(struct usbin *u, struct urb *urb)
{
unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, dmafree;
unsigned char *cp;
/*
* we output sync data
*/
-static int usbin_sync_prepare_desc(struct usbin *u, purb_t urb)
+static int usbin_sync_prepare_desc(struct usbin *u, struct urb *urb)
{
unsigned char *cp = urb->transfer_buffer;
unsigned int i, offs;
/*
* return value: 0 if descriptor should be restarted, -1 otherwise
*/
-static int usbin_sync_retire_desc(struct usbin *u, purb_t urb)
+static int usbin_sync_retire_desc(struct usbin *u, struct urb *urb)
{
unsigned int i;
{
struct usb_device *dev = as->state->usbdev;
struct usbin *u = &as->usbin;
- purb_t urb;
+ struct urb *urb;
unsigned long flags;
unsigned int maxsze, bufsz;
}
}
-static int usbout_prepare_desc(struct usbout *u, purb_t urb)
+static int usbout_prepare_desc(struct usbout *u, struct urb *urb)
{
unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, offs;
unsigned char *cp = urb->transfer_buffer;
/*
* return value: 0 if descriptor should be restarted, -1 otherwise
*/
-static int usbout_retire_desc(struct usbout *u, purb_t urb)
+static int usbout_retire_desc(struct usbout *u, struct urb *urb)
{
unsigned int i;
spin_unlock_irqrestore(&as->lock, flags);
}
-static int usbout_sync_prepare_desc(struct usbout *u, purb_t urb)
+static int usbout_sync_prepare_desc(struct usbout *u, struct urb *urb)
{
unsigned int i, offs;
/*
* return value: 0 if descriptor should be restarted, -1 otherwise
*/
-static int usbout_sync_retire_desc(struct usbout *u, purb_t urb)
+static int usbout_sync_retire_desc(struct usbout *u, struct urb *urb)
{
unsigned char *cp = urb->transfer_buffer;
unsigned int f, i;
{
struct usb_device *dev = as->state->usbdev;
struct usbout *u = &as->usbout;
- purb_t urb;
+ struct urb *urb;
unsigned long flags;
unsigned int maxsze, bufsz;
}
/*-------------------------------------------------------------------*/
#ifdef DEBUG
-static void dump_urb (purb_t purb)
+static void dump_urb (struct urb *urb)
{
- dbg("urb :%p", purb);
- dbg("next :%p", purb->next);
- dbg("dev :%p", purb->dev);
- dbg("pipe :%08X", purb->pipe);
- dbg("status :%d", purb->status);
- dbg("transfer_flags :%08X", purb->transfer_flags);
- dbg("transfer_buffer :%p", purb->transfer_buffer);
- dbg("transfer_buffer_length:%d", purb->transfer_buffer_length);
- dbg("actual_length :%d", purb->actual_length);
- dbg("setup_packet :%p", purb->setup_packet);
- dbg("start_frame :%d", purb->start_frame);
- dbg("number_of_packets :%d", purb->number_of_packets);
- dbg("interval :%d", purb->interval);
- dbg("error_count :%d", purb->error_count);
- dbg("context :%p", purb->context);
- dbg("complete :%p", purb->complete);
+ dbg("urb :%p", urb);
+ dbg("next :%p", urb->next);
+ dbg("dev :%p", urb->dev);
+ dbg("pipe :%08X", urb->pipe);
+ dbg("status :%d", urb->status);
+ dbg("transfer_flags :%08X", urb->transfer_flags);
+ dbg("transfer_buffer :%p", urb->transfer_buffer);
+ dbg("transfer_buffer_length:%d", urb->transfer_buffer_length);
+ dbg("actual_length :%d", urb->actual_length);
+ dbg("setup_packet :%p", urb->setup_packet);
+ dbg("start_frame :%d", urb->start_frame);
+ dbg("number_of_packets :%d", urb->number_of_packets);
+ dbg("interval :%d", urb->interval);
+ dbg("error_count :%d", urb->error_count);
+ dbg("context :%p", urb->context);
+ dbg("complete :%p", urb->complete);
}
#endif
/*-------------------------------------------------------------------*/
return 0;
}
/*-------------------------------------------------------------------*/
-static void dabusb_iso_complete (purb_t purb)
+static void dabusb_iso_complete (struct urb *purb)
{
pbuff_t b = purb->context;
pdabusb_t s = b->s;
int rem;
int cnt;
pbuff_t b;
- purb_t purb = NULL;
+ struct urb *purb = NULL;
dbg("dabusb_read");
typedef struct
{
pdabusb_t s;
- purb_t purb;
+ struct urb *purb;
struct list_head buff_list;
} buff_t,*pbuff_t;
return NULL;
}
-static void async_completed(purb_t urb)
+static void async_completed(struct urb *urb)
{
struct async *as = (struct async *)urb->context;
struct dev_state *ps = as->ps;
*/
lock_kernel();
ret = -ENOENT;
- if (ITYPE(inode->i_ino) != IDEVICE)
- goto out;
- dev = inode->u.usbdev_i.p.dev;
+ dev = inode->u.generic_ip;
if (!dev)
goto out;
ret = -ENOMEM;
--- /dev/null
+/*
+ * Copyright (c) 2001 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/uts.h> /* for UTS_SYSNAME */
+
+#ifndef CONFIG_USB_DEBUG
+ #define CONFIG_USB_DEBUG /* this is experimental! */
+#endif
+
+#ifdef CONFIG_USB_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
+
+#include <linux/usb.h>
+#include "hcd.h"
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * USB Host Controller Driver framework
+ *
+ * Plugs into usbcore (usb_bus) and lets HCDs share code, minimizing
+ * HCD-specific behaviors/bugs.
+ *
+ * This does error checks, tracks devices and urbs, and delegates to a
+ * "hc_driver" only for code (and data) that really needs to know about
+ * hardware differences. That includes root hub registers, i/o queues,
+ * and so on ... but as little else as possible.
+ *
+ * Shared code includes most of the "root hub" code (these are emulated,
+ * though each HC's hardware works differently) and PCI glue, plus request
+ * tracking overhead. The HCD code should only block on spinlocks or on
+ * hardware handshaking; blocking on software events (such as other kernel
+ * threads releasing resources, or completing actions) is all generic.
+ *
+ * Happens the USB 2.0 spec says this would be invisible inside the "USBD",
+ * and includes mostly a "HCDI" (HCD Interface) along with some APIs used
+ * only by the hub driver ... and that neither should be seen or used by
+ * usb client device drivers.
+ *
+ * Contributors of ideas or unattributed patches include: David Brownell,
+ * Roman Weissgaerber, Rory Bolt, ...
+ *
+ * HISTORY:
+ * 2001-12-12 Initial patch version for Linux 2.5.1 kernel.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* host controllers we manage */
+static LIST_HEAD (hcd_list);
+
+/* used when updating list of hcds */
+static DECLARE_MUTEX (hcd_list_lock);
+
+/* used when updating hcd data */
+static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED;
+
+static struct usb_operations hcd_operations;
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Sharable chunks of root hub code.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* usb 2.0 root hub device descriptor */
+static const u8 usb2_rh_dev_descriptor [18] = {
+ 0x12, /* __u8 bLength; */
+ 0x01, /* __u8 bDescriptorType; Device */
+ 0x00, 0x02, /* __u16 bcdUSB; v2.0 */
+
+ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 bDeviceSubClass; */
+ 0x01, /* __u8 bDeviceProtocol; [ usb 2.0 single TT ]*/
+ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
+
+ 0x00, 0x00, /* __u16 idVendor; */
+ 0x00, 0x00, /* __u16 idProduct; */
+ 0x40, 0x02, /* __u16 bcdDevice; (v2.4) */
+
+ 0x03, /* __u8 iManufacturer; */
+ 0x02, /* __u8 iProduct; */
+ 0x01, /* __u8 iSerialNumber; */
+ 0x01 /* __u8 bNumConfigurations; */
+};
+
+/* no usb 2.0 root hub "device qualifier" descriptor: one speed only */
+
+/* usb 1.1 root hub device descriptor */
+static const u8 usb11_rh_dev_descriptor [18] = {
+ 0x12, /* __u8 bLength; */
+ 0x01, /* __u8 bDescriptorType; Device */
+ 0x10, 0x01, /* __u16 bcdUSB; v1.1 */
+
+ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 bDeviceSubClass; */
+ 0x00, /* __u8 bDeviceProtocol; [ low/full speeds only ] */
+ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
+
+ 0x00, 0x00, /* __u16 idVendor; */
+ 0x00, 0x00, /* __u16 idProduct; */
+ 0x40, 0x02, /* __u16 bcdDevice; (v2.4) */
+
+ 0x03, /* __u8 iManufacturer; */
+ 0x02, /* __u8 iProduct; */
+ 0x01, /* __u8 iSerialNumber; */
+ 0x01 /* __u8 bNumConfigurations; */
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Configuration descriptor for all our root hubs */
+
+static const u8 rh_config_descriptor [] = {
+
+ /* one configuration */
+ 0x09, /* __u8 bLength; */
+ 0x02, /* __u8 bDescriptorType; Configuration */
+ 0x19, 0x00, /* __u16 wTotalLength; */
+ 0x01, /* __u8 bNumInterfaces; (1) */
+ 0x01, /* __u8 bConfigurationValue; */
+ 0x00, /* __u8 iConfiguration; */
+ 0x40, /* __u8 bmAttributes;
+ Bit 7: Bus-powered,
+ 6: Self-powered,
+ 5 Remote-wakwup,
+ 4..0: resvd */
+ 0x00, /* __u8 MaxPower; */
+
+ /* USB 1.1:
+ * USB 2.0, single TT organization (mandatory):
+ * one interface, protocol 0
+ *
+ * USB 2.0, multiple TT organization (optional):
+ * two interfaces, protocols 1 (like single TT)
+ * and 2 (multiple TT mode) ... config is
+ * sometimes settable
+ * NOT IMPLEMENTED
+ */
+
+ /* one interface */
+ 0x09, /* __u8 if_bLength; */
+ 0x04, /* __u8 if_bDescriptorType; Interface */
+ 0x00, /* __u8 if_bInterfaceNumber; */
+ 0x00, /* __u8 if_bAlternateSetting; */
+ 0x01, /* __u8 if_bNumEndpoints; */
+ 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 if_bInterfaceSubClass; */
+ 0x00, /* __u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
+ 0x00, /* __u8 if_iInterface; */
+
+ /* one endpoint (status change endpoint) */
+ 0x07, /* __u8 ep_bLength; */
+ 0x05, /* __u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* __u8 ep_bmAttributes; Interrupt */
+ 0x02, 0x00, /* __u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
+ 0x0c /* __u8 ep_bInterval; (12ms -- usb 2.0 spec) */
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * helper routine for returning string descriptors in UTF-16LE
+ * input can actually be ISO-8859-1; ASCII is its 7-bit subset
+ */
+static int ascii2utf (char *ascii, u8 *utf, int utfmax)
+{
+ int retval;
+
+ for (retval = 0; *ascii && utfmax > 1; utfmax -= 2, retval += 2) {
+ *utf++ = *ascii++ & 0x7f;
+ *utf++ = 0;
+ }
+ return retval;
+}
+
+/*
+ * rh_string - provides manufacturer, product and serial strings for root hub
+ * @id: the string ID number (1: serial number, 2: product, 3: vendor)
+ * @pci_desc: PCI device descriptor for the relevant HC
+ * @type: string describing our driver
+ * @data: return packet in UTF-16 LE
+ * @len: length of the return packet
+ *
+ * Produces either a manufacturer, product or serial number string for the
+ * virtual root hub device.
+ */
+static int rh_string (
+ int id,
+ struct pci_dev *pci_desc,
+ char *type,
+ u8 *data,
+ int len
+) {
+ char buf [100];
+
+ // language ids
+ if (id == 0) {
+ *data++ = 4; *data++ = 3; /* 4 bytes string data */
+ *data++ = 0; *data++ = 0; /* some language id */
+ return 4;
+
+ // serial number
+ } else if (id == 1) {
+ strcpy (buf, pci_desc->slot_name);
+
+ // product description
+ } else if (id == 2) {
+ strcpy (buf, pci_desc->name);
+
+ // id 3 == vendor description
+ } else if (id == 3) {
+ sprintf (buf, "%s %s %s", UTS_SYSNAME, UTS_RELEASE, type);
+
+ // unsupported IDs --> "protocol stall"
+ } else
+ return 0;
+
+ data [0] = 2 + ascii2utf (buf, data + 2, len - 2);
+ data [1] = 3; /* type == string */
+ return data [0];
+}
+
+
+/* Root hub control transfers execute synchronously */
+static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
+{
+ devrequest *cmd = (devrequest *) urb->setup_packet;
+ u16 typeReq, wValue, wIndex, wLength;
+ const u8 *bufp = 0;
+ u8 *ubuf = urb->transfer_buffer;
+ int len = 0;
+
+ typeReq = (cmd->requesttype << 8) | cmd->request;
+ wValue = le16_to_cpu (cmd->value);
+ wIndex = le16_to_cpu (cmd->index);
+ wLength = le16_to_cpu (cmd->length);
+
+ if (wLength > urb->transfer_buffer_length)
+ goto error;
+
+ /* set up for success */
+ urb->status = 0;
+ urb->actual_length = wLength;
+ switch (typeReq) {
+
+ /* DEVICE REQUESTS */
+
+ case DeviceRequest | USB_REQ_GET_STATUS:
+ // DEVICE_REMOTE_WAKEUP
+ ubuf [0] = 1; // selfpowered
+ ubuf [1] = 0;
+ /* FALLTHROUGH */
+ case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+ case DeviceOutRequest | USB_REQ_SET_FEATURE:
+ dbg ("no device features yet yet");
+ break;
+ case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+ ubuf [0] = 1;
+ /* FALLTHROUGH */
+ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+ break;
+ case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+ switch (wValue & 0xff00) {
+ case USB_DT_DEVICE << 8:
+ if (hcd->driver->flags & HCD_USB2)
+ bufp = usb2_rh_dev_descriptor;
+ else if (hcd->driver->flags & HCD_USB11)
+ bufp = usb11_rh_dev_descriptor;
+ else
+ goto error;
+ len = 18;
+ break;
+ case USB_DT_CONFIG << 8:
+ bufp = rh_config_descriptor;
+ len = sizeof rh_config_descriptor;
+ break;
+ case USB_DT_STRING << 8:
+ urb->actual_length = rh_string (
+ wValue & 0xff,
+ hcd->pdev,
+ (char *) hcd->description,
+ ubuf, wLength);
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case DeviceRequest | USB_REQ_GET_INTERFACE:
+ ubuf [0] = 0;
+ /* FALLTHROUGH */
+ case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+ break;
+ case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+ // wValue == urb->dev->devaddr
+ dbg ("%s root hub device address %d",
+ hcd->bus_name, wValue);
+ break;
+
+ /* INTERFACE REQUESTS (no defined feature/status flags) */
+
+ /* ENDPOINT REQUESTS */
+
+ case EndpointRequest | USB_REQ_GET_STATUS:
+ // ENDPOINT_HALT flag
+ ubuf [0] = 0;
+ ubuf [1] = 0;
+ /* FALLTHROUGH */
+ case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+ case EndpointOutRequest | USB_REQ_SET_FEATURE:
+ dbg ("no endpoint features yet");
+ break;
+
+ /* CLASS REQUESTS (and errors) */
+
+ default:
+ /* non-generic request */
+ urb->status = hcd->driver->hub_control (hcd,
+ typeReq, wValue, wIndex,
+ ubuf, wLength);
+ break;
+error:
+ /* "protocol stall" on error */
+ urb->status = -EPIPE;
+ dbg ("unsupported hub control message (maxchild %d)",
+ urb->dev->maxchild);
+ }
+ if (urb->status) {
+ urb->actual_length = 0;
+ dbg ("CTRL: TypeReq=0x%x val=0x%x idx=0x%x len=%d ==> %d",
+ typeReq, wValue, wIndex, wLength, urb->status);
+ }
+ if (bufp) {
+ if (urb->transfer_buffer_length < len)
+ len = urb->transfer_buffer_length;
+ urb->actual_length = len;
+ // always USB_DIR_IN, toward host
+ memcpy (ubuf, bufp, len);
+ }
+
+ /* any errors get returned through the urb completion */
+ usb_hcd_giveback_urb (hcd, urb);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Root Hub interrupt transfers are synthesized with a timer.
+ * Completions are called in_interrupt() but not in_irq().
+ */
+
+static void rh_report_status (unsigned long ptr);
+
+static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb)
+{
+ int len = 1 + (urb->dev->maxchild / 8);
+
+ /* rh_timer protected by hcd_data_lock */
+ if (timer_pending (&hcd->rh_timer)
+ || urb->status != -EINPROGRESS
+ || !HCD_IS_RUNNING (hcd->state)
+ || urb->transfer_buffer_length < len) {
+ dbg ("not queuing status urb, stat %d", urb->status);
+ return -EINVAL;
+ }
+
+ urb->hcpriv = hcd; /* nonzero to indicate it's queued */
+ init_timer (&hcd->rh_timer);
+ hcd->rh_timer.function = rh_report_status;
+ hcd->rh_timer.data = (unsigned long) urb;
+ hcd->rh_timer.expires = jiffies
+ + (HZ * (urb->interval < 30
+ ? 30
+ : urb->interval)) / 1000;
+ add_timer (&hcd->rh_timer);
+ return 0;
+}
+
+/* timer callback */
+
+static void rh_report_status (unsigned long ptr)
+{
+ struct urb *urb;
+ struct usb_hcd *hcd;
+ int length;
+ unsigned long flags;
+
+ urb = (struct urb *) ptr;
+ spin_lock_irqsave (&urb->lock, flags);
+ if (!urb->dev) {
+ spin_unlock_irqrestore (&urb->lock, flags);
+ return;
+ }
+
+ hcd = urb->dev->bus->hcpriv;
+ if (urb->status == -EINPROGRESS) {
+ if (HCD_IS_RUNNING (hcd->state)) {
+ length = hcd->driver->hub_status_data (hcd,
+ urb->transfer_buffer);
+ spin_unlock_irqrestore (&urb->lock, flags);
+ if (length > 0) {
+ urb->actual_length = length;
+ urb->status = 0;
+ urb->complete (urb);
+ }
+ spin_lock_irqsave (&hcd_data_lock, flags);
+ urb->status = -EINPROGRESS;
+ if (HCD_IS_RUNNING (hcd->state)
+ && rh_status_urb (hcd, urb) != 0) {
+ /* another driver snuck in? */
+ dbg ("%s, can't resubmit roothub status urb?",
+ hcd->bus_name);
+ spin_unlock_irqrestore (&hcd_data_lock, flags);
+ BUG ();
+ }
+ spin_unlock_irqrestore (&hcd_data_lock, flags);
+ } else
+ spin_unlock_irqrestore (&urb->lock, flags);
+ } else {
+ /* this urb's been unlinked */
+ urb->hcpriv = 0;
+ spin_unlock_irqrestore (&urb->lock, flags);
+
+ usb_hcd_giveback_urb (hcd, urb);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
+{
+ if (usb_pipeint (urb->pipe)) {
+ int retval;
+ unsigned long flags;
+
+ spin_lock_irqsave (&hcd_data_lock, flags);
+ retval = rh_status_urb (hcd, urb);
+ spin_unlock_irqrestore (&hcd_data_lock, flags);
+ return retval;
+ }
+ if (usb_pipecontrol (urb->pipe))
+ return rh_call_control (hcd, urb);
+ else
+ return -EINVAL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave (&hcd_data_lock, flags);
+ del_timer_sync (&hcd->rh_timer);
+ hcd->rh_timer.data = 0;
+ spin_unlock_irqrestore (&hcd_data_lock, flags);
+
+ /* we rely on RH callback code not unlinking its URB! */
+ usb_hcd_giveback_urb (hcd, urb);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PCI
+
+/* PCI-based HCs are normal, but custom bus glue should be ok */
+
+static void hcd_irq (int irq, void *__hcd, struct pt_regs *r);
+static void hc_died (struct usb_hcd *hcd);
+
+/*-------------------------------------------------------------------------*/
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+/**
+ * usb_hcd_pci_probe - initialize PCI-based HCDs
+ * @dev: USB Host Controller being probed
+ * @id: pci hotplug id connecting controller to HCD framework
+ *
+ * Allocates basic PCI resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ * Store this function in the HCD's struct pci_driver as probe().
+ */
+int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
+{
+ struct hc_driver *driver;
+ unsigned long resource, len;
+ void *base;
+ u8 latency, limit;
+ struct usb_bus *bus;
+ struct usb_hcd *hcd;
+ int retval, region;
+ char buf [8], *bufp = buf;
+
+ if (!id || !(driver = (struct hc_driver *) id->driver_data))
+ return -EINVAL;
+
+ if (pci_enable_device (dev) < 0)
+ return -ENODEV;
+
+ if (!dev->irq) {
+ err ("Found HC with no IRQ. Check BIOS/PCI %s setup!",
+ dev->slot_name);
+ return -ENODEV;
+ }
+
+ if (driver->flags & HCD_MEMORY) { // EHCI, OHCI
+ region = 0;
+ resource = pci_resource_start (dev, 0);
+ len = pci_resource_len (dev, 0);
+ if (!request_mem_region (resource, len, driver->description)) {
+ dbg ("controller already in use");
+ return -EBUSY;
+ }
+ base = ioremap_nocache (resource, len);
+ if (base == NULL) {
+ dbg ("error mapping memory");
+ retval = -EFAULT;
+clean_1:
+ release_mem_region (resource, len);
+ err ("init %s fail, %d", dev->slot_name, retval);
+ return retval;
+ }
+
+ } else { // UHCI
+ resource = len = 0;
+ for (region = 0; region < PCI_ROM_RESOURCE; region++) {
+ if (!(pci_resource_flags (dev, region) & IORESOURCE_IO))
+ continue;
+
+ resource = pci_resource_start (dev, region);
+ len = pci_resource_len (dev, region);
+ if (request_region (resource, len,
+ driver->description))
+ break;
+ }
+ if (region == PCI_ROM_RESOURCE) {
+ dbg ("no i/o regions available");
+ return -EBUSY;
+ }
+ base = (void *) resource;
+ }
+
+ // driver->start(), later on, will transfer device from
+ // control by SMM/BIOS to control by Linux (if needed)
+
+ pci_set_master (dev);
+ hcd = driver->hcd_alloc ();
+ if (hcd == NULL){
+ dbg ("hcd alloc fail");
+ retval = -ENOMEM;
+clean_2:
+ if (driver->flags & HCD_MEMORY) {
+ iounmap (base);
+ goto clean_1;
+ } else {
+ release_region (resource, len);
+ err ("init %s fail, %d", dev->slot_name, retval);
+ return retval;
+ }
+ }
+ dev->driver_data = hcd;
+ hcd->driver = driver;
+ hcd->description = driver->description;
+ hcd->pdev = dev;
+ info ("%s @ %s, %s", hcd->description, dev->slot_name, dev->name);
+
+ pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency);
+ if (latency) {
+ pci_read_config_byte (dev, PCI_MAX_LAT, &limit);
+ if (limit && limit < latency) {
+ dbg ("PCI latency reduced to max %d", limit);
+ pci_write_config_byte (dev, PCI_LATENCY_TIMER, limit);
+ }
+ }
+
+#ifndef __sparc__
+ sprintf (buf, "%d", dev->irq);
+#else
+ bufp = __irq_itoa(irq);
+#endif
+ if (request_irq (dev->irq, hcd_irq, SA_SHIRQ, hcd->description, hcd)
+ != 0) {
+ err ("request interrupt %s failed", bufp);
+ retval = -EBUSY;
+clean_3:
+ driver->hcd_free (hcd);
+ goto clean_2;
+ }
+ hcd->irq = dev->irq;
+
+ hcd->regs = base;
+ hcd->region = region;
+ info ("irq %s, %s %p", bufp,
+ (driver->flags & HCD_MEMORY) ? "pci mem" : "io base",
+ base);
+
+// FIXME simpler: make "bus" be that data, not pointer to it.
+ bus = usb_alloc_bus (&hcd_operations);
+ if (bus == NULL) {
+ dbg ("usb_alloc_bus fail");
+ retval = -ENOMEM;
+ free_irq (dev->irq, hcd);
+ goto clean_3;
+ }
+ hcd->bus = bus;
+ hcd->bus_name = dev->slot_name;
+ bus->hcpriv = (void *) hcd;
+
+ INIT_LIST_HEAD (&hcd->dev_list);
+ INIT_LIST_HEAD (&hcd->hcd_list);
+
+ down (&hcd_list_lock);
+ list_add (&hcd->hcd_list, &hcd_list);
+ up (&hcd_list_lock);
+
+ usb_register_bus (bus);
+
+ if ((retval = driver->start (hcd)) < 0)
+ usb_hcd_pci_remove (dev);
+
+ return retval;
+}
+EXPORT_SYMBOL (usb_hcd_pci_probe);
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_pci_remove - shutdown processing for PCI-based HCDs
+ * @dev: USB Host Controller being removed
+ *
+ * Reverses the effect of usb_hcd_pci_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ * Store this function in the HCD's struct pci_driver as remove().
+ */
+void usb_hcd_pci_remove (struct pci_dev *dev)
+{
+ struct usb_hcd *hcd;
+ struct usb_device *hub;
+
+ hcd = (struct usb_hcd *) dev->driver_data;
+ if (!hcd)
+ return;
+ info ("remove: %s, state %x", hcd->bus_name, hcd->state);
+
+ if (in_interrupt ()) BUG ();
+
+ hub = hcd->bus->root_hub;
+ hcd->state = USB_STATE_QUIESCING;
+
+ dbg ("%s: roothub graceful disconnect", hcd->bus_name);
+ usb_disconnect (&hub);
+ // usb_disconnect (&hcd->bus->root_hub);
+
+ hcd->driver->stop (hcd);
+ hcd->state = USB_STATE_HALT;
+
+ free_irq (hcd->irq, hcd);
+ if (hcd->driver->flags & HCD_MEMORY) {
+ iounmap (hcd->regs);
+ release_mem_region (pci_resource_start (dev, 0),
+ pci_resource_len (dev, 0));
+ } else {
+ release_region (pci_resource_start (dev, hcd->region),
+ pci_resource_len (dev, hcd->region));
+ }
+
+ down (&hcd_list_lock);
+ list_del (&hcd->hcd_list);
+ up (&hcd_list_lock);
+
+ usb_deregister_bus (hcd->bus);
+ usb_free_bus (hcd->bus);
+ hcd->bus = NULL;
+
+ hcd->driver->hcd_free (hcd);
+}
+EXPORT_SYMBOL (usb_hcd_pci_remove);
+
+
+#ifdef CONFIG_PM
+
+/*
+ * Some "sleep" power levels imply updating struct usb_driver
+ * to include a callback asking hcds to do their bit by checking
+ * if all the drivers can suspend. Gets involved with remote wakeup.
+ *
+ * If there are pending urbs, then HCs will need to access memory,
+ * causing extra power drain. New sleep()/wakeup() PM calls might
+ * be needed, beyond PCI suspend()/resume(). The root hub timer
+ * still be accessing memory though ...
+ *
+ * FIXME: USB should have some power budgeting support working with
+ * all kinds of hubs.
+ *
+ * FIXME: This assumes only D0->D3 suspend and D3->D0 resume.
+ * D1 and D2 states should do something, yes?
+ *
+ * FIXME: Should provide generic enable_wake(), calling pci_enable_wake()
+ * for all supported states, so that USB remote wakeup can work for any
+ * devices that support it (and are connected via powered hubs).
+ *
+ * FIXME: resume doesn't seem to work right any more...
+ */
+
+
+// 2.4 kernels have issued concurrent resumes (w/APM)
+// we defend against that error; PCI doesn't yet.
+
+/**
+ * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
+ * @dev: USB Host Controller being suspended
+ *
+ * Store this function in the HCD's struct pci_driver as suspend().
+ */
+int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state)
+{
+ struct usb_hcd *hcd;
+ int retval;
+
+ hcd = (struct usb_hcd *) dev->driver_data;
+ info ("suspend %s to state %d", hcd->bus_name, state);
+
+ pci_save_state (dev, hcd->pci_state);
+
+ // FIXME for all connected devices, leaf-to-root:
+ // driver->suspend()
+ // proposed "new 2.5 driver model" will automate that
+
+ /* driver may want to disable DMA etc */
+ retval = hcd->driver->suspend (hcd, state);
+ hcd->state = USB_STATE_SUSPENDED;
+
+ pci_set_power_state (dev, state);
+ return retval;
+}
+EXPORT_SYMBOL (usb_hcd_pci_suspend);
+
+/**
+ * usb_hcd_pci_resume - power management resume of a PCI-based HCD
+ * @dev: USB Host Controller being resumed
+ *
+ * Store this function in the HCD's struct pci_driver as resume().
+ */
+int usb_hcd_pci_resume (struct pci_dev *dev)
+{
+ struct usb_hcd *hcd;
+ int retval;
+
+ hcd = (struct usb_hcd *) dev->driver_data;
+ info ("resume %s", hcd->bus_name);
+
+ /* guard against multiple resumes (APM bug?) */
+ atomic_inc (&hcd->resume_count);
+ if (atomic_read (&hcd->resume_count) != 1) {
+ err ("concurrent PCI resumes for %s", hcd->bus_name);
+ retval = 0;
+ goto done;
+ }
+
+ retval = -EBUSY;
+ if (hcd->state != USB_STATE_SUSPENDED) {
+ dbg ("can't resume, not suspended!");
+ goto done;
+ }
+ hcd->state = USB_STATE_RESUMING;
+
+ pci_set_power_state (dev, 0);
+ pci_restore_state (dev, hcd->pci_state);
+
+ retval = hcd->driver->resume (hcd);
+ if (!HCD_IS_RUNNING (hcd->state)) {
+ dbg ("resume %s failure, retval %d", hcd->bus_name, retval);
+ hc_died (hcd);
+// FIXME: recover, reset etc.
+ } else {
+ // FIXME for all connected devices, root-to-leaf:
+ // driver->resume ();
+ // proposed "new 2.5 driver model" will automate that
+ }
+
+done:
+ atomic_dec (&hcd->resume_count);
+ return retval;
+}
+EXPORT_SYMBOL (usb_hcd_pci_resume);
+
+#endif /* CONFIG_PM */
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Generic HC operations.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* called from khubd, or root hub init threads for hcd-private init */
+static int hcd_alloc_dev (struct usb_device *udev)
+{
+ struct hcd_dev *dev;
+ struct usb_hcd *hcd;
+ unsigned long flags;
+
+ if (!udev || udev->hcpriv)
+ return -EINVAL;
+ if (!udev->bus || !udev->bus->hcpriv)
+ return -ENODEV;
+ hcd = udev->bus->hcpriv;
+ if (hcd->state == USB_STATE_QUIESCING)
+ return -ENOLINK;
+
+ dev = (struct hcd_dev *) kmalloc (sizeof *dev, GFP_KERNEL);
+ if (dev == NULL)
+ return -ENOMEM;
+ memset (dev, 0, sizeof *dev);
+
+ INIT_LIST_HEAD (&dev->dev_list);
+ INIT_LIST_HEAD (&dev->urb_list);
+
+ spin_lock_irqsave (&hcd_data_lock, flags);
+ list_add (&dev->dev_list, &hcd->dev_list);
+ // refcount is implicit
+ udev->hcpriv = dev;
+ spin_unlock_irqrestore (&hcd_data_lock, flags);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void hc_died (struct usb_hcd *hcd)
+{
+ struct list_head *devlist, *urblist;
+ struct hcd_dev *dev;
+ struct urb *urb;
+ unsigned long flags;
+
+ /* flag every pending urb as done */
+ spin_lock_irqsave (&hcd_data_lock, flags);
+ list_for_each (devlist, &hcd->dev_list) {
+ dev = list_entry (devlist, struct hcd_dev, dev_list);
+ list_for_each (urblist, &dev->urb_list) {
+ urb = list_entry (urblist, struct urb, urb_list);
+ dbg ("shutdown %s urb %p pipe %x, current status %d",
+ hcd->bus_name, urb, urb->pipe, urb->status);
+ if (urb->status == -EINPROGRESS)
+ urb->status = -ESHUTDOWN;
+ }
+ }
+ urb = (struct urb *) hcd->rh_timer.data;
+ if (urb)
+ urb->status = -ESHUTDOWN;
+ spin_unlock_irqrestore (&hcd_data_lock, flags);
+
+ if (urb)
+ rh_status_dequeue (hcd, urb);
+ hcd->driver->stop (hcd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* may be called in any context with a valid urb->dev usecount */
+/* caller surrenders "ownership" of urb (and chain at urb->next). */
+
+static int hcd_submit_urb (struct urb *urb)
+{
+ int status;
+ struct usb_hcd *hcd;
+ struct hcd_dev *dev;
+ unsigned long flags;
+ int pipe;
+ int mem_flags;
+
+ if (!urb || urb->hcpriv || !urb->complete)
+ return -EINVAL;
+
+ urb->status = -EINPROGRESS;
+ urb->actual_length = 0;
+ INIT_LIST_HEAD (&urb->urb_list);
+
+ if (!urb->dev || !urb->dev->bus || urb->dev->devnum <= 0)
+ return -ENODEV;
+ hcd = urb->dev->bus->hcpriv;
+ dev = urb->dev->hcpriv;
+ if (!hcd || !dev)
+ return -ENODEV;
+
+ /* can't submit new urbs when quiescing, halted, ... */
+ if (hcd->state == USB_STATE_QUIESCING || !HCD_IS_RUNNING (hcd->state))
+ return -ESHUTDOWN;
+ pipe = urb->pipe;
+ if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe),
+ usb_pipeout (pipe)))
+ return -EPIPE;
+
+ // FIXME paging/swapping requests over USB should not use GFP_KERNEL
+ // and might even need to use GFP_NOIO ... that flag actually needs
+ // to be passed from the higher level.
+ mem_flags = in_interrupt () ? GFP_ATOMIC : GFP_KERNEL;
+
+#ifdef DEBUG
+ {
+ unsigned int orig_flags = urb->transfer_flags;
+ unsigned int allowed;
+
+ /* enforce simple/standard policy */
+ allowed = USB_ASYNC_UNLINK; // affects later unlinks
+ allowed |= USB_NO_FSBR; // only affects UHCI
+ switch (usb_pipetype (pipe)) {
+ case PIPE_CONTROL:
+ allowed |= USB_DISABLE_SPD;
+ break;
+ case PIPE_BULK:
+ allowed |= USB_DISABLE_SPD | USB_QUEUE_BULK
+ | USB_ZERO_PACKET | URB_NO_INTERRUPT;
+ break;
+ case PIPE_INTERRUPT:
+ allowed |= USB_DISABLE_SPD;
+ break;
+ case PIPE_ISOCHRONOUS:
+ allowed |= USB_ISO_ASAP;
+ break;
+ }
+ urb->transfer_flags &= allowed;
+
+ /* warn if submitter gave bogus flags */
+ if (urb->transfer_flags != orig_flags)
+ warn ("BOGUS urb flags, %x --> %x",
+ orig_flags, urb->transfer_flags);
+ }
+#endif
+ /*
+ * FIXME: alloc periodic bandwidth here, for interrupt and iso?
+ * Need to look at the ring submit mechanism for iso tds ... they
+ * aren't actually "periodic" in 2.4 kernels.
+ *
+ * FIXME: make urb timeouts be generic, keeping the HCD cores
+ * as simple as possible.
+ */
+
+ // NOTE: a generic device/urb monitoring hook would go here.
+ // hcd_monitor_hook(MONITOR_URB_SUBMIT, urb)
+ // It would catch submission paths for all urbs.
+
+ /*
+ * Atomically queue the urb, first to our records, then to the HCD.
+ * Access to urb->status is controlled by urb->lock ... changes on
+ * i/o completion (normal or fault) or unlinking.
+ */
+
+ // FIXME: verify that quiescing hc works right (RH cleans up)
+
+ spin_lock_irqsave (&hcd_data_lock, flags);
+ if (HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_QUIESCING) {
+ usb_inc_dev_use (urb->dev);
+ list_add (&urb->urb_list, &dev->urb_list);
+ status = 0;
+ } else {
+ INIT_LIST_HEAD (&urb->urb_list);
+ status = -ESHUTDOWN;
+ }
+ spin_unlock_irqrestore (&hcd_data_lock, flags);
+
+ if (!status) {
+ if (urb->dev == hcd->bus->root_hub)
+ status = rh_urb_enqueue (hcd, urb);
+ else
+ status = hcd->driver->urb_enqueue (hcd, urb, mem_flags);
+ }
+ if (status) {
+ if (urb->dev) {
+ urb->status = status;
+ usb_hcd_giveback_urb (hcd, urb);
+ }
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* called in any context */
+static int hcd_get_frame_number (struct usb_device *udev)
+{
+ struct usb_hcd *hcd = (struct usb_hcd *)udev->bus->hcpriv;
+ return hcd->driver->get_frame_number (hcd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+struct completion_splice { // modified urb context:
+ /* did we complete? */
+ int done;
+
+ /* original urb data */
+ void (*complete)(struct urb *);
+ void *context;
+};
+
+static void unlink_complete (struct urb *urb)
+{
+ struct completion_splice *splice;
+
+ splice = (struct completion_splice *) urb->context;
+
+ /* issue original completion call */
+ urb->complete = splice->complete;
+ urb->context = splice->context;
+ urb->complete (urb);
+
+ splice->done = 1;
+}
+
+/*
+ * called in any context; note ASYNC_UNLINK restrictions
+ *
+ * caller guarantees urb won't be recycled till both unlink()
+ * and the urb's completion function return
+ */
+static int hcd_unlink_urb (struct urb *urb)
+{
+ struct hcd_dev *dev;
+ struct usb_hcd *hcd = 0;
+ unsigned long flags;
+ struct completion_splice splice;
+ int retval;
+
+ if (!urb)
+ return -EINVAL;
+
+ // FIXME: add some explicit records to flag the
+ // state where the URB is "in periodic completion".
+ // Workaround is for driver to set the urb status
+ // to "-EINPROGRESS", so it can get through here
+ // and unlink from the completion handler.
+
+ /*
+ * we contend for urb->status with the hcd core,
+ * which changes it while returning the urb.
+ */
+ spin_lock_irqsave (&urb->lock, flags);
+ if (!urb->hcpriv
+ || urb->status != -EINPROGRESS
+ || urb->transfer_flags & USB_TIMEOUT_KILLED) {
+ retval = -EINVAL;
+ goto done;
+ }
+
+ if (!urb->dev || !urb->dev->bus) {
+ retval = -ENODEV;
+ goto done;
+ }
+ dev = urb->dev->hcpriv;
+ hcd = urb->dev->bus->hcpriv;
+ if (!dev || !hcd) {
+ retval = -ENODEV;
+ goto done;
+ }
+
+ /* maybe set up to block on completion notification */
+ if ((urb->transfer_flags & USB_TIMEOUT_KILLED))
+ urb->status = -ETIMEDOUT;
+ else if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) {
+ if (in_interrupt ()) {
+ dbg ("non-async unlink in_interrupt");
+ retval = -EWOULDBLOCK;
+ goto done;
+ }
+ /* synchronous unlink: block till we see the completion */
+ splice.done = 0;
+ splice.complete = urb->complete;
+ splice.context = urb->context;
+ urb->complete = unlink_complete;
+ urb->context = &splice;
+ urb->status = -ENOENT;
+ } else {
+ /* asynchronous unlink */
+ urb->status = -ECONNRESET;
+ }
+ spin_unlock_irqrestore (&urb->lock, flags);
+
+ if (urb == (struct urb *) hcd->rh_timer.data) {
+ rh_status_dequeue (hcd, urb);
+ retval = 0;
+ } else {
+ retval = hcd->driver->urb_dequeue (hcd, urb);
+// FIXME: if retval and we tried to splice, whoa!!
+if (retval && urb->status == -ENOENT) err ("whoa! retval %d", retval);
+ }
+
+ /* block till giveback, if needed */
+ if (!(urb->transfer_flags & (USB_ASYNC_UNLINK|USB_TIMEOUT_KILLED))
+ && HCD_IS_RUNNING (hcd->state)
+ && !retval) {
+ while (!splice.done) {
+ set_current_state (TASK_UNINTERRUPTIBLE);
+ schedule_timeout ((2/*msec*/ * HZ) / 1000);
+ dbg ("%s: wait for giveback urb %p",
+ hcd->bus_name, urb);
+ }
+ }
+ goto bye;
+done:
+ spin_unlock_irqrestore (&urb->lock, flags);
+bye:
+ if (retval)
+ dbg ("%s: hcd_unlink_urb fail %d",
+ hcd ? hcd->bus_name : "(no bus?)",
+ retval);
+ return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* called by khubd, rmmod, apmd, or other thread for hcd-private cleanup */
+
+// FIXME: likely best to have explicit per-setting (config+alt)
+// setup primitives in the usbcore-to-hcd driver API, so nothing
+// is implicit. kernel 2.5 needs a bunch of config cleanup...
+
+static int hcd_free_dev (struct usb_device *udev)
+{
+ struct hcd_dev *dev;
+ struct usb_hcd *hcd;
+ unsigned long flags;
+
+ if (!udev || !udev->hcpriv)
+ return -EINVAL;
+
+ if (!udev->bus || !udev->bus->hcpriv)
+ return -ENODEV;
+
+ // should udev->devnum == -1 ??
+
+ dev = udev->hcpriv;
+ hcd = udev->bus->hcpriv;
+
+ /* device driver problem with refcounts? */
+ if (!list_empty (&dev->urb_list)) {
+ dbg ("free busy dev, %s devnum %d (bug!)",
+ hcd->bus_name, udev->devnum);
+ return -EINVAL;
+ }
+
+ hcd->driver->free_config (hcd, udev);
+
+ spin_lock_irqsave (&hcd_data_lock, flags);
+ list_del (&dev->dev_list);
+ udev->hcpriv = NULL;
+ spin_unlock_irqrestore (&hcd_data_lock, flags);
+
+ kfree (dev);
+ return 0;
+}
+
+static struct usb_operations hcd_operations = {
+ allocate: hcd_alloc_dev,
+ get_frame_number: hcd_get_frame_number,
+ submit_urb: hcd_submit_urb,
+ unlink_urb: hcd_unlink_urb,
+ deallocate: hcd_free_dev,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static void hcd_irq (int irq, void *__hcd, struct pt_regs * r)
+{
+ struct usb_hcd *hcd = __hcd;
+ int start = hcd->state;
+
+ hcd->driver->irq (hcd);
+ if (hcd->state != start && hcd->state == USB_STATE_HALT)
+ hc_died (hcd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * usb_hcd_giveback_urb - return URB from HCD to device driver
+ * @hcd: host controller returning the URB
+ * @urb: urb being returned to the USB device driver.
+ *
+ * This hands the URB from HCD to its USB device driver, using its
+ * completion function. The HCD has freed all per-urb resources
+ * (and is done using urb->hcpriv). It also released all HCD locks;
+ * the device driver won't cause deadlocks if it resubmits this URB,
+ * and won't confuse things by modifying and resubmitting this one.
+ * Bandwidth and other resources will be deallocated.
+ *
+ * HCDs must not use this for periodic URBs that are still scheduled
+ * and will be reissued. They should just call their completion handlers
+ * until the urb is returned to the device driver by unlinking.
+ *
+ * In common cases, urb->next will be submitted before the completion
+ * function gets called. That's not done if the URB includes error
+ * status (including unlinking).
+ */
+void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb)
+{
+ unsigned long flags;
+ struct usb_device *dev;
+
+ /* Release periodic transfer bandwidth */
+ if (urb->bandwidth) {
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_INTERRUPT:
+ usb_release_bandwidth (urb->dev, urb, 0);
+ break;
+ case PIPE_ISOCHRONOUS:
+ usb_release_bandwidth (urb->dev, urb, 1);
+ break;
+ }
+ }
+
+ /* clear all state linking urb to this dev (and hcd) */
+
+ spin_lock_irqsave (&hcd_data_lock, flags);
+ list_del_init (&urb->urb_list);
+ dev = urb->dev;
+ urb->dev = NULL;
+ spin_unlock_irqrestore (&hcd_data_lock, flags);
+
+ // NOTE: a generic device/urb monitoring hook would go here.
+ // hcd_monitor_hook(MONITOR_URB_FINISH, urb, dev)
+ // It would catch exit/unlink paths for all urbs, but non-exit
+ // completions for periodic urbs need hooks inside the HCD.
+ // hcd_monitor_hook(MONITOR_URB_UPDATE, urb, dev)
+
+ if (urb->status)
+ dbg ("giveback urb %p status %d", urb, urb->status);
+
+ /* if no error, make sure urb->next progresses */
+ else if (urb->next) {
+ int status;
+
+ status = usb_submit_urb (urb->next);
+ if (status) {
+ dbg ("urb %p chain fail, %d", urb->next, status);
+ urb->next->status = -ENOTCONN;
+ }
+
+ /* HCDs never modify the urb->next chain, and only use it here,
+ * so that if urb->complete sees an URB there with -ENOTCONN,
+ * it knows the driver chained it but it couldn't be submitted.
+ */
+ }
+
+ /* pass ownership to the completion handler */
+ usb_dec_dev_use (dev);
+ urb->complete (urb);
+}
+EXPORT_SYMBOL (usb_hcd_giveback_urb);
--- /dev/null
+/*
+ * Copyright (c) 2001 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * USB Host Controller Driver (usb_hcd) framework
+ *
+ * Since "struct usb_bus" is so thin, you can't share much code in it.
+ * This framework is a layer over that, and should be more sharable.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+struct usb_hcd { /* usb_bus.hcpriv points to this */
+
+ /*
+ * housekeeping
+ */
+ struct usb_bus *bus; /* hcd is-a bus */
+ struct list_head hcd_list;
+
+ const char *bus_name;
+
+ const char *description; /* "ehci-hcd" etc */
+
+ struct timer_list rh_timer; /* drives root hub */
+ struct list_head dev_list; /* devices on this bus */
+
+ /*
+ * hardware info/state
+ */
+ struct hc_driver *driver; /* hw-specific hooks */
+ int irq; /* irq allocated */
+ void *regs; /* device memory/io */
+
+#ifdef CONFIG_PCI
+ /* a few non-PCI controllers exist, mostly for OHCI */
+ struct pci_dev *pdev; /* pci is typical */
+ int region; /* pci region for regs */
+ u32 pci_state [16]; /* for PM state save */
+ atomic_t resume_count; /* multiple resumes issue */
+#endif
+
+ int state;
+# define __ACTIVE 0x01
+# define __SLEEPY 0x02
+# define __SUSPEND 0x04
+# define __TRANSIENT 0x80
+
+# define USB_STATE_HALT 0
+# define USB_STATE_RUNNING (__ACTIVE)
+# define USB_STATE_READY (__ACTIVE|__SLEEPY)
+# define USB_STATE_QUIESCING (__SUSPEND|__TRANSIENT|__ACTIVE)
+# define USB_STATE_RESUMING (__SUSPEND|__TRANSIENT)
+# define USB_STATE_SUSPENDED (__SUSPEND)
+
+#define HCD_IS_RUNNING(state) ((state) & __ACTIVE)
+#define HCD_IS_SUSPENDED(state) ((state) & __SUSPEND)
+
+ /* more shared queuing code would be good; it should support
+ * smarter scheduling, handle transaction translators, etc;
+ * input size of periodic table to an interrupt scheduler.
+ * (ohci 32, uhci 1024, ehci 256/512/1024).
+ */
+};
+
+struct hcd_dev { /* usb_device.hcpriv points to this */
+ struct list_head dev_list; /* on this hcd */
+ struct list_head urb_list; /* pending on this dev */
+
+ /* per-configuration HC/HCD state, such as QH or ED */
+ void *ep[32];
+};
+
+// urb.hcpriv is really hardware-specific
+
+struct hcd_timeout { /* timeouts we allocate */
+ struct list_head timeout_list;
+ struct timer_list timer;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* each driver provides one of these, and hardware init support */
+
+struct hc_driver {
+ const char *description; /* "ehci-hcd" etc */
+
+ /* irq handler */
+ void (*irq) (struct usb_hcd *hcd);
+
+ int flags;
+#define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */
+#define HCD_USB11 0x0010 /* USB 1.1 */
+#define HCD_USB2 0x0020 /* USB 2.0 */
+
+ /* called to init HCD and root hub */
+ int (*start) (struct usb_hcd *hcd);
+
+ /* called after all devices were suspended */
+ int (*suspend) (struct usb_hcd *hcd, u32 state);
+
+ /* called before any devices get resumed */
+ int (*resume) (struct usb_hcd *hcd);
+
+ /* cleanly make HCD stop writing memory and doing I/O */
+ void (*stop) (struct usb_hcd *hcd);
+
+ /* return current frame number */
+ int (*get_frame_number) (struct usb_hcd *hcd);
+
+// FIXME: rework generic-to-specific HCD linkage (specific contains generic)
+
+ /* memory lifecycle */
+ struct usb_hcd *(*hcd_alloc) (void);
+ void (*hcd_free) (struct usb_hcd *hcd);
+
+ /* manage i/o requests, device state */
+ int (*urb_enqueue) (struct usb_hcd *hcd, struct urb *urb,
+ int mem_flags);
+ int (*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb);
+
+ // frees configuration resources -- allocated as needed during
+ // urb_enqueue, and not freed by urb_dequeue
+ void (*free_config) (struct usb_hcd *hcd,
+ struct usb_device *dev);
+
+ /* root hub support */
+ int (*hub_status_data) (struct usb_hcd *hcd, char *buf);
+ int (*hub_control) (struct usb_hcd *hcd,
+ u16 typeReq, u16 wValue, u16 wIndex,
+ char *buf, u16 wLength);
+};
+
+extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb);
+
+#ifdef CONFIG_PCI
+
+extern int usb_hcd_pci_probe (struct pci_dev *dev,
+ const struct pci_device_id *id);
+extern void usb_hcd_pci_remove (struct pci_dev *dev);
+
+#ifdef CONFIG_PM
+// FIXME: see Documentation/power/pci.txt (2.4.6 and later?)
+// extern int usb_hcd_pci_save_state (struct pci_dev *dev, u32 state);
+extern int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state);
+extern int usb_hcd_pci_resume (struct pci_dev *dev);
+// extern int usb_hcd_pci_enable_wake (struct pci_dev *dev, u32 state, int flg);
+#endif /* CONFIG_PM */
+
+#endif /* CONFIG_PCI */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * HCD Root Hub support
+ */
+
+#include "hub.h"
+
+/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */
+#define DeviceRequest \
+ ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
+#define DeviceOutRequest \
+ ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
+
+#define InterfaceRequest \
+ ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+
+#define EndpointRequest \
+ ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+#define EndpointOutRequest \
+ ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+
+/* table 9.6 standard features */
+#define DEVICE_REMOTE_WAKEUP 1
+#define ENDPOINT_HALT 0
+
+/* class requests from the USB 2.0 hub spec, table 11-15 */
+/* GetBusState and SetHubDescriptor are optional, omitted */
+#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE)
+#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE)
+#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR)
+#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS)
+#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS)
+#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE)
+#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE)
+
+
+/*-------------------------------------------------------------------------*/
+
+/* hub.h ... DeviceRemovable in 2.4.2-ac11, gone in 2.4.10 */
+// bleech -- resurfaced in 2.4.11 or 2.4.12
+#define bitmap DeviceRemovable
+
+
+/*-------------------------------------------------------------------------*/
+
+/* random stuff */
+
+#define RUN_CONTEXT (in_irq () ? "in_irq" \
+ : (in_interrupt () ? "in_interrupt" : "can sleep"))
--- /dev/null
+#
+# USB Host Controller Drivers
+#
+dep_tristate ' EHCI HCD (USB 2.0) support (EXPERIMENTAL)' CONFIG_EHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
+# dep_tristate ' OHCI HCD support (EXPERIMENTAL)' CONFIG_OHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
+# dep_tristate ' UHCI HCD (most Intel and VIA) support (EXPERIMENTAL)' CONFIG_UHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
+
--- /dev/null
+#
+# Makefile for USB Host Controller Driver
+# framework and drivers
+#
+
+O_TARGET :=
+
+obj-$(CONFIG_EHCI_HCD) += ehci-hcd.o
+# obj-$(CONFIG_OHCI_HCD) += ohci-hcd.o
+# obj-$(CONFIG_UHCI_HCD) += uhci-hcd.o
+
+# Extract lists of the multi-part drivers.
+# The 'int-*' lists are the intermediate files used to build the multi's.
+multi-y := $(filter $(list-multi), $(obj-y))
+multi-m := $(filter $(list-multi), $(obj-m))
+int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
+int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
+
+# Take multi-part drivers out of obj-y and put components in.
+obj-y := $(filter-out $(list-multi), $(obj-y)) $(int-y)
+
+# Translate to Rules.make lists.
+OX_OBJS := $(obj-y)
+MX_OBJS := $(obj-m)
+MIX_OBJS := $(int-m)
+
+include $(TOPDIR)/Rules.make
--- /dev/null
+/*
+ * Copyright (c) 2001 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+#ifdef EHCI_VERBOSE_DEBUG
+# define vdbg dbg
+#else
+ static inline void vdbg (char *fmt, ...) { }
+#endif
+
+#ifdef DEBUG
+
+/* check the values in the HCSPARAMS register - host controller structural parameters */
+/* see EHCI 0.95 Spec, Table 2-4 for each value */
+static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
+{
+ u32 params = readl (&ehci->caps->hcs_params);
+
+ dbg ("%s hcs_params 0x%x dbg=%d%s cc=%d pcc=%d%s%s ports=%d",
+ label, params,
+ HCS_DEBUG_PORT (params),
+ HCS_INDICATOR (params) ? " ind" : "",
+ HCS_N_CC (params),
+ HCS_N_PCC (params),
+ HCS_PORTROUTED (params) ? "" : " ordered",
+ HCS_PPC (params) ? "" : " !ppc",
+ HCS_N_PORTS (params)
+ );
+ /* Port routing, per EHCI 0.95 Spec, Section 2.2.5 */
+ if (HCS_PORTROUTED (params)) {
+ int i;
+ char buf [46], tmp [7], byte;
+
+ buf[0] = 0;
+ for (i = 0; i < HCS_N_PORTS (params); i++) {
+ byte = readb (&ehci->caps->portroute[(i>>1)]);
+ sprintf(tmp, "%d ",
+ ((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf)));
+ strcat(buf, tmp);
+ }
+ dbg ("%s: %s portroute %s",
+ ehci->hcd.bus_name, label,
+ buf);
+ }
+}
+#else
+
+static inline void dbg_hcs_params (struct ehci_hcd *ehci, char *label) {}
+
+#endif
+
+#ifdef DEBUG
+
+/* check the values in the HCCPARAMS register - host controller capability parameters */
+/* see EHCI 0.95 Spec, Table 2-5 for each value */
+static void dbg_hcc_params (struct ehci_hcd *ehci, char *label)
+{
+ u32 params = readl (&ehci->caps->hcc_params);
+
+ if (HCC_EXT_CAPS (params)) {
+ // EHCI 0.96 ... could interpret these (legacy?)
+ dbg ("%s extended capabilities at pci %d",
+ label, HCC_EXT_CAPS (params));
+ }
+ if (HCC_ISOC_CACHE (params)) {
+ dbg ("%s hcc_params 0x%04x caching frame %s%s%s",
+ label, params,
+ HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024",
+ HCC_CANPARK (params) ? " park" : "",
+ HCC_64BIT_ADDR (params) ? " 64 bit addr" : "");
+ } else {
+ dbg ("%s hcc_params 0x%04x caching %d uframes %s%s%s",
+ label,
+ params,
+ HCC_ISOC_THRES (params),
+ HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024",
+ HCC_CANPARK (params) ? " park" : "",
+ HCC_64BIT_ADDR (params) ? " 64 bit addr" : "");
+ }
+}
+#else
+
+static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {}
+
+#endif
+
+#ifdef DEBUG
+
+#if 0
+static void dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ dbg ("%s %p info1 %x info2 %x hw_curr %x qtd_next %x", label,
+ qh, qh->hw_info1, qh->hw_info2,
+ qh->hw_current, qh->hw_qtd_next);
+ dbg (" alt+errs= %x, token= %x, page0= %x, page1= %x",
+ qh->hw_alt_next, qh->hw_token,
+ qh->hw_buf [0], qh->hw_buf [1]);
+ if (qh->hw_buf [2]) {
+ dbg (" page2= %x, page3= %x, page4= %x",
+ qh->hw_buf [2], qh->hw_buf [3],
+ qh->hw_buf [4]);
+ }
+}
+#endif
+
+static const char *const fls_strings [] =
+ { "1024", "512", "256", "??" };
+
+#else
+#if 0
+static inline void dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) {}
+#endif
+#endif /* DEBUG */
+
+/* functions have the "wrong" filename when they're output... */
+
+#define dbg_status(ehci, label, status) \
+ dbg ("%s status 0x%x%s%s%s%s%s%s%s%s%s%s", \
+ label, status, \
+ (status & STS_ASS) ? " Async" : "", \
+ (status & STS_PSS) ? " Periodic" : "", \
+ (status & STS_RECL) ? " Recl" : "", \
+ (status & STS_HALT) ? " Halt" : "", \
+ (status & STS_IAA) ? " IAA" : "", \
+ (status & STS_FATAL) ? " FATAL" : "", \
+ (status & STS_FLR) ? " FLR" : "", \
+ (status & STS_PCD) ? " PCD" : "", \
+ (status & STS_ERR) ? " ERR" : "", \
+ (status & STS_INT) ? " INT" : "" \
+ )
+
+#define dbg_cmd(ehci, label, command) \
+ dbg ("%s %x cmd %s=%d ithresh=%d%s%s%s%s period=%s%s %s", \
+ label, command, \
+ (command & CMD_PARK) ? "park" : "(park)", \
+ CMD_PARK_CNT (command), \
+ (command >> 16) & 0x3f, \
+ (command & CMD_LRESET) ? " LReset" : "", \
+ (command & CMD_IAAD) ? " IAAD" : "", \
+ (command & CMD_ASE) ? " Async" : "", \
+ (command & CMD_PSE) ? " Periodic" : "", \
+ fls_strings [(command >> 2) & 0x3], \
+ (command & CMD_RESET) ? " Reset" : "", \
+ (command & CMD_RUN) ? "RUN" : "HALT" \
+ )
+
+#define dbg_port(hcd, label, port, status) \
+ dbg ("%s port %d status 0x%x%s%s speed=%d%s%s%s%s%s%s%s%s%s", \
+ label, port, status, \
+ (status & PORT_OWNER) ? " OWNER" : "", \
+ (status & PORT_POWER) ? " POWER" : "", \
+ (status >> 10) & 3, \
+ (status & PORT_RESET) ? " RESET" : "", \
+ (status & PORT_SUSPEND) ? " SUSPEND" : "", \
+ (status & PORT_RESUME) ? " RESUME" : "", \
+ (status & PORT_OCC) ? " OCC" : "", \
+ (status & PORT_OC) ? " OC" : "", \
+ (status & PORT_PEC) ? " PEC" : "", \
+ (status & PORT_PE) ? " PE" : "", \
+ (status & PORT_CSC) ? " CSC" : "", \
+ (status & PORT_CONNECT) ? " CONNECT" : "" \
+ )
+
--- /dev/null
+/*
+ * Copyright (c) 2000-2001 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+
+#ifndef CONFIG_USB_DEBUG
+ #define CONFIG_USB_DEBUG /* this is still experimental! */
+#endif
+
+#ifdef CONFIG_USB_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
+
+#include <linux/usb.h>
+#include "../hcd.h"
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+//#undef KERN_DEBUG
+//#define KERN_DEBUG ""
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI hc_driver implementation ... experimental, incomplete.
+ * Based on the 0.96 register interface specification.
+ *
+ * There are lots of things to help out with here ... notably
+ * everything "periodic", and of course testing with all sorts
+ * of usb 2.0 devices and configurations.
+ *
+ * USB 2.0 shows up in upcoming www.pcmcia.org technology.
+ * First was PCMCIA, like ISA; then CardBus, which is PCI.
+ * Next comes "CardBay", using USB 2.0 signals.
+ *
+ * Contains additional contributions by:
+ * Brad Hards
+ * Rory Bolt
+ * ...
+ */
+
+#define DRIVER_VERSION "$Revision: 0.25 $"
+#define DRIVER_AUTHOR "David Brownell"
+#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
+
+
+// #define EHCI_VERBOSE_DEBUG
+// #define have_iso
+
+#ifdef DEBUG
+# define EHCI_SLAB_FLAGS (SLAB_POISON)
+#else
+# define EHCI_SLAB_FLAGS 0
+#endif
+
+/* magic numbers that can affect system performance */
+#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */
+#define EHCI_TUNE_RL_HS 0 /* nak throttle; see 4.9 */
+#define EHCI_TUNE_RL_TT 0
+#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */
+#define EHCI_TUNE_MULT_TT 1
+
+/* Initial IRQ latency: lower than default */
+static int log2_irq_thresh = 0; // 0 to 6
+MODULE_PARM (log2_irq_thresh, "i");
+MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
+
+/* Some A steppings of the NEC controller need soft retries */
+//#define EHCI_SOFT_RETRIES 5 /* after CERR-induced fault */
+
+#define INTR_MASK (STS_IAA | STS_FATAL | STS_ERR | STS_INT)
+
+/*-------------------------------------------------------------------------*/
+
+#include "ehci.h"
+#include "ehci-dbg.c"
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * hc states include: unknown, halted, ready, running
+ * transitional states are messy just now
+ * trying to avoid "running" unless urbs are active
+ * a "ready" hc can be finishing prefetched work
+ */
+
+/* halt a non-running controller */
+static void ehci_reset (struct ehci_hcd *ehci)
+{
+ u32 command = readl (&ehci->regs->command);
+
+ command |= CMD_RESET;
+ dbg_cmd (ehci, "reset", command);
+ writel (command, &ehci->regs->command);
+ while (readl (&ehci->regs->command) & CMD_RESET)
+ continue;
+ ehci->hcd.state = USB_STATE_HALT;
+}
+
+/* idle the controller (from running) */
+static void ehci_ready (struct ehci_hcd *ehci)
+{
+ u32 command;
+
+#ifdef DEBUG
+ if (!HCD_IS_RUNNING (ehci->hcd.state))
+ BUG ();
+#endif
+
+ while (!(readl (&ehci->regs->status) & (STS_ASS | STS_PSS)))
+ udelay (100);
+ command = readl (&ehci->regs->command);
+ command &= ~(CMD_ASE | CMD_IAAD | CMD_PSE);
+ writel (command, &ehci->regs->command);
+
+ // hardware can take 16 microframes to turn off ...
+ ehci->hcd.state = USB_STATE_READY;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#include "ehci-hub.c"
+#include "ehci-mem.c"
+#include "ehci-q.c"
+#include "ehci-sched.c"
+
+/*-------------------------------------------------------------------------*/
+
+static void ehci_tasklet (unsigned long param);
+
+/* called by khubd or root hub init threads */
+
+static int ehci_start (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 temp;
+ struct usb_device *udev;
+ int retval;
+ u32 hcc_params;
+ u8 tempbyte;
+
+ // FIXME: given EHCI 0.96 or later, and a controller with
+ // the USBLEGSUP/USBLEGCTLSTS extended capability, make sure
+ // the BIOS doesn't still own this controller.
+
+ spin_lock_init (&ehci->lock);
+
+ ehci->caps = (struct ehci_caps *) hcd->regs;
+ ehci->regs = (struct ehci_regs *) (hcd->regs + ehci->caps->length);
+ dbg_hcs_params (ehci, "ehci_start");
+ dbg_hcc_params (ehci, "ehci_start");
+
+ /*
+ * hw default: 1K periodic list heads, one per frame.
+ * periodic_size can shrink by USBCMD update if hcc_params allows.
+ */
+ ehci->periodic_size = DEFAULT_I_TDPS;
+ if ((retval = ehci_mem_init (ehci, EHCI_SLAB_FLAGS | SLAB_KERNEL)) < 0)
+ return retval;
+ hcc_params = readl (&ehci->caps->hcc_params);
+
+ /* controllers may cache some of the periodic schedule ... */
+ if (HCC_ISOC_CACHE (hcc_params)) // full frame cache
+ ehci->i_thresh = 8;
+ else // N microframes cached
+ ehci->i_thresh = 2 + HCC_ISOC_THRES (hcc_params);
+
+ ehci->async = 0;
+ ehci->reclaim = 0;
+ ehci->next_frame = -1;
+
+ /* controller state: unknown --> reset */
+
+ /* EHCI spec section 4.1 */
+ ehci_reset (ehci);
+ writel (INTR_MASK, &ehci->regs->intr_enable);
+ writel (ehci->periodic_dma, &ehci->regs->frame_list);
+
+ /*
+ * hcc_params controls whether ehci->regs->segment must (!!!)
+ * be used; it constrains QH/ITD/SITD and QTD locations.
+ * By default, pci_alloc_consistent() won't hand out addresses
+ * above 4GB (via pdev->dma_mask) so we know this value.
+ *
+ * NOTE: that pdev->dma_mask setting means that all DMA mappings
+ * for I/O buffers will have the same restriction, though it's
+ * neither necessary nor desirable in that case.
+ */
+ if (HCC_64BIT_ADDR (hcc_params)) {
+ writel (0, &ehci->regs->segment);
+ info ("using segment 0 for 64bit DMA addresses ...");
+ }
+
+ /* clear interrupt enables, set irq latency */
+ temp = readl (&ehci->regs->command) & 0xff;
+ if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
+ log2_irq_thresh = 0;
+ temp |= 1 << (16 + log2_irq_thresh);
+ // keeping default periodic framelist size
+ temp &= ~(CMD_IAAD | CMD_ASE | CMD_PSE),
+ writel (temp, &ehci->regs->command);
+ dbg_cmd (ehci, "init", temp);
+
+ /* set async sleep time = 10 us ... ? */
+
+ ehci->tasklet.func = ehci_tasklet;
+ ehci->tasklet.data = (unsigned long) ehci;
+
+ /* wire up the root hub */
+ hcd->bus->root_hub = udev = usb_alloc_dev (NULL, hcd->bus);
+ if (!udev) {
+done2:
+ ehci_mem_cleanup (ehci);
+ return -ENOMEM;
+ }
+
+ /*
+ * Start, enabling full USB 2.0 functionality ... usb 1.1 devices
+ * are explicitly handed to companion controller(s), so no TT is
+ * involved with the root hub.
+ */
+ ehci->hcd.state = USB_STATE_READY;
+ writel (FLAG_CF, &ehci->regs->configured_flag);
+ readl (&ehci->regs->command); /* unblock posted write */
+
+ /* PCI Serial Bus Release Number is at 0x60 offset */
+ pci_read_config_byte(hcd->pdev, 0x60, &tempbyte);
+ temp = readw (&ehci->caps->hci_version);
+ info ("USB %x.%x support enabled, EHCI rev %x.%2x",
+ ((tempbyte & 0xf0)>>4),
+ (tempbyte & 0x0f),
+ temp >> 8,
+ temp & 0xff);
+
+ /*
+ * From here on, khubd concurrently accesses the root
+ * hub; drivers will be talking to enumerated devices.
+ *
+ * Before this point the HC was idle/ready. After, khubd
+ * and device drivers may start it running.
+ */
+ usb_connect (udev);
+ udev->speed = USB_SPEED_HIGH;
+ if (usb_new_device (udev) != 0) {
+ if (hcd->state == USB_STATE_RUNNING)
+ ehci_ready (ehci);
+ while (readl (&ehci->regs->status) & (STS_ASS | STS_PSS))
+ udelay (100);
+ ehci_reset (ehci);
+ // usb_disconnect (udev);
+ hcd->bus->root_hub = 0;
+ usb_free_dev (udev);
+ retval = -ENODEV;
+ goto done2;
+ }
+
+ return 0;
+}
+
+/* always called by thread; normally rmmod */
+
+static void ehci_stop (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+
+ dbg ("%s: stop", hcd->bus_name);
+
+ if (hcd->state == USB_STATE_RUNNING)
+ ehci_ready (ehci);
+ while (readl (&ehci->regs->status) & (STS_ASS | STS_PSS))
+ udelay (100);
+ ehci_reset (ehci);
+
+ // root hub is shut down separately (first, when possible)
+ scan_async (ehci);
+ if (ehci->next_frame != -1)
+ scan_periodic (ehci);
+ ehci_mem_cleanup (ehci);
+
+ dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status));
+}
+
+static int ehci_get_frame (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ return (readl (&ehci->regs->frame_index) >> 3) % ehci->periodic_size;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PM
+
+/* suspend/resume, section 4.3 */
+
+static int ehci_suspend (struct usb_hcd *hcd, u32 state)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 params;
+ int ports;
+ int i;
+
+ dbg ("%s: suspend to %d", hcd->bus_name, state);
+
+ params = readl (&ehci->caps->hcs_params);
+ ports = HCS_N_PORTS (params);
+
+ // FIXME: This assumes what's probably a D3 level suspend...
+
+ // FIXME: usb wakeup events on this bus should resume the machine.
+ // pci config register PORTWAKECAP controls which ports can do it;
+ // bios may have initted the register...
+
+ /* suspend each port, then stop the hc */
+ for (i = 0; i < ports; i++) {
+ int temp = readl (&ehci->regs->port_status [i]);
+
+ if ((temp & PORT_PE) == 0
+ || (temp & PORT_OWNER) != 0)
+ continue;
+dbg ("%s: suspend port %d", hcd->bus_name, i);
+ temp |= PORT_SUSPEND;
+ writel (temp, &ehci->regs->port_status [i]);
+ }
+
+ if (hcd->state == USB_STATE_RUNNING)
+ ehci_ready (ehci);
+ while (readl (&ehci->regs->status) & (STS_ASS | STS_PSS))
+ udelay (100);
+ writel (readl (&ehci->regs->command) & ~CMD_RUN, &ehci->regs->command);
+
+// save pci FLADJ value
+
+ /* who tells PCI to reduce power consumption? */
+
+ return 0;
+}
+
+static int ehci_resume (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 params;
+ int ports;
+ int i;
+
+ dbg ("%s: resume", hcd->bus_name);
+
+ params = readl (&ehci->caps->hcs_params);
+ ports = HCS_N_PORTS (params);
+
+ // FIXME: if controller didn't retain state,
+ // return and let generic code clean it up
+ // test configured_flag ?
+
+ /* resume HC and each port */
+// restore pci FLADJ value
+ // khubd and drivers will set HC running, if needed;
+ hcd->state = USB_STATE_READY;
+ for (i = 0; i < ports; i++) {
+ int temp = readl (&ehci->regs->port_status [i]);
+
+ if ((temp & PORT_PE) == 0
+ || (temp & PORT_SUSPEND) != 0)
+ continue;
+dbg ("%s: resume port %d", hcd->bus_name, i);
+ temp |= PORT_RESUME;
+ writel (temp, &ehci->regs->port_status [i]);
+ readl (&ehci->regs->command); /* unblock posted writes */
+
+ wait_ms (20);
+ temp &= ~PORT_RESUME;
+ writel (temp, &ehci->regs->port_status [i]);
+ }
+ readl (&ehci->regs->command); /* unblock posted writes */
+ return 0;
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * tasklet scheduled by some interrupts and other events
+ * calls driver completion functions ... but not in_irq()
+ */
+static void ehci_tasklet (unsigned long param)
+{
+ struct ehci_hcd *ehci = (struct ehci_hcd *) param;
+
+ if (ehci->reclaim_ready)
+ end_unlink_async (ehci);
+ scan_async (ehci);
+ if (ehci->next_frame != -1)
+ scan_periodic (ehci);
+
+ // FIXME: when nothing is connected to the root hub,
+ // turn off the RUN bit so the host can enter C3 "sleep" power
+ // saving mode; make root hub code scan memory less often.
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void ehci_irq (struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 status = readl (&ehci->regs->status);
+ int bh = 0;
+
+ /* clear (just) interrupts */
+ status &= INTR_MASK;
+ writel (status, &ehci->regs->status);
+ readl (&ehci->regs->command); /* unblock posted write */
+
+ if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */
+ return;
+
+#ifdef EHCI_VERBOSE_DEBUG
+ /* unrequested/ignored: Port Change Detect, Frame List Rollover */
+ if (status & INTR_MASK)
+ dbg_status (ehci, "irq", status);
+#endif
+
+ /* INT, ERR, and IAA interrupt rates can be throttled */
+
+ /* normal [4.15.1.2] or error [4.15.1.1] completion */
+ if (likely ((status & (STS_INT|STS_ERR)) != 0))
+ bh = 1;
+
+ /* complete the unlinking of some qh [4.15.2.3] */
+ if (status & STS_IAA) {
+ ehci->reclaim_ready = 1;
+ bh = 1;
+ }
+
+ /* PCI errors [4.15.2.4] */
+ if (unlikely ((status & STS_FATAL) != 0)) {
+ err ("%s: fatal error, state %x", hcd->bus_name, hcd->state);
+ ehci_reset (ehci);
+ // generic layer kills/unlinks all urbs
+ // then tasklet cleans up the rest
+ bh = 1;
+ }
+
+ /* most work doesn't need to be in_irq() */
+ if (likely (bh == 1))
+ tasklet_schedule (&ehci->tasklet);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * non-error returns are a promise to giveback() the urb later
+ * we drop ownership so next owner (or urb unlink) can get it
+ *
+ * urb + dev is in hcd_dev.urb_list
+ * we're queueing TDs onto software and hardware lists
+ *
+ * hcd-specific init for hcpriv hasn't been done yet
+ *
+ * NOTE: EHCI queues control and bulk requests transparently, like OHCI.
+ */
+static int ehci_urb_enqueue (
+ struct usb_hcd *hcd,
+ struct urb *urb,
+ int mem_flags
+) {
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ struct list_head qtd_list;
+
+ urb->transfer_flags &= ~EHCI_STATE_UNLINK;
+ INIT_LIST_HEAD (&qtd_list);
+ switch (usb_pipetype (urb->pipe)) {
+
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
+ return -ENOMEM;
+ submit_async (ehci, urb, &qtd_list, mem_flags);
+ break;
+
+ case PIPE_INTERRUPT:
+ if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
+ return -ENOMEM;
+ return intr_submit (ehci, urb, &qtd_list, mem_flags);
+
+ case PIPE_ISOCHRONOUS:
+#ifdef have_iso
+ if (urb->dev->speed == USB_SPEED_HIGH)
+ return itd_submit (ehci, urb);
+ else
+ return sitd_submit (ehci, urb);
+#else
+ // FIXME highspeed iso stuff is written but never run/tested.
+ // and the split iso support isn't even written yet.
+ dbg ("no iso support yet");
+ return -ENOSYS;
+#endif /* have_iso */
+
+ }
+ return 0;
+}
+
+/* remove from hardware lists
+ * completions normally happen asynchronously
+ */
+
+static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv;
+ unsigned long flags;
+
+ dbg ("%s urb_dequeue %p qh state %d",
+ hcd->bus_name, urb, qh->qh_state);
+
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ spin_lock_irqsave (&ehci->lock, flags);
+ if (ehci->reclaim) {
+dbg ("dq: reclaim busy, %s", RUN_CONTEXT);
+ if (in_interrupt ()) {
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return -EAGAIN;
+ }
+ while (qh->qh_state == QH_STATE_LINKED
+ && ehci->reclaim
+ && ehci->hcd.state != USB_STATE_HALT
+ ) {
+ spin_unlock_irqrestore (&ehci->lock, flags);
+// yeech ... this could spin for up to two frames!
+dbg ("wait for dequeue: state %d, reclaim %p, hcd state %d",
+ qh->qh_state, ehci->reclaim, ehci->hcd.state
+);
+ udelay (100);
+ spin_lock_irqsave (&ehci->lock, flags);
+ }
+ }
+ if (qh->qh_state == QH_STATE_LINKED)
+ start_unlink_async (ehci, qh);
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return 0;
+
+ case PIPE_INTERRUPT:
+ intr_deschedule (ehci, urb->start_frame, qh, urb->interval);
+ if (ehci->hcd.state == USB_STATE_HALT)
+ urb->status = -ESHUTDOWN;
+ qh_completions (ehci, &qh->qtd_list, 1);
+ return 0;
+
+ case PIPE_ISOCHRONOUS:
+ // itd or sitd ...
+
+ // wait till next completion, do it then.
+ // completion irqs can wait up to 128 msec,
+ urb->transfer_flags |= EHCI_STATE_UNLINK;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+// bulk qh holds the data toggle
+
+static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
+{
+ struct hcd_dev *dev = (struct hcd_dev *)udev->hcpriv;
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ int i;
+ unsigned long flags;
+
+ /* ASSERT: nobody can be submitting urbs for this any more */
+
+ dbg ("%s: free_config devnum %d", hcd->bus_name, udev->devnum);
+
+ spin_lock_irqsave (&ehci->lock, flags);
+ for (i = 0; i < 32; i++) {
+ if (dev->ep [i]) {
+ struct ehci_qh *qh;
+
+ // FIXME: this might be an itd/sitd too ...
+ // or an interrupt urb (not on async list)
+ // can use "union ehci_shadow"
+
+ qh = (struct ehci_qh *) dev->ep [i];
+ vdbg ("free_config, ep 0x%02x qh %p", i, qh);
+ if (!list_empty (&qh->qtd_list)) {
+ dbg ("ep 0x%02x qh %p not empty!", i, qh);
+ BUG ();
+ }
+ dev->ep [i] = 0;
+
+ /* wait_ms() won't spin here -- we're a thread */
+ while (qh->qh_state == QH_STATE_LINKED
+ && ehci->reclaim
+ && ehci->hcd.state != USB_STATE_HALT
+ ) {
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ wait_ms (1);
+ spin_lock_irqsave (&ehci->lock, flags);
+ }
+ if (qh->qh_state == QH_STATE_LINKED) {
+ start_unlink_async (ehci, qh);
+ while (qh->qh_state != QH_STATE_IDLE) {
+ spin_unlock_irqrestore (&ehci->lock,
+ flags);
+ wait_ms (1);
+ spin_lock_irqsave (&ehci->lock, flags);
+ }
+ }
+ qh_unput (ehci, qh);
+ }
+ }
+
+ spin_unlock_irqrestore (&ehci->lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const char hcd_name [] = "ehci-hcd";
+
+static const struct hc_driver ehci_driver = {
+ description: hcd_name,
+
+ /*
+ * generic hardware linkage
+ */
+ irq: ehci_irq,
+ flags: HCD_MEMORY | HCD_USB2,
+
+ /*
+ * basic lifecycle operations
+ */
+ start: ehci_start,
+#ifdef CONFIG_PM
+ suspend: ehci_suspend,
+ resume: ehci_resume,
+#endif
+ stop: ehci_stop,
+
+ /*
+ * memory lifecycle (except per-request)
+ */
+ hcd_alloc: ehci_hcd_alloc,
+ hcd_free: ehci_hcd_free,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ urb_enqueue: ehci_urb_enqueue,
+ urb_dequeue: ehci_urb_dequeue,
+ free_config: ehci_free_config,
+
+ /*
+ * scheduling support
+ */
+ get_frame_number: ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ hub_status_data: ehci_hub_status_data,
+ hub_control: ehci_hub_control,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* EHCI spec says PCI is required. */
+
+/* PCI driver selection metadata; PCI hotplugging uses this */
+static const struct pci_device_id __devinitdata pci_ids [] = { {
+
+ /* handle any USB 2.0 EHCI controller */
+
+ class: ((PCI_CLASS_SERIAL_USB << 8) | 0x20),
+ class_mask: ~0,
+ driver_data: (unsigned long) &ehci_driver,
+
+ /* no matter who makes it */
+ vendor: PCI_ANY_ID,
+ device: PCI_ANY_ID,
+ subvendor: PCI_ANY_ID,
+ subdevice: PCI_ANY_ID,
+
+}, { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE (pci, pci_ids);
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct pci_driver ehci_pci_driver = {
+ name: (char *) hcd_name,
+ id_table: pci_ids,
+
+ probe: usb_hcd_pci_probe,
+ remove: usb_hcd_pci_remove,
+
+#ifdef CONFIG_PM
+ suspend: usb_hcd_pci_suspend,
+ resume: usb_hcd_pci_resume,
+#endif
+};
+
+#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION (DRIVER_INFO);
+MODULE_AUTHOR (DRIVER_AUTHOR);
+MODULE_LICENSE ("GPL");
+
+static int __init init (void)
+{
+ dbg (DRIVER_INFO);
+ dbg ("block sizes: qh %d qtd %d itd %d sitd %d",
+ sizeof (struct ehci_qh), sizeof (struct ehci_qtd),
+ sizeof (struct ehci_itd), sizeof (struct ehci_sitd));
+
+ return pci_module_init (&ehci_pci_driver);
+}
+module_init (init);
+
+static void __exit cleanup (void)
+{
+ pci_unregister_driver (&ehci_pci_driver);
+}
+module_exit (cleanup);
--- /dev/null
+/*
+ * Copyright (c) 2001 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Root Hub ... the nonsharable stuff
+ *
+ * Registers don't need cpu_to_le32, that happens transparently
+ */
+
+/*-------------------------------------------------------------------------*/
+
+static int check_reset_complete (
+ struct ehci_hcd *ehci,
+ int index,
+ int port_status
+) {
+ if (!(port_status & PORT_CONNECT)) {
+ ehci->reset_done [index] = 0;
+ return port_status;
+ }
+
+ /* if reset finished and it's still not enabled -- handoff */
+ if (!(port_status & PORT_PE)) {
+ dbg ("%s port %d full speed, give to companion, 0x%x",
+ ehci->hcd.bus_name, index + 1, port_status);
+
+ // what happens if HCS_N_CC(params) == 0 ?
+ port_status |= PORT_OWNER;
+ writel (port_status, &ehci->regs->port_status [index]);
+
+ } else
+ dbg ("%s port %d high speed", ehci->hcd.bus_name, index + 1);
+
+ return port_status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+
+/* build "status change" packet (one or two bytes) from HC registers */
+
+static int
+ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 temp, status = 0;
+ int ports, i, retval = 1;
+ unsigned long flags;
+
+ /* init status to no-changes */
+ buf [0] = 0;
+ temp = readl (&ehci->caps->hcs_params);
+ ports = HCS_N_PORTS (temp);
+ if (ports > 7) {
+ buf [1] = 0;
+ retval++;
+ }
+
+ /* no hub change reports (bit 0) for now (power, ...) */
+
+ /* port N changes (bit N)? */
+ spin_lock_irqsave (&ehci->lock, flags);
+ for (i = 0; i < ports; i++) {
+ temp = readl (&ehci->regs->port_status [i]);
+ if (temp & PORT_OWNER) {
+ // get disconnected ports back if no companion driver
+ if (temp & PORT_CONNECT)
+ continue;
+ temp &= ~(PORT_OWNER|PORT_CSC);
+ writel (temp, &ehci->regs->port_status [i]);
+ }
+ if (!(temp & PORT_CONNECT))
+ ehci->reset_done [i] = 0;
+ if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) {
+ set_bit (i, buf);
+ status = STS_PCD;
+ }
+ }
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return status ? retval : 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+ehci_hub_descriptor (
+ struct ehci_hcd *ehci,
+ struct usb_hub_descriptor *desc
+) {
+ u32 params = readl (&ehci->caps->hcs_params);
+ int ports = HCS_N_PORTS (params);
+ u16 temp;
+
+ desc->bDescriptorType = 0x29;
+ desc->bPwrOn2PwrGood = 0; /* FIXME: f(system power) */
+ desc->bHubContrCurrent = 0;
+
+ desc->bNbrPorts = ports;
+ temp = 1 + (ports / 8);
+ desc->bDescLength = 7 + 2 * temp;
+
+ /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+ memset (&desc->bitmap [0], 0, temp);
+ memset (&desc->bitmap [temp], 0xff, temp);
+
+ temp = 0x0008; /* per-port overcurrent reporting */
+ if (HCS_PPC (params)) /* per-port power control */
+ temp |= 0x0001;
+ if (HCS_INDICATOR (params)) /* per-port indicators (LEDs) */
+ temp |= 0x0080;
+ desc->wHubCharacteristics = cpu_to_le16 (temp);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int ehci_hub_control (
+ struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue,
+ u16 wIndex,
+ char *buf,
+ u16 wLength
+) {
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ u32 params = readl (&ehci->caps->hcs_params);
+ int ports = HCS_N_PORTS (params);
+ u32 temp;
+ unsigned long flags;
+ int retval = 0;
+
+ /*
+ * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR.
+ * HCS_INDICATOR may say we can change LEDs to off/amber/green.
+ * (track current state ourselves) ... blink for diagnostics,
+ * power, "this is the one", etc. EHCI spec supports this.
+ */
+
+ spin_lock_irqsave (&ehci->lock, flags);
+ switch (typeReq) {
+ case ClearHubFeature:
+ switch (wValue) {
+ case C_HUB_LOCAL_POWER:
+ case C_HUB_OVER_CURRENT:
+ /* no hub-wide feature/status flags */
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case ClearPortFeature:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ temp = readl (&ehci->regs->port_status [wIndex]);
+ if (temp & PORT_OWNER)
+ break;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ writel (temp & ~PORT_PE,
+ &ehci->regs->port_status [wIndex]);
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ writel (temp | PORT_PEC,
+ &ehci->regs->port_status [wIndex]);
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ case USB_PORT_FEAT_C_SUSPEND:
+ /* ? */
+ break;
+ case USB_PORT_FEAT_POWER:
+ if (HCS_PPC (params))
+ writel (temp & ~PORT_POWER,
+ &ehci->regs->port_status [wIndex]);
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ writel (temp | PORT_CSC,
+ &ehci->regs->port_status [wIndex]);
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ writel (temp | PORT_OCC,
+ &ehci->regs->port_status [wIndex]);
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ /* GetPortStatus clears reset */
+ break;
+ default:
+ goto error;
+ }
+ readl (&ehci->regs->command); /* unblock posted write */
+ break;
+ case GetHubDescriptor:
+ ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *)
+ buf);
+ break;
+ case GetHubStatus:
+ /* no hub-wide feature/status flags */
+ memset (buf, 0, 4);
+ //cpu_to_le32s ((u32 *) buf);
+ break;
+ case GetPortStatus:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ memset (buf, 0, 4);
+ temp = readl (&ehci->regs->port_status [wIndex]);
+
+ // wPortChange bits
+ if (temp & PORT_CSC)
+ set_bit (USB_PORT_FEAT_C_CONNECTION, buf);
+ if (temp & PORT_PEC)
+ set_bit (USB_PORT_FEAT_C_ENABLE, buf);
+ // USB_PORT_FEAT_C_SUSPEND
+ if (temp & PORT_OCC)
+ set_bit (USB_PORT_FEAT_C_OVER_CURRENT, buf);
+
+ /* whoever resets must GetPortStatus to complete it!! */
+ if ((temp & PORT_RESET)
+ && jiffies > ehci->reset_done [wIndex]) {
+ set_bit (USB_PORT_FEAT_C_RESET, buf);
+
+ /* force reset to complete */
+ writel (temp & ~PORT_RESET,
+ &ehci->regs->port_status [wIndex]);
+ do {
+ temp = readl (
+ &ehci->regs->port_status [wIndex]);
+ udelay (10);
+ } while (temp & PORT_RESET);
+
+ /* see what we found out */
+ temp = check_reset_complete (ehci, wIndex, temp);
+ }
+
+ // don't show wPortStatus if it's owned by a companion hc
+ if (!(temp & PORT_OWNER)) {
+ if (temp & PORT_CONNECT) {
+ set_bit (USB_PORT_FEAT_CONNECTION, buf);
+ set_bit (USB_PORT_FEAT_HIGHSPEED, buf);
+ }
+ if (temp & PORT_PE)
+ set_bit (USB_PORT_FEAT_ENABLE, buf);
+ if (temp & PORT_SUSPEND)
+ set_bit (USB_PORT_FEAT_SUSPEND, buf);
+ if (temp & PORT_OC)
+ set_bit (USB_PORT_FEAT_OVER_CURRENT, buf);
+ if (temp & PORT_RESET)
+ set_bit (USB_PORT_FEAT_RESET, buf);
+ if (temp & PORT_POWER)
+ set_bit (USB_PORT_FEAT_POWER, buf);
+ }
+
+#ifndef EHCI_VERBOSE_DEBUG
+ if (*(u16*)(buf+2)) /* only if wPortChange is interesting */
+#endif
+ dbg_port (hcd, "GetStatus", wIndex + 1, temp);
+ cpu_to_le32s ((u32 *) buf);
+ break;
+ case SetHubFeature:
+ switch (wValue) {
+ case C_HUB_LOCAL_POWER:
+ case C_HUB_OVER_CURRENT:
+ /* no hub-wide feature/status flags */
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case SetPortFeature:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ temp = readl (&ehci->regs->port_status [wIndex]);
+ if (temp & PORT_OWNER)
+ break;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ writel (temp | PORT_SUSPEND,
+ &ehci->regs->port_status [wIndex]);
+ break;
+ case USB_PORT_FEAT_POWER:
+ if (HCS_PPC (params))
+ writel (temp | PORT_POWER,
+ &ehci->regs->port_status [wIndex]);
+ break;
+ case USB_PORT_FEAT_RESET:
+ /* line status bits may report this as low speed */
+ if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
+ && PORT_USB11 (temp)) {
+ dbg ("%s port %d low speed, give to companion",
+ hcd->bus_name, wIndex + 1);
+ temp |= PORT_OWNER;
+ } else {
+ vdbg ("%s port %d reset",
+ hcd->bus_name, wIndex + 1);
+ temp |= PORT_RESET;
+ temp &= ~PORT_PE;
+
+ /*
+ * caller must wait, then call GetPortStatus
+ * usb 2.0 spec says 50 ms resets on root
+ */
+ ehci->reset_done [wIndex] = jiffies
+ + ((50 /* msec */ * HZ) / 1000);
+ }
+ writel (temp, &ehci->regs->port_status [wIndex]);
+ break;
+ default:
+ goto error;
+ }
+ readl (&ehci->regs->command); /* unblock posted writes */
+ break;
+
+ default:
+error:
+ /* "stall" on error */
+ retval = -EPIPE;
+ }
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return retval;
+}
--- /dev/null
+/*
+ * Copyright (c) 2001 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * There's basically three types of memory:
+ * - data used only by the HCD ... kmalloc is fine
+ * - async and periodic schedules, shared by HC and HCD ... these
+ * need to use pci_pool or pci_alloc_consistent
+ * - driver buffers, read/written by HC ... single shot DMA mapped
+ *
+ * There's also PCI "register" data, which is memory mapped.
+ * No memory seen by this driver is pagable.
+ */
+
+/*-------------------------------------------------------------------------*/
+/*
+ * Allocator / cleanup for the per device structure
+ * Called by hcd init / removal code
+ */
+static struct usb_hcd *ehci_hcd_alloc (void)
+{
+ struct ehci_hcd *ehci;
+
+ ehci = (struct ehci_hcd *)
+ kmalloc (sizeof (struct ehci_hcd), GFP_KERNEL);
+ if (ehci != 0) {
+ memset (ehci, 0, sizeof (struct ehci_hcd));
+ return &ehci->hcd;
+ }
+ return 0;
+}
+
+static void ehci_hcd_free (struct usb_hcd *hcd)
+{
+ kfree (hcd_to_ehci (hcd));
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Allocate the key transfer structures from the previously allocated pool */
+
+static struct ehci_qtd *ehci_qtd_alloc (struct ehci_hcd *ehci, int flags)
+{
+ struct ehci_qtd *qtd;
+ dma_addr_t dma;
+
+ qtd = pci_pool_alloc (ehci->qtd_pool, flags, &dma);
+ if (qtd != 0) {
+ memset (qtd, 0, sizeof *qtd);
+ qtd->qtd_dma = dma;
+ qtd->hw_next = EHCI_LIST_END;
+ qtd->hw_alt_next = EHCI_LIST_END;
+ INIT_LIST_HEAD (&qtd->qtd_list);
+ }
+ return qtd;
+}
+
+static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd)
+{
+ pci_pool_free (ehci->qtd_pool, qtd, qtd->qtd_dma);
+}
+
+
+static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, int flags)
+{
+ struct ehci_qh *qh;
+ dma_addr_t dma;
+
+ qh = (struct ehci_qh *)
+ pci_pool_alloc (ehci->qh_pool, flags, &dma);
+ if (qh) {
+ memset (qh, 0, sizeof *qh);
+ atomic_set (&qh->refcount, 1);
+ qh->qh_dma = dma;
+ // INIT_LIST_HEAD (&qh->qh_list);
+ INIT_LIST_HEAD (&qh->qtd_list);
+ }
+ return qh;
+}
+
+/* to share a qh (cpu threads, or hc) */
+static inline struct ehci_qh *qh_put (/* ehci, */ struct ehci_qh *qh)
+{
+ // dbg ("put %p (%d++)", qh, qh->refcount.counter);
+ atomic_inc (&qh->refcount);
+ return qh;
+}
+
+static void qh_unput (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ // dbg ("unput %p (--%d)", qh, qh->refcount.counter);
+ if (!atomic_dec_and_test (&qh->refcount))
+ return;
+ /* clean qtds first, and know this is not linked */
+ if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) {
+ dbg ("unused qh not empty!");
+ BUG ();
+ }
+ pci_pool_free (ehci->qh_pool, qh, qh->qh_dma);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* The queue heads and transfer descriptors are managed from pools tied
+ * to each of the "per device" structures.
+ * This is the initialisation and cleanup code.
+ */
+
+static void ehci_mem_cleanup (struct ehci_hcd *ehci)
+{
+ /* PCI consistent memory and pools */
+ if (ehci->qtd_pool)
+ pci_pool_destroy (ehci->qtd_pool);
+ ehci->qtd_pool = 0;
+
+ if (ehci->qh_pool) {
+ pci_pool_destroy (ehci->qh_pool);
+ ehci->qh_pool = 0;
+ }
+
+ if (ehci->itd_pool)
+ pci_pool_destroy (ehci->itd_pool);
+ ehci->itd_pool = 0;
+
+ if (ehci->sitd_pool)
+ pci_pool_destroy (ehci->sitd_pool);
+ ehci->sitd_pool = 0;
+
+ if (ehci->periodic)
+ pci_free_consistent (ehci->hcd.pdev,
+ ehci->periodic_size * sizeof (u32),
+ ehci->periodic, ehci->periodic_dma);
+ ehci->periodic = 0;
+
+ /* shadow periodic table */
+ if (ehci->pshadow)
+ kfree (ehci->pshadow);
+ ehci->pshadow = 0;
+}
+
+/* remember to add cleanup code (above) if you add anything here */
+static int ehci_mem_init (struct ehci_hcd *ehci, int flags)
+{
+ int i;
+
+ /* QTDs for control/bulk/intr transfers */
+ ehci->qtd_pool = pci_pool_create ("ehci_qtd", ehci->hcd.pdev,
+ sizeof (struct ehci_qtd),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */,
+ flags);
+ if (!ehci->qtd_pool) {
+ dbg ("no qtd pool");
+ ehci_mem_cleanup (ehci);
+ return -ENOMEM;
+ }
+
+ /* QH for control/bulk/intr transfers */
+ ehci->qh_pool = pci_pool_create ("ehci_qh", ehci->hcd.pdev,
+ sizeof (struct ehci_qh),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */,
+ flags);
+ if (!ehci->qh_pool) {
+ dbg ("no qh pool");
+ ehci_mem_cleanup (ehci);
+ return -ENOMEM;
+ }
+
+ /* ITD for high speed ISO transfers */
+ ehci->itd_pool = pci_pool_create ("ehci_itd", ehci->hcd.pdev,
+ sizeof (struct ehci_itd),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */,
+ flags);
+ if (!ehci->itd_pool) {
+ dbg ("no itd pool");
+ ehci_mem_cleanup (ehci);
+ return -ENOMEM;
+ }
+
+ /* SITD for full/low speed split ISO transfers */
+ ehci->sitd_pool = pci_pool_create ("ehci_sitd", ehci->hcd.pdev,
+ sizeof (struct ehci_sitd),
+ 32 /* byte alignment (for hw parts) */,
+ 4096 /* can't cross 4K */,
+ flags);
+ if (!ehci->sitd_pool) {
+ dbg ("no sitd pool");
+ ehci_mem_cleanup (ehci);
+ return -ENOMEM;
+ }
+
+ /* Hardware periodic table */
+ ehci->periodic = (u32 *)
+ pci_alloc_consistent (ehci->hcd.pdev,
+ ehci->periodic_size * sizeof (u32),
+ &ehci->periodic_dma);
+ if (ehci->periodic == 0) {
+ dbg ("no hw periodic table");
+ ehci_mem_cleanup (ehci);
+ return -ENOMEM;
+ }
+ for (i = 0; i < ehci->periodic_size; i++)
+ ehci->periodic [i] = EHCI_LIST_END;
+
+ /* software shadow of hardware table */
+ ehci->pshadow = kmalloc (ehci->periodic_size * sizeof (void *),
+ flags & ~EHCI_SLAB_FLAGS);
+ if (ehci->pshadow == 0) {
+ dbg ("no shadow periodic table");
+ ehci_mem_cleanup (ehci);
+ return -ENOMEM;
+ }
+ memset (ehci->pshadow, 0, ehci->periodic_size * sizeof (void *));
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2001 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI hardware queue manipulation
+ *
+ * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd"
+ * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned
+ * buffers needed for the larger number). We use one QH per endpoint, queue
+ * multiple (bulk or control) urbs per endpoint. URBs may need several qtds.
+ * A scheduled interrupt qh always has one qtd, one urb.
+ *
+ * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with
+ * interrupts) needs careful scheduling. Performance improvements can be
+ * an ongoing challenge.
+ *
+ * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs,
+ * or otherwise through transaction translators (TTs) in USB 2.0 hubs using
+ * (b) special fields in qh entries or (c) split iso entries. TTs will
+ * buffer low/full speed data so the host collects it at high speed.
+ */
+
+#ifdef EHCI_SOFT_RETRIES
+static int soft_retries = EHCI_SOFT_RETRIES;
+MODULE_PARM (soft_retries, "i");
+MODULE_PARM_DESC (soft_retries, "Number of software retries for endpoint i/o");
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* fill a qtd, returning how much of the buffer we were able to queue up */
+
+static int
+qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len, int token)
+{
+ int i, count;
+
+ /* one buffer entry per 4K ... first might be short or unaligned */
+ qtd->hw_buf [0] = cpu_to_le32 (buf);
+ count = 0x1000 - (buf & 0x0fff); /* rest of that page */
+ if (likely (len < count)) /* ... iff needed */
+ count = len;
+ else {
+ buf += 0x1000;
+ buf &= ~0x0fff;
+
+ /* per-qtd limit: from 16K to 20K (best alignment) */
+ for (i = 1; count < len && i < 5; i++) {
+ u64 addr = buf;
+ qtd->hw_buf [i] = cpu_to_le32 ((u32)addr);
+ qtd->hw_buf_hi [i] = cpu_to_le32 ((u32)(addr >> 32));
+ buf += 0x1000;
+ if ((count + 0x1000) < len)
+ count += 0x1000;
+ else
+ count = len;
+ }
+ }
+ qtd->hw_token = cpu_to_le32 ((count << 16) | token);
+ qtd->length = count;
+
+#if 0
+ vdbg (" qtd_fill %p, token %8x bytes %d dma %x",
+ qtd, le32_to_cpu (qtd->hw_token), count, qtd->hw_buf [0]);
+#endif
+
+ return count;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* update halted (but potentially linked) qh */
+
+static inline void qh_update (struct ehci_qh *qh, struct ehci_qtd *qtd)
+{
+ qh->hw_current = 0;
+ qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma);
+ qh->hw_alt_next = EHCI_LIST_END;
+
+ /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
+ qh->hw_token &= __constant_cpu_to_le32 (QTD_TOGGLE | QTD_STS_PING);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void qtd_copy_status (struct urb *urb, size_t length, u32 token)
+{
+ /* count IN/OUT bytes, not SETUP (even short packets) */
+ if (likely (QTD_PID (token) != 2))
+ urb->actual_length += length - QTD_LENGTH (token);
+
+ /* don't modify error codes */
+ if (unlikely (urb->status == -EINPROGRESS && (token & QTD_STS_HALT))) {
+ if (token & QTD_STS_BABBLE) {
+ urb->status = -EOVERFLOW;
+ } else if (!QTD_CERR (token)) {
+ if (token & QTD_STS_DBE)
+ urb->status = (QTD_PID (token) == 1) /* IN ? */
+ ? -ENOSR /* hc couldn't read data */
+ : -ECOMM; /* hc couldn't write data */
+ else if (token & QTD_STS_MMF) /* missed tt uframe */
+ urb->status = -EPROTO;
+ else if (token & QTD_STS_XACT) {
+ if (QTD_LENGTH (token))
+ urb->status = -EPIPE;
+ else {
+ dbg ("3strikes");
+ urb->status = -EPROTO;
+ }
+ } else /* presumably a stall */
+ urb->status = -EPIPE;
+
+ /* CERR nonzero + data left + halt --> stall */
+ } else if (QTD_LENGTH (token))
+ urb->status = -EPIPE;
+ else /* unknown */
+ urb->status = -EPROTO;
+ dbg ("devpath %s ep %d-%s qtd token %x --> status %d",
+ urb->dev->devpath, usb_pipeendpoint (urb->pipe),
+ usb_pipein (urb->pipe) ? "in" : "out",
+ token, urb->status);
+
+ /* stall indicates some recovery action is needed */
+ if (urb->status == -EPIPE) {
+ int pipe = urb->pipe;
+
+ if (!usb_pipecontrol (pipe))
+ usb_endpoint_halt (urb->dev,
+ usb_pipeendpoint (pipe),
+ usb_pipeout (pipe));
+ if (urb->dev->tt && !usb_pipeint (pipe)) {
+err ("must CLEAR_TT_BUFFER, hub %s port %d%s addr %d ep %d",
+ urb->dev->tt->hub->devpath, urb->dev->ttport,
+ urb->dev->tt->multi ? "" : " (all-ports TT)",
+ urb->dev->devnum, usb_pipeendpoint (urb->pipe));
+ // FIXME something (khubd?) should make the hub
+ // CLEAR_TT_BUFFER ASAP, it's blocking other
+ // fs/ls requests... hub_tt_clear_buffer() ?
+ }
+ }
+ }
+}
+
+static void ehci_urb_complete (
+ struct ehci_hcd *ehci,
+ dma_addr_t addr,
+ struct urb *urb
+) {
+ if (urb->transfer_buffer_length && usb_pipein (urb->pipe))
+ pci_dma_sync_single (ehci->hcd.pdev, addr,
+ urb->transfer_buffer_length,
+ PCI_DMA_FROMDEVICE);
+
+ /* cleanse status if we saw no error */
+ if (likely (urb->status == -EINPROGRESS)) {
+ if (urb->actual_length != urb->transfer_buffer_length
+ && (urb->transfer_flags & USB_DISABLE_SPD))
+ urb->status = -EREMOTEIO;
+ else
+ urb->status = 0;
+ }
+
+ /* only report unlinks once */
+ if (likely (urb->status != -ENOENT && urb->status != -ENOTCONN))
+ urb->complete (urb);
+}
+
+/* urb->lock ignored from here on (hcd is done with urb) */
+
+static void ehci_urb_done (
+ struct ehci_hcd *ehci,
+ dma_addr_t addr,
+ struct urb *urb
+) {
+ if (urb->transfer_buffer_length)
+ pci_unmap_single (ehci->hcd.pdev,
+ addr,
+ urb->transfer_buffer_length,
+ usb_pipein (urb->pipe)
+ ? PCI_DMA_FROMDEVICE
+ : PCI_DMA_TODEVICE);
+ if (likely (urb->hcpriv != 0)) {
+ qh_unput (ehci, (struct ehci_qh *) urb->hcpriv);
+ urb->hcpriv = 0;
+ }
+
+ if (likely (urb->status == -EINPROGRESS)) {
+ if (urb->actual_length != urb->transfer_buffer_length
+ && (urb->transfer_flags & USB_DISABLE_SPD))
+ urb->status = -EREMOTEIO;
+ else
+ urb->status = 0;
+ }
+
+ /* hand off urb ownership */
+ usb_hcd_giveback_urb (&ehci->hcd, urb);
+}
+
+
+/*
+ * Process completed qtds for a qh, issuing completions if needed.
+ * When freeing: frees qtds, unmaps buf, returns URB to driver.
+ * When not freeing (queued periodic qh): retain qtds, mapping, and urb.
+ * Races up to qh->hw_current; returns number of urb completions.
+ */
+static int
+qh_completions (
+ struct ehci_hcd *ehci,
+ struct list_head *qtd_list,
+ int freeing
+) {
+ struct ehci_qtd *qtd = 0;
+ struct list_head *next = 0;
+ u32 token;
+ struct ehci_qh *qh = 0;
+ struct urb *urb = 0;
+ int halted = 0;
+ unsigned long flags;
+ int retval = 0;
+
+ spin_lock_irqsave (&ehci->lock, flags);
+ if (unlikely (list_empty (qtd_list))) {
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return retval;
+ }
+
+ for (qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list);
+ next != qtd_list;
+ qtd = list_entry (next, struct ehci_qtd, qtd_list)) {
+ token = le32_to_cpu (qtd->hw_token);
+ if (!qh) {
+ urb = qtd->urb;
+ qh = (struct ehci_qh *) urb->hcpriv;
+ }
+ if (likely (qh != 0)) {
+ halted = halted
+ || (ehci->hcd.state == USB_STATE_HALT)
+ || (qh->qh_state == QH_STATE_IDLE);
+
+ if (unlikely ((token & QTD_STS_HALT) != 0)) {
+#ifdef EHCI_SOFT_RETRIES
+ /* extra soft retries for protocol errors */
+ if (!halted
+ && qh->retries < soft_retries
+ && (QTD_STS_HALT|QTD_STS_XACT)
+ == (token & 0xff)
+ && QTD_CERR (token) == 0) {
+ if (qh->retries == 0)
+ dbg ("soft retry, qh %p qtd %p",
+ qh, qtd);
+ qh->retries++;
+ token &= ~0x0ff;
+ token |= QTD_STS_ACTIVE;
+ token |= (EHCI_TUNE_CERR << 10);
+ /* qtd update not needed */
+ qh->hw_token = cpu_to_le32 (token);
+ spin_unlock_irqrestore (&ehci->lock,
+ flags);
+ return;
+
+ } else if (qh->retries >= soft_retries
+ && soft_retries) {
+ dbg ("retried %d times, qh %p qtd %p",
+ qh->retries, qh, qtd);
+ }
+#endif /* EHCI_SOFT_RETRIES */
+ halted = 1;
+ }
+
+ if (unlikely ((token & QTD_STS_ACTIVE) != 0)) {
+ /* stop scan if qtd is visible to the HC */
+ if (!halted) {
+ urb = 0;
+ break;
+ }
+
+ /* continue cleanup if HC is halted */
+ if (ehci->hcd.state == USB_STATE_HALT) {
+ urb->status = -ESHUTDOWN;
+ goto scrub;
+ }
+
+ /* stall? some other urb was unlinked? */
+ if (urb->status == -EINPROGRESS) {
+dbg ("?why? qh %p, qtd %p halted, urb %p, token %8x, len %d",
+ qh, qtd, urb, token, urb->actual_length);
+spin_unlock_irqrestore (&ehci->lock, flags);
+return retval;
+ /*
+ * FIXME: write this code. When one queued urb is unlinked,
+ * unlink every succeeding urb.
+ */
+ continue;
+ }
+
+ /* else stopped for some other reason */
+ }
+scrub:
+ spin_lock (&urb->lock);
+ qtd_copy_status (urb, qtd->length, token);
+ spin_unlock (&urb->lock);
+ }
+ next = qtd->qtd_list.next;
+
+ /*
+ * NOTE: this won't work right with interrupt urbs that
+ * need multiple qtds ... only the first scan of qh->qtd_list
+ * starts at the right qtd, yet multiple scans could happen
+ * for transfers that are scheduled across multiple uframes.
+ */
+ if (likely (freeing != 0))
+ list_del (&qtd->qtd_list);
+ else {
+ /* restore everything the HC could change
+ * from an interrupt QTD
+ */
+ qtd->hw_token = (qtd->hw_token
+ & ~__constant_cpu_to_le32 (0x8300))
+ | cpu_to_le32 (qtd->length << 16)
+ | __constant_cpu_to_le32 (QTD_IOC
+ | (EHCI_TUNE_CERR << 10)
+ | QTD_STS_ACTIVE);
+ qtd->hw_buf [0] &= ~__constant_cpu_to_le32 (0x0fff);
+
+ /* this offset, and the length above,
+ * are likely wrong on QTDs #2..N
+ */
+ qtd->hw_buf [0] |= cpu_to_le32 (0x0fff & qtd->buf_dma);
+ }
+
+ spin_unlock_irqrestore (&ehci->lock, flags);
+
+#if 0
+ if (urb->status == -EINPROGRESS)
+ vdbg (" qtd %p ok, urb %p, token %8x, len %d",
+ qtd, urb, token, urb->actual_length);
+ else
+ vdbg ("urb %p status %d, qtd %p, token %8x, len %d",
+ urb, urb->status, qtd, token,
+ urb->actual_length);
+#endif
+
+ /* SETUP for control urb? */
+ if (unlikely (QTD_PID (token) == 2))
+ pci_unmap_single (ehci->hcd.pdev,
+ qtd->buf_dma, sizeof (devrequest),
+ PCI_DMA_TODEVICE);
+
+ /* another queued urb? */
+ if (unlikely (qtd->urb != urb)) {
+ if (likely (freeing != 0))
+ ehci_urb_done (ehci, qtd->buf_dma, urb);
+ else
+ ehci_urb_complete (ehci, qtd->buf_dma, urb);
+ retval++;
+ urb = qtd->urb;
+ }
+
+ if (likely (freeing != 0))
+ ehci_qtd_free (ehci, qtd);
+ spin_lock_irqsave (&ehci->lock, flags);
+ qtd = list_entry (next, struct ehci_qtd, qtd_list);
+ }
+
+ /* patch up list head? */
+ if (unlikely (halted && qh && !list_empty (qtd_list))) {
+ qh_update (qh, list_entry (qtd_list->next,
+ struct ehci_qtd, qtd_list));
+ }
+ spin_unlock_irqrestore (&ehci->lock, flags);
+
+ /* last urb's completion might still need calling */
+ if (likely (qtd && urb)) {
+ if (likely (freeing != 0))
+ ehci_urb_done (ehci, qtd->buf_dma, urb);
+ else
+ ehci_urb_complete (ehci, qtd->buf_dma, urb);
+ retval++;
+ }
+ return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * create a list of filled qtds for this URB; won't link into qh.
+ */
+static struct list_head *
+qh_urb_transaction (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct list_head *head,
+ int flags
+) {
+ struct ehci_qtd *qtd, *qtd_prev;
+ dma_addr_t buf, map_buf;
+ int len, maxpacket;
+ u32 token;
+
+ /*
+ * URBs map to sequences of QTDs: one logical transaction
+ */
+ qtd = ehci_qtd_alloc (ehci, flags);
+ if (unlikely (!qtd))
+ return 0;
+ qtd_prev = 0;
+ list_add_tail (&qtd->qtd_list, head);
+ qtd->urb = urb;
+
+ token = QTD_STS_ACTIVE;
+ token |= (EHCI_TUNE_CERR << 10);
+ /* for split transactions, SplitXState initialized to zero */
+
+ if (usb_pipecontrol (urb->pipe)) {
+ /* control request data is passed in the "setup" pid */
+
+ /* NOTE: this isn't smart about 64bit DMA, since it uses the
+ * default (32bit) mask rather than using the whole address
+ * space. we could set pdev->dma_mask to all-ones while
+ * getting this mapping, locking it and restoring before
+ * allocating qtd/qh/... or maybe only do that for the main
+ * data phase (below).
+ */
+ qtd->buf_dma = pci_map_single (
+ ehci->hcd.pdev,
+ urb->setup_packet,
+ sizeof (devrequest),
+ PCI_DMA_TODEVICE);
+ if (unlikely (!qtd->buf_dma))
+ goto cleanup;
+
+ /* SETUP pid */
+ qtd_fill (qtd, qtd->buf_dma, sizeof (devrequest),
+ token | (2 /* "setup" */ << 8));
+
+ /* ... and always at least one more pid */
+ token ^= QTD_TOGGLE;
+ qtd_prev = qtd;
+ qtd = ehci_qtd_alloc (ehci, flags);
+ if (unlikely (!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
+ list_add_tail (&qtd->qtd_list, head);
+ }
+
+ /*
+ * data transfer stage: buffer setup
+ */
+ len = urb->transfer_buffer_length;
+ if (likely (len > 0)) {
+ /* NOTE: sub-optimal mapping with 64bit DMA (see above) */
+ buf = map_buf = pci_map_single (ehci->hcd.pdev,
+ urb->transfer_buffer, len,
+ usb_pipein (urb->pipe)
+ ? PCI_DMA_FROMDEVICE
+ : PCI_DMA_TODEVICE);
+ if (unlikely (!buf))
+ goto cleanup;
+ } else
+ buf = map_buf = 0;
+
+ if (!buf || usb_pipein (urb->pipe))
+ token |= (1 /* "in" */ << 8);
+ /* else it's already initted to "out" pid (0 << 8) */
+
+ maxpacket = usb_maxpacket (urb->dev, urb->pipe,
+ usb_pipeout (urb->pipe));
+
+ /*
+ * buffer gets wrapped in one or more qtds;
+ * last one may be "short" (including zero len)
+ * and may serve as a control status ack
+ */
+ for (;;) {
+ int this_qtd_len;
+
+ qtd->urb = urb;
+ qtd->buf_dma = map_buf;
+ this_qtd_len = qtd_fill (qtd, buf, len, token);
+ len -= this_qtd_len;
+ buf += this_qtd_len;
+
+ /* qh makes control packets use qtd toggle; maybe switch it */
+ if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
+ token ^= QTD_TOGGLE;
+
+ if (likely (len <= 0))
+ break;
+
+ qtd_prev = qtd;
+ qtd = ehci_qtd_alloc (ehci, flags);
+ if (unlikely (!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
+ list_add_tail (&qtd->qtd_list, head);
+ }
+
+ /*
+ * control requests may need a terminating data "status" ack;
+ * bulk ones may need a terminating short packet (zero length).
+ */
+ if (likely (buf != 0)) {
+ int one_more = 0;
+
+ if (usb_pipecontrol (urb->pipe)) {
+ one_more = 1;
+ token ^= 0x0100; /* "in" <--> "out" */
+ token |= QTD_TOGGLE; /* force DATA1 */
+ } else if (usb_pipebulk (urb->pipe)
+ && (urb->transfer_flags & USB_ZERO_PACKET)
+ && !(urb->transfer_buffer_length % maxpacket)) {
+ one_more = 1;
+ }
+ if (one_more) {
+ qtd_prev = qtd;
+ qtd = ehci_qtd_alloc (ehci, flags);
+ if (unlikely (!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
+ list_add_tail (&qtd->qtd_list, head);
+
+ /* never any data in such packets */
+ qtd_fill (qtd, 0, 0, token);
+ }
+ }
+
+ /* by default, enable interrupt on urb completion */
+ if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT)))
+ qtd->hw_token |= __constant_cpu_to_le32 (QTD_IOC);
+ return head;
+
+cleanup:
+ urb->status = -ENOMEM;
+ qh_completions (ehci, head, 1);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Hardware maintains data toggle (like OHCI) ... here we (re)initialize
+ * the hardware data toggle in the QH, and set the pseudo-toggle in udev
+ * so we can see if usb_clear_halt() was called. NOP for control, since
+ * we set up qh->hw_info1 to always use the QTD toggle bits.
+ */
+static inline void
+clear_toggle (struct usb_device *udev, int ep, int is_out, struct ehci_qh *qh)
+{
+ vdbg ("clear toggle, dev %d ep 0x%x-%s",
+ udev->devnum, ep, is_out ? "out" : "in");
+ qh->hw_token &= ~__constant_cpu_to_le32 (QTD_TOGGLE);
+ usb_settoggle (udev, ep, is_out, 1);
+}
+
+// Would be best to create all qh's from config descriptors,
+// when each interface/altsetting is established. Unlink
+// any previous qh and cancel its urbs first; endpoints are
+// implicitly reset then (data toggle too).
+// That'd mean updating how usbcore talks to HCDs. (2.5?)
+
+
+/*
+ * Each QH holds a qtd list; a QH is used for everything except iso.
+ *
+ * For interrupt urbs, the scheduler must set the microframe scheduling
+ * mask(s) each time the QH gets scheduled. For highspeed, that's
+ * just one microframe in the s-mask. For split interrupt transactions
+ * there are additional complications: c-mask, maybe FSTNs.
+ */
+static struct ehci_qh *
+ehci_qh_make (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct list_head *qtd_list,
+ int flags
+) {
+ struct ehci_qh *qh = ehci_qh_alloc (ehci, flags);
+ u32 info1 = 0, info2 = 0;
+
+ if (!qh)
+ return qh;
+
+ /*
+ * init endpoint/device data for this QH
+ */
+ info1 |= usb_pipeendpoint (urb->pipe) << 8;
+ info1 |= usb_pipedevice (urb->pipe) << 0;
+
+ /* using TT? */
+ switch (urb->dev->speed) {
+ case USB_SPEED_LOW:
+ info1 |= (1 << 12); /* EPS "low" */
+ /* FALL THROUGH */
+
+ case USB_SPEED_FULL:
+ /* EPS 0 means "full" */
+ info1 |= (EHCI_TUNE_RL_TT << 28);
+ if (usb_pipecontrol (urb->pipe)) {
+ info1 |= (1 << 27); /* for TT */
+ info1 |= 1 << 14; /* toggle from qtd */
+ }
+ info1 |= usb_maxpacket (urb->dev, urb->pipe,
+ usb_pipeout (urb->pipe)) << 16;
+
+ info2 |= (EHCI_TUNE_MULT_TT << 30);
+ info2 |= urb->dev->ttport << 23;
+ info2 |= urb->dev->tt->hub->devnum << 16;
+
+ /* NOTE: if (usb_pipeint (urb->pipe)) { scheduler sets c-mask }
+ * ... and a 0.96 scheduler might use FSTN nodes too
+ */
+ break;
+
+ case USB_SPEED_HIGH: /* no TT involved */
+ info1 |= (2 << 12); /* EPS "high" */
+ info1 |= (EHCI_TUNE_RL_HS << 28);
+ if (usb_pipecontrol (urb->pipe)) {
+ info1 |= 64 << 16; /* usb2 fixed maxpacket */
+ info1 |= 1 << 14; /* toggle from qtd */
+ } else if (usb_pipebulk (urb->pipe)) {
+ info1 |= 512 << 16; /* usb2 fixed maxpacket */
+ info2 |= (EHCI_TUNE_MULT_HS << 30);
+ } else
+ info1 |= usb_maxpacket (urb->dev, urb->pipe,
+ usb_pipeout (urb->pipe)) << 16;
+ break;
+ default:
+#ifdef DEBUG
+ BUG ();
+#endif
+ }
+
+ /* NOTE: if (usb_pipeint (urb->pipe)) { scheduler sets s-mask } */
+
+ qh->qh_state = QH_STATE_IDLE;
+ qh->hw_info1 = cpu_to_le32 (info1);
+ qh->hw_info2 = cpu_to_le32 (info2);
+
+ /* initialize sw and hw queues with these qtds */
+ list_splice (qtd_list, &qh->qtd_list);
+ qh_update (qh, list_entry (qtd_list->next, struct ehci_qtd, qtd_list));
+
+ /* initialize data toggle state */
+ if (!usb_pipecontrol (urb->pipe))
+ clear_toggle (urb->dev,
+ usb_pipeendpoint (urb->pipe),
+ usb_pipeout (urb->pipe),
+ qh);
+
+ return qh;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* move qh (and its qtds) onto async queue; maybe enable queue. */
+
+static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ u32 dma = QH_NEXT (qh->qh_dma);
+ struct ehci_qh *q;
+
+ if (unlikely (!(q = ehci->async))) {
+ u32 cmd = readl (&ehci->regs->command);
+
+ /* in case a clear of CMD_ASE didn't take yet */
+ while (readl (&ehci->regs->status) & STS_ASS)
+ udelay (100);
+
+ qh->hw_info1 |= __constant_cpu_to_le32 (QH_HEAD); /* [4.8] */
+ qh->qh_next.qh = qh;
+ qh->hw_next = dma;
+ ehci->async = qh;
+ writel ((u32)qh->qh_dma, &ehci->regs->async_next);
+ cmd |= CMD_ASE | CMD_RUN;
+ writel (cmd, &ehci->regs->command);
+ ehci->hcd.state = USB_STATE_RUNNING;
+ /* posted write need not be known to HC yet ... */
+ } else {
+ /* splice right after "start" of ring */
+ qh->hw_info1 &= ~__constant_cpu_to_le32 (QH_HEAD); /* [4.8] */
+ qh->qh_next = q->qh_next;
+ qh->hw_next = q->hw_next;
+ q->qh_next.qh = qh;
+ q->hw_next = dma;
+ }
+ qh->qh_state = QH_STATE_LINKED;
+ /* qtd completions reported later by interrupt */
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+submit_async (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct list_head *qtd_list,
+ int mem_flags
+) {
+ struct ehci_qtd *qtd;
+ struct hcd_dev *dev;
+ int epnum;
+ unsigned long flags;
+ struct ehci_qh *qh = 0;
+
+ qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list);
+ dev = (struct hcd_dev *)urb->dev->hcpriv;
+ epnum = usb_pipeendpoint (urb->pipe);
+ if (usb_pipein (urb->pipe))
+ epnum |= 0x10;
+
+ vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]",
+ ehci->hcd.bus_name, urb, urb->transfer_buffer_length,
+ epnum & 0x0f, (epnum & 0x10) ? "in" : "out",
+ qtd, dev ? dev->ep [epnum] : (void *)~0);
+
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ qh = (struct ehci_qh *) dev->ep [epnum];
+ if (likely (qh != 0)) {
+ u32 hw_next = QTD_NEXT (qtd->qtd_dma);
+
+ /* maybe patch the qh used for set_address */
+ if (unlikely (epnum == 0
+ && le32_to_cpu (qh->hw_info1 & 0x7f) == 0))
+ qh->hw_info1 |= cpu_to_le32 (usb_pipedevice(urb->pipe));
+
+ /* is an URB is queued to this qh already? */
+ if (unlikely (!list_empty (&qh->qtd_list))) {
+ struct ehci_qtd *last_qtd;
+
+ // dbg_qh ("non-empty qh", ehci, qh);
+ last_qtd = list_entry (qh->qtd_list.prev,
+ struct ehci_qtd, qtd_list);
+ last_qtd->hw_next = hw_next;
+
+ /* previous urb allows short rx? maybe optimize. */
+ if (!(last_qtd->urb->transfer_flags & USB_DISABLE_SPD)
+ && (epnum & 0x10)) {
+ // only the last QTD for now
+ last_qtd->hw_alt_next = hw_next;
+ }
+
+ /* no URB queued */
+ } else {
+ // dbg_qh ("empty qh", ehci, qh);
+
+// FIXME: how handle usb_clear_halt() for an EP with queued URBs?
+// usbcore may not let us handle that cleanly...
+// likely must cancel them all first!
+
+ /* usb_clear_halt() means qh data toggle gets reset */
+ if (usb_pipebulk (urb->pipe)
+ && unlikely (!usb_gettoggle (urb->dev,
+ (epnum & 0x0f),
+ !(epnum & 0x10)))) {
+ clear_toggle (urb->dev,
+ epnum & 0x0f, !(epnum & 0x10), qh);
+ }
+ qh_update (qh, qtd);
+ }
+ list_splice (qtd_list, qh->qtd_list.prev);
+
+ } else {
+ /* can't sleep here, we have ehci->lock... */
+ qh = ehci_qh_make (ehci, urb, qtd_list, SLAB_ATOMIC);
+ if (likely (qh != 0)) {
+ // dbg_qh ("new qh", ehci, qh);
+ dev->ep [epnum] = qh;
+ } else
+ urb->status = -ENOMEM;
+ }
+
+ /* Control/bulk operations through TTs don't need scheduling,
+ * the HC and TT handle it when the TT has a buffer ready.
+ */
+ if (likely (qh != 0)) {
+ urb->hcpriv = qh_put (qh);
+ if (likely (qh->qh_state == QH_STATE_IDLE))
+ qh_link_async (ehci, qh_put (qh));
+ }
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ if (unlikely (!qh))
+ qh_completions (ehci, qtd_list, 1);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* the async qh for the qtds being reclaimed are now unlinked from the HC */
+/* caller must not own ehci->lock */
+
+static void end_unlink_async (struct ehci_hcd *ehci)
+{
+ struct ehci_qh *qh = ehci->reclaim;
+
+ qh->qh_state = QH_STATE_IDLE;
+ qh->qh_next.qh = 0;
+ qh_unput (ehci, qh); // refcount from reclaim
+ ehci->reclaim = 0;
+ ehci->reclaim_ready = 0;
+
+ qh_completions (ehci, &qh->qtd_list, 1);
+
+ // FIXME unlink any urb should unlink all following urbs,
+ // so that this will never happen
+ if (!list_empty (&qh->qtd_list)
+ && HCD_IS_RUNNING (ehci->hcd.state))
+ qh_link_async (ehci, qh);
+ else
+ qh_unput (ehci, qh); // refcount from async list
+}
+
+
+/* makes sure the async qh will become idle */
+/* caller must own ehci->lock */
+
+static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ int cmd = readl (&ehci->regs->command);
+ struct ehci_qh *prev;
+
+#ifdef DEBUG
+ if (ehci->reclaim
+ || !ehci->async
+ || qh->qh_state != QH_STATE_LINKED
+#ifdef CONFIG_SMP
+// this macro lies except on SMP compiles
+ || !spin_is_locked (&ehci->lock)
+#endif
+ )
+ BUG ();
+#endif
+
+ qh->qh_state = QH_STATE_UNLINK;
+ ehci->reclaim = qh = qh_put (qh);
+
+ // dbg_qh ("start unlink", ehci, qh);
+
+ /* Remove the last QH (qhead)? Stop async schedule first. */
+ if (unlikely (qh == ehci->async && qh->qh_next.qh == qh)) {
+ /* can't get here without STS_ASS set */
+ if (ehci->hcd.state != USB_STATE_HALT) {
+ if (cmd & CMD_PSE)
+ writel (cmd & __constant_cpu_to_le32 (~CMD_ASE),
+ &ehci->regs->command);
+ else {
+ ehci_ready (ehci);
+ while (!(readl (&ehci->regs->status) & STS_ASS))
+ udelay (100);
+ }
+ }
+ qh->qh_next.qh = ehci->async = 0;
+
+ ehci->reclaim_ready = 1;
+ tasklet_schedule (&ehci->tasklet);
+ return;
+ }
+
+ if (unlikely (ehci->hcd.state == USB_STATE_HALT)) {
+ ehci->reclaim_ready = 1;
+ tasklet_schedule (&ehci->tasklet);
+ return;
+ }
+
+ prev = ehci->async;
+ while (prev->qh_next.qh != qh && prev->qh_next.qh != ehci->async)
+ prev = prev->qh_next.qh;
+#ifdef DEBUG
+ if (prev->qh_next.qh != qh)
+ BUG ();
+#endif
+
+ if (qh->hw_info1 & __constant_cpu_to_le32 (QH_HEAD)) {
+ ehci->async = prev;
+ prev->hw_info1 |= __constant_cpu_to_le32 (QH_HEAD);
+ }
+ prev->hw_next = qh->hw_next;
+ prev->qh_next = qh->qh_next;
+
+ ehci->reclaim_ready = 0;
+ cmd |= CMD_IAAD;
+ writel (cmd, &ehci->regs->command);
+ /* posted write need not be known to HC yet ... */
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void scan_async (struct ehci_hcd *ehci)
+{
+ struct ehci_qh *qh;
+ unsigned long flags;
+
+ spin_lock_irqsave (&ehci->lock, flags);
+rescan:
+ qh = ehci->async;
+ if (likely (qh != 0)) {
+ do {
+ /* clean any finished work for this qh */
+ if (!list_empty (&qh->qtd_list)) {
+ // dbg_qh ("scan_async", ehci, qh);
+ qh = qh_put (qh);
+ spin_unlock_irqrestore (&ehci->lock, flags);
+
+ /* concurrent unlink could happen here */
+ qh_completions (ehci, &qh->qtd_list, 1);
+
+ spin_lock_irqsave (&ehci->lock, flags);
+ qh_unput (ehci, qh);
+ }
+
+ /* unlink idle entries (reduces PCI usage) */
+ if (list_empty (&qh->qtd_list) && !ehci->reclaim) {
+ if (qh->qh_next.qh != qh) {
+ // dbg ("irq/empty");
+ start_unlink_async (ehci, qh);
+ } else {
+ // FIXME: arrange to stop
+ // after it's been idle a while.
+ }
+ }
+ qh = qh->qh_next.qh;
+ if (!qh) /* unlinked? */
+ goto rescan;
+ } while (qh != ehci->async);
+ }
+
+ spin_unlock_irqrestore (&ehci->lock, flags);
+}
--- /dev/null
+/*
+ * Copyright (c) 2001 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI scheduled transaction support: interrupt, iso, split iso
+ * These are called "periodic" transactions in the EHCI spec.
+ */
+
+/*
+ * Ceiling microseconds (typical) for that many bytes at high speed
+ * ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed
+ * to preallocate bandwidth)
+ */
+#define EHCI_HOST_DELAY 5 /* nsec, guess */
+#define HS_USECS(bytes) NS_TO_US ( ((55 * 8 * 2083)/1000) \
+ + ((2083UL * (3167 + BitTime (bytes)))/1000) \
+ + EHCI_HOST_DELAY)
+#define HS_USECS_ISO(bytes) NS_TO_US ( ((long)(38 * 8 * 2.083)) \
+ + ((2083UL * (3167 + BitTime (bytes)))/1000) \
+ + EHCI_HOST_DELAY)
+
+static int ehci_get_frame (struct usb_hcd *hcd);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * periodic_next_shadow - return "next" pointer on shadow list
+ * @periodic: host pointer to qh/itd/sitd
+ * @tag: hardware tag for type of this record
+ */
+static union ehci_shadow *
+periodic_next_shadow (union ehci_shadow *periodic, int tag)
+{
+ switch (tag) {
+ case Q_TYPE_QH:
+ return &periodic->qh->qh_next;
+ case Q_TYPE_FSTN:
+ return &periodic->fstn->fstn_next;
+#ifdef have_iso
+ case Q_TYPE_ITD:
+ return &periodic->itd->itd_next;
+ case Q_TYPE_SITD:
+ return &periodic->sitd->sitd_next;
+#endif /* have_iso */
+ }
+ dbg ("BAD shadow %p tag %d", periodic->ptr, tag);
+ // BUG ();
+ return 0;
+}
+
+/* returns true after successful unlink */
+/* caller must hold ehci->lock */
+static int periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
+{
+ union ehci_shadow *prev_p = &ehci->pshadow [frame];
+ u32 *hw_p = &ehci->periodic [frame];
+ union ehci_shadow here = *prev_p;
+ union ehci_shadow *next_p;
+
+ /* find predecessor of "ptr"; hw and shadow lists are in sync */
+ while (here.ptr && here.ptr != ptr) {
+ prev_p = periodic_next_shadow (prev_p, Q_NEXT_TYPE (*hw_p));
+ hw_p = &here.qh->hw_next;
+ here = *prev_p;
+ }
+ /* an interrupt entry (at list end) could have been shared */
+ if (!here.ptr) {
+ dbg ("entry %p no longer on frame [%d]", ptr, frame);
+ return 0;
+ }
+ // vdbg ("periodic unlink %p from frame %d", ptr, frame);
+
+ /* update hardware list ... HC may still know the old structure, so
+ * don't change hw_next until it'll have purged its cache
+ */
+ next_p = periodic_next_shadow (&here, Q_NEXT_TYPE (*hw_p));
+ *hw_p = here.qh->hw_next;
+
+ /* unlink from shadow list; HCD won't see old structure again */
+ *prev_p = *next_p;
+ next_p->ptr = 0;
+
+ return 1;
+}
+
+/* how many of the uframe's 125 usecs are allocated? */
+static unsigned short
+periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
+{
+ u32 *hw_p = &ehci->periodic [frame];
+ union ehci_shadow *q = &ehci->pshadow [frame];
+ unsigned usecs = 0;
+#ifdef have_iso
+ u32 temp = 0;
+#endif
+
+ while (q->ptr) {
+ switch (Q_NEXT_TYPE (*hw_p)) {
+ case Q_TYPE_QH:
+ /* is it in the S-mask? */
+ if (q->qh->hw_info2 & cpu_to_le32 (1 << uframe))
+ usecs += q->qh->usecs;
+ q = &q->qh->qh_next;
+ break;
+ case Q_TYPE_FSTN:
+ /* for "save place" FSTNs, count the relevant INTR
+ * bandwidth from the previous frame
+ */
+ if (q->fstn->hw_prev != EHCI_LIST_END) {
+ dbg ("not counting FSTN bandwidth yet ...");
+ }
+ q = &q->fstn->fstn_next;
+ break;
+#ifdef have_iso
+ case Q_TYPE_ITD:
+ temp = le32_to_cpu (q->itd->transaction [uframe]);
+ temp >>= 16;
+ temp &= 0x0fff;
+ if (temp)
+ usecs += HS_USECS_ISO (temp);
+ q = &q->itd->itd_next;
+ break;
+ case Q_TYPE_SITD:
+ temp = q->sitd->hw_fullspeed_ep &
+ __constant_cpu_to_le32 (1 << 31);
+
+ // FIXME: this doesn't count data bytes right...
+
+ /* is it in the S-mask? (count SPLIT, DATA) */
+ if (q->sitd->hw_uframe & cpu_to_le32 (1 << uframe)) {
+ if (temp)
+ usecs += HS_USECS (188);
+ else
+ usecs += HS_USECS (1);
+ }
+
+ /* ... C-mask? (count CSPLIT, DATA) */
+ if (q->sitd->hw_uframe &
+ cpu_to_le32 (1 << (8 + uframe))) {
+ if (temp)
+ usecs += HS_USECS (0);
+ else
+ usecs += HS_USECS (188);
+ }
+ q = &q->sitd->sitd_next;
+ break;
+#endif /* have_iso */
+ default:
+ BUG ();
+ }
+ }
+#ifdef DEBUG
+ if (usecs > 100)
+ err ("overallocated uframe %d, periodic is %d usecs",
+ frame * 8 + uframe, usecs);
+#endif
+ return usecs;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void intr_deschedule (
+ struct ehci_hcd *ehci,
+ unsigned frame,
+ struct ehci_qh *qh,
+ unsigned period
+) {
+ unsigned long flags;
+
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ do {
+ periodic_unlink (ehci, frame, qh);
+ qh_unput (ehci, qh);
+ frame += period;
+ } while (frame < ehci->periodic_size);
+
+ qh->qh_state = QH_STATE_UNLINK;
+ qh->qh_next.ptr = 0;
+ ehci->periodic_urbs--;
+
+ /* maybe turn off periodic schedule */
+ if (!ehci->periodic_urbs) {
+ u32 cmd = readl (&ehci->regs->command);
+
+ /* did setting PSE not take effect yet?
+ * takes effect only at frame boundaries...
+ */
+ while (!(readl (&ehci->regs->status) & STS_PSS))
+ udelay (20);
+
+ cmd &= ~CMD_PSE;
+ writel (cmd, &ehci->regs->command);
+ /* posted write ... */
+
+ ehci->next_frame = -1;
+ } else
+ vdbg ("periodic schedule still enabled");
+
+ spin_unlock_irqrestore (&ehci->lock, flags);
+
+ /*
+ * If the hc may be looking at this qh, then delay a uframe
+ * (yeech!) to be sure it's done.
+ * No other threads may be mucking with this qh.
+ */
+ if (((ehci_get_frame (&ehci->hcd) - frame) % period) == 0)
+ udelay (125);
+
+ qh->qh_state = QH_STATE_IDLE;
+ qh->hw_next = EHCI_LIST_END;
+
+ vdbg ("descheduled qh %p, per = %d frame = %d count = %d, urbs = %d",
+ qh, period, frame,
+ atomic_read (&qh->refcount), ehci->periodic_urbs);
+}
+
+static int intr_submit (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct list_head *qtd_list,
+ int mem_flags
+) {
+ unsigned epnum, period;
+ unsigned temp;
+ unsigned short mult, usecs;
+ unsigned long flags;
+ struct ehci_qh *qh;
+ struct hcd_dev *dev;
+ int status = 0;
+
+ /* get endpoint and transfer data */
+ epnum = usb_pipeendpoint (urb->pipe);
+ if (usb_pipein (urb->pipe)) {
+ temp = urb->dev->epmaxpacketin [epnum];
+ epnum |= 0x10;
+ } else
+ temp = urb->dev->epmaxpacketout [epnum];
+ mult = 1;
+ if (urb->dev->speed == USB_SPEED_HIGH) {
+ /* high speed "high bandwidth" is coded in ep maxpacket */
+ mult += (temp >> 11) & 0x03;
+ temp &= 0x03ff;
+ } else {
+ dbg ("no intr/tt scheduling yet");
+ status = -ENOSYS;
+ goto done;
+ }
+
+ /*
+ * NOTE: current completion/restart logic doesn't handle more than
+ * one qtd in a periodic qh ... 16-20 KB/urb is pretty big for this.
+ * such big requests need many periods to transfer.
+ */
+ if (unlikely (qtd_list->next != qtd_list->prev)) {
+ dbg ("only one intr qtd per urb allowed");
+ status = -EINVAL;
+ goto done;
+ }
+
+ usecs = HS_USECS (urb->transfer_buffer_length);
+
+ /*
+ * force a power-of-two (frames) sized polling interval
+ *
+ * NOTE: endpoint->bInterval for highspeed is measured in uframes,
+ * while for full/low speeds it's in frames. Here we "know" that
+ * urb->interval doesn't give acccess to high interrupt rates.
+ */
+ period = ehci->periodic_size;
+ temp = period;
+ if (unlikely (urb->interval < 1))
+ urb->interval = 1;
+ while (temp > urb->interval)
+ temp >>= 1;
+ period = urb->interval = temp;
+
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ /* get the qh (must be empty and idle) */
+ dev = (struct hcd_dev *)urb->dev->hcpriv;
+ qh = (struct ehci_qh *) dev->ep [epnum];
+ if (qh) {
+ /* only allow one queued interrupt urb per EP */
+ if (unlikely (qh->qh_state != QH_STATE_IDLE
+ || !list_empty (&qh->qtd_list))) {
+ dbg ("interrupt urb already queued");
+ status = -EBUSY;
+ } else {
+ /* maybe reset hardware's data toggle in the qh */
+ if (unlikely (!usb_gettoggle (urb->dev, epnum & 0x0f,
+ !(epnum & 0x10)))) {
+ qh->hw_token |=
+ __constant_cpu_to_le32 (QTD_TOGGLE);
+ usb_settoggle (urb->dev, epnum & 0x0f,
+ !(epnum & 0x10), 1);
+ }
+ /* trust the QH was set up as interrupt ... */
+ list_splice (qtd_list, &qh->qtd_list);
+ qh_update (qh, list_entry (qtd_list->next,
+ struct ehci_qtd, qtd_list));
+ }
+ } else {
+ /* can't sleep here, we have ehci->lock... */
+ qh = ehci_qh_make (ehci, urb, qtd_list, SLAB_ATOMIC);
+ qtd_list = &qh->qtd_list;
+ if (likely (qh != 0)) {
+ // dbg ("new INTR qh %p", qh);
+ dev->ep [epnum] = qh;
+ } else
+ status = -ENOMEM;
+ }
+
+ /* Schedule this periodic QH. */
+ if (likely (status == 0)) {
+ unsigned frame = urb->interval;
+
+ qh->hw_next = EHCI_LIST_END;
+ qh->hw_info2 |= cpu_to_le32 (mult << 30);
+ qh->usecs = usecs;
+
+ urb->hcpriv = qh_put (qh);
+ status = -ENOSPC;
+
+ /* pick a set of schedule slots, link the QH into them */
+ do {
+ int uframe;
+
+ /* Select some frame 0..(urb->interval - 1) with a
+ * microframe that can hold this transaction.
+ *
+ * FIXME for TT splits, need uframes for start and end.
+ * FSTNs can put end into next frame (uframes 0 or 1).
+ */
+ frame--;
+ for (uframe = 0; uframe < 8; uframe++) {
+ int claimed;
+ claimed = periodic_usecs (ehci, frame, uframe);
+ /* 80% periodic == 100 usec max committed */
+ if ((claimed + usecs) <= 100) {
+ vdbg ("frame %d.%d: %d usecs, plus %d",
+ frame, uframe, claimed, usecs);
+ break;
+ }
+ }
+ if (uframe == 8)
+ continue;
+// FIXME delete when code below handles non-empty queues
+ if (ehci->pshadow [frame].ptr)
+ continue;
+
+ /* QH will run once each period, starting there */
+ urb->start_frame = frame;
+ status = 0;
+
+ /* set S-frame mask */
+ qh->hw_info2 |= cpu_to_le32 (1 << uframe);
+ // dbg_qh ("Schedule INTR qh", ehci, qh);
+
+ /* stuff into the periodic schedule */
+ qh->qh_state = QH_STATE_LINKED;
+ vdbg ("qh %p usecs %d period %d starting frame %d.%d",
+ qh, qh->usecs, period, frame, uframe);
+ do {
+ if (unlikely ((int)ehci->pshadow [frame].ptr)) {
+// FIXME -- just link to the end, before any qh with a shorter period,
+// AND handle it already being (implicitly) linked into this frame
+ BUG ();
+ } else {
+ ehci->pshadow [frame].qh = qh_put (qh);
+ ehci->periodic [frame] =
+ QH_NEXT (qh->qh_dma);
+ }
+ frame += period;
+ } while (frame < ehci->periodic_size);
+
+ /* maybe enable periodic schedule processing */
+ if (!ehci->periodic_urbs++) {
+ u32 cmd;
+
+ /* did clearing PSE did take effect yet?
+ * takes effect only at frame boundaries...
+ */
+ while (readl (&ehci->regs->status) & STS_PSS)
+ udelay (20);
+
+ cmd = readl (&ehci->regs->command) | CMD_PSE;
+ writel (cmd, &ehci->regs->command);
+ /* posted write ... PSS happens later */
+ ehci->hcd.state = USB_STATE_RUNNING;
+
+ /* make sure tasklet scans these */
+ ehci->next_frame = ehci_get_frame (&ehci->hcd);
+ }
+ break;
+
+ } while (frame);
+ }
+ spin_unlock_irqrestore (&ehci->lock, flags);
+done:
+ if (status) {
+ usb_complete_t complete = urb->complete;
+
+ urb->complete = 0;
+ urb->status = status;
+ qh_completions (ehci, qtd_list, 1);
+ urb->complete = complete;
+ }
+ return status;
+}
+
+static unsigned long
+intr_complete (
+ struct ehci_hcd *ehci,
+ unsigned frame,
+ struct ehci_qh *qh,
+ unsigned long flags /* caller owns ehci->lock ... */
+) {
+ struct ehci_qtd *qtd;
+ struct urb *urb;
+ int unlinking;
+
+ /* nothing to report? */
+ if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE))
+ != 0))
+ return flags;
+
+ qtd = list_entry (qh->qtd_list.next, struct ehci_qtd, qtd_list);
+ urb = qtd->urb;
+ unlinking = (urb->status == -ENOENT) || (urb->status == -ECONNRESET);
+
+ /* call any completions, after patching for reactivation */
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ /* NOTE: currently restricted to one qtd per qh! */
+ if (qh_completions (ehci, &qh->qtd_list, 0) == 0)
+ urb = 0;
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ /* never reactivate requests that were unlinked ... */
+ if (likely (urb != 0)) {
+ if (unlinking
+ || urb->status == -ECONNRESET
+ || urb->status == -ENOENT
+ // || (urb->dev == null)
+ || ehci->hcd.state == USB_STATE_HALT)
+ urb = 0;
+ // FIXME look at all those unlink cases ... we always
+ // need exactly one completion that reports unlink.
+ // the one above might not have been it!
+ }
+
+ /* normally reactivate */
+ if (likely (urb != 0)) {
+ if (usb_pipeout (urb->pipe))
+ pci_dma_sync_single (ehci->hcd.pdev,
+ qtd->buf_dma,
+ urb->transfer_buffer_length,
+ PCI_DMA_TODEVICE);
+ urb->status = -EINPROGRESS;
+ urb->actual_length = 0;
+
+ /* patch qh and restart */
+ qh_update (qh, qtd);
+ }
+ return flags;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef have_iso
+
+static inline void itd_free (struct ehci_hcd *ehci, struct ehci_itd *itd)
+{
+ pci_pool_free (ehci->itd_pool, itd, itd->itd_dma);
+}
+
+/*
+ * Create itd and allocate into uframes within specified frame.
+ * Caller must update the resulting uframe links.
+ */
+static struct ehci_itd *
+itd_make (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ unsigned index, // urb->iso_frame_desc [index]
+ unsigned frame, // scheduled start
+ dma_addr_t dma, // mapped transfer buffer
+ int mem_flags
+) {
+ struct ehci_itd *itd;
+ u64 temp;
+ u32 buf1;
+ unsigned epnum, maxp, multi, usecs;
+ unsigned length;
+ unsigned i, bufnum;
+
+ /* allocate itd, start to fill it */
+ itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &dma);
+ if (!itd)
+ return itd;
+
+ itd->hw_next = EHCI_LIST_END;
+ itd->urb = urb;
+ itd->index = index;
+ INIT_LIST_HEAD (&itd->itd_list);
+ itd->uframe = (frame * 8) % ehci->periodic_size;
+
+ /* tell itd about the buffer its transfers will consume */
+ length = urb->iso_frame_desc [index].length;
+ dma += urb->iso_frame_desc [index].offset;
+ temp = dma & ~0x0fff;
+ for (i = 0; i < 7; i++) {
+ itd->hw_bufp [i] = cpu_to_le32 ((u32) temp);
+ itd->hw_bufp_hi [i] = cpu_to_le32 ((u32)(temp >> 32));
+ temp += 0x0fff;
+ }
+
+ /*
+ * this might be a "high bandwidth" highspeed endpoint,
+ * as encoded in the ep descriptor's maxpacket field
+ */
+ epnum = usb_pipeendpoint (urb->pipe);
+ if (usb_pipein (urb->pipe)) {
+ maxp = urb->dev->epmaxpacketin [epnum];
+ buf1 = (1 << 11) | maxp;
+ } else {
+ maxp = urb->dev->epmaxpacketout [epnum];
+ buf1 = maxp;
+ }
+ multi = 1;
+ multi += (temp >> 11) & 0x03;
+ maxp &= 0x03ff;
+
+ /* "plus" info in low order bits of buffer pointers */
+ itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum);
+ itd->hw_bufp [1] |= cpu_to_le32 (buf1);
+ itd->hw_bufp [2] |= cpu_to_le32 (multi);
+
+ /* schedule as many uframes as needed */
+ maxp *= multi;
+ usecs = HS_USECS_ISO (maxp);
+ bufnum = 0;
+ for (i = 0; i < 8; i++) {
+ unsigned t, offset, scratch;
+
+ if (length <= 0) {
+ itd->hw_transaction [i] = 0;
+ continue;
+ }
+
+ /* don't commit more than 80% periodic == 100 usec */
+ if ((periodic_usecs (ehci, itd->uframe, i) + usecs) > 100)
+ continue;
+
+ /* we'll use this uframe; figure hw_transaction */
+ t = EHCI_ISOC_ACTIVE;
+ t |= bufnum << 12; // which buffer?
+ offset = temp & 0x0fff; // offset therein
+ t |= offset;
+ if ((offset + maxp) >= 4096) // hc auto-wraps end-of-"page"
+ bufnum++;
+ if (length <= maxp) {
+ // interrupt only needed at end-of-urb
+ if ((index + 1) == urb->number_of_packets)
+ t |= EHCI_ITD_IOC;
+ scratch = length;
+ } else
+ scratch = maxp;
+ t |= scratch << 16;
+ t = cpu_to_le32 (t);
+
+ itd->hw_transaction [i] = itd->transaction [i] = t;
+ length -= scratch;
+ }
+ if (length > 0) {
+ dbg ("iso frame too big, urb %p [%d], %d extra (of %d)",
+ urb, index, length, urb->iso_frame_desc [index].length);
+ itd_free (ehci, itd);
+ itd = 0;
+ }
+ return itd;
+}
+
+static inline void
+itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
+{
+ u32 ptr;
+
+ ptr = cpu_to_le32 (itd->itd_dma); // type 0 == itd
+ if (ehci->pshadow [frame].ptr) {
+ if (!itd->itd_next.ptr) {
+ itd->itd_next = ehci->pshadow [frame];
+ itd->hw_next = ehci->periodic [frame];
+ } else if (itd->itd_next.ptr != ehci->pshadow [frame].ptr) {
+ dbg ("frame %d itd link goof", frame);
+ BUG ();
+ }
+ }
+ ehci->pshadow [frame].itd = itd;
+ ehci->periodic [frame] = ptr;
+}
+
+#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
+
+static unsigned long
+itd_complete (struct ehci_hcd *ehci, struct ehci_itd *itd, unsigned long flags)
+{
+ struct urb *urb = itd->urb;
+
+ /* if not unlinking: */
+ if (!(urb->transfer_flags & EHCI_STATE_UNLINK)
+ && ehci->hcd.state != USB_STATE_HALT) {
+ int i;
+ iso_packet_descriptor_t *desc;
+ struct ehci_itd *first_itd = urb->hcpriv;
+
+ /* update status for this frame's transfers */
+ desc = &urb->iso_frame_desc [itd->index];
+ desc->status = 0;
+ desc->actual_length = 0;
+ for (i = 0; i < 8; i++) {
+ u32 t = itd->hw_transaction [i];
+ if (t & (ISO_ERRS | EHCI_ISOC_ACTIVE)) {
+ if (t & EHCI_ISOC_ACTIVE)
+ desc->status = -EXDEV;
+ else if (t & EHCI_ISOC_BUF_ERR)
+ desc->status = usb_pipein (urb->pipe)
+ ? -ENOSR /* couldn't read */
+ : -ECOMM; /* couldn't write */
+ else if (t & EHCI_ISOC_BABBLE)
+ desc->status = -EOVERFLOW;
+ else /* (t & EHCI_ISOC_XACTERR) */
+ desc->status = -EPROTO;
+ break;
+ }
+ desc->actual_length += EHCI_ITD_LENGTH (t);
+ }
+
+ /* handle completion now? */
+ if ((itd->index + 1) != urb->number_of_packets)
+ return flags;
+
+ i = usb_pipein (urb->pipe);
+ if (i)
+ pci_dma_sync_single (ehci->hcd.pdev,
+ first_itd->buf_dma,
+ urb->transfer_buffer_length,
+ PCI_DMA_FROMDEVICE);
+
+ /* call completion with no locks; it can unlink ... */
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ urb->complete (urb);
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ /* re-activate this URB? or unlink? */
+ if (!(urb->transfer_flags & EHCI_STATE_UNLINK)
+ && ehci->hcd.state != USB_STATE_HALT) {
+ if (!i)
+ pci_dma_sync_single (ehci->hcd.pdev,
+ first_itd->buf_dma,
+ urb->transfer_buffer_length,
+ PCI_DMA_TODEVICE);
+
+ itd = urb->hcpriv;
+ do {
+ for (i = 0; i < 8; i++)
+ itd->hw_transaction [i]
+ = itd->transaction [i];
+ itd = list_entry (itd->itd_list.next,
+ struct ehci_itd, itd_list);
+ } while (itd != urb->hcpriv);
+ return flags;
+ }
+
+ /* unlink done only on the last itd */
+ } else if ((itd->index + 1) != urb->number_of_packets)
+ return flags;
+
+ /* we're unlinking ... */
+
+ /* decouple urb from the hcd */
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ if (ehci->hcd.state == USB_STATE_HALT)
+ urb->status = -ESHUTDOWN;
+ itd = urb->hcpriv;
+ urb->hcpriv = 0;
+ ehci_urb_done (ehci, itd->buf_dma, urb);
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ /* take itds out of the hc's periodic schedule */
+ list_entry (itd->itd_list.prev, struct ehci_itd, itd_list)
+ ->itd_list.next = 0;
+ do {
+ struct ehci_itd *next;
+
+ if (itd->itd_list.next)
+ next = list_entry (itd->itd_list.next,
+ struct ehci_itd, itd_list);
+ else
+ next = 0;
+
+ // FIXME: hc WILL (!) lap us here, if we get behind
+ // by 128 msec (or less, with smaller periodic_size).
+ // Reading/caching these itds will cause trouble...
+
+ periodic_unlink (ehci, itd->uframe, itd);
+ itd_free (ehci, itd);
+ itd = next;
+ } while (itd);
+ return flags;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int itd_submit (struct ehci_hcd *ehci, struct urb *urb)
+{
+ struct ehci_itd *first_itd = 0, *itd;
+ unsigned frame_index;
+ dma_addr_t dma;
+ unsigned long flags;
+
+ dbg ("itd_submit");
+
+ /* set up one dma mapping for this urb */
+ dma = pci_map_single (ehci->hcd.pdev,
+ urb->transfer_buffer, urb->transfer_buffer_length,
+ usb_pipein (urb->pipe)
+ ? PCI_DMA_FROMDEVICE
+ : PCI_DMA_TODEVICE);
+ if (dma == 0)
+ return -ENOMEM;
+
+ /*
+ * Schedule as needed. This is VERY optimistic about free
+ * bandwidth! But the API assumes drivers can pick frames
+ * intelligently (how?), so there's no other good option.
+ *
+ * FIXME this doesn't handle urb->next rings, or try to
+ * use the iso periodicity.
+ */
+ if (urb->transfer_flags & USB_ISO_ASAP) {
+ urb->start_frame = ehci_get_frame (&ehci->hcd);
+ urb->start_frame++;
+ }
+ urb->start_frame %= ehci->periodic_size;
+
+ /* create and populate itds (doing uframe scheduling) */
+ spin_lock_irqsave (&ehci->lock, flags);
+ for (frame_index = 0;
+ frame_index < urb->number_of_packets;
+ frame_index++) {
+ itd = itd_make (ehci, urb, frame_index,
+ urb->start_frame + frame_index,
+ dma, SLAB_ATOMIC);
+ if (itd) {
+ if (first_itd)
+ list_add_tail (&itd->itd_list,
+ &first_itd->itd_list);
+ else
+ first_itd = itd;
+ } else {
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ if (first_itd) {
+ while (!list_empty (&first_itd->itd_list)) {
+ itd = list_entry (
+ first_itd->itd_list.next,
+ struct ehci_itd, itd_list);
+ list_del (&itd->itd_list);
+ itd_free (ehci, itd);
+ }
+ itd_free (ehci, first_itd);
+ }
+ pci_unmap_single (ehci->hcd.pdev,
+ dma, urb->transfer_buffer_length,
+ usb_pipein (urb->pipe)
+ ? PCI_DMA_FROMDEVICE
+ : PCI_DMA_TODEVICE);
+ return -ENOMEM;
+ }
+ }
+
+ /* stuff into the schedule */
+ itd = first_itd;
+ do {
+ unsigned i;
+
+ for (i = 0; i < 8; i++) {
+ if (!itd->hw_transaction [i])
+ continue;
+ itd_link (ehci, itd->uframe + i, itd);
+ }
+ itd = list_entry (itd->itd_list.next,
+ struct ehci_itd, itd_list);
+ } while (itd != first_itd);
+ urb->hcpriv = first_itd;
+
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * "Split ISO TDs" ... used for USB 1.1 devices going through
+ * the TTs in USB 2.0 hubs.
+ */
+
+static inline void
+sitd_free (struct ehci_hcd *ehci, struct ehci_sitd *sitd)
+{
+ pci_pool_free (ehci->sitd_pool, sitd, sitd->sitd_dma);
+}
+
+static struct ehci_sitd *
+sitd_make (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ unsigned index, // urb->iso_frame_desc [index]
+ unsigned uframe, // scheduled start
+ dma_addr_t dma, // mapped transfer buffer
+ int mem_flags
+) {
+ struct ehci_sitd *sitd;
+ unsigned length;
+
+ sitd = pci_pool_alloc (ehci->sitd_pool, mem_flags, &dma);
+ if (!sitd)
+ return sitd;
+ sitd->urb = urb;
+ length = urb->iso_frame_desc [index].length;
+ dma += urb->iso_frame_desc [index].offset;
+
+#if 0
+ // FIXME: do the rest!
+#else
+ sitd_free (ehci, sitd);
+ return 0;
+#endif
+
+}
+
+static inline void
+sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd)
+{
+ u32 ptr;
+
+ ptr = cpu_to_le32 (sitd->sitd_dma | 2); // type 2 == sitd
+ if (ehci->pshadow [frame].ptr) {
+ if (!sitd->sitd_next.ptr) {
+ sitd->sitd_next = ehci->pshadow [frame];
+ sitd->hw_next = ehci->periodic [frame];
+ } else if (sitd->sitd_next.ptr != ehci->pshadow [frame].ptr) {
+ dbg ("frame %d sitd link goof", frame);
+ BUG ();
+ }
+ }
+ ehci->pshadow [frame].sitd = sitd;
+ ehci->periodic [frame] = ptr;
+}
+
+static unsigned long
+sitd_complete (
+ struct ehci_hcd *ehci,
+ struct ehci_sitd *sitd,
+ unsigned long flags
+) {
+ // FIXME -- implement!
+
+ dbg ("NYI -- sitd_complete");
+ return flags;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb)
+{
+ // struct ehci_sitd *first_sitd = 0;
+ unsigned frame_index;
+ dma_addr_t dma;
+ int mem_flags;
+
+ dbg ("NYI -- sitd_submit");
+
+ // FIXME -- implement!
+
+ // FIXME: setup one big dma mapping
+ dma = 0;
+
+ mem_flags = SLAB_ATOMIC;
+
+ for (frame_index = 0;
+ frame_index < urb->number_of_packets;
+ frame_index++) {
+ struct ehci_sitd *sitd;
+ unsigned uframe;
+
+ // FIXME: use real arguments, schedule this!
+ uframe = -1;
+
+ sitd = sitd_make (ehci, urb, frame_index,
+ uframe, dma, mem_flags);
+
+ if (sitd) {
+ /*
+ if (first_sitd)
+ list_add_tail (&sitd->sitd_list,
+ &first_sitd->sitd_list);
+ else
+ first_sitd = sitd;
+ */
+ } else {
+ // FIXME: clean everything up
+ }
+ }
+
+ // if we have a first sitd, then
+ // store them all into the periodic schedule!
+ // urb->hcpriv = first sitd in sitd_list
+
+ return -ENOSYS;
+}
+
+#endif /* have_iso */
+
+/*-------------------------------------------------------------------------*/
+
+static void scan_periodic (struct ehci_hcd *ehci)
+{
+ unsigned frame;
+ unsigned clock;
+ unsigned long flags;
+
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ /*
+ * When running, scan from last scan point up to "now"
+ * Touches as few pages as possible: cache-friendly.
+ * It's safe to scan entries more than once, though.
+ */
+ if (HCD_IS_RUNNING (ehci->hcd.state)) {
+ frame = ehci->next_frame;
+ clock = ehci_get_frame (&ehci->hcd);
+
+ /* when shutting down, scan everything for thoroughness */
+ } else {
+ frame = 0;
+ clock = ehci->periodic_size - 1;
+ }
+ for (;;) {
+ union ehci_shadow q;
+ u32 type;
+
+restart:
+ q.ptr = ehci->pshadow [frame].ptr;
+ type = Q_NEXT_TYPE (ehci->periodic [frame]);
+
+ /* scan each element in frame's queue for completions */
+ while (q.ptr != 0) {
+ int last;
+ union ehci_shadow temp;
+
+ switch (type) {
+ case Q_TYPE_QH:
+ last = (q.qh->hw_next == EHCI_LIST_END);
+ flags = intr_complete (ehci, frame,
+ qh_put (q.qh), flags);
+ type = Q_NEXT_TYPE (q.qh->hw_next);
+ temp = q.qh->qh_next;
+ qh_unput (ehci, q.qh);
+ q = temp;
+ break;
+ case Q_TYPE_FSTN:
+ last = (q.fstn->hw_next == EHCI_LIST_END);
+ /* for "save place" FSTNs, look at QH entries
+ * in the previous frame for completions.
+ */
+ if (q.fstn->hw_prev != EHCI_LIST_END) {
+ dbg ("ignoring completions from FSTNs");
+ }
+ type = Q_NEXT_TYPE (q.fstn->hw_next);
+ temp = q.fstn->fstn_next;
+ break;
+#ifdef have_iso
+ case Q_TYPE_ITD:
+ last = (q.itd->hw_next == EHCI_LIST_END);
+ flags = itd_complete (ehci, q.itd, flags);
+ type = Q_NEXT_TYPE (q.itd->hw_next);
+ q = q.itd->itd_next;
+ break;
+ case Q_TYPE_SITD:
+ last = (q.sitd->hw_next == EHCI_LIST_END);
+ flags = sitd_complete (ehci, q.sitd, flags);
+ type = Q_NEXT_TYPE (q.sitd->hw_next);
+ q = q.sitd->sitd_next;
+ break;
+#endif /* have_iso */
+ default:
+ dbg ("corrupt type %d frame %d shadow %p",
+ type, frame, q.ptr);
+ // BUG ();
+ last = 1;
+ q.ptr = 0;
+ }
+
+ /* did completion remove an interior q entry? */
+ if (unlikely (q.ptr == 0 && !last))
+ goto restart;
+ }
+
+ /* stop when we catch up to the HC */
+
+ // FIXME: this assumes we won't get lapped when
+ // latencies climb; that should be rare, but...
+ // detect it, and just go all the way around.
+ // FLR might help detect this case, so long as latencies
+ // don't exceed periodic_size msec (default 1.024 sec).
+
+ // FIXME: likewise assumes HC doesn't halt mid-scan
+
+ if (frame == clock) {
+ unsigned now;
+
+ if (!HCD_IS_RUNNING (ehci->hcd.state))
+ break;
+ ehci->next_frame = clock;
+ now = ehci_get_frame (&ehci->hcd);
+ if (clock == now)
+ break;
+ clock = now;
+ } else if (++frame >= ehci->periodic_size)
+ frame = 0;
+ }
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ }
--- /dev/null
+/*
+ * Copyright (c) 2001 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __LINUX_EHCI_HCD_H
+#define __LINUX_EHCI_HCD_H
+
+/* definitions used for the EHCI driver */
+
+/* ehci_hcd->lock guards shared data against other CPUs:
+ * ehci_hcd: async, reclaim, periodic (and shadow), ...
+ * hcd_dev: ep[]
+ * ehci_qh: qh_next, qtd_list
+ * ehci_qtd: qtd_list
+ *
+ * Also, hold this lock when talking to HC registers or
+ * when updating hw_* fields in shared qh/qtd/... structures.
+ */
+
+#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
+
+struct ehci_hcd { /* one per controller */
+ spinlock_t lock;
+
+ /* async schedule support */
+ struct ehci_qh *async;
+ struct ehci_qh *reclaim;
+ int reclaim_ready;
+
+ /* periodic schedule support */
+#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
+ unsigned periodic_size;
+ u32 *periodic; /* hw periodic table */
+ dma_addr_t periodic_dma;
+ unsigned i_thresh; /* uframes HC might cache */
+
+ union ehci_shadow *pshadow; /* mirror hw periodic table */
+ int next_frame; /* scan periodic, start here */
+ unsigned periodic_urbs; /* how many urbs scheduled? */
+
+ /* deferred work from IRQ, etc */
+ struct tasklet_struct tasklet;
+
+ /* per root hub port */
+ unsigned long reset_done [EHCI_MAX_ROOT_PORTS];
+
+ /* glue to PCI and HCD framework */
+ struct usb_hcd hcd;
+ struct ehci_caps *caps;
+ struct ehci_regs *regs;
+
+ /* per-HC memory pools (could be per-PCI-bus, but ...) */
+ struct pci_pool *qh_pool; /* qh per active urb */
+ struct pci_pool *qtd_pool; /* one or more per qh */
+ struct pci_pool *itd_pool; /* itd per iso urb */
+ struct pci_pool *sitd_pool; /* sitd per split iso urb */
+};
+
+/* unwrap an HCD pointer to get an EHCI_HCD pointer */
+#define hcd_to_ehci(hcd_ptr) list_entry(hcd_ptr, struct ehci_hcd, hcd)
+
+/* NOTE: urb->transfer_flags expected to not use this bit !!! */
+#define EHCI_STATE_UNLINK 0x8000 /* urb being unlinked */
+
+/*-------------------------------------------------------------------------*/
+
+/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
+
+/* Section 2.2 Host Controller Capability Registers */
+struct ehci_caps {
+ u8 length; /* CAPLENGTH - size of this struct */
+ u8 reserved; /* offset 0x1 */
+ u16 hci_version; /* HCIVERSION - offset 0x2 */
+ u32 hcs_params; /* HCSPARAMS - offset 0x4 */
+#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */
+#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */
+#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */
+#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */
+#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */
+#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
+#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
+
+ u32 hcc_params; /* HCCPARAMS - offset 0x8 */
+#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */
+#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */
+#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */
+#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */
+#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/
+#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */
+ u8 portroute [8]; /* nibbles for routing - offset 0xC */
+} __attribute__ ((packed));
+
+
+/* Section 2.3 Host Controller Operational Registers */
+struct ehci_regs {
+
+ /* USBCMD: offset 0x00 */
+ u32 command;
+/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
+#define CMD_PARK (1<<11) /* enable "park" on async qh */
+#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */
+#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */
+#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */
+#define CMD_ASE (1<<5) /* async schedule enable */
+#define CMD_PSE (1<<4) /* periodic schedule enable */
+/* 3:2 is periodic frame list size */
+#define CMD_RESET (1<<1) /* reset HC not bus */
+#define CMD_RUN (1<<0) /* start/stop HC */
+
+ /* USBSTS: offset 0x04 */
+ u32 status;
+#define STS_ASS (1<<15) /* Async Schedule Status */
+#define STS_PSS (1<<14) /* Periodic Schedule Status */
+#define STS_RECL (1<<13) /* Reclamation */
+#define STS_HALT (1<<12) /* Not running (any reason) */
+/* some bits reserved */
+ /* these STS_* flags are also intr_enable bits (USBINTR) */
+#define STS_IAA (1<<5) /* Interrupted on async advance */
+#define STS_FATAL (1<<4) /* such as some PCI access errors */
+#define STS_FLR (1<<3) /* frame list rolled over */
+#define STS_PCD (1<<2) /* port change detect */
+#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */
+#define STS_INT (1<<0) /* "normal" completion (short, ...) */
+
+ /* USBINTR: offset 0x08 */
+ u32 intr_enable;
+
+ /* FRINDEX: offset 0x0C */
+ u32 frame_index; /* current microframe number */
+ /* CTRLDSSEGMENT: offset 0x10 */
+ u32 segment; /* address bits 63:32 if needed */
+ /* PERIODICLISTBASE: offset 0x14 */
+ u32 frame_list; /* points to periodic list */
+ /* ASYNCICLISTADDR: offset 0x18 */
+ u32 async_next; /* address of next async queue head */
+
+ u32 reserved [9];
+
+ /* CONFIGFLAG: offset 0x40 */
+ u32 configured_flag;
+#define FLAG_CF (1<<0) /* true: we'll support "high speed" */
+
+ /* PORTSC: offset 0x44 */
+ u32 port_status [0]; /* up to N_PORTS */
+/* 31:23 reserved */
+#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */
+#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */
+#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */
+/* 19:16 for port testing */
+/* 15:14 for using port indicator leds (if HCS_INDICATOR allows) */
+#define PORT_OWNER (1<<13) /* true: companion hc owns this port */
+#define PORT_POWER (1<<12) /* true: has power (see PPC) */
+#define PORT_USB11(x) (((x)&(3<<10))==(1<<10)) /* USB 1.1 device */
+/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
+/* 9 reserved */
+#define PORT_RESET (1<<8) /* reset port */
+#define PORT_SUSPEND (1<<7) /* suspend port */
+#define PORT_RESUME (1<<6) /* resume it */
+#define PORT_OCC (1<<5) /* over current change */
+#define PORT_OC (1<<4) /* over current active */
+#define PORT_PEC (1<<3) /* port enable change */
+#define PORT_PE (1<<2) /* port enable */
+#define PORT_CSC (1<<1) /* connect status change */
+#define PORT_CONNECT (1<<0) /* device connected */
+} __attribute__ ((packed));
+
+
+/*-------------------------------------------------------------------------*/
+
+#define QTD_NEXT(dma) cpu_to_le32((u32)dma)
+
+/*
+ * EHCI Specification 0.95 Section 3.5
+ * QTD: describe data transfer components (buffer, direction, ...)
+ * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
+ *
+ * These are associated only with "QH" (Queue Head) structures,
+ * used with control, bulk, and interrupt transfers.
+ */
+struct ehci_qtd {
+ /* first part defined by EHCI spec */
+ u32 hw_next; /* see EHCI 3.5.1 */
+ u32 hw_alt_next; /* see EHCI 3.5.2 */
+ u32 hw_token; /* see EHCI 3.5.3 */
+#define QTD_TOGGLE (1 << 31) /* data toggle */
+#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
+#define QTD_IOC (1 << 15) /* interrupt on complete */
+#define QTD_CERR(tok) (((tok)>>10) & 0x3)
+#define QTD_PID(tok) (((tok)>>8) & 0x3)
+#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */
+#define QTD_STS_HALT (1 << 6) /* halted on error */
+#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */
+#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */
+#define QTD_STS_XACT (1 << 3) /* device gave illegal response */
+#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */
+#define QTD_STS_STS (1 << 1) /* split transaction state */
+#define QTD_STS_PING (1 << 0) /* issue PING? */
+ u32 hw_buf [5]; /* see EHCI 3.5.4 */
+ u32 hw_buf_hi [5]; /* Appendix B */
+
+ /* the rest is HCD-private */
+ dma_addr_t qtd_dma; /* qtd address */
+ struct list_head qtd_list; /* sw qtd list */
+
+ /* dma same in urb's qtds, except 1st control qtd (setup buffer) */
+ struct urb *urb; /* qtd's urb */
+ dma_addr_t buf_dma; /* buffer address */
+ size_t length; /* length of buffer */
+} __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+/* type tag from {qh,itd,sitd,fstn}->hw_next */
+#define Q_NEXT_TYPE(dma) ((dma) & __constant_cpu_to_le32 (3 << 1))
+
+/* values for that type tag */
+#define Q_TYPE_ITD __constant_cpu_to_le32 (0 << 1)
+#define Q_TYPE_QH __constant_cpu_to_le32 (1 << 1)
+#define Q_TYPE_SITD __constant_cpu_to_le32 (2 << 1)
+#define Q_TYPE_FSTN __constant_cpu_to_le32 (3 << 1)
+
+/* next async queue entry, or pointer to interrupt/periodic QH */
+#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH)
+
+/* for periodic/async schedules and qtd lists, mark end of list */
+#define EHCI_LIST_END __constant_cpu_to_le32(1) /* "null pointer" to hw */
+
+/*
+ * Entries in periodic shadow table are pointers to one of four kinds
+ * of data structure. That's dictated by the hardware; a type tag is
+ * encoded in the low bits of the hardware's periodic schedule. Use
+ * Q_NEXT_TYPE to get the tag.
+ *
+ * For entries in the async schedule, the type tag always says "qh".
+ */
+union ehci_shadow {
+ struct ehci_qh *qh; /* Q_TYPE_QH */
+ struct ehci_itd *itd; /* Q_TYPE_ITD */
+ struct ehci_sitd *sitd; /* Q_TYPE_SITD */
+ struct ehci_fstn *fstn; /* Q_TYPE_FSTN */
+ void *ptr;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.95 Section 3.6
+ * QH: describes control/bulk/interrupt endpoints
+ * See Fig 3-7 "Queue Head Structure Layout".
+ *
+ * These appear in both the async and (for interrupt) periodic schedules.
+ */
+
+struct ehci_qh {
+ /* first part defined by EHCI spec */
+ u32 hw_next; /* see EHCI 3.6.1 */
+ u32 hw_info1; /* see EHCI 3.6.2 */
+#define QH_HEAD 0x00008000
+ u32 hw_info2; /* see EHCI 3.6.2 */
+ u32 hw_current; /* qtd list - see EHCI 3.6.4 */
+
+ /* qtd overlay (hardware parts of a struct ehci_qtd) */
+ u32 hw_qtd_next;
+ u32 hw_alt_next;
+ u32 hw_token;
+ u32 hw_buf [5];
+ u32 hw_buf_hi [5];
+
+ /* the rest is HCD-private */
+ dma_addr_t qh_dma; /* address of qh */
+ union ehci_shadow qh_next; /* ptr to qh; or periodic */
+ struct list_head qtd_list; /* sw qtd list */
+
+ atomic_t refcount;
+ unsigned short usecs; /* intr bandwidth */
+ short qh_state;
+#define QH_STATE_LINKED 1 /* HC sees this */
+#define QH_STATE_UNLINK 2 /* HC may still see this */
+#define QH_STATE_IDLE 3 /* HC doesn't see this */
+
+#ifdef EHCI_SOFT_RETRIES
+ int retries;
+#endif
+} __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.95 Section 3.3
+ * Fig 3-4 "Isochronous Transaction Descriptor (iTD)"
+ *
+ * Schedule records for high speed iso xfers
+ */
+struct ehci_itd {
+ /* first part defined by EHCI spec */
+ u32 hw_next; /* see EHCI 3.3.1 */
+ u32 hw_transaction [8]; /* see EHCI 3.3.2 */
+#define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */
+#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */
+#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */
+#define EHCI_ISOC_XACTERR (1<<28) /* XactErr - transaction error */
+#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x7fff)
+#define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */
+
+ u32 hw_bufp [7]; /* see EHCI 3.3.3 */
+ u32 hw_bufp_hi [7]; /* Appendix B */
+
+ /* the rest is HCD-private */
+ dma_addr_t itd_dma; /* for this itd */
+ union ehci_shadow itd_next; /* ptr to periodic q entry */
+
+ struct urb *urb;
+ unsigned index; /* in urb->iso_frame_desc */
+ struct list_head itd_list; /* list of urb frames' itds */
+ dma_addr_t buf_dma; /* frame's buffer address */
+
+ unsigned uframe; /* in periodic schedule */
+ u32 transaction [8]; /* copy of hw_transaction */
+
+} __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.95 Section 3.4
+ * siTD, aka split-transaction isochronous Transfer Descriptor
+ * ... describe low/full speed iso xfers through TT in hubs
+ * see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD)
+ */
+struct ehci_sitd {
+ /* first part defined by EHCI spec */
+ u32 hw_next;
+/* uses bit field macros above - see EHCI 0.95 Table 3-8 */
+ u32 hw_fullspeed_ep; /* see EHCI table 3-9 */
+ u32 hw_uframe; /* see EHCI table 3-10 */
+ u32 hw_tx_results1; /* see EHCI table 3-11 */
+ u32 hw_tx_results2; /* see EHCI table 3-12 */
+ u32 hw_tx_results3; /* see EHCI table 3-12 */
+ u32 hw_backpointer; /* see EHCI table 3-13 */
+ u32 hw_buf_hi [2]; /* Appendix B */
+
+ /* the rest is HCD-private */
+ dma_addr_t sitd_dma;
+ union ehci_shadow sitd_next; /* ptr to periodic q entry */
+ struct urb *urb;
+ dma_addr_t buf_dma; /* buffer address */
+} __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.96 Section 3.7
+ * Periodic Frame Span Traversal Node (FSTN)
+ *
+ * Manages split interrupt transactions (using TT) that span frame boundaries
+ * into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN
+ * makes the HC jump (back) to a QH to scan for fs/ls QH completions until
+ * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work.
+ */
+struct ehci_fstn {
+ u32 hw_next; /* any periodic q entry */
+ u32 hw_prev; /* qh or EHCI_LIST_END */
+
+ /* the rest is HCD-private */
+ dma_addr_t fstn_dma;
+ union ehci_shadow fstn_next; /* ptr to periodic q entry */
+} __attribute__ ((aligned (32)));
+
+#endif /* __LINUX_EHCI_HCD_H */
static DECLARE_MUTEX(usb_address0_sem);
static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */
-static LIST_HEAD(hub_list); /* List containing all of the hubs (for cleanup) */
+static LIST_HEAD(hub_list); /* List of all hubs (for cleanup) */
static DECLARE_WAIT_QUEUE_HEAD(khubd_wait);
static int khubd_pid = 0; /* PID of khubd */
data, sizeof(struct usb_hub_status), HZ);
}
+/* completion function, fires on port status changes and various faults */
static void hub_irq(struct urb *urb)
{
struct usb_hub *hub = (struct usb_hub *)urb->context;
unsigned long flags;
- /* Cause a hub reset after 10 consecutive errors */
- if (urb->status) {
- if (urb->status == -ENOENT)
- return;
-
- dbg("nonzero status in irq %d", urb->status);
+ switch (urb->status) {
+ case -ENOENT: /* synchronous unlink */
+ case -ECONNRESET: /* async unlink */
+ case -ESHUTDOWN: /* hardware going away */
+ return;
+ default: /* presumably an error */
+ /* Cause a hub reset after 10 consecutive errors */
+ dbg("hub '%s' status %d for interrupt transfer",
+ urb->dev->devpath, urb->status);
if ((++hub->nerrors < 10) || hub->error)
return;
-
hub->error = urb->status;
+ /* FALL THROUGH */
+
+ /* let khubd handle things */
+ case 0: /* we got data: port status changed */
+ break;
}
hub->nerrors = 0;
wait_ms(hub->descriptor->bPwrOn2PwrGood * 2);
}
-static int usb_hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint)
+static int usb_hub_configure(struct usb_hub *hub,
+ struct usb_endpoint_descriptor *endpoint)
{
struct usb_device *dev = hub->dev;
struct usb_hub_status hubstatus;
hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);
if (!hub->descriptor) {
- err("Unable to kmalloc %Zd bytes for hub descriptor", sizeof(*hub->descriptor));
+ err("Unable to kmalloc %Zd bytes for hub descriptor",
+ sizeof(*hub->descriptor));
return -1;
}
- /* Request the entire hub descriptor. */
- ret = usb_get_hub_descriptor(dev, hub->descriptor, sizeof(*hub->descriptor));
- /* <hub->descriptor> is large enough for a hub with 127 ports;
- * the hub can/will return fewer bytes here. */
+ /* Request the entire hub descriptor.
+ * hub->descriptor can handle USB_MAXCHILDREN ports,
+ * but the hub can/will return fewer bytes here.
+ */
+ ret = usb_get_hub_descriptor(dev, hub->descriptor,
+ sizeof(*hub->descriptor));
if (ret < 0) {
err("Unable to get hub descriptor (err = %d)", ret);
kfree(hub->descriptor);
return -1;
+ } else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) {
+ err("Hub is too big! %d children", hub->descriptor->bNbrPorts);
+ kfree(hub->descriptor);
+ return -1;
}
dev->maxchild = hub->descriptor->bNbrPorts;
- info("%d port%s detected", hub->descriptor->bNbrPorts, (hub->descriptor->bNbrPorts == 1) ? "" : "s");
+ info("%d port%s detected", dev->maxchild,
+ (dev->maxchild == 1) ? "" : "s");
le16_to_cpus(&hub->descriptor->wHubCharacteristics);
break;
case 1:
dbg("Single TT");
+ hub->tt.hub = dev;
break;
case 2:
- dbg("Multiple TT");
+ dbg("TT per port");
+ hub->tt.hub = dev;
+ hub->tt.multi = 1;
break;
default:
dbg("Unrecognized hub protocol %d",
}
dbg("Port indicators are %s supported",
- (hub->descriptor->wHubCharacteristics & HUB_CHAR_PORTIND) ? "" : "not");
+ (hub->descriptor->wHubCharacteristics & HUB_CHAR_PORTIND)
+ ? "" : "not");
- dbg("power on to power good time: %dms", hub->descriptor->bPwrOn2PwrGood * 2);
- dbg("hub controller current requirement: %dmA", hub->descriptor->bHubContrCurrent);
+ dbg("power on to power good time: %dms",
+ hub->descriptor->bPwrOn2PwrGood * 2);
+ dbg("hub controller current requirement: %dmA",
+ hub->descriptor->bHubContrCurrent);
for (i = 0; i < dev->maxchild; i++)
- portstr[i] = hub->descriptor->DeviceRemovable[((i + 1) / 8)] & (1 << ((i + 1) % 8)) ? 'F' : 'R';
+ portstr[i] = hub->descriptor->DeviceRemovable
+ [((i + 1) / 8)] & (1 << ((i + 1) % 8))
+ ? 'F' : 'R';
portstr[dev->maxchild] = 0;
dbg("port removable status: %s", portstr);
le16_to_cpus(&hubstatus.wHubStatus);
dbg("local power source is %s",
- (hubstatus.wHubStatus & HUB_STATUS_LOCAL_POWER) ? "lost (inactive)" : "good");
+ (hubstatus.wHubStatus & HUB_STATUS_LOCAL_POWER)
+ ? "lost (inactive)" : "good");
dbg("%sover-current condition exists",
(hubstatus.wHubStatus & HUB_STATUS_OVERCURRENT) ? "" : "no ");
}
/* If it's not an interrupt endpoint, we'd better punt! */
- if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) {
- err("Device #%d is hub class, but has endpoint other than interrupt?",
+ if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ != USB_ENDPOINT_XFER_INT) {
+ err("Device #%d is hub class, but endpoint is not interrupt?",
dev->devnum);
return NULL;
}
/* We found a hub */
- info("USB hub found");
+ info("USB hub found at %s", dev->devpath);
hub = kmalloc(sizeof(*hub), GFP_KERNEL);
if (!hub) {
if (usb_hub_configure(hub, endpoint) >= 0)
return hub;
- err("hub configuration failed for device #%d", dev->devnum);
+ err("hub configuration failed for device at %s", dev->devpath);
/* free hub, but first clean up its list. */
spin_lock_irqsave(&hub_event_lock, flags);
if (hub->children[i] == NULL)
info->port[i] = 0;
else
- info->port[i] = hub->children[i]->devnum;
+ info->port[i] =
+ hub->children[i]->devnum;
}
}
spin_unlock_irqrestore(&hub_event_lock, flags);
}
}
- err("cannot disconnect hub %d", dev->devnum);
+ err("cannot disconnect hub %s", dev->devpath);
}
#define HUB_RESET_TRIES 5
struct usb_port_status portsts;
unsigned short portchange, portstatus;
- for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; delay_time += delay) {
+ for (delay_time = 0;
+ delay_time < HUB_RESET_TIMEOUT;
+ delay_time += delay) {
/* wait to give the device a chance to reset */
wait_ms(delay);
/* read and decode port status */
ret = usb_get_port_status(hub, port + 1, &portsts);
if (ret < 0) {
- err("get_port_status(%d) failed (err = %d)", port + 1, ret);
+ err("get_port_status(%d) failed (err = %d)",
+ port + 1, ret);
return -1;
}
if (delay_time >= 2 * HUB_SHORT_RESET_TIME)
delay = HUB_LONG_RESET_TIME;
- dbg("port %d of hub %d not reset yet, waiting %dms", port + 1,
- hub->devnum, delay);
+ dbg("port %d of hub %s not reset yet, waiting %dms", port + 1,
+ hub->devpath, delay);
}
return -1;
/* return on disconnect or reset */
status = usb_hub_port_wait_reset(hub, port, dev, delay);
if (status != -1) {
- usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_RESET);
+ usb_clear_port_feature(hub,
+ port + 1, USB_PORT_FEAT_C_RESET);
return status;
}
- dbg("port %d of hub %d not enabled, trying reset again...",
- port + 1, hub->devnum);
+ dbg("port %d of hub %s not enabled, trying reset again...",
+ port + 1, hub->devpath);
delay = HUB_LONG_RESET_TIME;
}
- err("Cannot enable port %i of hub %d, disabling port.",
- port + 1, hub->devnum);
+ err("Cannot enable port %i of hub %s, disabling port.",
+ port + 1, hub->devpath);
err("Maybe the USB cable is bad?");
return -1;
ret = usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_ENABLE);
if (ret)
- err("cannot disable port %d of hub %d (err = %d)",
- port + 1, hub->devnum, ret);
+ err("cannot disable port %d of hub %s (err = %d)",
+ port + 1, hub->devpath, ret);
}
-static void usb_hub_port_connect_change(struct usb_device *hub, int port,
+static void usb_hub_port_connect_change(struct usb_hub *hubstate, int port,
struct usb_port_status *portsts)
{
+ struct usb_device *hub = hubstate->dev;
struct usb_device *dev;
unsigned short portstatus, portchange;
unsigned int delay = HUB_SHORT_RESET_TIME;
int i;
- char *portstr, *tempstr;
portstatus = le16_to_cpu(portsts->wPortStatus);
portchange = le16_to_cpu(portsts->wPortChange);
- dbg("port %d, portstatus %x, change %x, %s",
- port + 1, portstatus, portchange, portspeed (portstatus));
+ dbg("hub %s port %d, portstatus %x, change %x, %s",
+ hub->devpath, port + 1,
+ portstatus, portchange, portspeed (portstatus));
/* Clear the connection change status */
usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_CONNECTION);
down(&usb_address0_sem);
- tempstr = kmalloc(1024, GFP_KERNEL);
- portstr = kmalloc(1024, GFP_KERNEL);
-
for (i = 0; i < HUB_PROBE_TRIES; i++) {
- struct usb_device *pdev, *cdev;
+ struct usb_device *pdev;
+ int len;
/* Allocate a new device struct */
dev = usb_alloc_dev(hub, hub->bus);
hub->children[port] = dev;
- /* Reset the device */
+ /* Reset the device, and detect its speed */
if (usb_hub_port_reset(hub, port, dev, delay)) {
usb_free_dev(dev);
break;
}
- /* Find a new device ID for it */
+ /* Find a new address for it */
usb_connect(dev);
- /* Create a readable topology string */
- cdev = dev;
- pdev = dev->parent;
- if (portstr && tempstr) {
- portstr[0] = 0;
- while (pdev) {
- int port;
-
- for (port = 0; port < pdev->maxchild; port++)
- if (pdev->children[port] == cdev)
- break;
-
- strcpy(tempstr, portstr);
- if (!strlen(tempstr))
- sprintf(portstr, "%d", port + 1);
- else
- sprintf(portstr, "%d/%s", port + 1, tempstr);
+ /* Set up TT records, if needed */
+ if (hub->tt) {
+ dev->tt = hub->tt;
+ dev->ttport = hub->ttport;
+ } else if (dev->speed != USB_SPEED_HIGH
+ && hub->speed == USB_SPEED_HIGH) {
+ dev->tt = &hubstate->tt;
+ dev->ttport = port + 1;
+ }
- cdev = pdev;
- pdev = pdev->parent;
- }
- info("USB new device connect on bus%d/%s, assigned device number %d",
- dev->bus->busnum, portstr, dev->devnum);
- } else
- info("USB new device connect on bus%d, assigned device number %d",
- dev->bus->busnum, dev->devnum);
+ /* Save readable and stable topology id, distinguishing
+ * devices by location for diagnostics, tools, etc. The
+ * string is a path along hub ports, from the root. Each
+ * device's id will be stable until USB is re-cabled, and
+ * hubs are often labled with these port numbers.
+ *
+ * Initial size: "/NN" times five hubs + NUL = 16 bytes max
+ * (quite rare, since most hubs have 4-6 ports).
+ */
+ pdev = dev->parent;
+ if (pdev->devpath [1] != '\0') /* parent not root */
+ len = snprintf (dev->devpath, sizeof dev->devpath,
+ "%s/%d", pdev->devpath, port + 1);
+ else /* root == "/", root port 2 == "/2" */
+ len = snprintf (dev->devpath, sizeof dev->devpath,
+ "/%d", port + 1);
+ if (len == sizeof dev->devpath)
+ warn ("devpath size! usb/%03d/%03d path %s",
+ dev->bus->busnum, dev->devnum, dev->devpath);
+ info("new USB device on bus %d path %s, assigned address %d",
+ dev->bus->busnum, dev->devpath, dev->devnum);
/* Run it through the hoops (find a driver, etc) */
if (!usb_new_device(dev))
usb_hub_port_disable(hub, port);
done:
up(&usb_address0_sem);
- if (portstr)
- kfree(portstr);
- if (tempstr)
- kfree(tempstr);
}
static void usb_hub_events(void)
spin_unlock_irqrestore(&hub_event_lock, flags);
if (hub->error) {
- dbg("resetting hub %d for error %d", dev->devnum, hub->error);
+ dbg("resetting hub %s for error %d",
+ dev->devpath, hub->error);
if (usb_hub_reset(hub)) {
- err("error resetting hub %d - disconnecting", dev->devnum);
+ err("error resetting hub %s - disconnecting",
+ dev->devpath);
up(&hub->khubd_sem);
usb_hub_disconnect(dev);
continue;
ret = usb_get_port_status(dev, i + 1, &portsts);
if (ret < 0) {
- err("get_port_status failed (err = %d)", ret);
+ err("hub %s get_port_status failed (err = %d)",
+ dev->devpath, ret);
continue;
}
portchange = le16_to_cpu(portsts.wPortChange);
if (portchange & USB_PORT_STAT_C_CONNECTION) {
- dbg("port %d connection change", i + 1);
-
- usb_hub_port_connect_change(dev, i, &portsts);
+ dbg("hub %s port %d connection change",
+ dev->devpath, i + 1);
+ usb_hub_port_connect_change(hub, i, &portsts);
} else if (portchange & USB_PORT_STAT_C_ENABLE) {
- dbg("port %d enable change, status %x", i + 1, portstatus);
- usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE);
+ dbg("hub %s port %d enable change, status %x",
+ dev->devpath, i + 1, portstatus);
+ usb_clear_port_feature(dev,
+ i + 1, USB_PORT_FEAT_C_ENABLE);
/*
- * EM interference sometimes causes bad shielded USB devices to
- * be shutdown by the hub, this hack enables them again.
+ * EM interference sometimes causes badly
+ * shielded USB devices to be shutdown by
+ * the hub, this hack enables them again.
* Works at least with mouse driver.
*/
- if (!(portstatus & USB_PORT_STAT_ENABLE) &&
- (portstatus & USB_PORT_STAT_CONNECTION) && (dev->children[i])) {
- err("already running port %i disabled by hub (EMI?), re-enabling...",
- i + 1);
- usb_hub_port_connect_change(dev, i, &portsts);
+ if (!(portstatus & USB_PORT_STAT_ENABLE)
+ && (portstatus & USB_PORT_STAT_CONNECTION)
+ && (dev->children[i])) {
+ err("already running hub %s port %i "
+ "disabled by hub (EMI?), "
+ "re-enabling...",
+ dev->devpath, i + 1);
+ usb_hub_port_connect_change(hub,
+ i, &portsts);
}
}
if (portchange & USB_PORT_STAT_C_SUSPEND) {
- dbg("port %d suspend change", i + 1);
- usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_SUSPEND);
+ dbg("hub %s port %d suspend change",
+ dev->devpath, i + 1);
+ usb_clear_port_feature(dev,
+ i + 1, USB_PORT_FEAT_C_SUSPEND);
}
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
- err("port %d over-current change", i + 1);
- usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_OVER_CURRENT);
+ err("hub %s port %d over-current change",
+ dev->devpath, i + 1);
+ usb_clear_port_feature(dev,
+ i + 1, USB_PORT_FEAT_C_OVER_CURRENT);
usb_hub_power_on(hub);
}
if (portchange & USB_PORT_STAT_C_RESET) {
- dbg("port %d reset change", i + 1);
- usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET);
+ dbg("hub %s port %d reset change",
+ dev->devpath, i + 1);
+ usb_clear_port_feature(dev,
+ i + 1, USB_PORT_FEAT_C_RESET);
}
} /* end for i */
/* deal with hub status changes */
if (usb_get_hub_status(dev, &hubsts) < 0)
- err("get_hub_status failed");
+ err("get_hub_status %s failed", dev->devpath);
else {
hubstatus = le16_to_cpup(&hubsts.wHubStatus);
hubchange = le16_to_cpup(&hubsts.wHubChange);
if (hubchange & HUB_CHANGE_LOCAL_POWER) {
- dbg("hub power change");
+ dbg("hub %s power change", dev->devpath);
usb_clear_hub_feature(dev, C_HUB_LOCAL_POWER);
}
if (hubchange & HUB_CHANGE_OVERCURRENT) {
- dbg("hub overcurrent change");
+ dbg("hub %s overcurrent change", dev->devpath);
wait_ms(500); /* Cool down */
usb_clear_hub_feature(dev, C_HUB_OVER_CURRENT);
usb_hub_power_on(hub);
}
static struct usb_device_id hub_id_table [] = {
+ { match_flags: USB_DEVICE_ID_MATCH_DEV_CLASS,
+ bDeviceClass: USB_CLASS_HUB},
{ match_flags: USB_DEVICE_ID_MATCH_INT_CLASS,
bInterfaceClass: USB_CLASS_HUB},
{ } /* Terminating entry */
ret = usb_get_device_descriptor(dev);
if (ret < sizeof(dev->descriptor)) {
if (ret < 0)
- err("unable to get device descriptor (error=%d)", ret);
+ err("unable to get device %s descriptor "
+ "(error=%d)", dev->devpath, ret);
else
- err("USB device descriptor short read (expected %Zi, got %i)", sizeof(dev->descriptor), ret);
+ err("USB device %s descriptor short read "
+ "(expected %Zi, got %i)",
+ dev->devpath,
+ sizeof(dev->descriptor), ret);
clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
dev->devnum = -1;
ret = usb_set_configuration(dev, dev->actconfig->bConfigurationValue);
if (ret < 0) {
- err("failed to set active configuration (error=%d)", ret);
+ err("failed to set dev %s active configuration (error=%d)",
+ dev->devpath, ret);
return ret;
}
for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
struct usb_interface *intf = &dev->actconfig->interface[i];
- struct usb_interface_descriptor *as = &intf->altsetting[intf->act_altsetting];
+ struct usb_interface_descriptor *as;
- ret = usb_set_interface(dev, as->bInterfaceNumber, as->bAlternateSetting);
+ as = &intf->altsetting[intf->act_altsetting];
+ ret = usb_set_interface(dev, as->bInterfaceNumber,
+ as->bAlternateSetting);
if (ret < 0) {
- err("failed to set active alternate setting for interface %d (error=%d)", i, ret);
+ err("failed to set active alternate setting "
+ "for dev %s interface %d (error=%d)",
+ dev->devpath, i, ret);
return ret;
}
}
#ifndef __LINUX_HUB_H
#define __LINUX_HUB_H
+/*
+ * Hub protocol and driver data structures.
+ *
+ * Some of these are known to the "virtual root hub" code
+ * in host controller drivers.
+ */
+
#include <linux/list.h>
/*
#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE)
#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER)
+/*
+ * Hub class requests
+ * See USB 2.0 spec Table 11-16
+ */
+#define HUB_CLEAR_TT_BUFFER 8
+#define HUB_RESET_TT 9
+#define HUB_GET_TT_STATE 10
+#define HUB_STOP_TT 11
+
/*
* Hub Class feature numbers
* See USB 2.0 spec Table 11-17
#define USB_PORT_STAT_SUSPEND 0x0004
#define USB_PORT_STAT_OVERCURRENT 0x0008
#define USB_PORT_STAT_RESET 0x0010
-/* bits 5 for 7 are reserved */
+/* bits 5 to 7 are reserved */
#define USB_PORT_STAT_POWER 0x0100
#define USB_PORT_STAT_LOW_SPEED 0x0200
#define USB_PORT_STAT_HIGH_SPEED 0x0400
struct usb_device;
struct usb_hub {
- struct usb_device *dev;
-
- struct urb *urb; /* Interrupt polling pipe */
-
- char buffer[(USB_MAXCHILDREN + 1 + 7) / 8]; /* add 1 bit for hub status change */
- /* and add 7 bits to round up to byte boundary */
- int error;
- int nerrors;
+ struct usb_device *dev; /* the "real" device */
+ struct urb *urb; /* for interrupt polling pipe */
- struct list_head hub_list;
+ /* buffer for urb ... 1 bit each for hub and children, rounded up */
+ char buffer[(USB_MAXCHILDREN + 1 + 7) / 8];
- struct list_head event_list;
+ int error; /* last reported error */
+ int nerrors; /* track consecutive errors */
- struct usb_hub_descriptor *descriptor;
+ struct list_head hub_list; /* all hubs */
+ struct list_head event_list; /* hubs w/data or errs ready */
- struct semaphore khubd_sem;
+ struct usb_hub_descriptor *descriptor; /* class descriptor */
+ struct semaphore khubd_sem;
+ struct usb_tt tt; /* Transaction Translator */
};
#endif /* __LINUX_HUB_H */
/*
* inode.c -- Inode/Dentry functions for the USB device file system.
*
- * Copyright (C) 2000
- * Thomas Sailer (sailer@ife.ee.ethz.ch)
+ * Copyright (C) 2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: inode.c,v 1.3 2000/01/11 13:58:25 tom Exp $
- *
* History:
* 0.1 04.01.2000 Created
+ * 0.2 10.12.2001 converted to use the vfs layer better
*/
/*****************************************************************************/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/fs.h>
-#include <linux/sched.h>
-#include <linux/smp_lock.h>
-#include <linux/locks.h>
+#include <linux/pagemap.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/usb.h>
#include <linux/usbdevice_fs.h>
-#include <asm/uaccess.h>
-
-/* --------------------------------------------------------------------- */
-
-/*
- * This list of superblocks is still used,
- * but since usbdevfs became FS_SINGLE
- * there is only one super_block.
- */
-static LIST_HEAD(superlist);
-
-struct special {
- const char *name;
- struct file_operations *fops;
- struct inode *inode;
- struct list_head inodes;
-};
-
-static struct special special[] = {
- { "devices", &usbdevfs_devices_fops, },
- { "drivers", &usbdevfs_drivers_fops, }
-};
-
-#define NRSPECIAL (sizeof(special)/sizeof(special[0]))
-
-/* --------------------------------------------------------------------- */
-
-static int dnumber(struct dentry *dentry)
-{
- const char *name;
- unsigned int s;
-
- if (dentry->d_name.len != 3)
- return -1;
- name = dentry->d_name.name;
- if (name[0] < '0' || name[0] > '9' ||
- name[1] < '0' || name[1] > '9' ||
- name[2] < '0' || name[2] > '9')
- return -1;
- s = name[0] - '0';
- s = s * 10 + name[1] - '0';
- s = s * 10 + name[2] - '0';
- return s;
-}
-
-/*
- * utility functions; should be called with the kernel lock held
- * to protect against busses/devices appearing/disappearing
- */
-
-static void new_dev_inode(struct usb_device *dev, struct super_block *sb)
-{
- struct inode *inode;
- unsigned int devnum = dev->devnum;
- unsigned int busnum = dev->bus->busnum;
-
- if (devnum < 1 || devnum > 127 || busnum > 255)
- return;
- inode = iget(sb, IDEVICE | (busnum << 8) | devnum);
- if (!inode) {
- printk(KERN_ERR "usbdevfs: cannot create inode for bus %u device %u\n", busnum, devnum);
- return;
- }
- inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- inode->i_uid = sb->u.usbdevfs_sb.devuid;
- inode->i_gid = sb->u.usbdevfs_sb.devgid;
- inode->i_mode = sb->u.usbdevfs_sb.devmode | S_IFREG;
- inode->i_fop = &usbdevfs_device_file_operations;
- inode->i_size = sizeof(struct usb_device_descriptor);
- inode->u.usbdev_i.p.dev = dev;
- list_add_tail(&inode->u.usbdev_i.slist, &sb->u.usbdevfs_sb.ilist);
- list_add_tail(&inode->u.usbdev_i.dlist, &dev->inodes);
-}
-static void recurse_new_dev_inode(struct usb_device *dev, struct super_block *sb)
-{
- unsigned int i;
- if (!dev)
- return;
- new_dev_inode(dev, sb);
- for (i = 0; i < dev->maxchild; i++) {
- if (!dev->children[i])
- continue;
- recurse_new_dev_inode(dev->children[i], sb);
- }
-}
-
-static void new_bus_inode(struct usb_bus *bus, struct super_block *sb)
-{
- struct inode *inode;
- unsigned int busnum = bus->busnum;
-
- if (busnum > 255)
- return;
- inode = iget(sb, IBUS | (busnum << 8));
- if (!inode) {
- printk(KERN_ERR "usbdevfs: cannot create inode for bus %u\n", busnum);
- return;
- }
- inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- inode->i_uid = sb->u.usbdevfs_sb.busuid;
- inode->i_gid = sb->u.usbdevfs_sb.busgid;
- inode->i_mode = sb->u.usbdevfs_sb.busmode | S_IFDIR;
- inode->i_op = &usbdevfs_bus_inode_operations;
- inode->i_fop = &usbdevfs_bus_file_operations;
- inode->u.usbdev_i.p.bus = bus;
- list_add_tail(&inode->u.usbdev_i.slist, &sb->u.usbdevfs_sb.ilist);
- list_add_tail(&inode->u.usbdev_i.dlist, &bus->inodes);
-}
-
-static void free_inode(struct inode *inode)
-{
- inode->u.usbdev_i.p.bus = NULL;
- inode->u.usbdev_i.p.dev = NULL;
- inode->i_mode &= ~S_IRWXUGO;
- inode->i_uid = inode->i_gid = 0;
- inode->i_size = 0;
- list_del(&inode->u.usbdev_i.slist);
- INIT_LIST_HEAD(&inode->u.usbdev_i.slist);
- list_del(&inode->u.usbdev_i.dlist);
- INIT_LIST_HEAD(&inode->u.usbdev_i.dlist);
- iput(inode);
-}
+static struct super_operations usbfs_ops;
+static struct address_space_operations usbfs_aops;
+static struct file_operations usbfs_dir_operations;
+static struct file_operations default_file_operations;
+static struct inode_operations usbfs_dir_inode_operations;
+static struct vfsmount *usbfs_mount;
+static spinlock_t mount_lock = SPIN_LOCK_UNLOCKED;
+static int mount_count; /* = 0 */
+
+static struct dentry *devices_dentry;
+static struct dentry *drivers_dentry;
+static int num_buses; /* = 0 */
+
+static uid_t devuid; /* = 0 */
+static uid_t busuid; /* = 0 */
+static uid_t listuid; /* = 0 */
+static gid_t devgid; /* = 0 */
+static gid_t busgid; /* = 0 */
+static gid_t listgid; /* = 0 */
+static umode_t devmode = S_IWUSR | S_IRUGO;
+static umode_t busmode = S_IXUGO | S_IRUGO;
+static umode_t listmode = S_IRUGO;
static int parse_options(struct super_block *s, char *data)
{
- uid_t devuid = 0, busuid = 0, listuid = 0;
- gid_t devgid = 0, busgid = 0, listgid = 0;
- umode_t devmode = S_IWUSR | S_IRUGO, busmode = S_IXUGO | S_IRUGO, listmode = S_IRUGO;
char *curopt = NULL, *value;
- /* parse options */
if (data)
curopt = strtok(data, ",");
for (; curopt; curopt = strtok(NULL, ",")) {
}
}
- s->u.usbdevfs_sb.devuid = devuid;
- s->u.usbdevfs_sb.devgid = devgid;
- s->u.usbdevfs_sb.devmode = devmode;
- s->u.usbdevfs_sb.busuid = busuid;
- s->u.usbdevfs_sb.busgid = busgid;
- s->u.usbdevfs_sb.busmode = busmode;
- s->u.usbdevfs_sb.listuid = listuid;
- s->u.usbdevfs_sb.listgid = listgid;
- s->u.usbdevfs_sb.listmode = listmode;
-
return 0;
}
-static struct usb_bus *usbdevfs_findbus(int busnr)
+
+/* --------------------------------------------------------------------- */
+
+static struct dentry *usbfs_lookup (struct inode *dir, struct dentry *dentry)
{
- struct list_head *list;
- struct usb_bus *bus;
+ d_add(dentry, NULL);
+ return NULL;
+}
- down (&usb_bus_list_lock);
- for (list = usb_bus_list.next; list != &usb_bus_list; list = list->next) {
- bus = list_entry(list, struct usb_bus, bus_list);
- if (bus->busnum == busnr) {
- up (&usb_bus_list_lock);
- return bus;
+static int usbfs_statfs(struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = USBDEVICE_SUPER_MAGIC;
+ buf->f_bsize = PAGE_CACHE_SIZE;
+ buf->f_namelen = NAME_MAX;
+ return 0;
+}
+
+static struct inode *usbfs_get_inode (struct super_block *sb, int mode, int dev)
+{
+ struct inode *inode = new_inode(sb);
+
+ if (inode) {
+ inode->i_mode = mode;
+ inode->i_uid = current->fsuid;
+ inode->i_gid = current->fsgid;
+ inode->i_blksize = PAGE_CACHE_SIZE;
+ inode->i_blocks = 0;
+ inode->i_rdev = NODEV;
+ inode->i_mapping->a_ops = &usbfs_aops;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ switch (mode & S_IFMT) {
+ default:
+ init_special_inode(inode, mode, dev);
+ break;
+ case S_IFREG:
+ inode->i_fop = &default_file_operations;
+ break;
+ case S_IFDIR:
+ inode->i_op = &usbfs_dir_inode_operations;
+ inode->i_fop = &usbfs_dir_operations;
+ break;
}
- }
- up (&usb_bus_list_lock);
- return NULL;
+ }
+ return inode;
}
-#if 0
-static struct usb_device *finddev(struct usb_device *dev, int devnr)
+static int usbfs_mknod (struct inode *dir, struct dentry *dentry, int mode,
+ int dev)
{
- unsigned int i;
- struct usb_device *d2;
+ struct inode *inode = usbfs_get_inode(dir->i_sb, mode, dev);
+ int error = -ENOSPC;
- if (!dev)
- return NULL;
- if (dev->devnum == devnr)
- return dev;
- for (i = 0; i < dev->maxchild; i++) {
- if (!dev->children[i])
- continue;
- if ((d2 = finddev(dev->children[i], devnr)))
- return d2;
- }
- return NULL;
+ if (inode) {
+ d_instantiate(dentry, inode);
+ dget(dentry);
+ error = 0;
+ }
+ return error;
}
-static struct usb_device *usbdevfs_finddevice(struct usb_bus *bus, int devnr)
+static int usbfs_mkdir (struct inode *dir, struct dentry *dentry, int mode)
{
- return finddev(bus->root_hub, devnr);
+ return usbfs_mknod (dir, dentry, mode | S_IFDIR, 0);
}
-#endif
-/* --------------------------------------------------------------------- */
+static int usbfs_create (struct inode *dir, struct dentry *dentry, int mode)
+{
+ return usbfs_mknod (dir, dentry, mode | S_IFREG, 0);
+}
-static int usbdevfs_revalidate(struct dentry *dentry, int flags)
+static int usbfs_link (struct dentry *old_dentry, struct inode *dir,
+ struct dentry *dentry)
{
- struct inode *inode = dentry->d_inode;
+ struct inode *inode = old_dentry->d_inode;
- if (!inode)
- return 0;
- if (ITYPE(inode->i_ino) == IBUS && !inode->u.usbdev_i.p.bus)
- return 0;
- if (ITYPE(inode->i_ino) == IDEVICE && !inode->u.usbdev_i.p.dev)
- return 0;
- return 1;
+ if(S_ISDIR(inode->i_mode))
+ return -EPERM;
+
+ inode->i_nlink++;
+ atomic_inc(&inode->i_count);
+ dget(dentry);
+ d_instantiate(dentry, inode);
+ return 0;
}
-static struct dentry_operations usbdevfs_dentry_operations = {
- d_revalidate: usbdevfs_revalidate,
-};
+static inline int usbfs_positive (struct dentry *dentry)
+{
+ return dentry->d_inode && !d_unhashed(dentry);
+}
-static struct dentry *usbdevfs_root_lookup(struct inode *dir, struct dentry *dentry)
+static int usbfs_empty (struct dentry *dentry)
{
- int busnr;
- unsigned long ino = 0;
- unsigned int i;
- struct inode *inode;
+ struct list_head *list;
- /* sanity check */
- if (dir->i_ino != IROOT)
- return ERR_PTR(-EINVAL);
- dentry->d_op = &usbdevfs_dentry_operations;
- busnr = dnumber(dentry);
- if (busnr >= 0 && busnr <= 255)
- ino = IBUS | (busnr << 8);
- if (!ino) {
- for (i = 0; i < NRSPECIAL; i++) {
- if (strlen(special[i].name) == dentry->d_name.len &&
- !strncmp(special[i].name, dentry->d_name.name, dentry->d_name.len)) {
- ino = ISPECIAL | (i + IROOT + 1);
- break;
- }
+ spin_lock(&dcache_lock);
+
+ list_for_each(list, &dentry->d_subdirs) {
+ struct dentry *de = list_entry(list, struct dentry, d_child);
+ if (usbfs_positive(de)) {
+ spin_unlock(&dcache_lock);
+ return 0;
}
}
- if (!ino)
- return ERR_PTR(-ENOENT);
- inode = iget(dir->i_sb, ino);
- if (!inode)
- return ERR_PTR(-EINVAL);
- if (inode && ITYPE(ino) == IBUS && inode->u.usbdev_i.p.bus == NULL) {
- iput(inode);
- inode = NULL;
- }
- d_add(dentry, inode);
- return NULL;
+
+ spin_unlock(&dcache_lock);
+ return 1;
}
-static struct dentry *usbdevfs_bus_lookup(struct inode *dir, struct dentry *dentry)
+static int usbfs_unlink (struct inode *dir, struct dentry *dentry)
{
- struct inode *inode;
- int devnr;
-
- /* sanity check */
- if (ITYPE(dir->i_ino) != IBUS)
- return ERR_PTR(-EINVAL);
- dentry->d_op = &usbdevfs_dentry_operations;
- devnr = dnumber(dentry);
- if (devnr < 1 || devnr > 127)
- return ERR_PTR(-ENOENT);
- inode = iget(dir->i_sb, IDEVICE | (dir->i_ino & (0xff << 8)) | devnr);
- if (!inode)
- return ERR_PTR(-EINVAL);
- if (inode && inode->u.usbdev_i.p.dev == NULL) {
- iput(inode);
- inode = NULL;
+ int error = -ENOTEMPTY;
+
+ if (usbfs_empty(dentry)) {
+ struct inode *inode = dentry->d_inode;
+
+ inode->i_nlink--;
+ dput(dentry);
+ error = 0;
}
- d_add(dentry, inode);
- return NULL;
+ return error;
}
-static int usbdevfs_root_readdir(struct file *filp, void *dirent, filldir_t filldir)
+static int usbfs_rename (struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
{
- struct inode *inode = filp->f_dentry->d_inode;
- unsigned long ino = inode->i_ino;
- struct special *spec;
- struct list_head *list;
- struct usb_bus *bus;
- char numbuf[8];
- unsigned int i;
-
- /* sanity check */
- if (ino != IROOT)
- return -EINVAL;
- i = filp->f_pos;
- switch (i) {
- case 0:
- if (filldir(dirent, ".", 1, i, IROOT, DT_DIR) < 0)
- return 0;
- filp->f_pos++;
- i++;
- /* fall through */
+ int error = -ENOTEMPTY;
- case 1:
- if (filldir(dirent, "..", 2, i, IROOT, DT_DIR) < 0)
- return 0;
- filp->f_pos++;
- i++;
- /* fall through */
-
- default:
-
- while (i >= 2 && i < 2+NRSPECIAL) {
- spec = &special[filp->f_pos-2];
- if (filldir(dirent, spec->name, strlen(spec->name), i, ISPECIAL | (filp->f_pos-2+IROOT), DT_UNKNOWN) < 0)
- return 0;
- filp->f_pos++;
- i++;
- }
- if (i < 2+NRSPECIAL)
- return 0;
- i -= 2+NRSPECIAL;
- down (&usb_bus_list_lock);
- for (list = usb_bus_list.next; list != &usb_bus_list; list = list->next) {
- if (i > 0) {
- i--;
- continue;
- }
- bus = list_entry(list, struct usb_bus, bus_list);
- sprintf(numbuf, "%03d", bus->busnum);
- if (filldir(dirent, numbuf, 3, filp->f_pos, IBUS | ((bus->busnum & 0xff) << 8), DT_UNKNOWN) < 0)
- break;
- filp->f_pos++;
+ if (usbfs_empty(new_dentry)) {
+ struct inode *inode = new_dentry->d_inode;
+ if (inode) {
+ inode->i_nlink--;
+ dput(new_dentry);
}
- up (&usb_bus_list_lock);
- return 0;
+ error = 0;
}
+ return error;
}
-static int bus_readdir(struct usb_device *dev, unsigned long ino, int pos, struct file *filp, void *dirent, filldir_t filldir)
+#define usbfs_rmdir usbfs_unlink
+
+/* default file operations */
+static ssize_t default_read_file (struct file *file, char *buf,
+ size_t count, loff_t *ppos)
{
- char numbuf[8];
- unsigned int i;
+ return 0;
+}
- if (!dev)
- return pos;
- sprintf(numbuf, "%03d", dev->devnum);
- if (pos > 0)
- pos--;
- else {
- if (filldir(dirent, numbuf, 3, filp->f_pos, ino | (dev->devnum & 0xff), DT_UNKNOWN) < 0)
- return -1;
- filp->f_pos++;
- }
- for (i = 0; i < dev->maxchild; i++) {
- if (!dev->children[i])
- continue;
- pos = bus_readdir(dev->children[i], ino, pos, filp, dirent, filldir);
- if (pos < 0)
- return -1;
- }
- return pos;
+static ssize_t default_write_file (struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ return count;
}
-static int usbdevfs_bus_readdir(struct file *filp, void *dirent, filldir_t filldir)
+static loff_t default_file_lseek (struct file *file, loff_t offset, int orig)
{
- struct inode *inode = filp->f_dentry->d_inode;
- unsigned long ino = inode->i_ino;
- struct usb_bus *bus;
+ loff_t retval = -EINVAL;
- /* sanity check */
- if (ITYPE(ino) != IBUS)
- return -EINVAL;
- switch ((unsigned int)filp->f_pos) {
+ switch(orig) {
case 0:
- if (filldir(dirent, ".", 1, filp->f_pos, ino, DT_DIR) < 0)
- return 0;
- filp->f_pos++;
- /* fall through */
-
+ if (offset > 0) {
+ file->f_pos = offset;
+ retval = file->f_pos;
+ }
+ break;
case 1:
- if (filldir(dirent, "..", 2, filp->f_pos, IROOT, DT_DIR) < 0)
- return 0;
- filp->f_pos++;
- /* fall through */
-
+ if ((offset + file->f_pos) > 0) {
+ file->f_pos += offset;
+ retval = file->f_pos;
+ }
+ break;
default:
- lock_kernel();
- bus = usbdevfs_findbus(IBUSNR(ino));
- bus_readdir(bus->root_hub, IDEVICE | ((bus->busnum & 0xff) << 8), filp->f_pos-2, filp, dirent, filldir);
- unlock_kernel();
- return 0;
+ break;
}
+ return retval;
+}
+
+static int default_open (struct inode *inode, struct file *filp)
+{
+ if (inode->u.generic_ip)
+ filp->private_data = inode->u.generic_ip;
+
+ return 0;
+}
+
+static int default_sync_file (struct file *file, struct dentry *dentry,
+ int datasync)
+{
+ return 0;
}
-static struct file_operations usbdevfs_root_file_operations = {
- readdir: usbdevfs_root_readdir,
+static struct address_space_operations usbfs_aops = {
};
-static struct inode_operations usbdevfs_root_inode_operations = {
- lookup: usbdevfs_root_lookup,
+static struct file_operations usbfs_dir_operations = {
+ read: generic_read_dir,
+ readdir: dcache_readdir,
+ fsync: default_sync_file,
};
-static struct file_operations usbdevfs_bus_file_operations = {
- readdir: usbdevfs_bus_readdir,
+static struct file_operations default_file_operations = {
+ read: default_read_file,
+ write: default_write_file,
+ open: default_open,
+ llseek: default_file_lseek,
+ fsync: default_sync_file,
+ mmap: generic_file_mmap,
};
-static struct inode_operations usbdevfs_bus_inode_operations = {
- lookup: usbdevfs_bus_lookup,
+static struct inode_operations usbfs_dir_inode_operations = {
+ create: usbfs_create,
+ lookup: usbfs_lookup,
+ link: usbfs_link,
+ unlink: usbfs_unlink,
+ mkdir: usbfs_mkdir,
+ rmdir: usbfs_rmdir,
+ mknod: usbfs_mknod,
+ rename: usbfs_rename,
};
-static void usbdevfs_read_inode(struct inode *inode)
-{
- struct special *spec;
-
- inode->i_ctime = inode->i_mtime = inode->i_atime = CURRENT_TIME;
- inode->i_mode = S_IFREG;
- inode->i_gid = inode->i_uid = 0;
- INIT_LIST_HEAD(&inode->u.usbdev_i.dlist);
- INIT_LIST_HEAD(&inode->u.usbdev_i.slist);
- inode->u.usbdev_i.p.dev = NULL;
- inode->u.usbdev_i.p.bus = NULL;
- switch (ITYPE(inode->i_ino)) {
- case ISPECIAL:
- if (inode->i_ino == IROOT) {
- inode->i_op = &usbdevfs_root_inode_operations;
- inode->i_fop = &usbdevfs_root_file_operations;
- inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
- return;
- }
- if (inode->i_ino <= IROOT || inode->i_ino > IROOT+NRSPECIAL)
- return;
- spec = &special[inode->i_ino-(IROOT+1)];
- inode->i_fop = spec->fops;
- return;
+static struct super_operations usbfs_ops = {
+ statfs: usbfs_statfs,
+ put_inode: force_delete,
+};
- case IDEVICE:
- return;
+static struct super_block *usbfs_read_super (struct super_block *sb, void *data,
+ int silent)
+{
+ struct inode *inode;
+ struct dentry *root;
- case IBUS:
- return;
+ if (parse_options(sb, data)) {
+ warn("usbfs: mount parameter error:");
+ return NULL;
+ }
- default:
- return;
- }
-}
+ sb->s_blocksize = PAGE_CACHE_SIZE;
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ sb->s_magic = USBDEVICE_SUPER_MAGIC;
+ sb->s_op = &usbfs_ops;
+ inode = usbfs_get_inode(sb, S_IFDIR | 0755, 0);
-static void usbdevfs_put_super(struct super_block *sb)
-{
- list_del(&sb->u.usbdevfs_sb.slist);
- INIT_LIST_HEAD(&sb->u.usbdevfs_sb.slist);
- while (!list_empty(&sb->u.usbdevfs_sb.ilist))
- free_inode(list_entry(sb->u.usbdevfs_sb.ilist.next, struct inode, u.usbdev_i.slist));
-}
+ if (!inode) {
+ dbg("%s: could not get inode!\n",__FUNCTION__);
+ return NULL;
+ }
-static int usbdevfs_statfs(struct super_block *sb, struct statfs *buf)
-{
- buf->f_type = USBDEVICE_SUPER_MAGIC;
- buf->f_bsize = PAGE_SIZE/sizeof(long); /* ??? */
- buf->f_bfree = 0;
- buf->f_bavail = 0;
- buf->f_ffree = 0;
- buf->f_namelen = NAME_MAX;
- return 0;
+ root = d_alloc_root(inode);
+ if (!root) {
+ dbg("%s: could not get root dentry!\n",__FUNCTION__);
+ iput(inode);
+ return NULL;
+ }
+ sb->s_root = root;
+ return sb;
}
-static int usbdevfs_remount(struct super_block *s, int *flags, char *data)
+/**
+ * fs_create_by_name - create a file, given a name
+ * @name: name of file
+ * @mode: type of file
+ * @parent: dentry of directory to create it in
+ * @dentry: resulting dentry of file
+ *
+ * There is a bit of overhead in creating a file - basically, we
+ * have to hash the name of the file, then look it up. This will
+ * prevent files of the same name.
+ * We then call the proper vfs_ function to take care of all the
+ * file creation details.
+ * This function handles both regular files and directories.
+ */
+static int fs_create_by_name (const char *name, mode_t mode,
+ struct dentry *parent, struct dentry **dentry)
{
- struct list_head *ilist = s->u.usbdevfs_sb.ilist.next;
- struct inode *inode;
- int ret;
+ struct dentry *d = NULL;
+ struct qstr qstr;
+ int error;
+
+ /* If the parent is not specified, we create it in the root.
+ * We need the root dentry to do this, which is in the super
+ * block. A pointer to that is in the struct vfsmount that we
+ * have around.
+ */
+ if (!parent ) {
+ if (usbfs_mount && usbfs_mount->mnt_sb) {
+ parent = usbfs_mount->mnt_sb->s_root;
+ }
+ }
- if ((ret = parse_options(s, data))) {
- printk(KERN_WARNING "usbdevfs: remount parameter error\n");
- return ret;
+ if (!parent) {
+ dbg("Ah! can not find a parent!\n");
+ return -EFAULT;
}
- for (; ilist != &s->u.usbdevfs_sb.ilist; ilist = ilist->next) {
- inode = list_entry(ilist, struct inode, u.usbdev_i.slist);
+ *dentry = NULL;
+ qstr.name = name;
+ qstr.len = strlen(name);
+ qstr.hash = full_name_hash(name,qstr.len);
- switch (ITYPE(inode->i_ino)) {
- case ISPECIAL :
- inode->i_uid = s->u.usbdevfs_sb.listuid;
- inode->i_gid = s->u.usbdevfs_sb.listgid;
- inode->i_mode = s->u.usbdevfs_sb.listmode | S_IFREG;
- break;
- case IBUS :
- inode->i_uid = s->u.usbdevfs_sb.busuid;
- inode->i_gid = s->u.usbdevfs_sb.busgid;
- inode->i_mode = s->u.usbdevfs_sb.busmode | S_IFDIR;
+ parent = dget(parent);
+
+ down(&parent->d_inode->i_sem);
+
+ d = lookup_hash(&qstr,parent);
+
+ error = PTR_ERR(d);
+ if (!IS_ERR(d)) {
+ switch(mode & S_IFMT) {
+ case 0:
+ case S_IFREG:
+ error = vfs_create(parent->d_inode,d,mode);
break;
- case IDEVICE :
- inode->i_uid = s->u.usbdevfs_sb.devuid;
- inode->i_gid = s->u.usbdevfs_sb.devgid;
- inode->i_mode = s->u.usbdevfs_sb.devmode | S_IFREG;
+ case S_IFDIR:
+ error = vfs_mkdir(parent->d_inode,d,mode);
break;
+ default:
+ err("cannot create special files\n");
}
+ *dentry = d;
}
+ up(&parent->d_inode->i_sem);
- return 0;
+ dput(parent);
+ return error;
}
-static struct super_operations usbdevfs_sops = {
- read_inode: usbdevfs_read_inode,
- put_super: usbdevfs_put_super,
- statfs: usbdevfs_statfs,
- remount_fs: usbdevfs_remount,
-};
+static struct dentry *fs_create_file (const char *name, mode_t mode,
+ struct dentry *parent, void *data,
+ struct file_operations *fops,
+ uid_t uid, gid_t gid)
+{
+ struct dentry *dentry;
+ int error;
+
+ dbg("creating file '%s'\n",name);
+
+ error = fs_create_by_name(name,mode,parent,&dentry);
+ if (error) {
+ dentry = NULL;
+ } else {
+ if (dentry->d_inode) {
+ if (data)
+ dentry->d_inode->u.generic_ip = data;
+ if (fops)
+ dentry->d_inode->i_fop = fops;
+ dentry->d_inode->i_uid = uid;
+ dentry->d_inode->i_gid = gid;
+ }
+ }
+
+ return dentry;
+}
-struct super_block *usbdevfs_read_super(struct super_block *s, void *data, int silent)
+static void fs_remove_file (struct dentry *dentry)
{
- struct inode *root_inode, *inode;
- struct list_head *blist;
- struct usb_bus *bus;
- unsigned int i;
+ struct dentry *parent = dentry->d_parent;
+
+ if (!parent || !parent->d_inode)
+ return;
- if (parse_options(s, data)) {
- printk(KERN_WARNING "usbdevfs: mount parameter error\n");
- return NULL;
- }
+ down(&parent->d_inode->i_sem);
+ if (usbfs_positive(dentry)) {
+ if (dentry->d_inode) {
+ if (S_ISDIR(dentry->d_inode->i_mode))
+ vfs_rmdir(parent->d_inode,dentry);
+ else
+ vfs_unlink(parent->d_inode,dentry);
+ }
- /* fill superblock */
- s->s_blocksize = 1024;
- s->s_blocksize_bits = 10;
- s->s_magic = USBDEVICE_SUPER_MAGIC;
- s->s_op = &usbdevfs_sops;
- INIT_LIST_HEAD(&s->u.usbdevfs_sb.slist);
- INIT_LIST_HEAD(&s->u.usbdevfs_sb.ilist);
- root_inode = iget(s, IROOT);
- if (!root_inode)
- goto out_no_root;
- s->s_root = d_alloc_root(root_inode);
- if (!s->s_root)
- goto out_no_root;
- list_add_tail(&s->u.usbdevfs_sb.slist, &superlist);
- for (i = 0; i < NRSPECIAL; i++) {
- if (!(inode = iget(s, IROOT+1+i)))
- continue;
- inode->i_uid = s->u.usbdevfs_sb.listuid;
- inode->i_gid = s->u.usbdevfs_sb.listgid;
- inode->i_mode = s->u.usbdevfs_sb.listmode | S_IFREG;
- special[i].inode = inode;
- list_add_tail(&inode->u.usbdev_i.slist, &s->u.usbdevfs_sb.ilist);
- list_add_tail(&inode->u.usbdev_i.dlist, &special[i].inodes);
- }
- down (&usb_bus_list_lock);
- for (blist = usb_bus_list.next; blist != &usb_bus_list; blist = blist->next) {
- bus = list_entry(blist, struct usb_bus, bus_list);
- new_bus_inode(bus, s);
- recurse_new_dev_inode(bus->root_hub, s);
+ dput(dentry);
}
- up (&usb_bus_list_lock);
- return s;
-
- out_no_root:
- printk("usbdevfs_read_super: get root inode failed\n");
- iput(root_inode);
- return NULL;
+ up(&parent->d_inode->i_sem);
}
+/* --------------------------------------------------------------------- */
+
+
+
/*
- * The usbdevfs name is now depreciated (as of 2.5.1).
+ * The usbdevfs name is now deprecated (as of 2.5.1).
* It will be removed when the 2.7.x development cycle is started.
* You have been warned :)
*/
-static DECLARE_FSTYPE(usbdevice_fs_type, "usbdevfs", usbdevfs_read_super, FS_SINGLE);
-static DECLARE_FSTYPE(usb_fs_type, "usbfs", usbdevfs_read_super, FS_SINGLE);
+static DECLARE_FSTYPE(usbdevice_fs_type, "usbdevfs", usbfs_read_super, FS_SINGLE);
+static DECLARE_FSTYPE(usb_fs_type, "usbfs", usbfs_read_super, FS_SINGLE);
/* --------------------------------------------------------------------- */
+static int get_mount (void)
+{
+ struct vfsmount *mnt;
+
+ spin_lock (&mount_lock);
+ if (usbfs_mount) {
+ mntget(usbfs_mount);
+ ++mount_count;
+ spin_unlock (&mount_lock);
+ goto go_ahead;
+ }
+
+ spin_unlock (&mount_lock);
+ mnt = kern_mount (&usbdevice_fs_type);
+ if (IS_ERR(mnt)) {
+ err ("could not mount the fs...erroring out!\n");
+ return -ENODEV;
+ }
+ spin_lock (&mount_lock);
+ if (!usbfs_mount) {
+ usbfs_mount = mnt;
+ ++mount_count;
+ spin_unlock (&mount_lock);
+ goto go_ahead;
+ }
+ mntget(usbfs_mount);
+ ++mount_count;
+ spin_unlock (&mount_lock);
+ mntput(mnt);
+
+go_ahead:
+ dbg("mount_count = %d\n", mount_count);
+ return 0;
+}
+
+static void remove_mount (void)
+{
+ struct vfsmount *mnt;
+
+ spin_lock (&mount_lock);
+ mnt = usbfs_mount;
+ --mount_count;
+ if (!mount_count)
+ usbfs_mount = NULL;
+
+ spin_unlock (&mount_lock);
+ mntput(mnt);
+ dbg("mount_count = %d\n", mount_count);
+}
-static void update_special_inodes (void)
+static int create_special_files (void)
{
- int i;
- for (i = 0; i < NRSPECIAL; i++) {
- struct inode *inode = special[i].inode;
+ int retval;
+
+ /* create the devices and drivers special files */
+ retval = get_mount ();
+ if (retval)
+ return retval;
+ devices_dentry = fs_create_file ("devices",
+ listmode | S_IFREG,
+ NULL, NULL,
+ &usbdevfs_devices_fops,
+ listuid, listgid);
+ if (devices_dentry == NULL) {
+ err ("Unable to create devices usbfs file");
+ return -ENODEV;
+ }
+
+ drivers_dentry = fs_create_file ("drivers",
+ listmode | S_IFREG,
+ NULL, NULL,
+ &usbdevfs_drivers_fops,
+ listuid, listgid);
+ if (drivers_dentry == NULL) {
+ err ("Unable to create drivers usbfs file");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void remove_special_files (void)
+{
+ if (devices_dentry)
+ fs_remove_file (devices_dentry);
+ if (drivers_dentry)
+ fs_remove_file (drivers_dentry);
+ devices_dentry = NULL;
+ drivers_dentry = NULL;
+ remove_mount();
+}
+
+void usbfs_update_special (void)
+{
+ struct inode *inode;
+
+ if (devices_dentry) {
+ inode = devices_dentry->d_inode;
+ if (inode)
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ }
+ if (drivers_dentry) {
+ inode = devices_dentry->d_inode;
if (inode)
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
}
}
-
-void usbdevfs_add_bus(struct usb_bus *bus)
+void usbfs_add_bus(struct usb_bus *bus)
{
- struct list_head *slist;
+ char name[8];
+ int retval;
+
+ /* create the special files if this is the first bus added */
+ if (num_buses == 0) {
+ retval = create_special_files();
+ if (retval)
+ return;
+ }
+ ++num_buses;
+
+ sprintf (name, "%03d", bus->busnum);
+ bus->dentry = fs_create_file (name,
+ busmode | S_IFDIR,
+ NULL, bus, NULL,
+ busuid, busgid);
+ if (bus->dentry == NULL)
+ return;
- lock_kernel();
- for (slist = superlist.next; slist != &superlist; slist = slist->next)
- new_bus_inode(bus, list_entry(slist, struct super_block, u.usbdevfs_sb.slist));
- update_special_inodes();
- unlock_kernel();
+ usbfs_update_special();
usbdevfs_conn_disc_event();
}
-void usbdevfs_remove_bus(struct usb_bus *bus)
+
+void usbfs_remove_bus(struct usb_bus *bus)
{
- lock_kernel();
- while (!list_empty(&bus->inodes))
- free_inode(list_entry(bus->inodes.next, struct inode, u.usbdev_i.dlist));
- update_special_inodes();
- unlock_kernel();
+ if (bus->dentry) {
+ fs_remove_file (bus->dentry);
+ bus->dentry = NULL;
+ }
+
+ --num_buses;
+ if (num_buses <= 0) {
+ remove_special_files();
+ num_buses = 0;
+ }
+
+ usbfs_update_special();
usbdevfs_conn_disc_event();
}
-void usbdevfs_add_device(struct usb_device *dev)
+void usbfs_add_device(struct usb_device *dev)
{
- struct list_head *slist;
+ char name[8];
+
+ sprintf (name, "%03d", dev->devnum);
+ dev->dentry = fs_create_file (name,
+ devmode | S_IFREG,
+ dev->bus->dentry, dev,
+ &usbdevfs_device_file_operations,
+ devuid, devgid);
+ if (dev->dentry == NULL)
+ return;
- lock_kernel();
- for (slist = superlist.next; slist != &superlist; slist = slist->next)
- new_dev_inode(dev, list_entry(slist, struct super_block, u.usbdevfs_sb.slist));
- update_special_inodes();
- unlock_kernel();
+ usbfs_update_special();
usbdevfs_conn_disc_event();
}
-void usbdevfs_remove_device(struct usb_device *dev)
+void usbfs_remove_device(struct usb_device *dev)
{
struct dev_state *ds;
struct siginfo sinfo;
- lock_kernel();
- while (!list_empty(&dev->inodes))
- free_inode(list_entry(dev->inodes.next, struct inode, u.usbdev_i.dlist));
+ if (dev->dentry) {
+ fs_remove_file (dev->dentry);
+ dev->dentry = NULL;
+ }
while (!list_empty(&dev->filelist)) {
ds = list_entry(dev->filelist.next, struct dev_state, list);
list_del(&ds->list);
send_sig_info(ds->discsignr, &sinfo, ds->disctask);
}
}
-
- update_special_inodes();
- unlock_kernel();
+ usbfs_update_special();
usbdevfs_conn_disc_event();
}
static struct proc_dir_entry *usbdir = NULL;
#endif
-int __init usbdevfs_init(void)
+int __init usbfs_init(void)
{
- int ret;
+ int retval;
- for (ret = 0; ret < NRSPECIAL; ret++) {
- INIT_LIST_HEAD(&special[ret].inodes);
- }
- if ((ret = usb_register(&usbdevfs_driver)))
- return ret;
- if ((ret = register_filesystem(&usb_fs_type))) {
+ retval = usb_register(&usbdevfs_driver);
+ if (retval)
+ return retval;
+
+ retval = register_filesystem(&usb_fs_type);
+ if (retval) {
usb_deregister(&usbdevfs_driver);
- return ret;
+ return retval;
}
- if ((ret = register_filesystem(&usbdevice_fs_type))) {
+ retval = register_filesystem(&usbdevice_fs_type);
+ if (retval) {
unregister_filesystem(&usb_fs_type);
usb_deregister(&usbdevfs_driver);
- return ret;
+ return retval;
}
+
#ifdef CONFIG_PROC_FS
/* create mount point for usbdevfs */
usbdir = proc_mkdir("usb", proc_bus);
#endif
- return ret;
+
+ return 0;
}
-void __exit usbdevfs_cleanup(void)
+void __exit usbfs_cleanup(void)
{
usb_deregister(&usbdevfs_driver);
unregister_filesystem(&usb_fs_type);
unregister_filesystem(&usbdevice_fs_type);
#ifdef CONFIG_PROC_FS
- if (usbdir)
- remove_proc_entry("usb", proc_bus);
+ if (usbdir)
+ remove_proc_entry("usb", proc_bus);
#endif
}
unsigned int endpoint [4];
- purb_t irq_urb;
+ struct urb * irq_urb;
wait_queue_head_t irq_wait;
int irq_woken;
char* irq_urb_buffer;
int camera_request_ready; // Status to synchronize with irq
char camera_response [8]; // last Bytes send after busy
- purb_t write_urb;
+ struct urb * write_urb;
char* write_urb_buffer;
wait_queue_head_t write_wait;
int written;
- purb_t download_urb;
+ struct urb * download_urb;
char* download_urb_buffer;
wait_queue_head_t download_wait;
int downloaded;
/* This gets called for the Isochronous pipe (video). This is done in
* interrupt time, so it has to be fast, not crash, and not stall. Neat.
*/
-static void pwc_isoc_handler(purb_t urb)
+static void pwc_isoc_handler(struct urb *urb)
{
struct pwc_device *pdev;
int i, fst, flen;
static int pwc_isoc_init(struct pwc_device *pdev)
{
struct usb_device *udev;
- purb_t urb;
+ struct urb *urb;
int i, j, ret;
struct usb_interface_descriptor *idesc;
void *data;
int length;
int read;
- purb_t urb;
+ struct urb *urb;
};
/* intermediate buffers with raw data from the USB cam */
--- /dev/null
+/*
+ * USB Compaq iPAQ driver
+ *
+ * Copyright (C) 2001
+ * Ganesh Varadarajan <ganesh@veritas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#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/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+ static int debug = 1;
+#else
+ static int debug = 0;
+#endif
+
+#include "usb-serial.h"
+#include "ipaq.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.1"
+#define DRIVER_AUTHOR "Ganesh Varadarajan <ganesh@veritas.com>"
+#define DRIVER_DESC "USB Compaq iPAQ driver"
+
+/* Function prototypes for an ipaq */
+static int ipaq_open (struct usb_serial_port *port, struct file *filp);
+static void ipaq_close (struct usb_serial_port *port, struct file *filp);
+static int ipaq_startup (struct usb_serial *serial);
+static void ipaq_shutdown (struct usb_serial *serial);
+static int ipaq_write(struct usb_serial_port *port, int from_user, const unsigned char *buf,
+ int count);
+static int ipaq_write_bulk(struct usb_serial_port *port, int from_user, const unsigned char *buf,
+ int count);
+static int ipaq_write_flush(struct usb_serial_port *port);
+static void ipaq_read_bulk_callback (struct urb *urb);
+static void ipaq_write_bulk_callback(struct urb *urb);
+static int ipaq_write_room(struct usb_serial_port *port);
+static int ipaq_chars_in_buffer(struct usb_serial_port *port);
+static void ipaq_destroy_lists(struct usb_serial_port *port);
+
+
+static __devinitdata struct usb_device_id ipaq_id_table [] = {
+ { USB_DEVICE(IPAQ_VENDOR_ID, IPAQ_PRODUCT_ID) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, ipaq_id_table);
+
+/* All of the device info needed for the Compaq iPAQ */
+struct usb_serial_device_type ipaq_device = {
+ owner: THIS_MODULE,
+ name: "Compaq iPAQ",
+ id_table: ipaq_id_table,
+ num_interrupt_in: 0,
+ num_bulk_in: 1,
+ num_bulk_out: 1,
+ num_ports: 1,
+ open: ipaq_open,
+ close: ipaq_close,
+ startup: ipaq_startup,
+ shutdown: ipaq_shutdown,
+ write: ipaq_write,
+ write_room: ipaq_write_room,
+ chars_in_buffer: ipaq_chars_in_buffer,
+ read_bulk_callback: ipaq_read_bulk_callback,
+ write_bulk_callback: ipaq_write_bulk_callback,
+};
+
+static spinlock_t write_list_lock;
+static int bytes_in;
+static int bytes_out;
+
+static int ipaq_open(struct usb_serial_port *port, struct file *filp)
+{
+ struct usb_serial *serial = port->serial;
+ struct ipaq_private *priv;
+ struct ipaq_packet *pkt;
+ int i, result = 0;
+
+ if (port_paranoia_check(port, __FUNCTION__)) {
+ return -ENODEV;
+ }
+
+ dbg(__FUNCTION__ " - port %d", port->number);
+
+ down(&port->sem);
+
+ ++port->open_count;
+
+ if (port->open_count == 1) {
+ bytes_in = 0;
+ bytes_out = 0;
+ priv = (struct ipaq_private *)kmalloc(sizeof(struct ipaq_private), GFP_KERNEL);
+ if (priv == NULL) {
+ err(__FUNCTION__ " - Out of memory");
+ return -ENOMEM;
+ }
+ port->private = (void *)priv;
+ priv->active = 0;
+ priv->queue_len = 0;
+ INIT_LIST_HEAD(&priv->queue);
+ INIT_LIST_HEAD(&priv->freelist);
+
+ for (i = 0; i < URBDATA_QUEUE_MAX / PACKET_SIZE; i++) {
+ pkt = kmalloc(sizeof(struct ipaq_packet), GFP_KERNEL);
+ if (pkt == NULL) {
+ goto enomem;
+ }
+ pkt->data = kmalloc(PACKET_SIZE, GFP_KERNEL);
+ if (pkt->data == NULL) {
+ kfree(pkt);
+ goto enomem;
+ }
+ pkt->len = 0;
+ pkt->written = 0;
+ INIT_LIST_HEAD(&pkt->list);
+ list_add(&pkt->list, &priv->freelist);
+ priv->free_len += PACKET_SIZE;
+ }
+
+ /*
+ * Force low latency on. This will immediately push data to the line
+ * discipline instead of queueing.
+ */
+
+ port->tty->low_latency = 1;
+
+ /*
+ * Lose the small buffers usbserial provides. Make larger ones.
+ */
+
+ kfree(port->bulk_in_buffer);
+ kfree(port->bulk_out_buffer);
+ port->bulk_in_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL);
+ if (port->bulk_in_buffer == NULL) {
+ goto enomem;
+ }
+ port->bulk_out_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL);
+ if (port->bulk_out_buffer == NULL) {
+ kfree(port->bulk_in_buffer);
+ goto enomem;
+ }
+ port->read_urb->transfer_buffer = port->bulk_in_buffer;
+ port->write_urb->transfer_buffer = port->bulk_out_buffer;
+ port->read_urb->transfer_buffer_length = URBDATA_SIZE;
+ port->bulk_out_size = port->write_urb->transfer_buffer_length = URBDATA_SIZE;
+
+ /* Start reading from the device */
+ FILL_BULK_URB(port->read_urb, serial->dev,
+ usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
+ ipaq_read_bulk_callback, port);
+ result = usb_submit_urb(port->read_urb);
+ if (result) {
+ err(__FUNCTION__ " - failed submitting read urb, error %d", result);
+ }
+
+ /*
+ * Send out two control messages observed in win98 sniffs. Not sure what
+ * they do.
+ */
+
+ result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21,
+ 0x1, 0, NULL, 0, 5 * HZ);
+ if (result < 0) {
+ err(__FUNCTION__ " - failed doing control urb, error %d", result);
+ }
+ result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21,
+ 0x1, 0, NULL, 0, 5 * HZ);
+ if (result < 0) {
+ err(__FUNCTION__ " - failed doing control urb, error %d", result);
+ }
+ }
+
+ up(&port->sem);
+
+ return result;
+
+enomem:
+ ipaq_destroy_lists(port);
+ kfree(priv);
+ err(__FUNCTION__ " - Out of memory");
+ return -ENOMEM;
+}
+
+
+static void ipaq_close(struct usb_serial_port *port, struct file *filp)
+{
+ struct usb_serial *serial;
+ struct ipaq_private *priv = port->private;
+
+ if (port_paranoia_check(port, __FUNCTION__)) {
+ return;
+ }
+
+ dbg(__FUNCTION__ " - port %d", port->number);
+
+ serial = get_usb_serial(port, __FUNCTION__);
+ if (!serial)
+ return;
+
+ down (&port->sem);
+
+ --port->open_count;
+
+ if (port->open_count <= 0) {
+
+ /*
+ * shut down bulk read and write
+ */
+
+ usb_unlink_urb(port->write_urb);
+ usb_unlink_urb(port->read_urb);
+ ipaq_destroy_lists(port);
+ kfree(priv);
+ port->private = NULL;
+ port->open_count = 0;
+
+ }
+ up (&port->sem);
+
+ /* Uncomment the following line if you want to see some statistics in your syslog */
+ /* info ("Bytes In = %d Bytes Out = %d", bytes_in, bytes_out); */
+}
+
+static void ipaq_read_bulk_callback(struct urb *urb)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+ struct tty_struct *tty;
+ unsigned char *data = urb->transfer_buffer;
+ int i, result;
+
+ if (port_paranoia_check(port, __FUNCTION__))
+ return;
+
+ dbg(__FUNCTION__ " - port %d", port->number);
+
+ if (!serial) {
+ dbg(__FUNCTION__ " - bad serial pointer, exiting");
+ return;
+ }
+
+ if (urb->status) {
+ dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status);
+ return;
+ }
+
+ usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+
+ tty = port->tty;
+ if (urb->actual_length) {
+ for (i = 0; i < urb->actual_length ; ++i) {
+ /* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */
+ if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ tty_flip_buffer_push(tty);
+ }
+ /* this doesn't actually push the data through unless tty->low_latency is set */
+ tty_insert_flip_char(tty, data[i], 0);
+ }
+ tty_flip_buffer_push(tty);
+ bytes_in += urb->actual_length;
+ }
+
+ /* Continue trying to always read */
+ FILL_BULK_URB(port->read_urb, serial->dev,
+ usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
+ ipaq_read_bulk_callback, port);
+ result = usb_submit_urb(port->read_urb);
+ if (result)
+ err(__FUNCTION__ " - failed resubmitting read urb, error %d", result);
+ return;
+}
+
+static int ipaq_write(struct usb_serial_port *port, int from_user, const unsigned char *buf,
+ int count)
+{
+ const unsigned char *current_position = buf;
+ int bytes_sent = 0;
+ int transfer_size;
+
+ dbg(__FUNCTION__ " - port %d", port->number);
+
+ usb_serial_debug_data(__FILE__, __FUNCTION__, count, buf);
+
+ while (count > 0) {
+ transfer_size = min(count, PACKET_SIZE);
+ if (ipaq_write_bulk(port, from_user, current_position, transfer_size)) {
+ break;
+ }
+ current_position += transfer_size;
+ bytes_sent += transfer_size;
+ count -= transfer_size;
+ bytes_out += transfer_size;
+ }
+
+ return bytes_sent;
+}
+
+static int ipaq_write_bulk(struct usb_serial_port *port, int from_user, const unsigned char *buf,
+ int count)
+{
+ struct ipaq_private *priv = port->private;
+ struct ipaq_packet *pkt = NULL;
+ int result = 0;
+ unsigned long flags;
+
+ if (priv->free_len <= 0) {
+ dbg(__FUNCTION__ " - we're stuffed");
+ return -EAGAIN;
+ }
+
+ spin_lock_irqsave(&write_list_lock, flags);
+ if (!list_empty(&priv->freelist)) {
+ pkt = list_entry(priv->freelist.next, struct ipaq_packet, list);
+ list_del(&pkt->list);
+ priv->free_len -= PACKET_SIZE;
+ }
+ spin_unlock_irqrestore(&write_list_lock, flags);
+ if (pkt == NULL) {
+ dbg(__FUNCTION__ " - we're stuffed");
+ return -EAGAIN;
+ }
+
+ if (from_user) {
+ copy_from_user(pkt->data, buf, count);
+ } else {
+ memcpy(pkt->data, buf, count);
+ }
+ usb_serial_debug_data(__FILE__, __FUNCTION__, count, pkt->data);
+
+ pkt->len = count;
+ pkt->written = 0;
+ spin_lock_irqsave(&write_list_lock, flags);
+ list_add_tail(&pkt->list, &priv->queue);
+ priv->queue_len += count;
+ if (priv->active == 0) {
+ priv->active = 1;
+ result = ipaq_write_flush(port);
+ }
+ spin_unlock_irqrestore(&write_list_lock, flags);
+ return result;
+}
+
+static int ipaq_write_flush(struct usb_serial_port *port)
+{
+ struct ipaq_private *priv = (struct ipaq_private *)port->private;
+ struct usb_serial *serial = port->serial;
+ int count, room, result;
+ struct ipaq_packet *pkt;
+ struct urb *urb = port->write_urb;
+ struct list_head *tmp;
+
+ if (urb->status == -EINPROGRESS) {
+ /* Should never happen */
+ err(__FUNCTION__ " - flushing while urb is active !");
+ return -EAGAIN;
+ }
+ room = URBDATA_SIZE;
+ for (tmp = priv->queue.next; tmp != &priv->queue;) {
+ pkt = list_entry(tmp, struct ipaq_packet, list);
+ tmp = tmp->next;
+ count = min(room, (int)(pkt->len - pkt->written));
+ memcpy(urb->transfer_buffer + (URBDATA_SIZE - room),
+ pkt->data + pkt->written, count);
+ room -= count;
+ pkt->written += count;
+ priv->queue_len -= count;
+ if (pkt->written == pkt->len) {
+ list_del(&pkt->list);
+ list_add(&pkt->list, &priv->freelist);
+ priv->free_len += PACKET_SIZE;
+ }
+ if (room == 0) {
+ break;
+ }
+ }
+
+ count = URBDATA_SIZE - room;
+ FILL_BULK_URB(port->write_urb, serial->dev,
+ usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress),
+ port->write_urb->transfer_buffer, count, ipaq_write_bulk_callback,
+ port);
+ result = usb_submit_urb(urb);
+ if (result) {
+ err(__FUNCTION__ " - failed submitting write urb, error %d", result);
+ }
+ return result;
+}
+
+static void ipaq_write_bulk_callback(struct urb *urb)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ struct ipaq_private *priv = (struct ipaq_private *)port->private;
+ unsigned long flags;
+
+ if (port_paranoia_check (port, __FUNCTION__)) {
+ return;
+ }
+
+ dbg(__FUNCTION__ " - port %d", port->number);
+
+ if (urb->status) {
+ dbg(__FUNCTION__ " - nonzero write bulk status received: %d", urb->status);
+ }
+
+ spin_lock_irqsave(&write_list_lock, flags);
+ if (!list_empty(&priv->queue)) {
+ ipaq_write_flush(port);
+ } else {
+ priv->active = 0;
+ }
+ spin_unlock_irqrestore(&write_list_lock, flags);
+ queue_task(&port->tqueue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+
+ return;
+}
+
+static int ipaq_write_room(struct usb_serial_port *port)
+{
+ struct ipaq_private *priv = (struct ipaq_private *)port->private;
+
+ dbg(__FUNCTION__ " - freelen %d", priv->free_len);
+ return priv->free_len;
+}
+
+static int ipaq_chars_in_buffer(struct usb_serial_port *port)
+{
+ struct ipaq_private *priv = (struct ipaq_private *)port->private;
+
+ dbg(__FUNCTION__ " - queuelen %d", priv->queue_len);
+ return priv->queue_len;
+}
+
+static void ipaq_destroy_lists(struct usb_serial_port *port)
+{
+ struct ipaq_private *priv = (struct ipaq_private *)port->private;
+ struct list_head *tmp;
+ struct ipaq_packet *pkt;
+
+ for (tmp = priv->queue.next; tmp != &priv->queue;) {
+ pkt = list_entry(tmp, struct ipaq_packet, list);
+ tmp = tmp->next;
+ kfree(pkt->data);
+ kfree(pkt);
+ }
+ for (tmp = priv->freelist.next; tmp != &priv->freelist;) {
+ pkt = list_entry(tmp, struct ipaq_packet, list);
+ tmp = tmp->next;
+ kfree(pkt->data);
+ kfree(pkt);
+ }
+ return;
+}
+
+
+static int ipaq_startup(struct usb_serial *serial)
+{
+ dbg(__FUNCTION__);
+ usb_set_configuration(serial->dev, 1);
+ return 0;
+}
+
+static void ipaq_shutdown(struct usb_serial *serial)
+{
+ int i;
+
+ dbg (__FUNCTION__);
+
+ /* stop reads and writes on all ports */
+ for (i=0; i < serial->num_ports; ++i) {
+ while (serial->port[i].open_count > 0) {
+ ipaq_close(&serial->port[i], NULL);
+ }
+ }
+}
+
+static int __init ipaq_init(void)
+{
+ usb_serial_register(&ipaq_device);
+ info(DRIVER_DESC " " DRIVER_VERSION);
+
+ return 0;
+}
+
+
+static void __exit ipaq_exit(void)
+{
+ usb_serial_deregister(&ipaq_device);
+}
+
+
+module_init(ipaq_init);
+module_exit(ipaq_exit);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
--- /dev/null
+/*
+ * USB Compaq iPAQ driver
+ *
+ * Copyright (C) 2001
+ * Ganesh Varadarajan <ganesh@veritas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ */
+
+#ifndef __LINUX_USB_SERIAL_IPAQ_H
+#define __LINUX_USB_SERIAL_IPAQ_H
+
+
+#define IPAQ_VENDOR_ID 0x049f
+#define IPAQ_PRODUCT_ID 0x0003
+
+/*
+ * Since we can't queue our bulk write urbs (don't know why - it just
+ * doesn't work), we can send down only one write urb at a time. The simplistic
+ * approach taken by the generic usbserial driver will work, but it's not good
+ * for performance. Therefore, we buffer upto URBDATA_QUEUE_MAX bytes of write
+ * requests coming from the line discipline. This is done by chaining them
+ * in lists of struct ipaq_packet, each packet holding a maximum of
+ * PACKET_SIZE bytes.
+ *
+ * ipaq_write() can be called from bottom half context; hence we can't
+ * allocate memory for packets there. So we initialize a pool of packets at
+ * the first open and maintain a freelist.
+ *
+ * The value of PACKET_SIZE was empirically determined by
+ * checking the maximum write sizes sent down by the ppp ldisc.
+ * URBDATA_QUEUE_MAX is set to 64K, which is the maximum TCP window size
+ * supported by the iPAQ.
+ */
+
+struct ipaq_packet {
+ char *data;
+ size_t len;
+ size_t written;
+ struct list_head list;
+};
+
+struct ipaq_private {
+ int active;
+ int queue_len;
+ int free_len;
+ struct list_head queue;
+ struct list_head freelist;
+};
+
+#define URBDATA_SIZE 4096
+#define URBDATA_QUEUE_MAX (64 * 1024)
+#define PACKET_SIZE 256
+
+#endif
--- /dev/null
+/*
+ * KLSI KL5KUSB105 chip RS232 converter driver
+ *
+ * Copyright (C) 2001 Utz-Uwe Haus <haus@uuhaus.de>
+ *
+ * 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.
+ *
+ * All information about the device was acquired using SniffUSB ans snoopUSB
+ * on Windows98.
+ * It was written out of frustration with the PalmConnect USB Serial adapter
+ * sold by Palm Inc.
+ * Neither Palm, nor their contractor (MCCI) or their supplier (KLSI) provided
+ * information that was not already available.
+ *
+ * It seems that KLSI bought some silicon-design information from ScanLogic,
+ * whose SL11R processor is at the core of the KL5KUSB chipset from KLSI.
+ * KLSI has firmware available for their devices; it is probable that the
+ * firmware differs from that used by KLSI in their products. If you have an
+ * original KLSI device and can provide some information on it, I would be
+ * most interested in adding support for it here. If you have any information
+ * on the protocol used (or find errors in my reverse-engineered stuff), please
+ * let me know.
+ *
+ * The code was only tested with a PalmConnect USB adapter; if you
+ * are adventurous, try it with any KLSI-based device and let me know how it
+ * breaks so that I can fix it!
+ */
+
+/* TODO:
+ * check modem line signals
+ * implement handshaking or decide that we do not support it
+ */
+
+/* History:
+ * 0.3a - implemented pools of write URBs
+ * 0.3 - alpha version for public testing
+ * 0.2 - TIOCMGET works, so autopilot(1) can be used!
+ * 0.1 - can be used to to pilot-xfer -p /dev/ttyUSB0 -l
+ *
+ * The driver skeleton is mainly based on mct_u232.c and various other
+ * pieces of code shamelessly copied from the drivers/usb/serial/ directory.
+ */
+
+
+#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/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+ static int debug = 1;
+#else
+ static int debug;
+#endif
+
+#include "usb-serial.h"
+#include "kl5kusb105.h"
+
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.3a"
+#define DRIVER_AUTHOR "Utz-Uwe Haus <haus@uuhaus.de>"
+#define DRIVER_DESC "KLSI KL5KUSB105 chipset USB->Serial Converter driver"
+
+
+/*
+ * Function prototypes
+ */
+static int klsi_105_startup (struct usb_serial *serial);
+static void klsi_105_shutdown (struct usb_serial *serial);
+static int klsi_105_open (struct usb_serial_port *port,
+ struct file *filp);
+static void klsi_105_close (struct usb_serial_port *port,
+ struct file *filp);
+static int klsi_105_write (struct usb_serial_port *port,
+ int from_user,
+ const unsigned char *buf,
+ int count);
+static void klsi_105_write_bulk_callback (struct urb *urb);
+static int klsi_105_chars_in_buffer (struct usb_serial_port *port);
+static int klsi_105_write_room (struct usb_serial_port *port);
+
+static void klsi_105_read_bulk_callback (struct urb *urb);
+static void klsi_105_set_termios (struct usb_serial_port *port,
+ struct termios * old);
+static int klsi_105_ioctl (struct usb_serial_port *port,
+ struct file * file,
+ unsigned int cmd,
+ unsigned long arg);
+static void klsi_105_throttle (struct usb_serial_port *port);
+static void klsi_105_unthrottle (struct usb_serial_port *port);
+/*
+static void klsi_105_break_ctl (struct usb_serial_port *port,
+ int break_state );
+ */
+
+/*
+ * All of the device info needed for the KLSI converters.
+ */
+static __devinitdata struct usb_device_id id_table [] = {
+ { USB_DEVICE(PALMCONNECT_VID, PALMCONNECT_PID) },
+ { USB_DEVICE(KLSI_VID, KLSI_KL5KUSB105D_PID) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, id_table);
+
+
+static struct usb_serial_device_type kl5kusb105d_device = {
+ owner: THIS_MODULE,
+ name: "KL5KUSB105D / PalmConnect",
+ id_table: id_table,
+ num_interrupt_in: 1,
+ num_bulk_in: 1,
+ num_bulk_out: 1,
+ num_ports: 1,
+ open: klsi_105_open,
+ close: klsi_105_close,
+ write: klsi_105_write,
+ write_bulk_callback: klsi_105_write_bulk_callback,
+ chars_in_buffer: klsi_105_chars_in_buffer,
+ write_room: klsi_105_write_room,
+ read_bulk_callback: klsi_105_read_bulk_callback,
+ ioctl: klsi_105_ioctl,
+ set_termios: klsi_105_set_termios,
+ /*break_ctl: klsi_105_break_ctl,*/
+ startup: klsi_105_startup,
+ shutdown: klsi_105_shutdown,
+ throttle: klsi_105_throttle,
+ unthrottle: klsi_105_unthrottle,
+};
+
+struct klsi_105_port_settings {
+ __u8 pktlen; /* always 5, it seems */
+ __u8 baudrate;
+ __u8 databits;
+ __u8 unknown1;
+ __u8 unknown2;
+} __attribute__ ((packed));
+
+/* we implement a pool of NUM_URBS urbs per usb_serial */
+#define NUM_URBS 1
+#define URB_TRANSFER_BUFFER_SIZE 64
+struct klsi_105_private {
+ struct klsi_105_port_settings cfg;
+ struct termios termios;
+ unsigned long line_state; /* modem line settings */
+ /* write pool */
+ struct urb * write_urb_pool[NUM_URBS];
+ spinlock_t write_urb_pool_lock;
+ unsigned long bytes_in;
+ unsigned long bytes_out;
+};
+
+
+/*
+ * Handle vendor specific USB requests
+ */
+
+
+#define KLSI_TIMEOUT (HZ * 5 ) /* default urb timeout */
+
+static int klsi_105_chg_port_settings(struct usb_serial *serial,
+ struct klsi_105_port_settings *settings)
+{
+ int rc;
+
+ rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ KL5KUSB105A_SIO_SET_DATA,
+ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_INTERFACE,
+ 0, /* value */
+ 0, /* index */
+ settings,
+ sizeof(struct klsi_105_port_settings),
+ KLSI_TIMEOUT);
+ if (rc < 0)
+ err("Change port settings failed (error = %d)", rc);
+ info(__FUNCTION__ " - %d byte block, baudrate %x, databits %d, u1 %d, u2 %d",
+ settings->pktlen,
+ settings->baudrate, settings->databits,
+ settings->unknown1, settings->unknown2);
+ return rc;
+} /* klsi_105_chg_port_settings */
+
+/* translate a 16-bit status value from the device to linux's TIO bits */
+static unsigned long klsi_105_status2linestate(const __u16 status)
+{
+ unsigned long res = 0;
+
+ res = ((status & KL5KUSB105A_DSR) ? TIOCM_DSR : 0)
+ | ((status & KL5KUSB105A_CTS) ? TIOCM_CTS : 0)
+ ;
+
+ return res;
+}
+/*
+ * Read line control via vendor command and return result through
+ * *line_state_p
+ */
+/* It seems that the status buffer has always only 2 bytes length */
+#define KLSI_STATUSBUF_LEN 2
+static int klsi_105_get_line_state(struct usb_serial *serial,
+ unsigned long *line_state_p)
+{
+ int rc;
+ __u8 status_buf[KLSI_STATUSBUF_LEN] = { -1,-1};
+ __u16 status;
+
+ info(__FUNCTION__ " - sending SIO Poll request");
+ rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+ KL5KUSB105A_SIO_POLL,
+ USB_TYPE_VENDOR | USB_DIR_IN,
+ 0, /* value */
+ 0, /* index */
+ status_buf, KLSI_STATUSBUF_LEN,
+ 10*HZ
+ );
+ if (rc < 0)
+ err("Reading line status failed (error = %d)", rc);
+ else {
+ status = status_buf[0] + (status_buf[1]<<8);
+
+ info(__FUNCTION__ " - read status %x %x",
+ status_buf[0], status_buf[1]);
+
+ *line_state_p = klsi_105_status2linestate(status);
+ }
+
+ return rc;
+}
+
+
+/*
+ * Driver's tty interface functions
+ */
+
+static int klsi_105_startup (struct usb_serial *serial)
+{
+ struct klsi_105_private *priv;
+ int i;
+
+ /* check if we support the product id (see keyspan.c)
+ * FIXME
+ */
+
+ /* allocate the private data structure */
+ for (i=0; i<serial->num_ports; i++) {
+ serial->port[i].private = kmalloc(sizeof(struct klsi_105_private),
+ GFP_KERNEL);
+ if (!serial->port[i].private) {
+ dbg(__FUNCTION__ "kmalloc for klsi_105_private failed.");
+ return (-1); /* error */
+ }
+ priv = (struct klsi_105_private *)serial->port[i].private;
+ /* set initial values for control structures */
+ priv->cfg.pktlen = 5;
+ priv->cfg.baudrate = kl5kusb105a_sio_b9600;
+ priv->cfg.databits = kl5kusb105a_dtb_8;
+ priv->cfg.unknown1 = 0;
+ priv->cfg.unknown2 = 1;
+
+ priv->line_state = 0;
+
+ priv->bytes_in = 0;
+ priv->bytes_out = 0;
+
+ spin_lock_init (&priv->write_urb_pool_lock);
+ for (i=0; i<NUM_URBS; i++) {
+ struct urb* urb = usb_alloc_urb(0);
+
+ priv->write_urb_pool[i] = urb;
+ if (urb == NULL) {
+ err("No more urbs???");
+ continue;
+ }
+
+ urb->transfer_buffer = NULL;
+ urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE,
+ GFP_KERNEL);
+ if (!urb->transfer_buffer) {
+ err (__FUNCTION__
+ " - out of memory for urb buffers.");
+ continue;
+ }
+ }
+
+ /* priv->termios is left uninitalized until port opening */
+ init_waitqueue_head(&serial->port[i].write_wait);
+ }
+
+ return (0);
+} /* klsi_105_startup */
+
+
+static void klsi_105_shutdown (struct usb_serial *serial)
+{
+ int i;
+
+ dbg (__FUNCTION__);
+
+ /* stop reads and writes on all ports */
+ for (i=0; i < serial->num_ports; ++i) {
+ struct klsi_105_private *priv =
+ (struct klsi_105_private*) serial->port[i].private;
+ unsigned long flags;
+ while (serial->port[i].open_count > 0) {
+ klsi_105_close (&serial->port[i], NULL);
+ }
+
+ if (priv) {
+ /* kill our write urb pool */
+ int j;
+ struct urb **write_urbs = priv->write_urb_pool;
+ spin_lock_irqsave(&priv->write_urb_pool_lock,flags);
+
+ for (j = 0; j < NUM_URBS; j++) {
+ if (write_urbs[j]) {
+ /* FIXME - uncomment the following
+ * usb_unlink_urb call when the host
+ * controllers get fixed to set
+ * urb->dev = NULL after the urb is
+ * finished. Otherwise this call
+ * oopses. */
+ /* usb_unlink_urb(write_urbs[j]); */
+ if (write_urbs[j]->transfer_buffer)
+ kfree(write_urbs[j]->transfer_buffer);
+ usb_free_urb (write_urbs[j]);
+ }
+ }
+
+ spin_unlock_irqrestore (&priv->write_urb_pool_lock,
+ flags);
+
+ kfree(serial->port[i].private);
+ }
+ }
+} /* klsi_105_shutdown */
+
+static int klsi_105_open (struct usb_serial_port *port, struct file *filp)
+{
+ struct usb_serial *serial = port->serial;
+ struct klsi_105_private *priv = (struct klsi_105_private *)port->private;
+ int retval = 0;
+
+ dbg(__FUNCTION__" port %d", port->number);
+
+ down (&port->sem);
+
+ ++port->open_count;
+
+ if (port->open_count == 1) {
+ int rc;
+ int i;
+ unsigned long line_state;
+
+ /* force low_latency on so that our tty_push actually forces
+ * the data through
+ * port->tty->low_latency = 1; */
+
+ /* Do a defined restart:
+ * Set up sane default baud rate and send the 'READ_ON'
+ * vendor command.
+ * FIXME: set modem line control (how?)
+ * Then read the modem line control and store values in
+ * priv->line_state.
+ */
+ priv->cfg.pktlen = 5;
+ priv->cfg.baudrate = kl5kusb105a_sio_b9600;
+ priv->cfg.databits = kl5kusb105a_dtb_8;
+ priv->cfg.unknown1 = 0;
+ priv->cfg.unknown2 = 1;
+ klsi_105_chg_port_settings(serial, &(priv->cfg));
+
+ /* set up termios structure */
+ priv->termios.c_iflag = port->tty->termios->c_iflag;
+ priv->termios.c_oflag = port->tty->termios->c_oflag;
+ priv->termios.c_cflag = port->tty->termios->c_cflag;
+ priv->termios.c_lflag = port->tty->termios->c_lflag;
+ for (i=0; i<NCCS; i++)
+ priv->termios.c_cc[i] = port->tty->termios->c_cc[i];
+
+
+ /* READ_ON and urb submission */
+ FILL_BULK_URB(port->read_urb, serial->dev,
+ usb_rcvbulkpipe(serial->dev,
+ port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer,
+ port->read_urb->transfer_buffer_length,
+ klsi_105_read_bulk_callback,
+ port);
+ port->read_urb->transfer_flags |= USB_QUEUE_BULK;
+
+ rc = usb_submit_urb(port->read_urb);
+ if (rc) {
+ err(__FUNCTION__
+ " - failed submitting read urb, error %d", rc);
+ retval = rc;
+ goto exit;
+ }
+
+ rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev,0),
+ KL5KUSB105A_SIO_CONFIGURE,
+ USB_TYPE_VENDOR|USB_DIR_OUT|USB_RECIP_INTERFACE,
+ KL5KUSB105A_SIO_CONFIGURE_READ_ON,
+ 0, /* index */
+ NULL,
+ 0,
+ KLSI_TIMEOUT);
+ if (rc < 0) {
+ err("Enabling read failed (error = %d)", rc);
+ retval = rc;
+ } else
+ dbg(__FUNCTION__ " - enabled reading");
+
+ rc = klsi_105_get_line_state(serial, &line_state);
+ if (rc >= 0) {
+ priv->line_state = line_state;
+ dbg(__FUNCTION__
+ " - read line state 0x%lx", line_state);
+ retval = 0;
+ } else
+ retval = rc;
+ }
+
+exit:
+ up (&port->sem);
+
+ return retval;
+} /* klsi_105_open */
+
+
+static void klsi_105_close (struct usb_serial_port *port, struct file *filp)
+{
+ struct usb_serial *serial;
+ struct klsi_105_private *priv
+ = (struct klsi_105_private *)port->private;
+ dbg(__FUNCTION__" port %d", port->number);
+
+ serial = get_usb_serial (port, __FUNCTION__);
+
+ if(!serial)
+ return;
+
+ down (&port->sem);
+
+ --port->open_count;
+
+ if (port->open_count <= 0) {
+ /* send READ_OFF */
+ int rc = usb_control_msg(serial->dev,
+ usb_sndctrlpipe(serial->dev, 0),
+ KL5KUSB105A_SIO_CONFIGURE,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ KL5KUSB105A_SIO_CONFIGURE_READ_OFF,
+ 0, /* index */
+ NULL, 0,
+ KLSI_TIMEOUT);
+ if (rc < 0)
+ err("Disabling read failed (error = %d)", rc);
+
+ /* shutdown our bulk reads and writes */
+ usb_unlink_urb (port->write_urb);
+ usb_unlink_urb (port->read_urb);
+ /* unlink our write pool */
+ /* FIXME */
+ /* wgg - do I need this? I think so. */
+ usb_unlink_urb (port->interrupt_in_urb);
+ port->open_count = 0;
+ info("kl5kusb105 port stats: %ld bytes in, %ld bytes out", priv->bytes_in, priv->bytes_out);
+ }
+
+ up (&port->sem);
+} /* klsi_105_close */
+
+
+/* We need to write a complete 64-byte data block and encode the
+ * number actually sent in the first double-byte, LSB-order. That
+ * leaves at most 62 bytes of payload.
+ */
+#define KLSI_105_DATA_OFFSET 2 /* in the bulk urb data block */
+
+
+static int klsi_105_write (struct usb_serial_port *port, int from_user,
+ const unsigned char *buf, int count)
+{
+ struct usb_serial *serial = port->serial;
+ struct klsi_105_private *priv =
+ (struct klsi_105_private*) port->private;
+ int result, size;
+ int bytes_sent=0;
+
+ dbg(__FUNCTION__ " - port %d", port->number);
+
+ down (&port->sem); /* to lock against someone else trying to
+ take an URB we just selected from the pool */
+
+ while (count > 0) {
+ /* try to find a free urb (write 0 bytes if none) */
+ struct urb *urb = NULL;
+ unsigned long flags;
+ int i;
+ /* since the pool is per-port we might not need the spin lock !? */
+ spin_lock_irqsave (&priv->write_urb_pool_lock, flags);
+ for (i=0; i<NUM_URBS; i++) {
+ if (priv->write_urb_pool[i]->status != -EINPROGRESS) {
+ urb = priv->write_urb_pool[i];
+ dbg(__FUNCTION__ " - using pool URB %d", i);
+ break;
+ }
+ }
+ spin_unlock_irqrestore (&priv->write_urb_pool_lock, flags);
+
+ if (urb==NULL) {
+ dbg (__FUNCTION__ " - no more free urbs");
+ goto exit;
+ }
+
+ if (urb->transfer_buffer == NULL) {
+ urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
+ if (urb->transfer_buffer == NULL) {
+ err(__FUNCTION__ " - no more kernel memory...");
+ goto exit;
+ }
+ }
+
+ size = min (count, port->bulk_out_size - KLSI_105_DATA_OFFSET);
+ size = min (size, URB_TRANSFER_BUFFER_SIZE - KLSI_105_DATA_OFFSET);
+
+ if (from_user) {
+ if (copy_from_user(urb->transfer_buffer
+ + KLSI_105_DATA_OFFSET, buf, size)) {
+ up (&port->sem);
+ return -EFAULT;
+ }
+ } else {
+ memcpy (urb->transfer_buffer + KLSI_105_DATA_OFFSET,
+ buf, size);
+ }
+
+ /* write payload size into transfer buffer */
+ ((__u8 *)urb->transfer_buffer)[0] = (__u8) (size & 0xFF);
+ ((__u8 *)urb->transfer_buffer)[1] = (__u8) ((size & 0xFF00)>>8);
+
+ /* set up our urb */
+ FILL_BULK_URB(urb, serial->dev,
+ usb_sndbulkpipe(serial->dev,
+ port->bulk_out_endpointAddress),
+ urb->transfer_buffer,
+ URB_TRANSFER_BUFFER_SIZE,
+ klsi_105_write_bulk_callback,
+ port);
+ urb->transfer_flags |= USB_QUEUE_BULK;
+
+
+ /* send the data out the bulk port */
+ result = usb_submit_urb(urb);
+ if (result) {
+ err(__FUNCTION__
+ " - failed submitting write urb, error %d", result);
+ goto exit;
+ }
+ buf += size;
+ bytes_sent += size;
+ count -= size;
+ }
+exit:
+ up (&port->sem);
+ priv->bytes_out+=bytes_sent;
+
+ return bytes_sent; /* that's how much we wrote */
+} /* klsi_105_write */
+
+static void klsi_105_write_bulk_callback ( struct urb *urb)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ struct usb_serial *serial = port->serial;
+
+ dbg(__FUNCTION__ " - port %d", port->number);
+
+ if (!serial) {
+ dbg(__FUNCTION__ " - bad serial pointer, exiting");
+ return;
+ }
+
+ if (urb->status) {
+ dbg(__FUNCTION__ " - nonzero write bulk status received: %d",
+ urb->status);
+ return;
+ }
+
+ /* from generic_write_bulk_callback */
+ queue_task(&port->tqueue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+
+ return;
+} /* klsi_105_write_bulk_completion_callback */
+
+
+/* return number of characters currently in the writing process */
+static int klsi_105_chars_in_buffer (struct usb_serial_port *port)
+{
+ int chars = 0;
+ int i;
+ unsigned long flags;
+ struct klsi_105_private *priv =
+ (struct klsi_105_private*) port->private;
+
+ spin_lock_irqsave (&priv->write_urb_pool_lock, flags);
+
+ for (i = 0; i < NUM_URBS; ++i) {
+ if (priv->write_urb_pool[i]->status == -EINPROGRESS) {
+ chars += URB_TRANSFER_BUFFER_SIZE;
+ }
+ }
+
+ spin_unlock_irqrestore (&priv->write_urb_pool_lock, flags);
+
+ dbg (__FUNCTION__ " - returns %d", chars);
+ return (chars);
+}
+
+static int klsi_105_write_room (struct usb_serial_port *port)
+{
+ unsigned long flags;
+ int i;
+ int room = 0;
+ struct klsi_105_private *priv =
+ (struct klsi_105_private*) port->private;
+
+ spin_lock_irqsave (&priv->write_urb_pool_lock, flags);
+ for (i = 0; i < NUM_URBS; ++i) {
+ if (priv->write_urb_pool[i]->status != -EINPROGRESS) {
+ room += URB_TRANSFER_BUFFER_SIZE;
+ }
+ }
+
+ spin_unlock_irqrestore (&priv->write_urb_pool_lock, flags);
+
+ dbg(__FUNCTION__ " - returns %d", room);
+ return (room);
+}
+
+
+
+static void klsi_105_read_bulk_callback (struct urb *urb)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ struct usb_serial *serial = port->serial;
+ struct klsi_105_private *priv =
+ (struct klsi_105_private*) port->private;
+ struct tty_struct *tty;
+ unsigned char *data = urb->transfer_buffer;
+ int rc;
+
+ dbg(__FUNCTION__ " - port %d", port->number);
+
+ /* The urb might have been killed. */
+ if (urb->status) {
+ dbg(__FUNCTION__ " - nonzero read bulk status received: %d",
+ urb->status);
+ return;
+ }
+ if (!serial) {
+ dbg(__FUNCTION__ " - bad serial pointer, exiting");
+ return;
+ }
+
+ /* The data received is again preceded by a length double-byte in LSB-
+ * first order (see klsi_105_write() )
+ */
+ if (urb->actual_length == 0) {
+ /* empty urbs seem to happen, we ignore them */
+ /* dbg(__FUNCTION__ " - emtpy URB"); */
+ ;
+ } else if (urb->actual_length <= 2) {
+ dbg(__FUNCTION__ " - size %d URB not understood",
+ urb->actual_length);
+ usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+ } else {
+ int i;
+ int bytes_sent = ((__u8 *) data)[0] +
+ ((unsigned int) ((__u8 *) data)[1] << 8);
+ tty = port->tty;
+ /* we should immediately resubmit the URB, before attempting
+ * to pass the data on to the tty layer. But that needs locking
+ * against re-entry an then mixed-up data because of
+ * intermixed tty_flip_buffer_push()s
+ * FIXME
+ */
+ usb_serial_debug_data (__FILE__, __FUNCTION__,
+ urb->actual_length, data);
+
+ if (bytes_sent + 2 > urb->actual_length) {
+ dbg(__FUNCTION__
+ " - trying to read more data than available"
+ " (%d vs. %d)",
+ bytes_sent+2, urb->actual_length);
+ /* cap at implied limit */
+ bytes_sent = urb->actual_length - 2;
+ }
+
+ for (i = 2; i < 2+bytes_sent; i++) {
+ /* if we insert more than TTY_FLIPBUF_SIZE characters,
+ * we drop them. */
+ if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ tty_flip_buffer_push(tty);
+ }
+ /* this doesn't actually push the data through unless
+ * tty->low_latency is set */
+ tty_insert_flip_char(tty, ((__u8*) data)[i], 0);
+ }
+ tty_flip_buffer_push(tty);
+ priv->bytes_in += bytes_sent;
+ }
+ /* Continue trying to always read */
+ FILL_BULK_URB(port->read_urb, serial->dev,
+ usb_rcvbulkpipe(serial->dev,
+ port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer,
+ port->read_urb->transfer_buffer_length,
+ klsi_105_read_bulk_callback,
+ port);
+ rc = usb_submit_urb(port->read_urb);
+ if (rc)
+ err(__FUNCTION__
+ " - failed resubmitting read urb, error %d", rc);
+} /* klsi_105_read_bulk_callback */
+
+
+static void klsi_105_set_termios (struct usb_serial_port *port,
+ struct termios *old_termios)
+{
+ struct usb_serial *serial = port->serial;
+ struct klsi_105_private *priv = (struct klsi_105_private *)port->private;
+ unsigned int iflag = port->tty->termios->c_iflag;
+ unsigned int old_iflag = old_termios->c_iflag;
+ unsigned int cflag = port->tty->termios->c_cflag;
+ unsigned int old_cflag = old_termios->c_cflag;
+
+ /*
+ * Update baud rate
+ */
+ if( (cflag & CBAUD) != (old_cflag & CBAUD) ) {
+ /* reassert DTR and (maybe) RTS on transition from B0 */
+ if( (old_cflag & CBAUD) == B0 ) {
+ dbg(__FUNCTION__ ": baud was B0");
+#if 0
+ priv->control_state |= TIOCM_DTR;
+ /* don't set RTS if using hardware flow control */
+ if (!(old_cflag & CRTSCTS)) {
+ priv->control_state |= TIOCM_RTS;
+ }
+ mct_u232_set_modem_ctrl(serial, priv->control_state);
+#endif
+ }
+
+ switch(cflag & CBAUD) {
+ case B0: /* handled below */
+ break;
+ case B1200: priv->cfg.baudrate = kl5kusb105a_sio_b1200;
+ break;
+ case B2400: priv->cfg.baudrate = kl5kusb105a_sio_b2400;
+ break;
+ case B4800: priv->cfg.baudrate = kl5kusb105a_sio_b4800;
+ break;
+ case B9600: priv->cfg.baudrate = kl5kusb105a_sio_b9600;
+ break;
+ case B19200: priv->cfg.baudrate = kl5kusb105a_sio_b19200;
+ break;
+ case B38400: priv->cfg.baudrate = kl5kusb105a_sio_b38400;
+ break;
+ case B57600: priv->cfg.baudrate = kl5kusb105a_sio_b57600;
+ break;
+ case B115200: priv->cfg.baudrate = kl5kusb105a_sio_b115200;
+ break;
+ default:
+ err("KLSI USB->Serial converter:"
+ " unsupported baudrate request, using default"
+ " of 9600");
+ priv->cfg.baudrate = kl5kusb105a_sio_b9600;
+ break;
+ }
+ if ((cflag & CBAUD) == B0 ) {
+ dbg(__FUNCTION__ ": baud is B0");
+ /* Drop RTS and DTR */
+ /* maybe this should be simulated by sending read
+ * disable and read enable messages?
+ */
+ ;
+#if 0
+ priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
+ mct_u232_set_modem_ctrl(serial, priv->control_state);
+#endif
+ }
+ }
+
+ if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
+ /* set the number of data bits */
+ switch (cflag & CSIZE) {
+ case CS5:
+ dbg(__FUNCTION__ " - 5 bits/byte not supported");
+ return ;
+ case CS6:
+ dbg(__FUNCTION__ " - 6 bits/byte not supported");
+ return ;
+ case CS7:
+ priv->cfg.databits = kl5kusb105a_dtb_7;
+ break;
+ case CS8:
+ priv->cfg.databits = kl5kusb105a_dtb_8;
+ break;
+ default:
+ err("CSIZE was not CS5-CS8, using default of 8");
+ priv->cfg.databits = kl5kusb105a_dtb_8;
+ break;
+ }
+ }
+
+ /*
+ * Update line control register (LCR)
+ */
+ if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD))
+ || (cflag & CSTOPB) != (old_cflag & CSTOPB) ) {
+
+#if 0
+ priv->last_lcr = 0;
+
+ /* set the parity */
+ if (cflag & PARENB)
+ priv->last_lcr |= (cflag & PARODD) ?
+ MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN;
+ else
+ priv->last_lcr |= MCT_U232_PARITY_NONE;
+
+ /* set the number of stop bits */
+ priv->last_lcr |= (cflag & CSTOPB) ?
+ MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1;
+
+ mct_u232_set_line_ctrl(serial, priv->last_lcr);
+#endif
+ ;
+ }
+
+ /*
+ * Set flow control: well, I do not really now how to handle DTR/RTS.
+ * Just do what we have seen with SniffUSB on Win98.
+ */
+ if( (iflag & IXOFF) != (old_iflag & IXOFF)
+ || (iflag & IXON) != (old_iflag & IXON)
+ || (cflag & CRTSCTS) != (old_cflag & CRTSCTS) ) {
+
+ /* Drop DTR/RTS if no flow control otherwise assert */
+#if 0
+ if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS) )
+ priv->control_state |= TIOCM_DTR | TIOCM_RTS;
+ else
+ priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
+ mct_u232_set_modem_ctrl(serial, priv->control_state);
+#endif
+ ;
+ }
+
+ /* now commit changes to device */
+ klsi_105_chg_port_settings(serial, &(priv->cfg));
+} /* klsi_105_set_termios */
+
+
+#if 0
+static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state )
+{
+ struct usb_serial *serial = port->serial;
+ struct mct_u232_private *priv = (struct mct_u232_private *)port->private;
+ unsigned char lcr = priv->last_lcr;
+
+ dbg (__FUNCTION__ "state=%d", break_state);
+
+ if (break_state)
+ lcr |= MCT_U232_SET_BREAK;
+
+ mct_u232_set_line_ctrl(serial, lcr);
+} /* mct_u232_break_ctl */
+#endif
+
+static int klsi_105_ioctl (struct usb_serial_port *port, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct usb_serial *serial = port->serial;
+ struct klsi_105_private *priv = (struct klsi_105_private *)port->private;
+ int mask;
+
+ dbg (__FUNCTION__ "cmd=0x%x", cmd);
+
+ /* Based on code from acm.c and others */
+ switch (cmd) {
+ case TIOCMGET: {
+ int rc;
+ unsigned long line_state;
+ dbg (__FUNCTION__ " - TIOCMGET request, just guessing");
+
+ rc = klsi_105_get_line_state(serial, &line_state);
+ if (rc < 0) {
+ err("Reading line control failed (error = %d)", rc);
+ /* better return value? EAGAIN? */
+ return -ENOIOCTLCMD;
+ } else {
+ priv->line_state = line_state;
+ dbg(__FUNCTION__ " - read line state 0x%lx", line_state);
+ }
+ return put_user(priv->line_state, (unsigned long *) arg);
+ };
+
+ case TIOCMSET: /* Turns on and off the lines as specified by the mask */
+ case TIOCMBIS: /* turns on (Sets) the lines as specified by the mask */
+ case TIOCMBIC: /* turns off (Clears) the lines as specified by the mask */
+ if (get_user(mask, (unsigned long *) arg))
+ return -EFAULT;
+
+ if ((cmd == TIOCMSET) || (mask & TIOCM_RTS)) {
+ /* RTS needs set */
+ if( ((cmd == TIOCMSET) && (mask & TIOCM_RTS)) ||
+ (cmd == TIOCMBIS) )
+ dbg (__FUNCTION__ " - set RTS not handled");
+ /* priv->control_state |= TIOCM_RTS; */
+ else
+ dbg (__FUNCTION__ " - clear RTS not handled");
+ /* priv->control_state &= ~TIOCM_RTS; */
+ }
+
+ if ((cmd == TIOCMSET) || (mask & TIOCM_DTR)) {
+ /* DTR needs set */
+ if( ((cmd == TIOCMSET) && (mask & TIOCM_DTR)) ||
+ (cmd == TIOCMBIS) )
+ dbg (__FUNCTION__ " - set DTR not handled");
+ /* priv->control_state |= TIOCM_DTR; */
+ else
+ dbg (__FUNCTION__ " - clear DTR not handled");
+ /* priv->control_state &= ~TIOCM_DTR; */
+ }
+ /*
+ mct_u232_set_modem_ctrl(serial, priv->control_state);
+ */
+ break;
+
+ case TIOCMIWAIT:
+ /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/
+ /* TODO */
+ dbg (__FUNCTION__ " - TIOCMIWAIT not handled");
+ return -ENOIOCTLCMD;
+
+ case TIOCGICOUNT:
+ /* return count of modemline transitions */
+ /* TODO */
+ dbg (__FUNCTION__ " - TIOCGICOUNT not handled");
+ return -ENOIOCTLCMD;
+ case TCGETS: {
+ /* return current info to caller */
+ int retval;
+
+ dbg (__FUNCTION__ " - TCGETS data faked/incomplete");
+
+ retval = verify_area(VERIFY_WRITE, (void *)arg,
+ sizeof(struct termios));
+
+ if (retval)
+ return(retval);
+
+ kernel_termios_to_user_termios((struct termios *)arg,
+ &priv->termios);
+ return(0);
+ }
+ case TCSETS: {
+ /* set port termios to the one given by the user */
+ int retval;
+
+ dbg (__FUNCTION__ " - TCSETS not handled");
+
+ retval = verify_area(VERIFY_READ, (void *)arg,
+ sizeof(struct termios));
+
+ if (retval)
+ return(retval);
+
+ user_termios_to_kernel_termios(&priv->termios,
+ (struct termios *)arg);
+ klsi_105_set_termios(port, &priv->termios);
+ return(0);
+ }
+ case TCSETSW: {
+ /* set port termios and try to wait for completion of last
+ * write operation */
+ /* We guess here. If there are not too many write urbs
+ * outstanding, we lie. */
+ /* what is the right way to wait here? schedule() ? */
+ /*
+ while (klsi_105_chars_in_buffer(port) > (NUM_URBS / 4 ) * URB_TRANSFER_BUFFER_SIZE)
+ schedule();
+ */
+ return -ENOIOCTLCMD;
+ }
+ default:
+ dbg(__FUNCTION__ ": arg not supported - 0x%04x",cmd);
+ return(-ENOIOCTLCMD);
+ break;
+ }
+ return 0;
+} /* klsi_105_ioctl */
+
+static void klsi_105_throttle (struct usb_serial_port *port)
+{
+
+ dbg(__FUNCTION__ " - port %d", port->number);
+
+ down (&port->sem);
+
+ usb_unlink_urb (port->read_urb);
+
+ up (&port->sem);
+
+ return;
+}
+static void klsi_105_unthrottle (struct usb_serial_port *port)
+{
+ int result;
+
+ dbg(__FUNCTION__ " - port %d", port->number);
+
+ down (&port->sem);
+
+ port->read_urb->dev = port->serial->dev;
+ result = usb_submit_urb(port->read_urb);
+ if (result)
+ err(__FUNCTION__ " - failed submitting read urb, error %d",
+ result);
+
+ up (&port->sem);
+
+ return;
+}
+
+
+
+static int __init klsi_105_init (void)
+{
+ usb_serial_register (&kl5kusb105d_device);
+
+ info(DRIVER_DESC " " DRIVER_VERSION);
+ return 0;
+}
+
+
+static void __exit klsi_105_exit (void)
+{
+ usb_serial_deregister (&kl5kusb105d_device);
+}
+
+
+module_init (klsi_105_init);
+module_exit (klsi_105_exit);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
+
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "enable extensive debugging messages");
+/* FIXME: implement
+MODULE_PARM(num_urbs, "i");
+MODULE_PARM_DESC(num_urbs, "number of URBs to use in write pool");
+*/
+
+/* vim: set sts=8 ts=8 sw=8: */
--- /dev/null
+/*
+ * Definitions for the KLSI KL5KUSB105 serial port adapter
+ */
+
+/* vendor/product pairs that are known to contain this chipset */
+#define PALMCONNECT_VID 0x0830
+#define PALMCONNECT_PID 0x0080
+
+#define KLSI_VID 0x05e9
+#define KLSI_KL5KUSB105D_PID 0x00c0
+
+/* Vendor commands: */
+
+
+/* port table -- the chip supports up to 4 channels */
+
+/* baud rates */
+
+typedef enum {
+ kl5kusb105a_sio_b115200 = 0,
+ kl5kusb105a_sio_b57600 = 1,
+ kl5kusb105a_sio_b38400 = 2,
+ kl5kusb105a_sio_b19200 = 4,
+ kl5kusb105a_sio_b14400 = 5,
+ kl5kusb105a_sio_b9600 = 6,
+ kl5kusb105a_sio_b4800 = 8, /* unchecked */
+ kl5kusb105a_sio_b2400 = 9, /* unchecked */
+ kl5kusb105a_sio_b1200 = 0xa, /* unchecked */
+ kl5kusb105a_sio_b600 = 0xb /* unchecked */
+} KL5KUSB105A_SIO_baudrate_t;
+
+/* data bits */
+#define kl5kusb105a_dtb_7 7
+#define kl5kusb105a_dtb_8 8
+
+
+
+/* requests: */
+#define KL5KUSB105A_SIO_SET_DATA 1
+#define KL5KUSB105A_SIO_POLL 2
+#define KL5KUSB105A_SIO_CONFIGURE 3
+/* values used for request KL5KUSB105A_SIO_CONFIGURE */
+#define KL5KUSB105A_SIO_CONFIGURE_READ_ON 3
+#define KL5KUSB105A_SIO_CONFIGURE_READ_OFF 2
+
+/* Interpretation of modem status lines */
+/* These need sorting out by individually connecting pins and checking
+ * results. FIXME!
+ * When data is being sent we see 0x30 in the lower byte; this must
+ * contain DSR and CTS ...
+ */
+#define KL5KUSB105A_DSR ((1<<4) | (1<<5))
+#define KL5KUSB105A_CTS ((1<<5) | (1<<4))
+
+#define KL5KUSB105A_WANTS_TO_SEND 0x30
+//#define KL5KUSB105A_DTR /* Data Terminal Ready */
+//#define KL5KUSB105A_CTS /* Clear To Send */
+//#define KL5KUSB105A_CD /* Carrier Detect */
+//#define KL5KUSB105A_DSR /* Data Set Ready */
+//#define KL5KUSB105A_RxD /* Receive pin */
+
+//#define KL5KUSB105A_LE
+//#define KL5KUSB105A_RTS
+//#define KL5KUSB105A_ST
+//#define KL5KUSB105A_SR
+//#define KL5KUSB105A_RI /* Ring Indicator */
+
+/* vim: set ts=8 sts=8: */
+
kfree(buf);
}
-void usb_dump_urb (purb_t purb)
+void usb_dump_urb (struct urb *urb)
{
- printk ("urb :%p\n", purb);
- printk ("next :%p\n", purb->next);
- printk ("dev :%p\n", purb->dev);
- printk ("pipe :%08X\n", purb->pipe);
- printk ("status :%d\n", purb->status);
- printk ("transfer_flags :%08X\n", purb->transfer_flags);
- printk ("transfer_buffer :%p\n", purb->transfer_buffer);
- printk ("transfer_buffer_length:%d\n", purb->transfer_buffer_length);
- printk ("actual_length :%d\n", purb->actual_length);
- printk ("setup_packet :%p\n", purb->setup_packet);
- printk ("start_frame :%d\n", purb->start_frame);
- printk ("number_of_packets :%d\n", purb->number_of_packets);
- printk ("interval :%d\n", purb->interval);
- printk ("error_count :%d\n", purb->error_count);
- printk ("context :%p\n", purb->context);
- printk ("complete :%p\n", purb->complete);
+ printk ("urb :%p\n", urb);
+ printk ("next :%p\n", urb->next);
+ printk ("dev :%p\n", urb->dev);
+ printk ("pipe :%08X\n", urb->pipe);
+ printk ("status :%d\n", urb->status);
+ printk ("transfer_flags :%08X\n", urb->transfer_flags);
+ printk ("transfer_buffer :%p\n", urb->transfer_buffer);
+ printk ("transfer_buffer_length:%d\n", urb->transfer_buffer_length);
+ printk ("actual_length :%d\n", urb->actual_length);
+ printk ("setup_packet :%p\n", urb->setup_packet);
+ printk ("start_frame :%d\n", urb->start_frame);
+ printk ("number_of_packets :%d\n", urb->number_of_packets);
+ printk ("interval :%d\n", urb->interval);
+ printk ("error_count :%d\n", urb->error_count);
+ printk ("context :%p\n", urb->context);
+ printk ("complete :%p\n", urb->complete);
}
usb_scan_devices();
+ usbfs_update_special();
+
return 0;
}
usb_drivers_purge(driver, bus->root_hub);
}
up (&usb_bus_list_lock);
+
+ usbfs_update_special();
}
/**
bus->bandwidth_isoc_reqs = 0;
INIT_LIST_HEAD(&bus->bus_list);
- INIT_LIST_HEAD(&bus->inodes);
atomic_set(&bus->refcnt, 1);
list_add(&bus->bus_list, &usb_bus_list);
up (&usb_bus_list_lock);
- usbdevfs_add_bus(bus);
+ usbfs_add_bus(bus);
info("new USB bus registered, assigned bus number %d", bus->busnum);
}
list_del(&bus->bus_list);
up (&usb_bus_list_lock);
- usbdevfs_remove_bus(bus);
+ usbfs_remove_bus(bus);
clear_bit(bus->busnum, busmap.busmap);
/* a simple/common case: one config, one interface, one driver
* with current altsetting being a reasonable setting.
- * everything needs a smart agent and usbdevfs; or can rely on
+ * everything needs a smart agent and usbfs; or can rely on
* device-specific binding policies.
*/
envp [i++] = scratch;
usb_bus_get(bus);
+ if (!parent)
+ dev->devpath [0] = '/';
dev->bus = bus;
dev->parent = parent;
atomic_set(&dev->refcnt, 1);
- INIT_LIST_HEAD(&dev->inodes);
INIT_LIST_HEAD(&dev->filelist);
init_MUTEX(&dev->serialize);
/* Free the device number and remove the /proc/bus/usb entry */
if (dev->devnum > 0) {
clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
- usbdevfs_remove_device(dev);
+ usbfs_remove_device(dev);
}
/* Free up the device itself */
#endif
/* now that the basic setup is over, add a /proc/bus/usb entry */
- usbdevfs_add_device(dev);
+ usbfs_add_device(dev);
/* find drivers willing to handle this device */
usb_find_drivers(dev);
static int usb_open(struct inode * inode, struct file * file)
{
- int minor = MINOR(inode->i_rdev);
+ int minor = minor(inode->i_rdev);
struct usb_driver *c = usb_minors[minor/16];
int err = -ENODEV;
struct file_operations *old_fops, *new_fops = NULL;
{
init_MUTEX(&usb_bus_list_lock);
usb_major_init();
- usbdevfs_init();
+ usbfs_init();
usb_hub_init();
return 0;
static void __exit usb_exit(void)
{
usb_major_cleanup();
- usbdevfs_cleanup();
+ usbfs_cleanup();
usb_hub_cleanup();
}
}
inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
- inode->i_rdev = 0;
+ inode->i_rdev = NODEV;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
if (S_ISDIR(inf->mode)) {
static unsigned long max_block(kdev_t dev)
{
unsigned int retval = ~0U;
- int major = MAJOR(dev);
+ int major = major(dev);
if (blk_size[major]) {
- int minor = MINOR(dev);
+ int minor = minor(dev);
unsigned int blocks = blk_size[major][minor];
if (blocks) {
unsigned int size = block_size(dev);
static loff_t blkdev_size(kdev_t dev)
{
unsigned int blocks = ~0U;
- int major = MAJOR(dev);
+ int major = major(dev);
if (blk_size[major]) {
- int minor = MINOR(dev);
+ int minor = minor(dev);
blocks = blk_size[major][minor];
}
return (loff_t) blocks << BLOCK_SIZE_BITS;
return -EINVAL;
/* No blocksize array? Implies hardcoded BLOCK_SIZE */
- if (!blksize_size[MAJOR(dev)]) {
+ if (!blksize_size[major(dev)]) {
if (size == BLOCK_SIZE)
return 0;
return -EINVAL;
}
- oldsize = blksize_size[MAJOR(dev)][MINOR(dev)];
+ oldsize = blksize_size[major(dev)][minor(dev)];
if (oldsize == size)
return 0;
if (!oldsize && size == BLOCK_SIZE) {
- blksize_size[MAJOR(dev)][MINOR(dev)] = size;
+ blksize_size[major(dev)][minor(dev)] = size;
return 0;
}
/* Ok, we're actually changing the blocksize.. */
- bdev = bdget(dev);
+ bdev = bdget(kdev_t_to_nr(dev));
sync_buffers(dev, 2);
- blksize_size[MAJOR(dev)][MINOR(dev)] = size;
+ blksize_size[major(dev)][minor(dev)] = size;
bdev->bd_inode->i_blkbits = blksize_bits(size);
kill_bdev(bdev);
bdput(bdev);
int i;
const struct block_device_operations * bdops = NULL;
- i = MAJOR(dev);
+ i = major(dev);
if (i < MAX_BLKDEV)
bdops = blkdevs[i].bdops;
if (bdops == NULL) {
devfs_handle_t de;
- de = devfs_find_handle (NULL, NULL, i, MINOR (dev),
+ de = devfs_find_handle (NULL, NULL, i, minor(dev),
DEVFS_SPECIAL_BLK, 0);
if (de) {
bdops = devfs_get_ops (de);
down(&bdev->bd_sem);
lock_kernel();
if (!bdev->bd_op)
- bdev->bd_op = get_blkfops(MAJOR(dev));
+ bdev->bd_op = get_blkfops(major(dev));
if (bdev->bd_op) {
ret = 0;
if (bdev->bd_op->owner)
const char * bdevname(kdev_t dev)
{
static char buffer[32];
- const char * name = blkdevs[MAJOR(dev)].name;
+ const char * name = blkdevs[major(dev)].name;
if (!name)
name = "unknown-block";
- sprintf(buffer, "%s(%d,%d)", name, MAJOR(dev), MINOR(dev));
+ sprintf(buffer, "%s(%d,%d)", name, major(dev), minor(dev));
return buffer;
}
struct buffer_head * bh = next;
next = bh->b_next_free;
- if (dev && bh->b_dev != dev)
+ if (!kdev_none(dev) && !kdev_same(bh->b_dev, dev))
continue;
if (test_and_set_bit(BH_Lock, &bh->b_state))
continue;
__refile_buffer(bh);
continue;
}
- if (dev && bh->b_dev != dev)
+ if (!kdev_none(dev) && !kdev_same(bh->b_dev, dev))
continue;
get_bh(bh);
lock_kernel();
sync_inodes(dev);
- if (dev) {
+ if (!kdev_none(dev)) {
struct super_block *sb = get_super(dev);
if (sb) {
DQUOT_SYNC(sb);
asmlinkage long sys_sync(void)
{
- fsync_dev(0);
+ fsync_dev(NODEV);
return 0;
}
continue;
if (bh->b_size != size)
continue;
- if (bh->b_dev != dev)
+ if (!kdev_same(bh->b_dev, dev))
continue;
get_bh(bh);
break;
bh_next = bh->b_next_free;
/* Another device? */
- if (bh->b_dev != dev)
+ if (!kdev_same(bh->b_dev, dev))
continue;
/* Not hashed? */
if (!bh->b_pprev)
void __invalidate_buffers(kdev_t dev, int destroy_dirty_buffers)
{
- struct block_device *bdev = bdget(dev);
+ struct block_device *bdev = bdget(kdev_t_to_nr(dev));
if (bdev) {
invalidate_bdev(bdev, destroy_dirty_buffers);
bdput(bdev);
struct buffer_head * p = tmp;
tmp = tmp->b_this_page;
- if (p->b_dev == B_FREE) BUG();
+ if (kdev_same(p->b_dev, B_FREE)) BUG();
remove_inode_queue(p);
__remove_from_queues(p);
{
lock_kernel();
sync_unlocked_inodes();
- sync_supers(0);
+ sync_supers(NODEV);
unlock_kernel();
for (;;) {
/* serial module kmod load support */
struct tty_driver *get_tty_driver(kdev_t device);
#define isa_tty_dev(ma) (ma == TTY_MAJOR || ma == TTYAUX_MAJOR)
-#define need_serial(ma,mi) (get_tty_driver(MKDEV(ma,mi)) == NULL)
+#define need_serial(ma,mi) (get_tty_driver(mk_kdev(ma,mi)) == NULL)
#endif
struct device_struct {
{
int ret = -ENODEV;
- filp->f_op = get_chrfops(MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
+ filp->f_op = get_chrfops(major(inode->i_rdev), minor(inode->i_rdev));
if (filp->f_op) {
ret = 0;
if (filp->f_op->open != NULL) {
const char * kdevname(kdev_t dev)
{
static char buffer[32];
- sprintf(buffer, "%02x:%02x", MAJOR(dev), MINOR(dev));
+ sprintf(buffer, "%02x:%02x", major(dev), minor(dev));
return buffer;
}
const char * cdevname(kdev_t dev)
{
static char buffer[32];
- const char * name = chrdevs[MAJOR(dev)].name;
+ const char * name = chrdevs[major(dev)].name;
if (!name)
name = "unknown-char";
- sprintf(buffer, "%s(%d,%d)", name, MAJOR(dev), MINOR(dev));
+ sprintf(buffer, "%s(%d,%d)", name, major(dev), minor(dev));
return buffer;
}
/*
* Search the super_blocks array for the device(s) to sync.
*/
- if (dev) {
+ if (!kdev_none(dev)) {
if ((s = get_super(dev)) != NULL) {
sync_inodes_sb(s);
drop_super(s);
inodes_stat.nr_inodes++;
list_add(&inode->i_list, &inode_in_use);
inode->i_sb = NULL;
- inode->i_dev = 0;
+ inode->i_dev = NODEV;
inode->i_blkbits = 0;
inode->i_ino = ++last_ino;
inode->i_flags = 0;
* stored in the low field, and use that.
*/
if((low & ~0xff) && high == 0) {
- inode->i_rdev = MKDEV(low >> 8, low & 0xff);
+ inode->i_rdev = mk_kdev(low >> 8, low & 0xff);
} else {
- inode->i_rdev = MKDEV(high, low);
+ inode->i_rdev = mk_kdev(high, low);
}
}
break;
struct nlm_block *block;
int error;
- dprintk("lockd: nlmsvc_lock(%04x/%ld, ty=%d, pi=%d, %Ld-%Ld, bl=%d)\n",
- file->f_file.f_dentry->d_inode->i_dev,
+ dprintk("lockd: nlmsvc_lock(%02x:%02x/%ld, ty=%d, pi=%d, %Ld-%Ld, bl=%d)\n",
+ major(file->f_file.f_dentry->d_inode->i_dev),
+ minor(file->f_file.f_dentry->d_inode->i_dev),
file->f_file.f_dentry->d_inode->i_ino,
lock->fl.fl_type, lock->fl.fl_pid,
(long long)lock->fl.fl_start,
{
struct file_lock *fl;
- dprintk("lockd: nlmsvc_testlock(%04x/%ld, ty=%d, %Ld-%Ld)\n",
- file->f_file.f_dentry->d_inode->i_dev,
+ dprintk("lockd: nlmsvc_testlock(%02x:%02x/%ld, ty=%d, %Ld-%Ld)\n",
+ major(file->f_file.f_dentry->d_inode->i_dev),
+ minor(file->f_file.f_dentry->d_inode->i_dev),
file->f_file.f_dentry->d_inode->i_ino,
lock->fl.fl_type,
(long long)lock->fl.fl_start,
{
int error;
- dprintk("lockd: nlmsvc_unlock(%04x/%ld, pi=%d, %Ld-%Ld)\n",
- file->f_file.f_dentry->d_inode->i_dev,
+ dprintk("lockd: nlmsvc_unlock(%02x:%02x/%ld, pi=%d, %Ld-%Ld)\n",
+ major(file->f_file.f_dentry->d_inode->i_dev),
+ minor(file->f_file.f_dentry->d_inode->i_dev),
file->f_file.f_dentry->d_inode->i_ino,
lock->fl.fl_pid,
(long long)lock->fl.fl_start,
{
struct nlm_block *block;
- dprintk("lockd: nlmsvc_cancel(%04x/%ld, pi=%d, %Ld-%Ld)\n",
- file->f_file.f_dentry->d_inode->i_dev,
+ dprintk("lockd: nlmsvc_cancel(%02x:%02x/%ld, pi=%d, %Ld-%Ld)\n",
+ major(file->f_file.f_dentry->d_inode->i_dev),
+ minor(file->f_file.f_dentry->d_inode->i_dev),
file->f_file.f_dentry->d_inode->i_ino,
lock->fl.fl_pid,
(long long)lock->fl.fl_start,
goto exit_lock;
error = -EXDEV;
- if (dir->i_dev != inode->i_dev)
+ if (!kdev_same(dir->i_dev, inode->i_dev))
goto exit_lock;
/*
if (error)
return error;
- if (new_dir->i_dev != old_dir->i_dev)
+ if (!kdev_same(new_dir->i_dev, old_dir->i_dev))
return -EXDEV;
if (!new_dentry->d_inode)
if (error)
return error;
- if (new_dir->i_dev != old_dir->i_dev)
+ if (!kdev_same(new_dir->i_dev, old_dir->i_dev))
return -EXDEV;
if (!new_dentry->d_inode)
struct nfs_fh fhandle;
int error;
- dfprintk(VFS, "NFS: create(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name);
+ dfprintk(VFS, "NFS: create(%x:%x/%ld, %s\n",
+ major(dir->i_dev), minor(dir->i_dev), dir->i_ino, dentry->d_name.name);
attr.ia_mode = mode;
attr.ia_valid = ATTR_MODE;
struct nfs_fh fhandle;
int error;
- dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name);
+ dfprintk(VFS, "NFS: mknod(%x:%x/%ld, %s\n",
+ major(dir->i_dev), minor(dir->i_dev), dir->i_ino, dentry->d_name.name);
attr.ia_mode = mode;
attr.ia_valid = ATTR_MODE;
struct nfs_fh fhandle;
int error;
- dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name);
+ dfprintk(VFS, "NFS: mkdir(%x:%x/%ld, %s\n",
+ major(dir->i_dev), minor(dir->i_dev), dir->i_ino, dentry->d_name.name);
attr.ia_valid = ATTR_MODE;
attr.ia_mode = mode | S_IFDIR;
{
int error;
- dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name);
+ dfprintk(VFS, "NFS: rmdir(%x:%x/%ld, %s\n",
+ major(dir->i_dev), minor(dir->i_dev), dir->i_ino, dentry->d_name.name);
nfs_zap_caches(dir);
error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
{
int error;
- dfprintk(VFS, "NFS: unlink(%x/%ld, %s)\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name);
+ dfprintk(VFS, "NFS: unlink(%x:%x/%ld, %s)\n",
+ major(dir->i_dev), minor(dir->i_dev), dir->i_ino, dentry->d_name.name);
error = nfs_sillyrename(dir, dentry);
if (error && error != -EBUSY) {
unsigned int maxlen;
int error;
- dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name, symname);
+ dfprintk(VFS, "NFS: symlink(%x:%x/%ld, %s, %s)\n",
+ major(dir->i_dev), minor(dir->i_dev), dir->i_ino, dentry->d_name.name, symname);
error = -ENAMETOOLONG;
maxlen = (NFS_PROTO(dir)->version==2) ? NFS2_MAXPATHLEN : NFS3_MAXPATHLEN;
struct inode *inode = file->f_dentry->d_inode;
int status;
- dfprintk(VFS, "nfs: flush(%x/%ld)\n", inode->i_dev, inode->i_ino);
+ dfprintk(VFS, "nfs: flush(%02x:%02x/%ld)\n",
+ major(inode->i_dev), minor(inode->i_dev), inode->i_ino);
/* Make sure all async reads have been sent off. We don't bother
* waiting on them though... */
struct inode *inode = dentry->d_inode;
int status;
- dfprintk(VFS, "nfs: fsync(%x/%ld)\n", inode->i_dev, inode->i_ino);
+ dfprintk(VFS, "nfs: fsync(%02x:%02x/%ld)\n",
+ major(inode->i_dev), minor(inode->i_dev), inode->i_ino);
lock_kernel();
status = nfs_wb_file(inode, file);
struct inode * inode = filp->f_dentry->d_inode;
int status = 0;
- dprintk("NFS: nfs_lock(f=%4x/%ld, t=%x, fl=%x, r=%Ld:%Ld)\n",
- inode->i_dev, inode->i_ino,
+ dprintk("NFS: nfs_lock(f=%02x:%02x/%ld, t=%x, fl=%x, r=%Ld:%Ld)\n",
+ major(inode->i_dev), minor(inode->i_dev), inode->i_ino,
fl->fl_type, fl->fl_flags,
(long long)fl->fl_start, (long long)fl->fl_end);
{
inode->i_blksize = inode->i_sb->s_blocksize;
inode->i_mode = 0;
- inode->i_rdev = 0;
+ inode->i_rdev = NODEV;
/* We can't support UPDATE_ATIME(), since the server will reset it */
inode->i_flags |= S_NOATIME;
INIT_LIST_HEAD(&inode->u.nfs_i.read);
static void
nfs_delete_inode(struct inode * inode)
{
- dprintk("NFS: delete_inode(%x/%ld)\n", inode->i_dev, inode->i_ino);
+ dprintk("NFS: delete_inode(%x:%x/%ld)\n", major(inode->i_dev), minor(inode->i_dev), inode->i_ino);
/*
* The following can never actually happen...
goto out_no_inode;
nfs_fill_inode(inode, fh, fattr);
- dprintk("NFS: __nfs_fhget(%x/%Ld ct=%d)\n",
- inode->i_dev, (long long)NFS_FILEID(inode),
+ dprintk("NFS: __nfs_fhget(%x:%x/%Ld ct=%d)\n",
+ major(inode->i_dev), minor(inode->i_dev),
+ (long long)NFS_FILEID(inode),
atomic_read(&inode->i_count));
out:
int status = -ESTALE;
struct nfs_fattr fattr;
- dfprintk(PAGECACHE, "NFS: revalidating (%x/%Ld)\n",
- inode->i_dev, (long long)NFS_FILEID(inode));
+ dfprintk(PAGECACHE, "NFS: revalidating (%x:%x/%Ld)\n",
+ major(inode->i_dev), minor(inode->i_dev),
+ (long long)NFS_FILEID(inode));
lock_kernel();
if (!inode || is_bad_inode(inode))
status = NFS_PROTO(inode)->getattr(inode, &fattr);
if (status) {
- dfprintk(PAGECACHE, "nfs_revalidate_inode: (%x/%Ld) getattr failed, error=%d\n",
- inode->i_dev, (long long)NFS_FILEID(inode), status);
+ dfprintk(PAGECACHE, "nfs_revalidate_inode: (%x:%x/%Ld) getattr failed, error=%d\n",
+ major(inode->i_dev), minor(inode->i_dev),
+ (long long)NFS_FILEID(inode), status);
if (status == -ESTALE) {
NFS_FLAGS(inode) |= NFS_INO_STALE;
if (inode != inode->i_sb->s_root->d_inode)
status = nfs_refresh_inode(inode, &fattr);
if (status) {
- dfprintk(PAGECACHE, "nfs_revalidate_inode: (%x/%Ld) refresh failed, error=%d\n",
- inode->i_dev, (long long)NFS_FILEID(inode), status);
+ dfprintk(PAGECACHE, "nfs_revalidate_inode: (%x:%x/%Ld) refresh failed, error=%d\n",
+ major(inode->i_dev), minor(inode->i_dev),
+ (long long)NFS_FILEID(inode), status);
goto out;
}
- dfprintk(PAGECACHE, "NFS: (%x/%Ld) revalidation complete\n",
- inode->i_dev, (long long)NFS_FILEID(inode));
+ dfprintk(PAGECACHE, "NFS: (%x:%x/%Ld) revalidation complete\n",
+ major(inode->i_dev), minor(inode->i_dev),
+ (long long)NFS_FILEID(inode));
NFS_FLAGS(inode) &= ~NFS_INO_STALE;
out:
time_t new_atime;
int invalid = 0;
- dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d info=0x%x)\n",
- inode->i_dev, inode->i_ino,
+ dfprintk(VFS, "NFS: refresh_inode(%x:%x/%ld ct=%d info=0x%x)\n",
+ major(inode->i_dev), minor(inode->i_dev), inode->i_ino,
atomic_read(&inode->i_count), fattr->valid);
if (NFS_FSID(inode) != fattr->fsid ||
inode->i_blocks = fattr->du.nfs2.blocks;
inode->i_blksize = fattr->du.nfs2.blocksize;
}
- inode->i_rdev = 0;
+ inode->i_rdev = NODEV;
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
inode->i_rdev = to_kdev_t(fattr->rdev);
if (count < rsize)
rsize = count;
- dprintk("NFS: nfs_proc_read(%s, (%x/%Ld), %Ld, %d, %p)\n",
+ dprintk("NFS: nfs_proc_read(%s, (%x:%x/%Ld), %Ld, %d, %p)\n",
NFS_SERVER(inode)->hostname,
- inode->i_dev, (long long)NFS_FILEID(inode),
+ major(inode->i_dev), minor(inode->i_dev),
+ (long long)NFS_FILEID(inode),
(long long)offset, rsize, buffer);
lock_kernel();
msg.rpc_cred = data->cred;
/* Start the async call */
- dprintk("NFS: %4d initiated read call (req %x/%Ld count %d nriov %d.\n",
+ dprintk("NFS: %4d initiated read call (req %x:%x/%Ld count %d nriov %d.\n",
task->tk_pid,
- inode->i_dev, (long long)NFS_FILEID(inode),
+ major(inode->i_dev), minor(inode->i_dev),
+ (long long)NFS_FILEID(inode),
data->args.count, data->args.nriov);
rpc_clnt_sigmask(clnt, &oldset);
kunmap(page);
UnlockPage(page);
- dprintk("NFS: read (%x/%Ld %d@%Ld)\n",
- req->wb_inode->i_dev,
+ dprintk("NFS: read (%x:%x/%Ld %d@%Ld)\n",
+ major(req->wb_inode->i_dev),
+ minor(req->wb_inode->i_dev),
(long long)NFS_FILEID(req->wb_inode),
req->wb_bytes,
(long long)(page_offset(page) + req->wb_offset));
if (!cred)
cred = get_rpccred(NFS_I(inode)->mm_cred);
- dprintk("NFS: nfs_writepage_sync(%x/%Ld %d@%Ld)\n",
- inode->i_dev, (long long)NFS_FILEID(inode),
+ dprintk("NFS: nfs_writepage_sync(%x:%x/%Ld %d@%Ld)\n",
+ major(inode->i_dev), minor(inode->i_dev),
+ (long long)NFS_FILEID(inode),
count, (long long)(page_offset(page) + offset));
buffer = kmap(page) + offset;
msg.rpc_resp = &data->res;
msg.rpc_cred = data->cred;
- dprintk("NFS: %4d initiated write call (req %x/%Ld count %d nriov %d)\n",
+ dprintk("NFS: %4d initiated write call (req %x:%x/%Ld count %d nriov %d)\n",
task->tk_pid,
- inode->i_dev,
+ major(inode->i_dev), minor(inode->i_dev),
(long long)NFS_FILEID(inode),
data->args.count, data->args.nriov);
kunmap(page);
- dprintk("NFS: write (%x/%Ld %d@%Ld)",
- req->wb_inode->i_dev,
+ dprintk("NFS: write (%x:%x/%Ld %d@%Ld)",
+ major(req->wb_inode->i_dev),
+ minor(req->wb_inode->i_dev),
(long long)NFS_FILEID(req->wb_inode),
req->wb_bytes,
(long long)(page_offset(page) + req->wb_offset));
#define CLIENT_HASH(a) \
((((a)>>24) ^ ((a)>>16) ^ ((a)>>8) ^(a)) & CLIENT_HASHMASK)
/* XXX: is this adequate for 32bit kdev_t ? */
-#define EXPORT_HASH(dev) ((dev) & (NFSCLNT_EXPMAX - 1))
+#define EXPORT_HASH(dev) (minor(dev) & (NFSCLNT_EXPMAX - 1))
struct svc_clnthash {
struct svc_clnthash * h_next;
svc_export * exp;
exp = clp->cl_export[EXPORT_HASH(dev)];
- while (exp && exp->ex_dev != dev)
+ while (exp && !kdev_same(exp->ex_dev, dev))
exp = exp->ex_next;
return exp;
}
exp = clp->cl_export[EXPORT_HASH(dev)];
if (exp)
do {
- if (exp->ex_ino == ino && exp->ex_dev == dev)
+ if (exp->ex_ino == ino && kdev_same(exp->ex_dev, dev))
goto out;
} while (NULL != (exp = exp->ex_next));
exp = NULL;
inode = nd.dentry->d_inode;
err = -EINVAL;
- if (inode->i_dev != dev || inode->i_ino != nxp->ex_ino) {
- printk(KERN_DEBUG "exp_export: i_dev = %x, dev = %x\n",
- inode->i_dev, dev);
+ if (!kdev_same(inode->i_dev, dev) || inode->i_ino != nxp->ex_ino) {
+ printk(KERN_DEBUG "exp_export: i_dev = %02x:%02x, dev = %02x:%02x\n",
+ major(inode->i_dev), minor(inode->i_dev),
+ major(dev), minor(dev));
/* I'm just being paranoid... */
goto finish;
}
dentry = unexp->ex_dentry;
mnt = unexp->ex_mnt;
inode = dentry->d_inode;
- if (unexp->ex_dev != inode->i_dev || unexp->ex_ino != inode->i_ino)
+ if (!kdev_same(unexp->ex_dev, inode->i_dev) || unexp->ex_ino != inode->i_ino)
printk(KERN_WARNING "nfsd: bad dentry in unexport!\n");
dput(dentry);
mntput(mnt);
err = -EINVAL;
clp = exp_getclientbyname(nxp->ex_client);
if (clp) {
- expp = clp->cl_export + EXPORT_HASH(nxp->ex_dev);
+ kdev_t ex_dev = to_kdev_t(nxp->ex_dev);
+ expp = clp->cl_export + EXPORT_HASH(ex_dev);
while ((exp = *expp) != NULL) {
- if (exp->ex_dev == nxp->ex_dev) {
+ if (kdev_same(exp->ex_dev, ex_dev)) {
if (exp->ex_ino == nxp->ex_ino) {
*expp = exp->ex_next;
exp_do_unexport(exp);
dev = nd.dentry->d_inode->i_dev;
ino = nd.dentry->d_inode->i_ino;
- dprintk("nfsd: exp_rootfh(%s [%p] %s:%x/%ld)\n",
- path, nd.dentry, clp->cl_ident, dev, (long) ino);
+ dprintk("nfsd: exp_rootfh(%s [%p] %s:%02x:%02x/%ld)\n",
+ path, nd.dentry, clp->cl_ident,
+ major(dev), minor(dev), (long) ino);
exp = exp_parent(clp, dev, nd.dentry);
} else {
- dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n",
- clp->cl_ident, dev, (long) ino);
+ dprintk("nfsd: exp_rootfh(%s:%02x:%02x/%ld)\n",
+ clp->cl_ident, major(dev), minor(dev), (long) ino);
if ((exp = exp_get(clp, dev, ino))) {
nd.mnt = mntget(exp->ex_mnt);
nd.dentry = dget(exp->ex_dentry);
printk("exp_rootfh: Aieee, NULL d_inode\n");
goto out;
}
- if (inode->i_dev != dev || inode->i_ino != ino) {
+ if (!kdev_same(inode->i_dev, dev) || inode->i_ino != ino) {
printk("exp_rootfh: Aieee, ino/dev mismatch\n");
- printk("exp_rootfh: arg[dev(%x):ino(%ld)]"
- " inode[dev(%x):ino(%ld)]\n",
- dev, (long) ino, inode->i_dev, (long) inode->i_ino);
+ printk("exp_rootfh: arg[dev(%02x:%02x):ino(%ld)]"
+ " inode[dev(%02x:%02x):ino(%ld)]\n",
+ major(dev), minor(dev), (long) ino,
+ major(inode->i_dev), minor(inode->i_dev), (long) inode->i_ino);
}
/*
if (!(clp = exp_getclient(sin)))
err = -EPERM;
else
- err = exp_rootfh(clp, 0, 0, data->gd_path, res, data->gd_maxlen);
+ err = exp_rootfh(clp, NODEV, 0, data->gd_path, res, data->gd_maxlen);
exp_unlock();
return err;
}
if (!(clp = exp_getclient(sin)))
err = -EPERM;
else
- err = exp_rootfh(clp, 0, 0, data->gd_path, &fh, NFS_FHSIZE);
+ err = exp_rootfh(clp, NODEV, 0, data->gd_path, &fh, NFS_FHSIZE);
exp_unlock();
if (err == 0) {
/* It's a directory, or we are required to confirm the file's
* location in the tree.
*/
- dprintk("nfs_fh: need to look harder for %d/%d\n",sb->s_dev,datap[0]);
+ dprintk("nfs_fh: need to look harder for %02x:%02x/%d\n",
+ major(sb->s_dev), minor(sb->s_dev), datap[0]);
if (!S_ISDIR(result->d_inode->i_mode)) {
nfsdstats.fh_nocache_nondir++;
case 0:
if ((data_left-=2)<0) goto out;
nfsdev = ntohl(*datap++);
- xdev = MKDEV(nfsdev>>16, nfsdev&0xFFFF);
+ xdev = mk_kdev(nfsdev>>16, nfsdev&0xFFFF);
xino = *datap++;
break;
default:
struct dentry *parent = dentry->d_parent;
__u32 *datap;
- dprintk("nfsd: fh_compose(exp %x/%ld %s/%s, ino=%ld)\n",
- exp->ex_dev, (long) exp->ex_ino,
+ dprintk("nfsd: fh_compose(exp %02x:%02x/%ld %s/%s, ino=%ld)\n",
+ major(exp->ex_dev), minor(exp->ex_dev), (long) exp->ex_ino,
parent->d_name.name, dentry->d_name.name,
(inode ? inode->i_ino : 0));
memset(&fhp->fh_handle.fh_base, 0, NFS_FHSIZE);
fhp->fh_handle.fh_size = NFS_FHSIZE;
fhp->fh_handle.ofh_dcookie = 0xfeebbaca;
- fhp->fh_handle.ofh_dev = htonl((MAJOR(exp->ex_dev)<<16)| MINOR(exp->ex_dev));
+ fhp->fh_handle.ofh_dev = htonl((major(exp->ex_dev)<<16)| minor(exp->ex_dev));
fhp->fh_handle.ofh_xdev = fhp->fh_handle.ofh_dev;
fhp->fh_handle.ofh_xino = ino_t_to_u32(exp->ex_ino);
fhp->fh_handle.ofh_dirino = ino_t_to_u32(dentry->d_parent->d_inode->i_ino);
fhp->fh_handle.fh_fsid_type = 0;
datap = fhp->fh_handle.fh_auth+0;
/* fsid_type 0 == 2byte major, 2byte minor, 4byte inode */
- *datap++ = htonl((MAJOR(exp->ex_dev)<<16)| MINOR(exp->ex_dev));
+ *datap++ = htonl((major(exp->ex_dev)<<16)| minor(exp->ex_dev));
*datap++ = ino_t_to_u32(exp->ex_ino);
fhp->fh_handle.fh_size = 3*4;
if (inode) {
struct inode *inode;
struct dentry *dchild;
int nfserr, type, mode;
- dev_t rdev = NODEV;
+ dev_t rdev = 0;
dprintk("nfsd: CREATE %s %.*s\n",
SVCFH_fmt(dirfhp), argp->len, argp->name);
case S_IFCHR:
case S_IFBLK:
/* reserve rdev for later checking */
- attr->ia_size = inode->i_rdev;
+ attr->ia_size = kdev_t_to_nr(inode->i_rdev);
attr->ia_valid |= ATTR_SIZE;
/* FALLTHROUGH */
/* Make sure the type and device matches */
nfserr = nfserr_exist;
if (inode && (type != (inode->i_mode & S_IFMT) ||
- (is_borc && inode->i_rdev != rdev)))
+ (is_borc && kdev_t_to_nr(inode->i_rdev) != rdev)))
goto out_unlock;
}
}
*p++ = htonl((u32) inode->i_blksize);
if (S_ISCHR(type) || S_ISBLK(type))
- *p++ = htonl((u32) inode->i_rdev);
+ *p++ = htonl((u32) kdev_t_to_nr(inode->i_rdev));
else
*p++ = htonl(0xffffffff);
*p++ = htonl((u32) inode->i_blocks);
- *p++ = htonl((u32) inode->i_dev);
+ *p++ = htonl((u32) kdev_t_to_nr(inode->i_dev));
*p++ = htonl((u32) inode->i_ino);
*p++ = htonl((u32) inode->i_atime);
*p++ = 0;
struct raparms *p_next;
unsigned int p_count;
ino_t p_ino;
- dev_t p_dev;
+ kdev_t p_dev;
unsigned long p_reada,
p_ramax,
p_raend,
* specified by (dev, ino).
*/
static inline struct raparms *
-nfsd_get_raparms(dev_t dev, ino_t ino)
+nfsd_get_raparms(kdev_t dev, ino_t ino)
{
struct raparms *ra, **rap, **frap = NULL;
int depth = 0;
for (rap = &raparm_cache; (ra = *rap); rap = &ra->p_next) {
- if (ra->p_ino == ino && ra->p_dev == dev)
+ if (ra->p_ino == ino && kdev_same(ra->p_dev, dev))
goto found;
depth++;
if (ra->p_count == 0)
*/
if (EX_WGATHER(exp)) {
if (atomic_read(&inode->i_writecount) > 1
- || (last_ino == inode->i_ino && last_dev == inode->i_dev)) {
+ || (last_ino == inode->i_ino && kdev_same(last_dev, inode->i_dev))) {
dprintk("nfsd: write defer %d\n", current->pid);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout((HZ+99)/100);
tdir = tdentry->d_inode;
err = (rqstp->rq_vers == 2) ? nfserr_acces : nfserr_xdev;
- if (fdir->i_dev != tdir->i_dev)
+ if (!kdev_same(fdir->i_dev, tdir->i_dev))
goto out;
err = nfserr_perm;
int atari_partition (struct gendisk *hd, struct block_device *bdev,
unsigned long first_sector, int minor)
{
- int m_lim = minor + hd->max_p;
+ int m_lim = minor + (1 << hd->minor_shift);
Sector sect;
struct rootsector *rs;
struct partition_info *pi;
int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/
-static int (*check_part[])(struct gendisk *hd, struct block_device *bdev, unsigned long first_sect, int first_minor) = {
+static int (*check_part[])(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sect, int first_minor) = {
#ifdef CONFIG_ACORN_PARTITION
acorn_partition,
#endif
if (first_time)
printk(KERN_INFO "Partition check:\n");
first_time = 0;
- first_sector = hd->part[MINOR(dev)].start_sect;
+ first_sector = hd->part[minor(dev)].start_sect;
/*
* This is a kludge to allow the partition check to be
* skipped for specific drives (e.g. IDE CD-ROM drives)
*/
if ((int)first_sector == -1) {
- hd->part[MINOR(dev)].start_sect = 0;
+ hd->part[minor(dev)].start_sect = 0;
return;
}
if (hd->de_arr)
- de = hd->de_arr[MINOR(dev) >> hd->minor_shift];
+ de = hd->de_arr[minor(dev) >> hd->minor_shift];
i = devfs_generate_path (de, buf, sizeof buf);
if (i >= 0)
printk(KERN_INFO " /dev/%s:", buf + i);
else
- printk(KERN_INFO " %s:", disk_name(hd, MINOR(dev), buf));
+ printk(KERN_INFO " %s:", disk_name(hd, minor(dev), buf));
bdev = bdget(kdev_t_to_nr(dev));
- bdev->bd_inode->i_size = (loff_t)hd->part[MINOR(dev)].nr_sects << 9;
+ bdev->bd_inode->i_size = (loff_t)hd->part[minor(dev)].nr_sects << 9;
bdev->bd_inode->i_blkbits = blksize_bits(block_size(dev));
for (i = 0; check_part[i]; i++) {
int res;
void devfs_register_partitions (struct gendisk *dev, int minor, int unregister)
{
#ifdef CONFIG_DEVFS_FS
- int part;
+ int part, max_p;
if (!unregister)
devfs_register_disc (dev, minor);
- for (part = 1; part < dev->max_p; part++) {
+ max_p = (1 << dev->minor_shift);
+ for (part = 1; part < max_p; part++) {
if ( unregister || (dev->part[part + minor].nr_sects < 1) ) {
devfs_unregister (dev->part[part + minor].de);
dev->part[part + minor].de = NULL;
return;
minors = 1 << g->minor_shift;
- first_minor = MINOR(dev);
+ first_minor = minor(dev);
if (first_minor & (minors-1)) {
printk("grok_partitions: bad device 0x%02x:%02x\n",
- MAJOR(dev), first_minor);
+ major(dev), first_minor);
first_minor &= ~(minors-1);
}
end_minor = first_minor + minors;
g->sizes[i] = 0;
}
blk_size[g->major] = g->sizes;
- check_partition(g, MKDEV(g->major, first_minor), 1 + first_minor);
+ check_partition(g, mk_kdev(g->major, first_minor), 1 + first_minor);
/*
* We need to set the sizes array before we will be able to access
return -EINVAL;
max_p = 1 << g->minor_shift;
- major = MAJOR(dev);
- minor = MINOR(dev);
+ major = major(dev);
+ minor = minor(dev);
minor0 = minor & ~(max_p - 1);
if (minor0 != minor) /* for now only whole-disk reread */
return -EINVAL; /* %%% later.. */
/* invalidate stuff */
for (p = max_p - 1; p >= 0; p--) {
minor = minor0 + p;
- devp = MKDEV(major,minor);
+ devp = mk_kdev(major,minor);
#if 0 /* %%% superfluous? */
if (g->part[minor].nr_sects == 0)
continue;
str[3] = flags & VM_MAYSHARE ? 's' : 'p';
str[4] = 0;
- dev = 0;
+ dev = NODEV;
ino = 0;
if (map->vm_file != NULL) {
dev = map->vm_file->f_dentry->d_inode->i_dev;
list_for_each(p, &super_blocks) {
struct super_block * s = sb_entry(p);
- if (s->s_dev == dev) {
+ if (kdev_same(s->s_dev, dev)) {
s->s_count++;
return s;
}
{
struct super_block * sb;
- if (dev) {
+ if (!kdev_none(dev)) {
sb = get_super(dev);
if (sb) {
if (sb->s_dirt)
{
struct super_block * s;
- if (!dev)
+ if (kdev_none(dev))
return NULL;
while (1) {
{
int retval;
- if (!(flags & MS_RDONLY) && sb->s_dev && is_read_only(sb->s_dev))
+ if (!(flags & MS_RDONLY) && !kdev_none(sb->s_dev) && is_read_only(sb->s_dev))
return -EACCES;
/*flags |= MS_RDONLY;*/
if (flags & MS_RDONLY)
static void put_anon_dev(kdev_t dev)
{
spin_lock(&unnamed_dev_lock);
- clear_bit(MINOR(dev), unnamed_dev_in_use);
+ clear_bit(minor(dev), unnamed_dev_in_use);
spin_unlock(&unnamed_dev_lock);
}
int (*compare)(struct super_block *,void *), void *data)
{
struct super_block *s = alloc_super();
- kdev_t dev;
+ int dev;
struct list_head *p;
if (!s)
return old;
}
- s->s_dev = dev;
+ s->s_dev = mk_kdev(0, dev);
insert_super(s, type);
return s;
}
list_for_each(p, &super_blocks) {
struct super_block *old = sb_entry(p);
- if (old->s_dev != dev)
+ if (!kdev_same(old->s_dev, dev))
continue;
if (old->s_type != fs_type ||
((flags ^ old->s_flags) & MS_RDONLY)) {
#ifdef IDE_DRIVER
-#define DEVICE_NR(device) (MINOR(device) >> PARTN_BITS)
+#define DEVICE_NR(device) (minor(device) >> PARTN_BITS)
#define DEVICE_NAME "ide"
#elif (MAJOR_NR == RAMDISK_MAJOR)
/* ram disk */
#define DEVICE_NAME "ramdisk"
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#define DEVICE_NO_RANDOM
#elif (MAJOR_NR == Z2RAM_MAJOR)
/* Zorro II Ram */
#define DEVICE_NAME "Z2RAM"
#define DEVICE_REQUEST do_z2_request
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#elif (MAJOR_NR == FLOPPY_MAJOR)
#define DEVICE_NAME "floppy"
#define DEVICE_INTR do_floppy
#define DEVICE_REQUEST do_fd_request
-#define DEVICE_NR(device) ( (MINOR(device) & 3) | ((MINOR(device) & 0x80 ) >> 5 ))
+#define DEVICE_NR(device) ( (minor(device) & 3) | ((minor(device) & 0x80 ) >> 5 ))
#define DEVICE_OFF(device) floppy_off(DEVICE_NR(device))
#elif (MAJOR_NR == HD_MAJOR)
#define DEVICE_INTR do_hd
#define TIMEOUT_VALUE (6*HZ)
#define DEVICE_REQUEST do_hd_request
-#define DEVICE_NR(device) (MINOR(device)>>6)
+#define DEVICE_NR(device) (minor(device)>>6)
#elif (SCSI_DISK_MAJOR(MAJOR_NR))
#define DEVICE_NAME "scsidisk"
#define TIMEOUT_VALUE (2*HZ)
-#define DEVICE_NR(device) (((MAJOR(device) & SD_MAJOR_MASK) << (8 - 4)) + (MINOR(device) >> 4))
+#define DEVICE_NR(device) (((major(device) & SD_MAJOR_MASK) << (8 - 4)) + (minor(device) >> 4))
/* Kludge to use the same number for both char and block major numbers */
#elif (MAJOR_NR == MD_MAJOR) && defined(MD_DRIVER)
#define DEVICE_NAME "Multiple devices driver"
#define DEVICE_REQUEST do_md_request
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#elif (MAJOR_NR == SCSI_TAPE_MAJOR)
#define DEVICE_NAME "scsitape"
#define DEVICE_INTR do_st
-#define DEVICE_NR(device) (MINOR(device) & 0x7f)
+#define DEVICE_NR(device) (minor(device) & 0x7f)
#elif (MAJOR_NR == OSST_MAJOR)
#define DEVICE_NAME "onstream"
#define DEVICE_INTR do_osst
-#define DEVICE_NR(device) (MINOR(device) & 0x7f)
+#define DEVICE_NR(device) (minor(device) & 0x7f)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif (MAJOR_NR == SCSI_CDROM_MAJOR)
#define DEVICE_NAME "CD-ROM"
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#elif (MAJOR_NR == XT_DISK_MAJOR)
#define DEVICE_NAME "xt disk"
#define DEVICE_REQUEST do_xd_request
-#define DEVICE_NR(device) (MINOR(device) >> 6)
+#define DEVICE_NR(device) (minor(device) >> 6)
#elif (MAJOR_NR == PS2ESDI_MAJOR)
#define DEVICE_NAME "PS/2 ESDI"
#define DEVICE_REQUEST do_ps2esdi_request
-#define DEVICE_NR(device) (MINOR(device) >> 6)
+#define DEVICE_NR(device) (minor(device) >> 6)
#elif (MAJOR_NR == CDU31A_CDROM_MAJOR)
#define DEVICE_NAME "CDU31A"
#define DEVICE_REQUEST do_cdu31a_request
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#elif (MAJOR_NR == ACSI_MAJOR) && (defined(CONFIG_ATARI_ACSI) || defined(CONFIG_ATARI_ACSI_MODULE))
#define DEVICE_NAME "ACSI"
#define DEVICE_INTR do_acsi
#define DEVICE_REQUEST do_acsi_request
-#define DEVICE_NR(device) (MINOR(device) >> 4)
+#define DEVICE_NR(device) (minor(device) >> 4)
#elif (MAJOR_NR == MITSUMI_CDROM_MAJOR)
#define DEVICE_NAME "Mitsumi CD-ROM"
/* #define DEVICE_INTR do_mcd */
#define DEVICE_REQUEST do_mcd_request
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#elif (MAJOR_NR == MITSUMI_X_CDROM_MAJOR)
#define DEVICE_NAME "Mitsumi CD-ROM"
/* #define DEVICE_INTR do_mcdx */
#define DEVICE_REQUEST do_mcdx_request
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#elif (MAJOR_NR == MATSUSHITA_CDROM_MAJOR)
#define DEVICE_NAME "Matsushita CD-ROM controller #1"
#define DEVICE_REQUEST do_sbpcd_request
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#elif (MAJOR_NR == MATSUSHITA_CDROM2_MAJOR)
#define DEVICE_NAME "Matsushita CD-ROM controller #2"
#define DEVICE_REQUEST do_sbpcd2_request
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#elif (MAJOR_NR == MATSUSHITA_CDROM3_MAJOR)
#define DEVICE_NAME "Matsushita CD-ROM controller #3"
#define DEVICE_REQUEST do_sbpcd3_request
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#elif (MAJOR_NR == MATSUSHITA_CDROM4_MAJOR)
#define DEVICE_NAME "Matsushita CD-ROM controller #4"
#define DEVICE_REQUEST do_sbpcd4_request
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#elif (MAJOR_NR == AZTECH_CDROM_MAJOR)
#define DEVICE_NAME "Aztech CD-ROM"
#define DEVICE_REQUEST do_aztcd_request
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#elif (MAJOR_NR == CDU535_CDROM_MAJOR)
#define DEVICE_NAME "SONY-CDU535"
#define DEVICE_INTR do_cdu535
#define DEVICE_REQUEST do_cdu535_request
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#elif (MAJOR_NR == GOLDSTAR_CDROM_MAJOR)
#define DEVICE_NAME "Goldstar R420"
#define DEVICE_REQUEST do_gscd_request
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#elif (MAJOR_NR == CM206_CDROM_MAJOR)
#define DEVICE_NAME "Philips/LMS CD-ROM cm206"
#define DEVICE_REQUEST do_cm206_request
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#elif (MAJOR_NR == OPTICS_CDROM_MAJOR)
#define DEVICE_NAME "DOLPHIN 8000AT CD-ROM"
#define DEVICE_REQUEST do_optcd_request
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#elif (MAJOR_NR == SANYO_CDROM_MAJOR)
#define DEVICE_NAME "Sanyo H94A CD-ROM"
#define DEVICE_REQUEST do_sjcd_request
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#elif (MAJOR_NR == APBLOCK_MAJOR)
#define DEVICE_NAME "apblock"
#define DEVICE_REQUEST ap_request
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#elif (MAJOR_NR == DDV_MAJOR)
#define DEVICE_NAME "ddv"
#define DEVICE_REQUEST ddv_request
-#define DEVICE_NR(device) (MINOR(device)>>PARTN_BITS)
+#define DEVICE_NR(device) (minor(device)>>PARTN_BITS)
#elif (MAJOR_NR == MFM_ACORN_MAJOR)
#define DEVICE_NAME "mfm disk"
#define DEVICE_INTR do_mfm
#define DEVICE_REQUEST do_mfm_request
-#define DEVICE_NR(device) (MINOR(device) >> 6)
+#define DEVICE_NR(device) (minor(device) >> 6)
#elif (MAJOR_NR == NBD_MAJOR)
#define DEVICE_NAME "nbd"
#define DEVICE_REQUEST do_nbd_request
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#elif (MAJOR_NR == MDISK_MAJOR)
#define DEVICE_NAME "mdisk"
#define DEVICE_REQUEST mdisk_request
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (minor(device))
#elif (MAJOR_NR == DASD_MAJOR)
#define DEVICE_NAME "dasd"
#define DEVICE_REQUEST do_dasd_request
-#define DEVICE_NR(device) (MINOR(device) >> PARTN_BITS)
+#define DEVICE_NR(device) (minor(device) >> PARTN_BITS)
#elif (MAJOR_NR == I2O_MAJOR)
#define DEVICE_NAME "I2O block"
#define DEVICE_REQUEST i2ob_request
-#define DEVICE_NR(device) (MINOR(device)>>4)
+#define DEVICE_NR(device) (minor(device)>>4)
#elif (MAJOR_NR == COMPAQ_SMART2_MAJOR)
#define DEVICE_NAME "ida"
#define TIMEOUT_VALUE (25*HZ)
#define DEVICE_REQUEST do_ida_request
-#define DEVICE_NR(device) (MINOR(device) >> 4)
+#define DEVICE_NR(device) (minor(device) >> 4)
#endif /* MAJOR_NR == whatever */
CLEAR_INTR; \
return; \
} \
- if (MAJOR(CURRENT->rq_dev) != MAJOR_NR) \
+ if (major(CURRENT->rq_dev) != MAJOR_NR) \
panic(DEVICE_NAME ": request list destroyed"); \
if (!CURRENT->bio) \
panic(DEVICE_NAME ": no bio"); \
return;
#ifndef DEVICE_NO_RANDOM
- add_blkdev_randomness(MAJOR(req->rq_dev));
+ add_blkdev_randomness(major(req->rq_dev));
#endif
DEVICE_OFF(req->rq_dev);
blkdev_dequeue_request(req);
extern inline unsigned int block_size(kdev_t dev)
{
int retval = BLOCK_SIZE;
- int major = MAJOR(dev);
+ int major = major(dev);
if (blksize_size[major]) {
- int minor = MINOR(dev);
+ int minor = minor(dev);
if (blksize_size[major][minor])
retval = blksize_size[major][minor];
}
sprintf (vname, "cdroms/cdrom%d", cdi->number);
cdi->de = devfs_register (NULL, vname, DEVFS_FL_DEFAULT,
- MAJOR (cdi->dev), MINOR (cdi->dev),
+ major(cdi->dev), minor(cdi->dev),
S_IFBLK | S_IRUGO | S_IWUGO,
ops, NULL);
}
#include <linux/udf_fs_i.h>
#include <linux/ncp_fs_i.h>
#include <linux/proc_fs_i.h>
-#include <linux/usbdev_fs_i.h>
#include <linux/jffs2_fs_i.h>
#include <linux/cramfs_fs_sb.h>
struct ncp_inode_info ncpfs_i;
struct proc_inode_info proc_i;
struct socket socket_i;
- struct usbdev_inode_info usbdev_i;
struct jffs2_inode_info jffs2_i;
void *generic_ip;
} u;
#include <linux/bfs_fs_sb.h>
#include <linux/udf_fs_sb.h>
#include <linux/ncp_fs_sb.h>
-#include <linux/usbdev_fs_sb.h>
#include <linux/cramfs_fs_sb.h>
#include <linux/jffs2_fs_sb.h>
struct bfs_sb_info bfs_sb;
struct udf_sb_info udf_sb;
struct ncp_sb_info ncpfs_sb;
- struct usbdev_sb_info usbdevfs_sb;
struct jffs2_sb_info jffs2_sb;
struct cramfs_sb_info cramfs_sb;
void *generic_sbp;
const char *major_name; /* name of major driver */
int minor_shift; /* number of times minor is shifted to
get real minor */
- int max_p; /* maximum partitions per device */
struct hd_struct *part; /* [indexed by minor] */
int *sizes; /* [idem], device size in blocks */
int nr_real; /* number of real devices */
- void *real_devices; /* internal use */
struct gendisk *next;
struct block_device_operations *fops;
static inline unsigned int disk_index (kdev_t dev)
{
struct gendisk *g = get_gendisk(dev);
- return g ? (MINOR(dev) >> g->minor_shift) : 0;
+ return g ? (minor(dev) >> g->minor_shift) : 0;
}
#endif
/*
* This is the multiple IDE interface driver, as evolved from hd.c.
- * It supports up to four IDE interfaces, on one or more IRQs (usually 14 & 15).
+ * It supports up to four IDE interfaces, on one or more IRQs (usually 14, 15).
* There can be up to two drives per interface, as per the ATA-2 spec.
*
- * Primary i/f: ide0: major=3; (hda) minor=0; (hdb) minor=64
- * Secondary i/f: ide1: major=22; (hdc or hd1a) minor=0; (hdd or hd1b) minor=64
- * Tertiary i/f: ide2: major=33; (hde) minor=0; (hdf) minor=64
- * Quaternary i/f: ide3: major=34; (hdg) minor=0; (hdh) minor=64
+ * Primary i/f: ide0: major=3; (hda) minor=0; (hdb) minor=64
+ * Secondary i/f: ide1: major=22; (hdc) minor=0; (hdd) minor=64
+ * Tertiary i/f: ide2: major=33; (hde) minor=0; (hdf) minor=64
+ * Quaternary i/f: ide3: major=34; (hdg) minor=0; (hdh) minor=64
*/
/******************************************************************************
aeb - 950811
*/
-/* Since MINOR(dev) is used as index in static arrays,
- the kernel is not quite ready yet for larger minors.
- However, everything runs fine with an arbitrary kdev_t type. */
+/*
+ * NOTE NOTE NOTE!
+ *
+ * The kernel-internal "kdev_t" will eventually have
+ * 20 bits for minor numbers, and 12 bits for majors.
+ *
+ * HOWEVER, the external representation is still 8+8
+ * bits, and there is no way to generate the extended
+ * "kdev_t" format yet. Which is just as well, since
+ * we still use "minor" as an index into various
+ * static arrays, and they are sized for a 8-bit index.
+ */
+typedef struct {
+ unsigned short value;
+} kdev_t;
+
+#define KDEV_MINOR_BITS 8
+#define KDEV_MAJOR_BITS 8
+
+#define __mkdev(major,minor) (((major) << KDEV_MINOR_BITS) + (minor))
+
+#define mk_kdev(major, minor) ((kdev_t) { __mkdev(major,minor) } )
+
+/*
+ * The "values" are just _cookies_, usable for
+ * internal equality comparisons and for things
+ * like NFS filehandle conversion.
+ */
+static inline unsigned int kdev_val(kdev_t dev)
+{
+ return dev.value;
+}
+
+static inline kdev_t val_to_kdev(unsigned int val)
+{
+ kdev_t dev;
+ dev.value = val;
+ return dev;
+}
+
+#define HASHDEV(dev) (kdev_val(dev))
+#define NODEV (mk_kdev(0,0))
+#define B_FREE (mk_kdev(0xff,0xff))
+
+extern const char * kdevname(kdev_t); /* note: returns pointer to static data! */
+
+static inline int kdev_same(kdev_t dev1, kdev_t dev2)
+{
+ return dev1.value == dev2.value;
+}
+
+#define kdev_none(d1) (!kdev_val(d1))
+
+/* Mask off the high bits for now.. */
+#define minor(dev) ((dev).value & 0xff)
+#define major(dev) (((dev).value >> KDEV_MINOR_BITS) & 0xff)
+
+/* These are for user-level "dev_t" */
#define MINORBITS 8
#define MINORMASK ((1U << MINORBITS) - 1)
-typedef unsigned short kdev_t;
-
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
-#define HASHDEV(dev) ((unsigned int) (dev))
-#define NODEV 0
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
-#define B_FREE 0xffff /* yuk */
-
-extern const char * kdevname(kdev_t); /* note: returns pointer to static data! */
/*
-As long as device numbers in the outside world have 16 bits only,
-we use these conversions.
-*/
+ * Conversion functions
+ */
-static inline unsigned int kdev_t_to_nr(kdev_t dev) {
- return (MAJOR(dev)<<8) | MINOR(dev);
+static inline int kdev_t_to_nr(kdev_t dev)
+{
+ return MKDEV(major(dev), minor(dev));
}
static inline kdev_t to_kdev_t(int dev)
{
- int major, minor;
-#if 0
- major = (dev >> 16);
- if (!major) {
- major = (dev >> 8);
- minor = (dev & 0xff);
- } else
- minor = (dev & 0xffff);
-#else
- major = (dev >> 8);
- minor = (dev & 0xff);
-#endif
- return MKDEV(major, minor);
+ return mk_kdev(MAJOR(dev),MINOR(dev));
}
#else /* __KERNEL__ || _LVM_H_INCLUDE */
/*
* Conversion macros for the filehandle fields.
+ *
+ * Keep the device numbers in "backwards compatible
+ * format", ie the low 16 bits contain the low 8 bits
+ * of the 20-bit minor and the 12-bit major number.
+ *
+ * The high 16 bits contain the rest (4 bits major
+ * and 12 bits minor),
*/
static inline __u32 kdev_t_to_u32(kdev_t dev)
{
- return (__u32) dev;
+ unsigned int minor = minor(dev);
+ unsigned int major = major(dev);
+ __u32 udev;
+
+ /* Create the low 16 bits.. */
+ udev = ((major & 0xff) << 8) + (minor & 0xff);
+
+ /* ..and then the rest. */
+ major >>= 8; minor >>= 8;
+ udev |= (major << 28) | (minor << 16);
+
+ return udev;
}
static inline kdev_t u32_to_kdev_t(__u32 udev)
{
- return (kdev_t) udev;
+ unsigned int minor, major;
+
+ minor = (udev & 0xff) | ((udev >> 8) & 0xfff00);
+ major = ((udev >> 8) & 0xff) | ((udev >> 20) & 0xf00);
+ return mk_kdev(major, minor);
}
static inline __u32 ino_t_to_u32(ino_t ino)
static inline mddev_t * kdev_to_mddev (kdev_t dev)
{
- if (MAJOR(dev) != MD_MAJOR)
+ if (major(dev) != MD_MAJOR)
BUG();
- return mddev_map[MINOR(dev)].mddev;
+ return mddev_map[minor(dev)].mddev;
}
/*
static inline kdev_t mddev_to_kdev(mddev_t * mddev)
{
- return MKDEV(MD_MAJOR, mdidx(mddev));
+ return mk_kdev(MD_MAJOR, mdidx(mddev));
}
extern mdk_rdev_t * find_rdev(mddev_t * mddev, kdev_t dev);
struct semaphore serialize;
- /* ioctl -- userspace apps can talk to drivers through usbdevfs */
+ /* ioctl -- userspace apps can talk to drivers through usbfs */
int (*ioctl)(struct usb_device *dev, unsigned int code, void *buf);
/* support for "new-style" USB hotplugging */
unsigned int length; /* expected length */
unsigned int actual_length;
unsigned int status;
-} iso_packet_descriptor_t, *piso_packet_descriptor_t;
+} iso_packet_descriptor_t;
struct urb;
*
* This structure identifies USB transfer requests. URBs may be allocated
* in any way, although usb_alloc_urb() is often convenient. Initialization
- * may be done using various FILL_*_URB() macros. URBs are submitted
+ * may be done using various usb_fill_*_urb() functions. URBs are submitted
* using usb_submit_urb(), and pending requests may be canceled using
* usb_unlink_urb().
*
iso_packet_descriptor_t iso_frame_desc[0]; /* (in) ISO ONLY */
};
-typedef struct urb urb_t, *purb_t;
-
-/**
- * FILL_CONTROL_URB - macro to help initialize a control urb
- * @URB: pointer to the urb to initialize.
- * @DEV: pointer to the struct usb_device for this urb.
- * @PIPE: the endpoint pipe
- * @SETUP_PACKET: pointer to the setup_packet buffer
- * @TRANSFER_BUFFER: pointer to the transfer buffer
- * @BUFFER_LENGTH: length of the transfer buffer
- * @COMPLETE: pointer to the usb_complete_t function
- * @CONTEXT: what to set the urb context to.
- *
- * Initializes a control urb with the proper information needed to submit
- * it to a device. This macro is depreciated, the usb_fill_control_urb()
- * function should be used instead.
- */
-#define FILL_CONTROL_URB(URB,DEV,PIPE,SETUP_PACKET,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT) \
- do {\
- spin_lock_init(&(URB)->lock);\
- (URB)->dev=DEV;\
- (URB)->pipe=PIPE;\
- (URB)->setup_packet=SETUP_PACKET;\
- (URB)->transfer_buffer=TRANSFER_BUFFER;\
- (URB)->transfer_buffer_length=BUFFER_LENGTH;\
- (URB)->complete=COMPLETE;\
- (URB)->context=CONTEXT;\
- } while (0)
-
-/**
- * FILL_BULK_URB - macro to help initialize a bulk urb
- * @URB: pointer to the urb to initialize.
- * @DEV: pointer to the struct usb_device for this urb.
- * @PIPE: the endpoint pipe
- * @TRANSFER_BUFFER: pointer to the transfer buffer
- * @BUFFER_LENGTH: length of the transfer buffer
- * @COMPLETE: pointer to the usb_complete_t function
- * @CONTEXT: what to set the urb context to.
- *
- * Initializes a bulk urb with the proper information needed to submit it
- * to a device. This macro is depreciated, the usb_fill_bulk_urb()
- * function should be used instead.
- */
-#define FILL_BULK_URB(URB,DEV,PIPE,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT) \
- do {\
- spin_lock_init(&(URB)->lock);\
- (URB)->dev=DEV;\
- (URB)->pipe=PIPE;\
- (URB)->transfer_buffer=TRANSFER_BUFFER;\
- (URB)->transfer_buffer_length=BUFFER_LENGTH;\
- (URB)->complete=COMPLETE;\
- (URB)->context=CONTEXT;\
- } while (0)
-
-/**
- * FILL_INT_URB - macro to help initialize a interrupt urb
- * @URB: pointer to the urb to initialize.
- * @DEV: pointer to the struct usb_device for this urb.
- * @PIPE: the endpoint pipe
- * @TRANSFER_BUFFER: pointer to the transfer buffer
- * @BUFFER_LENGTH: length of the transfer buffer
- * @COMPLETE: pointer to the usb_complete_t function
- * @CONTEXT: what to set the urb context to.
- * @INTERVAL: what to set the urb interval to.
- *
- * Initializes a interrupt urb with the proper information needed to submit
- * it to a device. This macro is depreciated, the usb_fill_int_urb()
- * function should be used instead.
- */
-#define FILL_INT_URB(URB,DEV,PIPE,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT,INTERVAL) \
- do {\
- spin_lock_init(&(URB)->lock);\
- (URB)->dev=DEV;\
- (URB)->pipe=PIPE;\
- (URB)->transfer_buffer=TRANSFER_BUFFER;\
- (URB)->transfer_buffer_length=BUFFER_LENGTH;\
- (URB)->complete=COMPLETE;\
- (URB)->context=CONTEXT;\
- (URB)->interval=INTERVAL;\
- (URB)->start_frame=-1;\
- } while (0)
+typedef struct urb urb_t;
/**
* usb_fill_control_urb - initializes a control urb
urb->interval = interval;
urb->start_frame = -1;
}
-
+
+/*
+ * old style macros to enable 2.4 and 2.2 drivers to build
+ * properly. Please do not use these for new USB drivers.
+ */
+#define FILL_CONTROL_URB(URB,DEV,PIPE,SETUP_PACKET,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT) \
+ usb_fill_control_urb(URB,DEV,PIPE,SETUP_PACKET,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT)
+#define FILL_BULK_URB(URB,DEV,PIPE,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT) \
+ usb_fill_bulk_urb(URB,DEV,PIPE,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT)
+#define FILL_INT_URB(URB,DEV,PIPE,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT,INTERVAL) \
+ usb_fill_int_urb(URB,DEV,PIPE,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT,INTERVAL)
+
extern struct urb *usb_alloc_urb(int iso_packets);
-extern void usb_free_urb(struct urb *purb);
-extern int usb_submit_urb(struct urb *purb);
-extern int usb_unlink_urb(struct urb *purb);
+extern void usb_free_urb(struct urb *urb);
+extern int usb_submit_urb(struct urb *urb);
+extern int usb_unlink_urb(struct urb *urb);
/*-------------------------------------------------------------------*
* SYNCHRONOUS CALL SUPPORT *
int (*allocate)(struct usb_device *);
int (*deallocate)(struct usb_device *);
int (*get_frame_number) (struct usb_device *usb_dev);
- int (*submit_urb) (struct urb* purb);
- int (*unlink_urb) (struct urb* purb);
+ int (*submit_urb) (struct urb *urb);
+ int (*unlink_urb) (struct urb *urb);
};
#define DEVNUM_ROUND_ROBIN /***** OPTION *****/
int bandwidth_int_reqs; /* number of Interrupt requesters */
int bandwidth_isoc_reqs; /* number of Isoc. requesters */
- /* usbdevfs inode list */
- struct list_head inodes;
+ struct dentry *dentry; /* usbfs dentry entry for the bus */
atomic_t refcnt;
};
#define NS_TO_US(ns) ((ns + 500L) / 1000L)
/* convert & round nanoseconds to microseconds */
+/*
+ * As of USB 2.0, full/low speed devices are segregated into trees.
+ * One type grows from USB 1.1 host controllers (OHCI, UHCI etc).
+ * The other type grows from high speed hubs when they connect to
+ * full/low speed devices using "Transaction Translators" (TTs).
+ *
+ * TTs should only be known to the hub driver, and high speed bus
+ * drivers (only EHCI for now). They affect periodic scheduling and
+ * sometimes control/bulk error recovery.
+ */
+struct usb_tt {
+ struct usb_device *hub; /* upstream highspeed hub */
+ int multi; /* true means one TT per port */
+};
+
/* -------------------------------------------------------------------------- */
#define USB_MAXCHILDREN (16)
struct usb_device {
- int devnum; /* Device number on USB bus */
+ int devnum; /* Address on USB bus */
+ char devpath [16]; /* Use in messages: /port/port/... */
enum {
USB_SPEED_UNKNOWN = 0, /* enumerating */
USB_SPEED_HIGH /* usb 2.0 */
} speed;
- struct usb_device *tt; /* usb1.1 device on usb2.0 bus */
- int ttport; /* device/hub port on that tt */
+ struct usb_tt *tt; /* low/full speed dev, highspeed hub */
+ int ttport; /* device port on that tt hub */
atomic_t refcnt; /* Reference count */
struct semaphore serialize;
void *hcpriv; /* Host Controller private data */
- /* usbdevfs inode list */
- struct list_head inodes;
struct list_head filelist;
+ struct dentry *dentry; /* usbfs dentry entry for the device */
/*
* Child devices - these can be either new devices
/*
* bus and driver list
- * exported only for usbdevfs (not visible outside usbcore)
+ * exported only for usbfs (not visible outside usbcore)
*/
extern struct list_head usb_driver_list;
* these are expected to be called from the USB core/hub thread
* with the kernel lock held
*/
-extern void usbdevfs_add_bus(struct usb_bus *bus);
-extern void usbdevfs_remove_bus(struct usb_bus *bus);
-extern void usbdevfs_add_device(struct usb_device *dev);
-extern void usbdevfs_remove_device(struct usb_device *dev);
+extern void usbfs_add_bus(struct usb_bus *bus);
+extern void usbfs_remove_bus(struct usb_bus *bus);
+extern void usbfs_add_device(struct usb_device *dev);
+extern void usbfs_remove_device(struct usb_device *dev);
+extern void usbfs_update_special (void);
-extern int usbdevfs_init(void);
-extern void usbdevfs_cleanup(void);
+extern int usbfs_init(void);
+extern void usbfs_cleanup(void);
#else /* CONFIG_USB_DEVICEFS */
-static inline void usbdevfs_add_bus(struct usb_bus *bus) {}
-static inline void usbdevfs_remove_bus(struct usb_bus *bus) {}
-static inline void usbdevfs_add_device(struct usb_device *dev) {}
-static inline void usbdevfs_remove_device(struct usb_device *dev) {}
+static inline void usbfs_add_bus(struct usb_bus *bus) {}
+static inline void usbfs_remove_bus(struct usb_bus *bus) {}
+static inline void usbfs_add_device(struct usb_device *dev) {}
+static inline void usbfs_remove_device(struct usb_device *dev) {}
+static inline void usbfs_update_special (void) {}
-static inline int usbdevfs_init(void) { return 0; }
-static inline void usbdevfs_cleanup(void) { }
+static inline int usbfs_init(void) { return 0; }
+static inline void usbfs_cleanup(void) { }
#endif /* CONFIG_USB_DEVICEFS */
#include <linux/list.h>
#include <asm/semaphore.h>
-/*
- * inode number macros
- */
-#define ITYPE(x) ((x)&(0xf<<28))
-#define ISPECIAL (0<<28)
-#define IBUS (1<<28)
-#define IDEVICE (2<<28)
-#define IBUSNR(x) (((x)>>8)&0xff)
-#define IDEVNR(x) ((x)&0xff)
-
-#define IROOT 1
struct dev_state {
struct list_head list; /* state list */
#define __KERNEL_SYSCALLS__
#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/unistd.h>
#include <linux/blk.h>
#include <linux/fd.h>
#include <linux/tty.h>
+#include <linux/init.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_fs_sb.h>
if (!do_devfs)
return sys_mknod(name, S_IFBLK|0600, kdev_t_to_nr(dev));
- handle = devfs_find_handle(NULL, dev ? NULL : devfs_name,
- MAJOR(dev), MINOR(dev), DEVFS_SPECIAL_BLK, 1);
+ handle = devfs_find_handle(NULL, kdev_none(dev) ? devfs_name : NULL,
+ major(dev), minor(dev), DEVFS_SPECIAL_BLK, 1);
if (!handle)
return -1;
n = devfs_generate_path(handle, path + 5, sizeof (path) - 5);
devfs_make_root(root_device_name);
create_dev("/dev/root", ROOT_DEV, root_device_name);
#ifdef CONFIG_BLK_DEV_FD
- if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {
+ if (major(ROOT_DEV) == FLOPPY_MAJOR) {
/* rd_doload is 2 for a dual initrd/ramload setup */
if (rd_doload==2) {
if (rd_load_disk(1)) {
- ROOT_DEV = MKDEV(RAMDISK_MAJOR, 1);
+ ROOT_DEV = mk_kdev(RAMDISK_MAJOR, 1);
create_dev("/dev/root", ROOT_DEV, NULL);
}
} else
*/
void prepare_namespace(void)
{
- int is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
+ int is_floppy = major(ROOT_DEV) == FLOPPY_MAJOR;
#ifdef CONFIG_BLK_DEV_INITRD
if (!initrd_start)
mount_initrd = 0;
create_dev("/dev/root", ROOT_DEV, NULL);
if (mount_initrd) {
- if (initrd_load() && ROOT_DEV != MKDEV(RAMDISK_MAJOR, 0)) {
+ if (initrd_load() && kdev_same(ROOT_DEV, mk_kdev(RAMDISK_MAJOR, 0))) {
handle_initrd();
goto out;
}
} else if (is_floppy && rd_doload && rd_load_disk(0))
- ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0);
+ ROOT_DEV = mk_kdev(RAMDISK_MAJOR, 0);
mount_root();
out:
sys_umount("/dev", 0);
* NOTE! The unix "nice" value influences how long a process
* gets. The nice value ranges from -20 to +19, where a -20
* is a "high-priority" task, and a "+10" is a low-priority
- * task.
- *
- * We want the time-slice to be around 50ms or so, so this
- * calculation depends on the value of HZ.
+ * task. The default time slice for zero-nice tasks will be 43ms.
*/
-#if HZ < 200
-#define TICK_SCALE(x) ((x) >> 2)
-#elif HZ < 400
-#define TICK_SCALE(x) ((x) >> 1)
-#elif HZ < 800
-#define TICK_SCALE(x) (x)
-#elif HZ < 1600
-#define TICK_SCALE(x) ((x) << 1)
-#else
-#define TICK_SCALE(x) ((x) << 2)
-#endif
+#define NICE_RANGE 40
+#define MIN_NICE_TSLICE 10000
+#define MAX_NICE_TSLICE 80000
+#define TASK_TIMESLICE(p) ((int) ts_table[19 - (p)->nice])
-#define TASK_TIMESLICE(p) (TICK_SCALE(20-(p)->nice)+1)
+static unsigned char ts_table[NICE_RANGE];
+#define MM_AFFINITY_BONUS 1
/*
* Init task must be ok at boot for the ix86 as we will check its signals
/* .. and a slight advantage to the current MM */
if (p->mm == this_mm || !p->mm)
- weight += 1;
+ weight += MM_AFFINITY_BONUS;
weight += 20 - p->nice;
goto out;
}
extern void init_timervecs (void);
+static void fill_tslice_map(void)
+{
+ int i;
+
+ for (i = 0; i < NICE_RANGE; i++)
+ ts_table[i] = ((MIN_NICE_TSLICE +
+ ((MAX_NICE_TSLICE - MIN_NICE_TSLICE) / NICE_RANGE) * i) * HZ) / 1000000;
+}
+
void __init sched_init(void)
{
/*
for(nr = 0; nr < PIDHASH_SZ; nr++)
pidhash[nr] = NULL;
+ fill_tslice_map();
+
init_timervecs();
init_bh(TIMER_BH, timer_bh);
static inline int get_max_readahead(struct inode * inode)
{
- if (!inode->i_dev || !max_readahead[MAJOR(inode->i_dev)])
+ if (kdev_none(inode->i_dev) || !max_readahead[major(inode->i_dev)])
return MAX_READAHEAD;
- return max_readahead[MAJOR(inode->i_dev)][MINOR(inode->i_dev)];
+ return max_readahead[major(inode->i_dev)][minor(inode->i_dev)];
}
static void generic_file_readahead(int reada_ok,
unsigned long offset;
sector_t zones[PAGE_SIZE/512];
int zones_used;
- kdev_t dev = 0;
+ kdev_t dev = NODEV;
int block_size;
struct inode *swapf = 0;
kstat.pswpout++;
get_swaphandle_info(entry, &offset, &dev, &swapf);
- if (dev) {
+ if (!kdev_none(dev)) {
zones[0] = offset;
zones_used = 1;
block_size = PAGE_SIZE;
swap_list_unlock();
goto out_dput;
}
- if (p->swap_device)
+ if (!kdev_none(p->swap_device))
blkdev_put(p->swap_file->d_inode->i_bdev, BDEV_SWAP);
path_release(&nd);
nd.dentry = p->swap_file;
p->swap_vfsmnt = NULL;
p->swap_file = NULL;
- p->swap_device = 0;
+ p->swap_device = NODEV;
p->max = 0;
swap_map = p->swap_map;
p->swap_map = NULL;
}
len += sprintf(buf + len, "%-39s %s\t%d\t%d\t%d\n",
path,
- ptr->swap_device ? "partition" : "file\t",
+ kdev_none(ptr->swap_device) ? "file\t" : "partition",
ptr->pages << (PAGE_SHIFT - 10),
usedswap << (PAGE_SHIFT - 10),
ptr->prio);
for (i = 0 ; i < nr_swapfiles ; i++, ptr++) {
if (ptr->flags & SWP_USED)
- if (ptr->swap_device == dev)
+ if (kdev_same(ptr->swap_device, dev))
return 1;
}
return 0;
p->flags = SWP_USED;
p->swap_file = NULL;
p->swap_vfsmnt = NULL;
- p->swap_device = 0;
+ p->swap_device = NODEV;
p->swap_map = NULL;
p->lowest_bit = 0;
p->highest_bit = 0;
goto bad_swap_2;
set_blocksize(dev, PAGE_SIZE);
error = -ENODEV;
- if (!dev || (blk_size[MAJOR(dev)] &&
- !blk_size[MAJOR(dev)][MINOR(dev)]))
+ if (kdev_none(dev) || (blk_size[major(dev)] &&
+ !blk_size[major(dev)][minor(dev)]))
goto bad_swap;
swapfilesize = 0;
- if (blk_size[MAJOR(dev)])
- swapfilesize = blk_size[MAJOR(dev)][MINOR(dev)]
+ if (blk_size[major(dev)])
+ swapfilesize = blk_size[major(dev)][minor(dev)]
>> (PAGE_SHIFT - 10);
} else if (S_ISREG(swap_inode->i_mode))
swapfilesize = swap_inode->i_size >> PAGE_SHIFT;
swap_map = p->swap_map;
nd.mnt = p->swap_vfsmnt;
nd.dentry = p->swap_file;
- p->swap_device = 0;
+ p->swap_device = NODEV;
p->swap_file = NULL;
p->swap_vfsmnt = NULL;
p->swap_map = NULL;
return;
}
- if (p->swap_device) {
+ if (!kdev_none(p->swap_device)) {
*dev = p->swap_device;
} else if (p->swap_file) {
*swapf = p->swap_file->d_inode;