// usb_inteface.c // BCD -- Ben Hutton, Chris Leary, Devrin Talen // 100-column width // See usb_interface.h for headers. #include "usb_interface.h" #define debug(string) sprintf(print_buffer, string); send_debug() // ------------------- // - HOST CONTROLLER - // ------------------- HC_T *HostController() // Constructor. Initializes the queue pointers and bound SIE pointer to NULL. { HC_T *hc; hc = malloc(sizeof(HC_T)); hc->transaction_queue_head = NULL; hc->transaction_queue_tail = NULL; hc->sie = NULL; return hc; } void DestroyHostController(HC_T *hc) // Destructor. NOTE: it does not destroy a bound SIE, if there is one. { free(hc); } void hc_bind_sie(HC_T *hc, SIE_T *sie) // Binds an SIE to a host controller { hc->sie = sie; } SIE_T *hc_unbind_sie(HC_T *hc) // Unbinds an associated SIE from a host controller. Returns the previously-bound SIE pointer, in // case you cared. { SIE_T *previously_bound_sie; previously_bound_sie = hc->sie; hc->sie = NULL; return previously_bound_sie; } void hc_push_transaction(HC_T *hc, TRANSACTION_T *new_transaction) // Appends the new transaction onto the tail end of the queue. { TRANSACTION_NODE_T *new_transaction_node; new_transaction_node = TransactionNode(new_transaction, hc->transaction_queue_tail, NULL); if (hc->transaction_queue_tail) hc->transaction_queue_tail->next = new_transaction_node; else hc->transaction_queue_head = new_transaction_node; // make the new node the tail of the queue hc->transaction_queue_tail = new_transaction_node; debug("pushed a transaction\n"); } TRANSACTION_T *hc_pop_transaction(HC_T *hc) // Pop a transaction pointer off the front of the queue and returns it. { TRANSACTION_NODE_T *return_transaction_node; // necessary so we can destroy it before return TRANSACTION_T *return_transaction; debug("trying to pop a transaction\n"); return_transaction_node = hc->transaction_queue_head; // make sure that there are transaction nodes on the queue if (return_transaction_node) { return_transaction = return_transaction_node->ptr; hc->transaction_queue_head = return_transaction_node->next; if (hc->transaction_queue_head == NULL) hc->transaction_queue_tail = NULL; DestroyTransactionNode(return_transaction_node); debug("popped a transaction"); return return_transaction; } debug("failed to pop the transaction"); // if we get to this point there was no transaction node in the queue, so return a null pointer return NULL; } TRANSACTION_T *hc_do_transaction(HC_T *hc) { // NOTE: transient first state is idle line state. For low speed this is D- high, D+ low, which // corresponds to a differential zero. #define transient_first_state 1 #define debug_buffer \ sprintf(print_buffer, \ " 0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", \ (unsigned char) hc->sie->buffer[0] & 0xff, \ (unsigned char) hc->sie->buffer[1] & 0xff, \ (unsigned char) hc->sie->buffer[2] & 0xff, \ (unsigned char) hc->sie->buffer[3] & 0xff, \ (unsigned char) hc->sie->buffer[4] & 0xff, \ (unsigned char) hc->sie->buffer[5] & 0xff, \ (unsigned char) hc->sie->buffer[6] & 0xff, \ (unsigned char) hc->sie->buffer[7] & 0xff, \ (unsigned char) hc->sie->buffer[8] & 0xff, \ (unsigned char) hc->sie->buffer[9] & 0xff, \ (unsigned char) hc->sie->buffer[10] & 0xff, \ (unsigned char) hc->sie->buffer[11] & 0xff, \ (unsigned char) hc->sie->buffer[12] & 0xff, \ (unsigned char) hc->sie->buffer[13] & 0xff, \ (unsigned char) hc->sie->buffer[14] & 0xff,\ (unsigned char) hc->sie->buffer[15] & 0xff); \ send_debug() TRANSACTION_T *executing_transaction; unsigned char iter; size_t sie_valid_bytes; // Get the transaction off of the queue. executing_transaction = hc_pop_transaction(hc); if (!executing_transaction) { #ifdef DEBUG_HC { sprintf(print_buffer, "No executing transaction! Returning to caller.\n"); send_debug(); } #endif return executing_transaction; } #ifdef DEBUG_HC { sprintf(print_buffer, "\nPopped transaction at 0x%x\n", executing_transaction); send_debug(); } #endif // Calculate token packet CRC. packet_make_crc5(executing_transaction->token_packet); // Set token packet length in the SIE (before bit stuffing). hc->sie->token_packet_length = TOKEN_PACKET_SIZE * CHAR_BITS; // Check if we have a data packet. if (executing_transaction->data_packet) { if (executing_transaction->data_packet->payload_size) { // if the data packet length is non-zero, then we're transmitting #ifdef DEBUG_HC { sprintf(print_buffer, "Transmitting...\n"); send_debug(); } #endif // indicate that SIE should be transmitting hc->sie->data_direction = TRANSMIT; // calculate data packet crc packet_make_crc16(executing_transaction->data_packet); // set data packet length in SIE hc->sie->data_packet_length = (BASE_DATA_PACKET_SIZE + executing_transaction->data_packet->payload_size) * 8; #ifdef DEBUG_HC { sprintf(print_buffer, "Data packet CRC16: 0x%x\n", (unsigned int) executing_transaction->data_packet->crc16); send_debug(); } #endif } else { // payload is size zero, which indicates that we're receiving #ifdef DEBUG_HC { sprintf(print_buffer, "Receiving...\n"); send_debug(); } #endif hc->sie->data_direction = RECEIVE; hc->sie->data_packet_length = 0; } } else { // we have no data packet, so transmit, but transmit nothing hc->sie->data_direction = TRANSMIT; hc->sie->data_packet_length = 0; } // Place transaction in the buffer. transaction_place_in_buffer(executing_transaction, hc->sie->buffer, &(hc->sie->token_packet_length), &(hc->sie->data_packet_length)); #ifdef DEBUG_HC_FAKE { sprintf(print_buffer, "Placed transaction in the buffer: token len: %d; data len: %d\n", hc->sie->token_packet_length, hc->sie->data_packet_length); send_debug(); debug_buffer; } #endif // Bit stuff the buffer. Note: assumes function will change the token and data packet lengths // in the SIE as appropriate. sie_bitstuff_buffer(hc->sie); #ifdef DEBUG_HC_FAKE { sprintf(print_buffer, "Bit stuffed the buffer: token len: %d; data len: %d\n", hc->sie->token_packet_length, hc->sie->data_packet_length); send_debug(); debug_buffer; } #endif // Encode the buffer. sie_nrzi_encode_buffer(hc->sie, transient_first_state); #ifdef DEBUG_HC_FAKE { sprintf(print_buffer, "NRZI encoded the buffer.\n"); send_debug(); //debug_buffer; } #endif sie_valid_bytes = ((hc->sie->token_packet_length + hc->sie->data_packet_length - 1) >> 3) + 1; // Flip everything in the buffer for the SIE. for (iter = 0; iter < sie_valid_bytes; iter++) { hc->sie->buffer[iter] = flip_byte(hc->sie->buffer[iter], 8); } #ifdef DEBUG_HC_FAKE { sprintf(print_buffer, "LSB'd each byte in the buffer.\n"); send_debug(); debug_buffer; } #endif // Tell the SIE where to put the handshake reply. hc->sie->handshake_result = &(executing_transaction->handshake); // Based on the transfer type, call the appropriate SIE transfer function -- it will perform // this transfer with the data that has just been set up in its buffer and internal variables. switch (executing_transaction->transfer_type) { case CONTROL: #ifdef DEBUG_HC { sprintf(print_buffer, "Initiating control transfer.\n"); send_debug(); } #endif #ifndef NO_AVR_GCC sie_control_transfer(hc->sie); #endif break; case INTERRUPT: #ifdef DEBUG_HC { sprintf(print_buffer, "Initiating interrupt transfer.\n"); send_debug(); } #endif #ifndef NO_AVR_GCC sie_interrupt_transfer(hc->sie); #endif break; } sie_valid_bytes = ((hc->sie->token_packet_length + hc->sie->data_packet_length - 1) >> 3) + 1; // Flip everything in the buffer -- the SIE puts it in backwards. for (iter = 0; iter < sie_valid_bytes; iter++) { hc->sie->buffer[iter] = flip_byte(hc->sie->buffer[iter], 8); } // At this point, the transaction has been completed. #ifdef DEBUG_HC debug_buffer; #endif // Unencode the buffer. sie_nrzi_decode_buffer(hc->sie, transient_first_state); #ifdef DEBUG_HC { sprintf(print_buffer, "De-NRZI'd buffer, tpl: %d, dpl: %d:\n", hc->sie->token_packet_length, hc->sie->data_packet_length); send_debug(); debug_buffer; } #endif // Bit unstuff the buffer. sie_bitunstuff_buffer(hc->sie); #ifdef DEBUG_HC_FAKE { sprintf(print_buffer, "Bitunstuffed buffer: token len: %d; data len: %d\n", hc->sie->token_packet_length, hc->sie->data_packet_length); send_debug(); debug_buffer; } #endif // Flip everything in the buffer -- the SIE puts it in backwards. for (iter = 0; iter < sie_valid_bytes; iter++) { hc->sie->buffer[iter] = flip_byte(hc->sie->buffer[iter], 8); } #ifdef DEBUG_HC debug_buffer; #endif // Recall that the handshake result was already placed into the SIE (by the SIE) during the // transfer. return executing_transaction; #undef debug_buffer #undef transient_first_state } // --------------------------- // - SERIAL INTERFACE ENGINE - // --------------------------- SIE_T *SerialInterfaceEngine() // Constructor. Returns a pointer to an SIE with an initialized buffer, where all pointers // (besides the buffer) are NULL and values are in their most undetermined state. This prepares // the values to be set by the Host Controller. // NOTE: this undetermined-value-setting feature could be optimized out, if need be. { SIE_T *sie; sie = malloc(sizeof(SIE_T)); sie->buffer = calloc(sizeof(char), SIE_BUFFER_SIZE); sie->token_packet_length = 0; sie->data_packet_length = 0; // data direction left alone... no good value to set it to sie->handshake_result = NULL; sie->buffer_encoding = UNENCODED; return sie; } void DestroySerialInterfaceEngine(SIE_T *sie) // Destructor. Frees only the value itself, as the buffer and other properties are set and unset // by the Host Controller. { free(sie); } void sie_bitstuff_buffer(SIE_T *sie) { sie_bitstuff_buffer_helper(sie, STUFF); } void sie_bitunstuff_buffer(SIE_T *sie) { sie_bitstuff_buffer_helper(sie, UNSTUFF); } void sie_bitstuff_buffer_helper(SIE_T *sie, STUFF_METHOD_T stuff_method) { #define byte_length(bit_count) (((bit_count - 1) >> 3) + 1) uint8_t *buf_ptr; uint8_t *new_token_buf, *new_data_buf; uint8_t new_token_packet_length; uint16_t new_data_packet_length; uint8_t iter; // Token packet bitstuffing. buf_ptr = sie->buffer; new_token_packet_length = bitstuff_buffer(&buf_ptr, sie->token_packet_length, stuff_method); new_token_buf = buf_ptr; // Copy the token values into the SIE buffer. memcpy(sie->buffer, new_token_buf, byte_length(sie->token_packet_length)); sie->token_packet_length = new_token_packet_length; // Free the temp array. free(new_token_buf); if (sie->data_packet_length) { // Data packet bitstuffing. buf_ptr = &(sie->buffer[byte_length(sie->token_packet_length)]); new_data_packet_length = bitstuff_buffer(&buf_ptr, sie->data_packet_length, stuff_method); new_data_buf = buf_ptr; // Copy the data values into the SIE buffer. memcpy(&(sie->buffer[byte_length(sie->token_packet_length)]), new_data_buf, byte_length(sie->data_packet_length)); sie->data_packet_length = new_data_packet_length; // Free the temp array. free(new_data_buf); } #undef byte_length } void sie_nrzi_decode_buffer(SIE_T *sie, uint8_t transient_first_state) // NRZI decodes (in place) for the buffer associated with the given SIE. Takes a transient first // state (H/L corresponding to 1/0), as it is necessary to determine the encoding for the first bit. { // What these bitwise macros do is NRZI decode bit by bit. The first bit is XOR'd with // the initial state of the line. This corresponds to the NRZI scheme: // 0 means the line stays the same // 1 means the line changes // This is effetively an XOR from one bit to the next. What these macros are doing is taking // bit n of the byte and XORing that bit with bit n-1, leaving the result in the n-bit spot. // The shift left one that you see on the left side of the expression is to align the (n-1)th // bit with the nth bit on the RHS. When you OR all these bits together, you will end up with // the final NRZI result, as given by the toggle macro. #define nrzi_bit_one(byte) (~(((byte & 0x02) >> 1) ^ (byte & 0x01)) & 0x01) #define nrzi_bit_two(byte) (~(((byte & 0x04) >> 1) ^ (byte & 0x02)) & 0x02) #define nrzi_bit_three(byte) (~(((byte & 0x08) >> 1) ^ (byte & 0x04)) & 0x04) #define nrzi_bit_four(byte) (~(((byte & 0x10) >> 1) ^ (byte & 0x08)) & 0x08) #define nrzi_bit_five(byte) (~(((byte & 0x20) >> 1) ^ (byte & 0x10)) & 0x10) #define nrzi_bit_six(byte) (~(((byte & 0x40) >> 1) ^ (byte & 0x20)) & 0x20) #define nrzi_bit_seven(byte) (~(((byte & 0x80) >> 1) ^ (byte & 0x40)) & 0x40) #define nrzi_bit_eight(byte, init) (~(((byte & 0x80) >> 0) ^ (init << 7)) & 0x80) // Returns the NRZI decoded version of the byte, given an initial state (which corresponds to // the line state previous to the byte). #define nrzi_decode(byte, init) \ (nrzi_bit_one(byte) | nrzi_bit_two(byte) | nrzi_bit_three(byte) | nrzi_bit_four(byte)\ | nrzi_bit_five(byte) | nrzi_bit_six(byte) | nrzi_bit_seven(byte) \ | nrzi_bit_eight(byte, init)) uint8_t initial_state, iter, current_byte; const uint8_t token_packet_bytes = ((sie->token_packet_length - 1) >> 3) + 1; const uint8_t data_packet_bytes = ((sie->data_packet_length - 1) >> 3) + 1; for (iter = 0; iter < token_packet_bytes; iter++) sie->buffer[iter] = 0; initial_state = transient_first_state & 0x1; for (iter = token_packet_bytes; iter < token_packet_bytes + data_packet_bytes; iter++) { current_byte = sie->buffer[iter]; sie->buffer[iter] = nrzi_decode(current_byte, initial_state); // Take the current byte as the next initial state -- note that only the LSb is used through // a shift-left-logical 7, so no masking is necessary. initial_state = current_byte; } for (iter = token_packet_bytes + data_packet_bytes; iter < SIE_BUFFER_SIZE; iter++) sie->buffer[iter] = 0; sie->buffer_encoding = UNENCODED; #undef nrzi_decode #undef nrzi_bit_eight #undef nrzi_bit_seven #undef nrzi_bit_six #undef nrzi_bit_five #undef nrzi_bit_four #undef nrzi_bit_three #undef nrzi_bit_two #undef nrzi_bit_one } void sie_nrzi_encode_buffer(SIE_T *sie, uint8_t transient_first_state) { const uint8_t token_packet_size = 3; const uint8_t data_packet_size = ((sie->data_packet_length - 1) >> 3) + 1; uint8_t byte_iter, bit_iter; uint8_t current_state; uint8_t current_buffer_byte; uint8_t shift_amount; uint8_t new_bit; current_state = transient_first_state; // iterate bit by bit -- if the current bit is set, toggle the current state for (byte_iter = 0; byte_iter < token_packet_size; byte_iter++) { // copy the current buffer byte, so that we can clobber the real one current_buffer_byte = sie->buffer[byte_iter]; sie->buffer[byte_iter] = 0; for (bit_iter = 0; bit_iter < 8; bit_iter++) { shift_amount = (7 - bit_iter); // current state current bit new bit value // 0 0 1 // 0 1 0 // 1 0 0 // 1 1 1 // operation: XNOR! // take the nth bit of the current buffer byte and XNOR it with the current state new_bit = (current_buffer_byte & (1 << shift_amount)) ^ (((~current_state) & 0x01) << shift_amount); sie->buffer[byte_iter] |= new_bit; // shift the new bit into the LSB to indicate the current state current_state = new_bit >> shift_amount; } } current_state = transient_first_state; // iterate bit by bit -- if the current bit is set, toggle the current state for (byte_iter = token_packet_size; byte_iter < data_packet_size + token_packet_size; byte_iter++) { // copy the current buffer byte, so that we can clobber the real one current_buffer_byte = sie->buffer[byte_iter]; sie->buffer[byte_iter] = 0; for (bit_iter = 0; bit_iter < 8; bit_iter++) { shift_amount = (7 - bit_iter); // current state current bit new bit value // 0 0 1 // 0 1 0 // 1 0 0 // 1 1 1 // operation: XNOR! // take the nth bit of the current buffer byte and XNOR it with the current state new_bit = (current_buffer_byte & (1 << shift_amount)) ^ (((~current_state) & 0x01) << shift_amount); sie->buffer[byte_iter] |= new_bit; // shift the new bit into the LSB to indicate the current state current_state = new_bit >> shift_amount; } } sie->buffer_encoding = NRZI; } #ifndef NO_AVR_GCC void sie_transfer(SIE_T *sie, TRANSFER_TYPE_T type) // This is where the magic happens. // Handles transferring data between a device and the host (this). { /* First, the relevant data from the SIE struct is extracted. We make copies so that we * can keep from trashing the original data. * * The fields are: * - buffer: The address of the buffer. We will load the first byte of the data packet * from here. * - token_packet_length: The size (in bits) of the token packet. * - data_packet_length: The size (in bits) of the data packet, if it exists. * - data_direction: If 0, we are transmitting the data packet, if 1 we are receiving. * - handshake_result: The address where we will store the handshake if we receive one. */ register uint8_t token_length asm("r16") = sie->token_packet_length; register uint8_t data_length asm("r17") = sie->data_packet_length; register uint8_t dir asm("r7") = sie->data_direction; /* These are variables declared solely for the use of the assembly code. They are * declared here to make it easier for GCC to find registers to store them in. */ register uint8_t byte_buffer asm("r3") = 0; register uint8_t temp asm("r20") = 0; register uint8_t timeout_counter asm("r21") = 0; register uint8_t constant_5 asm("r10") = 5; register uint8_t NACK_byte asm("r11") = 0xf7; register uint8_t STALL_byte asm("r12") = 0xc4; register uint8_t transfer_type asm("r8") = type; // zero out the handshake result *(sie->handshake_result) = 0x7; #ifdef DEBUG_SIE { /* Debugging statements. Note that these will only happen when the library is compiled with * the -D DEBUG_SIE flag. */ /*sprintf( print_buffer, ">>Beginning SIE transfer.\n" ); send_debug(); sprintf( print_buffer, " Buffer address: 0x%x\n", (unsigned int) sie->buffer ); send_debug(); sprintf( print_buffer, " Token length: %d\n", (unsigned char) token_length ); send_debug(); sprintf( print_buffer, " Data length: %d\n", (unsigned char) data_length ); send_debug(); sprintf( print_buffer, " Data direction: 0x%x\n", (unsigned char) dir ); send_debug(); sprintf(print_buffer, " Handshake address: 0x%x\n", (unsigned int) sie->handshake_result); send_debug();*/ } #endif /* We now send the device a resume signal before the transfer kicks in. This is in order * to wake the device up from suspend, which it most likely has entered as a result of the * very long debug statements. * * After the resume signal, the SIE will then send a keep alive, which prevents low-speed * devices from going to sleep. */ sie_resume(sie); for( temp = 0; temp < 25; temp++ ) { _delay_ms(1); // Delay for a total of 25 ms. } asm volatile ( "ldi r20, 0x04\n\t" "out %0, r20\n\t" NOP8 "ldi r20, 0x04\n\t" "out %0, r20\n\t" NOP8 "ldi r20, 0x05\n\t" "out %0, r20\n\t" NOP8 :: "I" (_SFR_IO_ADDR(USBPORT)) ); sie_idle(sie); _delay_us(20); sie_keepalive(sie); sie_idle(sie); _delay_us(10); /* Here begins the assembly for the SIE. This will handle sending and receiving packets both * to and from a device. Once we begin the transfer, we have to stick to a pretty tight 10-cycle * interval between bits. We depend on the host controller to provide the SIE with enough * information so that it does not have to do much thinking. The thousand-foot view of the * assembly that follows is this: * * 1. Perform a small amount of initialization (storing constants into registers, etc.). * 2. Send the token packet (including sync and EOP). * 3. Decide if we are receiving or sending data and branch to the appropriate handler. * 4. */ asm volatile ( NOP_INIT // needed for making nop jumps happen. "ldi r20, 0x05\n\t" "mov r10, r20\n\t" "ldi r20, 0x9c\n\t" "mov r11, r20\n\t" "ldi r20, 0xa0\n\t" "mov r12, r20\n\t" /* Sending the token packet happens regardless of what type of transfer we are doing, * control or interrupt. At most, we'll have to send 6 bytes (with bitstuffing). Once * the token packet is began, we then have to adhere to a pretty tight timing schedule: * 1 bit sent every 10 cycles. */ ".sie_send_token:\n\t" SIE_SEND_SYNC "ld r3, X+\n\t" // buffer up the first token byte NOP3 ".sie_send_token_bits:\n\t" // Send up to 6 bytes SIE_TOKEN_BYTE SIE_TOKEN_BYTE SIE_TOKEN_BYTE SIE_TOKEN_BYTE SIE_TOKEN_BYTE SIE_TOKEN_BYTE ".sie_send_token_eop:\n\t" SIE_SEND_EOP // 8 cycles free at this point /* This is where we differentiate between input and output transfers. A transfer is * anywhere from 1-8 bytes. However, we have to be ready to accept a handshake packet * instead of a data packet if the device sends us one. In that case, we don't send * any sort of handshake, and idle. Normally, we will respond with a handshake when we * get all of the data. * * This little piece of code compares the register where the data direction is stored to 1. * If it's equal to 1, we are receiving data. Otherwise, we're transmitting the data. */ "ldi r20, 0x01\n\t" "cp r7, r20\n\t" "breq .+4\n\t" "jmp .sie_tx_data\n\t" // If we take the jump: 3 cycles left // If we don't take the jump: 5 cycles left ".sie_rx_data:\n\t" // Turn off the transmitter so we can receive. "ldi r20, 0x00\n\t" "out %1, r20\n\t" /* Receiving the sync is a tricky time. We can not know when the device will begin to respond, * although we do know that it should be in the window of about 2 to 7.5 bit times (10 cycles * each). We assume (unfortunately) that the device *will* respond. This is so we can save * cycles when trying to find the first pulse of the sync. This means that if the device does * not respond, then we sit in an infinite loop, looking for this first pulse. Not ideal, * but it has to do. TODO: FIX THIS */ SIE_RX_SYNC_FIRST SIE_RX_SYNC /* At this point, we have a special byte receive for the 8th bit of a data packet. This * is to handle receiving a handshake instead of a data packet as we expected. Otherwise we * jump straight to the regular byte receivers, which handle regular data. This handler is * necessary because a device is allowed to respond to a DATAIN request with a NACK if it * has no data to send us. */ ".sie_rx_control_data_bits:\n\t" SIE_DATA_RX_BYTE // byte 0 SIE_DATA_RX_SPECIAL_BYTE // byte 1, if EOP on bit 0 jump to handshake handler SIE_DATA_RX_BYTE // byte 2 SIE_DATA_RX_BYTE // byte 3 SIE_DATA_RX_BYTE // byte 4 SIE_DATA_RX_BYTE // byte 5 SIE_DATA_RX_BYTE // byte 6 SIE_DATA_RX_BYTE // byte 7 SIE_DATA_RX_BYTE // byte 8 SIE_DATA_RX_BYTE // byte 9 SIE_DATA_RX_BYTE // byte 10 SIE_DATA_RX_BYTE // byte 11 SIE_DATA_RX_BYTE // byte 12 /* This handles the special situation of an data transfer ending after 1 byte * is received. This could be just normal, or it could mean that the function was not ready * to send us data, and therefore sent a handshake (NAK or STALL) instead of actual data. * To handle this, we'll check the data that we just received and compare it to the two * cases given above. * * A NAK is identified by 0x5a. The NRZI value is: * 01011010 -> (11) 00111001 (NRZI) -> 10011100 (stored in buffer) * A STALL is identified by 0x78. The NRZI value is: * 01111000 -> (11) 00000101 (NRZI) -> 10100000 (stored in buffer) * * If we see one of these two values in the buffer, then we've just received a handshake, * not data, and we'll store this into the handshake. The data, while stored in the buffer, * should be disregarded if there is a NAK or STALL in the handshake buffer. We'll also zero * out the data size, as it was a handshake and not actual data. */ ".sie_rx_data_first_eop:\n\t" "in r20, %2\n\t" "cp r20, __zero_reg__\n\t" //"brne .sie_datain_error\n\t" "cp r3, r11\n\t" "breq .+8\n\t" "cp r3, r12\n\t" "breq .+4\n\t" "jmp .sie_ackout\n\t" "st Z, r3\n\t" "ldi r17, 0x00\n\t" "jmp .sie_done\n\t" /* This handles a normal EOP that would be received after a control or interrupt data in * finishes, and we don't suspect that it was a special case (as in a 1-byte data * in). As we're not able to check the CRC to make sure that it was right, we assume that the * data from the device is correct and we received it correctly. This might be a bad * assumption, but it's necessary because the Mega32 is too slow. So we send out an ACK. */ ".sie_rx_data_eop:\n\t" "in r20, %2\n\t" "cp r20, __zero_reg__\n\t" //"brne .sie_datain_error\n\t" "jmp .sie_ackout\n\t" // we got the data, send the ack ".sie_sync_timeout:\n\t" // TODO: implement this "jmp .sie_done\n\t" ".sie_datain_error:\n\t" // TODO: implement this also "jmp .sie_done\n\t" /* {{{ Sending a data packet */ /* This is essentially a copy of sending the token packet, but modified to send more bytes. * * According to section 7.1.18.1, we need to insert between 2 and 7.5 "bit times" of delay * between the EOP of the token packet and the sync of the data packet. */ ".sie_tx_data:" /* There needs to be between 20 and 75 cycles of delay between the end of the EOP * for the token packet and the sync of the data packet. This will insert a few. */ "cpi r17, 0x00\n\t" // To handle 0-size data packets, we just end. This is mainly for debug. "brne .+4\n\t" "jmp .sie_done\n\t" NOP8 NOP8 NOP8 NOP8 NOP8 NOP8 NOP8 SIE_SEND_SYNC NOP3 // Have 8 cycles, sending a data bit takes 3 cycles before it sends. ".sie_send_data_bits:\n\t" // Send 13 bytes, the maximum that we could be expected to send. "ld r3, X+\n\t" SIE_DATA_BYTE // byte 0 SIE_DATA_BYTE // byte 1 SIE_DATA_BYTE // byte 2 SIE_DATA_BYTE // byte 3 SIE_DATA_BYTE // byte 4 SIE_DATA_BYTE // byte 5 SIE_DATA_BYTE // byte 6 SIE_DATA_BYTE // byte 7 SIE_DATA_BYTE // byte 8 SIE_DATA_BYTE // byte 9 SIE_DATA_BYTE // byte 10 SIE_DATA_BYTE // byte 11 SIE_DATA_BYTE // byte 12 ".sie_send_data_eop:\n\t" // no nops - needs to send right away. SIE_SEND_EOP // 9 cycles left "ldi r20, 0x00\n\t" "out %1, r20\n\t" // This should now "fall" right through to the ackin handler. /* Handshakes are a tricky issue in this implementation. We don't really have enough time * to know if the data was corrupted (CRC doesn't match up) or not, so we don't know * whether to send an ACK or NACK. At this point in time, we are forgoing the necessary * data checking and just sending an ACK. We may later find out a way to be able to verify * the data in the small amount of time that we have before we are required to send the * handshake. * * The amount of time that we have is anywhere from 2 to 7.5 "bit times", which is * equivalent to 20 to 75 cycles. This is enough time to calculate a CRC5, but not a CRC16, * which is what is needed to check the data packet. * * Receiving handshakes, however, doesn't have the same problems and we can rely on the * function to send us whatever handshake it will. */ /* We begin catching the sync and handshake immediately after the EOP of the data packet * that we just sent. */ ".sie_ackin:\n\t" // Clear the buffer, sync up to the sync (ha!) and read the handshake. Then store it. "clr r3\n\t" SIE_RX_SYNC_FIRST_ACK SIE_RX_SYNC SIE_HANDSHAKE_RX_BYTE // after this, the handshake is in the buffer register (r3) "st Z, r3\n\t" "jmp .sie_done\n\t" /* This will be called after a data in that got real data (not a handshake). We wait a little * bit, then push out the sync, ack, and EOP. We don't have time to do much else. */ ".sie_ackout:\n\t" // Here's some more delay: NOP8 NOP8 NOP8 NOP8 NOP8 NOP4 SIE_SEND_SYNC NOP7 SIE_HANDSHAKE_TX_ACK NOP7 SIE_SEND_EOP /* The transfer is now done. Regardless of if we just received the handshake, or if we * sent it, we need to idle the lines. Hopefully the transmit lines will already be idled * by the handshake receive code, but for safe measure we do it again here. */ ".sie_done:\n\t" NOP8 "ldi r20, 0x00\n\t" "out %1, r20\n\t" : /* no other variables that are written */ : /* read-only variables that we don't assign ourselves */ "x" ((unsigned char *) sie->buffer), "I" (_SFR_IO_ADDR(USBPORT)), /* This and the pin are assigned by the user. */ "I" (_SFR_IO_ADDR(USBPIN)), "z" ((unsigned char *) sie->handshake_result), "M" (20), /* Number of bit-times to wait before timing out on receives. */ "I" (_SFR_IO_ADDR(PORTC)) ); /* Load up the correct values into the SIE struct. If we received data, we want to modify * the data packet length to reflect how many bits we received. This should be all that is * necessary for the interface to decipher the data. */ sie->data_packet_length = data_length; sie->token_packet_length += token_length; // THIS IS A HACK // We're done transmitting everything, so let's resettle to the idle state. sie_idle(sie); #ifdef DEBUG_SIE { /* Post-transfer debugging */ _delay_ms(10); sprintf(print_buffer, ">>Results of transfer:\n"); send_debug(); sprintf( print_buffer, " Token packet: %d bits left in buffer.\n", token_length ); send_debug(); if( dir == RECEIVE ) { sprintf(print_buffer, " Bits received: %d\n", data_length ); send_debug(); sprintf(print_buffer, " Handshake: 0x%x\n", (unsigned char) *(sie->handshake_result) ); send_debug(); } else if( dir == TRANSMIT ) { sprintf(print_buffer, " Data packet: %d bits left in buffer.\n", data_length ); send_debug(); sprintf(print_buffer, " Handshake: 0x%x\n", (unsigned char) *(sie->handshake_result) ); send_debug(); } } #endif } #endif #ifndef NO_AVR_GCC void sie_idle(SIE_T *sie) // Sets the output lines to the idle "J" state { asm( "ldi r16, 0x40\n\t" // blank out the lines. "out %0, r16\n\t" NOP4 "ldi r16, 0x00\n\t" "out %0, r16\n\t" : /* no input registers */ : "I" (_SFR_IO_ADDR(USBPORT)) /* outputs */ : "r16" /* clobbered */ ); } #endif #ifndef NO_AVR_GCC void sie_control_transfer(SIE_T *sie) // Runs through a control transfer. { sie_transfer( sie, CONTROL ); } #endif #ifndef NO_AVR_GCC void sie_interrupt_transfer(SIE_T *sie) // Runs through a interrupt transfer. { sie_transfer( sie, INTERRUPT ); } #endif #ifndef NO_AVR_GCC void sie_keepalive(SIE_T *sie) // Sends a keep alive signal port, same as a low speed EOP { asm( "ldi r20, 0x04\n\t" "out %0, r20\n\t" NOP8 "ldi r20, 0x04\n\t" "out %0, r20\n\t" NOP8 "ldi r20, 0x05\n\t" "out %0, r20\n\t" : /* no input registers */ : /* output registers */ "I" (_SFR_IO_ADDR(USBPORT)) : /* clobbers */ "r20" ); // Resume idle state. sie_idle(sie); } #endif #ifndef NO_AVR_GCC void sie_reset(SIE_T *sie) // Holds a SE0 on the lines. It's up to the user to set the idle state after 10 ms. { asm( "ldi r20, 0x04\n\t" "out %0, r20\n\t" : /* no input registers */ : "I" (_SFR_IO_ADDR(USBPORT)) /* outputs */ : "r20" /* clobbered */ ); } #endif #ifndef NO_AVR_GCC void sie_resume(SIE_T *sie) // Sends a resume signal. It's again up to the user to set the idle state after 20 ms. { asm( "ldi r20, 0x06\n\t" // differential 1 (K) "out %0, r20\n\t" : /* no input registers */ : "I" (_SFR_IO_ADDR(USBPORT)) /* output */ : "r20" /* clobbered */ ); } #endif #ifndef NO_AVR_GCC uint8_t sie_detect_device(SIE_T *sie) // Detects if there is a low-speed device attached to the USB port. { char result=0; /* There is a device connected (or activity on the lines), but we need to figure out what. * The values that we could see: * 0x40: Low-speed device connected. * 0x80: Full-speed device connected. * Else: Nothing we know of. */ asm( "sbis %1, 6\n\t" "ldi %0, 1\n\t" "sbis %1, 7\n\t" "ldi %0, -1\n\t" : "=d" (result) /* result gets written. */ : "I" (_SFR_IO_ADDR(USBPIN)) ); return result; } #endif