AVR-USB works well with the ATMega16 and 32. However, the fuse bits differ from device to device. This means that you can't simply use fuse bit values from an example device for one of the bigger ATMegas.

I can give you the hint to look at the fuse bits. But I'm afraid I can't help you more than that. Just have a look at the data sheet or use Atmel's AVR-Studio which represents the fuse bit meaning in clear text.

You must ensure that the AVR is clocked with an external 12 MHz crystal and no additional dividers.

The delay while pulling D+ and D- low has no effect when you first plug in the device. Since the hub or host detects the device by a high level on D- or D+ (for low or full speed respectively), the device is not detected until the delay is over.

The main purpose of this type of initialization is for watchdog resets. If you get a reset through the watchdog, the host sees a device disconnect and then re-connect. It will thus re-enumerate so that device and host agree on a device ID.

The re-enumeration SHOULD be initiated by removing the pull-up resistor from D- instead of pulling D+ and D- low, but the resistor is usually hard wired.

I have not found out how to coerce it into calling usbRead() but usbWrite() and Setup transfers work flawless.

(This is getting off-topic -- please open a new topic for questions not directly related to the original topic.)

I assume that you want to implement a control-write transfer with a non-zero data block. In order to do that, you must:

Define USB_CFG_IMPLEMENT_FN_WRITE to 1 in usbconfig.h. This tells the driver that you intend to implement usbFunctionWrite().

Implement usbFunctionWrite() in your code. You will receive the data block of a control-write transfer there. If the block is more than 8 bytes, usbFunctionWrite() will be called multiple times. Return 0 in this function if you expect more data, 1 if the last block was received (see doc in usbdrv.h).

In usbFunctionSetup(), decode the request number. If you find your control-write transfer which involves a data block, store the number of bytes expected (so that you can return the right value in usbFunctionWrite()) and return 0xff. If another request was received, return the number of bytes you want to send.