//------------------------------------------------------------------- // buffalo.c // // This character-mode device-driver lets users and application // programs view the memory-mapped register-space for a Buffalo // USB 3.0 eXtensible Host Controller (xHCI) adapter-card. Its // '/dev/xhci' device-file can be read by utility programs such // as our 'fileview' application, while its '/proc/xhci' pseudo // file shows programming interface registers of key relevance, // and '/proc/pci' shows its PCI Configuration Space registers. // // NOTE: Written and tested with Linux kernel version 2.6.33.3. // // programmer: ALLAN CRUSE // written on: 22 SEP 2010 // revised on: 30 SEP 2010 -- to show port-numbering as 1-based //------------------------------------------------------------------- #include // for init_module() #include // for create_proc_read_entry() #include // for pci_get_device() #include // for copy_to_user() #define VENDOR_ID 0x1033 // Renesas Electronics (aka NEC) #define DEVICE_ID 0x0194 // Buffalo USB 3.0 Adapter Card char modname[] = "buffalo"; char devname[] = "xhci"; char pciname[] = "pci"; unsigned int my_major = 76; struct pci_dev *devp = NULL; unsigned long mmbase; unsigned long mmsize; void *mmio; char legend[] = "PCI Configuration Space for Buffalo USB 3.0 Host Controller"; int my_pci_info( char *buf, char **start, off_t off, int count, int *eof, void *data ) { int i, len = 0; len += sprintf( buf+len, "\n\n %s\n", legend ); for (i = 0; i < 0x100; i+=4) { u32 datum; pci_read_config_dword( devp, i, &datum ); if ( (i % 32) == 0 ) len += sprintf( buf+len, "\n 0x%02X:", i ); len += sprintf( buf+len, " %08X", datum ); } len += sprintf( buf+len, "\n\n\n" ); return len; } loff_t my_llseek( struct file *file, loff_t where, int whence ) { loff_t newpos = -1; switch ( whence ) { case 0: newpos = where; break; // SEEK_SET case 1: newpos = file->f_pos + where; break; // SEET_CUR case 2: newpos = mmsize + where; break; // SEEK_END } if (( newpos < 0 )||( newpos > mmsize )) return -EINVAL; file->f_pos = newpos; return newpos; } ssize_t my_read( struct file *file, char *buf, size_t len, loff_t *pos ) { unsigned int fpos = (unsigned int)(*pos); int nbytes = 0; int regval; // we cannot return fewer than 4 bytes (unless at end-of-file) if ( len < 4 ) return -EINVAL; // we cannot read from a misaligned address if ( ( fpos % 4 ) != 0 ) return -EINVAL; // we cannot read anything past the end of the i/o memory if ( fpos > mmsize ) return 0; // ok, transfer the integer at the current file-position regval = ioread32( mmio + fpos ); if ( copy_to_user( buf, ®val, 4 ) ) return -EFAULT; // advance file-pointer and notify kernel of bytes transferred nbytes += 4; *pos += nbytes; return nbytes; } struct file_operations my_fops = { owner: THIS_MODULE, llseek: my_llseek, read: my_read, }; int my_dev_info( char *buf, char **start, off_t off, int count, int *eof, void *data ) { int hcsparams1 = ioread32( mmio + 0x04 ); int n_ports = (hcsparams1 >> 24)&0x0FF; int n_intrs = (hcsparams1 >> 8)&0x3FF; int caplen = ioread32( mmio + 0x00 )&0xFF; int rtsoff = ioread32( mmio + 0x18 ); void *mmop = mmio + caplen; void *mmrt = mmio + rtsoff; int i, len = 0; len += sprintf( buf+len, "\n capability registers: \n" ); len += sprintf( buf+len, " CAPLENGTH=%02X ", ioread8( mmio + 0x00 ) ); len += sprintf( buf+len, " HCSPARAMS1=%08X ", ioread32( mmio + 0x04 ) ); len += sprintf( buf+len, " HCSPARAMS2=%08X ", ioread32( mmio + 0x08 ) ); len += sprintf( buf+len, " HCSPARAMS3=%08X ", ioread32( mmio + 0x0C ) ); len += sprintf( buf+len, "\n" ); len += sprintf( buf+len, " HCCPARAMS=%08X ", ioread32( mmio + 0x10 ) ); len += sprintf( buf+len, " DBOFF=%08X ", ioread32( mmio + 0x14 ) ); len += sprintf( buf+len, " RTSOFF=%08X ", ioread32( mmio + 0x18 ) ); len += sprintf( buf+len, " HCIVERSION=%04X ", ioread16( mmio + 0x02 ) ); len += sprintf( buf+len, "\n\n operational registers: \n" ); len += sprintf( buf+len, " USBCMD=%08X ", ioread32( mmop + 0x00 ) ); len += sprintf( buf+len, " USBSTS=%08X ", ioread32( mmop + 0x04 ) ); len += sprintf( buf+len, " PAGESIZE=%04X ", ioread16( mmop + 0x08 ) ); len += sprintf( buf+len, " DNCTRL=%08X ", ioread32( mmop + 0x14 ) ); len += sprintf( buf+len, "\n" ); len += sprintf( buf+len, " CRCR=%08X%08X ", ioread32( mmop + 0x1C ), ioread32( mmop + 0x18 ) ); len += sprintf( buf+len, " DCBAAP=%08X%08X ", ioread32( mmop + 0x34 ), ioread32( mmop + 0x30 ) ); len += sprintf( buf+len, " CONFIG=0x%X ", ioread32( mmop + 0x38 ) ); len += sprintf( buf+len, "\n" ); for (i = 0; i < n_ports; i++) { unsigned int portsc = ioread32( mmop + 0x400 + i*16 ); len += sprintf( buf+len, " PORTSC%X=%08X ", i+1, portsc ); len += sprintf( buf+len, " speed=%X ", (portsc >> 10)&0xF ); len += sprintf( buf+len, " powered=%X ", (portsc >> 9)&0x1 ); len += sprintf( buf+len, " state=%X ", (portsc >> 5)&0xF ); len += sprintf( buf+len, " enabled=%X ", (portsc >> 1)&0x1 ); len += sprintf( buf+len, " connected=%X ", (portsc >> 0)&0x1 ); len += sprintf( buf+len, "\n" ); } len += sprintf( buf+len, "\n runtime registers: \n" ); len += sprintf( buf+len, " MFINDEX=%04X", ioread32( mmrt + 0x00 ) ); len += sprintf( buf+len, " IMOD " ); len += sprintf( buf+len, " IMAN " ); len += sprintf( buf+len, " ERSTSZ " ); len += sprintf( buf+len, " ERSTBA " ); len += sprintf( buf+len, " ERDP " ); len += sprintf( buf+len, "\n" ); for (i = 0; i < n_intrs; i++) { unsigned int *rt = mmrt + 32*(i + 1); len += sprintf( buf+len, " intr #%X ", i ); len += sprintf( buf+len, " %08X ", rt[0] ); len += sprintf( buf+len, " %08X ", rt[1] ); len += sprintf( buf+len, " %08X ", rt[2] ); len += sprintf( buf+len, " %08X%08X ", rt[5], rt[4] ); len += sprintf( buf+len, " %08X%08X ", rt[7], rt[6] ); len += sprintf( buf+len, "\n" ); } return len; } static int __init my_init( void ) { printk( "<1>\nInstalling \'%s\' module\n", modname ); devp = pci_get_device( VENDOR_ID, DEVICE_ID, devp ); if ( !devp ) return -ENODEV; printk( " Found Buffalo USB 3.0 Host Controller Adapter \n" ); mmbase = pci_resource_start( devp, 0); mmsize = pci_resource_len( devp, 0); printk( " mmbase=%08lX ", mmbase ); printk( " mmsize=%08lX ", mmsize ); printk( "\n" ); mmio = ioremap_nocache( mmbase, mmsize ); if ( !mmio ) return -ENOSPC; create_proc_read_entry( pciname, 0, NULL, my_pci_info, NULL ); create_proc_read_entry( devname, 0, NULL, my_dev_info, NULL ); return register_chrdev( my_major, devname, &my_fops ); } static void __exit my_exit(void ) { unregister_chrdev( my_major, devname ); remove_proc_entry( devname, NULL ); remove_proc_entry( pciname, NULL ); iounmap( mmio ); printk( "<1>Removing \'%s\' module\n", modname ); } module_init( my_init ); module_exit( my_exit ); MODULE_LICENSE("GPL");