Treiberprogrammierung - USB Mass Storage -> wie SCSI Commands absetzen?



  • Hallo zusammen,

    Ich arbeite gerade an einem USB Mass Storage Treiber im Rahmen eines Uni-Projektes. Den ersten Versuch an einer Tastatur herumzufummeln ist fehlgeschlagen, daher nun ein zweiter in diesem Bereich. Mit Hilfe von viel Doku / Bücher / Specs lesen ist es mir gelungen einen Treiber zu bauen, der ein paar Informationen über ein Mass Storage Gerät (USB-Stick) herausgibt. Er kann sich am USB Subsystem anmelden und Informationen über USB-Class, -SubClass und Transportprotokoll ausgeben.

    Dadurch habe ich herausgefunden, dass ich, um Daten vom Stick lesen bzw. schreiben zu können, SCSI-Befehle absetzen muss. Gut, SCSI Specs im Internet gefunden, nur... wie setze ich diese ab?

    Muss ich dazu ein URB mit SCSI-Commands absetzen? Wird das SCSI Command über einen der beiden Endpoints verschickt? Wie genau sieht das Abschicken eines SCSI Commands aus?

    Code ist folgender:

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/usb.h>
    
    /* declaration of helper_functions */
    /* helper_functions for parsing usb classes */
    static char * getInterfaceClassFromClassCode(__u8 bInterfaceClassCode);
    static char * getInterfaceSubClassFromClassCode(__u8 bInterfaceSubClassCode);
    static char * getInterfaceProtocolFromClassCode(__u8 bInterfaceProtocolClassCode);
    
    static struct usb_device *device;
    
    static int stick_probe(struct usb_interface *interface, const struct usb_device_id *id)
    {
    	/* what happens, when stick is plugged in and no driver module is already loaded */
    
    	struct usb_host_interface *iface_desc;
    	struct usb_endpoint_descriptor *endpoint;
    	int i;
    
    	iface_desc = interface->cur_altsetting;
    	printk(KERN_INFO "Stick i/f %d now probed: (%04X:%04X)\n", iface_desc->desc.bInterfaceNumber, id->idVendor, id->idProduct);
    	printk(KERN_INFO "Information about used Interface Descriptor\n");
    	printk(KERN_INFO "--------------------------------------");
    	printk(KERN_INFO "ID->bLength: %02X\n", iface_desc->desc.bLength);
    	printk(KERN_INFO "ID->bDescriptorType: %02X\n", iface_desc->desc.bDescriptorType);
    	// printk(KERN_INFO "ID->bInterfaceClass: %02X\n", iface_desc->desc.bInterfaceClass);
    	printk(KERN_INFO "ID->bInterfaceClass: %s\n", getInterfaceClassFromClassCode(iface_desc->desc.bInterfaceClass));
    	// printk(KERN_INFO "ID->bInterfaceSubClass: %02X\n", iface_desc->desc.bInterfaceSubClass);
    	printk(KERN_INFO "ID->bInterfaceSubClass: %s\n", getInterfaceSubClassFromClassCode(iface_desc->desc.bInterfaceSubClass));
    	// printk(KERN_INFO "ID->bInterfaceProtocol: %02X\n", iface_desc->desc.bInterfaceProtocol);
    	printk(KERN_INFO "ID->bInterfaceProtocol: %s\n", getInterfaceProtocolFromClassCode(iface_desc->desc.bInterfaceProtocol));
    	printk(KERN_INFO "ID->bNumEndpoints: %02X\n", iface_desc->desc.bNumEndpoints);
    
    	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++)
    	{
    		endpoint = &iface_desc->endpoint[i].desc;
            printk(KERN_INFO "Information about used Endpoint Descriptor\n");
            printk(KERN_INFO "--------------------------------------");
    		printk(KERN_INFO "ED[%d]->bEndpointAddress: 0x%02X\n", i, endpoint->bEndpointAddress);
    		printk(KERN_INFO "ED[%d]->bmAttributes: 0x%02X\n", i, endpoint->bmAttributes);
    		printk(KERN_INFO "ED[%d]->wMaxPacketSize: 0x%04X (%d)\n", i, endpoint->wMaxPacketSize, endpoint->wMaxPacketSize);
    	}
    
    	device = interface_to_usbdev(interface);
    	return 0;
    }
    
    static void stick_disconnect(struct usb_interface *interface)
    {
    	printk(KERN_INFO "Stick i/f %d now disconnected\n", interface->cur_altsetting->desc.bInterfaceNumber);
    }
    
    static struct usb_device_id stick_table[] =
    {
    	{ USB_DEVICE(0x0204, 0x6025) },
    	{}
    	/* Information about the stick plugged (vendor/product) id */
    };
    MODULE_DEVICE_TABLE (usb, stick_table);
    
    static struct usb_driver stick_driver =
    {
    	/* Information about the usb driver (which function is called by probe dissconect, ...) */
    	.name = "stick_driver",
    	.id_table = stick_table,
    	.probe = stick_probe,
    	.disconnect = stick_disconnect,
    };
    
    static int __init stick_init(void)
    {
    	return usb_register(&stick_driver);
    }
    
    static void __exit stick_exit(void)
    {
    	usb_deregister(&stick_driver);
    }
    
    module_init(stick_init);
    module_exit(stick_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Name");
    MODULE_DESCRIPTION("USB-Stick Information Driver");
    
    /* definition of helperfunctions */
    /* Helper Functions for parsing information from usb descriptors */
    
    static char * getInterfaceClassFromClassCode(__u8 bInterfaceClassCode)
    {
        switch(bInterfaceClassCode)
        {
            case 1:
                return "Audio";
            break;
    
            case 3:
                return "Human Interface Device";
            break;
    
            case 7:
                return "Printer";
            break;
    
            case 8:
                return "Mass Storage";
            break;
    
            case 9:
                return "Hub";
            break;
    
            case 11:
                return "Smard Card";
            break;
    
            default:
                return "Other. Look @ USB specifiction.";
            break;
        }
    
    }
    
    static char * getInterfaceSubClassFromClassCode(__u8 bInterfaceSubClassCode)
    {
        switch(bInterfaceSubClassCode)
        {
            case 0:
                return "SCSI command set not reported";
            break;
            case 1:
                return "RBC";
            break;
            case 2:
                return "MMC-5 (ATAPI)";
            break;
            case 3:
                return "obsolete QIC-157";
            break;
            case 4:
                return "UFI";
            break;
            case 5:
                return "obsolete SFF-8070i";
            break;
            case 6:
                return "SCSI transparent command set";
            break;
            case 7:
                return "LSD FS";
            break;
            case 8:
                return "IEEE 1667";
            break;
            default:
                return "Reserved";
            break;
        }
    
    }
    
    static char * getInterfaceProtocolFromClassCode(__u8 bInterfaceProtocolClassCode)
    {
        switch (bInterfaceProtocolClassCode)
        {
            case 0:
                return "CBI (with command completion interrupt)";
            break;
            case 1:
                return "CBI (with no command completion interrupt)";
            break;
            case 2:
                return "obsolete (not described Protocol)";
            break;
            case 80:
                return "BBB (Bulk-Only Transport)";
            break;
            case 98:
                return "UAS";
            break;
            default:
                return "Reserved";
            break;
        }
    }
    

    EDIT:
    Der Output:

    Feb  6 16:31:32 uj-geraetetreiber kernel: [12676.980957] usbcore: registered new interface driver stick_driver
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12689.516062] usb 1-2: new full-speed USB device number 8 using ohci_hcd
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.007841] usb 1-2: New USB device found, idVendor=0204, idProduct=6025
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.007843] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.007845] usb 1-2: Product: Flash Disk
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.007845] usb 1-2: Manufacturer: CBM
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.007846] usb 1-2: SerialNumber: 0917270091467D09
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.014100] Stick i/f 0 now probed: (0204:6025)
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.014102] Information about used Interface Descriptor
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.014102] --------------------------------------
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.014103] ID->bLength: 09
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.014104] ID->bDescriptorType: 04
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.014105] ID->bInterfaceClass: Mass Storage
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.014106] ID->bInterfaceSubClass: SCSI transparent command set
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.014107] ID->bInterfaceProtocol: BBB (Bulk-Only Transport)
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.014108] ID->bNumEndpoints: 02
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.014109] Information about used Endpoint Descriptor
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.014109] --------------------------------------
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.014110] ED[0]->bEndpointAddress: 0x01
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.014111] ED[0]->bmAttributes: 0x02
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.014112] ED[0]->wMaxPacketSize: 0x0200 (512)
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.014113] Information about used Endpoint Descriptor
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.014113] --------------------------------------
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.014114] ED[1]->bEndpointAddress: 0x81
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.014115] ED[1]->bmAttributes: 0x02
    Feb  6 16:31:45 uj-geraetetreiber kernel: [12690.014116] ED[1]->wMaxPacketSize: 0x0200 (512)
    Feb  6 16:30:51 uj-geraetetreiber kernel: [12635.062467] usb 1-2: USB disconnect, device number 7
    Feb  6 16:30:51 uj-geraetetreiber kernel: [12635.062497] Stick i/f 0 now disconnected
    Feb  6 16:31:05 uj-geraetetreiber kernel: [12649.058547] usbcore: deregistering interface driver stick_driver
    


  • So, der nächste Schritt ist getan,

    Der Treiber ist nun fähig das Gerät im sysfs anzulegen und lesend/schreibend auf ihn zuzugreifen (theoretisch).

    #include <linux/slab.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/usb.h>
    
    /* use lsusb -v -d <vendorID>:<productID> to get output Details or look at USB spec */
    #define USB_EP_IN 0x81
    #define USB_EP_OUT 0x01
    
    /* declaration of helper_functions */
    /* helper_functions for parsing usb classes */
    static char * getInterfaceClassFromClassCode(__u8 bInterfaceClassCode);
    static char * getInterfaceSubClassFromClassCode(__u8 bInterfaceSubClassCode);
    static char * getInterfaceProtocolFromClassCode(__u8 bInterfaceProtocolClassCode);
    
    static struct usb_device *device;
    static struct usb_class_driver class;
    
    static struct urb *urb_in;
    
    static char *ctx = "[Data Blob]";
    
    static void printComplete(struct urb *urb, struct pt_regs *regs)
    {
        printk(KERN_INFO "Completion Handler: URB-Status=%d", urb->status);
        switch(urb->status)
        {
            case 0:
                printk(KERN_INFO "Completion Handler: USB Request Block (URB) successfully transmitted\n");
            break;
    
            case -ESHUTDOWN:
                printk(KERN_INFO "Completion Handler: The USB device is now gone from the system\n");
            break;
        }
    
    }
    
    static int stick_open(struct inode *i, struct file *f)
    {
        return 0;
    }
    
    static int stick_close(struct inode *i, struct file *f)
    {
        return 0;
    }
    
    static ssize_t stick_read(struct file *f, char __user *buf, size_t cnt, loff_t *off)
    {
        urb_in = usb_alloc_urb(0, GFP_KERNEL);
        if (urb_in == NULL)
        {
            printk(KERN_INFO "Create USB Request Block (URB)... Failed\n");
        }
        else
        {
            printk(KERN_INFO "Create USB Request Block (URB)... Success\n");
        }
        printk(KERN_INFO "--------------------------------------\n");
    
        size_t buffer_size = 0;
        char *buffer = kmalloc(sizeof(size_t), GFP_KERNEL);// = usb_buffer_alloc(device, buffer_size, GFP_KERNEL, &urb_in->transfer_dma);
        printk(KERN_INFO "Buffer Content: %s", buffer);
        usb_fill_bulk_urb(urb_in, device, usb_rcvbulkpipe(device, USB_EP_IN), buffer, buffer_size, (usb_complete_t)(printComplete), ctx);
        int val = usb_submit_urb(urb_in, GFP_KERNEL);
        if (val == 0)
        {
            printk(KERN_INFO "Queued USB Request Block (URB)... Success\n");
            printk(KERN_INFO "Buffer Content: %s", buffer);
            printk(KERN_INFO "--------------------------------------\n");
        }
        else
        {
            printk(KERN_INFO "Queued USB Request Block (URB)... Failed\n");
        }
    
        printk(KERN_INFO "Delete USB Request Block (URB)\n");
        printk(KERN_INFO "--------------------------------------\n");
        usb_free_urb(urb_in);
    
        return 0;
    }
    
    static ssize_t stick_write(struct file *f, const char __user *buf, size_t cnt, loff_t *off)
    {
        return 0;
    }
    
    static struct file_operations fops =
    {
        .open = stick_open,
        .release = stick_close,
        .read = stick_read,
        .write = stick_write,
    };
    
    static int stick_probe(struct usb_interface *interface, const struct usb_device_id *id)
    {
    	/* what happens, when stick is plugged in and no driver module is already loaded */
    
        /* Information about the stick */
    	struct usb_host_interface *iface_desc;
    	struct usb_endpoint_descriptor *endpoint;
    	int i;
    
    	iface_desc = interface->cur_altsetting;
    	printk(KERN_INFO "--------------------------------------");
    	printk(KERN_INFO "Stick i/f %d now probed: (%04X:%04X)\n", iface_desc->desc.bInterfaceNumber, id->idVendor, id->idProduct);
    	printk(KERN_INFO "Information about used Interface Descriptor\n");
    	printk(KERN_INFO "ID->bLength: %02X\n", iface_desc->desc.bLength);
    	printk(KERN_INFO "ID->bDescriptorType: %02X\n", iface_desc->desc.bDescriptorType);
    	// printk(KERN_INFO "ID->bInterfaceClass: %02X\n", iface_desc->desc.bInterfaceClass);
    	printk(KERN_INFO "ID->bInterfaceClass: %s\n", getInterfaceClassFromClassCode(iface_desc->desc.bInterfaceClass));
    	// printk(KERN_INFO "ID->bInterfaceSubClass: %02X\n", iface_desc->desc.bInterfaceSubClass);
    	printk(KERN_INFO "ID->bInterfaceSubClass: %s\n", getInterfaceSubClassFromClassCode(iface_desc->desc.bInterfaceSubClass));
    	// printk(KERN_INFO "ID->bInterfaceProtocol: %02X\n", iface_desc->desc.bInterfaceProtocol);
    	printk(KERN_INFO "ID->bInterfaceProtocol: %s\n", getInterfaceProtocolFromClassCode(iface_desc->desc.bInterfaceProtocol));
    	printk(KERN_INFO "ID->bNumEndpoints: %02X\n", iface_desc->desc.bNumEndpoints);
        printk(KERN_INFO "--------------------------------------\n");
    
    	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++)
    	{
    		endpoint = &iface_desc->endpoint[i].desc;
            printk(KERN_INFO "Information about used Endpoint Descriptor\n");
    		printk(KERN_INFO "ED[%d]->bEndpointAddress: 0x%02X\n", i, endpoint->bEndpointAddress);
    		printk(KERN_INFO "ED[%d]->bmAttributes: 0x%02X\n", i, endpoint->bmAttributes);
    		printk(KERN_INFO "ED[%d]->wMaxPacketSize: 0x%04X (%d)\n", i, endpoint->wMaxPacketSize, endpoint->wMaxPacketSize);
    		printk(KERN_INFO "--------------------------------------\n");
    	}
    
    	/* Get device */
        device = interface_to_usbdev(interface);
    
        /* Create device in /dev */
        int retval;
    
        class.name = "usb/stick%d";
        class.fops = &fops;
        if ((retval = usb_register_dev(interface, &class)) < 0)
        {
            err("Not able to get a minor number for this device");
        }
        else
        {
            printk(KERN_INFO "Create /dev/stick%d\n", interface->minor);
            printk(KERN_INFO "--------------------------------------\n");
        }
    
    	return retval;
    }
    
    static void stick_disconnect(struct usb_interface *interface)
    {
    	printk(KERN_INFO "Stick i/f %d now disconnected\n", interface->cur_altsetting->desc.bInterfaceNumber);
        printk(KERN_INFO "Remove /dev/stick%d\n", interface->minor);
        printk(KERN_INFO "--------------------------------------\n");
    
    	/* removes the usb class device from sysfs tree */
    	usb_deregister_dev(interface, &class);
    }
    
    static struct usb_device_id stick_table[] =
    {
    	{ USB_DEVICE(0x0204, 0x6025) },
    	{}
    	/* Information about the stick plugged (vendor/product) id */
    };
    MODULE_DEVICE_TABLE (usb, stick_table);
    
    static struct usb_driver stick_driver =
    {
    	/* Information about the usb driver (which function is called by probe dissconect, ...) */
    	.name = "stick_driver",
    	.id_table = stick_table,
    	.probe = stick_probe,
    	.disconnect = stick_disconnect,
    };
    
    static int __init stick_init(void)
    {
    	return usb_register(&stick_driver);
    }
    
    static void __exit stick_exit(void)
    {
    	usb_deregister(&stick_driver);
    }
    
    module_init(stick_init);
    module_exit(stick_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Toni Bolduan");
    MODULE_DESCRIPTION("USB-Stick Information Driver");
    
    /* definition of helperfunctions */
    /* Helper Functions for parsing information from usb descriptors */
    
    static char * getInterfaceClassFromClassCode(__u8 bInterfaceClassCode)
    {
        switch(bInterfaceClassCode)
        {
            case 1:
                return "Audio";
            break;
    
            case 3:
                return "Human Interface Device";
            break;
    
            case 7:
                return "Printer";
            break;
    
            case 8:
                return "Mass Storage";
            break;
    
            case 9:
                return "Hub";
            break;
    
            case 11:
                return "Smard Card";
            break;
    
            default:
                return "Other. Look @ USB specifiction.";
            break;
        }
    
    }
    
    static char * getInterfaceSubClassFromClassCode(__u8 bInterfaceSubClassCode)
    {
        switch(bInterfaceSubClassCode)
        {
            case 0:
                return "SCSI command set not reported";
            break;
            case 1:
                return "RBC";
            break;
            case 2:
                return "MMC-5 (ATAPI)";
            break;
            case 3:
                return "obsolete QIC-157";
            break;
            case 4:
                return "UFI";
            break;
            case 5:
                return "obsolete SFF-8070i";
            break;
            case 6:
                return "SCSI transparent command set";
            break;
            case 7:
                return "LSD FS";
            break;
            case 8:
                return "IEEE 1667";
            break;
            default:
                return "Reserved";
            break;
        }
    
    }
    
    static char * getInterfaceProtocolFromClassCode(__u8 bInterfaceProtocolClassCode)
    {
        switch (bInterfaceProtocolClassCode)
        {
            case 0:
                return "CBI (with command completion interrupt)";
            break;
            case 1:
                return "CBI (with no command completion interrupt)";
            break;
            case 2:
                return "obsolete (not described Protocol)";
            break;
            case 80:
                return "BBB (Bulk-Only Transport)";
            break;
            case 98:
                return "UAS";
            break;
            default:
                return "Reserved";
            break;
        }
    }
    

    Beim Anstecken des USB-Sticks bzw. Ausführen der probe() Funktion.

    Feb 10 09:26:22 uj-geraetetreiber kernel: [90616.874257] usbcore: deregistering interface driver stick_driver
    Feb 10 09:27:40 uj-geraetetreiber kernel: [90694.608098] usbcore: registered new interface driver stick_driver
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.189928] usb 1-2: new full-speed USB device number 34 using ohci_hcd
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.398887] usb 1-2: New USB device found, idVendor=0204, idProduct=6025
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.398889] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.398891] usb 1-2: Product: Flash Disk
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.398892] usb 1-2: Manufacturer: CBM
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.398893] usb 1-2: SerialNumber: 0917270091467D09
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404117] --------------------------------------
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404119] Stick i/f 0 now probed: (0204:6025)
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404120] Information about used Interface Descriptor
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404121] ID->bLength: 09
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404121] ID->bDescriptorType: 04
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404122] ID->bInterfaceClass: Mass Storage
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404123] ID->bInterfaceSubClass: SCSI transparent command set
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404124] ID->bInterfaceProtocol: BBB (Bulk-Only Transport)
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404125] ID->bNumEndpoints: 02
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404126] --------------------------------------
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404126] Information about used Endpoint Descriptor
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404127] ED[0]->bEndpointAddress: 0x01
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404128] ED[0]->bmAttributes: 0x02
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404129] ED[0]->wMaxPacketSize: 0x0200 (512)
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404129] --------------------------------------
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404130] Information about used Endpoint Descriptor
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404131] ED[1]->bEndpointAddress: 0x81
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404132] ED[1]->bmAttributes: 0x02
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404133] ED[1]->wMaxPacketSize: 0x0200 (512)
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404133] --------------------------------------
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404207] Create /dev/stick2
    Feb 10 09:28:04 uj-geraetetreiber kernel: [90719.404208] --------------------------------------
    

    Beim Lesen des Gerätes (z.B.: mit cat /dev/stick2):

    Feb 10 09:33:10 uj-geraetetreiber kernel: [91025.391719] Create USB Request Block (URB)... Success
    Feb 10 09:33:10 uj-geraetetreiber kernel: [91025.391721] --------------------------------------
    Feb 10 09:33:10 uj-geraetetreiber kernel: [91025.391722] Buffer Content: /lib64/ld-linux-x86-64.so.2
    Feb 10 09:33:10 uj-geraetetreiber kernel: [91025.391742] Queued USB Request Block (URB)... Success
    Feb 10 09:33:10 uj-geraetetreiber kernel: [91025.391743] Buffer Content: /lib64/ld-linux-x86-64.so.2
    Feb 10 09:33:10 uj-geraetetreiber kernel: [91025.391744] --------------------------------------
    Feb 10 09:33:10 uj-geraetetreiber kernel: [91025.391744] Delete USB Request Block (URB)
    Feb 10 09:33:10 uj-geraetetreiber kernel: [91025.391745] --------------------------------------
    

    Soweit, so gut. Schonmal ein Anfang. Dennoch stehe ich vor dem Problem, wie ich ein SCSI Command, zum Beispiel das einfache INQUIRY Command, in den Buffer bekommen soll.
    Laut Spezifikation ist es lediglich ein Command Descriptor Block (16 Byte Block), dessen Anfangsbyte 0x12h ist. Reicht es aus dazu ein:
    [code="c"] uint8_t cdb_inquiry[16]:
    cdb_inquiry[0] = 0x12h;
    [code]
    zu definieren und diesen dann als Buffer der usb_fill_bulk_urb(..., cdb_inquiry, ...) zu übergeben?

    PS: Es ist mir aufgefallen, dass, wenn man einen char* mit kmalloc alloziiert, er auf " /lib64/ld-linux-x86-64.so.2" steht. Warum ist das so?

    EDIT: Mir ist außerdem aufgefallen, dass der Completion Handler erst ausgeführt wird, wenn ich den USB-Stick wieder abziehe, was im Umkehrschluss heißen muss, dass etwas mit dem URB fehlerhaft ist. Nur was!?


  • Mod

    Quellcode schrieb:

    PS: Es ist mir aufgefallen, dass, wenn man einen char* mit kmalloc alloziiert, er auf " /lib64/ld-linux-x86-64.so.2" steht. Warum ist das so?

    Weil der Inhalt uninitialisiert ist. Da kann dann auch manchmal was lesbares drinstehen.



  • Danke für den Hinweis. Habe den Buffer nun alloziert und mit dem INQUIRY Command initialisiert:
    (INQUIRY COMMAND laut: http://www.tldp.org/, Zeile 23)

    #define INQ_REPLY_LEN 96
    #define INQ_CMD_CODE 0x12
    #define INQ_CMD_LEN 6
    ...
    unsigned char inquiry_cmd[INQ_CMD_LEN] = {INQ_CMD_CODE, 0, 0, 0, INQ_REPLY_LEN, 0};
    unsigned char* buffer = kmalloc(sizeof(inquiry_cmd, GFP_KERNEL);
    buffer = inquiry_cmd;
    unsigned short buffer_size = sizeof(buffer);
    

    Soweit sogut, der return-Wert des abgesetzten URBs über usb_submit_urb(...) ist 0, was heißt, dass das URB erfolgreich in die Abarbeitungswarteschlange eingefügt wurde (ACHTUNG: aber noch nicht abgearbeitet wurde!).

    Aber die Completion Callback Funktion des URBs wird nicht aufgerufen.
    Da man den Status des URBs ja erst innerhalb der Callback Funktion abrufen soll, kann ich nicht so recht nachvollziehen wo genau der Fehler liegen soll.

    Meiner Auffassung nach sollte das INQUIRY-Command richtig abgesetzt worden sein und im Buffer nun Infos zu dem Gerät stehen. Diese würde ich gern im Completion Handler über printk(...); ausgeben, aber der wird ja nie aufgerufen -> URB nicht verarbeitet.

    Was kann ich tun?

    EDIT: Habe den Status des URBs jetzt einfach mal alle 500 Millisekunden abgefragt und es zeigt ständig auf -115, was laut errno.h "EINPROGRESS ->/* Operation now in progress */" entspricht.
    Die Verarbeitung läuft also, aber warum passiert nichts weiter?

    EDIT2: Nun habe ich das Ganze mal mit:

    usb_bulk_msg(device, usb_recvbulkpipe(device, USB_EP_IN), buffer, buffer_size, &actual_len, HZ*40);
    

    probiert, aber außer einem Timeout-Error nach Ablauf der Zeit passiert auch nichts weiter. Warum zum Teufel funktionieren die Aufrufe nicht??



  • Habe den Teufel gefunden:

    Es lag in dem Aufruf:

    usb_fill_bulk_urb(urb, usb_rcvbulkpipe(device, USB_EP_IN), buffer, buffer_size, (usb_cmplete_t)(printComplete), ctx)
    

    Es muss heißen:

    usb_fill_bulk_urb(urb, device, usb_sndbulkpipe(...) ...);
    

    Schon wird der Completion-Handler aufgerufen, der Status des URBs ist nun auch 0, was heißt, dass das Request erfolgreich abgesetzt werden konnte.
    Beim Lesen des Gerätes (z.B.: mit cat /dev/stick0) erscheint nun folgende Meldung:

    Feb 12 17:09:56 uj-geraetetreiber kernel: [19310.434843] Create USB Request Block (URB)... Success
    Feb 12 17:09:56 uj-geraetetreiber kernel: [19310.434845] --------------------------------------
    Feb 12 17:09:56 uj-geraetetreiber kernel: [19310.434846] Buffer Content: 
    Feb 12 17:09:56 uj-geraetetreiber kernel: [19310.434866] Queued USB Request Block (URB)... Success
    Feb 12 17:09:56 uj-geraetetreiber kernel: [19310.434866] --------------------------------------
    Feb 12 17:09:56 uj-geraetetreiber kernel: [19310.457443] [Completion Handler] URB-Status=0
    Feb 12 17:09:56 uj-geraetetreiber kernel: [19310.457445] [Completion Handler] Submit USB Request Block (URB)... Success
    Feb 12 17:09:56 uj-geraetetreiber kernel: [19310.457446] [Completion Handler] Buffer Length: 8
    Feb 12 17:09:56 uj-geraetetreiber kernel: [19310.457447] [Completion Handler] Buffer Content: 2071330458
    Feb 12 17:09:56 uj-geraetetreiber kernel: [19310.457448] --------------------------------------
    Feb 12 17:09:56 uj-geraetetreiber kernel: [19310.638365] Delete USB Request Block (URB)
    Feb 12 17:09:56 uj-geraetetreiber kernel: [19310.638368] --------------------------------------
    

    Nach erfolgreichem Ablauf müssten doch jetzt die Daten im Buffer stehen, oder wie komme ich jetzt an die Antwort heran?



  • Hm... so richtig verstehe ich das Ganze noch nicht.

    Ich habe mal ein bisschen herumgespielt mit verschiedensten Parametern. Dabei ist mir aufgefallen, dass ich bei Verwendung von:

    usb_sndbulkpipe(device, USB_EP_IN)
    

    und

    usb_sndbulkpipe(device, USB_EP_OUT)
    

    immer erfolgreich meine URBs absetzen kann, während:

    usb_rcvbulkpipe(device, USB_EP_IN)
    

    und

    usb_rcvbulkpipe(device, USB_EP_OUT)
    

    immer zu Timeout-Fehler führen bzw ewig im -EINPROGRESS Status sind.

    Ich dachte, man muss ein Inquiry-Command in den Buffer stecken und das ganze dann über eine usb_rcvbulkpipe an den IN Endpoint schicken...

    Was mache ich denn nur falsch bzw. wo liegt mein Fehler?



  • Okay, laut anil_pugalia von www.opensourceforu.com und den Kommentaren von tbol unter seinem USB-Device Driver Tutorial muss zunächst das Command an den OUT-Endpoint gesendet werden und dann am IN-Endpoint auf Antwort gelauscht werden.

    Genau das habe ich nun getan und festgestellt, dass ich immer einen -EPIPE (Broken Pipe) Fehler bekomme, sobald ich am IN Endpoint lausche (bzw. bei Verwendung von usb_bulk_msg(...) einen Timeout). Laut anil's Tutorial kann das ein Hinweis auf ein nicht SCSI-konformes (fehlerhaftes) SCSI-Command sein.

    Frage: Wie sieht denn nun ein SCSI-konformes INQUIRY Command aus?

    Laut SCSI SPC-2 Specification und The Linux Document Project muss das so aussehen:

    +=====-========-========-========-========-========-========-========-========+
    |  Bit|   7    |   6    |   5    |   4    |   3    |   2    |   1    |   0    |
    |Byte |        |        |        |        |        |        |        |        |
    |=====+=======================================================================|
    | 0   |                           Operation Code (12h)                        |
    |-----+-----------------------------------------------------------------------|
    | 1   | Logical Unit Number      |                  Reserved         |  EVPD  |
    |-----+-----------------------------------------------------------------------|
    | 2   |                           Page Code                                   |
    |-----+-----------------------------------------------------------------------|
    | 3   |                           Reserved                                    |
    |-----+-----------------------------------------------------------------------|
    | 4   |                           Allocation Length                           |
    |-----+-----------------------------------------------------------------------|
    | 5   |                           Control                                     |
    +=============================================================================+
    

    Was ich wie folgt umgesetzt habe:

    unsigned char inquiry_cmd[INQ_CMD_LEN] = {INQ_CMD_CODE, 0, 0, 0, INQ_REPLY_LEN, 0};
    

    Die Rückantwort muss so aussehen:

    +=====-========-========-========-========-========-========-========-========+
    |  Bit|   7    |   6    |   5    |   4    |   3    |   2    |   1    |   0    |
    |Byte |        |        |        |        |        |        |        |        |
    |=====+==========================+============================================|
    | 0   | Peripheral Qualifier     |           Peripheral Device Type           |
    |-----+-----------------------------------------------------------------------|
    | 1   |  RMB   |                  Device-Type Modifier                        |
    |-----+-----------------------------------------------------------------------|
    | 2   |   ISO Version   |       ECMA Version       |  ANSI-Approved Version   |
    |-----+-----------------+-----------------------------------------------------|
    | 3   |  AENC  | TrmIOP |     Reserved    |         Response Data Format      |
    |-----+-----------------------------------------------------------------------|
    | 4   |                           Additional Length (n-4)                     |
    |-----+-----------------------------------------------------------------------|
    | 5   |                           Reserved                                    |
    |-----+-----------------------------------------------------------------------|
    | 6   |                           Reserved                                    |
    |-----+-----------------------------------------------------------------------|
    | 7   | RelAdr | WBus32 | WBus16 |  Sync  | Linked |Reserved| CmdQue | SftRe  |
    |-----+-----------------------------------------------------------------------|
    | 8   | (MSB)                                                                 |
    |- - -+---                        Vendor Identification                    ---|
    | 15  |                                                                 (LSB) |
    |-----+-----------------------------------------------------------------------|
    | 16  | (MSB)                                                                 |
    |- - -+---                        Product Identification                   ---|
    | 31  |                                                                 (LSB) |
    |-----+-----------------------------------------------------------------------|
    | 32  | (MSB)                                                                 |
    |- - -+---                        Product Revision Level                   ---|
    | 35  |                                                                 (LSB) |
    |-----+-----------------------------------------------------------------------|
    | 36  |                                                                       |
    |- - -+---                        Vendor Specific                          ---|
    | 55  |                                                                       |
    |-----+-----------------------------------------------------------------------|
    | 56  |                                                                       |
    |- - -+---                        Reserved                                 ---|
    | 95  |                                                                       |
    |=====+=======================================================================|
    |     |                       Vendor-Specific Parameters                      |
    |=====+=======================================================================|
    | 96  |                                                                       |
    |- - -+---                        Vendor Specific                          ---|
    | n   |                                                                       |
    +=============================================================================+
    

    Was ich wie folgt umgesetzt habe:

    unsigned char inquiry_cmd[INQ_REPLY_LEN];
    

    Natürlich sind beide per kmalloc(0, GFP_KERNEL); alloziert.

    Warum bekomme ich dann "Broken Pipe"-Errors?
    Liegt der Fehler im SCSI Command (gesendet an OUT-Endpoint) oder am Data-Format (Empfang am IN-Endpoint)?
    Oder hab ich etwas total verwechselt und bin auf dem falschem Weg?


Anmelden zum Antworten